v2.32 — Defekte Musik erkennen (Magic Bytes, ffprobe, DB-Flag, UI)
This commit is contained in:
@@ -245,6 +245,7 @@ class DownloadService extends ChangeNotifier {
|
||||
dateiPfad = '${dir.path}/$lokalerName';
|
||||
|
||||
final stopwatch2 = Stopwatch()..start();
|
||||
bool istKorrupt = false;
|
||||
try {
|
||||
final mp3Antwort = await _retryHttpGet(
|
||||
Uri.parse('$_proxyBasisUrl$mp3Url'),
|
||||
@@ -266,6 +267,13 @@ class DownloadService extends ChangeNotifier {
|
||||
final file = File(dateiPfad);
|
||||
await file.writeAsBytes(mp3Antwort.bodyBytes);
|
||||
|
||||
// ── Magic-Byte-Prüfung: Echte MP3-Datei? ──
|
||||
istKorrupt = !_hatValideMagicBytes(file);
|
||||
if (istKorrupt) {
|
||||
debugPrint('⚠️ Korrupte Datei erkannt (Magic Bytes): $dateiPfad');
|
||||
MeloLogger().fehler('magic_bytes_check', 'Ungültiger MP3-Header in $dateiPfad');
|
||||
}
|
||||
|
||||
MeloLogger().netzwerk('GET', '$_proxyBasisUrl$mp3Url',
|
||||
statusCode: 200, dauerMs: stopwatch2.elapsedMilliseconds);
|
||||
|
||||
@@ -307,6 +315,7 @@ class DownloadService extends ChangeNotifier {
|
||||
dateiPfad: dateiPfad,
|
||||
groesseBytes: await File(dateiPfad).length(),
|
||||
istHeruntergeladen: true,
|
||||
istKorrupt: istKorrupt,
|
||||
downloadQuelle: 'youtube',
|
||||
);
|
||||
await _db.songEinfuegen(song);
|
||||
@@ -327,6 +336,49 @@ class DownloadService extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
/// Prüft die Magic Bytes einer Audiodatei.
|
||||
/// MP3: ID3-Tag (49 44 33) oder MPEG-Frame (FF FB, FF FA, FF F3, FF F2)
|
||||
/// M4A/AAC: ftyp-Box (66 74 79 70)
|
||||
/// FLAC: fLaC (66 4C 61 43)
|
||||
/// WAV: RIFF (52 49 46 46)
|
||||
/// OGG: OggS (4F 67 67 53)
|
||||
static bool _hatValideMagicBytes(File file) {
|
||||
try {
|
||||
if (!file.existsSync()) return false;
|
||||
final bytes = file.readAsBytesSync().take(16).toList();
|
||||
if (bytes.length < 4) return false;
|
||||
|
||||
// ID3v2 Tag (MP3 mit Metadaten) — Bytes: 49 44 33
|
||||
if (bytes[0] == 0x49 && bytes[1] == 0x44 && bytes[2] == 0x33) return true;
|
||||
|
||||
// MP3 ohne ID3: MPEG Audio Frame Sync (FF FB, FF FA, FF F3, FF F2)
|
||||
if (bytes[0] == 0xFF && (bytes[1] & 0xFE) == 0xFA) return true; // MPEG v1
|
||||
if (bytes[0] == 0xFF && bytes[1] == 0xF3) return true; // MPEG v2 / v2.5
|
||||
if (bytes[0] == 0xFF && bytes[1] == 0xF2) return true; // MPEG v2 / v2.5
|
||||
|
||||
// M4A/AAC: ftyp-Box
|
||||
if (bytes.length >= 8 &&
|
||||
bytes[4] == 0x66 && bytes[5] == 0x74 &&
|
||||
bytes[6] == 0x79 && bytes[7] == 0x70) return true;
|
||||
|
||||
// FLAC: fLaC
|
||||
if (bytes[0] == 0x66 && bytes[1] == 0x4C &&
|
||||
bytes[2] == 0x61 && bytes[3] == 0x43) return true;
|
||||
|
||||
// WAV: RIFF
|
||||
if (bytes[0] == 0x52 && bytes[1] == 0x49 &&
|
||||
bytes[2] == 0x46 && bytes[3] == 0x46) return true;
|
||||
|
||||
// OGG: OggS
|
||||
if (bytes[0] == 0x4F && bytes[1] == 0x67 &&
|
||||
bytes[2] == 0x67 && bytes[3] == 0x53) return true;
|
||||
|
||||
return false;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// HTTP POST mit Retry (exponentieller Backoff)
|
||||
Future<http.Response> _retryHttpPost(
|
||||
Uri url, {
|
||||
|
||||
Reference in New Issue
Block a user