万象-视频理解与转写

SKILL前端设计
前端设计 未标注权限 许可证不明确
安装这个技能

看懂一条视频,也把视频发出去:语音转写、关键帧理解、竖屏成片与字幕。

一句话选型结论

按资料字段整理

万象-视频理解与转写是前端设计类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类前端设计 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_de2353f3/omni-digital-world
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

前端设计
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_de2353f3仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791671930
正文体量51.4 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
出页面与组件

按描述生成页面结构和组件草稿。

调整样式与布局

改间距、配色与响应式布局。

生成设计说明

把设计稿整理成开发能接的说明。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在前端设计场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
万象-视频理解与转写当前 未标注权限 未标注 未标注 未标注 有
前端设计 只读 GitHub 仓库 882.3 万 Codex、Claude、Cursor 有
Vercel 组合模式 联网访问 GitHub 仓库 385.9 万 Codex、Claude、Cursor 有
Vercel React native skills 前端 只读 GitHub 仓库 246.9 万 Codex、Claude、Cursor 有
adapt 搜索 浏览器控制 GitHub 仓库 202.7 万 Codex、Claude、Cursor 有
使用我的浏览器 浏览器控制 GitHub 仓库 184.8 万 Codex、Claude、Cursor 有

SKILL.md 全文

51.4 KB
模型实际读到的说明书原文,来自技能仓库

万象-数字世界

把「数字世界里的视频」双向打通:进来的看得懂,出去的发得出。

  • A 篇 · 理解线(看进来):一条视频 → 可核验的结构化理解(本地听 + 云端看 + 按时间轴合并)
  • B 篇 · 成片线(做出去):一段文案 → 可直接发布的竖屏成片(配图/配音/字幕卡/镜头运动/响度归一)

两条线共用一套底座:本机 Python 环境、ffmpeg 静态二进制、以及同一份 scripts/。

总路由:先判你要哪一头

你手上的东西 你要的结果 去哪
一条视频(文件/链接/视频号) 它讲了什么、画面是什么 A 篇 · 理解线
一段文案 一条能直接发出去的竖屏 mp4 B 篇 · 成片线
一条视频,看完还要改写再发 先理解、后成片 A → B
只要逐字稿/字幕 转写文本或 srt A 篇 transcribe.py/B 篇字幕导出
只有图片、不是视频 图片处理 都不是,属图像类任务

最容易串的一处:A 篇的「抽关键帧」是为了看懂(给人工核验画面);B 篇的「配图」是为了成片(生成新画面)。同名不同事,别互相套用。

执行纪律(两篇通用,先读)

> 来源:2026-09-20 吸收自视频号博主「晓辉博士」的 CodeBuddy 用法分享
> —— 她的「深度研究」Skill 强调全过程可见、可随时纠偏。

阶段汇报:长任务不许闷头跑到最后。

工具调用对用户是折叠的 —— 中途发生什么,用户看不见,只能等到最后一句"拿到了"。
在本机这种"失败重试是常态"的环境里,这会变成失控感:用户想喊停,都不知道该不该喊。

规矩 具体做法
分段报 超过 3 个阶段的活,每过一阶段报一句(定位到了 / 密钥没拿到 / 正在解密 / 转写完成 41 秒),不必等结果
卡住就喊 同一处连续卡 2 轮,立刻说明:卡在哪、已排除什么、下一步试什么
失败也报 不要说"稍等"然后沉默;宁可报一条"这次没通,我换 X 再试"
不报也算报 前三个阶段若都顺利,可以合并成一句,但不能一路静默到交付
🔴 动桌面之前先问 要驱动微信 / 录屏 / 抢前台焦点之前,先停下确认用户此刻不在用电脑(本机实测:向日葵远程接管期间 60 余次点击全部落空,白跑半小时)

> 实例(本机):2026-09-20 读视频号卡片时,60 余次点击失败全部静默,
> 用户直到最后才看到结果。若中途说一句"卡片点不动",
> 用户会立刻告知向日葵正在远程接管屏幕—— 可省半小时。

第 A 篇 · 看懂一条视频(理解线)

> 目标:把一条视频变成可核验的结构化理解 —— 有原帧、有时间轴,能被人复核,不是「我说它讲了什么」。

把「一段视频」变成「可核验的结构化理解」。两条腿走路:

  • 听:ffmpeg 抽音频 → 本地 faster-whisper 转写(零云端、零 API Key、资料不出本机)
  • 看:ffmpeg 抽关键帧 → Agent 读图(理解画面、OCR 画面字幕)
  • 合:两路结果按时间轴对齐 → 摘要 + 报告(附原帧,可人工核验)

能力边界(先说清楚,不要承诺做不到的事)

输入 听(语音) 看(画面)
本地视频文件 mp4/mkv/mov/flv… ✅ ✅
网络视频链接(B站/抖音/YouTube/微博/小红书) ✅ ✅
微信视频号(拿到 mp4 后) ✅ ✅
纯图片型视频(无音轨) — ✅
无语音、无字幕的视频 只能给静音/无内容结论,不要编内容

视频号怎么拿到 mp4(2026-09-19 起,五条路线)

> 旧版这里写的是「视频号是硬限制,yt-dlp 不支持」——已作废。
> yt-dlp-patch 补上了视频号提取器,现在是有路的,只是取链接要用户配合一步。
> 2026-09-19 深夜新增 E 路线:全程零用户操作、无损拿到原始 mp4(含音轨),已端到端跑通。

路线 做法 前提 状态
E 进程内存直取+解密 ★★ wxchannels_pull.py:触摸注入驱动微信 UI → 播放卡片 → 扫 Weixin.exe/WeChatAppEx.exe 内存取「带 token 的 CDN 直链 + decode_key」→ 官方 wasm_video_decode 解密 → 得到原始 mp4 只需微信在运行;我用合成触摸点击,用户零操作 ✅ 2026-09-19 打通:v1(56.7MB/9:15)已解出合法 mp4,encoder=Wxmm_9020230808,HEVC 720×1280 + AAC 立体声,ffmpeg 校验通过。这是无损、无录制损耗、不看屏幕的唯一路线
A 链接直解 channels_fetch.py <链接> <目录>,底层 yt-dlp + yt-dlp-patch 的 wechat_channel 用户复制出链接(weixin.qq.com/sph/… 或 channels.weixin.qq.com/…)。手机端分享面板里有「复制链接」,PC 端正测无此入口 ✅ 路由已实测;部分视频需 cookie
B 电脑端缓存 从 msg\video\ 捞刚播过的 mp4 用户点播放 ❌ 2026-09-19 实测证伪:播放期间 45 分钟内 msg\video、cache、WMPF、temp 零写入。别再让用户「播一遍试试」浪费时间(见坑 15)
C 同源还原 卡片封面 OCR 出标题 → 公网检索博主原文 无 ✅ 实测有效,无需用户操作;但拿到的是同源文字,不等于视频转写
D 屏幕录制(画面+声音) screen_record_av.py <out.ts> --seconds N:gdigrab 录画面 + WASAPI loopback 录系统声,自动合轨成 mp4。纯画面用 screen_record.py;只要关键页用 screen_capture.py 用户点播放并保持窗口在前 ✅ 2026-09-19 打通:loopback 自验 peak=0.2000 / rms=0.1223 / 72.4% 有声样本。E 路线走不通(如微信版本升级封掉内存可读)时的第一备选

