54 lines
5.1 KiB
Markdown
54 lines
5.1 KiB
Markdown
# 🟢 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`.
|