refactor: remove built-in provider registry in favor of extensions

All search, metadata, and download providers are now exclusively
supplied by extensions. The built-in provider registry that previously
exposed Deezer/Tidal/Qobuz as hardcoded providers is fully removed.

Removed across Go, Dart, Kotlin, and Swift:
- BuiltInProviderSpec class, registry, and all accessor helpers
- SearchProviderAllJSON, GetBuiltInProvidersJSON, ParseProviderURLJSON,
  ParseDeezerURLExport Go exports and their platform channel bindings
- Built-in provider items in search dropdown, service picker, and
  provider priority UI lists
- provider_ui_utils.dart helper file

Deezer metadata enrichment (ISRC lookup, extended metadata, cover
upgrade) remains fully functional through direct DeezerClient calls
in the download pipeline — these are not part of the provider
registry and are unaffected.

Mark deezer as a retired built-in metadata provider so stale user
priority lists are cleaned up on next launch.
This commit is contained in:
zarzet
2026-05-05 03:55:24 +07:00
parent cfc8e699f3
commit 101ab3f521
49 changed files with 275 additions and 1502 deletions
+2 -48
View File
@@ -61,17 +61,6 @@ Future<List<Map<String, dynamic>>> _searchMetadataProvider(
required String filter,
required int limit,
}) async {
if (isBuiltInSearchProvider(providerId)) {
final result = await PlatformBridge.searchProviderAll(
providerId,
query,
trackLimit: 0,
artistLimit: filter == 'artist' ? limit : 0,
filter: filter,
);
return _extractSearchItems(result, filter);
}
return PlatformBridge.customSearchWithExtension(
providerId,
query,
@@ -79,24 +68,6 @@ Future<List<Map<String, dynamic>>> _searchMetadataProvider(
);
}
List<Map<String, dynamic>> _extractSearchItems(
Map<String, dynamic> result,
String filter,
) {
final key = switch (filter) {
'artist' => 'artists',
'album' => 'albums',
_ => '${filter}s',
};
final items = result[key];
if (items is! List) return const [];
return items
.whereType<Map<Object?, Object?>>()
.map((item) => Map<String, dynamic>.from(item))
.toList(growable: false);
}
List<String> _metadataSearchProviderCandidates(
BuildContext context, {
String? sourceProviderId,
@@ -142,10 +113,6 @@ List<String> _metadataSearchProviderCandidates(
addProvider(extension.id);
}
for (final providerId in builtInSearchProviderIds) {
addProvider(providerId);
}
return candidates;
}
@@ -153,7 +120,6 @@ bool _canSearchMetadataProvider(
String providerId,
ExtensionState extensionState,
) {
if (isBuiltInSearchProvider(providerId)) return true;
return extensionState.extensions.any(
(ext) => ext.enabled && ext.hasCustomSearch && ext.id == providerId,
);
@@ -333,8 +299,7 @@ void _pushArtistScreen(
String? coverUrl,
String? extensionId,
}) {
final isExtension =
extensionId != null && !isBuiltInMetadataProvider(extensionId);
final isExtension = extensionId != null;
final resolvedProviderId = extensionId;
_pushViaPreferredNavigator(
@@ -362,8 +327,7 @@ void _pushAlbumScreen(
String? coverUrl,
String? extensionId,
}) {
final isExtension =
extensionId != null && !isBuiltInMetadataProvider(extensionId);
final isExtension = extensionId != null;
final resolvedExtensionId = extensionId;
_pushViaPreferredNavigator(
@@ -677,19 +641,9 @@ bool _canNavigateArtistDirectly({
required String? extensionId,
}) {
if (extensionId != null) return true;
final providerPrefix = _resourceProviderPrefix(artistId);
if (providerPrefix != null && isBuiltInMetadataProvider(providerPrefix)) {
return true;
}
return _spotifyArtistIdPattern.hasMatch(artistId);
}
String? _resourceProviderPrefix(String resourceId) {
final colonIndex = resourceId.indexOf(':');
if (colonIndex <= 0) return null;
return resourceId.substring(0, colonIndex).trim();
}
final RegExp _spotifyArtistIdPattern = RegExp(r'^[A-Za-z0-9]{22}$');
class ClickableAlbumName extends StatelessWidget {
-24
View File
@@ -1,24 +0,0 @@
import 'package:flutter/material.dart';
import 'package:spotiflac_android/providers/extension_provider.dart';
IconData resolveProviderIcon(
String providerId, {
IconData tidalIcon = Icons.music_note,
IconData builtInDefaultIcon = Icons.album,
IconData deezerIcon = Icons.graphic_eq,
IconData fallbackIcon = Icons.extension,
}) {
final builtIn = builtInProviderSpecForId(providerId);
if (builtIn != null) {
if (providerId == 'tidal') {
return tidalIcon;
}
return builtInDefaultIcon;
}
if (providerId == 'deezer') {
return deezerIcon;
}
return fallbackIcon;
}