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
@@ -2795,23 +2795,6 @@ class MainActivity: FlutterFragmentActivity() {
}
result.success(null)
}
"searchProviderAll" -> {
val providerId = call.argument<String>("provider_id") ?: ""
val query = call.argument<String>("query") ?: ""
val trackLimit = call.argument<Int>("track_limit") ?: 15
val artistLimit = call.argument<Int>("artist_limit") ?: 2
val filter = call.argument<String>("filter") ?: ""
val response = withContext(Dispatchers.IO) {
Gobackend.searchProviderAllJSON(providerId, query, trackLimit.toLong(), artistLimit.toLong(), filter)
}
result.success(response)
}
"getBuiltInProviders" -> {
val response = withContext(Dispatchers.IO) {
Gobackend.getBuiltInProvidersJSON()
}
result.success(response)
}
"getDeezerRelatedArtists" -> {
val artistId = call.argument<String>("artist_id") ?: ""
val limit = call.argument<Int>("limit") ?: 12
@@ -2829,13 +2812,6 @@ class MainActivity: FlutterFragmentActivity() {
}
result.success(response)
}
"parseProviderUrl" -> {
val url = call.argument<String>("url") ?: ""
val response = withContext(Dispatchers.IO) {
Gobackend.parseProviderURLJSON(url)
}
result.success(response)
}
"searchDeezerByISRC" -> {
val isrc = call.argument<String>("isrc") ?: ""
val itemId = call.argument<String>("item_id") ?: ""
@@ -205,6 +205,7 @@ object SafDownloadHandler {
mimeType: String,
srcPath: String
): String? {
var stagedDocument: DocumentFile? = null
return try {
val treeUri = Uri.parse(treeUriStr)
val targetDir = ensureDocumentDir(context, treeUri, relativeDir) ?: return null
@@ -213,11 +214,18 @@ object SafDownloadHandler {
val stagedName = buildStagedSafFileName(finalName, ext)
val document = createOrReuseDocumentFile(targetDir, mimeType, stagedName)
?: return null
context.contentResolver.openOutputStream(document.uri, "wt")?.use { output ->
stagedDocument = document
val outputStream = context.contentResolver.openOutputStream(document.uri, "wt")
if (outputStream == null) {
document.delete()
stagedDocument = null
return null
}
outputStream.use { output ->
File(srcPath).inputStream().use { input ->
input.copyTo(output)
}
} ?: return null
}
val existingFinal = targetDir.findFile(finalName)
if (existingFinal != null && existingFinal.uri != document.uri) {
@@ -227,8 +235,10 @@ object SafDownloadHandler {
document.delete()
return null
}
stagedDocument = null
targetDir.findFile(finalName)?.uri?.toString() ?: document.uri.toString()
} catch (e: Exception) {
stagedDocument?.delete()
android.util.Log.w("SpotiFLAC", "Failed to write file to SAF: ${e.message}")
null
}