Commit Graph
34 Commits
Author SHA1 Message Date
Hermes (Server)andClaude Sonnet 5 6687f2f67c Fix: PlayerExpansionController.dragEnd rät Öffnen/Schließen nicht mehr aus Vorzeichen
Bug (Code-Review, P0-nah): dragEnd() wählte die Entscheidungslogik allein
über das Vorzeichen von strecke. Bei bereits vollständig offenem Player
klappte ein schwaches, versehentliches Fingerzittern nach oben (kleine
negative Strecke, weit unter der Wisch-Schwelle) den Player ungewollt zu,
weil das Vorzeichen in den ÖFFNEN-Zweig führte statt in den (korrekten)
Schließen-Zweig, der bei so einer schwachen Geste keine Wirkung hätte.

dragEnd() bekommt jetzt den Pflichtparameter `ausVollbild`, der den
Aufrufer-Kontext explizit benennt statt ihn zu erraten. mini_player.dart
(Öffnen-Geste) ruft mit ausVollbild: false, now_playing_screen.dart
(Schließen-Geste) mit ausVollbild: true.

Test zuerst (RED mit der alten 3-Parameter-Signatur reproduziert, dann
GREEN nach dem Fix): test/player/player_expansion_controller_test.dart.
Bestehende Tests (player_expansion_controller_test.dart,
home_shell_expansion_test.dart) an die neue Signatur angepasst.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SM6e73V9ZethPiqHSocxUA
2026-08-29 15:36:46 +02:00
Hermes (Server) e2d2a01965 Vollbild-Player: Schließgeste live an progress gekoppelt, symmetrisch zum Öffnen 2026-08-29 14:59:20 +02:00
Hermes (Server) 92148bc97c Mini-Player-Wischgeste steuert PlayerExpansionController statt lokalen Versatz 2026-08-29 14:54:05 +02:00
Hermes (Server) a1eb5831e0 WanderndesCover ersetzt Hero-Übergang: Cover wandert live mit progress 2026-08-29 14:50:40 +02:00
Hermes (Server) 9d0cb90a51 NowPlayingScreen: von Scaffold/Route zu Overlay-Inhalt in HomeShell umgebaut 2026-08-29 14:45:41 +02:00
Hermes (Server) 2baac9ed12 PlayerExpansionController: optischer Öffnen-Fortschritt für den Vollbild-Player 2026-08-29 14:19:29 +02:00
Hermes (Server)andClaude Sonnet 5 2cabe36a6c Zuletzt zeigt zuletzt Gehörtes statt neu Hinzugefügtes; Herz sitzt jetzt neben dem Songnamen
- MeloDb.watchRecentlyPlayed(): wertet PlaybackHistory statt dateAddedMs aus,
  ein mehrfach gehörter Song erscheint nur einmal, an der Position seines
  jüngsten Abspielens (Subquery mit GROUP BY songId / MAX(playedAtMs)).
- "Zuletzt" (Karte + RecentlyPlayedScreen, vormals RecentlyAddedScreen) in
  my_music_screen.dart nutzt jetzt watchRecentlyPlayed() statt watchRecent().
- Herz im Vollbild-Player: aus der AppBar-actions-Row entfernt, sitzt jetzt
  direkt neben dem Songtitel in _Angaben (now_playing_screen.dart).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012A2pmbnVNPiHdyf2GW8eLP
2026-08-29 11:24:42 +02:00
Hermes (Server) 648b22bfa7 AudioSession konfigurieren: Audio-Fokus bleibt beim automatischen Songwechsel erhalten 2026-08-27 14:53:59 +02:00
Hermes (Server)andClaude Sonnet 5 7dbea128e0 Shuffle/Wiederholung: PlaybackState meldet echten Zustand statt immer "aus"
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012A2pmbnVNPiHdyf2GW8eLP
2026-08-27 14:49:32 +02:00
Hermes (Server) c2f31dee01 UX-Simulation, Teil 3: Abbrechen, Badge, Versionsnummer, Wisch-Strecke
Die vier zuletzt zurückgestellten Punkte aus der UX-Simulation:

- Abbrechen-Knopf für hängende YouTube-Downloads (Future.any gegen ein
  Abbruch-Signal, Anfrage läuft im Hintergrund aus statt die App zu
  blockieren)
- Punkt am inaktiven Download-Unterreiter, wenn dort etwas im Hintergrund
  läuft (SubTabs bekommt einen optionalen badges-Parameter)
- Versionsnummer unter "Über Melo" (neue Abhängigkeit: package_info_plus)
- Mini-Player führt beim Wischen jetzt sichtbar mit und wertet zusätzlich
  zum Schwung auch die zurückgelegte Strecke aus — ein langsames, aber
  weites Wischen wurde vorher komplett ignoriert