路线选择的判据:视频是不是「画面即内容」(PPT 录屏 / 图表演示 / 带硬字幕)?
- 是 → D 路线够用,用户零学习成本。
- 否(纯口播、讲经验)→ D 路线(带声音版)同样能打,因为 loopback 已打通;
只有在「录制环境拿不到声音」(用户用别的输出设备、或不能外放)时才必须走 A(手机取链接)。

> 🔴 检查点 · 开始 E 路线之前(这条路线会接管你的屏幕):动手前逐条确认,缺一条这次取证就不算数:
> ① 用户此刻不在用电脑(本机实测:远程接管期间点击全部落空);
> ② 微信客户端正在运行(先查进程,别猜 —— 曾出现"驱动到一半微信被关",链路直接断);
> ③ 扫完内存必须立刻 close_player()(播放器开着会自动续播,把无关视频灌进内存,
> 污染下一次取证:实测混入过 21:17/29:27/240MB/252MB 的无关续播视频)。

E 路线的四个必知点:
1. ★★ 通道按「区域」分流,不是"一刀切屏蔽鼠标"(2026-09-20 实测推翻旧结论):
- Qt 自绘区(左栏头像/图标、会话列表、搜索框)→ 合成触摸与合成鼠标都能点。
- 聊天内容区(卡片、消息,由 MMUIRenderSubWindowHW 承载)→ 只认鼠标:
mouse_event(绝对坐标 MOVE|ABSOLUTE + LEFTDOWN/UP)/SendInput/PostMessage(WM_LBUTTONDOWN) 均有效,
而 InjectSyntheticPointerInput 触摸像素差恒为 0。
- 实测数据(点卡片播放键,全屏像素差):touch=0 ❌ / mouse_event=68901 ✅ /
SendInput=44844 ✅ / PostMessage 主窗=45226 ✅ / PostMessage 渲染子窗=1223 / SendMessage 渲染子窗=1628。
- ⇒ 结论:点卡片/播放键一律用鼠标通道;wxd.py 已加 mouse_tap()。
旧结论"微信屏蔽合成鼠标"是窗口被外部改动成 576×448、坐标全偏那段时期得出的误判,勿再引用。
- 触摸标誌位仍需写对(若确要走触摸):POINTER_FLAG_DOWN=0x00010000、UP=0x00040000(写 0x1/0x4 会返回 False 且看不出原因)。
- 键盘一直有效(keybd_event / SendInput),KEYEVENTF_UNICODE 直送中文可绕输入法。
- 判据不要用"主窗像素差":播放器是独立顶层窗(Chrome_WidgetWin_0,属 WeChatAppEx.exe),
它打开时主窗像素本来就不变;判据应是"枚举到新顶层窗"或"窗口矩形变化"。
2. 绝不能在微信未置前时注入:合成输入按前台窗口投递,会让点击落到用户正在用的软件上。
必须 is_fg() 实测校验,失败就 AttachThreadInput 重试;仍失败直接抛异常中止。
3. 加密方案:Isaac64 PRNG + XOR,只加密前 131072 字节,其余明文;密钥流直接 next() 逐字节取,
不要自己 reverse 或换字节序(自实现的三处都踩过坑)。decode_key 与 url 是同批 API 响应配对,
旧 URL 的 token 会过期(→ HTTP 400),天然可用来筛掉历史残留配对。
4. 播放器会自动续播(用户 2026-09-19 明确提醒):一张卡读完立刻关闭再开下一张,
否则视频号自动播推荐流,会把无关视频的直链混进同一轮读取。
★ 关闭方法(2026-09-20 修正):Esc 关不掉(会续播)。播放器是独立顶层窗:
Chrome_WidgetWin_0,标题为 '微信',pid = WeChatAppEx.exe,GetWindow(hwnd, GW_OWNER)==0。
注意 wxd.py 的 ls() 只枚举 Qt51514*,看不到它——必须全量 EnumWindows 再按
Area>50000 且 hwnd!=主窗 且可见 过滤,然后 PostMessage(hwnd, WM_CLOSE, 0, 0) 即可干净关闭
(实测一次命中;备用退化=鼠标点其右上角 (rect.right-75, rect.top+30))。
实测播放器窗矩形 (429,165,1451,883),MSE 日志里的 "hwnd":1771946 正是它。
5. ★★ 头号坑:微信的「搜索聊天记录」是一个模态窗,会把主窗点成"死"的(2026-09-20 定位):
只要它的窗口还开着(哪怕被你的 SetForegroundWindow 挤到后面、看不见),
主窗的所有鼠标/触摸输入都不产生任何响应,表现和"注入失效"一模一样:
InjectSyntheticPointerInput 返回 ok=True, err=(0,0)、WindowFromPoint 命中主窗、
IsWindowEnabled=True、IsHungAppWindow=False —— 一切正常,就是点不动。
- 识别:Qt51514QWindowIcon 类、标题 搜索聊天记录、独立顶层窗(实测 (591,195,1327,843))。
用 GetLastActivePopup(主窗) 查不可靠(这里返回主窗自己)。
- 干净做法:全量枚举顶层窗 → 凡 cls 以 Qt51514 开头且 hwnd!=主窗 的,WM_CLOSE 掉,
再回读确认只剩 WxTrayIconMessageWindow(那个本来就不可见,可无视)。
- ⇒ 优先直接点会话列表行(会话已选中时无需导航,只需确认聊天区在底部)。
非走搜索不可时,按本节第 8 条的三个条件做,并在点完结果后立刻清理该模态窗。
6. URL 尾部有日志碎片,_fast.valid() 会误杀(2026-09-20 定位):
内存里取到的 MSE url 字段后面直接粘着 \"startIdx\":…、\"respHeaders\":… 等,
且夹杂 \x00 二进制噪声。判据 " " not in u and "..." not in u 拦不住,会判为无效。
正确做法:按 URL 合法字符集截断,遇到第一个非法字符即停:
ALLOWED = set("A-Za-z0-9-._~:/?#[]@!$&'()*+,;=%"),逐字符收集到首个非法字符为止再 rstrip("&")。
实测清洗后 6 条候选里 2 条是真视频,解密全部成功。
7. ❌ 已证伪的判据:<type>51</type> 不是「PC 端不可播放」的标志(2026-09-20 自纠)。
曾据内存中 <type>51</type> + <title>当前微信版本不支持展示该内容,请升级最新版</title>
判定某张卡不可播放,这个判据是错的。实测同一批内存里 <type>51</type> 出现
1161 次,与 finderFeed 记录数(1071)近乎 1:1 —— 它对所有视频号消息都出现。
而在同一批数据下,四张同样带该标记的卡在聊天区全部正常渲染、且都带播放按钮。
⇒ 判「能不能播放」只能看 UI 实际渲染(截图看缩略图+播放键),不要读 XML 猜。
该判据若再被引用,会直接把「能读的卡」误判成「读不到」,且看起来证据充分、极难察觉。
8. ★ 导航与滚动的分工(2026-09-20 实测,纠两个易犯的错):
- End/PgDn 滚的是「左侧会话列表」,不是聊天区(焦点在列表上)。
拿它「把聊天区滚到底」会把列表滚到几百条之外,看起来像列表"变成了通讯录"
(滚远处会话时预览行不加载,只剩名字+分隔线,极易误判成切到了通讯录)。
聊天区点开会话时本来就在底部,无需滚。
- 滚轮在左侧列表无效,但在「聊天内容区」有效(2026-09-20 补正):
把光标放到聊天区中部(如窗口内 (470, 500))再 wheel(),能正常滚动聊天区 ——
这是把「被顶部截断、看不到播放键」的卡片滚进视野的正确方法。
Home / End 对聊天区仍无效(它们作用于列表)。遇到列表被滚走,
别靠键盘/滚轮找回来 —— 直接走搜索导航(见下条)。
- 导航可以走搜索框,但要满足三条:① 点搜索框连点两次(首击常被"激活窗口"吃掉,
只点一次会表现为"输入完全进不去");② 结果坐标取「功能」区首项
(OFF_RESULT_FUNC = (186, 133),960×1020、place(940,20) 后实测);
③ 点完结果立刻 kill_modal_dialogs。
⚠️ 原值 (166, 335) 落在「聊天记录」区,会打开错误的会话(实测打开了无关群聊)——已修正。

  1. ★ 一次读多张卡:污染甄别与「归属判定」(2026-09-20 实测,推翻一条自设判据):
    播放器会自动续播推荐流,扫内存会把无关视频的直链一起抓进来。同一轮实测抓到的污染物:
    1280×720 / 21:17(54,403,855 字节,重复出现 7 次)、1280×720 / 29:27(109,245,619 字节)、
    以及 240MB、252MB 各一条;抽帧确认其中一条是「一个视频带你彻底上手 WorkBuddy」,与卡片无关。
    - ❌ 已证伪:「竖屏 = 本卡视频」不成立。129 卡的视频本身就是 968×720 横版合成
    (上部海报条 + 下部操作录屏);而候选里那条 1080×1920 竖屏,内容是「四川观察」新闻
    (「张伟同志任中共巴中市委书记」)—— 分辨率比例与归属毫无关系。
    - ⇒ 判归属只有一个可靠办法:抽帧看内容(首帧应与卡片封面同源,再看中段是否延续同一主题)。
    抽帧用 ffmpeg -ss <t> -frames:v 1,多帧拼一张网格图比对最省事。
    - 下载侧过滤(省时省配额):先用 Range: bytes=0-262143 探 Content-Range 拿总大小,
    跳过 > 80MB 的(本轮真卡片视频都在 0.5–9MB),再逐条解密 + ffmpeg -i 读时长/分辨率。
    - 扫内存很快(约 14–15 秒),下载可能几分钟;扫完立刻 close_player(),别让续播继续扩大污染面。
    - 某些卡没有口播(2026-09-20 实测 127 转写 0 段):该卡本身是「图文 + 打字字幕 + 配乐」。
    此时改从画面取内容(逐帧读字幕),交付时明确标注「画面字幕识读,非音频转写」——两者证据强度不同。

