I listen to a lot of podcasts, and dynamically inserted ads have become increasingly annoying. Unlike the old days where ad reads were baked into episodes, modern podcast platforms now insert ads on-the-fly based on location, listening history, and other targeting criteria. This means I might hear the same ads over and over again on different episodes (or even the same episode!), the ads are often poorly spliced into the audio, and worst of all - ad blockers can’t help since the ads come from the same source as the content.
The YouTube workaround
Many of the podcasts I follow also publish their episodes on YouTube, and here’s the key insight: YouTube’s ad system is completely separate from the podcast’s dynamic ad insertion. While YouTube does show ads, these are not part of the video when downloading it via . The podcast content itself remains untouched.
As a bonus, yt-dlp also supports integration, which can automatically skip sponsor segments that are baked into videos. This isn’t relevant for my use case since the podcasts I follow don’t have embedded sponsor reads in their YouTube versions, but it’s a nice option to have.
So all you need is a system to automatically download the audio of new videos of my podcasts, store them and add them to a rss feed my podcast player understands. This is where Podsync comes in.
Podsync: YouTube to RSS
Podsync is a self-hosted tool that converts YouTube channels and playlists into standard podcast RSS feeds. It uses yt-dlp under the hood to download the audio from videos and serves them through a web interface that any podcast player can subscribe to.
The basic setup was straightforward:
- Configure feeds in a TOML file pointing to YouTube playlists
- Run Podsync (ideally via Docker)
- Subscribe to the generated RSS feed in the podcast player
Podsync periodically checks for new videos, downloads them, and updates the RSS feed automatically.
My improvements
While setting up Podsync, I noticed a few rough edges that I decided to fix. Here are three PRs I submitted to improve the experience:
Better playlist metadata
This PR addresses and improves how Podsync handles YouTube playlist metadata:
YouTube playlist with dedicated thumbnail and proper metadata
- Correct author and title: Previously, the feed would use incorrect metadata. Now it properly extracts the playlist title and channel name.
- Playlist thumbnails: Instead of using the thumbnail of the first video (which changes as new episodes are added), Podsync now fetches the actual playlist thumbnail via yt-dlp.
- Smarter date handling: Some publishers upload episodes as private videos before publishing them. This caused new episodes to appear with dates in the past, making them not show up at the top in podcast players. The fix ensures the most recent date is used.
A simple web UI
Podsync generates XML feeds that work great with podcast players, but sometimes I just want to quickly check what’s available or debug why an episode is missing from my podcast player. I mostly use this to verify whether an episode hasn’t synced yet or if there’s an issue on the player side. It’s also nice to just check what’s available without having to dig through the XML files.
This PR adds a minimal web frontend that lists all episodes across all feeds, sorted by date (see screenshot at the top of this article). It’s a single HTML file that loads Bootstrap from a CDN - no backend changes required. Feeds that aren’t configured to be added to the OPML are hidden from the list.
Docker documentation fix
A small but important fix: the Docker instructions didn’t mention mapping the database directory outside the container. This caused issues with data persistence and also when trying to run the container as a non-root user (since /app isn’t writable by non-root users).
Security note
If you’re exposing Podsync on the web (to sync podcasts when away from home), you’ll want to put a reverse proxy in front of it since Podsync doesn’t have built-in authentication. Make sure to use an authentication method compatible with your podcast player - basic auth works fine for me.