# 🟢 Melo App — Aktuelles Code-Review (v2.5) > **Geprüft:** 24.07.2026 | **Projekt:** ~/Projects/melo_app/ | **Dustin (Baka) — Kiel** > **Prinzip:** Ponytail — minimaler Code, maximale Wirkung, max 500 Zeilen pro Datei > **Gesamt-Score:** **A-** (Hervorragend verbessert!) --- ## ⭐️ Status der Altschulden (Review-Vergleich) Alle kritischen Probleme aus dem vorherigen Review wurden **vollständig und sauber gelöst**: - ✅ **CRIT-1 (Memory Leak im MiniPlayer)**: `StreamSubscriptions` werden jetzt ordnungsgemäß in `dispose()` gecancelled. - ✅ **CRIT-2 (Null-Crash im Metadaten-Editor)**: `widget.song.id!` wurde durch ein sicheres ID-Null-Guard ersetzt. - ✅ **CRIT-3 (Dauer-Sekunden = 0)**: Wird jetzt beim Scannen über den Player ermittelt. (Siehe Performance für Optimierungspotenzial). - ✅ **CRIT-4 (Verlaufs-Flut)**: `positionAktualisieren` führt jetzt saubere Updates/Upserts statt endloser Inserts durch. - ✅ **CRIT-5 (ErrorBoundary-Deko)**: Der tote `AppWrapper` und `FlutterErrorBoundary` in `main.dart` wurden komplett entfernt (Ponytail-Prinzip!). --- ## 📐 Five-Axis Quality Gate ### 1. CORRECTNESS (Korrektheits-Check) — **Score: A-** - **Sicherheits-Guards**: Hervorragende null-safety Guards im Metadaten-Editor und beim Wiedergabestatus. - **Division durch Null (Download)**: In `download_service.dart:103` bei `_fortschritt = downloaded / total;` besteht ein minimales theoretisches Risiko eines `NaN` (Division durch Null), falls `total` (audio.size.totalBytes) jemals `0` zurückgeben sollte. - *Empfehlung:* Ein kurzer Ternary Guard: `_fortschritt = total > 0 ? downloaded / total : 0.0;` - **Substring-Range**: `p.erstelltAm.substring(0, 10)` in `playlist_sheet.dart:103` ist sicher, da `erstelltAm` durch das ISO-Format standardmäßig >= 19 Zeichen lang ist. ### 2. READABILITY (Lesbarkeit) — **Score: A** - **Konsistente Namensgebung**: Großartiger, verständlicher Code. Deutsche Variablennamen (`_aktiverTab`, `_ladeSongs()`) werden konsistent und clean durchgezogen. - **Kein Code-Spam**: Die Klassen sind kompakt, sauber formatiert und verzichten auf unnötige Schachtelungen. ### 3. ARCHITECTURE (Architektur & Struktur) — **Score: B+** - **`dynamic vm` Anti-Pattern**: In `playlist_sheet.dart` und `navidrome_browser.dart` wird `final dynamic vm` verwendet. Das deaktiviert jegliche Typprüfung, Autovervollständigung und birgt das Risiko von Laufzeitfehlern, falls sich ViewModel-Methoden ändern. - *Empfehlung:* Ersetze `dynamic vm` durch den konkreten Typ `final MeloHomeViewModel vm`. - **Zukünftige DB-Migrationen**: `DbHelper.db` initialisiert die SQLite-Datenbank mit `version: 1` ohne `onUpgrade`-Callback. Wenn du später Tabellen anpasst, crasht die App bei bestehenden Nutzern beim Start. - *Empfehlung:* Bereits jetzt eine leere `onUpgrade` Struktur im `openDatabase` vorsehen. - **Redundanter Aufruf**: In `MeloHomeViewModel.ladeSongs()` wird `tagCounts = _berechneTagCounts();` aufgerufen, während `tags` noch ein leeres Array `[]` ist. Erst danach wird `await ladeTags()` aufgerufen, welches `_berechneTagCounts()` erneut aufruft. Der erste Aufruf ist also redundant. ### 4. PERFORMANCE (Laufzeit & Ressourcen) — **Score: B** - **Granularer Rebuild (Großartig!)**: Die Implementierung des `MiniPlayer` ist **perfekt gelöst**. Er lauscht direkt auf die Streams des Players und aktualisiert seinen State lokal per `setState`. Dadurch wird verhindert, dass bei jedem Millisekunden-Update des Fortschrittsbalkens der gesamte Home-Bildschirm (mit Statistiken, Listen und Covern) neu gerendert wird! - **🚨 Der Scanner-Flaschenhals**: In `lib/services/musik_scanner.dart` wird in `_ermittleDauer(pfad)` für **jeden einzelnen** gefundenen Song ein komplett neuer `AudioPlayer` instanziiert, die Datei geöffnet, die Dauer gelesen und der Player wieder disposed. - *Problem:* Wenn ein Ordner 200 Songs enthält, wird 200-mal nacheinander ein nativer Player erzeugt und zerstört. Das blockiert den Scanvorgang extrem, zieht viel Akku und kann auf manchen Systemen zu Abstürzen oder "Platform Channel Exception"-Fehlern (Erschöpfung der Audio-Player-Kanäle) führen. - *Empfehlung:* Entweder eine extrem leichtgewichtige Metadata-Bibliothek (`flutter_media_metadata`) nutzen, oder eine einzige `AudioPlayer`-Instanz für den gesamten Scanvorgang wiederverwenden, anstatt sie in der Schleife ständig neu zu erstellen. ### 5. SECURITY (Sicherheit) — **Score: A** - **Berechtigungen**: Vorbildliche Handhabung von Android Scoped Storage Permissions über `Permission.audio` statt der veralteten `Permission.storage` (deprecating ab Android 13). --- ## 🛠 Top 3 Quick Wins 1. **Typsicherheit im UI**: Ändere `final dynamic vm` in `playlist_sheet.dart` und `navidrome_browser.dart` zu `final MeloHomeViewModel vm`. Das gibt dir volles Autocomplete und Compile-Safety. 2. **Scanner beschleunigen**: Instanziiere den `AudioPlayer` für die Dauer-Ermittlung einmalig außerhalb der Schleife im `MusikScanner` und nutze ihn für alle Dateien, statt ihn pro Song neu zu erstellen und zu disposen. 3. **VM-Cleanup**: Entferne den redundanten Aufruf von `tagCounts = _berechneTagCounts();` aus Zeile 78 in `melo_home_viewmodel.dart`.