react-native-audio-browser 0.2.0-next.11 → 0.2.0-next.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +110 -25
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +254 -188
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +23 -3
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +77 -58
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +68 -26
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +49 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +9 -7
- package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
- package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +120 -67
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -24
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +93 -73
- package/android/src/main/java/com/audiobrowser/player/Player.kt +48 -30
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +44 -40
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +35 -0
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +45 -53
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +26 -39
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +228 -50
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +1 -1
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +91 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -53
- package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +87 -0
- package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
- package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +1 -2
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +161 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
- package/ios/Browser/BrowserConfig.swift +102 -5
- package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
- package/ios/Browser/BrowserManager.swift +131 -143
- package/ios/Browser/BrowserPathHelper.swift +44 -44
- package/ios/Browser/JsonModels.swift +187 -106
- package/ios/Browser/SearchDrillIn.swift +6 -6
- package/ios/Browser/SectionPresentation.swift +34 -0
- package/ios/Browser/SectionScope.swift +58 -24
- package/ios/Browser/StyleResolver.swift +47 -0
- package/ios/Browser/TabBarEntries.swift +2 -2
- package/ios/Browser/TrackIdTemplate.swift +37 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
- package/ios/CarPlay/CarPlayController.swift +105 -67
- package/ios/CarPlay/CarPlayImageLoader.swift +38 -18
- package/ios/CarPlay/CarPlayListItemFactory.swift +217 -84
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
- package/ios/Extension/ResolvedTrack+Copying.swift +11 -15
- package/ios/Extension/Section+Copying.swift +31 -0
- package/ios/Extension/Track+Copying.swift +6 -12
- package/ios/Extension/Track+Identity.swift +18 -0
- package/ios/HybridAudioBrowser.swift +113 -35
- package/ios/Model/NitroTypeStubs.swift +46 -21
- package/ios/Model/PlayerUpdateOptions.swift +7 -0
- package/ios/Player/NowPlayingUpdater.swift +1 -1
- package/ios/Player/PlaybackCoordinator.swift +2 -2
- package/ios/Player/RetryManager.swift +4 -3
- package/ios/Tests/BrowserPathHelperTests.swift +50 -20
- package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
- package/ios/Tests/JsonModelsTests.swift +75 -1
- package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
- package/ios/Tests/ResolvedTrackCopyingTests.swift +37 -35
- package/ios/Tests/SearchDrillInTests.swift +16 -16
- package/ios/Tests/SectionPresentationTests.swift +61 -0
- package/ios/Tests/SectionScopeTests.swift +87 -55
- package/ios/Tests/StyleResolverTests.swift +67 -0
- package/ios/Tests/TabBarEntriesTests.swift +18 -18
- package/ios/Tests/TrackIdTemplateTests.swift +67 -0
- package/ios/Tests/TrackIdentityTests.swift +20 -0
- package/ios/Tests/TrackSelectorTests.swift +168 -33
- package/ios/TrackSelector.swift +45 -30
- package/ios/Util/ArtworkImageFetcher.swift +16 -1
- package/lib/commonjs/AudioBrowser.js +11 -0
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/features/browser-config.js +11 -8
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/browser.js +2 -2
- package/lib/commonjs/features/favorites.js +13 -10
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/playback/controls.js +4 -2
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +2 -3
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +3 -1
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +2 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -1
- package/lib/commonjs/features/queue/activeTrack.js +14 -3
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +11 -4
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/utils/LazyNativeEmitter.js +21 -10
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/getTrackIdentity.js +24 -0
- package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +32 -24
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +144 -87
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
- package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/commonjs/web/browser/sections.js +89 -0
- package/lib/commonjs/web/browser/sections.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
- package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/module/AudioBrowser.js +1 -0
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/features/browser-config.js +11 -8
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/browser.js +2 -2
- package/lib/module/features/favorites.js +13 -10
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/playback/controls.js +4 -2
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/state.js +2 -3
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +3 -1
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +2 -0
- package/lib/module/features/player/validateOptions.js.map +1 -1
- package/lib/module/features/queue/activeTrack.js +14 -3
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +11 -4
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/utils/LazyNativeEmitter.js +21 -10
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +31 -13
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/getTrackIdentity.js +20 -0
- package/lib/module/utils/getTrackIdentity.js.map +1 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +32 -24
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +144 -87
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/FavoriteManager.js +31 -21
- package/lib/module/web/browser/FavoriteManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/module/web/browser/sections.js +83 -0
- package/lib/module/web/browser/sections.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +4 -3
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/util/BrowserPathHelper.js +56 -11
- package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/typescript/src/AudioBrowser.d.ts +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
- package/lib/typescript/src/features/browser.d.ts +2 -2
- package/lib/typescript/src/features/favorites.d.ts +13 -10
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +4 -2
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +2 -3
- package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
- package/lib/typescript/src/features/player/options.d.ts +21 -19
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +1 -8
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts +8 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +5 -2
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-native.d.ts +2 -2
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +260 -127
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +33 -53
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +16 -17
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
- package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +0 -1
- package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +4 -3
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +10 -4
- package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +4 -3
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +4 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +4 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +10 -4
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +2 -5
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +4 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +9 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +4 -2
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +21 -14
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +4 -3
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +16 -10
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +8 -2
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +6 -7
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +4 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +49 -51
- package/nitrogen/generated/android/c++/JSection.hpp +114 -0
- package/nitrogen/generated/android/c++/JSectionStyle.hpp +69 -0
- package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +19 -48
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +4 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +25 -18
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +10 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -18
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Section.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → SectionStyle.kt} +12 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +23 -5
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +104 -82
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +16 -10
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +16 -10
- package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
- package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
- package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +5 -5
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +54 -83
- package/nitrogen/generated/ios/swift/Section.swift +100 -0
- package/nitrogen/generated/ios/swift/SectionStyle.swift +64 -0
- package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +20 -73
- package/nitrogen/generated/ios/swift/TrackStyle.swift +28 -22
- package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
- package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -6
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +30 -38
- package/nitrogen/generated/shared/c++/Section.hpp +106 -0
- package/nitrogen/generated/shared/c++/SectionStyle.hpp +96 -0
- package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +15 -31
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +50 -34
- package/package.json +1 -1
- package/src/AudioBrowser.ts +1 -0
- package/src/features/browser-config.test.ts +21 -6
- package/src/features/browser-config.ts +11 -8
- package/src/features/browser.test.ts +1 -1
- package/src/features/browser.ts +2 -2
- package/src/features/favorites.ts +13 -10
- package/src/features/hook-wiring.test.ts +63 -15
- package/src/features/playback/controls.ts +4 -2
- package/src/features/playback/state.ts +2 -3
- package/src/features/playback/trackPlaybackTime.ts +1 -1
- package/src/features/player/options.ts +27 -19
- package/src/features/player/setup.test.ts +12 -3
- package/src/features/player/setup.ts +3 -1
- package/src/features/player/validateOptions.ts +2 -0
- package/src/features/queue/activeTrack.ts +14 -3
- package/src/features/queue/queue.ts +11 -4
- package/src/nitro-stub-drift.test.ts +40 -0
- package/src/public-surface.test.ts +40 -0
- package/src/specs/audio-browser.nitro.ts +4 -3
- package/src/style-resolution.test.ts +139 -0
- package/src/types/browser-native.ts +2 -2
- package/src/types/browser-nodes.ts +268 -136
- package/src/types/browser.ts +34 -55
- package/src/utils/LazyNativeEmitter.ts +20 -10
- package/src/utils/NativeUpdatedValue.ts +29 -13
- package/src/utils/getTrackIdentity.ts +20 -0
- package/src/utils/useNativeValueRefreshedBy.ts +38 -0
- package/src/web/NativeAudioBrowser.ts +37 -22
- package/src/web/browser/BrowserManager.ts +176 -95
- package/src/web/browser/FavoriteManager.test.ts +84 -0
- package/src/web/browser/FavoriteManager.ts +28 -23
- package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
- package/src/web/browser/assertBrowsePageShape.ts +5 -3
- package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
- package/src/web/browser/classifyTrackNavigation.ts +9 -9
- package/src/web/browser/parseSearchResponse.test.ts +8 -2
- package/src/web/browser/sections.test.ts +91 -0
- package/src/web/browser/sections.ts +72 -0
- package/src/web/player/NowPlayingManager.ts +4 -3
- package/src/web/util/BrowserPathHelper.test.ts +52 -0
- package/src/web/util/BrowserPathHelper.ts +61 -13
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
- package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
package com.audiobrowser.util
|
|
2
|
+
|
|
3
|
+
import androidx.media.utils.MediaConstants
|
|
4
|
+
import com.audiobrowser.TestFixtures
|
|
5
|
+
import com.audiobrowser.browser.styleResolvedSections
|
|
6
|
+
import com.margelo.nitro.audiobrowser.SectionStyle
|
|
7
|
+
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
8
|
+
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
9
|
+
import org.junit.Assert.assertEquals
|
|
10
|
+
import org.junit.Assert.assertFalse
|
|
11
|
+
import org.junit.Test
|
|
12
|
+
import org.junit.runner.RunWith
|
|
13
|
+
import org.robolectric.RobolectricTestRunner
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The extras are where a section survives the flat MediaBrowser protocol (ADR 0010): the owning
|
|
17
|
+
* section stamps the Android Auto group-title header hint and the per-item display hint. Per-item
|
|
18
|
+
* hints derive from the section resolution; a track's own `display` is positional — the page
|
|
19
|
+
* promise a browsable track emits as the parent-level hint, never its own tile shape (ADR 0011).
|
|
20
|
+
*/
|
|
21
|
+
@RunWith(RobolectricTestRunner::class)
|
|
22
|
+
class MediaExtrasBuilderTest {
|
|
23
|
+
private val track = TestFixtures.track()
|
|
24
|
+
|
|
25
|
+
private fun sectionStyle(display: StyleDisplay?) =
|
|
26
|
+
SectionStyle(display = display, artworkRendering = null, gridWrap = null)
|
|
27
|
+
|
|
28
|
+
@Test
|
|
29
|
+
fun `owning section title stamps the group-title hint`() {
|
|
30
|
+
val section = TestFixtures.section(arrayOf(track), title = "Popular")
|
|
31
|
+
val extras = MediaExtrasBuilder.build(track, section)
|
|
32
|
+
assertEquals(
|
|
33
|
+
"Popular",
|
|
34
|
+
extras.getString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE),
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Test
|
|
39
|
+
fun `an untitled section stamps no group-title hint`() {
|
|
40
|
+
val section = TestFixtures.section(arrayOf(track))
|
|
41
|
+
val extras = MediaExtrasBuilder.build(track, section)
|
|
42
|
+
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Test
|
|
46
|
+
fun `grid section display stamps the grid item hint`() {
|
|
47
|
+
val section = TestFixtures.section(arrayOf(track), style = sectionStyle(StyleDisplay.GRID))
|
|
48
|
+
val extras = MediaExtrasBuilder.build(track, section)
|
|
49
|
+
assertEquals(
|
|
50
|
+
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM,
|
|
51
|
+
extras.getInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM),
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Test
|
|
56
|
+
fun `list section display stamps the list item hint`() {
|
|
57
|
+
val section = TestFixtures.section(arrayOf(track), style = sectionStyle(StyleDisplay.LIST))
|
|
58
|
+
val extras = MediaExtrasBuilder.build(track, section)
|
|
59
|
+
assertEquals(
|
|
60
|
+
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM,
|
|
61
|
+
extras.getInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM),
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Test
|
|
66
|
+
fun `a track's own display never sets its item hint - display is positional`() {
|
|
67
|
+
val gridPromiseTrack =
|
|
68
|
+
track.copy(style = TrackStyle(display = StyleDisplay.GRID, artworkRendering = null))
|
|
69
|
+
val section = TestFixtures.section(arrayOf(gridPromiseTrack), style = null)
|
|
70
|
+
val extras = MediaExtrasBuilder.build(gridPromiseTrack, section)
|
|
71
|
+
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@Test
|
|
75
|
+
fun `a browsable track's declared display emits the parent-level promise`() {
|
|
76
|
+
val handle =
|
|
77
|
+
TestFixtures.browseTrack()
|
|
78
|
+
.copy(style = TrackStyle(display = StyleDisplay.GRID, artworkRendering = null))
|
|
79
|
+
val extras = MediaExtrasBuilder.build(handle)
|
|
80
|
+
val expected = MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
|
|
81
|
+
assertEquals(
|
|
82
|
+
expected,
|
|
83
|
+
extras.getInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE),
|
|
84
|
+
)
|
|
85
|
+
assertEquals(
|
|
86
|
+
expected,
|
|
87
|
+
extras.getInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE),
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Test
|
|
92
|
+
fun `an undeclared promise emits no parent-level hint - never derived`() {
|
|
93
|
+
val handle = TestFixtures.browseTrack()
|
|
94
|
+
val extras = MediaExtrasBuilder.build(handle)
|
|
95
|
+
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE))
|
|
96
|
+
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@Test
|
|
100
|
+
fun `a playable track's display is inert - playables open no page`() {
|
|
101
|
+
val playable =
|
|
102
|
+
track.copy(style = TrackStyle(display = StyleDisplay.GRID, artworkRendering = null))
|
|
103
|
+
val extras = MediaExtrasBuilder.build(playable)
|
|
104
|
+
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE))
|
|
105
|
+
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Test
|
|
109
|
+
fun `no style anywhere stamps no style hint`() {
|
|
110
|
+
val extras = MediaExtrasBuilder.build(track, TestFixtures.section(arrayOf(track)))
|
|
111
|
+
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@Test
|
|
115
|
+
fun `no section (queue and now-playing surfaces) stamps neither section hint`() {
|
|
116
|
+
val extras = MediaExtrasBuilder.build(track)
|
|
117
|
+
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE))
|
|
118
|
+
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Test
|
|
122
|
+
fun `the view-all navigation track carries the section's declared display as its promise`() {
|
|
123
|
+
// A "view all" page has no consumer-authored handle — the synthetic
|
|
124
|
+
// navigation track projects the section's declared block, the only
|
|
125
|
+
// declared promise that page can ever have.
|
|
126
|
+
val section =
|
|
127
|
+
TestFixtures.section(
|
|
128
|
+
arrayOf(track),
|
|
129
|
+
title = "Popular",
|
|
130
|
+
path = "/popular",
|
|
131
|
+
style = SectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = false),
|
|
132
|
+
)
|
|
133
|
+
val navigation = TrackFactory.navigationTrack(section)
|
|
134
|
+
assertEquals(StyleDisplay.GRID, navigation.style?.display)
|
|
135
|
+
|
|
136
|
+
val extras = MediaExtrasBuilder.build(navigation)
|
|
137
|
+
assertEquals(
|
|
138
|
+
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM,
|
|
139
|
+
extras.getInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE),
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@Test
|
|
144
|
+
fun `an unstyled section under a grid page stamps the grid hint end to end`() {
|
|
145
|
+
// The wire that makes ResolvedTrack.style real: page block ->
|
|
146
|
+
// styleResolvedSections fold -> per-item hint. Without the fold, every
|
|
147
|
+
// narrower unit test stays green while the page tier goes dark.
|
|
148
|
+
val page =
|
|
149
|
+
TestFixtures.resolvedTrack(
|
|
150
|
+
style = SectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = null),
|
|
151
|
+
sections = arrayOf(TestFixtures.section(arrayOf(track))),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
val folded = page.styleResolvedSections()!!.single()
|
|
155
|
+
val extras = MediaExtrasBuilder.build(track, folded)
|
|
156
|
+
assertEquals(
|
|
157
|
+
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM,
|
|
158
|
+
extras.getInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM),
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -22,27 +22,27 @@ class TrackFactoryMediaIdTest {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
@Test
|
|
25
|
-
fun `stable id wins over the contextual
|
|
25
|
+
fun `stable id wins over the contextual path for playable tracks`() {
|
|
26
26
|
val track =
|
|
27
27
|
TestFixtures.track(id = "abc123", src = "/listen/abc123/channel.mp3")
|
|
28
|
-
.copy(
|
|
28
|
+
.copy(path = "/home?__trackId=%2Flisten%2Fabc123%2Fchannel.mp3")
|
|
29
29
|
assertEquals("abc123", TrackFactory.toMedia3(track).mediaId)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
@Test
|
|
33
|
-
fun `playable track without id falls back to
|
|
34
|
-
val
|
|
35
|
-
assertEquals("/home?__trackId=%2Fa.mp3", TrackFactory.toMedia3(
|
|
33
|
+
fun `playable track without id falls back to path then src`() {
|
|
34
|
+
val withPath = TestFixtures.track(src = "/a.mp3").copy(path = "/home?__trackId=%2Fa.mp3")
|
|
35
|
+
assertEquals("/home?__trackId=%2Fa.mp3", TrackFactory.toMedia3(withPath).mediaId)
|
|
36
36
|
|
|
37
37
|
val srcOnly = TestFixtures.track(src = "/a.mp3")
|
|
38
38
|
assertEquals("/a.mp3", TrackFactory.toMedia3(srcOnly).mediaId)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
@Test
|
|
42
|
-
fun `browsable-only track keeps
|
|
42
|
+
fun `browsable-only track keeps path as mediaId even with an id`() {
|
|
43
43
|
// Navigation parentIds must stay resolvable paths, so a container's id is
|
|
44
44
|
// never its mediaId.
|
|
45
|
-
val track = TestFixtures.browseTrack(
|
|
45
|
+
val track = TestFixtures.browseTrack(path = "/stations").copy(id = "abc123")
|
|
46
46
|
assertEquals("/stations", TrackFactory.toMedia3(track).mediaId)
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -48,9 +48,9 @@ struct BrowserConfig {
|
|
|
48
48
|
let carPlayLoadingTitle: String?
|
|
49
49
|
|
|
50
50
|
/// Resolves a browse path for the CarPlay Now Playing album line when the
|
|
51
|
-
/// active track has no
|
|
51
|
+
/// active track has no albumPath. Invoked on track changes (not at tap) so
|
|
52
52
|
/// the album line only becomes tappable when a destination exists.
|
|
53
|
-
let
|
|
53
|
+
let resolveAlbumPath: ((_ track: Track) -> Promise<String?>)?
|
|
54
54
|
|
|
55
55
|
/// Custom handler for track load events (overrides default load behavior)
|
|
56
56
|
let handleTrackLoad: ((_ event: TrackLoadEvent) -> Promise<Promise<Void>>)?
|
|
@@ -72,7 +72,7 @@ struct BrowserConfig {
|
|
|
72
72
|
handleTrackLoad: ((_ event: TrackLoadEvent) -> Promise<Promise<Void>>)? = nil,
|
|
73
73
|
androidControllerOfflineError: Bool = true,
|
|
74
74
|
carPlayLoadingTitle: String? = nil,
|
|
75
|
-
|
|
75
|
+
resolveAlbumPath: ((_ track: Track) -> Promise<String?>)? = nil,
|
|
76
76
|
formatNavigationError: ((_ params: FormatNavigationErrorParams) -> Promise<FormattedNavigationError?>)? = nil,
|
|
77
77
|
) {
|
|
78
78
|
self.request = request
|
|
@@ -87,7 +87,7 @@ struct BrowserConfig {
|
|
|
87
87
|
self.handleTrackLoad = handleTrackLoad
|
|
88
88
|
self.androidControllerOfflineError = androidControllerOfflineError
|
|
89
89
|
self.carPlayLoadingTitle = carPlayLoadingTitle
|
|
90
|
-
self.
|
|
90
|
+
self.resolveAlbumPath = resolveAlbumPath
|
|
91
91
|
self.formatNavigationError = formatNavigationError
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -105,10 +105,31 @@ struct BrowserConfig {
|
|
|
105
105
|
handleTrackLoad = config.handleTrackLoad
|
|
106
106
|
androidControllerOfflineError = config.androidControllerOfflineError ?? true
|
|
107
107
|
carPlayLoadingTitle = config.carPlayLoadingTitle
|
|
108
|
-
|
|
108
|
+
resolveAlbumPath = config.resolveAlbumPath
|
|
109
109
|
formatNavigationError = config.formatNavigationError
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
/// A copy of this config with every JS-provided function dropped — at every
|
|
113
|
+
/// level, not just the top; each nested type's strip below nils its own —
|
|
114
|
+
/// keeping the static fields (URLs, headers, routes' shape). Makes the config
|
|
115
|
+
/// safe to hold after its JS runtime is gone: callers take their
|
|
116
|
+
/// static/built-in fallback paths (a callback-only route degrades to its
|
|
117
|
+
/// no-content path). See `HybridAudioBrowser.dropStaleJSCallbacks` for why
|
|
118
|
+
/// invoking a stale callback is fatal.
|
|
119
|
+
func strippingJSCallbacks() -> BrowserConfig {
|
|
120
|
+
BrowserConfig(
|
|
121
|
+
request: request?.strippingJSCallbacks(),
|
|
122
|
+
browse: browse?.strippingJSCallbacks(),
|
|
123
|
+
media: media?.strippingJSCallbacks(),
|
|
124
|
+
artwork: artwork?.strippingJSCallbacks(),
|
|
125
|
+
nowPlayingArtwork: nowPlayingArtwork?.strippingJSCallbacks(),
|
|
126
|
+
routes: routes?.map { $0.strippingJSCallbacks() },
|
|
127
|
+
singleTrack: singleTrack,
|
|
128
|
+
androidControllerOfflineError: androidControllerOfflineError,
|
|
129
|
+
carPlayLoadingTitle: carPlayLoadingTitle,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
112
133
|
// MARK: - Track Load Handler
|
|
113
134
|
|
|
114
135
|
private static let logger = Logger(subsystem: "com.audiobrowser", category: "BrowserConfig")
|
|
@@ -139,3 +160,79 @@ struct BrowserConfig {
|
|
|
139
160
|
// MARK: - NativeRouteEntry + RouteEntry conformance
|
|
140
161
|
|
|
141
162
|
extension NativeRouteEntry: RouteEntry {}
|
|
163
|
+
|
|
164
|
+
// MARK: - Deep JS-callback stripping
|
|
165
|
+
|
|
166
|
+
// The generated structs are get-only from Swift, so each strip rebuilds the
|
|
167
|
+
// value through the memberwise initializer with the function fields nil'd.
|
|
168
|
+
|
|
169
|
+
private extension TransformableRequestConfig {
|
|
170
|
+
func strippingJSCallbacks() -> TransformableRequestConfig {
|
|
171
|
+
TransformableRequestConfig(
|
|
172
|
+
transform: nil,
|
|
173
|
+
transformSync: nil,
|
|
174
|
+
method: method,
|
|
175
|
+
path: path,
|
|
176
|
+
baseUrl: baseUrl,
|
|
177
|
+
headers: headers,
|
|
178
|
+
query: query,
|
|
179
|
+
body: body,
|
|
180
|
+
contentType: contentType,
|
|
181
|
+
userAgent: userAgent,
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private extension MediaRequestConfig {
|
|
187
|
+
func strippingJSCallbacks() -> MediaRequestConfig {
|
|
188
|
+
MediaRequestConfig(
|
|
189
|
+
resolve: nil,
|
|
190
|
+
resolveSync: nil,
|
|
191
|
+
transform: nil,
|
|
192
|
+
transformSync: nil,
|
|
193
|
+
method: method,
|
|
194
|
+
path: path,
|
|
195
|
+
baseUrl: baseUrl,
|
|
196
|
+
headers: headers,
|
|
197
|
+
query: query,
|
|
198
|
+
body: body,
|
|
199
|
+
contentType: contentType,
|
|
200
|
+
userAgent: userAgent,
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private extension ArtworkRequestConfig {
|
|
206
|
+
func strippingJSCallbacks() -> ArtworkRequestConfig {
|
|
207
|
+
ArtworkRequestConfig(
|
|
208
|
+
resolve: nil,
|
|
209
|
+
resolveSync: nil,
|
|
210
|
+
transform: nil,
|
|
211
|
+
transformSync: nil,
|
|
212
|
+
imageQueryParams: imageQueryParams,
|
|
213
|
+
method: method,
|
|
214
|
+
path: path,
|
|
215
|
+
baseUrl: baseUrl,
|
|
216
|
+
headers: headers,
|
|
217
|
+
query: query,
|
|
218
|
+
body: body,
|
|
219
|
+
contentType: contentType,
|
|
220
|
+
userAgent: userAgent,
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private extension NativeRouteEntry {
|
|
226
|
+
func strippingJSCallbacks() -> NativeRouteEntry {
|
|
227
|
+
NativeRouteEntry(
|
|
228
|
+
path: path,
|
|
229
|
+
browseCallback: nil,
|
|
230
|
+
browseConfig: browseConfig?.strippingJSCallbacks(),
|
|
231
|
+
browseStatic: browseStatic,
|
|
232
|
+
searchCallback: nil,
|
|
233
|
+
searchConfig: searchConfig?.strippingJSCallbacks(),
|
|
234
|
+
media: media?.strippingJSCallbacks(),
|
|
235
|
+
artwork: artwork?.strippingJSCallbacks(),
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -262,10 +262,14 @@ extension BrowserManager {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
// Substitute the `{id}` template token with the track's id across path/query/header values.
|
|
265
|
-
// (Configs without the token are unaffected — e.g. browse artwork.)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
265
|
+
// (Configs without the token are unaffected — e.g. browse artwork.) A track without a
|
|
266
|
+
// non-blank id leaves the token LITERALLY in place — the request then visibly 404s, which
|
|
267
|
+
// is intended (self-describing error trail) — with a single warning logged below once the
|
|
268
|
+
// URL is built.
|
|
269
|
+
mergedConfig = substituteTrackId(in: mergedConfig, id: track.id)
|
|
270
|
+
let hasUnfilledIdToken = TrackIdTemplate.containsToken(mergedConfig.path)
|
|
271
|
+
|| TrackIdTemplate.containsToken(mergedConfig.query)
|
|
272
|
+
|| TrackIdTemplate.containsToken(mergedConfig.headers)
|
|
269
273
|
|
|
270
274
|
// Build final URL - if no path after merging, no artwork to transform
|
|
271
275
|
guard mergedConfig.path != nil else {
|
|
@@ -280,6 +284,10 @@ extension BrowserManager {
|
|
|
280
284
|
headers["User-Agent"] = userAgent
|
|
281
285
|
}
|
|
282
286
|
|
|
287
|
+
if hasUnfilledIdToken {
|
|
288
|
+
logger.warning("nowPlayingArtwork: track has no id — '{id}' left unfilled in \(uri)")
|
|
289
|
+
}
|
|
290
|
+
|
|
283
291
|
logger.debug("Artwork URL transformed: \(track.artwork?.url ?? "nil") -> \(uri)")
|
|
284
292
|
|
|
285
293
|
return ImageSource(
|
|
@@ -315,18 +323,14 @@ extension BrowserManager {
|
|
|
315
323
|
|
|
316
324
|
/// Replaces the `{id}` token with the track id in a request config's path, query values, and
|
|
317
325
|
/// header values. Used so a `nowPlayingArtwork` like `{ path: "/artwork/{id}" }` resolves.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
guard let d else { return nil }
|
|
322
|
-
return d.mapValues { $0.replacingOccurrences(of: "{id}", with: id) }
|
|
323
|
-
}
|
|
324
|
-
return RequestConfig(
|
|
326
|
+
/// A nil/blank id leaves the values unchanged (token left literal — see TrackIdTemplate).
|
|
327
|
+
private func substituteTrackId(in config: RequestConfig, id: String?) -> RequestConfig {
|
|
328
|
+
RequestConfig(
|
|
325
329
|
method: config.method,
|
|
326
|
-
path:
|
|
330
|
+
path: TrackIdTemplate.substitute(config.path, id: id),
|
|
327
331
|
baseUrl: config.baseUrl,
|
|
328
|
-
headers:
|
|
329
|
-
query:
|
|
332
|
+
headers: TrackIdTemplate.substitute(config.headers, id: id),
|
|
333
|
+
query: TrackIdTemplate.substitute(config.query, id: id),
|
|
330
334
|
body: config.body,
|
|
331
335
|
contentType: config.contentType,
|
|
332
336
|
userAgent: config.userAgent,
|