video2doc 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- video2doc-0.1.0/.gitattributes +10 -0
- video2doc-0.1.0/.github/workflows/ci.yml +46 -0
- video2doc-0.1.0/.gitignore +23 -0
- video2doc-0.1.0/CHANGELOG.md +48 -0
- video2doc-0.1.0/LICENSE +21 -0
- video2doc-0.1.0/PKG-INFO +406 -0
- video2doc-0.1.0/README.md +365 -0
- video2doc-0.1.0/docs//344/275/277/347/224/250/346/211/213/345/206/214.md +1108 -0
- video2doc-0.1.0/pyproject.toml +67 -0
- video2doc-0.1.0/src/video2doc/__init__.py +3 -0
- video2doc-0.1.0/src/video2doc/__main__.py +4 -0
- video2doc-0.1.0/src/video2doc/cli.py +815 -0
- video2doc-0.1.0/src/video2doc/config.py +288 -0
- video2doc-0.1.0/src/video2doc/doctor.py +216 -0
- video2doc-0.1.0/src/video2doc/errors.py +33 -0
- video2doc-0.1.0/src/video2doc/hotwords.py +118 -0
- video2doc-0.1.0/src/video2doc/job.py +77 -0
- video2doc-0.1.0/src/video2doc/overrides.py +188 -0
- video2doc-0.1.0/src/video2doc/pipeline.py +278 -0
- video2doc-0.1.0/src/video2doc/providers/__init__.py +1 -0
- video2doc-0.1.0/src/video2doc/providers/asr/__init__.py +26 -0
- video2doc-0.1.0/src/video2doc/providers/asr/base.py +90 -0
- video2doc-0.1.0/src/video2doc/providers/asr/faster_whisper.py +180 -0
- video2doc-0.1.0/src/video2doc/providers/asr/openai_compat.py +181 -0
- video2doc-0.1.0/src/video2doc/providers/llm/__init__.py +19 -0
- video2doc-0.1.0/src/video2doc/providers/llm/base.py +109 -0
- video2doc-0.1.0/src/video2doc/providers/llm/openai_compat.py +155 -0
- video2doc-0.1.0/src/video2doc/providers/ocr/__init__.py +21 -0
- video2doc-0.1.0/src/video2doc/providers/ocr/base.py +84 -0
- video2doc-0.1.0/src/video2doc/providers/ocr/rapidocr.py +84 -0
- video2doc-0.1.0/src/video2doc/render/__init__.py +3 -0
- video2doc-0.1.0/src/video2doc/render/html.py +414 -0
- video2doc-0.1.0/src/video2doc/review/__init__.py +3 -0
- video2doc-0.1.0/src/video2doc/review/server.py +563 -0
- video2doc-0.1.0/src/video2doc/review/static/app.css +723 -0
- video2doc-0.1.0/src/video2doc/review/static/app.js +545 -0
- video2doc-0.1.0/src/video2doc/review/static/index.html +69 -0
- video2doc-0.1.0/src/video2doc/revision.py +119 -0
- video2doc-0.1.0/src/video2doc/schema.py +348 -0
- video2doc-0.1.0/src/video2doc/stages/__init__.py +46 -0
- video2doc-0.1.0/src/video2doc/stages/align.py +191 -0
- video2doc-0.1.0/src/video2doc/stages/audio.py +68 -0
- video2doc-0.1.0/src/video2doc/stages/correct.py +138 -0
- video2doc-0.1.0/src/video2doc/stages/critique.py +62 -0
- video2doc-0.1.0/src/video2doc/stages/frames.py +249 -0
- video2doc-0.1.0/src/video2doc/stages/ingest.py +277 -0
- video2doc-0.1.0/src/video2doc/stages/ocr.py +66 -0
- video2doc-0.1.0/src/video2doc/stages/render.py +92 -0
- video2doc-0.1.0/src/video2doc/stages/segment.py +61 -0
- video2doc-0.1.0/src/video2doc/stages/transcript.py +100 -0
- video2doc-0.1.0/src/video2doc/stages/write.py +65 -0
- video2doc-0.1.0/src/video2doc/subtitles.py +181 -0
- video2doc-0.1.0/src/video2doc/utils/__init__.py +1 -0
- video2doc-0.1.0/src/video2doc/utils/common.py +47 -0
- video2doc-0.1.0/src/video2doc/utils/markdown.py +72 -0
- video2doc-0.1.0/src/video2doc/utils/media.py +97 -0
- video2doc-0.1.0/src/video2doc/utils/proc.py +80 -0
- video2doc-0.1.0/src/video2doc/visual/__init__.py +25 -0
- video2doc-0.1.0/src/video2doc/visual/contact.py +280 -0
- video2doc-0.1.0/src/video2doc/visual/extract.py +74 -0
- video2doc-0.1.0/src/video2doc/visual/shots.py +180 -0
- video2doc-0.1.0/src/video2doc/visual/stats.py +131 -0
- video2doc-0.1.0/src/video2doc/writing/__init__.py +65 -0
- video2doc-0.1.0/src/video2doc/writing/critique.py +269 -0
- video2doc-0.1.0/src/video2doc/writing/materials.py +150 -0
- video2doc-0.1.0/src/video2doc/writing/prompts.py +171 -0
- video2doc-0.1.0/src/video2doc/writing/rewrite.py +66 -0
- video2doc-0.1.0/src/video2doc/writing/segment.py +272 -0
- video2doc-0.1.0/src/video2doc/writing/write.py +213 -0
- video2doc-0.1.0/tests/test_align.py +195 -0
- video2doc-0.1.0/tests/test_cli.py +138 -0
- video2doc-0.1.0/tests/test_config.py +86 -0
- video2doc-0.1.0/tests/test_correct.py +120 -0
- video2doc-0.1.0/tests/test_critique.py +236 -0
- video2doc-0.1.0/tests/test_frames.py +218 -0
- video2doc-0.1.0/tests/test_hotwords.py +55 -0
- video2doc-0.1.0/tests/test_job.py +28 -0
- video2doc-0.1.0/tests/test_llm.py +79 -0
- video2doc-0.1.0/tests/test_m3.py +201 -0
- video2doc-0.1.0/tests/test_ocr.py +129 -0
- video2doc-0.1.0/tests/test_overrides.py +166 -0
- video2doc-0.1.0/tests/test_pipeline.py +171 -0
- video2doc-0.1.0/tests/test_render.py +148 -0
- video2doc-0.1.0/tests/test_review.py +276 -0
- video2doc-0.1.0/tests/test_revision.py +221 -0
- video2doc-0.1.0/tests/test_rewrite.py +175 -0
- video2doc-0.1.0/tests/test_segment.py +201 -0
- video2doc-0.1.0/tests/test_subtitles.py +141 -0
- video2doc-0.1.0/tests/test_write.py +253 -0
- video2doc-0.1.0/video2doc.example.toml +46 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: ${{ matrix.os }} / py${{ matrix.python }}
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, windows-latest]
|
|
16
|
+
python: ["3.11", "3.12"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python }}
|
|
24
|
+
|
|
25
|
+
# Windows 上一定要手动装 ffmpeg,否则很多路径问题只会在用户机器上暴露
|
|
26
|
+
- name: Install ffmpeg (Ubuntu)
|
|
27
|
+
if: runner.os == 'Linux'
|
|
28
|
+
run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
|
29
|
+
|
|
30
|
+
- name: Install ffmpeg (Windows)
|
|
31
|
+
if: runner.os == 'Windows'
|
|
32
|
+
run: choco install ffmpeg -y --no-progress
|
|
33
|
+
|
|
34
|
+
- name: Install package
|
|
35
|
+
run: |
|
|
36
|
+
python -m pip install --upgrade pip
|
|
37
|
+
pip install -e ".[dev]"
|
|
38
|
+
|
|
39
|
+
- name: Lint
|
|
40
|
+
run: ruff check .
|
|
41
|
+
|
|
42
|
+
- name: Test
|
|
43
|
+
run: pytest
|
|
44
|
+
|
|
45
|
+
- name: CLI smoke test
|
|
46
|
+
run: v2doc --version
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
.venv/
|
|
5
|
+
venv/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.coverage
|
|
11
|
+
htmlcov/
|
|
12
|
+
|
|
13
|
+
# 运行产物
|
|
14
|
+
output/
|
|
15
|
+
out/
|
|
16
|
+
*.wav
|
|
17
|
+
*.mp4
|
|
18
|
+
*.mkv
|
|
19
|
+
*.webm
|
|
20
|
+
|
|
21
|
+
# 本地配置可能含密钥
|
|
22
|
+
video2doc.toml
|
|
23
|
+
.env
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
版本号遵循[语义化版本](https://semver.org/lang/zh-CN/),格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/)。
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-10-22
|
|
6
|
+
|
|
7
|
+
首个公开版本:从一条视频链接(或本地文件)到一份单页图文教程的完整流水线。
|
|
8
|
+
|
|
9
|
+
### 新增
|
|
10
|
+
|
|
11
|
+
**视频理解流水线(M1–M3)**
|
|
12
|
+
|
|
13
|
+
- 下载与探测:B 站、YouTube、本地文件;优先复用平台自带字幕
|
|
14
|
+
- 语音识别:`faster-whisper`(本地、默认)与 OpenAI 兼容的云端接口
|
|
15
|
+
- 可选的术语 / 错别字校正,支持热词表
|
|
16
|
+
- 抽帧:场景检测 + 信息密度打分 + 去重
|
|
17
|
+
- 画面文字识别:RapidOCR(可选,惰性加载,不装也能跑)
|
|
18
|
+
- 镜头 ↔ 口播对齐,生成 `preview.html` 调试视图
|
|
19
|
+
- LLM 语义分段、逐节写作、确定性质检与自动返工
|
|
20
|
+
- 单文件 HTML(图片内嵌)与 Markdown 双格式导出
|
|
21
|
+
|
|
22
|
+
**批量处理与人工校对(M4)**
|
|
23
|
+
|
|
24
|
+
- `v2doc batch`:顺序批量、单项失败隔离、`--skip-done` / `--stop-on-error`
|
|
25
|
+
- `v2doc status`:任务进度、失败项、`--json` 输出
|
|
26
|
+
- LLM token 用量按任务累计,写入 `job.json`
|
|
27
|
+
- `overrides.json` 覆盖层:改标题、导语、单节标题 / 正文 / 图片顺序
|
|
28
|
+
- `v2doc render`:不调用模型重新渲染
|
|
29
|
+
- `v2doc rewrite`:只重写一节,自动重跑质检和渲染
|
|
30
|
+
- `v2doc review`:本地浏览器校对界面(仅 `127.0.0.1`、令牌鉴权、无前端构建),保存即写覆盖层并重新渲染
|
|
31
|
+
|
|
32
|
+
**工程**
|
|
33
|
+
|
|
34
|
+
- 11 个阶段统一指纹缓存,支持 `--from` / `--force` / 断点续跑
|
|
35
|
+
- 150 个测试、`ruff` 检查、GitHub Actions(Ubuntu / Windows × Python 3.11 / 3.12)
|
|
36
|
+
- MIT License,约 1100 行中文[使用手册](docs/使用手册.md)
|
|
37
|
+
|
|
38
|
+
### 说明
|
|
39
|
+
|
|
40
|
+
- 多 P 视频只处理第一 P
|
|
41
|
+
- OCR 对代码符号的识别不可靠,发布前建议对照原片核对代码
|
|
42
|
+
- 语音识别时间戳有约 ±0.5 秒误差
|
|
43
|
+
- 纯人脸口播视频只能得到口播文字,没有可用的画面信息
|
|
44
|
+
- 写作质量取决于所选模型;30–60 分钟视频通常产生 8–15 次 LLM 调用
|
|
45
|
+
|
|
46
|
+
完整安装、配置、命令与排错说明见 [docs/使用手册.md](docs/使用手册.md)。
|
|
47
|
+
|
|
48
|
+
[0.1.0]: https://github.com/volicano/video2doc/releases/tag/v0.1.0
|
video2doc-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 volicano
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
video2doc-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: video2doc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Turn interview and tutorial videos into illustrated HTML tutorials.
|
|
5
|
+
Project-URL: Homepage, https://github.com/volicano/video2doc
|
|
6
|
+
Project-URL: Repository, https://github.com/volicano/video2doc
|
|
7
|
+
Project-URL: Issues, https://github.com/volicano/video2doc/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/volicano/video2doc/blob/main/CHANGELOG.md
|
|
9
|
+
Author: video2doc contributors
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: asr,bilibili,subtitle,tutorial,video,whisper,youtube
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Multimedia :: Video
|
|
21
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: httpx>=0.27
|
|
24
|
+
Requires-Dist: markdown-it-py>=3
|
|
25
|
+
Requires-Dist: numpy>=1.26
|
|
26
|
+
Requires-Dist: pillow>=10.2
|
|
27
|
+
Requires-Dist: pydantic>=2.6
|
|
28
|
+
Requires-Dist: rich>=13.7
|
|
29
|
+
Requires-Dist: typer>=0.12
|
|
30
|
+
Requires-Dist: yt-dlp>=2024.4.9
|
|
31
|
+
Provides-Extra: cloud
|
|
32
|
+
Requires-Dist: httpx>=0.27; extra == 'cloud'
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
36
|
+
Provides-Extra: local
|
|
37
|
+
Requires-Dist: faster-whisper>=1.0.3; extra == 'local'
|
|
38
|
+
Provides-Extra: ocr
|
|
39
|
+
Requires-Dist: rapidocr-onnxruntime>=1.3.24; extra == 'ocr'
|
|
40
|
+
Description-Content-Type: text/markdown
|
|
41
|
+
|
|
42
|
+
# video2doc
|
|
43
|
+
|
|
44
|
+
把**面试 / 教学视频**转成**带配图的图文教程**。
|
|
45
|
+
|
|
46
|
+
输入一条 B 站 / YouTube 链接或本地视频,输出一份可以直接发布的学习笔记:结构化的章节、可跳回原片的时间戳、关键画面截图。
|
|
47
|
+
|
|
48
|
+
[](https://github.com/volicano/video2doc/actions/workflows/ci.yml)
|
|
49
|
+
[](LICENSE)
|
|
50
|
+
[](pyproject.toml)
|
|
51
|
+
|
|
52
|
+
> **项目状态:M4 完成(批量处理、人工改稿、本地校对界面均已可用)**
|
|
53
|
+
> 当前可用:视频下载 → 音轨抽取 → 字幕复用 / 语音识别 → 可选的术语校正 → 抽帧 → 画面文字识别 → 图文对齐 → LLM 语义分段 → 逐节写作 → 质检 → **单页 HTML 教程**;批量跑批、单节重写、人工覆盖层、不调模型的重新渲染,以及浏览器里的可视化校对(`v2doc review`)也已就绪。
|
|
54
|
+
> 下一步:更多 ASR、说话人分离与模板系统(M5)。
|
|
55
|
+
>
|
|
56
|
+
> 📖 **完整的安装、配置、命令与排错说明见 [docs/使用手册.md](docs/使用手册.md)。**
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 为什么不是「把视频丢给大模型」?
|
|
61
|
+
|
|
62
|
+
因为那样得到的东西流畅但空洞:图和文对不上,时间戳是编的,关键细节全丢。
|
|
63
|
+
|
|
64
|
+
video2doc 把这件事拆成一条**可缓存、可断点续跑的流水线**,LLM 只在三个地方出手:分段打标、写作、质检。其余全是确定性代码:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
视频链接 / 本地文件
|
|
68
|
+
├─ ingest 下载、探测时长、收集字幕 ✅ M1
|
|
69
|
+
├─ audio 抽 16kHz 单声道音轨 ✅ M1
|
|
70
|
+
├─ transcript 字幕优先,否则语音识别 ✅ M1
|
|
71
|
+
├─ correct 术语/错别字校正(可选) ✅ M1
|
|
72
|
+
├─ frames 场景检测 + 信息量打分 + 去重 ✅ M2
|
|
73
|
+
├─ ocr 提取画面文字 ✅ M2
|
|
74
|
+
├─ align 镜头 ↔ 口播对齐 + 调试图 ✅ M2
|
|
75
|
+
├─ segment 语义分段 + 章节打标 ✅ M3
|
|
76
|
+
├─ write 逐节写作,图随文走 ✅ M3
|
|
77
|
+
├─ critique 覆盖率 / 配图 / 长度检查 → 重写 ✅ M3
|
|
78
|
+
└─ render 单文件 HTML + Markdown 导出 ✅ M3
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
每一步的产物都落在 `<任务目录>/stages/*.json`,指纹一变才重跑。改了提示词不会重新下载视频,改了模型不会重新抽帧。
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 安装
|
|
86
|
+
|
|
87
|
+
需要 **Python 3.11+** 和 **ffmpeg**(`ffmpeg`、`ffprobe` 都要在 PATH 里)。
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# 本地语音识别 + 画面文字识别(离线、免费,推荐)
|
|
91
|
+
pip install -e ".[local,ocr]"
|
|
92
|
+
|
|
93
|
+
# 或者只用云端 ASR / 不要 OCR,不装 torch
|
|
94
|
+
pip install -e "."
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Windows 装 ffmpeg:
|
|
98
|
+
|
|
99
|
+
```powershell
|
|
100
|
+
winget install Gyan.FFmpeg
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
装完先自检:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
v2doc doctor
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
它会逐项告诉你 ffmpeg、yt-dlp、CUDA、API Key、网络连通性哪里有问题,以及怎么修。
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 快速开始
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# 1. 生成配置文件(可选,不生成也能跑)
|
|
117
|
+
v2doc init
|
|
118
|
+
|
|
119
|
+
# 2. 处理一条视频(需要 LLM 的 API Key,见下文)
|
|
120
|
+
v2doc run "https://www.bilibili.com/video/BVxxxxxxxxx"
|
|
121
|
+
|
|
122
|
+
# 3. 打开生成的图文教程
|
|
123
|
+
v2doc open output/BVxxxxxxxxx
|
|
124
|
+
|
|
125
|
+
# 4. 看转写结果
|
|
126
|
+
v2doc preview output/BVxxxxxxxxx
|
|
127
|
+
|
|
128
|
+
# 5. 看抽帧结果(时间、类型、每张图的文字)
|
|
129
|
+
v2doc frames output/BVxxxxxxxxx --open
|
|
130
|
+
|
|
131
|
+
# 6. 一次跑很多条(urls.txt 一行一个链接,# 开头是注释)
|
|
132
|
+
v2doc batch --file urls.txt --skip-done
|
|
133
|
+
|
|
134
|
+
# 7. 看所有任务的状态
|
|
135
|
+
v2doc status
|
|
136
|
+
|
|
137
|
+
# 8. 某一节不满意:只重写这一节(调一次模型)
|
|
138
|
+
v2doc rewrite output/BVxxxxxxxxx --section 4 --hint "这节重点是任务接管"
|
|
139
|
+
|
|
140
|
+
# 9. 手改 overrides.json 后重新出 HTML(不调用模型)
|
|
141
|
+
v2doc render output/BVxxxxxxxxx
|
|
142
|
+
|
|
143
|
+
# 10. 在浏览器里可视化校对:改标题 / 正文 / 配图,保存即重新渲染
|
|
144
|
+
v2doc review output/BVxxxxxxxxx
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
重复执行同一条命令会自动续跑;想强制重来加 `--force`,想只重跑某一步加 `--from audio`,只重写教程加 `--from write`。
|
|
148
|
+
|
|
149
|
+
`tutorial.html` 里想改标题、正文或配图,不用重跑模型。推荐直接用 `v2doc review` 打开本地校对界面(只监听 `127.0.0.1`,纯离线),保存时写 `overrides.json` 并自动重新渲染;也可以手写 `overrides.json` 后执行 `v2doc render`(详见[使用手册](docs/使用手册.md#128-手工修改成稿overridesjson))。
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
output/BVxxxxxxxxx/
|
|
153
|
+
├─ tutorial.html # 最终产物:单文件图文教程(图片已内嵌)
|
|
154
|
+
├─ tutorial.md # 同一份内容的 Markdown,图片用相对路径
|
|
155
|
+
├─ job.json # 任务状态、配置快照、每步指纹
|
|
156
|
+
├─ overrides.json # 人工覆盖层(可选,流水线不写)
|
|
157
|
+
├─ preview.html # 抽帧 / OCR / 对齐的调试图
|
|
158
|
+
├─ media/ # 原视频、音频、字幕
|
|
159
|
+
├─ frames/ # 选中的原始画面(jpg)
|
|
160
|
+
└─ stages/
|
|
161
|
+
├─ ingest.json
|
|
162
|
+
├─ audio.json
|
|
163
|
+
├─ transcript.json # 带时间戳的句子
|
|
164
|
+
├─ frames.json # 选帧结果 + 信息密度曲线
|
|
165
|
+
├─ ocr.json # 每张图的文字块
|
|
166
|
+
├─ align.json # (图, 画面文字, 这段口播)
|
|
167
|
+
├─ segment.json # 章节边界(句子编号 + 标题)
|
|
168
|
+
├─ write.json # 每节 Markdown + 配图映射
|
|
169
|
+
├─ critique.json # 质检结果
|
|
170
|
+
└─ render.json # 产物路径与大小
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 配置
|
|
176
|
+
|
|
177
|
+
`v2doc init` 生成的 `video2doc.toml` 里所有字段都有默认值,只需要写你想改的。
|
|
178
|
+
|
|
179
|
+
### 本地识别(默认)
|
|
180
|
+
|
|
181
|
+
```toml
|
|
182
|
+
[asr]
|
|
183
|
+
provider = "faster-whisper"
|
|
184
|
+
model = "large-v3" # 显存吃紧就换 medium / small
|
|
185
|
+
language = "zh"
|
|
186
|
+
device = "auto" # 自动选 cuda,没有就 cpu
|
|
187
|
+
compute_type = "auto" # cuda→float16, cpu→int8
|
|
188
|
+
hotwords = ["闭包", "垃圾回收", "时间复杂度", "AQS", "CAS"]
|
|
189
|
+
auto_hotwords = true # 从视频标题和标签自动补充热词
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`hotwords` 是目前性价比最高的一个开关:中文技术术语(尤其是缩写和英文标识符)本来就是 ASR 的重灾区,把它们塞进 `initial_prompt` 几乎零成本。
|
|
193
|
+
|
|
194
|
+
### 术语校正(可选)
|
|
195
|
+
|
|
196
|
+
小模型(`tiny` / `small`)在中文技术内容上常把「闭包」听成「弊包」、「垃圾回收」听成「拉机」。开启校正后,会用 LLM 结合词表只修错别字,**不改写、不改时间戳**:
|
|
197
|
+
|
|
198
|
+
```toml
|
|
199
|
+
[asr]
|
|
200
|
+
correct_with_llm = true
|
|
201
|
+
glossary = ["闭包", "词法环境", "垃圾回收"] # 留空则复用 hotwords
|
|
202
|
+
|
|
203
|
+
[llm]
|
|
204
|
+
provider = "openai-compatible"
|
|
205
|
+
base_url = "https://api.deepseek.com/v1"
|
|
206
|
+
model = "deepseek-chat"
|
|
207
|
+
api_key_env = "DEEPSEEK_API_KEY"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
> **默认关闭。** 开启意味着字幕文本会被发送到 LLM 服务商,介意隐私就保持关闭。
|
|
211
|
+
> 校正只会应用长度变化在 0.5–1.6 倍之间的修改,防止模型自由发挥。
|
|
212
|
+
|
|
213
|
+
### 云端识别
|
|
214
|
+
|
|
215
|
+
任何兼容 OpenAI `/audio/transcriptions` 的服务都能用,改三个字段即可:
|
|
216
|
+
|
|
217
|
+
```toml
|
|
218
|
+
[asr]
|
|
219
|
+
provider = "openai"
|
|
220
|
+
model = "whisper-1"
|
|
221
|
+
base_url = "https://api.openai.com/v1"
|
|
222
|
+
api_key_env = "OPENAI_API_KEY"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```powershell
|
|
226
|
+
$env:OPENAI_API_KEY = "sk-..."
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
> 注意:配图对齐依赖**时间戳**。部分兼容服务只返回纯文本,那样后续的图文对齐会退化,`v2doc` 会在日志里警告。
|
|
230
|
+
|
|
231
|
+
### 抽帧与 OCR
|
|
232
|
+
|
|
233
|
+
抽帧不追求「每秒一张」,而是找**信息量最大且值得配图**的画面:
|
|
234
|
+
|
|
235
|
+
```toml
|
|
236
|
+
[frames]
|
|
237
|
+
strategy = "info-density" # uniform 就是均匀抽,用来做对比
|
|
238
|
+
max_frames = 40 # 一条 30~60 分钟的视频够用了
|
|
239
|
+
min_gap_sec = 8 # 两张图至少隔 8 秒,避免同一页 PPT 截三张
|
|
240
|
+
change_threshold = 0.10 # 相邻帧差异超过 10% 就算换镜头
|
|
241
|
+
hash_threshold = 12 # 白底 PPT 换页时靠感知哈希补刀
|
|
242
|
+
min_shot_score = 0.04 # 低于这个分数基本是纯人脸口播,不配图
|
|
243
|
+
coverage_buckets = 8 # 把时间轴切 8 段,每段至少留一张
|
|
244
|
+
|
|
245
|
+
[ocr]
|
|
246
|
+
provider = "auto" # auto | rapidocr | none
|
|
247
|
+
min_confidence = 0.5 # 低于这个置信度的文字块丢弃
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
选中哪些帧,看 `<任务目录>/preview.html`:上面是信息密度曲线,下面是每张图的 OCR 文字和这段时间的口播。如果某段该有图却没有,通常是 `min_shot_score` 太高或者采样太粗。
|
|
251
|
+
|
|
252
|
+
OCR 默认用 `rapidocr-onnxruntime`(PP-OCR 模型的 ONNX 版):不用装 paddlepaddle,Windows 上装得动,完全离线。没装也不会让流水线失败,只是 `align` 阶段少一维画面文字。
|
|
253
|
+
|
|
254
|
+
### 写作与渲染
|
|
255
|
+
|
|
256
|
+
M3 会把转写发给 LLM(默认 DeepSeek)。分段只让模型回答「从第几句开始、标题是什么」,**时间戳一律由代码按句子编号换算**;写作时把这一节的口播、配图及 OCR 文字一起交给它,并要求只使用材料里的信息。
|
|
257
|
+
|
|
258
|
+
```toml
|
|
259
|
+
[llm]
|
|
260
|
+
provider = "openai-compatible"
|
|
261
|
+
base_url = "https://api.deepseek.com/v1"
|
|
262
|
+
model = "deepseek-chat"
|
|
263
|
+
api_key_env = "DEEPSEEK_API_KEY"
|
|
264
|
+
max_tokens = 8192 # 单节正文被截断时调大
|
|
265
|
+
|
|
266
|
+
[write]
|
|
267
|
+
enabled = true
|
|
268
|
+
language = "auto" # auto | zh | en
|
|
269
|
+
max_sections = 12 # 超出时合并相邻最短的两节
|
|
270
|
+
min_section_sec = 60 # 太短的段落不单独成节
|
|
271
|
+
max_section_sec = 600 # 单节超过 10 分钟就按停顿再切一刀
|
|
272
|
+
max_images_per_section = 4
|
|
273
|
+
review = false # 质检不过时让模型重写那一节(额外花费)
|
|
274
|
+
# 本地 8k 上下文的模型建议调小下面两项
|
|
275
|
+
# chunk_utterances = 260
|
|
276
|
+
# max_section_chars = 6000
|
|
277
|
+
|
|
278
|
+
[render]
|
|
279
|
+
theme = "light" # light | dark
|
|
280
|
+
embed_images = true # base64 内嵌,产出单个可分享的 HTML
|
|
281
|
+
show_toc = true
|
|
282
|
+
image_captions = false # 图注显示 OCR 画面文字;录屏类视频噪声大,默认关
|
|
283
|
+
# title = "自定义教程标题"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
本地模型(Ollama / LM Studio / vLLM)只要兼容 `/chat/completions`:
|
|
287
|
+
|
|
288
|
+
```toml
|
|
289
|
+
[llm]
|
|
290
|
+
base_url = "http://127.0.0.1:11434/v1"
|
|
291
|
+
model = "qwen2.5:14b"
|
|
292
|
+
api_key_env = "" # 空字符串 = 不发 Authorization
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
不想把转写发到云端就设 `write.enabled = false`:前七个阶段的产物照样保留,只是不生成教程。没配密钥时这四个阶段也会自动跳过并说明原因,不会丢掉已经跑完的下载和识别结果。
|
|
296
|
+
|
|
297
|
+
`tutorial.html` 是自包含的单文件(CSS、图片都在里面),可以直接发给别人或存档;`tutorial.md` 用相对路径引用 `frames/`,适合继续手工编辑。
|
|
298
|
+
|
|
299
|
+
### 网络
|
|
300
|
+
|
|
301
|
+
```toml
|
|
302
|
+
[network]
|
|
303
|
+
proxy = "http://127.0.0.1:7890" # YouTube 直连不通时
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
需要登录才能看的视频:
|
|
307
|
+
|
|
308
|
+
```toml
|
|
309
|
+
[video]
|
|
310
|
+
cookies_from_browser = "chrome"
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## 设计取舍
|
|
316
|
+
|
|
317
|
+
**字幕优先于 ASR。** B 站和 YouTube 上大量教学视频自带 CC,质量和速度都优于自己转写,还省一次模型推理。`prefer_subtitles = true` 时,解析不出内容会自动回退到 ASR,不会卡住。
|
|
318
|
+
|
|
319
|
+
**缓存是围绕「指纹」做的。** 每个阶段的指纹 = hash(阶段名 + 版本 + 参数 + 源文件大小/mtime + 上游指纹)。所以:
|
|
320
|
+
|
|
321
|
+
- 改 `asr.model` → 只重跑识别;
|
|
322
|
+
- 改视频文件 → 全部重跑;
|
|
323
|
+
- 什么都没改 → 秒过。
|
|
324
|
+
|
|
325
|
+
**每个阶段的产物都是可校验的 JSON。** 用 pydantic 建模,上游改字段下游立刻报错,而不是默默算出垃圾。
|
|
326
|
+
|
|
327
|
+
**provider 用注册表,不写死在代码里。** 加一个新 ASR / OCR 服务 = 写一个类 + 一行 `@register`。
|
|
328
|
+
|
|
329
|
+
**抽帧不用人脸检测,用边缘密度 + 肤色占比。** 教学视频需要区分的是「PPT / 代码 / 白板」和「人脸口播」。文字画面的高对比笔画会拉高边缘密度,人脸则是低频渐变;再加一个 YCbCr 肤色占比做否定判断就够了。这样不用为了一个 Haar 级联多背 60MB 的 OpenCV。选完帧再回原视频抽原图,避免为几千帧小图付全分辨率的磁盘和 OCR 成本。
|
|
330
|
+
|
|
331
|
+
**OCR 只跑选中的 40 张,而不是全片。** 一小时的视频逐帧 OCR 要几十分钟,收益却集中在那几十张稳定画面上。
|
|
332
|
+
|
|
333
|
+
**LLM 永远不碰时间戳。** 分段时给模型的是带全局编号的句子,它只回答「从第 117 句开始,标题是 X」;边界校验、最小时长、过长再切、过多合并全部由代码完成。写作时也不给模型看时间,只给这一节的文本和图片编号,图注和跳转链接在渲染时用原始时间生成。模型编不出对不上的 12:34。
|
|
334
|
+
|
|
335
|
+
**一张图只归一节。** 图片按时间重叠归给重叠最多的那一节,再按信息量取前 N 张、按时间编号。这样同一张截图不会在全文出现两次,`[[img:3]]` 永远指向正确的那张图。
|
|
336
|
+
|
|
337
|
+
**质检先跑确定性规则。** 太短、有图没引用、画面有代码却没有代码块、覆盖率不足——这些用规则就能发现,不花钱也不随机。只有用户显式打开 `write.review` 时,才会让模型重写严重出问题的那几节,且重写后长度明显缩水就保留原稿。
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## 路线图
|
|
342
|
+
|
|
343
|
+
- [x] **M1** 下载 / 音轨 / 字幕 / 语音识别 / 术语校正
|
|
344
|
+
- [x] **M2** 抽帧(场景检测 + 信息密度打分)、画面文字识别、镜头与口播对齐、调试图
|
|
345
|
+
- [x] **M3** LLM 语义分段与逐节写作、确定性质检、单文件 HTML + Markdown 导出
|
|
346
|
+
- [x] **M4** 人工校对与批量处理
|
|
347
|
+
- [x] 批量处理(`v2doc batch`)、任务状态(`v2doc status`)、token 用量统计
|
|
348
|
+
- [x] 单节重跑(`v2doc rewrite`)与人工覆盖层(`overrides.json` + `v2doc render`)
|
|
349
|
+
- [x] 本地校对界面(`v2doc review`):章节 / 正文 / 配图可视化编辑,保存即重新渲染
|
|
350
|
+
- [ ] **M5** 更多 ASR(通义听悟 / FunASR)、说话人分离、模板系统
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## 常见问题
|
|
355
|
+
|
|
356
|
+
**模型下载卡住 / 报 401、CAS Client Error**
|
|
357
|
+
|
|
358
|
+
huggingface 的 xet 加速在部分网络下会失败,关掉它,或者直接换镜像:
|
|
359
|
+
|
|
360
|
+
```powershell
|
|
361
|
+
$env:HF_HUB_DISABLE_XET = "1"
|
|
362
|
+
$env:HF_ENDPOINT = "https://hf-mirror.com" # 国内网络
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**B 站视频下载失败**
|
|
366
|
+
|
|
367
|
+
高清和部分视频需要登录态,用浏览器的 cookie:
|
|
368
|
+
|
|
369
|
+
```toml
|
|
370
|
+
[video]
|
|
371
|
+
cookies_from_browser = "chrome"
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**YouTube 连不上**
|
|
375
|
+
|
|
376
|
+
```toml
|
|
377
|
+
[network]
|
|
378
|
+
proxy = "http://127.0.0.1:7890"
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**识别出来的术语错得离谱**
|
|
382
|
+
|
|
383
|
+
先换大模型(`large-v3`),再开 `correct_with_llm` 并给一份 `glossary`。小模型(`tiny`)+ 中文技术内容,不校正基本不能用。
|
|
384
|
+
|
|
385
|
+
**同一个视频跑了两遍,结果不一样**
|
|
386
|
+
|
|
387
|
+
说明命中了不同的缓存路径。看 `job.json` 里每阶段的 `fingerprint`,或者直接加 `--force` 全量重跑。
|
|
388
|
+
|
|
389
|
+
## 已知限制
|
|
390
|
+
|
|
391
|
+
- 只处理视频的第一 P(多 P 视频会在日志里提示)。
|
|
392
|
+
- 画质低于 720p 时,代码类画面的 OCR 效果会明显下降。
|
|
393
|
+
- OCR 会把代码符号认错(实测 `outer()` 会变成 `outer0`)。写作提示词要求给代码块加注「代码为画面识别结果,符号可能有误」,但模型偶尔会漏,发布前请对照原片核对代码。
|
|
394
|
+
- 写作质量取决于所用模型。一次 30~60 分钟的视频通常有 8~15 次 LLM 调用;本地小模型能跑通,但结构和文字质量明显不如云端大模型。
|
|
395
|
+
- 本地视频的标题就是文件名,想要好看点用 `[render] title = "..."` 覆盖。
|
|
396
|
+
- 语音识别的时间戳有 ±0.5s 误差,对齐时留了缓冲。
|
|
397
|
+
- 横屏、有稳定 PPT / 代码画面的教学视频效果最好;全程人脸口播的视频只能拿到口播文字。
|
|
398
|
+
- 没有任何自动发布能力,产出就是文件。
|
|
399
|
+
|
|
400
|
+
## 合规
|
|
401
|
+
|
|
402
|
+
本项目仅用于个人学习与内容整理。不存储、不二次分发原始视频;请遵守 B 站 / YouTube 的服务条款与版权规定。用别人的视频做二次创作前,请确认授权。
|
|
403
|
+
|
|
404
|
+
## License
|
|
405
|
+
|
|
406
|
+
MIT
|