mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-02 00:58:37 +02:00
fix(queue): keep cancelled downloads retryable across every cancel path and app restarts
This commit is contained in:
@@ -5948,6 +5948,18 @@ abstract class AppLocalizations {
|
||||
/// **'Keep'**
|
||||
String get cancelDownloadKeep;
|
||||
|
||||
/// Dialog title when opening a cancelled queue item
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Download cancelled'**
|
||||
String get queueCancelledTitle;
|
||||
|
||||
/// Dialog body when opening a cancelled queue item
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This download was cancelled. Retry it or remove it from the queue.'**
|
||||
String get queueCancelledMessage;
|
||||
|
||||
/// Snackbar error when FFmpeg fails to write metadata
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -3590,6 +3590,13 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Behalten';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg =>
|
||||
'Fehler beim Speichern der Metadaten über FFmpeg';
|
||||
|
||||
@@ -3547,6 +3547,13 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Keep';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg';
|
||||
|
||||
|
||||
@@ -3547,6 +3547,13 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Keep';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg';
|
||||
|
||||
|
||||
@@ -3641,6 +3641,13 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Conserver';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg =>
|
||||
'Échec de l\'enregistrement des métadonnées via FFmpeg';
|
||||
|
||||
@@ -3550,6 +3550,13 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Keep';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg';
|
||||
|
||||
|
||||
@@ -3536,6 +3536,13 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Keep';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg';
|
||||
|
||||
|
||||
@@ -3443,6 +3443,13 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => '유지';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg => 'FFmpeg를 통해 메타데이터 저장 실패';
|
||||
|
||||
|
||||
@@ -3547,6 +3547,13 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Keep';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg';
|
||||
|
||||
|
||||
@@ -3576,6 +3576,13 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Удерживать';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg =>
|
||||
'Не удалось сохранить метаданные через FFmpeg';
|
||||
|
||||
@@ -3577,6 +3577,13 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Keep';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg => 'Failed to save metadata via FFmpeg';
|
||||
|
||||
|
||||
@@ -3594,6 +3594,13 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
@override
|
||||
String get cancelDownloadKeep => 'Зберегти';
|
||||
|
||||
@override
|
||||
String get queueCancelledTitle => 'Download cancelled';
|
||||
|
||||
@override
|
||||
String get queueCancelledMessage =>
|
||||
'This download was cancelled. Retry it or remove it from the queue.';
|
||||
|
||||
@override
|
||||
String get metadataSaveFailedFfmpeg =>
|
||||
'Не вдалося зберегти метадані через FFmpeg';
|
||||
|
||||
@@ -4577,6 +4577,14 @@
|
||||
"@cancelDownloadKeep": {
|
||||
"description": "Dialog button - keep the active download (do not cancel)"
|
||||
},
|
||||
"queueCancelledTitle": "Download cancelled",
|
||||
"@queueCancelledTitle": {
|
||||
"description": "Dialog title when opening a cancelled queue item"
|
||||
},
|
||||
"queueCancelledMessage": "This download was cancelled. Retry it or remove it from the queue.",
|
||||
"@queueCancelledMessage": {
|
||||
"description": "Dialog body when opening a cancelled queue item"
|
||||
},
|
||||
"metadataSaveFailedFfmpeg": "Failed to save metadata via FFmpeg",
|
||||
"@metadataSaveFailedFfmpeg": {
|
||||
"description": "Snackbar error when FFmpeg fails to write metadata"
|
||||
|
||||
@@ -261,7 +261,8 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
item.status == DownloadStatus.finalizing) {
|
||||
item = item.copyWith(status: DownloadStatus.queued, progress: 0);
|
||||
}
|
||||
if (item.status == DownloadStatus.queued) {
|
||||
if (item.status == DownloadStatus.queued ||
|
||||
item.status == DownloadStatus.skipped) {
|
||||
pendingItems.add(item);
|
||||
}
|
||||
} catch (_) {
|
||||
@@ -306,12 +307,15 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
|
||||
Future<void> _writeQueueChanges() async {
|
||||
try {
|
||||
// skipped (user-cancelled) rows persist too, so a cancelled download can
|
||||
// still be retried after an app restart instead of being re-searched.
|
||||
final pendingItems = state.items
|
||||
.where(
|
||||
(item) =>
|
||||
item.status == DownloadStatus.queued ||
|
||||
item.status == DownloadStatus.downloading ||
|
||||
item.status == DownloadStatus.finalizing,
|
||||
item.status == DownloadStatus.finalizing ||
|
||||
item.status == DownloadStatus.skipped,
|
||||
)
|
||||
.toList(growable: false);
|
||||
final nowIso = DateTime.now().toIso8601String();
|
||||
|
||||
@@ -1624,7 +1624,9 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
),
|
||||
);
|
||||
if (confirmed == true) {
|
||||
ref.read(downloadQueueProvider.notifier).dismissItem(item.id);
|
||||
// cancelItem (not dismissItem): the item stays in the queue as
|
||||
// cancelled so it can be retried without re-searching the track.
|
||||
ref.read(downloadQueueProvider.notifier).cancelItem(item.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1634,13 +1636,18 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
) async {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final isRateLimit = item.errorType == DownloadErrorType.rateLimit;
|
||||
final isCancelled = item.status == DownloadStatus.skipped;
|
||||
final isFolderAccessLost =
|
||||
item.errorMessage == safPermissionLostErrorMessage ||
|
||||
item.errorMessage == downloadFolderAccessLostErrorMessage;
|
||||
final title = isRateLimit
|
||||
final title = isCancelled
|
||||
? context.l10n.queueCancelledTitle
|
||||
: isRateLimit
|
||||
? context.l10n.queueRateLimitTitle
|
||||
: context.l10n.updateDownloadFailed;
|
||||
final message = isRateLimit
|
||||
final message = isCancelled
|
||||
? context.l10n.queueCancelledMessage
|
||||
: isRateLimit
|
||||
? context.l10n.queueRateLimitMessage
|
||||
: (item.errorMessage.trim().isNotEmpty
|
||||
? _localizedDownloadError(context, item.errorMessage)
|
||||
|
||||
@@ -27,10 +27,8 @@ extension _QueueTabCollectionItemWidgets on _QueueTabState {
|
||||
child: Icon(Icons.music_note, color: colorScheme.onSurfaceVariant),
|
||||
);
|
||||
|
||||
final onTap = isFailed
|
||||
final onTap = isFailed || item.status == DownloadStatus.skipped
|
||||
? () => _showDownloadErrorDialog(context, item)
|
||||
: item.status == DownloadStatus.skipped
|
||||
? () => ref.read(downloadQueueProvider.notifier).removeItem(item.id)
|
||||
: () => _confirmCancelDownload(context, item);
|
||||
|
||||
return SmoothedProgressScope(
|
||||
|
||||
Reference in New Issue
Block a user