music
dashboard
self-hosted · last.fm · spotify · discogs

Music Dashboard
Last.fm-, Spotify- & Discogs-Stats selbst gehostet

Eine einzige statische HTML-Datei, kein Build-Prozess, kein Framework — trotzdem ein vollwertiges Multi-User Music-Stats-Dashboard mit Wrapped-Jahresrückblick, Aktivitäts-Heatmaps und Discogs-Sammlungsverwaltung. Ausgeliefert über Nginx auf einem Thin Client, erreichbar über Reverse Proxy und Pi-hole DNS.

Nginx Alpine Docker Last.fm API Spotify API Discogs API ✓ Kein Build-Prozess
01 Übersicht 02 Architektur 03 Deployment 04 Proxy & DNS 05 API Keys 06 Profil-System 07 Features 08 Fallstricke
01 — Übersicht

Eine HTML-Datei als komplettes Dashboard

Das Music Dashboard ist eine selbst gehostete Single-Page-App — eine einzelne HTML-Datei, kein Build-Prozess nötig. Sie bringt Multi-User-Profile, Last.fm-Statistiken, optionale Spotify-Cover-Bilder und eine vollständige Discogs-Sammlungsverwaltung mit.

Multi-User Profil-System
Bewohner 1 + Bewohner 2, Credentials getrennt in localStorage gespeichert.
Last.fm Integration
Top Künstler, Tracks, Alben, Zuletzt gehört, Geliebt.
Spotify Integration
Optional — Cover-Bilder über Client Credentials Flow.
Künstler-Detailseite
Hero-Bild, persönliche Stats, Biografie, ähnliche Künstler, Discogs-Schnellsuche.
Aktivität & Genres
GitHub-style Heatmap (timezone-korrekt), Uhrzeit-Heatmap, Hörstreaks, Genre-Statistiken.
Wrapped-Tab
Jahresrückblick mit Jahresauswahl, Podium, Monat-für-Monat, lokalem Archiv.
Discogs-Tab
Wantlist, Sammlung, lokale Kaufliste, Datenbanksuche.
Light/Dark Theme
Persistiert über localStorage, wie der Rest der Seite hier.
02 — Architektur

Vom Browser bis zur statischen Datei

Vier Stationen bis zur ausgelieferten Seite: Pi-hole löst die lokale Domain auf, die FortiGate leitet per VIP weiter, DaServer1 terminiert TLS mit einem mkcert-Zertifikat und reicht per Reverse Proxy an DaServer3 weiter, wo Nginx Alpine die HTML-Datei direkt aus einem Docker-Volume ausliefert.

Traffic-Fluss Browser → musicstats.local
Browser (musicstats.local)
    │  HTTPS 443
    ▼
Pi-hole DNS
    musicstats.local → 192.168.68.81
    │
    ▼
FortiGate WAN (192.168.68.81)
    VIP-HTTPS → 192.168.1.71:443
    │
    ▼
DaServer1 (192.168.1.71)
    Nginx – SSL-Terminierung (mkcert)
    proxy_pass → http://192.168.1.83:8181
    │  HTTP 8181
    ▼
DaServer3 (192.168.1.83)
    Docker: nginx:alpine
    Datei: /opt/musicstats/index.html

Statische Seite ausgeliefert  ✓
KomponenteBeschreibung
DaServer3192.168.1.83 — Nginx Alpine Container, liefert die statische Datei
DaServer1192.168.1.71 — Reverse Proxy, SSL-Terminierung mit mkcert
FortiGateVIP-HTTPS auf 192.168.68.81 → DaServer1
Pi-holeDNS-Eintrag musicstats.local → FortiGate WAN
03 — Deployment

Setup auf DaServer3

1
Verzeichnis anlegen
Auf DaServer3: /opt/musicstats als Basis für Compose-Datei und HTML.
2
HTML-Datei deployen
Als index.html ins Verzeichnis kopieren. Kein Docker-Neustart nötig — Nginx serviert direkt aus dem gemounteten Volume.
3
Docker Compose starten
Nginx Alpine Container mit Volume-Mount auf das Verzeichnis, Port 8181 nur auf der internen IP gebunden.
bash Verzeichnis anlegen
sudo mkdir -p /opt/musicstats
sudo chown -R $USER:$USER /opt/musicstats
bash Update einspielen (von DaServer3 aus)
cp /opt/musicstats/music-dashboard.html /opt/musicstats/index.html
sudo chmod 644 /opt/musicstats/index.html

# Kein Docker-Neustart nötig — Nginx serviert direkt aus dem Volume
# Im Browser: Strg+Shift+R für Hard Reload
yaml /opt/musicstats/docker-compose.yml
services:
  musicstats:
    image: nginx:alpine
    container_name: musicstats
    restart: unless-stopped
    ports:
      - "192.168.1.83:8181:80"
    volumes:
      - ./:/usr/share/nginx/html:ro
bash Container starten
cd /opt/musicstats
docker compose up -d
docker-compose.yml per Heredoc anlegen, nicht mit nano

cat > docker-compose.yml << 'EOF' verwenden, um Einrückungsfehler zu vermeiden. Bei Permission Denied: sudo chown -R $USER:$USER /opt/musicstats.

04 — Externer Zugriff

Nginx Reverse Proxy & Pi-hole DNS

DaServer1 terminiert TLS mit dem bestehenden mkcert-Zertifikat und reicht den Traffic per proxy_pass an DaServer3 weiter.

