This repository has been archived on 2026-08-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
melo-app/CODE_REVIEW.md

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`.