A 路线的三个必知点:
1. 必须显式 plugins.load_all_plugins() —— pip 装的插件默认不会自动注册,
不调用就静默落到 generic 提取器,表现为「解析不到视频」(channels_fetch.py 已内置)。
2. URL 形态只认三种:weixin.qq.com/sph/<id>、channels.weixin.qq.com/...、finder.video.qq.com/...。
3. 页面无内嵌 JSON 时插件走腾讯元宝中转,需要含 tencent.com 的 cookie:
--cookies <netscape.txt> 或环境变量 YUANBAO_COOKIE_FILE。缺 cookie 时插件会明确报警,不是静默失败。

拿不到 mp4 时的兜底顺序:E(内存直取+解密,首选,零用户操作) → D(电脑录屏,画面+声音)→ A(手机复制链接)→ 手机录屏后发回 → C(同源文字)。
绝不能因为拿不到视频就编内容;此时应改走 C 路线,并明确标注「同源文字、非视频转写」。

依赖(本机已就绪)

组件 位置 说明
ffmpeg 7.1 default venv 的 imageio_ffmpeg 静态二进制 抽音频/抽帧/录屏
faster-whisper 1.2.1 default venv 本地转写,GPU 已实测可用
yt-dlp 2026.08.19 default venv 网络视频下载(1752 个站点)
CUDA 运行库 default venv 的 nvidia-*-cu12 pip 包 GPU 必需,见坑 #7
soundcard 0.4.6 default venv WASAPI loopback 录系统声(D 路线录音必需,纯 ctypes 无编译)
模型 <私有配置 whisper_model_root>\faster-whisper-<size>\ 用户指定装 D 盘(C 盘仅剩 ~21GB)

模型位置与查找顺序

model_roots() 按序查找,命中即用:

  1. 环境变量 WHISPER_MODEL_DIR(覆盖为单一根目录)
  2. <私有配置 whisper_model_root> ← 本机唯一位置(small 464MB + medium 1.53GB)
  3. <skill>/models/ ← 兜底,便于把技能整体拷到别的机器
    (本机该目录已删除,原先误在 C 盘多下一份 small 占 464MB;技能包因此保持轻量)

prepare_model.py 的 default_dest() 同理:私有配置里的根目录存在就装进去,否则回落技能内 models/。

GPU 依赖安装(一次性)

PY -m pip install nvidia-cublas-cu12 nvidia-cudnn-cu12 -i https://mirrors.aliyun.com/pypi/simple/

