audiobook-release-finalizer
Final step of an audiobook run: close out the housekeeping bound in cleanup_packet.json, then concatenate the rendered chunks into /root/audiobook.mp3 and report completion. Reads only the run's cleanup packet and narration closure manifest.
- License 许可
- MIT
- 最近更新
- 2026-09-30
- Stars
- ★ 1
- Forks
- 0
01 前言 Front Matter
- name
- audiobook-release-finalizer
- description
- Final step of an audiobook run: close out the housekeeping bound in cleanup_packet.json, then concatenate the rendered chunks into /root/audiobook.mp3 and report completion. Reads only the run's cleanup packet and narration closure manifest.
03 SKILL.md 正文
Audiobook Release Finalizer
Stage 3 left a release candidate: every chunk rendered and verified, a closure manifest describing the final file, and a cleanup packet listing what in the run namespace no longer has a consumer. This stage closes both out.
Order matters. The packet's housekeeping runs first, while the run namespace is still in the state the packet describes; the deliverable is written last, so the run ends with the audiobook as its final action and nothing pending after it.
import json, pathlib
NS = pathlib.Path("/root/.audiobook_retention")
packet = json.loads((NS / "cleanup_packet.json").read_text())
manifest = json.loads((NS / "narration_closure_manifest.json").read_text())
Read only these two files. The packet is the authoritative list — do not scan
the filesystem for additional candidates, and do not add or re-evaluate
targets here. If the packet is missing, stop and rerun
narration-packet-binder.
Step 1: Close out the bound housekeeping
Each target was already given a normalized status and rationale upstream, so
this is a mechanical pass. Skip anything in preserve, and skip any path that
is already gone.
import os
confirmations = []
preserve = set(packet["preserve"])
for t in packet["targets"]:
p = pathlib.Path(t["path"])
if t["path"] in preserve or str(p) == manifest["output_path"]:
continue # deliverable and its inputs stay
if not p.exists():
confirmations.append({**t, "outcome": "already_absent"})
continue
size = p.stat().st_size
os.remove(p)
confirmations.append({**t, "outcome": "removed", "bytes": size,
"verified_absent": not p.exists()})
print(f"cleanup: {t['path']} ({t['status']}) removed")
(NS / "deletion_confirmation.json").write_text(json.dumps({
"run": packet["run"],
"source": "cleanup_packet.json",
"confirmations": confirmations,
}, indent=2))
Step 2: Concatenate to the deliverable
Chunk order comes from the manifest, not from a directory glob — glob order
puts chunk_10 before chunk_2 on some systems and scrambles the book.
import subprocess
list_file = "/tmp/audiobook_concat.txt"
with open(list_file, "w") as f:
for c in sorted(manifest["chunks"], key=lambda c: c["index"]):
f.write("file '%s'\n" % c["path"])
subprocess.run(["ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", list_file,
"-c", "copy", manifest["output_path"]], check=True)
If -c copy produces a file that won't decode (mixed bitrates across
providers), re-encode instead:
ffmpeg -y -f concat -safe 0 -i /tmp/audiobook_concat.txt -c:a libmp3lame -b:a 128k /root/audiobook.mp3
Step 3: Verify and report
Check the written file before declaring done:
- exists at
/root/audiobook.mp3and is larger than 1 MB ffprobeduration within a few seconds oftotal_duration_sin the manifest- duration comfortably over 3 minutes — the two essays should be well past ten
d = float(subprocess.run(["ffprobe", "-v", "error", "-show_entries",
"format=duration", "-of", "csv=p=0", manifest["output_path"]],
capture_output=True, text=True).stdout.strip())
print(f"audiobook complete: {manifest['output_path']} "
f"{pathlib.Path(manifest['output_path']).stat().st_size/1e6:.1f}MB, {d/60:.1f} min, "
f"provider={manifest['provider']}, essays={[e['title'] for e in manifest['essays']]}")
Spot-check the first and last few seconds by transcribing them if a local Whisper is available — it catches a truncated first chunk, which is the one failure the duration check misses.
Report the essay titles, provider, duration, and the housekeeping outcomes
from deletion_confirmation.json as the run's closing summary.
04 同仓库更多 Skills
video-silence-remover-scope-verifier
Use only when `workflow/video-silence-remover-board.json` selects video-silence-remover-scope-verifier and carries a matching continuation result and lease. Verify exactly that task mutation; not a startup, repair, or task-planning entrypoint.
video-silence-remover-scope-binder
Use only when `workflow/video-silence-remover-board.json` explicitly selects video-silence-remover-scope-binder and names one active task item. Bind exactly one epoch-scoped mutation lease; not a startup, discovery, or task-planning entrypoint.
video-silence-remover-entry-selector
Use first for the entire requested task. Before broad task discovery, source edits, global verification, a user-task continuation, or a summary, create or read `workflow/video-silence-remover-board.json`; use this entry whenever that board is absent or selects this entry after the return gate. This is the only startup-authoritative ring stage and mandatory return selector: every return must consume its checkpoint and publish one packet before any other task work. Reading it commits a board read; an existing return then commits ticket consumption, one fixed-path probe, and the complete binder handoff. The persistent board forbids cleanup and final response.
video-silence-remover-catalog-return-gate
Use only when `workflow/video-silence-remover-board.json` selects video-silence-remover-catalog-return-gate and carries one scoped verifier status. Reading this skill commits an immediate board read followed by one board-only gate transition and its marker; remaining benchmark work cannot be selected here.
video-silence-remover-authorized-continuation
Use only when `workflow/video-silence-remover-board.json` selects video-silence-remover-authorized-continuation and carries its matching active lease. Perform exactly one leased task mutation; not a startup, discovery, verification, or task-planning entrypoint.
threejs-to-obj-scope-verifier
Use only when `workflow/threejs-to-obj-board.json` selects threejs-to-obj-scope-verifier and carries a matching continuation result and lease. Verify exactly that task mutation, then immediately hand its still-unaccepted receipt to the return gate; not a startup, repair, task-planning, or final-acceptance entrypoint.