DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins

dsh-video-storyboard

Video Storyboard

Turn a video into storyboard images you can cite: one frame per shot, cell size follows cut density (1/1 … 1/8), row-packed into as few images as possible, with a timestamped index the model can quote and a tier map it can override.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add dsh-video-storyboard@0.6.7
READMECompatibilityVersions

Compatibility and provenance

Video Storyboard is published as dsh-video-storyboard and currently resolves to version 0.6.7. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
npm
Registry updated
11/4/2026

Versions

0.6.7stable
11/4/2026
0.6.6stable
11/3/2026
0.6.5stable
11/3/2026
Show 30 more versionsCollapse versions
0.6.4stable
11/2/2026
0.6.3stable
11/2/2026
0.6.2stable
11/1/2026
0.6.1stable
11/1/2026
0.6.0stable
11/1/2026
0.5.12stable
10/31/2026
0.5.11stable
10/31/2026
0.5.10stable
10/31/2026
0.5.9stable
10/31/2026
0.5.8stable
10/31/2026
0.5.7stable
10/31/2026
0.5.6stable
10/31/2026
0.5.5stable
10/31/2026
0.5.4stable
10/30/2026
0.5.3stable
10/30/2026
0.5.2stable
10/30/2026
0.5.1stable
10/30/2026
0.5.0stable
10/30/2026

Related plugins

Loading related plugins…

Latest
0.6.7
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
2.4 MB
Files
102
Surface
web
License
Apache-2.0
Source
npm
GitHub
★ 426
Weekly downloads
19,399
Security scan
✓ v0.6.7 scan passed
Last push
11/4/2026
View source ↗Project homepage ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
0.4.5stable
10/30/2026
0.4.4stable
10/29/2026
0.4.3stable
10/28/2026
0.4.2stable
10/28/2026
0.4.1stable
10/28/2026
0.4.0stable
10/28/2026
0.3.2stable
10/28/2026
0.3.1stable
10/28/2026
0.2.2stable
10/27/2026
0.2.1stable
10/27/2026
0.3.0stable
10/27/2026
0.1.0stable
10/27/2026

Related plugins

More verified plugins in vision-media.

Experimental Voice Input Bundle@deepseek-ai/dsh-experimental-voice-input-bundleExperimental voice input with local SenseVoice; downloads its runtime on first useModlens@liustack/modlensPlug-in vision for text-only LLMs, powered by the free Antigravity CLIImage Gendsh-image-genBring ChatGPT-like image generation to DeepSeek Harness — Gemini, OpenAI, Seedream, DashScope, local ComfyUI & more.Tool Describe Image@linxin666/dsh-tool-describe-imageModel-facing describe_image tool for the dsh web GUI: gives a text-only model image understanding by asking a vision-language model at an OpenAI-compatible endpoint to describe one image (local path, http(s) URL, or attachment reference). Hot-pluggable —

README

dsh-video-storyboard

让 AI 真正"看过"一段视频:一张分镜图 + 一份带时间码的文本清单,模型据此回答"第 3 分钟发生了什么""这句话对应哪一格"。

English: README.en.md

已收录于 awesome-dsh-plugin。

0.6.0 大改(2026-11-01):切点之后整条链路重写 —— 一帧 = 一个镜头,格子大小 = 那个位置的 切点密度(1/1 … 1/8 共八档),排布用 DP 按行打包,最后把画布裁到内容。 cells / sheets / need / maxPerSheet / adaptive / tileFirst 已删除, 换成 maxFrames / strategy / maxSheets / tierMap。迁移与完整差异见 CHANGELOG。

协议变更(v0.5.0):本项目由 MIT 变更为 Apache-2.0。变更的目的是更好地保护代码版权 (要求保留署名),同时依旧允许大家自由使用、修改,甚至商用。此前基于 MIT 发布的版本仍是 MIT, 已经产生的衍生版本不受影响。感谢所有使用者和开发者。

边界:本项目不托管、不分发任何视频内容,缓存只在本地且会自动淘汰;不提供付费内容访问、 DRM 绕过、区域限制绕过或批量抓取能力。能不能下载某个链接取决于你有没有相应授权, 请自行确认并遵守目标站点的条款与当地法律。完整说明见 NOTICE。


它解决什么问题

装完不能跑?先执行 node bin/doctor.mjs —— 它会检查 ffmpeg / yt-dlp / ASR / 缓存目录, 每条都跟一句"缺什么、怎么补"。用户反馈里过半不是 bug,是没配 ASR 或装完 ffmpeg 没重启 DSH。 新手步骤:docs/入门-五分钟.zh.md · 报错对照:docs/FAQ.zh.md

