Live: http://doublemice.github.io/poddeck/
PodDeck 把播客 RSS 里的长访谈和 transcript 自动转成结构化 Slidev 演示文稿,并部署到 GitHub Pages。每集包含可 grep 验证的嘉宾原话、核心论点、手绘示意图和可阅读 article。
- RSS-only pipeline:
cache:refresh → plan → plan:run → build → deploy - 数据入口:
sources.yml中的rss_url - transcript 来源:RSS
podcast:transcript,优先text/plain,再 fallback 到 VTT/SRT/HTML - 只有音频的 RSS episode 会进入
needs_transcript队列,等待本地或手动转写补全 - 音频转写:默认 MiMo
mimo-v2.5;可用TRANSCRIPT_PROVIDER=dashscope回退 DashScope;受限音频走本地下载 +ffmpeg切片 + data URI - 封面来源:RSS item
itunes:image,缺失时 fallback 到 channel image - 生成入口:
claude -psubprocess;本地使用 Claude Code 登录态,GitHub Actions 使用ANTHROPIC_AUTH_TOKEN - 部署入口:GitHub Actions → GitHub Pages
- 首页“最新添加”按 slides 生成/更新时间排序,缺失时回退到播客发布时间
| Source | RSS |
|---|---|
| TBPN | https://feeds.transistor.fm/technology-brother |
| Unchained | https://unchained.libsyn.com/unchained |
| Risky Business | https://risky.biz/feeds/risky-business/ |
| Acquired | https://feeds.transistor.fm/acquired |
| Flirting with Models | https://feeds.captivate.fm/flirting-with-models/ |
| 张小珺 Xiaojun Podcast | https://feed.xyzfm.space/dk4yh3pkpjp3 |
| 硅谷101 | https://feeds.fireside.fm/sv101/rss |
| Lex Fridman Podcast | https://lexfridman.com/feed/podcast/ |
Huberman Lab、Theories of Everything 仍保留 source 配置,rss_url 为空时会生成空 cache/plan,等待补充 RSS。
┌───────────────┐ fetch RSS
│ sources.yml │───────────────┐
└───────────────┘ │
▼
┌──────────────────────────┐
│ data/scan-cache/*.jsonl │ normalized RSS entries
└────────┬─────────────────┘
│ plan.ts (date + duration + transcript status)
▼
┌──────────────────────────┐
│ data/plans/<source>.yml │ status tracking
└────────┬─────────────────┘
│ run-plan.ts (download transcript + claude -p)
▼
┌──────────────────────────┐
│ episodes/<id>/ │ slides.md + meta.yml + article.html
└────────┬─────────────────┘
│ build-all.ts (Slidev + Astro)
▼
dist/ → GitHub Pages
# 安装依赖
pnpm install
# 1. 拉取 RSS metadata
pnpm run cache:refresh
pnpm run cache:refresh -- --id=tbpn
pnpm run cache:refresh -- --limit=20
# 2. 从 cache 生成 plan
pnpm run plan
pnpm run plan -- --id=tbpn
pnpm run plan -- --min-duration=5400
# 3. 执行 plan 中 pending 的条目
pnpm run plan:run
pnpm run plan:run -- --limit=1
pnpm run plan:run -- --id=tbpn
pnpm run plan:run -- --auto-transcribe --transcribe-limit=3 --transcribe-wait-minutes=2
pnpm run plan:run -- --dry-run
# 4. metadata 校验、构建、本地预览
pnpm run normalize:meta
pnpm run build
pnpm run preview
# 5. 单集开发模式
pnpm run dev:episode <episodeId>
# 6. 本地统计 RSS cache
pnpm run analyze
pnpm run analyze -- --thresholds=30,60,120
# 7. 真实 API 转写 E2E(读取当前环境、.env.local、scripts/env.local.sh)
pnpm run e2e:transcription本地预览地址:http://localhost:4173
pnpm run cache:refresh
pnpm run plan
pnpm run plan:run -- --limit=1
pnpm run build
git add -A
git commit -m "generate rss episode"
git push生成并部署由手动或定时 GitHub Actions 触发,发布地址为 http://doublemice.github.io/poddeck/。
仓库提供三个 workflow:
Discover:定时刷新 RSS cache 和 plan,提交needs_transcript/pending队列。Deploy to GitHub Pages:手动触发,只构建并部署已有内容。Generate and Deploy:每天定时或手动触发,执行cache:refresh → plan → plan:run → normalize:meta → build → commit → deploy。
Generate and Deploy 需要在 GitHub repository secrets 中配置:
ANTHROPIC_AUTH_TOKEN=<你的 DeepSeek API key 或兼容 Anthropic token>
MIMO_API_KEY=<MiMo API key,默认用于自动转写缺 transcript 的 RSS 音频>
DASHSCOPE_API_KEY=<DashScope API key,可选,用于 TRANSCRIPT_PROVIDER=dashscope 回退>
workflow 使用 DeepSeek Anthropic 兼容环境变量:
ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic
ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4-flash
ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-v4-pro
ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-v4-pro
ANTHROPIC_MODEL=deepseek-v4-pro
ENABLE_TOOL_SEARCH=true
手动触发参数:
source:可选 source id,例如tbpn;留空表示所有 source。limit:生成数量上限,默认4。transcribe_limit:本轮提交转写任务上限,默认3。transcribe_wait_minutes:提交转写后短轮询等待分钟数,默认2。category:可选分类过滤。
push 到 main 不触发 GitHub Actions;自动生成与部署只来自定时任务或手动 workflow。
编辑 sources.yml:
sources:
- id: my-source
name: My Podcast
rss_url: https://example.com/feed.xml
min_duration: 3600
min_date: "20260101"
cache_limit: 100
color: "#2563eb"
description: 简短描述
filter_keywords: [AI, agent, llm]然后运行:
pnpm run cache:refresh -- --id=my-source
pnpm run plan -- --id=my-source
pnpm run plan:run -- --id=my-source --limit=1
pnpm run buildplan.ts 会把 episode 分成几类,并在 data/plans/<source>.yml 写入 pending、downloaded、needs_transcript 计数:
pending:RSS 自带 transcript,或data/transcripts/<id>.txt已存在downloaded:transcript 已落盘,等待继续生成 slidesneeds_transcript:RSS 只有音频和封面,等待转写补全文本
跨源去重:如果同一 episode(标准化标题 + 发布日期匹配)出现在多个 source 中,只保留第一次出现的条目。RSS <guid> 是每个 feed 专属的,同一节目不同 RSS 源的 guid 不同;时长也可能因编码差异相差几秒,因此不用 duration 做去重。
run-plan.ts 会为每个 pending episode:
- 下载 RSS transcript 到
data/transcripts/<id>.txt - scaffold
episodes/<id>/ - 调用
claude -p生成slides.md、meta.yml、article.html - 只有当 Claude 成功退出且
slides.md、meta.yml都存在时,plan 状态才写为generated
--auto-transcribe 会为 needs_transcript episode 提交自动转写。默认 TRANSCRIPT_PROVIDER=mimo,调用 MiMo chat/completions 音频理解接口,并统一本地下载、ffmpeg 切片、data URI 分段提交,避开 URL 抓取差异和 MiMo URL 100MB 限制;可设置 TRANSCRIPT_PROVIDER=dashscope 回退 DashScope 异步 ASR,DashScope 普通公网音频直接提交 URL,Megaphone/Unchained 这类受限音频走切片 data URI。分段任务的状态保存在 data/transcription-jobs.yml,临时 chunk 文本放在 data/transcripts/.chunks/,该目录用短 hash 命名并被 git ignore;所有 chunk 成功后合并为 data/transcripts/<id>.txt,plan 状态回到 pending。
转写配置项:TRANSCRIPT_PROVIDER=mimo|dashscope、MIMO_API_KEY、MIMO_BASE_URL、MIMO_MODEL、MIMO_MAX_COMPLETION_TOKENS、DASHSCOPE_API_KEY、DASHSCOPE_DATA_URI_CHUNK_SECONDS、DASHSCOPE_DATA_URI_MAX_MB。
真实 API E2E 使用 pnpm run e2e:transcription。脚本加载顺序为当前环境变量、.env.local、scripts/env.local.sh,不会创建额外本地配置文件;默认测试 MiMo,设置 TRANSCRIPT_PROVIDER=dashscope 可测试 DashScope。
pnpm run normalize:meta 会校验所有 episodes/*/meta.yml。pnpm run normalize:meta -- --fix 会修复 LLM 生成的可恢复 YAML 问题,例如双引号字符串中的非法 \',CI 在 build 前强制运行该步骤。
硬规则位于 scripts/prompts/slides-system-rules.md:
- 每条引言必须能在 transcript 里 grep 到
- 不跨 episode 串台
- 不编造公司名、年份、数字
- deck 长度按 transcript 长度扩展
- 至少 20% 内容页使用 Excalidraw 手绘图
- 倒数第二页为核心金句页
- 生成后必须导出 PNG 自审
poddeck/
├── sources.yml # RSS source 配置
├── tags.yml # 标签词表
├── episodes.yml # 历史 episode 目录兼容
├── data/
│ ├── transcripts/ # 清洗后的 transcript
│ ├── scan-cache/ # RSS normalized JSONL,按需刷新
│ └── plans/ # 每个 source 的执行状态
├── episodes/
│ ├── _templates/ # 新 episode scaffold
│ └── <episodeId>/ # 单集 Slidev 项目
├── landing/ # Astro landing
├── scripts/
│ ├── lib/rss.ts # RSS parser + transcript cleaner
│ ├── refresh-cache.ts # RSS → scan-cache
│ ├── plan.ts # scan-cache → plan
│ ├── run-plan.ts # plan → transcript + generation
│ ├── build-all.ts # episodes + landing → dist
│ └── prompts/ # generation prompts and hard rules
└── .github/workflows/
├── discover.yml # scheduled RSS cache/plan refresh
└── deploy.yml # GitHub Pages deploy
.github/workflows/deploy.yml 和 .github/workflows/generate-and-deploy.yml 设置:
PODDECK_BASE=/poddeck/PODDECK_SITE=https://doublemice.github.io
部署流程:
pnpm install --frozen-lockfilepnpm exec playwright install --with-deps chromiumsudo apt-get install -y ffmpegpnpm run normalize:meta -- --fixpnpm run build- commit generated content
- upload
dist/ - deploy GitHub Pages
仓库 Pages 设置使用 GitHub Actions。
- CI 生成需要
ANTHROPIC_AUTH_TOKENsecret;缺少该 secret 时只使用本地生成流程。 - 默认自动转写需要
MIMO_API_KEYsecret;显式TRANSCRIPT_PROVIDER=dashscope时需要DASHSCOPE_API_KEYsecret;缺少对应 secret 时needs_transcript只排队。 - Megaphone/Unchained 分段转写需要系统
ffmpeg;CI 已安装,本地执行需确保ffmpeg -version可用。 sources.yml中rss_url为空的 source 会写空 cache/plan。- GitHub Pages 深度链接依赖
landing/public/404.html做 fallback。 - 只修改转写队列、
data/transcripts/.chunks/或脚本缓存逻辑时,无需重建 GitHub Pages;新增/修改episodes/*、landing/*、data/transcripts/*.txt后生成 deck 或页面内容时需要重新 build/deploy。
Slides 由 Slidev 渲染。生成由 Claude Code 驱动。