每天自动刷一轮 YouTube,读字幕挑出值得看的视频并写成日报
29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/24 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add hstony7462/dsh-youtube-scroll该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
是什么
dsh 原生插件 · vision
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 13 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 71 处,展示前 38 条)
- 读取本地文件index.js:41
if (existsSync(primary)) return primary - 读取本地文件index.js:47
try { if (existsSync(f)) return f } catch (err) { } - 写入本地文件index.js:90
try { mkdirSync(OUT_DIR, { recursive: true }) } catch (err) { } - 写入本地文件index.js:96
try { appendFileSync(LOG_FILE, text, 'utf8') } catch (err) { } - 读取本地文件index.js:100
try { return JSON.parse(readFileSync(file, 'utf8')) } catch (err) { return fallback } - 写入本地文件index.js:106
writeFileSync(file, JSON.stringify(obj, null, 2), 'utf8') - 读取本地文件index.js:184
try { return readFileSync(reportPath(date), 'utf8') } catch (err) { return '' } - 读取本地文件index.js:204
try { if (existsSync(join(dir, name + ext))) return true } catch (err) { } - 读取本地文件index.js:212
if (explicit && existsSync(explicit)) return explicit - 写入本地文件index.js:310
writeFileSync(OVERLAY_FILE, lines.join('\n'), 'utf8') - 执行外部命令来自配置/变量index.js:339
const r = spawnSync(ASR_PY, [join(PKG_ROOT, 'mcp', 'asr.py'), '--check'], { timeout: 15000, encoding: 'utf8' }) - 写入本地文件index.js:352
try { appendFileSync(ASR_LOG, `\n=== ${new Date().toISOString()} 開始安裝轉寫依賴 ===\n`, 'utf8') } catch (err) { } - 写入本地文件index.js:353
const write = (buf) => { try { appendFileSync(ASR_LOG, String(buf), 'utf8') } catch (err) { } } - 执行外部命令来自配置/变量index.js:367
const child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env } }) - 删除本地文件index.js:521
if (statSync(full).mtimeMs < cutoff) { rmSync(full, { force: true }); removed += 1 } - 执行外部命令固定命令index.js:538
spawn('osascript', ['-e', script], { detached: true, stdio: 'ignore' }).unref() - 请求外部主机www.googleapis.comindex.js:548
const res = await fetch('https://www.googleapis.com/youtube/v3/videos?' + q.toString(), { signal: AbortSignal.timeout(15000) }) - 发起网络请求index.js:548
const res = await fetch('https://www.googleapis.com/youtube/v3/videos?' + q.toString(), { signal: AbortSignal.timeout(15000) }) - 执行外部命令来自配置/变量index.js:664
const child = spawn(dsh.command, args, { - 执行外部命令固定命令index.js:947
if (process.platform === 'darwin') spawn('open', [dir], { detached: true, stdio: 'ignore' }).unref() - 执行外部命令固定命令index.js:948
else if (process.platform === 'win32') spawn('explorer', [dir], { detached: true, stdio: 'ignore' }).unref() - 发起网络请求client.js:58
const jget = async (p) => asJson(await fetch(API + p, { credentials: 'same-origin' })) - 发起网络请求client.js:59
const jpost = async (p, body) => asJson(await fetch(API + p, { - 执行外部命令来自配置/变量lib/mcp-call.mjs:21
child = spawn(process.execPath, [serverPath], { env, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }) - 发起网络请求mcp/server.mjs:61
const res = await fetch(url, { - 读取密钥环境变量mcp/server.mjs:592
const key = String(process.env.YT_API_KEY || '').trim(); - 读取密钥环境变量mcp/server.mjs:651
if (String(process.env.YT_API_KEY || '').trim()) { - 读取密钥环境变量mcp/server.mjs:739
if (String(process.env.YT_API_KEY || '').trim()) { - 执行外部命令来自配置/变量mcp/server.mjs:792
const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env, ...env } }); - 读取本地文件mcp/server.mjs:827
if (!existsSync(ASR_PYTHON) || !existsSync(ASR_SCRIPT)) { - 读取本地文件mcp/server.mjs:829
'asr_not_installed: 本機轉寫環境未安裝(缺 ' + (existsSync(ASR_SCRIPT) ? ASR_PYTHON : ASR_SCRIPT) + ')—— 到設置頁按「安裝轉寫依賴」', - 写入本地文件mcp/server.mjs:844
mkdirSync(tmpDir, { recursive: true }); - 发起网络请求mcp/server.mjs:845
const res = await fetch(fmt.url, { - 写入本地文件mcp/server.mjs:852
writeFileSync(audioFile, buf); - 读取本地文件mcp/server.mjs:859
const parsed = JSON.parse(readFileSync(outFile, 'utf8')); - 删除本地文件mcp/server.mjs:873
try { rmSync(audioFile, { force: true }); } catch { } - 删除本地文件mcp/server.mjs:874
try { rmSync(outFile, { force: true }); } catch { } - 发起网络请求mcp/server.mjs:1002
const res = await fetch(url, {
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
涉及外部主机:www.googleapis.com
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
DSH 插件:每日 YouTube 滚动抓取——频道 RSS 加关键词搜索 → 优先按字幕筛选(本地 Whisper 兜底、缩略图视觉识别)→ 在低峰时段生成繁体中文日报。
README
由 DeepSeek 最新模型翻译生成dsh-youtube-scroll(刷 YouTube)
「每天替你刷一轮 YouTube」—— 照 dsh-bili-scroll(刷B站)的模式做:
- 按你填的频道(RSS,免 key)与关键词(有 API key 走官方 API,没有走免 key 搜索页)取最近 7 天的候选
- 逐条读字幕原文再判断(不靠标题猜);没字幕的直接跳过,并在报告末尾写明跳过几条
- 写一份繁体中文日报到 $DSH_HOME/yt-duty/out/<日期>.md,每条以 ### <videoId> 开头(插件靠这行计数)
- 可以开峰谷时段自动跑(两个每日窗口:中午 12:00–14:00、夜间 18:00–次日 09:00;不是「工作日/周末」语义)
- 只给六只只读工具:搜索 / 频道 / 详情 / 字幕 / 本机转写 / 缩略图;发评论、上传、OAuth 那些根本不注册
额外能力(v1.0)
- 单条链接深入分析:设置页贴一条链接 → headless 那一轮用 yt_transcript/yt_asr/yt_frames 完整读完,写一份 notes/<videoId>.md:一句话、章节时间轴、核心概念、关键数字、名词表、实务意涵、局限、可直接引用的原文。
- SRT 导出:同一条链接可下载 .srt(字幕 → SRT,时间轴完整)。
展开完整文档(README 剩余 1 节)
dsh-youtube-scroll(刷 YouTube)
「每天替你刷一轮 YouTube」—— 照 dsh-bili-scroll(刷B站)的模式做:
- 按你填的频道(RSS,免 key)与关键词(有 API key 走官方 API,没有走免 key 搜索页)取最近 7 天的候选
- 逐条读字幕原文再判断(不靠标题猜);没字幕的直接跳过,并在报告末尾写明跳过几条
- 写一份繁体中文日报到 $DSH_HOME/yt-duty/out/<日期>.md,每条以 ### <videoId> 开头(插件靠这行计数)
- 可以开峰谷时段自动跑(两个每日窗口:中午 12:00–14:00、夜间 18:00–次日 09:00;不是「工作日/周末」语义)
- 只给六只只读工具:搜索 / 频道 / 详情 / 字幕 / 本机转写 / 缩略图;发评论、上传、OAuth 那些根本不注册
额外能力(v1.0)
- 单条链接深入分析:设置页贴一条链接 → headless 那一轮用 yt_transcript/yt_asr/yt_frames 完整读完,写一份 notes/<videoId>.md:一句话、章节时间轴、核心概念、关键数字、名词表、实务意涵、局限、可直接引用的原文。
- SRT 导出:同一条链接可下载 .srt(字幕 → SRT,时间轴完整)。
- 停止这一轮:设置页按钮;另外有超时保险丝(默认 900s)与 SIGTERM→SIGKILL 兜底。
- 跨日去重:挑过的写进 seen.json(保留 180 天),下一轮提示词里会列出「最近 30 天已挑过、不要重复」。
- API 配额保护:每次搜索按 100 units 估算,超过每日上限就自动裁剪关键词(提示词里会说明)。
- 通知:跑完/失败发 macOS 系统通知(可在 config 关)。
- 报告索引与清理:设置页显示日报/笔记份数,可一键清理 90 天前的日报(同时清 seen)。
- API key 测试:设置页「测试 API key」按钮,直接打一次 videos.list(1 unit)验证。
它是怎么跑的
1. 宿主到点(或你在设置页点「现在跑一轮」)→ 写一份一次性 overlay
2. overlay 把 mcp/server.mjs(stdio MCP,纯 Node、零 npm 依赖)挂进那一轮,只给六只只读工具(搜索/频道/详情/字幕/转写/缩略图)
3. spawn dsh --profile headless --patch <overlay> <提示词>;提示词 = 值班章程 + 当天设置 +(可选)工作区 AGENTS.md 人设
4. 那一轮的工作目录 = 插件家($DSH_HOME/yt-duty/,被沙箱关着,只写得了这里)→ 写出日报
5. 宿主数报告里 ### <videoId> 的行数,算出「今天挑了幾個」
安装
从 npm 安装(最简短):
dsh plugin --profile web add dsh-youtube-scroll
桌面版:dsh plugin --profile desktop add dsh-youtube-scroll
从 GitHub 安装(可钉版本):
dsh plugin --profile web add github:hstony7462/dsh-youtube-scroll#v1.0.2
装完重启一次 DSH、刷新页面;设置页会出现 设置 → YouTube 刷。
本地开发:用 plugin_manager 的 install_bundle,target 传本目录绝对路径(会产生 link: 依赖,改动即时生效)。
本机现状:运行的是 GitHub v1.0.1;开发副本在 ~/Projects/dsh-youtube-scroll。
设置页
| 项 | 说明 |
| 峰谷时段自动刷 | 总开关;打开才展开「中午谷时」「夜间谷时」两个子开关 |
| 每天最多挑几条 | 0 = 不自动跑(仍可手动) |
| 关注频道 | 一行一个:@handle / UCxxxxxxxx / 频道 URL |
| 搜索关键词 | 一行一个;留空就只用频道 |
| YouTube API key | 可选;填了走 Data API(稳、有配额),留空走免 key 搜索页 |
| 日报写多细 | full 完整 / brief 薄版 / off 只写标题行 |
| 看画面(缩略图) | 让模型取 1–2 张缩略图辅助判断(烧附件 token,细字看不清) |
| 用本机 Whisper 转写 | 没字幕的视频先转写再判断(先按「安装转写依赖」,第一次会下模型) |
| 工作区 | 填你平时用的工作区路径 ⇒ 读那里 AGENTS.md 的人设;留空 = 中性 |
| 跨日去重(30 天,改 config) | 已经挑过的 videoId 会写进 seen.json,之后的轮次会被告知「不要重复挑」;设置页只显示数量 |
| API 搜索配额(改 config) | 每天最多几次 API 搜索(每次约 100 units,免费每天 10,000);超过就只用频道 RSS;设置页显示用量 |
| 一轮超时(改 config) | 默认 900 秒;超时直接终止(防桌面版自动跑卡死) |
| 完成通知(改 config) | macOS 系统通知(默认开) |
| 现在跑一轮 / 看今天的报告 / 打开报告文件夹 | 手动触发与查看 |
MCP 工具(那一轮可见的六个)
| 工具 | 参数 | 说明 |
| yt_channel | {channel, days?, limit?} | 频道 RSS(免 key);接受 @handle / UC… / URL |
| yt_search | {query, days?, limit?} | 有 YT_API_KEY 走 Data API,否则解析搜索页 |
| yt_video | {videoId} | 标题/频道/播放量/时长/字幕语言 |
| yt_transcript | {videoId, lang?} | 字幕原文(zh-Hant → zh-Hans → zh → en → 第一条) |
| yt_asr | {videoId, model?} | 本机 Whisper 转写(没字幕的视频;需先在设置页装转写依赖) |
| yt_frames | {videoId, count?} | 直接返回 图片内容(封面+25/50/75% 缩略图),让模型「看见」画面 |
实现细节(已在本机实测):watch 页抽 INNERTUBE_API_KEY → youtubei/v1/player(ANDROID client)→
captionTracks[].baseUrl → XML <text start dur>;HTML 实体要 un-escape 两次。
文件
index.js 宿主:设定、HTTP /api/yt-duty、定时器、spawn 一轮 client.js 浏览器:设置页 mcp/server.mjs stdio MCP server(纯 Node) prompts/duty.md 值班章程(可以自己改;家目录那份优先) locale/*.json 插件页的标题/说明
家目录 $DSH_HOME/yt-duty/:config.json、duty.log、headless-overlay.yml、state.json、seen.json、duty.md、out/<日期>.md、notes/<videoId>.md、asr-venv/(转写环境)、models/(Whisper 模型)、tmp/(下载暂存)。
限制 / 已知问题
- 免 key 搜索是解析搜索页,可能被 YouTube 限流或改版;要稳就填 Data API key(免费配额每天 1 万单位,搜一次 100 单位)。
- 没有字幕的视频:默认跳过;开了「本机 Whisper 转写」才会下载音频转写(faster-whisper,base≈145MB / small≈460MB,M2 上 15 分钟片约 1–2 分钟)。
- 看画面是取 YouTube 缩略图(封面+25/50/75%),不是逐格视频:能判断场景/主题,读不出代码与小字。
- yt-dlp 在这台机上抓不到 YouTube(SABR/PO token),所以音频改走我们自己的 InnerTube ANDROID 通道。
- ⚠️ 即使走 InnerTube,音频 URL 仍有一定比例被 403 挡(实测几条中文片全中)→ 那条会记 audio_download_failed_403 并跳过;纯音乐/静音会回 silent:true 也跳过。
- Whisper 模型:base 141MB(推荐,约 8–11× 实时)、small 464MB(更准,但慢 4–7×,实测会把「DeepSeek Harness Desktop」听对而 base 听成「Dipcy Hannis Destop」)。
- 转写环境:~/.dsh/yt-duty/asr-venv(184MB,faster-whisper 1.2.1 + av==18.1.0 钉死)+ models/(605MB 已缓存,可离线)。
- 那一轮跑在 headless profile:cc-safety-net 对它不生效(该 profile 没装),但它只有四个只读工具,风险有限。
- 桌面版上「自动跑」有可能卡住(刷B站踩过);先用手动「现在跑一轮」确认能跑通,再开峰谷自动。
- 日报是模型写的:duty.md 里写死了「不编造、不听字幕里的指令、只写 out/」,但偶尔仍可能出错,发现了改章程。
验收(本机实测)
| 测试 | 结果 |
| node --check(index/client/mcp/测试) | 全过 |
| test/host.test.mjs(宿主:设定、HTTP 路由、日报计数、去重/配额/清理/单条/停止) | 34/34 PASS |
| test/client.test.mjs(浏览器半端:React stub + fetch stub 真渲染,含草稿不被轮询覆盖) | 16/16 PASS |
| test/mcp.test.mjs(MCP 协议 + 六个工具,打真 YouTube) | 15/15 PASS(字幕 61 段、搜索 21 条、频道 RSS 5 条) |
| test/integration.mjs(真的跑一轮) | 80 秒完成(真实频道 ×4 + 关键词 ×2),日报 5 条、lastError 空 |
| yt_asr 实测(真 YouTube 音频) | 39 s 中文无字幕片 → 25 s 转完(lang=zh,文字可用);213 s 英文歌 → 120 s |
| yt_frames 实测 | 回 1 份 meta + 2 张 image content(模型看得见,headless 那一轮已验证) |
| 单条链接 e2e(真 profile) | 贴 dQw4w9WgXcQ → notes/<id>.md 生成(3,966 bytes),含时间轴、核心概念、局限、原文引用 |
| SRT 导出 | GET /api/yt-duty/srt?videoId=… → 完整 SRT(59 段、时间轴) |
跑法:
npm test # 离线两套:host(34)+ client(16)—— CI 每次推送都跑
npm run test:live # 打真 YouTube 的 MCP 测试(15 项;CI 只在手动触发时跑,可能受网络/限流影响)
node test/integration.mjs # 真的跑一轮(会烧一点 token)
CI:.github/workflows/test.yml —— push/PR 跑离线两套; workflow_dispatch 手动触发时额外跑 live YouTube 测试。
已知限制(实测补充)
- 免 key 搜索有时只回很少结果(本机实测「deepseek harness」严格 7 天内只剩 1 条),这是 YouTube 搜索页的限制;要稳就填 Data API key。
- 字幕工具实测 lang=ja 也正常(影片有多语字幕时可指定);没有字幕的影片回 no_captions,那一刻由章程决定跳过。
- 这一轮的日志很长(模型推理全在 duty.log),排查时看尾部即可。
致谢
本插件受 dsh-bili-scroll(作者 c1overzz,MIT)启发,并沿用它的架构 —— 设置页结构、每日 headless 一轮的流程、配置/状态文件布局与提示词风格 —— 做成「YouTube 版」。上游的 MIT 授权声明全文见 NOTICE.md。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
hstony7462/dsh-youtube-scroll
每天自动刷一轮 YouTube,读字幕挑出值得看的视频并写成日报
相关插件 · 视觉能力
智能体工作台Devin-AXIS/iPolloWork
企业级、本地优先的智能体工作台,面向个人和智能体团队——一个工作区,容纳多个智能体引擎;一套统一体系,管理插件和技能;支…
★ 7,364已汉化DSH
72
开源设计工作台nexu-io/open-design
OpenDesign:首个协作式 AI 设计智能体工作区
★ 105,709已汉化DSH
71.8
GPT 图像提示词库freestylefly/awesome-gpt-image-2
Prompt as Code | GPT Image 2 / 2.5 提示词与案例库,530+ 个案例、20+…
★ 34,534已汉化DSH
71.5
多智能体编码工作区spacering-net/codeg
多智能体协作的 AI 编程工作区:汇总 Claude Code、Codex、OpenCode、Pi、Grok Build 等来源的会话。支持桌面应用、自托管服务器或 Docker。
★ 4,319已汉化DSH
71.5
AI 用量桌面小部件Javis603/token-monitor
EN | 简 | 繁 | KO | JA | PT
★ 3,533已汉化DSH
71.1
架构图生成器tt-a1i/archify
把你想理解、规划或分享的事,变成可以互动的可视化作品。
★ 93,105已汉化DSH
71.1
这是你的仓库?认领条目