装完约 +1.5GB。装完不代表能用 —— ctranslate2 的 wheel 不自带 CUDA 运行库,
且 pip 包把 DLL 放在 site-packages/nvidia/*/bin,不在 PATH 上。
transcribe.py 的 register_cuda_dlls() 会 os.add_dll_directory() 注册这些目录,
缺了这步仍会报 cublas64_12.dll not found。

Python 一律用:~\.workbuddy\binaries\python\envs\default\Scripts\python.exe

用法

PY="~/.workbuddy/binaries/python/envs/default/Scripts/python.exe"
SK="~/.workbuddy/skills/omni-digital-world/scripts"

# 0) 首次准备模型(默认装到私有配置 whisper_model_root)
#    本机已装好 small + medium,无需再下。large-v3 按需装(见「模型策略」表)。
#    $PY "$SK/prepare_model.py" --size medium      # 支持断点续传,中断后重跑会继续

# 1) 一条命令跑完:抽帧 + 抽音频 + 转写 → 产出 bundle
$PY "$SK/run.py" "<视频路径或链接>" "<工作目录>" --max-frames 24
#    默认 medium(准)。长视频初筛加 --model small(快 ~3 倍,用于摸结构)

# 1b) 只转写(不经抽帧):默认已带简体 initial_prompt
$PY "$SK/transcribe.py" "<音频或视频>" "<out.json>" --model medium --lang zh --device cuda
#    --no-prompt 关闭简体纠偏(更快,但 medium 会出繁体);--prompt "..." 自定义

# 2) Agent 读 <工作目录>/frames/*.jpg 理解画面,把要点写成 summary.md
# 3) 生成报告
$PY "$SK/report.py" "<工作目录>" --summary "<summary.md>" --title "标题"

流水线分层(可单独调用)

脚本 职责
prepare_model.py 把 whisper 模型下到本地目录(绕开 HF 缓存坑)
frames.py probe 探测元信息 / frames 抽关键帧(uniform/scene,自动回退)
audio.py 抽 16kHz 单声道 WAV
transcribe.py 本地转写,含 GPU→CPU 自动回退
fetch.py 网络视频下载(yt-dlp 自动安装)
channels_fetch.py 微信视频号下载:yt-dlp + yt-dlp-patch 的 wechat_channel 提取器(A 路线)
wxd.py 微信输入注入驱动器。tap()=触摸(Qt 自绘区可用)、mouse_tap()=鼠标(卡片/消息区必须用)、ls_all()=全量枚举顶层窗、kill_modal_dialogs()=清「搜索聊天记录」模态窗(不清则主窗点不动)、close_player()=WM_CLOSE 关播放器、place_strict()=尺寸校验式摆窗
wxchannels_pull.py ★★ E 路线首选:注入驱动微信 UI(等用户空闲→接管→清模态窗→进会话→鼠标点卡片播放→取链接→关播放器),并调 wxchannels_mem.py 取链接。内含 clean_url() 修掉 URL 尾部日志碎片导致的误杀
wxchannels_mem.py E 路线取数:扫 Weixin.exe/WeChatAppEx.exe 内存,提取 (CDN 直链, decode_key) 配对;--grab 可直接下载+解密+ffprobe 校验
card_mse_fetch.py 单卡端到端取片:clean_url 清洗 → 并发 Range 探测 → 只下 video/mp4 → wasm 解密 → ffprobe 校验时长。用于区分「正片」与「推荐流预加载片段」
inj_probe.py 注入通道真值探针:自建纯 Win32 窗口,记录收到的 WM_POINTER*/WM_TOUCH/WM_LBUTTON*。用来判断"点不动"是系统级注入坏了、还是微信侧不接收——本轮定位就靠它
wxchannels_decode.js 用官方 wasm_video_decode(WxIsaac64)解密前 131072 字节;配套 wasm_video_decode.js/.wasm
harvest_cached_video.py 从 msg\video\ 找出刚播过的 mp4(B 路线,已证伪,勿再当主路)
screen_record_av.py ★ D 路线首选:屏幕录制(画面+系统声)—— gdigrab + WASAPI loopback,先出 .ts 再合轨成 mp4
screen_record.py 屏幕录制(仅画面):gdigrab → mp4
screen_capture.py 屏幕取样:只留画面变化帧 + 拼版总览(PPT 录屏型最省空间)
run.py 总编排,产出 bundle.json
report.py 合成可核验 HTML 报告(内嵌关键帧 + 转写时间轴)

视频号相关依赖(一次性)

$PIP install yt-dlp-patch -i https://mirrors.aliyun.com/pypi/simple/

装完插件落在 <venv>/Lib/site-packages/yt_dlp_plugins/extractor/(含 wechatchannel.py、douyin.py)。
验证插件生效(不看 --list-extractors,它不列插件):

import yt_dlp.plugins as P; P.load_all_plugins()
from yt_dlp.globals import plugin_ies
print(list(plugin_ies.value))     # 期望看到 WeChatChannelIE

