Commit Graph
57 Commits
Author SHA1 Message Date
Dustin 83bb86a1fa v2.53.1 — LOW-1: Willkommen-zurück-Dialog nur bei >24h Offline (24h-Check) 2026-08-05 11:26:00 +02:00
Dustin 2cd34d61dc v2.53.1 — MED-2: Neue Server-Songs werden nach Download in die lokale DB eingefügt
## MED-2 (Review-Fix, KRITISCH)
- sync_service.dart: Nach erfolgreichem cloud.download wird der Song jetzt
  MIT cloud_id + Titeldaten (Titel/Künstler/Dauer/Größe/istKorrupt aus der
  Server-Liste) in die lokale DB eingefügt — vorher fehlte der Song in der
  Bibliothek, songNachCloudId blieb null und jeder Sync lud alle 325 Songs
  erneut (≈ GB/Sync) und meldete sie fälschlich als „+neu“
- Neuer statischer Helfer `songAusServerMap` (testbar): baut den Song aus
  den handle_list-Feldern; ytUrl wird bewusst NICHT übernommen (v2.52-F2:
  Server bleibt YT-Link-Quelle)
- Zielpfad-Kollisionen abgesichert: existiert die Datei bereits (verwaister
  Download aus früherem Sync ohne DB-Eintrag), wird „ (2)“, „ (3)“ … an-
  gehängt — sonst scheiterte der Insert am UNIQUE-Constraint datei_pfad
2026-08-05 11:24:11 +02:00
Dustin 3fe20e1c7b v2.53.1 — MED-1: Tombstone-Race behoben (Snapshot vor listSongs + Server-Zeit)
## MED-1 (Review-Fix)
- sync_service.dart: letzterSync-Snapshot wird VOR listSongs gelesen und am
  Sync-Ende persistiert (Sync-BEGINN statt now-am-Ende) — Löschungen, die
  zwischen listSongs und Sync-Ende auf dem Server ankommen, werden im
  nächsten Sync korrekt angewendet statt als „bereits gesehen“ übersprungen
- cloud_screen.dart: _download schreibt cloud_last_sync_ts nicht mehr
  nachträglich mit now-am-Ende (redundant + Race-Wieder-Einführung);
  UI-Anzeige liest den von SyncService persistierten Snapshot
- melo_cloud.py: handle_delete stempelt deleted_at IMMER mit Server-Zeit
  (datetime('now')) — Client-Timestamps werden ignoriert (Uhr-Drift würde
  Löschungen dauerhaft überspringen lassen; konsistente Uhr für alle Geräte)
2026-08-05 11:22:36 +02:00
Dustin 2d735464c0 v2.53 — Tombstone-Sync + Konflikt-Report (Sprint E): deleted_at, lokal nachgezogene Löschungen, Willkommen-zurück-Dialog 2026-08-05 11:14:21 +02:00
Dustin 22ce611537 v2.53 — Sprint E: Recap-Zeiträume (Woche/Monat/Jahr)
## Recap-Zeiträume
- recap_service.dart: RecapZeitraum-Enum (woche/monat/jahr), berechne(zeitraum:, jetzt:) mit injizierbarem Historie-Loader (Tests ohne DB), Fallback-Kette Woche → Monat → Jahr → Gesamt-Historie, Zeitraum-Getter (kopfzeile, introTitel, introUntertitel, zeitraumLabel, shareTitel, Monatsname)
- recap_screen.dart: Zeitraum-Umschalter (7 Tage / Monat / Jahr) oben im Recap, dynamische Texte („Deine Woche in Musik“, „Abspielungen in dieser Woche“, „Melo Wochen-Recap“), Seiten-Reset bei Wechsel
- home_screen.dart: Recap-Karte „Recap 2026“ + „Woche · Monat · Jahr“-Untertitel
- settings_screen.dart: Kachel „Recap (Woche/Monat/Jahr)“
- test/recap_service_test.dart: 12 neue Tests (Filter, Fallback-Kette, Aggregation, Label-Getter)
- flutter analyze 0 Issues, Tests 123/123
2026-08-05 11:10:49 +02:00
Dustin bc3357bf72 v2.52.4 — Einstellungen (F4): Auto-Sync-Intervall, Sleep-Timer-Default, Wiedergabegeschwindigkeit, Akzentfarbe, Storage-Info 2026-08-05 11:05:55 +02:00
Dustin 187b26603b v2.52.3 — F3-Rest: ☁️-Chip-Puls + Auto-Sync-Timer app-weit
## Chip-Puls (home_screen)
- ListenableBuilder hört auf Listenable.merge([_vm, _cloud, SyncService.laeuftNotifier]) — der ☁️-Musikserver-Chip pulsiert jetzt bei laufendem Sync (auch im Hintergrund/Auto-Sync), nicht nur beim Verbinden/Laden
- serverSyncLaeuft = SyncService.laeuftNotifier.value || CloudStatus.verbinde || serverLadt

