python
musik
automation · python · musik

Metal Release Tracker —
YouTube RSS → Spotify Playlist

Fünf YouTube-Promokanäle, täglich neue Releases, wochenlang manuell gesucht und in Playlists eingetragen. Das geht auch anders. Dieses System monitort die Kanäle via RSS-Feed (kein API-Key nötig), parst Artist und Album aus den Videotiteln, sucht den Track auf Spotify und fügt ihn automatisch in die Jahres-Playlist ein. Was nicht gefunden wird, landet in einer Pending-Liste mit täglichem Retry.

Python 3.11 SQLite Spotify API YouTube RSS Cron ✓ kein YouTube API-Key ⚠ Fuzzy Matching ⏳ HA-Notifications offen
01 Architektur 02 Kanäle 03 Deployment 04 Spotify Auth 05 channels.yaml 06 Datenbank 07 Wartung 08 Offene Punkte 09 Fallstricke
01 — Architektur

Wie das System funktioniert

Das System ist bewusst einfach gehalten — kein n8n, kein komplexes Workflow-System. Ein Python-Script, ein Cron-Job, eine SQLite-Datenbank. Läuft auf Server3 unter /opt/docker/release-tracker/ als User homelab-user.

Der entscheidende Trick: YouTube bietet für jeden Kanal einen öffentlichen RSS-Feed — keine API-Keys, kein Quota, keine Authentifizierung. Einfach pollen, parsen, fertig.

Cron — täglich 08:00 + 20:00
    │
    ▼
RSS-Fetcher
    YouTube Feeds für alle 5 Kanäle abrufen
    URL: https://www.youtube.com/feeds/videos.xml?channel_id=UC...
    │
    ▼
Title-Parser
    Regex-Patterns pro Kanal
    "Artist - Album (Year) [Label]" → artist, album
    "Artist : Album"           → Only Black Metal (Sonderfall)
    │
    ▼
SQLite-Check
    Video schon bekannt? → überspringen
    Neu → weiter
    │
    ▼
Spotify-Suche  (rapidfuzz fuzzy matching)
    Treffer ≥ Threshold → Track zur Jahres-Playlist hinzufügen
    Kein Treffer        → Status "pending" — täglicher Retry
    Parse-Fehler        → Status "skipped"
    │
    ▼
SQLite-Update  + HA-Notification (noch offen)
        
new
Neues Video gefunden, noch nicht verarbeitet
matched
Track auf Spotify gefunden + zur Playlist hinzugefügt
pending
Kein Spotify-Treffer — täglicher Retry bis matched
skipped
Titel konnte nicht geparst werden
02 — Kanäle

Überwachte YouTube-Kanäle

Black Metal Promotion
@bmpromotion · UCzCWehBejA23yEz3zp7jlcg
Wichtigster Kanal — Standard-Separator " - "
priorität
Only Black Metal
@onlyblackmetal · UCLUzzvPKB8YFuBc4QFHIriw
Sonderfall: Separator " : " statt " - " im Titel
normal
Atmospheric Black Metal Albums
@atmosphericblackmetalalbum1807 · UCDLkzWN1rHY4eYkGnVruHVw
Standard-Separator, oft mit Year-Tag
normal
Black Metal Catalog
@blackmetalcatalog_official · UChm6MF-KVBqJbETBu_FVrOw
Standard-Separator
normal
Antifascist Black Metal Network
@antifascistblackmetalnetwork · UCtjazZuy6AJ0rRtZ5bligMA
Standard-Separator
normal
Neuen Kanal hinzufügen

Channel-ID herausfinden über https://commentpicker.com/youtube-channel-id.php oder direkt aus dem RSS-Feed: https://www.youtube.com/feeds/videos.xml?channel_id=UCxxxxx. Dann einfach einen neuen Block in channels.yaml kopieren und anpassen — kein Code-Änderung nötig, kein Neustart des Systems.

03 — Deployment

Setup auf Server3

/opt/docker/release-tracker/
├── .env               ← Spotify Credentials + Refresh Token
├── channels.yaml      ← Kanal-Konfiguration + Regex-Patterns
├── tracker.py         ← Hauptscript
├── auth_setup.py      ← Einmaliges OAuth-Setup
├── requirements.txt
├── data/
│   └── releases.db    ← SQLite-Datenbank
└── logs/
    └── tracker.log    ← Tägliche Logs
