fix(queue): clarify and coalesce verification progress

This commit is contained in:
zarzet
2026-07-23 09:57:02 +07:00
parent 685392f073
commit 480a96bd0e
23 changed files with 335 additions and 17 deletions
+30
View File
@@ -6694,6 +6694,36 @@ abstract class AppLocalizations {
/// **'Starting...'**
String get queueDownloadStarting;
/// No description provided for @queueCheckingDownloadSession.
///
/// In en, this message translates to:
/// **'Checking download session...'**
String get queueCheckingDownloadSession;
/// No description provided for @queueResolvingDownloadMetadata.
///
/// In en, this message translates to:
/// **'Resolving track metadata...'**
String get queueResolvingDownloadMetadata;
/// No description provided for @queueResolvingDownloadStream.
///
/// In en, this message translates to:
/// **'Preparing audio stream...'**
String get queueResolvingDownloadStream;
/// No description provided for @queueWaitingForVerification.
///
/// In en, this message translates to:
/// **'Waiting for verification...'**
String get queueWaitingForVerification;
/// No description provided for @queueResumingAfterVerification.
///
/// In en, this message translates to:
/// **'Resuming after verification...'**
String get queueResumingAfterVerification;
/// Accessibility label for selecting a track
///
/// In en, this message translates to:
+15
View File
@@ -4075,6 +4075,21 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -4120,6 +4120,21 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starte...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Titel auswählen';
+15
View File
@@ -4075,6 +4075,21 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -4069,6 +4069,21 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -4181,6 +4181,21 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get queueDownloadStarting => 'C\'est parti...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Sélectionner une piste';
+15
View File
@@ -4075,6 +4075,21 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+16
View File
@@ -4078,6 +4078,22 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Memeriksa sesi unduhan...';
@override
String get queueResolvingDownloadMetadata => 'Mencari metadata lagu...';
@override
String get queueResolvingDownloadStream => 'Menyiapkan stream audio...';
@override
String get queueWaitingForVerification => 'Menunggu verifikasi...';
@override
String get queueResumingAfterVerification =>
'Melanjutkan setelah verifikasi...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -4064,6 +4064,21 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -3954,6 +3954,21 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get queueDownloadStarting => '시작하는 중...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => '트랙 선택';
+15
View File
@@ -4075,6 +4075,21 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -4069,6 +4069,21 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -4106,6 +4106,21 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -4105,6 +4105,21 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -4123,6 +4123,21 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+15
View File
@@ -4069,6 +4069,21 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get queueDownloadStarting => 'Starting...';
@override
String get queueCheckingDownloadSession => 'Checking download session...';
@override
String get queueResolvingDownloadMetadata => 'Resolving track metadata...';
@override
String get queueResolvingDownloadStream => 'Preparing audio stream...';
@override
String get queueWaitingForVerification => 'Waiting for verification...';
@override
String get queueResumingAfterVerification => 'Resuming after verification...';
@override
String get a11ySelectTrack => 'Select track';
+5
View File
@@ -5268,6 +5268,11 @@
}
},
"queueDownloadStarting": "Starting...",
"queueCheckingDownloadSession": "Checking download session...",
"queueResolvingDownloadMetadata": "Resolving track metadata...",
"queueResolvingDownloadStream": "Preparing audio stream...",
"queueWaitingForVerification": "Waiting for verification...",
"queueResumingAfterVerification": "Resuming after verification...",
"@queueDownloadStarting": {
"description": "Queue status before download progress is available"
},
+5
View File
@@ -5307,6 +5307,11 @@
"extensionHealthServiceUnknown": "Service status unknown",
"navHome": "Beranda",
"queueDownloadStarting": "Starting...",
"queueCheckingDownloadSession": "Memeriksa sesi unduhan...",
"queueResolvingDownloadMetadata": "Mencari metadata lagu...",
"queueResolvingDownloadStream": "Menyiapkan stream audio...",
"queueWaitingForVerification": "Menunggu verifikasi...",
"queueResumingAfterVerification": "Melanjutkan setelah verifikasi...",
"@dialogRemoveExtension": {
"description": "Dialog title - uninstall extension"
},
+4
View File
@@ -34,6 +34,7 @@ class DownloadItem {
final String? filePath;
final String? error;
final DownloadErrorType? errorType;
final String preparationStage;
final DateTime createdAt;
final String? qualityOverride;
final String? playlistName;
@@ -53,6 +54,7 @@ class DownloadItem {
this.filePath,
this.error,
this.errorType,
this.preparationStage = '',
required this.createdAt,
this.qualityOverride,
this.playlistName,
@@ -73,6 +75,7 @@ class DownloadItem {
String? filePath,
String? error,
DownloadErrorType? errorType,
String? preparationStage,
DateTime? createdAt,
String? qualityOverride,
String? playlistName,
@@ -92,6 +95,7 @@ class DownloadItem {
filePath: filePath ?? this.filePath,
error: error ?? this.error,
errorType: errorType ?? this.errorType,
preparationStage: preparationStage ?? this.preparationStage,
createdAt: createdAt ?? this.createdAt,
qualityOverride: qualityOverride ?? this.qualityOverride,
playlistName: playlistName ?? this.playlistName,
+2
View File
@@ -20,6 +20,7 @@ DownloadItem _$DownloadItemFromJson(Map<String, dynamic> json) => DownloadItem(
filePath: json['filePath'] as String?,
error: json['error'] as String?,
errorType: $enumDecodeNullable(_$DownloadErrorTypeEnumMap, json['errorType']),
preparationStage: json['preparationStage'] as String? ?? '',
createdAt: DateTime.parse(json['createdAt'] as String),
qualityOverride: json['qualityOverride'] as String?,
playlistName: json['playlistName'] as String?,
@@ -41,6 +42,7 @@ Map<String, dynamic> _$DownloadItemToJson(DownloadItem instance) =>
'filePath': instance.filePath,
'error': instance.error,
'errorType': _$DownloadErrorTypeEnumMap[instance.errorType],
'preparationStage': instance.preparationStage,
'createdAt': instance.createdAt.toIso8601String(),
'qualityOverride': instance.qualityOverride,
'playlistName': instance.playlistName,
+44 -17
View File
@@ -145,6 +145,7 @@ class _ProgressUpdate {
final double? speedMBps;
final int? bytesReceived;
final int? bytesTotal;
final String preparationStage;
const _ProgressUpdate({
required this.status,
@@ -152,6 +153,7 @@ class _ProgressUpdate {
this.speedMBps,
this.bytesReceived,
this.bytesTotal,
this.preparationStage = '',
});
}
@@ -224,6 +226,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
final Set<String> _locallyCancelledItemIds = {};
final Set<String> _pausePendingItemIds = {};
final Set<String> _verificationRetriedItemIds = {};
final Map<String, Future<bool>> _verificationFlowsByExtension = {};
final Set<String> _rateLimitRetriedItemIds = {};
String? _activeNativeWorkerRunId;
@@ -412,21 +415,42 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
}
Future<bool> _openVerificationAndWait(String extensionId) {
return openVerificationAndAwaitGrant(
extensionId,
browserMode: ref.read(settingsProvider).extensionVerificationBrowserMode,
awaitForeground: (normalizedExtensionId) async {
if (WidgetsBinding.instance.lifecycleState !=
AppLifecycleState.resumed) {
_log.i(
'Verification required for $normalizedExtensionId while app is in '
'background; deferring challenge until the app is foregrounded',
);
unawaited(_notificationService.showVerificationRequired());
await _waitForForeground();
}
},
);
final key = extensionId.trim().toLowerCase();
final activeFlow = _verificationFlowsByExtension[key];
if (activeFlow != null) {
_log.i(
'Joining active verification flow for $extensionId instead of opening another challenge',
);
return activeFlow;
}
final flow = _runVerificationFlow(extensionId, key);
_verificationFlowsByExtension[key] = flow;
return flow;
}
Future<bool> _runVerificationFlow(String extensionId, String key) async {
try {
return await openVerificationAndAwaitGrant(
extensionId,
browserMode: ref
.read(settingsProvider)
.extensionVerificationBrowserMode,
awaitForeground: (normalizedExtensionId) async {
if (WidgetsBinding.instance.lifecycleState !=
AppLifecycleState.resumed) {
_log.i(
'Verification required for $normalizedExtensionId while app is in '
'background; deferring challenge until the app is foregrounded',
);
unawaited(_notificationService.showVerificationRequired());
await _waitForForeground();
}
},
);
} finally {
_verificationFlowsByExtension.remove(key);
}
}
Future<bool> _handleVerificationRequiredDownload(
@@ -727,12 +751,13 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
}
if (status == 'preparing') {
progressUpdates[itemId] = const _ProgressUpdate(
progressUpdates[itemId] = _ProgressUpdate(
status: DownloadStatus.downloading,
progress: 0.0,
speedMBps: 0,
bytesReceived: 0,
bytesTotal: 0,
preparationStage: itemProgress['stage']?.toString() ?? '',
);
if (LogBuffer.loggingEnabled) {
@@ -801,12 +826,14 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
speedMBps: update.speedMBps ?? current.speedMBps,
bytesReceived: update.bytesReceived ?? current.bytesReceived,
bytesTotal: update.bytesTotal ?? current.bytesTotal,
preparationStage: update.preparationStage,
);
if (current.status != next.status ||
current.progress != next.progress ||
current.speedMBps != next.speedMBps ||
current.bytesReceived != next.bytesReceived ||
current.bytesTotal != next.bytesTotal) {
current.bytesTotal != next.bytesTotal ||
current.preparationStage != next.preparationStage) {
if (!changed) {
updatedItems = List<DownloadItem>.from(updatedItems);
changed = true;
+15
View File
@@ -93,6 +93,21 @@ String _formatDownloadProgressLabel(BuildContext context, DownloadItem item) {
);
}
if (item.error == 'Waiting for verification') {
return context.l10n.queueWaitingForVerification;
}
switch (item.preparationStage) {
case 'checking_session':
return context.l10n.queueCheckingDownloadSession;
case 'resolving_metadata':
return context.l10n.queueResolvingDownloadMetadata;
case 'resolving_stream':
return context.l10n.queueResolvingDownloadStream;
}
if (item.error == 'Retrying after verification') {
return context.l10n.queueResumingAfterVerification;
}
return context.l10n.queueDownloadStarting;
}
+4
View File
@@ -213,6 +213,7 @@ void main() {
bytesTotal: 1024,
qualityOverride: 'HI_RES',
playlistName: 'Favorites',
preparationStage: 'resolving_stream',
preserveQualityVariant: true,
);
@@ -227,6 +228,7 @@ void main() {
expect(updated.bytesTotal, 1024);
expect(updated.qualityOverride, 'HI_RES');
expect(updated.playlistName, 'Favorites');
expect(updated.preparationStage, 'resolving_stream');
expect(updated.preserveQualityVariant, isTrue);
});
@@ -279,9 +281,11 @@ void main() {
expect(item.errorType, DownloadErrorType.network);
expect(item.progress, 0);
expect(item.bytesReceived, 0);
expect(item.preparationStage, isEmpty);
expect(item.preserveQualityVariant, isFalse);
expect(item.toJson()['status'], 'failed');
expect(item.toJson()['errorType'], 'network');
expect(item.toJson()['preparationStage'], isEmpty);
});
});