fix(download): publish converted SAF files with the final extension

This commit is contained in:
zarzet
2026-09-07 01:06:35 +07:00
parent 650af6e59d
commit 660ed9fb60
3 changed files with 74 additions and 11 deletions
@@ -1002,13 +1002,14 @@ class _DownloadRun {
(localFile.uri.pathSegments.isEmpty
? 'track$safOutputExt'
: localFile.uri.pathSegments.last);
final localExt = n._downloadResultOutputExt(result, filePath: localPath);
if (localExt != null && localExt.isNotEmpty) {
finalName = finalName.replaceFirst(RegExp(r'\.[^.]+$'), localExt);
if (!finalName.toLowerCase().endsWith(localExt.toLowerCase())) {
finalName = '$finalName$localExt';
}
}
finalName = finalizedAudioFileName(
fileName: finalName,
localPath: localPath,
fallbackExtension:
n._downloadResultOutputExt(result, filePath: localPath) ??
safOutputExt,
);
final localExt = finalName.substring(finalName.lastIndexOf('.'));
final measured = probedFinalMetadata;
final qualityLabel = buildQualityVariantFilenameLabel(
@@ -1039,7 +1040,11 @@ class _DownloadRun {
String? publishedName;
var alreadyExists = false;
if (item.preserveQualityVariant && qualityVariantCollisionOnly) {
final logicalName = safFileName ?? finalName;
final logicalName = finalizedAudioFileName(
fileName: safFileName ?? finalName,
localPath: localPath,
fallbackExtension: localExt,
);
final stagingLabel = qualityVariantStagingLabel(item.id);
final cleanName = removeQualityVariantStagingLabel(
fileName: logicalName,
@@ -1057,7 +1062,7 @@ class _DownloadRun {
relativeDir: effectiveOutputDir,
cleanFileName: cleanName,
variantFileName: variantName,
mimeType: n._mimeTypeForExt(localExt ?? safOutputExt),
mimeType: n._mimeTypeForExt(localExt),
srcPath: localPath,
preservedSuffix: qualityLabel ?? '',
);
@@ -1068,7 +1073,7 @@ class _DownloadRun {
treeUri: settings.downloadTreeUri,
relativeDir: effectiveOutputDir,
fileName: finalName,
mimeType: n._mimeTypeForExt(localExt ?? safOutputExt),
mimeType: n._mimeTypeForExt(localExt),
srcPath: localPath,
preservedSuffix: qualityLabel ?? '',
);
@@ -1079,7 +1084,7 @@ class _DownloadRun {
treeUri: settings.downloadTreeUri,
relativeDir: effectiveOutputDir,
fileName: finalName,
mimeType: n._mimeTypeForExt(localExt ?? safOutputExt),
mimeType: n._mimeTypeForExt(localExt),
srcPath: localPath,
);
publishedUri = published?.uri;
+17
View File
@@ -134,6 +134,23 @@ String isoBmffAudioExtensionForCodec(String? codec) {
return normalizeAudioFormatValue(codec) == 'ac4' ? '.mp4' : '.m4a';
}
/// Uses the finalized local container after conversion, before publishing to
/// SAF. Provider output fields still describe the original download here.
String finalizedAudioFileName({
required String fileName,
required String localPath,
required String fallbackExtension,
}) {
final extension =
RegExp(
r'\.(flac|m4a|mp4|mp3|opus|ogg|aac|wav|aiff)$',
caseSensitive: false,
).firstMatch(localPath)?.group(0)?.toLowerCase() ??
fallbackExtension;
final stem = fileName.replaceFirst(RegExp(r'\.[^.]+$'), '');
return '$stem$extension';
}
/// Resolves the actual audio codec reported by native metadata probing, while
/// falling back to the container format when the codec is absent or generic.
///
+41
View File
@@ -23,6 +23,47 @@ import 'package:spotiflac_android/utils/path_match_keys.dart';
import 'package:spotiflac_android/utils/string_utils.dart';
void main() {
group('Finalized SAF audio names', () {
for (final extension in ['mp3', 'opus', 'flac', 'ogg', 'm4a', 'mp4']) {
test(
'publishes the final $extension container instead of source M4A',
() {
expect(
finalizedAudioFileName(
fileName: 'Example song.m4a',
localPath: '/cache/final_audio.$extension',
fallbackExtension: '.m4a',
),
'Example song.$extension',
);
},
);
}
test('preserves variant labels and normalizes uppercase suffixes', () {
expect(
finalizedAudioFileName(
fileName: 'Song [OPUS 256kbps].m4a',
localPath: '/cache/final.OPUS',
fallbackExtension: '.m4a',
),
'Song [OPUS 256kbps].opus',
);
});
test(
'uses the reported container when the temporary path has no suffix',
() {
expect(
finalizedAudioFileName(
fileName: 'Song',
localPath: '/cache/final_audio',
fallbackExtension: '.m4a',
),
'Song.m4a',
);
},
);
});
group('file deletion', () {
test('confirms a local file is absent before reporting success', () async {
final tempDir = await Directory.systemTemp.createTemp(