mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-13 14:40:20 +02:00
refactor: remove Qobuz built-in provider and delete qobuz.go
Delete the entire Qobuz downloader implementation (qobuz.go, qobuz_test.go) including all API clients, search, metadata, download, and track matching code. Empty the builtInProviderRegistry now that all built-in providers are retired. Remove Qobuz-specific exports (SearchQobuzAll, GetQobuzMetadata, ParseQobuzURLExport) and the downloadWithBuiltInQobuz adapter. Stub out PreWarmTrackCache and cache management since no built-in providers remain. Move qobuz cover upgrade regex to cover.go. Update Dart screens, providers, and localization strings for the provider-agnostic UI.
This commit is contained in:
@@ -376,10 +376,10 @@ abstract class AppLocalizations {
|
||||
/// **'Choose which tab opens first for new search results.'**
|
||||
String get optionsDefaultSearchTabSubtitle;
|
||||
|
||||
/// Hint to switch back to built-in providers
|
||||
/// Hint to switch away from the current provider selection
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Tap Deezer or Spotify to switch back from extension'**
|
||||
/// **'Choose another provider below to stop using the current extension'**
|
||||
String get optionsSwitchBack;
|
||||
|
||||
/// Auto-retry with other services
|
||||
@@ -406,10 +406,10 @@ abstract class AppLocalizations {
|
||||
/// **'Extensions will be tried first'**
|
||||
String get optionsUseExtensionProvidersOn;
|
||||
|
||||
/// Status when extension providers disabled
|
||||
/// Status when extension providers are disabled
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Using built-in providers only'**
|
||||
/// **'Extensions are turned off'**
|
||||
String get optionsUseExtensionProvidersOff;
|
||||
|
||||
/// Embed lyrics in audio files
|
||||
@@ -5073,16 +5073,16 @@ abstract class AppLocalizations {
|
||||
/// **'Off: strict HTTPS certificate validation (recommended)'**
|
||||
String get downloadNetworkCompatibilityModeDisabled;
|
||||
|
||||
/// Hint shown instead of Ask-quality subtitle when no built-in service selected
|
||||
/// Hint shown instead of Ask-quality subtitle when the selected provider does not expose built-in quality controls
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select a built-in service to enable'**
|
||||
/// **'Select a compatible download provider to enable quality options'**
|
||||
String get downloadSelectServiceToEnable;
|
||||
|
||||
/// Info hint when non-Tidal/Qobuz service is selected
|
||||
/// Info hint when the selected provider does not expose built-in quality controls
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select Tidal or Qobuz above to configure quality'**
|
||||
/// **'Select a compatible provider above to configure quality'**
|
||||
String get downloadSelectTidalQobuz;
|
||||
|
||||
/// Subtitle for Embed Lyrics when Embed Metadata is disabled
|
||||
|
||||
@@ -2952,11 +2952,11 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -143,7 +143,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get optionsSwitchBack =>
|
||||
'Tap Deezer or Spotify to switch back from extension';
|
||||
'Choose another provider below to stop using the current extension';
|
||||
|
||||
@override
|
||||
String get optionsAutoFallback => 'Auto Fallback';
|
||||
@@ -159,7 +159,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
String get optionsUseExtensionProvidersOn => 'Extensions will be tried first';
|
||||
|
||||
@override
|
||||
String get optionsUseExtensionProvidersOff => 'Using built-in providers only';
|
||||
String get optionsUseExtensionProvidersOff => 'Extensions are turned off';
|
||||
|
||||
@override
|
||||
String get optionsEmbedLyrics => 'Embed Lyrics';
|
||||
@@ -2920,11 +2920,11 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -2920,11 +2920,11 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -2921,11 +2921,11 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -2919,11 +2919,11 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -145,7 +145,7 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get optionsSwitchBack =>
|
||||
'Ketuk Deezer atau Spotify untuk beralih dari ekstensi';
|
||||
'Pilih provider lain di bawah untuk berhenti memakai ekstensi saat ini';
|
||||
|
||||
@override
|
||||
String get optionsAutoFallback => 'Cadangan Otomatis';
|
||||
@@ -162,8 +162,7 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
'Ekstensi akan dicoba terlebih dahulu';
|
||||
|
||||
@override
|
||||
String get optionsUseExtensionProvidersOff =>
|
||||
'Hanya menggunakan provider bawaan';
|
||||
String get optionsUseExtensionProvidersOff => 'Ekstensi dimatikan';
|
||||
|
||||
@override
|
||||
String get optionsEmbedLyrics => 'Sematkan Lirik';
|
||||
@@ -2930,11 +2929,11 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -142,8 +142,7 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
'Choose which tab opens first for new search results.';
|
||||
|
||||
@override
|
||||
String get optionsSwitchBack =>
|
||||
'Tap Deezer or Spotify to switch back from extension';
|
||||
String get optionsSwitchBack => '現在の拡張の使用をやめるには、下から別のプロバイダーを選択してください';
|
||||
|
||||
@override
|
||||
String get optionsAutoFallback => 'Auto Fallback';
|
||||
@@ -159,7 +158,7 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
String get optionsUseExtensionProvidersOn => '最初に拡張で試みます';
|
||||
|
||||
@override
|
||||
String get optionsUseExtensionProvidersOff => '内蔵のプロバイダーのみを使用する';
|
||||
String get optionsUseExtensionProvidersOff => '拡張はオフです';
|
||||
|
||||
@override
|
||||
String get optionsEmbedLyrics => '歌詞を埋め込む';
|
||||
@@ -2906,11 +2905,11 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -2899,11 +2899,11 @@ class AppLocalizationsKo extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -2919,11 +2919,11 @@ class AppLocalizationsNl extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -2920,11 +2920,11 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -2979,11 +2979,11 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -146,7 +146,7 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get optionsSwitchBack =>
|
||||
'Eklentiden çıkıp varsayılana dönmek için Deezer veya Spotify\'a dokunun';
|
||||
'Geçerli eklentiyi bırakmak için aşağıdan başka bir sağlayıcı seçin';
|
||||
|
||||
@override
|
||||
String get optionsAutoFallback => 'Otomatik Geçiş';
|
||||
@@ -163,8 +163,7 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
'İndirme için önce eklentiler denenecek';
|
||||
|
||||
@override
|
||||
String get optionsUseExtensionProvidersOff =>
|
||||
'Sadece yerleşik sağlayıcılar kullanılıyor';
|
||||
String get optionsUseExtensionProvidersOff => 'Eklentiler kapalı';
|
||||
|
||||
@override
|
||||
String get optionsEmbedLyrics => 'Şarkı Sözlerini Gömer';
|
||||
@@ -2979,11 +2978,11 @@ class AppLocalizationsTr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Seçenekleri açmak için yerleşik bir sağlayıcı seçin';
|
||||
'Kalite seçeneklerini açmak için uyumlu bir indirme sağlayıcısı seçin';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Kaliteyi ayarlamak için lütfen yukarıdan Tidal veya Qobuz seçin';
|
||||
'Kaliteyi ayarlamak için yukarıdan uyumlu bir sağlayıcı seçin';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -2920,11 +2920,11 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get downloadSelectServiceToEnable =>
|
||||
'Select a built-in service to enable';
|
||||
'Select a compatible download provider to enable quality options';
|
||||
|
||||
@override
|
||||
String get downloadSelectTidalQobuz =>
|
||||
'Select Tidal or Qobuz above to configure quality';
|
||||
'Select a compatible provider above to configure quality';
|
||||
|
||||
@override
|
||||
String get downloadEmbedLyricsDisabled =>
|
||||
|
||||
@@ -174,9 +174,9 @@
|
||||
"@optionsDefaultSearchTabSubtitle": {
|
||||
"description": "Subtitle for the preferred default search tab setting"
|
||||
},
|
||||
"optionsSwitchBack": "Tap Deezer or Spotify to switch back from extension",
|
||||
"optionsSwitchBack": "Choose another provider below to stop using the current extension",
|
||||
"@optionsSwitchBack": {
|
||||
"description": "Hint to switch back to built-in providers"
|
||||
"description": "Hint to switch away from the current provider selection"
|
||||
},
|
||||
"optionsAutoFallback": "Auto Fallback",
|
||||
"@optionsAutoFallback": {
|
||||
@@ -194,9 +194,9 @@
|
||||
"@optionsUseExtensionProvidersOn": {
|
||||
"description": "Status when extension providers enabled"
|
||||
},
|
||||
"optionsUseExtensionProvidersOff": "Using built-in providers only",
|
||||
"optionsUseExtensionProvidersOff": "Extensions are turned off",
|
||||
"@optionsUseExtensionProvidersOff": {
|
||||
"description": "Status when extension providers disabled"
|
||||
"description": "Status when extension providers are disabled"
|
||||
},
|
||||
"optionsEmbedLyrics": "Embed Lyrics",
|
||||
"@optionsEmbedLyrics": {
|
||||
@@ -3867,14 +3867,14 @@
|
||||
"@downloadNetworkCompatibilityModeDisabled": {
|
||||
"description": "Subtitle when network compatibility mode is disabled"
|
||||
},
|
||||
"downloadSelectServiceToEnable": "Select a built-in service to enable",
|
||||
"downloadSelectServiceToEnable": "Select a compatible download provider to enable quality options",
|
||||
"@downloadSelectServiceToEnable": {
|
||||
"description": "Hint shown instead of Ask-quality subtitle when no built-in service selected"
|
||||
"description": "Hint shown instead of Ask-quality subtitle when the selected provider does not expose built-in quality controls"
|
||||
},
|
||||
|
||||
"downloadSelectTidalQobuz": "Select Tidal or Qobuz above to configure quality",
|
||||
"downloadSelectTidalQobuz": "Select a compatible provider above to configure quality",
|
||||
"@downloadSelectTidalQobuz": {
|
||||
"description": "Info hint when non-Tidal/Qobuz service is selected"
|
||||
"description": "Info hint when the selected provider does not expose built-in quality controls"
|
||||
},
|
||||
"downloadEmbedLyricsDisabled": "Disabled while Embed Metadata is turned off",
|
||||
"@downloadEmbedLyricsDisabled": {
|
||||
|
||||
@@ -158,9 +158,9 @@
|
||||
"@optionsDefaultSearchTabSubtitle": {
|
||||
"description": "Subtitle for the preferred default search tab setting"
|
||||
},
|
||||
"optionsSwitchBack": "Ketuk Deezer atau Spotify untuk beralih dari ekstensi",
|
||||
"optionsSwitchBack": "Pilih provider lain di bawah untuk berhenti memakai ekstensi saat ini",
|
||||
"@optionsSwitchBack": {
|
||||
"description": "Hint to switch back to built-in providers"
|
||||
"description": "Hint to switch away from the current provider selection"
|
||||
},
|
||||
"optionsAutoFallback": "Cadangan Otomatis",
|
||||
"@optionsAutoFallback": {
|
||||
@@ -178,9 +178,9 @@
|
||||
"@optionsUseExtensionProvidersOn": {
|
||||
"description": "Status when extension providers enabled"
|
||||
},
|
||||
"optionsUseExtensionProvidersOff": "Hanya menggunakan provider bawaan",
|
||||
"optionsUseExtensionProvidersOff": "Ekstensi dimatikan",
|
||||
"@optionsUseExtensionProvidersOff": {
|
||||
"description": "Status when extension providers disabled"
|
||||
"description": "Status when extension providers are disabled"
|
||||
},
|
||||
"optionsEmbedLyrics": "Sematkan Lirik",
|
||||
"@optionsEmbedLyrics": {
|
||||
|
||||
@@ -150,9 +150,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsSwitchBack": "Tap Deezer or Spotify to switch back from extension",
|
||||
"optionsSwitchBack": "現在の拡張の使用をやめるには、下から別のプロバイダーを選択してください",
|
||||
"@optionsSwitchBack": {
|
||||
"description": "Hint to switch back to built-in providers"
|
||||
"description": "Hint to switch away from the current provider selection"
|
||||
},
|
||||
"optionsAutoFallback": "Auto Fallback",
|
||||
"@optionsAutoFallback": {
|
||||
@@ -170,9 +170,9 @@
|
||||
"@optionsUseExtensionProvidersOn": {
|
||||
"description": "Status when extension providers enabled"
|
||||
},
|
||||
"optionsUseExtensionProvidersOff": "内蔵のプロバイダーのみを使用する",
|
||||
"optionsUseExtensionProvidersOff": "拡張はオフです",
|
||||
"@optionsUseExtensionProvidersOff": {
|
||||
"description": "Status when extension providers disabled"
|
||||
"description": "Status when extension providers are disabled"
|
||||
},
|
||||
"optionsEmbedLyrics": "歌詞を埋め込む",
|
||||
"@optionsEmbedLyrics": {
|
||||
@@ -3198,4 +3198,4 @@
|
||||
"@downloadUseAlbumArtistForFoldersTrackSubtitle": {
|
||||
"description": "Subtitle when Track Artist is used for folder naming"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,9 +150,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsSwitchBack": "Eklentiden çıkıp varsayılana dönmek için Deezer veya Spotify'a dokunun",
|
||||
"optionsSwitchBack": "Geçerli eklentiyi bırakmak için aşağıdan başka bir sağlayıcı seçin",
|
||||
"@optionsSwitchBack": {
|
||||
"description": "Hint to switch back to built-in providers"
|
||||
"description": "Hint to switch away from the current provider selection"
|
||||
},
|
||||
"optionsAutoFallback": "Otomatik Geçiş",
|
||||
"@optionsAutoFallback": {
|
||||
@@ -170,9 +170,9 @@
|
||||
"@optionsUseExtensionProvidersOn": {
|
||||
"description": "Status when extension providers enabled"
|
||||
},
|
||||
"optionsUseExtensionProvidersOff": "Sadece yerleşik sağlayıcılar kullanılıyor",
|
||||
"optionsUseExtensionProvidersOff": "Eklentiler kapalı",
|
||||
"@optionsUseExtensionProvidersOff": {
|
||||
"description": "Status when extension providers disabled"
|
||||
"description": "Status when extension providers are disabled"
|
||||
},
|
||||
"optionsEmbedLyrics": "Şarkı Sözlerini Gömer",
|
||||
"@optionsEmbedLyrics": {
|
||||
@@ -3689,13 +3689,13 @@
|
||||
"@downloadNetworkCompatibilityModeDisabled": {
|
||||
"description": "Subtitle when network compatibility mode is disabled"
|
||||
},
|
||||
"downloadSelectServiceToEnable": "Seçenekleri açmak için yerleşik bir sağlayıcı seçin",
|
||||
"downloadSelectServiceToEnable": "Kalite seçeneklerini açmak için uyumlu bir indirme sağlayıcısı seçin",
|
||||
"@downloadSelectServiceToEnable": {
|
||||
"description": "Hint shown instead of Ask-quality subtitle when no built-in service selected"
|
||||
"description": "Hint shown instead of Ask-quality subtitle when the selected provider does not expose built-in quality controls"
|
||||
},
|
||||
"downloadSelectTidalQobuz": "Kaliteyi ayarlamak için lütfen yukarıdan Tidal veya Qobuz seçin",
|
||||
"downloadSelectTidalQobuz": "Kaliteyi ayarlamak için yukarıdan uyumlu bir sağlayıcı seçin",
|
||||
"@downloadSelectTidalQobuz": {
|
||||
"description": "Info hint when non-Tidal/Qobuz service is selected"
|
||||
"description": "Info hint when the selected provider does not expose built-in quality controls"
|
||||
},
|
||||
"downloadEmbedLyricsDisabled": "Şarkı Verilerini Dosyaya Gömme ayarı kapalıyken kullanılamaz",
|
||||
"@downloadEmbedLyricsDisabled": {
|
||||
|
||||
@@ -4815,7 +4815,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
String payloadTidalId = '';
|
||||
if (trackToDownload.id.startsWith('qobuz:')) {
|
||||
payloadQobuzId = trackToDownload.id.substring(6);
|
||||
if (item.service == 'qobuz') {
|
||||
if (_usesBuiltInCompatibleDownloadProvider(item.service, 'qobuz')) {
|
||||
payloadSpotifyId = '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +288,85 @@ List<String> get builtInDownloadProviderIds => List<String>.unmodifiable(
|
||||
builtInDownloadProviderSpecs.map((provider) => provider.id),
|
||||
);
|
||||
|
||||
String resolveEffectiveDownloadService(
|
||||
String requestedService,
|
||||
ExtensionState extensionState,
|
||||
) {
|
||||
final normalizedRequested = requestedService.trim().toLowerCase();
|
||||
final builtInDownloadIds = extensionState.builtInProviders
|
||||
.where((provider) => provider.supportsDownload)
|
||||
.map((provider) => provider.id.trim().toLowerCase())
|
||||
.where((providerId) => providerId.isNotEmpty)
|
||||
.toSet();
|
||||
final enabledDownloadExtensions = extensionState.extensions
|
||||
.where((ext) => ext.enabled && ext.hasDownloadProvider)
|
||||
.toList(growable: false);
|
||||
|
||||
if (normalizedRequested.isNotEmpty) {
|
||||
if (builtInDownloadIds.contains(normalizedRequested)) {
|
||||
return normalizedRequested;
|
||||
}
|
||||
|
||||
final matchingExtension = enabledDownloadExtensions
|
||||
.where((ext) => ext.id.trim().toLowerCase() == normalizedRequested)
|
||||
.firstOrNull;
|
||||
if (matchingExtension != null) {
|
||||
return matchingExtension.id;
|
||||
}
|
||||
|
||||
final replacementExtension = enabledDownloadExtensions
|
||||
.where(
|
||||
(ext) => ext.replacesBuiltInProviders.contains(normalizedRequested),
|
||||
)
|
||||
.firstOrNull;
|
||||
if (replacementExtension != null) {
|
||||
return replacementExtension.id;
|
||||
}
|
||||
}
|
||||
|
||||
const preferredBuiltInOrder = ['tidal', 'qobuz', 'deezer'];
|
||||
for (final builtInId in preferredBuiltInOrder) {
|
||||
final replacement = enabledDownloadExtensions
|
||||
.where((ext) => ext.replacesBuiltInProviders.contains(builtInId))
|
||||
.firstOrNull;
|
||||
if (replacement != null) {
|
||||
return replacement.id;
|
||||
}
|
||||
if (builtInDownloadIds.contains(builtInId)) {
|
||||
return builtInId;
|
||||
}
|
||||
}
|
||||
|
||||
return enabledDownloadExtensions.firstOrNull?.id ??
|
||||
extensionState.builtInProviders
|
||||
.where((provider) => provider.supportsDownload)
|
||||
.map((provider) => provider.id)
|
||||
.firstOrNull ??
|
||||
'';
|
||||
}
|
||||
|
||||
bool isDeezerCompatibleDownloadService(
|
||||
String service,
|
||||
ExtensionState extensionState,
|
||||
) {
|
||||
final normalizedService = service.trim().toLowerCase();
|
||||
if (normalizedService.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (normalizedService == 'deezer') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return extensionState.extensions.any(
|
||||
(ext) =>
|
||||
ext.enabled &&
|
||||
ext.hasDownloadProvider &&
|
||||
ext.id.trim().toLowerCase() == normalizedService &&
|
||||
ext.replacesBuiltInProviders.contains('deezer'),
|
||||
);
|
||||
}
|
||||
|
||||
bool isBuiltInSearchProvider(String? providerId) =>
|
||||
builtInProviderSpecForId(providerId)?.supportsSearch ?? false;
|
||||
|
||||
|
||||
@@ -599,7 +599,6 @@ class TrackNotifier extends Notifier<TrackState> {
|
||||
if (effectiveBuiltInProvider == null || effectiveBuiltInProvider.isEmpty) {
|
||||
state = TrackState(
|
||||
isLoading: false,
|
||||
error: 'No active search provider available',
|
||||
hasSearchText: state.hasSearchText,
|
||||
isShowingRecentAccess: state.isShowingRecentAccess,
|
||||
selectedSearchFilter: currentFilter,
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:spotiflac_android/services/cover_cache_manager.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/models/track.dart';
|
||||
import 'package:spotiflac_android/providers/download_queue_provider.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/providers/recent_access_provider.dart';
|
||||
import 'package:spotiflac_android/providers/local_library_provider.dart';
|
||||
@@ -644,9 +645,20 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
},
|
||||
);
|
||||
} else {
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addToQueue(track, settings.defaultService);
|
||||
.addToQueue(track, service);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.snackbarAddedToQueue(track.name))),
|
||||
);
|
||||
@@ -716,9 +728,20 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
},
|
||||
);
|
||||
} else {
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addMultipleToQueue(tracksToQueue, settings.defaultService);
|
||||
.addMultipleToQueue(tracksToQueue, service);
|
||||
_showQueuedSnackbar(context, tracksToQueue.length, skippedCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:intl/intl.dart';
|
||||
import 'package:spotiflac_android/services/cover_cache_manager.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/models/track.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/providers/track_provider.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/providers/download_queue_provider.dart';
|
||||
@@ -1627,7 +1628,18 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
|
||||
return;
|
||||
}
|
||||
|
||||
enqueue(settings.defaultService);
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
enqueue(service);
|
||||
}
|
||||
|
||||
Widget _buildAlbumSection(
|
||||
|
||||
@@ -1004,9 +1004,20 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
},
|
||||
);
|
||||
} else {
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addToQueue(track, settings.defaultService);
|
||||
.addToQueue(track, service);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(context.l10n.snackbarAddedToQueue(track.name)),
|
||||
@@ -1227,9 +1238,24 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
},
|
||||
);
|
||||
} else {
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(this.context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(this.context.l10n.extensionsNoDownloadProvider),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addMultipleToQueue(tracksToQueue, settings.defaultService);
|
||||
.addMultipleToQueue(tracksToQueue, service);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(this.context).showSnackBar(
|
||||
SnackBar(
|
||||
@@ -2139,9 +2165,20 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
},
|
||||
);
|
||||
} else {
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addToQueue(track, settings.defaultService);
|
||||
.addToQueue(track, service);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.snackbarAddedToQueue(track.name))),
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ 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/download_queue_provider.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/providers/library_collections_provider.dart';
|
||||
import 'package:spotiflac_android/providers/playback_provider.dart';
|
||||
import 'package:spotiflac_android/providers/local_library_provider.dart';
|
||||
@@ -214,12 +215,23 @@ class _LibraryTracksFolderScreenState
|
||||
|
||||
void _downloadSelected(List<CollectionTrackEntry> entries) {
|
||||
final settings = ref.read(settingsProvider);
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final queueNotifier = ref.read(downloadQueueProvider.notifier);
|
||||
var count = 0;
|
||||
|
||||
for (final entry in entries) {
|
||||
if (!_selectedKeys.contains(entry.key)) continue;
|
||||
queueNotifier.addToQueue(entry.track, settings.defaultService);
|
||||
queueNotifier.addToQueue(entry.track, service);
|
||||
count++;
|
||||
}
|
||||
|
||||
@@ -1349,9 +1361,20 @@ class _CollectionTrackTile extends ConsumerWidget {
|
||||
},
|
||||
);
|
||||
} else {
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addToQueue(track, settings.defaultService);
|
||||
.addToQueue(track, service);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.snackbarAddedToQueue(track.name))),
|
||||
);
|
||||
|
||||
@@ -965,10 +965,18 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen> {
|
||||
);
|
||||
final targetService = LocalTrackRedownloadService.preferredFlacService(
|
||||
settings,
|
||||
extensionState,
|
||||
);
|
||||
if (targetService.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final targetQuality =
|
||||
LocalTrackRedownloadService.preferredFlacQualityForService(
|
||||
targetService,
|
||||
extensionState,
|
||||
);
|
||||
|
||||
final matchedTracks = <Track>[];
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:spotiflac_android/services/platform_bridge.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/models/track.dart';
|
||||
import 'package:spotiflac_android/providers/download_queue_provider.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/providers/library_collections_provider.dart';
|
||||
import 'package:spotiflac_android/utils/file_access.dart';
|
||||
import 'package:spotiflac_android/utils/image_cache_utils.dart';
|
||||
@@ -495,11 +496,22 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
},
|
||||
);
|
||||
} else {
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addToQueue(
|
||||
track,
|
||||
settings.defaultService,
|
||||
service,
|
||||
playlistName: _playlistName,
|
||||
);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
|
||||
@@ -1845,10 +1845,24 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
if (confirmed != true || !context.mounted) return;
|
||||
|
||||
final settings = ref.read(settingsProvider);
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final queueNotifier = ref.read(downloadQueueProvider.notifier);
|
||||
|
||||
void enqueueAll({String? qualityOverride, String? service}) {
|
||||
final svc = service ?? settings.defaultService;
|
||||
final svc =
|
||||
service ??
|
||||
resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (svc.isEmpty) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (final playlist in selectedPlaylists) {
|
||||
final tracks = playlist.tracks.map((e) => e.track).toList();
|
||||
queueNotifier.addMultipleToQueue(
|
||||
@@ -5218,10 +5232,18 @@ class _QueueTabState extends ConsumerState<QueueTab> {
|
||||
);
|
||||
final targetService = LocalTrackRedownloadService.preferredFlacService(
|
||||
settings,
|
||||
extensionState,
|
||||
);
|
||||
if (targetService.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final targetQuality =
|
||||
LocalTrackRedownloadService.preferredFlacQualityForService(
|
||||
targetService,
|
||||
extensionState,
|
||||
);
|
||||
|
||||
final matchedTracks = <Track>[];
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/services/cover_cache_manager.dart';
|
||||
import 'package:spotiflac_android/models/track.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/providers/track_provider.dart';
|
||||
import 'package:spotiflac_android/providers/download_queue_provider.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
@@ -49,9 +50,20 @@ class _SearchScreenState extends ConsumerState<SearchScreen> {
|
||||
|
||||
void _downloadTrack(Track track) {
|
||||
final settings = ref.read(settingsProvider);
|
||||
final extensionState = ref.read(extensionProvider);
|
||||
final service = resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
if (service.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.extensionsNoDownloadProvider)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
ref
|
||||
.read(downloadQueueProvider.notifier)
|
||||
.addToQueue(track, settings.defaultService);
|
||||
.addToQueue(track, service);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.snackbarAddedToQueue(track.name))),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:spotiflac_android/models/settings.dart';
|
||||
import 'package:spotiflac_android/models/track.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/services/library_database.dart';
|
||||
import 'package:spotiflac_android/services/platform_bridge.dart';
|
||||
|
||||
@@ -101,19 +102,26 @@ class LocalTrackRedownloadService {
|
||||
);
|
||||
}
|
||||
|
||||
static String preferredFlacService(AppSettings settings) {
|
||||
switch (settings.defaultService.toLowerCase()) {
|
||||
case 'tidal':
|
||||
case 'qobuz':
|
||||
case 'deezer':
|
||||
return settings.defaultService.toLowerCase();
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
static String preferredFlacService(
|
||||
AppSettings settings,
|
||||
ExtensionState extensionState,
|
||||
) {
|
||||
return resolveEffectiveDownloadService(
|
||||
settings.defaultService,
|
||||
extensionState,
|
||||
);
|
||||
}
|
||||
|
||||
static String preferredFlacQualityForService(String service) {
|
||||
return service.toLowerCase() == 'deezer' ? 'FLAC' : 'LOSSLESS';
|
||||
static String preferredFlacQualityForService(
|
||||
String service,
|
||||
ExtensionState extensionState,
|
||||
) {
|
||||
if (service.trim().isEmpty) {
|
||||
return 'LOSSLESS';
|
||||
}
|
||||
return isDeezerCompatibleDownloadService(service, extensionState)
|
||||
? 'FLAC'
|
||||
: 'LOSSLESS';
|
||||
}
|
||||
|
||||
static String _buildSearchQuery(LocalLibraryItem item) {
|
||||
|
||||
Reference in New Issue
Block a user