🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

播客切段与RSS yuehancn/dsh-tool-podcast

DeepSeek Harness
yuehancn30 天前有提交已提供中文文档扫描:含敏感能力

按静音断点切分录音并生成播客RSS源

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add yuehancn/dsh-tool-podcast
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · tool
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 30 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件
查看扫描证据(共 8 处,展示前 8 条)
  • 执行外部命令来自配置/变量
    lib/index.js:137
    child = spawn(command, args, { windowsHide: true, shell: false });
  • 写入本地文件
    lib/index.js:815
    await mkdir(directory, { recursive: true });
  • 读取本地文件
    lib/index.js:837
    const info = await stat(outputPath);
  • 读取本地文件
    lib/index.js:945
    info = await stat(episode.audioPath);
  • 写入本地文件
    lib/index.js:988
    await mkdir(dirname(feedPath), { recursive: true });
  • 写入本地文件
    lib/index.js:989
    await writeFile(feedPath, xml, "utf8");
  • 写入本地文件
    lib/index.js:1080
    await mkdir(directory, { recursive: true });
  • 读取本地文件
    lib/index.js:1099
    const info = await stat(outputPath);
扫描于 2026/10/31(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/dsh-tools
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 按静音断点切分录音
  • 生成播客RSS源

README

由 DeepSeek 最新模型翻译生成
dsh-tool-podcast 在 dsh 里把一期录音做成播客 —— 先从静音里看出这期节目分几段、断点应该落在哪,再照这个断点切、把响度统一、最后发一个客户端认的 RSS。 给 DeepSeek Harness 用的自建工具插件:调用本机 ffmpeg / ffprobe, 不装 Node 侧的音频库。 Compatibility: built and tested against dsh 0.2.0-rc.2 (preview). The apply(ctx) plugin spec is stable; verify against your own dsh version if newer. 一行安装 dsh plugin --profile desktop add github:yuehancn/dsh-tool-podcast 支持的 profile:desktop(桌面版)/ web(Web 版)。装完重启 dsh 即可用。 为什么需要它 一期播客就是一个很长的音频文件,里面没有任何结构。结构藏在静音里 ——主持人和嘉宾对话之间、片头和正文之间、正文和片尾之间都有停顿。 「这期分几段」这件事,只能从静音里量出来。 而真正容易出错的是判断和约定: - 断点是要决定的。你不知道它准备在哪切,就没法检查切得对不对。
展开完整文档(README 剩余 1 节)
dsh-tool-podcast

在 dsh 里把一期录音做成播客 —— 先从静音里看出这期节目分几段、断点应该落在哪,再照这个断点切、把响度统一、最后发一个客户端认的 RSS。

给 DeepSeek Harness 用的自建工具插件:调用本机 ffmpeg / ffprobe, 不装 Node 侧的音频库。

Compatibility: built and tested against dsh 0.2.0-rc.2 (preview).
The apply(ctx) plugin spec is stable; verify against your own dsh version if newer.

一行安装

dsh plugin --profile desktop add github:yuehancn/dsh-tool-podcast

支持的 profile:desktop(桌面版)/ web(Web 版)。装完重启 dsh 即可用。

为什么需要它

一期播客就是一个很长的音频文件,里面没有任何结构。结构藏在静音里 ——主持人和嘉宾对话之间、片头和正文之间、正文和片尾之间都有停顿。
「这期分几段」这件事,只能从静音里量出来。

而真正容易出错的是判断和约定:

- 断点是要决定的。你不知道它准备在哪切,就没法检查切得对不对。
- RSS 播客源会因为看不见的原因被客户端拒收:<enclosure> 的 length 和文件真实字节数不一致,或者 type 写错,客户端会静默丢掉这一期 ——源解析正常,就是不出现在列表里。
- 每个节目的响度是自己定的,客户端不会帮你归一化。一期比别人小声 10 dB,听众就得手动调音量。

这个插件把「看清楚」和「动手」分开:

podcast_chapters(path="raw.mp3") → 3 章 / 断点 4.6s、8.8s / 依据:1.2s 的静音
podcast_trim(path, cuts=[4.6,8.8]) → 切成 3 段,默认秒级流拷贝
podcast_feed(...) → 客户端能用的 RSS,长度从磁盘量

关键设计:podcast_chapters 是独立的工具,不是 podcast_trim 的一个参数。
因为切错了是静默的 —— 文件照样能播,只是内容错位。所以先看断点、再动手。

五个工具

podcast_status

报 ffmpeg / ffprobe 能不能跑、版本号、可用操作、支持的输出格式,以及当前的默认阈值。长分析前先问一句,免得到出错才发现路径没配。

podcast_chapters(path, silenceDb?, minSilenceSeconds?, maxChapters?)

只算不切。用 ffmpeg 的 silencedetect 找出所有静音区间,再把「够长的」那些转成章节断点。

| 参数 | 说明 |
| silenceDb | 噪声门限,低于这个 dB 算静音,默认 -35 |
| minSilenceSeconds | 静音至少多长才算断点,默认 0.8 |
| maxChapters | 超过就合并尾部而不是报错 |

返回里 rule 字段会明确写出用的规则,例如 a silence gap of at least 0.8s below -35dB starts a new chapter; the cut lands at the midpoint of the gap。
断点取静音区间的中点 —— 那是离两侧说话都最远的位置,最容易解释也最稳。

推断出来的东西必须可解释,因为切错了没人看得出来。

podcast_trim(path, cuts, format?, accurate?, ...)

按给定断点切段,每段一个文件。

| 参数 | 说明 |
| cuts | 必填,升序的秒数,每个值开始一个新段;第一段永远从 0 开始 |
| format | mp3 / m4a / wav / flac / opus,默认跟随源扩展名 |
| accurate | true 走重编码、精确切;默认走流拷贝 |
| prefix | 文件名前缀(支持中文) |

关于快慢的取舍:流拷贝(-c copy)不碰一个采样,一小时节目不到一秒就切完,但它只能落在包边界上。所以判据不是「目标格式有没有编码器」,而是 源编码能不能原样放进目标容器:

- MP3 源切成 MP3 → 流拷贝(同一个编码,零损失快路径)
- MP3 源切成 WAV → 重编码(编码不同,拷贝出来是坏文件)
- 任何源 + accurate: true → 重编码(要精确落点,就必须解码重编)

返回里的 mode 和 notes 会说清这次走的是哪条路、以及为什么。

podcast_feed(feedPath, title, episodes, ...)

写一份 iTunes 兼容的 RSS 2.0 源。

关键点:<enclosure> 的 length 是从磁盘上的文件量出来的,不接受调用方传入。 因为长度写错是最隐蔽的失败 —— 源本身合法,客户端就是不上架这一期。
type 由文件扩展名推导(.mp3 → audio/mpeg),扩展名不认识就报错,不猜。

写完会把自己写的文档重新解析一遍做校验(XML 声明、RSS 版本、 channel 标题、每条 item 恰好一个 enclosure、length 是整数、type 是 audio/*、标签配平),把问题列在 problems 里返回 —— 让坏源在上架前就被挡住。

podcast_normalize(path, targetLufs?, truePeakDb?, ...)

用 ffmpeg 的 EBU R128 loudnorm,跑两遍。

这是要跑两遍的原因:单遍 loudnorm 是「实时」的,它只能边走边猜,结果是 靠近目标而不是落到目标。第一遍只测量(不输出),把测到的响度喂给第二遍,第二遍才能算准增益。

返回带 inputLufs → outputLufs → gainApplied,前后都量给你看。
-16 LUFS 适合播客,-14 LUFS 适合流媒体。

配置

~/.dsh/profiles/<profile>/cordis.patch.yml
- id: tool-podcast
config:
ffmpegPath: C:/AI/ffmpeg/ffmpeg-release-full/bin/ffmpeg.exe
ffprobePath: C:/AI/ffmpeg/ffmpeg-release-full/bin/ffprobe.exe
outputDir: C:/Users/you/Music/podcast-output
silenceDb: -35
minSilenceSeconds: 0.8

| 字段 | 默认 | 说明 |
| ffmpegPath | ffmpeg | ffmpeg 可执行文件;本机建议写全路径 |
| ffprobePath | ffprobe | ffprobe 可执行文件 |
| outputDir | podcast-output | 产物目录 |
| silenceDb | -35 | 静音门限(dB) |
| minSilenceSeconds | 0.8 | 静音最少多长算断点 |
| maxInputSeconds | 21600 | 超过这个长度就拒绝分析(6 小时) |
| timeoutMs | 900000 | 单次调用预算(15 分钟) |
| status/chapters/trim/feed/normalize | true | 按需关掉某个工具 |

安全说明

- 只用 spawn(command, argsArray) 调用 —— 不拼 shell 字符串,路径里有空格、引号、中文都不会出事。
- 下载/写盘只发生在你指定的 outputDir / feedPath,不联网。
- outputName 里的 .. 会被拒绝,不给路径穿越的机会。
- 阈值和长度上限都能挡住超大输入:maxInputSeconds 限制分析时长, maxChapters 限制章节数(超了就合并尾部,而不是把内存吃光)。
- 临时产物写在系统临时目录,跑完自己清理。

实现说明:三个值得说的地方

1. 静音解析要处理「没有结束的静音」。 silencedetect 在录音结尾没有尾音时会只打 silence_start 不打 silence_end。
把这种区间当成 end = null 保留下来(而不是丢掉、也不是当成 0),然后在判定断点时排除落在文件最开头/最末尾的断点 —— 否则会切出一个空章节。

2. 流拷贝的判据是「编码兼容」,不是「有没有编码器」。 最初写成 target.acodec !== "copy" 就重编码 —— 那是错的:MP3 的 acodec 是 libmp3lame(一个编码器名),于是 MP3→MP3 也被判成必须重编码, 快路径永远不会被走到。正确判据是查一张「目标容器认哪个编码」的表(CODEC_FOR_TARGET),拿它和源文件实测的编码 比。这个 bug 是集成测试断言 mode 时抓出来的。

3. RSS 的长度必须自己量。 调用方说这个文件多少字节,是不能信的 —— 中间可能被转码、被截断、被改名。
所以 length 一律 stat() 出来。同理 type 是从扩展名推导的,遇到不认识的扩展名就报错而不是猜一个 audio/mpeg 糊过去。

验证方式:_test/verify-feed.py 用 feedparser(Python 生态里的事实标准 RSS 解析器)重新解析产出的源,再检查客户端真正在意的那些不变量 ——每条 item 恰好一个 enclosure、type 和扩展名一致、length 等于磁盘上文件的真实字节数、itunes:duration 能解析回合理的秒数。
本插件的 validateFeed 是「自己验自己」,这个脚本是「别人验自己」。

当前结果:19 项检查全过,0 个问题(2 期节目,长度与磁盘逐字节一致)。

跑测试

mkdir -p node_modules/@deepseek-ai
cp -r "$HOME/.dsh/profiles/desktop/node_modules/@deepseek-ai/." node_modules/@deepseek-ai/

夹具(纯合成音频,可复现;已随仓库提供,需要时可重建)
node _test/make-fixtures.mjs

PODCAST_FFMPEG=/path/to/ffmpeg PODCAST_FFPROBE=/path/to/ffprobe node _test/run-all.mjs

三个套件,274 条断言全绿(对着真实 @deepseek-ai/dsh-tools 与真实 ffmpeg 跑,不 mock):

| 套件 | 断言 | 内容 |
| test-logic.mjs | 142 | Config 默认值与覆盖、注册开关、schema 归一化、时长/时间戳格式化、CJK slug、XML 转义(含控制字符剔除)、静音解析(含未闭合区间、容差推导)、章节推断(短间隔不算、未闭合尾部不产生空章、乱序输入仍有序)、MIME/编码映射表、RSS 写出与自校验(缺 length / 错 type / item 与 enclosure 数量不符 / 标签不配平 / 非 RSS 根) |
| test-integration.mjs | 87 | 真实 ffmpeg:status 探测、两个已知间隔的录音检出 3 章且断点落在中点(±0.2s)、无间隔录音只有 1 章、更严/更松阈值、maxChapters 合并尾部、4 条 chapters 错误路径、trim 流拷贝与重编码两条路、格式默认与拒绝、0 秒切点丢弃、5 条 trim 错误路径、feed 长度取自磁盘、2 条 feed 错误路径、normalize 前后响度都测且落到目标 ±3 LUFS、3 条 normalize 错误路径、注册开关、presentation 与 render |
| test-e2e.mjs | 45 | 完整链路:原始录音 → 检出章节 → 按检出的断点精确切 → 逐段 ffprobe 独立复核时长 → 逐段归一化到 -16 LUFS → 发布带 enclosure 的源 → 重新读盘上的源再校验、每个 enclosure 长度对得上发布出去的文件、章节检测与源写出两次运行字节一致 |

再跑一次独立校验(需要 Python + feedparser):

python _test/verify-feed.py _test/samples/podcast-feed.xml

夹具是合成的:4.0s 440Hz、1.2s 静音、3.0s 660Hz、1.2s 静音、4.0s 880Hz,共 13.4s、两处间隔 —— 所以「断点应该在 4.6s 和 8.8s」是事先算好的,不是跑完再看结果反推的。make-fixtures.mjs 可以随时重建,产出字节一致。

许可

MIT
预览分享卡片长什么样 ↓yuehancn/dsh-tool-podcast
分享 · DeepSeek Harness Hub
yuehancn/dsh-tool-podcast

按静音断点切分录音并生成播客RSS源

回到顶部