Dabei drei bestehende Widget-Tests korrigiert, die den Download-Tab ohne
Provider<DownloadService> aufbauten (hauptmenue_test.dart,
home_shell_test.dart, online_screen_test.dart) — lief bisher nur, weil
niemand danach fragte; das neue Badge tut das jetzt app-weit.

550 Tests grün (vorher 539), flutter analyze ohne Befund.
2026-08-25 10:25:43 +02:00
Hermes (Server) 5645660a2d UX-Simulation, Teil 2: Player-Feinschliff, Downloads, Einstellungen
Zweiter Block aus der UX-Simulation (39 Funde insgesamt):

- Songtext-Icon deaktiviert statt totem Knopf, wenn kein Ziel bekannt ist
- Sleep-Timer-Restzeit jetzt auch im Sheet sichtbar, nicht nur im Tooltip
- Drag-Handle in der Warteschlange, Wisch-nach-unten schließt den
  Vollbild-Player
- Fehlender Tooltip am Songtext-Sheet-Schließen-Knopf ergänzt
- YouTube-Download: Zeitüberschreitung meldet sich anders als toter Server
- Server-Tab zeigte beim Öffnen kurz fälschlich "nicht verbunden"
- Einstellungen: Favoriten-/Playlisten-Sync von "Speicher" zu
  "Musikserver" verschoben, Navidrome-Eintrag erklärt jetzt seinen Zweck
- Kommentar in geteilte_adresse.dart korrigiert (Verhalten unverändert,
  dafür gibt es einen Test)

Bewusst zurückgestellt: ServerFavoriteButton bleibt lokal-only (dokumentierte
Architektur-Entscheidung, siehe G3 im UX-Review), Live-Wischgeste am
Mini-Player, YouTube-Download-Abbrechen, Download-Badge im inaktiven
Unterreiter, Versionsnummer (neue Abhängigkeit nötig).

539 Tests grün (vorher 537), flutter analyze ohne Befund.
2026-08-25 09:49:47 +02:00
Hermes (Server) 60c57577a3 Cover-Farbe, Teilen aus YouTube, Android Auto, geschärfte Typografie
P3-Liste aus dem UX-Review: Player nimmt Cover-Farbe auf, "Teilen" aus
YouTube trägt die Adresse in den Downloader ein, Android Auto blättert
durch die Bibliothek, Laufweite der Typografie geschärft, Überschriften
vereinheitlicht. 533 Tests grün, flutter analyze ohne Befund.
2026-08-25 09:09:27 +02:00
Hermes (Server)andClaude Opus 5 490e73ce41 Politur: Wischen, wandernde Cover, Alphabet-Sprung, Mehrfachauswahl
Die P2-Liste aus dem Review — Kleinigkeiten, die man einzeln nicht
vermisst und zusammen sofort merkt.

- Das Coverbild wandert beim Oeffnen der Vollbild-Wiedergabe an seinen
  Platz, statt dass ein neuer Bildschirm aufspringt.
- Wischen am Mini-Player: nach oben oeffnen, seitwaerts Titelwechsel. Mit
  Schwelle, weil der Mini-Player zugleich ein Knopf ist und der Finger
  beim Tippen fast immer ein paar Pixel wandert. Nach unten bewusst
  unbelegt, damit niemand die Wiedergabe versehentlich wegschiebt.
- Kuenstlerliste mit Bildern und Albenzahl; dahinter eine Kuenstlerseite
  mit seinen Alben und darunter allen Titeln (dafuer neu: groupByAlbum).
  Bei nur einem Album entfaellt die Zwischenstufe.
- Alphabet-Sprung am rechten Rand, nur bei Namenssortierung und ab 30
  Titeln. Umlaute zaehlen zu ihrem Grundbuchstaben, Ziffern und Zeichen
  unter "#" — dieselbe Regel wie die Sortierung.
- Mehrfachauswahl per langem Druck, mit "Zur Warteschlange" und "Zu
  Wiedergabeliste".
- Abgeschaltete Systemanimationen gelten jetzt ueberall, nicht nur bei den
  Lauf-Balken.
- Querformat und Tablet: der Player legt Cover und Bedienung nebeneinander
  und begrenzt die Textbreite.

Beim Bauen gefunden: der Alphabet-Sprung landete bei absteigender
Sortierung am Ende eines Buchstaben-Blocks statt an seinem Anfang.

