Ergebnis einer Durchsicht der App gegen den Massstab "Apple Music".
Alle fuenf Punkte waren keine fehlenden Funktionen, sondern Versprechen,
die die Oberflaeche gab und der Code nicht hielt.
- POST_NOTIFICATIONS fehlte im Manifest. Auf Android 13+ blieb die
Wiedergabe-Benachrichtigung dadurch unsichtbar. Das Recht wird erst
erfragt, wenn wirklich ein Titel laeuft.
- Der Offline-Schalter wurde nirgends gelesen. Er greift jetzt an allen
drei Wegen in die Warteschlange; uebersprungene Titel werden benannt.
- CoverImage kannte nur file://, und der Server-Browser gab die
Cover-Adresse gar nicht erst mit. Server-Titel waren deshalb immer ein
graues Notensymbol.
- Warteschlange entfernen/umsortieren baute die ganze Queue neu auf:
hoerbarer Aussetzer und doppelte Wiedergabe-Zaehlung. Jetzt mutiert nur
der betroffene Eintrag, die Buchfuehrung steht vor dem Player.
- Das Herz bei Server-Titeln legte einen Favoriten mit leerer Kennung an,
der nie in der Liste auftauchte. Es ist jetzt ausgegraut.
Nachgebessert aus dem Code-Review: Offline-Meldung an die Stelle geholt,
wo sie greifen kann; Queue-Buchfuehrung vor den Player gezogen;
Fehlerpfad fuer die Berechtigungs-Abfrage ergaenzt.
360 Tests gruen (vorher 328), flutter analyze ohne Befund, Release-APK
gebaut und POST_NOTIFICATIONS darin nachgeprueft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xAHJTJM6UUqmjUgk1PUEd
Kein Redesign — Schwarz/Rot, 4 Tabs und Lieder/Kategorie bleiben. Grundlage
sind die Design-Skills: ui-ux-pro-max (Barrierefreiheit, Touch, Typografie,
Bewegung), ui-design/mobile-android (Material 3) und die Token-Disziplin
aus Hue.
Behobene Maengel (messbar, nicht Geschmack):
- Text in Colors.white38 an 17 Stellen = 3,4:1 Kontrast, WCAG verlangt
4,5:1. Ersetzt durch drei benannte Stufen (text1 15,9:1 / text2 8,8:1 /
text3 4,9:1). theme_kontrast_test.dart rechnet die Verhaeltnisse bei
jedem Lauf nach, statt sie zu behaupten.
- SubTabs waren ~38 dp hoch (Material 3 verlangt 48), ohne Ripple und ohne
Uebergang. Jetzt 48 dp, InkWell, AnimatedContainer, und die Auswahl wird
Vorlesehilfen als Zustand gemeldet (Semantics.selected).
- mini_player.dart: Expanded UM eine feste Hoehe herum — zwei
widerspruechliche Angaben. Entschaerft.
- Emoji in Bedienelementen der Einstellungen (jeweils neben einem echten
Icon) entfernt.
Neu in shared/theme.dart: MeloSpace (8er-Raster), MeloRadius, MeloMotion,
Farbrollen text1/2/3 + border + hairline, minTouchTarget, vollstaendiges
textTheme (7 Stufen) und Themes fuer Listen, Sheets, Snackbars,
Fortschritt, Trenner.
318 Tests gruen (23 neue, 1 uebersprungen), flutter analyze ohne Befund.
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
Sperrbildschirm und ReplayGain
- DB-Schema 6: lyrics + gain_db
- Songtext bevorzugt lokalen Tag, Server nur als Rueckfall; Song-ID-Bug
beim Lyrics-Aufruf behoben
- "Als Naechstes spielen" / "Zur Warteschlange hinzufuegen" ohne Eingriff
in Favoriten oder Wiedergabelisten
- songToMediaItem nimmt eine Cover-Vorgabe: Sperrbildschirm zeigt das
Kategorie-Cover
- ReplayGain: Tags werden gelesen, laute Titel abgesenkt, abschaltbar
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011snPXPafPC5i87o68H14W7
- Meine Musik als Startbildschirm ohne Topbar: Kopfzeile (Einstellungen,
Suche, Musikerkennung), Schnellzugriffe, Shuffle + Sortieren, Liederliste
- Sortierung nach Zuletzt hinzugefuegt / Name (A-Z-#) / Wie oft abgespielt,
auf- und absteigend, pro Liste gespeichert (SortStore)
- Favoriten als eigener Tab mit gleicher Shuffle-/Sortier-Leiste
- Download-Tab uebernimmt den Navidrome-Server-Browser
- DB-Schema 3: playCount, gezaehlt beim Titelstart
- Theme auf #0B0B10 / #c0392b
- Scan-Aktionen aus der entfallenen Topbar jetzt in den Einstellungen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011snPXPafPC5i87o68H14W7
Tiefe Debug-Runde bewiesen: Server, DB-Passwort und App-Token-Logik sind
korrekt (8/8-Repro gegen Live-Server) — abgelehnt wird ein falscher
Passwort-Wert vom Gerät. Damit das künftig sofort sichtbar ist:
- NavidromeService.letzterLoginFehler hält die Server-Meldung fest
- Login-Dialog zeigt sie statt generischem "Login fehlgeschlagen"
- Fehler-Log enthält die Passwort-Länge (nie das Passwort selbst)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRj3pupcigUi683vy2Wwnz
Systematische Diagnose bewies: App-Code, Server, User 'Baka' und Passwort sind
korrekt (1:1-Nachbau des App-Requests inkl. Salt/Token -> Navidrome status:ok).
Der Fehler lag im auf dem Gerät verwendeten Credential-Wert:
- der alte Build (Ping-Fehlalarm) persistierte ungültige Zugangsdaten,
- verdeckte Tippfehler im obscureText-Passwortfeld blieben unsichtbar.
Fix:
- Passwort-Anzeigen-Umschalter (Auge-Icon) im Login-Dialog.
- URL + Passwort werden getrimmt (Autofill/Tastatur-Leerzeichen).
Ein erfolgreicher Login ueberschreibt die alten gespeicherten Credentials;
der Ping-Fix stellt sicher, dass nur gueltige gespeichert werden.
84/84 Tests gruen, analyze sauber.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tr2UwurtxyJUpWpBSeVmBm
Diagnose-Logs werden zentral bereinigt, bevor sie an logs.baka-net.de gehen.
Lokaler debugPrint bleibt vollständig.
- Freitext-Scrubber (LoggerService.scrubText) für message + stackTrace:
maskiert user=/pw=/password=/token=/secret=/url= sowie Datei-Pfade,
nackte URLs, Bearer-Token, JWTs und lange Hex-IDs.
- stackTrace auf 8 Zeilen gekürzt (capFrames).
- deviceModel nur noch grobe Plattform (android/ios) statt Hersteller+Modell;
device_info_plus im Logger entfernt.
- Quelle entschärft: settings_screen loggte Navidrome-Login mit Klartext
user=/url= — jetzt nur neutraler Status inkl. pwLeer-Flag.
- Neue Tests: test/logger_service_test.dart (9). 79/79 grün, analyze sauber.
Opt-in/Einwilligung bleibt separater Folgeschritt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tr2UwurtxyJUpWpBSeVmBm
ROOT CAUSE für die fehlenden Logs — und für den fehlschlagenden
Navidrome-Login: Flutter injiziert android.permission.INTERNET nur in
debug/ und profile/AndroidManifest.xml. In main/AndroidManifest.xml
stand sie nicht, das Release-APK hatte damit gar keinen Netzzugriff.
Jede http-Anfrage schlug sofort mit SocketException fehl.
- INTERNET + ACCESS_NETWORK_STATE in main/AndroidManifest.xml ergänzt
- Login-Versuch/-Erfolg/-Fehlschlag im Settings-Dialog explizit geloggt
(URL + User + ob das Passwortfeld leer war; das Passwort selbst nicht)
- Batch-Schwelle 20 -> 5, damit INFO-Logs auch ohne vorherigen Fehler
hochgeladen werden
Nicht geaendert: der Sofort-Upload bei ERROR war bereits implementiert
(error() ruft _uploadIfError -> _uploadLogs direkt auf) und war nicht
die Ursache.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
1. 🔗 AUTO-URL: Server-URL \"https://musik.baka-net.de\" vorausgefüllt (editierbar)
2. 👤 USER-BUTTONS: Baka/Tinker/Dustin als Button-Auswahl (statt Text-Input)
3. 🔐 PASSWORT-ONLY: Dialog zeigt nur Passwort-Feld + User-Auswahl (wie beim Register!)
4. 💡 GAST-MODUS: Info-Text erklärt lokale Nutzung ohne Server-Konto
→ App funktioniert auch komplett offline mit lokaler Musikbibliothek!
UX-Verbesserungen:
- Weniger zu tippen (URL + User vorgegeben)
- Schneller Login für Baka/Tinker/Dustin
- Klare Fallback-Option für lokale Nutzung
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
- getPlaylists() + getPlaylistSongs() APIs für Subsonic-Playlist-Laden
- syncPlaylistsFromServer() in PlaylistService importiert alle Server-Playlisten als lokale
- Benennung mit 🌐 Präfix um Server-Playlisten zu kennzeichnen
- Settings-Button lädt alle Playlisten mit ihren Songs vom Server
- Zeigt Anzahl importierter Playlisten mit SnackBar
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
- Neuer 'Cache-Speicher' ListTile in Musikserver-Sektion
- Zeigt Cache-Größe in MB/KB/B
- Clear-Button: Löscht alle gecachten Lieder
- _formatBytes() für Größen-Anzeige
- _clearCache() mit SnackBar-Feedback
Nutzer kann jetzt Cache-Status sehen + leeren!
Tests: 70/70 ✅
- Bibliotheks-Statistik: Song-Anzahl + Gesamtspieldauer via watchSongs()
(reine Summier-/Format-Funktion in lib/settings/library_stats.dart,
unabhängig von Flutter/Plattform testbar)
- Berechtigungen: Musik-Berechtigung öffnet Systemeinstellungen über das
bereits vorhandene openMusicPermissionSettings()
- Über Melo: kurzer Name + Slogan, keine erfundene Versionsnummer
(package_info_plus ist keine Dependency)
- Keine Platzhalter für Profil/Cloud-Sync/Geräte-Verwaltung, da es noch
kein Account-/Cloud-System gibt (Phase 2)
- HomeShell nutzt SettingsScreen statt des Settings-Placeholders,
toter _Placeholder-Widget-Code entfernt
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>