## Auto-Sync-Timer app-weit (main.dart)
- _starteAutoSyncFallsNoetig() entfernt (F3a: kein Sync beim App-Start); stattdessen SyncService.starteAutoSyncTimer() — Timer tickt erst nach dem Intervall, Fälligkeit via istSyncFaellig
- shared_preferences-Import aus main.dart entfernt (Intervall-Logik lebt in SyncService/CloudScreen)

## Sync-Notification
- Sync-Kanal de.baka.melo.sync wird in main() angelegt (Android 8+: Channel nötig, sonst erscheint die Notification nicht)
- onDidReceiveNotificationResponse: Tipp auf 'sync_fertig'/'sync_fehler' → SyncService.syncBenachrichtigungGetippt++ → MeloHome öffnet den Cloud-Tab

## Version
- Versionsanzeige 2.51 → 2.52.3 (settings/home/erweitert), MeloLogger.init('2.35') → '2.52.3'
2026-08-05 11:03:03 +02:00
Dustin 73aed70edd v2.52.2 — SyncService-Zentralisierung (F3): persistente Sync-Notification, Auto-Sync-Intervall, Schritt-Feedback, 12/25-Zähler 2026-08-05 10:59:36 +02:00
Dustin bf0f544e4b v2.52.1 — F2: YT-Links nur als Server-Fallback
## YT-Quell-URL (Server ist die Quelle)
- cloud_service.ytUrlSetzen(): POST /api/v1/cloud/yt-url (per-User UPDATE user_songs.yt_url)
- musik_scanner._sucheYtUrls(): durchsucht NUR Songs mit cloud_id, die auf dem Server noch keine yt_url haben; Treffer werden auf den Server hochgeladen statt lokal gespeichert
- db_helper: ytUrlAktualisieren/songsOhneYtUrl entfernt, songsMitCloudId() ergänzt
- download_service: yt_url wird beim Download nicht mehr lokal persistiert

## Fallback-Kette reDownload (korrupte/fehlende Datei)
- 1) Server-Download via cloud_id + Magic-Byte-Check (primäre Quelle)
- 2) Server-ytUrl (aus listSongs) → yt-proxy als letzte Chance
- 3) Legacy: lokal gespeicherte ytUrl (Bestandsdaten vor v2.52)
- song_tile: „Neu laden“ erscheint bei istKorrupt && (cloudId != null || ytUrl != null)
- 3 neue Tests (ytUrlSetzen: ok/error/401) → 111 Tests grün, analyze 0 Issues
2026-08-05 10:40:37 +02:00
Dustin 3b898e238e v2.52 — Favoriten-Sync-Fix: bidirektionaler Merge (lokal↔Server), Toggle-Push sofort, cloud_id-Verdrahtung 2026-08-05 10:33:23 +02:00
Dustin a24875726f v2.51.4 — Review-Fix MED-2: „Trennen“ (Musikserver) persistiert jetzt
## MED-2 (navidrome_service.dart + home_screen.dart)
- Neues NavidromeService.loescheZugangsdaten(): löscht In-Memory-Zustand
  UND persistierte Credentials (SharedPreferences navidrome_url/user +
  SecureStorage navidrome_pass)
- _zeigeServerBrowser() ist async und ruft loescheZugangsdaten() beim
  Trennen auf — Chip bleibt nach App-Neustart rot statt wieder grün
2026-08-05 08:10:30 +02:00
Dustin f886b0a42e v2.51.1 — Technik ausgelagert: Erweitert-Sektion (Logs, Diagnose, Entwickler, Scanner)
## Erweitert
- Neuer ErweitertScreen (lib/screens/erweitert_screen.dart): Logs, Diagnosedaten-Toggle, Entwickler, Musik scannen
- Neuer LogViewerScreen: In-Memory-Logbuch mit Kategorie-Filter (MeloLogger bekommt Anzeige-Puffer, der beim Senden nicht geleert wird)
- Neuer EntwicklerScreen: Version, Session-ID, DB-Songzahl, Server-URLs, Test-Log senden
- Einstellungen-Screen: 'Erweitert'-Sektion eingebaut, Diagnose-Toggle dorthin verschoben, Version auf 2.51

