Search hits omit album covers and artist images. The worker now reads the full song and artist, stores the description, and leaves Genius image URLs unchanged on mobile.
Co-authored-by: Cursor <cursoragent@cursor.com>
Reviewing the upload queue is now a per-user flag (reviews_uploads), not part
of the admin role: several people can hold it, a moderator can have it, and an
admin may not. Only admins grant or revoke it; existing admins keep it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quality upgrades, batch covers, staging preview, and resume after reload are in the queue. Loudness and crossfade work on web and Android, and the documented checks now have suites.
Co-authored-by: Cursor <cursoragent@cursor.com>
The wave logs each served candidate's feature vector to rec_impression_features.
Nightly training joins them with impression outcomes, validates on the last week
and activates the model only when it beats the v1 order and the active model.
The service loads the active ranker and uses its order in place of the v1 score.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- GET /onboarding offers the library's top artists (with album covers)
and genres and says whether to show it (not done and < 10 plays);
PUT /me/onboarding stores the choice or a skip (migration 00009).
- The recommender seeds the taste from it while the user has fewer than
30 scored tracks: chosen artists' tracks +1, chosen genres +0.5.
- Web: onboarding replaces the wave on the home page until done or
skipped; artist grid with covers, genre chips, at least 3 artists.
- e2e journey and the recommender DB test cover it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Migration 00008: upload_batches, upload_files, upload_quota.
- Quota reserved under a row lock when a batch is created; every status
change goes through one transition that frees space in the same
transaction. SHA-256 duplicates are rejected before any upload.
- tus 1.0.0 core (HEAD/PATCH) for the author only; offset is the real
staging file size, data beyond the declared size is refused (ADR-015).
- Processing worker: ffprobe checks, SHA-256 verification, exact and
Chromaprint duplicates against library and queue, metadata from tags
or the dropped folder path, possible_duplicate on metadata match, and
the target path from write_template reusing existing folder spelling.
- Import worker: edited tags written with ffmpeg stream copy, file
written by storage-agent, point scan of the new file, imported status;
idempotent and resumed after restart. Stale uploads expire after 24 h,
orphan staging files are removed at startup.
- API: quota, precheck, batches, my uploads, withdraw, admin list,
metadata edit, bulk review, retry and queue settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A background Fingerprinter runs after every scan and at startup, one file
at a time, filling media_files.sha256 and track_fingerprints (fpcalc raw,
first 120 s). Without fpcalc only SHA-256 is computed and files stay
queued for Chromaprint. Changing a file's content resets its prints.
Similarity() compares raw prints with a ±80-point alignment search; it is
the basis for duplicate detection in the upload queue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- migration 00006: track features and embeddings (pgvector, HNSW),
analysis queue filled by a trigger on new tracks, wave sessions and
impressions, recommender profiles, model versions
- analyzer basic-1 (ADR-014): ffmpeg decode, three 30 s excerpts,
256-d timbre/rhythm embedding, tempo by onset autocorrelation, key,
loudness, energy, danceability, acousticness, intro length; worker
with SKIP LOCKED claims, retries and re-analysis on version change
- wave engine v1: candidates (session/taste similarity, fresh,
liked artists, rediscover, popular, exploration, seed), filters
(dislikes, played in 3 h, already served), heuristic score, activity
and mood fit from contexts.yaml with relaxing thresholds, MMR with
one track per artist, exploration share, session vector updated by
reactions and a change of course after three skips
- POST /internal/wave/batch with the catalog index cached in memory
and the session kept in Redis
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- tracks carry a stream_url signed for the user for 12 h and the
user's reaction; /stream checks the signature, expiry and that the
user is not blocked, then hands the file to nginx by X-Accel-Redirect
(or serves it with Range support when X_ACCEL=false)
- original by default; formats browsers can't play and explicit
high/medium/low go through Opus transcoding, never upwards: a lossy
source under the target bitrate stays original; transcodes are
cached per file and size, deduplicated in flight, limited by
TRANSCODE_CONCURRENCY and trimmed hourly by LRU to the size limit
- /events takes up to 500 events with client UUIDs, drops repeats and
events older than 30 days, counts plays (≥ half or 4 min, or
finished), early skips and history entries (≥ 30 s)
- history with clearing, likes and dislikes with cursors and totals,
track reactions, playback state with last-write-wins, tracks batch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- covers are taken from the embedded picture of the first track or a
cover/folder/front image in the album folder, converted by ffmpeg to
WebP 64/300/1200 and named by content hash, so albums sharing a cover
share the files; a full scan looks again for albums without one
- a 3-colour palette (dominant, second, most saturated accent) from a
deterministic k-means feeds placeholders, page tints and the wave
- /covers/{id} is public but HMAC-signed and immutable; nginx serves
the file via X-Accel-Redirect, X_ACCEL=false serves it from api
- the web client picks the size by display size and pixel density
- install.sh and update.sh generate STREAM_SIGNING_KEY
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- schema for sources, path patterns, media files, artists, albums,
tracks, genres (migration 00003)
- path patterns (RE2 with named groups, skip), merge with tags by
source priority, fallbacks from the file name and folder, name
normalization and multi-artist split
- scanner: walk, skip unchanged files by size and mtime, ffprobe in 8
workers with a single catalog writer, renames by content hash keep
the track, duplicates fold into one track with the best file as
primary, missing files are kept 30 days
- storage-agent mounts NFS with validated server, export and an option
whitelist, no shell; test mount counts audio files
- admin API: sources CRUD, connection test, patterns with preview,
scan, library status, file errors, host folder picker; scheduled
scans and health checks; mounts are restored on start
- catalog API: artist, artist albums, album with tracks, track
- api image is alpine with ffmpeg for ffprobe
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- users, refresh_tokens and audit_log schema (migration 00002)
- argon2id passwords with rehash on login, password policy, lockout
with exponential back-off and a per-IP rate limit in Redis
- Ed25519 JWT access tokens with kid-based key rotation; rotating
refresh tokens with reuse detection (cookie for web, body for Android)
- bootstrap admin on first start; must_change_credentials and
must_change_password gate every other endpoint
- /auth/login, /auth/refresh, /auth/logout(-all), /me, /me/password,
/me/bootstrap-credentials, /me/sessions; server-info reports
bootstrap_required
- role policy from docs/auth-and-roles.md with table tests
- problem+json errors with field errors; ru/en translations of the codes
- install.sh and update.sh generate JWT_PRIVATE_KEY
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>