🔄 卡若AI 同步 2026-03-03 12:01 | 更新:水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 14 个

This commit is contained in:
2026-03-03 12:01:34 +08:00
parent 6846b6f7df
commit 69c0531baa
11 changed files with 464 additions and 30 deletions

View File

@@ -15,7 +15,9 @@ from urllib.parse import quote
FEISHU_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
TOKEN_FILE = os.path.join(FEISHU_SCRIPT_DIR, '.feishu_tokens.json')
WIKI_NODE_OR_SPREADSHEET_TOKEN = os.environ.get('FEISHU_SPREADSHEET_TOKEN', 'wikcnIgAGSNHo0t36idHJ668Gfd')
SHEET_ID = os.environ.get('FEISHU_SHEET_ID', '7A3Cy9')
SHEET_ID = os.environ.get('FEISHU_SHEET_ID', '7A3Cy9') # 2月默认 sheet
# 月份 → 工作表 sheetId2月=7A3Cy93月=bJR5sA与飞书「3月」标签一致
SHEET_ID_BY_MONTH = {2: '7A3Cy9', 3: 'bJR5sA'}
# 飞书群机器人 webhook推送运营报表链接与场次数据
FEISHU_GROUP_WEBHOOK = os.environ.get('FEISHU_GROUP_WEBHOOK', 'https://open.feishu.cn/open-apis/bot/v2/hook/34b762fc-5b9b-4abb-a05a-96c8fb9599f1')
OPERATION_REPORT_LINK = 'https://cunkebao.feishu.cn/wiki/wikcnIgAGSNHo0t36idHJ668Gfd?sheet=7A3Cy9'
@@ -39,9 +41,13 @@ ROWS = {
'106': [ '退伍军人低空经济 贴息8800', 135, 33312, 395, 7, 88, 3, 24, 9, 42 ],
# 107场 2026-02-23关闭页 137min/398进房/60最高/36关注/2礼物/16灵魂力/33820曝光小助手 10人均/85互动/34关注
'107': [ '职场情绪价值 核心团队管理', 137, 33820, 398, 10, 85, 2, 16, 36, 60 ],
# 113场 2026-03-02关闭页 163min/445成员/54最高/19新增粉丝/1礼物/29灵魂力/42360曝光小助手 8人均/139互动/16关注
'113': [ '钱一月Ai创业私域', 163, 42360, 445, 8, 139, 1, 29, 19, 54 ],
}
# 场次→按日期列填写时的日期(表头为当月日期 1~31
SESSION_DATE_COLUMN = {'105': '20', '106': '21', '107': '23'}
SESSION_DATE_COLUMN = {'105': '20', '106': '21', '107': '23', '113': '2'}
# 场次→月份(用于选择 2月/3月 等工作表标签,避免写入错月)
SESSION_MONTH = {'105': 2, '106': 2, '107': 2, '113': 3}
# 小程序当日运营数据:日期号 → {访问次数, 访客, 交易金额},填表时自动写入对应日期列
# 数据来源:微信公众平台 → 小程序 → 统计 → 实时访问/概况
@@ -154,6 +160,32 @@ def get_sheet_meta(access_token, spreadsheet_token):
return sheets[0].get('sheetId') or sheets[0].get('title') or SHEET_ID
def get_sheet_id_by_month(access_token, spreadsheet_token, month):
"""按月份选工作表标签标题含「X月」的 sheet如 3月→ 返回其 sheetId避免写入错月。"""
if month in SHEET_ID_BY_MONTH:
return SHEET_ID_BY_MONTH[month]
url = f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/metainfo'
r = requests.get(
url,
headers={'Authorization': f'Bearer {access_token}'},
timeout=15,
)
if r.status_code != 200:
return SHEET_ID
body = r.json()
if body.get('code') != 0:
return SHEET_ID
sheets = (body.get('data') or {}).get('sheets') or []
month_label = f'{month}'
for s in sheets:
title = (s.get('title') or '').strip()
if month_label in title:
sid = s.get('sheetId') or s.get('title')
if sid:
return sid
return SHEET_ID
def read_sheet_range(access_token, spreadsheet_token, range_str):
"""读取表格范围,返回 values 或 None"""
url = f'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{spreadsheet_token}/values/{quote(range_str, safe="")}'
@@ -258,7 +290,7 @@ def main():
session = (sys.argv[1] if len(sys.argv) > 1 else '104').strip()
row = ROWS.get(session)
if not row:
print('❌ 未知场次,可用: 96, 97, 98, 99, 100, 103, 104, 105, 106')
print('❌ 未知场次,可用: 96, 97, 98, 99, 100, 103, 104, 105, 106, 107, 113')
sys.exit(1)
token = load_token() or refresh_and_load_token()
if not token:
@@ -267,7 +299,10 @@ def main():
raw = (row + [None] * EFFECT_COLS)[:EFFECT_COLS]
values = [_to_cell_value(raw[0])] + [_to_cell_value(raw[i]) for i in range(1, EFFECT_COLS)]
spreadsheet_token = WIKI_NODE_OR_SPREADSHEET_TOKEN
sheet_id = SHEET_ID
month = SESSION_MONTH.get(session, 2)
sheet_id = get_sheet_id_by_month(token, spreadsheet_token, month)
if month != 2:
print(f'✅ 已选 {month}月 工作表sheet_id={sheet_id}')
range_read = f"{sheet_id}!A1:AG35"
vals, read_code, read_body = read_sheet_range(token, spreadsheet_token, range_read)
# 401 时刷新 token 并重试读取,确保能定位到日期列
@@ -300,19 +335,20 @@ def main():
LABELS_GROUP = ['主题', '时长(分钟)', 'Soul推流人数', '进房人数', '人均时长(分钟)', '互动数量', '礼物', '灵魂力', '增加关注', '最高在线']
def _maybe_send_group(sess, raw_vals):
if sess not in ('105', '106', '107'):
if sess not in ('105', '106', '107', '113'):
return
date_label = {'105': '2月20日', '106': '2月21日', '107': '2月23日'}.get(sess, sess + '')
date_label = {'105': '2月20日', '106': '2月21日', '107': '2月23日', '113': '3月2日'}.get(sess, sess + '')
report_link = OPERATION_REPORT_LINK if sheet_id == SHEET_ID else f'https://cunkebao.feishu.cn/wiki/wikcnIgAGSNHo0t36idHJ668Gfd?sheet={sheet_id}'
lines = [
'【Soul 派对运营报表】',
f'链接:{OPERATION_REPORT_LINK}',
f'链接:{report_link}',
'',
f'{sess}场({date_label})已登记:',
]
for i, label in enumerate(LABELS_GROUP):
val = raw_vals[i] if i < len(raw_vals) else ''
lines.append(f'{label}{val}')
src_date = {'105': '20260220', '106': '20260221', '107': '20260223'}.get(sess, '20260220')
src_date = {'105': '20260220', '106': '20260221', '107': '20260223', '113': '20260302'}.get(sess, '20260220')
lines.append(f'数据来源soul 派对 {sess}{src_date}.txt')
msg = '\n'.join(lines)
ok, _ = send_feishu_group_message(FEISHU_GROUP_WEBHOOK, msg)