## Einstellungen
- 'Server verbinden' → 'Musikserver' umbenannt
- Abmelden/Info/Cloud Sync unverändert
2026-08-05 07:54:22 +02:00
Dustin 5b5f72b548 v2.50.2 — Cloud/Auth-Tests (http-Mocks): LWW-Sync + Token-Ablauf-Pfade
## Testbarkeit (kein neues Package)
- CloudService: http.Client injizierbar (Konstruktor-Parameter, Default unverändert)
- AuthService: http.Client + TokenSpeicher injizierbar; SecureTokenSpeicher kapselt FlutterSecureStorage, @visibleForTesting AuthService.fuerTests
- Alle HTTP-Calls über _client statt Top-Level http.get/post

## test/cloud_service_test.dart (22 Tests)
- login: 200/401/Netzwerkfehler
- verbinde() ohne Benutzer → Status fehler, kein Request
- statusDaten: 200/401(Auth-Fehler)/Netzwerkfehler
- syncChanges LWW-Konflikt-Pfade: updated_at bleibt für Client-Vergleich erhalten, leere/fehlende Changes, 401 → [], Netzwerkfehler → [], since-Body
- syncAll (LWW-Schreibpfad): 200/401
- Playlists, Favoriten, History, listSongs, delete, globalList inkl. Fehlerpfade

## test/auth_service_test.dart (19 Tests)
- initialisieren: 200 → eingeloggt, 401 → Logout + Speicher gelöscht, Netzwerkfehler → Offline-Trust, 5xx → Token bleibt, ohne Token → kein Request
- tokenPruefen: 200/401/Netzwerkfehler/503
- login: ok mit Persistenz, 401, 200-ohne-Token (Fall-Through-Regression), Netzwerkfehler
- logout löscht Speicher, authHeader mit/ohne Bearer
2026-08-04 20:46:51 +02:00
Dustin 69b8f29f88 v2.49.6 — Async-Exceptions abgesichert (kein Crash bei nicht-seekbaren Quellen)
## Fix (Code-Review Finding 4, MED)
- PlayerService.play/playPause/pause/seek: try/catch mit MeloLogger —
  schützt ALLE Aufrufer (Buttons, Listener, System-Controls)
- naechstes: Repeat-Titel-Zweig (seek+play) try/catch — Live-Streams ohne
  Seek-Unterstützung crashen nicht mehr
- vorheriges: kompletter Ablauf try/catch (Seek-Pfad + Songwechsel)
- NowPlayingScreen: Lyrics-Tap + Slider-Seek laufen über die abgesicherte
  seek()-Methode — keine unhandled async Exceptions aus Fire-and-Forget
2026-08-04 16:47:58 +02:00
Dustin 8e9d9efc64 v2.49.5 — Play-Next verschiebt statt zu duplizieren + Shuffle-Priorität
## Fix (Code-Review Finding 3, MED)
- spieleAlsNaechstes: Song bereits in der Queue wird an Position nach dem
  aktuellen VERSCHOBEN (inkl. _aktuellerIndex-Anpassung), keine Kopie mehr
- Shuffle: keine Neu-Mischung nach Play-Next — Song wird via
  _pinneInShuffleReihenfolge an die nächste Position gepinnt (Index-
  Korrektur für Verschiebungen, _shufflePos-Nachführung am Reihenende)
- 3 Tests ergänzt: Verschieben statt Duplizieren, Song von vor dem
  aktuellen, Shuffle-Pin (kein Duplikat + naechsterIndex == gepinnter Song)
2026-08-04 16:47:29 +02:00
Dustin bd692d4682 v2.49.4 — Favoriten-Toggle race-sicher (kein PK-Crash bei Doppel-Tap)
## Fix (Code-Review Finding 2, HIGH)
- DbHelper.songZurPlaylist: INSERT OR IGNORE (ConflictAlgorithm.ignore) —
  Doppel-Tap-Race verletzt PRIMARY KEY (playlist_id, song_id) nicht mehr
- FavoritenService.umschalten: gibt neuen Status zurück, try/catch mit
  MeloLogger, Zustand nach Fehler aus DB neu geladen statt blind geflippt
- NowPlayingScreen._toggleFavorit: In-Flight-Guard _toggleLaeuft gegen
  parallele Toggles + Statusübernahme aus DB (Quelle der Wahrheit)
