Commit Graph
79 Commits
Author SHA1 Message Date
Dustin 8f9ed1ff53 v2.56.2 — Versions-String aktualisiert + sichtbare Version in Einstellungen 2026-08-06 20:08:55 +02:00
Dustin 489cb129b1 v2.56.1 — Code-Review-Fix: Settings verbindungsStatus korrigiert (CloudService()-Instanz hatte keinen echten Status → zeigt '–') 2026-08-06 20:01:19 +02:00
Dustin 79b15a3cb8 v2.56 — UI/Design-Rework: Melo Dark Fusion (neue Startseite, 2-stufige Einstellungen, Design-System)
- Design-System: MeloDesignTokens ThemeExtension, Outfit-Font, Farben #0B0B10/#14141C/#1C1C26
- Akzentfarbe-User-Einstellung behalten (MeloTheme.akzentNotifier), Gradient aus Akzent
- Startseite: Hero-Weiterhören-Karte mit Cover, Titel, Glow-Play-Button
- 2-stufige Einstellungen: Standard + Expertenmodus (SharedPreferences Key 'experten_modus')
- Komponenten: Pill-Buttons (radius 999), Card-Radius 20, Glow-Shadow, PressScale-Widget
- Login: Gradient-Button mit Akzent-Glow
- 199 Tests grün (194 bestehend + 5 neu): ThemeExtension, Expertenmodus, Akzent, PressScale, Hero
- flutter analyze lib/ + test/ = 0 Issues
- KEIN APK
2026-08-06 19:56:02 +02:00
Dustin 85615d253c v2.55.3 — Code-Review-Fixes: foregroundServiceType (Android 14+), POST_NOTIFICATIONS-Check, FG-Service-Notification-Channel, try/catch _zeigeSyncNotification
CRITICAL:
- AndroidManifest: foregroundServiceType="dataSync" für BackgroundService (Android 14+ MissingForegroundServiceTypeException)
- main.dart: notificationChannelId + initialNotificationTitle + foregroundServiceTypes für FlutterBackgroundService (fehlende FG-Notification → ANR/crash nach 5s)

MEDIUM:
- sync_service.dart: POST_NOTIFICATIONS-Permission-Check vor _starteForegroundService (Android 13+)
- sync_service.dart: try/catch um _zeigeSyncNotification (plugin-dispose-Sicherheit)

