{ "title": "New package python_pkg/wsg_grabber: scrape 4chan /wsg/ for videos and triage them with an embedded-mpv keep/pass reviewer", "objective": "There is no tool for pulling /wsg/ video content and deciding what to keep. Build one that walks every thread on the board, downloads every video it has not already seen, and presents them one at a time in a tinder-style reviewer -- mpv embedded in a Tk window via --wid with Keep/Pass controls directly beneath the video. The defining requirement is that a video reviewed once is never shown or downloaded again, across runs and across reposts. Success: running the reviewer plays a downloaded video within seconds while the rest of the board continues downloading behind it; keep and pass relocate the file; and a subsequent run offers only genuinely new videos.", "acceptance_criteria": [ "Deduplication uses the API's own `md5` field (base64 MD5 of the whole file, verified byte-exact against a live download) as the sqlite primary key, so an already-seen file costs zero bytes to skip and a cross-thread repost of identical bytes collapses onto the existing row", "A pass moves the file to trash/ and a keep moves it to keep/; there is no unlink() anywhere in the package except to discard a corrupt partial download, so nothing the user might want back is ever destroyed", "The board is polled politely: at most one request per second to a.4cdn.org, If-Modified-Since on every API request so an unchanged thread costs a 304, one persistent requests.Session (required, because i.4cdn.org 429s under load and sets __cf_bm/_cfuvid cookies), and Retry-After-aware exponential backoff", "A file that 404s or is deleted upstream reaches a terminal `gone` state and is never retried on any future run; a file already downloaded keeps its state even if the post is deleted upstream", "Downloads are prioritised over further indexing, so the first playable video reaches the screen seconds after launch rather than after the whole board is catalogued", "100% branch coverage across every module including ui.py and player.py, with zero `# noqa` and zero `# type: ignore` (both blocked by pre-commit), ruff `select = [\"ALL\"]` clean, mypy strict clean, pylint 10.00/10, and every file under the repo's 500-line cap", "No test constructs a real tkinter.Tk(), starts a real mpv, opens a socket, or writes outside tmp_path; an autouse fixture redirects Path.home, HOME and XDG_DATA_HOME so the suite cannot touch the real ~/.local/share/wsg_grabber" ], "out_of_scope": [ "Any board other than /wsg/ -- the board is a hardcoded constant, not a flag, per the explicit scope decision", "A disk budget, cache eviction or download cap -- the requirement is to download everything, so a first run is deliberately ~9,000 files and 20-25GB", "Automatic emptying of trash/ -- clearing it is the user's decision and the package must never do it", "Perceptual/near-duplicate detection -- md5 catches byte-identical reposts only, which is the dominant case on /wsg/; a re-encoded repost is a different file and will be shown again", "python-mpv as the player binding -- it resolves libmpv at import time and would break CI, so mpv is driven as a subprocess over its JSON IPC socket instead" ], "verifier": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.wsg_grabber scrape --seconds 100 (downloads real files); PYTHONPATH=~/testsAndMisc python3 -m python_pkg.wsg_grabber (reviewer window renders video inside the Tk frame with the control bar beneath); mark files reviewed and rescan, confirming kept/passed counts are unchanged; python -m pytest python_pkg/wsg_grabber --cov --cov-branch --cov-fail-under=100; pre-commit run --files " }