2026-08-04 16:46:49 +02:00
Dustin c6826db651 v2.49.3 — Shuffle-Edge-Case: Einzel-Song-Queue + Repeat-aus → Queue-Ende
## Fix (Code-Review Finding 1, HIGH)
- _baueShuffleReihenfolge: Index 0 nur noch bei Repeat-playlist in die
  Shuffle-Reihenfolge aufnehmen (Einzel-Song-Queue)
- naechsterIndex: Sicherheitsnetz gegen stale Reihenfolge — Kandidat ==
  aktueller Song bei Repeat-aus liefert null (Queue-Ende) statt denselben
  Song erneut
- Test ergänzt: shuffle + repeat aus + 1-Song-Queue → null
2026-08-04 16:46:08 +02:00
Dustin a1f1d87cd0 v2.49.2 — Netease-Suche auf funktionierenden Endpoint umgestellt
- /api/search/get/web liefert seit Netease-Update nur noch verschlüsselte Antworten (getestet)
- Umstellung auf /api/search/get (public, kein Key) — liefert result.songs[].id im JSON
- Lyric-Endpoint (/api/song/lyric) unverändert, funktioniert
- Kette curl-verifiziert: Suche → Song-ID → LRC-Text
- flutter analyze 0 Issues, Tests 35/35
2026-08-04 16:37:55 +02:00
Dustin 4226612065 v2.49 — Lyrics-Service (LRC + Netease)
## LyricsService (neu: lib/services/lyrics_service.dart)
- LyricsZeile-Modell (zeit + text)
- LRC-Parser: [mm:ss], [mm:ss.xx], [mm:ss.xxx], mehrere Zeitstempel/Zeile, [offset:±ms], Meta-Tags ignoriert, Karaoke-Tags <...> entfernt, sortiert
- Quellen: lokale .lrc neben dem Song (song.dateiPfad → .lrc) → Netease-API (Suche + Lyric, public, kein Key) → null
- Keine neuen Packages (nur http)

## Tests (test/lyrics_service_test.dart, 14 neu)
- parseLrc: Formate, Multi-Timestamps, Offset, Sortierung, Karaoke-Tags
- indexFuerPosition: Grenzfälle
- ladeLyrics: lokale .lrc-Datei (Temp-Datei), ohne Datei → null (HTTP im Test geblockt)
- flutter analyze 0 Issues, Tests 35/35
2026-08-04 16:35:19 +02:00
Dustin 34ac791fa6 v2.48.3 — Sleep-Timer
## PlayerService
- setSleepTimer(Duration): nach Ablauf wird pausiert (Timer aus dart:async)
- cancelSleepTimer(), sleepTimerAktiv, sleepRestzeit (live berechnet)
- Timer wird in dispose() aufgeräumt

## WarteschlangeSheet
- Schlaf-Timer-Zeile: Optionen Aus/15/30/45/60 Min als Chips
- Aktiver Timer zeigt Restzeit (⏱ MM:SS), Aktualisierung jede Sekunde
- 'Aus'-Chip rot wenn Timer inaktiv, sonst gewählte Dauer aktiv markiert

Keine neuen Packages, flutter analyze 0 Issues, Tests 4/4
2026-08-04 16:25:30 +02:00
Dustin 5edfae01e5 v2.48.2 — Queue-UI (Warteschlange-Sheet)
## PlayerService
- warteschlange Getter (unveränderliche Kopie) + aktuellerIndex
- spieleAlsNaechstes(Song): fügt nach aktuellem Song ein
- amEndeHinzufuegen(Song): hängt ans Ende an
- verschiebeInWarteschlange(von, nach): Drag-and-Drop mit korrekter Anpassung des aktuellen Index

## WarteschlangeSheet (neu: lib/widgets/warteschlange_sheet.dart)
- Bottom-Sheet: aktuelle Warteschlange, aktueller Song rot markiert (▶)
- Tipp auf Song → sofort abspielen
- ReorderableListView (Bordmittel) zum Umsortieren per Drag
- Leer-Zustand + Titel-Zähler

## MiniPlayer
- Tipp auf den Miniplayer öffnet die Warteschlange (Bottom-Sheet)

Keine neuen Packages, flutter analyze 0 Issues, Tests 4/4
2026-08-04 16:25:04 +02:00
Dustin 42767a5646 v2.48.1 — Repeat-Modus (Aus/Titel/Playlist)
## PlayerService
- Enum Wiederholmodus { aus, titel, playlist } + Getter/Setter
- naechstes(): titel → gleicher Song von vorn (seek 0 + play)
- naechsterIndex(): zentrale Berechnung des nächsten Index
  - playlist: nach letztem Song wieder von vorne (auch bei Shuffle: neu mischen)
  - aus: Ende der Warteschlange stoppt
