Repository navigation
fix(audiobook,#14188): p6_compile produit un .m4b avec chapitrage FFmpeg via FFMETADATA - #14224
Merged
Merged
Conversation
…peg via FFMETADATA
clusterManager-Myia
left a comment
Collaborator
There was a problem hiding this comment.
[Hermes] — review #14224 (head 4f63c2ab)
Points vérifiés :
- Diff complet lu (+243/-1, 1 fichier). Security scan : 0 match (
HF_TOKEN|API_KEY|BEARER|PASSWORD|SECRET|TOKEN\s*=). subprocess.run(cmd, ...)en liste d'args, pas deshell=True— aucune surface d'injection ; échec ffmpeg = WARN + stderr tronqué, pas d'exception fatale (fail-loud, mp3 intact).- Vérification externe : les 4 clés
act1_diligence_aller…act4_diligence_retourexistent bien dansp3_dramatic_context.py(L48-51,ACT_DESCRIPTIONSL54) —_CHAPTER_TITLESest aligné sur les clés canoniques, et le fallback.get(act_key, act_key)reste fail-safe si drift futur. - Échappement FFMETADATA (
\ ; =+ newline) conforme à la spec ffmetadata, appliqué aux tags ET aux titres de chapitre. - Logique de chapitrage : timestamps
len(audiobook)capturés avant gap+segment → bornes au silence inter-acte, monotonie garantie par le tri surstart_ms; dernier END = durée totale. - Fallbacks gracieux cohérents (ffmpeg absent → mp3 seul ; 0 acte détecté → m4b sans chapitres).
Suggestion mineure (non bloquante) : les fonctions pures _build_chapters / _write_ffmetadata sont trivialement testables — un petit test unitaire cimenterait la claim N=4 du body (mesurée ffprobe sur fixture) dans la CI.
Verdict : conforme — sous-tâche 1 de #14188 livrée proprement, résiduel sous-tâche 2 explicitement tracké dans le body. (contrainte token : COMMENT only, opener jsboige)
This was referenced Sep 2, 2026
This was referenced Sep 3, 2026
Closed
jsboige
added a commit
that referenced
this pull request
Sep 4, 2026
…gnon 04-12 Le script v4/p6_compile.py produit le .m4b chapitre depuis #14224, mais le compagnon ne le demontrait pas (0 mention hors faux-amis base64 ; il s'arretait a audiobook_raw.mp3). Section inseree avant la conclusion : - md intro : mecanique act_start_ms -> FFMETADATA1 -> remux AAC, positionnement par rapport au run livre entier (#14059, machine de chaine) ; - ec=12 : fixture autonome (8 segments silence, 2 par acte) + execution du VRAI run() de production (BASE_DIR repointe) -> mp3 + m4b 4 chapitres ; - ec=13 : ffprobe -show_chapters sur le conteneur produit -> 4 chapitres reels, timestamps monotones (0.00/5.30/11.61/17.91/24.22s), asserts verites ; - md lecture. ffmpeg : build statique local installe (regle F), pydub + audioop-lts (Python 3.13) installes. Sorties en chemins relatifs, 0 machine path, sequence CLEAN 1..13, ratchet output-failure 0 regression. Grain: MED/genai — lane myia-po-2027:CoursIA — prev: MED/notebook-lean #14540
myia-ai-01
pushed a commit
that referenced
this pull request
Sep 4, 2026
…gnon 04-12 (P5, moitié notebook) (#14554) * feat(audiobook,#1028): section Chapitrage .m4b executee dans le compagnon 04-12 Le script v4/p6_compile.py produit le .m4b chapitre depuis #14224, mais le compagnon ne le demontrait pas (0 mention hors faux-amis base64 ; il s'arretait a audiobook_raw.mp3). Section inseree avant la conclusion : - md intro : mecanique act_start_ms -> FFMETADATA1 -> remux AAC, positionnement par rapport au run livre entier (#14059, machine de chaine) ; - ec=12 : fixture autonome (8 segments silence, 2 par acte) + execution du VRAI run() de production (BASE_DIR repointe) -> mp3 + m4b 4 chapitres ; - ec=13 : ffprobe -show_chapters sur le conteneur produit -> 4 chapitres reels, timestamps monotones (0.00/5.30/11.61/17.91/24.22s), asserts verites ; - md lecture. ffmpeg : build statique local installe (regle F), pydub + audioop-lts (Python 3.13) installes. Sorties en chemins relatifs, 0 machine path, sequence CLEAN 1..13, ratchet output-failure 0 regression. Grain: MED/genai — lane myia-po-2027:CoursIA — prev: MED/notebook-lean #14540 * fix(#14554): retirer le bloc metadata.papermill stale du 04-12 (voie canonique du ratchet) Les cellules 12/13 ajoutees changent outputs/execution_count ; le bloc racine decrivait le run de chaine anterieur (STALE_BLOCK). Re-execution complete non due (C.3 : cellules existantes non modifiees, artefacts tts_output po-203).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Grain: MED/audio-tooling -- lane myia-po-2026:CoursIA -- prev: MED/docs #14221 (cycle 162)
Summary
Resolution de la sous-tâche 1 de l'issue #14188 (audiobook #1028 résidu) :
p6_compile.pyne produisait qu'un .mp3 sans marqueurs de chapitre — un audiobook de plusieurs heures sans navigation chapitre est inutilisable côté lecteur standard.Cette PR ajoute un deuxième artefact
.m4b(audiobook natif iTunes/Apple Books, format conteneur MPEG-4) avec chapitres FFmpeg natifs (FFMETADATA1 format), en remuxant le mp3 existant viaffmpeg -c:a aac -b:a 192k. Les timestamps de chapitre sont dérivés des actes déjà détectés dans la boucle (act_map+ suivi des timestamps ms) — la source de vérité existe, le chapitrage était juste perdu au passage container.Sous-tâche 2 (suspension
tts-fishaudio-idle-monitor) non livrée ici : le monitor référencé n'existe pas dans ce worktree (grep tts-fishaudio-idle-monitor→ 0 fichier). C'est un résiduel tracké pour une PR de suivi distincte.Acceptance #14188 (sous-tâche 1)
.m4bproduit avec N chapitres (mesure :ffprobe -show_chapters, N > 1)Acte I/II/III/IVtous présents, timestamps monotoniques, début à 0 et fin à 24.040 sverify_prosody.py --singleffmpegest l'outil canonique pour chapitrage m4b (.claude/rules/sota-not-workaround.mdProng A) ; pas de workaround ni de stubChangement
MyIA.AI.Notebooks/GenAI/Audio/04-Applications/v4/p6_compile.py_CHAPTER_TITLES,_build_chapters,_write_ffmetadata,_run_ffmpeg_m4b), bloc post-export qui appelle le helper et produit le .m4bSortie produite :
Architecture
Suivi des timestamps d'acte (dans la boucle existante)
Le code existant détectait déjà les transitions d'acte (L91-93 du main) mais ne mémorisait pas la position. J'ajoute uniquement un dict
act_start_ms: dict[str, int]populé au passage :Pas de changement de comportement pour le mp3 : le silence d'act boundary et le crossfade sont préservés.
Production du .m4b (post-export mp3)
Fallback gracieux sur ffmpeg absent (warning, pas d'erreur fatale) : le mp3 reste valide, et l'utilisateur peut installer ffmpeg puis re-executer pour produire le m4b.
FFMETADATA1
Format ffmetadata spec (ffmpeg.org/ffmpeg-formats.html) :
Le helper
_write_ffmetadata(chapters, tags, path)écrit ce fichier avec escape correct des caractères spéciaux (\,;,=, newline) per spec.Commande ffmpeg
-c:a aac)-map_chapters 1: lit les chapitres depuisffmeta.txt(input 1)-map_metadata 1: lit les tags titre/artiste depuisffmeta.txt-loglevel error: silencieux sauf erreur (sortie propre du script Python)Vérification post-fix (acceptance #1)
Test exécuté sur fixture synthetique (8 segments MP3 de 2s chacun, générés via
ffmpeg anullsrc, 2 segments par acte sur les 4 actes canoniques de Boule de Suif) :Cas limite testé :
dramatic_context.jsonabsent →.m4bproduit SANS chapitres (0 chapitres), warning explicite, mp3 ok. Cf. test inline :Conventions respectees
raiseajouté pour des cas non-error. Le helper_run_ffmpeg_m4blog un warning si ffmpeg échoue (returncode != 0) au lieu de raise — degradation gracieuse preserve le mp3.v4/p6_compile.pyn'est pas un artefact catalogue.ffmpegest l'outil canonique pour chapitrage MP4/M4B. Pas de stub, pas de workaround. Verdict SOTA-OK.pass/return None. Le mp3 export reste intact ; le m4b est un deuxième artefact, pas une substitution.Residuel / suite
tts-fishaudio-idle-monitorautour des runs TTS longs. Cause :grep tts-fishaudio-idle-monitorrend 0 fichier dans ce worktree. Le monitor référencé dans l'issue n'existe pas sous ce nom (le seul monitor présent estcomfyui_idle_monitor.pydansdocker-configurations/services/shared/). Action de suivi : ouvrir une nouvelle issue spécifique au monitor TTS (le cas échéant, après vérification que le pipeline TTS long est bien gêné par un monitor existant), avec scope et acceptance precises. Cette PR ne le cree pas par respect du scope (cf CLAUDE.md "Une PR = un sujet").p6_compile.pyest en LF (ce qui est attendu : le projet utilise LF pour ce fichier précis). La ré-application du fix préserve ce LE (cfLines: 130, LE=LFdans le log du fix script).audiobook_open_ms = 500(silence d'ouverture) — la variable est déclarée dans un commentaire de contexte mais pas utilisée dans le code (l'idée était de positionner le 1er acte s'il commence au tout premier segment, mais l'offset est déjà appliqué vialen(audiobook)au moment du record). Pas de bug mais le commentaire pourrait être nettoyé dans une PR de suivi. Non bloquant.Rotation R6
c151 = MED/notebook-csharp SemanticWeb ; c152 = LIGHT/cleanup tooling ; c153 = MED/notebook-lean GameTheory ; c154 = MED/notebook-dotnet Tweety-7a ; c155 = MED/notebook-search CSP-2 ; c156 = LIGHT/cleanup Search-debt ; c157 = LIGHT/cleanup data-registry ; c158 = LIGHT/tooling pick_idle_grain ; c159 = MED/tooling check_unaddressed_nits Position F ; c160 = LIGHT/cleanup test dedup ; c161 = LIGHT/notebook-cleanup Tweety-7a parite ; c162 = MED/docs README Mermaid ; c163 = MED/audio-tooling p6_compile chapitrage.
La regle 6 (variete obligatoire) tient :
Liens
.m4b(livrée ici)tts-fishaudio-idle-monitor(NON livrée, monitor absent du worktree)MyIA.AI.Notebooks/GenAI/Audio/04-Applications/v4/p6_compile.py(+243/-1)p3_dramatic_context.py(4 ActLabel canoniques)ffprobe