fix(thumbnails): surface backfill counts + tighten validity check #47
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Two coupled fixes for "missing thumbnails but triggering backfill found
nothing" (operator-flagged 2026-06-01):
1. Backfill UI was a black box
POST /api/thumbnails/backfillreturned only{celery_task_id}and theadmin card said "Enqueued." with no counts. Impossible to tell whether
the scan found 0 candidates, 5000 candidates, or whether the worker was
even picking up the task. "Found nothing" was indistinguishable from a
broken queue.
_run_backfill_scan(shared helper).{scanned, enqueued, ok, regenerated}. The actual thumbnailgeneration still goes to the thumbnail Celery queue per row.
Scanned 5,432 · enqueued 3 (2 regenerated) · 5,429 ok.2.
_thumb_is_validaccepted header-only corrupt filesThe magic-byte check passed for any 8+ byte file starting with a JPEG or
PNG header, including empty/truncated/zero-pad files that browsers
render as broken. Backfill counted these as
okand never regenerated.MIN_THUMB_BYTES(256). Real thumbnails areminimum ~2KB even on solid-color sources; header-only corrupt files
top out around 12 bytes. 256 sits well above corrupt and well below
any legitimate thumbnail.
Test plan
43b778atell us which scenario the broken-thumbnail tiles are actually in.
🤖 Generated with Claude Code
Two coupled problems, operator-flagged 2026-06-01: "missing thumbnails but triggering backfill found nothing." 1. **Backfill UI was a black box.** `POST /api/thumbnails/backfill` returned just `{celery_task_id}` and the admin card said "Enqueued." with no counts. There was no way to tell whether the scan found 0 candidates, 5000 candidates, or whether the worker even picked up the task. "Found nothing" was indistinguishable from a broken queue. Fix: refactor the scan into a sync helper (`_run_backfill_scan`) shared by the Celery task and the API endpoint. The API now runs the scan in an executor and returns `{scanned, enqueued, ok, regenerated}`. The actual thumbnail generation work still goes to the thumbnail Celery queue per row via `generate_thumbnail.delay()` — the scan itself is fast (SELECT id+thumbnail_path + a file.stat() per row). 2. **`_thumb_is_valid` accepted header-only corrupt files.** The magic-byte check passed for any 8-byte file starting with a JPEG or PNG header, including empty/truncated/zero-pad files that browsers render as broken. Backfill counted these as `ok` and never regenerated. Fix: also require file size ≥ MIN_THUMB_BYTES (256). Real thumbnails are minimum ~2KB even on solid-color sources; header- only corrupt files top out around 12 bytes. 256 is well above the corrupt floor and well below any legitimate thumbnail. Plus the admin card now shows the per-run counts instead of "Enqueued.": Scanned 5,432 · enqueued 3 (2 regenerated) · 5,429 ok