let's backfill your watched movies on popfeed

README.md

popback #

Rate TMDB movies in a TUI, backfill ratings to popfeed. Resumes where you left off -- close it, come back, keep rating.

Why #

Popfeed's own backfill is unreliable: duplicates, broken pagination, missing ratings. This reads your CSV of rated movies and backfills one entry at a time with proper error tracking.

Usage #

export TMDB_API_KEY=...
popback viewed [output.csv]

export TMDB_API_KEY=... POPFEED_AUTH_TOKEN=...
popback backfill [input.csv]

Resume is automatic: viewed skips already-rated movies from the CSV. backfill skips entries already posted to popfeed.

How it works #

viewed #

Streaming TUI: movies arrive from TMDB one page at a time (~20). A background fetch runs while you rate so the next movie is always ready. Press b to go back and re-rate. Results write to CSV on exit.

TMDB pages --> [pre-fetch buffer] --> movie at cursor --> rate/skip --> CSV

backfill #

Reads CSV, posts each entry to popfeed's internal API two-step: a review (rating + metadata) and an experience (watched/seen timestamp). Pure request builders are tested separately from HTTP I/O.

popfeed internal API #

No public docs. Endpoints reverse-engineered from browser devtools:

  • POST /api/rest/v1/review
  • POST /api/rest/v1/experience

Auth: JWT token from browser session, sent as cookie.

Persistence #

CSV format: id,rating,seen,skip. Human-readable, append-only. The store interface is mocked in tests so the write path is tested without disk.

Design #

  • Sandwich pattern: impure gather (CSV, TMDB) -> pure logic (build requests, dedup) -> impure commit (write CSV, post HTTP).
  • Streaming TUI: no batch cap. Async pre-fetch hides network latency.
  • The CSV is the skip set. No in-memory state across restarts.
  • slog to stderr for ops, fmt to stdout for UX. No log noise.
  • Table-driven tests, no shared mutable state.

Environment #

TMDB_API_KEY           TMDB key or JWT bearer token
POPFEED_AUTH_TOKEN     Popfeed session JWT (for backfill)