- Auto-Next (ProcessingState.completed) läuft über naechstes() → Repeat wirkt auch automatisch

## MiniPlayer
- Repeat-Icon mit 3 Zuständen (repeat / repeat_one), rot wenn aktiv, Wechsel bei Tipp

Keine neuen Packages, flutter analyze 0 Issues, Tests 4/4
2026-08-04 16:24:26 +02:00
Dustin bb8ee56894 v2.48 — Shuffle-Modus
## PlayerService
- PlayerService ist jetzt ChangeNotifier (UI hängt per addListener/ListenableBuilder an)
- zufallsmodus Getter + setZufallsmodus(bool)
- Zufallsreihenfolge: alle Queue-Indizes außer dem aktuellen Song, gemischt
- naechstes(): bei Shuffle nächster Song aus Zufallsreihenfolge, ohne Wiederholung bis alle durch
- vorheriges(): geht in der Zufallsreihenfolge zurück
- spiele(): synchronisiert Shuffle-Position (auch bei manueller Auswahl)

## MiniPlayer
- Shuffle-Icon (rot wenn aktiv, Tipp schaltet um)

Keine neuen Packages, flutter analyze 0 Issues, Tests 4/4
2026-08-04 16:23:55 +02:00
Dustin 319b765d08 v2.47 — Cloud-Verbindung beim App-Start + Live-Status-Anzeige (Verbinde→Verbunden)
## CloudService
- CloudStatus-Enum (verbinde/verbunden/fehler), CloudService extends ChangeNotifier
- verbinde(): Login+Status beim App-Start, setzt Status, notifyListeners
- Getter status/statusText/istVerbunden/verbindungGestartet
- Bestehende Methoden behalten — nur status() → statusDaten() umbenannt (Namenskonflikt mit neuem status-Getter)

## Home-Screen
- _cloud.verbinde() in initState (feuern-und-vergessen) — Verbindung steht beim App-Start, nicht erst beim Tab-Öffnen

## Cloud-Screen
- initState verbindet nicht mehr selbst — zeigt Service-Status via ListenableBuilder
- Status wechselt live: 'Verbinde...' (gelb) → 'Verbunden' (grün) / 'Keine Verbindung' (rot)
- Serverdaten (Playlists/Favoriten/Zähler) laden automatisch, sobald Service verbunden
- Refresh-Button re-verbindet + lädt Zähler neu
- Fallback: ungestarteter Service (Einstellungen-Screen) wird beim Öffnen gestartet
2026-08-03 03:08:45 +02:00
Dustin 20ef788178 v2.46 — Jahres-Recap (Spotify-Wrapped-Stil) mit Hörstatistiken
## Neuer Recap-Screen
- lib/screens/recap_screen.dart: Full-Screen mit 7 animierten Seiten (Intro, Top 5 Songs, Top 3 Künstler, Hörzeit, häufigster Tag, Nachtaktive %, Gesamtzahl + Teilen)
- Wrapped-Stil: Verlaufs-Gradienten, große Zahlen mit Count-up-Animation, Fade+Scale-Seitenwechsel, Fortschritts-Punkte, Weiter-Button
- Teilen-Button kopiert Recap-Text in die Zwischenablage (kein neues Package)

## Datenbasis
- lib/services/recap_service.dart: aggregiert lokale Abspiel-Historie zu Top-5-Songs, Top-3-Künstler, Hörzeit, Wochentag, Nacht-%, Gesamtzahl
- Fallback auf Gesamt-Historie wenn im Zieljahr keine Daten; freundlicher Leer-Zustand ohne History

## DB + Tracking
- db_helper.dart: Migration v6→v7 mit neuer Tabelle abspiel_historie (song_id, played_at) + Backfill aus wiedergabe_verlauf
- player_service.dart: registriert jeden Abspielvorgang in der Historie

## Einstieg
- Home-Screen: Karte 'Jahres-Recap 2026' unter der Statistik-Card
- Einstellungen: Kachel 'Jahres-Recap' in Sektion Daten & Privatsphäre

