diff --git a/.ui-design/reviews/melo_vollwertigkeit_20260823.md b/.ui-design/reviews/melo_vollwertigkeit_20260823.md index a319c4b..2108314 100644 --- a/.ui-design/reviews/melo_vollwertigkeit_20260823.md +++ b/.ui-design/reviews/melo_vollwertigkeit_20260823.md @@ -18,8 +18,8 @@ Erklär-Kommentare an genau den Stellen, wo früher Bugs saßen. Das ist deutlic der Durchschnitt eines Hobby-Projekts. Als **vollwertige Musik-App im Sinne von Apple Music ist sie es aber noch nicht.** Es fehlen -nicht Details, sondern drei tragende Säulen: **Alben als Browse-Achse**, **echtes Offline- -Management** und **Cover-Art für Server-Titel**. Dazu kommen zwei stille Fehler, die man +nicht Details, sondern drei tragende Säulen: **die Album-Detailansicht** (siehe die Korrektur +unter K4), **echtes Offline-Management** und **Cover-Art für Server-Titel**. Dazu kommen zwei stille Fehler, die man der App nicht ansieht: der Offline-Schalter tut nichts, und ohne `POST_NOTIFICATIONS` erscheint auf Android 13+ keine Wiedergabe-Benachrichtigung. @@ -89,23 +89,41 @@ bleibt. --- -### K4 — Alben fehlen als Browse-Achse -**Ort:** `lib/library/song_grouping.dart` · `lib/library/my_music_screen.dart:88` +### K4 — ~~Alben fehlen als Browse-Achse~~ → **korrigiert: Album-Detailansicht fehlt** +**Ort:** `lib/library/category_list.dart:12` · `lib/library/categories.dart:26` **Kategorie:** Funktionsumfang -`Songs.album` ist eine Textspalte, aber es gibt weder eine Alben-Liste noch einen -Album-Detail-Screen. `song_grouping.dart` kann nur `groupByArtist`. Die Unterreiter -heißen **Lieder / Kategorie / Künstler** — im Referenz-Screenshot -(`ui-referenz/bugs/img_2d5761551808.jpg`, das ist Melo selbst) stand dort noch -**Songs / Künstler / Alben**. Die Achse ist also verlorengegangen. +> **Korrektur vom 2026-08-23 (nach der P0-Runde).** Die ursprüngliche Fassung +> dieses Punktes war falsch. Sie behauptete, die Alben-Achse sei +> „verlorengegangen". Sie wurde **bewusst ersetzt**: `category_list.dart` +> dokumentiert das im Klassenkommentar, und `kategorienAusTags` setzt den +> Album-Namen beim Scan als **erste Kategorie** jedes Titels. Der Reiter +> „Kategorie" *ist* der Alben-Reiter — nur allgemeiner, weil ein Titel +> mehreren Kategorien angehören darf. Der Fehler entstand, weil nur +> `song_grouping.dart` und `my_music_screen.dart` gelesen wurden. -**Wirkung:** Album ist neben Playlist die wichtigste Einheit, in der Menschen Musik -denken. Ohne sie ist "Bibliothek" eine flache Titelliste — brauchbar bei 200 Titeln, -unbrauchbar bei 5.000. +**Was wirklich fehlt**, ist nicht die Achse, sondern die **Ansicht eines +Albums als Album**: + +1. **Kein Album-Kopf.** `_KategorieLiederScreen` ist eine `AppBar` über einer + Titelliste. Es fehlen großes Cover, Künstler, Jahr, Titelzahl, + Gesamtdauer und ein „Abspielen / Zufällig"-Paar. Bei Apple Music trägt + genau dieser Kopf die Identität eines Albums. +2. **Falsche Reihenfolge.** Innerhalb einer Kategorie wird nach Titel + sortiert. Ein Album gehört in **Tracknummer-Reihenfolge** — sonst beginnt + jedes Konzeptalbum mit dem Lied, das alphabetisch vorn steht. +3. **Die Tracknummer wird gar nicht gelesen.** Verifiziert: weder `Songs` noch + der Scan kennen `trackNumber`. Punkt 2 braucht also zuerst eine + Schema-Erweiterung plus Auslesen des Tags. +4. **Kein Weg vom Künstler zu seinen Alben.** `_ArtistSongsScreen` zeigt alle + Titel flach. + +**Empfehlung (ersetzt die alte):** Keinen vierten Reiter bauen — der würde +denselben Bestand doppelt zeigen. Stattdessen `_KategorieLiederScreen` zu +einem richtigen Detail-Screen ausbauen und die Tracknummer nachrüsten. Der +Aufwand liegt damit **nicht** bei „ein Reiter wie der Künstler-Reiter", +sondern bei Schema-Migration + Scan + Screen. -**Empfehlung:** `groupByAlbum` (analog zu `groupByArtist`, existierende `_groupBy`- -Hilfsfunktion nutzt man mit), vierter Unterreiter, Album-Screen mit großem Cover, -Künstler, Jahr, Titelzahl, Gesamtdauer und "Abspielen / Zufällig". --- diff --git a/CHANGELOG.md b/CHANGELOG.md index f7429e1..ae5d70c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,28 @@ die die App gab und der Code nicht hielt**. mehr — vorbestehend), und die Server-Cover haben nur einen Zwischenspeicher im Arbeitsspeicher, keinen auf der Platte. +### 🔁 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. + ### 🏷️ Der zweite Tab heißt wieder „Download" (2026-08-21) - 📥 **Aus „Online" wird „Download".** Der Bereich behält seinen Inhalt diff --git a/lib/player/audio_handler.dart b/lib/player/audio_handler.dart index eeaa606..6d7a2d5 100644 --- a/lib/player/audio_handler.dart +++ b/lib/player/audio_handler.dart @@ -405,10 +405,18 @@ class MeloAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler { @override Future pause() => _player.pause(); + /// Der Positions-Timer wird hier bewusst **nicht** abgebrochen. + /// + /// Er ist ein `Timer.periodic` und entsteht nur einmal im Konstruktor — + /// einmal abgebrochen, käme er für den Rest der App-Sitzung nicht zurück. + /// [stop] löst aber nicht nur der Nutzer aus: das System ruft es beim + /// Wegwischen der Benachrichtigung auf. Danach merkte sich die App keine + /// Hörposition mehr und scrobbelte nicht mehr — bis zum Neustart und ohne + /// jeden Hinweis. Er kostet nichts: seine erste Zeile steigt aus, solange + /// nichts läuft. Abgebrochen wird er allein in [dispose]. @override Future stop() async { sleepTimer.cancel(); - _positionRecordTimer.cancel(); await _player.stop(); await super.stop(); }