From b4f239c6927d337d5e3cf92d48ad6a14338e03e3 Mon Sep 17 00:00:00 2001 From: zarzet Date: Tue, 28 Jul 2026 14:11:14 +0700 Subject: [PATCH] fix(download): honor explicitly selected provider --- go_backend/extension_fallback.go | 7 +++++- go_backend/extension_fallback_helpers.go | 31 ++++++++++++++++++++++-- go_backend/extension_providers_test.go | 14 +++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/go_backend/extension_fallback.go b/go_backend/extension_fallback.go index 0deeb3e4..591fbce7 100644 --- a/go_backend/extension_fallback.go +++ b/go_backend/extension_fallback.go @@ -524,7 +524,12 @@ func DownloadWithExtensionFallback(req DownloadRequest) (*DownloadResponse, erro } } - priority = prioritizeFallbackProvidersByHealth(priority, extManager, req.Source) + healthProtectedProvider := selectedProvider + if strings.TrimSpace(healthProtectedProvider) == "" { + healthProtectedProvider = req.Source + } + priority = prioritizeFallbackProvidersByHealth(priority, extManager, healthProtectedProvider) + priority = moveProviderToFront(priority, selectedProvider) for _, providerID := range priority { if isDownloadCancelled(req.ItemID) { diff --git a/go_backend/extension_fallback_helpers.go b/go_backend/extension_fallback_helpers.go index 26ce420b..4533cb9c 100644 --- a/go_backend/extension_fallback_helpers.go +++ b/go_backend/extension_fallback_helpers.go @@ -280,7 +280,7 @@ func fallbackRuntimeHealthStatus(ext *loadedExtension) string { } } -func prioritizeFallbackProvidersByHealth(priority []string, extManager *extensionManager, sourceProvider string) []string { +func prioritizeFallbackProvidersByHealth(priority []string, extManager *extensionManager, protectedProvider string) []string { if len(priority) == 0 || extManager == nil { return priority } @@ -294,7 +294,7 @@ func prioritizeFallbackProvidersByHealth(priority []string, extManager *extensio if providerID == "" { continue } - if strings.EqualFold(providerID, sourceProvider) || !isExtensionFallbackAllowed(providerID) { + if strings.EqualFold(providerID, protectedProvider) || !isExtensionFallbackAllowed(providerID) { unknown = append(unknown, providerID) continue } @@ -324,6 +324,33 @@ func prioritizeFallbackProvidersByHealth(priority []string, extManager *extensio return result } +// moveProviderToFront preserves the user's explicit provider selection after +// health-based fallback sorting. Health may order the remaining fallback +// candidates, but it must never silently replace the provider the user picked. +func moveProviderToFront(priority []string, providerID string) []string { + providerID = strings.TrimSpace(providerID) + if providerID == "" || len(priority) < 2 { + return priority + } + + selectedIndex := -1 + for i, candidate := range priority { + if strings.EqualFold(strings.TrimSpace(candidate), providerID) { + selectedIndex = i + break + } + } + if selectedIndex <= 0 { + return priority + } + + reordered := make([]string, 0, len(priority)) + reordered = append(reordered, priority[selectedIndex]) + reordered = append(reordered, priority[:selectedIndex]...) + reordered = append(reordered, priority[selectedIndex+1:]...) + return reordered +} + func resolveExtensionAvailabilityReason(availability *ExtAvailabilityResult, err error) string { if availability != nil { if reason := strings.TrimSpace(availability.Reason); reason != "" { diff --git a/go_backend/extension_providers_test.go b/go_backend/extension_providers_test.go index bd2a9a54..9ad82ec0 100644 --- a/go_backend/extension_providers_test.go +++ b/go_backend/extension_providers_test.go @@ -10,6 +10,7 @@ import ( "net/http/httptest" "os" "path/filepath" + "reflect" "strings" "sync" "testing" @@ -457,6 +458,19 @@ func TestShouldStopProviderFallback(t *testing.T) { } } +func TestMoveProviderToFrontPreservesExplicitSelection(t *testing.T) { + priority := []string{"qobuz-web", "amazon-web", "tidal-web"} + got := moveProviderToFront(priority, "AMAZON-WEB") + want := []string{"amazon-web", "qobuz-web", "tidal-web"} + + if !reflect.DeepEqual(got, want) { + t.Fatalf("moveProviderToFront() = %#v, want %#v", got, want) + } + if !reflect.DeepEqual(priority, []string{"qobuz-web", "amazon-web", "tidal-web"}) { + t.Fatalf("moveProviderToFront mutated input: %#v", priority) + } +} + func TestBuildExtensionFallbackStoppedResponsePrefersAvailabilityReason(t *testing.T) { resp := buildExtensionFallbackStoppedResponse("soundcloud", &ExtAvailabilityResult{ Reason: "direct SoundCloud track ID",