nginx /opt/docker/nginx/conf.d/musicstats.local.conf
server {
    listen 443 ssl;
    server_name musicstats.local;

    ssl_certificate     /etc/nginx/certs/homelab.pem;
    ssl_certificate_key /etc/nginx/certs/homelab-key.pem;

    location / {
        proxy_pass         http://192.168.1.83:8181;
        proxy_http_version 1.1;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}
bash Config testen & reloaden
docker exec nginx nginx -t && docker exec nginx nginx -s reload
DomainIP
musicstats.local192.168.68.81

Eintrag in Pi-hole unter Local DNS Records anlegen, sonst löst der Client die Domain nicht auf.

05 — Integrationen

API Keys & Credentials

DienstFundort / Ablauf
Last.fm API Keylast.fm/api/account/create — Key kommt sofort nach App-Anlage
Spotify (optional)Bestehende MyHa Home-Assistant-App verwenden, Client ID + Secret aus developer.spotify.com/dashboard
Discogsdiscogs.com/settings/developers → "Generate Token" — nur ein Token nötig, kein OAuth
Spotify nutzt Client Credentials Flow

Kein User-Login, kein Konflikt mit der bestehenden HA-Integration. Die Redirect URI muss trotzdem http://localhost enthalten, wird aber nicht aktiv genutzt. Dient hier nur den Cover-Bildern.

Der Discogs-Token wird für Wantlist, Sammlung, Suche und Wantlist-Änderungen genutzt und direkt im Dashboard eingetragen.

06 — Multi-User

Profil-System

Beim ersten Aufruf erscheint eine Profilauswahl:

  • Bewohner 1 🎸 — eigenes Last.fm-Konto + Spotify + Discogs
  • Bewohner 2 🎵 — eigenes Last.fm-Konto + Spotify (Discogs optional)
Credentials pro Profil getrennt

Jedes Profil speichert seine Zugangsdaten separat in localStorage. Auf einem neuen Gerät müssen sie einmalig neu eingegeben werden. Profil wechseln über die Pill oben links.

07 — Tabs

Dashboard-Features im Detail

Übersicht-Tab: Scrobble-Zähler, Künstler-Anzahl, Top Künstler + Track, Now Playing mit pulsierendem Dot bei Live-Scrobble. Top Künstler / Tracks / Alben mit Zeitraum-Filter (7 Tage bis Gesamt). Zuletzt gehört + Geliebt mit Cover-Bildern via Spotify. Klick auf Künstler öffnet die Detailseite.

Künstler-Detailseite: Hero-Banner mit Blur-Hintergrund (Spotify-Bild), Genre-Tags, persönliche Stats, Ranking, globale Reichweite (monatliche Hörer, Follower), Biografie auf Deutsch via Last.fm, Top Tracks und klickbare ähnliche Künstler. Der Discogs-Button klappt die Releaseliste des Künstlers direkt zur Wantlist/Kaufliste auf.

Aktivität & Genres-Tab: Hörstreaks (aktuell, längster, aktive Tage gesamt), eine GitHub-style 52-Wochen-Heatmap Mo–So mit korrekter Zeitzone (lokale Zeit, kein UTC-Versatz), eine 24h-Uhrzeit-Heatmap mit Peak-Stunde sowie Top-12-Genre-Statistiken, via Spotify nach Plays gewichtet.

Wrapped-Tab: Jahresauswahl für alle Jahre seit der Last.fm-Registrierung. Datenquelle sind die Last.fm Weekly Charts — jahresgenau, ohne das 200-Track-Limit anderer Last.fm-Endpunkte. Statistik-Karten (Scrobbles, Lieblingskünstler, aktivster Monat), ein Podium mit 🥇🥈🥉 und Spotify-Bildern, Monat-für-Monat-Übersicht mit rot markiertem Peak, Top 10 Tracks und Top 10 Künstler. Jeder geladene Wrapped wird automatisch im localStorage-Archiv gespeichert.

Discogs-Tab: Wantlist mit Cover, Metadaten und Marketplace-Preisen auf Abruf, eine Sammlungsansicht mit 50 Einträgen pro Seite und Filter nach Vinyl/CD/Kassette, eine lokale Kaufliste mit Priorität und Notizen pro Album sowie die Discogs-Datenbanksuche mit direkter Übernahme in Wantlist oder Kaufliste.

08 — Fallstricke

Was schiefgehen kann

403 im Browser
Fehlende Berechtigungen. Fix: chmod 644 index.html && chmod 755 /opt/musicstats/.
Seite nicht erreichbar
Pi-hole DNS-Eintrag fehlt. musicstats.local → 192.168.68.81 setzen.
Bilder fehlen
Last.fm hat die Bilder-API abgeschaltet. Fix: Spotify Client ID + Secret im Dashboard eintragen.
Spotify Unauthorized
Token abgelaufen. Client ID/Secret prüfen, Seite neu laden.
Heatmap zeigt falsche Monate
Browser-Cache. Fix: Strg+Shift+R Hard Reload.
Wrapped zeigt falsche Zahlen
Alter Cache oder period:12month war rolling — die aktuelle Version nutzt Weekly Charts. Stats-Tab neu laden.
Profil-Credentials weg
localStorage wurde geleert (z.B. privater Modus). Credentials erneut eingeben.
Docker startet nicht
YAML-Einrückungsfehler. cat > docker-compose.yml statt nano verwenden.
Discogs 401
Token ungültig. Neuen Token unter discogs.com/settings/developers generieren.
Discogs Preise leer
Release hat keine Marketplace-Listings — normal, nicht alle Releases haben Preisdaten.
KomponenteWert
Verzeichnis/opt/musicstats/ (docker-compose.yml, index.html)
musicstats nginxPort 8181 intern — http://192.168.1.83:8181
musicstats via ProxyPort 443 — https://musicstats.local