refactor(metadata): share the sidecar publish path between save-cover and save-lyrics

_saveCoverArt and _saveLyrics repeated the same SAF/local publish
skeleton: render into a temp dir and publish it to the download tree
with createSafFileFromPath (or write next to the audio file), clean the
temp dir, then report the outcome. Move that skeleton into
_publishSidecarFile with parameters for the temp prefix, extension, mime
type, success message and whether a snackbar hides the current one.

Each caller keeps its own try/catch, base name, lyrics progress
snackbar and the render step. The cover render returns null after its
no-source snackbar so the helper stops at the same point as before
(without deleting the temp dir, as before). Await order, mounted checks,
messages and cleanup placement are unchanged.
This commit is contained in:
zarzet committed 2026-10-19 00:23:50 +07:00
1 parent 20b4c73b8d
commit ff3f752014
1 file changed
+146 -257
+146 -257
View File
@@ -674,156 +674,151 @@ extension _TrackMetadataLyricsAndSaving on _TrackMetadataScreenState {
bool get _isSafFile => isContentUri(cleanFilePath);
/// Writes `baseName.extension` as a sidecar of the current track.
///
/// `writeTo` renders the file at the given path and returns the bridge
/// result, or null when it has already told the user why nothing was
/// written. SAF tracks are rendered into a temp directory and published to
/// the download tree; local tracks are written next to the audio file.
/// Exceptions propagate to the caller.
Future<void> _publishSidecarFile({
required String baseName,
required String tempDirPrefix,
required String extension,
required String mimeType,
required String Function() successMessage,
required bool hideCurrentSnackBar,
required Future<Map<String, dynamic>?> Function(String outputPath) writeTo,
}) async {
void showMessage(String message) {
final messenger = ScaffoldMessenger.of(context);
if (hideCurrentSnackBar) {
messenger.hideCurrentSnackBar();
}
messenger.showSnackBar(SnackBar(content: Text(message)));
}
final fileName = '$baseName.$extension';
if (_isSafFile) {
final tempDir = await Directory.systemTemp.createTemp(tempDirPrefix);
final tempOutput = '${tempDir.path}${Platform.pathSeparator}$fileName';
final result = await writeTo(tempOutput);
if (result == null) {
return;
}
if (result['error'] != null) {
if (mounted) {
showMessage(
context.l10n.trackSaveFailed(
context.friendlyError(result['error']),
),
);
}
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
return;
}
final treeUri = _downloadItem?.downloadTreeUri;
final relativeDir = _downloadItem?.safRelativeDir ?? '';
if (treeUri != null && treeUri.isNotEmpty) {
final safUri = await PlatformBridge.createSafFileFromPath(
treeUri: treeUri,
relativeDir: relativeDir,
fileName: fileName,
mimeType: mimeType,
srcPath: tempOutput,
);
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
if (mounted) {
if (safUri != null) {
showMessage(successMessage());
} else {
showMessage(
context.l10n.trackSaveFailed('Failed to write to storage'),
);
}
}
} else {
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
if (mounted) {
showMessage(context.l10n.trackSaveFailed('No storage access'));
}
}
return;
}
final dir = _getFileDirectory();
final outputPath = '$dir${Platform.pathSeparator}$fileName';
final result = await writeTo(outputPath);
if (result == null) {
return;
}
if (mounted) {
if (result['error'] != null) {
showMessage(
context.l10n.trackSaveFailed(context.friendlyError(result['error'])),
);
} else {
showMessage(successMessage());
}
}
}
Future<void> _saveCoverArt() async {
try {
final baseName = _buildSaveBaseName();
if (_isSafFile) {
final tempDir = await Directory.systemTemp.createTemp('cover_');
final tempOutput =
'${tempDir.path}${Platform.pathSeparator}$baseName.jpg';
Map<String, dynamic> result;
if (_fileExists) {
// Prefer extracting cover from the already-downloaded file to avoid
// a redundant network request.
result = await PlatformBridge.extractCoverToFile(
cleanFilePath,
tempOutput,
);
if (result['error'] != null &&
_coverUrl != null &&
_coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
tempOutput,
await _publishSidecarFile(
baseName: baseName,
tempDirPrefix: 'cover_',
extension: 'jpg',
mimeType: 'image/jpeg',
successMessage: () => context.l10n.trackCoverSaved(baseName),
hideCurrentSnackBar: false,
writeTo: (outputPath) async {
Map<String, dynamic> result;
if (_fileExists) {
// Prefer extracting cover from the already-downloaded file to
// avoid a redundant network request.
result = await PlatformBridge.extractCoverToFile(
cleanFilePath,
outputPath,
);
}
} else if (_coverUrl != null && _coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
tempOutput,
);
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.trackCoverNoSource)),
);
}
return;
}
if (result['error'] != null) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
context.l10n.trackSaveFailed(
context.friendlyError(result['error']),
),
),
),
);
}
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
return;
}
final treeUri = _downloadItem?.downloadTreeUri;
final relativeDir = _downloadItem?.safRelativeDir ?? '';
if (treeUri != null && treeUri.isNotEmpty) {
final safUri = await PlatformBridge.createSafFileFromPath(
treeUri: treeUri,
relativeDir: relativeDir,
fileName: '$baseName.jpg',
mimeType: 'image/jpeg',
srcPath: tempOutput,
);
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
if (mounted) {
if (safUri != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.trackCoverSaved(baseName))),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
context.l10n.trackSaveFailed('Failed to write to storage'),
),
),
if (result['error'] != null &&
_coverUrl != null &&
_coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
outputPath,
);
}
}
} else {
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
context.l10n.trackSaveFailed('No storage access'),
),
),
} else if (_coverUrl != null && _coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
outputPath,
);
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.trackCoverNoSource)),
);
}
return null;
}
}
return;
}
final dir = _getFileDirectory();
final outputPath = '$dir${Platform.pathSeparator}$baseName.jpg';
Map<String, dynamic> result;
if (_fileExists) {
result = await PlatformBridge.extractCoverToFile(
cleanFilePath,
outputPath,
);
if (result['error'] != null &&
_coverUrl != null &&
_coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
outputPath,
);
}
} else if (_coverUrl != null && _coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
outputPath,
);
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.trackCoverNoSource)),
);
}
return;
}
if (mounted) {
if (result['error'] != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
context.l10n.trackSaveFailed(
context.friendlyError(result['error']),
),
),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(context.l10n.trackCoverSaved(baseName))),
);
}
}
return result;
},
);
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -849,128 +844,22 @@ extension _TrackMetadataLyricsAndSaving on _TrackMetadataScreenState {
);
}
if (_isSafFile) {
final tempDir = await Directory.systemTemp.createTemp('lyrics_');
final tempOutput =
'${tempDir.path}${Platform.pathSeparator}$baseName.lrc';
final result = await PlatformBridge.fetchAndSaveLyrics(
await _publishSidecarFile(
baseName: baseName,
tempDirPrefix: 'lyrics_',
extension: 'lrc',
mimeType: 'application/octet-stream',
successMessage: () => context.l10n.trackLyricsSaved(baseName),
hideCurrentSnackBar: true,
writeTo: (outputPath) => PlatformBridge.fetchAndSaveLyrics(
trackName: trackName,
artistName: artistName,
spotifyId: _spotifyId ?? '',
durationMs: durationMs,
outputPath: tempOutput,
outputPath: outputPath,
audioFilePath: _fileExists ? cleanFilePath : '',
);
if (result['error'] != null) {
if (mounted) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text(
context.l10n.trackSaveFailed(
context.friendlyError(result['error']),
),
),
),
);
}
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
return;
}
final treeUri = _downloadItem?.downloadTreeUri;
final relativeDir = _downloadItem?.safRelativeDir ?? '';
if (treeUri != null && treeUri.isNotEmpty) {
final safUri = await PlatformBridge.createSafFileFromPath(
treeUri: treeUri,
relativeDir: relativeDir,
fileName: '$baseName.lrc',
mimeType: 'application/octet-stream',
srcPath: tempOutput,
);
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
if (mounted) {
if (safUri != null) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text(context.l10n.trackLyricsSaved(baseName)),
),
);
} else {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text(
context.l10n.trackSaveFailed(
'Failed to write to storage',
),
),
),
);
}
}
} else {
try {
await Directory(tempDir.path).delete(recursive: true);
} catch (_) {}
if (mounted) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text(
context.l10n.trackSaveFailed('No storage access'),
),
),
);
}
}
return;
}
final dir = _getFileDirectory();
final outputPath = '$dir${Platform.pathSeparator}$baseName.lrc';
final result = await PlatformBridge.fetchAndSaveLyrics(
trackName: trackName,
artistName: artistName,
spotifyId: _spotifyId ?? '',
durationMs: durationMs,
outputPath: outputPath,
audioFilePath: _fileExists ? cleanFilePath : '',
),
);
if (mounted) {
if (result['error'] != null) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text(
context.l10n.trackSaveFailed(
context.friendlyError(result['error']),
),
),
),
);
} else {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(content: Text(context.l10n.trackLyricsSaved(baseName))),
);
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context)