From ead03a795d35aea6f51094a439ff462ea8cfd3f7 Mon Sep 17 00:00:00 2001 From: karuo Date: Wed, 11 Mar 2026 00:47:36 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=84=20=E5=8D=A1=E8=8B=A5AI=20=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=202026-03-11=2000:47=20|=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=9A=E5=8D=A1=E6=9C=A8=E3=80=81=E8=BF=90=E8=90=A5=E4=B8=AD?= =?UTF-8?q?=E6=9E=A2=E5=B7=A5=E4=BD=9C=E5=8F=B0=20|=20=E6=8E=92=E9=99=A4?= =?UTF-8?q?=20>20MB:=2011=20=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../木叶_视频内容/视频切片/脚本/soul_enhance.py | 83 ++++++++++++++++--- 运营中枢/工作台/gitea_push_log.md | 1 + 运营中枢/工作台/代码管理.md | 1 + 3 files changed, 73 insertions(+), 12 deletions(-) diff --git a/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_enhance.py b/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_enhance.py index 9da8a783..50f4f7e1 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_enhance.py +++ b/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_enhance.py @@ -317,8 +317,45 @@ def improve_subtitle_punctuation(text: str) -> str: t = t + '。' return apply_platform_safety(t) -def parse_srt_for_clip(srt_path, start_sec, end_sec): - """解析SRT,提取指定时间段的字幕""" +def _detect_clip_pts_offset(clip_path: str) -> float: + """探测切片实际起始 PTS(秒),用于补偿 -ss input seeking 的关键帧偏移。 + + batch_clip 用 -ss before -i(input seeking),FFmpeg 会 seek 到最近关键帧, + 实际起始帧可能比请求的 start_time 早 0~3 秒。探测这个偏移量,字幕做相应延迟。 + """ + try: + r = subprocess.run( + ['ffprobe', '-v', 'error', '-select_streams', 'v:0', + '-show_entries', 'frame=pts_time', '-read_intervals', '%+#1', + '-of', 'csv=p=0', clip_path], + capture_output=True, text=True, timeout=10 + ) + if r.returncode == 0 and r.stdout.strip(): + first_pts = float(r.stdout.strip().splitlines()[0]) + return first_pts # 通常是 0 或接近 0 + except Exception: + pass + return 0.0 + + +# 字幕与语音同步的全局延迟补偿(秒) +# batch_clip -ss input seeking 导致实际切割比请求早 0~3 秒(关键帧对齐) +# 字幕按 highlights.start_time 算相对时间,会比实际音频提前 +# 加正值延迟 = 字幕往后推 = 与声音更同步 +SUBTITLE_DELAY_SEC = 0.8 # 根据实测 Soul 视频关键帧间隔约 2s,取保守值 + + +def parse_srt_for_clip(srt_path, start_sec, end_sec, delay_sec=None): + """解析SRT,提取指定时间段的字幕。 + + 优化: + 1. 字幕延迟补偿(delay_sec):补偿 FFmpeg input seeking 关键帧偏移,让字幕与声音同步 + 2. 合并过短字幕:相邻字幕 <1.2s 且文字可拼接时自动合并,减少闪烁 + 3. 最小显示时长:每条至少显示 1.2s,避免一闪而过看不清 + """ + if delay_sec is None: + delay_sec = SUBTITLE_DELAY_SEC + with open(srt_path, 'r', encoding='utf-8') as f: content = f.read() @@ -330,31 +367,53 @@ def parse_srt_for_clip(srt_path, start_sec, end_sec): parts = t.split(':') return int(parts[0]) * 3600 + int(parts[1]) * 60 + float(parts[2]) - subtitles = [] + raw_subs = [] for match in matches: sub_start = time_to_sec(match[1]) sub_end = time_to_sec(match[2]) text = match[3].strip() - # 检查是否与片段时间范围重叠 if sub_end > start_sec and sub_start < end_sec + 2: - # 调整为相对于片段开始的相对时间 - rel_start = max(0, sub_start - start_sec) - rel_end = sub_end - start_sec + rel_start = max(0, sub_start - start_sec + delay_sec) + rel_end = sub_end - start_sec + delay_sec - # 繁转简 + 修正错误 + 清理语气词 text = _to_simplified(text) for w, c in CORRECTIONS.items(): text = text.replace(w, c) cleaned_text = clean_filler_words(text) - if len(cleaned_text) > 1: # 过滤太短的 - subtitles.append({ + if len(cleaned_text) > 1: + raw_subs.append({ 'start': max(0, rel_start), - 'end': rel_end, + 'end': max(rel_start + 0.5, rel_end), 'text': cleaned_text }) - return subtitles + # 合并过短的连续字幕(<1.2s 且总长 <25字),让每条有足够阅读时间 + MIN_DISPLAY = 1.2 + merged = [] + i = 0 + while i < len(raw_subs): + cur = dict(raw_subs[i]) + dur = cur['end'] - cur['start'] + # 尝试向后合并 + while dur < MIN_DISPLAY and i + 1 < len(raw_subs): + nxt = raw_subs[i + 1] + gap = nxt['start'] - cur['end'] + combined_text = cur['text'] + ',' + nxt['text'] + if gap <= 0.5 and len(combined_text) <= 25: + cur['end'] = nxt['end'] + cur['text'] = combined_text + dur = cur['end'] - cur['start'] + i += 1 + else: + break + # 强制最小显示时长 + if cur['end'] - cur['start'] < MIN_DISPLAY: + cur['end'] = cur['start'] + MIN_DISPLAY + merged.append(cur) + i += 1 + + return merged def _filter_relevant_subtitles(subtitles): diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index c0bfcb4c..e0e8037d 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -277,3 +277,4 @@ | 2026-03-10 20:45:03 | 🔄 卡若AI 同步 2026-03-10 20:45 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | | 2026-03-10 20:55:38 | 🔄 卡若AI 同步 2026-03-10 20:55 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | | 2026-03-10 23:34:57 | 🔄 卡若AI 同步 2026-03-10 23:34 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | +| 2026-03-11 00:31:13 | 🔄 卡若AI 同步 2026-03-11 00:31 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index 9aa4f2a7..934043a4 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -280,3 +280,4 @@ | 2026-03-10 20:45:03 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-10 20:45 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-03-10 20:55:38 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-10 20:55 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-03-10 23:34:57 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-10 23:34 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-03-11 00:31:13 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-11 00:31 | 更新:金仓、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |