feedBack/lib/routers
byrongamatos d459caf527 fix(playlists): split cover decode (400) from persist (500), unique temp, existence re-check
Two pre-existing cover-upload issues CodeRabbit flagged on #841 (verbatim move,
so correctly not fixed there):

1. One `except Exception as e` wrapped BOTH the PIL decode and the img.save/
   tmp.replace, returned 400 for both, and echoed `e` — so a disk/permission
   failure was mislabeled a client error and could leak a filesystem path. Now:
   decode/validation -> 400 "Invalid image" (generic); save/replace failure ->
   logged 500 "could not save cover" (no detail).
2. A shared `{pid}.png.tmp` let two concurrent uploads clobber each other's temp
   file. Now a unique `tempfile.mkstemp` in the cover dir, atomic replace to
   publish. Plus an existence re-check just before publishing so an upload that
   raced a playlist delete can't leave an orphan cover.

mkstemp itself is INSIDE the try (Codex catch): an unwritable dir / full disk
raises there and is the same persistence failure as save/replace, so it hits the
logged generic-500 path instead of escaping as an unhandled 500. Cleanup guards
`tmp is not None` for the mkstemp-failed case.

Did NOT add a full per-playlist critical section (CodeRabbit's "heavy lift"):
FeedBack is single-user (Principle I), so a cover upload racing a delete on the
same id can't happen — documented in the code rather than building a lock
framework for a precluded race.

tests/test_playlist_cover_errors.py pins all of it; negative-checked three ways:
the old single-except-400 shape fails the 500 + no-leak-400 tests, and moving
mkstemp back outside the try fails the temp-creation-500 test. Fix passes 5/5.
Full suite 2405 passed; boot smoke: valid cover 200, bad image -> generic
"Invalid image" 400, no .tmp litter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 20:05:38 +02:00
..
__init__.py fix(build): move appstate.py + routers/ under lib/ so the desktop app ships them (#836) 2026-07-10 17:16:34 +02:00
artist_aliases.py refactor(server): extract the artist-alias routes into routers/artist_aliases.py (R3) (#838) 2026-07-10 18:55:03 +02:00
audio_effects.py fix(build): move appstate.py + routers/ under lib/ so the desktop app ships them (#836) 2026-07-10 17:16:34 +02:00
loops.py fix(loops): take the DB lock for the count+insert and the list read (#840) 2026-07-10 19:16:01 +02:00
playlists.py fix(playlists): split cover decode (400) from persist (500), unique temp, existence re-check 2026-07-10 20:05:38 +02:00