Dokumentiert in PLAN.md den offenen Folgeschritt zu Branch
feature/log-datenschutz-scrubber: Diagnose-Versand auf Opt-in (Einwilligung)
umstellen. Kein Code-Change, nur Planung.
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
Bisher stand im Log nur die nackte Exception-Message ("Connection timeout")
— ohne URL und ohne Fehlertyp war die Ursache nicht eingrenzbar.
Neu im Fehler-Log:
- Fehlerart klassifiziert: TIMEOUT / SOCKET / TLS-HANDSHAKE / HTTP / FORMAT
- SocketException zusätzlich mit osError-Message, errorCode, Adresse und
Adress-Typ (IPv4 vs IPv6 — zeigt ein IPv6-Problem direkt an)
- Server-URL sowie geparstes scheme/host/port (falsche URL, fehlendes
https, DNS-Auflösung erkennbar)
- e.runtimeType + Message
Bewusst NICHT geloggt: die volle Ping-URI — sie enthält Auth-Token und
Salt als Query-Parameter, die nicht auf dem Log-Server landen sollen.
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
- Größeres MiniPlayer-Widget auf allen Non-Player-Screens (80px statt 60px)
- Größeres Album-Cover (64px statt 44px) mit besserer Sichtbarkeit
- Skip-Buttons (⏮️ Vorheriges + ⏭️ Nächstes) für schnelle Navigation
- Progress-Bar zeigt Wiedergabe-Position in Echtzeit
- Besseres Styling + Schrift-Gewicht für Titel
- Funktioniert wie Home-Widget: zeigt überall was gerade spielt
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
- androidShowNotificationBadge aktiviert für bessere Notification-Sichtbarkeit
- Notification bleibt aktiv auch wenn Musik pausiert (AndroidService-Konfiguration)
- Play/Pause/Skip-Controls auf Lock Screen + Notification (AudioService-Standard)
- Media-Session Integration für Android SystemUI Controls
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
- Neue NavidromeService APIs: scrobble() + getBookmark()
- Lädt Wiedergabe-Position vom Server beim Song-Start (Subsonic-API)
- Sendet Position alle 5s zum Server wenn verbunden
- Priorisiert Server-Position über lokale Position
- Full Sync: lokal speichern + Server synchen parallel
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
- Toggle-Buttons in Navidrome-Tab: "📀 Alben" ↔ "🎤 Künstler"
- Artist-Liste zeigt Namen pro Künstler, Tap lädt alle Songs des Künstlers
- _playArtist() Methode lädt Künstler-Songs via getArtistSongs() API
- Albums/Artists sind Lazy-Loaded (nur beim Toggle laden)
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 ✅
IMPLEMENTATION:
- NavidromeService.streamAndCacheToLocal(): Stream + gleichzeitig cachen
- Cache-First-Logik: Wenn Cache existiert → lokal spielen
- AudioHandler erweitert: loadPlaylist() nutzt Auto-Caching
- CacheManager Integration in AudioHandler
FLOW:
1. User spielt Album ab → streamt vom Server
2. Während des Abspielens → lokal in Cache speichern
3. Nächstes Mal: Lokal spielen (schneller, Offline möglich)
Phase 2 P0 COMPLETE! ✅
Tests: 70/70 ✅
LYRICS:
- Lyrics-Button im Now-Playing Screen (📝 Icon)
- Bottom-Sheet mit Lyrics vom Server (getLyrics() API)
- Fallback: 'Keine Lyrics verfügbar'
- Lyrics-Klasse für Datenmodell
SHUFFLE/REPEAT:
- Shuffle-Button (schon implementiert!) zeigt aktiven Status rot
- Repeat-Button (Off → All → One → Off cycle)
- Beide via AudioHandler.setShuffleMode() / setRepeatMode()
- Integration in _Controls (Player-UI)
Phase 1 P1-Features jetzt funktionsfähig!
Tests: 70/70 ✅
- getArtists(): Alle Künstler vom Server laden
- getArtistSongs(): Alle Songs eines Künstlers
- setFavorite()/removeFavorite(): Server-Favoriten toggle
- getFavorites(): Favoriten vom Server laden
- SubsonicArtist-Klasse für Künstler
APIs für Phase 2 vorbereitet (Artist-Tab, Server-Favoriten-Sync)
Tests: 70/70 grün
- Neuer 4. Tab: '🌐 Server' zeigt Alben vom Musikserver
- Album-Liste laden und anzeigen (AsyncBuilder)
- Tap auf Album → alle Songs in Queue laden + abspielen
- Graceful Fallback wenn Server nicht verbunden
- Tests: 70/70 grün
- Analyzer: ✅ keine Fehler
Phase 1 P0-Feature: Album-Browser vom Server
- 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>
- MeloAudioHandler bekommt MeloDb per Konstruktor injiziert
- Positions-Timer (alle 5s) schreibt db.recordPlayback(songId, positionMs)
während aktiv abgespielt wird (songId aus mediaItem.extras['songId'])
- loadPlaylist liest db.lastPosition() für den Start-Song und seekt vor
dem play() an die gespeicherte Position, statt immer von vorne zu starten
- shouldResumeAt() als reine, testbare Funktion extrahiert: kein Resume bei
Position 0 oder innerhalb der letzten 3s einer bekannten Track-Dauer
- dispose() zum gefahrlosen Abbrechen des Positions-Timers in Tests ohne
vollen App-Lifecycle (super.stop() lässt sich in Widget-Tests wegen der
aktiven playbackState-Pipe nicht gefahrlos aufrufen)
- library_screen_test.dart: MeloAudioHandler-Konstruktion angepasst (db
jetzt Pflichtparameter) + handler.dispose() im Teardown
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Neuer QueueScreen: zeigt handler.queue, hebt aktuell spielenden Titel
hervor (fett/rot), Antippen springt per skipToQueueItem
- Entfernen und Umsortieren ohne Änderung an MeloAudioHandler: Queue wird
clientseitig neu berechnet und per loadPlaylist neu geladen; Position
wird nur wiederhergestellt, wenn der abgespielte Titel unverändert blieb
- Reine Queue-Logik (removeFromQueue/reorderQueue/newPlayingIndex) als
eigenständige, ohne AudioHandler testbare Funktionen extrahiert
- Warteschlangen-Button in der Now-Playing-AppBar verlinkt den neuen Screen
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Library screen now has a TabBar (Alle / Künstler / Alben) inside a
DefaultTabController; scan/permission/error banners stay above the
TabBarView so they remain visible regardless of the active sub-tab
- New lib/library/song_grouping.dart: pure, unit-tested functions
groupByArtist/groupByAlbum (null artist/album -> "Unbekannt" /
"Unbekanntes Album", alphabetically sorted) plus albumArtistLabel
("Verschiedene Interpreten" when an album mixes artists)
- New lib/library/artist_list.dart + album_list.dart: StreamBuilder on
MeloDb.watchSongs(), grouped client-side, tapping a row pushes a
SongList screen for that artist/album; album rows show a CoverImage
thumbnail from the first song's coverPath
- Existing "Alle" tab content (recently-added strip, full song list,
empty state) preserved unchanged, moved into _AllSongsTab
Genre browsing is intentionally out of scope: the Songs table has no
genre column and neither scanner extracts genre metadata — needs a
schema migration + scanner work in a future task.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
PlaylistDetailScreen zeigt die Songs einer Playlist (watchPlaylistSongs),
erlaubt Entfernen einzelner Songs und Umsortieren per Drag & Drop
(ReorderableListView.builder). Tippen auf einen Song spielt die Playlist
ab dieser Stelle. Leer-Zustand mit Hinweistext.
Da bestehende position-Werte nach Einfügereihenfolge lückenhaft sein
können, schreibt das Umsortieren nicht nur den verschobenen Song um,
sondern vergibt für die gesamte Playlist neue lückenlose Positionen
(0..N-1): MeloDb.reorderAllPlaylistSongs (in einer Transaktion) +
PlaylistService.reorderAll als dünner Wrapper.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Ohne Eintrittspunkt wären Playlisten unbenutzbar, da sie sonst nie Songs
erhalten könnten. SongList bekommt einen zusätzlichen Trailing-Button
(Icons.playlist_add) neben dem Favoriten-Herz, der ein Bottom-Sheet mit
allen Playlisten öffnet ("Neue Playlist" oben, dann bestehende). Antippen
hängt den Song ans Ende der gewählten Playlist an und zeigt eine SnackBar.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>