subtitle-studio: 字幕生成系统
- 选择文件夹/单文件 → SenseVoice 转录 → LLM 修正 → 多路线翻译 → 双语 SRT - 路线:直译中文 / 英转中 / 仅转录 - 并发流水线:转录(可调) + LLM 并发(可调),降噪拆锁并发 - 断点续跑:.subtitle-work/ 中间产物,三阶段独立续跑 + 翻译段级续跑 - 去重:history 跟随视频文件夹,防重复任务保护 - 实时进度:SSE 推送 + 耗时显示 + 子任务状态 + 重新生成按钮 - 时间戳调优:VAD silence_schedule + noisereduce 降噪 + 完整性校验
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
subtitle-studio 后台启动器(无阻塞)
|
||||
用 subprocess.Popen 启动 uvicorn,shell 调用立即返回
|
||||
日志写到 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()
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user