批量处理字幕偏移的脚本

wen 实用脚本 1

本文目录导读:

批量处理字幕偏移的脚本

  1. Python 脚本(最通用、强大)
  2. Bash 脚本(Linux/macOS 使用 ffmpeg
  3. Windows PowerShell 脚本
  4. 使用在线工具(无需安装)
  5. 注意事项

这里为您提供几个不同场景下的批量字幕偏移脚本,涵盖常见的 .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()

使用方法:

  1. 保存为 batch_shift_subtitles.py

  2. 安装 Python(已内置 timedeltare 模块,无需额外安装)

  3. 命令行运行:

    # 将所有字幕延后 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

前提: 安装了 ffmpegperl

脚本: 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

注意事项

  1. 备份原始文件:建议先备份再处理。
  2. 偏移单位:脚本以秒为单位,支持小数(如 5 表示 2.5 秒)。
  3. 负偏移:如果字幕显示太快,用负数(如 -1.0)让字幕提前出现。
  4. 时间不会为负:脚本会自动将负时间修正为 0。
  5. 编码问题:Python 脚本会自动尝试常见编码,但某些特殊编码可能需要手动指定。

选择最适合你需求的脚本即可!

抱歉,评论功能暂时关闭!