@@ -126,6 +126,22 @@ def run_whisper_words(wav_path: str) -> list:
126126 return words
127127
128128
129+ def _to_moshi_format (words : list , speaker : str | None = None ) -> dict :
130+ """
131+ Convert internal word-dict list to moshi-finetune alignment format:
132+ {"alignments": [[word_text, [start_sec, end_sec]], ...]}
133+ Speaker is omitted (no third element) when not provided — matches the
134+ format produced by annotate.py when speaker info is unavailable.
135+ """
136+ alignments = []
137+ for w in words :
138+ entry = [w ["word" ], [round (w ["start" ], 4 ), round (w ["end" ], 4 )]]
139+ if speaker is not None :
140+ entry .append (speaker )
141+ alignments .append (entry )
142+ return {"alignments" : alignments }
143+
144+
129145# ══════════════════════════════════════════════════════════════════════════════
130146# Step 1 — Transcribe full raw MP3
131147# ══════════════════════════════════════════════════════════════════════════════
@@ -140,7 +156,8 @@ def step1_transcribe(ep_num: int, mp3_path: str) -> list:
140156
141157 print (f" step1: { len (words )} words "
142158 f"{ words [0 ]['start' ]:.2f} s – { words [- 1 ]['end' ]:.2f} s" , flush = True )
143- json .dump (words , open (out_path , "w" , encoding = "utf-8" ), ensure_ascii = False )
159+ json .dump (_to_moshi_format (words ), open (out_path , "w" , encoding = "utf-8" ),
160+ ensure_ascii = False )
144161 print (f" step1: saved → { out_path } " , flush = True )
145162 return words
146163
@@ -813,24 +830,25 @@ def step4_verify(ep_num: int) -> None:
813830
814831 # ── Transcribe stripped MP3 → w3 ─────────────────────────────────────────
815832 print (f" step4: transcribing stripped MP3 → w3…" , flush = True )
816- w3 = run_whisper_words (stripped_path )
817- json .dump (w3 , open (w3_path , "w" , encoding = "utf-8" ), ensure_ascii = False )
818- print (f" step4: { len (w3 )} words saved → { w3_path } " , flush = True )
833+ w3_words = run_whisper_words (stripped_path )
834+ json .dump (_to_moshi_format (w3_words ), open (w3_path , "w" , encoding = "utf-8" ),
835+ ensure_ascii = False )
836+ print (f" step4: { len (w3_words )} words saved → { w3_path } " , flush = True )
819837
820838 # ── Compare w2 vs w3 ──────────────────────────────────────────────────────
821- # w2 is in moshi-finetune format: {"alignments": [[text, [start, end], spk], ...]}
822- w2_raw = json .load (open (w2_path , encoding = "utf-8" ))
823- w2_alignments = w2_raw ["alignments" ] # list of [text, [start, end], speaker]
839+ # Both in moshi-finetune format: {"alignments": [[text, [start, end], ...], ...]}
840+ w2_alignments = json .load (open (w2_path , encoding = "utf-8" ))["alignments" ]
824841
825- w3_norm = [((norm_words (w ["word" ]) or ["" ])[0 ]) for w in w3 ]
826- w3_start = [w ["start" ] for w in w3 ]
842+ w3_norm = [((norm_words (w ["word" ]) or ["" ])[0 ]) for w in w3_words ]
843+ w3_start = [w ["start" ] for w in w3_words ]
827844
828845 MATCH_WIN = 5.0 # ±s around w2 time to search for w3 counterpart
829846 n_match = n_miss = 0
830847 deltas : list = []
831848 large : list = []
832849
833- for text , (t2 , _t2e ), _spk in w2_alignments :
850+ for entry in w2_alignments :
851+ text , (t2 , _t2e ) = entry [0 ], entry [1 ]
834852 key = (norm_words (text ) or ["" ])[0 ]
835853 if not key :
836854 continue
@@ -932,7 +950,10 @@ def process_episode(ep_num: int, transcript_path: str, mp3_path: str,
932950 # ── Step 1 ────────────────────────────────────────────────────────────────
933951 w1_path = os .path .join (WHISPER_CACHE , f"ep{ ep_num } _w1.json" )
934952 if os .path .exists (w1_path ):
935- w1 = json .load (open (w1_path , encoding = "utf-8" ))
953+ # w1 on disk is in moshi format; convert back to internal dict list
954+ raw = json .load (open (w1_path , encoding = "utf-8" ))
955+ w1 = [{"word" : e [0 ], "start" : e [1 ][0 ], "end" : e [1 ][1 ], "p" : 1.0 }
956+ for e in raw ["alignments" ]]
936957 print (f"\n [Step 1] cached — { len (w1 )} words from { w1_path } " , flush = True )
937958 else :
938959 print (f"\n [Step 1] Transcribe full MP3" , flush = True )
0 commit comments