Back to Experiments
Published on
Published
Last updated
Updated

Apple Podcast Helper

A tiny CLI tool that turns Apple Podcasts transcripts into Markdown so I can skim episodes and catch up on shows I would’ve otherwise missed.


I follow more podcasts than I can reasonably keep up with. I'm counting 20+ shows with multiple new episodes per week. My feed is pretty much always at 100+ unplayed episodes that are on average ~1 hour long. Even when I do get to listen to a full episode, it's usually while commuting, so I never really get a chance to take down notes.

I noticed recently that Apple Podcasts automatically generates transcripts after a new episode is published, allowing a listener to follow along and jump through the episode in a UI that's similar to how Apple Music handles lyrics. Soon after, I found this little transcript viewer tool made by Alex Beals, which also showed me where these transcripts were being stored locally on my Mac. I started using this tool to extract transcripts to put directly into ChatGPT or NotebookLM to give me quick summaries and help me with notes. However, even then, it felt like too much friction, especially as new episodes continued to stream into my feed every day.

So, I got inspired by Lee Robinson's post on Personal Software and created Apple Podcast Helper, a little CLI tool for myself to reduce some of that friction.

How it works

The tool reads from Apple Podcasts' local TTML cache and converts those transcripts into markdown files. Running node extract-transcripts.js syncs all cached transcripts, which is typically those for unplayed and downloaded episodes with the way my Apple Podcasts is set up. Since the transcript export just reads local files with no external API calls, it's fast and I always run it to grab all transcripts at once.

If you add a GEMINI_API_KEY to your environment variables, that's where the tool really shines by also generating summaries of the transcripts. I wrote a prompt for Gemini 2.5 Flash model that pulls out key takeaways with timestamps, notable quotes, and a high-level overview. Enough to know whether an episode is worth my time.

There's an interactive selector that lets me pick which episodes to process. I can filter by show (e.g. --show="Hard Fork") or station (e.g. --station="Daily") using fuzzy search, or pass just --show to first pick a show interactively and then an episode.

Interactive episode selector UI
Interactive show selector UI

Once summaries are generated, I can read them in the terminal directly. This includes navigating within a summary and jumping between summaries all with keyboard shortcuts.

Summary viewer with keyboard navigation for takeaways, quotes, and timestamps.

The tool caches summaries so I'm not hitting the API twice for the same episode. And if I want to read something outside the terminal, I can export unplayed summaries to a different directory defined in .env as SUMMARY_EXPORT_TARGET by running node scripts/export-unplayed-summaries.js. My workflow is to export to my iCloud Obsidian folder that I define in the environment variables, which allows me to easily access these summaries on my phone when I'm on the go.

Where it falls short

I built this version quickly with Codex when the CLI first came out. It still has some rough edges and bugs. That's fine, since it does exactly what I need it to do (in the spirit of "Personal Software").

A few specific known shortcomings that I can't really be bothered to fix:

  • macOS only. The tool reads from Apple Podcasts' cache location, which doesn't exist on other platforms. The export feature is my workaround for accessing summaries on my phone.
  • Caching doesn't always behave. Sometimes stale markdown sticks around when it shouldn't and prevents played podcasts from getting removed from the interactive UI selector. My fix is dumb but fast: delete the transcripts/ and summaries/ folders, rerun, move on. I couldn't be bothered to figure out how the caching was actually implemented to troubleshoot, and this fix is quick enough that it doesn't bother me to do. 1
  • Some episodes have no transcript. Apple only caches TTML for episodes you've actually played (or at least started). If nothing exports for a particular episode, I just wait until the transcript shows up. I could probably get clever and pass the cached audio through Whisper, but so far, Apple has always eventually produced a transcript so might as well just wait.

If any of this sounds useful, the code is on GitHub and you can use it for yourself. Just clone it and run pnpm install. Feel free to also leave any feedback by creating issues.

Footnotes

  1. 2026-01-08 update: This bug had stuck around since the first version I built in August 2025, including when I tried to fix it again in October 2025. With the progress from GPT-5.0 to GPT-5.2, I decided to take another stab. I had GPT-5.2-high review the entire repo and point out potential bugs and improvements (and I specifically asked it to take a deep look at this caching bug). It produced a plan, and then I had GPT-5.2-Codex-high implement that plan end-to-end. The fix (commit 49ed309) seems to have worked, and I haven’t seen the stale-cache behavior since. It’s kind of wild how fast these models are improving.