flutter analyze: 0 Issues, Tests 4/4
2026-08-03 02:54:11 +02:00
Dustin cd49e80293 v2.45.2 — JWT-Auth für yt-proxy: X-API-Key komplett entfernt, Bearer-Token überall 2026-08-03 02:49:30 +02:00
Dustin 4c987036bc v2.45.1 — Auto-Scan ohne AudioPlayer: Byte-Parser für Dauer (MP3/M4A/FLAC/WAV), MPEG-Header-Offset fix, WAV Little-Endian fix 2026-08-03 02:47:05 +02:00
Dustin 0b8d8ce2f8 fix: MEDIUM+LOW Claude-Audit-Findings (Issues #7-16)
MEDIUM:
- #7 API-Key XOR-obfuskiert in app_config.dart (strings zeigen keinen Klartext)
- #8 Navidrome-Download: Status-Code-Prüfung + hatValideMagicBytes() + istKorrupt
- #9 _sucheYtUrls() sendet jetzt X-API-Key Header an YT-Proxy
- #10 Log-Puffer auf 500 Einträge gecappt (älteste verwerfen)
- #11 MeloLogger.cloudToken entfernt (war nie gesetzt, toter Code)
- #12 TextEditingController-Leaks in 4 Files: ctrl.dispose() nach showDialog
- #13 Cloud-Sync: Last-Write-Wins Konfliktauflösung dokumentiert

LOW:
- #14 API-Versionierung: alle /api/cloud/ → /api/v1/cloud/
- #15 Auto-Scan: nur /Music, /Download, nicht ganz /storage
- #16 2 pre-existing flutter analyze Infos behoben (curly_braces, use_build_context_synchronously)

flutter analyze: No issues found.
2026-08-03 01:50:07 +02:00
Dustin e46ffa76a3 fix: 6 Claude-Audit-Fixes (3 CRIT + 3 HIGH)
CRITICAL:
- Logout: LoginScreen statt MeloHome pushen (home_screen.dart)
- Path Traversal: sanitizeDateiname() zentral + sync-loop abgesichert
- SecureStorage: flutter_secure_storage für Token+Passwort (auth_service, navidrome)

HIGH:
- Seek: PlayerService.seek() reload-frei (audio_handler)
- Play/Pause: Zielzustand statt Toggle für System-Controls
- X-User: Header entfernt, Server verlässt sich auf JWT
2026-08-03 01:41:04 +02:00
Dustin de71119235 v2.42.3 — MEDIUM: Dead Code entfernt
- download_screen.dart: Unbenutztes Feld _speichertInDownloads entfernt
- home_screen.dart: Unbenutzte Methode _zeigePlaylistSheet entfernt
- home_screen.dart: Unbenutzten Import playlist_sheet.dart entfernt
- melo_logger.dart: Unnötigen Import material.dart entfernt
2026-08-02 17:00:31 +02:00
Dustin 50a810934d v2.42.2 — HIGH: Background-Service try/finally + MEDIUM: debugPrint Leak
## HIGH: Background-Service bleibt hängen
- downloadVonUrl: try/finally stellt sicher dass _stopBackgroundService() immer aufgerufen wird
- downloadBatch: try/finally stellt sicher dass _stopBackgroundService() immer aufgerufen wird
- reDownload: try/finally stellt sicher dass _stopBackgroundService() immer aufgerufen wird

## MEDIUM: debugPrint Leak
- HTTP-Response-Body in debugPrint auf max 200 Zeichen gekürzt
2026-08-02 17:00:27 +02:00
Dustin a483436490 v2.42 — Issue #7: ID3-Reader erweitern + Metadaten-Dialog verschönern
## ID3-Reader (lib/services/id3_reader.dart)
- ID3v1: Jahr (Bytes 93-97) + Genre-Index (Byte 127, vollständige Genre-Liste 0-147)
- ID3v2: Text-Frames TIT2 (Titel), TPE1 (Künstler), TALB (Album), TYER (Jahr), TCON (Genre), TRCK (Track)
- ID3v2-Frames haben Vorrang vor ID3v1 (Füll-Logik)
- UTF-16/UTF-8 Encoding-Unterstützung für Text-Frames
- Genre-Bereinigung: Klammer-Präfixe entfernt, Track-Nummer extrahiert

## Song-Model (lib/models/song.dart)
- Neue Felder: jahr, genre, track (nullable, optional)
- Neue getter: dateiFormat (MP3/M4A/FLAC/WAV/OGG aus Dateiendung)
- DB-Migration v5→v6: jahr, genre, track Spalten

## Metadaten-Dialog (lib/widgets/metadaten_dialog.dart)
- Cover-Bild (128×128) mit rotem Glow-Rahmen oben falls vorhanden
- Editable Felder mit Icons: Titel, Künstler, Album, Jahr, Genre
- Read-Only-Info-Card: Dauer, Dateigröße, Format
- Cards mit roten Akzenten + Melo-Design (schwarz/rot/dunkelgrau)
- ID3-Fallback: Jahr/Genre aus Datei nachladen wenn nicht in DB
- Speichern persistiert auch Jahr und Genre in die DB

## Musik-Scanner (lib/services/musik_scanner.dart)
- jahr, genre, track aus ID3-Tags an Song-Model durchgereicht
- _nichtLeer-Helfer für null-sichere String-Extraktion
2026-08-02 16:10:58 +02:00
Dustin ae11a85872 v2.41 — Issue #3: Musik-Download im Hintergrund (flutter_background_service)
- flutter_background_service ^5.0.0 hinzugefügt
- AndroidManifest: FOREGROUND_SERVICE_DATA_SYNC Permission
- main.dart: Background Service mit configure() initialisiert
- _onServiceStart(): Service-Callback für Foreground-Garantie
- DownloadService._startBackgroundService(): Foreground Service vor Download
- DownloadService._stopBackgroundService(): Stopp nach Batch/Single
- Integration in _resetStatus, downloadBatch, downloadVonUrl
2026-08-02 16:06:04 +02:00
Dustin b8c206e58f v2.40 — Issue #9: Progress-Notification für Download/Upload (flutter_local_notifications)
- flutter_local_notifications ^22.2.0 hinzugefügt
- Notification-Channel 'de.baka.melo.downloads' in main() initialisiert
- DownloadService._showProgress(): Fortschritts-Notification mit ProgressBar
- DownloadService._completeNotification(): Abschluss-Notification (Erfolg/Fehler/Abbruch)
- Integration in downloadBatch(): Fortschritt pro Song, Abschluss nach Batch
- CloudScreen._upload(): Upload-Fortschritt + Abschluss-Notification
2026-08-02 16:04:09 +02:00
Dustin f23dc685cb v2.39 — Issue #8: Persistente Notification (nicht wegwischbar)
- MediaControl.stop aus compact-Controls entfernt (nur play/pause, prev, next)
- Notification bleibt auch bei Pause bestehen
Build: ✓ flutter analyze
2026-08-02 15:56:25 +02:00
Dustin 0fc383daba v2.38 — Issue #2: Dauerverbindung Cloud Server (persistenter http.Client)
- CloudService: static final http.Client _client als Instanzvariable
- Alle http.get/post/put/delete/upload nutzen nun _client statt neuem Client
- DownloadService: gleicher Fix für _retryHttpPost und _retryHttpGet
Build: ✓ flutter analyze
2026-08-02 15:56:22 +02:00
Dustin d555a7f146 v2.36 — Sprint 1: Behebe Issues #1 (Zurück-Button), #5 (Ausgeloggt), #4 (Berechtigungen)
Issue #1: CloudScreen Zurück-Button — onZurueck Callback statt Navigator.pop()
- CloudScreen erhält VoidCallback? onZurueck Parameter
- Bei onZurueck != null → Callback (Tab-Wechsel in home_screen)
- Fallback: Navigator.pop(context) für Standalone-Nutzung

Issue #5: Token wird nur bei HTTP 401 gelöscht
- _tokenOnlinePruefen(): nur statusCode == 401 → false
- 5xx, andere Fehler, Netzwerkfehler → true (Token behalten)
- Verhindert Ausloggen bei temporären Server-Problemen

Issue #4: Android 13+ Berechtigungen beim App-Start
- main.dart: frageSpeicherZugriff() + POST_NOTIFICATIONS nach Auth-Init
- musik_scanner.dart: Permission-Check vor externen Ordnern in _sammleMusikPfade()

Build: ⏭️ (no APK)
2026-08-02 15:48:08 +02:00
Dustin 15993b1d0a v2.35 — Vollständiges Sync-System: Playlisten, Favoriten, Auto-Sync, Persistent Login, Benutzerdefinierte Namen
## Server (melo_cloud.py v3.0.0)
- Neue DB-Tabellen: user_playlists, user_playlist_songs, user_favorites, user_history, sync_meta
- Playlist-Endpunkte: GET/POST /api/cloud/playlists, GET /api/cloud/playlists/<id>, POST songs, DELETE songs, PUT positions
- Favoriten-Endpunkte: GET/POST /api/cloud/favorites, POST /api/cloud/favorites/toggle
- History-Endpunkte: GET/POST /api/cloud/history (mit Dedup)
- Rename-Endpunkt: POST /api/cloud/rename (custom_title/custom_artist)
- Sync-Endpunkte: POST /api/cloud/sync-all, GET /api/cloud/sync-status
- delete-all erweitert um Playlists + Favorites + History

## App
- db_helper.dart: Migration v4→v5 (cloud_id, cloud_title, cloud_artist, server_favorites, sync_metadata)
- auth_service.dart: Token-Validierung beim Start (online check + Offline-Fallback), Persistent Login
- cloud_service.dart: Neue Methoden für Playlists (CRUD), Favorites (get/sync/toggle), Rename, History, syncAll/syncStatus
- cloud_screen.dart: Komplett-Rewrite mit Sync-Modus (Manuell/Auto), Intervall-Auswahl, Sync-Fortschritt-Anzeige, Playlist-Verwaltung, Favoriten-Anzeige + Umbenennen, Sync-Info-Karte, nächster Sync
- main.dart: Auto-Sync beim App-Start (nur wenn konfiguriert + fällig)

## Flutter Analyze
- 0 neue Issues — nur 7 pre-existing
2026-08-01 19:32:35 +02:00
Dustin ac543eb84b fix: CloudService silent error → logging, Status zeigt Token-Fehler an 2026-08-01 18:15:04 +02:00
Dustin a7dbf5e9e1 fix: Login-Fehlermeldung zeigt jetzt saubere Server-Nachricht, Passwort-Reset Baka 2026-08-01 18:08:10 +02:00
Dustin 30c85f823e debug: Body-Inhalt im Login-Fehler anzeigen 2026-08-01 18:04:01 +02:00
Dustin ebd3131117 debug: auth-response logging + Body-Länge im Fehler 2026-08-01 17:51:10 +02:00
Dustin a495a183d8 fix: Login-Fehlerhandler — 200 ohne Token abfangen, Server-Nachrichten parsen 2026-08-01 17:44:05 +02:00
Dustin 7d5cae64d1 fix: syntax error in player_service (missing }); 2026-08-01 16:54:05 +02:00
Dustin f00fd445f5 fix: code-review #2 — dedup MagicBytes, auth für check_corrupted, UI-Flag, merged player streams, jq-JSON 2026-08-01 16:48:45 +02:00
Dustin 2d4c7c8b98 v2.32 — Defekte Musik erkennen (Magic Bytes, ffprobe, DB-Flag, UI) 2026-08-01 16:40:42 +02:00
Dustin 4a05c76199 fix: code-review findings — error handling, null safety, persist settings, auth header 2026-08-01 15:09:48 +02:00
Dustin 669a02304e fix: code-review warnings — deprecated APIs, debugPrint→Logger, unused import 2026-08-01 15:08:05 +02:00
Dustin 36c744d6e3 v2.31 — Login, Einstellungen & Cloud Sync Redesign
- Login-Screen im Melo-Design (Schwarz+Rot) mit Baka-Auth JWT
- AuthService fuer Token-Management ueber baka-net.de/auth
- Registrierungs-Modus + 'Ohne Login fortfahren'
- Navidrome-Credentials optional im Login aufklappbar

