Skip to main content

Podcast Helper — universal audio stream consumer exposed as library, argparse CLI, click CLI, FastAPI HTTP surface and MCP tools. URL-in → PCM-out for local files, direct audio URLs, RSS feed enclosures, and yt-dlp-supported sources (YouTube, Vimeo, Twitch, SoundCloud, …). Built on youtube-helper + ffmpeg + feedparser + podcastparser; Shannon-correct resampling, mono downmix or native channels preserved.

Project description

Podcast Helper

🇫🇷 · 🇬🇧

CI License: BSD-3-Clause Python

Podcast Helper belongs to a collection of libraries called AI Helpers developed for building Artificial Intelligence.

Universal audio stream consumer for podcasts and any audio-bearing URL. URL-in → PCM-out for local files, direct audio URLs (RSS enclosure MP3 / M4A / Opus / WAV / HLS m3u8), RSS / Atom feed URLs (auto-picks the latest episode), and every yt-dlp-supported source (YouTube, Vimeo, SoundCloud, Twitch VOD / live, …). Refuses Spotify-DRM and Apple Podcasts catalog URLs upfront, with clear hints toward the RSS feed workaround.

🌍 AI Helpers

logo

Documentation

💻 Documentation

📋 Examples

Why it exists

Podcast pipelines (ASR, diarization, summarisation, search indexing) usually start with the same question: "give me a stream of PCM frames from this URL, never mind whether it's a .mp3 link, a feed, a YouTube video, or a podcast hosted on a CDN I've never heard of." This library is that one function — and the small extras around it (feed, latest_episode) that make working with RSS sources friendly.

Installation

PrerequisitesPython 3.10–3.13 and git, ffmpeg, cross-platform:

  • 🍎 macOS (Homebrew): brew install python git ffmpeg
  • 🐧 Ubuntu/Debian: sudo apt update && sudo apt install -y python3 python3-pip git ffmpeg
  • 🪟 Windows (PowerShell): winget install Python.Python.3.12 Git.Git Gyan.FFmpeg

We recommend using Python environments. Check this link if you're unfamiliar with setting one up: 🥸 Tech tips.

From source

# Core library
pip install "git+https://github.com/warith-harchaoui/podcast-helper.git@v0.3.5"

# Optional surfaces
pip install "podcast-helper[cli] @ git+https://github.com/warith-harchaoui/podcast-helper.git@v0.3.5"
pip install "podcast-helper[api] @ git+https://github.com/warith-harchaoui/podcast-helper.git@v0.3.5"
pip install "podcast-helper[api,mcp] @ git+https://github.com/warith-harchaoui/podcast-helper.git@v0.3.5"

PyPI release coming soon.

This pulls in youtube-helper (and transitively yt-dlp, os-helper, audio-helper, video-helper) plus feedparser + podcastparser for RSS.

Quick start

import asyncio
import podcast_helper as ph

async def main():
    # Pass *any* URL — file, direct mp3, RSS feed, YouTube, SoundCloud, Twitch VOD.
    async for frame in ph.extract_audio_stream(
        "https://feeds.npr.org/510289/podcast.xml",   # ← RSS, auto-pick latest episode
        target_sample_rate=16000,
        to_mono=True,
        frame_ms=20,
    ):
        # frame["pcm"]: np.float32 (320,) for 20ms @ 16kHz
        # frame["t_abs_s"]: 0.0, 0.02, 0.04, ...
        await asr.feed(frame["pcm"])

asyncio.run(main())

For the full catalog of recipes (RSS, yt-dlp sources, live streams, stereo / multichannel, anti-aliasing, downstream ASR / VAD / summarisation pipelines), see 📋 EXAMPLES.md.

What URLs are accepted

Source Detection What happens
Local file / file:// path exists on disk OR file:// scheme ffmpeg opens it directly.
Direct audio URL (.mp3, .m4a, .opus, .wav, .m3u8, …) URL extension is a known audio container ffmpeg opens it directly with your headers= if any.
RSS / Atom feed (.xml, .rss, .atom) URL extension is a known feed container podcastparser (fallback: feedparser) parses it; latest episode's enclosure is fetched.
YouTube / Vimeo / SoundCloud / Twitch VOD / Twitch live / … yt-dlp's extractor identifies it yt-dlp picks bestaudio*, hands the direct URL + headers to ffmpeg.
Generic web URL (anything else) yt-dlp's generic extractor URL used as-is.
Spotify (open.spotify.com) hostname match NotImplementedError — Spotify audio is DRM-gated. Use the show's RSS feed if it exists.
Apple Podcasts (podcasts.apple.com) hostname match NotImplementedError — Apple URLs point to the catalog, not the audio. Use the show's RSS feed (linked on the show's site, or via getrssfeed.com / Podcast Index).

Signal-processing correctness

When target_sample_rate differs from the source rate, the conversion is performed by ffmpeg's libswresample (default) or libsoxr (resample_quality="high"). Both apply an anti-aliasing low-pass filter at the new Nyquist frequency (target_sample_rate / 2) before decimation — satisfying the Shannon-Nyquist sampling theorem. Naïve subsampling is never used.

Channel handling has exactly two modes — no synthetic upmix:

to_mono Output shape What ffmpeg does
True (default) (n_samples,) Standard downmix (stereo → L+R with -3 dB, 5.1 → ITU mix)
False (n_samples, n_channels) interleaved Preserves the source's native channel count