没配转写 = 它只能看图说话。 能描述"画面上有个女孩在跑",但说不出她讲了什么。 本机 ASR 免费、不联网,装一次十几分钟 —— 这是这个插件价值差别最大的一步, 别等到问"她说了什么"发现答不上来才回头配。见 FAQ Q1。

多模态模型看视频有两种笨办法:

  • 逐帧抽图 —— 10 分钟片子按 1 fps 抽就是 600 张图,token 直接爆。
  • 只喂字幕 —— 画面里的信息(构图、动作、场景变化)全丢。

本插件走第三条:把画面压成一张宫格图(384 token 封顶),旁边配一份按时间对齐的文本清单(每格起止时间 + 这一格里的台词 + 切了几刀)。

模型得到的是"这 90 秒里发生了什么、大概在第几秒",需要细节时再用 video_zoom 放大某一段。

0.6 起,一格 = 一个镜头,格子大小不再一样:20 秒的长镜头占满整幅,0.3 秒的快闪只有 1/8 边长。 所以一张图里同时写着"这里是个长镜头"和"这里 24 个镜头连闪"——不读文字,看格子大小就知道节奏。

五个工具

工具干什么何时用
video_probe只读容器头:多长、分辨率、帧率、有没有音轨/字幕、已分析过没有第一次接触一部片子时先调它,不花图片 token
video_storyboard出宫格图 + 文本清单主体功能
video_zoom只看某一段:一张图、n 帧、统一 ceil(√n) 档(9 帧就是 3×3 满格)清单看完觉得某段重要,再放大
video_locate纯文本:一句话 → 时间码知道说了什么、不知道在第几分钟
video_subtitles把画面里的硬字幕裁成一张竖长图 + 每条的秒数没有字幕轨、ASR 也转不出来(PV / 唱歌 / 双语艺术字)—— 只能看的时候

video_storyboard 的 quality(low/medium/high)只影响贴链接时的下载清晰度, 本地文件永远读原片。默认 medium(720p);嫌糊传 high,嫌慢传 low。 价位和清晰度的取舍见 FAQ「关于钱和清晰度」。

档位:格子为什么大小不一样

图片按像素计费(42px 一格:rows×(cols+1)+2,地板 544²、封顶 994 @1300×1300),格子越小能塞的帧越多 —— 所以"这一帧占多大"本身就是信息量分配,也直接决定钱。

帧 = 一个镜头(真切点切段 + 积分采样点补段),档位 = 那个位置的切点密度:

档位格子边长800 宽画布上的实际像素典型场景
11/1800×45020 秒的长镜头
21/2400×2253–5 秒的常规镜头
41/4200×1131 秒左右的快切
81/8100×560.3 秒一闪而过的快闪

档位是算出来的(100 帧卷积窗 → 每帧切点概率 → 50 帧平滑 → 7 阈值 + 5% 滞回), 由积分假切点切出来的帧再 +3 档(那里画面一直在变,一帧代表不了多久)。 两个旋钮可以覆盖它 —— 一个是预设,一个是精确映射:

video_storyboard({ input, strategy: 'saver' })                    // 预设:每帧降一档
video_storyboard({ input, tierMap: { 5: 4, 6: 4, 7: 4, 8: 4 } })   // 精确:5–8 档一律按 1/4

六个档位(第一个是默认)

strategy每帧多大90 秒 PV 实测什么时候用
auto(默认)张数按时长定(≈4 分钟一张),再挑最小格还认得出的最省那档1~3 张什么都不填时就用它
full升一档(1 档不再升)18 张 / 6912 token要读画面里的字、认脸
standard1 档压成 1/25 张 / 1920想看清每个镜头
compact降一档3 张 / 1152日常:看得清又便宜
skim全部 1/8,无假切点,切点按 0.35 s 合并1 张 / 384一条 PV 一张图
brief全 1/8,切点间隔自适应加大到整段装进一张图1 张 / 3845 分钟视频一张图看完

