# 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)