🔄 卡若AI 同步 2026-03-22 21:22 | 更新:Cursor规则、金仓、卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 11 个

This commit is contained in:
2026-03-22 21:22:03 +08:00
parent 98ec34ce50
commit a5060ae4a8
10 changed files with 113 additions and 44 deletions

View File

@@ -41,6 +41,7 @@ alwaysApply: true
- **终端命令**一律直接执行不询问50 字内说明后执行
- **常规操作**:优先命令行 + 复用现成流程,不提问
- **默认零提问(强制)**:开发、改需求、跑脚本、查日志、部署类任务,**禁止**向卡若发起「是否执行」「要不要我…」「请选一个」等确认式提问。缺信息时:**先读仓库配置 / 代码 / 环境变量 / 文档** → 合理默认 → **直接做完**。仅当 **客观上无法代劳** 时极简说明缺什么(如:本机短信验证码、支付密码、用户明文密钥未配置、明确不可逆删除且规范要求人工确认)。
- **Skill 更新后·流水线「下一步」= 立即开跑(强制)**:用户说「下一步」「接着跑」「从跑完下一步」「重新剪辑」「直到全部完成」「别问我直接执行」等,或**刚同步完**某条 Skill如 `Soul竖屏切片`、`木叶_视频切片`)后要继续产出时,视为已授权**连续执行**:按该 Skill 的标准步骤从**当前缺口**自动往下做(例:缺 `transcript.srt` 则先抽音频+MLX/Whisper再 `batch_clip`,再 `soul_enhance` 成片)。**第一步起就不要问**「要不要帮你跑」;用本机路径惯例(如 `~/Movies/soul视频/第N场_*_output/`、`卡若AI/03_卡木/.../视频切片/脚本`)与仓库内 `场次稿`/`highlights.json` 合理补全。仅当**客观阻塞**(如无原片文件、磁盘满、需用户独占凭证)时用**一句话**说明卡点,不展开选项问卷。
- **直接执行模式**:当用户明确要求「直接做 / 不要罗列 / 别讲写了什么」时,以**改代码与跑通为主**,正文**极简**可≤3句**复盘块仍放在回复最末且保持完整五块**(与 `soul-karuo-dialogue.mdc` 一致)。
- **飞书日志**:直接执行 `write_today_three_focus.py`,不询问
- **对外输出**:报告/图片 → `/Users/karuo/Documents/卡若Ai的文件夹/` 下对应子目录;图片登记 `图片/图片索引.md`

View File