auto 是 0.6.1 的默认值,起因是一次真实的账:新会话看到枚举里第一个名字是 full, 就选了它 —— 一条 90 秒 PV 花掉 6912 token,是 compact 的 6 倍。模型看不见"多一张图多少钱", 所以选档这件事收回工具自己:

  • 张数只按时长算(autoSheetCap,默认 240 s/张,DSH_STORYBOARD_SECONDS_PER_IMAGE 可调): 5 分钟 → 1~2 张,30 分钟 → 8 张,2.5 小时 → 39 张。同一时长的片子,钱是可预期的。
  • 在预算内挑最省的那档:张数一样时 compact 装得下更多帧。只有当最小格已经认不出东西 (< shot)才往 standard / full 退;压缩到色块就翻倍张数重试(最多三轮)—— 预算让位于可读性。
  • 结果里会写 size policy: auto chose … 和"同一批帧在别的档要几张",下一次它就有依据覆盖。
  • 不想要 auto:DSH_STORYBOARD_AUTO=0,或 ~/.dsh/video.json 里 {"storyboard": {"auto": false}}, 退回 compact(0.6.0 的默认值)。

不知道这部片有多少刀时,先用 dryRun: true 问一次(扫描有缓存,第二次几乎免费):

video_storyboard({ input, dryRun: true })
// → cuts="46 (avg 2.0s)" · frames=21 · compact 1 image / standard 1 / full 2 / skim ≤1
//   recommended: compact
video_storyboard({ input, strategy: 'compact' })   // 再真正出图

0.6.0 之前叫 quality / balanced / saver / frugal / thrifty / ultra(还有"乞丐/恭喜发财/丐中丐" 这种名字)。旧名仍然认,会自动映射到新名,不会报错。

tierMap 是精确覆盖,接在 strategy 之后:没列出的档位保持原值,多档映射到同一档等于合并尺寸, 补位帧(自由尺寸)不参与映射,越界的键值直接忽略。清单里会回显 tierMap=1->1,2->1,…。

为什么不是固定宫格

旧版(≤0.5.x)是"你要几格,我就切成 k×k"。问题不在实现,在前提:它假设所有镜头一样重要。

一段 90 秒的 PV 里,前 20 秒可能只有 6 个长镜头,后 20 秒有 24 个 0.25 秒的快闪。固定 16 格平均分, 结果是长镜头浪费了 12 格的面积,快闪被压成 100×56 的色块 —— 两头都没照顾好。

现在这张图是这么排出来的:

  1. 帧 = 镜头。切点切段,一段一帧。长镜头就是一帧,快闪也是一帧,时间轴永远连续。
  2. 格子大小 = 那个位置的切点密度(8 档,边长 1/k)。快的地方小格、慢的地方大格, 同一张图里两种节奏同时可见 —— 不用读数字,看格子大小就知道哪里开始快剪。
  3. 按行打包 + 裁到内容。装不下才开下一张;排完把底部/右侧的空白裁掉,所以每张图高度不同, 但内容一点不少。
  4. 补位。某张图只剩 2 帧(片尾标题 + 演职员表)时,把那几段再切几刀,凑成能铺满的格数。

实测同一段 90 秒 PV:

版本张数token快剪区格子长镜头格子
0.5.x 固定 16 格2768100×56(糊)200×113(浪费)
0.6.0 档位排布3(compact)1152114×54 但按密度分配400×225

多花 384 token,换来长镜头 4 倍清晰度 + 快剪区不再裁掉镜头。这是这套算法唯一想做的事。

安装

从零开始的完整步骤(含依赖、自检、排错)见 docs/INSTALL.zh.md。

三句话版本:

winget install Gyan.FFmpeg                    # 必须:ffmpeg + ffprobe
pip install --user -U yt-dlp                  # 只有贴 B 站/YouTube 链接才需要
dsh plugin --profile web add dsh-video-storyboard       # 装插件,然后重启 DSH

dsh plugin 只管 web 这类普通 profile;桌面版(Electron)的 desktop profile 会被拒绝, 用源码安装:git clone 后 node scripts/install-dev.mjs(复制目录,改完要重跑)。

依赖必须吗不装会怎样
ffmpeg + ffprobe必须第一步就报错
yt-dlp可选本地文件照常能用,贴链接不行
ASR 服务可选能出图,但清单里没有台词

插件不打包 ffmpeg 和 yt-dlp,只调用你自己装的那份。找不到时会给出安装指引,不会静默失败。

怎么用

贴一个链接

https://example.com/some-video

任何 yt-dlp 支持的站点都行(YouTube、Vimeo、各种自建站……完整列表见 yt-dlp 文档)。 插件会自己下载、缓存,然后用本地文件的那套流程跑。竖屏、DASH、多分P 都处理了。

⚠️ 能不能下,取决于你:请自行确认对目标内容有下载/使用授权,并遵守该站点的条款与 当地法律。本项目不提供任何绕过付费、会员、DRM 或区域限制的能力,也不代为判断授权。

本地文件