- SettingsScreen ersetzt AlertDialog
- BottomNav navigiert statt Dialog zu triggern
- Cloud Sync, Server verbinden, Diagnosedaten, App-Info, Abmelden

- CloudEinstellungen-Dialog entfernt, alles in CloudScreen konsolidiert
- Hardcode login('Baka','') entfernt → AuthService.benutzer
- Gradient-Statuskarte, animierte Segment-Intervall-Auswahl
- JWT Authorization statt Klartext-Passwort

- AppConfig: API-Key defaultValue entfernt (kein Fallback-Leak)
- Build crasht ohne --dart-define MELO_API_KEY

Build: split APK (arm64 19.6M, armeabi 17.1M, x86_64 21.1M)
2026-08-01 15:03:22 +02:00
Hermes (Server) 77a20422be Security-Fixes aus dem flutter-security Audit
CRIT:
- Cloud-Token jetzt in flutter_secure_storage (Keystore/Keychain) statt SharedPreferences, mit Migration alter Eintraege
- usesCleartextTraffic entfernt (kein HTTP-Klartext mehr)
HIGH:
- Path-Traversal gefixt: p.basename bei YouTube-Download, Navidrome-Download und Cloud-Auto-Sync
MED:
- allowBackup=false (kein Backup von Token-Daten)
- Navidrome-Salt kryptographisch sicher (Random.secure statt Timestamp)
2026-08-01 13:10:18 +02:00