Aus dem Code-Review nachgebessert, sieben Punkte: die Alphabet-Leiste
wurde breiter, je weniger Buchstaben sie hatte (bei einem einzigen 528 dp
— die Songliste waere verschwunden); die neue Kuenstlerseite baute alle
Titel sofort und oeffnete damit bei 300 Titeln ebenso viele Abos; die
Bildlauf-Animation im Songtext stuerzte bei abgeschalteten
Systemanimationen ab; die feste Zeilenhoehe war 64 statt 72 und die
Schriftskalierung an 1.0 statt an einer echten Groesse gemessen; bereits
enthaltene Titel wurden in Wiedergabelisten verschoben statt uebersprungen
und trotzdem mitgezaehlt; ein Fehler im Stapel brach still ab; und die
Zurueck-Geste verliess den Bildschirm, statt die Auswahl zu beenden.

Selbst gefunden: in der Kuenstlerliste hing der Datenbank-Strom an
CategoryService und wurde bei jeder Kategorie-Meldung neu aufgesetzt.

Nebenbei aufgeklaert, warum ein neuer Widget-Test endlos hing: Aufbau in
setUp laeuft ausserhalb der Testzone, die drift-Stroeme liefen dort in
echter Zeit, waehrend pumpAndSettle in Testzeit auf sie wartete. Aufbau in
den Testkoerper verschoben. Der aeltere Haenger im BACKLOG hat eine andere
Ursache (db.close() dort nachweislich ausgeschlossen); die Spur ist
notiert.

500 Tests gruen (vorher 457), 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-24 15:09:46 +02:00
Hermes (Server)andClaude Opus 5 0d76d8cc58 Album-Ansicht, mitlaufender Songtext, Cover-Cache, Server-Favoriten
Die letzten vier Punkte aus dem Review-Bericht.

G2 — Server-Bereich: Ein Tipp auf ein Album spielte sofort alles ab;
hineinsehen ging nicht. Jetzt Album- und Kuenstler-Ansicht mit Cover,
Kuenstler, Titelzahl, Gesamtdauer, "Abspielen"/"Zufaellig" und der
Titelliste; Antippen startet an dieser Stelle. Dazu "Mehr Alben laden" —
die Liste hoerte bei 50 auf, ohne dass etwas darauf hinwies.
NICHT umgesetzt: den Server-Bereich aus dem Download-Tab loesen (das
widerspricht Commit 34d6d8e, "Auf Wunsch von Dustin") und eine
Server-Suche (die Suche bleibt ausdruecklich rein lokal).

G8 — Songtext: Traegt der Text Zeitmarken, laeuft er mit; die aktive Zeile
ist hervorgehoben, Antippen springt an die Stelle. Ohne Zeitmarken bleibt
es eine Textwand — die App schaut nach, statt zu raten.

G10 — Cover bleiben liegen. Eigener Ordner, einmal geholt. Der Schluessel
haengt an der Album-Kennung, nicht an der Adresse: die traegt bei jedem
App-Start ein neues Token, und der Ordner waere sonst gewachsen, ohne je
einen Treffer zu liefern. Keine neue Abhaengigkeit.

G3 — Das Herz ist bei Server-Titeln nicht mehr ausgegraut, sondern
favorisiert am Server (star/unstar) — dort, wo der Favoriten-Abgleich sie
auch wieder abholt. Offen bleibt der schwere Teil: Wiedergabezaehler,
Historie und Kategorien braeuchten fuer Server-Titel eine Zeile in der
Bibliothek.

Aus dem Code-Review nachgebessert (sieben Punkte): das Herz behielt beim
Titelwechsel seinen Zustand; recycelte Listenzeilen zeigten kurz das Cover
der vorherigen; der neue Songtext bekam bei Server-Titeln die
Abspiel-Adresse statt der Server-Kennung und fand deshalb nie einen Text;
ein einmal fehlgeschlagenes Cover blieb die ganze Sitzung grau; "Server
nicht erreichbar" sah aus wie "Album hat keine Titel", womit der
Wiederholen-Knopf unerreichbar war; die Kuenstler-Ansicht waere immer leer
geblieben, weil getArtist die Titel gar nicht mitliefert; und zwei
gleichzeitige Cover-Abrufe konnten ein zerschnittenes Bild hinterlassen.

Selbst gefunden: das Haekchen "schon geladen" fragte pro Zeile die Platte
ab, bei jedem Positionswechsel neu. Jetzt einmal je Bildschirm.

457 Tests gruen (vorher 433), 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 21:53:31 +02:00
Hermes (Server)andClaude Opus 5 b42e045e67 Alben und Kuenstler offline mitnehmen; Zwischenspeicher bekommt eine Grenze
Bisher landete Musik nur zufaellig auf dem Geraet: was man abspielte, blieb
nebenbei liegen. Gezielt "dieses Album fuer die Zugfahrt" gab es nicht. Und
dieser Zwischenspeicher wuchs unbegrenzt — es gab nur "alles loeschen".