Working with RSS feeds explicitly

If you want to inspect or select episodes yourself:

import podcast_helper as ph

# Full episode list, most-recent first
episodes = ph.feed("https://feeds.npr.org/510289/podcast.xml", max_episodes=20)
for ep in episodes:
    print(ep["published_at"], "—", ep["title"], "—", ep["duration_seconds"], "s")

# Or just the latest one
ep = ph.latest_episode("https://feeds.npr.org/510289/podcast.xml")
print(ep["title"], "→", ep["enclosure_url"])

# Then stream its audio
import asyncio
async def main():
    async for frame in ph.extract_audio_stream(ep["enclosure_url"]):
        ...
asyncio.run(main())

Each Episode dict has a normalised schema regardless of feed flavour:

{guid, title, description, link, published_at (ISO UTC),
 duration_seconds, enclosure_url, enclosure_type, enclosure_size_bytes,
 image_url}

Multi-surface exposure

podcast-helper exposes the same public functions through four interchangeable surfaces — pick the one that fits the caller.

Surface Entry point Extra Best for
Library (async iterator) import podcast_helper as ph Python code, notebooks, downstream ASR / VAD / summarisation
argparse CLI podcast-helper — (stdlib only) shell scripts, CI, ffmpeg pipelines
click CLI podcast-helper-click [cli] click-native shells (bash / zsh completion, colored help)
FastAPI HTTP uvicorn podcast_helper.api:app [api] HTTP microservices, cross-language callers
MCP server podcast-helper-mcp [api,mcp] Claude Desktop, MCP-aware agents, IDE integrations

Install any combination of extras:

pip install 'podcast-helper[cli]'          # + click twin
pip install 'podcast-helper[api]'          # + FastAPI HTTP surface
pip install 'podcast-helper[api,mcp]'      # + MCP tools on top of FastAPI
pip install 'podcast-helper[cli,api,mcp]'  # everything

Every surface publishes the same verbs — feed, latest, stream, record, probe — with identical argument names, so switching between them is a copy-paste. The Dockerfile in this repo ships the FastAPI + MCP surfaces on port 8000 out of the box (docker build -t podcast-helper . && docker run --rm -p 8000:8000 podcast-helper).

For an ambitious visual product on top, see GUI.md. For a competitive comparison against the Python audio / podcast ecosystem, see LANDSCAPE.md.

Live streams

For YouTube / Twitch live URLs, the resolved direct URL is typically an HLS .m3u8 manifest. extract_audio_stream detects this (is_live=True) and automatically disables -re real-time pacing (the source paces itself). The async iterator runs indefinitely until the live stream ends; callers should break when they're done.

speed != 1.0 for live streams raises ValueError (as of v0.2.0) — you can't fast-forward beyond the live edge. Use speed=... on VOD only.

Roadmap

Version Feature
v0.1.0 extract_audio_stream + feed + latest_episode. yt-dlp + ffmpeg + feedparser + podcastparser.
v0.2.0 (this release) record_to="ep.mp3" | ".m4a" | ".opus" | ".ogg" | ".flac" | ".wav" (ffmpeg multi-output: PCM to caller + compressed archive to disk in parallel). speed: float for VOD (raises on live), via atempo= filter (pitch-preserving).
v0.3.0 start_instant / end_instant for VOD seek. apple_podcasts_to_rss(url) via iTunes Search API. Podcast Index API integration.
v0.3.0 apple_podcasts_to_rss(url) via iTunes Search API. Podcast Index API integration. Mic capture moves to capture-helper.
v0.4.0+ Chapters (ID3 CTOC/CHAP, Podcasting 2.0 <podcast:chapters>), transcripts, OPML import/export.

Author

Acknowledgements

Special thanks to Mohamed Chelali and Bachir Zerroug for fruitful discussions.

License

This project is licensed under the BSD-3-Clause License — see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

podcast_helper-0.3.6.tar.gz (43.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

podcast_helper-0.3.6-py3-none-any.whl (36.0 kB view details)

Uploaded Python 3

File details

Details for the file podcast_helper-0.3.6.tar.gz.

File metadata

  • Download URL: podcast_helper-0.3.6.tar.gz
  • Upload date:
  • Size: 43.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for podcast_helper-0.3.6.tar.gz
Algorithm Hash digest
SHA256 3bcdb210c2baee7f71c109c387c653430a127dca86d40ce26a834aaa7966cd04
MD5 c6e16111e1b74fb99e0648e58ed503f5
BLAKE2b-256 6f849e19fa736183c076b6b7d9990396c0f209449767ad791588848f8f923078

See more details on using hashes here.

File details

Details for the file podcast_helper-0.3.6-py3-none-any.whl.

File metadata

  • Download URL: podcast_helper-0.3.6-py3-none-any.whl
  • Upload date:
  • Size: 36.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for podcast_helper-0.3.6-py3-none-any.whl
Algorithm Hash digest
SHA256 630ac9ab6b4e3af673b7ad02359cbdb72b457ab82edbc007f653b99803b7087b
MD5 c9df2bdf27907f1344b30109b1bc7be9
BLAKE2b-256 b73f25cf6af83aa434b4ec5519fd23c2c26cfd7f3b400c1aba475c56e5b7c22c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page