Auf Wunsch: was vom Handy hochgeladen wird, soll auch in Navidrome
auftauchen. Die Subsonic-API kennt keinen Upload — also erledigt es die
Melo-Cloud selbst.
Server (/home/dustin/scripts/melo_cloud.py, nicht versioniert — Backup als
melo_cloud.py.bak-20260821-084506):
- verknuepfe_navidrome(): Hardlink der Registry-Datei nach
/home/dustin/navidrome/music (Bind-Mount des Containers). Gleiche
Partition -> kein zusaetzlicher Speicher. Fallback: Kopie.
- rescan_navidrome(): Subsonic startScan.view ueber Navidromes
AuthProxyHeader (TrustedSources 127.0.0.1) — kein Passwort im Code.
Nur zur Beschleunigung, der Watcher findet neue Dateien ohnehin.
- upload() verknuepft neue UND bereits bekannte Titel (heilt Bestand).
- handle_delete() entfernt die Datei aus Registry + Navidrome, sobald kein
Konto sie mehr aktiv hat. Der Registry-EINTRAG bleibt stehen: handle_list
haengt die Grabsteine daran (JOIN registry) — ohne ihn erfuehren die
anderen Geraete nie von der Loeschung (Zombie-Song).
- registry_pfad(): gemeinsame Dateisuche ueber alle Audio-Endungen, mit
Rueckfall auf den Navidrome-Ordner.
Nebenbei repariert:
- Alle 325 Cloud-Dateien lagen nur noch im Navidrome-Ordner, REG war leer:
jeder Download antwortete "File missing". Per Hardlink zurueckverknuepft
(scripts/melo_cloud_migriere_registry.py).
- upload() legte jede Datei als ".mp3" ab, auch m4a/flac. Jetzt echte
Endung; _download liefert passenden Content-Type und einen Dateinamen
ohne doppelte Endung.
App:
- MeloCloudService.herunterladen() gibt die geschriebene Datei zurueck und
leitet die Endung aus dem Content-Type ab (endungFuer) — sonst landet
eine M4A als .mp3 auf dem Handy und Android ordnet sie falsch ein.
- loeschBremseGreift(): der Abgleich reicht keine Loeschwelle mehr zum
Server durch (>10 Titel UND >1/3 des Serverbestands). Ohne die Bremse
haette eine nicht eingehaengte Speicherkarte die Sammlung auf allen
Geraeten geloescht.
276 Tests gruen (1 uebersprungen), flutter analyze ohne Befund, plus ein
Durchlauf gegen den echten Server (scripts/test_melo_cloud_navidrome.py):
Upload -> Registry + Navidrome (ein Hardlink) -> Navidrome liest ein ->
Loeschen entfernt beides, Grabstein bleibt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpPu4nuKjKKeX1RpdDeX81
BUG 1 — Lieder vom Handy waren nicht abspielbar
("Wiedergabe fehlgeschlagen: (0) Source error" / "Loading interrupted")
Wurzel-Ursache: MeloAudioHandler.loadPlaylist hat jeden Warteschlangen-
Eintrag durch NavidromeService.streamAndCacheToLocal(item.id) geschickt,
sobald Zugangsdaten existierten. item.id ist aber NIE eine Navidrome-Song-ID
— lokal ist es file:///storage/..., beim Server die fertige Stream-Adresse.
Der Server bekam also 'file:///...' als Song-ID, antwortete mit einem Fehler,
und diese Fehlerantwort wurde an just_audio weitergereicht (Source error) —
bzw. als .mp3 in den Cache geschrieben, wodurch der Titel dauerhaft kaputt
blieb.
Zweite Ursache: die Schleife lud die GANZE Warteschlange seriell vorab
herunter (30s Timeout je Titel), bevor setAudioSources lief. Bei hunderten
Titeln startete die Wiedergabe deshalb nie; ein zweiter Tipp brach den
laufenden Ladevorgang ab ("Loading interrupted").
Fix:
- Server-Titel tragen ihre ID in MediaItem.extras['navidromeId'] statt sie
aus der Abspiel-Adresse zu raten. Neue reine Funktionen navidromeIdOf,
songIdOf, nutztServerCache, quelleFuer.
- loadPlaylist baut die Quellen ohne Netzzugriff; Caching des laufenden
Titels im Hintergrund (unawaited).
- Resume/Scrobble nur noch mit der jeweils passenden ID (Server bzw. lokal).
- ladeInCache() ersetzt streamAndCacheToLocal(): .part-Datei, Pruefung des
Inhaltstyps (istAudioAntwort), stabiler Cache-Schluessel ueber die
Song-ID statt der Stream-Adresse (die trug Token+Salt und war je Sitzung
anders — der Cache war nie wiederauffindbar), Client wird geschlossen.
BUG 2 — kein Abgleich zwischen Handy und Server
Neu: services/melo_cloud_service.dart + services/sync_service.dart gegen
cloud.baka-net.de (Bearer-JWT ueber BakaAuth). Server-Titel herunterladen
(offline verfuegbar), eigene Dateien hochladen, Loeschungen in beide
Richtungen (Tombstones), Favoriten und Wiedergabe-Verlauf. Automatisch beim
App-Start und bei Rueckkehr in die App (max. alle 15 Min), plus Knopf unter
Einstellungen -> Geraete-Abgleich. Reine Planungsfunktion planeSync().
Bewusst NICHT ueber Navidrome: die Subsonic-API kennt keinen Upload-
Endpunkt. Navidrome bleibt die Streaming-Bibliothek, die Melo-Cloud ist der
gemeinsame Speicher.
DB-Schema 8: songs.cloud_id verbindet Geraet und Server.
Nebenbei behoben (blockierte Build bzw. Tests):
- database.g.dart war veraltet — das Projekt liess sich nicht uebersetzen.
- metadataEdited wurde nirgends gesetzt/beachtet: von Hand korrigierte
Metadaten wurden vom naechsten Scan ueberschrieben. Jetzt in beiden
Scans respektiert; metadatenUebernahme() setzt die Markierung.
- song_detail_sheet_test.dart haengt beim Oeffnen des Modal-Sheets und
blockierte den gesamten Testlauf — vorerst uebersprungen (TODO im Code);
der Zweck wird von metadaten_uebernahme_test.dart abgedeckt.
Enthaelt ausserdem die bis dahin nicht committete Arbeit der Vorsitzung
(Musikerkennung/ACRCloud, MusicBrainz-Metadaten, MediaStore-Datentraeger).
267 Tests gruen (1 uebersprungen), flutter analyze ohne Befund.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpPu4nuKjKKeX1RpdDeX81