Open-source project
aizhimou/pigeon-pod avatar
aizhimou/pigeon-pod

PigeonPod: Turning YouTube and Bilibili Channels into Podcast RSS Feeds

Listen to YouTube & Bilibili. Anywhere.

1,169 stars101 forksJavaGPL-3.0

At a glance

What is it?
PigeonPod is a self-hosted Spring Boot application that polls video channels, downloads episodes with yt-dlp, and republishes them as protected RSS feeds for any podcast client. It solves a real problem, but the dependency on yt-dlp and the YouTube Data API quota shape what it can do.
Who is it for?
Adopt PigeonPod if you already run Docker or a JVM service and you want your podcast app to carry YouTube and Bilibili channels without a cloud subscription. Skip it if you have no appetite for maintaining yt-dlp, or if your channel list is large enough that YouTube Data API v3 quota becomes the binding constraint.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 28 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap PigeonPod fills between video platforms and podcast apps

Podcast clients speak RSS. YouTube and Bilibili do not publish per-channel RSS feeds, and neither platform offers a native way to subscribe to a channel inside Overcast, Pocket Casts, AntennaPod or Apple Podcasts. PigeonPod sits in that gap. According to the README, it lets you subscribe to YouTube or Bilibili channels and playlists, then generates protected standard RSS feeds that any podcast app can consume. The target user is someone who already has a podcast listening habit and a server, and who wants video-channel audio to arrive through the same client rather than through a second app with its own notification behaviour and its own playback position tracking. The project also covers single-video subscriptions, which the README describes as turning one YouTube video into an auto-generated playlist feed. That is a narrower use case but a telling one: it means the unit of subscription is not fixed to a channel, and the feed generator treats a one-item source as legitimate. Multi-user support with role-based access is listed as a core feature, with system and feed configuration restricted to admin accounts. That places PigeonPod closer to a small shared service than to a single-person script.

How the pipeline actually moves: Spring Boot, SQLite, yt-dlp and Rome

The tech stack section is the clearest window into the architecture. The backend is Java 17 on Spring Boot 3.5, with MyBatis-Plus as the ORM and SQLite as the database. Flyway handles schema migrations, which matters for upgrade behaviour: the database evolves through versioned migration scripts rather than manual ALTER statements. YouTube data retrieval goes through the YouTube Data API v3, while actual media download is delegated to yt-dlp. RSS generation uses the Rome library. The data flow implied by that stack runs in one direction. A sync job queries the YouTube Data API for channel or playlist metadata, decides which items are new or fall within the configured filters, and hands the selected video URLs to yt-dlp for download. The resulting files land in either LOCAL disk storage or an S3-compatible bucket, and Rome renders the feed XML that podcast clients poll. Two details in the feature list confirm that this is a queued, stateful system rather than a stateless proxy. There is a download dashboard that tracks task status with retry, cancel and delete actions, and there are failed-download digests sent by email or webhook when automatic retries are exhausted. Both only make sense if download state is persisted and jobs can fail independently of the request that created them. The choice of SQLite reinforces the single-node assumption: there is one database file, referenced in the Docker example as jdbc:sqlite:/data/pigeon-pod.db.

Deployment: two supported paths and the auth switch you must not ignore

The README documents Docker Compose as the recommended route. The compose file pulls ghcr.io/aizhimou/pigeon-pod:latest, maps port 8834 on the host to 8080 in the container, sets SPRING_DATASOURCE_URL to jdbc:sqlite:/data/pigeon-pod.db, and mounts a named volume at /data. After docker-compose up -d, the application is reachable at http://localhost:8834 with default credentials root and Root@123. The second path is the JAR. It requires Java 17 or later and yt-dlp installed on the host, which is the first meaningful difference between the two options: the container image bundles its own yt-dlp runtime, while the JAR deployment expects you to provide one. The JAR command in the README creates a data directory with mkdir -p data and then runs java -jar with -Dspring.datasource.url=jdbc:sqlite:/path/to/your/pigeon-pod.db, listening on port 8080. The authentication switch deserves attention. PIGEON_AUTH_ENABLED defaults to true, and the README carries an explicit warning: set it to false only when another trusted layer already protects the web UI, such as an auth proxy, reverse proxy access control, VPN, or private network. It states plainly that an auth-disabled instance must not be exposed directly to the public Internet. This is a reasonable design, since reverse-proxy auth is a common self-hosting pattern, but it is also the single configuration most likely to be copied from a forum post without the surrounding context. The README also notes a trusted-environment auto login feature that skips manual sign-in when PigeonPod runs behind trusted access controls, which pairs with the same deployment shape.

Storage modes, retention filters and the migration gap between them