Zwei getrennte Ablagen. Bewusste Downloads liegen NICHT im Cache-Ordner:
den darf Android bei Speichernot jederzeit selbst leeren, und genau das
darf einem mitgenommenen Album nicht passieren. Sie liegen deshalb neben
der Datenbank im Support-Ordner. Die Grenze gilt nur fuer den
Zufalls-Cache; Downloads sind unbegrenzt und werden nie verdraengt.

- Download-Pfeil je Album und Kuenstler, mit Fortschritt und Abbruch
- Rueckfrage ab 30 Titeln samt Groessenschaetzung
- Obergrenze wahlbar (aus / 512 MB / 1 / 2 / 4 / 8 GB), Standard 2 GB;
  am laengsten nicht Gehoertes weicht zuerst
- Liste "Heruntergeladen" mit Titel, Kuenstler, Groesse, einzeln oder alle
  entfernbar (Schema 10)
- Wiedergabe nimmt erst den Download, dann den Zwischenspeicher

Aus dem Code-Review nachgebessert, vier ernste Punkte: "aus" (0 MB)
schaltete das Aufraeumen ab statt das Zwischenspeichern und liess den
Cache unbegrenzt wachsen; wer sich erst waehrend der Sitzung am Server
anmeldete, konnte gar nichts laden; die Verdraengung konnte Dateien
loeschen, die die laufende Warteschlange schon als Quelle eingetragen
hatte (Abbruch mitten im Album, ohne Rueckfall aufs Streamen); und ein
heruntergeladener Titel wurde beim Abspielen ein zweites Mal in den Cache
geholt. Dazu sechs kleinere und der "zuletzt gehoert"-Stempel, der beim
Aufbau der Warteschlange statt beim Abspielen gesetzt wurde.

Nebenbei aufgeraeumt, weil die Dateien ohnehin offen waren: keine Emoji
als Knoepfe mehr, kein Colors.redAccent und kein grey.shade mehr im
ganzen Projekt, und die Einstellungen heissen nicht mehr "Settings".

433 Tests gruen (vorher 411), 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 21:31:53 +02:00
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
Hermes (Server)andClaude Opus 5 d15867d962 Positions-Timer ueberlebt stop(); K4 im Review-Bericht richtiggestellt
stop() brach _positionRecordTimer ab. Der Timer entsteht nur einmal im
Konstruktor, war also fuer den Rest der Sitzung weg. Da auch das System
stop() aufruft (Benachrichtigung wegwischen), merkte sich die App danach
keine Hoerposition mehr und scrobbelte nicht — bis zum Neustart und ohne
Hinweis. Der Timer steigt ohnehin selbst aus, solange nichts laeuft;
abgebrochen wird er jetzt nur noch in dispose().

Dazu eine Korrektur am eigenen Review: K4 behauptete, die Alben-Achse sei
verlorengegangen. Sie wurde bewusst durch "Kategorie" ersetzt — der
Album-Name ist in Melo die erste Kategorie eines Titels
(category_list.dart:12, kategorienAusTags). Was wirklich fehlt, ist die
Album-Detailansicht und die Tracknummer, die gar nicht eingelesen wird.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xAHJTJM6UUqmjUgk1PUEd
2026-08-23 18:51:18 +02:00
Hermes (Server)andClaude Opus 5 e6d5f1c9ee Fuenf P0-Fehler behoben: Benachrichtigung, Offline, Cover, Warteschlange
Ergebnis einer Durchsicht der App gegen den Massstab "Apple Music".
Alle fuenf Punkte waren keine fehlenden Funktionen, sondern Versprechen,
die die Oberflaeche gab und der Code nicht hielt.

- POST_NOTIFICATIONS fehlte im Manifest. Auf Android 13+ blieb die
  Wiedergabe-Benachrichtigung dadurch unsichtbar. Das Recht wird erst
  erfragt, wenn wirklich ein Titel laeuft.
- Der Offline-Schalter wurde nirgends gelesen. Er greift jetzt an allen
  drei Wegen in die Warteschlange; uebersprungene Titel werden benannt.
- CoverImage kannte nur file://, und der Server-Browser gab die
  Cover-Adresse gar nicht erst mit. Server-Titel waren deshalb immer ein
  graues Notensymbol.
- Warteschlange entfernen/umsortieren baute die ganze Queue neu auf:
  hoerbarer Aussetzer und doppelte Wiedergabe-Zaehlung. Jetzt mutiert nur
  der betroffene Eintrag, die Buchfuehrung steht vor dem Player.