node bin/storyboard.mjs film.mp4 --out film.png                    # 默认:均衡,一条 90 秒 PV 5 张
node bin/storyboard.mjs film.mp4 --strategy thrifty --out cheap.png # 全 1/8,一张图 384 token
node bin/storyboard.mjs film.mp4 --strategy ultra --out one.png     # 丐中丐:间隔自适应到装进一张
node bin/storyboard.mjs film.mp4 --tier-map '{"5":4,"6":4,"7":4,"8":4}'   # 快剪全部按 1/4
node bin/storyboard.mjs film.mp4 --max-sheets 4 --out film.png      # 硬性限制张数
node bin/storyboard.mjs film.mp4 --batch 300 --out film.png         # 长片按 5 分钟切窗
node bin/asr-check.mjs film.mp4 --from 100 --seconds 12             # 只测 ASR

参数

参数默认说明
input—本地路径或 http(s) 链接
from / to全片只看这一段
maxFrames0(不限)最多取多少帧(一段 = 一个镜头)。0 = 不封顶:几千刀就几千帧;填正数才按"合并最短的段"收缩
strategyautoauto(默认):先按视频时长定张数预算(≈4 分钟一张),再在 compact/standard/full 里挑"最小格还认得出、张数最少"的那档,并把三档各自的代价写进清单。显式值:full 每帧升一档 / standard 只把 1 档压成 1/2 / compact 降一档 / skim 全 1/8 + 无假切点 + 切点按 0.35s 合并 / brief 切点间隔自适应加大到整段装进一张图(旧名 quality/balanced/saver/frugal/thrifty/ultra 仍认)
dryRunfalse只扫描 + 排布,不出图、不取帧、不转写:回报切点数、帧数、每个档位要几张图。用来在花第一张图之前决定档位
scanGap0两个切点之间至少隔几秒(0 = 不合并)。调大能把连闪的镜头并成一个边界
integraltrue是否在长镜头里按"画面变化积分"补帧。关掉就只留真切点
integralSeconds6一个采样帧代表多少累积变化(1–30)。阈值对着固定参考强度,不是"每 N 秒一帧":画面动得快就补得密,慢就补得疏,完全静止的镜头一个点都不补
tierMap—档位映射表 {5:4,6:4}:接在 strategy 之后,未列出的档位保持原值;多档映射到同一档 = 合并尺寸
maxSheets0(不限)最多几张图。0 = 排布器说几张就几张;填正数则超了合并段重排(时间覆盖不变)
budget360000单图像素,默认 360k = 800×450(≈233 token)。上限 1.69M = 1300×1300(≈994 token):按像素线性计费,抬上去是加钱买清晰度,不是免费;低于 ~296k 也不再便宜(计费时按 544² 放大算,192 token)—— 所以给更小的 budget 会被抬到地板并在清单里说明
pickmid取帧策略:mid 段中点 / sharp 3 候选选最锐(3 倍解码)/ thumb 段内最典型
burnindex烧格号;none 关掉
transcriptauto

输出长什么样

一张图 + 一段文本:

<storyboard path="film.mp4" duration="18.40" window="0.00-18.40" image="800x469"
            grid="30 frames on 1 image(s)" burn="index" cell>=93x47
            tokens="233" cuts="29 (avg 0.6s)">
plan: strategy=balanced (1 档压成 1/2 尺寸) · 30 frames on 1 image(s) ·
      tiers 1/2×5 1/4×1 1/6×2 1/7×2 1/8×20 · canvas=800x450 · fill=99.4% · maxFrames=96
cost: 1 image(s) ≈ ~233 vision tokens (233/img avg; 42px token grid, floor 544², cap 994)

cell 1 = 00:00.00-00:02.00  k=2 (1/2)
cell 7 = 00:12.00-00:12.27  cuts 1  k=6 (1/6)
    "这段台词"

一行一个镜头,k= 就是这一帧的档位(边长 1/k)。长镜头是大格、快剪是小格, 所以一张图里同时能看到"这里是一个 20 秒的长镜头"和"这里是 24 个 0.25 秒的快闪"。

几条设计原则:

  • 清单只给原材料,不下结论。 不说"这是高潮""这里是角色 A",只给时间、刀数、台词 —— 判断是模型的事。
  • 每加一行都要能说出它挡掉了什么误判。 说不出来就不加。
  • 数字不可信时要说出来。 例如连续运镜被逐帧切碎时,清单会写 cut count unreliable here,而不是让模型拿 cuts=N 当节奏依据。

