Deepseek 字幕翻译脚本

字幕批量翻译工具(DeepSeek API 离线 SRT 翻译脚本)

功能概述

基于 Python + DeepSeek 云端 API 开发的本地 SRT 字幕批量翻译工具,运行后弹窗选择字幕文件夹,自动批量翻译目录内所有原版.srt字幕;短台词、语气助词、纯标点 / 数字短句自动跳过翻译,减少 API 调用消耗,同时解决 AI 翻译序号错乱、漏译、接口超时中断等问题。

核心特性

  1. 可视化文件夹选择:内置弹窗选取字幕目录,无需手动修改代码路径;
  2. 智能短句过滤:字符少于指定长度、纯标点、纯数字字幕直接保留原文,不发起翻译请求,节省 Token 与翻译时长;
  3. 稳健翻译容错机制:API 请求失败自动重试,网络异常批次保留原始字幕不丢失;独立序号映射解析译文,杜绝多行台词、换行导致的字幕匹配错乱、漏翻;
  4. 文件输出规则:翻译完成文件与原字幕存放于同一文件夹,自动命名为 xxx.chn.srt;自动过滤已生成的.chn.srt文件,避免重复翻译;
  5. 限流防护:每批次翻译添加请求间隔,防止 API 短时间高频调用被限流;
  6. 可选双语字幕:支持一键切换「仅中文译文」「原文 + 中文双语对照」两种字幕输出格式。

使用流程

  1. 配置 DeepSeek API 密钥、批量条数、短句过滤阈值等参数;
  2. 运行脚本,弹窗选中存放 SRT 字幕的文件夹;
  3. 程序自动筛选原版字幕,分片调用 DeepSeek 接口翻译;
  4. 同目录生成带.chn后缀的中文字幕文件,全程控制台打印处理进度。

依赖环境

Python3.12,需提前安装 openai、srt 依赖库;网络可访问 DeepSeek API、账户拥有可用余额即可使用。

源代码
from openai import OpenAI
import srt
import os
import tkinter as tk
from tkinter import filedialog
import time
import string

# ===================== 【配置区自行修改】 =====================
DEEPSEEK_API_KEY = "sk-替换成你的密钥"
BATCH_SIZE = 5       # 调小,降低丢句概率
MODEL_NAME = "deepseek-v4-flash"
REQUEST_INTERVAL = 0.8  # 每批请求间隔,防API限流
MAX_RETRY = 2        # 接口失败重试次数
SKIP_MAX_CHAR = 4    # 少于等于该字符直接跳过翻译
SUFFIX_TAG = ".chn"  # 翻译文件后缀标识
# ==========================================================

client = OpenAI(
    api_key=DEEPSEEK_API_KEY,
    base_url="https://api.deepseek.com"
)

def select_folder_dialog():
    root = tk.Tk()
    root.withdraw()
    folder_path = filedialog.askdirectory(title="请选择存放SRT字幕的源文件夹")
    return folder_path

def is_skip_text(text: str) -> bool:
    """判断是否为短句/语气词/纯符号,需要跳过翻译"""
    txt = text.strip()
    if len(txt) <= SKIP_MAX_CHAR:
        return True
    # 全是标点符号
    all_punc = all(c in string.punctuation + " " for c in txt)
    if all_punc:
        return True
    # 纯数字
    if txt.isdigit():
        return True
    return False

