subtitle-studio: 字幕生成系统

- 选择文件夹/单文件 → SenseVoice 转录 → LLM 修正 → 多路线翻译 → 双语 SRT
- 路线:直译中文 / 英转中 / 仅转录
- 并发流水线:转录(可调) + LLM 并发(可调),降噪拆锁并发
- 断点续跑:.subtitle-work/ 中间产物,三阶段独立续跑 + 翻译段级续跑
- 去重:history 跟随视频文件夹,防重复任务保护
- 实时进度:SSE 推送 + 耗时显示 + 子任务状态 + 重新生成按钮
- 时间戳调优:VAD silence_schedule + noisereduce 降噪 + 完整性校验
This commit is contained in:
hmo
2026-08-16 20:17:14 +08:00
commit 7de5ab3902
20 changed files with 2410 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
@echo off
chcp 65001 >nul
title subtitle-studio 字幕生成系统
setlocal
REM ===== subtitle-studio 启动脚本 =====
REM 自动定位项目根目录(bat 所在目录的上上级)
set SCRIPT_DIR=%~dp0
set PROJECT_ROOT=%SCRIPT_DIR%..
set APP_DIR=%PROJECT_ROOT%\src
set PYTHON=D:\ProgramData\anaconda3\envs\py312_cuda\python.exe
set PORT=8788
REM 检查 Python 环境
if not exist "%PYTHON%" (
echo [错误] 找不到 Python 环境: %PYTHON%
echo 请确认 py312_cuda 环境存在
pause
exit /b 1
)
REM 检查端口是否已被占用(已有实例在跑)
netstat -ano | findstr ":%PORT%" | findstr "LISTENING" >nul 2>&1
if %errorlevel%==0 (
echo subtitle-studio 已在运行: http://127.0.0.1:%PORT%
start http://127.0.0.1:%PORT%
exit /b 0
)
echo ============================================
echo subtitle-studio 字幕生成系统
echo 服务地址: http://127.0.0.1:%PORT%
echo 关闭本窗口即停止服务
echo ============================================
echo.
REM 后台清理旧日志(可选)
if exist "%PROJECT_ROOT%\logs\server.log" del "%PROJECT_ROOT%\logs\server.log" >nul 2>&1
REM 启动服务(前台,窗口关闭即停止)
cd /d "%PROJECT_ROOT%"
"%PYTHON%" -m uvicorn substudio.main:app --host 127.0.0.1 --port %PORT% --app-dir "%APP_DIR%"
echo.
echo 服务已停止。
pause
+56
View File
@@ -0,0 +1,56 @@
@echo off
chcp 65001 >nul
title subtitle-studio 启动器
setlocal
REM ===== subtitle-studio 后台启动(无窗口)=====
set SCRIPT_DIR=%~dp0
set PROJECT_ROOT=%SCRIPT_DIR%..
set APP_DIR=%PROJECT_ROOT%\src
set PYTHON=D:\ProgramData\anaconda3\envs\py312_cuda\python.exe
set PORT=8788
set LOG_DIR=%PROJECT_ROOT%\logs
if not exist "%PYTHON%" (
echo [错误] 找不到 Python 环境: %PYTHON%
pause
exit /b 1
)
netstat -ano | findstr ":%PORT%" | findstr "LISTENING" >nul 2>&1
if %errorlevel%==0 (
echo subtitle-studio 已在运行: http://127.0.0.1:%PORT%
start http://127.0.0.1:%PORT%
exit /b 0
)
if not exist "%LOG_DIR%" mkdir "%LOG_DIR%"
REM 后台启动:用 cmd /c 包装重定向,start 本身不阻塞
cd /d "%PROJECT_ROOT%"
start "subtitle-studio-server" /min cmd /c ""%PYTHON%" -m uvicorn substudio.main:app --host 127.0.0.1 --port %PORT% --app-dir "%APP_DIR%" >"%LOG_DIR%\server.log" 2>&1"
REM 等待端口就绪
set /a tries=0
:waitloop
set /a tries+=1
if %tries% GTR 20 goto timeout
netstat -ano | findstr ":%PORT%" | findstr "LISTENING" >nul 2>&1
if %errorlevel%==0 goto ready
timeout /t 1 /nobreak >nul
goto waitloop
:ready
echo.
echo ✅ subtitle-studio 已启动
echo http://127.0.0.1:%PORT%
echo 日志: %LOG_DIR%\server.log
echo.
start http://127.0.0.1:%PORT%
exit /b 0
:timeout
echo.
echo ⚠️ 启动超时,请查看日志: %LOG_DIR%\server.log
pause
exit /b 1
+55
View File
@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
"""
subtitle-studio 后台启动器(无阻塞)
用 subprocess.Popen 启动 uvicornshell 调用立即返回
日志写到 logs/server.log
用法: python start_server.py [port]
"""
import sys, os, subprocess, time, threading
PYTHON = sys.executable
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
APP_DIR = os.path.join(PROJECT_ROOT, "src")
LOG_DIR = os.path.join(PROJECT_ROOT, "logs")
PORT = sys.argv[1] if len(sys.argv) > 1 else "8788"
os.makedirs(LOG_DIR, exist_ok=True)
log_path = os.path.join(LOG_DIR, "server.log")
def is_running(port):
"""端口是否已被监听"""
try:
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(1)
return s.connect_ex(("127.0.0.1", int(port))) == 0
except Exception:
return False
def main():
if is_running(PORT):
print(f"subtitle-studio 已在运行: http://127.0.0.1:{PORT}")
return
# 打开日志文件(追加模式)
logf = open(log_path, 'a', encoding='utf-8', buffering=1)
# 真正的异步启动:不等待,不继承句柄
proc = subprocess.Popen(
[PYTHON, "-m", "uvicorn", "substudio.main:app",
"--host", "127.0.0.1", "--port", PORT, "--app-dir", APP_DIR],
cwd=PROJECT_ROOT,
stdout=logf,
stderr=subprocess.STDOUT,
creationflags=subprocess.CREATE_NO_WINDOW,
)
print(f"已启动 subtitle-studio (PID {proc.pid})")
print(f"服务地址: http://127.0.0.1:{PORT}")
print(f"日志: {log_path}")
# 不等待进程,直接返回
if __name__ == "__main__":
main()
+23
View File
@@ -0,0 +1,23 @@
@echo off
chcp 65001 >nul
title subtitle-studio 停止
setlocal
REM ===== 停止 subtitle-studio 服务 =====
set PORT=8788
echo 查找端口 %PORT% 上的进程...
for /f "tokens=5" %%p in ('netstat -ano ^| findstr ":%PORT%" ^| findstr "LISTENING"') do (
echo 停止 PID %%p
taskkill /f /pid %%p >nul 2>&1
)
timeout /t 1 /nobreak >nul
netstat -ano | findstr ":%PORT%" | findstr "LISTENING" >nul 2>&1
if %errorlevel%==0 (
echo ⚠️ 端口仍被占用,可能需要手动结束
) else (
echo ✅ subtitle-studio 已停止
)
pause