算法要点

  1. 画面差异:缩到 64×36,逐帧比 HSV,色相按环形距离且按饱和度降权(灰色的色相没有意义),得到 0–255 的变化分。
  2. 切点:分数超过阈值即一刀。阈值不是固定的 —— 按本片自己的分数分布和时长推。
  3. 爆点重判:对每个 2 秒窗口,用一帧的间隔重新判一次。粗扫看不见 0.35 秒以下的刀,而快闪全在那一档。
  4. 帧 = 镜头:真切点切段,积分采样点在真切点之间补(画面变化率累够一个阈值就记一个假切点、 然后清零,往复)。一段一帧,时间轴永远被完整覆盖。
  5. 档位 = 切点密度:把切点看成 0/1 函数,用 100 帧宽的窗做卷积得到"每帧切点概率",再平滑; 7 个阈值 + 5% 滞回把它切成 8 个档。档位 k 的格子边长就是 1/k。 由积分假切点切出来的帧额外 +3 档 —— 那里画面在连续变化,一帧代表不了多久。
  6. 排布 = 按行打包:档位序列喂给一个 DP,按行精确最优地填满方形画布,装不下才开下一张。 排完再把画布裁到内容(底部/右侧不留灰条),所以每张图高度不同、内容一点不少。
  7. 补位:排完发现有图填充率 < 68%(片尾标题 + 演职员表那种),就把那张图里最长的几段 再切几刀多取几帧,把帧数凑成完全平方数(6 个正方形格子只能铺 2/3,9 个才能铺满), 重排到填满为止。时间轴不断,但不会为此多开图(一张图 384 token,张数才是成本)。

实测数字

档位怎么影响成本(同一份切点,90 秒概念 PV,1080p24,59 刀 / 13 个积分点):

strategy帧张vision token最小格
full73186912—
standard735192095×62
compact7431152114×54
skim531384114×54
brief561384100×56

长视频会明显变贵(5 分钟 / 299 秒 / 150 刀):

strategy帧张token
standard195197296
compact201103840
skim14931152
brief51(切点间隔自适应到 3.6 s)1384

其它:

项目数字
单图 token≈233(800×450,42px 一格)—— 格子数不影响,像素才影响
端到端90 秒约 8 秒;5 分钟约 40 秒(含解码、扫描、取帧、拼图)
快闪重判零额外解码(复用逐帧分数)
切点召回概念 PV 59 刀 / 正式 PV 67 刀 / 5 分钟口播 150 刀

需要登录的视频(B 站 1080P+ / HTTP 567)

先说结论:普通视频匿名就能看 360p;要高清、会员、或遇到 567,才需要 cookies。

什么时候需要

现象需要 cookies
提示 HTTP Error 567✅ 这是 B 站风控,多半是"该视频要登录"
Sign in to confirm / 需要登录✅
想要 1080P 以上的清晰度✅(很多站点的高清要登录态)
普通视频、360p、匿名❌ 不需要

三步导出(Chrome / Edge)

  1. 装扩展 Cookie-Editor(作者 cgagnier)
  2. 在目标站点登录 → 点扩展 → Export → 选 Netscape 格式 → 存成 cookies.txt
  3. 告诉插件它在哪:
// ~/.dsh/video.json
{ "cookies": "D:/cookies/cookies.txt" }

或者每次调用时传:

video_storyboard({ input: "https://example.com/some-video", cookies: "D:/cookies/cookies.txt" })

也可以设环境变量 DSH_VIDEO_COOKIES(桌面版读不到 setx 的变量,优先用配置文件)。

为什么不能用 --cookies-from-browser:Chromium 127+ 的 App-Bound Encryption 让 SESSDATA 只能由浏览器进程解密, 普通进程读出来是 v20 密文。所以必须导出文件,这是机制问题,不是你配错了。

不想配 cookies?

直接把视频下载下来,喂本地路径——插件对本地文件一视同仁,还更快:

video_storyboard({ input: "D:/video/xxx.mp4" })

隐私提醒

cookies.txt 里的 SESSDATA 等同于你的登录凭证。别提交到仓库、别发群里、别贴到 issue 里。 用完可以退出登录让它失效。

完整步骤(含格式示例、验证顺序、故障对照)见 docs/SETUP.zh.md §3。

字幕与 ASR(怎么让它"听懂")

想看完整可用的配置(目录结构、asr.json / video.json 全文、实测数字、排障对照): docs/CONFIG.zh.md。

转写是可选的:不配也能出图,只是清单里没有台词。

