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

@@ -56,13 +56,32 @@ def log(msg: str, level: str = "INFO"):
print(f"[{ts}] {prefix} {msg}", flush=True)
# ─── Manifest 操作 ─────────────────────────────────────────────────────────────
# ─── Manifest 操作 (v2 schema) ────────────────────────────────────────────────
#
# v2 schema:每个条目形如
# {
# "extraction": { "extracted": bool, "audio_path": str, "split_segments": [],
# "error": str|None, "updated_at": str },
# "transcriptions": {
# "<model>[:<suffix>]": { "transcribed": bool, "txt_path": str, ... }
# }
# }
# transcribe_audio.py 会把转录结果并入同一条视频记录的 transcriptions 里。
# ─────────────────────────────────────────────────────────────────────────────
MANIFEST_VERSION = 2
def load_manifest(manifest_path: Path) -> dict:
if manifest_path.exists():
with open(manifest_path, encoding="utf-8") as f:
return json.load(f)
return {"version": 1, "files": {}}
if not manifest_path.exists():
return {"version": MANIFEST_VERSION, "files": {}}
with open(manifest_path, encoding="utf-8") as f:
data = json.load(f)
# 旧版 manifest (v1) 直接废弃,从头开始(用户已明确接受)
if data.get("version", 1) < MANIFEST_VERSION:
log("检测到旧版 manifest.json (v1),将以 v2 schema 重新初始化 - 旧记录将被覆盖", "WARN")
return {"version": MANIFEST_VERSION, "files": {}}
return data
def save_manifest(manifest_path: Path, manifest: dict):
@@ -72,34 +91,41 @@ def save_manifest(manifest_path: Path, manifest: dict):
def get_file_entry(manifest: dict, video_path: str) -> dict:
"""获取或初始化某个视频文件的 manifest 条目"""
"""获取或初始化某个视频文件的 manifest 条目 (v2 schema)"""
if video_path not in manifest["files"]:
manifest["files"][video_path] = {
"audio_extracted": False,
"audio_path": None,
"split_segments": [], # 切分后的片段列表(若有)
"error": None,
"updated_at": None,
"extraction": None,
"transcriptions": {},
}
return manifest["files"][video_path]
entry = manifest["files"][video_path]
if "transcriptions" not in entry:
entry["transcriptions"] = {}
return entry
def mark_done(manifest: dict, manifest_path: Path, video_path: str,
audio_path: str, segments: list = None):
entry = get_file_entry(manifest, video_path)
entry["audio_extracted"] = True
entry["audio_path"] = audio_path
entry["split_segments"] = segments or []
entry["error"] = None
entry["updated_at"] = time.strftime("%Y-%m-%dT%H:%M:%S")
entry["extraction"] = {
"extracted": True,
"audio_path": audio_path,
"split_segments": segments or [],
"error": None,
"updated_at": time.strftime("%Y-%m-%dT%H:%M:%S"),
}
save_manifest(manifest_path, manifest)
def mark_failed(manifest: dict, manifest_path: Path, video_path: str, error: str):
entry = get_file_entry(manifest, video_path)
entry["audio_extracted"] = False
entry["error"] = error
entry["updated_at"] = time.strftime("%Y-%m-%dT%H:%M:%S")
prev = entry.get("extraction") or {}
entry["extraction"] = {
"extracted": False,
"audio_path": prev.get("audio_path"),
"split_segments": prev.get("split_segments") or [],
"error": error,
"updated_at": time.strftime("%Y-%m-%dT%H:%M:%S"),
}
save_manifest(manifest_path, manifest)
@@ -220,22 +246,25 @@ def show_status(manifest: dict, all_videos: list):
print("\n" + "=" * 60)
print("📊 提取音频进度状态")
print("=" * 60)
done = sum(1 for v in all_videos
if manifest["files"].get(v, {}).get("audio_extracted"))
failed = sum(1 for v in all_videos
if manifest["files"].get(v, {}).get("error"))
pending = len(all_videos) - done
done, failed = 0, 0
for v in all_videos:
ext = ((manifest["files"].get(v) or {}).get("extraction")) or {}
if ext.get("extracted"):
done += 1
elif ext.get("error"):
failed += 1
pending = len(all_videos) - done - failed
print(f" 总文件数 : {len(all_videos)}")
print(f" 已完成 : {done}")
print(f" 失败 : {failed}")
print(f" 待处理 : {pending}")
print("=" * 60)
for v in all_videos:
entry = manifest["files"].get(v, {})
if entry.get("audio_extracted"):
ext = ((manifest["files"].get(v) or {}).get("extraction")) or {}
if ext.get("extracted"):
status = "✅ 完成"
elif entry.get("error"):
status = f"❌ 失败: {entry['error']}"
elif ext.get("error"):
status = f"❌ 失败: {ext['error']}"
else:
status = "⏳ 待处理"
print(f" {status} {Path(v).name}")
@@ -292,9 +321,10 @@ def main():
pending = []
for v in all_videos:
entry = get_file_entry(manifest, v)
already_done = entry.get("audio_extracted") and entry.get("audio_path") and \
mp3_exists(entry["audio_path"])
is_failed = bool(entry.get("error"))
ext = entry.get("extraction") or {}
already_done = ext.get("extracted") and ext.get("audio_path") and \
mp3_exists(ext["audio_path"])
is_failed = bool(ext.get("error"))
if already_done:
continue