Files
ipodderx-rs/PROGRESS.md
rays a13f19136c Step 2: TOML config and SQLite state
config.rs replaces iPXSettings.py and feeds.plist; db.rs replaces the
per-feed .ipxd plists, history.dat and qmcache.dat. enclosures.url is
UNIQUE, which is the dedupe key the old pickle history provided.

ipx list is the first working subcommand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RPyeapneuXrCdojsaiXGbe
2026-09-09 19:37:02 +00:00

4.9 KiB

Progress

Running record of what has actually landed. Newest entry first. The full design and step list live in the plan file at /config/.claude/plans/i-want-to-create-playful-quiche.md.

Build order

  • 1. Repo skeleton — git init (main), cargo init --name ipx, deps pinned, LICENSE, README, this file.
  • 2. config.rs + db.rs — TOML config structs + SQLite schema.
  • 3. feed.rs — conditional GET, RSS-then-Atom parse, persist entries. Done when: ipx fetch populates entries, second run 304s.
  • 4. download.rs — filename derivation + sanitizer, streaming download, infer sniff, HTML rejection, move into place, dedupe, keyword/explicit filters, max_new_per_check. Done when: smoke 1, 2, 4 pass.
  • 5. retention.rs — oldest-first quota + age reaper, ipx reap [--dry-run]. Done when: smoke 6 passes.
  • 6. ipc.rs + daemon — broadcast event bus, UDS JSON-lines server, TTL scheduler, CLI-proxies-to-daemon. Done when: smoke 3 passes.
  • 7. torrent.rs — librqbit, seed to ratio/time, stall abort. Done when: smoke 5 passes.
  • 8. OPML + polish — import/export, add/rm/status, tracing setup, systemd unit, README.

Smoke tests

  1. ipx add <feed> + ipx fetch → file in download_dir/<Show>/, row in enclosures.
  2. ipx fetch again → no re-download, feed skipped for TTL.
  3. ipx daemon & + nc -U $XDG_RUNTIME_DIR/ipx.sock, send {"cmd":"fetch"} → JSON events; a concurrent ipx fetch proxies to the daemon instead of downloading in parallel.
  4. Delete a downloaded file by hand, ipx fetch → NOT re-downloaded.
  5. Torrent enclosure → downloads, moves, stops seeding at the configured ratio/time.
  6. ipx reap --dry-run under quota pressure → oldest-first hit list; real run flips rows to reaped.

2026-09-09 — Step 2: config.rs + db.rs

src/config.rs: serde structs for [general], [torrent] and [feeds.<id>] with defaults, ~ expansion, IPX_CONFIG / IPX_DATA_DIR overrides, save() at mode 0600, Feed::password() (password_env beats a literal password), Torrent::ports() parsing "6881-6889". A missing config file loads as an empty one so a fresh install works. Retention defaults are 0/0 (unlimited, keep forever) — nothing gets deleted until Ray asks for it.

src/db.rs: schema exactly as planned, WAL + busy_timeout, Db::open() idempotent, connection behind a Mutex, feed_summary() for list, now() helper. enclosures.url is UNIQUE — the dedupe key that replaces history.dat.

src/main.rs: clap skeleton with ipx list. Only the subcommands that work exist; the rest arrive with their steps.

Verified: cargo test 5/5 green (config defaults, port-range fallback incl. reversed range, password_env precedence, schema idempotency, enclosure-url uniqueness). Gate met — ipx --config <scratch> list printed both feeds and created state.db once across two runs.

Deferred: nothing. Two dead-code warnings (Config::save, Feed::password) are expected; steps 3 and 8 consume them. Next: step 3 — feed.rs.

Also added chrono 0.4 (std, clock) for RFC-2822 pubDate parsing in step 3.


2026-09-09 — Step 1: repo skeleton

Repo created at /src/ipodderx-rs, default branch main, cargo init --name ipx (edition 2024, rustc 1.95.0). LICENSE (MIT, carrying the 2010 copyright), README with the lineage note, and this file. Hello-world main.rs builds.

Dependency versions pinned today — the later steps are written against these APIs:

crate version notes
tokio 1.53.1 rt-multi-thread, macros, fs, io-util, net, sync, time, signal
reqwest 0.13.5 default-features = false; features rustls (not rustls-tls — renamed in 0.13), http2, gzip, stream, json, charset, system-proxy (env-var proxy pickup is opt-in in 0.13)
rss 2.1.1 default features; the with-syndication feature name in my notes does not exist — Atom is handled by the separate crate
atom_syndication 0.12.10
rusqlite 0.40.2 bundled
librqbit 9.0.1 default-features = false; features rust-tls, http-api-client. The default default-tls feature pulls reqwest/native-tls and an OpenSSL sha1 backend (crypto-hash), which fails to build without pkg-config/OpenSSL headers. API not yet exercised — step 7
serde 1.0.229 / serde_json 1.0.151 / toml 1.1.5
clap 4.6.6 derive
infer 0.22.0 / dirs 7.0.0 / anyhow 1.0.104 / opml 1.1.6
tracing 0.1.44 / tracing-subscriber 0.3.23 env-filter

Deferred: nothing.

Gotcha worth keeping: three feature names in the plan were wrong against current crate versions — reqwest/rustls-tls is now rustls, env-var proxy support moved behind system-proxy, and rss/with-syndication does not exist. librqbit's default features drag in OpenSSL; rust-tls is the fix. Whole tree is rustls-only now, no C TLS dependency. Next: step 2 — config.rs + db.rs. (done)