feat: VRSub 单体应用(WOV 单机版)初始提交

为视频生成 VR 双眼字幕的单体实现:FastAPI 后端、调度器与全部节点
(提音/转写/翻译/ASS/抽帧/OCR/LLM 过滤)在单进程内运行。

- 节点协议(wov_sdk 数据模型)与分布式版保持一致,预留回退桥梁
- 工作流即数据:DAG 存于 workflows/*.json,模型/链路改动只改数据
- 调度器:拓扑顺序执行、断点续跑(产物重建)、任务暂停/继续
- 抽帧按帧间隔(select 按帧号精确取帧),VLM OCR 与 LLM 过滤使用
  自适应线程池弹性并发,并打印数据处理速度进度日志
- 100% 行覆盖率(pytest --cov-fail-under=100)
This commit is contained in:
2026-08-16 23:58:25 +08:00
commit 4746e0363f
75 changed files with 10969 additions and 0 deletions
Executable
+70
View File
@@ -0,0 +1,70 @@
<!doctype html>
<!-- VRSub 首页:发起任务。工作流选择始终可见;选择后若该工作流需要 crop
(如字幕 OCR 的抽帧节点),则展示视频预览与框选字幕区域面板,
框选后才可提交。 -->
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>VRSub - 发起任务</title>
<link rel="stylesheet" href="/assets/styles.css?v=3" />
</head>
<body>
<header class="topbar">
<a class="brand" href="/">VRSub</a>
<nav>
<a href="/">发起任务</a>
<a href="/tasks.html">任务管理</a>
<a href="/admin.html">管理后台</a>
<a href="/workflow.html">工作流</a>
</nav>
</header>
<main class="container">
<h1>VRSub 字幕生成</h1>
<p class="muted">选择工作流并上传视频。字幕 OCR 类工作流需要先在预览中框选字幕区域。</p>
<!-- 工作流选择放在两种模式之外,任何情况下都可见、可切换。 -->
<label for="workflowSelect">选择工作流</label>
<select id="workflowSelect"></select>
<div class="app-grid">
<!-- 标准任务:选择文件后提交。 -->
<article class="card" id="standardCard">
<h2>视频字幕生成</h2>
<p>上传视频,后台自动执行字幕生成。</p>
<label for="videoFile">选择视频</label>
<input id="videoFile" type="file" accept="video/*,.mp4,.mkv,.avi,.mov,.m4a,.mp3" />
<button id="uploadVideo" class="primary">上传并生成</button>
<pre id="runProgress" class="result">等待上传...</pre>
</article>
<!-- 字幕 OCR 面板:选择视频后在预览中拖动框选字幕区域。 -->
<article class="card" id="ocrCard" hidden>
<h2>字幕 OCR 提取</h2>
<p>选择视频并在预览中<b>拖动框选字幕区域</b>,框选完成后才能提交任务。</p>
<label for="ocrVideoFile">选择视频</label>
<input id="ocrVideoFile" type="file" accept="video/*,.mp4,.mkv,.avi,.mov" />
<div class="ocr-stage">
<div id="videoBox" class="video-box">
<video id="ocrVideo" controls preload="metadata"></video>
<canvas id="ocrCanvas"></canvas>
</div>
</div>
<label for="cropValue">字幕区域 crop [x, y, w, h]</label>
<input id="cropValue" type="text" readonly placeholder="选择视频并框选后自动生成" />
<div class="actions">
<button id="ocrDrawMode" class="ghost">进入框选模式</button>
<button id="ocrReset" class="ghost">清除框选</button>
<button id="ocrSubmit" class="primary" disabled>提交任务</button>
</div>
<p class="muted">提示:先用进度条定位到有字幕的画面,再点击「进入框选模式」拖动框选字幕区域。</p>
<pre id="ocrProgress" class="result">等待框选...</pre>
</article>
</div>
</main>
<script src="/assets/crop.js?v=2"></script>
<script src="/assets/app.js?v=6"></script>
</body>
</html>