No description
  • Python 98.1%
  • Dockerfile 1.1%
  • Shell 0.8%
Find a file
ventoz91 a58f4127b6 Add correct_review(): an explicit escape hatch for a wrong decision
decide_review()'s one-way guard (ReviewAlreadyDecided) stays untouched
-- correct_review() is a separate, deliberately-named function for a
human overriding an already-decided review on purpose, and it records
the override (ReviewResult.corrected_from/corrected_at) rather than
silently rewriting history. SafeArr's new /decisions correction UI
calls this in-process; no HTTP route needed for it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 22:13:12 -06:00
poisonreview Add correct_review(): an explicit escape hatch for a wrong decision 2026-08-12 22:13:12 -06:00
tests Add correct_review(): an explicit escape hatch for a wrong decision 2026-08-12 22:13:12 -06:00
.dockerignore Add Dockerfile + entrypoint, update README to reflect actual status 2026-08-10 16:49:20 -06:00
.gitignore Replace bare review_scan.py with a proper package skeleton 2026-08-09 21:22:16 -06:00
docker-entrypoint.sh Add Dockerfile + entrypoint, update README to reflect actual status 2026-08-10 16:49:20 -06:00
Dockerfile Set PYTHONUNBUFFERED=1 so docker logs actually shows output 2026-08-10 19:54:33 -06:00
pyproject.toml Implement disk-backed review state and the review UI/API server (Phase 2/3) 2026-08-10 12:52:50 -06:00
README.md Add correct_review(): an explicit escape hatch for a wrong decision 2026-08-12 22:13:12 -06:00
TODO.md Add correct_review(): an explicit escape hatch for a wrong decision 2026-08-12 22:13:12 -06:00

poisonreview

Generic media-review engine: sample representative screenshots from a video, generate a review session, and return a human approve/reject decision. Built for catching mislabeled/"poisoned" video files in a media library (e.g. kids' shows uploaded under a legit episode name but containing unrelated or inappropriate content), but deliberately ignorant of why a caller wants something reviewed.

This package is deliberately ignorant of Sonarr, Radarr, Plex, or "kids content" — those concepts belong to the consuming project. See SafeArr, the opinionated Sonarr/Radarr quarantine orchestrator that depends on this package for exactly that reason.

from poisonreview import create_review

review = create_review(source="/quarantine/movie.mkv", screenshots=30)
review.id
review.frames        # [{"image": "07.jpg", "timestamp_sec": 483.4}, ...]
review.approve()
# or: review.reject(reason="Wrong content")

Requirements

  • Python 3
  • ffmpeg and ffprobe on PATH

No other dependencies (stdlib only) for the core package.

Status

The core create_review()/approve()/reject() contract above is real and working, with review state persisted to disk (not just browser localStorage) so a decision made in one process is visible to another. poisonreview serve runs the actual review UI/API a human uses:

poisonreview serve --port 8790

poisonreview scan -i <library-dir> -o <output-dir> still does the original bulk-sweep static-gallery workflow (the original review_scan.py behavior), for auditing a whole library rather than reviewing a single quarantined file. A decision can also be corrected after the fact via correct_review() — a deliberate second entry point alongside decide_review()'s one-way guard, not a loosening of it; see TODO.md Phase 10. 66 tests cover all of this — see TODO.md for exactly what's done vs. still open per phase (the AI-assisted suspicion-scoring classifier is still unbuilt).

A Dockerfile exists (docker build ., needs ffmpeg/gosu which it installs itself) and is build-tested and running in production as part of SafeArr's docker-compose setup, which also verified the Sonarr/Radarr integration this package feeds against a live instance — see SafeArr's own README/TODO.md for specifics.

How it works

For each video, ffprobe gets the duration, then a single ffmpeg pass seeks past an intro margin and decodes forward at a fixed interval to pull N frames evenly across the episode — one process per video rather than N separate seeks, which is significantly faster on network-mounted libraries. See TODO.md for the fingerprint-based cache invalidation, HTML-injection hardening, disk-backed review state, and per-frame timestamps built on top of that core loop.