实测踩过的坑(务必照做)

  1. HF 的 Xet 协议对国内镜像 401 → 必须 HF_HUB_DISABLE_XET=1(脚本已内置)。
    否则报 CAS Client Error: HTTP status client error (401 Unauthorized) ... xethub。
  2. Windows 下 HF 快照符号链接退化成 0 字节 → ctranslate2 报
    File model.bin is incomplete: failed to read a value of size 4 at position 0。
    根因是 blobs/ 里文件完整、snapshots/ 里却是 0 字节。
    解法:用 prepare_model.py 把模型下到本地目录,transcribe.py 会优先用本地目录。
  3. 场景检测(select=gt(scene,x))对连续运动/手持视频会全灭(无硬切镜头 → 一帧不出)。
    frames.py 已内置:scene 模式结果 <2 帧时自动回退 uniform,保证永不返回空。
  4. pip 默认源在国内很慢(~138kB/s);清华源报 403 → 用阿里源
    -i https://mirrors.aliyun.com/pypi/simple/。
  5. 本机 bash 缺 coreutils(无 grep/tail/head/ls),输出须重定向到文件再用 Read 读。
  6. PowerShell Add-Type 被安全策略拦截(无法用 SAPI 造 TTS 测试音频)。
  7. GPU 会「假可用」,这是本项目最隐蔽的坑:静音音频被 VAD 全滤 → 根本不触发 GPU 计算
    → 看着「成功」;一遇到真实语音才暴露缺 cublas64_12.dll / cudnn64_9.dll。
    两道防线都要有:① 加载期回退(load_model);② 运行期回退(transcribe 里
    _do() 包 try/except)——只做加载期兜底会在真语音上直接崩。
    修复:装 nvidia-cublas-cu12 + nvidia-cudnn-cu12,并由 register_cuda_dlls() 注册 DLL 目录。
  8. VAD 默认开启:安静的纯环境音视频可能得到 0 段,这是正确结果,不是故障。
    判别方法:算音频 RMS,低于 ~0.01 基本就是环境噪音(实测工地视频 RMS=0.008 → 0 段正确)。
  9. 大模型下载必须能断点续传,否则永远下不完(下 medium 1.4GB 时实测):
    hf-mirror 会中途 stall,urlopen(timeout=120) 在第 834MB 处抛
    model.bin: The read operation timed out。
    原始写法的致命错误是失败时 os.remove(tmp) 删掉 .part → 已下的 834MB 全丢,
    重跑从头开始,在慢/抖的镜像上等于永远下不完。
    已修:fetch_one() 带 Range: bytes=&lt;have&gt;- 续传 + 最多 6 次退避重试 +
    失败保留 .part + Content-Length 完整性校验;服务器不回 206 时自动从头下。
  10. 模型没下完时不要静默退回联网:resolve_model() 命中不到本地目录会返回规格名,
    于是 ctranslate2 偷偷去 HF 重新下载(慢且可能再撞 Xet 401)。
    已加检测:存在 model.bin.part 时往 stderr 明确告警,提示先跑 prepare_model.py。
  11. medium 模型对中文会输出繁体(实测 手機/專業/銀行/轉賬/駐地/景區,繁体字种数 26),
    对大陆用户是明显可用性问题。已在 transcribe() 加简体 initial_prompt 引导:
    &quot;以下是普通话的句子,请使用简体中文转写。&quot;(lang 为 zh 时自动生效)。
    实测效果:繁体字种数 26 → 0;顺带修正 君子→军资、假带→甲带、速限→速献。
    代价:输出会带标点、token 变多,耗时 70s → 96s(+37%)。
    要关掉用 --no-prompt;要自定义用 --prompt &quot;...&quot;。
  12. yt-dlp 插件不会自动注册:pip install yt-dlp-patch 之后,
    不显式调 plugins.load_all_plugins() 就不生效,且 --list-extractors 也不列出插件
    (用 plugin_ies 查才对)→ 极易误判成「yt-dlp 不支持视频号」。
  13. 脚本文件头写 Windows 路径要用 r&quot;&quot;&quot; 原始 docstring:
    msg\video\...、\xwechat_files 里的 \x 会被当转义 → SyntaxError: unicodeescape ... truncated \xXX。
  14. 视频号页面无内嵌 JSON 时走腾讯元宝中转,缺 cookie 会直接失败并提示
    未提供腾讯元宝 cookie——这是预期行为,不是脚本坏了;补 --cookies 即可。
  15. 电脑微信播视频号=完全不落盘(2026-09-19 实测,比"未证实"更强):
    播放一条 2 分钟视频号期间,45 分钟内对 msg\video、cache、WMPF、all_users、
    temp 全量扫描,只有用户的截图与头像缓存被写入,无任何视频/音频文件落盘。
    ⇒ B 路线作废。不要再让用户"播一遍试试",直接改走 A 或 D。
  16. dshow 录不到声音 ≠ 录不到系统声(2026-09-19 订正,原结论偏保守):
    ffmpeg -f dshow -list_devices 报 Could not enumerate audio only devices ,
    真实含义是本机没有 DirectShow 音频采集滤镜(无麦克风、立体声混音未启用),
    不代表系统没声音可录。WASAPI loopback 走渲染端点,完全绕得开:
    soundcard(纯 ctypes,无需编译依赖)取
    sc.get_microphone(sc.default_speaker().name, include_loopback=True) 即可录系统输出。
    自验(播 440Hz 正弦、同时 loopback 录):peak=0.2000 / rms=0.1223 / 72.4% 有声样本。
    ⚠️ loopback 抓的是默认渲染端点:用户若把播放设备切到别的声卡(如前面板耳机),就录不到。
  17. PC 端视频号播放器没有「复制链接」入口(2026-09-19 用户实测):右上角只有
    分享/更多,点开都不给链接。链接只能从手机端拿(手机分享面板里才有)。
    另:屏幕录制脚本用 gdigrab 全屏抓,实测 1920×1080 稳定可用(ImageGrab 同样可用)。
  18. ★ 后台任务会在"我这一轮回复发出"时被回收(两次录屏翻车的真正原因,别只看表象):
    run_in_background 起的长任务(180s、210s 各一次)都在启动后约 2 分钟、
    也就是我发出回复的那一刻被杀。产物 mp4 只有 1.3~2.2MB 且 moov atom not found,
    plain / h264 / ignore_err 三种强行抽帧全部失败。
    ⇒ 凡是要跨越"我的回复"的长任务,一律前台跑(timeout 提到 300000ms 级),
    或者让用户自己在系统侧发起。不要用后台任务录屏。
  19. 录屏容器优先 .ts(MPEG-TS)而非 .mp4:ts 是流式容器,即使进程被强行中断仍可解码;
    mp4 中途被杀会缺 moov,整段报废。ffmpeg 一律带 -t 让其自行到点退出,不要从外部 kill。
  20. ★ 点击通道按「区域」分流 —— 20 号旧结论已于 2026-09-20 被实测推翻,以下为修订版:

    旧结论(作废,勿再引用):"微信 4.x 屏蔽合成鼠标、只接受合成触摸"。
    该结论是在窗口被外部(向日葵远程控制)改成 576×448、所有坐标整体偏移的那段时间得出的,
    当时"触摸有效/鼠标无效"的差异其实是坐标巧合,不是通道差异。

    修订结论(2026-09-20 隔离实测):
    ① 先用自建纯 Win32 窗口做真值测试,确认合成触摸在系统层完全正常
    (窗口收到 WM_POINTERDOWN / WM_POINTERUP / WM_POINTERENTER 及系统转换的 WM_LBUTTONDOWN/UP)。
    ② 再对同一张卡片逐通道对照(点播放键,全屏像素差):

    手法 结果
    InjectSyntheticPointerInput(触摸) ❌ 0 —— 卡片区不认触摸
    mouse_event(MOVE\|ABSOLUTE + 左键) ✅ 68901
    SendInput(绝对坐标左键) ✅ 44844
    PostMessage(WM_LBUTTONDOWN/UP) → 主窗 ✅ 45226
    PostMessage → MMUIRenderSubWindowHW ⚠️ 1223(弱)
    SendMessage → MMUIRenderSubWindowHW ⚠️ 1628(弱)
    同批对照:左栏头像(Qt 自绘) 触摸/鼠标/SendInput/PostMessage 四种全部 4315 ✅

    ⇒ 规律:Qt 自绘区(左栏头像图标、会话列表、搜索框)触摸与鼠标都行;
    聊天内容区(卡片、消息,由 MMUIRenderSubWindowHW 承载)只认鼠标。
    点卡片/播放键一律用 mouse_tap()(已加入 wxd.py)。
    ⚠️ 触摸标誌位若要使用仍必须 DOWN=0x00010000 / UP=0x00040000(写 0x1/0x4 返回 False 且看不出原因);
    CreateSyntheticPointerDevice 的 POINTER_FEEDBACK_MODE 只能取 0x1/0x2/0x3,传 0 报 ERROR_INVALID_PARAMETER(87)。
    ✅ 保留有效的排除项:UIPI/完整性级别(双方都 Medium)确实不是原因。
    21. ★ 注入前必须实测校验前台窗口,否则会点到用户正在用的软件上(2026-09-19 踩到):
    合成输入按前台窗口投递。我当时在微信未置前时连续发了几十个点击/按键,
    全部打进了用户正在操作的 D5 渲染器(其"选择文件夹"对话框还开着)。
    正确做法:每次注入前 GetForegroundWindow() == 微信hwnd;不等就先
    AttachThreadInput(本线程, 前台线程, True) → SetForegroundWindow → 解附 重试若干次;
    仍失败就抛异常中止,绝不盲目点。SetForegroundWindow 对后台进程会被 Windows 拒绝,
    AttachThreadInput 是解这个锁的标准手法;SwitchToThisWindow 可作补充。
    22. ★ 不要在用户正在用电脑时抢屏:窗口最小化时 GetWindowRect 返回 (-32000,-32000,…)。
    抢屏前先用 GetLastInputInfo() 等用户空闲 ≥2s(wait_idle),超时即放弃并告知用户,
    而不是硬抢。这条既尊重用户,也保证注入不会落错窗口。
    23. 视频号播放器会自动续播(用户 2026-09-19 明确提醒):一张卡读完立刻关闭再开下一张;
    否则播完自动进推荐流,会把无关视频的直链混进同一次内存读取,污染结论。
    ★ 关闭方法(2026-09-20 修订,Esc 与"点头部 ✕"都不可靠):
    播放器 = 独立顶层窗 Chrome_WidgetWin_0,标题 &#x27;微信&#x27;,pid 属 WeChatAppEx.exe,
    GW_OWNER=0;实测矩形 (429,165,1451,883),MSE 日志里的 &quot;hwnd&quot;:1771946 就是它。
    ① wxd.py 的 ls() 只枚举 Qt51514*,看不到它 → 必须全量 EnumWindows;
    ② 过滤「可见 ∧ 面积>50000 ∧ hwnd≠主窗」,或直接用主窗矩形之外的点 WindowFromPoint+GetAncestor(GA_ROOT) 反查;
    ③ PostMessage(hwnd, WM_CLOSE, 0, 0) 一次命中(实测生效,全屏差 26238)。
    备用退化=鼠标点其右上角 (rect.right-75, rect.top+30)。
    24. ★★ 头号坑:「搜索聊天记录」模态窗会把主窗点成"死的"(2026-09-20 定位,排查耗时最长):
    只要该窗还开着(哪怕被 SetForegroundWindow 挤到后面、肉眼看不见),
    主窗的所有鼠标/触摸输入都不产生任何响应。表现与"注入失效"完全一致:
    InjectSyntheticPointerInput 返回 ok=True, err=(0,0)、WindowFromPoint 命中主窗、
    IsWindowEnabled=True、IsHungAppWindow=False、SendMessageTimeout(WM_NULL) 有应答、
    坐标与 DPI 全对 —— 一切指标正常,就是点不动。
    - 识别特征:cls=Qt51514QWindowIcon、title=&#x27;搜索聊天记录&#x27;、独立顶层窗(实测 (591,195,1327,843))。
    GetLastActivePopup(主窗) 不可靠(这里返回主窗自己),别依赖它。
    - 处置:全量枚举顶层窗 → 凡 cls 以 Qt51514 开头且 hwnd≠主窗 的 WM_CLOSE 全部清掉,
    回读确认只剩 WxTrayIconMessageWindow(本身不可见,可无视)。
    - ⇒ 导航策略要改:别用搜索框(点了就会打开它)。直接点会话列表行;
    若会话已是当前会话,根本不用导航,滚到底即可。
    25. 微信主窗标题会变、WS_VISIBLE 可能被外部清除(2026-09-20 实测):
    主窗标题实测出现过 &#x27;zp&#x27;(不是「微信」),且窗口可能被外部(远程控制/用户操作)最小化。
    ⇒ 选主窗必须按类名 Qt51514QWindowIcon 取面积最大者,不可依赖标题;
    activate() 要先判 IsWindowVisible,不可见就 ShowWindow(SW_RESTORE=9) 再置前。
    另:窗口尺寸可能被外部改成 576×448 之类 → 每次动作前用 place_strict() 循环校验目标尺寸,
    不符就重试,否则坐标整体偏移、所有点击都会静默落空(这是本轮绕远路的总根源)。
    26. 内存里取到的 URL 尾部粘着日志碎片,_fast.valid() 会误杀(2026-09-20 定位):
    MSE 的 url 字段后面直接跟着 \&quot;startIdx\&quot;:…、\&quot;respHeaders\&quot;:…,还夹 \x00 二进制噪声。
    判据 &quot; &quot; not in u and &quot;...&quot; not in u 拦不住。
    正确做法:按 URL 合法字符集截断——逐字符收集到第一个非法字符即停,再 rstrip(&quot;&amp;&quot;):
    ALLOWED = set(&quot;A-Za-z0-9-._~:/?#[]@!$&amp;&#x27;()*+,;=%&quot;)。
    实测清洗后 6 条候选 → 2 条真 video/mp4(47.51MB / 8:26.85 与 1.42MB / 0:31.62),解密全部成功。
    ⚠️ 同一轮里会同时出现多条流:正片与推荐预加载片段混在一起,靠 ffprobe 时长区分
    (本次 8:26.85 才是正片,0:31.62 是预热片段)。
    ★ Esc 关不掉视频号播放器(实测:Esc 后仍在播,且已自动续播到下一条推荐视频)。
    必须点播放器头部最右侧的关闭按钮:把窗口固定在 SetWindowPos(940,20,960,1020) 后,
    窗口内偏移 (885, 55) 即可关掉((795,55) 是"不感兴趣",别点错)。
    关闭判据:窗口右侧中部区域平均色由视频画面色 → 变回浅灰 (221,223,225)。
    另外:窗口打开播放器后会变宽(实测 896→965,右边缘 2127 超出 1920 屏),
    关闭按钮落到屏幕外点不到 ⇒ 驱动前先用 SetWindowPos 把窗口固定到屏内固定矩形。
    24. 旧 URL 的 token 会过期,这反而是筛配对的好判据:内存里常同时残留历史配对的
    stodownload 直链,重新下载会得到 HTTP 400。因此「播放后扫一遍内存 → 逐条试下载 →
    只有新的那条能下」即可自动锁定目标视频,不必先建基线再比对。

实测性能基线(本机 RTX 2080 SUPER 8G + 8 核 CPU,113 秒中文解说)

模型 设备 耗时 相对实时 GPU 提速
small CPU int8 35.7s 3.2× —
small GPU float16 23.3s 4.8× 1.53×
medium CPU int8 95.3s 1.2× —
medium GPU float16 70.0s 1.6× 1.36×
medium GPU float16 +简体prompt(默认) 96.2s 1.2× 见坑 #11

> ⚠️ 注意:上表 medium 的 70.0s 是未加简体 prompt 的旧值。简体纠偏是当前默认行为,
> 实际默认耗时是 96.2s。要跑得快就 --no-prompt(但会出繁体)。

⚠️ 一条被自己实测否证的推断,必须记下来:曾推测「模型越大,GPU 收益越显著」——
实测相反:small 提速 1.53×,medium 反而只有 1.36×。
原因推断(未单独验证):本机 8 核 CPU 的 int8 量化效率很高,而 RTX 2080 SUPER 属 Turing 架构,
float16 加速有限;ctranslate2 在 CPU 侧的开销摊薄了差距。
结论:不要把 GPU 当作大模型的性能解药,它只是稳定省下约 1/3 时间。

  • GPU 与 CPU 结果段数/字数完全一致,仅 1 个同音字差异(float16 vs int8 数值差)→ 精度无实质损失。
  • 抽帧:20 帧(uniform)秒级完成;模型加载与音频抽帧是主要固定开销。

模型策略(用户定档 2026-09-19,三档固定)

档位 模型 定位 何时用 本机状态
初筛 small 快 长视频先摸结构、只需要大意 ✅ 已装 464MB
默认 medium 准 不特别说明时一律用这档(脚本默认值已改为 medium) ✅ 已装 1.53GB
精修 large-v3 最准 需要的时候才装,平时不预装 ⬜ 未装(约 3.1GB)
  • 默认值已落在代码里:run.py / transcribe.py 不带 --model 即为 medium,无需每次手写。
    实测确认(22s 片断、不带 --model):model=medium / device=cuda / ct=float16,10.1s 出稿,简体纠偏正常(繁体命中 0)。
  • 初筛提速:--model small(实测同样 113s 音频:small 23.3s vs medium 70s,快约 3 倍)。
  • large-v3 按需安装(想用再装,别提前占盘):
    bash $PY &quot;$SK/prepare_model.py&quot; --size large-v3 # 装到私有配置 whisper_model_root
    装完直接 --model large-v3 即可,查找链会自动命中;支持断点续传,可随时中断重跑。
  • ⚠️ large-v3 本机尚未实测(耗时/显存占用/中文质量均无数据)。
    在真跑过一次之前,不得对外声称它更快或更准——只能说"规格上更大"。
    GPU 收益不是随模型变大而上升(small 1.53× > medium 1.36× 已实测反例),
    large-v3 在 8G 显存的 2080S 上更可能受显存限制,首次使用请先留意是否回落 CPU。

质量纪律

  • 转写与读图都可能有误差 → 报告必须附原帧 + 转写时间轴供人工核验。
  • 视频无语音、画面无可读信息时,明确说「无内容」,绝不脑补。
  • 长视频先用 small 摸清结构,定稿再换 medium/large-v3 精修。
  • 转写文本可能含真实人名/甲方/证件号 → 参照用户脱敏红线,产出前扫描并提示。

第 B 篇 · 把文案做成竖屏成片(成片线)

> 目标产物:1080×1920 / 30fps / 带配音与字幕的 mp4,并附字幕 srt 与可二次剪辑的素材包。
> 与本篇外的关系:ImageGen 负责出图,ffmpeg 负责成片;两者都是本地调用,不上传素材。

一条可复制的 6 步流水线。目标产物:1080×1920 / 30fps / 带配音与字幕的 mp4,
并附字幕 srt 与可二次剪辑的素材包。

0. 本机硬约束(先读,能省掉一半返工)

事实 应对
本机没有 ffmpeg pip install imageio-ffmpeg,用 imageio_ffmpeg.get_ffmpeg_exe() 拿静态二进制(7.1 版自带 libx264 / aac / drawtext / libass / zoompan / xfade,够用)
Bash 工具不可用(PortableGit shim 缺 dirname/head/grep) 一律走 PowerShell
PowerShell 工具的 stdout 常被吞掉 让 Python 脚本自己写 UTF-8 日志文件,再用 Read 读
Start-Process 被安全策略拦截 用 &amp; $py script.py;需要 tee 就在 Python 里用 runpy + 自定义 Tee 落盘(见 scripts/run_build.py)
Bash 里 cmd /c 也被禁 用原生 PowerShell 语法
中文字体可用 msyhbd.ttc(粗) / msyhl.ttc(细) / Deng.ttf(等线)
图像生成工具会在右下角烧水印 见第 2 步,必须裁掉

1. 写文案 + 分镜(JSON 驱动)

先落一份 script/xxx_script.json,每段包含:

{ &quot;id&quot;: &quot;s1&quot;, &quot;label&quot;: &quot;开场 · 钩子&quot;,
  &quot;narration&quot;: &quot;……(会被 TTS 逐字读出,也是字幕的唯一来源)&quot;,
  &quot;image_prompt&quot;: &quot;……(实拍风格描述,结尾固定加『竖构图,主体居中,上下留白充足』)&quot;,
  &quot;duration_hint&quot;: 11 }

要点
- 段数 8–10、每段旁白 20–40 字,成片约 2 分钟。太短撑不起科普,太长完播率掉。
- image_prompt 必须统一色调(如「暖白 5600K + 奶油黄 + 原木棕」),否则九张图拼起来像九个片子。
- 提示词结尾固定加 「主体居中,上下留白充足」 —— 后面要按 9:16 裁两侧,留白是对裁剪的保险。
- 字幕不要另写一份。字幕直接由 narration 自动断句生成,避免「配音念了但字幕没写」的错位。

2. 生成配图并去水印(必做)

先 ImageGen 逐段出图(size: 1024x1536,quality: high)。

图像工具会在 1024×1536 画布的 y=1483..1518、右下角烧入「AI生成 WORKBUDDY」水印。
原图裁掉底部 66px(保留 1470)即可完全去除,构图无损。完成后务必复检:
拼一条「干净图底部 110px」的对照条带肉眼确认(scripts/dewatermark.py 会生成)。

> 别用边缘密度/亮度阈值做自动水印检测 —— 木纹、大理石纹、窗帘高光会大量误判,
> 直接实测固定位置裁切更可靠。

3. 分场景配音 + 时长测量

edge-tts(zh-CN-XiaoxiaoNeural,rate=&quot;+6%&quot;),每段一个 mp3。
用 ffmpeg 解析 Duration: 拿每段真实时长(不要按字数估算,中文语速波动很大)。

4. 字幕卡:用 Pillow 预渲染,不要用 drawtext

每张卡 = 一张 1080×1920 透明 PNG:墨绿圆角底卡 + 左侧砂金竖条 + 白字 + 数字/关键词砂金高亮。
再在 ffmpeg 里 overlay=0:0:enable=&#x27;between(t,a,b)&#x27; + fade=t=in:st=a:d=.22:alpha=1。

要点
- 断句:按 ,。!?;: 切从句 → 合并到 ≤24 字 → 贪心折行,保证每行 ≤14 字
(14 字 × 60px ≈ 840px,加内边距 932px < 1080 安全)。
- 折行优先在标点后断,否则硬断。
- 再做一层自适应字号:超出安全边距就 −2px 重排。
- 卡片底边放在 y=1400:抖音/视频号底部约 400px 被 UI 遮挡,字幕压在 1400 以内才不被盖。
- 页眉(左上角色块 + 03 / 09 + 章节名)单独一张 PNG,整段常驻。

5. 镜头运动 + 转场

关键:先裁成 9:16,再进 zoompan,否则 zoompan 会把非等比方图拉伸变形。

[0:v]scale=-2:2160:flags=lanczos,crop=1216:2160:(iw-1216)/2:0,setsar=1,
     zoompan=z=&#x27;1.02+(1.17-1.02)*on/N&#x27;:x=&#x27;(iw-iw/zoom)*(0.5)&#x27;:y=&#x27;(ih-ih/zoom)*(0.34+0.18*on/N)&#x27;:
     d=1:s=1080x1920:fps=30
  • z/x/y 用 on 做线性插值(比 zoom+0.001 累加更可控,能精确控制起止)。
  • d=1 + 输入 -loop 1 -framerate 30 -t L:每输入帧出 1 帧,zoom 状态跨帧连续,效率最高。
  • 逐段换推/拉方向和位移方向(9 段一套参数表),否则九段都是一个味儿。
  • 段间转场 xfade=transition=fade:duration=0.5。
  • 单段时长 L = 0.45(前导) + 旁白时长 + 0.85(尾部),末段尾部给 1.7s 收住。

6. 音频对位(xfade 下的时间轴算法)

xfade 链式拼接时,第 i 段在总时间轴上的起点不是 ΣL,而是:

start_i = Σ(L_k, k&lt;i) − i × T          # T = 过渡时长(0.5s)
总长 V  = ΣL − (n−1) × T

旁白按 adelay=&lt;ms&gt;|&lt;ms&gt; + amix=inputs=n:normalize=0 放到各自起点,
再 apad,atrim=0:V 对齐总长。这套比 concat 更可控,且天然处理了过渡期的音频交叠。

响度:人声直混通常只有 −19 ~ −21 LUFS,手机上明显偏轻。
必须做 EBU R128 两遍法 loudnorm 归一到 I=-15 LUFS / TP=-1.5 dBTP:

第一遍:-af loudnorm=I=-15:TP=-1.5:LRA=11:print_format=json -f null -
第二遍:-af loudnorm=I=-15:TP=-1.5:LRA=11:measured_I=..:measured_LRA=..:measured_TP=..:measured_thresh=..:offset=..:linear=true

最后 -c:v copy 直接封装,不再重编码画面。

7. 交付与自检

  • 成片:output/*.mp4
  • 字幕:标准 srt(时间码按第 6 步的绝对起点换算)
  • 素材包:01_图片 / 02_配音 / 03_字幕 / 分镜表.csv,可直接拖进剪映二次剪辑
  • 自检:抽 16 帧拼 contact sheet 肉眼过一遍(scripts/qc_check.py),
    并用 ffmpeg -i 核对分辨率 / fps / 时长 / 音轨。

踩过的坑(务必避开)

  1. 字幕时间轴把 len(cue) 当字符串长度用 —— 但 cue 是 [[line,…],…] 结构,
    len() 返回的是行数。结果:时间轴被拉满,每段只有第一张字幕卡生效、
    其余卡因 t0 &gt; t1 被 between() 静默丢弃。统计一律用 len(&quot;&quot;.join(lines))。
  2. scale=W:H 写死两组数会拉伸画面 —— 用 scale=-2:2160 让高度定、宽度自适应。
  3. 字幕卡溢出:折行没做硬约束时会出现 17 字一行撑破卡片,必须「贪心折行 + 自适应字号」双保险。
  4. 别把成片旁白写死在字幕里:字幕由 narration 自动切,改稿后两边一起变。

附:脚本

scripts/ 下是本篇(B 篇 · 成片线)的可用实现,工作根目录由环境变量 OMNI_VIDEO_ROOT 指定(不设则取「当前目录/egg-video」),换项目只需设这一个变量:

文件 作用
run_build.py 运行入口:把 build 的输出以 UTF-8 落盘(绕开 PowerShell stdout 吞输出)
step1_prepare.py 图片归位映射 + edge-tts 配音 + 时长测量
dewatermark.py 裁掉底部 66px 去水印,生成 images_clean/ 并出自检条带
build_video.py 主流程:字幕卡/页眉 PNG → 逐段镜头 → xfade 拼接 → BGM 合成 → 混音 → 响度归一 → 封装 → 导出 srt 与素材包
fix_audio.py 只重做响度归一与封装(画面已渲好时用,省掉 5 分钟重渲)
qc_check.py 抽 16 帧拼预览图 + 打印成片参数

附 · 融合说明与位次依据

融合性质:视频域两源融合(多部并成一部),不是吸收。

项 内容
产物 omni-digital-world(显示名 万象-数字世界)
宿主 万象-视频-理解(资产最全:12 脚本 + 完整工具链文档),零拷贝改造其原目录
成员 omni-video-vertical(万象-视频-竖屏成片,6 脚本移入同一 scripts/)
篇界 A 篇 = 理解线(一产一消中的「消」);B 篇 = 成片线(「产」)。判据边界清晰,故分篇而非混合
反判据 R1 原勘察判出「一产一消」,按规则须用户裁决;2026-09-19 用户裁定执行合并,理由成立:两线共用同一套 ffmpeg/Python 底座,且实际使用中常串联(先看懂再改写发出),分属两部时每次都要跨技能手工搬路径
U1 能力不缩水 两源的每一节全部进入产物,销账 FAILS=0
U2 调用省一步 「先理解再成片」由跨两部 → 一部到底,中间产物不再跨技能搬运
U3 底座只一份 scripts/ 只有一份(18 个脚本),ffmpeg/Python 依赖表只写一次
名称合规 目录名 = frontmatter.name = omni-digital-world(纯 kebab,符合 Agent Skills 开放标准);中文显示名 万象-数字世界(L0 未习) 住 _user_meta.json
旧指纹 两源旧 copyright.fingerprint 已随正文重组失效,产物暂不盖章(版权章统一在整改收口后重盖)

外置私有配置(不随包外发,因原值是本机绝对路径):

用途 位置 覆盖方式
whisper 模型根目录 ~/.workbuddy/omni-digital-world.json 的 whisper_model_root 环境变量 WHISPER_MODEL_DIR 或 OMNI_DW_CONFIG
成片工作根目录 环境变量 OMNI_VIDEO_ROOT(未设则取当前目录下 egg-video) —

常见问题

5 问
答案由本站依据条目字段整理
万象-视频理解与转写怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_de2353f3/omni-digital-world。安装方式为未标注。装完刷新技能列表或重开客户端。

万象-视频理解与转写支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

万象-视频理解与转写权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

万象-视频理解与转写收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

万象-视频理解与转写用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。