View File

@@ -8,7 +8,8 @@ cd "$SCRIPT_DIR"
TOKEN_FILE="$SCRIPT_DIR/.feishu_tokens.json"
NOTE_DIR="/Users/karuo/Documents/个人/2、我写的日记/读书笔记"
JSON_DIR="/Users/karuo/Documents/卡若Ai的文件夹/导出/读书笔记_feishu_json"
PARENT="KNf7wA8Rki1NSdkkSIqcdFtTnWb"
# 读书笔记目录链接为快捷方式(KY7ewL21Ki5YRqkuDbecQuCTnTc),需在其父节点下创建
PARENT="QPyPwwUmtiweUOk6aTmcZLBxnIg"
PUBLISH="python3 $SCRIPT_DIR/feishu_article_unified_publish.py"
# 若本地无 token尝试从本地 API 拉取
@@ -53,4 +54,4 @@ $PUBLISH --parent "$PARENT" --title "卡若读书笔记:曾仕强《易经》"
$PUBLISH --parent "$PARENT" --title "卡若读书笔记5000天后的世界 - 凯文凯利" --md "$NOTE_DIR/卡若读书笔记5000天后的世界 - 凯文凯利.md" --json "$JSON_DIR/凯文凯利.json" && echo " ✅ 凯文凯利"
$PUBLISH --parent "$PARENT" --title "卡若读书笔记盐铁之辩与AI之道" --md "$NOTE_DIR/卡若读书笔记盐铁之辩与AI之道.md" --json "$JSON_DIR/盐铁之辩.json" && echo " ✅ 盐铁之辩"
echo "📌 全部上传完成。飞书节点: https://cunkebao.feishu.cn/wiki/$PARENT"
echo "📌 全部上传完成。读书笔记目录(你给的链接): https://cunkebao.feishu.cn/wiki/KY7ewL21Ki5YRqkuDbecQuCTnTc"

View File

