mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-28 23:08:59 +02:00
fix(queue): restore pre-refactor guarantees found by commit audit
- _replaceSafFileVia: op now registers produced files via addCleanup the moment they exist, so converted temp files are deleted even when a later step throws (old code's outer-variable finally) - native worker history: recompute format from the final file path at history-write time and gate bitrate on lossy output again - the extracted helper was recording pre-conversion format/bitrate (FLAC conversions logged as m4a with nulled bit depth on SAF) - drop dead ignore_for_file header in the finalization part
This commit is contained in:
@@ -2836,7 +2836,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
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<DownloadQueueState> {
|
||||
convertFailed = true;
|
||||
return null;
|
||||
}
|
||||
addCleanup(convertedPath);
|
||||
_log.i(
|
||||
'Successfully converted M4A to $format (temp): $convertedPath',
|
||||
);
|
||||
@@ -2917,7 +2918,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
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<DownloadQueueState> {
|
||||
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<DownloadQueueState> {
|
||||
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<DownloadQueueState> {
|
||||
uri: currentFilePath,
|
||||
treeUri: settings.downloadTreeUri,
|
||||
relativeDir: effectiveOutputDir,
|
||||
op: (tempPath) async {
|
||||
op: (tempPath, _) async {
|
||||
opStarted = true;
|
||||
updateItemStatus(
|
||||
item.id,
|
||||
|
||||
@@ -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>{};
|
||||
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?) ??
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user