feat(scrobble): Android stats screen + cover art in stats (web + android)

Phase 4 (part B): Android statistics screen consuming GET /scrobble/stats —
tiles (plays + listening time), day/month/year play series, top artists/tracks/
albums, 24h listening clock, period tabs; opened from a Settings button
("Meine Statistiken"). New StatsViewModel + StatsScreen + ScrobbleModels;
PlayLogClient.fetchStats() (tolerant JSON decode, null on any failure);
repo.fetchScrobbleStats(); nav route + SettingsScreen(onOpenStats).

Cover art everywhere: top artists show a circular artist image, tracks/albums
a square cover — resolved client-side from Navidrome via artistId/albumId/songId
(no coverArt knowledge in the scrobble service). Endpoint now also returns
albumId for top tracks so track covers resolve reliably. Web stats list gets the
same thumbnails. SW cache -> ns-v67.

Android compileDebugKotlin + web build green. Needs on-device runtime test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 4.8 committed 2026-08-07 18:41:27 +02:00
1 parent cc1ec1653f
commit fff6f9e4da
11 files changed
+469 -11

No files matched your search

+2 -1
View File
@@ -202,7 +202,8 @@ app.get('/scrobble/stats', async (req, res) => {
`).all(winL);
const topTracks = db.prepare(`
SELECT MAX(title) AS title, MAX(artist) AS artist, MAX(album) AS album, song_id AS songId, COUNT(*) AS plays
SELECT MAX(title) AS title, MAX(artist) AS artist, MAX(album) AS album,
MAX(album_id) AS albumId, MAX(artist_id) AS artistId, song_id AS songId, COUNT(*) AS plays
FROM plays WHERE ${WHERE}
GROUP BY song_id ORDER BY plays DESC LIMIT :limit
`).all(winL);