本文目录导读:

这里为您提供几个不同场景下的批量字幕偏移脚本,涵盖常见的 .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 脚本会自动尝试常见编码,但某些特殊编码可能需要手动指定。
选择最适合你需求的脚本即可!
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播