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
+23
View File
@@ -3,6 +3,9 @@ class Song {
final String titel;
final String kuenstler;
final String album;
final String? jahr;
final String? genre;
final String? track;
final int dauerSekunden;
final String dateiPfad;
final String? coverPfad;
@@ -21,6 +24,9 @@ class Song {
required this.titel,
required this.kuenstler,
this.album = '',
this.jahr,
this.genre,
this.track,
required this.dauerSekunden,
required this.dateiPfad,
this.coverPfad,
@@ -39,6 +45,9 @@ class Song {
'titel': titel,
'kuenstler': kuenstler,
'album': album,
'jahr': jahr,
'genre': genre,
'track': track,
'dauer_sekunden': dauerSekunden,
'datei_pfad': dateiPfad,
'cover_pfad': coverPfad,
@@ -57,6 +66,9 @@ class Song {
titel: m['titel'] as String,
kuenstler: m['kuenstler'] as String,
album: m['album'] as String? ?? '',
jahr: m['jahr'] as String?,
genre: m['genre'] as String?,
track: m['track'] as String?,
dauerSekunden: m['dauer_sekunden'] as int,
dateiPfad: m['datei_pfad'] as String,
coverPfad: m['cover_pfad'] as String?,
@@ -80,4 +92,15 @@ class Song {
if (groesseBytes < 1048576) return '${(groesseBytes / 1024).toStringAsFixed(0)} KB';
return '${(groesseBytes / 1048576).toStringAsFixed(1)} MB';
}
/// Dateiformat aus Dateiendung ableiten
String get dateiFormat {
final lower = dateiPfad.toLowerCase();
if (lower.endsWith('.mp3')) return 'MP3';
if (lower.endsWith('.m4a')) return 'M4A (AAC)';
if (lower.endsWith('.flac')) return 'FLAC';
if (lower.endsWith('.wav')) return 'WAV';
if (lower.endsWith('.ogg')) return 'OGG';
return lower.split('.').last.toUpperCase();
}
}