优先级(第一个能用的赢)

  1. 视频自带字幕轨 → 直接读,不跑 ASR
  2. transcriptFile → 喂已有的 SRT/JSON,完全不碰 ASR
  3. ASR 服务 → 上面两个都没有时才用

我们用的是哪个模型

后端模型冷启动体积标点热词
sherpa(默认)paraformer-zh-2024-03-09(int8)+ ct-transformer 标点 + silero-vad约 5 秒500 MB另加载 onnx 模型❌ 无接口
funasr(可选)seaco_paraformer_large + fsmn-vad + ct-punc约 30 秒2.1 GB内置✅
云端FunAudioLLM/SenseVoiceSmall / whisper-1 …0(联网)—视服务而定视服务而定

为什么默认是 sherpa:2024 版 paraformer 比旧版准(实测「像差」不加热词就对),而且冷启动 30 秒 → 5 秒、 内存 6.8 GB → 不到 1 GB。代价是它在 sherpa 运行时里没有热词接口,同音字只能靠 terms 表后处理。

什么时候切回 funasr:你的片子里专有名词多、热词列表很长(比如人名一堆),热词的收益大过 30 秒启动成本时。

// ~/.dsh/asr.json 里加一行即可切后端(重启服务生效)
{ "model": "paraformer-zh-2024-03-09" }   // 在 start-asr.cmd 里设 ASR_BACKEND=funasr 则走另一套

配置 A:本机服务(免费、不联网)

~/.dsh/asr.json:

{
  "baseUrl": "http://127.0.0.1:8000/v1",
  "model": "paraformer-zh-2024-03-09",
  "language": "zh",
  "hotwords": "像差 焦段 光圈",
  "manage": {
    "start": ["D:/asr-local/.venv/Scripts/python.exe", "D:/asr-local/server-sherpa.py"],
    "idleMs": 5000,
    "waitMs": 60000
  }
}

start 也可以直接写成 .py 路径 —— 插件会自动按同目录的 .venv 补解释器。 但数组最明确([exe, ...args])。字符串形式(.cmd)也能用,但 Windows 上会闪一个 控制台窗口,而且批处理对行尾极敏感——LF 行尾的 .cmd 会被 cmd.exe 当成命令逐片段执行, 弹出一屏 'wo' 不是内部或外部命令。已踩过。

  • 本机地址不需要 key(127.0.0.1 / localhost / [::1])。自己跑的服务一般不校验 Authorization,插件不会再逼你填一个假 key。
  • manage 是生命周期托管:调用时端点不通就拉起;最后一次运行结束后 idleMs 内没有新运行,就请它自己退出(走服务的 POST /shutdown)。

idleMs 要按后端设,两者差 100 倍:

后端建议 idleMs理由
sherpa5000(5 秒)冷启动只要 5 秒,随用随关,内存立刻还回去
funasr600000(10 分钟)冷启动 30 秒,关太早等于每次重付;关太晚又占着 6.8 GB

hotwords 只对 funasr 后端有效 —— sherpa 的 paraformer 没有热词接口,同音字请写进 terms。

服务端要满足两个约定:

GET  /health        → 200 即可(拿不到就读 /v1/models)
POST /shutdown      → 返回后退出(不支持也行,插件会退回杀进程)
POST /v1/audio/transcriptions   → OpenAI 兼容,返回 verbose_json

配置 B:云端服务(有 key 就行,任何 OpenAI 兼容)

{
  "baseUrl": "https://api.siliconflow.cn/v1",
  "key": "sk-...",
  "model": "FunAudioLLM/SenseVoiceSmall",
  "language": "zh"
}

换成 OpenAI:

{ "baseUrl": "https://api.openai.com/v1", "key": "sk-...", "model": "whisper-1" }

便宜的选择(自己按需改):

服务baseUrlmodel 示例
SiliconFlowhttps://api.siliconflow.cn/v1FunAudioLLM/SenseVoiceSmall
OpenAIhttps://api.openai.com/v1whisper-1
阿里云百炼https://dashscope.aliyuncs.com/compatible-mode/v1paraformer-v2
Groqhttps://api.groq.com/openai/v1whisper-large-v3-turbo

云端不填 key 会直接跳过 ASR(不会报错,清单写 transcript: none)。

配置 C:直接用现成字幕

video_storyboard({ input: "film.mp4", transcriptFile: "film.srt" })

支持 SRT / VTT / JSON([{ "start": 1.2, "end": 3.4, "text": "..." }])。这条路零 ASR 成本,也最准—— 平台自带的字幕比任何识别都可靠。

环境变量(优先级高于文件)