- Das Herz bei Server-Titeln legte einen Favoriten mit leerer Kennung an,
  der nie in der Liste auftauchte. Es ist jetzt ausgegraut.

Nachgebessert aus dem Code-Review: Offline-Meldung an die Stelle geholt,
wo sie greifen kann; Queue-Buchfuehrung vor den Player gezogen;
Fehlerpfad fuer die Berechtigungs-Abfrage ergaenzt.

360 Tests gruen (vorher 328), flutter analyze ohne Befund, Release-APK
gebaut und POST_NOTIFICATIONS darin nachgeprueft.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xAHJTJM6UUqmjUgk1PUEd
2026-08-23 18:34:26 +02:00
Hermes (Server)andClaude Opus 5 bc861f6087 UI-Politur: Kontrast, Abstaende, Typografie, Bewegung
Kein Redesign — Schwarz/Rot, 4 Tabs und Lieder/Kategorie bleiben. Grundlage
sind die Design-Skills: ui-ux-pro-max (Barrierefreiheit, Touch, Typografie,
Bewegung), ui-design/mobile-android (Material 3) und die Token-Disziplin
aus Hue.

Behobene Maengel (messbar, nicht Geschmack):
- Text in Colors.white38 an 17 Stellen = 3,4:1 Kontrast, WCAG verlangt
  4,5:1. Ersetzt durch drei benannte Stufen (text1 15,9:1 / text2 8,8:1 /
  text3 4,9:1). theme_kontrast_test.dart rechnet die Verhaeltnisse bei
  jedem Lauf nach, statt sie zu behaupten.
- SubTabs waren ~38 dp hoch (Material 3 verlangt 48), ohne Ripple und ohne
  Uebergang. Jetzt 48 dp, InkWell, AnimatedContainer, und die Auswahl wird
  Vorlesehilfen als Zustand gemeldet (Semantics.selected).
- mini_player.dart: Expanded UM eine feste Hoehe herum — zwei
  widerspruechliche Angaben. Entschaerft.
- Emoji in Bedienelementen der Einstellungen (jeweils neben einem echten
  Icon) entfernt.

Neu in shared/theme.dart: MeloSpace (8er-Raster), MeloRadius, MeloMotion,
Farbrollen text1/2/3 + border + hairline, minTouchTarget, vollstaendiges
textTheme (7 Stufen) und Themes fuer Listen, Sheets, Snackbars,
Fortschritt, Trenner.

318 Tests gruen (23 neue, 1 uebersprungen), flutter analyze ohne Befund.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpPu4nuKjKKeX1RpdDeX81
2026-08-21 12:40:25 +02:00
Hermes (Server)andClaude Opus 5 9fa027fca1 Fix: Lokale Wiedergabe + neuer Geraete-Abgleich (Handy <-> Server)
BUG 1 — Lieder vom Handy waren nicht abspielbar
("Wiedergabe fehlgeschlagen: (0) Source error" / "Loading interrupted")

Wurzel-Ursache: MeloAudioHandler.loadPlaylist hat jeden Warteschlangen-
Eintrag durch NavidromeService.streamAndCacheToLocal(item.id) geschickt,
sobald Zugangsdaten existierten. item.id ist aber NIE eine Navidrome-Song-ID
— lokal ist es file:///storage/..., beim Server die fertige Stream-Adresse.
Der Server bekam also 'file:///...' als Song-ID, antwortete mit einem Fehler,
und diese Fehlerantwort wurde an just_audio weitergereicht (Source error) —
bzw. als .mp3 in den Cache geschrieben, wodurch der Titel dauerhaft kaputt
blieb.

Zweite Ursache: die Schleife lud die GANZE Warteschlange seriell vorab
herunter (30s Timeout je Titel), bevor setAudioSources lief. Bei hunderten
Titeln startete die Wiedergabe deshalb nie; ein zweiter Tipp brach den
laufenden Ladevorgang ab ("Loading interrupted").

Fix:
- Server-Titel tragen ihre ID in MediaItem.extras['navidromeId'] statt sie
  aus der Abspiel-Adresse zu raten. Neue reine Funktionen navidromeIdOf,
  songIdOf, nutztServerCache, quelleFuer.
- loadPlaylist baut die Quellen ohne Netzzugriff; Caching des laufenden
  Titels im Hintergrund (unawaited).