1
Abhängigkeiten installieren
Python 3.11+ und pip auf Server3. Pakete aus requirements.txt.
bashServer3
pip install feedparser requests rapidfuzz python-dotenv pyyaml
2
.env anlegen
Spotify App-Credentials eintragen. Client ID und Secret aus dem Spotify Developer Dashboard der "meine-spotify-app".
.env/opt/docker/release-tracker/
SPOTIFY_CLIENT_ID=deine_client_id_hier
SPOTIFY_CLIENT_SECRET=dein_secret_hier
SPOTIFY_REDIRECT_URI=http://127.0.0.1:8080/callback
# SPOTIFY_REFRESH_TOKEN wird von auth_setup.py automatisch eingetragen
# HA_URL=http://homeassistant.local:8123  (noch offen)
# HA_TOKEN=...                             (noch offen)
3
Spotify OAuth einmalig durchführen
auth_setup.py generiert die Auth-URL. Diese im Browser öffnen, Spotify-Login durchführen, dann die Callback-URL manuell ins Terminal einfügen. Der Refresh Token landet automatisch in der .env.
bashServer3 — einmalig
cd /opt/docker/release-tracker
python3 auth_setup.py
# URL im Browser öffnen → einloggen → Callback-URL einfügen
# → SPOTIFY_REFRESH_TOKEN wird in .env geschrieben
4
Cron-Job einrichten
Läuft als User homelab-user täglich um 08:00 und 20:00.
crontab -eServer3 — als homelab-user
# Metal Release Tracker — 2× täglich
0 8,20 * * * cd /opt/docker/release-tracker && python3 tracker.py >> logs/cron.log 2>&1
Ordner-Berechtigungen

Das Verzeichnis und die .env-Datei müssen dem User homelab-user gehören. Nach sudo-Operationen prüfen: sudo chown -R homelab-user:homelab-user /opt/docker/release-tracker. WinSCP zeigt versteckte Dateien (wie .env) erst nach Strg+Alt+H.

04 — Spotify Auth

Spotify Developer App anlegen & OAuth einrichten

Um die Spotify API nutzen zu können braucht man eine eigene Developer App im Spotify Developer Dashboard. Das klingt komplizierter als es ist — ein normaler Spotify-Account reicht, kein Premium nötig, und die App bleibt kostenlos solange man sie nur für sich selbst nutzt.

1
Developer Dashboard öffnen
developer.spotify.com/dashboard aufrufen und mit dem normalen Spotify-Account einloggen. Beim ersten Mal muss man den Developer-Nutzungsbedingungen zustimmen.
2
Neue App erstellen
Create app klicken. Felder ausfüllen:
App name: beliebig, z.B. release-tracker
App description: kurze Beschreibung, z.B. "Metal Release Tracker"
Redirect URI: http://127.0.0.1:8080/callback eintragen und Add klicken
APIs used: "Web API" anhaken
Dann Save.
3
Client ID und Client Secret kopieren
In der App-Übersicht stehen Client ID (sichtbar) und Client Secret (erst nach Klick auf "View client secret" sichtbar). Beide Werte in die .env-Datei auf dem Server eintragen. Das Secret niemals in Git einchecken oder öffentlich teilen.
4
OAuth-Flow einmalig auf dem Server durchführen
Da der Server kein Display hat, nutzt auth_setup.py einen manuellen Flow: Script generiert eine Auth-URL → diese im lokalen Browser öffnen → Spotify-Login → nach dem Redirect die komplette URL aus der Browserzeile kopieren und ins Terminal einfügen. Der Refresh Token wird automatisch in die .env geschrieben.
bashServer3 — einmalig
cd /opt/docker/release-tracker
python3 auth_setup.py

