mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-27 05:12:29 +02:00
fix(playlists): thread the source provider id through to recent playlists
Fixes #368. Reopening a Spotify playlist from "recent access" showed no tracks, while the first view (right after pasting the URL) worked fine. Root cause traced across two layers: 1. Go: ExtURLHandleResult (the parsed shape of an extension's handleUrl() return value) never captured a top-level `id` for the handled resource. Track/album/artist results carry their own id inside their nested metadata, but a plain playlist result has no such object, so its id was silently dropped everywhere from the goja parser through to the JSON the Dart side receives. 2. Dart: TrackState had nowhere to put that id even if it existed (only playlistName), so recording a "recent" playlist access stored the playlist's *name* as if it were its id. Reopening it later fed that name into PlaylistScreen's provider-guessing logic (legacyProviderIdFromResourceId, which only recognizes legacy "provider:id" prefixes), which naturally failed and fell through to a hardcoded Deezer metadata fetch using a Spotify playlist's name as the resource id - guaranteed to return nothing. Fix, matching the "generic API, not per-provider checks" architecture in CONTRIBUTING.md: - go_backend/extension_provider_wrapper.go: add ExtURLHandleResult.ID. - go_backend/extension_goja_convert.go: parse it from the handler's return value. - go_backend/exports_extensions.go: surface it in the JSON response. - lib/providers/track_provider.dart: add TrackState.playlistId, populated from the response's `id` field for playlist results. - lib/screens/home_tab.dart: record the real playlist id (falling back to the name only if a provider never supplies one) and pass both the id and the already-known provider id forward to PlaylistScreen. - lib/screens/playlist_screen.dart: PlaylistScreen gains a metadataProviderId param that takes priority over guessing from the id's shape. - lib/screens/home_tab_recent.dart: pass the recent-access entry's stored providerId through when reopening a playlist. - lib/utils/provider_resource_ids.dart: extract the known-id-vs-guessed-id preference into a small, directly testable resolvePreferredMetadataProviderId helper. Note: this fixes the common case (an extension already reported its own id as the source provider for the URL it handled). If a provider never supplies an id for its handleUrl() playlist result, playlistId stays null and behavior is unchanged from before this fix - no regression, just not a complete fix for that narrower case, since that would require changes in extension-side JS code outside this repo. Tests: - go_backend/extension_goja_convert_url_handle_test.go: the new ID field round-trips from a handler's return value, and stays empty when the handler doesn't supply one. - test/provider_resource_ids_test.dart: resolvePreferredMetadataProviderId prefers a known provider id, falls back to the legacy-prefix guess, treats a blank known id as unknown, and returns null for the unprefixed-id-with-no-known-provider case from #368 itself. Verification: go build/vet/test and flutter analyze/test all green.
This commit is contained in:
@@ -805,6 +805,7 @@ func HandleURLWithExtensionJSON(url string) (string, error) {
|
||||
|
||||
response := map[string]any{
|
||||
"type": result.Type,
|
||||
"id": result.ID,
|
||||
"extension_id": extensionID,
|
||||
"name": result.Name,
|
||||
"cover_url": result.CoverURL,
|
||||
|
||||
@@ -522,6 +522,7 @@ func parseExtensionURLHandleValue(vm *goja.Runtime, value goja.Value) (ExtURLHan
|
||||
obj := value.ToObject(vm)
|
||||
handleResult := ExtURLHandleResult{
|
||||
Type: gojaObjectString(obj, "type"),
|
||||
ID: gojaObjectString(obj, "id"),
|
||||
Name: gojaObjectString(obj, "name"),
|
||||
CoverURL: gojaObjectString(obj, "cover_url", "coverUrl"),
|
||||
HeaderImage: gojaObjectString(obj, "header_image", "headerImage"),
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package gobackend
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
)
|
||||
|
||||
func TestParseExtensionURLHandleValueCapturesID(t *testing.T) {
|
||||
vm := goja.New()
|
||||
value, err := vm.RunString(`({
|
||||
type: "playlist",
|
||||
id: "37i9dQZF1DXcBWIGoYBM5M",
|
||||
name: "Discover Weekly",
|
||||
tracks: []
|
||||
})`)
|
||||
if err != nil {
|
||||
t.Fatalf("RunString: %v", err)
|
||||
}
|
||||
|
||||
result, err := parseExtensionURLHandleValue(vm, value)
|
||||
if err != nil {
|
||||
t.Fatalf("parseExtensionURLHandleValue: %v", err)
|
||||
}
|
||||
if result.Type != "playlist" {
|
||||
t.Fatalf("Type = %q", result.Type)
|
||||
}
|
||||
if result.ID != "37i9dQZF1DXcBWIGoYBM5M" {
|
||||
t.Fatalf("ID = %q, want the playlist id from the handler result", result.ID)
|
||||
}
|
||||
if result.Name != "Discover Weekly" {
|
||||
t.Fatalf("Name = %q", result.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseExtensionURLHandleValueOmitsIDWhenAbsent(t *testing.T) {
|
||||
vm := goja.New()
|
||||
// Track/album/artist results carry their own ID inside their nested
|
||||
// metadata, so a handler that omits the top-level "id" (as these do
|
||||
// today) must not surface a stale or zero-value ID.
|
||||
value, err := vm.RunString(`({ type: "track", name: "A Track" })`)
|
||||
if err != nil {
|
||||
t.Fatalf("RunString: %v", err)
|
||||
}
|
||||
|
||||
result, err := parseExtensionURLHandleValue(vm, value)
|
||||
if err != nil {
|
||||
t.Fatalf("parseExtensionURLHandleValue: %v", err)
|
||||
}
|
||||
if result.ID != "" {
|
||||
t.Fatalf("ID = %q, want empty when the handler result has none", result.ID)
|
||||
}
|
||||
}
|
||||
@@ -724,7 +724,11 @@ func (p *extensionProviderWrapper) customSearch(query string, options map[string
|
||||
}
|
||||
|
||||
type ExtURLHandleResult struct {
|
||||
Type string `json:"type"`
|
||||
Type string `json:"type"`
|
||||
// ID identifies the handled resource itself (e.g. a playlist ID). Track,
|
||||
// album, and artist results already carry their own ID inside their
|
||||
// nested metadata; this covers result types with no such nested object.
|
||||
ID string `json:"id,omitempty"`
|
||||
Track *ExtTrackMetadata `json:"track,omitempty"`
|
||||
Tracks []ExtTrackMetadata `json:"tracks,omitempty"`
|
||||
Album *ExtAlbumMetadata `json:"album,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user