- Resume/Scrobble nur noch mit der jeweils passenden ID (Server bzw. lokal).
- ladeInCache() ersetzt streamAndCacheToLocal(): .part-Datei, Pruefung des
  Inhaltstyps (istAudioAntwort), stabiler Cache-Schluessel ueber die
  Song-ID statt der Stream-Adresse (die trug Token+Salt und war je Sitzung
  anders — der Cache war nie wiederauffindbar), Client wird geschlossen.

BUG 2 — kein Abgleich zwischen Handy und Server

Neu: services/melo_cloud_service.dart + services/sync_service.dart gegen
cloud.baka-net.de (Bearer-JWT ueber BakaAuth). Server-Titel herunterladen
(offline verfuegbar), eigene Dateien hochladen, Loeschungen in beide
Richtungen (Tombstones), Favoriten und Wiedergabe-Verlauf. Automatisch beim
App-Start und bei Rueckkehr in die App (max. alle 15 Min), plus Knopf unter
Einstellungen -> Geraete-Abgleich. Reine Planungsfunktion planeSync().

Bewusst NICHT ueber Navidrome: die Subsonic-API kennt keinen Upload-
Endpunkt. Navidrome bleibt die Streaming-Bibliothek, die Melo-Cloud ist der
gemeinsame Speicher.

DB-Schema 8: songs.cloud_id verbindet Geraet und Server.

Nebenbei behoben (blockierte Build bzw. Tests):
- database.g.dart war veraltet — das Projekt liess sich nicht uebersetzen.
- metadataEdited wurde nirgends gesetzt/beachtet: von Hand korrigierte
  Metadaten wurden vom naechsten Scan ueberschrieben. Jetzt in beiden
  Scans respektiert; metadatenUebernahme() setzt die Markierung.
- song_detail_sheet_test.dart haengt beim Oeffnen des Modal-Sheets und
  blockierte den gesamten Testlauf — vorerst uebersprungen (TODO im Code);
  der Zweck wird von metadaten_uebernahme_test.dart abgedeckt.

Enthaelt ausserdem die bis dahin nicht committete Arbeit der Vorsitzung
(Musikerkennung/ACRCloud, MusicBrainz-Metadaten, MediaStore-Datentraeger).

267 Tests gruen (1 uebersprungen), flutter analyze ohne Befund.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FpPu4nuKjKKeX1RpdDeX81
2026-08-21 08:32:32 +02:00
dustinandClaude Opus 5 4ebd0a8dd4 Songtext aus Datei-Tags, Warteschlangen-Aktionen, Kategorie-Cover im
Sperrbildschirm und ReplayGain

- DB-Schema 6: lyrics + gain_db
- Songtext bevorzugt lokalen Tag, Server nur als Rueckfall; Song-ID-Bug
  beim Lyrics-Aufruf behoben
- "Als Naechstes spielen" / "Zur Warteschlange hinzufuegen" ohne Eingriff
  in Favoriten oder Wiedergabelisten
- songToMediaItem nimmt eine Cover-Vorgabe: Sperrbildschirm zeigt das
  Kategorie-Cover
- ReplayGain: Tags werden gelesen, laute Titel abgesenkt, abschaltbar

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011snPXPafPC5i87o68H14W7
2026-08-20 18:41:36 +02:00
dustinandClaude Opus 5 1325badf90 Equalizer (nativ), Dolby-Atmos-Zugang und Auto-Wiedergabe bei Kopfhoerern
- Native Bruecke AudioEffectsBridge.kt: Equalizer + BassBoost + Virtualizer
  auf der Audio-Session des Players, inkl. System-Klangeffekt-Panel
- Dart-Seite AudioEffects: Presets, Baender, Bass/Immersiv, persistiert
- EqualizerScreen nach Vorlage inkl. Drehregler (CustomPainter)
- Auto-Wiedergabe bei verbundenen Kopfhoerern (Klinke + Bluetooth)
- MODIFY_AUDIO_SETTINGS im Manifest
- Einstellungen: neuer Bereich "Ton"

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011snPXPafPC5i87o68H14W7
2026-08-20 18:35:06 +02:00
dustinandClaude Opus 5 2667613a2e Neue Melo-UI Phase 1: 4 Tabs (Meine Musik/Suche/Favoriten/Download) + Sortierung
- Meine Musik als Startbildschirm ohne Topbar: Kopfzeile (Einstellungen,
  Suche, Musikerkennung), Schnellzugriffe, Shuffle + Sortieren, Liederliste