@@ -150,10 +150,11 @@ MINIPROGRAM_EXTRA = {
'107': ['主题关键词 ≤12字', 140, 35000, 400, 8, 90, 3, 25, 10, 45],
```
`SESSION_DATE_COLUMN` 中添加日期映射
`SESSION_DATE_COLUMN` `SESSION_MONTH` 中添加映射(**按月份选工作表标签**3 月填 3 月表)
```python
SESSION_DATE_COLUMN = {'105': '20', '106': '21', '107': '22'}
SESSION_DATE_COLUMN = {'105': '20', '106': '21', '107': '23', '113': '2'}
SESSION_MONTH = {'105': 2, '106': 2, '107': 2, '113': 3} # 113场=3月→选「3月」标签
```
#### Step 3执行写入 + 校验
@@ -324,9 +325,12 @@ export FEISHU_APP_SECRET=dhjU0qWd5AzicGWTf4cTqhCWJOrnuCk4
# 2. SESSION_DATE_COLUMN 加日期映射
SESSION_DATE_COLUMN = {..., 'NEW': '日期号'}
# 3. _maybe_send_group 内 date_label 和 src_date 可选加映射(可选,不加则不发群
# 3. SESSION_MONTH 加月份跨月时必填3 月场次填 3写入「3月」标签而非 2 月
SESSION_MONTH = {..., 'NEW': 3}
# 4. 若当日有小程序数据,在 MINIPROGRAM_EXTRA 中加:
# 4. _maybe_send_group 内 date_label 和 src_date 可选加映射(可选,不加则不发群)
# 5. 若当日有小程序数据,在 MINIPROGRAM_EXTRA 中加:
# MINIPROGRAM_EXTRA = {..., '23': {'访问次数': 55, '访客': 55, '交易金额': 0}}
```

View File

@@ -1,11 +1,11 @@
---
name: 视频切片
description: Soul派对视频切片 + 切片动效包装(片头/片尾/程序化)。触发词含视频剪辑、切片发布、切片动效包装、程序化包装、片头片尾。
description: Soul派对视频切片 + 切片动效包装(片头/片尾/程序化)+ 剪映思路借鉴(智能剪口播/镜头分割)。触发词含视频剪辑、切片发布、切片动效包装、程序化包装、片头片尾。
group: 木
triggers: 视频剪辑、切片发布、字幕烧录、**切片动效包装、程序化包装、片头片尾、批量封面、视频包装**
triggers: 视频剪辑、切片发布、字幕烧录、**切片动效包装、程序化包装、片头片尾、批量封面、视频包装**、镜头切分、场景检测
owner: 木叶
version: "1.2"
updated: "2026-02-17"
version: "1.3"
updated: "2026-03-03"
---
# 视频切片
@@ -260,6 +260,7 @@ python3 scripts/burn_subtitles_clean.py -i enhanced.mp4 -s clean.srt -o 成片.m
| **soul_slice_pipeline.py** | Soul 切片一体化流水线 | ⭐⭐⭐ 最常用 |
| **soul_enhance.py** | 封面+字幕(简体)+加速+去语气词 | ⭐⭐⭐ |
| **soul_vertical_crop.py** | Soul 竖屏中段批量裁剪横版→498×1080 去白边) | ⭐⭐⭐ |
| **scene_detect_to_highlights.py** | 镜头/场景检测 → highlights.jsonPySceneDetect可接 batch_clip | ⭐⭐ |
| chapter_themes_to_highlights.py | 按章节 .md 主题提取片段本地模型→highlights.json | ⭐⭐⭐ |
| identify_highlights.py | 高光识别Ollama→规则 | ⭐⭐ |
| batch_clip.py | 批量切片 | ⭐⭐ |
@@ -309,6 +310,9 @@ pip3 list | grep -E "moviepy|Pillow|opencc"
```bash
pip3 install --break-system-packages moviepy Pillow opencc-python-reimplemented
# 镜头切分可选PySceneDetect
pip3 install 'scenedetect[opencv]'
```
---
@@ -385,6 +389,66 @@ ffmpeg 合成:片头 + 切片 + 片尾
---
## 🎞 剪映思路借鉴与自实现(可选能力)
> 参考 **剪映专业版**`/Applications/VideoFusion-macOS.app`)内可读配置与流程,用开源方案自实现「智能剪口播」与「智能镜头分割」,不依赖剪映二进制。详见:`参考资料/剪映_智能剪口播与智能片段分割_逆向分析.md`。
### 智能剪口播(口播稿 → 按文案/时间轴切片段)
| 剪映逻辑 | 本技能对应实现 |
|----------|----------------|
| 语音→文字 + 时间戳 | **MLX Whisper** 转录 → `transcript.srt` |
| 按文案智能剪、口播稿↔时间轴对齐 | **高光识别**`identify_highlights` / `chapter_themes_to_highlights`)→ `highlights.json``batch_clip` |
| 前端配置键 | `script_ai_cut_config``transcript_options`(仅作对照,不读写剪映) |
**结论**:现有流程「转录 → 字幕转简 → 高光识别 → 批量切片 → soul_enhance」已覆盖「智能剪口播」能力按句/按段细切可与 `transcript.srt` 时间戳结合,在 `highlights.json` 中按句生成条目即可。
### 智能镜头分割(按镜头/场景切分)
剪映 **SceneEditDetection** 思路(仅借鉴思路与参数,算法用开源实现):
- **输入**:帧序列;剪映内部为 96×96 小图 + 数组缓冲。
- **算法思路**:图像特征 + 滑动窗口 + 后处理阈值 → 输出镜头边界。
- **剪映可读参数**`SceneEditDetection/config.json`
`sliding_window_size: 7``img_feat_dims: 128``post_process_threshold: 0.35`、backbone/predhead 模型名(内部用,不引用)。
**自实现方案**:使用 **PySceneDetect**ContentDetector/AdaptiveDetector按阈值与最小场景长度得到切点再转为与 `batch_clip` 兼容的 `highlights.json`
**一键:镜头检测 → highlights → 批量切片 → 增强**
```bash
cd 03_卡木/木叶_视频内容/视频切片/脚本
pip install 'scenedetect[opencv]' # 仅首次
# 镜头检测 → 生成 highlights.json
python3 scene_detect_to_highlights.py -i "原视频.mp4" -o "输出目录/highlights_from_scenes.json" -t 27 --min-scene-len 15
# 用生成的 highlights 做切片 + 增强(与现有流水线一致)
python3 batch_clip.py -i "原视频.mp4" -l "输出目录/highlights_from_scenes.json" -o "输出目录/clips/" -p scene
python3 soul_enhance.py -c "输出目录/clips/" -l "输出目录/highlights_from_scenes.json" -t "输出目录/transcript.srt" -o "输出目录/clips_enhanced/"
```
**参数速查**
| 参数 | 说明 | 建议 |
|------|------|------|
| `--threshold` / `-t` | 内容变化阈值,越大切点越少 | 27可试 2035 |
| `--min-scene-len` | 最小场景长度(帧) | 15 |
| `--min-duration` | 过滤短于 N 秒的片段 | 按需 |
| `--max-clips` / `-n` | 最多保留片段数 | 0=不限制 |
**与「高光切片」二选一**
- **高光切片**:按话题/金句/提问(需转录 + 高光识别),适合口播、访谈。
- **镜头切片**:按画面切换切分,适合多机位、快剪、无稿素材;可先跑 `scene_detect_to_highlights` 再走同一套 `batch_clip` + `soul_enhance`
### 参考资料(剪映与流程)
- **剪映逆向分析**`03_卡木/木叶_视频内容/视频切片/参考资料/剪映_智能剪口播与智能片段分割_逆向分析.md`
- 智能剪口播 H5 路径、智能片段分割 config 与参数、自实现建议与合规说明。
- **热点切片标准流程**`参考资料/热点切片_标准流程.md`(五步、两目录、命令速查)。
---
## 📊 输出示例
```
@@ -403,11 +467,16 @@ ffmpeg 合成:片头 + 切片 + 片尾
```
03_卡木/木叶_视频内容/视频切片/
├── scripts/
├── 脚本/
│ ├── soul_slice_pipeline.py # ⭐ Soul 一体化
│ ├── soul_enhance.py # ⭐ 封面+字幕+加速
│ ├── scene_detect_to_highlights.py # 镜头检测→highlights剪映思路自实现
│ ├── one_video.py # 单视频成片
│ └── ...
├── 参考资料/
│ ├── 剪映_智能剪口播与智能片段分割_逆向分析.md # 剪映思路与参数参考
│ ├── 热点切片_标准流程.md
│ └── 竖屏中段裁剪参数说明.md
├── 切片动效包装/ # 联动能力:片头/片尾/程序化
│ ├── 10秒视频/ # React 程序化模板
│ └── 参考资料/切片动效包装速查.md

