@@ -259,21 +259,67 @@ def _bootstrap_offset(whisper_words: list, transcript_segs: list,
259259 max_segs : int = 40 ,
260260 max_raw_t : float = 180.0 ) -> float | None :
261261 """
262- Estimate the initial raw_time − transcript_time offset by finding the
263- first 4-word exact run shared between the transcript (first max_segs
264- segments) and the whisper output (first max_raw_t seconds only).
265-
266- Constraining to the first max_raw_t seconds of whisper prevents a rare
267- false match deep in the audio from producing a wildly wrong initial offset.
262+ Estimate the initial raw_time − transcript_time offset.
263+
264+ Strategy (primary): for each of the first max_segs transcript segments
265+ with ≥4 words, run a true LCS search within the first max_raw_t seconds
266+ of whisper output. The first segment that scores above threshold gives
267+ the offset as: raw_start_of_match − transcript_seg_start.
268+
269+ This is better than a 4-gram exact scan because:
270+ - It tolerates whisper dropping the first 1-2 words of a segment (common
271+ at intro boundaries) — true LCS scores the remaining words correctly.
272+ - It scores the best-matching position in the window rather than the
273+ first exact 4-gram, so it's robust to accidental 4-gram repeats
274+ elsewhere in the audio.
275+ - Bounding to max_raw_t ensures we can never get a false offset from a
276+ repeated phrase deep in the episode.
277+
278+ Fallback: if LCS finds nothing, try an exact 4-gram scan in the same
279+ window (handles cases where all early segments are very short and LCS
280+ thresholds are never met).
268281
269282 Returns offset (float) or None if nothing found.
270283 """
271- w_norm = [((norm_words (w ["word" ]) or ["" ])[0 ]) for w in whisper_words ]
272- w_starts = [w ["start" ] for w in whisper_words ]
284+ w_starts_all = [w ["start" ] for w in whisper_words ]
273285
274- # Only search within the first max_raw_t seconds of whisper output
275- wi_limit = bisect .bisect_right (w_starts , max_raw_t )
286+ # Window: first max_raw_t seconds only
287+ hi_idx = bisect .bisect_right (w_starts_all , max_raw_t )
288+ if hi_idx == 0 :
289+ return None
276290
291+ w_window = whisper_words [:hi_idx ]
292+ w_starts = w_starts_all [:hi_idx ]
293+
294+ def _min_score (n : int ) -> float :
295+ if n <= 4 : return 0.75
296+ if n <= 6 : return 0.67
297+ if n <= 10 : return 0.60
298+ return 0.55
299+
300+ # ── Primary: LCS search across all early segments, pick earliest raw hit ─
301+ # We collect all candidates and return the one with the smallest raw_time,
302+ # because intro jingles/ads always precede content — the first content
303+ # segment to appear in whisper output anchors the offset most accurately.
304+ best_raw_t = float ("inf" )
305+ best_offset = None
306+ mid = w_starts [- 1 ] / 2.0
307+ for seg in transcript_segs [:max_segs ]:
308+ tn = norm_words (seg ["text" ])
309+ n = len (tn )
310+ t_start = float (seg ["start" ])
311+ if n < 4 :
312+ continue
313+ abs_s , abs_e , score = search_segment (
314+ tn , w_window , w_starts , 0 , mid , mid + 1.0 , _min_score (n ))
315+ if abs_s is not None and w_starts [abs_s ] < best_raw_t :
316+ best_raw_t = w_starts [abs_s ]
317+ best_offset = w_starts [abs_s ] - t_start
318+ if best_offset is not None :
319+ return best_offset
320+
321+ # ── Fallback: exact 4-gram scan ───────────────────────────────────────────
322+ w_norm = [((norm_words (w ["word" ]) or ["" ])[0 ]) for w in w_window ]
277323 for seg in transcript_segs [:max_segs ]:
278324 tn = norm_words (seg ["text" ])
279325 t_start = float (seg ["start" ])
@@ -282,12 +328,12 @@ def _bootstrap_offset(whisper_words: list, transcript_segs: list,
282328 continue
283329 for gi in range (len (tn ) - 3 ):
284330 gram = tn [gi :gi + 4 ]
285- for wi in range (min ( wi_limit , len (w_norm ) - 3 ) ):
331+ for wi in range (len (w_norm ) - 3 ):
286332 if w_norm [wi :wi + 4 ] == gram :
287333 word_frac = gi / len (tn )
288334 approx_gram_t = t_start + word_frac * seg_dur
289- offset = w_starts [wi ] - approx_gram_t
290- return offset
335+ return w_starts [wi ] - approx_gram_t
336+
291337 return None
292338
293339
@@ -331,7 +377,7 @@ def min_score(n: int) -> float:
331377 # ── Bootstrap offset via 4-gram exact scan ────────────────────────────────
332378 offset = _bootstrap_offset (whisper_words , transcript_segs )
333379 if offset is not None :
334- print (f" step2: bootstrap offset={ offset :+.2f} s (4-gram scan) " , flush = True )
380+ print (f" step2: bootstrap offset={ offset :+.2f} s" , flush = True )
335381 else :
336382 offset = 0.0
337383 print (f" step2: bootstrap failed, starting with offset=0.0" , flush = True )
0 commit comments