feat(scrobble stats): genre ranking (Top-Genres) — web + android

Play-log now stores each play's genre (from the song tags; web scrobbleLog +
android PlayLogClient capture song.genre). The stats endpoint returns topGenres
(GROUP BY lower(genre), case-insensitive, blanks ignored); web + android render a
Top-Genres section. Schema migrates via ALTER TABLE. Old plays have no genre
(fills going forward, like cover art). SW cache -> ns-v72.

Android compileDebugKotlin + web build green; genre grouping + null-ignore unit-tested.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 4.8 committed 2026-08-11 01:35:00 +02:00
1 parent 249b693936
commit 0c7132a7f2
7 files changed
+56 -9

No files matched your search

+1
View File
@@ -41,6 +41,7 @@ export function logPlay(song, source = 'web') {
album: song.album ?? null,
albumId: song.albumId ?? null,
coverArt: song.coverArt ?? null, // Subsonic coverArt token — what getCoverArt actually needs
genre: song.genre ?? null,
duration: Number.isFinite(Number(song.duration)) ? Math.floor(Number(song.duration)) : null,
playedAt: Math.floor(Date.now() / 1000), // Unix seconds, UTC
source,