diff --git a/lib/providers/download_queue_provider.dart b/lib/providers/download_queue_provider.dart index 75679ffd..da9a7ff9 100644 --- a/lib/providers/download_queue_provider.dart +++ b/lib/providers/download_queue_provider.dart @@ -2836,7 +2836,7 @@ class DownloadQueueNotifier extends Notifier { uri: currentFilePath, treeUri: settings.downloadTreeUri, relativeDir: effectiveOutputDir, - op: (tempPath) async { + op: (tempPath, addCleanup) async { opStarted = true; updateItemStatus( item.id, @@ -2853,6 +2853,7 @@ class DownloadQueueNotifier extends Notifier { convertFailed = true; return null; } + addCleanup(convertedPath); _log.i( 'Successfully converted M4A to $format (temp): $convertedPath', ); @@ -2917,7 +2918,7 @@ class DownloadQueueNotifier extends Notifier { uri: currentFilePath, treeUri: settings.downloadTreeUri, relativeDir: effectiveOutputDir, - op: (tempPath) async { + op: (tempPath, _) async { opStarted = true; if (metadataEmbeddingEnabled) { updateItemStatus( @@ -2967,7 +2968,7 @@ class DownloadQueueNotifier extends Notifier { uri: currentFilePath, treeUri: settings.downloadTreeUri, relativeDir: effectiveOutputDir, - op: (tempPath) async { + op: (tempPath, addCleanup) async { final length = await File(tempPath).length(); if (length < 1024) { _log.w( @@ -3041,6 +3042,7 @@ class DownloadQueueNotifier extends Notifier { branch = 'convertFailed'; return null; } + addCleanup(flacPath); _log.d('Converted to FLAC (temp): $flacPath'); _log.d( 'Embedding metadata and cover to converted FLAC...', @@ -3368,7 +3370,7 @@ class DownloadQueueNotifier extends Notifier { uri: currentFilePath, treeUri: settings.downloadTreeUri, relativeDir: effectiveOutputDir, - op: (tempPath) async { + op: (tempPath, _) async { opStarted = true; updateItemStatus( item.id, diff --git a/lib/providers/download_queue_provider_finalization.dart b/lib/providers/download_queue_provider_finalization.dart index f6705a57..096c6aaa 100644 --- a/lib/providers/download_queue_provider_finalization.dart +++ b/lib/providers/download_queue_provider_finalization.dart @@ -1,4 +1,3 @@ -// ignore_for_file: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member part of 'download_queue_provider.dart'; /// Result of [DownloadQueueNotifier._finalizeDecryption]. [failStage] is @@ -199,14 +198,20 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier { required String uri, required String treeUri, required String relativeDir, - required Future<(String path, String fileName)?> Function(String tempPath) + required Future<(String path, String fileName)?> Function( + String tempPath, + void Function(String path) addCleanup, + ) op, }) async { final tempPath = await _copySafToTemp(uri); if (tempPath == null) return null; + // Files op produces are registered here the moment they exist, so they + // are cleaned up even if op throws before returning. + final producedTemps = {}; String? outPath; try { - final produced = await op(tempPath); + final produced = await op(tempPath, producedTemps.add); if (produced == null) return null; outPath = produced.$1; final fileName = produced.$2; @@ -228,9 +233,13 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier { try { await File(tempPath).delete(); } catch (_) {} - if (outPath != null && outPath != tempPath) { + if (outPath != null) { + producedTemps.add(outPath); + } + for (final path in producedTemps) { + if (path == tempPath) continue; try { - await File(outPath).delete(); + await File(path).delete(); } catch (_) {} } } @@ -276,7 +285,7 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier { uri: filePath, treeUri: downloadTreeUri, relativeDir: safRelativeDir, - op: (tempPath) async { + op: (tempPath, addCleanup) async { opStarted = true; final decryptedTempPath = await FFmpegService.decryptWithDescriptor( inputPath: tempPath, @@ -287,6 +296,7 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier { failStage = DownloadQueueNotifier._decryptStageDecrypt; return null; } + addCleanup(decryptedTempPath); if (repairAc4) { try { await PlatformBridge.ensureAC4Config(decryptedTempPath, tempPath); @@ -416,7 +426,7 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier { uri: filePath, treeUri: treeUri, relativeDir: context.safRelativeDir ?? '', - op: (tempPath) async { + op: (tempPath, addCleanup) async { final convertedPath = await FFmpegService.convertM4aToLossy( tempPath, format: format, @@ -424,6 +434,7 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier { deleteOriginal: false, ); if (convertedPath == null) return null; + addCleanup(convertedPath); await embedConvertedMetadata(convertedPath); return (convertedPath, newFileName); }, @@ -540,7 +551,7 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier { uri: filePath, treeUri: treeUri, relativeDir: context.safRelativeDir ?? '', - op: (tempPath) async { + op: (tempPath, addCleanup) async { final codec = await FFmpegService.probePrimaryAudioCodec(tempPath); final isAlreadyNativeFlac = codec == 'flac' && await FFmpegService.isNativeFlacFile(tempPath); @@ -571,6 +582,7 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier { if (flacPath == null) { return null; } + addCleanup(flacPath); await embedFlacMetadata(flacPath); final rawFileName = (result['file_name'] as String?) ?? diff --git a/lib/providers/download_queue_provider_native_worker.dart b/lib/providers/download_queue_provider_native_worker.dart index a996acdc..09b7c29c 100644 --- a/lib/providers/download_queue_provider_native_worker.dart +++ b/lib/providers/download_queue_provider_native_worker.dart @@ -1047,8 +1047,16 @@ extension _DownloadQueueNativeWorker on DownloadQueueNotifier { final resultSafFileName = result['file_name'] as String?; final lowerFilePath = filePath.toLowerCase(); + // Recompute from the FINAL file path/result: the HIGH and container + // conversions above may have changed the format since actualFormat was + // derived from the pre-conversion output. + final historyFormat = + normalizeAudioFormatValue( + result['audio_codec']?.toString() ?? result['format']?.toString(), + ) ?? + normalizeAudioFormatValue(audioFormatForPath(filePath)); final isLossyOutput = - isLossyAudioFormat(actualFormat) || + isLossyAudioFormat(historyFormat) || lowerFilePath.endsWith('.mp3') || lowerFilePath.endsWith('.opus') || lowerFilePath.endsWith('.ogg'); @@ -1072,8 +1080,8 @@ extension _DownloadQueueNativeWorker on DownloadQueueNotifier { : context.safFileName, bitDepth: isLossyOutput ? null : actualBitDepth, sampleRate: isLossyOutput ? null : actualSampleRate, - bitrate: actualBitrate, - format: actualFormat, + bitrate: isLossyOutput ? actualBitrate : null, + format: historyFormat, genre: normalizeOptionalString(result['genre'] as String?), label: normalizeOptionalString(result['label'] as String?), copyright: normalizeOptionalString(