# Changelog Alle nennenswerten Änderungen an diesem Projekt werden hier dokumentiert. Format angelehnt an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/). ## [Unreleased] ### 🐛 Cloud-Kontingent-Fix aus dem Task-Review (2026-08-29) - 🔁 **`BakaAuth.abmelden()` setzt jetzt auch `verbleibend` zurück.** Referenzmuster `GastZugang.verwerfen()` setzt `_verbleibend = null` mit, `abmelden()` tat das bisher nicht — nur `_token`/`_benutzer` wurden zurückgesetzt. Meldet sich ein Cloud-User ab und ein ANDERER Cloud-User (Dustin/Baka/Tinker) danach an, wäre sonst bis zum nächsten Download der alte Kontingent-Stand des vorherigen Accounts sichtbar geblieben. Aktuell folgenlos (da `verbleibend` bis zum Server-Rollout ohnehin immer `null` ist), aber ein latenter Bug, der beim Server-Rollout live gegangen wäre. (`lib/services/baka_auth.dart`) - ✅ **Getestet (RED→GREEN):** neuer Test „Abmelden setzt auch den Kontingent-Stand zurück" in `test/services/baka_auth_test.dart`. Volle Suite grün, `flutter analyze` ohne Befund. ### ☁️ Cloud-Account-Kontingent: App-Seite für das 100/Tag-Limit (2026-08-29) - ⚠️ **Reine App-Seite, ohne Wirkung bis der Server mitzieht.** Die Server-Zählung (`cloud_quota`, 100/Tag je Nutzername) in `yt_proxy.py` ist NICHT Teil dieser Änderung — das macht die `claude-server`-Session separat (siehe Nachtrag "100/Tag-Limit auch für Cloud-Accounts" in `docs/superpowers/specs/2026-08-26-youtube-guest-quota-design.md`). Bis der Server das neue Feld `cloud_remaining` liefert, bleibt `auth.verbleibend` einfach `null` — kein Fehler, kein sichtbarer Unterschied zu heute. - 🔢 **`BakaAuth` bekommt einen Kontingent-Stand** — analog zu `GastZugang.verbleibend`/`.merkeVerbleibend(n)`: neues Feld `int? verbleibend` (Getter) und `void merkeVerbleibend(int n)` (`lib/services/baka_auth.dart`). - 🔌 **`YtDownloadService.herunterladen()` liest `cloud_remaining`** aus einer erfolgreichen `POST /api/yt-dl`-Antwort und ruft bei Cloud-Zugriff (`!gastAktiv`) `auth.merkeVerbleibend(...)` auf — genau wie beim bestehenden `guest_remaining`/`gast.merkeVerbleibend(...)` fürs Gast-Kontingent, aber strikt getrennt (ein Gast-Zugriff liest das Feld nie, ein Cloud-Zugriff nie `guest_remaining`). (`lib/services/yt_download_service.dart`) - 📟 **Angemeldete Cloud-Accounts sehen jetzt „Noch N von 100 heute“**, sobald der Server den Stand gemeldet hat — an vergleichbarer Stelle wie der bestehende Gast-Zähler, aber nur bei `auth.istAngemeldet` (Gäste haben weiter ihren eigenen Text, beide schließen sich gegenseitig aus). (`lib/downloads/downloads_screen.dart`, `lib/downloads/youtube_search_screen.dart`) - ✅ **Getestet (TDD, RED→GREEN):** `merkeVerbleibend` in `test/services/baka_auth_test.dart`; Erfolgsfall mit `cloud_remaining` plus eine Trennschärfe-Gegenprobe (Gast-Zugriff liest `cloud_remaining` nie, selbst wenn es im Body steht) in `test/services/yt_download_test.dart`; je ein Widget-Test für den neuen „Noch N von 100 heute“-Text in `test/downloads/online_screen_test.dart` und `test/downloads/youtube_search_screen_test.dart`. Volle Suite grün, `flutter analyze` ohne Befund. ### 🐛 Gast-Zugang: Fixe aus dem Branch-Abschluss-Review (2026-08-26) - 🔓 **Gast-Modus war eine Einbahnstraße.** Einmal "Als Gast fortfahren" angetippt, blieb `gast.hatToken` dauerhaft `true` (Token steckt im Secure Storage, wird bei jedem App-Start wiederhergestellt) — und damit war `_AnmeldeDialog`, der EINZIGE Einstieg in der App für die Baka-Anmeldung (auch für den Melo-Cloud-Sync-Login!), unerreichbar. Neu: `GastZugang.verwerfen()` verwirft den Token wieder, und ein "Anmelden"-Knopf neben der Gast-Statuszeile (Download-Tab und YT-Suche-Tab) ruft ihn auf. - 🔀 **Ein alter Gast-Token hat den Auto-Login-Rückfallweg für Server-User verdeckt.** `zugriffOk` prüfte bisher den rohen `gast.hatToken` statt `istGastModus` — ein Gerät, das früher mal als Gast lief und jetzt als Server-User mit fehlgeschlagenem Auto-Login dasteht, bekam die Fehlermeldung dazu nie zu sehen und lud still über den (limitierten) Gast-Pfad. `istGastModus` wird jetzt vor `zugriffOk` berechnet und dort statt des rohen Tokens verwendet. (`lib/downloads/downloads_screen.dart`, `lib/downloads/youtube_search_screen.dart`) - ⏱️ **Doppel-Tap auf "Als Gast fortfahren" konnte zwei Anfragen gleichzeitig auslösen.** Beide Screens sperren den Button jetzt während `holeToken()` läuft (`_gastLaeuft`). - 📝 **Widersprüchlicher Hinweistext im YT-Suche-Tab behoben** — der Text über dem Gast-Button verlangte fälschlich eine Baka-Anmeldung. - ⚠️ **CHANGELOG ergänzt:** der Abschnitt zum Gast-Zugang unten macht jetzt klar, dass die Server-Seite (`POST /api/guest-token`) noch fehlt. - ✅ **Getestet:** 2 neue Regressionstests fürs "Anmelden"-Escape-Hatch (`test/downloads/online_screen_test.dart`, `test/downloads/youtube_search_screen_test.dart`), 2 bestehende Gast-Tests im YT-Suche-Tab jetzt mit echtem `gast:`-Wiring in `YtSearchService`/`YtDownloadService`. Volle Suite grün, `flutter analyze` ohne Befund. ### 📺👤 YouTube-Gast-Zugang: bewusste Wahl + Tages-Zähler (Download-Tab + YT-Suche-Tab) (2026-08-26) - ⚠️ **Braucht noch die Server-Seite.** Der neue Endpunkt `POST /api/guest-token` und die Quota-Logik in `yt_proxy.py` existieren noch nicht (separates Vorhaben, siehe `docs/superpowers/specs/2026-08-26-youtube-guest-quota-design.md`). Bis die Server-Seite steht, liefert der "Als Gast fortfahren"-Button zuverlässig einen 404-Fehler. - 👤 **Echte Gäste (kein Baka-Konto, kein Melo-Server) sehen jetzt zwei Buttons** statt nur der Anmeldung: "Beim Baka-Konto anmelden" und neu "Als Gast fortfahren (5 Downloads/Tag)". Der Gast-Button holt sich per `GastZugang.holeToken()` (aus Task 1-4 dieser Reihe) einen anonymen Token vom Proxy und schaltet damit den Downloader frei — ganz ohne Konto. Schlägt das Holen fehl, erscheint der Fehlertext direkt unter dem Button. - 🔢 **Im Gast-Modus zeigt eine Statuszeile "Als Gast unterwegs — noch N von 5 heute"**, sobald der Server den Kontingent-Stand gemeldet hat (vorher nur der generische Hinweistext "5 Downloads/Tag"). Der Cookie-Schalter ("YouTube-Cookies des Servers verwenden") ist im Gast-Modus ausgeblendet — der Server erzwingt für Gast-Zugriffe ohnehin `cookies=false`, ein sichtbarer Schalter hätte nur eine Wahl vorgetäuscht, die es nicht gibt. - 🐛 **Nebenbefund durchs Qualitäts-Gate:** `_YouTubeBereich.build()` beobachtet jetzt `GastZugang` app-weit über den Provider-Baum. Zwei Tests (`hauptmenue_test.dart`, `home_shell_test.dart`) bauen ihren eigenen Provider-Baum nach, statt `main.dart` zu importieren, und hatten `GastZugang` dort noch nicht — beide entsprechend ergänzt (analog zum bestehenden Muster für `BakaAuth`/`YtDownloadService`). - 📺 **YT-Suche-Tab bekommt denselben Gast-Button und -Zähler** wie der Download-Tab. Das alte "Baka-Anmeldung (siehe Download-Tab)" Hint für Gäste ist weg — echte Gäste in `lib/downloads/youtube_search_screen.dart` sehen jetzt "Als Gast fortfahren (5 Downloads/Tag)" und die Status-Zeile "Als Gast unterwegs — noch N von 5 heute", genauso wie im Download-Tab. - ✅ **Getestet:** volle Suite grün (600 Tests), `flutter analyze` ohne Befund. 3 neue Widget-Tests in `test/downloads/online_screen_test.dart`: beide Buttons sichtbar, Gast-Token-Erfolg schaltet das Adressfeld frei, Gast-Token-Fehler wird angezeigt. ### 📺🔑 YouTube-Tab: Server-User brauchen keine eigene Baka-Anmeldung mehr (2026-08-26) - 🔑 **Server-User (Dustin, Baka, Tinker) sehen den Baka-Anmelde-Schritt nicht mehr.** Bisher verlangte der YouTube-Downloader/die YT-Suche IMMER eine eigene, separate Anmeldung am Baka-Konto — unabhängig davon, ob man schon am Melo-Server angemeldet war. Neu: Wer mit dem Melo-Server verbunden ist (`NavidromeService.istVerbunden`), meldet sich beim Öffnen des YouTube-Bereichs automatisch im Hintergrund mit denselben Zugangsdaten beim Baka-Konto an (`BakaAuth.autoAnmelden`, neu in `baka_auth.dart`) — kein Dialog, kein Warten. Voraussetzung: Navidrome- und Baka-Konto teilen sich Benutzername + Passwort (so bei Dustin/Baka/Tinker der Fall). Schlägt die Hintergrund-Anmeldung fehl (z. B. abweichendes Passwort), bekommt der Server-User den bisherigen manuellen Anmelde-Weg als Rückfalloption zurück — sonst gäbe es keinen Weg mehr zu `_AnmeldeDialog`, das war ein im @code-review-Fund entdeckter Aussperr-Bug (siehe `pruefeServerUser` in `baka_auth.dart`). - 👋 **Die Anmeldung ist jetzt wirklich nur noch für Gäste sichtbar.** Ohne Server-Verbindung (kein eigener Melo-Account) erscheint weiterhin der Hinweistext + Anmelde-Button wie bisher — Server-User sehen stattdessen direkt das Adressfeld, ganz ohne Konto-Status-Zeile. - 🎨 **Login-UI vergrößert.** "Beim Baka-Konto anmelden" ist jetzt ein Button über die volle Breite (52 px hoch, größere Schrift); die Buttons "Abbrechen"/"Anmelden" im Anmelde-Dialog haben jetzt eine Mindesthöhe von 48 px. Der Dialog hatte ohnehin nie ein Adressfeld — die Proxy-URL bleibt fest im Code hinterlegt, nicht sichtbar oder änderbar. - ✅ **Getestet:** 582 Tests grün (vorher 574), `flutter analyze` ohne Befund. 8 neue Tests: `BakaAuth.autoAnmelden` (meldet an, greift nicht ein wenn schon angemeldet, tut nichts ohne Zugangsdaten) sowie Widget-Tests für den Server-User-Weg und den Rückfallweg bei fehlgeschlagener Auto-Anmeldung in Download-Tab und YT-Suche-Tab. ### 🎚️📺 Equalizer-Fix (Xiaomi) + In-App-YouTube-Suche, Extra-Tab (2026-08-25) - 🎚️ **Equalizer: "kein System-Klangeffekte" trotz Dolby Atmos behoben.** `openSystemPanel()` nutzte bisher nur den Android-Standard-Intent (`ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL`) — dafür registriert MIUI/HyperOS auf dem POCO X7 Pro keine Activity, `resolveActivity()` liefert dort immer `null`. Neu: zuerst der Xiaomi-Weg direkt zur System-Ton-Seite (`com.android.settings.Settings$SoundSettingsActivity`, wo Dolby Atmos liegt), danach `Settings.ACTION_SOUND_SETTINGS` als universeller Fallback, zuletzt weiter der Android-Standard-Intent für Geräte, die den unterstützen. Kotlin kompiliert sauber (`gradlew :app:compileDebugKotlin`); echtes Testen auf einem POCO X7 Pro war von hier aus nicht möglich. - 📺 **In-App-YouTube-Suche — neuer Extra-Tab "YT-Suche" (zum Testen).** Bisher ließ sich nur eine YouTube-Adresse einfügen; jetzt gibt es eine echte Suche mit Ergebnisliste (Vorschaubild, Titel, Dauer) und Download-Knopf pro Treffer. Nutzt den bereits vorhandenen `/api/search`-Endpunkt des Baka-Proxys (yt-dlp-gestützt, dieselbe Anmeldung wie der bestehende YouTube-Downloader) — kein neuer Server, kein YouTube-API-Key nötig. Die Vorschaubilder kommen direkt von YouTubes öffentlichem Thumbnail-CDN, aus der Video-ID der Ergebnis-URL. Der Download-Knopf läuft über denselben Weg wie im Download-Tab (Baka-Proxy → App-Zwischenordner → MediaStore → Bibliotheks-Scan). - 🐛 **Beim Testen gefunden:** `YtDownloadService.herunterladen()` (Future.any + `.timeout()`) lässt sich nicht sicher aus einem `testWidgets()`-Test heraus aufrufen — der Timeout-Timer bleibt unter Flutters `fake_async`-Testbindung dauerhaft "pending" und lässt den Lauf hängen bzw. an der Pending-Timer-Prüfung scheitern (reproduziert auch ganz ohne die neue UI). Genau deshalb hat auch der bestehende Download-Knopf im Download-Tab keinen eigenen Widget-Test — die Download-Logik selbst ist über `yt_download_test.dart` schon ausführlich abgedeckt, an `YtDownloadService` wurde nichts geändert. - ✅ **Getestet:** 574 Tests grün (vorher 557), `flutter analyze` ohne Befund. Neu: Tests für `youtubeVideoId`/Thumbnail-Ableitung, für `YtSearchService.suchen` (Anmeldung, Erfolg, 401, Fehlertext, Timeout, neue Suche ersetzt alte Treffer) und zwei Widget-Tests für den neuen Tab (Anmelde-Hinweis, Suchergebnisse mit Titel/Dauer). ### 🔒 Worktree-Lock gegen Doppelarbeit (2026-08-25) Aus der Workflow-Analyse desselben Tages (Dustin↔Hermes↔Claude, siehe Eintrag unten): zwei Sessions bekamen 11 Minuten auseinander denselben Auftrag für diesen Worktree — die erste committete nicht rechtzeitig, die zweite musste die fertige Arbeit nachträglich unversioniert einsammeln. - `melo-app-workflow`-Skill (nicht Teil dieses Repos) hat jetzt eine Pflicht-Regel: vor der ersten Änderung eine `.claude-worktree-lock`-Datei im Worktree-Root auf Frische prüfen (< 60 Minuten = vermutlich aktiv → erst Dustin fragen), sonst eigene Sperre setzen und am Ende entfernen. - `.claude-worktree-lock` neu in `.gitignore` — lokaler Laufzeit-Zustand, kein Projektstand. - Test-Timeout-Vorgabe im Skill korrigiert: `flutter test` dauert bei 550+ Tests oft >400s: Hintergrund-Lauf statt Foreground-Timeout ist jetzt der dokumentierte Standard. - Kein Code geändert, nur `.gitignore` — kein Testlauf nötig. ### 🔐 Berechtigungen beim Start, schärferes Sperrbildschirm-Cover (2026-08-25) Vier Punkte aus dem Auftrag geprüft — zwei davon waren schon erledigt: - ✅ **Tab-Reihenfolge** (Meine Musik, Suchen, Download, Favoriten) und ✅ **konfigurierbare Schnellzugriffe in "Meine Musik"** (Checkboxen in den Einstellungen) waren bereits vollständig umgesetzt — nichts zu tun, siehe Test `home_shell_test.dart` bzw. `AppSettings.meineMusikSektionen`. - 🔑 **Alle Berechtigungen beim ersten Start.** Bisher fragte die App das Musik-Recht erst beim manuellen Scannen und das Benachrichtigungs-Recht erst nach dem ersten abgespielten Titel — beides isoliert und spät. `LibraryService.ensureStartupPermissions()` fragt jetzt gleich beim App-Start beide Rechte nacheinander ab (nicht gleichzeitig — `permission_handler` wirft sonst einen Fehler). Wird eines dauerhaft verweigert, zeigt "Meine Musik" ein Hinweisbanner mit Button direkt zu den App-Einstellungen (`openAppSettings`) — für die Benachrichtigung neu, fürs Musik-Recht gab es das schon. Auch in den Einstellungen selbst gibt es jetzt für beide Rechte eine eigene Zeile mit "Einstellungen öffnen". - 🖼️ **Schärferes Cover für den Sperrbildschirm.** `audio_service` liefert das Album-Cover fürs große Sperrbildschirm-Bild (MIUI & Co.) technisch schon über die MediaSession — die extrahierte Auflösung beim Android-Scan war mit 512px aber knapp. Jetzt 1024px. - 🐛 **Dabei gefunden:** Der neue Start-Berechtigungs-Aufruf ließ einen bestehenden Widget-Test (`home_shell_test.dart`) hängen — ein unbedingter `notifyListeners()`-Aufruf ohne echte Wertänderung kollidierte mit dessen Test-Teardown (nur ein `pump()`, kein `pumpAndSettle()`). `LibraryService` meldet Änderungen an `permissionDenied` und `notificationPermissionDenied` jetzt nur noch, wenn sich der Wert tatsächlich ändert. - ✅ **Getestet:** 557 Tests grün (vorher 550), `flutter analyze` ohne Befund. Neu: Banner-Test für die verweigerte Benachrichtigung, Test für die neue Berechtigungs-Zeile in den Einstellungen. ### 🧑‍🤝‍🧑 UX-Simulation, Teil 3: die zurückgestellten Punkte (2026-08-25) Die vier Punkte aus Teil 2, die als "eigenes kleines Vorhaben" zurückgestellt waren, sind jetzt auch erledigt: - ⏹️ **Abbrechen-Knopf für YouTube-Downloads.** Ein hängender Download (bis zu zwei mal 180s) ließ sich vorher nur durch Schließen der App stoppen. `YtDownloadService` wartet jetzt mit `Future.any` gegen ein Abbruch-Signal — die Netzwerk-Anfrage läuft im Hintergrund aus, die App wartet aber nicht mehr darauf und meldet sich sofort zurück. - 🔴 **Punkt am inaktiven Download-Unterreiter,** wenn dort im Hintergrund ein Download läuft — man sah vorher nicht, dass beim Wechsel zu YouTube/Server nebenan noch etwas passiert. `SubTabs` kann das jetzt für jeden Reiter einzeln (neuer, optionaler `badges`-Parameter). - 🔢 **Versionsnummer unter "Über Melo"** — dafür kam `package_info_plus` als neue Abhängigkeit dazu (federated plugin, kein zusätzliches Manifest nötig). - 👆 **Mini-Player folgt jetzt dem Finger.** Vorher zählte nur ein kräftiger Schwung (≥ 300 px/s); wer langsam, aber weit genug wischte, bekam gar keine Reaktion — wie ein hängender Bildschirm statt "zu langsam gewischt". Der Player führt die Bewegung jetzt sichtbar mit (`AnimatedContainer.transform`) und entscheidet beim Loslassen über Schwung **oder** zurückgelegte Strecke; ohne Wirkung schnappt er sichtbar zurück. Nach unten bleibt weiterhin unbelegt (Absicht, unverändert). - 🔧 **Nebenbei gefunden:** Drei bestehende Widget-Tests (`hauptmenue_test.dart`, `home_shell_test.dart`, `online_screen_test.dart`) bauten den Download-Tab ohne `Provider` auf — funktionierte bisher nur, weil niemand ihn dort abfragte. Das Badge tut das jetzt app-weit; die drei Test-Aufbauten sind entsprechend ergänzt. - ✅ **Getestet:** 550 Tests grün (vorher 539), `flutter analyze` ohne Befund. Neu: Abbruch-Test für den YouTube-Download (hängende Anfrage per `Completer`), zwei Tests für das SubTabs-Badge, vier für die streckenbasierten Wisch-Funktionen. ### 🧑‍🤝‍🧑 UX-Simulation, Teil 2: Player-Feinschliff, Downloads, Einstellungen (2026-08-25) Zweiter Block derselben 39 Funde: - 🎧 **Songtext-Icon ohne ID war ein toter Knopf** — sichtbar, tippbar, aber ohne jede Wirkung. Jetzt ausgegraut mit erklärendem Tooltip, wenn kein Songtext-Ziel bekannt ist. - ⏱️ **Sleep-Timer-Restzeit war faktisch unsichtbar** (nur im Tooltip, den kaum jemand per Long-Press entdeckt) — steht jetzt auch im Sheet. - ↕️ **Warteschlange umsortieren war unauffindbar:** kein sichtbares Zeichen, dass die Liste per Drag reagiert. Jetzt mit Drag-Handle-Symbol wie bei Spotify/Apple Music. - 👇 **Vollbild-Player ließ sich nicht wegwischen** — nur über den Zurück-Pfeil schließbar, obwohl der Mini-Player schon Wischgesten kann. Jetzt schließt Wischen nach unten den Player, wie bei Apple Music/Spotify. - 🏷️ Fehlendes `tooltip:` am Schließen-Knopf des Songtext-Sheets ergänzt — jeder andere Icon-Button der App hat einen. - 📡 **YouTube-Downloads:** Zeitüberschreitung (Server erreichbar, braucht nur zu lange) und toter Server meldeten sich bisher identisch — jetzt mit eigener, ehrlicherer Meldung ("braucht zu lange" statt "nicht erreichbar"). - 🔄 **Server-Tab blitzte beim Öffnen kurz "nicht verbunden" auf**, obwohl verbunden — weil `build()` lief, bevor die gespeicherten Zugangsdaten fertig geladen waren. Jetzt erst ein Ladezustand, dann die echte Antwort. - 🗂️ **Einstellungen umsortiert:** Favoriten-Sync und Playlisten-Sync standen unter "Speicher", obwohl sie nichts mit Speicherplatz zu tun haben — jetzt bei "Musikserver", direkt beim Navidrome-Eintrag. Der Navidrome-Eintrag selbst sagt jetzt, wofür er da ist ("Eigener Musik-Server"), statt nur den Software-Namen zu zeigen. - ✏️ Kommentar in `geteilte_adresse.dart` korrigiert: er behauptete, der Server entscheide über nicht-YouTube-Adressen — tatsächlich lehnt `YtDownloadService` sie schon vorher ab. Verhalten bewusst nicht geändert (dafür gibt es einen eigenen Test), nur die Doku richtiggestellt. - ⏸️ **Bewusst nicht angefasst:** das separate Server-Favoriten-Herz (`ServerFavoriteButton`) bleibt wie dokumentiert lokal-only — das ist eine bestehende, begründete Architektur-Entscheidung (siehe G3 im UX-Review) und keine Kleinigkeit, die sich nebenbei mitreparieren lässt. Ebenso zurückgestellt: Live-Mitführen des Fingers beim langsamen Wischen am Mini-Player, ein Abbrechen-Knopf für laufende YouTube-Downloads, ein Lauf-Badge auf dem inaktiven Download-Unterreiter und eine Versionsnummer unter "Über Melo" (bräuchte eine neue Abhängigkeit) — alles eigene kleine Vorhaben, keine Fünf-Minuten-Fixe. - ✅ **Getestet:** 539 Tests grün (vorher 537, 2 neu für die Timeout-Unterscheidung beim YouTube-Download), `flutter analyze` ohne Befund. ### 🧑‍🤝‍🧑 UX-Simulation: Mehrfachauswahl, Baka-Login, Bestätigungen, Suche (2026-08-25) 7 Agenten sind die App wie ein normaler Nutzer durchgegangen (Erststart, Bibliothek, Download, Suche, Favoriten, Player, Einstellungen) und haben 39 konkrete Reibungspunkte gefunden — verglichen mit Apple Music/Spotify. Erster Block der Fixe: - 🖐️ **Mehrfachauswahl funktionierte nur im Lieder- und Favoriten-Reiter.** Öffnete man eine Kategorie oder das Album eines Künstlers, tat langes Drücken auf einen Titel gar nichts — derselbe Ripple-Effekt, aber ohne Wirkung. Die Auswahl-Leiste und die Sammel-Aktionen ("Zur Warteschlange", "Zu Wiedergabeliste") aus `SortableSongList` sind jetzt in `lib/shared/auswahl_leiste.dart` geteilt und laufen auch in der Album-/Kategorie-Detailansicht (`TitelListenScreen`, jetzt mit Sortierung und Shuffle wie der Lieder-Reiter) und in der Künstlerseite (`KuenstlerScreen`). - 🔁 **Zwei "Favoriten"-Listen mit unterschiedlichem Funktionsumfang.** Der Favoriten-Tab bot Sortieren/Shuffle/Auswahl, der über Playlisten erreichbare zweite Favoriten-Bildschirm zeigte dieselben Songs nur nackt. Beide zeigen jetzt dieselbe `SortableSongList`. - 🔐 **Baka-Login blieb bei falschem Passwort bis zu 15 Sekunden lang stumm.** Der Dialog schloss sich sofort beim Antippen von "Anmelden", noch bevor die Anfrage überhaupt losging; der Fehler kam erst danach in einer unauffälligen grauen Zeile. Jetzt bleibt der Dialog offen, zeigt einen Ladespinner und den Fehler direkt darin — wie beim (bereits guten) Navidrome-Login. - 🗑️ **Cache leeren und Navidrome-Abmelden liefen ohne Rückfrage.** Ein versehentlicher Tipp auf das kleine Papierkorb- bzw. Logout-Icon hat sofort gewirkt. Beide fragen jetzt nach, das einzelne Entfernen eines Downloads ebenso (analog zum bereits vorhandenen "Alle entfernen"-Dialog). - 📡 **Downloads bei toter Serververbindung meldeten "War schon heruntergeladen"** — dieselbe Meldung wie bei tatsächlich schon vorhandenen Titeln. Jetzt erscheint der wirkliche Grund ("Nicht mit dem Musikserver verbunden"). YouTube-Fehlermeldungen sind außerdem jetzt rot statt in derselben Neutralfarbe wie Erfolgsmeldungen. - 🔍 **Suche:** Die Tastatur blieb nach Auswahl eines Treffers offen und verdeckte die Liste — schließt jetzt automatisch. "Nichts gefunden" nennt jetzt den gesuchten Begriff. Einzelne Verlaufseinträge lassen sich entfernen, ohne den ganzen Suchverlauf zu löschen. - 🎤 **Zwei irreführende Icons:** "Musik-Berechtigung" in den Einstellungen zeigte ein Mikrofon (fragt aber Speicherzugriff ab, kein Mikrofon); "Musik erkennen" zeigte ein Fragezeichen-Symbol (Hilfe-Icon) statt eines erkennbaren Mikrofon-Symbols. - ✏️ Kleinere Textkorrekturen: der Hinweis "beim Baka-Konto anmelden" verwies auf einen nicht existierenden Tab "Online" (richtig: Download-Tab); die ReplayGain-Beschreibung nannte unerklärten Audio-Fachjargon. - ✅ **Getestet:** 537 Tests grün (vorher 533, 4 neu für `verlaufOhne`), `flutter analyze` ohne Befund. ### 🎨 Cover-Farbe, Teilen, Android Auto, geschärfte Typografie (2026-08-24) Die P3-Liste aus dem Review. - 🌈 **Der Player nimmt die Farbe des Covers auf.** Bisher war er für jeden Titel gleich schwarz; jetzt läuft ein Verlauf aus der prägenden Farbe des Albums nach unten in Schwarz aus. **Ohne neue Abhängigkeit:** die Farbwahl ist eine reine Funktion über die Bildpunkte und dadurch prüfbar. Nicht einfach die häufigste Farbe — Cover sind oft überwiegend dunkel oder grau, und dann sähe wieder alles gleich aus. Häufigkeit **und** Farbigkeit entscheiden zusammen. Anschließend wird so weit abgedunkelt, dass weiße Schrift darauf lesbar bleibt. - 📤 **„Teilen → Melo" aus der YouTube-App.** Vorher musste man die Adresse von Hand kopieren und einfügen. Die App springt in den Download-Bereich und trägt die Adresse ein — **ohne** den Download zu starten: Kategorie und Speicherort will man vorher noch wählen können. Die geteilte Nachricht enthält meist Titel und „über @YouTube" drumherum; daraus wird die Adresse herausgelöst. Bewusst nicht auf YouTube eingeschränkt — der Downloader läuft über yt-dlp und kann mehr, und was davon geht, entscheidet der Server. - 🚗 **Android Auto blättert jetzt wirklich durch die Bibliothek.** Der MediaBrowserService war im Manifest zwar angemeldet, lieferte aber nie Inhalte — die Bibliothek blieb dort leer. Vier Ordner wie in der App: Favoriten · Zuletzt hinzugefügt · Kategorien · Künstler. Ein Titel im Auto startet den **ganzen Ordner** ab dieser Stelle, nicht nur das eine Lied. Die Adressen halten Doppelpunkte und Schrägstriche aus — „Intro: Anfang" und „AC/DC" sind normale Namen. - 🔠 **Typografie geschärft, ohne neue Schrift.** Melo bleibt bei der System-Schrift: sie ist immer da, trägt jede Sprache und kostet nichts an App-Größe. Geschärft wurde vor allem die **Laufweite** — große Grade stehen enger, damit die Zeile als ein Wortbild liest; Abschnitts-Beschriftungen in Großbuchstaben dagegen weiter, weil die Luft brauchen. - 🐛 **Dabei gefunden:** Die drei Bildschirm-Überschriften („Download", „Favoriten", „Suchen") setzten ihre Größe **von Hand**, während das Theme eine eigene Stufe dafür vorhielt — die nie benutzt wurde. Eine Änderung am Theme wäre nirgends angekommen. Jetzt gehen alle drei durch eine gemeinsame Überschrift. - ↩️ **Und eine Rücknahme:** Ich hatte den Fließtext dichter gesetzt und damit eine Regel gebrochen, die seit Längerem als Test festgehalten ist („Fließtext hat mindestens 1,35 Zeilenhöhe"). Die Regel ist älter als diese Politur und hat einen Grund — zurückgenommen wurde deshalb mein Code, nicht der Test. - ✅ **Getestet:** 533 Tests grün (vorher 500), `flutter analyze` ohne Befund, Release-APK gebaut; Auto-Eintrag und Teilen-Filter im fertigen APK nachgeprüft. Neu: 9 Tests für die Cover-Farbe, 8 für das Herauslösen der geteilten Adresse, 9 für die Adressen des Auto-Baums und 7 für die Stufenleiter der Schrift. ### ✨ Politur: Wischen, wandernde Cover, Alphabet-Sprung, Mehrfachauswahl (2026-08-24) Die P2-Liste aus dem Review — lauter Kleinigkeiten, die man einzeln nicht vermisst und zusammen sofort merkt. - 🎞️ **Das Coverbild wandert.** Beim Öffnen der Vollbild-Wiedergabe springt kein neuer Bildschirm auf; das Bild aus der Leiste wächst an seinen Platz. - 👆 **Wischen am Mini-Player.** Nach oben öffnet die Wiedergabe, seitwärts wechselt den Titel. Mit Schwelle: der Mini-Player ist zugleich ein Knopf, und beim Antippen wandert der Finger fast immer ein paar Pixel — ohne Schwelle spränge bei jedem zweiten Tipp der Titel. Nach unten ist bewusst unbelegt, damit niemand die Wiedergabe aus Versehen wegschiebt. - 🎨 **Die Künstlerliste hat Bilder** und nennt die Zahl der Alben. Dahinter jetzt eine Künstlerseite mit seinen Alben, darunter alle Titel — vorher stand bei einem Künstler mit acht Alben alles ungeordnet untereinander. Bei nur einem Album entfällt die Zwischenstufe. - 🔤 **Alphabet-Sprung am rechten Rand.** Erscheint nur, wo er stimmt: bei Sortierung nach Namen und ab 30 Titeln. Bei „Zuletzt hinzugefügt" stünden die Buchstaben in willkürlicher Reihenfolge. Umlaute zählen zu ihrem Grundbuchstaben, Ziffern und Zeichen sammeln sich unter „#" — dieselbe Regel wie die Sortierung selbst. - ☑️ **Mehrfachauswahl.** Langes Drücken wählt aus, danach wählt jeder Tipp weitere Titel. Oben stehen „Zur Warteschlange" und „Zu Wiedergabeliste". Wer zwanzig Lieder in eine Liste wollte, tippte vorher sechzig Mal. - ♿ **Abgeschaltete Systemanimationen gelten jetzt überall.** Bisher hielt sich nur die Balken-Animation daran; Reiterwechsel, Songtext und Mini-Player zogen weiter durch. - 📱 **Querformat und Tablet.** Der Player legt Cover und Bedienung nebeneinander, statt vom Cover einen Streifen übrig zu lassen oder die Knöpfe aus dem Bild zu schieben. Auf breiten Schirmen bekommt der Text eine Obergrenze, statt über die ganze Breite zu zerren. - 🐛 **Beim Bauen gefunden:** Der Alphabet-Sprung landete bei absteigender Sortierung am **Ende** eines Buchstaben-Blocks statt an seinem Anfang. Der Index wird jetzt direkt auf der angezeigten Reihenfolge gebaut, mit Test für beide Richtungen. - 🔍 **Und ein hängender Test aufgeklärt.** Der neue Auswahl-Test kam nie zurück — dieselbe Signatur wie der seit dem 21.8. übersprungene Test im BACKLOG. Ursache: **Aufbau in `setUp`.** Das läuft außerhalb der Testzone, also liefen Datenbank und Dienste in echter Zeit, während der Test in Testzeit auf sie wartete. Aufbau in den Testkörper verschoben → fünf Tests in einer Sekunde. Der alte Fall im BACKLOG hat eine andere Ursache (nachgeprüft: `db.close()` ist es dort nicht), aber die Spur ist dort notiert. - 🔍 **Aus dem Code-Review nachgebessert — sieben Punkte, einer davon schwer:** **(1)** Die Alphabet-Leiste wurde **breiter, je weniger Buchstaben** sie hatte. Jeder Buchstabe skalierte auf die volle Höhe seines Platzes. Bei einem Album mit 40 Titeln „Track 01…40" gäbe es genau einen Buchstaben — die Leiste wäre 528 dp breit geworden und die Songliste vollständig verschwunden. Feste Breite jetzt, und ab drei verschiedenen Buchstaben überhaupt erst sichtbar. **(2)** Die neue Künstlerseite baute **alle** Titel sofort. Jede Zeile hört auf den Player und öffnet für ihr Herz eine eigene Abfrage — bei einem Künstler mit 300 Titeln wären das 300 Abos beim Öffnen. Vorher war die Liste faul; das war ein Rückschritt von mir. **(3)** Wer Systemanimationen abgeschaltet hat, hätte beim mitlaufenden Songtext einen Absturz bekommen: die Bildlauf-Animation verträgt keine Dauer null. Jetzt wird gesprungen — das ist ohnehin, was die Einstellung meint. **(4)** Die feste Zeilenhöhe war mit 64 zu klein (ein `ListTile` mit Untertitel ist 72). Sobald der Schnellzugriff aktiv wurde, schrumpften alle Zeilen sichtbar gegenüber jeder anderen Liste. Außerdem wird die Schriftskalierung jetzt an einer echten Schriftgröße gemessen statt an 1.0 — Android skaliert seit Version 14 nichtlinear. **(5)** Titel, die schon in der Zielliste waren, wurden **verschoben** statt übersprungen, und die Meldung zählte sie trotzdem mit. Jetzt: „7 hinzugefügt · 3 waren schon drin". **(6)** Ein Fehler mitten im Stapel brach still ab. Jetzt wird gesagt, wie weit es kam. **(7)** Die Zurück-Geste verließ den Bildschirm, statt die Auswahl zu beenden — zwanzig markierte Titel mit einer Geste weg, die überall sonst „einen Schritt zurück" heißt. - 🧹 **Selbst gefunden:** In der Künstlerliste hing der Datenbank-Strom an `CategoryService` und wurde bei **jeder** Kategorie-Meldung neu aufgesetzt — während eines Scans also laufend. Derselbe Fehler, den der Review mir eine Runde zuvor bei der Suche gezeigt hatte. - ✅ **Getestet:** 500 Tests grün (vorher 457), `flutter analyze` ohne Befund, Release-APK gebaut. Neu: 8 Tests für die Wisch-Schwellen, 10 für den Alphabet-Index, 5 für die Breite der Leiste (inklusive des Falls mit einem einzigen Buchstaben), 11 für die Auswahl-Regeln und Meldungen, 5 Widget-Tests für den Auswahl-Modus, 3 für die Album-Gruppierung und 3 für die Künstler-Zeile. ### 🧩 Die letzten vier Punkte aus dem Review (2026-08-23) **G2 — Server-Bereich.** Ein Tipp auf ein Album spielte sofort das ganze Album ab; hineinsehen ging nicht. - 💿 **Album- und Künstler-Ansicht.** Großes Cover, Künstler, Titelzahl, Gesamtdauer, „Abspielen" und „Zufällig" — darunter die Titelliste mit Nummer und Dauer. Antippen startet **an dieser Stelle**, nicht am Anfang. Der laufende Titel ist auch hier markiert, und schon geladene Titel tragen ein Häkchen. - 📄 **Mehr als 50 Alben.** Die Liste hörte bei 50 auf, ohne dass etwas darauf hinwies. Jetzt gibt es „Mehr Alben laden", und wenn der Server weniger als eine volle Seite liefert, verschwindet der Knopf von selbst. - 🚫 **Nicht umgesetzt:** „Server-Bereich aus dem Download-Tab lösen" stand auch im Befund — das widerspricht aber Commit `34d6d8e` („Auf Wunsch von Dustin"), mit dem der Tab bewusst wieder „Download" heißt. Die Tab-Struktur bleibt unangetastet. Ebenso weggelassen: eine Suche über den Server, weil die Suche ausdrücklich rein lokal bleiben soll. **G8 — Songtext läuft mit.** Bisher eine Textwand ohne Bezug zur Wiedergabe. - 🎤 Trägt der Text Zeitmarken (LRC), läuft er jetzt **mit**: die aktuelle Zeile ist hell und größer, der Rest gedämpft, und die Ansicht schiebt sie in die Mitte. **Antippen springt an die Stelle im Lied.** - 📃 Ein Text ohne Zeitmarken bleibt eine Textwand — beides kommt vor, je nach Tag und Server, und die App schaut nach, statt zu raten. **G10 — Cover bleiben jetzt da.** `Image.network` merkt sich Bilder nur im Arbeitsspeicher; nach jedem Neustart fragte jede Zeile ihr Cover erneut beim Server an, und ohne Netz gab es gar keins. - 🖼️ Cover landen in einem eigenen Ordner und werden nur einmal geholt. Der Schlüssel hängt an der Album-Kennung, **nicht** an der Adresse — die trägt bei jedem App-Start ein neues Token, und der Ordner wäre sonst gewachsen, ohne je einen Treffer zu liefern. - 🧹 Bewusst im Cache-Ordner: ein Cover ist ersetzbar. Räumt Android auf, lädt es sich beim nächsten Mal neu — anders als die Musik-Downloads. - ⚠️ Nur echte Bilder werden abgelegt: Subsonic meldet Fehler mit HTTP 200 und JSON, und das läge sonst dauerhaft als „Cover" im Ordner. **Aus dem Code-Review nachgebessert — sieben Punkte:** - 🤍 **Das Herz behielt beim Titelwechsel seinen Zustand.** Einen Server-Titel favorisieren, der Player geht zum nächsten — und der zeigte sich als Favorit, obwohl er nie einer war. Der nächste Tipp hätte ihn dann „entfavorisiert". - 🖼️ **Beim Scrollen zeigten Zeilen kurz das Cover der vorherigen.** Listen verwenden ihre Zeilen wieder; ohne Schlüssel behielt die Bildabfrage das alte Ergebnis, bis das neue da war — bei ungecachten Covern sekundenlang. Ein Rückschritt gegenüber dem vorherigen Stand, deshalb hier mitbehoben. - 🎤 **Der neue mitlaufende Songtext funktionierte für Server-Titel gar nicht.** Ihm wurde die Abspiel-Adresse statt der Server-Kennung übergeben; die App fragte den Server nach einem Titel namens „https://…" und bekam nie einen Text. - 🎨 **Ein Cover, das einmal nicht lud, blieb die ganze Sitzung grau.** Startet die App ohne Netz, waren alle Cover bis zum Neustart weg — auch wenn die Verbindung längst wieder stand. Fehlschläge werden nicht mehr gemerkt. - 🚨 **„Server nicht erreichbar" sah aus wie „Album hat keine Titel".** Beide Abfragen verschluckten ihre Fehler und lieferten eine leere Liste; der Wiederholen-Knopf, den ich gebaut hatte, war damit unerreichbar. - 🎸 **Die Künstler-Ansicht wäre immer leer geblieben.** Die App las die Titel aus `getArtist`, wo die Subsonic-Schnittstelle sie gar nicht mitliefert — Alben kommen dort ohne ihre Titel. Jetzt wird je Album nachgefragt. (Vorbestehend, aber ich habe einen ganzen Bildschirm darauf gebaut.) - 🧩 Dazu ein seltener, aber dauerhafter Fall: zwei gleichzeitige Abrufe desselben Covers schrieben in dieselbe Zwischendatei und konnten ein zerschnittenes Bild hinterlassen, das nie wieder verschwindet. - 🧹 **Selbst gefunden:** Das Häkchen „schon geladen" fragte pro Zeile die Platte ab — und die Zeile zeichnet sich bei jedem Positionswechsel neu. Das wäre eine Dateiprüfung pro Sekunde und Titel gewesen. Jetzt einmal je Bildschirm aus der Datenbank. - ✅ **Getestet:** 457 Tests grün (vorher 433), `flutter analyze` ohne Befund, Release-APK gebaut. Neu: 16 Tests für den LRC-Parser, 5 für den Cover-Schlüssel und 3 für die Album-Daten. **G3 — Server-Titel nicht länger zweiter Klasse.** - 🤍 Das Herz ist bei Server-Titeln nicht mehr ausgegraut, sondern favorisiert **am Server** (`star`/`unstar`) — genau dort, wo der Favoriten-Abgleich in den Einstellungen sie auch wieder abholt. Ein Titel der Bibliothek wird weiterhin lokal favorisiert. - ℹ️ Weiterhin offen: Wiedergabezähler, Historie und Kategorien gibt es für reine Server-Titel nicht — dafür bräuchten sie eine Zeile in der Bibliothek, und das wäre ein eigener Umbau. ### 📥 Alben und Künstler offline mitnehmen — und der Zwischenspeicher hat endlich eine Grenze (2026-08-23) Bisher landete Musik nur zufällig auf dem Gerät: was man abspielte, blieb nebenbei liegen. Gezielt „dieses Album für die Zugfahrt" gab es nicht. Und dieser Zwischenspeicher wuchs **unbegrenzt** — es gab nur „alles löschen". - 📥 **Download-Pfeil neben jedem Album und Künstler** im Bereich Download → Server. Während es läuft, steht oben ein Fortschritt mit Abbruch-Knopf. - ⚠️ **Rückfrage bei großen Mengen.** Ein Album geht ohne Nachfrage durch; ab 30 Titeln fragt die App erst („347 Titel, geschätzt etwa 2100 MB"). Ein ganzer Künstler soll niemandem aus Versehen das Datenvolumen kosten. - 🗂️ **Zwei getrennte Töpfe.** Bewusste Downloads liegen **nicht** im Cache-Ordner. Der Grund ist keine Geschmacksfrage: den Cache-Ordner darf Android bei Speichernot jederzeit selbst leeren — genau das darf einem Album, das man mitgenommen hat, nicht passieren. Downloads liegen deshalb neben der Datenbank, wo das System nichts anrührt. - 🚧 **Obergrenze für den Zwischenspeicher**, voreingestellt 2 GB, in den Einstellungen wählbar (aus / 512 MB / 1 / 2 / 4 / 8 GB). Ist sie erreicht, fliegt das am längsten nicht Gehörte zuerst raus. **Heruntergeladene Titel sind davon nie betroffen** — wer ein Album mitnimmt, hat das entschieden, und die App nimmt es ihm nicht wieder weg. - 📋 **Liste „Heruntergeladen"** in den Einstellungen: Titel, Künstler, Größe, einzeln oder alle entfernbar. Sie kommt aus der Datenbank und nicht aus dem Ordner — die Dateien heißen nach einem Hash und stünden dort sonst als „a3f9…mp3". - ▶️ **Die Wiedergabe schaut erst in die Downloads, dann in den Zwischenspeicher.** Ein mitgenommener Titel hängt nie am Netz, auch wenn zufällig eine Cache-Kopie existiert, die morgen verdrängt sein kann. - 🧹 **Nebenbei aufgeräumt** (die Dateien waren ohnehin offen): die Emoji-Knöpfe „📀 Alben" und „🎤 Künstler" heißen jetzt schlicht „Alben" und „Künstler", das grelle `Colors.redAccent` ist überall dem Melo-Rot gewichen, und die Einstellungen heißen nicht mehr „Settings". - 🔍 **Aus dem Code-Review nachgebessert — zehn Punkte, vier davon ernst:** **(1)** „Aus" bewirkte das Gegenteil. Bei 0 MB übersprang die App das Aufräumen ganz, legte aber weiter jeden Titel ab — genau der unbegrenzt wachsende Speicher, den diese Änderung beheben soll. „Aus" heißt jetzt wirklich: nichts behalten. **(2)** Wer sich erst **in** der Sitzung am Musikserver anmeldete, konnte nichts herunterladen. Der Dienst hielt eine eigene Verbindung, die nur beim App-Start eingerichtet wurde; die Titelliste kam, der Download lief ins Leere. Erst ein Neustart half. **(3)** Die Aufräum-Aktion konnte Dateien löschen, die die **laufende** Warteschlange schon als Quelle eingetragen hatte — die Wiedergabe wäre mitten im Album abgebrochen, ohne auf Streamen zurückzufallen. Benutzte Dateien sind jetzt geschützt; im Zweifel bleibt die App lieber kurz über der Grenze, als die Musik abzuwürgen. **(4)** Ein bewusst heruntergeladener Titel wurde beim Abspielen **noch einmal** in den Zwischenspeicher geladen — doppelter Platz, doppeltes Datenvolumen. Dazu sechs kleinere: ein einzelner Fehlschlag verdeckte nicht mehr den Erfolg der übrigen Titel („18 heruntergeladen · 2 fehlgeschlagen"); eine Datei ohne Buchführungs-Zeile (App mittendrin beendet) wird jetzt nachgetragen statt für immer unsichtbar zu bleiben; zweimal antippen meldet nicht mehr fälschlich Erfolg; „Lädt 21 von 20" gibt es nicht mehr; eine gesenkte Grenze wirkt sofort statt erst beim nächsten gestreamten Titel; und drei ungenutzte Methoden sind raus. - 🕐 **Auch aus dem Review:** Der „zuletzt gehört"-Stempel wurde beim **Aufbau** der Warteschlange gesetzt — damit hatten nach einem Albumstart fünfzig Dateien fast denselben Zeitstempel und die Verdrängung wusste nicht mehr, was wirklich gehört wurde. Gestempelt wird jetzt, wenn ein Titel tatsächlich losspielt. - ✅ **Getestet:** 433 Tests grün (vorher 411), `flutter analyze` ohne Befund, Release-APK gebaut. Neu: 13 Tests für die Verdrängungs-Regel (welche Datei weicht wann, bei gleichem Zeitstempel nie zufällig, und benutzte Dateien nie) und 9 für Rückfrage-Schwelle, Größenschätzung und Dateinamen. ### ▶️ Man sieht jetzt, welcher Titel gerade läuft (2026-08-23) Bisher scrollte man durch die Bibliothek und musste raten, wo man gerade ist — die einzige Rückmeldung war die Leiste ganz unten. - 🎚️ **Drei tanzende Balken über dem Coverbild** des laufenden Titels, sein Name in Rot. Die Balken liegen **auf** dem Cover statt daneben: eine vierte Spalte hätte dem Titel Platz genommen, und die Zeile wäre bei jedem Titelwechsel in der Breite gesprungen. - ⏸️ **Pausiert bleibt die Markierung stehen, die Balken stehen still.** Man sieht weiter, wo man ist, und zugleich, dass gerade nichts vorangeht. - 🌐 **Überall zugleich.** Die Markierung sitzt in der Songzeile selbst, also gilt sie in „Meine Musik“, Favoriten, Suche, hinter einem Künstler, hinter einer Kategorie, in einer Wiedergabeliste und bei „Zuletzt hinzugefügt“ — ohne dass irgendeine dieser Ansichten angefasst werden musste. Die Warteschlange bekommt dasselbe Zeichen, damit man dieselbe Stelle wiedererkennt. - ♿ **Wer Systemanimationen abgeschaltet hat, bekommt stehende Balken.** Eine Endlosschleife ist genau das, was diese Einstellung vermeiden soll. - 🐛 **Beim Testen gefunden:** Die Balken lasen die Animations-Einstellung im falschen Moment (`initState`), was Flutter mit einem Fehler quittiert. Der Test, der prüft, dass nichts endlos läuft, hat es aufgedeckt. - ⚡ **Und beim Nachsehen noch etwas:** Zuerst hing *jede* sichtbare Zeile am Wiedergabe-Zustand — der meldet aber auch den Pufferstand und feuert mehrmals pro Sekunde. Die halbe Liste hätte sich also ständig neu aufgebaut. Jetzt hört nur die eine laufende Zeile darauf; alle anderen bekommen nur mit, wenn der Titel wechselt. - 🔍 **Aus dem Code-Review nachgebessert:** **(1)** Die Markierung erschien einen Frame zu spät. Scrollte man die laufende Zeile aus dem Blick und zurück, ploppten Balken und rote Schrift sichtbar nach — der Strom liefert seinen Wert erst im nächsten Frame. Jetzt mit Startwert. **(2)** Beim Wechsel auf „markiert" änderte die Zeile die Form ihres Aufbaus, und Flutter warf sie deshalb weg und baute sie neu. Folge: das Herz daneben verlor sein Abo und sah einen Moment lang leer aus — genau bei dem Titel, den man gerade gestartet hat. Der Aufbau bleibt jetzt gleich, nur der Strom wird an- und abgehängt. **(3)** In der **Wiedergabeliste** fehlte die Markierung ganz. Der Changelog-Eintrag oben behauptete zuerst das Gegenteil — diese Ansicht baut ihre Zeilen selbst (rechts steht „Aus Playlist entfernen" statt Herz und Menü) und lief deshalb nicht über die gemeinsame Songzeile. Sie hat die Markierung jetzt wirklich. Dazu eine Kleinigkeit: der Abdunkel-Schleier hatte seinen Eckenradius fest verdrahtet und hätte bei einem anderen Cover-Radius nicht mehr gepasst. - 🧪 **Testlücke geschlossen, die der Review benannt hat.** Geprüft waren nur die Regel und die Balken für sich — die Verdrahtung dazwischen nicht. Genau dort saßen alle drei Fehler oben. Es gibt jetzt fünf Tests dafür, unter anderem einen, der nachweist, dass die Zeile beim Wechsel **dasselbe** Element bleibt. - ✅ **Getestet:** 411 Tests grün (vorher 396), `flutter analyze` ohne Befund, Release-APK gebaut. Neu: 5 Tests für die Regel, wann eine Zeile markiert wird (inklusive: ein Server-Titel markiert nie eine Bibliothekszeile), 5 Widget-Tests für die Balken und 5 für die Verdrahtung. ### 🔎 Die Suche trennt jetzt nach Titel, Künstler, Kategorien und Listen (2026-08-23) Der Suchen-Tab war bisher **59 Zeilen**: ein Textfeld über einer flachen Liederliste. Wer „Nightcore“ eingab, bekam alle Titel dieses Künstlers untereinander — den Künstler selbst, seine Kategorie oder eine gleichnamige Wiedergabeliste fand er nicht. - 🗂️ **Vier Abschnitte statt einer Liste.** Titel · Künstler · Kategorien · Wiedergabelisten, jeder mit Überschrift und Trefferzahl. Alben stecken in „Kategorien“ — der Album-Name *ist* in Melo die erste Kategorie eines Titels, ein eigener Alben-Abschnitt wäre dieselbe Liste zweimal. - ⬆️ **Treffer am Wortanfang stehen oben.** Wer „roses“ tippt, meint eher „Roses“ als „Wild Roses“. Innerhalb dieser beiden Ränge alphabetisch. - ⏱️ **Entprellt (250 ms).** Vorher baute jeder einzelne Tastendruck die komplette Trefferliste neu auf. - 🕘 **Letzte acht Suchen.** Sie stehen da, solange das Feld leer ist; Antippen übernimmt den Begriff. Gemerkt wird ein Begriff erst beim Abschicken — halbe Wörter aus dem Tippen sind keine Suche, die man wiederholen möchte. - 🧹 **Der Tab sieht aus wie die anderen.** Er war ein `Scaffold` mit eigener `AppBar` *innerhalb* eines Tabs, während „Meine Musik“ und „Favoriten“ eine Kopfzeile im Inhalt haben. Jetzt einheitlich, mit demselben runden Suchfeld wie auf der Startseite. - ♻️ **Aufgeräumt, weil es sonst eine dritte Kopie gegeben hätte:** Die Ansicht „Überschrift + Liederliste“ stand zweimal wortgleich im Baum (hinter einem Künstler und hinter einer Kategorie). Sie ist jetzt eine Klasse, die auch die Suche benutzt. Ebenso ist die einzelne Songzeile aus der Liederliste herausgelöst — die Suche setzt einzelne Zeilen in ihre Abschnitte, eine ganze Liste wäre dort eine verschachtelte Scrollfläche. - 🐛 **Beim Bauen selbst gefunden und behoben:** Der Verlauf wurde zuerst beim Aufbau der Trefferliste geschrieben. Das hätte eine Endlosschleife ergeben — schreiben löst ein Neuzeichnen aus, das wieder schreibt. - 📴 **Kein Server-Teil.** Bewusst so entschieden: die Suche läuft rein lokal, damit sie sofort antwortet und ohne Netz funktioniert. - 🔍 **Aus dem Code-Review nachgebessert (die wichtigsten drei):** **(1)** Die Suche fasste „Neoni", „NEONI" und „Neoni " zu einem Treffer zusammen — die Titel dazu wurden aber unter der rohen Schreibweise gesucht. Ergebnis: „0 Titel" und ein leerer Bildschirm beim Antippen. Beide Seiten benutzen jetzt dieselbe Normalform, und verschiedene Schreibweisen desselben Künstlers fallen dabei zusammen statt sich gegenseitig zu verstecken. Dasselbe galt für Kategorien. **(2)** Eine Kategorie, an der nur noch gelöschte Titel hängen, erschien als Treffer und führte ins Leere. Solche Treffer fallen jetzt weg. **(3)** Alle Abschnitte sind auf fünf Einträge gedeckelt, nicht nur die Titel. Eine Eingabe wie „a" hätte sonst hunderte Künstlerzeilen bei jedem Neuzeichnen gebaut. Die Gesamtzahl steht ohnehin in der Überschrift. Dazu kleinere Sachen: die beiden Datenbank-Ströme werden nicht mehr bei jedem Neuzeichnen neu abonniert, der Fehlerfall der Wiedergabelisten wird behandelt, und das Löschkreuz im Suchfeld erscheint sofort statt nach 250 ms — vorher sprang das Textfeld dabei um eine Knopfbreite enger. - 🗑️ **Verlauf lässt sich löschen.** Ein „Löschen" neben der Überschrift. (Ohne das hätte es keine Möglichkeit gegeben, gespeicherte Suchbegriffe wieder loszuwerden.) - ♻️ **Tot geworden und entfernt:** `MeloDb.searchSongs` samt Test — die alte LIKE-Abfrage hat nach dem Umbau keinen Aufrufer mehr. - ✅ **Getestet:** 396 Tests grün (vorher 360), `flutter analyze` ohne Befund, Release-APK gebaut. Neu: 25 Tests für die Suchregeln und die Bündelung nach Schreibweise, 7 für den Verlauf, 7 Widget-Tests für die Abschnitte (inklusive einem, der prüft, dass wirklich entprellt wird). ### 🔁 Positions-Timer überlebt jetzt das Wegwischen (2026-08-23) - ⏱️ **Behoben: nach dem Wegwischen der Benachrichtigung merkte sich die App keine Hörposition mehr.** `stop()` brach dabei den Timer ab, der alle fünf Sekunden die Wiedergabeposition sichert — und der entsteht nur **einmal** beim App-Start. Einmal abgebrochen, kam er nie zurück: „Fortsetzen an alter Stelle" hörte still auf zu funktionieren, und zum Server wurde nicht mehr gemeldet, was gehört wurde. Erst ein Neustart half. Der Timer läuft jetzt durch; er kostet nichts, weil er von selbst aussteigt, solange nichts spielt. Abgebrochen wird er nur noch beim endgültigen Aufräumen. - 📄 **Korrektur am eigenen Review-Bericht (K4).** Dort stand, die Alben-Ansicht sei „verlorengegangen". Das war falsch: der Alben-Reiter wurde **bewusst** durch „Kategorie" ersetzt — der Album-Name eines Liedes *ist* in Melo seine erste Kategorie (`category_list.dart:12`, `kategorienAusTags`). Was wirklich fehlt, ist die Album-**Detailansicht** (großes Cover, Künstler, Gesamtdauer) und die Sortierung nach Tracknummer — letztere braucht zuerst eine Schema-Erweiterung, weil die Tracknummer bisher überhaupt nicht eingelesen wird. Im Bericht ist der Punkt richtiggestellt. - ✅ **Getestet:** 360 Tests grün, `flutter analyze` ohne Befund. ### 🚑 Fünf P0-Fehler behoben: Benachrichtigung, Offline, Cover, Warteschlange (2026-08-23) Ergebnis einer vollständigen Durchsicht der App gegen den Maßstab „Apple Music". Fünf Punkte waren keine fehlenden Funktionen, sondern **Versprechen, die die App gab und der Code nicht hielt**. - 🔔 **Die Wiedergabe-Benachrichtigung erschien auf Android 13+ gar nicht.** Dafür braucht das System die Berechtigung `POST_NOTIFICATIONS` — sie fehlte im Manifest, auch im fertig gebauten APK. Die Musik lief, aber Notification und die Bedienknöpfe darin blieben unsichtbar; steuern ließ sich nur in der geöffneten App. Die Berechtigung ist jetzt drin und wird **erst dann** erfragt, wenn wirklich ein Titel läuft — beim kalten Start käme die Frage ohne Zusammenhang, und zweimal weggetippt sperrt Android sie dauerhaft. - 📴 **Der Offline-Schalter tat nichts.** Er speicherte brav ein Ja/Nein, aber **niemand las es**: die App streamte weiter und verbrauchte Mobilfunkdaten, obwohl „Offline-Modus" anstand. Jetzt greift er an allen drei Wegen in die Warteschlange — Abspielen, „Als Nächstes", „Zur Warteschlange". Titel vom Gerät laufen immer, Titel vom Server nur, wenn sie schon hier liegen. Was übersprungen wird, sagt die App: *„Offline-Modus: 3 Titel übersprungen — nicht heruntergeladen"*, statt still eine kürzere Liste zu spielen. - 🖼️ **Titel vom Server hatten nie ein Coverbild.** Die App konnte Cover nur aus Dateien laden; das Bild vom Navidrome-Server liegt aber hinter einer Web-Adresse. Jeder Server-Titel war deshalb ein graues Notensymbol — in der Liste, im Player, auf dem Sperrbildschirm. Jetzt lädt die App auch Web-Cover, und der Server-Browser gibt die Adresse überhaupt erst mit (vorher wurde sie an beiden Stellen schlicht vergessen). - ⏭️ **Warteschlange umsortieren riss die Wiedergabe auf.** Jedes Entfernen und jedes Verschieben baute die *ganze* Warteschlange neu auf: hörbarer Aussetzer, Sprung an den Titelanfang — und der laufende Titel wurde dabei **jedes Mal erneut als Wiedergabe gezählt**. Die Sortierung „Wie oft abgespielt" zählte damit Queue-Bedienung statt Hören. Jetzt wird nur der eine betroffene Eintrag bewegt; die Wiedergabe merkt davon nichts. - 🤍 **Das Herz bei Server-Titeln log.** Ein Titel, der nur am Server liegt, hat keinen Eintrag in der Bibliothek — das Herz bekam deshalb eine leere Kennung, füllte sich beim Antippen und legte einen Favoriten an, der in der Favoritenliste nie auftauchte. Es ist jetzt sichtbar ausgegraut, mit dem Hinweis „Nur für Titel in deiner Bibliothek". - ⚡ **Nebenwirkung der Offline-Umstellung, aber eine gute:** „Als Nächstes" und „Zur Warteschlange" sahen bisher gar nicht im Zwischenspeicher nach und streamten einen Server-Titel auch dann, wenn er längst auf dem Gerät lag. Alle drei Wege in die Warteschlange bauen die Abspielquelle jetzt an derselben Stelle — ein bereits geladener Titel läuft immer von der Platte. - 🧹 **Nebenbei:** „📴 Offline-Modus" heißt jetzt ohne Emoji „Offline-Modus" (Emoji sehen auf jedem Hersteller-Handy anders aus und lassen sich nicht einfärben), und der Untertitel sagt, was der Schalter wirklich tut. - 🔍 **Aus dem Code-Review nachgebessert (drei Punkte):** **(1)** Die Offline-Meldung hing an der falschen Stelle. Sie stand in der Bibliotheksliste — die zeigt aber nur Titel vom Gerät, dort wird nie etwas übersprungen. Beim Server-Album, wo es wirklich passiert, wurde sie verworfen. Jetzt sitzt sie dort, wo sie greifen kann; und „nichts davon ist heruntergeladen" erscheint nicht mehr als *„Fehler: …"* — das ist kein Fehler, sondern die eingestellte Wahl. **(2)** Beim Umsortieren wurde die Warteschlange erst *nach* dem Player nachgeführt. In der Lücke dazwischen konnte der Player schon einen neuen Titelindex melden, während die App noch die alte Liste kannte — falscher Titel im Player, Wiedergabe für das falsche Lied gezählt. Buchführung steht jetzt vor dem Player, genau wie beim normalen Abspielen. **(3)** Die Abfrage der Benachrichtigungs-Berechtigung hatte keinen Fehlerpfad. Läuft beim Start noch die Abfrage der Musik-Berechtigung, wirft Android — das wäre als unbehandelter Fehler durchgeschlagen. Jetzt wird es nur protokolliert; die Wiedergabe läuft weiter, nur eben ohne Benachrichtigung. - ✅ **Getestet:** 360 Tests grün (vorher 328), `flutter analyze` ohne Befund, Release-APK gebaut. Im fertigen APK ist `POST_NOTIFICATIONS` nachgeprüft enthalten. Neu: 39 Tests für Offline-Filter, Warteschlangen-Mutation, Zähler-Nachführung, Server-Cover und das ausgegraute Herz. Entfallen: 9 Tests für `newPlayingIndex` — die Funktion gab es nur, um nach dem Neuaufbau der Warteschlange den laufenden Titel wiederzufinden, und den Neuaufbau gibt es nicht mehr. - 📋 **Nicht in dieser Runde:** Alben-Ansicht, Suche über den Server, gezieltes Herunterladen und die Cache-Obergrenze. Die stehen als P1 im Review-Bericht unter `.ui-design/reviews/melo_vollwertigkeit_20260823.md`. Dazu zwei Punkte, die erst bei dieser Arbeit auffielen und dort als **G9** und **G10** stehen: `stop()` bricht den Positions-Timer dauerhaft ab (nach dem Wegwischen der Benachrichtigung merkt sich die App keine Hörposition mehr — vorbestehend), und die Server-Cover haben nur einen Zwischenspeicher im Arbeitsspeicher, keinen auf der Platte. ### 🏷️ Der zweite Tab heißt wieder „Download" (2026-08-21) - 📥 **Aus „Online" wird „Download".** Der Bereich behält seinen Inhalt unverändert — YouTube-Downloader und Server-Bibliothek als Unterreiter. Nur die Beschriftung im Hauptmenü ändert sich. Die vier Bereiche heißen jetzt: **Meine Musik · Download · Suchen · Favoriten**. - ⬇️ **Passendes Symbol dazu.** Statt der Note steht wieder ein Download-Pfeil im Menü — genau das Symbol, das der Tab schon einmal hatte, bevor er in „Online" umbenannt wurde. Also nichts Neues erfunden, sondern der frühere Stand wiederhergestellt. - 📄 **Auch die Überschrift im Bereich selbst zieht nach.** Sie stand noch auf „Online" — ein Tab namens „Download", der auf einen Bildschirm namens „Online" führt, wäre verwirrend gewesen. - ✅ **Getestet:** 328 Tests grün, `flutter analyze` ohne Befund. ### 🧭 Hauptmenü wieder deutlich sichtbar (2026-08-21) **Zuerst die Entwarnung:** Es fehlt nichts und nichts ist verschoben. Alle vier Bereiche standen und stehen unten im Hauptmenü — `lib/main.dart` wurde von der UI-Politur überhaupt nicht angefasst. - 🐛 **Behoben: die Politur hatte das Menü abgeschwächt.** Zwei Fehler von mir aus der letzten Runde: - Ich hatte für **aktiv und inaktiv dieselbe Schriftgröße** (12) gesetzt. Damit verlor der aktive Bereich den Größenunterschied (sonst 14 zu 12), an dem man auf einen Blick erkennt, wo man ist. - Die inaktiven Einträge liefen auf der **blassen Beiwerk-Stufe**. Ein Hauptmenü ist kein Beiwerk — sie sind jetzt wieder kräftig. - ✨ **Zusätzlich deutlicher gemacht:** - Eine **Haarlinie über dem Menü** — auf schwarzem Grund ging die Leiste sonst optisch im Inhalt auf. - Der aktive Bereich hat jetzt ein **gefülltes Symbol** (ausgefülltes Herz, volle Note). Wer Rot schlecht sieht, erkennt trotzdem, wo er steht — vorher unterschied nur die Farbe. - Der aktive Eintrag ist fetter gesetzt. - 🔒 **Damit das keine Politur mehr abschwächen kann:** ein neuer Test prüft, dass alle vier Bereiche vorhanden **und auf dem Bildschirm** sind, dass das Menü am unteren Rand klebt, dass jeder Tipp dorthin wechselt, und dass der aktive Bereich sich nicht nur durch Farbe abhebt. - 🔧 **Details für Technikinteressierte:** - Neu: `test/hauptmenue_test.dart` (10 Prüfungen). - `bottomNavigationBarTheme`: keine feste `fontSize` mehr (damit Flutters 14/12 gelten), `unselectedItemColor` von `text3` auf `text2`, `showUnselectedLabels` ausdrücklich gesetzt. - Beim Schreiben des Tests ist eine **Test-Falle** aufgefallen und gelöst: `await db.close()` nach einem `tester.tap` lässt den Testlauf **unbegrenzt hängen**. Rezept dagegen steht im Test und in `BACKLOG.md`. Beim schon bekannten Hänger in `song_detail_sheet_test.dart` hilft es allerdings nicht — dort ist die Ursache eine andere, jetzt ebenfalls im Backlog vermerkt. - ✅ **Getestet:** 328 Tests grün (10 neue), `flutter analyze` ohne Befund. ### ✨ UI-Politur: Kontrast, Abstände, Typografie, Bewegung (2026-08-21) Kein neues Design — dasselbe Melo in sauber. Schwarz/Rot, die vier Tabs und Lieder/Kategorie bleiben unverändert. - 🔍 **Behoben: blasse Schrift war nicht mehr lesbar.** An 17 Stellen stand Text in `white38`. Nachgerechnet sind das **3,4:1** Kontrast — die Barrierefreiheits-Grenze liegt bei **4,5:1**. Betroffen waren unter anderem Metadaten, Equalizer und Wiedergabelisten. Alle Textfarben laufen jetzt über drei benannte Stufen, und ein Test **rechnet die Kontraste bei jedem Lauf nach** (WCAG 2.1) — behauptet wird nichts mehr. | Stufe | Wofür | Kontrast | |---|---|---| | `text1` | Titel | 15,9:1 | | `text2` | Künstler, Untertitel | 8,8:1 | | `text3` | Anzahl, Zeit, Hinweis | 4,9:1 | - 👆 **Behoben: die Reiter waren zu klein zum Treffen.** *Lieder · Kategorie · Künstler* waren nur ~38 dp hoch — Android verlangt **48 dp**, sonst tippt man im Gehen daneben. Jetzt volle 48 dp, mit spürbarer Rückmeldung beim Antippen (vorher gab es keine) und weichem Übergang statt hartem Sprung. Vorlesehilfen bekommen die Auswahl jetzt als Zustand gemeldet, nicht nur als weiße Farbe. - 📏 **Abstände nach festem Raster.** Vorher standen überall Einzelwerte (12, 9, 10, 18 …). Jetzt fünf benannte Stufen auf einem 8er-Raster — deshalb wirken Listen und Karten ruhiger, ohne dass etwas verschoben wurde. - 🔤 **Eine Schriftgrößen-Skala statt 52 Einzelangaben.** Sieben Stufen von der Bildschirm-Überschrift bis zum Beiwerk, mit passender Zeilenhöhe. Kein Text ist kleiner als 12. - 🎛️ **Mini-Player überarbeitet.** Der Abspiel-Knopf ist jetzt rot gefüllt und volle 48 dp groß — der wichtigste Knopf der Leiste lässt sich ohne Hinsehen treffen. Die Leiste blendet sich ein und aus, statt aufzupoppen, und ist durch eine Haarlinie vom Inhalt getrennt. 🐛 Nebenbei ein Layout-Fehler entschärft: die Zeile lag in einem `Expanded` **um** eine feste Höhe herum — zwei widersprüchliche Angaben. - 🖼️ **Karten und Cover bekommen einen Umriss** statt eines Schattens. Auf Schwarz ist ein Schatten unsichtbar; eine Haarlinie trennt sauber. - 🚫 **Emoji raus aus Bedienelementen.** In den Einstellungen stand „🌐 Navidrome", „🌟 Favoriten-Sync" — jeweils direkt neben einem echten Symbol. Emoji werden von Vorlesehilfen vorgelesen („Globus mit Meridianen") und sehen auf jedem Gerät anders aus. Die Symbole bleiben, die Emoji sind weg. - 🔧 **Details für Technikinteressierte:** - Neu in `shared/theme.dart`: `MeloSpace`, `MeloRadius`, `MeloMotion`, Textstufen `text1/2/3`, `border`, `hairline`, `minTouchTarget` sowie ein vollständiges `textTheme` und Themes für Listen, Sheets, Snackbars, Fortschritt und Trenner. - Neu: `test/shared/theme_kontrast_test.dart` (rechnet WCAG-Kontraste, Raster und Typo-Skala nach) und `test/shared/sub_tabs_test.dart` (48 dp, Rückmeldung, Semantik, Übergang). - Grundlage: Regeln aus **ui-ux-pro-max** (Priorität 1 Barrierefreiheit, 2 Touch, 6 Typografie/Farbe, 7 Bewegung), **ui-design/mobile-android** (Material 3: 48 dp, Theme über `context`) und die Token-Disziplin aus **Hue** (Primitives → benannte Rollen). - ✅ **Getestet:** 318 Tests grün (23 neue), `flutter analyze` ohne Befund. ### 📋 Backlog-Datei angelegt (2026-08-21) - Neu: `BACKLOG.md` — bewusst zurückgestellte Punkte an einer Stelle, damit nichts stillschweigend liegen bleibt. Drin stehen aktuell der übersprungene Widget-Test `song_detail_sheet_test.dart` (mit dem Stand der Eingrenzung und dem nächsten Schritt) und die fehlende Wiederherstellung gelöschter Titel. ### 🗂️ Alben sind jetzt Kategorien — neue Bibliotheks-Reiter (2026-08-21) - 🏷️ **Album = Kategorie.** Wer seine Sammlung über das **Album-Feld** sortiert hat, findet sie ab sofort genau so unter *Kategorie* wieder — der Album-Titel eines Liedes **ist** in Melo seine Kategorie. Nichts muss neu gemacht werden. 🔧 Der Album-Titel steht dabei **vorn** in der Kategorienliste, weil die erste Kategorie das Coverbild bestimmt (Einstellung „Gleiche Kategorie = gleiches Coverbild"). Genres aus den Datei-Tags kommen weiterhin dazu, aber dahinter. 🔁 **Bestehende Bibliotheken werden beim ersten Start automatisch nachgerüstet** — du musst nicht erst neu scannen. Von Hand gepflegte Kategorien bleiben dabei unangetastet. - 📑 **Neue Reiter in „Meine Musik": Lieder · Kategorie · Künstler.** Der Alben-Reiter ist verschwunden — er ist im Kategorie-Reiter aufgegangen. „Songs" heißt jetzt „Lieder". Der Kategorie-Reiter zeigt jede Kategorie mit Coverbild und Anzahl; Lieder ganz ohne Kategorie sammeln sich am Ende unter *Ohne Kategorie*. - ⬇️ **YouTube-Downloads: Kategorie beim Herunterladen wählen.** Im YouTube-Bereich gibt es ein Feld **Kategorie (optional)** — es schlägt die Kategorien deiner Bibliothek vor, nimmt aber auch eine neue entgegen. Leer lassen = der Titel kommt ohne Kategorie in die Bibliothek. 🐛 **Warum das nötig ist:** YouTube-Dateien bringen ein Album-Tag mit, das yt-dlp aus Kanal, Playlist oder Videotitel ableitet — Dinge wie „Topic - Various Artists". Da das Album jetzt die Kategorie ist, hätte **jeder Download eine neue Unsinns-Kategorie** erzeugt. Melo **verwirft** dieses Album-Tag deshalb bewusst und nimmt stattdessen deine Angabe. Beides wird als „von Hand gesetzt" markiert, damit der nächste Scan es nicht wieder hereinholt. - 🔧 **Details für Technikinteressierte:** - Neu: `kategorienAusTags()` und `groupByCategory()` in `library/categories.dart`, `library/category_list.dart`, `downloads/download_einordnung.dart` (`ordneDownloadEin`, `kategorieVorschlaege`). - Beide Scans (Gerät + Ordner) tragen den Album-Titel als erste Kategorie ein; `MeloDb.songByPath()` und `MeloDb.verwirfAlbum()` neu. - Datenbank auf Version 9: einmalige Nachrüstung der Album-Kategorien. - `CategoryService.alleNamen` liefert die Kategorienamen jetzt ohne zusätzliche Abfrage — die Vorschlagsliste flackert dadurch nicht. - Entfernt: `library/album_list.dart` sowie `groupByAlbum()` / `albumArtistLabel()` — mit dem Alben-Reiter tot geworden. Das **Album-Feld selbst bleibt** in den Metadaten erhalten. - ✅ **Getestet:** 295 Tests grün (19 neue), `flutter analyze` ohne Befund. ### 📚 Hochgeladene Titel landen jetzt auch in Navidrome (2026-08-21) - 🎵 **Was sich ändert:** Was du vom Handy hochlädst, taucht ab sofort **auch in der Navidrome-Bibliothek** auf — also im Browser und in jeder anderen Musik-App, die auf den Server zugreift. Vorher blieb es in der Melo-Cloud liegen und war nur in Melo sichtbar. 🔧 **Wie:** Die Melo-Cloud hängt die hochgeladene Datei zusätzlich in Navidromes Musikordner (als *Hardlink* — dieselbe Datei unter zwei Namen, also **kein zusätzlicher Speicherplatz**) und stößt einen Scan an. Navidrome hat den Titel binnen Sekunden. Löschst du ihn, verschwindet er auch aus Navidrome — aber nur, wenn ihn kein anderes Konto mehr hat. ⚠️ **Zu wissen:** Navidrome hat **eine gemeinsame** Bibliothek. Was ein Konto hochlädt, sehen dort alle Navidrome-Nutzer. In Melo bleibt die Trennung pro Konto bestehen. - 🩹 **Nebenbei repariert: Downloads vom Server gingen gar nicht.** Die 325 Titel der Melo-Cloud lagen nur noch im Navidrome-Ordner — der Ablageordner der Cloud war leer. Jeder Download antwortete deshalb mit *„File missing"*. Alle Dateien sind zurückverknüpft, Downloads gehen wieder. - 🏷️ **Nebenbei repariert: falsche Dateiendungen.** Hochgeladene Dateien wurden immer als `.mp3` abgelegt, auch M4A und FLAC; heruntergeladene bekamen den Namen `Lied.m4a.m4a`. Jetzt zählt der echte Dateityp — auf dem Server wie auf dem Handy (wichtig, damit Android den Titel richtig einordnet). - 🛑 **Neu: Sicherheitsbremse gegen Massenlöschung.** Der Bibliotheks-Scan markiert jeden Titel als gelöscht, dessen Datei er nicht findet. Ist die Speicherkarte einmal nicht eingehängt oder die Berechtigung entzogen, betrifft das die halbe Sammlung — und ohne Bremse hätte der Abgleich sie am Server und damit auf **allen** Geräten gelöscht. Melo hält jetzt an, wenn mehr als 10 Titel **und** mehr als ein Drittel des Serverbestands auf einmal verschwinden sollen, und sagt warum. Einzelne Löschungen laufen unverändert durch. - 🔧 **Details für Technikinteressierte:** - `melo_cloud.py`: `verknuepfe_navidrome()` (Hardlink, Fallback Kopie), `loese_navidrome()`, `rescan_navidrome()` (Subsonic `startScan.view` über Navidromes `AuthProxyHeader` — **kein Passwort im Code**), `registry_pfad()` als gemeinsame Dateisuche über alle Audio-Endungen. - `handle_delete()` räumt die Datei ab, sobald kein Konto sie mehr aktiv hat — der Registry-**Eintrag** bleibt aber stehen, sonst verlöre `/list` den Grabstein und andere Geräte bekämen den Titel als Zombie zurück. - Neu: `scripts/melo_cloud_migriere_registry.py` (einmalige Rückverknüpfung) und `scripts/test_melo_cloud_navidrome.py` (Durchlauf-Test). - App: `MeloCloudService.herunterladen()` gibt die geschriebene Datei zurück und leitet die Endung aus dem Inhaltstyp ab (`endungFuer`); `loeschBremseGreift()` in `sync_service.dart`. - ✅ **Getestet:** 276 Tests grün (9 neue) plus ein Durchlauf gegen den echten Server: Upload → Registry + Navidrome (ein Hardlink) → Navidrome liest ein → Löschen entfernt beides, Grabstein bleibt. ### 🔊 Lokale Lieder spielen wieder + Abgleich zwischen Handy und Server (2026-08-21) - ▶️ **Behoben: Musik vom Handy war nicht abspielbar.** Titel, die auf dem Gerät liegen, tauchten zwar in der Bibliothek auf, beim Antippen kam aber nur *„Wiedergabe fehlgeschlagen: (0) Source error"* oder *„Loading interrupted"*. 🐛 **Die Ursache:** Sobald ein Musikserver eingerichtet war, hat Melo **jeden** Titel beim Server nachgefragt — auch die vom Handy. Als „Song-Nummer" schickte die App dabei den Dateipfad (`file:///storage/…`). Der Server kennt so etwas natürlich nicht und antwortete mit einer Fehlermeldung. Diese Fehlermeldung reichte Melo dann als „Musikdatei" an den Player weiter — daher der Fehler. Schlimmer noch: die Fehlermeldung wurde als `.mp3` im Zwischenspeicher abgelegt, wodurch der Titel **dauerhaft** kaputt blieb. 🔧 **Was jetzt anders ist:** Melo unterscheidet sauber zwischen Titeln vom Gerät und Titeln vom Server. Für eine Datei auf dem Handy geht keine einzige Anfrage mehr ins Netz — sie wird direkt abgespielt. - ⚡ **Behoben: Wiedergabe startete gar nicht mehr.** Beim Antippen eines Liedes lud Melo vorher die **gesamte** Warteschlange vom Server herunter, bevor der erste Ton kam — bei hunderten Titeln also nie. Tippte man noch einmal, brach der laufende Ladevorgang ab (*„Loading interrupted"*). 🔧 Jetzt startet der Titel sofort; das Zwischenspeichern für später läuft im Hintergrund und nur für das Lied, das gerade läuft. - 💾 **Behoben: der Offline-Zwischenspeicher wurde nie wiedergefunden.** Als Ablage-Schlüssel diente die komplette Stream-Adresse — die enthält aber ein Zufallsmerkmal, das Melo bei **jedem App-Start neu** erzeugt. Derselbe Titel bekam also jedes Mal einen anderen Schlüssel: der Zwischenspeicher wuchs nur noch und half nie. Jetzt zählt die Song-Nummer, und offline gespeicherte Server-Titel laufen wirklich ohne Netz. - 🧹 **Zwischenspeicher kann sich nicht mehr vergiften:** Es wird erst in eine `.part`-Datei geschrieben und nur dann als Musik übernommen, wenn der Server wirklich Audio geliefert hat. Abgebrochene Downloads hinterlassen keine halben Dateien mehr. - ☁️ **Neu: Abgleich zwischen Handy, Server und Web.** Unter **Einstellungen → Geräte-Abgleich** hält Melo alles auf demselben Stand: - ⬇️ Titel vom Server landen als richtige Datei auf dem Handy und sind danach **offline** verfügbar. - ⬆️ Eigene Dateien vom Handy gehen zum Server — auch von anderen Geräten aus abrufbar. - 🗑️ Löschungen wirken in beide Richtungen: was hier verschwindet, taucht auf den anderen Geräten nicht wieder auf, und umgekehrt. - ❤️ Favoriten und 🕐 Wiedergaben werden mitgemeldet. - 🔄 Das passiert **automatisch** beim App-Start und beim Zurückkehren in die App (höchstens alle 15 Minuten, damit es keinen Akku frisst). Der Knopf für „jetzt sofort" ist trotzdem da. ⚠️ **Ehrlich gesagt:** Der Abgleich läuft über die **Melo-Cloud** (`cloud.baka-net.de`), nicht über Navidrome. Grund: Navidrome bietet schlicht keine Möglichkeit, Dateien hochzuladen — die Subsonic-Schnittstelle kennt keinen Upload. Navidrome bleibt die Streaming-Bibliothek, die Melo-Cloud ist der gemeinsame Speicher. Dafür ist eine Anmeldung beim Baka-Konto nötig (Tab **Online → YouTube**). Einzelne Datei: max. 50 MB. - 🏷️ **Behoben: Korrigierte Titelangaben gingen beim Scannen verloren.** Wer Titel, Künstler oder Album von Hand richtiggestellt hatte, bekam beim nächsten Bibliotheks-Scan wieder die falschen Angaben aus der Datei. Die Markierung „von Hand bearbeitet" wurde nirgends gesetzt und nirgends beachtet. Jetzt schon — in beiden Scans (Handy und Ordner). - 🔧 **Details für Technikinteressierte:** - `MeloAudioHandler.loadPlaylist` baut die Warteschlange ohne Netzzugriff; neue reine Funktionen `navidromeIdOf`, `songIdOf`, `nutztServerCache`, `quelleFuer`. - Server-Titel tragen ihre Server-Nummer in `MediaItem.extras['navidromeId']` statt sie aus der Abspiel-Adresse zu raten. - `NavidromeService.ladeInCache()` ersetzt `streamAndCacheToLocal()`: `.part`-Datei + Prüfung des Inhaltstyps (`istAudioAntwort`) + stabiler Schlüssel (`cacheSchluessel`); der HTTP-Client wird geschlossen. - Neu: `services/melo_cloud_service.dart`, `services/sync_service.dart` mit der reinen Planungsfunktion `planeSync()`. - Datenbank auf Version 8: neue Spalte `songs.cloud_id` verbindet einen Titel auf dem Gerät mit seinem Gegenstück am Server. - `database.g.dart` war veraltet und musste neu erzeugt werden — dadurch ließ sich das Projekt vorher gar nicht übersetzen. - ✅ **Getestet:** 267 Tests grün (78 neue), `flutter analyze` ohne Befund. - ⚠️ **Offen:** Ein bestehender Widget-Test (`song_detail_sheet_test.dart`) bleibt beim Öffnen des Detail-Fensters hängen und blockierte den gesamten Testlauf. Er ist vorerst als *übersprungen* markiert; sein Zweck wird jetzt von `metadaten_uebernahme_test.dart` ohne Widgets abgedeckt. ### 🔎 Melo merkt selbst, wenn neue Musik da ist (2026-08-20) - 📂 **Neue Dateien auf dem Handy:** Kommst du in die App zurück, sieht Melo nach, ob sich der Musikbestand geändert hat — und liest neue Titel von allein ein. Musik per Kabel überspielt oder anderswo heruntergeladen? Sie ist einfach da, ohne dass du "Musik scannen" antippen musst. 🔧 **Wie sparsam das ist:** Melo vergleicht nur die **Anzahl** der Dateien und prüft höchstens alle 5 Minuten. Der eigentliche Scan läuft nur, wenn die Zahl abweicht — sonst passiert gar nichts. ⚠️ **Grenze, ehrlich gesagt:** Werden genauso viele Titel gelöscht wie hinzukommen, bleibt die Zahl gleich und Melo merkt es nicht. Dafür gibt es weiterhin den Knopf zum Scannen von Hand. - 🌐 **Neues auf dem Musikserver:** Im Tab **Online → Server** erscheint ein Hinweis, wenn auf Navidrome Alben dazugekommen sind ("3 neue Alben auf dem Server"). Ein Tipp auf *Verstanden* und der Hinweis ist weg — dieselben Alben melden sich nicht noch einmal. 🔧 Beim allerersten Mal meldet Melo bewusst **nichts**: sonst wäre deine ganze Sammlung auf einmal "neu". Melo merkt sich still den Stand und meldet ab dann nur noch, was wirklich dazukommt. Der Hinweis kostet keinen zusätzlichen Serveraufruf — er nutzt die Alben, die ohnehin geladen werden. - 🔧 **Details:** - Neu: `library/auto_scan.dart` (6 Tests), `services/server_neuheiten.dart` (5 Tests), `MeloDb.countSongs()` (1 Test), `countAndroidMediaStoreSongs()`. - `HomeShell` hört jetzt auf den App-Lebenszyklus (`resumed`). - ✅ **Getestet:** 189 Tests grün (12 neue), `flutter analyze` ohne Befund. ### ⬇️ YouTube-Downloader mit Baka-Anmeldung (2026-08-20) - 🎬 **Musik von YouTube holen:** Im Tab **Online → YouTube** eine Adresse einfügen, auf *Herunterladen* tippen — fertig. Der Titel landet danach automatisch in **Meine Musik**. - 🔑 **Warum eine Anmeldung nötig ist:** Das Herunterladen erledigt der Baka-Server (yt.baka-net.de), und der nimmt nur Anfragen von angemeldeten Konten an. Deshalb gibt es im YouTube-Bereich einen Anmelde-Knopf für dein **Baka-Konto**. Die Anmeldung wird **verschlüsselt auf dem Gerät** gespeichert und gilt auch nach dem Neustart. Läuft sie ab, sagt Melo das im Klartext ("Anmeldung abgelaufen — bitte neu anmelden") statt eine Fehlernummer zu zeigen. - 📂 **Wo die Musik landet:** In `Music/Melo` — dem **öffentlichen** Musikordner. Das ist wichtig: Melo liest seine Bibliothek auf Android über den MediaStore. Eine Datei, die nur in einem App-Ordner läge, wäre beim nächsten Scan wieder verschwunden. Nebeneffekt: die Musik ist auch in anderen Apps da. - 🧹 **Keine Bruchstücke:** Der Download geht erst in einen Zwischenordner. Erst wenn die Datei vollständig da ist, wandert sie in den Musikordner. Bricht etwas ab, wird die halbe Datei gelöscht statt als kaputter Titel in der Bibliothek zu landen. - 🎤 **Künstler:** Steht der Titel als "Künstler - Lied" da, wird der Künstler übernommen. Steht da nichts dergleichen, bleibt das Feld leer — geraten wird nicht. - 🔧 **Details:** - Neu: `services/baka_auth.dart` (6 Tests), `services/yt_download_service.dart` (8 Tests), `services/media_store.dart` (2 Tests) und die native Brücke `MediaStoreBridge.kt` (MediaStore ab Android 10, Dateisystem + Scanner davor). - `WRITE_EXTERNAL_STORAGE` (nur bis Android 9) im Manifest ergänzt. - Der Online-Tab hat jetzt die Unterreiter **YouTube · Server** (3 Tests); `SubTabs` liegt dafür in `shared/sub_tabs.dart`. - ⚠️ Der Anmelde-Server meldet Fehler mit Status 200 und Text im Rumpf — der Code prüft deshalb den Inhalt, nicht nur den Statuscode. - ✅ **Getestet:** 177 Tests grün (19 neue), `flutter analyze` ohne Befund, Release-APK baut (22,8 MB). ⚠️ **Ehrlich dazugesagt:** Der Weg über den echten Proxy und das Schreiben in den Musikordner sind **noch nicht auf dem Handy ausprobiert** — das geht nur auf dem Gerät. Alles davor ist mit Tests abgedeckt. ### 🧭 Neue Tab-Struktur nach deinen Referenzbildern (2026-08-20) - 🔀 **Untere Leiste wie auf deinem Bild:** Die Reihenfolge ist jetzt **Meine Musik · Online · Suchen · Favoriten**. Der frühere Reiter "Download" heißt jetzt **Online** (Musiknoten-Symbol) — dort kommt als Nächstes der YouTube-Downloader dazu. - 🗂️ **Unterreiter in "Meine Musik":** Über der Liste sitzen jetzt **Songs · Künstler · Alben** als Pillen — genau wie in der Referenz. Der aktive Reiter ist hell, die anderen sind nur Text. Wichtig: Ein Unterreiter **wechselt nur den Inhalt** und öffnet keinen neuen Bildschirm — Suchfeld und Schnellzugriffe bleiben stehen. - 🧹 **Aufgeräumt:** Die Schnellzugriff-Kacheln "Künstler" und "Alben" sind weg, weil sie jetzt Unterreiter sind. Übrig bleiben — wie in der Referenz — **Favoriten · Wiedergabelisten · Zuletzt**. - 🔧 **Details:** `MyMusicScreen` ist jetzt zustandsbehaftet (`_subTab`), neues `_SubTabs`-Widget; die dadurch toten `ArtistsScreen`/`AlbumsScreen`-Hüllen wurden entfernt (`ArtistListScreen`/`AlbumListScreen` bleiben und werden direkt eingebettet). - ✅ **Getestet:** 158 Tests grün (4 neue), `flutter analyze` ohne Befund. ### 🎼 Songtext, Warteschlange, Sperrbildschirm-Cover und gleiche Lautstärke (2026-08-20) - 📝 **Automatischer Songtext:** Beim Scan wird der Songtext aus dem Tag der Datei mitgelesen und in der Wiedergabe sofort angezeigt — **ohne Server und ohne Internet**. Nur wenn die Datei keinen Text enthält, wird der Navidrome-Server gefragt. 🐛 Nebenbei gefixt: Der Songtext-Knopf hat bisher die Datei-Adresse statt der Song-ID weitergegeben, dadurch kam vom Server nie ein Text zurück. - ➕ **Warteschlange ohne Nebenwirkungen:** Im ⋮-Menü einer Songzeile gibt es jetzt **"Als Nächstes spielen"** und **"Zur Warteschlange hinzufügen"** — beides ändert **nichts** an Favoriten oder Wiedergabelisten und unterbricht die laufende Wiedergabe nicht. Umgekehrt entfernt das ✕ in der Warteschlange den Titel weiterhin nur dort — er bleibt Favorit und bleibt in jeder Wiedergabeliste. - 🖼️ **Sperrbildschirm zeigt dasselbe Bild wie die App:** Läuft ein Titel, bekommt der Sperrbildschirm jetzt das **Kategorie-Cover** (sofern die Einstellung "Gleiche Kategorie = gleiches Coverbild" an ist) statt eines abweichenden Bildes. Auch Wiedergabelisten zeigen jetzt dieses Cover. - 🔉 **Gleiche Lautstärke (ReplayGain):** Neuer Schalter unter *Einstellungen → Ton* (standardmäßig an). Melo liest den ReplayGain-Tag der Datei und **senkt laute Titel ab**, damit alles gleich laut wirkt. ⚠️ **Ehrlich dazugesagt:** Verstärken kann ein Player nicht über 100 % hinaus — angeglichen wird deshalb immer nach unten. Und es funktioniert nur bei Dateien, die den Tag mitbringen (typisch FLAC/OGG und mit ReplayGain versehene MP3s). Dateien ohne Tag bleiben unverändert. - 🔧 **Details:** - DB-Schema 6: neue Spalten `lyrics` und `gain_db`. - Neu: `player/replay_gain.dart` (7 Tests), `playNextIndex` + `addToQueue`/`playNext` im Audio-Handler (3 Tests), Cover-Vorgabe in `songToMediaItem` (3 Tests). - ✅ **Getestet:** 154 Tests grün, `flutter analyze` ohne Befund. ### 🎚️ Equalizer, Dolby Atmos und automatische Wiedergabe (2026-08-20) - ✅ **Was:** Die Einstellungen haben einen neuen Bereich **Ton** mit Equalizer, Dolby Atmos und der automatischen Wiedergabe bei Kopfhörern. - 🎛️ **Equalizer (echt, nicht nachgebaut):** Neuer Bildschirm mit den Klangprofilen deines Geräts (Normal, Rock, Pop …), den Frequenzbändern als senkrechte Regler mit Beschriftung (60Hz … 14.0kHz) und den beiden Drehreglern **Bass** und **Immersives Audio** — wie auf deinem Bild. Zum Regeln senkrecht über den Drehregler wischen. Alle Werte werden gespeichert und nach dem Neustart wiederhergestellt. - 🔊 **Wie das technisch geht:** Neue native Android-Brücke (`AudioEffectsBridge.kt`) auf `android.media.audiofx` — Equalizer, BassBoost und Virtualizer hängen an der Audio-Session des Players. Da just_audio die Session-ID erst beim Abspielen meldet, verbindet sich die App bei jedem Wechsel automatisch neu. Bietet ein Gerät einen Effekt nicht an, wird er ausgegraut statt die App abstürzen zu lassen. - 🎬 **Dolby Atmos:** Öffnet die **Klangeffekte des Systems** — auf deinem POCO ist das die Dolby-Atmos-Seite. Eine App kann Dolby Atmos nicht selbst steuern; der System-Weg ist der einzige, der wirklich funktioniert. Bietet ein Gerät die Seite nicht an, sagt Melo das. - 🎧 **Automatische Wiedergabe:** Neuer Schalter — sobald Kopfhörer verbunden werden (Klinke **oder Bluetooth**), spielt Melo weiter. Eine bereits laufende Wiedergabe wird nie unterbrochen, und ohne Warteschlange passiert nichts. - 🔧 **Details:** - Neu: `player/audio_effects.dart` (6 Tests, u. a. Grenzwerte und Wiederherstellen), `player/headphone_autoplay.dart` (6 Tests), `settings/equalizer_screen.dart` inkl. selbst gezeichnetem Drehregler. - `MODIFY_AUDIO_SETTINGS` im Manifest ergänzt (nötig für die Effekte). - `MainActivity.kt` registriert die Brücke und gibt die Effekte beim Beenden wieder frei. - ✅ **Getestet:** 141 Tests grün, `flutter analyze` ohne Befund. ### 🏷️ Kategorien, Metadaten-Ansicht und Expertenmodus (2026-08-20) - ✅ **Was:** Jeder Song kann jetzt **mehreren Kategorien** angehören. Sie stehen in der Songzeile hinter dem Künstler ("Neoni | Trap cool · Chill") — genau wie in deinem Entwurf. - 📥 **Woher sie kommen:** Beim Scan aus dem **Genre-Tag** der Datei. Mehrere Werte in einem Tag werden automatisch getrennt (`;` `,` `|` `/`), doppelte fallen weg. Über **⋮ → Metadaten → Bearbeiten** kannst du Kategorien selbst hinzufügen oder entfernen; bereits vergebene Kategorien werden als Vorschläge angeboten. - 🔒 **Von Hand schlägt Automatik:** Sobald du die Kategorien eines Songs selbst änderst, überschreibt ein späterer Scan sie **nicht** mehr. - 🖼️ **Gleiche Kategorie = gleiches Coverbild:** Neue Einstellung unter *Einstellungen → Darstellung* (standardmäßig **an**, jederzeit abschaltbar). Ist sie an, zeigen alle Songs einer Kategorie das Bild der **ersten** Kategorie; hat die Kategorie kein Bild, bleibt das eigene Cover. Im Metadaten-Fenster ist die bildgebende Kategorie mit einem Bild-Symbol markiert. - 🔬 **Expertenmodus:** Im Metadaten-Fenster ausklappbar — Titel, Künstler, Album, Dauer, Wiedergaben, Hinzugefügt-Datum, Herkunft der Kategorien, Format, Dateigröße und Pfad. - 🔧 **Details:** - DB-Schema 4: neue Tabelle `song_categories` (Song ↔ Kategorie mit Reihenfolge) und Spalte `categories_edited`. Bestehende Bibliotheken werden automatisch migriert. - Neu: `library/categories.dart` (reine Logik: Tag zerlegen, Cover je Kategorie ermitteln — 12 Tests), `library/category_service.dart` (hält Kategorien + Cover-Zuordnung einmal zentral, 3 Tests), `library/song_detail_sheet.dart`, `settings/app_settings.dart`. - Beide Scanner (Android-MediaStore und Ordner-Scan) schreiben die Kategorien mit. - Das Drei-Punkte-Menü der Songzeile ersetzt den bisherigen Playlist-Knopf und enthält "Metadaten" und "Zu Wiedergabeliste hinzufügen". - ✅ **Getestet:** 129 Tests grün, `flutter analyze` ohne Befund. ### 🎨 Neue Melo-Oberfläche: 4 Tabs + Sortierung (2026-08-20) - ✅ **Was:** Die App hat jetzt die von dir gewünschte Struktur mit **4 Tabs: Meine Musik · Suche · Favoriten · Download**. "Meine Musik" ist der neue Startbildschirm — **ohne Topbar**, wie du es wolltest. - 🎵 **Meine Musik:** Oben eine Kopfzeile mit Einstellungs-Icon (links), Suchfeld (Mitte) und ?-Icon für die Musikerkennung (rechts, kommt später). Darunter Schnellzugriff-Karten (Favoriten · Wiedergabelisten · Zuletzt · Künstler · Alben), dann **Shuffle-Wiedergabe + Sortieren**, dann die komplette Liederliste. - ↕️ **Sortieren (neu!):** Über das Symbol rechts neben "Shuffle-Wiedergabe" kannst du wählen zwischen **Zuletzt hinzugefügt**, **Name (A–Z–#)** und **Wie oft abgespielt** — jeweils auf- oder absteigend. Die Wahl wird gespeichert und überlebt einen Neustart. Bei "Name" stehen Buchstaben vorne und Ziffern/Sonderzeichen hinten unter "#"; Umlaute sortieren richtig ein (Ärger steht bei A, nicht hinter Z). - ❤️ **Favoriten-Tab:** eigener Tab mit Shuffle-Wiedergabe und **genau derselben Sortierung** — mit eigener, getrennt gespeicherter Einstellung. - ⬇️ **Download-Tab:** durchsucht den verbundenen Navidrome-Server (Alben/Künstler); abgespielte Titel werden weiterhin automatisch lokal zwischengespeichert. - 🔢 **"Wie oft abgespielt" zählt jetzt wirklich:** Neue Spalte `playCount` in der Datenbank (Schema 3, bestehende Bibliotheken werden automatisch migriert). Gezählt wird beim Start eines Titels — nicht sekündlich, solange er läuft. - 🎨 **Farben:** Schwarz auf **#0B0B10** und Melo-Rot **#c0392b** (vorher ein helleres Rot). - 🔧 **Details:** - Neu: `library/song_sort.dart` (reine Sortier-Logik, 11 Tests), `shared/sort_store.dart` (Speichern der Wahl, 4 Tests), `shared/sortable_song_list.dart` (Shuffle + Sortier-Menü), `library/my_music_screen.dart`, `library/favorites_screen.dart`, `downloads/downloads_screen.dart`. - Der alte `library_screen.dart` (Tabs Alle/Künstler/Alben/Server) ist entfallen — seine Inhalte leben jetzt in "Meine Musik" (Alle), als eigene Bildschirme (Künstler/Alben) und im Download-Tab (Server). - **Musik hinzufügen** und **Erneut scannen** saßen in der entfernten Topbar und stehen jetzt in den **Einstellungen → Bibliothek** (zusätzlich weiterhin im leeren Zustand von "Meine Musik"). - Der Mini-Player ist jetzt auf allen Tabs sichtbar (vorher war der Player ein eigener Tab). - 🐛 **Nebenbei gefixt:** Das Sortier-Menü ist scrollbar — auf niedrigen Bildschirmen bzw. im Querformat war der "Übernehmen"-Knopf sonst nicht erreichbar. Schnellzugriff-Karten liefen um 2 Pixel über. - ✅ **Getestet:** 107 Tests grün, `flutter analyze` ohne Befund. ### 🔎 Login-Dialog zeigt jetzt die echte Server-Fehlermeldung (2026-08-20) - ✅ **Was:** Schlägt der Navidrome-Login fehl, zeigt der Dialog jetzt die Original-Meldung des Servers (z. B. „Wrong username or password") statt nur „Login fehlgeschlagen". So ist sofort klar, ob das Passwort falsch ist oder ein Netzwerkproblem vorliegt. - 🔧 **Details:** - `NavidromeService.letzterLoginFehler` hält die Server-Meldung des letzten abgelehnten Logins fest (wird bei jedem Ping zurückgesetzt). - Das Fehler-Log enthält jetzt die Passwort-**Länge** (`pwLen=…`, nie das Passwort selbst) — ein Tippfehler wird so im Log sofort sichtbar (`Melo…!` hat 11 Zeichen). - 🐛 **Hintergrund (tiefe Debug-Runde):** Server-seitig ist alles nachweislich korrekt — Subsonic-API (`/rest/*`) läuft trotz SSO-Umbau ohne Forward-Auth, DB-Passwort für „Baka" stimmt, und die exakte App-Token-Logik (Base64-Salt + md5) wurde 8/8-mal erfolgreich gegen den Live-Server reproduziert. Die Ablehnungen kommen von einem falschen Passwort-Wert auf dem Gerät; die neue Fehlermeldung macht genau das künftig direkt sichtbar. ### 🔎 Login-Diagnose: Passwort-Sichtbarkeit + Trim (2026-08-20) - 🐛 **„Login fehlgeschlagen" trotz korrekter Zugangsdaten:** Systematische Analyse (App-Code, App-Logs, Caddy/Navidrome-Logs, 1:1-Nachbau des App-Requests inkl. Salt/Token) hat bewiesen: **App-Code, Server, User „Baka" und Passwort sind korrekt** — der Fehler lag im auf dem Gerät verwendeten Credential-Wert (der ältere Build mit dem Ping-Fehlalarm persistierte ungültige Zugangsdaten; verdeckte Tippfehler im Passwortfeld blieben unsichtbar). - 🔧 **Passwort-Anzeigen-Umschalter** (Auge-Icon) im Navidrome-Login — Tippfehler werden sichtbar. - 🔧 **URL + Passwort werden getrimmt** (randständige Leerzeichen aus Autofill / Tastatur entfernt). - 🔧 **Default-User im Login ist jetzt „Baka"** (vorher „Dustin") — verhindert versehentlichen Login mit falschem User. - ℹ️ Ein erfolgreicher Login überschreibt die alten (ungültigen) gespeicherten Zugangsdaten — der Ping-Fix aus dieser Version stellt sicher, dass künftig nur noch gültige Credentials gespeichert werden. ### 🔒 Datenschutz: Log-Bereinigung vor Server-Upload (2026-08-20) - ✅ **Was:** Diagnose-Logs werden jetzt zentral bereinigt, bevor sie an `logs.baka-net.de` gehen. Der lokale `debugPrint` bleibt vollständig. - 🔧 **Details:** - Freitext-Scrubber für `message` + `stackTrace`: maskiert sensible `key=value`-Fragmente (`user=`, `pw=`, `password=`, `token=`, `secret=`, `url=`), Datei-Pfade (``), nackte URLs (``), `Bearer`-Token, JWTs (``) und lange Hex-IDs (``). - `stackTrace` wird auf max. 8 Zeilen gekürzt. - `deviceModel` sendet nur noch die grobe Plattform (`android`/`ios`) statt Hersteller+Modell (war ein Geräte-Fingerprint) — `device_info_plus` im Logger entfernt. - 🐛 **Fix (Quelle):** Der Navidrome-Login in `settings_screen` loggte Benutzername + Server-URL im Klartext (`Login: url=… user=…`). Diese Aufrufe senden jetzt nur noch den neutralen Status inkl. `pwLeer`-Flag. - 🧪 **Tests:** neue Suite `test/logger_service_test.dart` (9 Tests) — 79/79 grün, `flutter analyze` ohne Issues. - ℹ️ **Offen:** Opt-in/Einwilligung für den Diagnose-Versand bleibt ein separater Folgeschritt (braucht UI). ### 🐛 Server-Bibliothek lädt + Tab abgeschnitten (2026-08-20) - 🐛 **Server-Bibliothek lud nicht ("Erneut versuchen"):** Ursache war, dass der Code das Subsonic-`status`-Feld ignorierte. Navidrome liefert auch bei falschem Passwort **HTTP 200** mit `status:"failed"` — `ping()` meldete dadurch fälschlich „Login ok", und `getAlben`/`getArtists` behandelten die Fehlerantwort still als „leer". - 🔧 `ping()` prüft jetzt den Body (`status`) — kein Fehlalarm mehr beim Login. - 🔧 `getAlben`/`getArtists` werfen bei `status:"failed"` eine `NavidromeException` mit Server-Meldung statt still `[]` zurückzugeben. - 🔧 Der Server-Tab zeigt die echte Server-Fehlermeldung (z. B. „Wrong username or password") statt nur „Keine Alben geladen". - 🔧 Album-Abruf nutzt jetzt den ID3-Endpoint `getAlbumList2` (konsistent zu `getArtists`) — der alte Folder-Endpoint `getAlbumList` liefert bei tag-organisierten Libraries leer. - 🐛 **Tab „🌐 Server" war abgeschnitten** (nur „Serve" sichtbar): Die 4 festen Tabs waren zu eng. `labelPadding` verkleinert (engere Abstände) — Schriftgröße unverändert. - 🧪 Neue Tests: `test/navidrome_parse_test.dart` (5). 84/84 grün, analyze sauber. ### 🔧 Log-Upload System - Fixes (2026-08-19) - ✅ **ERROR-Level Logging**: Fehler werden SOFORT zum Server hochgeladen (async/await Fixed) - ✅ **Navidrome-Service Error Logging**: Alle API-Fehler (ping, getAlben, getSongs, getFavorites, getArtists, getArtistSongs, getLyrics, scrobble, getBookmarks, getPlaylists, getPlaylistSongs) werden jetzt mit `logger.error()` zum Server geloggt (statt nur debugPrint) - ✅ **Login-Fehler Detail-Logging**: Fehler enthalten URL, User, Exception-Typ und StackTrace - ✅ **HTTP-Error bei Stream**: Stream-Fehler mit HTTP-Statuscode werden auch geloggt - ✅ **Payload-Format**: Logs senden als `{"logs": [...]}` Array zum Server ### 🎉 RELEASE BUILD v3: Phase 2 + Login + Auto-Bugreport System! **Build Date:** 2026-08-19 21:30 **APK (Release/Universal):** `app-release.apk` (22.3 MB) — **Mit Auto-Logger + Login-Improvements!** **APK (ARM64-Specific):** `app-arm64-v8a-release.apk` (21 MB) — **Optimal für POCO X7 Pro** **Tests:** 70/70 ✅ **Build Status:** ✅ SUCCESS **Neue Features in dieser Build:** - ✨ Login-Verbesserungen: Auto-URL + User-Buttons + Gast-Modus - 📊 Auto-Bugreport System: Logger sendet Fehler sofort + andere Logs gesammelt zum Server - Device-Info (Model + OS) + Timestamp + Stack-Traces - Integration in Navidrome-Service für Login-Fehler-Diagnose - 📝 Alle Phase-2-Features (Sync, Caching, Player, Controls) - 🔒 Secure Credential-Speicherung - 🎵 Enhanced MiniPlayer mit Skip-Buttons ### Phase 2 (Part 1-8 Completed — MILESTONE!) - 🗄️ **Auto-Caching**: Lieder werden automatisch beim Streamen lokal gespeichert! - Cache-First-Logik: Existierende Dateien lokal spielen - Fallback: Vom Server streamen wenn kein Cache - Cache-Speicher-Status in Settings (MB/KB/B) mit Clear-Button - 🗄️ **CacheManager**: MD5-Hash-basierte Cache-Verwaltung (getCacheSize, clearCache) - 📴 **Offline-Mode**: Toggle in Settings aktiviert Offline-Betrieb (nur gecachte Lieder spielen) - 🎤 **Artist-Browse**: Toggle in Navidrome-Tab zwischen Albums ↔ Künstler, Tap lädt alle Songs des Künstlers - 🌟 **Favoriten-Sync**: Button in Settings lädt Server-Favoriten und importiert sie lokal - 📊 **History-Sync**: Wiedergabe-Position vom Server laden + alle 5s zum Server senden (Subsonic-Bookmarks) - 📋 **Playlisten-Sync**: Button in Settings lädt alle Server-Playlisten (mit 🌐 Präfix zur Unterscheidung) - 🔒 **Lock-Screen Controls**: Notification mit Play/Pause/Skip-Buttons auf Android Lock Screen - 🎵 **Enhanced MiniPlayer**: Now-Playing Widget auf allen Tabs mit Skip-Buttons + Progress-Bar - Infrastructure für Multi-Device Sync vorbereitet ### Added - 🌐 **Musikserver-Tab** in Bibliothek: Zeige Alben vom Navidrome-Server, tippe um Album abzuspielen - 📝 **Lyrics-Anzeige**: Lyrics-Button im Now-Playing Screen, Bottom-Sheet mit Song-Lyrics vom Server - 🔀 **Shuffle & Repeat Modi**: Shuffle-Button (aktiv = rot), Repeat-Cycle (Off → All → One), beide im Player - 🌐 Navidrome-Integration (Musikserver-Verbindung): Settings-Tab bietet Login/Logout für Subsonic-API-kompatible Server - Navidrome-Service mit sichere Credential-Speicherung (Secure Storage + SharedPreferences) - Server-Album-Browser: Lädt Alben vom Server, zeigt Songcount, tippe um Album in Queue zu laden - Server-APIs erweitert: getArtists(), getArtistSongs(), setFavorite(), getFavorites(), getLyrics() (Phase 2 vorbereitet) - Dependencies für Navidrome hinzugefügt: http, crypto, shared_preferences, flutter_secure_storage ### Known Limitations (Phase 2) - Server-Favoriten-Sync: APIs vorhanden, UI-Integration in Phase 2 - Artist-Browse: APIs vorhanden, UI in Phase 2 - Server-Caching: Musik wird nicht lokal für Offline gecacht (Phase 2) - Lyrics-Sync: Keine Timed-Synchronisation (Phase 2+) ## [1.0.0-alpha] ### Added - Playlisten-Tab: Favoriten-Kurzeinstieg oben, Liste aller Playlisten mit Songanzahl, Erstellen-Dialog (FAB), Löschen mit Bestätigung - 🎵➕ Songs per Bottom-Sheet zu Playlisten hinzufügen (neuer Button in der Songliste, neben dem Favoriten-Herz) - Playlist-Detailansicht: Songs per Drag & Drop umsortieren, einzeln entfernen, Antippen spielt die Playlist ab dieser Stelle - Datenbank: Playlisten, Favoriten und Wiedergabe-Historie Tabellen (Drift, Schema v2) - PlaylistService für Playlisten- und Favoriten-Verwaltung - ❤️ Favoriten-Herz in der Songliste (ausgefüllt/leer) - ❤️ Favoriten-Herz auch im Now-Playing-Screen (aktueller Song) - Bibliothek: Künstler- und Alben-Tabs neben "Alle" — Songs werden clientseitig aus der bestehenden Songliste gruppiert (alphabetisch sortiert, fehlender Künstler/Album → "Unbekannt" / "Unbekanntes Album"); Antippen öffnet die Songliste des jeweiligen Künstlers/Albums, Alben zeigen zusätzlich das Cover - Warteschlangen-Ansicht (Queue-Button in der Now-Playing-AppBar): zeigt die aktuelle Wiedergabeliste, hebt den laufenden Titel hervor, per Antippen direkt zu einem Titel springen, Titel entfernen oder per Drag & Drop umsortieren — ohne Änderungen am Audio-Handler (Queue wird clientseitig neu berechnet und neu geladen) - Wiedergabeposition wird alle 5 Sekunden gespeichert und beim erneuten Abspielen eines Songs automatisch fortgesetzt (kein Resume bei den letzten 3 Sekunden eines Songs, um ein sofortiges Ende zu vermeiden) - Settings-Tab: Bibliotheks-Statistik (Song-Anzahl + Gesamtspieldauer), Musik-Berechtigung mit Link in die Systemeinstellungen, kurzer "Über Melo"-Abschnitt — bewusst ohne Platzhalter für Profil/Cloud-Sync/ Geräte-Verwaltung, da es dafür noch kein Account-/Cloud-System gibt (Phase 2) **Damit ist das komplette 5-Tab-MVP fertig: Player / Bibliothek / Playlisten / Suche / Settings — inklusive Playlisten, Favoriten, Warteschlangen-Verwaltung, Künstler-/Alben-Browsing und automatischem Fortsetzen der Wiedergabeposition.** ### Changed - Now-Playing-Screen läuft jetzt als eigener Tab statt als gepushter Screen (kein Schließen-Button mehr in der AppBar) - MediaItem trägt jetzt die Song-UUID mit (`extras['songId']`), nicht mehr nur die Datei-URI - Finales 5-Tab-Layout: Player / Bibliothek / Playlisten / Suche / Settings (Playlisten und Settings vorerst als Platzhalter) - "Zuletzt hinzugefügt" aus der alten Startseite ist jetzt Teil der Bibliothek (horizontale Leiste oben) - MiniPlayer wird auf dem Player-Tab ausgeblendet (dort läuft schon die volle Ansicht) ### Fixed - 🐛 Der APK-Build funktioniert jetzt überhaupt. Drei Dinge waren nötig: `compileSdk = 37` und `kotlin.jvm.target.validation.mode=warning` existierten bisher nur lokal auf dem Server und fehlten im Repo — ohne sie bricht der Build ab, weil `permission_handler` SDK 37 verlangt. Zusätzlich hebt ein Gradle- Workaround jetzt Plugin-Module mit zu altem `compileSdk` auf 36 an: `on_audio_query_android` kompiliert gegen SDK 33, seine AndroidX-Abhängigkeiten verlangen aber mindestens 34 (16 Metadaten-Konflikte). ### Removed - Alte Startseite (`home_screen.dart`) entfernt — Funktionalität in die Bibliothek verschoben ### Known Issues - Genre-Browsing fehlt noch: die `Songs`-Tabelle hat keine Genre-Spalte und keiner der Scanner (scan_service.dart, android_scan.dart) liest Genre- Metadaten aus den Dateien aus. Das ursprünglich geplante dritte Bibliotheks- Tab "Genres" ist deshalb nicht umgesetzt — braucht eine Schema-Migration plus Metadaten-Extraktion im Scanner (eigene Aufgabe, außerhalb dieses Tasks)