mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-30 15:58:51 +02:00
feat(download): preserve quality variants
This commit is contained in:
@@ -6524,12 +6524,36 @@ abstract class AppLocalizations {
|
||||
/// **'Already-downloaded tracks will be skipped'**
|
||||
String get downloadDeduplicationEnabled;
|
||||
|
||||
/// Deduplication subtitle when separate quality versions are allowed
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Existing files at the selected quality will be skipped'**
|
||||
String get downloadDeduplicationWithQualityVariants;
|
||||
|
||||
/// Subtitle when deduplication is off
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'All tracks will be downloaded regardless of history'**
|
||||
String get downloadDeduplicationDisabled;
|
||||
|
||||
/// Setting to retain multiple quality versions of the same track
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Allow different quality versions'**
|
||||
String get downloadQualityVariants;
|
||||
|
||||
/// Description for retaining multiple quality versions
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add the selected quality to the filename and keep each version in download history'**
|
||||
String get downloadQualityVariantsDescription;
|
||||
|
||||
/// Track menu action to download another quality version
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Download another quality'**
|
||||
String get trackOptionDownloadQualityVariant;
|
||||
|
||||
/// Settings item for configuring fallback extension providers
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -3929,10 +3929,24 @@ class AppLocalizationsAr extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3975,10 +3975,24 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Bereits heruntergeladene Titel werden übersprungen';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'Alle Titel werden unabhängig vom Verlauf heruntergeladen';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback-Erweiterungen';
|
||||
|
||||
|
||||
@@ -3929,10 +3929,24 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3923,10 +3923,24 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -4033,10 +4033,24 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Les morceaux déjà téléchargés seront ignorés';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'Tous les morceaux seront téléchargés, quel que soit l\'historique';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Extensions de secours';
|
||||
|
||||
|
||||
@@ -3929,10 +3929,24 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3932,10 +3932,24 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'File yang sudah ada pada kualitas yang dipilih akan dilewati';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Izinkan versi dengan kualitas berbeda';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Tambahkan kualitas yang dipilih ke nama file dan simpan setiap versi di riwayat unduhan';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Unduh kualitas lain';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3918,10 +3918,24 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3810,9 +3810,23 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
@override
|
||||
String get downloadDeduplicationEnabled => '이미 다운로드된 트랙은 건너뜁니다';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled => '기록과 관계없이 모든 트랙이 다운로드됩니다';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => '대체 확장 프로그램';
|
||||
|
||||
|
||||
@@ -3929,10 +3929,24 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3923,10 +3923,24 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3960,10 +3960,24 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3959,10 +3959,24 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3977,10 +3977,24 @@ class AppLocalizationsUk extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -3923,10 +3923,24 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get downloadDeduplicationEnabled =>
|
||||
'Already-downloaded tracks will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationWithQualityVariants =>
|
||||
'Existing files at the selected quality will be skipped';
|
||||
|
||||
@override
|
||||
String get downloadDeduplicationDisabled =>
|
||||
'All tracks will be downloaded regardless of history';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariants => 'Allow different quality versions';
|
||||
|
||||
@override
|
||||
String get downloadQualityVariantsDescription =>
|
||||
'Add the selected quality to the filename and keep each version in download history';
|
||||
|
||||
@override
|
||||
String get trackOptionDownloadQualityVariant => 'Download another quality';
|
||||
|
||||
@override
|
||||
String get downloadFallbackExtensions => 'Fallback Extensions';
|
||||
|
||||
|
||||
@@ -5108,10 +5108,26 @@
|
||||
"@downloadDeduplicationEnabled": {
|
||||
"description": "Subtitle when deduplication is on"
|
||||
},
|
||||
"downloadDeduplicationWithQualityVariants": "Existing files at the selected quality will be skipped",
|
||||
"@downloadDeduplicationWithQualityVariants": {
|
||||
"description": "Deduplication subtitle when separate quality versions are allowed"
|
||||
},
|
||||
"downloadDeduplicationDisabled": "All tracks will be downloaded regardless of history",
|
||||
"@downloadDeduplicationDisabled": {
|
||||
"description": "Subtitle when deduplication is off"
|
||||
},
|
||||
"downloadQualityVariants": "Allow different quality versions",
|
||||
"@downloadQualityVariants": {
|
||||
"description": "Setting to retain multiple quality versions of the same track"
|
||||
},
|
||||
"downloadQualityVariantsDescription": "Add the selected quality to the filename and keep each version in download history",
|
||||
"@downloadQualityVariantsDescription": {
|
||||
"description": "Description for retaining multiple quality versions"
|
||||
},
|
||||
"trackOptionDownloadQualityVariant": "Download another quality",
|
||||
"@trackOptionDownloadQualityVariant": {
|
||||
"description": "Track menu action to download another quality version"
|
||||
},
|
||||
"downloadFallbackExtensions": "Fallback Extensions",
|
||||
"@downloadFallbackExtensions": {
|
||||
"description": "Settings item for configuring fallback extension providers"
|
||||
|
||||
@@ -5915,5 +5915,21 @@
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"downloadQualityVariants": "Izinkan versi dengan kualitas berbeda",
|
||||
"@downloadQualityVariants": {
|
||||
"description": "Pengaturan untuk menyimpan beberapa versi kualitas dari lagu yang sama"
|
||||
},
|
||||
"downloadQualityVariantsDescription": "Tambahkan kualitas yang dipilih ke nama file dan simpan setiap versi di riwayat unduhan",
|
||||
"@downloadQualityVariantsDescription": {
|
||||
"description": "Deskripsi penyimpanan beberapa versi kualitas"
|
||||
},
|
||||
"trackOptionDownloadQualityVariant": "Unduh kualitas lain",
|
||||
"@trackOptionDownloadQualityVariant": {
|
||||
"description": "Aksi menu lagu untuk mengunduh versi kualitas lain"
|
||||
},
|
||||
"downloadDeduplicationWithQualityVariants": "File yang sudah ada pada kualitas yang dipilih akan dilewati",
|
||||
"@downloadDeduplicationWithQualityVariants": {
|
||||
"description": "Subtitle deduplikasi ketika versi kualitas terpisah diizinkan"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ class DownloadItem {
|
||||
final String? playlistName;
|
||||
final int? playlistPosition; // 1-based position in the source playlist
|
||||
final bool fromBatch;
|
||||
final bool preserveQualityVariant;
|
||||
|
||||
const DownloadItem({
|
||||
required this.id,
|
||||
@@ -57,6 +58,7 @@ class DownloadItem {
|
||||
this.playlistName,
|
||||
this.playlistPosition,
|
||||
this.fromBatch = false,
|
||||
this.preserveQualityVariant = false,
|
||||
});
|
||||
|
||||
DownloadItem copyWith({
|
||||
@@ -76,6 +78,7 @@ class DownloadItem {
|
||||
String? playlistName,
|
||||
int? playlistPosition,
|
||||
bool? fromBatch,
|
||||
bool? preserveQualityVariant,
|
||||
}) {
|
||||
return DownloadItem(
|
||||
id: id ?? this.id,
|
||||
@@ -94,6 +97,8 @@ class DownloadItem {
|
||||
playlistName: playlistName ?? this.playlistName,
|
||||
playlistPosition: playlistPosition ?? this.playlistPosition,
|
||||
fromBatch: fromBatch ?? this.fromBatch,
|
||||
preserveQualityVariant:
|
||||
preserveQualityVariant ?? this.preserveQualityVariant,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ DownloadItem _$DownloadItemFromJson(Map<String, dynamic> json) => DownloadItem(
|
||||
playlistName: json['playlistName'] as String?,
|
||||
playlistPosition: (json['playlistPosition'] as num?)?.toInt(),
|
||||
fromBatch: json['fromBatch'] as bool? ?? false,
|
||||
preserveQualityVariant: json['preserveQualityVariant'] as bool? ?? false,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DownloadItemToJson(DownloadItem instance) =>
|
||||
@@ -45,6 +46,7 @@ Map<String, dynamic> _$DownloadItemToJson(DownloadItem instance) =>
|
||||
'playlistName': instance.playlistName,
|
||||
'playlistPosition': instance.playlistPosition,
|
||||
'fromBatch': instance.fromBatch,
|
||||
'preserveQualityVariant': instance.preserveQualityVariant,
|
||||
};
|
||||
|
||||
const _$DownloadStatusEnumMap = {
|
||||
|
||||
@@ -97,6 +97,7 @@ class AppSettings {
|
||||
lastSeenVersion; // Last app version the user has acknowledged (e.g. '3.7.0')
|
||||
|
||||
final bool deduplicateDownloads;
|
||||
final bool allowQualityVariants;
|
||||
final bool saveDownloadHistory;
|
||||
|
||||
final String playerMode;
|
||||
@@ -165,6 +166,7 @@ class AppSettings {
|
||||
this.musixmatchLanguage = '',
|
||||
this.lastSeenVersion = '',
|
||||
this.deduplicateDownloads = true,
|
||||
this.allowQualityVariants = false,
|
||||
this.saveDownloadHistory = true,
|
||||
this.playerMode = 'external',
|
||||
});
|
||||
@@ -236,6 +238,7 @@ class AppSettings {
|
||||
String? musixmatchLanguage,
|
||||
String? lastSeenVersion,
|
||||
bool? deduplicateDownloads,
|
||||
bool? allowQualityVariants,
|
||||
bool? saveDownloadHistory,
|
||||
String? playerMode,
|
||||
}) {
|
||||
@@ -329,6 +332,7 @@ class AppSettings {
|
||||
musixmatchLanguage: musixmatchLanguage ?? this.musixmatchLanguage,
|
||||
lastSeenVersion: lastSeenVersion ?? this.lastSeenVersion,
|
||||
deduplicateDownloads: deduplicateDownloads ?? this.deduplicateDownloads,
|
||||
allowQualityVariants: allowQualityVariants ?? this.allowQualityVariants,
|
||||
saveDownloadHistory: saveDownloadHistory ?? this.saveDownloadHistory,
|
||||
playerMode: playerMode ?? this.playerMode,
|
||||
);
|
||||
|
||||
@@ -87,6 +87,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
|
||||
musixmatchLanguage: json['musixmatchLanguage'] as String? ?? '',
|
||||
lastSeenVersion: json['lastSeenVersion'] as String? ?? '',
|
||||
deduplicateDownloads: json['deduplicateDownloads'] as bool? ?? true,
|
||||
allowQualityVariants: json['allowQualityVariants'] as bool? ?? false,
|
||||
saveDownloadHistory: json['saveDownloadHistory'] as bool? ?? true,
|
||||
playerMode: json['playerMode'] as String? ?? 'external',
|
||||
);
|
||||
@@ -158,6 +159,7 @@ Map<String, dynamic> _$AppSettingsToJson(
|
||||
'musixmatchLanguage': instance.musixmatchLanguage,
|
||||
'lastSeenVersion': instance.lastSeenVersion,
|
||||
'deduplicateDownloads': instance.deduplicateDownloads,
|
||||
'allowQualityVariants': instance.allowQualityVariants,
|
||||
'saveDownloadHistory': instance.saveDownloadHistory,
|
||||
'playerMode': instance.playerMode,
|
||||
};
|
||||
|
||||
@@ -1028,17 +1028,35 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
|
||||
return byId.values.toList(growable: false);
|
||||
}
|
||||
|
||||
void addToHistory(DownloadHistoryItem item) =>
|
||||
_persistHistoryItem(item, 'save to database');
|
||||
void addToHistory(
|
||||
DownloadHistoryItem item, {
|
||||
bool preserveTrackVariant = false,
|
||||
}) => _persistHistoryItem(
|
||||
item,
|
||||
'save to database',
|
||||
preserveTrackVariant: preserveTrackVariant,
|
||||
);
|
||||
|
||||
void adoptNativeHistoryItem(DownloadHistoryItem item) =>
|
||||
_persistHistoryItem(item, 'adopt native history item');
|
||||
void adoptNativeHistoryItem(
|
||||
DownloadHistoryItem item, {
|
||||
bool preserveTrackVariant = false,
|
||||
}) => _persistHistoryItem(
|
||||
item,
|
||||
'adopt native history item',
|
||||
preserveTrackVariant: preserveTrackVariant,
|
||||
);
|
||||
|
||||
void _persistHistoryItem(DownloadHistoryItem item, String action) {
|
||||
void _persistHistoryItem(
|
||||
DownloadHistoryItem item,
|
||||
String action, {
|
||||
required bool preserveTrackVariant,
|
||||
}) {
|
||||
unawaited(
|
||||
() async {
|
||||
final mergedItem = await _putInMemoryHistory(item);
|
||||
await _db.upsert(mergedItem.toJson());
|
||||
final persistedItem = preserveTrackVariant
|
||||
? _putInMemoryTrackVariant(item)
|
||||
: await _putInMemoryHistory(item);
|
||||
await _db.upsert(persistedItem.toJson());
|
||||
_bumpHistoryRevision();
|
||||
}().catchError((Object e, StackTrace stack) {
|
||||
_historyLog.e('Failed to $action: $e', e, stack);
|
||||
@@ -1046,6 +1064,25 @@ class DownloadHistoryNotifier extends Notifier<DownloadHistoryState> {
|
||||
);
|
||||
}
|
||||
|
||||
DownloadHistoryItem _putInMemoryTrackVariant(DownloadHistoryItem item) {
|
||||
final isReplacement = state.items.any((existing) => existing.id == item.id);
|
||||
final items = [
|
||||
item,
|
||||
...state.items.where((existing) => existing.id != item.id),
|
||||
];
|
||||
final lookupItems = [
|
||||
item,
|
||||
...state.lookupItems.where((existing) => existing.id != item.id),
|
||||
];
|
||||
state = state.copyWith(
|
||||
items: items,
|
||||
totalCount: isReplacement ? state.totalCount : state.totalCount + 1,
|
||||
lookupItems: lookupItems,
|
||||
);
|
||||
_historyLog.d('Added independent history variant: ${item.trackName}');
|
||||
return item;
|
||||
}
|
||||
|
||||
void removeFromHistory(String id) {
|
||||
state = state.copyWith(
|
||||
items: state.items.where((item) => item.id != id).toList(),
|
||||
|
||||
@@ -68,6 +68,10 @@ final _batchUniqueFilenameTokenPattern = RegExp(
|
||||
r'\{(?:title|track(?:_raw)?|track:\d+|playlist_position(?:_raw)?|playlist_position:\d+|playlist position|playlistPosition|position(?::\d+)?)\}',
|
||||
caseSensitive: false,
|
||||
);
|
||||
final _qualityFilenameTokenPattern = RegExp(
|
||||
r'\{quality\}',
|
||||
caseSensitive: false,
|
||||
);
|
||||
|
||||
class DownloadQueueState {
|
||||
static const Object _noChange = Object();
|
||||
@@ -1138,6 +1142,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
item.service,
|
||||
outputExt,
|
||||
),
|
||||
allowQualityVariant: item.preserveQualityVariant,
|
||||
songLinkRegion: settings.songLinkRegion,
|
||||
);
|
||||
}
|
||||
@@ -1221,6 +1226,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
qualityOverride: qualityOverride,
|
||||
playlistName: playlistName,
|
||||
playlistPosition: playlistPosition,
|
||||
preserveQualityVariant: settings.allowQualityVariants,
|
||||
);
|
||||
|
||||
state = state.copyWith(items: [...state.items, item]);
|
||||
@@ -1269,6 +1275,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
explicitPosition ??
|
||||
(shouldAssignPlaylistPositions ? index + 1 : null),
|
||||
fromBatch: fromBatch,
|
||||
preserveQualityVariant: settings.allowQualityVariants,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
@@ -2541,6 +2548,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
effectiveFilenameFormat,
|
||||
_filenameMetadataForTrack(
|
||||
trackToDownload,
|
||||
quality: quality,
|
||||
playlistPosition: _validPlaylistPosition(item),
|
||||
),
|
||||
);
|
||||
@@ -3831,6 +3839,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
label: effectiveLabel,
|
||||
copyright: effectiveCopyright,
|
||||
),
|
||||
preserveTrackVariant: item.preserveQualityVariant,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -622,6 +622,7 @@ extension _DownloadQueueNativeWorker on DownloadQueueNotifier {
|
||||
effectiveFilenameFormat,
|
||||
_filenameMetadataForTrack(
|
||||
item.track,
|
||||
quality: quality,
|
||||
playlistPosition: _validPlaylistPosition(item),
|
||||
),
|
||||
);
|
||||
@@ -865,6 +866,7 @@ extension _DownloadQueueNativeWorker on DownloadQueueNotifier {
|
||||
DownloadHistoryItem.fromJson(
|
||||
Map<String, dynamic>.from(historyItem),
|
||||
),
|
||||
preserveTrackVariant: item.preserveQualityVariant,
|
||||
);
|
||||
} catch (e) {
|
||||
_log.w('Failed to adopt native history item: $e');
|
||||
@@ -1088,6 +1090,7 @@ extension _DownloadQueueNativeWorker on DownloadQueueNotifier {
|
||||
result['copyright'] as String?,
|
||||
),
|
||||
),
|
||||
preserveTrackVariant: item.preserveQualityVariant,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -532,21 +532,25 @@ extension _DownloadQueuePaths on DownloadQueueNotifier {
|
||||
}
|
||||
|
||||
String _filenameFormatForItem(DownloadItem item, String baseFormat) {
|
||||
if (!item.fromBatch) {
|
||||
return baseFormat;
|
||||
}
|
||||
final trimmed = baseFormat.trim();
|
||||
if (trimmed.isEmpty) {
|
||||
return baseFormat;
|
||||
}
|
||||
if (_batchUniqueFilenameTokenPattern.hasMatch(trimmed)) {
|
||||
return baseFormat;
|
||||
var effective = trimmed;
|
||||
if (item.fromBatch &&
|
||||
!_batchUniqueFilenameTokenPattern.hasMatch(effective)) {
|
||||
effective = '$effective - {track:02} - {title}';
|
||||
}
|
||||
return '$trimmed - {track:02} - {title}';
|
||||
if (item.preserveQualityVariant &&
|
||||
!_qualityFilenameTokenPattern.hasMatch(effective)) {
|
||||
effective = '$effective - {quality}';
|
||||
}
|
||||
return effective;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _filenameMetadataForTrack(
|
||||
Track track, {
|
||||
required String quality,
|
||||
int playlistPosition = 0,
|
||||
}) {
|
||||
return {
|
||||
@@ -559,6 +563,7 @@ extension _DownloadQueuePaths on DownloadQueueNotifier {
|
||||
'date': track.releaseDate ?? '',
|
||||
'playlist_position': playlistPosition,
|
||||
'playlistPosition': playlistPosition,
|
||||
'quality': quality,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,6 +713,11 @@ class SettingsNotifier extends Notifier<AppSettings> {
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
void setAllowQualityVariants(bool enabled) {
|
||||
state = state.copyWith(allowQualityVariants: enabled);
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
void setSaveDownloadHistory(bool enabled) {
|
||||
state = state.copyWith(saveDownloadHistory: enabled);
|
||||
_saveSettings();
|
||||
|
||||
@@ -873,7 +873,7 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen>
|
||||
List<ArtistAlbum> albums,
|
||||
) async {
|
||||
final settings = ref.read(settingsProvider);
|
||||
if (settings.askQualityBeforeDownload) {
|
||||
if (settings.askQualityBeforeDownload || settings.allowQualityVariants) {
|
||||
DownloadServicePicker.show(
|
||||
context,
|
||||
recommendedService: _recommendedDownloadService(),
|
||||
@@ -993,22 +993,27 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen>
|
||||
return;
|
||||
}
|
||||
|
||||
final settings = ref.read(settingsProvider);
|
||||
final skipExisting =
|
||||
settings.deduplicateDownloads && !settings.allowQualityVariants;
|
||||
final historyLookups = allTracks
|
||||
.map(historyLookupForTrack)
|
||||
.toList(growable: false);
|
||||
final existingHistoryKeys = await ref.read(
|
||||
downloadHistoryBatchExistsProvider(
|
||||
HistoryBatchLookupRequest(historyLookups),
|
||||
).future,
|
||||
);
|
||||
final existingHistoryKeys = skipExisting
|
||||
? await ref.read(
|
||||
downloadHistoryBatchExistsProvider(
|
||||
HistoryBatchLookupRequest(historyLookups),
|
||||
).future,
|
||||
)
|
||||
: const <String>{};
|
||||
final tracksToQueue = <Track>[];
|
||||
int skippedCount = 0;
|
||||
|
||||
for (var i = 0; i < allTracks.length; i++) {
|
||||
final track = allTracks[i];
|
||||
final isDownloaded = existingHistoryKeys.contains(
|
||||
historyLookups[i].lookupKey,
|
||||
);
|
||||
final isDownloaded =
|
||||
skipExisting &&
|
||||
existingHistoryKeys.contains(historyLookups[i].lookupKey);
|
||||
|
||||
if (!isDownloaded) {
|
||||
tracksToQueue.add(track);
|
||||
@@ -1642,7 +1647,7 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen>
|
||||
);
|
||||
}
|
||||
|
||||
if (settings.askQualityBeforeDownload) {
|
||||
if (settings.askQualityBeforeDownload || settings.allowQualityVariants) {
|
||||
DownloadServicePicker.show(
|
||||
context,
|
||||
recommendedService: _recommendedDownloadService(),
|
||||
|
||||
@@ -825,13 +825,13 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
}
|
||||
}
|
||||
|
||||
void _downloadTrack(int index) {
|
||||
void _downloadTrack(int index, {bool forceQualityPicker = false}) {
|
||||
final trackState = ref.read(trackProvider);
|
||||
if (index >= 0 && index < trackState.tracks.length) {
|
||||
final track = trackState.tracks[index];
|
||||
final settings = ref.read(settingsProvider);
|
||||
|
||||
if (settings.askQualityBeforeDownload) {
|
||||
if (settings.askQualityBeforeDownload || forceQualityPicker) {
|
||||
DownloadServicePicker.show(
|
||||
context,
|
||||
trackName: track.name,
|
||||
@@ -1062,7 +1062,8 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (settings.askQualityBeforeDownload) {
|
||||
if (settings.askQualityBeforeDownload ||
|
||||
settings.allowQualityVariants) {
|
||||
DownloadServicePicker.show(
|
||||
this.context,
|
||||
trackName: l10n.csvImportTracks(tracksToQueue.length),
|
||||
@@ -2171,7 +2172,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
source: _providerIdForExploreItem(item),
|
||||
);
|
||||
|
||||
if (settings.askQualityBeforeDownload) {
|
||||
if (settings.askQualityBeforeDownload || settings.allowQualityVariants) {
|
||||
DownloadServicePicker.show(
|
||||
context,
|
||||
trackName: track.name,
|
||||
@@ -3019,7 +3020,10 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
track: sortedTracks[index],
|
||||
index: sortedTrackIndexes[index],
|
||||
showDivider: showDivider,
|
||||
onDownload: () => _downloadTrack(sortedTrackIndexes[index]),
|
||||
onDownload: ({bool forceQualityPicker = false}) => _downloadTrack(
|
||||
sortedTrackIndexes[index],
|
||||
forceQualityPicker: forceQualityPicker,
|
||||
),
|
||||
searchExtensionId: searchExtensionId,
|
||||
showLocalLibraryIndicator: showLocalLibraryIndicator,
|
||||
thumbnailSizesByExtensionId: thumbnailSizesByExtensionId,
|
||||
|
||||
@@ -206,7 +206,7 @@ class _TrackItemWithStatus extends ConsumerWidget {
|
||||
final Track track;
|
||||
final int index;
|
||||
final bool showDivider;
|
||||
final VoidCallback onDownload;
|
||||
final void Function({bool forceQualityPicker}) onDownload;
|
||||
final String? searchExtensionId;
|
||||
final bool showLocalLibraryIndicator;
|
||||
final Map<String, (double, double)> thumbnailSizesByExtensionId;
|
||||
@@ -376,7 +376,11 @@ class _TrackItemWithStatus extends ConsumerWidget {
|
||||
}) async {
|
||||
if (isQueued) return;
|
||||
|
||||
if (isInLocalLibrary) {
|
||||
final settings = ref.read(settingsProvider);
|
||||
final allowExistingDownload =
|
||||
settings.allowQualityVariants || !settings.deduplicateDownloads;
|
||||
|
||||
if (!allowExistingDownload && isInLocalLibrary) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
@@ -387,27 +391,34 @@ class _TrackItemWithStatus extends ConsumerWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
final historyNotifier = ref.read(downloadHistoryProvider.notifier);
|
||||
final historyItem = await historyNotifier.findExistingTrackAsync(
|
||||
historyLookupForTrack(track),
|
||||
);
|
||||
if (historyItem != null) {
|
||||
final exists = await fileExists(historyItem.filePath);
|
||||
if (exists) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.snackbarAlreadyDownloaded(track.name)),
|
||||
),
|
||||
);
|
||||
if (!allowExistingDownload) {
|
||||
final historyNotifier = ref.read(downloadHistoryProvider.notifier);
|
||||
final historyItem = await historyNotifier.findExistingTrackAsync(
|
||||
historyLookupForTrack(track),
|
||||
);
|
||||
if (historyItem != null) {
|
||||
final exists = await fileExists(historyItem.filePath);
|
||||
if (exists) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
context.l10n.snackbarAlreadyDownloaded(track.name),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
historyNotifier.removeFromHistory(historyItem.id);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
historyNotifier.removeFromHistory(historyItem.id);
|
||||
}
|
||||
}
|
||||
|
||||
onDownload();
|
||||
onDownload(
|
||||
forceQualityPicker:
|
||||
settings.allowQualityVariants && (isInHistory || isInLocalLibrary),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ extension _QueueTabSelectionActions on _QueueTabState {
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.askQualityBeforeDownload) {
|
||||
if (settings.askQualityBeforeDownload || settings.allowQualityVariants) {
|
||||
DownloadServicePicker.show(
|
||||
context,
|
||||
trackName: context.l10n.tracksCount(totalTracks),
|
||||
|
||||
@@ -46,8 +46,11 @@ class MetadataSettingsPage extends ConsumerWidget {
|
||||
context,
|
||||
settings.artistTagMode,
|
||||
),
|
||||
onTap: () =>
|
||||
_showArtistTagModePicker(context, ref, settings.artistTagMode),
|
||||
onTap: () => _showArtistTagModePicker(
|
||||
context,
|
||||
ref,
|
||||
settings.artistTagMode,
|
||||
),
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.image,
|
||||
@@ -111,12 +114,25 @@ class MetadataSettingsPage extends ConsumerWidget {
|
||||
icon: Icons.filter_list_outlined,
|
||||
title: context.l10n.downloadDeduplication,
|
||||
subtitle: settings.deduplicateDownloads
|
||||
? context.l10n.downloadDeduplicationEnabled
|
||||
? settings.allowQualityVariants
|
||||
? context
|
||||
.l10n
|
||||
.downloadDeduplicationWithQualityVariants
|
||||
: context.l10n.downloadDeduplicationEnabled
|
||||
: context.l10n.downloadDeduplicationDisabled,
|
||||
value: settings.deduplicateDownloads,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setDeduplicateDownloads(value),
|
||||
),
|
||||
SettingsSwitchItem(
|
||||
icon: Icons.library_music_outlined,
|
||||
title: context.l10n.downloadQualityVariants,
|
||||
subtitle: context.l10n.downloadQualityVariantsDescription,
|
||||
value: settings.allowQualityVariants,
|
||||
onChanged: (value) => ref
|
||||
.read(settingsProvider.notifier)
|
||||
.setAllowQualityVariants(value),
|
||||
showDivider: false,
|
||||
),
|
||||
],
|
||||
@@ -161,16 +177,18 @@ class MetadataSettingsPage extends ConsumerWidget {
|
||||
padding: const EdgeInsets.fromLTRB(24, 24, 24, 8),
|
||||
child: Text(
|
||||
context.l10n.optionsArtistTagMode,
|
||||
style: Theme.of(context).textTheme.titleLarge
|
||||
?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
|
||||
child: Text(
|
||||
context.l10n.optionsArtistTagModeDescription,
|
||||
style: Theme.of(context).textTheme.bodyMedium
|
||||
?.copyWith(color: colorScheme.onSurfaceVariant),
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
@@ -190,7 +208,9 @@ class MetadataSettingsPage extends ConsumerWidget {
|
||||
ListTile(
|
||||
leading: const Icon(Icons.library_music_outlined),
|
||||
title: Text(context.l10n.optionsArtistTagModeSplitVorbis),
|
||||
subtitle: Text(context.l10n.optionsArtistTagModeSplitVorbisSubtitle),
|
||||
subtitle: Text(
|
||||
context.l10n.optionsArtistTagModeSplitVorbisSubtitle,
|
||||
),
|
||||
trailing: currentMode == artistTagModeSplitVorbis
|
||||
? const Icon(Icons.check)
|
||||
: null,
|
||||
|
||||
@@ -48,6 +48,7 @@ class DownloadRequestPayload {
|
||||
final bool stageSafOutput;
|
||||
final bool deferSafPublish;
|
||||
final bool requiresContainerConversion;
|
||||
final bool allowQualityVariant;
|
||||
final String songLinkRegion;
|
||||
|
||||
const DownloadRequestPayload({
|
||||
@@ -98,6 +99,7 @@ class DownloadRequestPayload {
|
||||
this.stageSafOutput = false,
|
||||
this.deferSafPublish = false,
|
||||
this.requiresContainerConversion = false,
|
||||
this.allowQualityVariant = false,
|
||||
this.songLinkRegion = 'US',
|
||||
});
|
||||
|
||||
@@ -150,6 +152,7 @@ class DownloadRequestPayload {
|
||||
'stage_saf_output': stageSafOutput,
|
||||
'defer_saf_publish': deferSafPublish,
|
||||
'requires_container_conversion': requiresContainerConversion,
|
||||
'allow_quality_variant': allowQualityVariant,
|
||||
'songlink_region': songLinkRegion,
|
||||
};
|
||||
}
|
||||
@@ -206,6 +209,7 @@ class DownloadRequestPayload {
|
||||
stageSafOutput: stageSafOutput,
|
||||
deferSafPublish: deferSafPublish,
|
||||
requiresContainerConversion: requiresContainerConversion,
|
||||
allowQualityVariant: allowQualityVariant,
|
||||
songLinkRegion: songLinkRegion,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/models/track.dart';
|
||||
import 'package:spotiflac_android/providers/library_collections_provider.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/services/cover_cache_manager.dart';
|
||||
import 'package:spotiflac_android/widgets/playlist_picker_sheet.dart';
|
||||
import 'package:spotiflac_android/widgets/track_detail_actions.dart';
|
||||
import 'package:spotiflac_android/utils/clickable_metadata.dart';
|
||||
|
||||
class TrackCollectionQuickActions extends ConsumerWidget {
|
||||
@@ -64,6 +66,9 @@ class _TrackOptionsSheet extends ConsumerWidget {
|
||||
final isInWishlist = ref.watch(
|
||||
libraryCollectionsProvider.select((state) => state.isInWishlist(track)),
|
||||
);
|
||||
final allowQualityVariants = ref.watch(
|
||||
settingsProvider.select((settings) => settings.allowQualityVariants),
|
||||
);
|
||||
|
||||
return SafeArea(
|
||||
child: ConstrainedBox(
|
||||
@@ -163,6 +168,25 @@ class _TrackOptionsSheet extends ConsumerWidget {
|
||||
color: colorScheme.outlineVariant.withValues(alpha: 0.5),
|
||||
),
|
||||
|
||||
if (allowQualityVariants)
|
||||
_OptionTile(
|
||||
icon: Icons.download_outlined,
|
||||
title: context.l10n.trackOptionDownloadQualityVariant,
|
||||
onTap: () {
|
||||
final rootContext = Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).context;
|
||||
Navigator.pop(context);
|
||||
downloadSingleTrack(
|
||||
rootContext,
|
||||
ref,
|
||||
track,
|
||||
forceQualityPicker: true,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
_OptionTile(
|
||||
icon: isLoved ? Icons.favorite : Icons.favorite_border,
|
||||
iconColor: isLoved ? colorScheme.error : null,
|
||||
|
||||
@@ -19,6 +19,7 @@ void downloadSingleTrack(
|
||||
String? recommendedService,
|
||||
String? playlistName,
|
||||
int? playlistPosition,
|
||||
bool forceQualityPicker = false,
|
||||
}) {
|
||||
final settings = ref.read(settingsProvider);
|
||||
|
||||
@@ -29,7 +30,7 @@ void downloadSingleTrack(
|
||||
);
|
||||
}
|
||||
|
||||
if (settings.askQualityBeforeDownload) {
|
||||
if (settings.askQualityBeforeDownload || forceQualityPicker) {
|
||||
DownloadServicePicker.show(
|
||||
context,
|
||||
trackName: track.name,
|
||||
@@ -115,9 +116,7 @@ void showQueuedSnackbar(BuildContext context, int added, int skipped) {
|
||||
final message = skipped > 0
|
||||
? context.l10n.discographySkippedDownloaded(added, skipped)
|
||||
: context.l10n.snackbarAddedTracksToQueue(added);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
/// Shared batch "add to queue" flow for detail screens: skips tracks already
|
||||
@@ -139,18 +138,24 @@ Future<void> queueTracksSkippingDownloaded(
|
||||
}) async {
|
||||
if (tracks.isEmpty) return;
|
||||
|
||||
final settings = ref.read(settingsProvider);
|
||||
final skipExisting =
|
||||
settings.deduplicateDownloads && !settings.allowQualityVariants;
|
||||
final historyLookups = tracks
|
||||
.map(historyLookupForTrack)
|
||||
.toList(growable: false);
|
||||
final existingHistoryKeys = await ref.read(
|
||||
downloadHistoryBatchExistsProvider(
|
||||
HistoryBatchLookupRequest(historyLookups),
|
||||
).future,
|
||||
);
|
||||
final existingHistoryKeys = skipExisting
|
||||
? await ref.read(
|
||||
downloadHistoryBatchExistsProvider(
|
||||
HistoryBatchLookupRequest(historyLookups),
|
||||
).future,
|
||||
)
|
||||
: const <String>{};
|
||||
if (!context.mounted) return;
|
||||
final settings = ref.read(settingsProvider);
|
||||
final localLibState =
|
||||
(settings.localLibraryEnabled && settings.localLibraryShowDuplicates)
|
||||
(skipExisting &&
|
||||
settings.localLibraryEnabled &&
|
||||
settings.localLibraryShowDuplicates)
|
||||
? ref.read(localLibraryProvider)
|
||||
: null;
|
||||
final tracksToQueue = <Track>[];
|
||||
@@ -158,9 +163,9 @@ Future<void> queueTracksSkippingDownloaded(
|
||||
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
final track = tracks[i];
|
||||
final isInHistory = existingHistoryKeys.contains(
|
||||
historyLookups[i].lookupKey,
|
||||
);
|
||||
final isInHistory =
|
||||
skipExisting &&
|
||||
existingHistoryKeys.contains(historyLookups[i].lookupKey);
|
||||
final isInLocal =
|
||||
localLibState?.existsInLibrary(
|
||||
isrc: track.isrc,
|
||||
@@ -187,7 +192,7 @@ Future<void> queueTracksSkippingDownloaded(
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.askQualityBeforeDownload) {
|
||||
if (settings.askQualityBeforeDownload || settings.allowQualityVariants) {
|
||||
DownloadServicePicker.show(
|
||||
context,
|
||||
trackName: '${tracksToQueue.length} tracks',
|
||||
|
||||
Reference in New Issue
Block a user