From 01c08322c1f756b26c8dd5585c789f718641d6c0 Mon Sep 17 00:00:00 2001 From: zarzet Date: Fri, 24 Jul 2026 21:32:13 +0700 Subject: [PATCH] fix(extensions): preserve explicit track metadata --- go_backend/coverage_test_helpers_test.go | 3 +- go_backend/exports_extensions.go | 5 ++++ go_backend/exports_supplement_test.go | 35 ++++++++++++++++++++++++ lib/screens/artist_screen.dart | 1 + lib/screens/home_tab_widgets.dart | 1 + test/models_and_utils_test.dart | 16 +++++++++++ 6 files changed, 60 insertions(+), 1 deletion(-) diff --git a/go_backend/coverage_test_helpers_test.go b/go_backend/coverage_test_helpers_test.go index 822faec1..c771912e 100644 --- a/go_backend/coverage_test_helpers_test.go +++ b/go_backend/coverage_test_helpers_test.go @@ -73,7 +73,8 @@ function track(id) { genre: "Pop", composer: "Composer", audioQuality: "FLAC 24-bit", - audioModes: "DOLBY_ATMOS" + audioModes: "DOLBY_ATMOS", + explicit: true }; } diff --git a/go_backend/exports_extensions.go b/go_backend/exports_extensions.go index daf75c2b..c03ccf35 100644 --- a/go_backend/exports_extensions.go +++ b/go_backend/exports_extensions.go @@ -54,6 +54,7 @@ func normalizeExtensionTrackMetadataMap( "composer": track.Composer, "audio_quality": track.AudioQuality, "audio_modes": track.AudioModes, + "explicit": track.Explicit, } } @@ -791,6 +792,7 @@ func CustomSearchWithExtensionJSONWithRequestID(extensionID, query string, optio "composer": track.Composer, "audio_quality": track.AudioQuality, "audio_modes": track.AudioModes, + "explicit": track.Explicit, } } @@ -856,6 +858,7 @@ func HandleURLWithExtensionJSON(url string) (string, error) { "isrc": result.Track.ISRC, "provider_id": result.Track.ProviderID, "composer": result.Track.Composer, + "explicit": result.Track.Explicit, } } @@ -881,6 +884,7 @@ func HandleURLWithExtensionJSON(url string) (string, error) { "item_type": track.ItemType, "album_type": track.AlbumType, "composer": track.Composer, + "explicit": track.Explicit, } } response["tracks"] = tracks @@ -978,6 +982,7 @@ func HandleURLWithExtensionJSON(url string) (string, error) { "provider_id": track.ProviderID, "spotify_id": track.SpotifyID, "composer": track.Composer, + "explicit": track.Explicit, } } artistResponse["top_tracks"] = topTracks diff --git a/go_backend/exports_supplement_test.go b/go_backend/exports_supplement_test.go index e82b96dc..0523bd3e 100644 --- a/go_backend/exports_supplement_test.go +++ b/go_backend/exports_supplement_test.go @@ -69,6 +69,41 @@ func TestGetProviderMetadataPrefersEnabledDeezerExtension(t *testing.T) { } } +func TestExtensionTrackExportsPreserveExplicitFlag(t *testing.T) { + dir := t.TempDir() + if err := InitExtensionSystem(filepath.Join(dir, "extensions"), filepath.Join(dir, "data")); err != nil { + t.Fatalf("InitExtensionSystem: %v", err) + } + + ext := newTestLoadedExtension(t, ExtensionTypeMetadataProvider) + manager := getExtensionManager() + manager.mu.Lock() + manager.extensions = map[string]*loadedExtension{ext.ID: ext} + manager.mu.Unlock() + defer CleanupExtensions() + + assertExplicit := func(name, jsonText string, err error) { + t.Helper() + if err != nil { + t.Fatalf("%s: %v", name, err) + } + if !strings.Contains(jsonText, `"explicit":true`) { + t.Fatalf("%s dropped explicit flag: %s", name, jsonText) + } + } + + jsonText, err := CustomSearchWithExtensionJSON(ext.ID, "needle", `{"filter":"tracks"}`) + assertExplicit("custom search", jsonText, err) + + for _, resourceType := range []string{"track", "album", "playlist", "artist"} { + jsonText, err = GetProviderMetadataJSON(ext.ID, resourceType, resourceType+"-1") + assertExplicit("provider metadata "+resourceType, jsonText, err) + } + + jsonText, err = HandleURLWithExtensionJSON("https://example.test/track/1") + assertExplicit("URL handler", jsonText, err) +} + func TestExportsJSONWrappersAndExtensionManagerSurface(t *testing.T) { dir := t.TempDir() dataDir := filepath.Join(dir, "data") diff --git a/lib/screens/artist_screen.dart b/lib/screens/artist_screen.dart index 627237ae..b1e0db5b 100644 --- a/lib/screens/artist_screen.dart +++ b/lib/screens/artist_screen.dart @@ -409,6 +409,7 @@ class _ArtistScreenState extends ConsumerState composer: data['composer']?.toString(), source: data['provider_id']?.toString() ?? widget.extensionId, previewUrl: data['preview_url']?.toString(), + explicit: parseExplicitFlag(data['explicit']), ); } diff --git a/lib/screens/home_tab_widgets.dart b/lib/screens/home_tab_widgets.dart index f1af08b5..f83b575b 100644 --- a/lib/screens/home_tab_widgets.dart +++ b/lib/screens/home_tab_widgets.dart @@ -1317,6 +1317,7 @@ class _ExtensionArtistScreenState extends ConsumerState composer: base.composer, source: (data['provider_id'] ?? widget.extensionId).toString(), previewUrl: base.previewUrl, + explicit: base.explicit, ); } diff --git a/test/models_and_utils_test.dart b/test/models_and_utils_test.dart index 7ef11d22..a5659af6 100644 --- a/test/models_and_utils_test.dart +++ b/test/models_and_utils_test.dart @@ -148,6 +148,22 @@ void main() { }); group('Track', () { + test('preserves a generic extension explicit flag', () { + final track = Track.fromBackendMap({ + 'id': 'extension-track-1', + 'name': 'Explicit Song', + 'artists': 'Artist', + 'album_name': 'Album', + 'duration_ms': 180000, + 'provider_id': 'extension.example', + 'explicit': true, + }); + + expect(track.source, 'extension.example'); + expect(track.explicit, isTrue); + expect(track.isExplicit, isTrue); + }); + test('exposes collection, source, and quality flags', () { const album = Track( id: 'album-1',