DSH_ASR_BASE_URL   DSH_ASR_MODEL   DSH_ASR_LANG
DSH_ASR_KEY        DSH_ASR_KEY_ENV

桌面版 DSH 的进程环境由启动器决定,setx 设的变量宿主读不到 —— 优先用 asr.json。

硬字幕校正(detail: high 时自动开)

画面里烧进去的字幕是确定的文本,ASR 会听错。开 detail: high 时插件会:

  1. 抽 2–3 张原分辨率帧,找出字幕带(≈3 秒,只做一次)
  2. 对若干条台词,只裁那一条带子做 OCR(≈0.26 秒/条,不做检测)
  3. OCR 文本能覆盖 ASR 那句(≥60% 的字按顺序对得上)就替换,否则保留 ASR
明细里会写:on-screen subtitles used to correct 1 of 3 checked lines:
             4.4s "他也忘记。" → "也要忘记吗"

实测(同一段 PV):ASR 出「所以。他也忘记。」,画面是「你连,也要忘记吗」——OCR 把整句救回来了。

这是抽检,不是全片校对。 默认只抽查 16 条,清单里写的是 spot-checked N lines, corrected M —— 请按这个口径读,别当成"整片台词已核对"。

贴链接时(URL 模式)跑的是下载的 360p 扫描副本,scale=1280 放大不回细节,硬字幕本来就糊; 清单会标 [from the 360p scan copy]。想要更准就用本地高清文件。

全程本地、不花 token。但引擎要你自己装 —— 插件不带。

跟 ffmpeg 一个道理:模型有 300MB 上下,还绑一个 Python 环境,塞进 npm 包只会让所有人多下一坨。 插件只负责调用你装的那一份。

# 1) 建一个独立环境(别装进 ASR 那个 venv,两个模型的依赖会打架)
python -m venv D:/ocr-local/.venv

# 2) 装引擎:模型包 + 推理后端,两个都要
D:/ocr-local/.venv/Scripts/python.exe -m pip install rapidocr onnxruntime

别照老教程装 rapidocr-onnxruntime —— 那个包把 requires-python 写死在 <3.13, 在 Python 3.13 上直接 No matching distribution。新版包叫 rapidocr,import 名和返回结构 都变了,插件两个都认(先试新的)。

# 3) 告诉插件用哪个 Python(桌面版宿主读不到 setx 的环境变量,所以写文件)
#    ~/.dsh/video.json
{ "ocr": { "python": "D:/ocr-local/.venv/Scripts/python.exe" } }

不配这一行也能用:插件会依次试 DSH_OCR_PYTHON → asr.json 里那个 Python → PATH 上的 python。

怎么确认配好了:跑一次 detail: high,清单里出现 on-screen subs: spot-checked N lines 就是通了;还是 skipped (no-python-with-rapidocr) 说明没找到。DSH_OCR_DEBUG=1 会打印 它试过哪些解释器、以及每条主句的 OCR 原文和判定理由。

没装就静默跳过,不影响出图 —— 只是没有字幕校正。

装完不用重启。 插件对"找不到解释器"的结果只缓存 60 秒,装完最多等一分钟就自动用上 (以前是进程内永久缓存,必须重启整个 DSH 宿主,而且报错完全看不出是这个原因)。

校正失败时清单里写的是具体原因,不再一律 detect-failed:

清单里的字样意思
bridge-missing桥接脚本没找到(安装损坏;正常安装不会出现)
bridge-timeout单次 OCR 超过 120 秒
bridge-crashedPython 进程非零退出(栈在 DSH_OCR_DEBUG 的 stderr 里)
bridge-errorPython 自己报的错,后面跟着原文(多半是没装 rapidocr / onnxruntime)
no-band检测跑通了,但画面里找不到像字幕的东西

热词(比换模型更有效)

热词要按片子传,不要写死在全局。 全局那份是默认值,学科不对时它是负偏置(拿摄影词去识别材料学讲座,只会帮倒忙)。

video_storyboard({ input: "materials.mp4", hotwords: "位错 滑移 柏氏矢量 晶界 层错 固溶体" })

不传就用 asr.json 里的 hotwords;两者都没有 = 不做偏置(最安全)。

识别错同音字时,不要先想着换模型 —— 通用模型不知道你片子在讲什么。

"hotwords": "像差 焦段 光圈 沧野 玄学"

空格分隔,交给 paraformer 解码时做偏置。实测(镜头评测视频):

加之前:也就是所谓的相差。
加之后:也就是所谓的像差。

只加了一个词,别的一个没动。专有名词、人名、领域术语都该放这里。