PigeonPod supports two storage modes, LOCAL and S3, and the README states that only one can be enabled at a time. S3 mode is documented as working with MinIO, Cloudflare R2, AWS S3 and other S3-compatible services. The comparison table is short and honest about the trade: LOCAL is easy to set up with no external dependency but uses local disk and is harder to scale, while S3 scales better and suits cloud deployment but requires object storage setup and credentials. The line that matters most is the one about switching modes. The README says switching storage mode does not migrate historical media automatically and that you must migrate files manually. That is a real operational constraint. If you start on LOCAL, accumulate a few hundred downloaded episodes, and later move to S3, the database will still reference the old paths until you move the files yourself. There is no documented migration command in the material provided. Retention controls partially offset the storage question. Per-feed filters and retention are listed as a core feature, controlling sync scope with keywords, duration and episode limits. That gives you a way to cap how much a feed accumulates, which matters more on LOCAL than on S3. The README does not state what happens to feed entries when retention deletes an underlying file, and that is worth checking in the wiki's Troubleshooting page before relying on it.

Where the design strains: yt-dlp, API quota and the single-node assumption

The most consequential dependency is yt-dlp. PigeonPod does not implement platform extraction itself; it shells out to a tool that is updated frequently because the platforms it targets change frequently. The README acknowledges this by listing in-app yt-dlp management as a feature: manage runtimes, switch active versions, and update yt-dlp without leaving the app. That is a practical mitigation, but it also confirms the failure mode. When YouTube changes something and yt-dlp lags, downloads fail, and the failed-download digest exists precisely because that happens. The second constraint is the YouTube Data API v3. The README lists YouTube API usage insights as a feature that monitors quota usage before sync jobs hit the limit. The framing is telling: the quota is a ceiling that sync jobs can reach, not an unlimited resource. A user with a large channel list and a frequent sync interval can exhaust the daily quota, at which point metadata refresh stops even though yt-dlp itself would still work. The third constraint is architectural. SQLite plus a single Spring Boot process means PigeonPod is designed for one node. There is no documented clustering or shared-database mode. That is a defensible choice for a self-hosted tool aimed at individuals and small groups, but it means horizontal scaling is not on the table, and the S3 storage option does not change that. The README's storage table says S3 is suitable for cloud deployment, which is true for media storage, but the application tier remains a single process.

How PigeonPod differs from Podsync and similar converters

The repository topics list podsync alongside pigeon-pod, which is the natural comparison. Podsync is a widely used service that converts YouTube channels and playlists into podcast feeds, and it is typically run as a hosted instance or a container. The difference in approach is where the work happens. Podsync-style converters generally generate a feed that points at media URLs, letting the podcast client stream or download directly from the source. PigeonPod downloads the media itself with yt-dlp, stores it in LOCAL or S3 storage, and serves the episode from its own storage layer. That is a heavier design with real consequences in both directions. On the plus side, episodes remain available if the source video is removed or made private, playback does not depend on the client's ability to reach the platform's CDN, and the built-in SSL/TLS support covers encrypted RSS traffic from your own domain. On the minus side, you pay for storage and bandwidth, you inherit the yt-dlp maintenance burden, and the sync job has to complete a download before an episode appears in the feed. The README's cookie support for YouTube and Bilibili, and the proxy-ready network access for YouTube API and yt-dlp traffic, both point at the same reality: this is a system that talks to platforms directly from your network and needs the credentials and routing to do so. If your goal is a lightweight feed that streams from the source, PigeonPod is more machinery than you need. If your goal is a durable local archive with podcast-client ergonomics, the download-first design is the point.

Licence, upgrade path and what maintenance actually costs

PigeonPod is licensed under GPL-3.0. For self-hosting that is unproblematic: you are running the software, not distributing a derivative work. The licence becomes relevant if you modify PigeonPod and distribute the modified version, or if you bundle it into a product you ship to others, because GPL-3.0 carries copyleft obligations in those cases. This is a description of the licence, not legal advice, and anyone contemplating redistribution should read the licence text and possibly consult a lawyer. On maintenance, the material supports a few concrete observations. The release cadence is visible: 1.28.0 in early May 2026, 1.29.1 in late May, 1.30.0 in early June, with a repository push recorded in August 2026. That is a project under active development, which cuts both ways. Fixes arrive, and so do schema migrations. Flyway is listed in the stack, so database changes are versioned, but any migration tool still means you should back up the SQLite file before pulling a new image tag. The moving parts you own as an operator are yt-dlp (mitigated by in-app management), the SQLite database file, the media storage volume or bucket, and the network path to YouTube and Bilibili, which may require cookies or a proxy depending on your region and the content you follow. The project offers a hosted alternative at pigeonpod.cloud, and the README links to it directly. That is worth noting for anyone weighing the self-hosted route: the maintainers are not positioning self-hosting as the only option, and the cloud offering is the escape hatch if the operational load turns out to be more than expected.

Editorial conclusion

Adopt PigeonPod if you already run Docker or a JVM service and you want your podcast app to carry YouTube and Bilibili channels without a cloud subscription. Skip it if you have no appetite for maintaining yt-dlp, or if your channel list is large enough that YouTube Data API v3 quota becomes the binding constraint. Before committing, verify three things on your own instance: that yt-dlp resolves your target channels from your network, that the SQLite file at your configured SPRING_DATASOURCE_URL path survives your backup routine, and that PIGEON_AUTH_ENABLED is set deliberately rather than left at its default by accident.

Official sources

  1. aizhimou/pigeon-pod on GitHub
  2. License: GPL-3.0
  3. Project website
  4. README
  5. Releases
Community notes

Community notes