{"owner":"jianchang512","repo":"pyvideotrans","hasSkills":true,"totalSkillsCount":1,"totalTokensCount":14418,"categories":["project-spec"],"hasMcp":false,"mcpConfig":null,"found":["docs/architecture.md"],"skills":{"docs/architecture.md":"# pyVideoTrans 技术架构与实现原理\n\n`pyvideotrans` 是一款功能强大的开源视频翻译配音工具（v4.13），能够将视频自动翻译并配上目标语言的语音。其核心设计理念是模块化、多线程流水线，通过灵活的标志位组合支持多种工作模式。\n\n![](https://pvtr2.pyvideotrans.com/1760167240539_image.png)\n\n---\n\n## 一、核心处理流程\n\n![](https://pvtr2.pyvideotrans.com/1760165489380_image.png)\n\n软件将视频翻译配音过程分解为 **9 个独立阶段**，形成一条自动化的处理流水线。每个任务通过 5 个布尔标志位（`should_recogn`、`should_trans`、`should_dubbing`、`should_hebing`、`should_separate`）控制哪些阶段被跳过，从而支持不同的工作模式。\n\n### 1.1 九个处理阶段\n\n| 阶段 | 方法 | 职责 |\n|------|------|------|\n| **① 预处理** | `prepare()` | 从视频中分离无声视频流和原始音频流以及预先处理各项准备数据：音频转为单声道16k/wav格式；可选人声/背景分离（UVR/Spleeter）；可选降噪(onnx模型)；创建缓存目录和输出目录；决定清理缓存或是使用缓存 |\n| **② 语音识别** | `recogn()` | 调用 ASR 引擎（默认 Faster-Whisper+large-v3-turbo，支持 20+ ）,将音频转录为带时间戳的 SRT 字幕；可选标点恢复/删除、LLM纠错 |\n| **③ 说话人分离** | `diariz()` | 若选择了说话人分离，调用说话人分离模型（built-in(onnx模型)、ali_CAM、pyannote等后端），将字幕按说话人归类标注 （批量视频翻译时禁用分离）|\n| **④ 字幕翻译** | `trans()` | 若原始语言和目标语言不同，则将原始语言 SRT 字幕通过翻译渠道（20+ ）翻译为目标语言字幕；支持双语字幕输出 |\n| **⑤ TTS配音** | `dubbing()` | 若选择了配音角色，则根据目标语言字幕内容和时间戳，调用 TTS 引擎（34 +）逐条生成配音音频；支持声音克隆（从原始音频截取参考片段） |\n| **⑥ 音画对齐** | `align()` | 通过 `SpeedRate` 类处理：配音加速、视频慢放、去除字幕间隙静音、字幕音频强制对齐；完成后可选调节音量 |\n| **⑦ 二次识别** | `recogn2pass()` | 若选择了二次识别，对配音音频再次进行 ASR(固定faster-whisper渠道+large-v3-turo模型(默认，可切换))，生成时间轴精确且短小的字幕（仅在启用配音且非双字幕嵌入时执行） |\n| **⑧ 最终合成** | `assembling()` | 将无声视频流、配音音频、背景音乐、目标语言字幕合并为最终视频文件（ffmpeg） |\n| **⑨ 收尾** | `task_done()` | 将输出文件从临时目录移动到指定输出目录，清理临时文件，发送完成通知 |\n\n\n### 1.2 流程控制标志位\n\n定义在 `videotrans/task/_base.py:20-29`，五个标志位在 `TransCreate.__post_init__()` 中根据配置自动计算：\n\n```python\nshould_recogn: bool    # 是否需要语音识别（无已有字幕则为 True）\nshould_trans: bool     # 是否需要翻译（源语言 ≠ 目标语言则为 True）\nshould_dubbing: bool   # 是否需要配音（选择了配音角色且非 'No' 则为 True）\nshould_hebing: bool    # 是否需要嵌入合并（非 'tiqu' 模式且有配音或字幕嵌入则为 True）\nshould_separate: bool  # 是否需要人声背景分离\n```\n\n### 1.3 模式切换示例\n\n不同功能通过标志位组合实现：\n\n| 功能 | should_recogn | should_trans | should_dubbing | should_hebing |\n|------|:---:|:---:|:---:|:---:|\n| 视频翻译 | ✓ | ✓ | ✓ | ✓ |\n| 转录翻译（tiqu） | ✓ | 可选 | ✗ | ✗ |\n| 语音转录 | ✓ | ✗ | ✗ | ✗ |\n| 文字配音 | ✗ | ✗ | ✓ | ✓ |\n| 翻译字幕 | ✗ | ✓ | ✗ | ✗ |\n\n### 1.4 任务子类体系\n\n`BaseTask` 有四个具体子类，各自对应不同的使用场景：\n\n| 子类 | 文件 | 继承的 TaskCfg | 使用场景 |\n|------|------|----------------|---------|\n| `TransCreate` | `task/trans_create.py` | `TaskCfgVTT` | 完整视频翻译配音（标准模式 / tiqu转录同时翻译字幕模式） |\n| `SpeechToText` | `task/speech2text.py` | `TaskCfgSTT` | 语音转录独立功能 |\n| `DubbingSrt` | `task/dubbing.py` | `TaskCfgTTS` | 文字配音独立功能 |\n| `TranslateSrt` | `task/translate_srt.py` | `TaskCfgSTS` | 翻译字幕独立功能 |\n\n---\n\n## 二、任务配置数据类体系\n\n任务配置为分层继承的 `@dataclass` 体系（`videotrans/task/taskcfg.py`）：\n\n```\n@dataclass TaskCfgBase              ← 通用字段（路径、语言代码、缓存目录等）\n    ├── @dataclass TaskCfgSTT       ← 语音识别相关字段（recogn_type, model_name, rephrase 等）\n    ├── @dataclass TaskCfgTTS       ← 配音相关字段（tts_type, voice_role, voice_autorate 等）\n    ├── @dataclass TaskCfgSTS       ← 翻译相关字段（translate_type）\n    └── @dataclass TaskCfgVTT       ← 视频翻译全量字段（继承 STT + TTS + STS，新增视频特有字段）\n```\n\n辅助数据类：\n\n| 数据类 | 文件 | 用途 |\n|--------|------|------|\n| `InputFile` | `task/taskcfg.py` | 输入文件元信息（name, dirname, noextname, basename, ext, uuid, target_dir），支持 dict 式访问 |\n| `SignMsg` | `task/taskcfg.py` | 信号消息体（type, uuid, text），提供 `is_stop()` 和 `is_error()` 方法判断状态，在 Worker 线程与主线程间传递 |\n| `SrtItem` | `task/taskcfg.py` | 单条字幕数据（text, start_time, end_time, startraw, endraw, line, time, spk, filename） |\n\n`SrtItem` 支持同时用属性访问（`item.text`）和字典访问（`item['text']`），并可通过 `items()` 迭代。\n\n---\n\n## 三、多线程异步任务处理架构\n\n软件采用基于 **\"生产者-消费者\"模式** 的多线程多队列架构。`MultVideo` 线程充当生产者，将任务对象推入流水线的第一个队列；9 种专用 `BaseWorker` 子类作为消费者，各自监听专属队列。\n\n### 3.1 队列流水线\n\n```\n                     MultVideo (生产者)\n                           │\n                    app_cfg.prepare_queue\n                           ▼\n                   WorkerPrepare (×N)\n                    ┌────────┼────────┐\n                    │ should_recogn ?  │\n                    ▼        ▼        ▼\n           regcon_queue  trans_queue  dubb_queue / assemb_queue / taskdone_queue\n                │\n                ▼\n          WorkerRegcon (×N)\n                │\n         diariz_queue\n                │\n                ▼\n          WorkerDiariz (×N)\n           ┌────┼────┐\n           ▼    ▼    ▼\n      trans_queue  dubb_queue  assemb_queue / taskdone_queue\n           │\n           ▼\n     WorkerTrans (×1)\n      ┌────┼────┐\n      ▼    ▼    ▼\n dubb_queue  assemb_queue  taskdone_queue\n      │\n      ▼\nWorkerDubb (×1)\n      │\n align_queue\n      │\n      ▼\nWorkerAlign (×1)\n ┌────┼────┐\n ▼    ▼    ▼\nregcon2_queue  assemb_queue  taskdone_queue\n │\n ▼\nWorkerRegcon2Pass (×1)\n ┌────┼────┐\n ▼    ▼\nassemb_queue  taskdone_queue\n │\n ▼\nWorkerAssemb (×N)\n │\ntaskdone_queue\n │\n ▼\nWorkerTaskDone (×1)\n │\n(end)\n```\n\n### 3.2 Worker 基类设计\n\n所有工作线程继承自 `BaseWorker(QThread)`（`videotrans/task/job.py:13-66`）：\n\n```python\nclass BaseWorker(QThread):\n    def __init__(self, name, queue):\n        self.name = name\n        self.queue = queue\n\n    def run(self):\n        while True:\n            if app_cfg.exit_soft:          # 全局软退出标志\n                return\n            try:\n                trk = self.queue.get(timeout=1)  # 阻塞1秒取任务\n            except Empty:\n                continue\n            if trk.uuid in app_cfg.stoped_uuid_set:  # 任务已停止\n                continue\n            try:\n                self.process_task(trk)       # 子类实现具体逻辑\n            except Exception as e:\n                self.handle_error(e, trk)    # 统一错误处理\n```\n\n每个子类重写以下方法：\n\n| 方法 | 说明 |\n|------|------|\n| `process_task(trk)` | **必须** — 执行阶段逻辑，并将 trk 路由到下一个队列 |\n| `get_error_prefix(trk)` | 可选 — 返回错误前缀字符串（如 `\"识别出错[Faster-Whisper]\"`） |\n| `cleanup_on_error(trk)` | 可选 — 出错时的清理逻辑 |\n\n`handle_error()` 统一调用 `get_msg_from_except()` 解析异常为用户可读信息，然后通过 `trk.signal()` 发送错误消息。\n\n### 3.3 Worker 路由决策逻辑\n\n每个 Worker 在执行完 `process_task(trk)` 后，根据 `trk` 的标志位决定下一跳队列：\n\n```\nWorkerPrepare    →  regcon_queue | trans_queue | dubb_queue | assemb_queue | taskdone_queue\nWorkerRegcon     →  diariz_queue  (无条件)\nWorkerDiariz     →  trans_queue | dubb_queue | assemb_queue | taskdone_queue  (diariz 异常不阻断流程)\nWorkerTrans      →  dubb_queue | assemb_queue | taskdone_queue\nWorkerDubb       →  align_queue  (无条件)\nWorkerAlign      →  regcon2_queue | assemb_queue | taskdone_queue  (regcon2 仅当有 recogn2pass 属性时)\nWorkerRegcon2Pass → assemb_queue | taskdone_queue\nWorkerAssemb     →  taskdone_queue  (无条件)\nWorkerTaskDone   →  (终止)\n```\n\n### 3.4 线程/进程数量动态计算\n\n- 不存在GPU时，GPU进程固定设为1\n- 存在可用显卡时，进一步根据高级选项中的手动设置，判断应该启动几个GPU进程,  1->min(CPU核数,8)\n- CPU进程数 1 -> min(8,CPU核数,内存/1G, 高级选项中的手动设置数)\n`start_thread()`（`videotrans/task/job.py`）\n\n\n### 3.5 批量任务提交：MultVideo\n\n`MultVideo(QThread)`（`videotrans/task/mult_video.py`）负责将用户选择的多个视频文件逐个创建 `TransCreate` 对象并推入 `prepare_queue`。支持通过 `batch_nums` 参数控制每批并发数量：\n\n- `batch_nums == 0`：全部任务一次性推入队列（最大并发）\n- `batch_nums == 1`：逐次推入，每个任务完成后再推下一个\n- `batch_nums > 1`：每批推入 N 个，等待该批全部完成后再推下一批\n\n### 3.6 软退出机制\n\n全局标志 `app_cfg.exit_soft` 设为 `True` 时，所有 Worker 在下一轮循环中检测并安全退出。`app_cfg.stoped_uuid_set` 用于标记被手动停止的特定任务 UUID，Worker 在取出任务后跳过这些任务。\n\n---\n\n## 四、核心类的设计与继承关系\n\n### 4.1 类继承体系\n\n```\n@dataclass BaseCon                    ← videotrans/configure/base.py\n    │                                  基础属性和工具方法\n    ├── @dataclass BaseTask           ← videotrans/task/_base.py\n    │       │                          定义 8 个阶段空方法和 5 个标志位\n    │       ├── @dataclass TransCreate ← videotrans/task/trans_create.py (~1678 行核心)\n    │       ├── @dataclass SpeechToText ← videotrans/task/speech2text.py (批量语音识别)\n    │       ├── @dataclass DubbingSrt  ← videotrans/task/dubbing.py (批量字幕配音)\n    │       └── @dataclass TranslateSrt ← videotrans/task/translate_srt.py (批量字幕翻译)\n    │\n    ├── @dataclass BaseRecogn         ← videotrans/recognition/_base.py\n    │       │                          VAD 音频切分、字幕合并、CJK 处理\n    │       └── 子类（懒加载）    各 ASR 渠道具体实现\n    │\n    ├── @dataclass BaseTrans          ← videotrans/translator/_base.py\n    │       │                          MD5 缓存、逐行/全文翻译调度\n    │       └── 子类（懒加载）    各翻译渠道具体实现\n    │\n    └── @dataclass BaseTTS            ← videotrans/tts/_base.py\n            │                          异步/多线程并发调度\n            └── 子类（懒加载）    各 TTS 渠道具体实现\n```\n\n所有通道类均为 `@dataclass`，使用 `__post_init__` 初始化而非传统构造函数 `__init__`。\n\n### 4.2 BaseCon——顶层基类\n\n`videotrans/configure/base.py`（296 行）定义了所有类共用的核心能力：\n\n| 方法 | 职责 |\n|------|------|\n| `_exit()` | 检查是否应停止（`exit_soft` 或 UUID 在 `stoped_uuid_set` 中） |\n| `signal(**kwargs)` | 向 UI 发送消息（通过 `push_queue()` → `SignalHub`。CLI 模式下直接 print） |\n| `_set_proxy(type)` | 设置/清除 HTTP 代理（操作 `app_cfg.proxy` 和环境变量） |\n| `_new_process(callback, title, is_cuda, kwargs)` | **在子进程中执行耗时任务**（返回 `(data, error)` 元组） |\n| `_signal_of_process(logs_file)` | 通过轮询 JSON 日志文件的 mtime 实时读取子进程进度 |\n| `convert_to_wav()` | 音频统一转为 48kHz 立体声 WAV（可选去静音） |\n| `_base64_to_audio()` / `_audio_to_base64()` | Base64 音频编解码 |\n| `_process_callback(data)` | 下载进度回调（转发到 `signal()`） |\n\n`BaseCon.__post_init__()` 在初始化时自动调用 `_set_proxy(type='set')` 获取代理配置。\n\n### 4.3 BaseTask——任务基类\n\n`videotrans/task/_base.py:10-167` 定义了所有任务子类的阶段空方法和共享工具：\n\n**阶段方法**（均为空实现，由子类重写）：\n`prepare()`、`recogn()`、`diariz()`、`trans()`、`dubbing()`、`align()`、`assembling()`、`task_done()`\n\n> 注意：`recogn2pass()` 方法定义在 `TransCreate` 中，不在 `BaseTask` 基类中。\n\n**共享方法**：\n| 方法 | 职责 |\n|------|------|\n| `_unlink_size0(file)` | 删除尺寸为 0 的无效文件 |\n| `_save_srt_target(srtstr, file)` | 将 SrtItem 列表格式化为 SRT 字符串并写入文件，发送 `replace_subtitle` 信号 |\n| `check_target_sub(source, target)` | 校验翻译前后字幕行数一致性；不一致时按时间轴匹配对齐 |\n| `set_end(succeed=False)` | 标记任务结束，成功时发送通知并清理临时文件夹 |\n| `_edgetts_single(target_audio, kwargs)` | Edge-TTS 一次性异步配音（带代理回退） |\n\n### 4.4 TransCreate——视频翻译核心实现\n\n`videotrans/task/trans_create.py`（约 1678 行）是完整 9 阶段处理逻辑的实现类。关键内部方法：\n\n| 方法 | 职责 |\n|------|------|\n| `__post_init__()` | 初始化所有文件路径、计算标志位、启动进度计时线程 |\n| `_split_novoice_byraw()` | 从原始视频分离无声视频（优先硬件解码 h264_cuvid，回退 libx264） |\n| `_split_audio_byraw()` | 从原始视频提取 16kHz 单声道 PCM 音频 + 可选人声/背景分离 |\n| `_tts()` | 构建 `queue_tts` 列表（含 clone 参考音频片段），调用 `tts.run()` |\n| `_create_ref_from_vocal()` | 多线程（ThreadPoolExecutor）裁剪原始音频对应片段作为声音克隆参考 |\n| `_recogn_succeed()` | 识别完成后的处理（tiqu 模式下复制文件） |\n| `_back_music()` | 将用户上传的背景音乐与配音音频混合 |\n| `_separate()` | 将分离出的背景音乐重新嵌入配音音频 |\n| `_process_subtitles()` | 处理软/硬字幕嵌入逻辑（单/双字幕、样式设置） |\n\n### 4.5 子进程通道\n\n为防止 `faster-whisper` 崩溃导致整个软件退出，`Faster-Whisper、OpenAI-whisper、Qwen3-ASR、HuggingFace_ASR`、`Qwen3-TTS、OmniVoice、F5-TTS、Confucius-TTS`等部分重型渠道通过 `BaseCon._new_process()` 委托给 `GlobalProcessManager` 在独立子进程中执行。\n\n子进程通过写入 JSON 日志文件来报告进度。`BaseCon._signal_of_process()` 在守护线程中轮询该日志文件，检测到 mtime 变化时解析 JSON 并通过 `signal()` 上报。\n\n---\n\n## 五、配置系统\n\n软件将配置分为三个层次（`videotrans/configure/config.py`，902 行），均为 `@dataclass`：\n\n| 配置类 | 持久化 | 用途 | 示例字段 |\n|--------|--------|------|---------|\n| `AppCfg` | 纯内存 | 队列、状态、线程控制、运行时上下文、CPU、GPU进程数量 | `prepare_queue`, `exit_soft`, `stoped_uuid_set`, `current_status`, `line_roles`, `exec_mode`, `video_codec`, `onlyone_source_sub`, `onlyone_target_sub`, `proxy`, `SUPPORT_LANG` |\n| `AppSettings` | `videotrans/cfg.json` | 高级选项各设置、模型列表 | `homedir`, `model_list`, `vad_type`, `cuda_com_type` |\n| `AppParams` | `videotrans/params.json` | API 密钥、各种界面设置 | `source_language`, `recogn_type`, `chatgpt_key`, `voice_role`, `app_mode` |\n\n关键单例变量在模块加载时自动初始化：\n\n```python\napp_cfg: AppCfg = AppCfg()        # 运行时状态（含 9 个 Queue 实例）\nsettings: AppSettings = AppSettings()  # 从 cfg.json 加载\nparams: AppParams = AppParams()    # 从 params.json 加载\n```\n\n### 5.1 AppSettings 特性\n\n- 支持 `settings['key']` 字典式访问和 `settings.get('key', default)` 方法\n- `get()` 自动对数字类型字段进行类型强制转换（`int_type` 和 `float_type` 白名单）\n- `_get_defaults()` 定义了 ~100 个配置项的默认值\n- 支持连字符字段名映射（如 `\"initial_prompt_zh-cn\"` → `\"initial_prompt_zh_cn\"`）\n\n### 5.2 AppParams 特性\n\n- `_get_defaults()` 定义了 ~100 个用户参数默认值\n- `getset_params(update_data)` 支持批量更新（如 `check_start()` 中收集所有 UI 控件值）\n- API key 类字段统一在此管理，供 `is_input_api()` 校验\n\n### 5.3 AppCfg 运行时状态\n\n- 9 个 `Queue(maxsize=0)`（无限容量）：`prepare_queue` ~ `taskdone_queue`\n- `queue_novice: Dict` — 跟踪无声视频分离进度（key=uuid, value='ing'|'end'）\n- `line_roles: Dict` — 存储单视频模式下用户逐行分配的字幕角色\n- `child_forms: Dict` — 缓存已打开的窗口实例，避免重复创建\n- `exec_mode` — 执行模式（'gui' 或 'cli'）\n- `video_codec` / `codec_cache` — 视频编解码器缓存\n- `onlyone_source_sub` / `onlyone_target_sub` / `onlyone_trans` — 单视频模式字幕状态\n- `SUPPORT_LANG` — 支持的语言列表\n\n### 5.4 环境变量初始化\n\n`_set_env()` 在模块加载时自动执行（`videotrans/configure/config.py:44-72`），设置：\n- `MODELSCOPE_CACHE` / `HF_HOME` / `HF_HUB_CACHE` → `ROOT_DIR/models`\n- `QT_API = 'pyside6'`\n- `PATH` 追加 ffmpeg/sox 目录\n- `OMP_NUM_THREADS = 1`\n- `HF_HUB_DOWNLOAD_TIMEOUT = 3600`\n- `HF_HUB_DISABLE_XET = 1`\n\n---\n\n## 六、GlobalProcessManager——子进程池管理\n\n`videotrans/process/signelobj.py`（167 行）实现了一个类级别单例的 `GlobalProcessManager`：\n\n```\nGlobalProcessManager (类级别单例)\n    ├── _executor_cpu: multiprocessing.Pool\n    │       workers = app_cfg.MAX_CPU_PROCESS \n    │       maxtasksperchild = 1  ← 每个子进程执行一个任务后重启，防内存泄漏\n    │\n    └── _executor_gpu: multiprocessing.Pool\n            workers = app_cfg.MAX_GPU_PROCESS\n            maxtasksperchild = 1\n```\n\n\n\n### 6.3 任务提交接口\n\n```python\nGlobalProcessManager.submit_task_cpu(func, **kwargs)   → AsyncResultFutureWrapper\nGlobalProcessManager.submit_task_gpu(func, **kwargs)   → AsyncResultFutureWrapper\n```\n\n`AsyncResultFutureWrapper` 将 `Pool.apply_async` 的 `AsyncResult` 包装为 `Future` 兼容接口（`.result()`, `.done()`）。\n\n### 6.4 使用场景\n\n通过 `BaseCon._new_process()` 统一调用，用于执行：ASR 推理、TTS 合成、噪声去除、人声分离、说话人分离、标点恢复（均在独立子进程中运行，崩溃不影响主进程）。\n\n---\n\n## 七、SignalHub——跨线程消息中心\n\n`videotrans/configure/signal_hub.py`（33 行）实现了基于 Qt 信号的单例消息传递：\n\n```python\nclass SignalHub(QObject):\n    _instance = None\n    new_message = Signal(str, object)  # (uuid, SignMsg)\n\n    @classmethod\n    def instance(cls):\n        if cls._instance is None:\n            cls._instance = cls()\n        return cls._instance\n\n    @Slot(str, object)\n    def post(self, uuid=None, data=None):\n        self.new_message.emit(uuid, data)  # 跨线程自动使用 QueuedConnection\n```\n\n### 消息流\n\n```\nBaseCon.signal(**kwargs)\n    → push_queue(uuid, SignMsg(**kwargs))     [configure/config.py]\n        → SignalHub.instance().post(uuid, data)\n            → new_message Signal (QueuedConnection)\n                → WinAction.update_data(uuid, data)   [mainwin/_actions.py]\n                    → 按 type 分发:\n                        'logs'|'error'|'succeed'|'set_precent' → set_process_btn_text()\n                        'edit_subtitle_source' → 弹出 EditRecognResultDialog\n                        'edit_subtitle_target' → 弹出 SpeakerAssignmentDialog\n                        'edit_dubbing' → 弹出 EditDubbingResultDialog\n                        'replace_subtitle' → 更新字幕编辑区\n                        'end' → update_status('end')\n```\n\n### 消息类型枚举\n\n| type | 含义 | 处理逻辑 |\n|------|------|---------|\n| `logs` | 普通日志 | 更新进度条文本 |\n| `error` | 错误 | 进度条变红，加入重试队列 |\n| `succeed` | 成功 | 进度条变绿，标记完成 |\n| `set_precent` | 进度百分比 | `text=\"耗时???百分比\"` 格式 |\n| `edit_subtitle_source` | 弹出原始字幕编辑框 | 单视频模式暂停点① |\n| `edit_subtitle_target` | 弹出翻译字幕编辑框 | 单视频模式暂停点② |\n| `edit_dubbing` | 弹出配音结果编辑框 | 单视频模式暂停点③ |\n| `replace_subtitle` | 替换字幕区域内容 | 批量/单视频共用 |\n| `subtitle` | 追加字幕行 | 逐行输出到编辑器 |\n| `end` | 任务完成 | 触发 update_status('end') |\n| `disabled_edit` | 禁止编辑字幕 | 批量模式下锁定编辑器 |\n| `refreshtts` | 刷新 TTS 选择 | 重新设置 TTS 下拉框 |\n| `shitingerror` | 试听错误 | 弹出错误提示 |\n| `ffmpeg` | ffmpeg 状态 | 更新开始按钮文本 |\n\n---\n\n## 八、动态通道加载\n\n`videotrans/__init__.py`（35 行）提供了通用的懒加载机制：\n\n```python\n@dataclass\nclass ChannelProvider:\n    name: str           # 界面显示名称\n    imp: str            # 模块导入后缀（如 \"._whisper\" → \"videotrans.recognition._whisper\"）\n    key_name: str|None  # 对应 params.json 中的 API key 字段（用于 is_input_api 校验）\n    win: str|None       # 对应 winform 中的设置窗口名称\n\ndef get_class(channel_id=0, provider_type=None, _ID_NAME_DICT=None):\n    _key = f'{provider_type}-{channel_id}'\n    if _key in _loaded_modules:\n        return _loaded_modules[_key]\n    module = importlib.import_module(f'videotrans.{provider_type}{_module_map.imp}')\n    for _, obj in inspect.getmembers(module, inspect.isclass):\n        if obj.__module__ == module.__name__:\n            _loaded_modules[_key] = obj\n            return obj\n```\n\n三大模块各自的 `_ID_NAME_DICT`：\n\n| 模块 | 渠道数 | 定义位置 |\n|------|--------|---------|\n| 语音识别 (recognition) | 20+ | `videotrans/recognition/__init__.py:48-79` |\n| 字幕翻译 (translator) | 20+ | `videotrans/translator/__init__.py:60-90` |\n| 文字配音 (tts) | **30+** | `videotrans/tts/__init__.py:75-116` |\n\n### 8.1 统一入口函数\n\n每个模块提供 `run()` 统一入口，内部通过 `get_class()` 获取对应渠道类并实例化调用：\n\n```python\n# recognition/__init__.py\ndef run(*, recogn_type, detect_language, audio_file, ...) -> List[SrtItem]:\n    _cls = get_class(recogn_type, \"recognition\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n\n# translator/__init__.py\ndef run(*, translate_type, text_list, source_code, target_code, ...) -> List[SrtItem]:\n    _cls = get_class(translate_type, \"translator\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n\n# tts/__init__.py\ndef run(*, queue_tts, language, tts_type, ...) -> None:\n    _cls = get_class(tts_type, \"tts\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n```\n\n### 8.2 API Key 校验\n\n每个模块提供 `is_input_api(recogn_type/translate_type/tts_type)` 函数，检查对应渠道的 `key_name` 在 `params` 中是否已填写。未填写时自动弹出对应的 winform 设置窗口。\n\n### 8.3 翻译缓存\n\n`BaseTrans`（`videotrans/translator/_base.py`）实现了基于 MD5 的翻译缓存：\n- 缓存 key = `md5(channel_name + api_url + model + source_lang + target_lang + text)`\n- 缓存文件存储在 `{TEMP_ROOT}/translate_cache/`\n- 写入接口 `_set_cache()`，读取接口 `_get_cache()`\n\n### 8.4 CJK 特殊处理\n\n`BaseRecogn`（`videotrans/recognition/_base.py:58-80`）在 `__post_init__` 中对中日韩等语言进行特殊处理：\n- `join_word_flag`：CJK 语言（zh, ja, ko, yue, th, km）字幕词间不加空格（其他语言加空格）\n- `maxlen`：CJK 语言每行最大字符数为 `settings.cjk_len`（默认 15），其他语言为 `settings.other_len`（默认 40）\n- `jianfan`：中文语言且 `settings.zh_hant_s=True` 时启用繁简转换\n\n### 8.5 翻译调度策略\n\n`BaseTrans.run()` 根据 `aisendsrt` 标志选择不同策略：\n\n| 模式 | 条件 | 方法 | 并发数 |\n|------|------|------|--------|\n| 逐行翻译 | 非 AI 渠道 | `_run_text()` → `_item_task()` | `settings.trans_thread`（默认 10） |\n| 全文翻译 | AI 渠道 + `aisendsrt=True` | `_run_srt()` | `settings.aitrans_thread`（默认 50） |\n\n### 8.6 TTS 调度策略\n\n`BaseTTS.run()` 根据渠道类型选择执行方式：\n\n| 渠道类型 | 调度方式 | 说明 |\n|---------|---------|------|\n| Edge-TTS | `asyncio` 异步 | 单线程内 async 并发 |\n| 其他渠道 | `ThreadPoolExecutor` | 由 `dubbing_thread` 控制并发数（默认 1） |\n\n渠道子类可重写 `_exec()` 方法实现自定义调度。`BaseTTS` 默认调用 `__local_mul_thread()` → `_item_task()`。\n\n---\n\n## 九、交互式单视频处理模式\n\n当用户选择 **1 个视频** 且在**标准模式**下时，程序采用不同于批量流水线的处理模型。\n\n### 9.1 实现：Worker(QThread)\n\n`videotrans/task/only_one.py`（148 行）中的 `Worker` 类在**单个 QThread 内串行执行**全部 9 个阶段，通过 `uito = Signal(str, SignMsg)` 与主线程通信：\n\n```\nWorker.run()\n    ├── trk = TransCreate(cfg=TaskCfgVTT(**self.cfg | obj))\n    ├── trk.prepare()\n    ├── trk.recogn()\n    ├── trk.diariz()\n    ├── [暂停点 ①] → _post(type='edit_subtitle_source')\n    │    用户校对原始字幕 → 点击\"确定\"或等待倒计时\n    ├── trk.trans() (if should_trans)\n    ├── [暂停点 ②] → _post(type='edit_subtitle_target')\n    │    用户校对翻译字幕 + 分配说话人角色 → 点击\"确定\"\n    ├── trk.dubbing() (if should_dubbing)\n    ├── [暂停点 ③] → _post(type='edit_dubbing')\n    │    用户修改配音结果 → 点击\"确定\"\n    ├── trk.align()\n    ├── trk.recogn2pass() 暂停点，修改二次识别后的结果\t\n    ├── trk.assembling()\n    └── trk.task_done()\n```\n\n### 9.2 与批量模式的关键差异\n\n| 维度 | 单视频模式 | 批量模式 |\n|------|-----------|---------|\n| 执行线程 | `Worker(QThread)` 直接执行，不使用队列管道 | `TransCreate` 推入 `prepare_queue`，经 9 个 Worker 队列流动 |\n| 消息通道 | `uito` 信号直接连接到 `WinAction.update_data()` | `BaseCon.signal()` → `push_queue()` → `SignalHub` |\n| 暂停机制 | 三段暂停点，用户可中间编辑 | 不支持暂停编辑 |\n| 进度显示 | 字幕编辑区实时显示 | 进度条 + 按钮文本 |\n\n### 9.3 倒计时与暂停机制\n\n1. **自动倒计时**：`app_cfg.set_countdown(86400)` 设置初始值。Worker 线程每 `sleep(1)` 递减一次。默认倒计时由 `settings.countdown_sec` 控制。\n2. **无限期暂停**：用户点击\"停止\"按钮将 `app_cfg.current_status` 设为 `'stop'`，Worker 的 `_exit()` 检测后退出；或 `set_countdown(-1)` 让倒计时消失。\n3. **手动继续**：用户在校对对话框中点击\"确定\"后，`WinAction.set_djs_timeout()` 调用 `app_cfg.set_countdown(-1)` 使倒计时立即归零。\n\n### 9.4 校对对话框\n\n| 对话框 | 文件 | 功能 |\n|--------|------|------|\n| `EditRecognResultDialog` | `component/onlyone_set_recogn.py` | 原始字幕编辑（文本 + 时间轴） |\n| `SpeakerAssignmentDialog` | `component/onlyone_set_role.py` | 翻译字幕编辑 + 逐行分配配音角色 |\n| `EditDubbingResultDialog` | `component/onlyone_set_editdubb.py` | 配音结果试听 + 单独重新配音 |\n| `EditRecognResultDialog2` | `component/onlyone_set_recogn2.py` | 二次识别字幕编辑（文本 + 时间轴） |\n\n![](https://pvtr2.pyvideotrans.com/1760192881455_image.png)\n![](https://pvtr2.pyvideotrans.com/1760192930833_image.png)\n\n\n---\n\n## 十、音画对齐引擎（SpeedRate）\n\n`videotrans/task/_rate.py`（877 行）实现了 `SpeedRate` 和 `TtsSpeedRate` 两个对齐引擎：\n\n### 10.1 SpeedRate（视频翻译场景）\n\n处理策略（按优先级）：\n\n| 条件 | 策略 |\n|------|------|\n| 仅启用音频加速 | 加速配音到匹配字幕时长（最高不超过 `max_audio_speed_rate`） |\n| 启用音频加速 + 视频慢速 | 各负担一半时间差（忽略倍率限制） |\n| 仅启用视频慢速 | 慢放视频片段到匹配配音时长（最高不超过 `max_video_pts_rate`） |\n| 两者均未启用 | 按字幕时间轴拼接音频片段，填充静音/定格处理时长差异 |\n\n额外处理：\n- `remove_silent_mid`：去除字幕之间的静音区间\n- `align_sub_audio`：强制对齐字幕时间轴到实际配音位置\n- 末尾静音移除\n\n### 10.2 TtsSpeedRate（纯配音场景）\n\n简化版对齐引擎，仅负责音频拼接与加速，无视频慢放逻辑。\n\n---\n\n## 十一、软件启动与 UI 实现\n\n### 11.1 启动流程\n\n`sp.py` 是唯一入口，启动过程如下：\n\n```\nsp.py (if __name__ == \"__main__\")\n  │\n  ├── 1. multiprocessing.freeze_support() / set_start_method('spawn')\n  ├── 2. qInstallMessageHandler() 抑制 Qt 警告\n  ├── 3. atexit.register(cleanup) 注册退出清理\n  ├── 4. QApplication.setHighDpiScaleFactorRoundingPolicy(PassThrough)\n  ├── 5. 创建 QApplication\n  ├── 6. 检测是否在压缩包内运行（PyInstaller 打包版）\n  ├── 7. 创建 StartWindow (splash screen, 无边框半透明)\n  │       └── QTimer.singleShot(100ms) → initialize_full_app()\n  │           ├── 重定向 sys.stdout/stderr 到日志文件\n  │           ├── 设置全局异常钩子 show_global_error_dialog\n  │           ├── 解析 --lang CLI 参数\n  │           ├── 导入 darkstyle_rc（编译后的 QRC 资源）\n  │           ├── 加载 QSS 样式表 (videotrans/styles/style.qss)\n  │           ├── 恢复上次窗口大小 (QSettings)\n  │           └── 实例化 MainWindow → uito 连接 splash.update_lable\n  │               └── MainWindow.__init__()\n  │                   ├── setupUi() → 填充下拉列表（翻译/识别/TTS 渠道、语言列表）\n  │                   ├── AiLoaderThread 启动 → 检测 GPU → 回调 _start_workers()\n  │                   ├── _start_workers() → start_thread() 启动 9 种 Worker 线程\n  │                   ├── _set_default() → 恢复上次用户选择\n  │                   ├── _bind_signal() → 绑定 ~60 个控件事件\n  │                   ├── SignalHub.new_message.connect(win_action.update_data)\n  │                   └── uito.emit('end') → splash 关闭\n  └── 8. app.exec() → Qt 事件循环\n```\n\n### 11.2 退出机制\n\n用户点击关闭按钮时：\n1. 设置 `app_cfg.exit_soft = True`, `app_cfg.current_status = 'stop'`\n2. 主窗口立即隐藏（`hide()`）\n3. 保存窗口尺寸到 `QSettings`\n4. 隐藏/关闭所有子窗口\n5. 等待 ~4 秒让所有 Worker 完成当前工作并安全退出\n6. 清理临时目录 `TEMP_ROOT`\n7. `atexit` cleanup 回调执行 → 程序终止\n8. 若为重启模式，启动新进程后 `os._exit(0)`\n\n### 11.3 UI 架构分层\n\n```\nUI 定义层         videotrans/ui/         ← PySide6 UI 布局文件，dark/ 资源文件\n    ↓\n    ↓\n窗口管理层        videotrans/winform/     ← 懒加载的设置/功能窗口模块\n    ↓\n主窗口层          videotrans/mainwin/\n    ├── main_win.py                      ← MainWindow(QMainWindow): UI 初始化、信号绑定、Worker 启动、窗口生命周期\n    ├── _actions.py                       ← WinAction: 核心业务逻辑 → 参数收集 → 任务启动 → 状态分发\n    └── _actions_base.py                 ← WinActionBase: 代理管理、模式切换、文件选择、CUDA 检测、试听\n    ↓\n任务层            videotrans/task/        ← TransCreate、SpeechToText、DubbingSrt、TranslateSrt、Worker 线程、SpeedRate\n```\n\n### 11.4 MainWindow——主窗口\n\n`videotrans/mainwin/main_win.py`职责：\n- `setupUi()`：加载 UI 布局，填充下拉列表（翻译渠道、识别渠道、TTS 渠道、语言、字幕类型）\n- `_bind_signal()`：绑定约 60 个控件事件到 `WinAction` 方法\n- `_start_workers(status)`：GPU 检测完成后启动 9 种 Worker 后台线程\n- `open_winform(name)`：统一窗口打开入口（优先复用已缓存的 `app_cfg.child_forms`，否则调用 `winform.get_win(name).openwin()`）\n- `closeEvent()`：安全关闭流程（标记退出 → 隐藏窗口 → 停止线程 → 清理临时文件）\n- `restart_app()`：询问确认后触发 `closeEvent()` 并启动新进程\n\n### 11.5 WinAction——核心控制器\n\n`WinAction` 继承自 `WinActionBase`（两者均为 `@dataclass`），是连接 UI 和后台任务的关键枢纽：\n\n**WinActionBase**（`mainwin/_actions_base.py`，590 行）提供：\n- 文件选择（`get_mp4()`）—— 单文件/文件夹模式\n- 输出目录设置（`get_save_dir()`）\n- 代理配置（`change_proxy()`, `check_proxy()`, `proxy_alert()`）\n- 模式切换（`set_biaozhun()`, `set_tiquzimu()`）—— 控制 UI 元素显隐\n- CUDA 检测（`check_cuda()`, `cuda_isok()`）\n- 试听功能（`listen_voice_fun()`）—— 创建 `ListenVoice` 线程\n- 角色列表更新（`tts_type_change()`, `set_voice_role()`）\n- 更多设置选项折叠（`toggle_adv()`）\n- UI 启用/禁用控制（`disabled_widget()`, `_disabled_button()`）\n\n**WinAction**（`mainwin/_actions.py`，798 行）提供：\n- `check_start()`：收集所有 UI 控件值 → 构建 `cfg` 字典 → 参数校验 → 调用 `create_btns()`\n- `create_btns()`：格式化输入文件路径 → 创建进度条 → 单视频启动 `Worker`，批量启动 `MultVideo`\n- `update_data(uuid, SignMsg)`：连接 `SignalHub.new_message` 信号 → 按消息类型分发\n- `update_status(type)`：切换 `ing`/`stop`/`end` 状态，控制按钮和进度条\n- `set_process_btn_text(d)`：更新进度条文本/百分比/颜色\n- `retry()`：重新处理失败的任务\n- `_check_all_done()`：检测是否所有任务完成\n\n---\n\n## 十二、异常体系\n\n`videotrans/configure/excepts.py`（376 行）定义了分层异常：\n\n```\nVideoTransError (基类)\n    ├── TranslateSrtError       # 翻译相关错误\n    ├── DubbingSrtError         # 配音相关错误\n    ├── SpeechToTextError       # 语音识别相关错误\n    ├── LLMSegmentError         # LLM 重新断句错误\n    ├── FFmpegError             # FFmpeg 操作错误\n    ├── DownloadModelsError     # 模型下载错误\n    ├── SttTimeoutError         # STT 子进程超时\n    ├── StopTask                # 需立即停止的任务异常\n    └── StopRetry               # 不可重试的错误\n```\n\n`get_msg_from_except(e)` 函数映射数十种第三方库异常为用户可读的中/英文错误消息（覆盖 `httpx`、`openai`、`requests`、`deepgram`、`elevenlabs`、`tenacity` 等）。\n\n`NO_RETRY_EXCEPT` 元组定义了不可恢复的异常类型，翻译/配音模块在重试循环中遇到这些异常时直接放弃。\n\n---\n\n## 十三、代码结构概览\n\n```\n/\n├── sp.py                       # ★ 主程序入口\n├── cli.py                      # ★ CLI 命令行入口\n├── models/                     # 存放本地 AI 模型文件（ONNX 等）\n├── logs/                       # 日志文件目录（YYYYMMDD.log）\n├── ffmpeg/                     # ffmpeg 及 sox 二进制文件\n├── f5-tts/                     # 声音克隆参考音频存放目录\n├── docs/                       # 文档\n├── tmp/                        # 临时文件根目录\n│   ├── dubbing_cache/          # 配音缓存\n│   └── translate_cache/        # 翻译 MD5 缓存目录\n│\n└── videotrans/                 # 核心业务逻辑代码\n    │   __init__.py             # ★ VERSION, ChannelProvider 定义, get_class() 懒加载\n    │   cfg.json                # settings 持久化文件\n    │   params.json             # params 持久化文件\n    │   codec.json              # 视频编解码器缓存\n    │\n    ├── codes/\n    │   └── model.py            # 第三方模型相关定义\n    │\n    ├── configure/              # 全局配置、队列定义、顶层基类\n    │   ├── config.py           # ★ AppCfg / AppSettings / AppParams / logger / 队列定义 / tr() / push_queue()\n    │   ├── base.py             # ★ BaseCon 基类（_new_process, signal, _exit, convert_to_wav 等）\n    │   ├── contants.py         # ★ 全局常量（模型列表、语言测试文本、标点符号、代理白名单等）\n    │   ├── excepts.py          # ★ 异常体系 + get_msg_from_except()\n    │   ├── signal_hub.py       # ★ SignalHub 单例（跨线程 Qt 信号）\n    │   └── whispernet_config.py # Whisper.NET 配置\n    │\n    ├── task/                   # 任务处理逻辑与后台线程\n    │   ├── _base.py            # ★ BaseTask 基类\n    │   ├── taskcfg.py          # ★ TaskCfgBase/VTT/STT/TTS/STS + InputFile + SignMsg + SrtItem\n    │   ├── trans_create.py     # ★ TransCreate 完整实现（视频翻译核心）\n    │   ├── speech2text.py      # ★ SpeechToText（语音转录独立功能）\n    │   ├── dubbing.py          # ★ DubbingSrt（文字配音独立功能配音）\n    │   ├── translate_srt.py    # ★ TranslateSrt（翻译字幕独立功能）\n    │   ├── job.py              # ★ 9 种 BaseWorker 子类 + start_thread() 入口\n    │   ├── only_one.py         # ★ 单视频交互式 Worker(QThread) + uito 信号\n    │   ├── mult_video.py       # ★ 多视频批量提交 MultVideo(QThread)\n    │   ├── _rate.py            # SpeedRate / TtsSpeedRate 音画对齐引擎\n    │   ├── separate_worker.py  # SeparateWorker 独立人声分离 QThread\n    │   ├── simple_runnable_qt.py # QRunnable 线程池工具\n    │   ├── child_win_sign.py   # 子窗口信号处理\n    │   └── update_ffmpeg.py    # ffmpeg 更新脚本\n    │\n    ├── recognition/            # 语音识别 (ASR) 渠道包\n    │   ├── _constants.py       # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseRecogn（语音识别基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── translator/             # 字幕翻译渠道包\n    │   ├── _constants.py         # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseTrans（字幕翻译渠道基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── tts/                    # 文字配音 (TTS) 包\n    │   ├── _constants.py         # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseTTS（文字配音基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── process/                # 独立子进程实现：主要用于重型任务，例如语音识别、语音合成大模型等\n    │   ├── __init__.py         # 子进程函数导出\n    │   ├── signelobj.py        # ★ GlobalProcessManager（CPU/GPU 双进程池）\n    │   ├── prepare_audio.py    # 人声分离、降噪、标点恢复、说话人分离\n    │   ├── stt_fun.py          # ASR 子进程入口（openai_whisper, faster_whisper, paraformer, funasr_mlt, qwen3asr_fun 等）\n    │   ├── tts_fun.py          # TTS 子进程入口（qwen3tts_fun）\n    │   └── vad.py              # VAD 语音活动检测（已改为主进程中实现）\n    │\n    ├── mainwin/                # 主窗口界面与业务逻辑\n    │   ├── main_win.py         # ★ MainWindow(QMainWindow) 初始化、信号绑定、线程启动\n    │   ├── _actions.py         # ★ WinAction 核心控制器（检查、启动、状态更新）\n    │   └── _actions_base.py    # ★ WinActionBase 基类\n    │\n    ├── component/              # UI 通用组件\n    │   ├── progressbar.py      # 可点击进度条\n    │   ├── onlyone_set_recogn.py    # 单视频模式：原始字幕编辑对话框\n    │   ├── onlyone_set_recogn2.py    # 单视频模式：二次识别后的字幕编辑对话框\n    │   ├── onlyone_set_role.py      # 单视频模式：说话人角色分配对话框\n    │   ├── onlyone_set_editdubb.py  # 单视频模式：配音结果编辑对话框\n    │   ├── clip_video.py       # 按字幕裁剪视频功能\n    │   ├── realtime_stt.py     # 实时语音识别功能\n    │   ├── textmatching.py     # 文本匹配与打轴功能\n    │   ├── set_ass.py          # 硬字幕样式设置\n    │   ├── set_cpp.py          # Whisper.cpp 路径设置\n    │   ├── set_xxl.py          # Faster-Whisper-XXL 路径设置\n    │   └── controlobj.py       # 控件对象管理\n    │\n    ├── ui/                     # PySide6 UI 定义文件\n    │   ├── en.py               # ★ 主窗口 UI 布局定义\n    │   ├── chatgpt.py, deepseek.py, gemini.py, ...    # 各渠道设置对话框布局\n    │   ├── menu_list.py \t\t# 菜单定义配置文件\n    │   └── dark/               # 暗色主题资源（darkstyle_rc.py, palette.py）\n    │\n    ├── winform/                # 各渠道设置窗口\n    │   ├── __init__.py         # ★ get_win() 懒加载入口 \n    │   ├── chatgpt.py, azure.py, baidu.py, ...  # 渠道设置窗口\n    │\n    ├── styles/                 # UI 样式与媒体资源\n    │   ├── style.qss           # Qt 样式表\n    │   ├── logo.png            # 启动画面 logo\n    │   ├── icon.ico            # 应用图标\n    │   ├── simhei.ttf          # 黑体中文字体\n    │   ├── preview.png         # 设置硬字幕样式用预览图\n    │   ├── no-remove.mp4       # 测试硬编码支持用轻量占位视频\n    │   └── no-remove.wav       # 测试硬编码支持用轻量占位视频\n    │\n    ├── util/                   # 通用工具函数（18 个文件）\n    │   ├── tools.py            # ★ 核心工具函数（ffmpeg 封装、字幕解析/格式化、文件操作、系统通知、模型下载）\n    │   ├── gpus.py             # GPU 检测与分配\n    │   ├── checkgpu.py         # GPU 检测线程（AiLoaderThread）\n    │   ├── ListenVoice.py      # 声音试听功能（ListenVioce QThread）\n    │   ├── req_fac.py          # HuggingFace 自定义 session 工厂\n    │   ├── cn_tn.py            # 中文文本规范化\n    │   ├── en_tn.py            # 英文文本规范化\n    │   ├── help_down.py        # 下载工具函数\n    │   ├── help_ffmpeg.py      # ffmpeg 视频编解码器检测\n    │   ├── help_misc.py        # 杂项工具\n    │   ├── help_role.py        # 配音角色工具\n    │   ├── help_srt.py         # 字幕文件工具\n    │   ├── helper_supertonic.py # Supertonic TTS 辅助\n    │   ├── TestSrtTrans.py     # 翻译测试工具\n    │   └── TestSTT.py          # STT 测试工具\n    │\n    ├── language/               # 界面多语言 JSON 文件\n    │   ├── en_US.json\n    │   ├── zh_CN.json\n    │\n    ├── prompts/                # AI 翻译提示词模板\n    │   ├── srt/                # SRT 格式翻译 prompt（chatgpt.txt, deepseek.txt 等 13 个）\n    │   ├── language_prompts/   # 针对各个目标语言动态插入的 语言要求提示词片段\n    │   ├── text/               # 纯文本翻译 prompt\n    │   └── recharge/           # LLM纠错 prompt（llm.txt）\n    │\n    └── voicejson/              # TTS 音色配置文件（14 个 JSON）\n        ├── edge_tts.json       # Edge-TTS 各语言音色列表\n        ├── azure_voice_list.json # Azure TTS 音色列表\n        ├── qwen3tts.json       # Qwen3-TTS 音色\n        └── ...                 # 其他渠道音色配置\n```\n\n---\n\n\n---\n\n> **版本**: v4.13\n> **主页**: https://github.com/jianchang512/pyvideotrans\n> **文档**: https://pyvideotrans.com\n> **BBS**: https://bbs.pyvideotrans.com\n"},"files":{"docs/architecture.md":"# pyVideoTrans 技术架构与实现原理\n\n`pyvideotrans` 是一款功能强大的开源视频翻译配音工具（v4.13），能够将视频自动翻译并配上目标语言的语音。其核心设计理念是模块化、多线程流水线，通过灵活的标志位组合支持多种工作模式。\n\n![](https://pvtr2.pyvideotrans.com/1760167240539_image.png)\n\n---\n\n## 一、核心处理流程\n\n![](https://pvtr2.pyvideotrans.com/1760165489380_image.png)\n\n软件将视频翻译配音过程分解为 **9 个独立阶段**，形成一条自动化的处理流水线。每个任务通过 5 个布尔标志位（`should_recogn`、`should_trans`、`should_dubbing`、`should_hebing`、`should_separate`）控制哪些阶段被跳过，从而支持不同的工作模式。\n\n### 1.1 九个处理阶段\n\n| 阶段 | 方法 | 职责 |\n|------|------|------|\n| **① 预处理** | `prepare()` | 从视频中分离无声视频流和原始音频流以及预先处理各项准备数据：音频转为单声道16k/wav格式；可选人声/背景分离（UVR/Spleeter）；可选降噪(onnx模型)；创建缓存目录和输出目录；决定清理缓存或是使用缓存 |\n| **② 语音识别** | `recogn()` | 调用 ASR 引擎（默认 Faster-Whisper+large-v3-turbo，支持 20+ ）,将音频转录为带时间戳的 SRT 字幕；可选标点恢复/删除、LLM纠错 |\n| **③ 说话人分离** | `diariz()` | 若选择了说话人分离，调用说话人分离模型（built-in(onnx模型)、ali_CAM、pyannote等后端），将字幕按说话人归类标注 （批量视频翻译时禁用分离）|\n| **④ 字幕翻译** | `trans()` | 若原始语言和目标语言不同，则将原始语言 SRT 字幕通过翻译渠道（20+ ）翻译为目标语言字幕；支持双语字幕输出 |\n| **⑤ TTS配音** | `dubbing()` | 若选择了配音角色，则根据目标语言字幕内容和时间戳，调用 TTS 引擎（34 +）逐条生成配音音频；支持声音克隆（从原始音频截取参考片段） |\n| **⑥ 音画对齐** | `align()` | 通过 `SpeedRate` 类处理：配音加速、视频慢放、去除字幕间隙静音、字幕音频强制对齐；完成后可选调节音量 |\n| **⑦ 二次识别** | `recogn2pass()` | 若选择了二次识别，对配音音频再次进行 ASR(固定faster-whisper渠道+large-v3-turo模型(默认，可切换))，生成时间轴精确且短小的字幕（仅在启用配音且非双字幕嵌入时执行） |\n| **⑧ 最终合成** | `assembling()` | 将无声视频流、配音音频、背景音乐、目标语言字幕合并为最终视频文件（ffmpeg） |\n| **⑨ 收尾** | `task_done()` | 将输出文件从临时目录移动到指定输出目录，清理临时文件，发送完成通知 |\n\n\n### 1.2 流程控制标志位\n\n定义在 `videotrans/task/_base.py:20-29`，五个标志位在 `TransCreate.__post_init__()` 中根据配置自动计算：\n\n```python\nshould_recogn: bool    # 是否需要语音识别（无已有字幕则为 True）\nshould_trans: bool     # 是否需要翻译（源语言 ≠ 目标语言则为 True）\nshould_dubbing: bool   # 是否需要配音（选择了配音角色且非 'No' 则为 True）\nshould_hebing: bool    # 是否需要嵌入合并（非 'tiqu' 模式且有配音或字幕嵌入则为 True）\nshould_separate: bool  # 是否需要人声背景分离\n```\n\n### 1.3 模式切换示例\n\n不同功能通过标志位组合实现：\n\n| 功能 | should_recogn | should_trans | should_dubbing | should_hebing |\n|------|:---:|:---:|:---:|:---:|\n| 视频翻译 | ✓ | ✓ | ✓ | ✓ |\n| 转录翻译（tiqu） | ✓ | 可选 | ✗ | ✗ |\n| 语音转录 | ✓ | ✗ | ✗ | ✗ |\n| 文字配音 | ✗ | ✗ | ✓ | ✓ |\n| 翻译字幕 | ✗ | ✓ | ✗ | ✗ |\n\n### 1.4 任务子类体系\n\n`BaseTask` 有四个具体子类，各自对应不同的使用场景：\n\n| 子类 | 文件 | 继承的 TaskCfg | 使用场景 |\n|------|------|----------------|---------|\n| `TransCreate` | `task/trans_create.py` | `TaskCfgVTT` | 完整视频翻译配音（标准模式 / tiqu转录同时翻译字幕模式） |\n| `SpeechToText` | `task/speech2text.py` | `TaskCfgSTT` | 语音转录独立功能 |\n| `DubbingSrt` | `task/dubbing.py` | `TaskCfgTTS` | 文字配音独立功能 |\n| `TranslateSrt` | `task/translate_srt.py` | `TaskCfgSTS` | 翻译字幕独立功能 |\n\n---\n\n## 二、任务配置数据类体系\n\n任务配置为分层继承的 `@dataclass` 体系（`videotrans/task/taskcfg.py`）：\n\n```\n@dataclass TaskCfgBase              ← 通用字段（路径、语言代码、缓存目录等）\n    ├── @dataclass TaskCfgSTT       ← 语音识别相关字段（recogn_type, model_name, rephrase 等）\n    ├── @dataclass TaskCfgTTS       ← 配音相关字段（tts_type, voice_role, voice_autorate 等）\n    ├── @dataclass TaskCfgSTS       ← 翻译相关字段（translate_type）\n    └── @dataclass TaskCfgVTT       ← 视频翻译全量字段（继承 STT + TTS + STS，新增视频特有字段）\n```\n\n辅助数据类：\n\n| 数据类 | 文件 | 用途 |\n|--------|------|------|\n| `InputFile` | `task/taskcfg.py` | 输入文件元信息（name, dirname, noextname, basename, ext, uuid, target_dir），支持 dict 式访问 |\n| `SignMsg` | `task/taskcfg.py` | 信号消息体（type, uuid, text），提供 `is_stop()` 和 `is_error()` 方法判断状态，在 Worker 线程与主线程间传递 |\n| `SrtItem` | `task/taskcfg.py` | 单条字幕数据（text, start_time, end_time, startraw, endraw, line, time, spk, filename） |\n\n`SrtItem` 支持同时用属性访问（`item.text`）和字典访问（`item['text']`），并可通过 `items()` 迭代。\n\n---\n\n## 三、多线程异步任务处理架构\n\n软件采用基于 **\"生产者-消费者\"模式** 的多线程多队列架构。`MultVideo` 线程充当生产者，将任务对象推入流水线的第一个队列；9 种专用 `BaseWorker` 子类作为消费者，各自监听专属队列。\n\n### 3.1 队列流水线\n\n```\n                     MultVideo (生产者)\n                           │\n                    app_cfg.prepare_queue\n                           ▼\n                   WorkerPrepare (×N)\n                    ┌────────┼────────┐\n                    │ should_recogn ?  │\n                    ▼        ▼        ▼\n           regcon_queue  trans_queue  dubb_queue / assemb_queue / taskdone_queue\n                │\n                ▼\n          WorkerRegcon (×N)\n                │\n         diariz_queue\n                │\n                ▼\n          WorkerDiariz (×N)\n           ┌────┼────┐\n           ▼    ▼    ▼\n      trans_queue  dubb_queue  assemb_queue / taskdone_queue\n           │\n           ▼\n     WorkerTrans (×1)\n      ┌────┼────┐\n      ▼    ▼    ▼\n dubb_queue  assemb_queue  taskdone_queue\n      │\n      ▼\nWorkerDubb (×1)\n      │\n align_queue\n      │\n      ▼\nWorkerAlign (×1)\n ┌────┼────┐\n ▼    ▼    ▼\nregcon2_queue  assemb_queue  taskdone_queue\n │\n ▼\nWorkerRegcon2Pass (×1)\n ┌────┼────┐\n ▼    ▼\nassemb_queue  taskdone_queue\n │\n ▼\nWorkerAssemb (×N)\n │\ntaskdone_queue\n │\n ▼\nWorkerTaskDone (×1)\n │\n(end)\n```\n\n### 3.2 Worker 基类设计\n\n所有工作线程继承自 `BaseWorker(QThread)`（`videotrans/task/job.py:13-66`）：\n\n```python\nclass BaseWorker(QThread):\n    def __init__(self, name, queue):\n        self.name = name\n        self.queue = queue\n\n    def run(self):\n        while True:\n            if app_cfg.exit_soft:          # 全局软退出标志\n                return\n            try:\n                trk = self.queue.get(timeout=1)  # 阻塞1秒取任务\n            except Empty:\n                continue\n            if trk.uuid in app_cfg.stoped_uuid_set:  # 任务已停止\n                continue\n            try:\n                self.process_task(trk)       # 子类实现具体逻辑\n            except Exception as e:\n                self.handle_error(e, trk)    # 统一错误处理\n```\n\n每个子类重写以下方法：\n\n| 方法 | 说明 |\n|------|------|\n| `process_task(trk)` | **必须** — 执行阶段逻辑，并将 trk 路由到下一个队列 |\n| `get_error_prefix(trk)` | 可选 — 返回错误前缀字符串（如 `\"识别出错[Faster-Whisper]\"`） |\n| `cleanup_on_error(trk)` | 可选 — 出错时的清理逻辑 |\n\n`handle_error()` 统一调用 `get_msg_from_except()` 解析异常为用户可读信息，然后通过 `trk.signal()` 发送错误消息。\n\n### 3.3 Worker 路由决策逻辑\n\n每个 Worker 在执行完 `process_task(trk)` 后，根据 `trk` 的标志位决定下一跳队列：\n\n```\nWorkerPrepare    →  regcon_queue | trans_queue | dubb_queue | assemb_queue | taskdone_queue\nWorkerRegcon     →  diariz_queue  (无条件)\nWorkerDiariz     →  trans_queue | dubb_queue | assemb_queue | taskdone_queue  (diariz 异常不阻断流程)\nWorkerTrans      →  dubb_queue | assemb_queue | taskdone_queue\nWorkerDubb       →  align_queue  (无条件)\nWorkerAlign      →  regcon2_queue | assemb_queue | taskdone_queue  (regcon2 仅当有 recogn2pass 属性时)\nWorkerRegcon2Pass → assemb_queue | taskdone_queue\nWorkerAssemb     →  taskdone_queue  (无条件)\nWorkerTaskDone   →  (终止)\n```\n\n### 3.4 线程/进程数量动态计算\n\n- 不存在GPU时，GPU进程固定设为1\n- 存在可用显卡时，进一步根据高级选项中的手动设置，判断应该启动几个GPU进程,  1->min(CPU核数,8)\n- CPU进程数 1 -> min(8,CPU核数,内存/1G, 高级选项中的手动设置数)\n`start_thread()`（`videotrans/task/job.py`）\n\n\n### 3.5 批量任务提交：MultVideo\n\n`MultVideo(QThread)`（`videotrans/task/mult_video.py`）负责将用户选择的多个视频文件逐个创建 `TransCreate` 对象并推入 `prepare_queue`。支持通过 `batch_nums` 参数控制每批并发数量：\n\n- `batch_nums == 0`：全部任务一次性推入队列（最大并发）\n- `batch_nums == 1`：逐次推入，每个任务完成后再推下一个\n- `batch_nums > 1`：每批推入 N 个，等待该批全部完成后再推下一批\n\n### 3.6 软退出机制\n\n全局标志 `app_cfg.exit_soft` 设为 `True` 时，所有 Worker 在下一轮循环中检测并安全退出。`app_cfg.stoped_uuid_set` 用于标记被手动停止的特定任务 UUID，Worker 在取出任务后跳过这些任务。\n\n---\n\n## 四、核心类的设计与继承关系\n\n### 4.1 类继承体系\n\n```\n@dataclass BaseCon                    ← videotrans/configure/base.py\n    │                                  基础属性和工具方法\n    ├── @dataclass BaseTask           ← videotrans/task/_base.py\n    │       │                          定义 8 个阶段空方法和 5 个标志位\n    │       ├── @dataclass TransCreate ← videotrans/task/trans_create.py (~1678 行核心)\n    │       ├── @dataclass SpeechToText ← videotrans/task/speech2text.py (批量语音识别)\n    │       ├── @dataclass DubbingSrt  ← videotrans/task/dubbing.py (批量字幕配音)\n    │       └── @dataclass TranslateSrt ← videotrans/task/translate_srt.py (批量字幕翻译)\n    │\n    ├── @dataclass BaseRecogn         ← videotrans/recognition/_base.py\n    │       │                          VAD 音频切分、字幕合并、CJK 处理\n    │       └── 子类（懒加载）    各 ASR 渠道具体实现\n    │\n    ├── @dataclass BaseTrans          ← videotrans/translator/_base.py\n    │       │                          MD5 缓存、逐行/全文翻译调度\n    │       └── 子类（懒加载）    各翻译渠道具体实现\n    │\n    └── @dataclass BaseTTS            ← videotrans/tts/_base.py\n            │                          异步/多线程并发调度\n            └── 子类（懒加载）    各 TTS 渠道具体实现\n```\n\n所有通道类均为 `@dataclass`，使用 `__post_init__` 初始化而非传统构造函数 `__init__`。\n\n### 4.2 BaseCon——顶层基类\n\n`videotrans/configure/base.py`（296 行）定义了所有类共用的核心能力：\n\n| 方法 | 职责 |\n|------|------|\n| `_exit()` | 检查是否应停止（`exit_soft` 或 UUID 在 `stoped_uuid_set` 中） |\n| `signal(**kwargs)` | 向 UI 发送消息（通过 `push_queue()` → `SignalHub`。CLI 模式下直接 print） |\n| `_set_proxy(type)` | 设置/清除 HTTP 代理（操作 `app_cfg.proxy` 和环境变量） |\n| `_new_process(callback, title, is_cuda, kwargs)` | **在子进程中执行耗时任务**（返回 `(data, error)` 元组） |\n| `_signal_of_process(logs_file)` | 通过轮询 JSON 日志文件的 mtime 实时读取子进程进度 |\n| `convert_to_wav()` | 音频统一转为 48kHz 立体声 WAV（可选去静音） |\n| `_base64_to_audio()` / `_audio_to_base64()` | Base64 音频编解码 |\n| `_process_callback(data)` | 下载进度回调（转发到 `signal()`） |\n\n`BaseCon.__post_init__()` 在初始化时自动调用 `_set_proxy(type='set')` 获取代理配置。\n\n### 4.3 BaseTask——任务基类\n\n`videotrans/task/_base.py:10-167` 定义了所有任务子类的阶段空方法和共享工具：\n\n**阶段方法**（均为空实现，由子类重写）：\n`prepare()`、`recogn()`、`diariz()`、`trans()`、`dubbing()`、`align()`、`assembling()`、`task_done()`\n\n> 注意：`recogn2pass()` 方法定义在 `TransCreate` 中，不在 `BaseTask` 基类中。\n\n**共享方法**：\n| 方法 | 职责 |\n|------|------|\n| `_unlink_size0(file)` | 删除尺寸为 0 的无效文件 |\n| `_save_srt_target(srtstr, file)` | 将 SrtItem 列表格式化为 SRT 字符串并写入文件，发送 `replace_subtitle` 信号 |\n| `check_target_sub(source, target)` | 校验翻译前后字幕行数一致性；不一致时按时间轴匹配对齐 |\n| `set_end(succeed=False)` | 标记任务结束，成功时发送通知并清理临时文件夹 |\n| `_edgetts_single(target_audio, kwargs)` | Edge-TTS 一次性异步配音（带代理回退） |\n\n### 4.4 TransCreate——视频翻译核心实现\n\n`videotrans/task/trans_create.py`（约 1678 行）是完整 9 阶段处理逻辑的实现类。关键内部方法：\n\n| 方法 | 职责 |\n|------|------|\n| `__post_init__()` | 初始化所有文件路径、计算标志位、启动进度计时线程 |\n| `_split_novoice_byraw()` | 从原始视频分离无声视频（优先硬件解码 h264_cuvid，回退 libx264） |\n| `_split_audio_byraw()` | 从原始视频提取 16kHz 单声道 PCM 音频 + 可选人声/背景分离 |\n| `_tts()` | 构建 `queue_tts` 列表（含 clone 参考音频片段），调用 `tts.run()` |\n| `_create_ref_from_vocal()` | 多线程（ThreadPoolExecutor）裁剪原始音频对应片段作为声音克隆参考 |\n| `_recogn_succeed()` | 识别完成后的处理（tiqu 模式下复制文件） |\n| `_back_music()` | 将用户上传的背景音乐与配音音频混合 |\n| `_separate()` | 将分离出的背景音乐重新嵌入配音音频 |\n| `_process_subtitles()` | 处理软/硬字幕嵌入逻辑（单/双字幕、样式设置） |\n\n### 4.5 子进程通道\n\n为防止 `faster-whisper` 崩溃导致整个软件退出，`Faster-Whisper、OpenAI-whisper、Qwen3-ASR、HuggingFace_ASR`、`Qwen3-TTS、OmniVoice、F5-TTS、Confucius-TTS`等部分重型渠道通过 `BaseCon._new_process()` 委托给 `GlobalProcessManager` 在独立子进程中执行。\n\n子进程通过写入 JSON 日志文件来报告进度。`BaseCon._signal_of_process()` 在守护线程中轮询该日志文件，检测到 mtime 变化时解析 JSON 并通过 `signal()` 上报。\n\n---\n\n## 五、配置系统\n\n软件将配置分为三个层次（`videotrans/configure/config.py`，902 行），均为 `@dataclass`：\n\n| 配置类 | 持久化 | 用途 | 示例字段 |\n|--------|--------|------|---------|\n| `AppCfg` | 纯内存 | 队列、状态、线程控制、运行时上下文、CPU、GPU进程数量 | `prepare_queue`, `exit_soft`, `stoped_uuid_set`, `current_status`, `line_roles`, `exec_mode`, `video_codec`, `onlyone_source_sub`, `onlyone_target_sub`, `proxy`, `SUPPORT_LANG` |\n| `AppSettings` | `videotrans/cfg.json` | 高级选项各设置、模型列表 | `homedir`, `model_list`, `vad_type`, `cuda_com_type` |\n| `AppParams` | `videotrans/params.json` | API 密钥、各种界面设置 | `source_language`, `recogn_type`, `chatgpt_key`, `voice_role`, `app_mode` |\n\n关键单例变量在模块加载时自动初始化：\n\n```python\napp_cfg: AppCfg = AppCfg()        # 运行时状态（含 9 个 Queue 实例）\nsettings: AppSettings = AppSettings()  # 从 cfg.json 加载\nparams: AppParams = AppParams()    # 从 params.json 加载\n```\n\n### 5.1 AppSettings 特性\n\n- 支持 `settings['key']` 字典式访问和 `settings.get('key', default)` 方法\n- `get()` 自动对数字类型字段进行类型强制转换（`int_type` 和 `float_type` 白名单）\n- `_get_defaults()` 定义了 ~100 个配置项的默认值\n- 支持连字符字段名映射（如 `\"initial_prompt_zh-cn\"` → `\"initial_prompt_zh_cn\"`）\n\n### 5.2 AppParams 特性\n\n- `_get_defaults()` 定义了 ~100 个用户参数默认值\n- `getset_params(update_data)` 支持批量更新（如 `check_start()` 中收集所有 UI 控件值）\n- API key 类字段统一在此管理，供 `is_input_api()` 校验\n\n### 5.3 AppCfg 运行时状态\n\n- 9 个 `Queue(maxsize=0)`（无限容量）：`prepare_queue` ~ `taskdone_queue`\n- `queue_novice: Dict` — 跟踪无声视频分离进度（key=uuid, value='ing'|'end'）\n- `line_roles: Dict` — 存储单视频模式下用户逐行分配的字幕角色\n- `child_forms: Dict` — 缓存已打开的窗口实例，避免重复创建\n- `exec_mode` — 执行模式（'gui' 或 'cli'）\n- `video_codec` / `codec_cache` — 视频编解码器缓存\n- `onlyone_source_sub` / `onlyone_target_sub` / `onlyone_trans` — 单视频模式字幕状态\n- `SUPPORT_LANG` — 支持的语言列表\n\n### 5.4 环境变量初始化\n\n`_set_env()` 在模块加载时自动执行（`videotrans/configure/config.py:44-72`），设置：\n- `MODELSCOPE_CACHE` / `HF_HOME` / `HF_HUB_CACHE` → `ROOT_DIR/models`\n- `QT_API = 'pyside6'`\n- `PATH` 追加 ffmpeg/sox 目录\n- `OMP_NUM_THREADS = 1`\n- `HF_HUB_DOWNLOAD_TIMEOUT = 3600`\n- `HF_HUB_DISABLE_XET = 1`\n\n---\n\n## 六、GlobalProcessManager——子进程池管理\n\n`videotrans/process/signelobj.py`（167 行）实现了一个类级别单例的 `GlobalProcessManager`：\n\n```\nGlobalProcessManager (类级别单例)\n    ├── _executor_cpu: multiprocessing.Pool\n    │       workers = app_cfg.MAX_CPU_PROCESS \n    │       maxtasksperchild = 1  ← 每个子进程执行一个任务后重启，防内存泄漏\n    │\n    └── _executor_gpu: multiprocessing.Pool\n            workers = app_cfg.MAX_GPU_PROCESS\n            maxtasksperchild = 1\n```\n\n\n\n### 6.3 任务提交接口\n\n```python\nGlobalProcessManager.submit_task_cpu(func, **kwargs)   → AsyncResultFutureWrapper\nGlobalProcessManager.submit_task_gpu(func, **kwargs)   → AsyncResultFutureWrapper\n```\n\n`AsyncResultFutureWrapper` 将 `Pool.apply_async` 的 `AsyncResult` 包装为 `Future` 兼容接口（`.result()`, `.done()`）。\n\n### 6.4 使用场景\n\n通过 `BaseCon._new_process()` 统一调用，用于执行：ASR 推理、TTS 合成、噪声去除、人声分离、说话人分离、标点恢复（均在独立子进程中运行，崩溃不影响主进程）。\n\n---\n\n## 七、SignalHub——跨线程消息中心\n\n`videotrans/configure/signal_hub.py`（33 行）实现了基于 Qt 信号的单例消息传递：\n\n```python\nclass SignalHub(QObject):\n    _instance = None\n    new_message = Signal(str, object)  # (uuid, SignMsg)\n\n    @classmethod\n    def instance(cls):\n        if cls._instance is None:\n            cls._instance = cls()\n        return cls._instance\n\n    @Slot(str, object)\n    def post(self, uuid=None, data=None):\n        self.new_message.emit(uuid, data)  # 跨线程自动使用 QueuedConnection\n```\n\n### 消息流\n\n```\nBaseCon.signal(**kwargs)\n    → push_queue(uuid, SignMsg(**kwargs))     [configure/config.py]\n        → SignalHub.instance().post(uuid, data)\n            → new_message Signal (QueuedConnection)\n                → WinAction.update_data(uuid, data)   [mainwin/_actions.py]\n                    → 按 type 分发:\n                        'logs'|'error'|'succeed'|'set_precent' → set_process_btn_text()\n                        'edit_subtitle_source' → 弹出 EditRecognResultDialog\n                        'edit_subtitle_target' → 弹出 SpeakerAssignmentDialog\n                        'edit_dubbing' → 弹出 EditDubbingResultDialog\n                        'replace_subtitle' → 更新字幕编辑区\n                        'end' → update_status('end')\n```\n\n### 消息类型枚举\n\n| type | 含义 | 处理逻辑 |\n|------|------|---------|\n| `logs` | 普通日志 | 更新进度条文本 |\n| `error` | 错误 | 进度条变红，加入重试队列 |\n| `succeed` | 成功 | 进度条变绿，标记完成 |\n| `set_precent` | 进度百分比 | `text=\"耗时???百分比\"` 格式 |\n| `edit_subtitle_source` | 弹出原始字幕编辑框 | 单视频模式暂停点① |\n| `edit_subtitle_target` | 弹出翻译字幕编辑框 | 单视频模式暂停点② |\n| `edit_dubbing` | 弹出配音结果编辑框 | 单视频模式暂停点③ |\n| `replace_subtitle` | 替换字幕区域内容 | 批量/单视频共用 |\n| `subtitle` | 追加字幕行 | 逐行输出到编辑器 |\n| `end` | 任务完成 | 触发 update_status('end') |\n| `disabled_edit` | 禁止编辑字幕 | 批量模式下锁定编辑器 |\n| `refreshtts` | 刷新 TTS 选择 | 重新设置 TTS 下拉框 |\n| `shitingerror` | 试听错误 | 弹出错误提示 |\n| `ffmpeg` | ffmpeg 状态 | 更新开始按钮文本 |\n\n---\n\n## 八、动态通道加载\n\n`videotrans/__init__.py`（35 行）提供了通用的懒加载机制：\n\n```python\n@dataclass\nclass ChannelProvider:\n    name: str           # 界面显示名称\n    imp: str            # 模块导入后缀（如 \"._whisper\" → \"videotrans.recognition._whisper\"）\n    key_name: str|None  # 对应 params.json 中的 API key 字段（用于 is_input_api 校验）\n    win: str|None       # 对应 winform 中的设置窗口名称\n\ndef get_class(channel_id=0, provider_type=None, _ID_NAME_DICT=None):\n    _key = f'{provider_type}-{channel_id}'\n    if _key in _loaded_modules:\n        return _loaded_modules[_key]\n    module = importlib.import_module(f'videotrans.{provider_type}{_module_map.imp}')\n    for _, obj in inspect.getmembers(module, inspect.isclass):\n        if obj.__module__ == module.__name__:\n            _loaded_modules[_key] = obj\n            return obj\n```\n\n三大模块各自的 `_ID_NAME_DICT`：\n\n| 模块 | 渠道数 | 定义位置 |\n|------|--------|---------|\n| 语音识别 (recognition) | 20+ | `videotrans/recognition/__init__.py:48-79` |\n| 字幕翻译 (translator) | 20+ | `videotrans/translator/__init__.py:60-90` |\n| 文字配音 (tts) | **30+** | `videotrans/tts/__init__.py:75-116` |\n\n### 8.1 统一入口函数\n\n每个模块提供 `run()` 统一入口，内部通过 `get_class()` 获取对应渠道类并实例化调用：\n\n```python\n# recognition/__init__.py\ndef run(*, recogn_type, detect_language, audio_file, ...) -> List[SrtItem]:\n    _cls = get_class(recogn_type, \"recognition\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n\n# translator/__init__.py\ndef run(*, translate_type, text_list, source_code, target_code, ...) -> List[SrtItem]:\n    _cls = get_class(translate_type, \"translator\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n\n# tts/__init__.py\ndef run(*, queue_tts, language, tts_type, ...) -> None:\n    _cls = get_class(tts_type, \"tts\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n```\n\n### 8.2 API Key 校验\n\n每个模块提供 `is_input_api(recogn_type/translate_type/tts_type)` 函数，检查对应渠道的 `key_name` 在 `params` 中是否已填写。未填写时自动弹出对应的 winform 设置窗口。\n\n### 8.3 翻译缓存\n\n`BaseTrans`（`videotrans/translator/_base.py`）实现了基于 MD5 的翻译缓存：\n- 缓存 key = `md5(channel_name + api_url + model + source_lang + target_lang + text)`\n- 缓存文件存储在 `{TEMP_ROOT}/translate_cache/`\n- 写入接口 `_set_cache()`，读取接口 `_get_cache()`\n\n### 8.4 CJK 特殊处理\n\n`BaseRecogn`（`videotrans/recognition/_base.py:58-80`）在 `__post_init__` 中对中日韩等语言进行特殊处理：\n- `join_word_flag`：CJK 语言（zh, ja, ko, yue, th, km）字幕词间不加空格（其他语言加空格）\n- `maxlen`：CJK 语言每行最大字符数为 `settings.cjk_len`（默认 15），其他语言为 `settings.other_len`（默认 40）\n- `jianfan`：中文语言且 `settings.zh_hant_s=True` 时启用繁简转换\n\n### 8.5 翻译调度策略\n\n`BaseTrans.run()` 根据 `aisendsrt` 标志选择不同策略：\n\n| 模式 | 条件 | 方法 | 并发数 |\n|------|------|------|--------|\n| 逐行翻译 | 非 AI 渠道 | `_run_text()` → `_item_task()` | `settings.trans_thread`（默认 10） |\n| 全文翻译 | AI 渠道 + `aisendsrt=True` | `_run_srt()` | `settings.aitrans_thread`（默认 50） |\n\n### 8.6 TTS 调度策略\n\n`BaseTTS.run()` 根据渠道类型选择执行方式：\n\n| 渠道类型 | 调度方式 | 说明 |\n|---------|---------|------|\n| Edge-TTS | `asyncio` 异步 | 单线程内 async 并发 |\n| 其他渠道 | `ThreadPoolExecutor` | 由 `dubbing_thread` 控制并发数（默认 1） |\n\n渠道子类可重写 `_exec()` 方法实现自定义调度。`BaseTTS` 默认调用 `__local_mul_thread()` → `_item_task()`。\n\n---\n\n## 九、交互式单视频处理模式\n\n当用户选择 **1 个视频** 且在**标准模式**下时，程序采用不同于批量流水线的处理模型。\n\n### 9.1 实现：Worker(QThread)\n\n`videotrans/task/only_one.py`（148 行）中的 `Worker` 类在**单个 QThread 内串行执行**全部 9 个阶段，通过 `uito = Signal(str, SignMsg)` 与主线程通信：\n\n```\nWorker.run()\n    ├── trk = TransCreate(cfg=TaskCfgVTT(**self.cfg | obj))\n    ├── trk.prepare()\n    ├── trk.recogn()\n    ├── trk.diariz()\n    ├── [暂停点 ①] → _post(type='edit_subtitle_source')\n    │    用户校对原始字幕 → 点击\"确定\"或等待倒计时\n    ├── trk.trans() (if should_trans)\n    ├── [暂停点 ②] → _post(type='edit_subtitle_target')\n    │    用户校对翻译字幕 + 分配说话人角色 → 点击\"确定\"\n    ├── trk.dubbing() (if should_dubbing)\n    ├── [暂停点 ③] → _post(type='edit_dubbing')\n    │    用户修改配音结果 → 点击\"确定\"\n    ├── trk.align()\n    ├── trk.recogn2pass() 暂停点，修改二次识别后的结果\t\n    ├── trk.assembling()\n    └── trk.task_done()\n```\n\n### 9.2 与批量模式的关键差异\n\n| 维度 | 单视频模式 | 批量模式 |\n|------|-----------|---------|\n| 执行线程 | `Worker(QThread)` 直接执行，不使用队列管道 | `TransCreate` 推入 `prepare_queue`，经 9 个 Worker 队列流动 |\n| 消息通道 | `uito` 信号直接连接到 `WinAction.update_data()` | `BaseCon.signal()` → `push_queue()` → `SignalHub` |\n| 暂停机制 | 三段暂停点，用户可中间编辑 | 不支持暂停编辑 |\n| 进度显示 | 字幕编辑区实时显示 | 进度条 + 按钮文本 |\n\n### 9.3 倒计时与暂停机制\n\n1. **自动倒计时**：`app_cfg.set_countdown(86400)` 设置初始值。Worker 线程每 `sleep(1)` 递减一次。默认倒计时由 `settings.countdown_sec` 控制。\n2. **无限期暂停**：用户点击\"停止\"按钮将 `app_cfg.current_status` 设为 `'stop'`，Worker 的 `_exit()` 检测后退出；或 `set_countdown(-1)` 让倒计时消失。\n3. **手动继续**：用户在校对对话框中点击\"确定\"后，`WinAction.set_djs_timeout()` 调用 `app_cfg.set_countdown(-1)` 使倒计时立即归零。\n\n### 9.4 校对对话框\n\n| 对话框 | 文件 | 功能 |\n|--------|------|------|\n| `EditRecognResultDialog` | `component/onlyone_set_recogn.py` | 原始字幕编辑（文本 + 时间轴） |\n| `SpeakerAssignmentDialog` | `component/onlyone_set_role.py` | 翻译字幕编辑 + 逐行分配配音角色 |\n| `EditDubbingResultDialog` | `component/onlyone_set_editdubb.py` | 配音结果试听 + 单独重新配音 |\n| `EditRecognResultDialog2` | `component/onlyone_set_recogn2.py` | 二次识别字幕编辑（文本 + 时间轴） |\n\n![](https://pvtr2.pyvideotrans.com/1760192881455_image.png)\n![](https://pvtr2.pyvideotrans.com/1760192930833_image.png)\n\n\n---\n\n## 十、音画对齐引擎（SpeedRate）\n\n`videotrans/task/_rate.py`（877 行）实现了 `SpeedRate` 和 `TtsSpeedRate` 两个对齐引擎：\n\n### 10.1 SpeedRate（视频翻译场景）\n\n处理策略（按优先级）：\n\n| 条件 | 策略 |\n|------|------|\n| 仅启用音频加速 | 加速配音到匹配字幕时长（最高不超过 `max_audio_speed_rate`） |\n| 启用音频加速 + 视频慢速 | 各负担一半时间差（忽略倍率限制） |\n| 仅启用视频慢速 | 慢放视频片段到匹配配音时长（最高不超过 `max_video_pts_rate`） |\n| 两者均未启用 | 按字幕时间轴拼接音频片段，填充静音/定格处理时长差异 |\n\n额外处理：\n- `remove_silent_mid`：去除字幕之间的静音区间\n- `align_sub_audio`：强制对齐字幕时间轴到实际配音位置\n- 末尾静音移除\n\n### 10.2 TtsSpeedRate（纯配音场景）\n\n简化版对齐引擎，仅负责音频拼接与加速，无视频慢放逻辑。\n\n---\n\n## 十一、软件启动与 UI 实现\n\n### 11.1 启动流程\n\n`sp.py` 是唯一入口，启动过程如下：\n\n```\nsp.py (if __name__ == \"__main__\")\n  │\n  ├── 1. multiprocessing.freeze_support() / set_start_method('spawn')\n  ├── 2. qInstallMessageHandler() 抑制 Qt 警告\n  ├── 3. atexit.register(cleanup) 注册退出清理\n  ├── 4. QApplication.setHighDpiScaleFactorRoundingPolicy(PassThrough)\n  ├── 5. 创建 QApplication\n  ├── 6. 检测是否在压缩包内运行（PyInstaller 打包版）\n  ├── 7. 创建 StartWindow (splash screen, 无边框半透明)\n  │       └── QTimer.singleShot(100ms) → initialize_full_app()\n  │           ├── 重定向 sys.stdout/stderr 到日志文件\n  │           ├── 设置全局异常钩子 show_global_error_dialog\n  │           ├── 解析 --lang CLI 参数\n  │           ├── 导入 darkstyle_rc（编译后的 QRC 资源）\n  │           ├── 加载 QSS 样式表 (videotrans/styles/style.qss)\n  │           ├── 恢复上次窗口大小 (QSettings)\n  │           └── 实例化 MainWindow → uito 连接 splash.update_lable\n  │               └── MainWindow.__init__()\n  │                   ├── setupUi() → 填充下拉列表（翻译/识别/TTS 渠道、语言列表）\n  │                   ├── AiLoaderThread 启动 → 检测 GPU → 回调 _start_workers()\n  │                   ├── _start_workers() → start_thread() 启动 9 种 Worker 线程\n  │                   ├── _set_default() → 恢复上次用户选择\n  │                   ├── _bind_signal() → 绑定 ~60 个控件事件\n  │                   ├── SignalHub.new_message.connect(win_action.update_data)\n  │                   └── uito.emit('end') → splash 关闭\n  └── 8. app.exec() → Qt 事件循环\n```\n\n### 11.2 退出机制\n\n用户点击关闭按钮时：\n1. 设置 `app_cfg.exit_soft = True`, `app_cfg.current_status = 'stop'`\n2. 主窗口立即隐藏（`hide()`）\n3. 保存窗口尺寸到 `QSettings`\n4. 隐藏/关闭所有子窗口\n5. 等待 ~4 秒让所有 Worker 完成当前工作并安全退出\n6. 清理临时目录 `TEMP_ROOT`\n7. `atexit` cleanup 回调执行 → 程序终止\n8. 若为重启模式，启动新进程后 `os._exit(0)`\n\n### 11.3 UI 架构分层\n\n```\nUI 定义层         videotrans/ui/         ← PySide6 UI 布局文件，dark/ 资源文件\n    ↓\n    ↓\n窗口管理层        videotrans/winform/     ← 懒加载的设置/功能窗口模块\n    ↓\n主窗口层          videotrans/mainwin/\n    ├── main_win.py                      ← MainWindow(QMainWindow): UI 初始化、信号绑定、Worker 启动、窗口生命周期\n    ├── _actions.py                       ← WinAction: 核心业务逻辑 → 参数收集 → 任务启动 → 状态分发\n    └── _actions_base.py                 ← WinActionBase: 代理管理、模式切换、文件选择、CUDA 检测、试听\n    ↓\n任务层            videotrans/task/        ← TransCreate、SpeechToText、DubbingSrt、TranslateSrt、Worker 线程、SpeedRate\n```\n\n### 11.4 MainWindow——主窗口\n\n`videotrans/mainwin/main_win.py`职责：\n- `setupUi()`：加载 UI 布局，填充下拉列表（翻译渠道、识别渠道、TTS 渠道、语言、字幕类型）\n- `_bind_signal()`：绑定约 60 个控件事件到 `WinAction` 方法\n- `_start_workers(status)`：GPU 检测完成后启动 9 种 Worker 后台线程\n- `open_winform(name)`：统一窗口打开入口（优先复用已缓存的 `app_cfg.child_forms`，否则调用 `winform.get_win(name).openwin()`）\n- `closeEvent()`：安全关闭流程（标记退出 → 隐藏窗口 → 停止线程 → 清理临时文件）\n- `restart_app()`：询问确认后触发 `closeEvent()` 并启动新进程\n\n### 11.5 WinAction——核心控制器\n\n`WinAction` 继承自 `WinActionBase`（两者均为 `@dataclass`），是连接 UI 和后台任务的关键枢纽：\n\n**WinActionBase**（`mainwin/_actions_base.py`，590 行）提供：\n- 文件选择（`get_mp4()`）—— 单文件/文件夹模式\n- 输出目录设置（`get_save_dir()`）\n- 代理配置（`change_proxy()`, `check_proxy()`, `proxy_alert()`）\n- 模式切换（`set_biaozhun()`, `set_tiquzimu()`）—— 控制 UI 元素显隐\n- CUDA 检测（`check_cuda()`, `cuda_isok()`）\n- 试听功能（`listen_voice_fun()`）—— 创建 `ListenVoice` 线程\n- 角色列表更新（`tts_type_change()`, `set_voice_role()`）\n- 更多设置选项折叠（`toggle_adv()`）\n- UI 启用/禁用控制（`disabled_widget()`, `_disabled_button()`）\n\n**WinAction**（`mainwin/_actions.py`，798 行）提供：\n- `check_start()`：收集所有 UI 控件值 → 构建 `cfg` 字典 → 参数校验 → 调用 `create_btns()`\n- `create_btns()`：格式化输入文件路径 → 创建进度条 → 单视频启动 `Worker`，批量启动 `MultVideo`\n- `update_data(uuid, SignMsg)`：连接 `SignalHub.new_message` 信号 → 按消息类型分发\n- `update_status(type)`：切换 `ing`/`stop`/`end` 状态，控制按钮和进度条\n- `set_process_btn_text(d)`：更新进度条文本/百分比/颜色\n- `retry()`：重新处理失败的任务\n- `_check_all_done()`：检测是否所有任务完成\n\n---\n\n## 十二、异常体系\n\n`videotrans/configure/excepts.py`（376 行）定义了分层异常：\n\n```\nVideoTransError (基类)\n    ├── TranslateSrtError       # 翻译相关错误\n    ├── DubbingSrtError         # 配音相关错误\n    ├── SpeechToTextError       # 语音识别相关错误\n    ├── LLMSegmentError         # LLM 重新断句错误\n    ├── FFmpegError             # FFmpeg 操作错误\n    ├── DownloadModelsError     # 模型下载错误\n    ├── SttTimeoutError         # STT 子进程超时\n    ├── StopTask                # 需立即停止的任务异常\n    └── StopRetry               # 不可重试的错误\n```\n\n`get_msg_from_except(e)` 函数映射数十种第三方库异常为用户可读的中/英文错误消息（覆盖 `httpx`、`openai`、`requests`、`deepgram`、`elevenlabs`、`tenacity` 等）。\n\n`NO_RETRY_EXCEPT` 元组定义了不可恢复的异常类型，翻译/配音模块在重试循环中遇到这些异常时直接放弃。\n\n---\n\n## 十三、代码结构概览\n\n```\n/\n├── sp.py                       # ★ 主程序入口\n├── cli.py                      # ★ CLI 命令行入口\n├── models/                     # 存放本地 AI 模型文件（ONNX 等）\n├── logs/                       # 日志文件目录（YYYYMMDD.log）\n├── ffmpeg/                     # ffmpeg 及 sox 二进制文件\n├── f5-tts/                     # 声音克隆参考音频存放目录\n├── docs/                       # 文档\n├── tmp/                        # 临时文件根目录\n│   ├── dubbing_cache/          # 配音缓存\n│   └── translate_cache/        # 翻译 MD5 缓存目录\n│\n└── videotrans/                 # 核心业务逻辑代码\n    │   __init__.py             # ★ VERSION, ChannelProvider 定义, get_class() 懒加载\n    │   cfg.json                # settings 持久化文件\n    │   params.json             # params 持久化文件\n    │   codec.json              # 视频编解码器缓存\n    │\n    ├── codes/\n    │   └── model.py            # 第三方模型相关定义\n    │\n    ├── configure/              # 全局配置、队列定义、顶层基类\n    │   ├── config.py           # ★ AppCfg / AppSettings / AppParams / logger / 队列定义 / tr() / push_queue()\n    │   ├── base.py             # ★ BaseCon 基类（_new_process, signal, _exit, convert_to_wav 等）\n    │   ├── contants.py         # ★ 全局常量（模型列表、语言测试文本、标点符号、代理白名单等）\n    │   ├── excepts.py          # ★ 异常体系 + get_msg_from_except()\n    │   ├── signal_hub.py       # ★ SignalHub 单例（跨线程 Qt 信号）\n    │   └── whispernet_config.py # Whisper.NET 配置\n    │\n    ├── task/                   # 任务处理逻辑与后台线程\n    │   ├── _base.py            # ★ BaseTask 基类\n    │   ├── taskcfg.py          # ★ TaskCfgBase/VTT/STT/TTS/STS + InputFile + SignMsg + SrtItem\n    │   ├── trans_create.py     # ★ TransCreate 完整实现（视频翻译核心）\n    │   ├── speech2text.py      # ★ SpeechToText（语音转录独立功能）\n    │   ├── dubbing.py          # ★ DubbingSrt（文字配音独立功能配音）\n    │   ├── translate_srt.py    # ★ TranslateSrt（翻译字幕独立功能）\n    │   ├── job.py              # ★ 9 种 BaseWorker 子类 + start_thread() 入口\n    │   ├── only_one.py         # ★ 单视频交互式 Worker(QThread) + uito 信号\n    │   ├── mult_video.py       # ★ 多视频批量提交 MultVideo(QThread)\n    │   ├── _rate.py            # SpeedRate / TtsSpeedRate 音画对齐引擎\n    │   ├── separate_worker.py  # SeparateWorker 独立人声分离 QThread\n    │   ├── simple_runnable_qt.py # QRunnable 线程池工具\n    │   ├── child_win_sign.py   # 子窗口信号处理\n    │   └── update_ffmpeg.py    # ffmpeg 更新脚本\n    │\n    ├── recognition/            # 语音识别 (ASR) 渠道包\n    │   ├── _constants.py       # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseRecogn（语音识别基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── translator/             # 字幕翻译渠道包\n    │   ├── _constants.py         # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseTrans（字幕翻译渠道基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── tts/                    # 文字配音 (TTS) 包\n    │   ├── _constants.py         # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseTTS（文字配音基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── process/                # 独立子进程实现：主要用于重型任务，例如语音识别、语音合成大模型等\n    │   ├── __init__.py         # 子进程函数导出\n    │   ├── signelobj.py        # ★ GlobalProcessManager（CPU/GPU 双进程池）\n    │   ├── prepare_audio.py    # 人声分离、降噪、标点恢复、说话人分离\n    │   ├── stt_fun.py          # ASR 子进程入口（openai_whisper, faster_whisper, paraformer, funasr_mlt, qwen3asr_fun 等）\n    │   ├── tts_fun.py          # TTS 子进程入口（qwen3tts_fun）\n    │   └── vad.py              # VAD 语音活动检测（已改为主进程中实现）\n    │\n    ├── mainwin/                # 主窗口界面与业务逻辑\n    │   ├── main_win.py         # ★ MainWindow(QMainWindow) 初始化、信号绑定、线程启动\n    │   ├── _actions.py         # ★ WinAction 核心控制器（检查、启动、状态更新）\n    │   └── _actions_base.py    # ★ WinActionBase 基类\n    │\n    ├── component/              # UI 通用组件\n    │   ├── progressbar.py      # 可点击进度条\n    │   ├── onlyone_set_recogn.py    # 单视频模式：原始字幕编辑对话框\n    │   ├── onlyone_set_recogn2.py    # 单视频模式：二次识别后的字幕编辑对话框\n    │   ├── onlyone_set_role.py      # 单视频模式：说话人角色分配对话框\n    │   ├── onlyone_set_editdubb.py  # 单视频模式：配音结果编辑对话框\n    │   ├── clip_video.py       # 按字幕裁剪视频功能\n    │   ├── realtime_stt.py     # 实时语音识别功能\n    │   ├── textmatching.py     # 文本匹配与打轴功能\n    │   ├── set_ass.py          # 硬字幕样式设置\n    │   ├── set_cpp.py          # Whisper.cpp 路径设置\n    │   ├── set_xxl.py          # Faster-Whisper-XXL 路径设置\n    │   └── controlobj.py       # 控件对象管理\n    │\n    ├── ui/                     # PySide6 UI 定义文件\n    │   ├── en.py               # ★ 主窗口 UI 布局定义\n    │   ├── chatgpt.py, deepseek.py, gemini.py, ...    # 各渠道设置对话框布局\n    │   ├── menu_list.py \t\t# 菜单定义配置文件\n    │   └── dark/               # 暗色主题资源（darkstyle_rc.py, palette.py）\n    │\n    ├── winform/                # 各渠道设置窗口\n    │   ├── __init__.py         # ★ get_win() 懒加载入口 \n    │   ├── chatgpt.py, azure.py, baidu.py, ...  # 渠道设置窗口\n    │\n    ├── styles/                 # UI 样式与媒体资源\n    │   ├── style.qss           # Qt 样式表\n    │   ├── logo.png            # 启动画面 logo\n    │   ├── icon.ico            # 应用图标\n    │   ├── simhei.ttf          # 黑体中文字体\n    │   ├── preview.png         # 设置硬字幕样式用预览图\n    │   ├── no-remove.mp4       # 测试硬编码支持用轻量占位视频\n    │   └── no-remove.wav       # 测试硬编码支持用轻量占位视频\n    │\n    ├── util/                   # 通用工具函数（18 个文件）\n    │   ├── tools.py            # ★ 核心工具函数（ffmpeg 封装、字幕解析/格式化、文件操作、系统通知、模型下载）\n    │   ├── gpus.py             # GPU 检测与分配\n    │   ├── checkgpu.py         # GPU 检测线程（AiLoaderThread）\n    │   ├── ListenVoice.py      # 声音试听功能（ListenVioce QThread）\n    │   ├── req_fac.py          # HuggingFace 自定义 session 工厂\n    │   ├── cn_tn.py            # 中文文本规范化\n    │   ├── en_tn.py            # 英文文本规范化\n    │   ├── help_down.py        # 下载工具函数\n    │   ├── help_ffmpeg.py      # ffmpeg 视频编解码器检测\n    │   ├── help_misc.py        # 杂项工具\n    │   ├── help_role.py        # 配音角色工具\n    │   ├── help_srt.py         # 字幕文件工具\n    │   ├── helper_supertonic.py # Supertonic TTS 辅助\n    │   ├── TestSrtTrans.py     # 翻译测试工具\n    │   └── TestSTT.py          # STT 测试工具\n    │\n    ├── language/               # 界面多语言 JSON 文件\n    │   ├── en_US.json\n    │   ├── zh_CN.json\n    │\n    ├── prompts/                # AI 翻译提示词模板\n    │   ├── srt/                # SRT 格式翻译 prompt（chatgpt.txt, deepseek.txt 等 13 个）\n    │   ├── language_prompts/   # 针对各个目标语言动态插入的 语言要求提示词片段\n    │   ├── text/               # 纯文本翻译 prompt\n    │   └── recharge/           # LLM纠错 prompt（llm.txt）\n    │\n    └── voicejson/              # TTS 音色配置文件（14 个 JSON）\n        ├── edge_tts.json       # Edge-TTS 各语言音色列表\n        ├── azure_voice_list.json # Azure TTS 音色列表\n        ├── qwen3tts.json       # Qwen3-TTS 音色\n        └── ...                 # 其他渠道音色配置\n```\n\n---\n\n\n---\n\n> **版本**: v4.13\n> **主页**: https://github.com/jianchang512/pyvideotrans\n> **文档**: https://pyvideotrans.com\n> **BBS**: https://bbs.pyvideotrans.com\n"},"items":[{"name":"architecture.md","path":"docs/architecture.md","rawUrl":"https://raw.githubusercontent.com/jianchang512/pyvideotrans/HEAD/docs/architecture.md","title":"docs - System Architecture & Module Boundaries","category":"project-spec","format":"markdown","content":"# pyVideoTrans 技术架构与实现原理\n\n`pyvideotrans` 是一款功能强大的开源视频翻译配音工具（v4.13），能够将视频自动翻译并配上目标语言的语音。其核心设计理念是模块化、多线程流水线，通过灵活的标志位组合支持多种工作模式。\n\n![](https://pvtr2.pyvideotrans.com/1760167240539_image.png)\n\n---\n\n## 一、核心处理流程\n\n![](https://pvtr2.pyvideotrans.com/1760165489380_image.png)\n\n软件将视频翻译配音过程分解为 **9 个独立阶段**，形成一条自动化的处理流水线。每个任务通过 5 个布尔标志位（`should_recogn`、`should_trans`、`should_dubbing`、`should_hebing`、`should_separate`）控制哪些阶段被跳过，从而支持不同的工作模式。\n\n### 1.1 九个处理阶段\n\n| 阶段 | 方法 | 职责 |\n|------|------|------|\n| **① 预处理** | `prepare()` | 从视频中分离无声视频流和原始音频流以及预先处理各项准备数据：音频转为单声道16k/wav格式；可选人声/背景分离（UVR/Spleeter）；可选降噪(onnx模型)；创建缓存目录和输出目录；决定清理缓存或是使用缓存 |\n| **② 语音识别** | `recogn()` | 调用 ASR 引擎（默认 Faster-Whisper+large-v3-turbo，支持 20+ ）,将音频转录为带时间戳的 SRT 字幕；可选标点恢复/删除、LLM纠错 |\n| **③ 说话人分离** | `diariz()` | 若选择了说话人分离，调用说话人分离模型（built-in(onnx模型)、ali_CAM、pyannote等后端），将字幕按说话人归类标注 （批量视频翻译时禁用分离）|\n| **④ 字幕翻译** | `trans()` | 若原始语言和目标语言不同，则将原始语言 SRT 字幕通过翻译渠道（20+ ）翻译为目标语言字幕；支持双语字幕输出 |\n| **⑤ TTS配音** | `dubbing()` | 若选择了配音角色，则根据目标语言字幕内容和时间戳，调用 TTS 引擎（34 +）逐条生成配音音频；支持声音克隆（从原始音频截取参考片段） |\n| **⑥ 音画对齐** | `align()` | 通过 `SpeedRate` 类处理：配音加速、视频慢放、去除字幕间隙静音、字幕音频强制对齐；完成后可选调节音量 |\n| **⑦ 二次识别** | `recogn2pass()` | 若选择了二次识别，对配音音频再次进行 ASR(固定faster-whisper渠道+large-v3-turo模型(默认，可切换))，生成时间轴精确且短小的字幕（仅在启用配音且非双字幕嵌入时执行） |\n| **⑧ 最终合成** | `assembling()` | 将无声视频流、配音音频、背景音乐、目标语言字幕合并为最终视频文件（ffmpeg） |\n| **⑨ 收尾** | `task_done()` | 将输出文件从临时目录移动到指定输出目录，清理临时文件，发送完成通知 |\n\n\n### 1.2 流程控制标志位\n\n定义在 `videotrans/task/_base.py:20-29`，五个标志位在 `TransCreate.__post_init__()` 中根据配置自动计算：\n\n```python\nshould_recogn: bool    # 是否需要语音识别（无已有字幕则为 True）\nshould_trans: bool     # 是否需要翻译（源语言 ≠ 目标语言则为 True）\nshould_dubbing: bool   # 是否需要配音（选择了配音角色且非 'No' 则为 True）\nshould_hebing: bool    # 是否需要嵌入合并（非 'tiqu' 模式且有配音或字幕嵌入则为 True）\nshould_separate: bool  # 是否需要人声背景分离\n```\n\n### 1.3 模式切换示例\n\n不同功能通过标志位组合实现：\n\n| 功能 | should_recogn | should_trans | should_dubbing | should_hebing |\n|------|:---:|:---:|:---:|:---:|\n| 视频翻译 | ✓ | ✓ | ✓ | ✓ |\n| 转录翻译（tiqu） | ✓ | 可选 | ✗ | ✗ |\n| 语音转录 | ✓ | ✗ | ✗ | ✗ |\n| 文字配音 | ✗ | ✗ | ✓ | ✓ |\n| 翻译字幕 | ✗ | ✓ | ✗ | ✗ |\n\n### 1.4 任务子类体系\n\n`BaseTask` 有四个具体子类，各自对应不同的使用场景：\n\n| 子类 | 文件 | 继承的 TaskCfg | 使用场景 |\n|------|------|----------------|---------|\n| `TransCreate` | `task/trans_create.py` | `TaskCfgVTT` | 完整视频翻译配音（标准模式 / tiqu转录同时翻译字幕模式） |\n| `SpeechToText` | `task/speech2text.py` | `TaskCfgSTT` | 语音转录独立功能 |\n| `DubbingSrt` | `task/dubbing.py` | `TaskCfgTTS` | 文字配音独立功能 |\n| `TranslateSrt` | `task/translate_srt.py` | `TaskCfgSTS` | 翻译字幕独立功能 |\n\n---\n\n## 二、任务配置数据类体系\n\n任务配置为分层继承的 `@dataclass` 体系（`videotrans/task/taskcfg.py`）：\n\n```\n@dataclass TaskCfgBase              ← 通用字段（路径、语言代码、缓存目录等）\n    ├── @dataclass TaskCfgSTT       ← 语音识别相关字段（recogn_type, model_name, rephrase 等）\n    ├── @dataclass TaskCfgTTS       ← 配音相关字段（tts_type, voice_role, voice_autorate 等）\n    ├── @dataclass TaskCfgSTS       ← 翻译相关字段（translate_type）\n    └── @dataclass TaskCfgVTT       ← 视频翻译全量字段（继承 STT + TTS + STS，新增视频特有字段）\n```\n\n辅助数据类：\n\n| 数据类 | 文件 | 用途 |\n|--------|------|------|\n| `InputFile` | `task/taskcfg.py` | 输入文件元信息（name, dirname, noextname, basename, ext, uuid, target_dir），支持 dict 式访问 |\n| `SignMsg` | `task/taskcfg.py` | 信号消息体（type, uuid, text），提供 `is_stop()` 和 `is_error()` 方法判断状态，在 Worker 线程与主线程间传递 |\n| `SrtItem` | `task/taskcfg.py` | 单条字幕数据（text, start_time, end_time, startraw, endraw, line, time, spk, filename） |\n\n`SrtItem` 支持同时用属性访问（`item.text`）和字典访问（`item['text']`），并可通过 `items()` 迭代。\n\n---\n\n## 三、多线程异步任务处理架构\n\n软件采用基于 **\"生产者-消费者\"模式** 的多线程多队列架构。`MultVideo` 线程充当生产者，将任务对象推入流水线的第一个队列；9 种专用 `BaseWorker` 子类作为消费者，各自监听专属队列。\n\n### 3.1 队列流水线\n\n```\n                     MultVideo (生产者)\n                           │\n                    app_cfg.prepare_queue\n                           ▼\n                   WorkerPrepare (×N)\n                    ┌────────┼────────┐\n                    │ should_recogn ?  │\n                    ▼        ▼        ▼\n           regcon_queue  trans_queue  dubb_queue / assemb_queue / taskdone_queue\n                │\n                ▼\n          WorkerRegcon (×N)\n                │\n         diariz_queue\n                │\n                ▼\n          WorkerDiariz (×N)\n           ┌────┼────┐\n           ▼    ▼    ▼\n      trans_queue  dubb_queue  assemb_queue / taskdone_queue\n           │\n           ▼\n     WorkerTrans (×1)\n      ┌────┼────┐\n      ▼    ▼    ▼\n dubb_queue  assemb_queue  taskdone_queue\n      │\n      ▼\nWorkerDubb (×1)\n      │\n align_queue\n      │\n      ▼\nWorkerAlign (×1)\n ┌────┼────┐\n ▼    ▼    ▼\nregcon2_queue  assemb_queue  taskdone_queue\n │\n ▼\nWorkerRegcon2Pass (×1)\n ┌────┼────┐\n ▼    ▼\nassemb_queue  taskdone_queue\n │\n ▼\nWorkerAssemb (×N)\n │\ntaskdone_queue\n │\n ▼\nWorkerTaskDone (×1)\n │\n(end)\n```\n\n### 3.2 Worker 基类设计\n\n所有工作线程继承自 `BaseWorker(QThread)`（`videotrans/task/job.py:13-66`）：\n\n```python\nclass BaseWorker(QThread):\n    def __init__(self, name, queue):\n        self.name = name\n        self.queue = queue\n\n    def run(self):\n        while True:\n            if app_cfg.exit_soft:          # 全局软退出标志\n                return\n            try:\n                trk = self.queue.get(timeout=1)  # 阻塞1秒取任务\n            except Empty:\n                continue\n            if trk.uuid in app_cfg.stoped_uuid_set:  # 任务已停止\n                continue\n            try:\n                self.process_task(trk)       # 子类实现具体逻辑\n            except Exception as e:\n                self.handle_error(e, trk)    # 统一错误处理\n```\n\n每个子类重写以下方法：\n\n| 方法 | 说明 |\n|------|------|\n| `process_task(trk)` | **必须** — 执行阶段逻辑，并将 trk 路由到下一个队列 |\n| `get_error_prefix(trk)` | 可选 — 返回错误前缀字符串（如 `\"识别出错[Faster-Whisper]\"`） |\n| `cleanup_on_error(trk)` | 可选 — 出错时的清理逻辑 |\n\n`handle_error()` 统一调用 `get_msg_from_except()` 解析异常为用户可读信息，然后通过 `trk.signal()` 发送错误消息。\n\n### 3.3 Worker 路由决策逻辑\n\n每个 Worker 在执行完 `process_task(trk)` 后，根据 `trk` 的标志位决定下一跳队列：\n\n```\nWorkerPrepare    →  regcon_queue | trans_queue | dubb_queue | assemb_queue | taskdone_queue\nWorkerRegcon     →  diariz_queue  (无条件)\nWorkerDiariz     →  trans_queue | dubb_queue | assemb_queue | taskdone_queue  (diariz 异常不阻断流程)\nWorkerTrans      →  dubb_queue | assemb_queue | taskdone_queue\nWorkerDubb       →  align_queue  (无条件)\nWorkerAlign      →  regcon2_queue | assemb_queue | taskdone_queue  (regcon2 仅当有 recogn2pass 属性时)\nWorkerRegcon2Pass → assemb_queue | taskdone_queue\nWorkerAssemb     →  taskdone_queue  (无条件)\nWorkerTaskDone   →  (终止)\n```\n\n### 3.4 线程/进程数量动态计算\n\n- 不存在GPU时，GPU进程固定设为1\n- 存在可用显卡时，进一步根据高级选项中的手动设置，判断应该启动几个GPU进程,  1->min(CPU核数,8)\n- CPU进程数 1 -> min(8,CPU核数,内存/1G, 高级选项中的手动设置数)\n`start_thread()`（`videotrans/task/job.py`）\n\n\n### 3.5 批量任务提交：MultVideo\n\n`MultVideo(QThread)`（`videotrans/task/mult_video.py`）负责将用户选择的多个视频文件逐个创建 `TransCreate` 对象并推入 `prepare_queue`。支持通过 `batch_nums` 参数控制每批并发数量：\n\n- `batch_nums == 0`：全部任务一次性推入队列（最大并发）\n- `batch_nums == 1`：逐次推入，每个任务完成后再推下一个\n- `batch_nums > 1`：每批推入 N 个，等待该批全部完成后再推下一批\n\n### 3.6 软退出机制\n\n全局标志 `app_cfg.exit_soft` 设为 `True` 时，所有 Worker 在下一轮循环中检测并安全退出。`app_cfg.stoped_uuid_set` 用于标记被手动停止的特定任务 UUID，Worker 在取出任务后跳过这些任务。\n\n---\n\n## 四、核心类的设计与继承关系\n\n### 4.1 类继承体系\n\n```\n@dataclass BaseCon                    ← videotrans/configure/base.py\n    │                                  基础属性和工具方法\n    ├── @dataclass BaseTask           ← videotrans/task/_base.py\n    │       │                          定义 8 个阶段空方法和 5 个标志位\n    │       ├── @dataclass TransCreate ← videotrans/task/trans_create.py (~1678 行核心)\n    │       ├── @dataclass SpeechToText ← videotrans/task/speech2text.py (批量语音识别)\n    │       ├── @dataclass DubbingSrt  ← videotrans/task/dubbing.py (批量字幕配音)\n    │       └── @dataclass TranslateSrt ← videotrans/task/translate_srt.py (批量字幕翻译)\n    │\n    ├── @dataclass BaseRecogn         ← videotrans/recognition/_base.py\n    │       │                          VAD 音频切分、字幕合并、CJK 处理\n    │       └── 子类（懒加载）    各 ASR 渠道具体实现\n    │\n    ├── @dataclass BaseTrans          ← videotrans/translator/_base.py\n    │       │                          MD5 缓存、逐行/全文翻译调度\n    │       └── 子类（懒加载）    各翻译渠道具体实现\n    │\n    └── @dataclass BaseTTS            ← videotrans/tts/_base.py\n            │                          异步/多线程并发调度\n            └── 子类（懒加载）    各 TTS 渠道具体实现\n```\n\n所有通道类均为 `@dataclass`，使用 `__post_init__` 初始化而非传统构造函数 `__init__`。\n\n### 4.2 BaseCon——顶层基类\n\n`videotrans/configure/base.py`（296 行）定义了所有类共用的核心能力：\n\n| 方法 | 职责 |\n|------|------|\n| `_exit()` | 检查是否应停止（`exit_soft` 或 UUID 在 `stoped_uuid_set` 中） |\n| `signal(**kwargs)` | 向 UI 发送消息（通过 `push_queue()` → `SignalHub`。CLI 模式下直接 print） |\n| `_set_proxy(type)` | 设置/清除 HTTP 代理（操作 `app_cfg.proxy` 和环境变量） |\n| `_new_process(callback, title, is_cuda, kwargs)` | **在子进程中执行耗时任务**（返回 `(data, error)` 元组） |\n| `_signal_of_process(logs_file)` | 通过轮询 JSON 日志文件的 mtime 实时读取子进程进度 |\n| `convert_to_wav()` | 音频统一转为 48kHz 立体声 WAV（可选去静音） |\n| `_base64_to_audio()` / `_audio_to_base64()` | Base64 音频编解码 |\n| `_process_callback(data)` | 下载进度回调（转发到 `signal()`） |\n\n`BaseCon.__post_init__()` 在初始化时自动调用 `_set_proxy(type='set')` 获取代理配置。\n\n### 4.3 BaseTask——任务基类\n\n`videotrans/task/_base.py:10-167` 定义了所有任务子类的阶段空方法和共享工具：\n\n**阶段方法**（均为空实现，由子类重写）：\n`prepare()`、`recogn()`、`diariz()`、`trans()`、`dubbing()`、`align()`、`assembling()`、`task_done()`\n\n> 注意：`recogn2pass()` 方法定义在 `TransCreate` 中，不在 `BaseTask` 基类中。\n\n**共享方法**：\n| 方法 | 职责 |\n|------|------|\n| `_unlink_size0(file)` | 删除尺寸为 0 的无效文件 |\n| `_save_srt_target(srtstr, file)` | 将 SrtItem 列表格式化为 SRT 字符串并写入文件，发送 `replace_subtitle` 信号 |\n| `check_target_sub(source, target)` | 校验翻译前后字幕行数一致性；不一致时按时间轴匹配对齐 |\n| `set_end(succeed=False)` | 标记任务结束，成功时发送通知并清理临时文件夹 |\n| `_edgetts_single(target_audio, kwargs)` | Edge-TTS 一次性异步配音（带代理回退） |\n\n### 4.4 TransCreate——视频翻译核心实现\n\n`videotrans/task/trans_create.py`（约 1678 行）是完整 9 阶段处理逻辑的实现类。关键内部方法：\n\n| 方法 | 职责 |\n|------|------|\n| `__post_init__()` | 初始化所有文件路径、计算标志位、启动进度计时线程 |\n| `_split_novoice_byraw()` | 从原始视频分离无声视频（优先硬件解码 h264_cuvid，回退 libx264） |\n| `_split_audio_byraw()` | 从原始视频提取 16kHz 单声道 PCM 音频 + 可选人声/背景分离 |\n| `_tts()` | 构建 `queue_tts` 列表（含 clone 参考音频片段），调用 `tts.run()` |\n| `_create_ref_from_vocal()` | 多线程（ThreadPoolExecutor）裁剪原始音频对应片段作为声音克隆参考 |\n| `_recogn_succeed()` | 识别完成后的处理（tiqu 模式下复制文件） |\n| `_back_music()` | 将用户上传的背景音乐与配音音频混合 |\n| `_separate()` | 将分离出的背景音乐重新嵌入配音音频 |\n| `_process_subtitles()` | 处理软/硬字幕嵌入逻辑（单/双字幕、样式设置） |\n\n### 4.5 子进程通道\n\n为防止 `faster-whisper` 崩溃导致整个软件退出，`Faster-Whisper、OpenAI-whisper、Qwen3-ASR、HuggingFace_ASR`、`Qwen3-TTS、OmniVoice、F5-TTS、Confucius-TTS`等部分重型渠道通过 `BaseCon._new_process()` 委托给 `GlobalProcessManager` 在独立子进程中执行。\n\n子进程通过写入 JSON 日志文件来报告进度。`BaseCon._signal_of_process()` 在守护线程中轮询该日志文件，检测到 mtime 变化时解析 JSON 并通过 `signal()` 上报。\n\n---\n\n## 五、配置系统\n\n软件将配置分为三个层次（`videotrans/configure/config.py`，902 行），均为 `@dataclass`：\n\n| 配置类 | 持久化 | 用途 | 示例字段 |\n|--------|--------|------|---------|\n| `AppCfg` | 纯内存 | 队列、状态、线程控制、运行时上下文、CPU、GPU进程数量 | `prepare_queue`, `exit_soft`, `stoped_uuid_set`, `current_status`, `line_roles`, `exec_mode`, `video_codec`, `onlyone_source_sub`, `onlyone_target_sub`, `proxy`, `SUPPORT_LANG` |\n| `AppSettings` | `videotrans/cfg.json` | 高级选项各设置、模型列表 | `homedir`, `model_list`, `vad_type`, `cuda_com_type` |\n| `AppParams` | `videotrans/params.json` | API 密钥、各种界面设置 | `source_language`, `recogn_type`, `chatgpt_key`, `voice_role`, `app_mode` |\n\n关键单例变量在模块加载时自动初始化：\n\n```python\napp_cfg: AppCfg = AppCfg()        # 运行时状态（含 9 个 Queue 实例）\nsettings: AppSettings = AppSettings()  # 从 cfg.json 加载\nparams: AppParams = AppParams()    # 从 params.json 加载\n```\n\n### 5.1 AppSettings 特性\n\n- 支持 `settings['key']` 字典式访问和 `settings.get('key', default)` 方法\n- `get()` 自动对数字类型字段进行类型强制转换（`int_type` 和 `float_type` 白名单）\n- `_get_defaults()` 定义了 ~100 个配置项的默认值\n- 支持连字符字段名映射（如 `\"initial_prompt_zh-cn\"` → `\"initial_prompt_zh_cn\"`）\n\n### 5.2 AppParams 特性\n\n- `_get_defaults()` 定义了 ~100 个用户参数默认值\n- `getset_params(update_data)` 支持批量更新（如 `check_start()` 中收集所有 UI 控件值）\n- API key 类字段统一在此管理，供 `is_input_api()` 校验\n\n### 5.3 AppCfg 运行时状态\n\n- 9 个 `Queue(maxsize=0)`（无限容量）：`prepare_queue` ~ `taskdone_queue`\n- `queue_novice: Dict` — 跟踪无声视频分离进度（key=uuid, value='ing'|'end'）\n- `line_roles: Dict` — 存储单视频模式下用户逐行分配的字幕角色\n- `child_forms: Dict` — 缓存已打开的窗口实例，避免重复创建\n- `exec_mode` — 执行模式（'gui' 或 'cli'）\n- `video_codec` / `codec_cache` — 视频编解码器缓存\n- `onlyone_source_sub` / `onlyone_target_sub` / `onlyone_trans` — 单视频模式字幕状态\n- `SUPPORT_LANG` — 支持的语言列表\n\n### 5.4 环境变量初始化\n\n`_set_env()` 在模块加载时自动执行（`videotrans/configure/config.py:44-72`），设置：\n- `MODELSCOPE_CACHE` / `HF_HOME` / `HF_HUB_CACHE` → `ROOT_DIR/models`\n- `QT_API = 'pyside6'`\n- `PATH` 追加 ffmpeg/sox 目录\n- `OMP_NUM_THREADS = 1`\n- `HF_HUB_DOWNLOAD_TIMEOUT = 3600`\n- `HF_HUB_DISABLE_XET = 1`\n\n---\n\n## 六、GlobalProcessManager——子进程池管理\n\n`videotrans/process/signelobj.py`（167 行）实现了一个类级别单例的 `GlobalProcessManager`：\n\n```\nGlobalProcessManager (类级别单例)\n    ├── _executor_cpu: multiprocessing.Pool\n    │       workers = app_cfg.MAX_CPU_PROCESS \n    │       maxtasksperchild = 1  ← 每个子进程执行一个任务后重启，防内存泄漏\n    │\n    └── _executor_gpu: multiprocessing.Pool\n            workers = app_cfg.MAX_GPU_PROCESS\n            maxtasksperchild = 1\n```\n\n\n\n### 6.3 任务提交接口\n\n```python\nGlobalProcessManager.submit_task_cpu(func, **kwargs)   → AsyncResultFutureWrapper\nGlobalProcessManager.submit_task_gpu(func, **kwargs)   → AsyncResultFutureWrapper\n```\n\n`AsyncResultFutureWrapper` 将 `Pool.apply_async` 的 `AsyncResult` 包装为 `Future` 兼容接口（`.result()`, `.done()`）。\n\n### 6.4 使用场景\n\n通过 `BaseCon._new_process()` 统一调用，用于执行：ASR 推理、TTS 合成、噪声去除、人声分离、说话人分离、标点恢复（均在独立子进程中运行，崩溃不影响主进程）。\n\n---\n\n## 七、SignalHub——跨线程消息中心\n\n`videotrans/configure/signal_hub.py`（33 行）实现了基于 Qt 信号的单例消息传递：\n\n```python\nclass SignalHub(QObject):\n    _instance = None\n    new_message = Signal(str, object)  # (uuid, SignMsg)\n\n    @classmethod\n    def instance(cls):\n        if cls._instance is None:\n            cls._instance = cls()\n        return cls._instance\n\n    @Slot(str, object)\n    def post(self, uuid=None, data=None):\n        self.new_message.emit(uuid, data)  # 跨线程自动使用 QueuedConnection\n```\n\n### 消息流\n\n```\nBaseCon.signal(**kwargs)\n    → push_queue(uuid, SignMsg(**kwargs))     [configure/config.py]\n        → SignalHub.instance().post(uuid, data)\n            → new_message Signal (QueuedConnection)\n                → WinAction.update_data(uuid, data)   [mainwin/_actions.py]\n                    → 按 type 分发:\n                        'logs'|'error'|'succeed'|'set_precent' → set_process_btn_text()\n                        'edit_subtitle_source' → 弹出 EditRecognResultDialog\n                        'edit_subtitle_target' → 弹出 SpeakerAssignmentDialog\n                        'edit_dubbing' → 弹出 EditDubbingResultDialog\n                        'replace_subtitle' → 更新字幕编辑区\n                        'end' → update_status('end')\n```\n\n### 消息类型枚举\n\n| type | 含义 | 处理逻辑 |\n|------|------|---------|\n| `logs` | 普通日志 | 更新进度条文本 |\n| `error` | 错误 | 进度条变红，加入重试队列 |\n| `succeed` | 成功 | 进度条变绿，标记完成 |\n| `set_precent` | 进度百分比 | `text=\"耗时???百分比\"` 格式 |\n| `edit_subtitle_source` | 弹出原始字幕编辑框 | 单视频模式暂停点① |\n| `edit_subtitle_target` | 弹出翻译字幕编辑框 | 单视频模式暂停点② |\n| `edit_dubbing` | 弹出配音结果编辑框 | 单视频模式暂停点③ |\n| `replace_subtitle` | 替换字幕区域内容 | 批量/单视频共用 |\n| `subtitle` | 追加字幕行 | 逐行输出到编辑器 |\n| `end` | 任务完成 | 触发 update_status('end') |\n| `disabled_edit` | 禁止编辑字幕 | 批量模式下锁定编辑器 |\n| `refreshtts` | 刷新 TTS 选择 | 重新设置 TTS 下拉框 |\n| `shitingerror` | 试听错误 | 弹出错误提示 |\n| `ffmpeg` | ffmpeg 状态 | 更新开始按钮文本 |\n\n---\n\n## 八、动态通道加载\n\n`videotrans/__init__.py`（35 行）提供了通用的懒加载机制：\n\n```python\n@dataclass\nclass ChannelProvider:\n    name: str           # 界面显示名称\n    imp: str            # 模块导入后缀（如 \"._whisper\" → \"videotrans.recognition._whisper\"）\n    key_name: str|None  # 对应 params.json 中的 API key 字段（用于 is_input_api 校验）\n    win: str|None       # 对应 winform 中的设置窗口名称\n\ndef get_class(channel_id=0, provider_type=None, _ID_NAME_DICT=None):\n    _key = f'{provider_type}-{channel_id}'\n    if _key in _loaded_modules:\n        return _loaded_modules[_key]\n    module = importlib.import_module(f'videotrans.{provider_type}{_module_map.imp}')\n    for _, obj in inspect.getmembers(module, inspect.isclass):\n        if obj.__module__ == module.__name__:\n            _loaded_modules[_key] = obj\n            return obj\n```\n\n三大模块各自的 `_ID_NAME_DICT`：\n\n| 模块 | 渠道数 | 定义位置 |\n|------|--------|---------|\n| 语音识别 (recognition) | 20+ | `videotrans/recognition/__init__.py:48-79` |\n| 字幕翻译 (translator) | 20+ | `videotrans/translator/__init__.py:60-90` |\n| 文字配音 (tts) | **30+** | `videotrans/tts/__init__.py:75-116` |\n\n### 8.1 统一入口函数\n\n每个模块提供 `run()` 统一入口，内部通过 `get_class()` 获取对应渠道类并实例化调用：\n\n```python\n# recognition/__init__.py\ndef run(*, recogn_type, detect_language, audio_file, ...) -> List[SrtItem]:\n    _cls = get_class(recogn_type, \"recognition\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n\n# translator/__init__.py\ndef run(*, translate_type, text_list, source_code, target_code, ...) -> List[SrtItem]:\n    _cls = get_class(translate_type, \"translator\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n\n# tts/__init__.py\ndef run(*, queue_tts, language, tts_type, ...) -> None:\n    _cls = get_class(tts_type, \"tts\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n```\n\n### 8.2 API Key 校验\n\n每个模块提供 `is_input_api(recogn_type/translate_type/tts_type)` 函数，检查对应渠道的 `key_name` 在 `params` 中是否已填写。未填写时自动弹出对应的 winform 设置窗口。\n\n### 8.3 翻译缓存\n\n`BaseTrans`（`videotrans/translator/_base.py`）实现了基于 MD5 的翻译缓存：\n- 缓存 key = `md5(channel_name + api_url + model + source_lang + target_lang + text)`\n- 缓存文件存储在 `{TEMP_ROOT}/translate_cache/`\n- 写入接口 `_set_cache()`，读取接口 `_get_cache()`\n\n### 8.4 CJK 特殊处理\n\n`BaseRecogn`（`videotrans/recognition/_base.py:58-80`）在 `__post_init__` 中对中日韩等语言进行特殊处理：\n- `join_word_flag`：CJK 语言（zh, ja, ko, yue, th, km）字幕词间不加空格（其他语言加空格）\n- `maxlen`：CJK 语言每行最大字符数为 `settings.cjk_len`（默认 15），其他语言为 `settings.other_len`（默认 40）\n- `jianfan`：中文语言且 `settings.zh_hant_s=True` 时启用繁简转换\n\n### 8.5 翻译调度策略\n\n`BaseTrans.run()` 根据 `aisendsrt` 标志选择不同策略：\n\n| 模式 | 条件 | 方法 | 并发数 |\n|------|------|------|--------|\n| 逐行翻译 | 非 AI 渠道 | `_run_text()` → `_item_task()` | `settings.trans_thread`（默认 10） |\n| 全文翻译 | AI 渠道 + `aisendsrt=True` | `_run_srt()` | `settings.aitrans_thread`（默认 50） |\n\n### 8.6 TTS 调度策略\n\n`BaseTTS.run()` 根据渠道类型选择执行方式：\n\n| 渠道类型 | 调度方式 | 说明 |\n|---------|---------|------|\n| Edge-TTS | `asyncio` 异步 | 单线程内 async 并发 |\n| 其他渠道 | `ThreadPoolExecutor` | 由 `dubbing_thread` 控制并发数（默认 1） |\n\n渠道子类可重写 `_exec()` 方法实现自定义调度。`BaseTTS` 默认调用 `__local_mul_thread()` → `_item_task()`。\n\n---\n\n## 九、交互式单视频处理模式\n\n当用户选择 **1 个视频** 且在**标准模式**下时，程序采用不同于批量流水线的处理模型。\n\n### 9.1 实现：Worker(QThread)\n\n`videotrans/task/only_one.py`（148 行）中的 `Worker` 类在**单个 QThread 内串行执行**全部 9 个阶段，通过 `uito = Signal(str, SignMsg)` 与主线程通信：\n\n```\nWorker.run()\n    ├── trk = TransCreate(cfg=TaskCfgVTT(**self.cfg | obj))\n    ├── trk.prepare()\n    ├── trk.recogn()\n    ├── trk.diariz()\n    ├── [暂停点 ①] → _post(type='edit_subtitle_source')\n    │    用户校对原始字幕 → 点击\"确定\"或等待倒计时\n    ├── trk.trans() (if should_trans)\n    ├── [暂停点 ②] → _post(type='edit_subtitle_target')\n    │    用户校对翻译字幕 + 分配说话人角色 → 点击\"确定\"\n    ├── trk.dubbing() (if should_dubbing)\n    ├── [暂停点 ③] → _post(type='edit_dubbing')\n    │    用户修改配音结果 → 点击\"确定\"\n    ├── trk.align()\n    ├── trk.recogn2pass() 暂停点，修改二次识别后的结果\t\n    ├── trk.assembling()\n    └── trk.task_done()\n```\n\n### 9.2 与批量模式的关键差异\n\n| 维度 | 单视频模式 | 批量模式 |\n|------|-----------|---------|\n| 执行线程 | `Worker(QThread)` 直接执行，不使用队列管道 | `TransCreate` 推入 `prepare_queue`，经 9 个 Worker 队列流动 |\n| 消息通道 | `uito` 信号直接连接到 `WinAction.update_data()` | `BaseCon.signal()` → `push_queue()` → `SignalHub` |\n| 暂停机制 | 三段暂停点，用户可中间编辑 | 不支持暂停编辑 |\n| 进度显示 | 字幕编辑区实时显示 | 进度条 + 按钮文本 |\n\n### 9.3 倒计时与暂停机制\n\n1. **自动倒计时**：`app_cfg.set_countdown(86400)` 设置初始值。Worker 线程每 `sleep(1)` 递减一次。默认倒计时由 `settings.countdown_sec` 控制。\n2. **无限期暂停**：用户点击\"停止\"按钮将 `app_cfg.current_status` 设为 `'stop'`，Worker 的 `_exit()` 检测后退出；或 `set_countdown(-1)` 让倒计时消失。\n3. **手动继续**：用户在校对对话框中点击\"确定\"后，`WinAction.set_djs_timeout()` 调用 `app_cfg.set_countdown(-1)` 使倒计时立即归零。\n\n### 9.4 校对对话框\n\n| 对话框 | 文件 | 功能 |\n|--------|------|------|\n| `EditRecognResultDialog` | `component/onlyone_set_recogn.py` | 原始字幕编辑（文本 + 时间轴） |\n| `SpeakerAssignmentDialog` | `component/onlyone_set_role.py` | 翻译字幕编辑 + 逐行分配配音角色 |\n| `EditDubbingResultDialog` | `component/onlyone_set_editdubb.py` | 配音结果试听 + 单独重新配音 |\n| `EditRecognResultDialog2` | `component/onlyone_set_recogn2.py` | 二次识别字幕编辑（文本 + 时间轴） |\n\n![](https://pvtr2.pyvideotrans.com/1760192881455_image.png)\n![](https://pvtr2.pyvideotrans.com/1760192930833_image.png)\n\n\n---\n\n## 十、音画对齐引擎（SpeedRate）\n\n`videotrans/task/_rate.py`（877 行）实现了 `SpeedRate` 和 `TtsSpeedRate` 两个对齐引擎：\n\n### 10.1 SpeedRate（视频翻译场景）\n\n处理策略（按优先级）：\n\n| 条件 | 策略 |\n|------|------|\n| 仅启用音频加速 | 加速配音到匹配字幕时长（最高不超过 `max_audio_speed_rate`） |\n| 启用音频加速 + 视频慢速 | 各负担一半时间差（忽略倍率限制） |\n| 仅启用视频慢速 | 慢放视频片段到匹配配音时长（最高不超过 `max_video_pts_rate`） |\n| 两者均未启用 | 按字幕时间轴拼接音频片段，填充静音/定格处理时长差异 |\n\n额外处理：\n- `remove_silent_mid`：去除字幕之间的静音区间\n- `align_sub_audio`：强制对齐字幕时间轴到实际配音位置\n- 末尾静音移除\n\n### 10.2 TtsSpeedRate（纯配音场景）\n\n简化版对齐引擎，仅负责音频拼接与加速，无视频慢放逻辑。\n\n---\n\n## 十一、软件启动与 UI 实现\n\n### 11.1 启动流程\n\n`sp.py` 是唯一入口，启动过程如下：\n\n```\nsp.py (if __name__ == \"__main__\")\n  │\n  ├── 1. multiprocessing.freeze_support() / set_start_method('spawn')\n  ├── 2. qInstallMessageHandler() 抑制 Qt 警告\n  ├── 3. atexit.register(cleanup) 注册退出清理\n  ├── 4. QApplication.setHighDpiScaleFactorRoundingPolicy(PassThrough)\n  ├── 5. 创建 QApplication\n  ├── 6. 检测是否在压缩包内运行（PyInstaller 打包版）\n  ├── 7. 创建 StartWindow (splash screen, 无边框半透明)\n  │       └── QTimer.singleShot(100ms) → initialize_full_app()\n  │           ├── 重定向 sys.stdout/stderr 到日志文件\n  │           ├── 设置全局异常钩子 show_global_error_dialog\n  │           ├── 解析 --lang CLI 参数\n  │           ├── 导入 darkstyle_rc（编译后的 QRC 资源）\n  │           ├── 加载 QSS 样式表 (videotrans/styles/style.qss)\n  │           ├── 恢复上次窗口大小 (QSettings)\n  │           └── 实例化 MainWindow → uito 连接 splash.update_lable\n  │               └── MainWindow.__init__()\n  │                   ├── setupUi() → 填充下拉列表（翻译/识别/TTS 渠道、语言列表）\n  │                   ├── AiLoaderThread 启动 → 检测 GPU → 回调 _start_workers()\n  │                   ├── _start_workers() → start_thread() 启动 9 种 Worker 线程\n  │                   ├── _set_default() → 恢复上次用户选择\n  │                   ├── _bind_signal() → 绑定 ~60 个控件事件\n  │                   ├── SignalHub.new_message.connect(win_action.update_data)\n  │                   └── uito.emit('end') → splash 关闭\n  └── 8. app.exec() → Qt 事件循环\n```\n\n### 11.2 退出机制\n\n用户点击关闭按钮时：\n1. 设置 `app_cfg.exit_soft = True`, `app_cfg.current_status = 'stop'`\n2. 主窗口立即隐藏（`hide()`）\n3. 保存窗口尺寸到 `QSettings`\n4. 隐藏/关闭所有子窗口\n5. 等待 ~4 秒让所有 Worker 完成当前工作并安全退出\n6. 清理临时目录 `TEMP_ROOT`\n7. `atexit` cleanup 回调执行 → 程序终止\n8. 若为重启模式，启动新进程后 `os._exit(0)`\n\n### 11.3 UI 架构分层\n\n```\nUI 定义层         videotrans/ui/         ← PySide6 UI 布局文件，dark/ 资源文件\n    ↓\n    ↓\n窗口管理层        videotrans/winform/     ← 懒加载的设置/功能窗口模块\n    ↓\n主窗口层          videotrans/mainwin/\n    ├── main_win.py                      ← MainWindow(QMainWindow): UI 初始化、信号绑定、Worker 启动、窗口生命周期\n    ├── _actions.py                       ← WinAction: 核心业务逻辑 → 参数收集 → 任务启动 → 状态分发\n    └── _actions_base.py                 ← WinActionBase: 代理管理、模式切换、文件选择、CUDA 检测、试听\n    ↓\n任务层            videotrans/task/        ← TransCreate、SpeechToText、DubbingSrt、TranslateSrt、Worker 线程、SpeedRate\n```\n\n### 11.4 MainWindow——主窗口\n\n`videotrans/mainwin/main_win.py`职责：\n- `setupUi()`：加载 UI 布局，填充下拉列表（翻译渠道、识别渠道、TTS 渠道、语言、字幕类型）\n- `_bind_signal()`：绑定约 60 个控件事件到 `WinAction` 方法\n- `_start_workers(status)`：GPU 检测完成后启动 9 种 Worker 后台线程\n- `open_winform(name)`：统一窗口打开入口（优先复用已缓存的 `app_cfg.child_forms`，否则调用 `winform.get_win(name).openwin()`）\n- `closeEvent()`：安全关闭流程（标记退出 → 隐藏窗口 → 停止线程 → 清理临时文件）\n- `restart_app()`：询问确认后触发 `closeEvent()` 并启动新进程\n\n### 11.5 WinAction——核心控制器\n\n`WinAction` 继承自 `WinActionBase`（两者均为 `@dataclass`），是连接 UI 和后台任务的关键枢纽：\n\n**WinActionBase**（`mainwin/_actions_base.py`，590 行）提供：\n- 文件选择（`get_mp4()`）—— 单文件/文件夹模式\n- 输出目录设置（`get_save_dir()`）\n- 代理配置（`change_proxy()`, `check_proxy()`, `proxy_alert()`）\n- 模式切换（`set_biaozhun()`, `set_tiquzimu()`）—— 控制 UI 元素显隐\n- CUDA 检测（`check_cuda()`, `cuda_isok()`）\n- 试听功能（`listen_voice_fun()`）—— 创建 `ListenVoice` 线程\n- 角色列表更新（`tts_type_change()`, `set_voice_role()`）\n- 更多设置选项折叠（`toggle_adv()`）\n- UI 启用/禁用控制（`disabled_widget()`, `_disabled_button()`）\n\n**WinAction**（`mainwin/_actions.py`，798 行）提供：\n- `check_start()`：收集所有 UI 控件值 → 构建 `cfg` 字典 → 参数校验 → 调用 `create_btns()`\n- `create_btns()`：格式化输入文件路径 → 创建进度条 → 单视频启动 `Worker`，批量启动 `MultVideo`\n- `update_data(uuid, SignMsg)`：连接 `SignalHub.new_message` 信号 → 按消息类型分发\n- `update_status(type)`：切换 `ing`/`stop`/`end` 状态，控制按钮和进度条\n- `set_process_btn_text(d)`：更新进度条文本/百分比/颜色\n- `retry()`：重新处理失败的任务\n- `_check_all_done()`：检测是否所有任务完成\n\n---\n\n## 十二、异常体系\n\n`videotrans/configure/excepts.py`（376 行）定义了分层异常：\n\n```\nVideoTransError (基类)\n    ├── TranslateSrtError       # 翻译相关错误\n    ├── DubbingSrtError         # 配音相关错误\n    ├── SpeechToTextError       # 语音识别相关错误\n    ├── LLMSegmentError         # LLM 重新断句错误\n    ├── FFmpegError             # FFmpeg 操作错误\n    ├── DownloadModelsError     # 模型下载错误\n    ├── SttTimeoutError         # STT 子进程超时\n    ├── StopTask                # 需立即停止的任务异常\n    └── StopRetry               # 不可重试的错误\n```\n\n`get_msg_from_except(e)` 函数映射数十种第三方库异常为用户可读的中/英文错误消息（覆盖 `httpx`、`openai`、`requests`、`deepgram`、`elevenlabs`、`tenacity` 等）。\n\n`NO_RETRY_EXCEPT` 元组定义了不可恢复的异常类型，翻译/配音模块在重试循环中遇到这些异常时直接放弃。\n\n---\n\n## 十三、代码结构概览\n\n```\n/\n├── sp.py                       # ★ 主程序入口\n├── cli.py                      # ★ CLI 命令行入口\n├── models/                     # 存放本地 AI 模型文件（ONNX 等）\n├── logs/                       # 日志文件目录（YYYYMMDD.log）\n├── ffmpeg/                     # ffmpeg 及 sox 二进制文件\n├── f5-tts/                     # 声音克隆参考音频存放目录\n├── docs/                       # 文档\n├── tmp/                        # 临时文件根目录\n│   ├── dubbing_cache/          # 配音缓存\n│   └── translate_cache/        # 翻译 MD5 缓存目录\n│\n└── videotrans/                 # 核心业务逻辑代码\n    │   __init__.py             # ★ VERSION, ChannelProvider 定义, get_class() 懒加载\n    │   cfg.json                # settings 持久化文件\n    │   params.json             # params 持久化文件\n    │   codec.json              # 视频编解码器缓存\n    │\n    ├── codes/\n    │   └── model.py            # 第三方模型相关定义\n    │\n    ├── configure/              # 全局配置、队列定义、顶层基类\n    │   ├── config.py           # ★ AppCfg / AppSettings / AppParams / logger / 队列定义 / tr() / push_queue()\n    │   ├── base.py             # ★ BaseCon 基类（_new_process, signal, _exit, convert_to_wav 等）\n    │   ├── contants.py         # ★ 全局常量（模型列表、语言测试文本、标点符号、代理白名单等）\n    │   ├── excepts.py          # ★ 异常体系 + get_msg_from_except()\n    │   ├── signal_hub.py       # ★ SignalHub 单例（跨线程 Qt 信号）\n    │   └── whispernet_config.py # Whisper.NET 配置\n    │\n    ├── task/                   # 任务处理逻辑与后台线程\n    │   ├── _base.py            # ★ BaseTask 基类\n    │   ├── taskcfg.py          # ★ TaskCfgBase/VTT/STT/TTS/STS + InputFile + SignMsg + SrtItem\n    │   ├── trans_create.py     # ★ TransCreate 完整实现（视频翻译核心）\n    │   ├── speech2text.py      # ★ SpeechToText（语音转录独立功能）\n    │   ├── dubbing.py          # ★ DubbingSrt（文字配音独立功能配音）\n    │   ├── translate_srt.py    # ★ TranslateSrt（翻译字幕独立功能）\n    │   ├── job.py              # ★ 9 种 BaseWorker 子类 + start_thread() 入口\n    │   ├── only_one.py         # ★ 单视频交互式 Worker(QThread) + uito 信号\n    │   ├── mult_video.py       # ★ 多视频批量提交 MultVideo(QThread)\n    │   ├── _rate.py            # SpeedRate / TtsSpeedRate 音画对齐引擎\n    │   ├── separate_worker.py  # SeparateWorker 独立人声分离 QThread\n    │   ├── simple_runnable_qt.py # QRunnable 线程池工具\n    │   ├── child_win_sign.py   # 子窗口信号处理\n    │   └── update_ffmpeg.py    # ffmpeg 更新脚本\n    │\n    ├── recognition/            # 语音识别 (ASR) 渠道包\n    │   ├── _constants.py       # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseRecogn（语音识别基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── translator/             # 字幕翻译渠道包\n    │   ├── _constants.py         # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseTrans（字幕翻译渠道基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── tts/                    # 文字配音 (TTS) 包\n    │   ├── _constants.py         # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseTTS（文字配音基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── process/                # 独立子进程实现：主要用于重型任务，例如语音识别、语音合成大模型等\n    │   ├── __init__.py         # 子进程函数导出\n    │   ├── signelobj.py        # ★ GlobalProcessManager（CPU/GPU 双进程池）\n    │   ├── prepare_audio.py    # 人声分离、降噪、标点恢复、说话人分离\n    │   ├── stt_fun.py          # ASR 子进程入口（openai_whisper, faster_whisper, paraformer, funasr_mlt, qwen3asr_fun 等）\n    │   ├── tts_fun.py          # TTS 子进程入口（qwen3tts_fun）\n    │   └── vad.py              # VAD 语音活动检测（已改为主进程中实现）\n    │\n    ├── mainwin/                # 主窗口界面与业务逻辑\n    │   ├── main_win.py         # ★ MainWindow(QMainWindow) 初始化、信号绑定、线程启动\n    │   ├── _actions.py         # ★ WinAction 核心控制器（检查、启动、状态更新）\n    │   └── _actions_base.py    # ★ WinActionBase 基类\n    │\n    ├── component/              # UI 通用组件\n    │   ├── progressbar.py      # 可点击进度条\n    │   ├── onlyone_set_recogn.py    # 单视频模式：原始字幕编辑对话框\n    │   ├── onlyone_set_recogn2.py    # 单视频模式：二次识别后的字幕编辑对话框\n    │   ├── onlyone_set_role.py      # 单视频模式：说话人角色分配对话框\n    │   ├── onlyone_set_editdubb.py  # 单视频模式：配音结果编辑对话框\n    │   ├── clip_video.py       # 按字幕裁剪视频功能\n    │   ├── realtime_stt.py     # 实时语音识别功能\n    │   ├── textmatching.py     # 文本匹配与打轴功能\n    │   ├── set_ass.py          # 硬字幕样式设置\n    │   ├── set_cpp.py          # Whisper.cpp 路径设置\n    │   ├── set_xxl.py          # Faster-Whisper-XXL 路径设置\n    │   └── controlobj.py       # 控件对象管理\n    │\n    ├── ui/                     # PySide6 UI 定义文件\n    │   ├── en.py               # ★ 主窗口 UI 布局定义\n    │   ├── chatgpt.py, deepseek.py, gemini.py, ...    # 各渠道设置对话框布局\n    │   ├── menu_list.py \t\t# 菜单定义配置文件\n    │   └── dark/               # 暗色主题资源（darkstyle_rc.py, palette.py）\n    │\n    ├── winform/                # 各渠道设置窗口\n    │   ├── __init__.py         # ★ get_win() 懒加载入口 \n    │   ├── chatgpt.py, azure.py, baidu.py, ...  # 渠道设置窗口\n    │\n    ├── styles/                 # UI 样式与媒体资源\n    │   ├── style.qss           # Qt 样式表\n    │   ├── logo.png            # 启动画面 logo\n    │   ├── icon.ico            # 应用图标\n    │   ├── simhei.ttf          # 黑体中文字体\n    │   ├── preview.png         # 设置硬字幕样式用预览图\n    │   ├── no-remove.mp4       # 测试硬编码支持用轻量占位视频\n    │   └── no-remove.wav       # 测试硬编码支持用轻量占位视频\n    │\n    ├── util/                   # 通用工具函数（18 个文件）\n    │   ├── tools.py            # ★ 核心工具函数（ffmpeg 封装、字幕解析/格式化、文件操作、系统通知、模型下载）\n    │   ├── gpus.py             # GPU 检测与分配\n    │   ├── checkgpu.py         # GPU 检测线程（AiLoaderThread）\n    │   ├── ListenVoice.py      # 声音试听功能（ListenVioce QThread）\n    │   ├── req_fac.py          # HuggingFace 自定义 session 工厂\n    │   ├── cn_tn.py            # 中文文本规范化\n    │   ├── en_tn.py            # 英文文本规范化\n    │   ├── help_down.py        # 下载工具函数\n    │   ├── help_ffmpeg.py      # ffmpeg 视频编解码器检测\n    │   ├── help_misc.py        # 杂项工具\n    │   ├── help_role.py        # 配音角色工具\n    │   ├── help_srt.py         # 字幕文件工具\n    │   ├── helper_supertonic.py # Supertonic TTS 辅助\n    │   ├── TestSrtTrans.py     # 翻译测试工具\n    │   └── TestSTT.py          # STT 测试工具\n    │\n    ├── language/               # 界面多语言 JSON 文件\n    │   ├── en_US.json\n    │   ├── zh_CN.json\n    │\n    ├── prompts/                # AI 翻译提示词模板\n    │   ├── srt/                # SRT 格式翻译 prompt（chatgpt.txt, deepseek.txt 等 13 个）\n    │   ├── language_prompts/   # 针对各个目标语言动态插入的 语言要求提示词片段\n    │   ├── text/               # 纯文本翻译 prompt\n    │   └── recharge/           # LLM纠错 prompt（llm.txt）\n    │\n    └── voicejson/              # TTS 音色配置文件（14 个 JSON）\n        ├── edge_tts.json       # Edge-TTS 各语言音色列表\n        ├── azure_voice_list.json # Azure TTS 音色列表\n        ├── qwen3tts.json       # Qwen3-TTS 音色\n        └── ...                 # 其他渠道音色配置\n```\n\n---\n\n\n---\n\n> **版本**: v4.13\n> **主页**: https://github.com/jianchang512/pyvideotrans\n> **文档**: https://pyvideotrans.com\n> **BBS**: https://bbs.pyvideotrans.com\n","isInternal":false,"tokens":14418,"sizeBytes":45407}],"systemPromptSnippet":"<yakaai_skills repo=\"jianchang512/pyvideotrans\">\n<!-- File: docs/architecture.md (Tokens: ~14418 | Category: project-spec) -->\n# pyVideoTrans 技术架构与实现原理\n\n`pyvideotrans` 是一款功能强大的开源视频翻译配音工具（v4.13），能够将视频自动翻译并配上目标语言的语音。其核心设计理念是模块化、多线程流水线，通过灵活的标志位组合支持多种工作模式。\n\n![](https://pvtr2.pyvideotrans.com/1760167240539_image.png)\n\n---\n\n## 一、核心处理流程\n\n![](https://pvtr2.pyvideotrans.com/1760165489380_image.png)\n\n软件将视频翻译配音过程分解为 **9 个独立阶段**，形成一条自动化的处理流水线。每个任务通过 5 个布尔标志位（`should_recogn`、`should_trans`、`should_dubbing`、`should_hebing`、`should_separate`）控制哪些阶段被跳过，从而支持不同的工作模式。\n\n### 1.1 九个处理阶段\n\n| 阶段 | 方法 | 职责 |\n|------|------|------|\n| **① 预处理** | `prepare()` | 从视频中分离无声视频流和原始音频流以及预先处理各项准备数据：音频转为单声道16k/wav格式；可选人声/背景分离（UVR/Spleeter）；可选降噪(onnx模型)；创建缓存目录和输出目录；决定清理缓存或是使用缓存 |\n| **② 语音识别** | `recogn()` | 调用 ASR 引擎（默认 Faster-Whisper+large-v3-turbo，支持 20+ ）,将音频转录为带时间戳的 SRT 字幕；可选标点恢复/删除、LLM纠错 |\n| **③ 说话人分离** | `diariz()` | 若选择了说话人分离，调用说话人分离模型（built-in(onnx模型)、ali_CAM、pyannote等后端），将字幕按说话人归类标注 （批量视频翻译时禁用分离）|\n| **④ 字幕翻译** | `trans()` | 若原始语言和目标语言不同，则将原始语言 SRT 字幕通过翻译渠道（20+ ）翻译为目标语言字幕；支持双语字幕输出 |\n| **⑤ TTS配音** | `dubbing()` | 若选择了配音角色，则根据目标语言字幕内容和时间戳，调用 TTS 引擎（34 +）逐条生成配音音频；支持声音克隆（从原始音频截取参考片段） |\n| **⑥ 音画对齐** | `align()` | 通过 `SpeedRate` 类处理：配音加速、视频慢放、去除字幕间隙静音、字幕音频强制对齐；完成后可选调节音量 |\n| **⑦ 二次识别** | `recogn2pass()` | 若选择了二次识别，对配音音频再次进行 ASR(固定faster-whisper渠道+large-v3-turo模型(默认，可切换))，生成时间轴精确且短小的字幕（仅在启用配音且非双字幕嵌入时执行） |\n| **⑧ 最终合成** | `assembling()` | 将无声视频流、配音音频、背景音乐、目标语言字幕合并为最终视频文件（ffmpeg） |\n| **⑨ 收尾** | `task_done()` | 将输出文件从临时目录移动到指定输出目录，清理临时文件，发送完成通知 |\n\n\n### 1.2 流程控制标志位\n\n定义在 `videotrans/task/_base.py:20-29`，五个标志位在 `TransCreate.__post_init__()` 中根据配置自动计算：\n\n```python\nshould_recogn: bool    # 是否需要语音识别（无已有字幕则为 True）\nshould_trans: bool     # 是否需要翻译（源语言 ≠ 目标语言则为 True）\nshould_dubbing: bool   # 是否需要配音（选择了配音角色且非 'No' 则为 True）\nshould_hebing: bool    # 是否需要嵌入合并（非 'tiqu' 模式且有配音或字幕嵌入则为 True）\nshould_separate: bool  # 是否需要人声背景分离\n```\n\n### 1.3 模式切换示例\n\n不同功能通过标志位组合实现：\n\n| 功能 | should_recogn | should_trans | should_dubbing | should_hebing |\n|------|:---:|:---:|:---:|:---:|\n| 视频翻译 | ✓ | ✓ | ✓ | ✓ |\n| 转录翻译（tiqu） | ✓ | 可选 | ✗ | ✗ |\n| 语音转录 | ✓ | ✗ | ✗ | ✗ |\n| 文字配音 | ✗ | ✗ | ✓ | ✓ |\n| 翻译字幕 | ✗ | ✓ | ✗ | ✗ |\n\n### 1.4 任务子类体系\n\n`BaseTask` 有四个具体子类，各自对应不同的使用场景：\n\n| 子类 | 文件 | 继承的 TaskCfg | 使用场景 |\n|------|------|----------------|---------|\n| `TransCreate` | `task/trans_create.py` | `TaskCfgVTT` | 完整视频翻译配音（标准模式 / tiqu转录同时翻译字幕模式） |\n| `SpeechToText` | `task/speech2text.py` | `TaskCfgSTT` | 语音转录独立功能 |\n| `DubbingSrt` | `task/dubbing.py` | `TaskCfgTTS` | 文字配音独立功能 |\n| `TranslateSrt` | `task/translate_srt.py` | `TaskCfgSTS` | 翻译字幕独立功能 |\n\n---\n\n## 二、任务配置数据类体系\n\n任务配置为分层继承的 `@dataclass` 体系（`videotrans/task/taskcfg.py`）：\n\n```\n@dataclass TaskCfgBase              ← 通用字段（路径、语言代码、缓存目录等）\n    ├── @dataclass TaskCfgSTT       ← 语音识别相关字段（recogn_type, model_name, rephrase 等）\n    ├── @dataclass TaskCfgTTS       ← 配音相关字段（tts_type, voice_role, voice_autorate 等）\n    ├── @dataclass TaskCfgSTS       ← 翻译相关字段（translate_type）\n    └── @dataclass TaskCfgVTT       ← 视频翻译全量字段（继承 STT + TTS + STS，新增视频特有字段）\n```\n\n辅助数据类：\n\n| 数据类 | 文件 | 用途 |\n|--------|------|------|\n| `InputFile` | `task/taskcfg.py` | 输入文件元信息（name, dirname, noextname, basename, ext, uuid, target_dir），支持 dict 式访问 |\n| `SignMsg` | `task/taskcfg.py` | 信号消息体（type, uuid, text），提供 `is_stop()` 和 `is_error()` 方法判断状态，在 Worker 线程与主线程间传递 |\n| `SrtItem` | `task/taskcfg.py` | 单条字幕数据（text, start_time, end_time, startraw, endraw, line, time, spk, filename） |\n\n`SrtItem` 支持同时用属性访问（`item.text`）和字典访问（`item['text']`），并可通过 `items()` 迭代。\n\n---\n\n## 三、多线程异步任务处理架构\n\n软件采用基于 **\"生产者-消费者\"模式** 的多线程多队列架构。`MultVideo` 线程充当生产者，将任务对象推入流水线的第一个队列；9 种专用 `BaseWorker` 子类作为消费者，各自监听专属队列。\n\n### 3.1 队列流水线\n\n```\n                     MultVideo (生产者)\n                           │\n                    app_cfg.prepare_queue\n                           ▼\n                   WorkerPrepare (×N)\n                    ┌────────┼────────┐\n                    │ should_recogn ?  │\n                    ▼        ▼        ▼\n           regcon_queue  trans_queue  dubb_queue / assemb_queue / taskdone_queue\n                │\n                ▼\n          WorkerRegcon (×N)\n                │\n         diariz_queue\n                │\n                ▼\n          WorkerDiariz (×N)\n           ┌────┼────┐\n           ▼    ▼    ▼\n      trans_queue  dubb_queue  assemb_queue / taskdone_queue\n           │\n           ▼\n     WorkerTrans (×1)\n      ┌────┼────┐\n      ▼    ▼    ▼\n dubb_queue  assemb_queue  taskdone_queue\n      │\n      ▼\nWorkerDubb (×1)\n      │\n align_queue\n      │\n      ▼\nWorkerAlign (×1)\n ┌────┼────┐\n ▼    ▼    ▼\nregcon2_queue  assemb_queue  taskdone_queue\n │\n ▼\nWorkerRegcon2Pass (×1)\n ┌────┼────┐\n ▼    ▼\nassemb_queue  taskdone_queue\n │\n ▼\nWorkerAssemb (×N)\n │\ntaskdone_queue\n │\n ▼\nWorkerTaskDone (×1)\n │\n(end)\n```\n\n### 3.2 Worker 基类设计\n\n所有工作线程继承自 `BaseWorker(QThread)`（`videotrans/task/job.py:13-66`）：\n\n```python\nclass BaseWorker(QThread):\n    def __init__(self, name, queue):\n        self.name = name\n        self.queue = queue\n\n    def run(self):\n        while True:\n            if app_cfg.exit_soft:          # 全局软退出标志\n                return\n            try:\n                trk = self.queue.get(timeout=1)  # 阻塞1秒取任务\n            except Empty:\n                continue\n            if trk.uuid in app_cfg.stoped_uuid_set:  # 任务已停止\n                continue\n            try:\n                self.process_task(trk)       # 子类实现具体逻辑\n            except Exception as e:\n                self.handle_error(e, trk)    # 统一错误处理\n```\n\n每个子类重写以下方法：\n\n| 方法 | 说明 |\n|------|------|\n| `process_task(trk)` | **必须** — 执行阶段逻辑，并将 trk 路由到下一个队列 |\n| `get_error_prefix(trk)` | 可选 — 返回错误前缀字符串（如 `\"识别出错[Faster-Whisper]\"`） |\n| `cleanup_on_error(trk)` | 可选 — 出错时的清理逻辑 |\n\n`handle_error()` 统一调用 `get_msg_from_except()` 解析异常为用户可读信息，然后通过 `trk.signal()` 发送错误消息。\n\n### 3.3 Worker 路由决策逻辑\n\n每个 Worker 在执行完 `process_task(trk)` 后，根据 `trk` 的标志位决定下一跳队列：\n\n```\nWorkerPrepare    →  regcon_queue | trans_queue | dubb_queue | assemb_queue | taskdone_queue\nWorkerRegcon     →  diariz_queue  (无条件)\nWorkerDiariz     →  trans_queue | dubb_queue | assemb_queue | taskdone_queue  (diariz 异常不阻断流程)\nWorkerTrans      →  dubb_queue | assemb_queue | taskdone_queue\nWorkerDubb       →  align_queue  (无条件)\nWorkerAlign      →  regcon2_queue | assemb_queue | taskdone_queue  (regcon2 仅当有 recogn2pass 属性时)\nWorkerRegcon2Pass → assemb_queue | taskdone_queue\nWorkerAssemb     →  taskdone_queue  (无条件)\nWorkerTaskDone   →  (终止)\n```\n\n### 3.4 线程/进程数量动态计算\n\n- 不存在GPU时，GPU进程固定设为1\n- 存在可用显卡时，进一步根据高级选项中的手动设置，判断应该启动几个GPU进程,  1->min(CPU核数,8)\n- CPU进程数 1 -> min(8,CPU核数,内存/1G, 高级选项中的手动设置数)\n`start_thread()`（`videotrans/task/job.py`）\n\n\n### 3.5 批量任务提交：MultVideo\n\n`MultVideo(QThread)`（`videotrans/task/mult_video.py`）负责将用户选择的多个视频文件逐个创建 `TransCreate` 对象并推入 `prepare_queue`。支持通过 `batch_nums` 参数控制每批并发数量：\n\n- `batch_nums == 0`：全部任务一次性推入队列（最大并发）\n- `batch_nums == 1`：逐次推入，每个任务完成后再推下一个\n- `batch_nums > 1`：每批推入 N 个，等待该批全部完成后再推下一批\n\n### 3.6 软退出机制\n\n全局标志 `app_cfg.exit_soft` 设为 `True` 时，所有 Worker 在下一轮循环中检测并安全退出。`app_cfg.stoped_uuid_set` 用于标记被手动停止的特定任务 UUID，Worker 在取出任务后跳过这些任务。\n\n---\n\n## 四、核心类的设计与继承关系\n\n### 4.1 类继承体系\n\n```\n@dataclass BaseCon                    ← videotrans/configure/base.py\n    │                                  基础属性和工具方法\n    ├── @dataclass BaseTask           ← videotrans/task/_base.py\n    │       │                          定义 8 个阶段空方法和 5 个标志位\n    │       ├── @dataclass TransCreate ← videotrans/task/trans_create.py (~1678 行核心)\n    │       ├── @dataclass SpeechToText ← videotrans/task/speech2text.py (批量语音识别)\n    │       ├── @dataclass DubbingSrt  ← videotrans/task/dubbing.py (批量字幕配音)\n    │       └── @dataclass TranslateSrt ← videotrans/task/translate_srt.py (批量字幕翻译)\n    │\n    ├── @dataclass BaseRecogn         ← videotrans/recognition/_base.py\n    │       │                          VAD 音频切分、字幕合并、CJK 处理\n    │       └── 子类（懒加载）    各 ASR 渠道具体实现\n    │\n    ├── @dataclass BaseTrans          ← videotrans/translator/_base.py\n    │       │                          MD5 缓存、逐行/全文翻译调度\n    │       └── 子类（懒加载）    各翻译渠道具体实现\n    │\n    └── @dataclass BaseTTS            ← videotrans/tts/_base.py\n            │                          异步/多线程并发调度\n            └── 子类（懒加载）    各 TTS 渠道具体实现\n```\n\n所有通道类均为 `@dataclass`，使用 `__post_init__` 初始化而非传统构造函数 `__init__`。\n\n### 4.2 BaseCon——顶层基类\n\n`videotrans/configure/base.py`（296 行）定义了所有类共用的核心能力：\n\n| 方法 | 职责 |\n|------|------|\n| `_exit()` | 检查是否应停止（`exit_soft` 或 UUID 在 `stoped_uuid_set` 中） |\n| `signal(**kwargs)` | 向 UI 发送消息（通过 `push_queue()` → `SignalHub`。CLI 模式下直接 print） |\n| `_set_proxy(type)` | 设置/清除 HTTP 代理（操作 `app_cfg.proxy` 和环境变量） |\n| `_new_process(callback, title, is_cuda, kwargs)` | **在子进程中执行耗时任务**（返回 `(data, error)` 元组） |\n| `_signal_of_process(logs_file)` | 通过轮询 JSON 日志文件的 mtime 实时读取子进程进度 |\n| `convert_to_wav()` | 音频统一转为 48kHz 立体声 WAV（可选去静音） |\n| `_base64_to_audio()` / `_audio_to_base64()` | Base64 音频编解码 |\n| `_process_callback(data)` | 下载进度回调（转发到 `signal()`） |\n\n`BaseCon.__post_init__()` 在初始化时自动调用 `_set_proxy(type='set')` 获取代理配置。\n\n### 4.3 BaseTask——任务基类\n\n`videotrans/task/_base.py:10-167` 定义了所有任务子类的阶段空方法和共享工具：\n\n**阶段方法**（均为空实现，由子类重写）：\n`prepare()`、`recogn()`、`diariz()`、`trans()`、`dubbing()`、`align()`、`assembling()`、`task_done()`\n\n> 注意：`recogn2pass()` 方法定义在 `TransCreate` 中，不在 `BaseTask` 基类中。\n\n**共享方法**：\n| 方法 | 职责 |\n|------|------|\n| `_unlink_size0(file)` | 删除尺寸为 0 的无效文件 |\n| `_save_srt_target(srtstr, file)` | 将 SrtItem 列表格式化为 SRT 字符串并写入文件，发送 `replace_subtitle` 信号 |\n| `check_target_sub(source, target)` | 校验翻译前后字幕行数一致性；不一致时按时间轴匹配对齐 |\n| `set_end(succeed=False)` | 标记任务结束，成功时发送通知并清理临时文件夹 |\n| `_edgetts_single(target_audio, kwargs)` | Edge-TTS 一次性异步配音（带代理回退） |\n\n### 4.4 TransCreate——视频翻译核心实现\n\n`videotrans/task/trans_create.py`（约 1678 行）是完整 9 阶段处理逻辑的实现类。关键内部方法：\n\n| 方法 | 职责 |\n|------|------|\n| `__post_init__()` | 初始化所有文件路径、计算标志位、启动进度计时线程 |\n| `_split_novoice_byraw()` | 从原始视频分离无声视频（优先硬件解码 h264_cuvid，回退 libx264） |\n| `_split_audio_byraw()` | 从原始视频提取 16kHz 单声道 PCM 音频 + 可选人声/背景分离 |\n| `_tts()` | 构建 `queue_tts` 列表（含 clone 参考音频片段），调用 `tts.run()` |\n| `_create_ref_from_vocal()` | 多线程（ThreadPoolExecutor）裁剪原始音频对应片段作为声音克隆参考 |\n| `_recogn_succeed()` | 识别完成后的处理（tiqu 模式下复制文件） |\n| `_back_music()` | 将用户上传的背景音乐与配音音频混合 |\n| `_separate()` | 将分离出的背景音乐重新嵌入配音音频 |\n| `_process_subtitles()` | 处理软/硬字幕嵌入逻辑（单/双字幕、样式设置） |\n\n### 4.5 子进程通道\n\n为防止 `faster-whisper` 崩溃导致整个软件退出，`Faster-Whisper、OpenAI-whisper、Qwen3-ASR、HuggingFace_ASR`、`Qwen3-TTS、OmniVoice、F5-TTS、Confucius-TTS`等部分重型渠道通过 `BaseCon._new_process()` 委托给 `GlobalProcessManager` 在独立子进程中执行。\n\n子进程通过写入 JSON 日志文件来报告进度。`BaseCon._signal_of_process()` 在守护线程中轮询该日志文件，检测到 mtime 变化时解析 JSON 并通过 `signal()` 上报。\n\n---\n\n## 五、配置系统\n\n软件将配置分为三个层次（`videotrans/configure/config.py`，902 行），均为 `@dataclass`：\n\n| 配置类 | 持久化 | 用途 | 示例字段 |\n|--------|--------|------|---------|\n| `AppCfg` | 纯内存 | 队列、状态、线程控制、运行时上下文、CPU、GPU进程数量 | `prepare_queue`, `exit_soft`, `stoped_uuid_set`, `current_status`, `line_roles`, `exec_mode`, `video_codec`, `onlyone_source_sub`, `onlyone_target_sub`, `proxy`, `SUPPORT_LANG` |\n| `AppSettings` | `videotrans/cfg.json` | 高级选项各设置、模型列表 | `homedir`, `model_list`, `vad_type`, `cuda_com_type` |\n| `AppParams` | `videotrans/params.json` | API 密钥、各种界面设置 | `source_language`, `recogn_type`, `chatgpt_key`, `voice_role`, `app_mode` |\n\n关键单例变量在模块加载时自动初始化：\n\n```python\napp_cfg: AppCfg = AppCfg()        # 运行时状态（含 9 个 Queue 实例）\nsettings: AppSettings = AppSettings()  # 从 cfg.json 加载\nparams: AppParams = AppParams()    # 从 params.json 加载\n```\n\n### 5.1 AppSettings 特性\n\n- 支持 `settings['key']` 字典式访问和 `settings.get('key', default)` 方法\n- `get()` 自动对数字类型字段进行类型强制转换（`int_type` 和 `float_type` 白名单）\n- `_get_defaults()` 定义了 ~100 个配置项的默认值\n- 支持连字符字段名映射（如 `\"initial_prompt_zh-cn\"` → `\"initial_prompt_zh_cn\"`）\n\n### 5.2 AppParams 特性\n\n- `_get_defaults()` 定义了 ~100 个用户参数默认值\n- `getset_params(update_data)` 支持批量更新（如 `check_start()` 中收集所有 UI 控件值）\n- API key 类字段统一在此管理，供 `is_input_api()` 校验\n\n### 5.3 AppCfg 运行时状态\n\n- 9 个 `Queue(maxsize=0)`（无限容量）：`prepare_queue` ~ `taskdone_queue`\n- `queue_novice: Dict` — 跟踪无声视频分离进度（key=uuid, value='ing'|'end'）\n- `line_roles: Dict` — 存储单视频模式下用户逐行分配的字幕角色\n- `child_forms: Dict` — 缓存已打开的窗口实例，避免重复创建\n- `exec_mode` — 执行模式（'gui' 或 'cli'）\n- `video_codec` / `codec_cache` — 视频编解码器缓存\n- `onlyone_source_sub` / `onlyone_target_sub` / `onlyone_trans` — 单视频模式字幕状态\n- `SUPPORT_LANG` — 支持的语言列表\n\n### 5.4 环境变量初始化\n\n`_set_env()` 在模块加载时自动执行（`videotrans/configure/config.py:44-72`），设置：\n- `MODELSCOPE_CACHE` / `HF_HOME` / `HF_HUB_CACHE` → `ROOT_DIR/models`\n- `QT_API = 'pyside6'`\n- `PATH` 追加 ffmpeg/sox 目录\n- `OMP_NUM_THREADS = 1`\n- `HF_HUB_DOWNLOAD_TIMEOUT = 3600`\n- `HF_HUB_DISABLE_XET = 1`\n\n---\n\n## 六、GlobalProcessManager——子进程池管理\n\n`videotrans/process/signelobj.py`（167 行）实现了一个类级别单例的 `GlobalProcessManager`：\n\n```\nGlobalProcessManager (类级别单例)\n    ├── _executor_cpu: multiprocessing.Pool\n    │       workers = app_cfg.MAX_CPU_PROCESS \n    │       maxtasksperchild = 1  ← 每个子进程执行一个任务后重启，防内存泄漏\n    │\n    └── _executor_gpu: multiprocessing.Pool\n            workers = app_cfg.MAX_GPU_PROCESS\n            maxtasksperchild = 1\n```\n\n\n\n### 6.3 任务提交接口\n\n```python\nGlobalProcessManager.submit_task_cpu(func, **kwargs)   → AsyncResultFutureWrapper\nGlobalProcessManager.submit_task_gpu(func, **kwargs)   → AsyncResultFutureWrapper\n```\n\n`AsyncResultFutureWrapper` 将 `Pool.apply_async` 的 `AsyncResult` 包装为 `Future` 兼容接口（`.result()`, `.done()`）。\n\n### 6.4 使用场景\n\n通过 `BaseCon._new_process()` 统一调用，用于执行：ASR 推理、TTS 合成、噪声去除、人声分离、说话人分离、标点恢复（均在独立子进程中运行，崩溃不影响主进程）。\n\n---\n\n## 七、SignalHub——跨线程消息中心\n\n`videotrans/configure/signal_hub.py`（33 行）实现了基于 Qt 信号的单例消息传递：\n\n```python\nclass SignalHub(QObject):\n    _instance = None\n    new_message = Signal(str, object)  # (uuid, SignMsg)\n\n    @classmethod\n    def instance(cls):\n        if cls._instance is None:\n            cls._instance = cls()\n        return cls._instance\n\n    @Slot(str, object)\n    def post(self, uuid=None, data=None):\n        self.new_message.emit(uuid, data)  # 跨线程自动使用 QueuedConnection\n```\n\n### 消息流\n\n```\nBaseCon.signal(**kwargs)\n    → push_queue(uuid, SignMsg(**kwargs))     [configure/config.py]\n        → SignalHub.instance().post(uuid, data)\n            → new_message Signal (QueuedConnection)\n                → WinAction.update_data(uuid, data)   [mainwin/_actions.py]\n                    → 按 type 分发:\n                        'logs'|'error'|'succeed'|'set_precent' → set_process_btn_text()\n                        'edit_subtitle_source' → 弹出 EditRecognResultDialog\n                        'edit_subtitle_target' → 弹出 SpeakerAssignmentDialog\n                        'edit_dubbing' → 弹出 EditDubbingResultDialog\n                        'replace_subtitle' → 更新字幕编辑区\n                        'end' → update_status('end')\n```\n\n### 消息类型枚举\n\n| type | 含义 | 处理逻辑 |\n|------|------|---------|\n| `logs` | 普通日志 | 更新进度条文本 |\n| `error` | 错误 | 进度条变红，加入重试队列 |\n| `succeed` | 成功 | 进度条变绿，标记完成 |\n| `set_precent` | 进度百分比 | `text=\"耗时???百分比\"` 格式 |\n| `edit_subtitle_source` | 弹出原始字幕编辑框 | 单视频模式暂停点① |\n| `edit_subtitle_target` | 弹出翻译字幕编辑框 | 单视频模式暂停点② |\n| `edit_dubbing` | 弹出配音结果编辑框 | 单视频模式暂停点③ |\n| `replace_subtitle` | 替换字幕区域内容 | 批量/单视频共用 |\n| `subtitle` | 追加字幕行 | 逐行输出到编辑器 |\n| `end` | 任务完成 | 触发 update_status('end') |\n| `disabled_edit` | 禁止编辑字幕 | 批量模式下锁定编辑器 |\n| `refreshtts` | 刷新 TTS 选择 | 重新设置 TTS 下拉框 |\n| `shitingerror` | 试听错误 | 弹出错误提示 |\n| `ffmpeg` | ffmpeg 状态 | 更新开始按钮文本 |\n\n---\n\n## 八、动态通道加载\n\n`videotrans/__init__.py`（35 行）提供了通用的懒加载机制：\n\n```python\n@dataclass\nclass ChannelProvider:\n    name: str           # 界面显示名称\n    imp: str            # 模块导入后缀（如 \"._whisper\" → \"videotrans.recognition._whisper\"）\n    key_name: str|None  # 对应 params.json 中的 API key 字段（用于 is_input_api 校验）\n    win: str|None       # 对应 winform 中的设置窗口名称\n\ndef get_class(channel_id=0, provider_type=None, _ID_NAME_DICT=None):\n    _key = f'{provider_type}-{channel_id}'\n    if _key in _loaded_modules:\n        return _loaded_modules[_key]\n    module = importlib.import_module(f'videotrans.{provider_type}{_module_map.imp}')\n    for _, obj in inspect.getmembers(module, inspect.isclass):\n        if obj.__module__ == module.__name__:\n            _loaded_modules[_key] = obj\n            return obj\n```\n\n三大模块各自的 `_ID_NAME_DICT`：\n\n| 模块 | 渠道数 | 定义位置 |\n|------|--------|---------|\n| 语音识别 (recognition) | 20+ | `videotrans/recognition/__init__.py:48-79` |\n| 字幕翻译 (translator) | 20+ | `videotrans/translator/__init__.py:60-90` |\n| 文字配音 (tts) | **30+** | `videotrans/tts/__init__.py:75-116` |\n\n### 8.1 统一入口函数\n\n每个模块提供 `run()` 统一入口，内部通过 `get_class()` 获取对应渠道类并实例化调用：\n\n```python\n# recognition/__init__.py\ndef run(*, recogn_type, detect_language, audio_file, ...) -> List[SrtItem]:\n    _cls = get_class(recogn_type, \"recognition\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n\n# translator/__init__.py\ndef run(*, translate_type, text_list, source_code, target_code, ...) -> List[SrtItem]:\n    _cls = get_class(translate_type, \"translator\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n\n# tts/__init__.py\ndef run(*, queue_tts, language, tts_type, ...) -> None:\n    _cls = get_class(tts_type, \"tts\", _ID_NAME_DICT)\n    return _cls(**kwargs).run()\n```\n\n### 8.2 API Key 校验\n\n每个模块提供 `is_input_api(recogn_type/translate_type/tts_type)` 函数，检查对应渠道的 `key_name` 在 `params` 中是否已填写。未填写时自动弹出对应的 winform 设置窗口。\n\n### 8.3 翻译缓存\n\n`BaseTrans`（`videotrans/translator/_base.py`）实现了基于 MD5 的翻译缓存：\n- 缓存 key = `md5(channel_name + api_url + model + source_lang + target_lang + text)`\n- 缓存文件存储在 `{TEMP_ROOT}/translate_cache/`\n- 写入接口 `_set_cache()`，读取接口 `_get_cache()`\n\n### 8.4 CJK 特殊处理\n\n`BaseRecogn`（`videotrans/recognition/_base.py:58-80`）在 `__post_init__` 中对中日韩等语言进行特殊处理：\n- `join_word_flag`：CJK 语言（zh, ja, ko, yue, th, km）字幕词间不加空格（其他语言加空格）\n- `maxlen`：CJK 语言每行最大字符数为 `settings.cjk_len`（默认 15），其他语言为 `settings.other_len`（默认 40）\n- `jianfan`：中文语言且 `settings.zh_hant_s=True` 时启用繁简转换\n\n### 8.5 翻译调度策略\n\n`BaseTrans.run()` 根据 `aisendsrt` 标志选择不同策略：\n\n| 模式 | 条件 | 方法 | 并发数 |\n|------|------|------|--------|\n| 逐行翻译 | 非 AI 渠道 | `_run_text()` → `_item_task()` | `settings.trans_thread`（默认 10） |\n| 全文翻译 | AI 渠道 + `aisendsrt=True` | `_run_srt()` | `settings.aitrans_thread`（默认 50） |\n\n### 8.6 TTS 调度策略\n\n`BaseTTS.run()` 根据渠道类型选择执行方式：\n\n| 渠道类型 | 调度方式 | 说明 |\n|---------|---------|------|\n| Edge-TTS | `asyncio` 异步 | 单线程内 async 并发 |\n| 其他渠道 | `ThreadPoolExecutor` | 由 `dubbing_thread` 控制并发数（默认 1） |\n\n渠道子类可重写 `_exec()` 方法实现自定义调度。`BaseTTS` 默认调用 `__local_mul_thread()` → `_item_task()`。\n\n---\n\n## 九、交互式单视频处理模式\n\n当用户选择 **1 个视频** 且在**标准模式**下时，程序采用不同于批量流水线的处理模型。\n\n### 9.1 实现：Worker(QThread)\n\n`videotrans/task/only_one.py`（148 行）中的 `Worker` 类在**单个 QThread 内串行执行**全部 9 个阶段，通过 `uito = Signal(str, SignMsg)` 与主线程通信：\n\n```\nWorker.run()\n    ├── trk = TransCreate(cfg=TaskCfgVTT(**self.cfg | obj))\n    ├── trk.prepare()\n    ├── trk.recogn()\n    ├── trk.diariz()\n    ├── [暂停点 ①] → _post(type='edit_subtitle_source')\n    │    用户校对原始字幕 → 点击\"确定\"或等待倒计时\n    ├── trk.trans() (if should_trans)\n    ├── [暂停点 ②] → _post(type='edit_subtitle_target')\n    │    用户校对翻译字幕 + 分配说话人角色 → 点击\"确定\"\n    ├── trk.dubbing() (if should_dubbing)\n    ├── [暂停点 ③] → _post(type='edit_dubbing')\n    │    用户修改配音结果 → 点击\"确定\"\n    ├── trk.align()\n    ├── trk.recogn2pass() 暂停点，修改二次识别后的结果\t\n    ├── trk.assembling()\n    └── trk.task_done()\n```\n\n### 9.2 与批量模式的关键差异\n\n| 维度 | 单视频模式 | 批量模式 |\n|------|-----------|---------|\n| 执行线程 | `Worker(QThread)` 直接执行，不使用队列管道 | `TransCreate` 推入 `prepare_queue`，经 9 个 Worker 队列流动 |\n| 消息通道 | `uito` 信号直接连接到 `WinAction.update_data()` | `BaseCon.signal()` → `push_queue()` → `SignalHub` |\n| 暂停机制 | 三段暂停点，用户可中间编辑 | 不支持暂停编辑 |\n| 进度显示 | 字幕编辑区实时显示 | 进度条 + 按钮文本 |\n\n### 9.3 倒计时与暂停机制\n\n1. **自动倒计时**：`app_cfg.set_countdown(86400)` 设置初始值。Worker 线程每 `sleep(1)` 递减一次。默认倒计时由 `settings.countdown_sec` 控制。\n2. **无限期暂停**：用户点击\"停止\"按钮将 `app_cfg.current_status` 设为 `'stop'`，Worker 的 `_exit()` 检测后退出；或 `set_countdown(-1)` 让倒计时消失。\n3. **手动继续**：用户在校对对话框中点击\"确定\"后，`WinAction.set_djs_timeout()` 调用 `app_cfg.set_countdown(-1)` 使倒计时立即归零。\n\n### 9.4 校对对话框\n\n| 对话框 | 文件 | 功能 |\n|--------|------|------|\n| `EditRecognResultDialog` | `component/onlyone_set_recogn.py` | 原始字幕编辑（文本 + 时间轴） |\n| `SpeakerAssignmentDialog` | `component/onlyone_set_role.py` | 翻译字幕编辑 + 逐行分配配音角色 |\n| `EditDubbingResultDialog` | `component/onlyone_set_editdubb.py` | 配音结果试听 + 单独重新配音 |\n| `EditRecognResultDialog2` | `component/onlyone_set_recogn2.py` | 二次识别字幕编辑（文本 + 时间轴） |\n\n![](https://pvtr2.pyvideotrans.com/1760192881455_image.png)\n![](https://pvtr2.pyvideotrans.com/1760192930833_image.png)\n\n\n---\n\n## 十、音画对齐引擎（SpeedRate）\n\n`videotrans/task/_rate.py`（877 行）实现了 `SpeedRate` 和 `TtsSpeedRate` 两个对齐引擎：\n\n### 10.1 SpeedRate（视频翻译场景）\n\n处理策略（按优先级）：\n\n| 条件 | 策略 |\n|------|------|\n| 仅启用音频加速 | 加速配音到匹配字幕时长（最高不超过 `max_audio_speed_rate`） |\n| 启用音频加速 + 视频慢速 | 各负担一半时间差（忽略倍率限制） |\n| 仅启用视频慢速 | 慢放视频片段到匹配配音时长（最高不超过 `max_video_pts_rate`） |\n| 两者均未启用 | 按字幕时间轴拼接音频片段，填充静音/定格处理时长差异 |\n\n额外处理：\n- `remove_silent_mid`：去除字幕之间的静音区间\n- `align_sub_audio`：强制对齐字幕时间轴到实际配音位置\n- 末尾静音移除\n\n### 10.2 TtsSpeedRate（纯配音场景）\n\n简化版对齐引擎，仅负责音频拼接与加速，无视频慢放逻辑。\n\n---\n\n## 十一、软件启动与 UI 实现\n\n### 11.1 启动流程\n\n`sp.py` 是唯一入口，启动过程如下：\n\n```\nsp.py (if __name__ == \"__main__\")\n  │\n  ├── 1. multiprocessing.freeze_support() / set_start_method('spawn')\n  ├── 2. qInstallMessageHandler() 抑制 Qt 警告\n  ├── 3. atexit.register(cleanup) 注册退出清理\n  ├── 4. QApplication.setHighDpiScaleFactorRoundingPolicy(PassThrough)\n  ├── 5. 创建 QApplication\n  ├── 6. 检测是否在压缩包内运行（PyInstaller 打包版）\n  ├── 7. 创建 StartWindow (splash screen, 无边框半透明)\n  │       └── QTimer.singleShot(100ms) → initialize_full_app()\n  │           ├── 重定向 sys.stdout/stderr 到日志文件\n  │           ├── 设置全局异常钩子 show_global_error_dialog\n  │           ├── 解析 --lang CLI 参数\n  │           ├── 导入 darkstyle_rc（编译后的 QRC 资源）\n  │           ├── 加载 QSS 样式表 (videotrans/styles/style.qss)\n  │           ├── 恢复上次窗口大小 (QSettings)\n  │           └── 实例化 MainWindow → uito 连接 splash.update_lable\n  │               └── MainWindow.__init__()\n  │                   ├── setupUi() → 填充下拉列表（翻译/识别/TTS 渠道、语言列表）\n  │                   ├── AiLoaderThread 启动 → 检测 GPU → 回调 _start_workers()\n  │                   ├── _start_workers() → start_thread() 启动 9 种 Worker 线程\n  │                   ├── _set_default() → 恢复上次用户选择\n  │                   ├── _bind_signal() → 绑定 ~60 个控件事件\n  │                   ├── SignalHub.new_message.connect(win_action.update_data)\n  │                   └── uito.emit('end') → splash 关闭\n  └── 8. app.exec() → Qt 事件循环\n```\n\n### 11.2 退出机制\n\n用户点击关闭按钮时：\n1. 设置 `app_cfg.exit_soft = True`, `app_cfg.current_status = 'stop'`\n2. 主窗口立即隐藏（`hide()`）\n3. 保存窗口尺寸到 `QSettings`\n4. 隐藏/关闭所有子窗口\n5. 等待 ~4 秒让所有 Worker 完成当前工作并安全退出\n6. 清理临时目录 `TEMP_ROOT`\n7. `atexit` cleanup 回调执行 → 程序终止\n8. 若为重启模式，启动新进程后 `os._exit(0)`\n\n### 11.3 UI 架构分层\n\n```\nUI 定义层         videotrans/ui/         ← PySide6 UI 布局文件，dark/ 资源文件\n    ↓\n    ↓\n窗口管理层        videotrans/winform/     ← 懒加载的设置/功能窗口模块\n    ↓\n主窗口层          videotrans/mainwin/\n    ├── main_win.py                      ← MainWindow(QMainWindow): UI 初始化、信号绑定、Worker 启动、窗口生命周期\n    ├── _actions.py                       ← WinAction: 核心业务逻辑 → 参数收集 → 任务启动 → 状态分发\n    └── _actions_base.py                 ← WinActionBase: 代理管理、模式切换、文件选择、CUDA 检测、试听\n    ↓\n任务层            videotrans/task/        ← TransCreate、SpeechToText、DubbingSrt、TranslateSrt、Worker 线程、SpeedRate\n```\n\n### 11.4 MainWindow——主窗口\n\n`videotrans/mainwin/main_win.py`职责：\n- `setupUi()`：加载 UI 布局，填充下拉列表（翻译渠道、识别渠道、TTS 渠道、语言、字幕类型）\n- `_bind_signal()`：绑定约 60 个控件事件到 `WinAction` 方法\n- `_start_workers(status)`：GPU 检测完成后启动 9 种 Worker 后台线程\n- `open_winform(name)`：统一窗口打开入口（优先复用已缓存的 `app_cfg.child_forms`，否则调用 `winform.get_win(name).openwin()`）\n- `closeEvent()`：安全关闭流程（标记退出 → 隐藏窗口 → 停止线程 → 清理临时文件）\n- `restart_app()`：询问确认后触发 `closeEvent()` 并启动新进程\n\n### 11.5 WinAction——核心控制器\n\n`WinAction` 继承自 `WinActionBase`（两者均为 `@dataclass`），是连接 UI 和后台任务的关键枢纽：\n\n**WinActionBase**（`mainwin/_actions_base.py`，590 行）提供：\n- 文件选择（`get_mp4()`）—— 单文件/文件夹模式\n- 输出目录设置（`get_save_dir()`）\n- 代理配置（`change_proxy()`, `check_proxy()`, `proxy_alert()`）\n- 模式切换（`set_biaozhun()`, `set_tiquzimu()`）—— 控制 UI 元素显隐\n- CUDA 检测（`check_cuda()`, `cuda_isok()`）\n- 试听功能（`listen_voice_fun()`）—— 创建 `ListenVoice` 线程\n- 角色列表更新（`tts_type_change()`, `set_voice_role()`）\n- 更多设置选项折叠（`toggle_adv()`）\n- UI 启用/禁用控制（`disabled_widget()`, `_disabled_button()`）\n\n**WinAction**（`mainwin/_actions.py`，798 行）提供：\n- `check_start()`：收集所有 UI 控件值 → 构建 `cfg` 字典 → 参数校验 → 调用 `create_btns()`\n- `create_btns()`：格式化输入文件路径 → 创建进度条 → 单视频启动 `Worker`，批量启动 `MultVideo`\n- `update_data(uuid, SignMsg)`：连接 `SignalHub.new_message` 信号 → 按消息类型分发\n- `update_status(type)`：切换 `ing`/`stop`/`end` 状态，控制按钮和进度条\n- `set_process_btn_text(d)`：更新进度条文本/百分比/颜色\n- `retry()`：重新处理失败的任务\n- `_check_all_done()`：检测是否所有任务完成\n\n---\n\n## 十二、异常体系\n\n`videotrans/configure/excepts.py`（376 行）定义了分层异常：\n\n```\nVideoTransError (基类)\n    ├── TranslateSrtError       # 翻译相关错误\n    ├── DubbingSrtError         # 配音相关错误\n    ├── SpeechToTextError       # 语音识别相关错误\n    ├── LLMSegmentError         # LLM 重新断句错误\n    ├── FFmpegError             # FFmpeg 操作错误\n    ├── DownloadModelsError     # 模型下载错误\n    ├── SttTimeoutError         # STT 子进程超时\n    ├── StopTask                # 需立即停止的任务异常\n    └── StopRetry               # 不可重试的错误\n```\n\n`get_msg_from_except(e)` 函数映射数十种第三方库异常为用户可读的中/英文错误消息（覆盖 `httpx`、`openai`、`requests`、`deepgram`、`elevenlabs`、`tenacity` 等）。\n\n`NO_RETRY_EXCEPT` 元组定义了不可恢复的异常类型，翻译/配音模块在重试循环中遇到这些异常时直接放弃。\n\n---\n\n## 十三、代码结构概览\n\n```\n/\n├── sp.py                       # ★ 主程序入口\n├── cli.py                      # ★ CLI 命令行入口\n├── models/                     # 存放本地 AI 模型文件（ONNX 等）\n├── logs/                       # 日志文件目录（YYYYMMDD.log）\n├── ffmpeg/                     # ffmpeg 及 sox 二进制文件\n├── f5-tts/                     # 声音克隆参考音频存放目录\n├── docs/                       # 文档\n├── tmp/                        # 临时文件根目录\n│   ├── dubbing_cache/          # 配音缓存\n│   └── translate_cache/        # 翻译 MD5 缓存目录\n│\n└── videotrans/                 # 核心业务逻辑代码\n    │   __init__.py             # ★ VERSION, ChannelProvider 定义, get_class() 懒加载\n    │   cfg.json                # settings 持久化文件\n    │   params.json             # params 持久化文件\n    │   codec.json              # 视频编解码器缓存\n    │\n    ├── codes/\n    │   └── model.py            # 第三方模型相关定义\n    │\n    ├── configure/              # 全局配置、队列定义、顶层基类\n    │   ├── config.py           # ★ AppCfg / AppSettings / AppParams / logger / 队列定义 / tr() / push_queue()\n    │   ├── base.py             # ★ BaseCon 基类（_new_process, signal, _exit, convert_to_wav 等）\n    │   ├── contants.py         # ★ 全局常量（模型列表、语言测试文本、标点符号、代理白名单等）\n    │   ├── excepts.py          # ★ 异常体系 + get_msg_from_except()\n    │   ├── signal_hub.py       # ★ SignalHub 单例（跨线程 Qt 信号）\n    │   └── whispernet_config.py # Whisper.NET 配置\n    │\n    ├── task/                   # 任务处理逻辑与后台线程\n    │   ├── _base.py            # ★ BaseTask 基类\n    │   ├── taskcfg.py          # ★ TaskCfgBase/VTT/STT/TTS/STS + InputFile + SignMsg + SrtItem\n    │   ├── trans_create.py     # ★ TransCreate 完整实现（视频翻译核心）\n    │   ├── speech2text.py      # ★ SpeechToText（语音转录独立功能）\n    │   ├── dubbing.py          # ★ DubbingSrt（文字配音独立功能配音）\n    │   ├── translate_srt.py    # ★ TranslateSrt（翻译字幕独立功能）\n    │   ├── job.py              # ★ 9 种 BaseWorker 子类 + start_thread() 入口\n    │   ├── only_one.py         # ★ 单视频交互式 Worker(QThread) + uito 信号\n    │   ├── mult_video.py       # ★ 多视频批量提交 MultVideo(QThread)\n    │   ├── _rate.py            # SpeedRate / TtsSpeedRate 音画对齐引擎\n    │   ├── separate_worker.py  # SeparateWorker 独立人声分离 QThread\n    │   ├── simple_runnable_qt.py # QRunnable 线程池工具\n    │   ├── child_win_sign.py   # 子窗口信号处理\n    │   └── update_ffmpeg.py    # ffmpeg 更新脚本\n    │\n    ├── recognition/            # 语音识别 (ASR) 渠道包\n    │   ├── _constants.py       # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseRecogn（语音识别基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── translator/             # 字幕翻译渠道包\n    │   ├── _constants.py         # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseTrans（字幕翻译渠道基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── tts/                    # 文字配音 (TTS) 包\n    │   ├── _constants.py         # ★ 渠道常量 名称等定义\n    │   ├── _base.py            # ★ BaseTTS（文字配音基类）\n    │   └── _*.py               # 渠道实现代码\n    │\n    ├── process/                # 独立子进程实现：主要用于重型任务，例如语音识别、语音合成大模型等\n    │   ├── __init__.py         # 子进程函数导出\n    │   ├── signelobj.py        # ★ GlobalProcessManager（CPU/GPU 双进程池）\n    │   ├── prepare_audio.py    # 人声分离、降噪、标点恢复、说话人分离\n    │   ├── stt_fun.py          # ASR 子进程入口（openai_whisper, faster_whisper, paraformer, funasr_mlt, qwen3asr_fun 等）\n    │   ├── tts_fun.py          # TTS 子进程入口（qwen3tts_fun）\n    │   └── vad.py              # VAD 语音活动检测（已改为主进程中实现）\n    │\n    ├── mainwin/                # 主窗口界面与业务逻辑\n    │   ├── main_win.py         # ★ MainWindow(QMainWindow) 初始化、信号绑定、线程启动\n    │   ├── _actions.py         # ★ WinAction 核心控制器（检查、启动、状态更新）\n    │   └── _actions_base.py    # ★ WinActionBase 基类\n    │\n    ├── component/              # UI 通用组件\n    │   ├── progressbar.py      # 可点击进度条\n    │   ├── onlyone_set_recogn.py    # 单视频模式：原始字幕编辑对话框\n    │   ├── onlyone_set_recogn2.py    # 单视频模式：二次识别后的字幕编辑对话框\n    │   ├── onlyone_set_role.py      # 单视频模式：说话人角色分配对话框\n    │   ├── onlyone_set_editdubb.py  # 单视频模式：配音结果编辑对话框\n    │   ├── clip_video.py       # 按字幕裁剪视频功能\n    │   ├── realtime_stt.py     # 实时语音识别功能\n    │   ├── textmatching.py     # 文本匹配与打轴功能\n    │   ├── set_ass.py          # 硬字幕样式设置\n    │   ├── set_cpp.py          # Whisper.cpp 路径设置\n    │   ├── set_xxl.py          # Faster-Whisper-XXL 路径设置\n    │   └── controlobj.py       # 控件对象管理\n    │\n    ├── ui/                     # PySide6 UI 定义文件\n    │   ├── en.py               # ★ 主窗口 UI 布局定义\n    │   ├── chatgpt.py, deepseek.py, gemini.py, ...    # 各渠道设置对话框布局\n    │   ├── menu_list.py \t\t# 菜单定义配置文件\n    │   └── dark/               # 暗色主题资源（darkstyle_rc.py, palette.py）\n    │\n    ├── winform/                # 各渠道设置窗口\n    │   ├── __init__.py         # ★ get_win() 懒加载入口 \n    │   ├── chatgpt.py, azure.py, baidu.py, ...  # 渠道设置窗口\n    │\n    ├── styles/                 # UI 样式与媒体资源\n    │   ├── style.qss           # Qt 样式表\n    │   ├── logo.png            # 启动画面 logo\n    │   ├── icon.ico            # 应用图标\n    │   ├── simhei.ttf          # 黑体中文字体\n    │   ├── preview.png         # 设置硬字幕样式用预览图\n    │   ├── no-remove.mp4       # 测试硬编码支持用轻量占位视频\n    │   └── no-remove.wav       # 测试硬编码支持用轻量占位视频\n    │\n    ├── util/                   # 通用工具函数（18 个文件）\n    │   ├── tools.py            # ★ 核心工具函数（ffmpeg 封装、字幕解析/格式化、文件操作、系统通知、模型下载）\n    │   ├── gpus.py             # GPU 检测与分配\n    │   ├── checkgpu.py         # GPU 检测线程（AiLoaderThread）\n    │   ├── ListenVoice.py      # 声音试听功能（ListenVioce QThread）\n    │   ├── req_fac.py          # HuggingFace 自定义 session 工厂\n    │   ├── cn_tn.py            # 中文文本规范化\n    │   ├── en_tn.py            # 英文文本规范化\n    │   ├── help_down.py        # 下载工具函数\n    │   ├── help_ffmpeg.py      # ffmpeg 视频编解码器检测\n    │   ├── help_misc.py        # 杂项工具\n    │   ├── help_role.py        # 配音角色工具\n    │   ├── help_srt.py         # 字幕文件工具\n    │   ├── helper_supertonic.py # Supertonic TTS 辅助\n    │   ├── TestSrtTrans.py     # 翻译测试工具\n    │   └── TestSTT.py          # STT 测试工具\n    │\n    ├── language/               # 界面多语言 JSON 文件\n    │   ├── en_US.json\n    │   ├── zh_CN.json\n    │\n    ├── prompts/                # AI 翻译提示词模板\n    │   ├── srt/                # SRT 格式翻译 prompt（chatgpt.txt, deepseek.txt 等 13 个）\n    │   ├── language_prompts/   # 针对各个目标语言动态插入的 语言要求提示词片段\n    │   ├── text/               # 纯文本翻译 prompt\n    │   └── recharge/           # LLM纠错 prompt（llm.txt）\n    │\n    └── voicejson/              # TTS 音色配置文件（14 个 JSON）\n        ├── edge_tts.json       # Edge-TTS 各语言音色列表\n        ├── azure_voice_list.json # Azure TTS 音色列表\n        ├── qwen3tts.json       # Qwen3-TTS 音色\n        └── ...                 # 其他渠道音色配置\n```\n\n---\n\n\n---\n\n> **版本**: v4.13\n> **主页**: https://github.com/jianchang512/pyvideotrans\n> **文档**: https://pyvideotrans.com\n> **BBS**: https://bbs.pyvideotrans.com\n</yakaai_skills>"}