audio streaming app plyr.fm

docs: update STATUS.md with PRs #963-979 (#980)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

authored by zzstoatzz.io

Claude Opus 4.6 and committed by
GitHub
4fe2098a 42190d76

+30 -3
+30 -3
STATUS.md
··· 47 47 48 48 ### February 2026 49 49 50 + #### image thumbnails + storage cleanup (PRs #976-979, Feb 27) 51 + 52 + **96x96 WebP thumbnails for artwork**: track artwork and avatars display at 48px but full-resolution images (potentially megabytes) were being served. now generates a 96x96 WebP thumbnail (2x retina) on upload, stored as `images/{file_id}_thumb.webp` alongside the original. Pillow handles center-crop, LANCZOS resize, WebP encode. nullable `thumbnail_url` column on tracks, albums, and playlists. frontend falls back to `image_url` when `thumbnail_url` is null, so partially-backfilled states are safe. `generate_and_save()` helper wired into all image upload paths: track uploads, album covers, playlist covers, and track metadata edits. 53 + 54 + **storage protocol**: new `StorageProtocol` with `@runtime_checkable` formalizes the R2Storage contract. `build_image_url()` constructs public URLs without HEAD checks (caller knows the image exists). `save_thumbnail()` uploads WebP data to the image bucket. storage proxy typed as `StorageProtocol` in `__init__.py`. export_tasks decoupled from `settings.storage` — uses `storage.audio_bucket_name` instead. 55 + 56 + **backfill script**: `scripts/backfill_thumbnails.py` follows the embeddings backfill pattern (`--dry-run`, `--limit`, `--concurrency`). queries tracks/albums/playlists where `image_id IS NOT NULL AND thumbnail_url IS NULL`, downloads originals via httpx, generates thumbnails, uploads to R2, updates DB rows. 57 + 58 + 9 thumbnail tests, 5 storage protocol/regression tests. 533 total tests pass. 59 + 60 + --- 61 + 62 + #### jam polish + feature flag graduations (PRs #963-975, Feb 25-27) 63 + 64 + **jam UX fixes** (PRs #963-964): eliminated the "no output" state — auto-claim output when nobody has it. restructured jam header UI. 65 + 66 + **feature flag removals** (PRs #965, #969): jams and PDS audio uploads graduated to GA — available to all users without flags. 67 + 68 + **data fix** (PR #966): `support_gate` JSONB null vs SQL NULL — gated tracks were invisible to backfill queries because `IS NULL` doesn't match JSONB `null`. fixed with `none_as_null=True`. 69 + 70 + **loading state polish** (PR #972): fade transitions and `prefers-reduced-motion` support across loading states. 71 + 72 + **network artists perf** (PRs #970, #973-975): Bluesky follow graph cached in Redis. parallelized network artists fetch with other homepage data. module-level cache persists across navigations. fixed auth race where fetch fired before session was ready. 73 + 74 + --- 75 + 50 76 #### unified queue/jam architecture + output device (PRs #949-960, Feb 19-25) 51 77 52 78 **jams — shared listening rooms (PR #949)**: real-time shared listening rooms. one user creates a jam, gets a shareable code (`plyr.fm/jam/a1b2c3d4`), and anyone with the link can join. all participants control playback. `Jam` and `JamParticipant` models with partial indexes. `JamService` singleton manages lifecycle, WebSocket connections, and Redis Streams fan-out. playback state is server-authoritative — JSONB with monotonic revision counter. server-timestamp + client interpolation for sync. reconnect replays missed events via `XRANGE`, falls back to full DB snapshot if trimmed. personal queue preserved and restored on leave. gated behind `jams` feature flag. see `docs/architecture/jams.md`. ··· 297 323 298 324 ### current focus 299 325 300 - jams hardening and architecture cleanup: unified queue/jam so frontend does all queue mutation locally with a single `update_queue` command to the backend. output device mode lets one speaker play audio while everyone else is a remote. ATProto scope parsing upgraded to spec-compliant SDK. production reliability stable (Redis caches, Dockerfile hardening). 326 + image performance and architecture cleanup: 96x96 WebP thumbnails for all artwork (track, album, playlist), storage protocol abstraction, backfill script for existing images. jams and PDS audio uploads shipped to all users (feature flags removed). homepage performance improved with Redis-cached follow graph and parallelized data fetching. 301 327 302 328 ### known issues 303 329 - iOS PWA audio may hang on first play after backgrounding ··· 306 332 ### backlog 307 333 - share to bluesky (#334) 308 334 - lyrics and annotations (#373) 309 - - configurable rules engine for moderation 335 + - configurable rules engine for moderation (Osprey rules engine PR #958 open) 310 336 - time-release gating (#642) 337 + - social activity feed (#971) 311 338 312 339 ## technical state 313 340 ··· 471 498 472 499 --- 473 500 474 - this is a living document. last updated 2026-02-25 (unified queue/jam, output device, scope parsing). 501 + this is a living document. last updated 2026-02-27 (image thumbnails, storage protocol, jam GA, network artists perf). 475 502