Add pipeline code

This commit is contained in:
Grok Bot
2026-10-17 15:27:14 -05:00
parent 3cd5a4c658
commit 5f0e2a9768
21 changed files with 1612 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
"""Stage 4, narration script. One JSON file per audio file, Audible style:
opening-credits, chapter-NN (one chapter per file), closing-credits, retail-sample.
A script is a list of segments {kind, added, text}. Author text is verbatim (added=false).
Everything else is added=true. Each inserted part (preview, summary) is wrapped:
cue chime-start, label, text, cue chime-end."""
import json
import os
import bookio
import llm
import structure
LABELS = {"chapter_preview": "Chapter preview", "chapter_summary": "Chapter summary", "concept_summary": "Concept summary"}
PROMPTS = {
"chapter_preview": "In two sentences of plain spoken English, say what this chapter is about. Use only what the text says.",
"chapter_summary": "In three sentences of plain spoken English, summarize this chapter. Use only what the text says.",
"concept_summary": "In two sentences of plain spoken English, restate the main concept in this passage. Use only what the text says.",
}
def switch(cfg, book, chapter, name):
"""Chapter switch beats book switch beats settings default."""
for source in (chapter.get("switches", {}), book.get("summaries", {})):
if source.get(name) is not None:
return bool(source[name])
return bool(cfg["summaries"][name])
def seg(kind, text, added=True):
return {"kind": kind, "added": added, "text": text}
def inserted(kind, text):
return [seg("cue", "chime-start"), seg("label", LABELS[kind]), seg(kind, text), seg("cue", "chime-end")]
def spoken_header(ch):
n = ch["number"]
word = structure.WORDS[n - 1].title() if n <= len(structure.WORDS) else str(n)
return "Chapter %s. %s." % (word, ch["title"])
def credits(book, cfg, closing):
by = "written by %s" % book["author"]
if cfg.get("narrator"):
by += ", narrated by %s" % cfg["narrator"]
return [seg("credits", ("This is the end of %s, %s." if closing else "This is %s, %s.") % (book["title"], by))]
def chapter_script(cfg, book, ch, paragraphs):
segs = [seg("header", spoken_header(ch))]
full = "\n\n".join(paragraphs)
if switch(cfg, book, ch, "chapter_preview"):
segs += inserted("chapter_preview", llm.complete(cfg, PROMPTS["chapter_preview"], full))
mid = None
if switch(cfg, book, ch, "concept_summary"):
total, running = sum(len(p.split()) for p in paragraphs), 0
for i, p in enumerate(paragraphs):
running += len(p.split())
if running * 2 >= total:
mid = i
break
for i, p in enumerate(paragraphs):
segs.append(seg("author", p, added=False))
if i == mid:
segs += inserted("concept_summary", llm.complete(cfg, PROMPTS["concept_summary"], "\n\n".join(paragraphs[:i + 1])))
if switch(cfg, book, ch, "chapter_summary"):
segs += inserted("chapter_summary", llm.complete(cfg, PROMPTS["chapter_summary"], full))
return segs
def retail_sample(cfg, ch, paragraphs):
budget, segs, words = cfg["script"]["retail_sample_words"], [seg("header", spoken_header(ch))], 0
for p in paragraphs:
n = len(p.split())
if segs[1:] and words + n > budget:
break
segs.append(seg("author", p, added=False))
words += n
return segs
def write(book_dir, name, segs):
os.makedirs(os.path.join(book_dir, "script"), exist_ok=True)
with open(os.path.join(book_dir, "script", name + ".json"), "w", encoding="utf-8") as f:
json.dump(segs, f, indent=2, ensure_ascii=False)
f.write("\n")
def run(cfg, book_dir, book, chapter_numbers):
"""Writes script/*.json and the audio file list in book.json. Returns warnings."""
files = [("opening-credits", credits(book, cfg, False))]
chosen = [c for c in book["chapters"] if c["number"] in chapter_numbers]
for ch in chosen:
with open(os.path.join(book_dir, ch["text_file"]), encoding="utf-8") as f:
paragraphs = [p for p in f.read().split("\n\n") if p.strip()]
files.append(("chapter-%02d" % ch["number"], chapter_script(cfg, book, ch, [p.strip() for p in paragraphs])))
if ch is chosen[0]:
sample = ("retail-sample", retail_sample(cfg, ch, [p.strip() for p in paragraphs]))
files += [("closing-credits", credits(book, cfg, True)), sample]
for name, segs in files:
write(book_dir, name, segs)
book["audio_files"] = [n for n, _ in files]
return []