- Sortierung nach Zuletzt hinzugefuegt / Name (A-Z-#) / Wie oft abgespielt,
  auf- und absteigend, pro Liste gespeichert (SortStore)
- Favoriten als eigener Tab mit gleicher Shuffle-/Sortier-Leiste
- Download-Tab uebernimmt den Navidrome-Server-Browser
- DB-Schema 3: playCount, gezaehlt beim Titelstart
- Theme auf #0B0B10 / #c0392b
- Scan-Aktionen aus der entfallenen Topbar jetzt in den Einstellungen

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011snPXPafPC5i87o68H14W7
2026-08-20 18:00:53 +02:00
Hermes (Server)andClaude Haiku 4.5 c5b46e415a 🎵 Feature: Enhanced MiniPlayer (Phase 2, Part 8/X) — Now-Playing Widget
- Größeres MiniPlayer-Widget auf allen Non-Player-Screens (80px statt 60px)
- Größeres Album-Cover (64px statt 44px) mit besserer Sichtbarkeit
- Skip-Buttons (⏮️ Vorheriges + ⏭️ Nächstes) für schnelle Navigation
- Progress-Bar zeigt Wiedergabe-Position in Echtzeit
- Besseres Styling + Schrift-Gewicht für Titel
- Funktioniert wie Home-Widget: zeigt überall was gerade spielt

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
2026-08-19 19:33:18 +02:00
Hermes (Server)andClaude Haiku 4.5 f93b2d35da 📊 Feature: History-Sync vom Navidrome-Server (Phase 2, Part 5/X)
- Neue NavidromeService APIs: scrobble() + getBookmark()
- Lädt Wiedergabe-Position vom Server beim Song-Start (Subsonic-API)
- Sendet Position alle 5s zum Server wenn verbunden
- Priorisiert Server-Position über lokale Position
- Full Sync: lokal speichern + Server synchen parallel

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
2026-08-19 19:26:41 +02:00
Hermes (Server) 5c9a7dad2f feat(phase2): Auto-Caching beim Musikstreaming (P0)
IMPLEMENTATION:
- NavidromeService.streamAndCacheToLocal(): Stream + gleichzeitig cachen
- Cache-First-Logik: Wenn Cache existiert → lokal spielen
- AudioHandler erweitert: loadPlaylist() nutzt Auto-Caching
- CacheManager Integration in AudioHandler

FLOW:
1. User spielt Album ab → streamt vom Server
2. Während des Abspielens → lokal in Cache speichern
3. Nächstes Mal: Lokal spielen (schneller, Offline möglich)

Phase 2 P0 COMPLETE! 
Tests: 70/70 
2026-08-19 19:00:12 +02:00
Hermes (Server) 0e5b346f68 feat(player): Lyrics-Anzeige + Shuffle/Repeat Buttons
LYRICS:
- Lyrics-Button im Now-Playing Screen (📝 Icon)
- Bottom-Sheet mit Lyrics vom Server (getLyrics() API)
- Fallback: 'Keine Lyrics verfügbar'
- Lyrics-Klasse für Datenmodell

SHUFFLE/REPEAT:
- Shuffle-Button (schon implementiert!) zeigt aktiven Status rot
- Repeat-Button (Off → All → One → Off cycle)
- Beide via AudioHandler.setShuffleMode() / setRepeatMode()
- Integration in _Controls (Player-UI)

Phase 1 P1-Features jetzt funktionsfähig!
Tests: 70/70 
2026-08-19 18:49:34 +02:00
Hermes (Server)andClaude Haiku 4.5 edc93224b0 feat(player): Wiedergabeposition merken und beim Laden fortsetzen
- MeloAudioHandler bekommt MeloDb per Konstruktor injiziert
- Positions-Timer (alle 5s) schreibt db.recordPlayback(songId, positionMs)
  während aktiv abgespielt wird (songId aus mediaItem.extras['songId'])
- loadPlaylist liest db.lastPosition() für den Start-Song und seekt vor
  dem play() an die gespeicherte Position, statt immer von vorne zu starten
- shouldResumeAt() als reine, testbare Funktion extrahiert: kein Resume bei
  Position 0 oder innerhalb der letzten 3s einer bekannten Track-Dauer
- dispose() zum gefahrlosen Abbrechen des Positions-Timers in Tests ohne
  vollen App-Lifecycle (super.stop() lässt sich in Widget-Tests wegen der
  aktiven playbackState-Pipe nicht gefahrlos aufrufen)
- library_screen_test.dart: MeloAudioHandler-Konstruktion angepasst (db
  jetzt Pflichtparameter) + handler.dispose() im Teardown

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 18:17:26 +02:00
Hermes (Server)andClaude Haiku 4.5 d267bb9658 feat(player): Warteschlangen-Ansicht mit Springen, Entfernen, Umsortieren
- Neuer QueueScreen: zeigt handler.queue, hebt aktuell spielenden Titel
  hervor (fett/rot), Antippen springt per skipToQueueItem
- Entfernen und Umsortieren ohne Änderung an MeloAudioHandler: Queue wird
  clientseitig neu berechnet und per loadPlaylist neu geladen; Position
  wird nur wiederhergestellt, wenn der abgespielte Titel unverändert blieb
- Reine Queue-Logik (removeFromQueue/reorderQueue/newPlayingIndex) als
  eigenständige, ohne AudioHandler testbare Funktionen extrahiert
- Warteschlangen-Button in der Now-Playing-AppBar verlinkt den neuen Screen

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:55:01 +02:00
Hermes (Server)andClaude Haiku 4.5 2712ace665 feat(player): Now Playing als Tab statt Push-Screen, Favoriten-Herz
- songToMediaItem trägt jetzt die Song-UUID in extras['songId'], damit
  Downstream-Code (Favoriten-Button, später Playback-History) den echten
  Song wiederfinden kann statt nur die file-URI zu haben
- NowPlayingScreen: Schließen-Button entfernt (AppBar-leading), da der
  Screen jetzt als eigener Tab läuft statt gepusht zu werden
- FavoriteButton für den aktuellen Song in die AppBar-Actions eingebaut,
  reaktiv über einen verschachtelten StreamBuilder<MediaItem?>; ohne
  gültige songId (z.B. nichts spielt) wird kein Button gerendert

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-18 17:01:31 +02:00
Dustin-Mike Jens HähnelandClaude Opus 4.8 51c2229148 Add sleep timer feature
Timer stops playback (pause) after 5/10/15/30/60 min. Wall-clock
deadline is self-correcting against OS throttling; 1Hz tick drives
the countdown UI. Cancelled in stop() so no timer resurrects a paused
player after teardown.

- sleep_timer.dart: isolated time logic, injectable clock for testing
- audio_handler.dart: sleepTimer field + cancel() in stop()
- now_playing_screen.dart: AppBar button + bottom sheet
- 10 unit tests via mocked clock

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 20:06:08 +02:00
Dustin-Mike Jens HähnelandClaude Haiku 4.5 d04783073f Fix critical blocker issues and improve tests
## Critical Blockers Fixed
- Remove playbackState.addError() which broke audio_service permanently
  - Move error handling from audio_handler to song_list where UI context exists
  - Display error in SnackBar instead of destroying playback stream

- Add escapeChar parameter to LIKE queries for correct wildcard handling
  - Fixes broken search for titles with _ or % characters

- Add dismissScanError() method to allow closing error banner
  - Previously banner couldn't be dismissed (missing notifyListeners())

## Improvements
- Remove dead livePaths list, use companions.isNotEmpty instead
- Add initial progress update to show total files when scan starts
- Add tooltip to now_playing_screen Zurück button
- Add proper error handling in song_list with async/await

## Testing
- Add regression test for CRITICAL-1: empty scan doesn't delete songs
- Add regression test for CRITICAL-2: tombstoned files restore on re-scan
- All 11 tests pass

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-16 19:09:00 +02:00
Dustin-Mike Jens HähnelandClaude Haiku 4.5 4fa8d12fa0 Fix critical data loss bugs and improve error handling
## Critical Fixes
- Fix CRITICAL-1: Empty scans no longer delete entire library
  - Use timestamp-based tombstone logic instead of path-based
  - Only mark missing if livePaths is not empty

- Fix CRITICAL-2: Re-scanned files now return from deleted state
  - Add deleted=false to SongsCompanion.insert in both scanners

## High Priority Fixes
- HIGH-1: Fix SQLite variable limit crash on large libraries
  - Replace isNotIn() with timestamp comparison (O(1) not O(n))

- HIGH-2: Fix UI freezing during scan
  - Throttle progress updates (every 50 files or end of scan)

- HIGH-3: Add error handling for scan failures
  - Wrap scanFolders/scanAndroidMediaStore in try/catch
  - Display scanError banner in LibraryScreen

- HIGH-4: Add error handling for playback failures
  - Wrap loadPlaylist in try/catch

## Medium Priority Fixes
- MEDIUM-1: Replace ! with ?? to handle unknown ProcessingState
- MEDIUM-2: Escape LIKE wildcards in search queries
- MEDIUM-4: Display StreamBuilder errors instead of treating as empty
- MEDIUM-8: Add tooltips to all IconButtons for accessibility
- Add doc comments to public database APIs

## Testing
- Add regression tests for CRITICAL-1 and CRITICAL-2
- All 9 existing tests pass

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-16 19:04:54 +02:00
Dustin-Mike Jens Hähnel b5ab7bd9dd Initial commit 2026-08-16 18:46:06 +02:00