react-native-audio-browser 0.2.0-next.2 → 0.2.0-next.20
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 +35 -21
- package/android/build.gradle +35 -12
- package/android/gradle.properties +2 -2
- package/android/src/main/AndroidManifest.xml +3 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +389 -164
- package/android/src/main/java/com/audiobrowser/AudioBrowserLifecycleModule.kt +38 -0
- package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +18 -2
- package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +315 -213
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +24 -5
- package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +97 -58
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +70 -29
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +20 -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/http/HttpClient.kt +20 -3
- package/android/src/main/java/com/audiobrowser/model/IcyTitle.kt +24 -0
- package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +10 -6
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +12 -12
- package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +111 -58
- package/android/src/main/java/com/audiobrowser/player/CrossOriginHeaderStripper.kt +77 -0
- package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +28 -10
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +509 -230
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +218 -252
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -25
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +113 -78
- package/android/src/main/java/com/audiobrowser/player/Player.kt +81 -38
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
- package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +1 -1
- package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +2 -1
- package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +67 -11
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +168 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +58 -14
- package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +35 -7
- package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +278 -59
- package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +7 -9
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +56 -40
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +56 -52
- package/android/src/main/res/values/strings.xml +25 -5
- package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +119 -33
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +15 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +346 -8
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
- package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +3 -2
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -55
- package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -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/model/IcyTitleTest.kt +49 -0
- package/android/src/test/java/com/audiobrowser/player/BrowseTilesTest.kt +385 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +101 -58
- package/android/src/test/java/com/audiobrowser/player/CrossOriginHeaderStripperTest.kt +94 -0
- package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
- package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +3 -4
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +3 -3
- package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +197 -7
- package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +325 -4
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherCallSiteTest.kt +322 -0
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +83 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +162 -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 +230 -205
- package/ios/Browser/BrowserPathHelper.swift +44 -44
- package/ios/Browser/InertStyleDiagnostic.swift +279 -0
- package/ios/Browser/JsonModels.swift +264 -106
- package/ios/Browser/SearchDrillIn.swift +7 -7
- package/ios/Browser/SectionPresentation.swift +64 -0
- package/ios/Browser/SectionScope.swift +58 -24
- package/ios/Browser/StyleResolver.swift +58 -0
- package/ios/Browser/TabBarEntries.swift +9 -3
- package/ios/Browser/TrackIdTemplate.swift +37 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
- package/ios/CarPlay/CarPlayController.swift +227 -85
- package/ios/CarPlay/CarPlayImageLoader.swift +42 -20
- package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
- package/ios/CarPlay/RNABMediaAddHandler.swift +6 -6
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +2 -2
- package/ios/CarPlay/RNABMediaIntentHandler.swift +16 -16
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
- package/ios/Extension/ResolvedTrack+Copying.swift +13 -15
- package/ios/Extension/Section+Copying.swift +29 -0
- package/ios/Extension/String+IcyCharset.swift +21 -0
- package/ios/Extension/TimedMetadata+AVFoundation.swift +6 -4
- package/ios/Extension/Track+Copying.swift +8 -12
- package/ios/Extension/Track+Identity.swift +18 -0
- package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
- package/ios/Http/HttpClient.swift +8 -2
- package/ios/HybridAudioBrowser.swift +253 -100
- package/ios/Model/NitroTypeStubs.swift +125 -23
- package/ios/Model/PlayerUpdateOptions.swift +7 -0
- package/ios/Model/RemoteCommand.swift +18 -0
- package/ios/Model/TrackPlayerError.swift +2 -2
- package/ios/Option/SessionCategories.swift +3 -2
- package/ios/Player/MediaIntentCriteria.swift +17 -9
- package/ios/Player/MediaLoader.swift +33 -9
- package/ios/Player/NowPlayingUpdater.swift +54 -6
- package/ios/Player/PlaybackCoordinator.swift +13 -6
- package/ios/Player/PlaybackErrorHandler.swift +4 -4
- package/ios/Player/PlaybackTimer.swift +8 -2
- package/ios/Player/QueueManager.swift +46 -13
- package/ios/Player/RetryManager.swift +80 -18
- package/ios/Player/SeekCompletionHandler.swift +3 -1
- package/ios/Player/ShuffleOrder.swift +33 -0
- package/ios/Player/SleepTimerHandling.swift +13 -0
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +62 -24
- package/ios/Tests/BrowserPathHelperTests.swift +50 -20
- package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
- package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
- package/ios/Tests/JsonModelsTests.swift +150 -1
- package/ios/Tests/LRUCacheTests.swift +111 -0
- package/ios/Tests/LoadSeekCoordinatorTests.swift +3 -1
- package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
- package/ios/Tests/PlaybackCoordinatorTests.swift +1 -1
- package/ios/Tests/PlaybackErrorHandlerTests.swift +3 -1
- package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
- package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
- package/ios/Tests/QueueManagerTests.swift +122 -10
- package/ios/Tests/RemoteCommandControllerTests.swift +172 -0
- package/ios/Tests/RemoteCommandTests.swift +67 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +39 -35
- package/ios/Tests/RetryManagerTests.swift +89 -0
- package/ios/Tests/SearchDrillInTests.swift +16 -16
- package/ios/Tests/SectionPresentationTests.swift +90 -0
- package/ios/Tests/SectionScopeTests.swift +87 -55
- package/ios/Tests/StyleResolverTests.swift +127 -0
- package/ios/Tests/Support/TestTypes.swift +8 -1
- package/ios/Tests/TabBarEntriesTests.swift +19 -19
- package/ios/Tests/TimedMetadataAVFoundationTests.swift +42 -0
- package/ios/Tests/TrackIdTemplateTests.swift +67 -0
- package/ios/Tests/TrackIdentityTests.swift +20 -0
- package/ios/Tests/TrackSelectorTests.swift +168 -33
- package/ios/TrackPlayer.swift +63 -35
- package/ios/TrackPlayerCallbacks.swift +5 -38
- package/ios/TrackSelector.swift +45 -30
- package/ios/Util/ArtworkImageFetcher.swift +16 -1
- package/ios/Util/LRUCache.swift +4 -1
- package/ios/Util/NetworkMonitor.swift +43 -26
- package/ios/Util/SFSymbolRenderer.swift +24 -2
- package/ios/Util/SVGProcessor.swift +9 -2
- package/lib/commonjs/AudioBrowser.js +8 -8
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/CLAUDE.md +52 -0
- package/lib/commonjs/features/browser-config.js +12 -8
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/browser.js +11 -3
- package/lib/commonjs/features/browser.js.map +1 -1
- package/lib/commonjs/features/carConnection.js +1 -1
- package/lib/commonjs/features/equalizer.js +1 -1
- package/lib/commonjs/features/errors.js +3 -3
- package/lib/commonjs/features/favorites.js +14 -11
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/gate.js +16 -1
- package/lib/commonjs/features/gate.js.map +1 -1
- package/lib/commonjs/features/metadata.js +20 -3
- package/lib/commonjs/features/metadata.js.map +1 -1
- package/lib/commonjs/features/network.js +1 -1
- package/lib/commonjs/features/nowPlaying.js +1 -1
- package/lib/commonjs/features/output.js +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/playWhenReady.js +1 -1
- package/lib/commonjs/features/playback/playing.js +1 -1
- package/lib/commonjs/features/playback/progress.js +3 -2
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/rate.js +8 -1
- package/lib/commonjs/features/playback/rate.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +3 -4
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -6
- package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/commonjs/features/playback/volume.js +1 -1
- package/lib/commonjs/features/player/options.js +48 -34
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +18 -9
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +23 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -0
- package/lib/commonjs/features/queue/activeTrack.js +15 -4
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +13 -6
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/features/queue/repeatMode.js +1 -1
- package/lib/commonjs/features/queue/shuffle.js +1 -1
- package/lib/commonjs/features/remoteControls.js +13 -92
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/features/sleepTimer.js +13 -1
- package/lib/commonjs/features/sleepTimer.js.map +1 -1
- package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
- package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
- package/lib/commonjs/utils/LazyNativeEmitter.js +28 -14
- 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 +200 -75
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/Player.js +254 -39
- package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +175 -100
- 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/SearchManager.js +18 -23
- package/lib/commonjs/web/browser/SearchManager.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/http/RequestConfigBuilder.js +110 -27
- package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
- package/lib/commonjs/web/http/followMediaRedirect.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 +2 -1
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/CLAUDE.md +52 -0
- package/lib/module/features/browser-config.js +12 -8
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/browser.js +11 -3
- package/lib/module/features/browser.js.map +1 -1
- package/lib/module/features/carConnection.js +1 -1
- package/lib/module/features/equalizer.js +1 -1
- package/lib/module/features/errors.js +3 -3
- package/lib/module/features/favorites.js +14 -11
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/gate.js +16 -1
- package/lib/module/features/gate.js.map +1 -1
- package/lib/module/features/metadata.js +20 -3
- package/lib/module/features/metadata.js.map +1 -1
- package/lib/module/features/network.js +1 -1
- package/lib/module/features/nowPlaying.js +1 -1
- package/lib/module/features/output.js +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/playWhenReady.js +1 -1
- package/lib/module/features/playback/playing.js +1 -1
- package/lib/module/features/playback/progress.js +3 -2
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/rate.js +8 -1
- package/lib/module/features/playback/rate.js.map +1 -1
- package/lib/module/features/playback/state.js +3 -4
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/playback/trackPlaybackTime.js +0 -5
- package/lib/module/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/module/features/playback/volume.js +1 -1
- package/lib/module/features/player/options.js +47 -32
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +17 -8
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +19 -0
- package/lib/module/features/player/validateOptions.js.map +1 -0
- package/lib/module/features/queue/activeTrack.js +15 -4
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +13 -6
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/features/queue/repeatMode.js +1 -1
- package/lib/module/features/queue/shuffle.js +1 -1
- package/lib/module/features/remoteControls.js +13 -92
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/features/sleepTimer.js +13 -1
- package/lib/module/features/sleepTimer.js.map +1 -1
- package/lib/module/test-utils/reactNativeStub.js +58 -0
- package/lib/module/test-utils/reactNativeStub.js.map +1 -0
- package/lib/module/utils/LazyNativeEmitter.js +28 -14
- 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 +200 -75
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
- package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/module/web/TrackPlayer/Player.js +254 -39
- package/lib/module/web/TrackPlayer/Player.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +175 -100
- 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/SearchManager.js +18 -23
- package/lib/module/web/browser/SearchManager.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/http/RequestConfigBuilder.js +110 -27
- package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/module/web/http/followMediaRedirect.js +215 -0
- package/lib/module/web/http/followMediaRedirect.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/package.json +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts +1 -1
- 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 +10 -2
- package/lib/typescript/src/features/browser.d.ts.map +1 -1
- package/lib/typescript/src/features/carConnection.d.ts +1 -1
- package/lib/typescript/src/features/equalizer.d.ts +1 -1
- package/lib/typescript/src/features/errors.d.ts +3 -3
- package/lib/typescript/src/features/favorites.d.ts +14 -11
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/gate.d.ts +18 -2
- package/lib/typescript/src/features/gate.d.ts.map +1 -1
- package/lib/typescript/src/features/metadata.d.ts +20 -3
- package/lib/typescript/src/features/metadata.d.ts.map +1 -1
- package/lib/typescript/src/features/network.d.ts +1 -1
- package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
- package/lib/typescript/src/features/output.d.ts +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/playWhenReady.d.ts +1 -1
- package/lib/typescript/src/features/playback/playing.d.ts +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/rate.d.ts +8 -1
- package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +3 -4
- 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/playback/volume.d.ts +1 -1
- package/lib/typescript/src/features/player/options.d.ts +108 -56
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts +14 -4
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +2 -0
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
- package/lib/typescript/src/features/queue/activeTrack.d.ts +9 -2
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +7 -4
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
- package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
- package/lib/typescript/src/features/remoteControls.d.ts +23 -22
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
- package/lib/typescript/src/types/browser-native.d.ts +3 -2
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +380 -126
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +82 -57
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +19 -16
- 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 +1 -2
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -10
- package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
- package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
- package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__string__.hpp +102 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +16 -4
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +7 -10
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
- package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +45 -25
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +36 -11
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +15 -12
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +7 -10
- package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -10
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +7 -10
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
- package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +19 -19
- package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +57 -49
- package/nitrogen/generated/android/c++/JSection.hpp +116 -0
- package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
- package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +25 -46
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -3
- 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/CardImage.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +8 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -3
- 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/{NotificationButton.kt → RemoteButton.kt} +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +15 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
- 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 -12
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +9 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +244 -133
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +31 -16
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +33 -18
- package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -12
- package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
- package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
- package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
- package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +2 -2
- package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
- package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +52 -5
- package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -12
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
- package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +6 -6
- package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +62 -73
- package/nitrogen/generated/ios/swift/Section.swift +82 -0
- package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
- package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +28 -63
- package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
- package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
- package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -8
- 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++/CardImage.hpp +76 -0
- package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +10 -6
- package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -8
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
- package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +19 -19
- package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +33 -37
- package/nitrogen/generated/shared/c++/Section.hpp +102 -0
- package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
- package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +18 -30
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
- package/package.json +24 -11
- package/src/AudioBrowser.ts +2 -1
- package/src/CLAUDE.md +52 -0
- package/src/features/battery.test.ts +79 -0
- package/src/features/browser-config.test.ts +21 -6
- package/src/features/browser-config.ts +12 -8
- package/src/features/browser.test.ts +1 -1
- package/src/features/browser.ts +11 -3
- package/src/features/carConnection.ts +1 -1
- package/src/features/equalizer.ts +1 -1
- package/src/features/errors.ts +3 -3
- package/src/features/favorites.ts +14 -11
- package/src/features/gate.ts +16 -1
- package/src/features/hook-wiring.test.ts +302 -0
- package/src/features/metadata.ts +20 -3
- package/src/features/network.ts +1 -1
- package/src/features/nowPlaying.ts +1 -1
- package/src/features/output.ts +1 -1
- package/src/features/playback/controls.ts +4 -2
- package/src/features/playback/playWhenReady.ts +1 -1
- package/src/features/playback/playing.ts +1 -1
- package/src/features/playback/progress.test.ts +169 -0
- package/src/features/playback/progress.ts +3 -2
- package/src/features/playback/rate.ts +8 -1
- package/src/features/playback/state.ts +3 -4
- package/src/features/playback/trackPlaybackTime.test.ts +16 -6
- package/src/features/playback/trackPlaybackTime.ts +1 -6
- package/src/features/playback/volume.ts +1 -1
- package/src/features/player/options.ts +126 -76
- package/src/features/player/setup.test.ts +24 -7
- package/src/features/player/setup.ts +22 -10
- package/src/features/player/validateOptions.ts +21 -0
- package/src/features/queue/activeTrack.ts +15 -4
- package/src/features/queue/queue.ts +13 -6
- package/src/features/queue/repeatMode.ts +1 -1
- package/src/features/queue/shuffle.ts +1 -1
- package/src/features/remoteControls.ts +13 -92
- package/src/features/sleepTimer.test.ts +172 -0
- package/src/features/sleepTimer.ts +9 -1
- package/src/nitro-stub-drift.test.ts +277 -0
- package/src/public-surface.test.ts +147 -0
- package/src/specs/audio-browser.nitro.ts +5 -4
- package/src/style-resolution.test.ts +210 -0
- package/src/test-utils/reactNativeStub.ts +60 -0
- package/src/types/browser-native.ts +7 -3
- package/src/types/browser-nodes.ts +396 -134
- package/src/types/browser.ts +85 -59
- package/src/utils/LazyNativeEmitter.ts +28 -15
- package/src/utils/NativeUpdatedValue.ts +29 -13
- package/src/utils/getTrackIdentity.ts +20 -0
- package/src/utils/useDebug.test.ts +182 -0
- package/src/utils/useNativeUpdatedValue.test.ts +103 -0
- package/src/utils/useNativeValueRefreshedBy.ts +38 -0
- package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
- package/src/web/NativeAudioBrowser.ts +239 -82
- package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
- package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
- package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
- package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
- package/src/web/TrackPlayer/Player.load.test.ts +137 -1
- package/src/web/TrackPlayer/Player.ts +284 -37
- package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
- package/src/web/TrackPlayer/QueueManager.ts +14 -0
- package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
- package/src/web/__tests__/lifecycle.test.ts +345 -0
- package/src/web/__tests__/lifecycleHarness.ts +351 -0
- package/src/web/__tests__/loadEdgeCases.test.ts +246 -0
- package/src/web/__tests__/loadSupersession.test.ts +175 -0
- package/src/web/__tests__/retry.test.ts +169 -0
- package/src/web/browser/BrowserManager.ts +209 -111
- package/src/web/browser/FavoriteManager.test.ts +84 -0
- package/src/web/browser/FavoriteManager.ts +28 -23
- package/src/web/browser/SearchManager.test.ts +75 -0
- package/src/web/browser/SearchManager.ts +25 -28
- package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
- package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
- package/src/web/browser/__tests__/search-artwork.test.ts +46 -0
- package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
- 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/http/RequestConfigBuilder.test.ts +241 -1
- package/src/web/http/RequestConfigBuilder.ts +140 -33
- package/src/web/http/followMediaRedirect.test.ts +289 -0
- package/src/web/http/followMediaRedirect.ts +253 -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/website/guide/android-auto.md +114 -0
- package/website/guide/android-certificates.md +409 -0
- package/website/guide/artwork.md +255 -0
- package/website/guide/audio-output.md +135 -0
- package/website/guide/automotive.md +86 -0
- package/website/guide/basic-usage.md +151 -0
- package/website/guide/battery.md +115 -0
- package/website/guide/browser.md +581 -0
- package/website/guide/carplay.md +620 -0
- package/website/guide/configuration.md +173 -0
- package/website/guide/equalizer.md +231 -0
- package/website/guide/errors.md +310 -0
- package/website/guide/favorites.md +154 -0
- package/website/guide/gate.md +143 -0
- package/website/guide/getting-started.md +46 -0
- package/website/guide/hooks.md +238 -0
- package/website/guide/live-streams.md +125 -0
- package/website/guide/metadata.md +239 -0
- package/website/guide/migrating-from-track-player.md +244 -0
- package/website/guide/native-callback-fetch.md +127 -0
- package/website/guide/network.md +86 -0
- package/website/guide/now-playing.md +175 -0
- package/website/guide/playback.md +328 -0
- package/website/guide/queue.md +300 -0
- package/website/guide/remote-controls.md +256 -0
- package/website/guide/search.md +255 -0
- package/website/guide/sleep-timer.md +268 -0
- package/website/guide/track.md +247 -0
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/ios/Model/MediaURL.swift +0 -67
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
- package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
- 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/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
- 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/NotificationButtonLayout.swift +0 -92
- 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
- package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
package com.audiobrowser.browser
|
|
2
2
|
|
|
3
|
-
import com.audiobrowser.util.url
|
|
4
|
-
|
|
5
3
|
import com.audiobrowser.http.RequestConfigBuilder
|
|
4
|
+
import com.audiobrowser.util.url
|
|
6
5
|
import com.margelo.nitro.audiobrowser.ArtworkRequestConfig
|
|
7
6
|
import com.margelo.nitro.audiobrowser.ImageContext
|
|
8
7
|
import com.margelo.nitro.audiobrowser.ImageQueryParams
|
|
@@ -177,15 +176,22 @@ suspend fun BrowserManager.resolveArtworkUrl(
|
|
|
177
176
|
)
|
|
178
177
|
}
|
|
179
178
|
|
|
180
|
-
// `{id}` token substitution
|
|
179
|
+
// `{id}` token substitution. A track without a non-blank id leaves the token LITERALLY in
|
|
180
|
+
// place — the request then visibly 404s, which is intended (self-describing error trail) —
|
|
181
|
+
// with a single warning logged below once the URL is built. Mirrors iOS substituteTrackId.
|
|
181
182
|
track.id
|
|
182
183
|
?.takeIf { it.isNotEmpty() }
|
|
183
184
|
?.let { transformedConfig = substituteTrackId(transformedConfig, it) }
|
|
185
|
+
val hasUnfilledIdToken = configContainsIdToken(transformedConfig)
|
|
184
186
|
|
|
185
187
|
val uri = RequestConfigBuilder.buildUrl(transformedConfig)
|
|
186
188
|
// If URI is empty, there's no valid artwork path
|
|
187
189
|
if (uri.isEmpty()) return null
|
|
188
190
|
|
|
191
|
+
if (hasUnfilledIdToken) {
|
|
192
|
+
Timber.w("nowPlayingArtwork: track has no id — '{id}' left unfilled in %s", uri)
|
|
193
|
+
}
|
|
194
|
+
|
|
189
195
|
ImageSource(
|
|
190
196
|
uri = uri,
|
|
191
197
|
method = transformedConfig.method,
|
|
@@ -266,15 +272,18 @@ private fun applyImageQueryParams(
|
|
|
266
272
|
return config.copy(query = (config.query ?: emptyMap()) + contextQuery)
|
|
267
273
|
}
|
|
268
274
|
|
|
275
|
+
/** The `{id}` template token — see [substituteTrackId] / [configContainsIdToken]. */
|
|
276
|
+
private const val ID_TOKEN = "{id}"
|
|
277
|
+
|
|
269
278
|
/**
|
|
270
279
|
* Replaces the `{id}` token with the track id in a request config's path, query values, and header
|
|
271
280
|
* values. Used so a `nowPlayingArtwork` like `{ path: "/artwork/{id}" }` resolves. Configs without
|
|
272
281
|
* the token are returned unchanged. Mirrors the iOS `substituteTrackId` helper.
|
|
273
282
|
*/
|
|
274
283
|
private fun substituteTrackId(config: RequestConfig, id: String): RequestConfig {
|
|
275
|
-
fun sub(s: String?): String? = s?.replace(
|
|
284
|
+
fun sub(s: String?): String? = s?.replace(ID_TOKEN, id)
|
|
276
285
|
fun subMap(m: Map<String, String>?): Map<String, String>? =
|
|
277
|
-
m?.mapValues { (_, value) -> value.replace(
|
|
286
|
+
m?.mapValues { (_, value) -> value.replace(ID_TOKEN, id) }
|
|
278
287
|
return config.copy(
|
|
279
288
|
path = sub(config.path),
|
|
280
289
|
headers = subMap(config.headers),
|
|
@@ -282,6 +291,16 @@ private fun substituteTrackId(config: RequestConfig, id: String): RequestConfig
|
|
|
282
291
|
)
|
|
283
292
|
}
|
|
284
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Whether the config still carries an unfilled `{id}` token in its path, query values, or header
|
|
296
|
+
* values (checked AFTER substitution, so it matches exactly when the track had no non-blank id and
|
|
297
|
+
* the config uses the token). Mirrors iOS `TrackIdTemplate.containsToken`.
|
|
298
|
+
*/
|
|
299
|
+
private fun configContainsIdToken(config: RequestConfig): Boolean =
|
|
300
|
+
config.path?.contains(ID_TOKEN) == true ||
|
|
301
|
+
config.query?.values?.any { it.contains(ID_TOKEN) } == true ||
|
|
302
|
+
config.headers?.values?.any { it.contains(ID_TOKEN) } == true
|
|
303
|
+
|
|
285
304
|
/** Builds a headers map, merging explicit headers with userAgent and contentType. */
|
|
286
305
|
private fun buildHeadersMap(
|
|
287
306
|
headers: Map<String, String>?,
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
package com.audiobrowser.browser
|
|
2
|
+
|
|
3
|
+
import com.audiobrowser.BuildConfig
|
|
4
|
+
import com.audiobrowser.util.toWireString
|
|
5
|
+
import com.margelo.nitro.audiobrowser.GridTile
|
|
6
|
+
import com.margelo.nitro.audiobrowser.Section
|
|
7
|
+
import com.margelo.nitro.audiobrowser.SectionStyle
|
|
8
|
+
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
9
|
+
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
10
|
+
import timber.log.Timber
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Dev diagnostic (ADR 0011): the declarations on a resolved page that can never render.
|
|
14
|
+
*
|
|
15
|
+
* The block model deliberately gave up compile-time invalid-combination errors — combinations are
|
|
16
|
+
* not invalid, they are *inert*, the way `grid-template` on a non-grid element is inert in CSS.
|
|
17
|
+
* This is the recovery: at page resolution every declaration its own block makes unreadable is
|
|
18
|
+
* reported once, with the effective context and the fix.
|
|
19
|
+
*
|
|
20
|
+
* The line it draws, and never crosses: only **structural** inertness — a declaration the resolved
|
|
21
|
+
* block itself renders unreadable, on every surface (`gridWrap` outside a grid, `cardTint` with no
|
|
22
|
+
* card treatment in scope). *Surface* inertness is never reported, because declarations are
|
|
23
|
+
* aspirational: `imageShape` on Android Auto, or a card treatment on a pre-26 CarPlay, is intended
|
|
24
|
+
* usage — the renderer drops what it can't draw.
|
|
25
|
+
*
|
|
26
|
+
* Runs where every page passes regardless of its source (static config, route callback, or fetched
|
|
27
|
+
* browse JSON), and only on a cache miss, so a mistake is reported once per resolution rather than
|
|
28
|
+
* once per serve. Mirrors `ios/Browser/InertStyleDiagnostic.swift`.
|
|
29
|
+
*
|
|
30
|
+
* Debug builds only: [warn] is the whole seam, so a release build never even walks the page (the
|
|
31
|
+
* trade is that a page served only to a release build — an internal track, production browse JSON —
|
|
32
|
+
* reports nothing; reproduce it against a debug build).
|
|
33
|
+
*/
|
|
34
|
+
object InertStyleDiagnostic {
|
|
35
|
+
/**
|
|
36
|
+
* Reports every inert declaration on a resolved page. The one gate — the [BuildConfig] check is
|
|
37
|
+
* here rather than at the call sites so callers read unconditionally and the rule set stays free
|
|
38
|
+
* of build-configuration knowledge.
|
|
39
|
+
*/
|
|
40
|
+
fun warn(path: String, pageStyle: SectionStyle?, sections: List<Section>) {
|
|
41
|
+
if (!BuildConfig.DEBUG) return
|
|
42
|
+
findings(path, pageStyle, sections).forEach { Timber.w(it) }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The declarations that carry a structural inertness condition.
|
|
47
|
+
*
|
|
48
|
+
* Two keys of the block are deliberately absent — `display` and `artworkRendering`, dispositioned
|
|
49
|
+
* below instead.
|
|
50
|
+
*/
|
|
51
|
+
private enum class Property(val key: String, val isItemProperty: Boolean) {
|
|
52
|
+
// Container properties resolve by scope override (`section ?? page`) and never reach an item;
|
|
53
|
+
// item properties inherit (`track ?? section ?? page`), so they may also be declared per track.
|
|
54
|
+
GRID_WRAP("gridWrap", isItemProperty = false),
|
|
55
|
+
GRID_TILE("gridTile", isItemProperty = false),
|
|
56
|
+
IMAGE_SHAPE("imageShape", isItemProperty = true),
|
|
57
|
+
ACCESSORY_SYMBOL("accessorySymbol", isItemProperty = true),
|
|
58
|
+
CARD_TINT("cardTint", isItemProperty = true),
|
|
59
|
+
CARD_IMAGE("cardImage", isItemProperty = true);
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Whether a value of this property, resolved into [context], has any rendering to affect. Each
|
|
63
|
+
* branch is the spec's own "inert unless" clause.
|
|
64
|
+
*/
|
|
65
|
+
fun isInert(context: Context): Boolean =
|
|
66
|
+
when (this) {
|
|
67
|
+
GRID_WRAP,
|
|
68
|
+
GRID_TILE -> !context.isGrid
|
|
69
|
+
IMAGE_SHAPE -> !context.isGrid || context.isCard
|
|
70
|
+
ACCESSORY_SYMBOL -> context.isCard
|
|
71
|
+
CARD_TINT,
|
|
72
|
+
CARD_IMAGE -> !context.isCard
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The rule the declaration falls foul of, stated positively. */
|
|
76
|
+
val rule: String
|
|
77
|
+
get() =
|
|
78
|
+
when (this) {
|
|
79
|
+
GRID_WRAP,
|
|
80
|
+
GRID_TILE -> "`$key` renders only in a grid"
|
|
81
|
+
IMAGE_SHAPE -> "only plain and condensed tiles have a shape"
|
|
82
|
+
ACCESSORY_SYMBOL -> "card tiles have no accessory slot"
|
|
83
|
+
CARD_TINT,
|
|
84
|
+
CARD_IMAGE -> "the card properties render only under `gridTile: 'card'`"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
val hint: String
|
|
88
|
+
get() =
|
|
89
|
+
when (this) {
|
|
90
|
+
GRID_WRAP,
|
|
91
|
+
GRID_TILE -> "Declare `display: 'grid'` on the section or the page, or drop `$key`."
|
|
92
|
+
IMAGE_SHAPE -> "Render the items as plain or condensed grid tiles, or drop `imageShape`."
|
|
93
|
+
ACCESSORY_SYMBOL -> "Use `gridTile: 'plain'` or `'condensed'`, or drop `accessorySymbol`."
|
|
94
|
+
CARD_TINT,
|
|
95
|
+
CARD_IMAGE -> "Declare `display: 'grid'` with `gridTile: 'card'`, or drop `$key`."
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
fun declaredIn(style: SectionStyle?): Boolean =
|
|
99
|
+
when (this) {
|
|
100
|
+
GRID_WRAP -> style?.gridWrap != null
|
|
101
|
+
GRID_TILE -> style?.gridTile != null
|
|
102
|
+
IMAGE_SHAPE -> style?.imageShape != null
|
|
103
|
+
ACCESSORY_SYMBOL -> style?.accessorySymbol != null
|
|
104
|
+
CARD_TINT -> style?.cardTint != null
|
|
105
|
+
CARD_IMAGE -> style?.cardImage != null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Nitro flattens the spec's `extends`, so the two blocks share no type — hence two lookups over
|
|
109
|
+
// the same key set rather than one interface.
|
|
110
|
+
fun declaredIn(style: TrackStyle?): Boolean =
|
|
111
|
+
when (this) {
|
|
112
|
+
// Container properties have no Track slot to be declared in.
|
|
113
|
+
GRID_WRAP,
|
|
114
|
+
GRID_TILE -> false
|
|
115
|
+
IMAGE_SHAPE -> style?.imageShape != null
|
|
116
|
+
ACCESSORY_SYMBOL -> style?.accessorySymbol != null
|
|
117
|
+
CARD_TINT -> style?.cardTint != null
|
|
118
|
+
CARD_IMAGE -> style?.cardImage != null
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The rest of the block's keys, dispositioned rather than ruled on: `artworkRendering` has no
|
|
124
|
+
* structural inertness condition (artwork renders in every layout), and `display` is positional —
|
|
125
|
+
* inert only in the one position that opens no page (see [playableDisplayFinding]). Declared here
|
|
126
|
+
* so the spec-level completeness test can hold this file to the whole vocabulary: every key of
|
|
127
|
+
* the block is a rule, always renderable, or positional, and a newly added property is a decision
|
|
128
|
+
* rather than an omission.
|
|
129
|
+
*/
|
|
130
|
+
val ALWAYS_RENDERABLE = listOf("artworkRendering")
|
|
131
|
+
|
|
132
|
+
val POSITIONAL = listOf("display")
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* A section's effective container context: the two container values every inertness condition is
|
|
136
|
+
* stated against.
|
|
137
|
+
*
|
|
138
|
+
* Folded through [StyleResolver] rather than re-deriving the scope override: the diagnostic
|
|
139
|
+
* predicts what a renderer will do with these declarations, so it has to resolve them the way the
|
|
140
|
+
* renderers do.
|
|
141
|
+
*/
|
|
142
|
+
private class Context(section: SectionStyle?, page: SectionStyle?) {
|
|
143
|
+
private val folded = StyleResolver.sectionStyle(section, page)
|
|
144
|
+
val isGrid = folded.display == StyleDisplay.GRID
|
|
145
|
+
val tile = folded.gridTile ?: GridTile.PLAIN
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Whether the card treatment is what this container renders — the `card*` family's condition.
|
|
149
|
+
*/
|
|
150
|
+
val isCard: Boolean
|
|
151
|
+
get() = isGrid && tile == GridTile.CARD
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The effective style, for the message — in the wire spelling the author types ([toWireString]
|
|
155
|
+
* is the decoder's own mapping). `gridTile` is quoted only where a grid gives it meaning:
|
|
156
|
+
* naming it on a list would be advice to fix the wrong property.
|
|
157
|
+
*/
|
|
158
|
+
val described: String
|
|
159
|
+
get() = if (!isGrid) "display 'list'" else "display 'grid', gridTile '${tile.toWireString()}'"
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Every inert declaration on a resolved page, as ready-to-log messages — pure, so the whole rule
|
|
164
|
+
* set is testable off-device; the caller logs.
|
|
165
|
+
*
|
|
166
|
+
* @param path the resolved path, named in every message.
|
|
167
|
+
* @param pageStyle the page's own block (`ResolvedTrack.style`).
|
|
168
|
+
* @param sections the page's normalized sections, blocks *unfolded* — the diagnostic attributes
|
|
169
|
+
* each finding to the level that declared it, so it must see the levels before [StyleResolver]
|
|
170
|
+
* merges them.
|
|
171
|
+
*/
|
|
172
|
+
fun findings(path: String, pageStyle: SectionStyle?, sections: List<Section>): List<String> {
|
|
173
|
+
val findings = mutableListOf<String>()
|
|
174
|
+
// A page-level declaration is judged across the whole page: it is live if it renders in any
|
|
175
|
+
// section it reaches, however many others ignore it.
|
|
176
|
+
val pageReached = mutableSetOf<Property>()
|
|
177
|
+
val pageRendered = mutableSetOf<Property>()
|
|
178
|
+
|
|
179
|
+
sections.forEachIndexed { index, section ->
|
|
180
|
+
val context = Context(section.style, pageStyle)
|
|
181
|
+
val sectionLabel = label(section, index)
|
|
182
|
+
|
|
183
|
+
for (property in Property.entries) {
|
|
184
|
+
val inert = property.isInert(context)
|
|
185
|
+
|
|
186
|
+
if (property.declaredIn(section.style)) {
|
|
187
|
+
if (inert) {
|
|
188
|
+
findings +=
|
|
189
|
+
message(property, path, sectionLabel, "its effective style is ${context.described}")
|
|
190
|
+
}
|
|
191
|
+
} else if (property.declaredIn(pageStyle)) {
|
|
192
|
+
// The section takes the page's value (nothing closer overrode it).
|
|
193
|
+
pageReached += property
|
|
194
|
+
if (!inert) pageRendered += property
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!property.isItemProperty || !inert) continue
|
|
198
|
+
val tracks = section.children.count { property.declaredIn(it.style) }
|
|
199
|
+
if (tracks > 0) {
|
|
200
|
+
findings +=
|
|
201
|
+
message(
|
|
202
|
+
property,
|
|
203
|
+
path,
|
|
204
|
+
"${trackCount(tracks)} in $sectionLabel",
|
|
205
|
+
"the section's effective style is ${context.described}",
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
playableDisplayFinding(section, path, sectionLabel)?.let { findings += it }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (property in Property.entries) {
|
|
214
|
+
if (property in pageReached && property !in pageRendered) {
|
|
215
|
+
findings +=
|
|
216
|
+
message(property, path, "the page block", "no section that inherits it renders one")
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return findings
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* `display` is positional, so it is never inert *in* a container — but on a track that renders
|
|
225
|
+
* playable (`src` wins the rendering) it promises the layout of a page that never opens.
|
|
226
|
+
*/
|
|
227
|
+
private fun playableDisplayFinding(section: Section, path: String, label: String): String? {
|
|
228
|
+
val tracks = section.children.count { it.src != null && it.style?.display != null }
|
|
229
|
+
if (tracks == 0) return null
|
|
230
|
+
return message(
|
|
231
|
+
path = path,
|
|
232
|
+
owner = "${trackCount(tracks)} in $label",
|
|
233
|
+
key = "display",
|
|
234
|
+
reason =
|
|
235
|
+
"`display` is the layout promise for the page a track opens, and a track rendered playable " +
|
|
236
|
+
"opens none",
|
|
237
|
+
hint = "Drop `display`, or give the track a `path` if it should be browsable.",
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private fun message(property: Property, path: String, owner: String, scope: String): String =
|
|
242
|
+
message(
|
|
243
|
+
path = path,
|
|
244
|
+
owner = owner,
|
|
245
|
+
key = property.key,
|
|
246
|
+
reason = "${property.rule}, but $scope",
|
|
247
|
+
hint = property.hint,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* The one message skeleton — every finding names the page, the declaration, who declared it, why
|
|
252
|
+
* it can't render, and what to do about it.
|
|
253
|
+
*/
|
|
254
|
+
private fun message(
|
|
255
|
+
path: String,
|
|
256
|
+
owner: String,
|
|
257
|
+
key: String,
|
|
258
|
+
reason: String,
|
|
259
|
+
hint: String,
|
|
260
|
+
): String =
|
|
261
|
+
"Inert style declaration on page '$path': `style.$key` on $owner never renders — $reason. $hint"
|
|
262
|
+
|
|
263
|
+
private fun trackCount(count: Int): String = "$count ${if (count == 1) "track" else "tracks"}"
|
|
264
|
+
|
|
265
|
+
private fun label(section: Section, index: Int): String =
|
|
266
|
+
section.title?.takeIf { it.isNotEmpty() }?.let { "section '$it'" } ?: "section #${index + 1}"
|
|
267
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
package com.audiobrowser.browser
|
|
2
2
|
|
|
3
|
+
import com.audiobrowser.util.EMPTY_SECTION_STYLE
|
|
4
|
+
import com.audiobrowser.util.EMPTY_TRACK_STYLE
|
|
5
|
+
import com.audiobrowser.util.toArtworkRendering
|
|
6
|
+
import com.audiobrowser.util.toCardImage
|
|
7
|
+
import com.audiobrowser.util.toGridTile
|
|
8
|
+
import com.audiobrowser.util.toImageShape
|
|
9
|
+
import com.audiobrowser.util.toStyleDisplay
|
|
3
10
|
import com.margelo.nitro.audiobrowser.CarPlaySiriListButtonPosition
|
|
4
|
-
import com.margelo.nitro.audiobrowser.ImageRowItem
|
|
5
11
|
import com.margelo.nitro.audiobrowser.ResolvedTrack
|
|
12
|
+
import com.margelo.nitro.audiobrowser.Section
|
|
13
|
+
import com.margelo.nitro.audiobrowser.SectionStyle
|
|
6
14
|
import com.margelo.nitro.audiobrowser.Track
|
|
7
15
|
import com.margelo.nitro.audiobrowser.TrackRequest
|
|
8
16
|
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
9
17
|
import kotlinx.serialization.Serializable
|
|
18
|
+
import kotlinx.serialization.json.JsonElement
|
|
19
|
+
import kotlinx.serialization.json.JsonObject
|
|
20
|
+
import kotlinx.serialization.json.JsonPrimitive
|
|
21
|
+
import kotlinx.serialization.json.booleanOrNull
|
|
10
22
|
|
|
11
23
|
/**
|
|
12
24
|
* JSON serializable models for parsing API responses. These will be converted to Nitro types after
|
|
13
|
-
* parsing.
|
|
25
|
+
* parsing. Legacy `groupTitle`/`imageRow` keys in payloads are simply unknown to these models and
|
|
26
|
+
* decode as ignored dead weight (ADR 0010).
|
|
14
27
|
*/
|
|
15
|
-
@Serializable
|
|
16
|
-
data class JsonImageRowItem(
|
|
17
|
-
val id: String? = null,
|
|
18
|
-
val url: String? = null,
|
|
19
|
-
val src: String? = null,
|
|
20
|
-
val artwork: String? = null,
|
|
21
|
-
val title: String,
|
|
22
|
-
val artist: String? = null,
|
|
23
|
-
val album: String? = null,
|
|
24
|
-
val albumUrl: String? = null,
|
|
25
|
-
val live: Boolean? = null,
|
|
26
|
-
val request: JsonTrackRequest? = null,
|
|
27
|
-
)
|
|
28
28
|
|
|
29
29
|
/** JSON model for a track's per-request HTTP override (identity/auth/signed-url). */
|
|
30
30
|
@Serializable
|
|
@@ -36,60 +36,109 @@ data class JsonTrackRequest(
|
|
|
36
36
|
fun toNitro(): TrackRequest = TrackRequest(userAgent, headers, query)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/** JSON model for a page section (ADR 0010). */
|
|
40
|
+
@Serializable
|
|
41
|
+
data class JsonSection(
|
|
42
|
+
val title: String? = null,
|
|
43
|
+
val style: JsonElement? = null,
|
|
44
|
+
val path: String? = null,
|
|
45
|
+
val children: List<JsonTrack>,
|
|
46
|
+
)
|
|
47
|
+
|
|
39
48
|
@Serializable
|
|
40
49
|
data class JsonResolvedTrack(
|
|
41
50
|
val id: String? = null,
|
|
42
|
-
val
|
|
51
|
+
val path: String,
|
|
43
52
|
val title: String,
|
|
44
53
|
val subtitle: String? = null,
|
|
45
54
|
val icon: String? = null,
|
|
46
55
|
val artwork: JsonArtwork? = null,
|
|
47
56
|
val artist: String? = null,
|
|
48
|
-
val
|
|
57
|
+
val albumPath: String? = null,
|
|
49
58
|
val album: String? = null,
|
|
50
59
|
val description: String? = null,
|
|
51
60
|
val genre: String? = null,
|
|
52
61
|
val duration: Double? = null,
|
|
62
|
+
val sections: List<JsonSection>? = null,
|
|
53
63
|
val children: List<JsonTrack>? = null,
|
|
54
64
|
val src: String? = null,
|
|
55
|
-
val style:
|
|
56
|
-
val
|
|
57
|
-
val groupTitle: String? = null,
|
|
65
|
+
val style: JsonElement? = null,
|
|
66
|
+
val disabled: Boolean? = null,
|
|
58
67
|
val live: Boolean? = null,
|
|
68
|
+
val icyCharset: String? = null,
|
|
59
69
|
val carPlaySiriListButton: String? = null,
|
|
60
70
|
)
|
|
61
71
|
|
|
62
72
|
@Serializable
|
|
63
73
|
data class JsonTrack(
|
|
64
74
|
val id: String? = null,
|
|
65
|
-
val
|
|
75
|
+
val path: String? = null,
|
|
66
76
|
val title: String,
|
|
67
77
|
val subtitle: String? = null,
|
|
68
78
|
val icon: String? = null,
|
|
69
79
|
val artwork: JsonArtwork? = null,
|
|
70
80
|
val artist: String? = null,
|
|
71
|
-
val
|
|
81
|
+
val albumPath: String? = null,
|
|
72
82
|
val album: String? = null,
|
|
73
83
|
val description: String? = null,
|
|
74
84
|
val genre: String? = null,
|
|
75
85
|
val duration: Double? = null,
|
|
76
86
|
val src: String? = null,
|
|
77
87
|
val request: JsonTrackRequest? = null,
|
|
78
|
-
val style:
|
|
79
|
-
val
|
|
80
|
-
val groupTitle: String? = null,
|
|
88
|
+
val style: JsonElement? = null,
|
|
89
|
+
val disabled: Boolean? = null,
|
|
81
90
|
val live: Boolean? = null,
|
|
82
|
-
val
|
|
91
|
+
val icyCharset: String? = null,
|
|
83
92
|
)
|
|
84
93
|
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Convert JSON models to Nitro types.
|
|
96
|
+
*
|
|
97
|
+
* `style` rides the models as a raw [JsonElement] and maps leniently here — tolerant decoding by
|
|
98
|
+
* design (ADR 0011): a stale value (the retired string vocabulary), a wrong-typed field, or an
|
|
99
|
+
* unknown enum value is "no declaration", never a parse failure. A typed field would fail the whole
|
|
100
|
+
* page, since this decoder is otherwise strict.
|
|
101
|
+
*/
|
|
102
|
+
private fun JsonObject.stringField(key: String): String? =
|
|
103
|
+
(get(key) as? JsonPrimitive)?.takeIf { it.isString }?.content
|
|
104
|
+
|
|
105
|
+
/** A strict JSON boolean — a quoted "false" is a wrong-typed field, i.e. no declaration. */
|
|
106
|
+
private fun JsonObject.booleanField(key: String): Boolean? =
|
|
107
|
+
(get(key) as? JsonPrimitive)?.takeIf { !it.isString }?.booleanOrNull
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A block that resolves to no declarations at all — `{}` or all-unknown values — maps to null, so
|
|
111
|
+
* "no declaration" has one shape on every platform (iOS makes the same collapse).
|
|
112
|
+
*/
|
|
113
|
+
private fun JsonElement?.toTrackStyle(): TrackStyle? =
|
|
114
|
+
(this as? JsonObject)?.let { obj ->
|
|
115
|
+
TrackStyle(
|
|
116
|
+
display = obj.stringField("display").toStyleDisplay(),
|
|
117
|
+
artworkRendering = obj.stringField("artworkRendering").toArtworkRendering(),
|
|
118
|
+
imageShape = obj.stringField("imageShape").toImageShape(),
|
|
119
|
+
// 'none' is a legitimate accessory value (the inheritance escape);
|
|
120
|
+
// only emptiness collapses.
|
|
121
|
+
accessorySymbol = obj.stringField("accessorySymbol")?.takeIf { it.isNotEmpty() },
|
|
122
|
+
cardTint = obj.stringField("cardTint")?.takeIf { it.isNotEmpty() },
|
|
123
|
+
cardImage = obj.stringField("cardImage").toCardImage(),
|
|
124
|
+
)
|
|
125
|
+
.takeIf { it != EMPTY_TRACK_STYLE }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private fun JsonElement?.toSectionStyle(): SectionStyle? =
|
|
129
|
+
(this as? JsonObject)?.let { obj ->
|
|
130
|
+
SectionStyle(
|
|
131
|
+
display = obj.stringField("display").toStyleDisplay(),
|
|
132
|
+
artworkRendering = obj.stringField("artworkRendering").toArtworkRendering(),
|
|
133
|
+
imageShape = obj.stringField("imageShape").toImageShape(),
|
|
134
|
+
accessorySymbol = obj.stringField("accessorySymbol")?.takeIf { it.isNotEmpty() },
|
|
135
|
+
cardTint = obj.stringField("cardTint")?.takeIf { it.isNotEmpty() },
|
|
136
|
+
cardImage = obj.stringField("cardImage").toCardImage(),
|
|
137
|
+
gridWrap = obj.booleanField("gridWrap"),
|
|
138
|
+
gridTile = obj.stringField("gridTile").toGridTile(),
|
|
139
|
+
)
|
|
140
|
+
.takeIf { it != EMPTY_SECTION_STYLE }
|
|
91
141
|
}
|
|
92
|
-
}
|
|
93
142
|
|
|
94
143
|
private fun String?.toCarPlaySiriListButtonPosition(): CarPlaySiriListButtonPosition? {
|
|
95
144
|
return when (this?.lowercase()) {
|
|
@@ -99,26 +148,20 @@ private fun String?.toCarPlaySiriListButtonPosition(): CarPlaySiriListButtonPosi
|
|
|
99
148
|
}
|
|
100
149
|
}
|
|
101
150
|
|
|
102
|
-
|
|
103
|
-
return
|
|
104
|
-
id = id,
|
|
105
|
-
url = url,
|
|
106
|
-
src = src,
|
|
107
|
-
artwork = artwork,
|
|
108
|
-
artworkSource = null,
|
|
151
|
+
fun JsonSection.toNitro(): Section {
|
|
152
|
+
return Section(
|
|
109
153
|
title = title,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
live = live,
|
|
114
|
-
request = request?.toNitro(),
|
|
154
|
+
style = style.toSectionStyle(),
|
|
155
|
+
path = path,
|
|
156
|
+
children = children.map { it.toNitro() }.toTypedArray(),
|
|
115
157
|
)
|
|
116
158
|
}
|
|
117
159
|
|
|
118
160
|
fun JsonResolvedTrack.toNitro(): ResolvedTrack {
|
|
119
161
|
return ResolvedTrack(
|
|
120
162
|
id = id,
|
|
121
|
-
|
|
163
|
+
path = path,
|
|
164
|
+
sections = sections?.map { it.toNitro() }?.toTypedArray(),
|
|
122
165
|
children = children?.map { it.toNitro() }?.toTypedArray(),
|
|
123
166
|
carPlaySiriListButton = carPlaySiriListButton.toCarPlaySiriListButtonPosition(),
|
|
124
167
|
title = title,
|
|
@@ -127,45 +170,41 @@ fun JsonResolvedTrack.toNitro(): ResolvedTrack {
|
|
|
127
170
|
artworkSource = null,
|
|
128
171
|
// Containers carry no per-track media-request override (mirrors iOS JsonResolvedTrack).
|
|
129
172
|
request = null,
|
|
130
|
-
artworkCarPlayTinted = null,
|
|
131
173
|
artist = artist,
|
|
132
|
-
|
|
174
|
+
albumPath = albumPath,
|
|
133
175
|
album = album,
|
|
134
176
|
description = description,
|
|
135
177
|
genre = genre,
|
|
136
178
|
duration = duration,
|
|
137
179
|
src = src,
|
|
138
|
-
style = style.
|
|
139
|
-
|
|
180
|
+
style = style.toSectionStyle(),
|
|
181
|
+
disabled = disabled,
|
|
140
182
|
favorited = null,
|
|
141
|
-
groupTitle = groupTitle,
|
|
142
183
|
live = live,
|
|
143
|
-
|
|
184
|
+
icyCharset = icyCharset,
|
|
144
185
|
)
|
|
145
186
|
}
|
|
146
187
|
|
|
147
188
|
fun JsonTrack.toNitro(): Track {
|
|
148
189
|
return Track(
|
|
149
190
|
id = id,
|
|
150
|
-
|
|
191
|
+
path = path,
|
|
151
192
|
title = title,
|
|
152
193
|
subtitle = subtitle,
|
|
153
194
|
artwork = artwork?.toNitro(),
|
|
154
195
|
artworkSource = null,
|
|
155
196
|
request = request?.toNitro(),
|
|
156
|
-
artworkCarPlayTinted = null,
|
|
157
197
|
artist = artist,
|
|
158
|
-
|
|
198
|
+
albumPath = albumPath,
|
|
159
199
|
album = album,
|
|
160
200
|
description = description,
|
|
161
201
|
genre = genre,
|
|
162
202
|
duration = duration,
|
|
163
203
|
src = src,
|
|
164
204
|
style = style.toTrackStyle(),
|
|
165
|
-
|
|
205
|
+
disabled = disabled,
|
|
166
206
|
favorited = null,
|
|
167
|
-
groupTitle = groupTitle,
|
|
168
207
|
live = live,
|
|
169
|
-
|
|
208
|
+
icyCharset = icyCharset,
|
|
170
209
|
)
|
|
171
210
|
}
|