flutter analyze: clean | flutter test: 194/194 passed
2026-08-06 18:17:25 +02:00
Dustin 7777297073 v2.55.2 — Manueller Sync: Status-Popup, Hintergrund-Weiterlauf, Sperrbildschirm-Notification
- cloud_screen.dart: Sync-Popup mit Live-Fortschritt („12/325 synchronisiert"),
  „Im Hintergrund fortsetzen"-Button, Ergebnis-Snackbar nach Abschluss
- sync_service.dart: Notification-Kanal auf HIGH + Visibility.public,
  Foreground-Service starten/stoppen während Sync (Display-aus-Garantie),
  statische syncNotificationDetails() für Tests
- main.dart: Sync-Channel-Importance auf HIGH (überschreibt alte low-Einstellung)
- Tests: +7 (syncNotificationDetails HIGH/public, Lifecycle-Unabhängigkeit)
2026-08-06 18:11:41 +02:00
Dustin b4d63509d1 v2.55.1 — Code-Review-Fixes: Singleton, Race-Conditions, Fallback-Timer, Log-Security
CRITICAL (C1): RealtimeSyncService jetzt Singleton (Factory-Pattern)
  - Verhindert doppelte SSE-Verbindungen durch home_screen + settings
  - UI-Notifier (istVerbundenNotifier) app-weit konsistent

HIGH (H1): _reconnecteOderFallback + starteWennAktiviert setzen
  cloud_interval vor starteAutoSyncTimer() — sonst bleibt Timer
  bei Fallback auf 0 (kein Sync)

HIGH (H2): Race-Condition in _verbinde() nach await request.close()
  — _pausiert-Flag re-check; bei true → clean exit mit _laeuft=false

HIGH (H3): fortsetzen() re-checkt _pausiert nach starteWennAktiviert()
  — verhindert Leak bei schnellem App-Umschalten

MEDIUM (M1): _syncModusSetzen stoppt SSE bei Wechsel auf manual/interval
  (vorher lief die Verbindung weiter)

MEDIUM (M2): Event-Logging loggt nur noch Event-Typ, nicht rohe Daten
  (Privacy: Song-IDs/Titel/Künstler nicht im Log)

LOW (L1): _verbinde() setzt _laeuft=false bei Token-Fehler (kein Deadlock)
2026-08-06 18:04:52 +02:00
Dustin 3f169fa1d9 v2.55 — Echtzeit-Cloud-Sync (SSE) mit 3 Modi: Echtzeit/Manuell/Zeit 2026-08-06 18:01:02 +02:00
Dustin 2ce24398e4 Security-Review-Fixes (Builder): Salt-Regression behoben + Defense-in-Depth
- Navidrome-Salt wieder kryptographisch sicher (Random.secure, 16 Bytes)
  - Regression: v2.31-Redesign (36c744d) hatte den Security-Audit-Fix
    aus 77a2042 stillschweigend auf Timestamp zurueckgesetzt
- Server-IDs (Navidrome s.id, Registry sid) vor Dateinamen defensiv sanitized
  (sanitizeDateiname) — kein Path-Traversal ueber Server-Werte
- MANAGE_EXTERNAL_STORAGE entfernt: deklariert aber nie angefragt
  (toter Berechtigungs-Surface, Play-Store-Policy-Risiko)
- Neuer Test: navidrome_salt_test.dart (Salt != Timestamp, 16 Bytes, eindeutig)

Verifiziert: flutter analyze 0 Issues, flutter test 156/156
2026-08-05 22:54:27 +02:00
Dustin 8e9a808a9f v2.54.3 — Dead-Code-Bereinigung (code-review-graph): 7 ungenutzte DB-Methoden entfernt 2026-08-05 21:10:38 +02:00
Dustin 70a79520d0 v2.54.2 — Review-Fixes (Graph): Shuffle-Duplikat bei Einzel-Song+Repeat, Speed-Chip 1.75x, Drag-Race, Speed-Tap-Guard, Version 2026-08-05 21:04:18 +02:00
Dustin 5f7b652eb6 v2.54.1 — Queue zeigt echte Wiedergabe-Reihenfolge (inkl. Shuffle), aktuelles Lied markiert 2026-08-05 12:20:32 +02:00
Dustin 8b6f0925a6 v2.54 — Now-Playing-Redesign im Spotify-Stil
## Now-Playing-Fullscreen
- Kopfzeile: „JETZT LÄUFT"-Header + Like-Button + Queue-Button (Like aus der Titel-Zeile in den Header verschoben)
- Großes Cover zentriert (78 % Breite, Radius 24) mit Glow-Pulsation + Karten-Schatten
- Titel (24, w800) + Künstler (16) linksbündig in klarer Typografie
- Fortschrittsbalken: dickerer Slider (6 px, weiße Blende), Position links / Dauer rechts
- Controls-Reihenfolge wie Spotify: Shuffle, Prev, Play/Pause (74 px), Next, Repeat
- Neue untere Leiste: Schlaf-Timer links, Lyrics-Toggle + Geschwindigkeits-Chip rechts
- Geschwindigkeits-Chip: Tipp wechselt 0.5x–2.0x, wird wie in den Einstellungen persistiert (playback_speed)
- Blur-Hintergrund dezenter (Sigma 45 → 22)
- Versionsstring in main.dart auf 2.54 aktualisiert (Logger)
2026-08-05 12:16:15 +02:00
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 f14b658566 v2.53.1 — MED-3: Tests für Tombstone-Heuristik, SyncBericht, songAusServerMap + delete-deleted_at
## MED-3 (Review-Fix)
- test/sync_service_test.dart NEU (14 Tests): tombstoneAnwenden-Matrix
  (letzterSync null → true, deletedAt null → true, älter → false, neuer →
  true, Gleichstand → false, ungültiger Timestamp → true), SyncBericht
  (hatAenderungen, zusammenfassung kombiniert/leer, details), songAusServerMap
  (cloud_id/Titel/Künstler/Dauer/Größe/istKorrupt-Mapping, Defaults bei
  fehlenden Feldern, String-Robustheit) — deckt die MED-1-Heuristik + den
  MED-2-DB-Insert ab (123→140 Tests)
- test/cloud_service_test.dart +3: delete mit deletedAt sendet deleted_at im
  Body, delete ohne deletedAt sendet KEIN deleted_at-Feld, delete
  Netzwerkfehler → false
2026-08-05 11:24:51 +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 4694955f5f v2.51.4 — Review-Fix LOW: Longpress-Aktion „Herunterladen“ → „Neu laden“
## LOW (song_tile.dart)
- Label für gesunden Song mit ytUrl heißt jetzt „Neu laden“ statt
  „Herunterladen“ — die Aktion ist ein destruktiver reDownload
  (löscht DB-Eintrag + lokale Datei), das alte Label war irreführend
2026-08-05 08:12:45 +02:00
Dustin 9c854c5ac0 v2.51.4 — Review-Fix MED-3 + MED-4: Konflikt-Dialog (Batch/Auto) + echter Sync-Guard
## MED-3 (cloud_screen.dart)
- Konflikterkennung prüft jetzt Titel ODER Künstler (case-insensitiv)
- Dialog dismissable (Tap außerhalb = Abbruch → lokale Version behalten,
  Sync läuft weiter — kein Hängenbleiben)
- Batch-Optionen: Checkbox „Für alle weiteren Konflikte übernehmen“
  (_konfliktBatch) + Button „Alle weiteren überspringen“
- Auto-Sync (_autoSyncDurchfuehren) löst Konflikte NICHT interaktiv:
  Server-Metadaten gewinnen still (automatisch: true)

## MED-4 (cloud_screen.dart)
- Neuer echter Sync-Guard _syncProzessLaeuft, getrennt vom UI-Flag _syncLaeuft
- „Im Hintergrund fortsetzen“ setzt nur noch _syncLaeuft zurück — der Guard
  bleibt bis zum echten Loop-Ende (finally) gesperrt → kein Doppel-Sync
- _download/_autoSyncDurchfuehren prüfen _syncProzessLaeuft; „Jetzt Syncen“
  während Hintergrund-Sync zeigt Hinweis
- finally gibt den Guard immer frei + AnimationController-stop try/catch
  (Screen kann während Hintergrund-Sync disposed sein)

## LOW (cloud_screen.dart)
- cloud_last_sync/_ts werden nur noch bei erfolgreichem Sync geschrieben
  (_syncAlles returned bool; Fehler/Guard → kein Schreiben)
- _syncedItems-Zähler konsistent (Server-Übernahmen/Downloads je ++ statt
  Überschreiben)
2026-08-05 08:12:45 +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 148ff4f21b v2.51.4 — Review-Fix MED-1: „Musik scannen“ aus Einstellungen startet Scanner
## MED-1 (settings_screen.dart)
- Erweitert-Push wird awaited; Ergebnis 'scanner' wird an MeloHome weitergereicht
- onScan-Closure poppt nur noch den Erweitert-Screen, SettingsScreen propagiert
  das Ergebnis per eigenem Navigator.pop (mit State-Context + mounted-Guard)
2026-08-05 08:09:43 +02:00
Dustin eb379c7790 v2.51.3 — Cloud-Dashboard UI (Statistik-Karten, Speicher, Sync-Historie) + Lint-Fixes
## Cloud-Dashboard (cloud_screen.dart)
- Statistik-Karten 2x2: Songs, Playlisten, Favoriten, Letzter Sync (Daten aus statusDaten/syncStatus)
- Speicheranzeige mit Balken — Platzhalter „–" wenn der Server keine Storage-Daten liefert (storage_used/storage_total o.ä.)
- Sync-Historie-Zeile: „Heute: X Dateien, Y Favoriten, Z Playlisten" aus _syncHistorie (heute gefiltert)
- Konflikt-Dialog NUR bei abweichendem Titel (lokal ≠ Server), sonst kein Dialog
- Lint-Fixes: curly_braces (if ohne Block) + unused _syncHistorieHeuteText (jetzt in der Historie-Zeile verdrahtet)
- Sync-Animation während des Syncs: rotierendes ☁️-Icon + Fortschrittsring (CloudStatus/AnimationController)

Keine neuen Packages, flutter analyze 0 Issues, Tests 98/98 grün
2026-08-05 08:02:36 +02:00
Dustin 4b9cfefe00 v2.51.3 — Schnellaktionen per Longpress auf Songs
## Schnellaktionen (song_tile.dart)
- Longpress öffnet Bottom-Sheet mit: ▶️ Abspielen,  Als Nächstes,  Am Ende hinzufügen,  Favorit (hinzufügen/entfernen), ⬇️ Herunterladen/Erneut herunterladen, 🏷 Tags bearbeiten, ✏️ Umbenennen
- Nur verfügbare Aktionen werden gezeigt (Download nur mit ytUrl, Tags/Umbenennen nur mit DB-id)
- Song-Info-Kopfzeile im Sheet (Titel + Künstler)
2026-08-05 07:57:37 +02:00
Dustin fdfc166807 v2.51.2 — 5-Tab-Navigation (Rechtshänder) + Musikserver-Status
## Navigation (home_screen.dart)
- Neue Tab-Struktur: Start / Bibliothek / Jetzt läuft / Cloud / Mehr (NavigationBar, Icons home, library_music, play_circle_fill, cloud, more_horiz)
- ▶️ Jetzt läuft (Daumen-Zone rechts): öffnet Now-Playing-Fullscreen direkt; bei Pause Hinweis-Screen
- Start: Weiterhören (Zuletzt gehört), Recap-Karte, Statistik, Favoriten-Schnellzugriff, neue Downloads
- Bibliothek: Segmente Songs/Alben/Künstler/Jahre/Genres/Tags/Playlists/Favoriten/Downloads + 'Lied +'- und Such-Buttons oben
- Mehr: Einstellungen, Erweitert, Info, Abmelden
- Keine Funktionalität verloren: YT-Download, Scanner, Recap, Lyrics, Queue erreichbar

## Musikserver (Feature 2)
- 'Navidrome verbinden' → '☁️ Musikserver' + Statuspunkt (grün=verbunden, rot=offline, pulsierend=Sync läuft)
- Detail-Dialog: 'Verbunden mit Navidrome · musik.baka-net.de', Verbinden/Trennen/Browser
- Gespeicherte Zugangsdaten werden beim App-Start geladen
2026-08-05 07:57:01 +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 0439ce3ed2 v2.50.4 — Glow-Pulsation rebuildet + Cover-Crossfade beim Songwechsel
## Fix 1 (MED): Glow-Pulsation rebuildet jetzt wirklich
- now_playing_screen.dart: Cover-Container mit AnimatedBuilder(animation: _glowController) gewrappt — BoxShadow (blur 50–70 / spread 6–12) wird jetzt pro Animations-Tick neu gebaut statt nur bei fremden setStates (Pulsation war vorher nur zufällig über positionStream sichtbar und fror bei Pause/Pufferung ein)

## Fix 2 (MED): Echter Crossfade beim Songwechsel
- _songSub-Listener setzt _hatCover/_coverPfad jetzt SYNCHRON auf false/null (im selben setState) — Hintergrund und Front-Cover zeigen beim Songwechsel kein altes Cover mehr, bis die async Existenz-Prüfung durch ist
- _hintergrundBlur-Key auf den tatsächlichen Cover-Pfad gelegt ('cover-$pfad' statt song.id) — Switcher wechselt erst bei geladenem neuem Cover (Crossfade Gradient→Cover), kein Fade alt→alt + harter Cut mehr

flutter analyze: 0 Issues, Tests 98/98 grün, keine neuen Packages
2026-08-04 20:55:24 +02:00
Dustin dfa19c999b v2.50.3 — sanitize.dart gehärtet: Windows-Namen + Traversal-Sequenzen
## lib/utils/sanitize.dart
- Windows-reservierte Namen (CON, NUL, PRN, AUX, COM1-9, LPT1-9) abgefangen — auch mit Endung (CON.txt) und case-insensitiv; COM10/LPT10 bleiben erlaubt
- Mehrfache ..-Sequenzen nach Sonderzeichen-Entfernung → unknown
- Trimmen VOR der Punkt-Prüfung (Regression: ' ..' ergab vorher '..' = Path-Traversal)
- Trailing Punkte/Leerzeichen entfernt (Windows-Dateisystem ignoriert sie)
- Umlaute bleiben erhalten (unicode:true + \p{L}/\p{N} statt ASCII-\w — 'Müller.mp3' wurde vorher zu 'Mller.mp3')

## test/sanitize_test.dart (18 Tests)
- Traversal: Slash/Backslash-Pfade, '..', ' ..', '..:..', versteckte Dateien
- Windows: alle reservierten Namen exakt/mit Endung/case-insensitiv, COM10-Freigabe, Prefix-Fälle (console, LPT1 - Drucker)
- Normalisierung: NULL-Bytes, Steuerzeichen, Sonderzeichen, trailing Dots, leer, 200-Limit, Umlaute
2026-08-04 20:48:31 +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 e584a7d672 v2.50.1 — Player-Glow an Akzent + Pulsation, Cover-Crossfade, existsSync-Fix
## Fullscreen-Player (now_playing_screen.dart)
- Glow-Farbe folgt MeloTheme.akzentNotifier (Per-User-Akzent) statt fixem Rot; Listener rebuildet bei Akzentwechsel
- Dynamische Glow-Pulsation: AnimationController (Bordmittel) pulsiert blurRadius 50–70 / spreadRadius 6–12 nur bei Wiedergabe, stillstehend bei Pause
- Blur-Hintergrund crossfaded beim Songwechsel (AnimatedSwitcher 600ms, layoutBuilder mit StackFit.expand, ValueKey je Song)
- existsSync aus dem Build-Pfad entfernt: async _ladeCoverStatus mit try/catch, Guard gegen stale Song-Wechsel
2026-08-04 20:44:40 +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 19b115b000 v2.49.1 — Now-Playing-Fullscreen (Cover, Controls, Like, Queue, Sleep-Timer)
## NowPlayingScreen (neu: lib/screens/now_playing_screen.dart)
- Blur-Hintergrund aus dem Cover (ImageFiltered + ImageFilter.blur, Bordmittel), Abdunklungs-Verlauf
- Großes Cover: Radius 20, Glow-Effekt (BoxShadow rot), Fallback auf Noten-Icon
- Titel (groß) + Künstler, Like-Button (Herz): togglet Favoriten via FavoritenService (lokal) mit SnackBar
- Fortschritt: Slider mit Seek (Drag-State), Position/Dauer-Text
- Controls: Shuffle, Repeat (3 Zustände), Prev, Play/Pause (roter Kreis), Next (Long-Press = Play-Next-Menü), Play-Next-Button
- Play-Next-Menü: kommende Queue-Songs, Tipp = spieleAlsNaechstes + SnackBar
- Sleep-Timer: Button + Restzeit ⏱ in der Kopfzeile, Sheet mit Chips Aus/15/30/45/60
- Queue-Zugriff: Button in der Kopfzeile → WarteschlangeSheet
- Slide-up/Slide-down via PageRouteBuilder (MiniPlayer)

## MiniPlayer
- Tipp öffnet jetzt den Fullscreen-Player (statt direkt der Queue)
- Queue nur noch via Button im Fullscreen
- Keine neuen Packages, flutter analyze 0 Issues, Tests 35/35
2026-08-04 16:37:13 +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 b97914699e v2.48.5 — Tests für Player-Kern (Shuffle/Repeat/Queue/Sleep-Timer)
## test/player_service_test.dart (neu, 17 Tests)
- Warteschlange: setWarteschlange, unveränderliche Kopie, spieleAlsNaechstes,
  amEndeHinzufuegen, verschiebeInWarteschlange (Index-Anpassung)
- Repeat: aus (Ende → null), titel (immer aktuell), playlist (Wrap von vorn)
- Shuffle: alle anderen Songs genau einmal ohne Wiederholung, kein aktueller
  Song; shuffle+playlist mischt nach Durchlauf neu; Einzel-Song-Queue;
  Ausschluss des aktuellen Index nach Umsortieren
- Sleep-Timer: aktivieren, Restzeit, abbrechen, Ersetzen

flutter analyze 0 Issues, Tests 21/21
2026-08-04 16:27:36 +02:00
Dustin 82003c55d6 v2.48.4 — Als Nächstes abspielen (Play Next)
## SongTile
- Long-Press auf Song-Tile öffnet Kontextmenü (Bottom-Sheet):
  ▶ Als Nächstes abspielen + ⏭ Am Ende hinzufügen

## ViewModel
- spieleAlsNaechstes(Song) / amEndeHinzufuegen(Song) → PlayerService

## HomeScreen
- SongTile-Callbacks verdrahtet, SnackBar-Bestätigung bei beiden Aktionen

Keine neuen Packages, flutter analyze 0 Issues, Tests 4/4
2026-08-04 16:25:53 +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 b5b1475d5d v2.42.1 — HIGH: Timer-Periodic-Leak behoben
- MeloApp von StatelessWidget zu StatefulWidget konvertiert
- _autoScanTimer als globale Variable gespeichert
- Timer wird in _MeloAppState.dispose() gecancelt
2026-08-02 17:00:23 +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 6ed416c8b1 v2.37 — Issue #6: Auto-Scan beim Start + Timer.periodic alle 3h
- MusikScanner().scanneMusikOrdner() wird direkt nach Auth-Init aufgerufen
- Timer.periodic alle 3 Stunden für periodischen Scan
- androidStopForegroundOnPause=false für persistente Notification (Issue #8)
Build: ✓ flutter analyze
2026-08-02 15:56:13 +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 2b06a8e496 fix: Cloud-Status sofort anzeigen (kein leerer Ladekreis) 2026-08-01 18:23:19 +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 7f17a5afd6 v2.33 — Login vereinfacht: keine Registrierung, Navidrome auto-sync, nur eine Anmeldung 2026-08-01 17:36:02 +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 cdb137f724 fix: stale cloud_einstellungen.dart entfernt (Rebase-Artefakt) 2026-08-01 15:06:26 +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