View File

@@ -27,6 +27,8 @@
## 三、流程总览
**标准五步**(每步完成再走下一步):① 分析视频→识别话题→导出话题时间 ② 按高光时刻结构整理(前 3 秒/提问) ③ 按时间节点切片→**切片/** ④ 去语助词(合并到⑤) ⑤ 封面+字幕→**成片/**。详见 `参考资料/热点切片_标准流程.md`
```
原视频 → 转录(MLX) → 高光识别(含 question/hook_3sec见高光识别提示词) → batch_clip → soul_enhance(成片竖屏直出到 成片/)
```

View File

@@ -0,0 +1,124 @@
# 剪映专业版:智能剪口播 & 智能片段分割 · 逆向分析摘要
> 分析对象:`/Applications/VideoFusion-macOS.app`剪映专业版ByteDanceQt/QML 架构)
> 分析范围:**智能剪口播**、**智能片段分割** 两功能在应用内的实现位置与可读配置,不涉及二进制反编译。
---
## 一、应用身份与结构
- **Bundle ID**`com.lemon.lvpro`
- **显示名**剪映专业版Info.plist 中麦克风/摄像头描述为「剪映专业版」)
- **技术栈**Qt5、QML、React部分 H5 模块、NodeHub 算法管线JSON 配置 + 原生执行)
---
## 二、智能剪口播(口播稿 → 按文案/时间轴切片段)
### 2.1 前端与入口
- **资源路径**`Contents/Resources/image_h5_smart_voiceover_script/`
- **入口页**`voiceover-script.html`,加载 `voiceover-script.*.js`React 打包)
- **相关 H5**`smart-lyrics.html``smart-text.html`(智能歌词/智能文案同套前端体系)
### 2.2 可读逻辑(从 JS 字符串与调用链推断)
- **配置键**`script_ai_cut_config``transcript_options`(通过 `getSettingsV2({ nodeKey, keys })` 从宿主获取)
- **与宿主通信**:通过 `callJSB` 调用原生:
- **开始转录**`j.startTranscript(this.state.$value.selected)`
参数含:`duration``theme``input` 等,用于「语音 → 文字」并可能带时间戳
- **按文案智能剪**`j.startScriptAiCutEdit({ text: tg.state.currentContent.$value })`
把当前编辑区文案传给原生,由原生做「口播稿 ↔ 时间轴」对齐并切片段
- **事件回调**`onTranscriptUpdate`,收到 `state``transcript_text``progress_title``error_code``task_id``expected_time`
- **埋点/能力名**`ai_rough_cut_*`AI 粗剪)、`smart_voiceover_script_api_metrics``ai_rough_cut_ai_write_*`
### 2.3 实现要点归纳
- **输入**:口播稿正文(或先通过「转录」得到带时间戳的文本)。
- **输出**:由原生侧在时间轴上生成/切分片段(具体切分、对齐算法在 **native 二进制** 中,前端只传文案与收结果)。
- **流程**H5 编辑文案 → 选时长/主题等 → `startTranscript``startScriptAiCutEdit` → 原生执行 → 通过 `onTranscriptUpdate` 等回传状态与结果。
---
## 三、智能片段分割(按镜头/场景切分)
### 3.1 算法配置位置
- **资源路径**`Contents/Resources/SceneEditDetection/`
- **配置文件**`config.json`(当前)、`config_prev.json`(旧版)
### 3.2 算法管线(从 config.json 逆向)
- **节点**
- `input_0``blit`,尺寸 **96×96**(当前版;旧版 128×128
- `input_1``array_buffer_producer`(提供数组/缓冲,如帧序列或特征)
- `compress_shot_detect_0`**compress_shot_detect**(压缩镜头检测)
- **关键参数**(当前版):
- `compress_shot_detect_sliding_window_size`: **7**
- `compress_shot_detect_img_feat_dims`: **128**
- `compress_shot_detect_post_process_threshold`: **0.35**
- `compress_shot_detect_backbone_model_name`: **"jy_compressShotDetectBackbone_new"**
- `compress_shot_detect_predhead_model_name`: **"jy_compressShotDetectPredHead_new"**
- `compress_shot_detect_debug_data_save_path`: **""**
- **数据流**
`input_0`(图像) + `input_1`(数组缓冲) → **compress_shot_detect** → 输出应为镜头边界/切分点(具体输出格式在原生实现中)。
### 3.3 实现要点归纳
- **类型**:基于**图像特征 + 滑动窗口**的镜头/场景切分(非纯音频)。
- **思路**:小图 96×96 输入 → backbone 提特征 → predhead 在滑动窗口内做切点预测 → 后处理阈值 0.35。
- **模型**双模型结构backbone + predhead名称带 `jy_``_new`,为剪映内部命名;模型权重在应用其他目录或运行时下载,未在 SceneEditDetection 下明文暴露。
---
## 四、其他相关模块(便于对照)
| 功能 | 资源路径 | 说明 |
|----------------|----------------------------------|------|
| 智能裁剪 | `lvop_intelligent_crop/` | reframe人脸 + 显著性(nodehub_image_saliency) + video_reframe输出裁剪框 |
| 智能文案/歌词 | `image_h5_smart_text/``smart_lyrics` | 与口播稿共用一套「文案→时间轴」体系 |
| 算法通用配置 | `agicbach/algorithmConfig.json` | 人脸/表情等,与 AIGC 充电脚本(jianying_aigc_charge) 关联 |
| 片段/轨道逻辑 | `aigc_text_template/js/main.js` | ScriptSegment、SetWidgetTimeRange、canvasSplitThreshold 等,用于模板内片段与画布分割 |
---
## 五、自实现建议(不依赖逆向二进制)
1. **智能剪口播**
- 语音→文字:用 **Whisper**(如 MLX Whisper得到带时间戳的 SRT/文稿。
- 按文案切:用文稿时间戳对应到时间轴,在 FFmpeg/OpenCV 或时间线 API 上按句/段切分并生成片段。
- 你现有流程(`transcript.srt` + `highlights.json` + `batch_clip`)已覆盖「话题/高光时间 → 切片」逻辑,可与「按句切」结合做细粒度口播剪。
2. **智能片段分割**
- 思路与剪映一致:**帧级特征 → 切点检测 → 后处理**。
- 可用:**PySceneDetect**、**scenedetect**(阈值或基于内容)、或自训轻量 backbone+二分类头,用 96×96 或 128×128 输入、滑动窗口 + 阈值做切点。
- 数据:用公开镜头切分数据集(如 RAI、其他 scene detection 数据集)训练/微调。
3. **合规**
- 本分析仅基于应用内**可读资源**JSON、前端 JS 字符串、目录与命名)。
- 未对原生二进制做反编译或提取;自实现时使用开源模型与公开算法,不依赖剪映内部模型或代码。
---
## 六、文件与路径速查
```
VideoFusion-macOS.app/
├── Contents/Info.plist # 应用信息、剪映专业版描述
├── Contents/Resources/
│ ├── image_h5_smart_voiceover_script/ # 智能剪口播 H5
│ │ ├── voiceover-script.html
│ │ └── static/js/voiceover-script.*.js
│ ├── SceneEditDetection/ # 智能片段分割
│ │ ├── config.json # compress_shot_detect 参数
│ │ └── config_prev.json
│ ├── lvop_intelligent_crop/ # 智能裁剪reframe
│ │ └── algo/reframe_plugin.json
│ └── aigc_text_template/js/main.js # ScriptSegment、时间范围、canvas 分割
```
---
*文档生成后可用于:对照现有热点切片流程、设计自研「按口播切」与「按镜头切」管线,不涉及对剪映二进制的进一步逆向。*

View File

@@ -0,0 +1,81 @@
# 热点切片 · 标准流程(五步、两目录)
> 只保留**两个目录****切片**、**成片**。单段时长 **30 秒8 分钟**,按高光时刻时间节点切,前 3 秒抓眼球/提问。
---
## 第一步:分析视频 → 识别话题 → 导出话题时间
1. **找到视频**,确定源文件。
2. **转录**MLX Whisper 导出 `transcript.srt`(带时间戳)。
3. **识别话题**:从转录/文档中识别主要话题与结构。
4. **导出话题时间**:得到每个话题的**起止时间节点**(如 00:00:00-00:04:00整理成可裁剪的时间表。
**产出**`transcript.srt`、话题与时间节点列表(或 `highlights.json` 雏形)。
---
## 第二步:按高光时刻结构整理(前 3 秒规则)
1. **高光时刻与话术**:为每段确定「高光时刻」文案(前 3 秒展示的提问或金句)。
2. **时间节点与视频结构**:与第一步的时间节点一一对应。
3. **前 3 秒规则**:有**提问**的片段,前 3 秒用**提问话术**;无提问用金句/悬念。格式见 `高光识别提示词.md`
**产出**`highlights.json`(含 `title``start_time``end_time``hook_3sec``question` 等),作为后续切片与成片的结构依据。
---
## 第三步:按时间节点做切片提取
1. **按 highlights 时间**:用 `batch_clip` 根据 `start_time``end_time` 裁剪视频。
2. **单段时长**:控制在 **30 秒8 分钟** 一个话题。
3. **输出**:全部写入 **切片/** 目录,文件名含前缀与标题。
**产出****切片/** 目录下的横版切片 mp4。
---
## 第四步:去语助词并整理为新切片
1. **语助词去除**:在成片环节由 `soul_enhance` 统一处理(转录稿清理 + 烧录字幕时已去语助词)。
2. **不单独生成「去语助词版」目录**:去语助词与封面、字幕在同一链路完成,输出到 **成片/**
**说明**:当前流程将第四步合并到第五步,由 soul_enhance 一步完成(去语助词 + 封面 + 字幕)。
---
## 第五步:加封面 + 烧录字幕 → 成片
1. **封面**:前 3 秒使用高光时刻/提问文案半透明质感Soul 绿风格。
2. **字幕**:烧录到画面,封面结束后显示,居中去语助词。
3. **输出**:竖屏 498×1080可选、文件名为纯标题全部写入 **成片/**
**产出****成片/** 目录下的成片 mp4封面+字幕+去语助词)。
---
## 流程小结(顺序执行)
| 步骤 | 内容 | 产出 |
|------|------|------|
| 1 | 分析视频 → 识别话题 → 导出话题时间 | transcript.srt、时间节点 |
| 2 | 按高光时刻结构整理(前 3 秒、提问/金句) | highlights.json |
| 3 | 按时间节点切片提取30 秒8 分钟/段) | **切片/** |
| 4 | 去语助词(合并在步骤 5 | — |
| 5 | 加封面 + 烧录字幕 | **成片/** |
**只保留两个目录****切片**、**成片**。其他中间目录不保留。
---
## 命令速查112 场示例)
```bash
# 12. 已有 transcript.srt、highlights.json按热点切片时间节点
# 3. 切片
python3 batch_clip.py -i "原视频.mp4" -l highlights.json -o 切片/ -p soul112
# 45. 成片(去语助词+封面+字幕)
python3 soul_enhance.py -c 切片/ -l highlights.json -t transcript.srt -o 成片/ --vertical --title-only --force-burn-subs
```

View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
镜头/场景切分 → highlights.json
基于 PySceneDetect 做镜头边界检测,输出与 batch_clip / soul_slice_pipeline 兼容的 highlights 格式。
参考剪映专业版智能片段分割思路:帧级特征 + 切点检测 + 后处理阈值(见 参考资料/剪映_智能剪口播与智能片段分割_逆向分析.md
"""
import argparse
import json
import sys
from pathlib import Path
def format_timestamp(seconds: float) -> str:
"""秒 → HH:MM:SS"""
h = int(seconds // 3600)
m = int((seconds % 3600) // 60)
s = seconds % 60
return f"{h:02d}:{m:02d}:{int(s):02d}.{int((s % 1) * 100):02d}"
def detect_scenes(video_path: str, threshold: float = 27.0, min_scene_len: int = 15) -> list:
"""
使用 PySceneDetect 检测场景切点。
threshold: 内容变化阈值,越大切点越少(剪映参考约 0.35 后处理阈值,此处为 ContentDetector 的 0-255 档位)。
min_scene_len: 最小场景长度(帧数),避免过碎。
"""
try:
from scenedetect import detect, ContentDetector
except ImportError:
print("请安装: pip install scenedetect[opencv]")
sys.exit(1)
detector = ContentDetector(threshold=threshold, min_scene_len=min_scene_len)
scene_list = detect(video_path, detector)
if not scene_list:
return []
# scene_list: list of (start FrameTimecode, end FrameTimecode)
out = []
for start_tc, end_tc in scene_list:
start_sec = start_tc.get_seconds()
end_sec = end_tc.get_seconds()
out.append((start_sec, end_sec))
return out
def main():
parser = argparse.ArgumentParser(description="镜头检测 → highlights.json供 batch_clip 使用)")
parser.add_argument("--video", "-i", required=True, help="输入视频路径")
parser.add_argument("--output", "-o", required=True, help="输出 highlights.json 路径")
parser.add_argument("--threshold", "-t", type=float, default=27.0,
help="ContentDetector 阈值,越大切点越少(默认 27")
parser.add_argument("--min-scene-len", type=int, default=15,
help="最小场景长度(帧),默认 15")
parser.add_argument("--max-clips", "-n", type=int, default=0,
help="最多保留片段数0 表示不限制")
parser.add_argument("--min-duration", type=float, default=0,
help="过滤掉时长小于此值的片段(秒),默认 0")
args = parser.parse_args()
video_path = Path(args.video)
if not video_path.exists():
print(f"❌ 视频不存在: {video_path}")
sys.exit(1)
scene_list = detect_scenes(
str(video_path),
threshold=args.threshold,
min_scene_len=args.min_scene_len,
)
if not scene_list:
print("未检测到场景切点,可尝试降低 --threshold")
sys.exit(1)
clips = []
for i, (start_sec, end_sec) in enumerate(scene_list, 1):
duration = end_sec - start_sec
if args.min_duration and duration < args.min_duration:
continue
clips.append({
"start_time": format_timestamp(start_sec),
"end_time": format_timestamp(end_sec),
"title": f"镜头{i}",
})
if args.max_clips and len(clips) >= args.max_clips:
break
out_path = Path(args.output)
out_path.parent.mkdir(parents=True, exist_ok=True)
with open(out_path, "w", encoding="utf-8") as f:
json.dump({"clips": clips}, f, ensure_ascii=False, indent=2)
print(f"✓ 已写入 {len(clips)} 个镜头 → {out_path}")
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -96,6 +96,9 @@ def main():
parser.add_argument("--skip-subs", action="store_true", help="跳过字幕烧录(原片已有字幕时用)")
parser.add_argument("--force-burn-subs", action="store_true", help="强制烧录字幕(忽略检测)")
parser.add_argument("--force-transcribe", action="store_true", help="强制重新转录(删除旧 transcript 并重跑)")
parser.add_argument("--two-folders", action="store_true", help="仅用两文件夹:切片、成片(默认 clips、clips_enhanced")
parser.add_argument("--slices-only", action="store_true", help="只做到切片MLX 转录→高光→批量切片),不跑成片增强")
parser.add_argument("--prefix", default="", help="切片文件名前缀,如 soul112")
args = parser.parse_args()
video_path = Path(args.video).resolve()
@@ -109,11 +112,15 @@ def main():
base_dir = video_path.parent / (video_path.stem + "_output")
base_dir.mkdir(parents=True, exist_ok=True)
use_two_folders = getattr(args, "two_folders", False)
clips_dir_name = "切片" if use_two_folders else "clips"
enhanced_dir_name = "成片" if use_two_folders else "clips_enhanced"
audio_path = base_dir / "audio.wav"
transcript_path = base_dir / "transcript.srt"
highlights_path = base_dir / "highlights.json"
clips_dir = base_dir / "clips"
enhanced_dir = base_dir / "clips_enhanced"
clips_dir = base_dir / clips_dir_name
enhanced_dir = base_dir / enhanced_dir_name
print("=" * 60)
print("🎬 Soul 切片流水线:视频制作 + 视频切片")
@@ -202,6 +209,7 @@ def main():
# 3. 批量切片
clips_dir.mkdir(parents=True, exist_ok=True)
clip_prefix = getattr(args, "prefix", None) or "soul"
if not args.skip_clips:
run(
[
@@ -210,15 +218,25 @@ def main():
"--input", str(video_path),
"--highlights", str(highlights_path),
"--output", str(clips_dir),
"--prefix", "soul",
"--prefix", clip_prefix,
],
"批量切片",
timeout=300,
)
elif not list(clips_dir.glob("*.mp4")):
print("clips/ 为空,请去掉 --skip-clips 或先完成切片")
print(f"{clips_dir_name}/ 为空,请去掉 --skip-clips 或先完成切片")
sys.exit(1)
if getattr(args, "slices_only", False):
print()
print("=" * 60)
print("✅ 切片阶段完成(--slices-only")
print("=" * 60)
print(f" 切片: {clips_dir}")
print(f" 转录: {transcript_path}")
print(f" 高光: {highlights_path}")
return
# 4. 增强(封面+字幕+加速soul_enhancePillow无需 drawtext
enhanced_dir.mkdir(parents=True, exist_ok=True)
enhance_cmd = [
@@ -238,7 +256,7 @@ def main():
import shutil
enhanced_count = len(list(enhanced_dir.glob("*.mp4")))
if enhanced_count == 0 and clips_list:
print(" soul_enhance 失败,复制原始切片到 clips_enhanced")
print(f" soul_enhance 失败,复制原始切片到 {enhanced_dir_name}/")
for f in sorted(clips_dir.glob("*.mp4")):
shutil.copy(f, enhanced_dir / f.name)
@@ -247,7 +265,7 @@ def main():
print("✅ 流水线完成")
print("=" * 60)
print(f" 切片: {clips_dir}")
print(f" 增强: {enhanced_dir}")
print(f" 成片: {enhanced_dir}")
print(f" 清单: {base_dir / 'clips_manifest.json'}")

View File

@@ -214,3 +214,4 @@
| 2026-03-03 04:58:10 | 🔄 卡若AI 同步 2026-03-03 04:58 | 更新Cursor规则、总索引与入口、运营中枢工作台 | 排除 >20MB: 14 个 |
| 2026-03-03 05:02:46 | 🔄 卡若AI 同步 2026-03-03 05:02 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 14 个 |
| 2026-03-03 10:15:48 | 🔄 卡若AI 同步 2026-03-03 10:15 | 更新:水桥平台对接、卡木、火炬、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 14 个 |
| 2026-03-03 10:20:17 | 🔄 卡若AI 同步 2026-03-03 10:20 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 14 个 |

View File

@@ -217,3 +217,4 @@
| 2026-03-03 04:58:10 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-03 04:58 | 更新Cursor规则、总索引与入口、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-03 05:02:46 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-03 05:02 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-03 10:15:48 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-03 10:15 | 更新:水桥平台对接、卡木、火炬、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-03 10:20:17 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-03 10:20 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 14 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |