v2.42.2 — HIGH: Background-Service try/finally + MEDIUM: debugPrint Leak

## HIGH: Background-Service bleibt hängen
- downloadVonUrl: try/finally stellt sicher dass _stopBackgroundService() immer aufgerufen wird
- downloadBatch: try/finally stellt sicher dass _stopBackgroundService() immer aufgerufen wird
- reDownload: try/finally stellt sicher dass _stopBackgroundService() immer aufgerufen wird

## MEDIUM: debugPrint Leak
- HTTP-Response-Body in debugPrint auf max 200 Zeichen gekürzt
This commit is contained in:
Dustin
2026-08-02 17:00:27 +02:00
parent b5b1475d5d
commit 50a810934d
+96 -87
View File
@@ -104,90 +104,95 @@ class DownloadService extends ChangeNotifier {
/// Song von YouTube herunterladen
Future<Song?> downloadVonUrl(String url) async {
_resetStatus();
final result = await _downloadEinzeln(url);
if (!_ladt) _stopBackgroundService();
return result;
try {
return await _downloadEinzeln(url);
} finally {
_stopBackgroundService();
}
}
/// Mehrere URLs/Playlists nacheinander mit Cooldown
Future<int> downloadBatch(String input, {int cooldownSekunden = 5}) async {
_resetStatus();
MeloLogger().zustand('download_batch_start', {
'input_len': input.length,
});
final urls = _extrahiereUrls(input);
MeloLogger().zustand('urls_extrahiert', {
'anzahl': urls.length,
});
if (urls.isEmpty) {
_fehler = 'Keine gültigen URLs gefunden';
_ladt = false;
notifyListeners();
MeloLogger().fehler('keine_urls', 'Input: ${input.substring(0, min(input.length, 80))}');
return 0;
}
int erfolgreich = 0;
_aktuellerTitel = '0/${urls.length} Starte...';
notifyListeners();
_showProgress(0, urls.length);
for (int i = 0; i < urls.length; i++) {
if (sollAbbrechen) break;
_aktuellerTitel = '${i + 1}/${urls.length} ${urls[i]._titelKurz()}';
notifyListeners();
_showProgress(i, urls.length, titel: urls[i]._titelKurz());
MeloLogger().zustand('download_einzeln_start', {
'index': i,
'gesamt': urls.length,
'url': urls[i].url.substring(0, min(urls[i].url.length, 60)),
try {
MeloLogger().zustand('download_batch_start', {
'input_len': input.length,
});
final song = await _downloadEinzeln(urls[i].url);
if (song != null) {
erfolgreich++;
MeloLogger().zustand('download_einzeln_erfolg', {
'titel': song.titel,
'index': i,
'erfolgreich': erfolgreich,
});
} else {
MeloLogger().zustand('download_einzeln_fehlgeschlagen', {
'index': i,
'fehler': _fehler ?? 'unbekannt',
});
final urls = _extrahiereUrls(input);
MeloLogger().zustand('urls_extrahiert', {
'anzahl': urls.length,
});
if (urls.isEmpty) {
_fehler = 'Keine gültigen URLs gefunden';
_ladt = false;
notifyListeners();
MeloLogger().fehler('keine_urls', 'Input: ${input.substring(0, min(input.length, 80))}');
return 0;
}
// Cooldown zwischen Downloads (außer beim letzten)
if (i < urls.length - 1 && cooldownSekunden > 0) {
_aktuellerTitel = '$erfolgreich/${urls.length} Warte ${cooldownSekunden}s...';
int erfolgreich = 0;
_aktuellerTitel = '0/${urls.length} Starte...';
notifyListeners();
_showProgress(0, urls.length);
for (int i = 0; i < urls.length; i++) {
if (sollAbbrechen) break;
_aktuellerTitel = '${i + 1}/${urls.length} ${urls[i]._titelKurz()}';
notifyListeners();
for (int s = 0; s < cooldownSekunden; s++) {
if (sollAbbrechen) break;
await Future.delayed(const Duration(seconds: 1));
_showProgress(i, urls.length, titel: urls[i]._titelKurz());
MeloLogger().zustand('download_einzeln_start', {
'index': i,
'gesamt': urls.length,
'url': urls[i].url.substring(0, min(urls[i].url.length, 60)),
});
final song = await _downloadEinzeln(urls[i].url);
if (song != null) {
erfolgreich++;
MeloLogger().zustand('download_einzeln_erfolg', {
'titel': song.titel,
'index': i,
'erfolgreich': erfolgreich,
});
} else {
MeloLogger().zustand('download_einzeln_fehlgeschlagen', {
'index': i,
'fehler': _fehler ?? 'unbekannt',
});
}
// Cooldown zwischen Downloads (außer beim letzten)
if (i < urls.length - 1 && cooldownSekunden > 0) {
_aktuellerTitel = '$erfolgreich/${urls.length} Warte ${cooldownSekunden}s...';
notifyListeners();
for (int s = 0; s < cooldownSekunden; s++) {
if (sollAbbrechen) break;
await Future.delayed(const Duration(seconds: 1));
}
}
}
}
_ladt = false;
if (sollAbbrechen) {
_aktuellerTitel = '❌ Abgebrochen ($erfolgreich fertig)';
} else {
_aktuellerTitel = '$erfolgreich/${urls.length} Songs geladen';
_ladt = false;
if (sollAbbrechen) {
_aktuellerTitel = '❌ Abgebrochen ($erfolgreich fertig)';
} else {
_aktuellerTitel = '$erfolgreich/${urls.length} Songs geladen';
}
notifyListeners();
_completeNotification(erfolgreich, urls.length, abgebrochen: sollAbbrechen);
MeloLogger().zustand('download_batch_ende', {
'erfolgreich': erfolgreich,
'gesamt': urls.length,
'abgebrochen': sollAbbrechen,
});
return erfolgreich;
} finally {
_stopBackgroundService();
}
notifyListeners();
_completeNotification(erfolgreich, urls.length, abgebrochen: sollAbbrechen);
_stopBackgroundService();
MeloLogger().zustand('download_batch_ende', {
'erfolgreich': erfolgreich,
'gesamt': urls.length,
'abgebrochen': sollAbbrechen,
});
return erfolgreich;
}
/// Einzelnen Song über den yt-proxy herunterladen
@@ -256,7 +261,7 @@ class DownloadService extends ChangeNotifier {
_fehler = 'Proxy-Fehler (${dlAntwort.statusCode}): $fehlerText';
_ladt = false;
notifyListeners();
debugPrint('Proxy-Fehler: ${dlAntwort.body}');
debugPrint('Proxy-Fehler: ${dlAntwort.body.length > 200 ? '${dlAntwort.body.substring(0, 200)}...' : dlAntwort.body}');
MeloLogger().netzwerk('POST', '$_proxyBasisUrl/api/yt-dl',
statusCode: dlAntwort.statusCode, fehler: fehlerText);
return null;
@@ -483,24 +488,28 @@ class DownloadService extends ChangeNotifier {
}
_resetStatus();
_aktuellerTitel = '🔄 Lade neu: ${song.titel}';
notifyListeners();
// Alten Song-Eintrag aus DB löschen
if (song.id != null) {
await _db.loeschSong(song.id!);
}
// Alte Datei löschen
try {
final alteDatei = File(song.dateiPfad);
if (await alteDatei.exists()) {
await alteDatei.delete();
}
} catch (_) {}
_aktuellerTitel = '🔄 Lade neu: ${song.titel}';
notifyListeners();
// Neu herunterladen mit der gespeicherten ytUrl
return _downloadEinzeln(song.ytUrl!);
// Alten Song-Eintrag aus DB löschen
if (song.id != null) {
await _db.loeschSong(song.id!);
}
// Alte Datei löschen
try {
final alteDatei = File(song.dateiPfad);
if (await alteDatei.exists()) {
await alteDatei.delete();
}
} catch (_) {}
// Neu herunterladen mit der gespeicherten ytUrl
return _downloadEinzeln(song.ytUrl!);
} finally {
_stopBackgroundService();
}
}
// ─── #9: Notification-Progress ─────────────────