@@ -275,7 +275,7 @@ def transcribe_api(video_path: Path, caption_path: Path) -> bool:
275275 f"≤{ _API_CHUNK_MINUTES } min (total { total_dur :.0f} s)" )
276276
277277 all_segments : list = []
278- detected_lang : str | None = None # set after first chunk; locked for all subsequent chunks
278+ chunk_langs : list [ str ] = [] # per- chunk detected language for majority vote
279279 lang_prob = 1.0
280280 total_dropped = 0
281281
@@ -289,9 +289,10 @@ def transcribe_api(video_path: Path, caption_path: Path) -> bool:
289289 _extract_audio (audio_path , chunk_file , start = start , duration = dur )
290290 chunk_mb = chunk_file .stat ().st_size / (1024 ** 2 )
291291
292- # Chunk 1: auto-detect (or use explicit setting).
293- # Chunk 2+: lock to the language detected from chunk 1 to prevent drift.
294- chunk_lang = WHISPER_LANGUAGE if i == 0 else (detected_lang or WHISPER_LANGUAGE )
292+ # When language is set explicitly, use it for every chunk.
293+ # When auto-detecting (None), let each chunk detect independently
294+ # so one bad first-chunk detection doesn't poison the whole file.
295+ chunk_lang = WHISPER_LANGUAGE
295296 print (f" { chunk_mb :.1f} MB lang={ chunk_lang or 'auto' } "
296297 f"sending to API..." , end = "" , flush = True )
297298
@@ -306,10 +307,10 @@ def transcribe_api(video_path: Path, caption_path: Path) -> bool:
306307 )
307308 elapsed = _time .monotonic () - t0
308309
309- # First chunk determines language for all subsequent chunks
310- if i == 0 :
311- lang_full = getattr ( response , "language" , "english" ) or "english"
312- detected_lang = _LANG_NAMES . get ( lang_full . lower (), lang_full [: 2 ]. lower () )
310+ # Collect per- chunk language for majority vote
311+ lang_full = getattr ( response , "language" , "english" ) or "english"
312+ chunk_detect = _LANG_NAMES . get ( lang_full . lower (), lang_full [: 2 ]. lower ())
313+ chunk_langs . append ( chunk_detect )
313314
314315 resp_dict = response .model_dump ()
315316 api_segs = resp_dict .get ("segments" , [])
@@ -337,16 +338,19 @@ def transcribe_api(video_path: Path, caption_path: Path) -> bool:
337338 all_segments .extend (segs )
338339
339340 drop_note = f" ({ n_dropped } dropped)" if n_dropped else ""
340- lang_note = ( f" lang={ detected_lang } " if i == 0 else "" )
341+ lang_note = f" lang={ chunk_detect } "
341342 print (f" done in { elapsed :.0f} s { len (segs )} segs{ drop_note } { lang_note } " )
342343
343- if detected_lang and i == 0 : # only one chunk — print lang info here
344- pass # already printed above
345- elif detected_lang :
346- print (f" Language locked to '{ detected_lang } ' from first chunk" )
344+ # Majority vote across chunks for the final language label
345+ from collections import Counter
346+ detected_lang = Counter (chunk_langs ).most_common (1 )[0 ][0 ] if chunk_langs else "en"
347+ if len (set (chunk_langs )) > 1 :
348+ print (f" Language votes: { dict (Counter (chunk_langs ))} → '{ detected_lang } '" )
349+ else :
350+ print (f" Detected language: '{ detected_lang } '" )
347351
348352 result = {
349- "language" : detected_lang or "en" ,
353+ "language" : detected_lang ,
350354 "language_probability" : lang_prob ,
351355 "duration" : round (total_dur , 3 ),
352356 "segments" : all_segments ,
0 commit comments