manifest.json里extract audio, transcribe audio 共用一条记录

This commit is contained in:
2026-09-17 08:49:16 +08:00
parent 922c3b4071
commit 44110a0ccf
3 changed files with 235 additions and 110 deletions

View File

@@ -47,7 +47,7 @@
- 支持指定语言(`--language zh/en/...`)或自动检测
- 支持音频**切分转录**:超长音频切分后逐段转录再合并
- 支持多种音频格式:`.mp3/.m4a/.wav/.flac/.ogg/.aac`
- **Manifest key 含模型维度**:多模型多参数结果共存,互不覆盖
- **Manifest 嵌套 transcriptions**:同一视频/音频记录里,多模型多参数结果按 `<model>[:<suffix>]` 为 subkey 共存,互不覆盖
---
@@ -344,55 +344,78 @@ python3 transcribe_audio.py /home/user/audio/ --retry-failed
## Manifest 文件说明
两个脚本**共享同一个 `manifest.json`**,通过 key 前缀区分:
两个脚本**共享同一个 `manifest.json`**(schema v2)。每个视频文件在 manifest 中**只有一条记录**,`extract_audio.py` 与 `transcribe_audio.py` 分别写入其中的 `extraction` 与 `transcriptions` 两个子对象,互不覆盖。
- **视频提取记录**:key = 视频绝对路径
- **音频转录记录**:key = `transcribe:<model>[:<suffix>]:<音频绝对路径>`
### key 选择规则
### key 结构详解
| Key | 来源 | 场景 |
|-----|------|------|
| 视频绝对路径(如 `/path/to/x.mp4`)| `extract_audio.py` 产生的记录 | 完整流水线(视频 → 音频 → 转录)|
| 音频绝对路径(如 `/path/to/x.mp3`)| 直接对 mp3 跑 `transcribe_audio.py` | 独立音频转录(无对应视频)|
| Key 格式 | 场景 |
|---------|------|
| `/path/to/x.mp4` | 视频提取记录 |
| `transcribe:tiny:/path/to/x.mp3` | tiny 模型转录(无 suffix) |
| `transcribe:medium:/path/to/x.mp3` | medium 模型转录(无 suffix) |
| `transcribe:base:_v1:/path/to/x.mp3` | base 模型 + `_v1` 后缀 |
| `transcribe:base:_v2:/path/to/x.mp3` | base 模型 + `_v2` 后缀 |
**反向查找**:当 `transcribe_audio.py` 处理某个 mp3 时,会先扫描 manifest,如果发现某条视频记录的 `extraction.audio_path` 等于该 mp3 路径,就把转录结果并入该视频记录,而不是新建 key。
多个 key 可并存于同一 manifest,互不覆盖。
### manifest.json 完整示例(含多模型对比)
### 完整示例(v2)
```json
{
"version": 1,
"version": 2,
"files": {
"/path/to/lesson1.mp4": {
"audio_extracted": true,
"audio_path": "/path/to/lesson1.mp3",
"split_segments": [],
"error": null,
"updated_at": "2026-08-23T10:30:00"
"extraction": {
"extracted": true,
"audio_path": "/path/to/lesson1.mp3",
"split_segments": [],
"error": null,
"updated_at": "2026-08-23T10:30:00"
},
"transcriptions": {
"tiny": {
"transcribed": true,
"txt_path": "/path/to/lesson1.tiny.txt",
"split_segments": [],
"model": "tiny",
"language": "zh",
"output_suffix": null,
"error": null,
"updated_at": "2026-08-23T10:35:00"
},
"medium": {
"transcribed": true,
"txt_path": "/path/to/lesson1.medium.txt",
"split_segments": [],
"model": "medium",
"language": "zh",
"output_suffix": null,
"error": null,
"updated_at": "2026-08-23T10:50:00"
},
"base:_v1": {
"transcribed": true,
"txt_path": "/path/to/lesson1_v1.txt",
"split_segments": [],
"model": "base",
"language": null,
"output_suffix": "_v1",
"error": null,
"updated_at": "2026-08-23T11:00:00"
}
}
},
"transcribe:tiny:/path/to/lesson1.mp3": {
"transcribed": true,
"txt_path": "/path/to/lesson1.tiny.txt",
"split_segments": [],
"model": "tiny",
"language": "zh",
"output_suffix": null,
"error": null,
"updated_at": "2026-08-23T10:35:00"
},
"transcribe:medium:/path/to/lesson1.mp3": {
"transcribed": true,
"txt_path": "/path/to/lesson1.medium.txt",
"split_segments": [],
"model": "medium",
"language": "zh",
"output_suffix": null,
"error": null,
"updated_at": "2026-08-23T10:50:00"
"/path/to/standalone.mp3": {
"extraction": null,
"transcriptions": {
"medium": {
"transcribed": true,
"txt_path": "/path/to/standalone.medium.txt",
"split_segments": [],
"model": "medium",
"language": "en",
"output_suffix": null,
"error": null,
"updated_at": "2026-08-23T11:15:00"
}
}
}
}
}
@@ -400,14 +423,18 @@ python3 transcribe_audio.py /home/user/audio/ --retry-failed
### 字段说明
**视频提取记录字段**
- `audio_extracted` - 是否已提取音频(布尔)
**顶层每条记录**
- `extraction` - `extract_audio.py` 的产物;`null` 表示没有走过提取(例如直接对 mp3 跑转录)
- `transcriptions` - `transcribe_audio.py` 的产物;以 `<model>` 或 `<model>:<suffix>` 为 subkey,允许同一音频用多种模型/参数并存
**`extraction` 字段**
- `extracted` - 是否已提取音频(布尔);`false` + 有 `error` 表示上次失败
- `audio_path` - 生成的 MP3 路径(切分模式下为第一个片段)
- `split_segments` - 切分片段列表(非切分模式为空)
- `split_segments` - 切分片段列表(非切分模式为空 `[]`)
- `error` - 失败时的错误信息,成功为 `null`
- `updated_at` - 最后更新时间(ISO 8601)
**音频转录记录字段**
**`transcriptions.<subkey>` 字段**
- `transcribed` - 是否已转录(布尔)
- `txt_path` - 生成的 TXT 路径
- `split_segments` - 切分片段列表
@@ -420,12 +447,18 @@ python3 transcribe_audio.py /home/user/audio/ --retry-failed
### 断点续传逻辑
脚本判断是否需要处理某文件时,**同时校验**:
1. manifest 中标记为已完成
1. manifest 中标记为已完成(`extraction.extracted=true` 或 `transcriptions.<sk>.transcribed=true`)
2. 目标文件实际存在且非空
**任一条件不满足则重新处理**(例如手动删除了 mp3 后重跑,会重新提取)。
**多模型模式**:每个 (音频 × 模型 × 后缀) 组合独立跟踪。已完成的组合被跳过,未完成的继续。
**多模型模式**:每个 (音频 × 模型 × 后缀) 组合独立跟踪,对应 `transcriptions` 里独立的 subkey。已完成的组合被跳过,未完成的继续。
### 从旧版 (v1) 升级
老版本使用扁平 schema:视频记录 key = 视频路径;转录记录 key = `transcribe:<model>[:<suffix>]:<音频路径>`。这些散落的记录在 v2 里会被合并到同一条视频记录里。
**升级方式**:脚本首次加载旧 manifest 时会打印警告并**以空 v2 schema 重新开始**(旧数据将被下次保存覆盖)。如果你想保留旧记录,运行升级前**先手动备份 manifest.json**。所有已提取的 mp3 和已转录的 txt 会在下次运行时被识别为"存在但 manifest 无记录"→ 重新处理并写入新 schema。
---