Files
Melo/CHANGELOG.md
T
Hermes (Server)andClaude Opus 5 80948f1ad5 Listen zeigen, welcher Titel gerade laeuft
Bisher scrollte man durch die Bibliothek und musste raten, wo man ist —
die einzige Rueckmeldung war die Leiste ganz unten.

Drei tanzende Balken ueber dem Coverbild des laufenden Titels, sein Name
in Rot. Die Balken liegen auf dem Cover statt daneben: eine vierte Spalte
haette dem Titel Platz genommen und die Zeile bei jedem Titelwechsel in
der Breite springen lassen. Pausiert bleibt die Markierung stehen, die
Balken stehen still. Bei abgeschalteten Systemanimationen ebenfalls — eine
Endlosschleife ist genau das, was diese Einstellung vermeiden soll.

Die Markierung sitzt in SongZeile und gilt damit in einem Rutsch fuer
Meine Musik, Favoriten, Suche, Kuenstler, Kategorie und "Zuletzt
hinzugefuegt". Warteschlange und Wiedergabeliste bauen ihre Zeilen selbst
(dort steht rechts Entfernen statt Herz und Menue) und haben dasselbe
Zeichen einzeln bekommen.

Nur die laufende Zeile hoert auf den Wiedergabe-Zustand: der meldet auch
den Pufferstand und feuert mehrmals pro Sekunde. Haenge jede sichtbare
Zeile daran, baute sich die halbe Liste staendig neu auf.

Aus dem Code-Review nachgebessert: Startwert fuer den Strom, sonst
erschien die Markierung beim Zurueckscrollen einen Frame zu spaet; die
Zeile aendert beim Wechsel nicht mehr die Form ihres Aufbaus, sonst verlor
das Herz daneben sein Abo und sah kurz leer aus; die Wiedergabeliste
bekam die Markierung ueberhaupt erst (der Changelog hatte sie faelschlich
schon behauptet); der Abdunkel-Schleier nimmt den Eckenradius jetzt
entgegen statt ihn fest zu verdrahten.

Testluecke geschlossen, die der Review benannt hat: geprueft waren nur die
Regel und die Balken je fuer sich, die Verdrahtung dazwischen nicht —
genau dort sassen alle drei Fehler.

411 Tests gruen (vorher 396), flutter analyze ohne Befund, Release-APK
gebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xAHJTJM6UUqmjUgk1PUEd
2026-08-23 20:54:45 +02:00

940 lines
58 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
Alle nennenswerten Änderungen an diesem Projekt werden hier dokumentiert.
Format angelehnt an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/).
## [Unreleased]
### ▶️ 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 (AZ#)** 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 (`<path>`), nackte URLs (`<url>`), `Bearer`-Token,
JWTs (`<jwt>`) und lange Hex-IDs (`<hex>`).
- `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)