{"id":"5d9d32f0-202a-4f43-bb05-94152123ed7b","slug":"podforge","kind":"skill","name":"podforge","summary":"Podcast download & transcription CLI for Xiaoyuzhou, Apple Podcasts, YouTube, and common public podcast hosts. ｜ 支持小宇宙、苹果播客（Apple Podcasts）、YouTube 及常见公开播客平台 的下载与转写命令行工具","owner":"dairui1","repoUrl":"https://github.com/dairui1/podforge","license":"MIT","licenseTier":"permissive","trust":"community","stars":7,"match":null,"fewStars":false,"format":"skill","createdAt":"2026-03-14T18:33:18.000Z","addedAt":"2026-10-21T22:43:10.528Z","logoUrl":"https://avatars.githubusercontent.com/u/183250644?u=8653c9c6758fa6919e2658a3c301ffb30b7f5c8a&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["writing"],"tags":[],"runtime":"node","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-25T19:30:07.903Z","sample":false,"url":"https://conduits.dev/tools/podforge","language":"TypeScript","transports":["stdio"],"install":{"npm":"podforge"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# podforge\n\n[简体中文](./README.zh-CN.md)\n\n`podforge` is a Node-based CLI for podcast download and transcription workflows.\n\nThe project currently focuses on one concrete workflow:\n\n- Resolve a podcast episode URL from many major platforms\n- Resolve a public podcast episode page that exposes direct audio metadata or a discoverable RSS/Atom feed\n- Accept a direct remote audio URL\n- Accept a local audio file\n- Download the original audio\n- Transcribe with ElevenLabs or local `mlx-whisper`\n- Generate `audio + SRT + TXT` artifacts\n\n## Status\n\nThis project is early but functional.\n\nCurrent scope:\n\n- Source:\n  Xiaoyuzhou, Apple Podcasts, YouTube (via yt-dlp), Spotify (detection + DRM notice),\n  Pocket Casts, Castro, Ximalaya, Podcast Addict,\n  generic public podcast episode pages,\n  direct audio URLs,\n  and local audio files\n- Transcription backends:\n  `elevenlabs`, `openai`, `groq`, `deepgram`, `gladia`, `assemblyai`, `revai`, and local `mlx-whisper` on Apple Silicon\n- Outputs: original audio, `.srt`, `.txt`\n- Toolchain: `pnpm + biome + vitest + tsup`\n\nPlanned next:\n\n- Better subtitle segmentation and transcript cleanup\n- Local STT backends\n- npm publishing\n\n## Supported Platforms\n\n| Platform | Strategy | Notes |\n|----------|----------|-------|\n| **Xiaoyuzhou** (小宇宙) | Dedicated adapter | China's top podcast platform |\n| **Apple Podcasts** | iTunes Lookup API, RSS fallback | `podcasts.apple.com` episode URLs |\n| **YouTube** | `yt-dlp` audio extraction | Requires `yt-dlp` installed |\n| **Spotify** | Detection only | DRM-protected; prompts user to find RSS alternative |\n| **Pocket Casts** | oEmbed → embed page | `pca.st` share links |\n| **Castro** | HTML audio extraction | `castro.fm` episode links |\n| **Ximalaya** (喜马拉雅) | Mobile track API | Free tracks on `ximalaya.com` |\n| **Podcast Addict** | URL-encoded audio path | `podcastaddict.com` episode links |\n| **Generic** | og:audio / RSS / audio tags / JSON-LD | Fallback for any public episode page |\n\n## Requirements\n\n- Node.js 20+\n- One provider API key for remote transcription:\n  `ELEVENLABS_API_KEY`, `OPENAI_API_KEY`, `GROQ_API_KEY`, `DEEPGRAM_API_KEY`, `GLADIA_API_KEY`, `ASSEMBLYAI_API_KEY`, or `REVAI_API_KEY`\n- `ffmpeg` and `python3` for local Apple Silicon transcription\n\n## Default Engine Selection\n\n`podforge` chooses the transcription engine automatically:\n\n- If local `mlx-whisper` is available, it uses `mlx-whisper` first\n- `ELEVENLABS_API_KEY` -> `elevenlabs`\n- `OPENAI_API_KEY` -> `openai`\n- `GROQ_API_KEY` -> `groq`\n- `DEEPGRAM_API_KEY` -> `deepgram`\n- `GLADIA_API_KEY` -> `gladia`\n- `ASSEMBLYAI_API_KEY` -> `assemblyai`\n- `REVAI_API_KEY` -> `revai`\n- If local `mlx-whisper` is unavailable and none of the above are set, it falls back to `mlx-whisper` and the local run will fail until `mlx-whisper` is installed\n\nYou can always override this with `--engine <provider>`.\n\n## User Quick Start\n\nRun without installing globally:\n\n```bash\nnpx podforge --help\npnpm dlx podforge --help\n```\n\nInspect the local environment before enabling local transcription:\n\n```bash\nnpx podforge doctor\n```\n\nTranscribe a podcast episode page or audio file:\n\n```bash\nnpx podforge transcribe https://www.xiaoyuzhoufm.com/episode/69b4d2f9f8b8079bfa3ae7f2 --output-dir ./out/episode --json\n```\n\nGeneric episode-page support works best for public podcast pages that expose:\n\n- `og:audio` or similar audio metadata\n- `<audio>` or `<source>` tags\n- JSON-LD `AudioObject` or `PodcastEpisode`\n- an alternate RSS or Atom feed that links back to the current episode page\n\nThat covers many host-powered public pages without a dedicated adapter, including pages commonly built on Buzzsprout, Libsyn, Simplecast, Podbean, Transistor, Castos, Omny, Acast, and Spreaker.\n\nForce OpenAI explicitly:\n\n```bash\nexport OPENAI_API_KEY=your_key\npnpm dlx podforge transcribe https://storage.googleapis.com/eleven-public-cdn/audio/marketing/nicole.mp3 --engine openai --output-dir ./out/openai --json\n```\n\nApple Silicon local transcription with `mlx-whisper`:\n\n```bash\nnpx podforge setup mlx-whisper\nnpx podforge transcribe https://storage.googleapis.com/eleven-public-cdn/audio/marketing/nicole.mp3 --engine mlx-whisper --output-dir ./out/mlx --json\n```\n\nThe setup command installs `mlx-whisper` into a stable virtual environment under `~/.podforge/venvs/mlx-whisper`, and the CLI will auto-detect it on future runs.\n\nChunked local transcription with streaming progress:\n\n```bash\nnpx podforge transcribe https://storage.googleapis.com/eleven-public-cdn/audio/marketing/nicole.mp3 --engine mlx-whisper --chunk-duration 300 --progress jsonl --output-dir ./out/mlx --json\n```\n\nIf you prefer a persistent install:\n\n```bash\nnpm install -g podforge\npodforge --help\n```\n\n## Development Requirements\n\n- Node.js 20+\n- pnpm 10+\n\n## Development Quick Start\n\nInstall dependencies:\n\n```bash\npnpm install\n```\n\nRun the CLI from source:\n\n```bash\nexport ELEVENLABS_API_KEY=your_key\npnpm run dev -- transcribe https://www.xiaoyuzhoufm.com/episode/69b4d2f9f8b8079bfa3ae7f2 --output-dir ./out/example --json\n```\n\nBuild the distributable CLI:\n\n```bash\npnpm run build\nnode dist/cli.js --help\n```\n\n## Usage\n\nTranscribe a Xiaoyuzhou episode:\n\n```bash\nnpx podforge transcribe https://www.xiaoyuzhoufm.com/episode/69b4d2f9f8b8079bfa3ae7f2 --output-dir ./out/episode --json\n```\n\nTranscribe a public podcast episode page discovered through generic HTML or feed metadata:\n\n```bash\nnpx podforge transcribe https://example.fm/episodes/42 --output-dir ./out/episode-page --json\n```\n\nTranscribe a direct audio URL with Groq:\n\n```bash\nexport GROQ_API_KEY=your_key\npnpm dlx podforge transcribe https://storage.googleapis.com/eleven-public-cdn/audio/marketing/nicole.mp3 --engine groq --output-dir ./out/groq --json\n```\n\nTranscribe a local audio file with automatic engine selection:\n\n```bash\npodforge transcribe ./audio/interview.mp3 --output-dir ./out/local --json\n```\n\nTranscribe locally on Apple Silicon with `mlx-whisper`:\n\n```bash\nnpx podforge setup mlx-whisper\npodforge transcribe ./audio/interview.mp3 --engine mlx-whisper --output-dir ./out/local-mlx --json\n```\n\nKeep the isolated temp workspace for debugging:\n\n```bash\npodforge transcribe ./audio/interview.mp3 --engine mlx-whisper --keep-temp --output-dir ./out/local-mlx --json\n```\n\nExample output:\n\n```json\n{\n  \"ok\": true,\n  \"command\": \"transcribe\",\n  \"input\": \"https://www.xiaoyuzhoufm.com/episode/69b4d2f9f8b8079bfa3ae7f2\",\n  \"source\": \"xiaoyuzhou\",\n  \"episodeId\": \"69b4d2f9f8b8079bfa3ae7f2\",\n  \"language\": \"zho\",\n  \"artifacts\": {\n    \"audio\": \"/abs/path/xiaoyuzhou-69b4d2f9f8b8079bfa3ae7f2.m4a\",\n    \"srt\": \"/abs/path/xiaoyuzhou-69b4d2f9f8b8079bfa3ae7f2.srt\",\n    \"txt\": \"/abs/path/xiaoyuzhou-69b4d2f9f8b8079bfa3ae7f2.txt\"\n  }\n}\n```\n\nExample failure output on `stderr` when `--json` is enabled:\n\n```json\n{\n  \"ok\": false,\n  \"command\": \"transcribe\",\n  \"error\": {\n    \"code\": \"MLX_WHISPER_UNAVAILABLE\",\n    \"category\": \"dependency\",\n    \"message\": \"mlx-whisper is not available. Run `podforge doctor` to inspect your environment, then `podforge setup mlx-whisper` to install the local runtime.\",\n    \"hints\": [\n      \"Run `podforge doctor` to inspect the local runtime.\",\n      \"Run `podforge setup mlx-whisper` to install the local runtime.\"\n    ]\n  }\n}\n```\n\nFor agent automation, prefer:\n\n- `--json` for a stable success or failure envelope\n- `--progress jsonl` for machine-readable progress and terminal error events on `stderr`\n\n## Agent Skill\n\nThis repository ships a single agent skill for the full transcript workflow:\n\n- transcribe podcast audio\n- generate `audio + SRT + TXT`\n- optionally clean the transcript with Jina Reader context\n\nInstall the skill into the current project:\n\n```bash\nnpx skills add dairui1/podforge --skill transcribe\n```\n\nInstall globally:\n\n```bash\nnpx skills add dairui1/podforge --skill transcribe -g\n```\n\nThe skill lives at:\n\n- [skills/transcribe/SKILL.md](./skills/transcribe/SKILL.md)\n\nDistribution:\n\n- `skills.sh` has no separate publish step. This repository is installable with `npx skills add dairui1/podforge --skill transcribe`, and the skill shows up on `skills.sh` after users install it.\n- ClawHub supports explicit publishing from the same skill directory. This repository includes [`.github/workflows/publish-skills.yml`](./.github/workflows/publish-skills.yml), which publishes every skill in `./skills` to ClawHub when a GitHub release is published or when the workflow is run manually.\n- If a skill needs a stable ClawHub slug that differs from the local skill name, set it in `skills/<skill>/agents/clawhub.json`.\n- Configure the repository secret `CLAWHUB_TOKEN` before using the ClawHub workflow.\n- Skill metadata versions should stay semver because ClawHub publish expects semver, for example `1.4.0`.\n\nRelease flow:\n\n1. Bump the skill version in `skills/<skill>/SKILL.md` `metadata.version`.\n2. Commit and push the changes on `main`.\n3. Create a GitHub release so the `published` event triggers ClawHub publish:\n\n```bash\ngh release create v<next-package-version> --target main --generate-notes\n```\n\n4. Open the Actions run for [`.github/workflows/publish-skills.yml`](./.github/workflows/publish-skills.yml) and confirm each skill publishes successfully.\n5. If the release already exists or you only need to rerun ClawHub publish, trigger the same workflow manually with `workflow_dispatch`.\n\nThe skill teaches agents to prefer no-install entry points first:\n\n```bash\nnpx podforge transcribe <input> --output-dir <dir> --json\npnpm dlx podforge transcribe <input> --output-dir <dir> --json\n```\n\nWhen `--engine` is omitted:\n\n- local `mlx-whisper` available: use `mlx-whisper`\n- `ELEVENLABS_API_KEY`: use `elevenlabs`\n- `OPENAI_API_KEY`: use `openai`\n- `GROQ_API_KEY`: use `groq`\n- `DEEPGRAM_API_KEY`: use `deepgram`\n- `GLADIA_API_KEY`: use `gladia`\n- `ASSEMBLYAI_API_KEY`: use `assemblyai`\n- `REVAI_API_KEY`: use `revai`\n- otherwise: use `mlx-whisper`\n\nFor local Apple Silicon runs, the workflow now uses:\n\n- a per-request temp workspace\n- FFmpeg chunking with a default chunk size of `300` seconds for `mlx-whisper`\n- chunk-by-chunk progress and partial transcript events on `stderr`\n- structured error payloads for agents when `--json` or `--progress jsonl` is enabled\n- automatic cleanup unless `--keep-temp` is set\n- `podforge doctor` to inspect the local runtime\n- `podforge setup mlx-whisper` to install the local runtime into a stable venv\n\nFor low-cost live verification, the skill recommends:\n\n```bash\nhttps://storage.googleapis.com/eleven-public-cdn/audio/marketing/nicole.mp3\n```\n\nAfter transcription, the same skill tells the agent to ask whether cleanup is needed. If yes, it uses Jina Reader:\n\n```bash\nhttps://r.jina.ai/<podcast-url>\n```\n\nIt uses the episode page as external context to repair ASR mistakes, especially names, homophones, and noisy filler words, and writes a cleaned sibling transcript instead of overwriting the raw one.\n\n## Development\n\nRun the project checks:\n\n```bash\npnpm run check\n```\n\nRun formatting:\n\n```bash\npnpm run format\n```\n\nRun live transcription tests:\n\n```bash\nexport ELEVENLABS_API_KEY=your_key\npnpm run test:live\n```\n\nRun the local `mlx-whisper` live test:\n\n```bash\nnpx podforge setup mlx-whisper\nexport MLX_WHISPER_PYTHON=\"$HOME/.podforge/venvs/mlx-whisper/bin/python\"\npnpm run test:live\n```\n\n## Repository Layout\n\n```text\nsrc/\n  cli.ts\n  output/\n  sources/\n  transcribe/\ntest/\n```\n\n## License\n\n[MIT](./LICENSE)\n"}