My podcast app has one job: every morning at 6 AM, it pulls the newest talks from one lecturer’s feed — AI and Christian-missions topics, mixed — downloads the audio, and adds it to a list I browse and play from my phone. Six hundred-some episodes now, sorted by category, searchable by title. It does that one job well.

Then I found a novel online I wanted narrated into it, and there was no button for that.

The Need: One Feed In, Nothing Else Gets In

The app’s episode list isn’t something I edit by hand day to day. It’s rebuilt every morning from a JSON file, and that file is itself rebuilt every morning from the one lecturer’s source API. Anything I added manually would just be gone the next time the 6 AM job ran — the same way a hand-edited config file disappears the moment a script regenerates it from scratch. To add something the source feed never had — a long novel about a data scientist who designs his own dog a personalized cancer vaccine — I’d need the pipeline to understand the difference between “this came from the feed” and “I put this here myself.”

The Proof: Seventeen Episodes, Same Afternoon

💬 Prompt that worked “Turn the article at coding4rosie.iotok.org into an audio file and add it as a new episode in muntak.”

I logged that request at 13:22. In under 30 minutes, all seventeen chapters — a prologue, fifteen numbered chapters, and an epilogue — were synthesized, converted to the app’s own audio format, uploaded, and playing in a real browser session, each with its own cover picture and the “NEW” badge clearing itself the moment I actually pressed play. The next morning I checked again and logged the result: “Implemented well.”

The Story: A Real Alternative I Looked at and Skipped Anyway

Plan A was Windows’ own built-in Korean voice. It costs nothing and it works, but it sounds exactly like what it is — a decade-old text-to-speech engine, flat and mechanical, which is rough for something meant to run for hours.

I’d heard of a free voice-cloning tool called Voicebox and asked if we could use that instead. It turned out to be real — an open-source, local voice studio (MIT license, several TTS engines bundled, clone your own voice from a 30-second sample) — but two things pushed me toward something else for this job. Its own documentation never actually names Korean as a supported language for any of its engines. And it wants a real install: a several-hundred-megabyte app plus a multi-gigabyte model download, ideally with an NVIDIA GPU behind it — more machinery than I wanted just to read someone else’s novel out loud once.

What worked was already sitting on the machine: edge-tts, a small Python package that borrows Microsoft Edge’s own “read aloud” feature — the same neural voices the browser uses — without an account, an API key, or a dollar spent. Three Korean voices came with it. I picked the one that sounded least like a machine reading a manual.

The How: Split by Chapter, Match the App’s Format, and a Bug That Would Have Erased It by Morning

The novel’s own headings — Chapter 1, Chapter 2, and so on — turned out to be the natural place to cut it into episodes, rather than treating it as one giant file:

  1. Pull the article and split on its own chapter markers. Seventeen sections (prologue, 15 chapters, epilogue), 800 to 17,000 characters each, with image captions and duplicated web headings stripped out so they wouldn’t get read aloud.
  2. Synthesize each chapter separatelyedge-tts --voice ko-KR-SunHiNeural --text "..." --write-media chapter.mp3 — one call per file. Seventeen files, about 74,000 characters total, finished synthesizing in well under ten minutes.
  3. Convert to the app’s own audio format: ffmpeg -i chapter.mp3 -c:a aac -b:a 96k chapter.m4a. The app’s nginx config only serves .m4a correctly, so .mp3 output wouldn’t have played.
  4. Reuse the source page’s own illustrations as episode covers, resized to 800px wide and compressed with Pillow (roughly 50KB each), instead of generating new art.

The part that would have quietly undone all of it: the app’s daily crawler doesn’t add new episodes to the existing list — it rebuilds the entire list from the source API every single run. Anything not in that feed gets dropped, silently, the next time the 6 AM job fires. My seventeen chapters would have survived exactly one day.

🗂 Claude.md Rule A daily crawler that rebuilds a data file from a source API will erase anything added by hand, unless the rebuild step is explicitly told to keep it. Fix: tag manually-added records ("custom": true), read them back out of the existing file before each rebuild, and merge them into the freshly-built list every time — never just append once and hope. Reserve an ID range the source API will never reach (900001+, against a source that tops out under 700), so the two can’t collide. Verified by actually re-running the crawler and confirming all seventeen episodes were still there afterward — not just by reading the code and assuming it would work.

Total cost: $0. edge-tts is free, ffmpeg and Pillow ran locally, and the only thing that took real time was the roughly three hours and thirteen minutes of finished audio itself — which, unlike the writing, didn’t need anyone watching it happen.