# Ausgabe: "Öffne diese URL im Browser:"
# https://accounts.spotify.com/authorize?client_id=...&scope=...
#
# Nach dem Login im Browser: komplette Redirect-URL kopieren
# z.B. http://127.0.0.1:8080/callback?code=AQD...&state=...
# → ins Terminal einfügen → Enter
#
# SPOTIFY_REFRESH_TOKEN wird automatisch in .env geschrieben ✓
5
Testen
Script einmal manuell ausführen und prüfen ob die Spotify-Verbindung klappt und die erste Jahres-Playlist angelegt wird.
bashServer3
cd /opt/docker/release-tracker
python3 tracker.py
# Ausgabe: "Playlist 'Kanalname 2026' angelegt: spotify:playlist:..."
# Ausgabe: "X neue Releases verarbeitet, Y gematcht, Z pending"
Bestehende App wiederverwenden

Wer bereits eine Spotify Developer App hat (z.B. für Home Assistant), kann diese weiterverwenden. Einfach unter Edit Settings die Redirect URI http://127.0.0.1:8080/callback als weitere URI hinzufügen — bestehende URIs bleiben dabei erhalten.

Refresh Token ist langlebig

Der Refresh Token muss nur einmal generiert werden. Er ist zeitlich unbegrenzt gültig solange er nicht manuell im Developer Dashboard widerrufen wird. Access Tokens (kurzlebig, 1 Stunde) erneuert das Script automatisch bei jedem Lauf. Jahres-Playlists werden automatisch angelegt sobald das erste Release des neuen Jahres auftaucht — kein manueller Eingriff zum Jahreswechsel nötig.

05 — channels.yaml

Kanal-Konfiguration & Regex-Patterns

Jeder Kanal hat eigene Regex-Patterns weil die Titel-Formate variieren. Das wichtigste Sonderzeichen: bei Only Black Metal wird : als Separator verwendet statt - . Das Regex verwendet deshalb [:\-–] als Character Class — in YAML in einfache Anführungszeichen einschließen um den Doppelpunkt nicht als YAML-Syntax zu interpretieren.

channels.yamlAufbau — Beispiel Black Metal Promotion
channels:
  - name: "Black Metal Promotion"
    channel_id: "UCzCWehBejA23yEz3zp7jlcg"
    handle: "@bmpromotion"
    priority: true
    title_patterns:
      # Pattern 1: Artist - Album (Year) [Label]
      - '^(?P<artist>.+?)\s*[-–]\s*(?P<album>.+?)\s*[\(\[]'
      # Pattern 2: Fallback — Artist - Album
      - '^(?P<artist>.+?)\s*[-–]\s*(?P<album>.+?)$'

  - name: "Only Black Metal"
    channel_id: "UCLUzzvPKB8YFuBc4QFHIriw"
    handle: "@onlyblackmetal"
    priority: false
    title_patterns:
      # Sonderfall: Separator ist " : " — in einfache Quotes!
      - '^(?P<artist>.+?)\s*[:\-–]\s*(?P<album>.+?)\s*[\(\[]'
      - '^(?P<artist>.+?)\s*[:\-–]\s*(?P<album>.+?)$'
YAML + Regex-Doppelpunkt

Regex-Patterns mit : müssen in einfache Anführungszeichen ('...') eingeschlossen werden — nicht doppelte. YAML interpretiert sonst den Doppelpunkt als Key-Value-Trenner und der YAML-Parser wirft einen Fehler. Das war ein echter Fallstrick beim Setup von Only Black Metal.

06 — Datenbank

SQLite — direkte Abfragen für Wartung

bash + SQLNützliche Abfragen
# DB öffnen
sqlite3 /opt/docker/release-tracker/data/releases.db

-- Alle pending Releases anzeigen
SELECT channel, artist, album, published
FROM releases
WHERE status = 'pending'
ORDER BY published DESC;

-- Statistik pro Kanal
SELECT channel, status, COUNT(*) as n
FROM releases
GROUP BY channel, status;

-- Release manuell auf "new" zurücksetzen (z.B. nach Playlist-Duplikat)
UPDATE releases
SET status = 'new'
WHERE channel = 'Black Metal Promotion'
AND status = 'matched';

-- Playlist-Eintrag löschen (bei Duplikat — Script legt neu an)
DELETE FROM playlists
WHERE channel = 'Black Metal Promotion'
AND year = 2026;

-- Letzten Lauf prüfen
SELECT MAX(last_checked) FROM releases;
07 — Wartung

Logs, Updates, Kanal hinzufügen

