feat(scrobble): Head-to-Head "Musik-Match" — compare two users' listening

Adds GET /scrobble/users (distinct users for the partner picker) and
GET /scrobble/compare?with= to the scrobble service. Taste match = cosine
of the two users' artist-play vectors (0..100); also returns shared artists
(mirrored bars), shared tracks, and each side's unique top artists. Reuses
the existing per-user play-log — no new data capture.

Web UI src/ui/compare.js (overlay from Settings "🎧 Musik-Match"): partner
picker fed by /scrobble/users, match-score ring, per-user totals, shared +
unique lists with clickable artist/album rows. Cosine + shared logic verified
against a hand computation on an in-memory DB. Android pending (parity: 🔜).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 4.8 committed 2026-08-13 00:46:29 +02:00
1 parent ba2306656e
commit cbedbba6fc
6 files changed
+317 -1

No files matched your search

+2
View File
@@ -4,6 +4,7 @@ import { api, fetchRomajiIndex, loadRomajiOverrides, loadArtistOverrides } from
import { injectPWA } from './pwa.js';
import { initPlayLog } from './player/scrobbleLog.js';
import { openStats, closeStats } from './ui/stats.js';
import { openCompare, closeCompare } from './ui/compare.js';
import { closeArtistPin, clearArtistPin } from './ui/artistPin.js';
import { loadSavedTheme } from './themes.js';
import { initScrollRows } from './ui/components.js';
@@ -336,6 +337,7 @@ Object.assign(window, {
openSettings, closeSettings, saveSettings, confirmDisconnect, checkBackend,
openSidebar, closeSidebar, switchNavMode, showCtxMenu,
openStats, closeStats,
openCompare, closeCompare,
closeArtistPin, clearArtistPin,
// Views
loadAlbums, loadStarred, loadRandom, filterStarred, filterPlaylist,