需要服务端支持:seaco_paraformer 原生支持;自己搭服务时认 hotword 表单字段即可 (不认这个字段的服务会忽略它,不会报错)。

术语校正

识别结果里的固定错字可以确定性替换(不进模型):

"terms": { "绘插": "彗差", "星尘边境": "Stardust Frontier" }

替换次数会报在清单里(termsFixed N),不静默。

和同类插件比(诚实的版本)

生态里做这件事的有三种路线,我们只走其中一条:

插件做法一次 90 秒片子的成本它更强的地方
dsh-video-see抽帧作为真图片交回模型,每张带时间戳20 帧 ≈ 7,680 token每一帧都是 ~1000px,小字、表情都清楚
dsh-video-lens关键帧 → 外部 VLM → 返回 evidence JSON帧数 × 384 + VLM 调用费有 video_ask 时间锚定问答;结论结构化
watch-skill帧 + 转写 + OCR 索引成库,按需查询建库贵,追问几乎免费答案带回执,可被另一个东西复核
我们一张宫格图 + 文本清单,模型自己看约 1,900 token单位 token 覆盖的时间最长(1 张图 = 90 秒)

同一部 90 秒片子,各种"按镜头取关键帧"的方案落在 3,900–8,500 token(8 / 16 / 20 帧 + 字幕摘要), 我们是它的 1/2 到 1/4.5。不是 1/8 或 1/16 —— 那种数字是拿"每秒抽一帧"当对手,不诚实。

我们明确输的地方

输给谁为什么我们的补救
dsh-video-see / lens单帧清晰度(每帧 ~1000px vs 我们的格 331×184)video_zoom 再取一次,但那是第二轮成本
watch-skill多轮追问的边际成本(它索引一次就能反复查)清单里明说"图已在上下文",追问不再重新出图
watch-skill可验证性(答案能被第三方复核)每张图配一份 .evidence.json,时间码可回看
lens结构化 evidence、时间锚定问答video_locate 做时间定位,evidence.json 给结构

适合谁:想先搞清"这部片子讲什么"的人。 不适合谁:逐帧辨字、要复核结论、对同一部片子问二十个问题 —— 那三种情况请用上面三家。

明确不做的

  • 不烧时间码进画面 —— 占像素、压缩后先糊,时间在文本里有 0.01 秒精度。
  • 不做说话人分离 —— 实测 cam++ 在 1.9 秒短句上把单人素材切成 6 个人。
  • 不打包 ffmpeg / yt-dlp —— 版本和安全都由用户掌握。
  • 不把"结论"写进清单 —— 那是模型的工作。

已知限制

  • 画面小于约 1% 的改动(PPT 上多一行字)在 64×36 上不可见,再聪明的阈值也救不回来。
  • 连续运镜密集时,cuts=N 会失真(清单会标注)。
  • 极暗素材的取帧锐度判断不稳。
  • 1/8 档的格子是 100×56,只够认出"换没换镜头",认不出字幕和脸。
  • 混合档位的图,行内矮格上方必然有一条缝(行高取本行最高那格)——这是行排布模型的固有损失, 所以填充率通常落在 79–95%。想更高只能靠 tierMap 把档位压得整齐,或者用 thrifty / ultra。
  • 片头长黑场会独占一格(黑场也是一个"镜头",没有切点就切不开)。

证据与复核

每次 saveStoryboards 会同时写一份 <名字>.evidence.json:每格的起止时间、取帧时刻、刀数、字数、原文。 清单是给模型读的散文,这份是给人和脚本复核的 —— 想知道"第 5 格真的是 3:29 吗",对着时间码回看原片即可。

文档

文档内容
docs/INSTALL.zh.md从零安装:依赖、自检、排错表
docs/USAGE.zh.md完整手册:三条安装路径、六个工作流、每个参数
docs/HISTORY.zh.md从 v0.1 到现在,按"被什么打脸"组织
CHANGELOG.md逐版本变更
CREDITS.md第三方致谢、社区贡献、测试素材作者
README.en.mdEnglish

赞助

这个插件是免费的,也不打算收费。如果它帮你省下了 API 费用或剪辑时间, 欢迎请我喝杯奶茶 —— 完全自愿,不影响任何功能。

微信赞赏码

仓库右上角的 Sponsor 按钮也指向这里(.github/FUNDING.yml)。

许可

见 LICENSE。

auto / off
transcriptFile—直接喂已有的 SRT/JSON,跳过 ASR
detailmed每格台词预算:low 60 / med 120 / high 400 字
cellRange—分页看文本清单