mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-17 08:30:33 +02:00
refactor: remove built-in Spotify API provider, use Deezer as sole default
- Remove all Spotify credential management (client ID/secret, secure storage) - Remove Spotify platform channel handlers from MainActivity - Remove exported Go functions: GetSpotifyMetadata, SearchSpotify, SearchSpotifyAll, GetSpotifyRelatedArtists, SetSpotifyAPICredentials - Simplify GetSpotifyMetadataWithDeezerFallback to SpotFetch-only path - Remove Spotify built-in fallback in ReEnrichFile search pipeline - Always return false from HasSpotifyCredentials; getCredentials always errors - Default metadataProviderPriority is now ['deezer'] only - Sanitize provider priority list to strip 'spotify' entries on load/save - Add migration v5 to clear saved Spotify credentials from existing installs - Remove Spotify source chip and credentials UI from options settings page - Remove metadataSource param from search() — always uses Deezer - spotify-web extension remains supported via the extension provider system
This commit is contained in:
@@ -823,11 +823,19 @@ class ExtensionNotifier extends Notifier<ExtensionState> {
|
||||
List<String> priority;
|
||||
if (savedJson != null) {
|
||||
final saved = jsonDecode(savedJson) as List<dynamic>;
|
||||
priority = saved.map((e) => e as String).toList();
|
||||
priority = _sanitizeMetadataProviderPriority(
|
||||
saved.map((e) => e as String).toList(),
|
||||
);
|
||||
_log.d('Loaded metadata provider priority from prefs: $priority');
|
||||
await prefs.setString(
|
||||
_metadataProviderPriorityKey,
|
||||
jsonEncode(priority),
|
||||
);
|
||||
await PlatformBridge.setMetadataProviderPriority(priority);
|
||||
} else {
|
||||
priority = await PlatformBridge.getMetadataProviderPriority();
|
||||
priority = _sanitizeMetadataProviderPriority(
|
||||
await PlatformBridge.getMetadataProviderPriority(),
|
||||
);
|
||||
_log.d('Using default metadata provider priority: $priority');
|
||||
}
|
||||
|
||||
@@ -840,11 +848,15 @@ class ExtensionNotifier extends Notifier<ExtensionState> {
|
||||
Future<void> setMetadataProviderPriority(List<String> priority) async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_metadataProviderPriorityKey, jsonEncode(priority));
|
||||
final sanitized = _sanitizeMetadataProviderPriority(priority);
|
||||
await prefs.setString(
|
||||
_metadataProviderPriorityKey,
|
||||
jsonEncode(sanitized),
|
||||
);
|
||||
|
||||
await PlatformBridge.setMetadataProviderPriority(priority);
|
||||
state = state.copyWith(metadataProviderPriority: priority);
|
||||
_log.d('Saved metadata provider priority: $priority');
|
||||
await PlatformBridge.setMetadataProviderPriority(sanitized);
|
||||
state = state.copyWith(metadataProviderPriority: sanitized);
|
||||
_log.d('Saved metadata provider priority: $sanitized');
|
||||
} catch (e) {
|
||||
_log.e('Failed to set metadata provider priority: $e');
|
||||
state = state.copyWith(error: e.toString());
|
||||
@@ -880,7 +892,7 @@ class ExtensionNotifier extends Notifier<ExtensionState> {
|
||||
}
|
||||
|
||||
List<String> getAllMetadataProviders() {
|
||||
final providers = ['deezer', 'spotify'];
|
||||
final providers = ['deezer'];
|
||||
for (final ext in state.extensions) {
|
||||
if (ext.enabled && ext.hasMetadataProvider) {
|
||||
providers.add(ext.id);
|
||||
@@ -889,6 +901,23 @@ class ExtensionNotifier extends Notifier<ExtensionState> {
|
||||
return providers;
|
||||
}
|
||||
|
||||
List<String> _sanitizeMetadataProviderPriority(List<String> input) {
|
||||
final allowed = getAllMetadataProviders().toSet();
|
||||
final result = <String>[];
|
||||
|
||||
for (final provider in input) {
|
||||
if (allowed.contains(provider) && !result.contains(provider)) {
|
||||
result.add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.contains('deezer')) {
|
||||
result.insert(0, 'deezer');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
List<Extension> get searchProviders {
|
||||
return state.extensions
|
||||
.where((ext) => ext.enabled && ext.hasCustomSearch)
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:spotiflac_android/utils/logger.dart';
|
||||
|
||||
const _settingsKey = 'app_settings';
|
||||
const _migrationVersionKey = 'settings_migration_version';
|
||||
const _currentMigrationVersion = 4;
|
||||
const _currentMigrationVersion = 5;
|
||||
const _spotifyClientSecretKey = 'spotify_client_secret';
|
||||
final _log = AppLogger('SettingsProvider');
|
||||
|
||||
@@ -41,9 +41,7 @@ class SettingsNotifier extends Notifier<AppSettings> {
|
||||
await _normalizeSongLinkRegionIfNeeded();
|
||||
}
|
||||
|
||||
await _loadSpotifyClientSecret(prefs);
|
||||
|
||||
_applySpotifyCredentials();
|
||||
await _retireBuiltInSpotifyProvider();
|
||||
|
||||
LogBuffer.loggingEnabled = state.enableLogging;
|
||||
|
||||
@@ -105,6 +103,17 @@ class SettingsNotifier extends Notifier<AppSettings> {
|
||||
}
|
||||
state = state.copyWith(lyricsProviders: updatedProviders);
|
||||
}
|
||||
if (state.metadataSource != 'deezer' ||
|
||||
state.spotifyClientId.isNotEmpty ||
|
||||
state.spotifyClientSecret.isNotEmpty ||
|
||||
state.useCustomSpotifyCredentials) {
|
||||
state = state.copyWith(
|
||||
metadataSource: 'deezer',
|
||||
spotifyClientId: '',
|
||||
spotifyClientSecret: '',
|
||||
useCustomSpotifyCredentials: false,
|
||||
);
|
||||
}
|
||||
state = state.copyWith(lastSeenVersion: AppInfo.version);
|
||||
await prefs.setInt(_migrationVersionKey, _currentMigrationVersion);
|
||||
await _saveSettings();
|
||||
@@ -193,49 +202,28 @@ class SettingsNotifier extends Notifier<AppSettings> {
|
||||
await _saveSettings();
|
||||
}
|
||||
|
||||
Future<void> _loadSpotifyClientSecret(SharedPreferences prefs) async {
|
||||
Future<void> _retireBuiltInSpotifyProvider() async {
|
||||
final storedSecret = await _secureStorage.read(
|
||||
key: _spotifyClientSecretKey,
|
||||
);
|
||||
final prefsSecret = state.spotifyClientSecret;
|
||||
|
||||
if ((storedSecret == null || storedSecret.isEmpty) &&
|
||||
prefsSecret.isNotEmpty) {
|
||||
await _secureStorage.write(
|
||||
key: _spotifyClientSecretKey,
|
||||
value: prefsSecret,
|
||||
);
|
||||
}
|
||||
|
||||
final effectiveSecret = (storedSecret != null && storedSecret.isNotEmpty)
|
||||
? storedSecret
|
||||
: (prefsSecret.isNotEmpty ? prefsSecret : '');
|
||||
|
||||
if (effectiveSecret != state.spotifyClientSecret) {
|
||||
state = state.copyWith(spotifyClientSecret: effectiveSecret);
|
||||
}
|
||||
|
||||
if (prefsSecret.isNotEmpty) {
|
||||
await _saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _storeSpotifyClientSecret(String secret) async {
|
||||
if (secret.isEmpty) {
|
||||
if (storedSecret != null && storedSecret.isNotEmpty) {
|
||||
await _secureStorage.delete(key: _spotifyClientSecretKey);
|
||||
} else {
|
||||
await _secureStorage.write(key: _spotifyClientSecretKey, value: secret);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _applySpotifyCredentials() async {
|
||||
if (state.spotifyClientId.isNotEmpty &&
|
||||
state.spotifyClientSecret.isNotEmpty) {
|
||||
await PlatformBridge.setSpotifyCredentials(
|
||||
state.spotifyClientId,
|
||||
state.spotifyClientSecret,
|
||||
);
|
||||
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) {
|
||||
@@ -396,45 +384,9 @@ class SettingsNotifier extends Notifier<AppSettings> {
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
void setSpotifyClientId(String clientId) {
|
||||
state = state.copyWith(spotifyClientId: clientId);
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
Future<void> setSpotifyClientSecret(String clientSecret) async {
|
||||
state = state.copyWith(spotifyClientSecret: clientSecret);
|
||||
await _storeSpotifyClientSecret(clientSecret);
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
Future<void> setSpotifyCredentials(
|
||||
String clientId,
|
||||
String clientSecret,
|
||||
) async {
|
||||
state = state.copyWith(
|
||||
spotifyClientId: clientId,
|
||||
spotifyClientSecret: clientSecret,
|
||||
);
|
||||
await _storeSpotifyClientSecret(clientSecret);
|
||||
_saveSettings();
|
||||
_applySpotifyCredentials();
|
||||
}
|
||||
|
||||
Future<void> clearSpotifyCredentials() async {
|
||||
state = state.copyWith(spotifyClientId: '', spotifyClientSecret: '');
|
||||
await _storeSpotifyClientSecret('');
|
||||
_saveSettings();
|
||||
_applySpotifyCredentials();
|
||||
}
|
||||
|
||||
void setUseCustomSpotifyCredentials(bool enabled) {
|
||||
state = state.copyWith(useCustomSpotifyCredentials: enabled);
|
||||
_saveSettings();
|
||||
_applySpotifyCredentials();
|
||||
}
|
||||
|
||||
void setMetadataSource(String source) {
|
||||
state = state.copyWith(metadataSource: source);
|
||||
final normalized = source == 'deezer' ? 'deezer' : 'deezer';
|
||||
state = state.copyWith(metadataSource: normalized);
|
||||
_saveSettings();
|
||||
}
|
||||
|
||||
|
||||
@@ -458,7 +458,8 @@ class TrackNotifier extends Notifier<TrackState> {
|
||||
}
|
||||
|
||||
// If URL doesn't match any known service, it's unrecognized
|
||||
final isSpotifyUrl = url.contains('open.spotify.com') ||
|
||||
final isSpotifyUrl =
|
||||
url.contains('open.spotify.com') ||
|
||||
url.contains('spotify.link') ||
|
||||
url.startsWith('spotify:');
|
||||
if (!isSpotifyUrl) {
|
||||
@@ -546,11 +547,7 @@ class TrackNotifier extends Notifier<TrackState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> search(
|
||||
String query, {
|
||||
String? metadataSource,
|
||||
String? filterOverride,
|
||||
}) async {
|
||||
Future<void> search(String query, {String? filterOverride}) async {
|
||||
final requestId = ++_currentRequestId;
|
||||
|
||||
// Preserve selected filter during loading
|
||||
@@ -576,7 +573,7 @@ class TrackNotifier extends Notifier<TrackState> {
|
||||
searchProvider != null &&
|
||||
searchProvider.isNotEmpty;
|
||||
|
||||
final source = metadataSource ?? 'deezer';
|
||||
const source = 'deezer';
|
||||
|
||||
_log.i(
|
||||
'Search started: source=$source, query="$query", useExtensions=$useExtensions, filter=$currentFilter',
|
||||
@@ -602,32 +599,20 @@ class TrackNotifier extends Notifier<TrackState> {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
_log.w('Extension search failed, falling back to built-in: $e');
|
||||
_log.w('Extension search failed, falling back to Deezer: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (source == 'deezer') {
|
||||
_log.d('Calling Deezer search API...');
|
||||
results = await PlatformBridge.searchDeezerAll(
|
||||
query,
|
||||
trackLimit: 20,
|
||||
artistLimit: 2,
|
||||
filter: currentFilter,
|
||||
);
|
||||
_log.i(
|
||||
'Deezer returned ${(results['tracks'] as List?)?.length ?? 0} tracks, ${(results['artists'] as List?)?.length ?? 0} artists, ${(results['albums'] as List?)?.length ?? 0} albums',
|
||||
);
|
||||
} else {
|
||||
_log.d('Calling Spotify search API...');
|
||||
results = await PlatformBridge.searchSpotifyAll(
|
||||
query,
|
||||
trackLimit: 20,
|
||||
artistLimit: 2,
|
||||
);
|
||||
_log.i(
|
||||
'Spotify returned ${(results['tracks'] as List?)?.length ?? 0} tracks, ${(results['artists'] as List?)?.length ?? 0} artists',
|
||||
);
|
||||
}
|
||||
_log.d('Calling Deezer search API...');
|
||||
results = await PlatformBridge.searchDeezerAll(
|
||||
query,
|
||||
trackLimit: 20,
|
||||
artistLimit: 2,
|
||||
filter: currentFilter,
|
||||
);
|
||||
_log.i(
|
||||
'Deezer returned ${(results['tracks'] as List?)?.length ?? 0} tracks, ${(results['artists'] as List?)?.length ?? 0} artists, ${(results['albums'] as List?)?.length ?? 0} albums',
|
||||
);
|
||||
|
||||
if (!_isRequestValid(requestId)) {
|
||||
_log.w('Search request cancelled (requestId=$requestId)');
|
||||
|
||||
Reference in New Issue
Block a user