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
+21
View File
@@ -0,0 +1,21 @@
{
"id": "srt-to-dual-eye-ass",
"name": "SRT to Dual-Eye ASS",
"version": "0.1.0",
"capability": "subtitle",
"repo_dir": "wov-node-ass",
"command": ["python", "-m", "wov_node_ass"],
"env": {
"WOV_NODE_PORT": "0"
},
"input_schema": {
"cn_srt_uri": "file"
},
"output_schema": {
"ass_uri": "file"
},
"max_concurrency": 1,
"idle_ttl_seconds": 60,
"health_timeout_seconds": 10,
"keep_warm": false
}
+22
View File
@@ -0,0 +1,22 @@
{
"id": "echo",
"name": "Echo Node",
"version": "0.1.0",
"capability": "echo",
"repo_dir": "wov-node-echo",
"command": ["python", "-m", "wov_node_echo"],
"env": {
"WOV_NODE_PORT": "0"
},
"input_schema": {
"text": "string"
},
"output_schema": {
"text": "string",
"file_uri": "file"
},
"max_concurrency": 1,
"idle_ttl_seconds": 15,
"health_timeout_seconds": 10,
"keep_warm": false
}
+21
View File
@@ -0,0 +1,21 @@
{
"id": "ffmpeg-extract",
"name": "FFmpeg Audio Extract",
"version": "0.1.0",
"capability": "media",
"repo_dir": "wov-node-ffmpeg",
"command": ["python", "-m", "wov_node_ffmpeg"],
"env": {
"WOV_NODE_PORT": "0"
},
"input_schema": {
"video_uri": "file"
},
"output_schema": {
"audio_uri": "file"
},
"max_concurrency": 1,
"idle_ttl_seconds": 60,
"health_timeout_seconds": 10,
"keep_warm": false
}
+15
View File
@@ -0,0 +1,15 @@
{
"id": "frame-extract",
"name": "Frame Extract",
"version": "0.1.0",
"capability": "frame-extract",
"repo_dir": "nodes",
"command": ["python", "-m", "frame_extract"],
"env": {},
"input_schema": { "video_uri": "file" },
"output_schema": { "frames_manifest": "file", "frame_count": "integer" },
"max_concurrency": 1,
"idle_ttl_seconds": 300,
"health_timeout_seconds": 10,
"keep_warm": false
}
+15
View File
@@ -0,0 +1,15 @@
{
"id": "llm-filter",
"name": "LLM Subtitle Filter",
"version": "0.1.0",
"capability": "llm-filter",
"repo_dir": "nodes",
"command": ["python", "-m", "llm_filter"],
"env": {},
"input_schema": { "srt_uri": "file" },
"output_schema": { "srt_uri": "file", "kept": "integer", "removed": "integer" },
"max_concurrency": 1,
"idle_ttl_seconds": 300,
"health_timeout_seconds": 10,
"keep_warm": false
}
+21
View File
@@ -0,0 +1,21 @@
{
"id": "llm-translate",
"name": "LLM Subtitle Translate",
"version": "0.1.0",
"capability": "llm",
"repo_dir": "wov-node-llm",
"command": ["python", "-m", "wov_node_llm"],
"env": {
"WOV_NODE_PORT": "0"
},
"input_schema": {
"srt_uri": "file"
},
"output_schema": {
"cn_srt_uri": "file"
},
"max_concurrency": 1,
"idle_ttl_seconds": 60,
"health_timeout_seconds": 10,
"keep_warm": false
}
+15
View File
@@ -0,0 +1,15 @@
{
"id": "subtitle-ocr",
"name": "Subtitle OCR",
"version": "0.1.0",
"capability": "subtitle-ocr",
"repo_dir": "nodes",
"command": ["python", "-m", "subtitle_ocr"],
"env": {},
"input_schema": { "frames_manifest": "file" },
"output_schema": { "srt_uri": "file", "count": "integer" },
"max_concurrency": 1,
"idle_ttl_seconds": 300,
"health_timeout_seconds": 10,
"keep_warm": false
}
+15
View File
@@ -0,0 +1,15 @@
{
"id": "vlm-ocr",
"name": "VLM OCR",
"version": "0.1.0",
"capability": "ocr",
"repo_dir": "nodes",
"command": ["python", "-m", "vlm_ocr"],
"env": {},
"input_schema": { "image_uri": "file" },
"output_schema": { "text": "string", "text_uri": "file" },
"max_concurrency": 1,
"idle_ttl_seconds": 300,
"health_timeout_seconds": 10,
"keep_warm": false
}
+21
View File
@@ -0,0 +1,21 @@
{
"id": "faster-whisper",
"name": "Faster Whisper ASR",
"version": "0.1.0",
"capability": "asr",
"repo_dir": "wov-node-whisper",
"command": ["python", "-m", "wov_node_whisper"],
"env": {
"WOV_NODE_PORT": "0"
},
"input_schema": {
"audio_uri": "file"
},
"output_schema": {
"srt_uri": "file"
},
"max_concurrency": 1,
"idle_ttl_seconds": 300,
"health_timeout_seconds": 30,
"keep_warm": false
}