refactor: remove legacy API clients, Yoinkify fallback, and unused lyrics provider

- Delete dead metadata client and extract shared types to metadata_types.go
- Remove Yoinkify download fallback from Deezer, use MusicDL only
- Clean up retired settings fields and metadataSource
- Remove dead l10n keys for retired provider
- Add migration to strip retired provider from existing users' lyrics config
This commit is contained in:
zarzet
2026-03-30 23:23:24 +07:00
parent dd750b95ca
commit 93e77aeb84
27 changed files with 198 additions and 1679 deletions
-6
View File
@@ -4647,12 +4647,6 @@ abstract class AppLocalizations {
/// **'You have unsaved changes that will be lost.'**
String get lyricsProvidersDiscardContent;
/// Description for Spotify Lyrics API provider
///
/// In en, this message translates to:
/// **'Spotify-sourced synced lyrics via community API'**
String get lyricsProviderSpotifyApiDesc;
/// Description for LRCLIB provider
///
/// In en, this message translates to:
-4
View File
@@ -2706,10 +2706,6 @@ class AppLocalizationsDe extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2674,10 +2674,6 @@ class AppLocalizationsEn extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2674,10 +2674,6 @@ class AppLocalizationsEs extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2675,10 +2675,6 @@ class AppLocalizationsFr extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2673,10 +2673,6 @@ class AppLocalizationsHi extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2684,10 +2684,6 @@ class AppLocalizationsId extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2660,10 +2660,6 @@ class AppLocalizationsJa extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2653,10 +2653,6 @@ class AppLocalizationsKo extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2673,10 +2673,6 @@ class AppLocalizationsNl extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2674,10 +2674,6 @@ class AppLocalizationsPt extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2733,10 +2733,6 @@ class AppLocalizationsRu extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2679,10 +2679,6 @@ class AppLocalizationsTr extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -2674,10 +2674,6 @@ class AppLocalizationsZh extends AppLocalizations {
String get lyricsProvidersDiscardContent =>
'You have unsaved changes that will be lost.';
@override
String get lyricsProviderSpotifyApiDesc =>
'Spotify-sourced synced lyrics via community API';
@override
String get lyricsProviderLrclibDesc => 'Open-source synced lyrics database';
-4
View File
@@ -3551,10 +3551,6 @@
"@lyricsProvidersDiscardContent": {
"description": "Body text of the discard-changes dialog on lyrics provider page"
},
"lyricsProviderSpotifyApiDesc": "Spotify-sourced synced lyrics via community API",
"@lyricsProviderSpotifyApiDesc": {
"description": "Description for Spotify Lyrics API provider"
},
"lyricsProviderLrclibDesc": "Open-source synced lyrics database",
"@lyricsProviderLrclibDesc": {
"description": "Description for LRCLIB provider"
-18
View File
@@ -30,10 +30,6 @@ class AppSettings {
final String historyViewMode;
final String historyFilterMode;
final bool askQualityBeforeDownload;
final String spotifyClientId;
final String spotifyClientSecret;
final bool useCustomSpotifyCredentials;
final String metadataSource;
final bool enableLogging;
final bool useExtensionProviders;
final String? searchProvider;
@@ -107,10 +103,6 @@ class AppSettings {
this.historyViewMode = 'grid',
this.historyFilterMode = 'all',
this.askQualityBeforeDownload = true,
this.spotifyClientId = '',
this.spotifyClientSecret = '',
this.useCustomSpotifyCredentials = false,
this.metadataSource = 'deezer',
this.enableLogging = false,
this.useExtensionProviders = true,
this.searchProvider,
@@ -134,7 +126,6 @@ class AppSettings {
this.hasCompletedTutorial = false,
this.lyricsProviders = const [
'lrclib',
'spotify_api',
'musixmatch',
'netease',
'apple_music',
@@ -172,10 +163,6 @@ class AppSettings {
String? historyViewMode,
String? historyFilterMode,
bool? askQualityBeforeDownload,
String? spotifyClientId,
String? spotifyClientSecret,
bool? useCustomSpotifyCredentials,
String? metadataSource,
bool? enableLogging,
bool? useExtensionProviders,
String? searchProvider,
@@ -235,11 +222,6 @@ class AppSettings {
historyFilterMode: historyFilterMode ?? this.historyFilterMode,
askQualityBeforeDownload:
askQualityBeforeDownload ?? this.askQualityBeforeDownload,
spotifyClientId: spotifyClientId ?? this.spotifyClientId,
spotifyClientSecret: spotifyClientSecret ?? this.spotifyClientSecret,
useCustomSpotifyCredentials:
useCustomSpotifyCredentials ?? this.useCustomSpotifyCredentials,
metadataSource: metadataSource ?? this.metadataSource,
enableLogging: enableLogging ?? this.enableLogging,
useExtensionProviders:
useExtensionProviders ?? this.useExtensionProviders,
+1 -17
View File
@@ -32,11 +32,6 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
historyViewMode: json['historyViewMode'] as String? ?? 'grid',
historyFilterMode: json['historyFilterMode'] as String? ?? 'all',
askQualityBeforeDownload: json['askQualityBeforeDownload'] as bool? ?? true,
spotifyClientId: json['spotifyClientId'] as String? ?? '',
spotifyClientSecret: json['spotifyClientSecret'] as String? ?? '',
useCustomSpotifyCredentials:
json['useCustomSpotifyCredentials'] as bool? ?? false,
metadataSource: json['metadataSource'] as String? ?? 'deezer',
enableLogging: json['enableLogging'] as bool? ?? false,
useExtensionProviders: json['useExtensionProviders'] as bool? ?? true,
searchProvider: json['searchProvider'] as String?,
@@ -65,14 +60,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
(json['lyricsProviders'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [
'lrclib',
'spotify_api',
'musixmatch',
'netease',
'apple_music',
'qqmusic',
],
const ['lrclib', 'musixmatch', 'netease', 'apple_music', 'qqmusic'],
lyricsIncludeTranslationNetease:
json['lyricsIncludeTranslationNetease'] as bool? ?? false,
lyricsIncludeRomanizationNetease:
@@ -111,10 +99,6 @@ Map<String, dynamic> _$AppSettingsToJson(
'historyViewMode': instance.historyViewMode,
'historyFilterMode': instance.historyFilterMode,
'askQualityBeforeDownload': instance.askQualityBeforeDownload,
'spotifyClientId': instance.spotifyClientId,
'spotifyClientSecret': instance.spotifyClientSecret,
'useCustomSpotifyCredentials': instance.useCustomSpotifyCredentials,
'metadataSource': instance.metadataSource,
'enableLogging': instance.enableLogging,
'useExtensionProviders': instance.useExtensionProviders,
'searchProvider': instance.searchProvider,
+1 -2
View File
@@ -662,9 +662,8 @@ class ExtensionNotifier extends Notifier<ExtensionState> {
if (settings.searchProvider == extensionId) {
ref.read(settingsProvider.notifier).setSearchProvider(null);
ref.read(settingsProvider.notifier).setMetadataSource('deezer');
_log.d(
'Cleared search provider and reset to Deezer because extension $extensionId was disabled',
'Cleared search provider because extension $extensionId was disabled',
);
}
+17 -51
View File
@@ -12,7 +12,7 @@ import 'package:spotiflac_android/utils/logger.dart';
const _settingsKey = 'app_settings';
const _migrationVersionKey = 'settings_migration_version';
const _currentMigrationVersion = 7;
const _currentMigrationVersion = 9;
const _spotifyClientSecretKey = 'spotify_client_secret';
final _log = AppLogger('SettingsProvider');
@@ -44,7 +44,7 @@ class SettingsNotifier extends Notifier<AppSettings> {
await _normalizeSongLinkRegionIfNeeded();
}
await _retireBuiltInSpotifyProvider();
await _cleanupRetiredSpotifySettings();
LogBuffer.loggingEnabled = state.enableLogging;
@@ -86,13 +86,6 @@ class SettingsNotifier extends Notifier<AppSettings> {
Future<void> _runMigrations(SharedPreferences prefs) async {
final lastMigration = prefs.getInt(_migrationVersionKey) ?? 0;
if (lastMigration < 1) {
if (!state.useCustomSpotifyCredentials) {
state = state.copyWith(metadataSource: 'deezer');
await _saveSettings();
}
}
if (lastMigration < _currentMigrationVersion) {
if (state.downloadTreeUri.isNotEmpty && state.storageMode != 'saf') {
state = state.copyWith(storageMode: 'saf');
@@ -101,26 +94,20 @@ class SettingsNotifier extends Notifier<AppSettings> {
if (!state.isFirstLaunch && !state.hasCompletedTutorial) {
state = state.copyWith(hasCompletedTutorial: true);
}
// Migration 4: include Spotify Lyrics API in provider order for existing users
if (!state.lyricsProviders.contains('spotify_api')) {
final updatedProviders = List<String>.from(state.lyricsProviders);
final lrclibIndex = updatedProviders.indexOf('lrclib');
if (lrclibIndex >= 0) {
updatedProviders.insert(lrclibIndex + 1, 'spotify_api');
} else {
updatedProviders.add('spotify_api');
}
state = state.copyWith(lyricsProviders: updatedProviders);
}
if (state.metadataSource != 'deezer' ||
state.spotifyClientId.isNotEmpty ||
state.spotifyClientSecret.isNotEmpty ||
state.useCustomSpotifyCredentials) {
if (state.lyricsProviders.contains('spotify_api')) {
final updatedProviders = state.lyricsProviders
.where((provider) => provider != 'spotify_api')
.toList();
state = state.copyWith(
metadataSource: 'deezer',
spotifyClientId: '',
spotifyClientSecret: '',
useCustomSpotifyCredentials: false,
lyricsProviders: updatedProviders.isEmpty
? const [
'lrclib',
'musixmatch',
'netease',
'apple_music',
'qqmusic',
]
: updatedProviders,
);
}
state = state.copyWith(lastSeenVersion: AppInfo.version);
@@ -134,8 +121,7 @@ class SettingsNotifier extends Notifier<AppSettings> {
}
Future<void> _saveSettings() async {
final settingsToSave = state.copyWith(spotifyClientSecret: '');
_pendingSettingsJson = jsonEncode(settingsToSave.toJson());
_pendingSettingsJson = jsonEncode(state.toJson());
if (_isSavingSettings) {
_saveQueued = true;
@@ -186,28 +172,13 @@ class SettingsNotifier extends Notifier<AppSettings> {
await _saveSettings();
}
Future<void> _retireBuiltInSpotifyProvider() async {
Future<void> _cleanupRetiredSpotifySettings() async {
final storedSecret = await _secureStorage.read(
key: _spotifyClientSecretKey,
);
if (storedSecret != null && storedSecret.isNotEmpty) {
await _secureStorage.delete(key: _spotifyClientSecretKey);
}
if (state.metadataSource == 'deezer' &&
state.spotifyClientId.isEmpty &&
state.spotifyClientSecret.isEmpty &&
!state.useCustomSpotifyCredentials) {
return;
}
state = state.copyWith(
metadataSource: 'deezer',
spotifyClientId: '',
spotifyClientSecret: '',
useCustomSpotifyCredentials: false,
);
await _saveSettings();
}
void setDefaultService(String service) {
@@ -380,11 +351,6 @@ class SettingsNotifier extends Notifier<AppSettings> {
_saveSettings();
}
void setMetadataSource(String source) {
state = state.copyWith(metadataSource: source);
_saveSettings();
}
void setSearchProvider(String? provider) {
if (provider == null || provider.isEmpty) {
state = state.copyWith(clearSearchProvider: true);
@@ -1563,7 +1563,6 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
}
static const _providerDisplayNames = <String, String>{
'spotify_api': 'Spotify Lyrics API',
'lrclib': 'LRCLIB',
'netease': 'Netease',
'musixmatch': 'Musixmatch',
@@ -17,7 +17,6 @@ class _LyricsProviderPriorityPageState
extends ConsumerState<LyricsProviderPriorityPage> {
static const _allProviderIds = [
'lrclib',
'spotify_api',
'netease',
'musixmatch',
'apple_music',
@@ -133,9 +132,7 @@ class _LyricsProviderPriorityPageState
void _disableProvider(String id) {
if (_enabledProviders.length <= 1) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(context.l10n.lyricsProvidersAtLeastOne),
),
SnackBar(content: Text(context.l10n.lyricsProvidersAtLeastOne)),
);
return;
}
@@ -184,12 +181,6 @@ class _LyricsProviderPriorityPageState
BuildContext context,
) {
switch (id) {
case 'spotify_api':
return _LyricsProviderInfo(
name: 'Spotify Lyrics API',
description: context.l10n.lyricsProviderSpotifyApiDesc,
icon: Icons.music_note_outlined,
);
case 'lrclib':
return _LyricsProviderInfo(
name: 'LRCLIB',
@@ -70,15 +70,7 @@ class OptionsSettingsPage extends ConsumerWidget {
),
),
SliverToBoxAdapter(
child: SettingsGroup(
children: [
_MetadataSourceSelector(
onChanged: (v) => ref
.read(settingsProvider.notifier)
.setMetadataSource(v),
),
],
),
child: SettingsGroup(children: [const _MetadataSourceSelector()]),
),
SliverToBoxAdapter(
@@ -706,8 +698,7 @@ class _ChannelChip extends StatelessWidget {
}
class _MetadataSourceSelector extends ConsumerWidget {
final ValueChanged<String> onChanged;
const _MetadataSourceSelector({required this.onChanged});
const _MetadataSourceSelector();
static const _builtInProviders = {'tidal': 'Tidal', 'qobuz': 'Qobuz'};
@@ -770,7 +761,6 @@ class _MetadataSourceSelector extends ConsumerWidget {
if (hasNonDefaultProvider) {
ref.read(settingsProvider.notifier).setSearchProvider(null);
}
onChanged('deezer');
},
),
const SizedBox(width: 8),
@@ -782,7 +772,6 @@ class _MetadataSourceSelector extends ConsumerWidget {
ref
.read(settingsProvider.notifier)
.setSearchProvider('tidal');
onChanged('tidal');
},
),
const SizedBox(width: 8),
@@ -794,7 +783,6 @@ class _MetadataSourceSelector extends ConsumerWidget {
ref
.read(settingsProvider.notifier)
.setSearchProvider('qobuz');
onChanged('qobuz');
},
),
],
-1
View File
@@ -426,7 +426,6 @@ class _SetupScreenState extends ConsumerState<SetupScreen> {
);
}
ref.read(settingsProvider.notifier).setMetadataSource('deezer');
ref.read(settingsProvider.notifier).setFirstLaunchComplete();
if (mounted) context.go('/tutorial');