The Directory was one grid of every listed feed, 1,472 in production, under Podcasts and Blogs tabs, a wall of about 30 category buttons and a sort menu, with a picked category's subcategories appearing as a second row of buttons that looked like the first. It read as a list to scroll, and the two rows were hard to tell apart. It opens now on the ten most subscribed feeds, ranked, and the categories as tiles, each with three of its shows' covers fanned in its corner on the tint its initials would get, so the shows colour it and no theme's palette changes. A category has a page of its own: its most subscribed, its subcategories with how many each holds, and its grid. See all is every feed, as the grid was. Podcasts or Blogs holds across every page. Popular, the ten most subscribed, had a place of its own in the feed list; it is the Directory's first section instead, and its key, g p, is gone. /api/popular stays for scripts. The search box finds a feed in the Directory by name (#126). It said "Search items…" there and did nothing, since loadEntries returns early for a place that lists feeds. A feed you do not subscribe to opens a page of its own (#128): its cover, category (a link to that category's page), how many here subscribe, a subscribe button and its latest twenty items, each with its title, linking to the post where it has one, its first lines, date and length. Before, a click on it did nothing; only its + did. The items come from GET /api/directory/{id}, which answers only for a feed the Directory lists, so a guessed id reaches nothing private, and carries no file or its address. The feed's own description would belong at the top, but the database does not keep one. Every tile and chart row takes the keyboard, as the feed list's rows do. Feed text made plain keeps a space at a line break or a paragraph's end, which glued "2010)Recorded" together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3.5 KiB
ipx
A self-hosted podcatcher for a household. It checks your feeds, downloads the episodes, and serves
a web UI modelled on the 2004 Mac app iPodderX, for any number of people sharing one copy of
the files. One Rust binary, ipx, is both the daemon and the command line.
It is a rewrite of ipodderx-core, the Python engine behind iPodderX (2004-2008, Ray Slakinski & August Trometer).
What it does
- The web UI. It has a toolbar, and a feed list that opens with the Directory, Currently Listening and All Subscriptions. Items sit in a sortable table with a Files pane, and there is a player bar. It comes in Dark, Light and Classic themes, and works on a phone.
- Several people, one copy. Each person has their own subscriptions and their own read, pinned and playback state. There is one file on disk per episode, however many people want it. People sign in with a password or through a proxy (Cloudflare Zero Trust or Authentik), and admins manage accounts and settings.
- Scanning. Feeds are checked on a schedule, globally or per feed, and a feed's own TTL is honoured. Keyword, explicit-content and media-type filters decide what is downloaded, with a limit on how many of a feed's newest episodes are downloaded.
- Downloads. Files come over HTTP or BitTorrent and are filed into a folder per feed. Retention deletes the oldest files to stay under a disk quota or an age limit, and never touches an item someone has pinned.
- OPML. You can import and export your own subscriptions. You can also subscribe to an OPML URL, which keeps a whole list in step as a folder.
Run it
With Docker:
docker build -t ipx .
docker compose up -d
docker-compose.yml is set up for the author's own server. Point its image and its three volumes
(/config, /data and /downloads) at yours first. The UI is on port 8099. BitTorrent uses 6881
over TCP and UDP. Files are written as PUID/PGID, 99:100 by default.
From source:
cargo build --release
./target/release/ipx daemon
The first start creates admin / ipodderx. Sign in at /login, then change it:
echo -n 'a good password' | ipx user passwd admin
The UI is plain HTTP, so put TLS in front of it if it is reachable from outside your network.
Documentation
| docs/configuration.md | Every config key, path and environment variable |
| docs/cli.md | Every command, including ipx user |
| docs/users.md | Accounts, and what several people share |
| docs/sso.md | Signing in through Cloudflare Zero Trust or Authentik |
| docs/architecture.md | How it works: modules, schema, control socket, HTTP API |
| CHANGELOG.md | What changed, by release |
| CLAUDE.md | Notes for working on the code, including how production is deployed |
Tests
cargo test # the engine: parsing, filters, retention, schedules, SQL, per-user state
node tests/page-smoke.js # the page script loads without throwing
node tests/native-bridge.js # the page hands playback to a native shell
npx playwright test # a real browser against a real daemon on fixture feeds
npm install gets the test runner, and npx playwright install --with-deps chromium gets the
browser.
License
MIT, see LICENSE. The icons are Font Awesome Free 7.3.1 by @fontawesome, under CC BY 4.0, embedded as SVG.