def batch_translate(sub_batch):
    # 拆分:需要翻译的条目 / 直接保留原文的条目
    need_trans = []
    keep_origin = []
    for idx, sub in enumerate(sub_batch):
        if is_skip_text(sub.content):
            keep_origin.append((idx, sub))
        else:
            need_trans.append((idx, sub))

    # 没有需要翻译的,直接返回原批次
    if not need_trans:
        time.sleep(REQUEST_INTERVAL)
        return sub_batch

    # 组装待翻译文本
    send_text = ""
    map_idx = {}  # 记录内部序号对应原始下标
    for inner_id, (ori_idx, sub) in enumerate(need_trans):
        map_idx[inner_id + 1] = ori_idx
        send_text += f"{inner_id+1}\n{sub.content.strip()}\n\n"

    retry = 0
    raw_result = ""
    while retry <= MAX_RETRY:
        try:
            resp = client.chat.completions.create(
                model=MODEL_NAME,
                messages=[
                    {
                        "role": "system",
                        "content": """你是专业影视字幕翻译。
输入是带数字序号的台词,严格按顺序逐条翻译为通顺简体中文。
输出格式:每条译文前带上对应数字,一条序号对应一句译文,不要合并、不要漏译。
只输出译文内容,不加任何解释、注释、多余文字。"""
                    },
                    {"role": "user", "content": f"翻译以下台词:\n{send_text}"}
                ],
                temperature=0.2,
                timeout=15
            )
            raw_result = resp.choices[0].message.content.strip()
            break
        except Exception as e:
            retry += 1
            print(f"批次请求失败,重试{retry}/{MAX_RETRY},错误:{str(e)}")
            time.sleep(1.2)
            if retry > MAX_RETRY:
                # 多次失败保留原文
                time.sleep(REQUEST_INTERVAL)
                return sub_batch

    # 解析译文
    trans_map = {}
    current_num = None
    current_text = []
    for line in raw_result.splitlines():
        line = line.strip()
        if not line:
            continue
        if line.isdigit():
            if current_num is not None and current_text:
                trans_map[current_num] = " ".join(current_text)
            current_num = int(line)
            current_text = []
        else:
            current_text.append(line)
    if current_num is not None and current_text:
        trans_map[current_num] = " ".join(current_text)

    # 回填翻译
    for inner_seq, ori_idx in map_idx.items():
        if inner_seq in trans_map and trans_map[inner_seq].strip():
            sub_batch[ori_idx].content = trans_map[inner_seq]
            # 双语开启替换上面这行为:
            # sub_batch[ori_idx].content = sub_batch[ori_idx].content + "\n【译】" + trans_map[inner_seq]

    time.sleep(REQUEST_INTERVAL)
    return sub_batch

def process_single_srt(src_dir, filename):
    src_path = os.path.join(src_dir, filename)
    # 构造输出文件名:test.srt → test.chn.srt
    name_no_ext, ext = os.path.splitext(filename)
    out_filename = f"{name_no_ext}{SUFFIX_TAG}{ext}"
    out_path = os.path.join(src_dir, out_filename)

    print(f"\n==== 正在处理:{filename} → {out_filename} ====")
    try:
        with open(src_path, "r", encoding="utf-8") as f:
            subs = list(srt.parse(f.read()))
    except Exception as e:
        print(f"读取字幕失败:{e}")
        return

    total = len(subs)
    for start in range(0, total, BATCH_SIZE):
        end = start + BATCH_SIZE
        chunk = subs[start:end]
        chunk = batch_translate(chunk)
        subs[start:end] = chunk
        print(f"进度:{min(end, total)}/{total}")

    # 保存翻译后字幕到源文件夹
    with open(out_path, "w", encoding="utf-8") as f:
        f.write(srt.compose(subs))
    print(f"文件完成输出:{out_path}")

def main():
    input_dir = select_folder_dialog()
    if not input_dir:
        print("未选择文件夹,程序退出")
        return

    print(f"源目录:{input_dir}")
    print(f"过滤规则:≤{SKIP_MAX_CHAR}字符、纯标点、纯数字自动跳过翻译")
    print(f"输出规则:同目录生成 *.chn.srt 翻译字幕\n")

    all_files = os.listdir(input_dir)
    srt_files = [f for f in all_files if f.lower().endswith(".srt") and SUFFIX_TAG not in f]
    if not srt_files:
        print("文件夹内未找到未翻译的SRT字幕")
        return

    print(f"共检测到 {len(srt_files)} 个字幕文件,开始翻译")
    for filename in srt_files:
        process_single_srt(input_dir, filename)

    print("\n==================== 全部任务结束 ====================")
    print(f"翻译完成的 .chn.srt 文件保存在原字幕同一目录")

if __name__ == "__main__":
    main()

Share

You may also like...

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理。