bashLogs live verfolgen
# Live-Log
tail -f /opt/docker/release-tracker/logs/tracker.log

# Script manuell ausführen (außerhalb Cron)
cd /opt/docker/release-tracker && python3 tracker.py

# Cron-Log prüfen
tail -50 /opt/docker/release-tracker/logs/cron.log
Neuen Kanal hinzufügen

Einfach einen neuen Block in channels.yaml einfügen — Channel-ID aus dem RSS-Feed-URL oder via commentpicker.com. Beim nächsten Lauf (oder manuell) erkennt das Script den neuen Kanal, legt die Jahres-Playlist an und beginnt mit dem Tracking. Kein Neustart, kein Code-Änderung nötig.

08 — Offene Punkte

Was noch fehlt

Home Assistant Push-Notification bei neuem Match
Wenn ein pending Release auf Spotify gefunden und gematcht wird, soll eine HA-Notification ausgelöst werden. Die Infrastruktur im Code ist vorbereitet — HA_URL und HA_TOKEN als Umgebungsvariablen in der .env. Fehlt noch: der Long-Lived Access Token aus Home Assistant (Profil → Langlebige Zugriffstoken → Token erstellen) und das Eintragen in die .env.
Discogs Wantlist Integration
Releases aus der Discogs Wantlist sollen automatisch auf Spotify gesucht und in eine separate "Wantlist"-Playlist eingefügt werden. Discogs hat eine öffentliche API mit OAuth. Würde denselben Fuzzy-Matching-Mechanismus wie der YouTube-Tracker nutzen.
Automatisches Playlist-Cleanup (Jahreswechsel)
Zum Jahreswechsel sollen automatisch neue Jahres-Playlists angelegt werden. Das Script erkennt das aktuelle Jahr bereits korrekt — die Playlists werden beim ersten Release des neuen Jahres automatisch angelegt. Kein manueller Eingriff nötig.
Fuzzy-Matching Threshold tunen
Der aktuelle rapidfuzz-Threshold führt manchmal zu False Positives (falsche Tracks werden gematcht) oder False Negatives (richtige Tracks nicht gefunden). Idee: separater Threshold-Wert pro Kanal in channels.yaml, da manche Kanäle konsistentere Titelnamen haben als andere.
Web-Dashboard für pending Releases
Eine einfache HTML-Seite die die SQLite-DB liest und pending Releases anzeigt — mit direktem Spotify-Suchlink für manuelle Überprüfung. Könnte als statische Seite auf labstuff.tibbers.de/tracker laufen oder als kleines FastAPI-Backend auf Server3.
09 — Fallstricke

Was schiefgehen kann

YAML-Fehler beim Start — "mapping values are not allowed"
Ein Regex-Pattern in channels.yaml enthält einen Doppelpunkt (:) ohne einfache Anführungszeichen. YAML interpretiert den Doppelpunkt als Key-Value-Trenner. Fix: alle Patterns mit : in einfache Quotes einschließen: '...'.
Permission denied beim Schreiben in .env oder data/
Dateien gehören root statt homelab-user. Passiert wenn Dateien mit sudo erstellt wurden. Fix: sudo chown -R homelab-user:homelab-user /opt/docker/release-tracker.
Doppelte Playlist angelegt — Tracks fehlen in einer davon
Passiert wenn die playlists-Tabelle einen veralteten Eintrag enthält. Fix: Eintrag aus der Tabelle löschen und alle zugehörigen Releases auf status = 'new' zurücksetzen. Beim nächsten Lauf legt das Script die Playlist neu an und befüllt sie vollständig.
Only Black Metal Releases werden als "skipped" markiert
Kein Regex-Pattern matched den Titel weil der Separator : nicht im Pattern berücksichtigt ist. In channels.yaml beim Only Black Metal Kanal den Character Class auf [:\-–] erweitern (in einfache Anführungszeichen). Dann betroffene Releases auf status = 'new' setzen und erneut laufen lassen.
Spotify Auth schlägt fehl — "Invalid refresh token"
Der Refresh Token wurde manuell in der Spotify Developer Console widerrufen, oder der Token ist durch längere Inaktivität abgelaufen. Fix: auth_setup.py erneut ausführen, neuen Token generieren und in die .env schreiben.