v2.42 — Issue #7: ID3-Reader erweitern + Metadaten-Dialog verschönern

## ID3-Reader (lib/services/id3_reader.dart)
- ID3v1: Jahr (Bytes 93-97) + Genre-Index (Byte 127, vollständige Genre-Liste 0-147)
- ID3v2: Text-Frames TIT2 (Titel), TPE1 (Künstler), TALB (Album), TYER (Jahr), TCON (Genre), TRCK (Track)
- ID3v2-Frames haben Vorrang vor ID3v1 (Füll-Logik)
- UTF-16/UTF-8 Encoding-Unterstützung für Text-Frames
- Genre-Bereinigung: Klammer-Präfixe entfernt, Track-Nummer extrahiert

## Song-Model (lib/models/song.dart)
- Neue Felder: jahr, genre, track (nullable, optional)
- Neue getter: dateiFormat (MP3/M4A/FLAC/WAV/OGG aus Dateiendung)
- DB-Migration v5→v6: jahr, genre, track Spalten

## Metadaten-Dialog (lib/widgets/metadaten_dialog.dart)
- Cover-Bild (128×128) mit rotem Glow-Rahmen oben falls vorhanden
- Editable Felder mit Icons: Titel, Künstler, Album, Jahr, Genre
- Read-Only-Info-Card: Dauer, Dateigröße, Format
- Cards mit roten Akzenten + Melo-Design (schwarz/rot/dunkelgrau)
- ID3-Fallback: Jahr/Genre aus Datei nachladen wenn nicht in DB
- Speichern persistiert auch Jahr und Genre in die DB

## Musik-Scanner (lib/services/musik_scanner.dart)
- jahr, genre, track aus ID3-Tags an Song-Model durchgereicht
- _nichtLeer-Helfer für null-sichere String-Extraktion
This commit is contained in:
Dustin
2026-08-02 16:10:58 +02:00
parent ae11a85872
commit a483436490
5 changed files with 410 additions and 54 deletions
+9
View File
@@ -83,6 +83,9 @@ class MusikScanner {
titel: (tags['titel'] as String).isNotEmpty ? tags['titel'] : _dateiNameOhneEndung(pfad),
kuenstler: (tags['kuenstler'] as String).isNotEmpty ? tags['kuenstler'] : 'Unbekannt',
album: tags['album'] ?? '',
jahr: _nichtLeer(tags['jahr']),
genre: _nichtLeer(tags['genre']),
track: _nichtLeer(tags['track']),
dauerSekunden: await _ermittleDauer(player, pfad),
dateiPfad: pfad,
coverPfad: coverPfad,
@@ -207,6 +210,12 @@ class MusikScanner {
return dot > 0 ? name.substring(0, dot) : name;
}
String? _nichtLeer(dynamic wert) {
if (wert == null) return null;
final s = wert.toString().trim();
return s.isEmpty ? null : s;
}
/// Sucht YouTube-Quell-URLs für Songs ohne ytUrl (max 50 pro Scan, 1/s Rate-Limit)
Future<void> _sucheYtUrls() async {
const maxSuchanfragen = 50;