@@ -1,5 +1,5 @@
{
"updated": "2026-03-22T06:29:42.195575+00:00",
"updated": "2026-03-22T13:21:51.214081+00:00",
"conversations": [
{
"对话ID": "9f39025b-f695-4d7b-aff7-c124226e307e",

View File

@@ -1,10 +1,10 @@
---
name: Soul竖屏切片
description: Soul 派对视频→竖屏成片;字幕暖色条+金边(与封面墨绿区分)、纠错词表+关键词高亮(同字号单行)、concat 前导空白对齐主轨时间轴、音轨PTS同步补偿;推荐 --typewriter-subs 逐字渐显。流程裁剪检查→soul_enhance。MLX→visual_enhance v8 可选。
description: Soul 派对视频→竖屏成片;高光**单主题完整**30s5min/条,每场约 830 条)、去穿插话题;封面底**约 10% 轻模糊**(非全糊)。字幕暖色条+金边、纠错+关键词高亮、concat 前导空白、音轨 PTS推荐 --typewriter-subs。流程裁剪检查→soul_enhance。MLX→visual_enhance v8 可选。
triggers: Soul竖屏切片、视频切片、热点切片、竖屏成片、派对切片、裁剪检查、重新截图、全画面标定、竖屏裁剪、全画面成片、letterbox、画面显示全、白边、飞书录屏、LTX、AI生成视频、Retake重剪、字幕优化、字幕同步、逐字字幕
owner: 木叶
group: 木
version: "1.9"
version: "2.1"
updated: "2026-03-22"
---
@@ -24,13 +24,17 @@ updated: "2026-03-22"
不再单独生成 `clips_enhanced``clips_竖屏`;成片由 `soul_enhance` 一步直出到 `成片/`
**卡若 Cursor 执行约定**:用户说「下一步 / 接着跑 / 重新剪辑 / 直到完成」或刚更新本 Skill 后要出片时Agent 按 `卡若AI/.cursor/rules/karuo-ai.mdc` **流水线续跑**条**直接执行**(从转录、切片到成片),**不先询问是否执行**。
---
## 二、视频结构:提问→回答 + 前3秒高光 + 去语助词
- **每个话题前均优先提问→回答**:先看片段有没有人提问;**有提问**则把**提问的问题**放到前3秒封面/前贴),先展示问题再播回答;无提问则用金句/悬念作 hook
- **成片链路**前3秒展示问题金句)→ 正片回答**整片去除语助词**(提问与回答部分均由 soul_enhance 清理)。
- **高光**按「3秒高光亮点」剪每段 30300 秒完整语义单元;高光识别若有提问须填 `question`,且 `hook_3sec` 与之一致
- **每个话题前均优先提问→回答**:先看片段有没有人提问;**有提问**则把**提问的问题**放到前3秒封面/前贴),先展示问题再播回答;无提问则从**该片段口播里**抽**吸睛句或反问**作 `hook_3sec`,禁止与正片内容脱节
- **成片链路**前3秒展示问题 hook)→ 正片展开**整片去除语助词**(提问与回答部分均由 soul_enhance 清理)。
- **节奏与高光**竖屏段内全程**高光清晰、节奏清楚**(剪去拖沓静音与跑题可在 enhance 链路的静音裁剪中体现;**话题边界以 highlights 起止为准,剪辑阶段就要裁干净**
- **单主题、完整、有趣****一条成片只服务一个主题**;口播里若**临时插进其他话题**,在 `highlights` 对应时间段上**剪掉无关段**(或拆成另一条高光),保证**有头有尾、逻辑闭环**;选题要**有传播点**(反差、数字、痛点、金句),忌又长又平。
- **高光**:按「约 3 秒开场」剪,每段 **30 秒5 分钟≤300 秒)**;若有提问须填 `question`,且 `hook_3sec` 与之一致或同气质。
详见:`参考资料/视频结构_提问回答与高光.md``参考资料/高光识别提示词.md`
@@ -111,14 +115,16 @@ python3 soul_enhance.py \
---
## 四、高光与切片30 秒300
## 四、高光与切片30 秒~5 分钟 · 单主题 · 830
| 项 | 规则 |
|----|------|
| **单段时长** | **30300 秒**,由完整片段起止决定 |
| **完整性** | 每段是一个完整话题/情节,有头有尾 |
| **标题** | **一句刺激性观点****46 个汉字**为宜(单行封面好读、主题一眼懂);忌长句当文件名 |
| **数量** | 建议 ≤10 /场 |
| **单段时长** | **30300 秒(不超过 5 分钟)**;不足 30s 的亮点可合并进同主题相邻段,或放弃;超过 5min 必须拆主题或砍无关穿插 |
| **单主题(硬规则)** | **一个 mp4 = 一个主题**,整段**完整**;中间任何**无关主题**的闲聊、插话、跑题段落一律**不收录**(在粗剪/高光表里收窄 `start_time`/`end_time` 或改切两条) |
| **完整性 + 有趣** | 每段**有头有尾**(观众能听懂结论/态度);同时标题与 hook 要**有张力**(数字、对比、反常识、痛点),避免「正确的废话」 |
| **条数/场** | 依内容密度 **约 830 /场**(平均常见十几条);宁少勿滥,保证每条都达标:单主题 + 时长窗口 + 有趣 |
| **时间戳** | `start_time`/`end_time` 必须以**整场 transcript.srt** 核对,避免「标题与画面」错位 |
| **标题** | **一句刺激性观点****46 个汉字**为宜(单行封面好读);忌长句当文件名 |
| **语助词** | 识别与剪辑须符合 `参考资料/高光识别提示词.md`,成片由 soul_enhance 统一去语助词 |
---
@@ -126,7 +132,9 @@ python3 soul_enhance.py \
## 五、成片:封面 + 字幕 + 竖屏
- **封面**:竖条画布内**不超出界面****半透明质感**(背景 alpha=165深色渐变、左上角 Soul logo**封面显示标题 = 成片文件名 = highlights.title**(去杠、去下划线后一致,无 `:|—/_`、无序号);标题严格居中、多行自动换行。透明度由 `VERTICAL_COVER_ALPHA` 调节。
- **字幕**:封面结束后先留**约 3 秒纯画面**(无字幕),再开始叠字幕;字幕**居中**在竖条内。先尝试**单次 FFmpeg 通道**(一次 pass 完成所有字幕叠加最快若失败自动回退到分批模式batch_size=40语助词在解析阶段已由 `clean_filler_words` 去除。重新加字幕时加 `--force-burn-subs`。⚠️ 注意:当前 FFmpeg 不支持 drawtext/subtitles 滤镜,只能用 PIL 图像 overlay 方案。(脚本常量:`SUBS_START_AFTER_COVER_SEC`,默认 3.0
- **封面底层模糊(重要)****不要全屏强糊**。`soul_enhance.py` 默认 **`STYLE['cover']['bg_blur_mix']=0.1`**:清晰视频帧与一层高斯模糊按 **约 10% 混合**`bg_blur_radius` 生成模糊层),界面仍大致可辨,仅轻微虚化衬托文字。若需更强/更弱,改脚本内两常量,勿回到「整帧 radius=50+ 全糊」。
- **字幕****封面一结束即叠字幕**无额外「空几秒再等字」SRT 安全起点为封面结束 + **约 0.05s** epsilon避免与最后一帧封面打架。字幕**居中**在竖条内。先尝试**单次 FFmpeg 通道**(一次 pass 完成所有字幕叠加最快若失败自动回退到分批模式batch_size=40语助词在解析阶段已由 `clean_filler_words` 去除。重新加字幕时加 `--force-burn-subs`。⚠️ 注意:当前 FFmpeg 不支持 drawtext/subtitles 滤镜,只能用 PIL 图像 overlay 方案。(脚本常量:`SUBS_START_AFTER_COVER_SEC`**默认 0.0**
- **字幕字形**Whisper 词级轴常在**中日文之间插空格**,逐字/逐词显字时会像「字与字被撑开」;脚本在 `improve_subtitle_punctuation` 路径对 **CJK 相邻空白**做折叠(`_collapse_cjk_interchar_spaces`),保证整句显示正常、无异常中空。
- **封面标题**:高光 `title` 建议 **46 个汉字**;成片内封面主标题最多显示 **6 个汉字**(超长由 `soul_enhance` 自动截断,与文件名 `--title-only` 一致)。
- **竖屏竖条****高固定 1080宽 = analyze 的 OUTPUT_SIZE**,默认不压 498细节见 `参考资料/竖屏中段裁剪参数说明.md`
@@ -137,8 +145,8 @@ python3 soul_enhance.py \
| **与封面对比** | 封面为**半透明墨绿渐变**;字幕为**暖深棕圆角条 + 琥珀色描边**,避免与主题绿混成一团 |
| **纠错** | `transcript.srt` 解析时走 `_improve_subtitle_text`繁转简、CORRECTIONS 错词、违禁替换、去语助词);**渲染每一帧前**再走 `improve_subtitle_punctuation`,与口播稿对齐 |
| **重点词** | `KEYWORDS` 列表命中则**亮金色高亮**(同字号同基线,仅颜色区分,避免大字号造成"两排字"),长词优先匹配 |
| **逐字渐显** | 推荐成片加 **`--typewriter-subs`**:同一条字幕时间内前缀逐步加长,更贴人声节奏 |
| **音画对齐** | 默认 `SUBTITLE_DELAY_SEC` + **音轨/视频首帧 PTS 差**按比例补偿(脚本内动态计算),减轻「字比声快」 |
| **逐字渐显** | 推荐成片加 **`--typewriter-subs`**:同一条字幕时间内前缀逐步加长,更贴人声节奏;配合 CJK 去空格避免字间假空白 |
| **音画对齐** | ① 切片起点与高光表一致(见上表 batch_clip。② 默认 `SUBTITLE_DELAY_SEC=0`;若 ffprobe 首包音/视频 PTS 差 > 阈值则加小延迟。③ 仍偏差时用 `soul_enhance.py --subtitle-extra-delay 0.15`(秒,正数推迟字幕)整场微调。④ 成片 **先叠字幕再 10% 加速**,字幕与对白同倍率,不因加速错位。 |
### ⚠️ 字幕烧录常见坑(已修复)
@@ -147,12 +155,15 @@ python3 soul_enhance.py \
| 字幕全跳过(转录稿异常误判) | `_parse_clip_index` 取到场次号(如 119而非切片序号01导致 highlight_info 为空start_sec=0 落入噪声区 | 改为取 `_数字_` 模式中**最小值**119→01=1 ✓ |
| 标题/文件名有下划线 | `sanitize_filename` 保留了 `_` | 现在 `_` 也替换为空格 |
| 字幕烧录极慢N/5 次 encode | 原 batch_size=5180 条字幕需 36 次 FFmpeg 重编码 | 改为单次通道1 次 pass失败时 batch_size=40 兜底 |
| **字幕超前于说话(字幕比声音早)** | `batch_clip -ss` 输入端 seeking 导致切片从关键帧开始(早于请求时间 1-4s字幕按请求时间算相对位置导致超前 | **动态 PTS 检测**`enhance_clip` 对每条切片用 FFprobe 检测首帧 PTS动态计算精确 delay不再用固定值`SUBTITLE_DELAY_SEC=2.0` 作为兜底 |
| **字幕与声音不对齐** | ① **主因**`batch_clip -f`stream copy`-ss``-i` 前,输出从**关键帧**起剪,实际起点常比 `highlights.start_time` **早 03s**,整场 `transcript.srt` 仍按绝对时间裁 → 字与声错位。② 成片 **先烧字幕再整体 setpts/atempo 加速**,字幕与音轨同倍率,加速本身不引入相对漂移。 | **批量切片请用默认精确模式(勿加 `-f`**`batch_clip.py` 已改为「`-ss` 粗定位 + `-i` 后再 `-ss` 细裁 + 重编码」,起点与高光表一致`SUBTITLE_DELAY_SEC=0`,仅当 ffprobe 音/视频首包 PTS 差 > 阈值时小幅 delay。 |
| **封面期间出现字幕** | 字幕时间计算使字幕落在封面段(前 2.5s)内 | `write_clip_srt` 强制过滤 `end <= cover_duration` 的条目,并 `start = max(start, cover_duration)` |
| **字幕含 ASR 噪声行(单字母 L / Agent** | MLX Whisper 对静音/噪声段产生幻觉字符 | `_is_noise_line()` 提前过滤单字母、重复字符、噪声 token |
| **繁体字幕未转简体** | Soul 派对录音有港台口音ASR 输出繁体 | `_to_simplified()` 兜底 + CORRECTIONS 扩充 50+ 繁体常用字映射 |
| **字幕「两排字」** | 关键词用更大字号(`keyword_size_add=6`+ `base_y-1` 偏移,与正文交错形成两行错觉 | `keyword_size_add=0`、关键词 `base_y` 与正文一致、仅颜色区分v1.9 |
| **字幕整体前移 + 封面叠字幕** | concat demuxer 输出从 t=0 累加,未在开头插入 `[0, subtitle_overlay_start)` 透明段,导致字幕轨比主视频短约 5.5s | 在 `sub_concat.txt` 开头插入 `blank` + `duration=subtitle_overlay_start`v1.9 |
| **封面后长时间无字幕** | 旧版 `SUBS_START_AFTER_COVER_SEC=3` + SRT `safe_start` 再 +0.3s | 默认改为 **0** + **0.05s** epsilonv2.0 |
| **逐字字幕字间像被撑开** | Whisper `word_times` 在汉字间带空格 | `_collapse_cjk_interchar_spaces` 写入标点/安全替换链路v2.0 |
| **封面底图全糊、界面看不清** | 旧版整帧 `GaussianBlur(radius≈52)` + 叠层 | 改为 **清晰帧与模糊层 `Image.blend` 约 10%**`bg_blur_mix` / `bg_blur_radius`v2.1 |
| **highlights 时间戳不准** | 某些高光段实际对应静音区Whisper 产生幻觉 | 在转录稿中搜索话题关键词确认真实时间戳,修正后重新切片 |
---
@@ -195,6 +206,7 @@ python3 analyze_feishu_ui_crop.py "/path/本场原片.mp4" --at 0.2 --save-dir "
python3 batch_clip.py -i "原视频.mp4" -l highlights.json -o clips/ -p soul112
# 或输出到 切片/,则成片时 -c 指向 切片/
```
⚠️ **不要加 `-f` / `--fast`** 做最终成片copy 模式关键帧对齐会导致切片起点早于高光表,字幕与声音必歪。草稿试剪可 fast定稿对齐请用默认重编码模式。
**3. 成片**(竖屏条 + 封面 + 字幕 + 去语助词vf 以 analyze 为准)
```bash
@@ -238,7 +250,9 @@ xxx_output/
| 文件夹 | **切片(或 clips** + **成片**;另 **`裁剪检查/`** 放标定素材 |
| 成片尺寸 | 竖条 **W×1080**(默认 W 由 analyze`--vertical-fit-full` 时为 498×1080 letterbox |
| 成片文件名 | 纯标题(无 01、无 _enhanced |
| 单段时长 | 30300 秒 |
| 单段时长 | 30300 秒≤5 分钟),每段**仅一个主题**且**完整** |
| 条数/场 | 约 **830**(随密度),兼顾**有趣**与**信息完整** |
| 封面底模糊 | 约 **10%** 混入(`bg_blur_mix`),非全糊 |
| 高光/语助词 | 见 `参考资料/高光识别提示词.md` |
详细 crop 说明见:`参考资料/竖屏中段裁剪参数说明.md`

View File

@@ -11,7 +11,7 @@
## 2. 高光规则(脚本内已落地)
- **封面大字**:优先 `hook_3sec` → 其次 `question` → 最后 `title`(吸睛、观点感强)。
- **封面视觉**:竖屏为「视频帧强模糊 + 渐变叠层」;横版为双阶高斯模糊
- **封面视觉**:竖屏/横版均为「清晰帧 + 约 10% 模糊层混合(`bg_blur_mix`+ 渐变叠层」,非全屏强糊;见 `Soul竖屏切片_SKILL.md` v2.1
- **去停顿**`silencedetect` 检出的静音段用 `trim+concat` 切除,并**整体平移**字幕时间轴。
- **字幕**:默认逐字;相对已导出切片时间轴,音画 PTS 明显错位时才加小幅延迟。
- **片尾**`cta_ending` 在成片末尾约 23.8 秒以字幕条形式固定出现。

View File

@@ -107,6 +107,11 @@ def sanitize_filename(name: str, max_length: int = 50, chinese_only: bool = Fals
return result.strip(" _-") or "片段"
# 精确切片:-ss 在 -i 之前会落在关键帧上,输出起点常比 highlights.start 早 03s
# transcript 按绝对时间裁切 → 字幕会整体偏早/偏晚。先粗 seek 再在 -i 之后细 -ss重编码可对齐口播。
_PRESEEK_MARGIN_SEC = 120.0
def clip_video(input_path: str, start_time: str, end_time: str, output_path: str,
fast_mode: bool = False):
"""
@@ -117,7 +122,7 @@ def clip_video(input_path: str, start_time: str, end_time: str, output_path: str
start_time: 开始时间
end_time: 结束时间
output_path: 输出路径
fast_mode: 快速模式使用copy编码可能不精确
fast_mode: 快速模式(使用 copy 编码,可能不精确)
"""
# 使用 -t duration 避免 -to 在 ffmpeg 中的歧义(-to 可能被解释为输出时长)
start_sec = parse_timestamp(start_time)
@@ -125,23 +130,28 @@ def clip_video(input_path: str, start_time: str, end_time: str, output_path: str
duration_sec = end_sec - start_sec
if fast_mode:
# 快速模式:使用 copy 编码,-t 明确指定输出时长
# 快速模式:stream copy + input 侧 -ss起点可能早于 start_time关键帧
# 与整场 transcript 对齐烧录时易出现「声对字不对」。成片要求对齐时请用默认精确模式。
cmd = [
"ffmpeg",
"-ss", start_time,
"-i", input_path,
"-t", str(duration_sec),
"-c", "copy",
"-avoid_negative_ts", "1",
"-avoid_negative_ts", "make_zero",
"-y",
output_path
]
else:
# 精确模式:重新编码,-t 明确指定输出时长,体积可控
# 精确模式:粗 seek + 解码后细裁,起点与 highlights 一致,字幕与声音可对齐
preseek = max(0.0, start_sec - _PRESEEK_MARGIN_SEC)
inner_ss = start_sec - preseek
cmd = [
"ffmpeg",
"-ss", start_time,
"-y",
"-ss", str(preseek),
"-i", input_path,
"-ss", str(inner_ss),
"-t", str(duration_sec),
"-c:v", "libx264",
"-preset", "fast",
@@ -150,7 +160,7 @@ def clip_video(input_path: str, start_time: str, end_time: str, output_path: str
"-maxrate", "4M",
"-c:a", "aac",
"-b:a", "128k",
"-y",
"-avoid_negative_ts", "make_zero",
output_path
]

View File

@@ -2,11 +2,11 @@
"""
Soul切片增强脚本 v2.0
功能:
1. 封面贴片:高光 hook_3sec 优先(吸睛),竖屏带强模糊视频底 + 渐变
1. 封面贴片:高光 hook_3sec 优先(吸睛),竖屏底图为**清晰帧 + 约 10% 轻模糊混入**(非全糊)+ 渐变
2. 烧录字幕(关键词高亮、可选逐字)
3. 切除检出的长静音并重映射字幕时间轴
4. 片尾 CTActa_ending字幕条
5. 视频加速约 10%
5. 视频加速约 10%(字幕在加速前已烧进中间成片,再与音轨同倍率 setpts/atempo相对口播不因此漂移
6. 转录纠错 / 语气词过滤(见 CORRECTIONS、FILLER 等)
"""
@@ -124,7 +124,7 @@ def build_typewriter_subtitle_images(
逐词/逐字渐显:
- 若字幕带 word_timeswhisper word-level SRT按词的真实开始时间逐词追加与人声严格同步
- 否则按字符数等分句子时长(兜底)。
subtitle_overlay_start最早显示字幕的时间轴须 ≥ 封面结束 + 留白。
subtitle_overlay_start最早显示字幕的时间轴须 ≥ 封面结束(默认与封面紧接,无额外留白
"""
sub_images = []
img_idx = 0
@@ -368,7 +368,9 @@ VERTICAL_COVER_ALPHA = 165 # 0~255越大越不透明
# 样式配置
STYLE = {
'cover': {
'bg_blur': 52, # 底层视频帧高斯模糊,越大越「电影感」、越不抢字
# 封面底图:原画与「高斯模糊层」按 bg_blur_mix 混合0.1≈10% 模糊感),保留界面可辨;勿再用全幅强模糊
'bg_blur_mix': 0.10,
'bg_blur_radius': 14, # 仅用于生成模糊层的高斯半径,再与清晰帧 blend
'overlay_alpha': 200,
'duration': 2.5,
},
@@ -399,7 +401,7 @@ STYLE = {
SUBTITLE_DELAY_SEC = 0.0
SUBTITLE_PTS_OFFSET_THRESHOLD = 0.18 # 超过此秒数才加 delay
SUBTITLE_DELAY_MAX = 1.2
SUBS_START_AFTER_COVER_SEC = 3.0
SUBS_START_AFTER_COVER_SEC = 0.0
# 至少切除的静音总时长(秒)才触发重编码,避免无意义抖动
MIN_SILENCE_TRIM_TOTAL_SEC = 0.12
COVER_TITLE_MAX_CJK = 6
@@ -539,6 +541,19 @@ def apply_platform_safety(text: str) -> str:
return result
def _collapse_cjk_interchar_spaces(text: str) -> str:
"""去掉 CJK 字符之间的空白Whisper 词级时间轴常插空格,逐字/逐词显字时会像字间被撑开)。"""
if not text:
return text
s = text
prev = None
while prev != s:
prev = s
s = re.sub(r"([\u4e00-\u9fff])\s+([\u4e00-\u9fff])", r"\1\2", s)
s = re.sub(r" +", " ", s)
return s.strip()
def improve_subtitle_punctuation(text: str) -> str:
"""为字幕句子补充标点,让意思更清晰。
@@ -553,7 +568,7 @@ def improve_subtitle_punctuation(text: str) -> str:
return t
# 末尾已有标点则不重复加
if t and t[-1] in ',。?!,.:!?':
return apply_platform_safety(t)
return apply_platform_safety(_collapse_cjk_interchar_spaces(t))
# 疑问词检测
question_words = ('', '', '', '', '', '什么', '怎么', '为什么',
'', '哪里', '', '', '多少', '是否', '可以吗', '对吗')
@@ -568,7 +583,7 @@ def improve_subtitle_punctuation(text: str) -> str:
t = t + ''
elif len(t) >= 5:
t = t + ''
return apply_platform_safety(t)
return apply_platform_safety(_collapse_cjk_interchar_spaces(t))
def _detect_clip_pts_offset(clip_path: str) -> float:
"""探测切片实际起始 PTS用于补偿 -ss input seeking 的关键帧偏移。
@@ -843,8 +858,8 @@ def _sec_to_srt_time(sec):
def write_clip_srt(srt_path, subtitles, cover_duration, subs_after_cover_sec=SUBS_START_AFTER_COVER_SEC):
"""写出用于烧录的 SRT仅保留封面结束+留白后的字幕,时间已相对片段)"""
safe_start = cover_duration + subs_after_cover_sec + 0.3
"""写出用于烧录的 SRT仅保留封面结束后的字幕时间已相对片段"""
safe_start = cover_duration + subs_after_cover_sec + 0.05
lines = []
idx = 1
for sub in subtitles:
@@ -1007,7 +1022,7 @@ def create_cover_image(hook_text, width, height, output_path, video_path=None):
is_vertical = _is_vertical_strip_canvas(width, height)
if is_vertical:
# 竖屏成片:底层为「视频帧强模糊」+ 渐变叠层,字更突出、背景更「电影虚化」
# 竖屏成片:底层为「清晰帧 + 少量模糊混入(默认约 10%)」+ 渐变,避免全糊看不清界面
base = Image.new("RGBA", (width, height), (*VERTICAL_COVER_TOP, 255))
if video_path and os.path.exists(video_path):
temp_frame = output_path.replace(".png", "_vframe.jpg")
@@ -1029,8 +1044,14 @@ def create_cover_image(hook_text, width, height, output_path, video_path=None):
)
if os.path.exists(temp_frame):
try:
bf = Image.open(temp_frame).convert("RGBA").resize((width, height))
bf = bf.filter(ImageFilter.GaussianBlur(radius=style["bg_blur"]))
sharp = Image.open(temp_frame).convert("RGBA").resize((width, height))
mix = float(style.get("bg_blur_mix", 0.10))
r = float(style.get("bg_blur_radius", 14))
if mix > 0.001:
blurred = sharp.filter(ImageFilter.GaussianBlur(radius=r))
bf = Image.blend(sharp, blurred, mix)
else:
bf = sharp
dim = Image.new("RGBA", (width, height), (0, 0, 0, 115))
base = Image.alpha_composite(bf, dim)
finally:
@@ -1048,7 +1069,7 @@ def create_cover_image(hook_text, width, height, output_path, video_path=None):
img = Image.alpha_composite(img, overlay)
draw = ImageDraw.Draw(img)
else:
# 横版:沿用视频帧模糊背景
# 横版:清晰帧 + 少量模糊混入(与竖条封面一致)
if video_path and os.path.exists(video_path):
temp_frame = output_path.replace('.png', '_frame.jpg')
subprocess.run([
@@ -1056,9 +1077,14 @@ def create_cover_image(hook_text, width, height, output_path, video_path=None):
'-vframes', '1', '-q:v', '2', temp_frame
], capture_output=True)
if os.path.exists(temp_frame):
bg = Image.open(temp_frame).resize((width, height))
bg = bg.filter(ImageFilter.GaussianBlur(radius=style["bg_blur"]))
bg = bg.filter(ImageFilter.GaussianBlur(radius=6))
sharp = Image.open(temp_frame).resize((width, height)).convert('RGBA')
mix = float(style.get("bg_blur_mix", 0.10))
r = float(style.get("bg_blur_radius", 14))
if mix > 0.001:
blurred = sharp.filter(ImageFilter.GaussianBlur(radius=r))
bg = Image.blend(sharp, blurred, mix)
else:
bg = sharp
os.remove(temp_frame)
else:
bg = Image.new('RGB', (width, height), (25, 35, 30))
@@ -1520,7 +1546,8 @@ def _parse_clip_index(filename: str) -> int:
def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_path,
force_burn_subs=False, skip_subs=False, vertical=False,
crop_vf=None, overlay_x=None, typewriter_subs=False,
vertical_fit_full=False, trim_silence=True):
vertical_fit_full=False, trim_silence=True,
subtitle_extra_delay=0.0):
"""增强单个切片。vertical=True 时输出竖条,宽由 --crop-vf 决定(原生包络常见 560750×1080旧 498 为两段裁或 scale
vertical_fit_full整幅 16:9 缩放入 498×1080 + 上下黑边。
"""
@@ -1589,7 +1616,8 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa
end_sec = start_sec + original_duration
# 已导出切片:默认 delay=0。仅当音/视频首帧 PTS 明显不一致时小幅推迟字幕,贴人声。
actual_delay = float(SUBTITLE_DELAY_SEC)
# subtitle_extra_delay整场微调正数整体推迟字幕用于个别素材仍略有偏差时手工对齐。
actual_delay = float(SUBTITLE_DELAY_SEC) + float(subtitle_extra_delay or 0.0)
try:
pts_cmd = [
"ffprobe", "-v", "quiet", "-select_streams", "v:0",
@@ -1617,11 +1645,15 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa
audio_pts = float(audio_r.stdout.strip().split("\n")[0].strip())
offset = abs(first_pts - audio_pts)
if offset > SUBTITLE_PTS_OFFSET_THRESHOLD:
actual_delay = min(SUBTITLE_DELAY_MAX, offset * 0.85)
print(f" ✓ 音画 PTS 差 {offset:.2f}s → 字幕延迟补偿 {actual_delay:.2f}s", flush=True)
pts_delay = min(SUBTITLE_DELAY_MAX, offset * 0.85)
actual_delay = float(SUBTITLE_DELAY_SEC) + float(subtitle_extra_delay or 0.0) + pts_delay
print(f" ✓ 音画 PTS 差 {offset:.2f}s → 字幕延迟补偿 +{pts_delay:.2f}s含基准与 extra", flush=True)
except Exception:
pass
if abs(float(subtitle_extra_delay or 0.0)) > 1e-6:
print(f" ✓ 字幕额外延迟 --subtitle-extra-delay={float(subtitle_extra_delay):.3f}s", flush=True)
subtitles = parse_srt_for_clip(transcript_path, start_sec, end_sec, delay_sec=actual_delay)
for sub in subtitles:
if not _is_mostly_chinese(sub['text']):
@@ -1917,6 +1949,12 @@ def main():
action="store_true",
help="不去除静音长停顿(默认会切除 silencedetect 检出的静音并同步平移字幕时间轴)",
)
parser.add_argument(
"--subtitle-extra-delay",
type=float,
default=0.0,
help="字幕整体时间轴再加若干秒(正数=字幕更晚出现),用于个别素材在精确切片后仍须微调时",
)
args = parser.parse_args()
clips_dir = Path(args.clips) if args.clips else CLIPS_DIR
@@ -2016,6 +2054,7 @@ def main():
typewriter_subs=typewriter,
vertical_fit_full=vfit,
trim_silence=not getattr(args, "no_trim_silence", False),
subtitle_extra_delay=float(getattr(args, "subtitle_extra_delay", 0.0) or 0.0),
):
success_count += 1
finally:

View File

@@ -92,6 +92,7 @@
- **大文件** >20MB → `金仓_存储备份/大文件外置/``.venv` 不入库
- **数据库**:唯一 MongoDB27017库名 `karuo_site`
- **终端命令**:直接执行不询问
- **流水线续跑**:用户说「下一步」「接着跑」「重新剪辑」「直到完成」或刚更新某 Skill 后要产出时,按 `.cursor/rules/karuo-ai.mdc`**Skill 更新后·流水线「下一步」= 立即开跑**,从第一步起**不询问**、按 Skill 顺序自动做完(缺环节则先补该环节)。
- **风格**中文优先产品名保留原文Cursor/GitHub/Gitea/v0/MongoDB/Synology/宝塔等)
## 九、平台适配

View File

@@ -2,7 +2,7 @@
> **一张表查所有技能**。任何 AI 拿到这张表,就能按关键词找到对应技能的 SKILL.md 路径并执行。
> 77 技能 + 3 卡路Cursor入口 | 15 成员 | 5 负责人
> 版本5.10 | 更新2026-03-23
> 版本5.11 | 更新2026-03-20
>
> **技能配置、安装、删除、掌管人登记** → 见 **`运营中枢/工作台/01_技能控制台.md`**。
@@ -14,6 +14,8 @@
2. 找到行 → 读「SKILL 路径」列的文件
3. 按 SKILL.md 里的步骤执行
**Cursor 续跑**:用户说「下一步」「接着跑」「重新剪辑」「直到完成」或刚更新某 Skill 后要产出时,**直接执行**(不先问是否运行),见 `BOOTSTRAP.md` 八·流水线续跑、`卡若AI/.cursor/rules/karuo-ai.mdc`
多技能匹配时按 **金→水→木→火→土** 优先级。用户可用 `@成员名` 指定。
**基因胶囊查阅**:所有技能均已导出为基因胶囊,可于 `卡若Ai的文件夹/导出/基因胶囊/README_基因胶囊导出说明.md` 查看全量胶囊清单、流程图及 unpack 用法。支持「查胶囊」「胶囊列表」「继承能力」等触发。

View File

@@ -419,3 +419,4 @@
| 2026-03-22 13:21:45 | 🔄 卡若AI 同步 2026-03-22 13:21 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 11 个 |
| 2026-03-22 13:22:16 | 🔄 卡若AI 同步 2026-03-22 13:22 | 更新:运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 |
| 2026-03-22 13:23:40 | 🔄 卡若AI 同步 2026-03-22 13:23 | 更新:金仓、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 |
| 2026-03-22 14:38:11 | 🔄 卡若AI 同步 2026-03-22 14:38 | 更新Cursor规则、金仓、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 |

View File

@@ -422,3 +422,4 @@
| 2026-03-22 13:21:45 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-22 13:21 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-22 13:22:16 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-22 13:22 | 更新:运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-22 13:23:40 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-22 13:23 | 更新:金仓、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-22 14:38:11 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-22 14:38 | 更新Cursor规则、金仓、总索引与入口、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |