fix(storage): publish finalized SAF output once

This commit is contained in:
zarzet
2026-08-26 18:37:15 +07:00
parent f8bd1b5931
commit e149ee5358
4 changed files with 220 additions and 3 deletions
@@ -376,6 +376,36 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier {
}
}
Future<({String uri, String fileName, bool alreadyExists})?>
_writeTempToSafIfAbsent({
required String treeUri,
required String relativeDir,
required String fileName,
required String mimeType,
required String srcPath,
}) async {
try {
final result = await PlatformBridge.createSafFileIfAbsentFromPath(
treeUri: treeUri,
relativeDir: relativeDir,
fileName: fileName,
mimeType: mimeType,
srcPath: srcPath,
);
final uri = (result['uri'] as String? ?? '').trim();
final publishedName = (result['file_name'] as String? ?? '').trim();
if (uri.isEmpty || publishedName.isEmpty) return null;
return (
uri: uri,
fileName: publishedName,
alreadyExists: result['already_exists'] == true,
);
} catch (e) {
_log.w('Failed to publish deferred SAF file: $e');
return null;
}
}
Future<({String uri, String fileName})?> _writeTempToSafUnique({
required String treeUri,
required String relativeDir,
@@ -644,7 +674,7 @@ extension _DownloadQueueFinalization on DownloadQueueNotifier {
final stagingLabel = qualityVariantStagingLabel(item.id);
final localPathSegments = File(filePath).uri.pathSegments;
final currentFileName = storageMode == 'saf' && isContentUri(filePath)
final currentFileName = storageMode == 'saf'
? (fileName ?? result['file_name']?.toString() ?? '')
: (localPathSegments.isEmpty ? '' : localPathSegments.last);
final variantFileName = applyQualityVariantFilenameLabel(
@@ -580,6 +580,7 @@ class _DownloadRun {
genre: genre,
label: label,
copyright: copyright,
stageSafForDeferredPublish: useSaf,
qualityVariantCollisionOnly: qualityVariantCollisionOnly,
);
@@ -726,7 +727,14 @@ class _DownloadRun {
return false;
}
await _recoverSafUriIfNeeded();
final deferredSafPublish =
effectiveSafMode &&
result['saf_deferred_publish'] == true &&
filePath != null &&
!isContentUri(filePath!);
if (!deferredSafPublish) {
await _recoverSafUriIfNeeded();
}
final hookInput = filePath;
if (hookInput != null) {
@@ -788,6 +796,10 @@ class _DownloadRun {
);
}
if (deferredSafPublish && !await _publishDeferredSafOutputOnce()) {
throw StateError('Failed to publish deferred SAF output');
}
final lrcTarget = filePath;
if (effectiveSafMode && lrcTarget != null && isContentUri(lrcTarget)) {
await n._saveExternalLrc(
@@ -950,14 +962,148 @@ class _DownloadRun {
await _embedSafNonM4a(path);
} else if (metadataEmbeddingEnabled &&
!isContentUriPath &&
!effectiveSafMode &&
isFlacFile &&
!wasExisting &&
decryptionDescriptor != null) {
await _embedLocalFlacAfterDecrypt(path);
} else if (metadataEmbeddingEnabled &&
!isContentUriPath &&
effectiveSafMode &&
result['saf_deferred_publish'] == true &&
!isFlacFile &&
!isM4aFile &&
!wasExisting) {
final isOpus =
path.toLowerCase().endsWith('.opus') ||
path.toLowerCase().endsWith('.ogg') ||
resultOutputExt == '.opus' ||
resultOutputExt == '.ogg';
await _embedFinalMetadata(
path,
format: isOpus ? 'opus' : 'mp3',
writeExternalLrc: false,
);
}
}
Future<bool> _publishDeferredSafOutputOnce() async {
final localPath = filePath;
if (localPath == null || isContentUri(localPath)) return true;
final localFile = File(localPath);
if (!await localFile.exists() || await localFile.length() <= 0) {
return false;
}
var finalName =
normalizeOptionalString(finalSafFileName) ??
normalizeOptionalString(result['file_name']?.toString()) ??
normalizeOptionalString(safFileName) ??
(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';
}
}
final measured = probedFinalMetadata;
final qualityLabel = buildQualityVariantFilenameLabel(
detectedFormat:
normalizeAudioFormatValue(
measured?['audio_codec']?.toString() ??
measured?['format']?.toString(),
) ??
normalizeAudioFormatValue(
result['audio_codec']?.toString() ?? result['format']?.toString(),
),
bitDepth: readPositiveInt(
measured?['bit_depth'] ?? result['actual_bit_depth'],
),
sampleRate: readPositiveInt(
measured?['sample_rate'] ?? result['actual_sample_rate'],
),
bitrateKbps: readPositiveBitrateKbps(
measured?['bitrate'] ??
measured?['bit_rate'] ??
result['bitrate'] ??
result['actual_bitrate'],
),
measuredQuality: actualQuality,
);
String? publishedUri;
String? publishedName;
var alreadyExists = false;
if (item.preserveQualityVariant && qualityVariantCollisionOnly) {
final logicalName = safFileName ?? finalName;
final stagingLabel = qualityVariantStagingLabel(item.id);
final cleanName = removeQualityVariantStagingLabel(
fileName: logicalName,
stagingLabel: stagingLabel,
);
final variantName = qualityLabel == null
? finalName
: applyQualityVariantFilenameLabel(
fileName: logicalName,
stagingLabel: stagingLabel,
qualityLabel: qualityLabel,
);
final published = await n._writeTempToSafCollisionAware(
treeUri: settings.downloadTreeUri,
relativeDir: effectiveOutputDir,
cleanFileName: cleanName,
variantFileName: variantName,
mimeType: n._mimeTypeForExt(localExt ?? safOutputExt),
srcPath: localPath,
preservedSuffix: qualityLabel ?? '',
);
publishedUri = published?.uri;
publishedName = published?.fileName;
} else if (item.preserveQualityVariant) {
final published = await n._writeTempToSafUnique(
treeUri: settings.downloadTreeUri,
relativeDir: effectiveOutputDir,
fileName: finalName,
mimeType: n._mimeTypeForExt(localExt ?? safOutputExt),
srcPath: localPath,
preservedSuffix: qualityLabel ?? '',
);
publishedUri = published?.uri;
publishedName = published?.fileName;
} else {
final published = await n._writeTempToSafIfAbsent(
treeUri: settings.downloadTreeUri,
relativeDir: effectiveOutputDir,
fileName: finalName,
mimeType: n._mimeTypeForExt(localExt ?? safOutputExt),
srcPath: localPath,
);
publishedUri = published?.uri;
publishedName = published?.fileName;
alreadyExists = published?.alreadyExists == true;
}
if (publishedUri == null || publishedName == null) return false;
try {
await localFile.delete();
} catch (_) {}
filePath = publishedUri;
finalSafFileName = publishedName;
wasExisting = alreadyExists;
result['file_path'] = publishedUri;
result['file_name'] = publishedName;
result['saf_deferred_published'] = true;
if (alreadyExists) {
result['already_exists'] = true;
result['message'] = 'File already exists';
}
_log.i('Published finalized SAF output once: $publishedName');
return true;
}
Future<void> _convertSafM4aToLossy(String currentFilePath) async {
final tidalHighFormat = settings.autoConvertDownloads
? autoConvertLossySetting(
+16
View File
@@ -801,6 +801,22 @@ class PlatformBridge {
return result as String?;
}
static Future<Map<String, dynamic>> createSafFileIfAbsentFromPath({
required String treeUri,
required String relativeDir,
required String fileName,
required String mimeType,
required String srcPath,
}) {
return _invokeMap('safCreateIfAbsentFromPath', {
'tree_uri': treeUri,
'relative_dir': relativeDir,
'file_name': fileName,
'mime_type': mimeType,
'src_path': srcPath,
});
}
static Future<Map<String, dynamic>> createUniqueSafFileFromPath({
required String treeUri,
required String relativeDir,