本文目录导读:

这里为您提供几个不同场景下的批量字幕偏移脚本,涵盖常见的 .srt 和 .ass 格式。
Python 脚本(最通用、强大)
这个脚本可以处理 .srt 和 .ass 格式,支持正数(延后)和负数(提前)偏移。
脚本: batch_shift_subtitles.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
批量处理字幕时间轴偏移
支持格式: .srt, .ass
用法: python batch_shift_subtitles.py <偏移量秒数> <文件1> [文件2] ...
示例: python batch_shift_subtitles.py 2.5 movie1.srt movie2.srt
python batch_shift_subtitles.py -1.0 *.ass
"""
import re
import sys
import os
from datetime import timedelta
def parse_time_srt(time_str):
"""解析 SRT 时间格式: 00:01:23,456 -> 总秒数"""
pattern = r'(\d+):(\d+):(\d+)[,.](\d+)'
match = re.match(pattern, time_str.strip())
if not match:
return None
h, m, s, ms = map(int, match.groups())
return h * 3600 + m * 60 + s + ms / 1000.0
def format_time_srt(seconds):
"""总秒数 -> SRT 时间格式: 00:01:23,456"""
td = timedelta(seconds=seconds)
total_seconds = int(td.total_seconds())
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
secs = total_seconds % 60
millisecs = int((seconds - total_seconds) * 1000)
return f"{hours:02d}:{minutes:02d}:{secs:02d},{millisecs:03d}"
def parse_time_ass(time_str):
"""解析 ASS 时间格式: 0:01:23.45 -> 总秒数"""
pattern = r'(\d+):(\d+):(\d+)\.(\d+)'
match = re.match(pattern, time_str.strip())
if not match:
return None
h, m, s, centiseconds = map(int, match.groups())
return h * 3600 + m * 60 + s + centiseconds / 100.0
def format_time_ass(seconds):
"""总秒数 -> ASS 时间格式: 0:01:23.45"""
td = timedelta(seconds=seconds)
total_seconds = int(td.total_seconds())
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
secs = total_seconds % 60
centisecs = int((seconds - total_seconds) * 100)
return f"{hours}:{minutes:02d}:{secs:02d}.{centisecs:02d}"
def process_srt(filepath, shift_seconds):
"""处理单个 SRT 文件"""
encodings = ['utf-8', 'utf-16', 'gbk', 'latin-1']
content = None
for enc in encodings:
try:
with open(filepath, 'r', encoding=enc) as f:
content = f.read()
break
except UnicodeDecodeError:
continue
if content is None:
print(f"错误: 无法解码文件 {filepath}")
return False
lines = content.splitlines()
new_lines = []
modified = False
# SRT 时间行格式: 00:01:23,456 --> 00:01:25,789
time_pattern = re.compile(r'(\d{2}:\d{2}:\d{2}[,.]\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2}[,.]\d{3})')
for line in lines:
match = time_pattern.match(line)
if match:
start_orig = match.group(1)
end_orig = match.group(2)
start_sec = parse_time_srt(start_orig)
end_sec = parse_time_srt(end_orig)
if start_sec is not None and end_sec is not None:
new_start = max(0, start_sec + shift_seconds) # 防止负时间
new_end = max(0, end_sec + shift_seconds)
new_line = f"{format_time_srt(new_start)} --> {format_time_srt(new_end)}"
new_lines.append(new_line)
modified = True
continue
new_lines.append(line)
if modified:
output_path = filepath.replace('.srt', '_shifted.srt')
if output_path == filepath:
base, ext = os.path.splitext(filepath)
output_path = f"{base}_shifted{ext}"
with open(output_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines))
print(f"已处理: {filepath} -> {output_path}")
return True
else:
print(f"未找到时间轴: {filepath}")
return False
def process_ass(filepath, shift_seconds):
"""处理单个 ASS 文件"""
encodings = ['utf-8', 'utf-16', 'utf-8-sig', 'gbk', 'latin-1']
content = None
for enc in encodings:
try:
with open(filepath, 'r', encoding=enc) as f:
content = f.read()
break
except UnicodeDecodeError:
continue
if content is None:
print(f"错误: 无法解码文件 {filepath}")
return False
lines = content.splitlines()
new_lines = []
modified = False
in_events = False
for line in lines:
# 检查是否进入 Events 部分
if line.strip().startswith('[Events]'):
in_events = True
if in_events and line.startswith('Dialogue:'):
parts = line.split(',', 9) # ASS: Dialogue: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
if len(parts) >= 10:
# 处理开始时间 (第2个字段)
start_sec = parse_time_ass(parts[1].strip())
if start_sec is not None:
new_start = max(0, start_sec + shift_seconds)
parts[1] = format_time_ass(new_start)
modified = True
# 处理结束时间 (第3个字段)
end_sec = parse_time_ass(parts[2].strip())
if end_sec is not None:
new_end = max(0, end_sec + shift_seconds)
parts[2] = format_time_ass(new_end)
modified = True
line = ','.join(parts)
new_lines.append(line)
if modified:
output_path = filepath.replace('.ass', '_shifted.ass')
if output_path == filepath:
base, ext = os.path.splitext(filepath)
output_path = f"{base}_shifted{ext}"
with open(output_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines))
print(f"已处理: {filepath} -> {output_path}")
return True
else:
print(f"未找到时间轴: {filepath}")
return False
def main():
if len(sys.argv) < 3:
print("用法: python batch_shift_subtitles.py <偏移量秒数> <文件1> [文件2] ...")
print("示例: python batch_shift_subtitles.py 2.5 movie.srt")
print(" python batch_shift_subtitles.py -1.0 *.ass")
sys.exit(1)
try:
shift_seconds = float(sys.argv[1])
except ValueError:
print("错误: 偏移量必须是数字(正数延后,负数提前)")
sys.exit(1)
files = sys.argv[2:]
processed = 0
errors = 0
for filepath in files:
if not os.path.exists(filepath):
print(f"错误: 文件不存在 {filepath}")
errors += 1
continue
ext = os.path.splitext(filepath)[1].lower()
if ext == '.srt':
if process_srt(filepath, shift_seconds):
processed += 1
else:
errors += 1
elif ext == '.ass':
if process_ass(filepath, shift_seconds):
processed += 1
else:
errors += 1
else:
print(f"跳过不支持格式: {filepath}")
errors += 1
print(f"\n完成! 处理成功: {processed}, 失败/跳过: {errors}")
if __name__ == "__main__":
main()
使用方法:
-
保存为
batch_shift_subtitles.py -
安装 Python(已内置
timedelta和re模块,无需额外安装) -
命令行运行:
# 将所有字幕延后 2.5 秒 python batch_shift_subtitles.py 2.5 *.srt # 将所有字幕提前 1 秒 python batch_shift_subtitles.py -1.0 *.ass # 指定多个文件 python batch_shift_subtitles.py 3.0 movie1.srt movie2.srt episode3.ass
Bash 脚本(Linux/macOS 使用 ffmpeg)
前提: 安装了 ffmpeg 和 perl
脚本: shift_subs.sh
#!/bin/bash
# 批量 .srt 时间轴偏移,需要 ffmpeg 和 perl
# https://ffmpeg.org/ffmpeg-filters.html#subtitles
shift_seconds="$1" # 2.5 或 -1.0
if [ -z "$shift_seconds" ]; then
echo "用法: ./shift_subs.sh <偏移秒数> [文件列表]"
echo "例: ./shift_subs.sh 2.5 *.srt"
exit 1
fi
shift "${@:2}"
for file in "$@"; do
if [ -f "$file" ] && [[ "$file" == *.srt ]]; then
basename="${file%.*}"
output="${basename}_shifted.srt"
# 使用 ffmpeg 的 subtitles 滤镜进行偏移 (注意: 需要重新编码)
ffmpeg -i "$file" -c:s srt -map 0:s:0 -metadata:s:s:0 width=1920 -metadata:s:s:0 height=1080 \
-filter_complex "[0:v][0:s]overlay[out]" -map "[out]" -c:a copy \
-y "$output" 2>/dev/null
echo "处理: $file -> $output"
# 或者直接用 perl 修改时间戳 (更轻量)
# perl -pe "s/(\d{2}:\d{2}:\d{2}[,.])(\d{3})/.../g" "$file" > "$output"
fi
done
echo "所有文件处理完成。"
Windows PowerShell 脚本
脚本: shift_subtitles.ps1
# 批量偏移 .srt 字幕时间轴 (PowerShell)
param(
[Parameter(Mandatory=$true)]
[float]$ShiftSeconds,
[Parameter(Mandatory=$true, ValueFromRemainingArguments=$true)]
[string[]]$Files
)
function ConvertTo-SrtTime {
param([float]$Seconds)
$ts = [TimeSpan]::FromSeconds($Seconds)
return "{0:00}:{1:00}:{2:00},{3:000}" -f $ts.Hours, $ts.Minutes, $ts.Seconds, $ts.Milliseconds
}
foreach ($file in $Files) {
if (-not (Test-Path $file)) {
Write-Warning "文件不存在: $file"
continue
}
$output = [System.IO.Path]::GetFileNameWithoutExtension($file) + "_shifted.srt"
$reader = [System.IO.StreamReader]::new($file, [System.Text.Encoding]::UTF8)
$writer = [System.IO.StreamWriter]::new($output, $false, [System.Text.Encoding]::UTF8)
$count = 0
while (-not $reader.EndOfStream) {
$line = $reader.ReadLine()
# SRT 时间行: 00:01:23,456 --> 00:01:25,789
if ($line -match '(\d{2}:\d{2}:\d{2},\d{3})\s*-->\s*(\d{2}:\d{2}:\d{2},\d{3})') {
$startStr = $matches[1]
$endStr = $matches[2]
# 解析时间
$startSec = [TimeSpan]::Parse($startStr.Replace(',', '.')).TotalSeconds + $ShiftSeconds
$endSec = [TimeSpan]::Parse($endStr.Replace(',', '.')).TotalSeconds + $ShiftSeconds
# 防止负时间
if ($startSec -lt 0) { $startSec = 0 }
if ($endSec -lt 0) { $endSec = 0 }
$newLine = "{0} --> {1}" -f (ConvertTo-SrtTime $startSec), (ConvertTo-SrtTime $endSec)
$writer.WriteLine($newLine)
$count++
} else {
$writer.WriteLine($line)
}
}
$reader.Close()
$writer.Close()
Write-Host "已处理 $file -> $output (修改了 $count 个时间轴)"
}
使用:
.\shift_subtitles.ps1 -ShiftSeconds 2.5 -Files *.srt .\shift_subtitles.ps1 -ShiftSeconds -1.0 -Files episode1.srt, episode2.srt
使用在线工具(无需安装)
如果不想用脚本,也可以使用在线工具进行批量处理:
- Subtitle Edit Online 支持批量偏移
- 或者使用
ffmpeg命令行(已安装的话):# ffmpeg 直接偏移 srt (但会重新编码,可能改变文件) ffmpeg -i input.srt -c:s srt -time_shift 2.5 output.srt
注意事项
- 备份原始文件:建议先备份再处理。
- 偏移单位:脚本以秒为单位,支持小数(如
5表示 2.5 秒)。 - 负偏移:如果字幕显示太快,用负数(如
-1.0)让字幕提前出现。 - 时间不会为负:脚本会自动将负时间修正为 0。
- 编码问题:Python 脚本会自动尝试常见编码,但某些特殊编码可能需要手动指定。
选择最适合你需求的脚本即可!