From e01e1c9d85b9f50c7c337bc74e123d37775be4f9 Mon Sep 17 00:00:00 2001 From: LauraGPT Date: Mon, 3 Aug 2026 05:48:32 +0000 Subject: [PATCH] fix: handle case-insensitive clip matching --- funclip/utils/trans_utils.py | 13 ++++++-- funclip/videoclipper.py | 7 +++-- tests/test_duplicate_text_matching.py | 43 +++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/funclip/utils/trans_utils.py b/funclip/utils/trans_utils.py index f21fea4..118eabb 100644 --- a/funclip/utils/trans_utils.py +++ b/funclip/utils/trans_utils.py @@ -8,6 +8,9 @@ import numpy as np PUNC_LIST = [',', '。', '!', '?', '、', ',', '.', '?', '!'] +ASCII_LOWER_TABLE = str.maketrans( + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz" +) def pre_proc(text): res = '' @@ -28,14 +31,18 @@ def pre_proc(text): def proc(raw_text, timestamp, dest_text, lang='zh'): # simple matching ld = len(dest_text.split()) + normalized_raw_text = raw_text.translate(ASCII_LOWER_TABLE) + normalized_dest_text = dest_text.translate(ASCII_LOWER_TABLE) mi, ts = [], [] offset = 0 while True: - fi = raw_text.find(dest_text, offset, len(raw_text)) + fi = normalized_raw_text.find( + normalized_dest_text, offset, len(normalized_raw_text) + ) ti = raw_text[:fi].count(' ') if fi == -1: break - offset = fi + ld + offset = fi + len(normalized_dest_text) mi.append(fi) ts.append([timestamp[ti][0]*16, timestamp[ti+ld-1][1]*16]) return ts @@ -129,4 +136,4 @@ def extract_timestamps(input_text): "2. [00:00:07,120-00:00:12,940] 啊,在这样一个我们叫寻美在这样的一个项目当中,我们把它跟乡村振兴去结合起来,利用我们的设计的能力。" "3. [00:00:13,240-00:00:25,620] 问我们自身员工的设设计能力,我们设计生态伙伴的能力,帮助乡村振兴当中,要希望把他的产品推向市场,把他的农产品把他加工产品推向市场的这样的伙伴做一件事情,") - print(extract_timestamps(text)) \ No newline at end of file + print(extract_timestamps(text)) diff --git a/funclip/videoclipper.py b/funclip/videoclipper.py index ccb6165..e3c3be0 100644 --- a/funclip/videoclipper.py +++ b/funclip/videoclipper.py @@ -306,7 +306,7 @@ def video_clip(self, offset_b, offset_e = 0, 0 # import pdb; pdb.set_trace() _dest_text = pre_proc(_dest_text) - ts = proc(recog_res_raw, timestamp, _dest_text.lower()) + ts = proc(recog_res_raw, timestamp, _dest_text) for _ts in ts: all_ts.append([_ts[0]+offset_b*16, _ts[1]+offset_e*16]) if len(ts) > 1 and offset_match: warning_messages.append( @@ -382,8 +382,9 @@ def video_clip(self, video_clip.write_videofile(clip_video_file, audio_codec="aac", temp_audiofile=temp_audio_file) self.GLOBAL_COUNT += 1 else: - clip_video_file = video_filename - message = "No period found in the audio, return raw speech. You may check the recognition result and try other destination text." + log_append + clip_video_file = None + message = "No period found in the video; no output was generated. You may check the recognition result and try other destination text." + log_append + logging.warning(message) srt_clip = '' return clip_video_file, message, clip_srt diff --git a/tests/test_duplicate_text_matching.py b/tests/test_duplicate_text_matching.py index 5bf1428..b93e659 100644 --- a/tests/test_duplicate_text_matching.py +++ b/tests/test_duplicate_text_matching.py @@ -86,6 +86,49 @@ def test_video_clip_keeps_all_repeated_matches_without_offsets(self, _generate_s self.assertEqual(len(output_clip.write_calls), 1) self.assertIn("2 periods found", message) + @patch( + "videoclipper.generate_srt_clip", + return_value=("", [((0.0, 0.2), "HELLO WORLD")], 1), + ) + def test_video_clip_matches_ascii_case_insensitively(self, _generate_srt): + clipper = VideoClipper(None) + clipper.lang = "en" + video = DummyVideo() + state = { + "recog_res_raw": "HELLO WORLD", + "timestamp": [[0, 100], [100, 200]], + "sentences": [], + "video": video, + "clip_video_file": "/tmp/case_clip.mp4", + "video_filename": "/tmp/case.mp4", + } + + output_path, message, _ = clipper.video_clip("hello world", 0, 0, state) + + self.assertIsNotNone(output_path) + self.assertEqual(video.subclip_calls, [(0.0, 0.2)]) + self.assertIn("1 periods found", message) + + def test_video_clip_returns_no_output_when_text_does_not_match(self): + clipper = VideoClipper(None) + clipper.lang = "en" + video = DummyVideo() + state = { + "recog_res_raw": "HELLO WORLD", + "timestamp": [[0, 100], [100, 200]], + "sentences": [], + "video": video, + "clip_video_file": "/tmp/no_match_clip.mp4", + "video_filename": "/tmp/no_match.mp4", + } + + output_path, message, subtitle = clipper.video_clip("missing", 0, 0, state) + + self.assertIsNone(output_path) + self.assertEqual(video.subclip_calls, []) + self.assertEqual(subtitle, "") + self.assertIn("No period found", message) + @patch("videoclipper.generate_srt_clip", return_value=("", [], 0)) def test_audio_clip_formats_offset_warning_for_repeated_matches( self, _generate_srt