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
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Override what the lock-screen, notification, car, and headset buttons do with handleRemote*, and observe presses with the onRemote* events.'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Remote Controls
|
|
6
|
+
|
|
7
|
+
**Remote controls** are the playback buttons on surfaces outside your app — the
|
|
8
|
+
iOS lock screen and Control Center, the Android notification, CarPlay and Android
|
|
9
|
+
Auto, and Bluetooth/headset buttons. By default they just work: the system
|
|
10
|
+
play/pause/next/seek buttons drive the player for you. This guide is for the two
|
|
11
|
+
times you need more — **overriding** what a button does, and **observing** when
|
|
12
|
+
one is pressed.
|
|
13
|
+
|
|
14
|
+
There are two families:
|
|
15
|
+
|
|
16
|
+
- [`handleRemote*`](#overriding-a-control) — **override** a control's behavior.
|
|
17
|
+
A plain setter: `handleRemoteNext(cb)`, or `handleRemoteNext(undefined)` to
|
|
18
|
+
clear it.
|
|
19
|
+
- [`onRemote*`](#listening-to-presses) — **listen** to presses without changing
|
|
20
|
+
behavior (analytics, logging). An emitter:
|
|
21
|
+
`onRemoteNext.addListener(cb)` returns an unsubscribe function.
|
|
22
|
+
|
|
23
|
+
Both families work on **iOS and Android**. Which buttons appear at all is
|
|
24
|
+
governed by [`capabilities`](/guide/configuration#capabilities)
|
|
25
|
+
(e.g. `jumpForward` / `jumpBackward` are off by default), and on Android
|
|
26
|
+
[`remoteButtonLayout`](#button-layout-android) decides where they sit.
|
|
27
|
+
|
|
28
|
+
The snippets below import from `react-native-audio-browser` unless noted.
|
|
29
|
+
|
|
30
|
+
## They work by default
|
|
31
|
+
|
|
32
|
+
Without any wiring, each control drives the player:
|
|
33
|
+
|
|
34
|
+
| Control | Default action |
|
|
35
|
+
| ----------------------- | ----------------------------------- |
|
|
36
|
+
| play / pause / stop | `play()` / `pause()` / `stop()` |
|
|
37
|
+
| next / previous | `skipToNext()` / `skipToPrevious()` |
|
|
38
|
+
| seek | `seekTo(position)` |
|
|
39
|
+
| jump forward / backward | `seekBy(±interval)` |
|
|
40
|
+
|
|
41
|
+
So you only reach for the APIs below to _change_ or _watch_ that behavior.
|
|
42
|
+
|
|
43
|
+
## Button layout (Android)
|
|
44
|
+
|
|
45
|
+
Android gives you **three positions and no more**. Play/pause always holds the
|
|
46
|
+
centre and can't be moved, so the two named positions sit on either side of it —
|
|
47
|
+
they are not adjacent:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
back │ ▶ play/pause │ forward overflow ⋯
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Set them with [`updateOptions`](/api/features/player/#updateoptions) (or at
|
|
54
|
+
launch via [`setupPlayer`](/api/features/player/#setupplayer)):
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
updateOptions({
|
|
58
|
+
capabilities: { jumpBackward: true, jumpForward: true, favorite: true },
|
|
59
|
+
android: {
|
|
60
|
+
remoteButtonLayout: {
|
|
61
|
+
back: 'jump-backward',
|
|
62
|
+
forward: 'jump-forward',
|
|
63
|
+
overflow: ['skip-to-previous', 'skip-to-next', 'favorite']
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
One layout drives every Android surface — the notification, the Android Auto
|
|
70
|
+
Now Playing screen, and the Android 13+ system media controls.
|
|
71
|
+
|
|
72
|
+
**A layout describes the whole arrangement.** All three fields are required and
|
|
73
|
+
nothing is merged with the defaults, so what you write is exactly what renders —
|
|
74
|
+
list every button you want, not just the ones you're adding. Use `undefined` for
|
|
75
|
+
an empty position and `[]` for no overflow:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// Live radio: a forward skip only, nothing to the left of play/pause
|
|
79
|
+
remoteButtonLayout: {
|
|
80
|
+
back: undefined,
|
|
81
|
+
forward: 'skip-to-next',
|
|
82
|
+
overflow: []
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
To go back to the capability-derived defaults, omit `remoteButtonLayout`
|
|
87
|
+
entirely or set it to `null`. That's the only switch — there's no per-field
|
|
88
|
+
opt-out.
|
|
89
|
+
|
|
90
|
+
### Capabilities admit, the layout arranges
|
|
91
|
+
|
|
92
|
+
A layout can **rearrange** buttons but never **add** one. Every entry is still
|
|
93
|
+
gated by [`capabilities`](/guide/configuration#capabilities), so naming a
|
|
94
|
+
button whose capability is off does nothing at all.
|
|
95
|
+
|
|
96
|
+
This catches people out, because `jumpForward` and `jumpBackward` default to
|
|
97
|
+
**off**:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
// ✗ Both jump buttons are silently dropped — no error, no warning.
|
|
101
|
+
updateOptions({
|
|
102
|
+
android: {
|
|
103
|
+
remoteButtonLayout: { back: 'jump-backward', forward: 'jump-forward' }
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// ✓ Enable the capability and the layout takes effect.
|
|
108
|
+
updateOptions({
|
|
109
|
+
capabilities: { jumpBackward: true, jumpForward: true },
|
|
110
|
+
android: {
|
|
111
|
+
remoteButtonLayout: { back: 'jump-backward', forward: 'jump-forward' }
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The reverse also holds: placement is cosmetic. A button you leave out of the
|
|
117
|
+
layout still responds to a Bluetooth remote or headset as long as its
|
|
118
|
+
capability is enabled.
|
|
119
|
+
|
|
120
|
+
### Overflow is priority, not coordinates
|
|
121
|
+
|
|
122
|
+
`overflow` is an ordered list, and each surface renders as many buttons as it
|
|
123
|
+
has room for, **taking them from the front**:
|
|
124
|
+
|
|
125
|
+
| Surface | Roughly how many |
|
|
126
|
+
| -------------------------- | ----------------------------- |
|
|
127
|
+
| Collapsed notification | 3 — back, play/pause, forward |
|
|
128
|
+
| Expanded notification | all of them |
|
|
129
|
+
| Android 13+ media controls | about 5 |
|
|
130
|
+
| Android Auto | the head unit decides |
|
|
131
|
+
|
|
132
|
+
Two consequences worth knowing:
|
|
133
|
+
|
|
134
|
+
- A head unit with a spare slot **may promote** the first overflow entry onto
|
|
135
|
+
the main row. Seeing an "overflow" button beside play/pause in the car is
|
|
136
|
+
expected, not a bug.
|
|
137
|
+
- A long list gets **truncated**. Put what matters most first — a `'favorite'`
|
|
138
|
+
in fifth place may never be drawn on a phone.
|
|
139
|
+
|
|
140
|
+
If you want a clean three-button transport row in the car, give it fewer
|
|
141
|
+
buttons so there's nothing to promote.
|
|
142
|
+
|
|
143
|
+
::: tip iOS
|
|
144
|
+
This option is Android-only. CarPlay's Now Playing buttons are configured
|
|
145
|
+
separately with `ios.carPlayNowPlayingButtons` — see
|
|
146
|
+
[CarPlay](/guide/carplay).
|
|
147
|
+
:::
|
|
148
|
+
|
|
149
|
+
## Overriding a control
|
|
150
|
+
|
|
151
|
+
[`handleRemote*(callback)`](/api/features/remoteControls/#handleremotenext)
|
|
152
|
+
**replaces** a control's default action with your own. Pass `undefined` to remove
|
|
153
|
+
your override (restoring the default); the callback itself does nothing but what
|
|
154
|
+
you write — if you still want the normal action, call it yourself. Each control
|
|
155
|
+
has a **single** override: calling `handleRemote*` again replaces the previous one
|
|
156
|
+
(unlike `onRemote*`, which supports multiple listeners).
|
|
157
|
+
|
|
158
|
+
The canonical use is gating a control — e.g. a radio product with a skip
|
|
159
|
+
allowance — paired with a [now-playing flash](/guide/now-playing#the-flash-transient-top-priority)
|
|
160
|
+
for feedback, since external surfaces have no toast:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import {
|
|
164
|
+
handleRemoteNext,
|
|
165
|
+
skipToNext,
|
|
166
|
+
flashNowPlaying
|
|
167
|
+
} from 'react-native-audio-browser'
|
|
168
|
+
|
|
169
|
+
handleRemoteNext(() => {
|
|
170
|
+
if (skipsRemaining() > 0) {
|
|
171
|
+
skipToNext() // do the default action yourself
|
|
172
|
+
} else {
|
|
173
|
+
flashNowPlaying({ artist: 'Skip limit reached' }, 3000)
|
|
174
|
+
}
|
|
175
|
+
// Note: onRemoteNext listeners still fire on both branches (see below).
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// To remove the override and restore the default skip:
|
|
179
|
+
handleRemoteNext(undefined)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Overrides exist for `play`, `pause`, `next`, `previous`, `stop`, `seek`,
|
|
183
|
+
`jumpForward`, and `jumpBackward`. The ones carrying data receive a typed event:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import {
|
|
187
|
+
handleRemoteSeek,
|
|
188
|
+
handleRemoteJumpForward
|
|
189
|
+
} from 'react-native-audio-browser'
|
|
190
|
+
|
|
191
|
+
handleRemoteSeek(({ position }) => {
|
|
192
|
+
// position is in seconds
|
|
193
|
+
})
|
|
194
|
+
handleRemoteJumpForward(({ interval }) => {
|
|
195
|
+
// interval is in seconds (from forwardJumpInterval)
|
|
196
|
+
})
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The jump controls only appear when `jumpForward` / `jumpBackward` are enabled in
|
|
200
|
+
[`capabilities`](/guide/configuration#capabilities) (off by default). The jump
|
|
201
|
+
distance is the separate `forwardJumpInterval` / `backwardJumpInterval` option
|
|
202
|
+
(default 15s) — that's the `interval` your handler receives.
|
|
203
|
+
|
|
204
|
+
## Listening to presses
|
|
205
|
+
|
|
206
|
+
[`onRemote*`](/api/features/remoteControls/#onremotenext) subscribes to a press
|
|
207
|
+
**without** changing behavior — for analytics or logging. `addListener` returns a
|
|
208
|
+
cleanup function, and you can register multiple listeners:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import { onRemoteNext } from 'react-native-audio-browser'
|
|
212
|
+
|
|
213
|
+
const unsubscribe = onRemoteNext.addListener(() => {
|
|
214
|
+
logEvent('remote_next')
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// later:
|
|
218
|
+
unsubscribe()
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
::: warning `onRemote*` fires even when overridden
|
|
222
|
+
`onRemote*` reports that the **button was pressed**, not that the default action
|
|
223
|
+
ran. It fires on every press — including when a `handleRemote*` override
|
|
224
|
+
intercepted it (and even when that override refused to act). Use it to _observe_;
|
|
225
|
+
use `handleRemote*` to _change behavior_.
|
|
226
|
+
:::
|
|
227
|
+
|
|
228
|
+
## Voice commands ("play X")
|
|
229
|
+
|
|
230
|
+
A voice command like "play jazz on Android Auto" or a Siri "play …" is **not**
|
|
231
|
+
delivered as a remote-control event. It's handled by the library's
|
|
232
|
+
[search source](/guide/search) and play funnel: you provide
|
|
233
|
+
`configureBrowser({ search })`, and the library resolves the query and starts
|
|
234
|
+
playback for you (Android routes the system `MEDIA_PLAY_FROM_SEARCH` intent
|
|
235
|
+
through it). See [Search](/guide/search) for wiring voice intents.
|
|
236
|
+
|
|
237
|
+
## Payloads at a glance
|
|
238
|
+
|
|
239
|
+
| Event | Payload |
|
|
240
|
+
| ----------------------------------------------- | ------------------------ |
|
|
241
|
+
| `play` / `pause` / `stop` / `next` / `previous` | _(none)_ |
|
|
242
|
+
| `seek` | `{ position }` — seconds |
|
|
243
|
+
| `jumpForward` / `jumpBackward` | `{ interval }` — seconds |
|
|
244
|
+
|
|
245
|
+
## API summary
|
|
246
|
+
|
|
247
|
+
| API | Purpose |
|
|
248
|
+
| ---------------------------------------------------------------------------------- | ----------------------------------------------------- |
|
|
249
|
+
| `handleRemotePlay/Pause/Stop/Next/Previous(cb \| undefined)` | Override a control; `undefined` restores the default. |
|
|
250
|
+
| `handleRemoteSeek(cb)` / `handleRemoteJumpForward/Backward(cb)` | Override, with a typed event payload. |
|
|
251
|
+
| `onRemotePlay/Pause/Stop/Next/Previous.addListener(cb)` | Observe a press (fires even when overridden). |
|
|
252
|
+
| `onRemoteSeek` / `onRemoteJumpForward` / `onRemoteJumpBackward` `.addListener(cb)` | Observe, with payload. |
|
|
253
|
+
|
|
254
|
+
Every `onRemote*` is an emitter: `.addListener(cb)` returns an unsubscribe
|
|
255
|
+
function. Every `handleRemote*` is a setter: pass a callback, or `undefined` to
|
|
256
|
+
clear it.
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Configure the search source that answers both your in-app search UI and the voice search on Siri, CarPlay, and Android Auto.'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Search
|
|
6
|
+
|
|
7
|
+
**Search** is the query subsystem: it turns a voice command ("play jazz") or a text query into a list of playable tracks. It is distinct from **Browse** (navigating the content tree) — search takes structured query parameters and returns results.
|
|
8
|
+
|
|
9
|
+
Search powers both your in-app search UI and the voice search on external surfaces (Siri / CarPlay, Android Auto).
|
|
10
|
+
|
|
11
|
+
## Configuring search
|
|
12
|
+
|
|
13
|
+
Set `search` on your `BrowserConfiguration`. It takes one of two shapes:
|
|
14
|
+
|
|
15
|
+
**A callback** — resolve results yourself and return a `Track[]`. The simplest possible search just filters a list you already have in memory:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { configureBrowser } from 'react-native-audio-browser'
|
|
19
|
+
|
|
20
|
+
configureBrowser({
|
|
21
|
+
// …the rest of your config (tabs, browse, request, …)
|
|
22
|
+
search: async ({ query }) => {
|
|
23
|
+
const q = query.toLowerCase()
|
|
24
|
+
return allTracks.filter((track) => track.title?.toLowerCase().includes(q))
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The callback can return `Track[]` from anywhere — an in-memory filter as above, a local database, or your own API:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
configureBrowser({
|
|
33
|
+
search: async (params) => {
|
|
34
|
+
// db / toTrack are yours
|
|
35
|
+
const rows = await db.findStations(params.query)
|
|
36
|
+
return rows.map(toTrack)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**An HTTP endpoint** — a `TransformableRequestConfig`. The library issues the request natively and appends the query parameters for you. The endpoint must return a **page object** — `{ title?, children: Track[] }` — the same shape a browse endpoint returns. (iOS and Android require the page object; the web implementation additionally accepts a bare `Track[]` for back-compat, but return the page object so all three platforms work.)
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
configureBrowser({
|
|
45
|
+
search: {
|
|
46
|
+
baseUrl: 'https://api.example.com/search',
|
|
47
|
+
// "play jazz" → GET .../search?q=jazz&genre=jazz&limit=20
|
|
48
|
+
// (q always; genre because the intent carried one; no mode here
|
|
49
|
+
// because "play jazz" carries none; limit added by transform below)
|
|
50
|
+
transform(request) {
|
|
51
|
+
return { ...request, query: { ...request.query, limit: '20' } }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
With the HTTP form the library appends to `request.query`: `q` (always, even when empty), then `mode`, `reference` (only when it is `'my'` — see below), and the filter fields, each included only when the intent carried it. Query-param order doesn't matter to a server, but that is the order the library writes them in. Note the spoken text is the `query` field in a **callback** (`SearchParams.query`) but is sent as the **`q`** wire param on the **HTTP** form — same value, named `query` in JS and `q` on the URL.
|
|
58
|
+
|
|
59
|
+
## Searching from your own UI
|
|
60
|
+
|
|
61
|
+
The same source powers your in-app search box. [`search(query)`](/api/features/browser/#search) runs it and resolves to a `Track[]` — it only _returns_ results, it never queues or plays anything. [`hasSearch()`](/api/features/browser/#hassearch) tells you (synchronously) whether a search source is configured at all, so you can hide the search UI when it isn't:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { search, hasSearch, navigate } from 'react-native-audio-browser'
|
|
65
|
+
import type { Track } from 'react-native-audio-browser'
|
|
66
|
+
|
|
67
|
+
function SearchScreen() {
|
|
68
|
+
const [results, setResults] = useState<Track[]>([])
|
|
69
|
+
if (!hasSearch()) return null // no search source configured
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<SearchInput onSubmit={async (text) => setResults(await search(text))} />
|
|
74
|
+
<List
|
|
75
|
+
sections={[{ children: results }]}
|
|
76
|
+
onSelect={(track) => navigate(track)} // load & play via the library
|
|
77
|
+
/>
|
|
78
|
+
</>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Three things to know about the in-app call:
|
|
84
|
+
|
|
85
|
+
- **Your resolver sees a plain query.** An in-app `search('jazz')` arrives as `{ query: 'jazz', reference: 'unknown' }` — no `mode`, no filter fields. Those are extracted by the voice assistants; a text box has nothing to extract. (On the HTTP form, only `q` is appended.) A resolver written against _the fields that are present_ handles both callers with the same code.
|
|
86
|
+
- **You get the flat list, untouched.** The [browsable-first-result drill-in](#voice-playback-siri-google-assistant) is voice-only — in-app results are handed to you as returned (minus any result lacking both a `path` and a `src`, which is dropped as unrenderable), and the user picks.
|
|
87
|
+
- **Route taps through `navigate(track)`** rather than `setQueue` + `play` — that gives a tapped result the library's load path ([`handleTrackLoad`](#mixed-audio-video), [Gate](/guide/gate) checks, favorite hearts) just like a tap on a browse row.
|
|
88
|
+
|
|
89
|
+
## SearchParams
|
|
90
|
+
|
|
91
|
+
Both forms receive the same structured `SearchParams`:
|
|
92
|
+
|
|
93
|
+
| Field | Type | Meaning |
|
|
94
|
+
| ----------- | ------------------- | -------------------------------------------------------------------------------------- |
|
|
95
|
+
| `query` | `string` | The raw query (always present, may be `""`). |
|
|
96
|
+
| `mode` | `SearchMode?` | The **container vertical** — what _kind_ of result. Absent for an unstructured search. |
|
|
97
|
+
| `genre` | `string?` | Genre **filter**. |
|
|
98
|
+
| `artist` | `string?` | Artist **filter**. |
|
|
99
|
+
| `album` | `string?` | Album **filter**. |
|
|
100
|
+
| `title` | `string?` | Track-title filter (song intents). |
|
|
101
|
+
| `playlist` | `string?` | Playlist-name filter. |
|
|
102
|
+
| `reference` | `'my' \| 'unknown'` | Whether the user asked for _their own_ collection. |
|
|
103
|
+
|
|
104
|
+
`query` and `reference` are **always present** (`reference` defaults to `'unknown'`); the short literals in the tables below omit them for brevity, but a real `SearchParams` always carries both.
|
|
105
|
+
|
|
106
|
+
## Search modes are _verticals_, not filters
|
|
107
|
+
|
|
108
|
+
`mode` answers **"what kind of thing did the user ask for?"** — a station, a podcast, a song. It is orthogonal to the filter fields, which say _which_ item:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
// "play a jazz podcast"
|
|
112
|
+
{ mode: 'podcast', genre: 'jazz', query: 'jazz' }
|
|
113
|
+
// ^ vertical ^ filter
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The values:
|
|
117
|
+
|
|
118
|
+
| Mode | Asked for |
|
|
119
|
+
| ------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
120
|
+
| `any` | Anything sensible — smart shuffle / "play something" (empty query). |
|
|
121
|
+
| `station` | A live radio station / channel. |
|
|
122
|
+
| `podcast` | A podcast (show, episode, or station). |
|
|
123
|
+
| `audiobook` | An audiobook. |
|
|
124
|
+
| `news` | News content. |
|
|
125
|
+
| `music` | The music vertical (as opposed to talk/podcasts). |
|
|
126
|
+
| `song` | An individual track. |
|
|
127
|
+
| `playlist` | A named playlist / mix. |
|
|
128
|
+
| `music-video` / `movie` / `tv-show` / `tv-show-episode` | Video kinds (see [Mixed audio/video](#mixed-audio-video)). |
|
|
129
|
+
|
|
130
|
+
::: tip There is no `genre`/`artist`/`album` mode
|
|
131
|
+
Those are **filters**, not result shapes — read them from `params.genre` / `params.artist` / `params.album` directly. A genre search arrives as `{ genre: 'jazz' }` with `mode` left unset.
|
|
132
|
+
:::
|
|
133
|
+
|
|
134
|
+
`mode` is **advisory** — use it to route to the right index, or ignore it and full-text search `query`.
|
|
135
|
+
|
|
136
|
+
## Cross-platform differences
|
|
137
|
+
|
|
138
|
+
The two voice platforms feed the **same** `SearchParams`; they just differ in how many fields they fill:
|
|
139
|
+
|
|
140
|
+
- **iOS (SiriKit)** supplies the richer set — all the `mode` verticals and the `reference` axis.
|
|
141
|
+
- **Android (`onPlayFromSearch`)** supplies the subset it can express: `query`, the music focuses (`song` / `playlist` and the genre/artist/album filters), and always `reference: 'unknown'`.
|
|
142
|
+
|
|
143
|
+
Write your resolver against the _fields that are present_ (`if (params.genre) …`), not against the platform. A field that's empty on one platform simply means "the assistant didn't provide it."
|
|
144
|
+
|
|
145
|
+
## Voice playback (Siri & Google Assistant)
|
|
146
|
+
|
|
147
|
+
A spoken command — "play jazz on «App»" — funnels to the **same `search` source** on both platforms, then the result is queued and played. Any active **[Gate](/guide/gate)** sees the search first, so voice can't slip past a paywall or region block unless your gate lets it through. You configure `search` once; both assistants use it.
|
|
148
|
+
|
|
149
|
+
::: tip A browsable first result is drilled into
|
|
150
|
+
For voice playback specifically, the library inspects the **first** result. If it is a browsable-only container — a `path` (a place/genre page) with no `src` of its own — the library resolves that page and queues _its_ playable children, so "play jazz" plays the first station _inside_ the jazz page rather than the page itself. If the first result is already playable (has a `src`), or the drill-in finds nothing playable, the flat list of results is queued as-is. This applies on both platforms, and only to voice playback — an in-app search UI renders your results and lets the user pick.
|
|
151
|
+
:::
|
|
152
|
+
|
|
153
|
+
For ordinary queries ("play jazz", "play «station name»") the two behave identically — same resolver, same queue, same playback. The entry point and a few conveniences differ:
|
|
154
|
+
|
|
155
|
+
| | iOS (Siri) | Android (Google Assistant) |
|
|
156
|
+
| ----------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
157
|
+
| Delivered as | `INPlayMediaIntent` | `MEDIA_PLAY_FROM_SEARCH` intent |
|
|
158
|
+
| "play my favorites" | `reference: 'my'` → resolves to the user's collection | no collection signal → searched as plain text; reach favorites by **browsing** the Favorites tab instead |
|
|
159
|
+
| "play «App»" | recognised as a resume | searched literally (no app-name heuristic) |
|
|
160
|
+
| bare "play" / "resume" | resumes the current/last track | separate resume path (`onPlay`), not a search |
|
|
161
|
+
| "play music" (no query) | may send `mode: 'music'` | `mode: 'any'` → return smart-shuffle / recent content |
|
|
162
|
+
|
|
163
|
+
The gaps are all iOS-only conveniences — collection-by-voice and resume-by-name. Everything in the shared column is fully cross-platform.
|
|
164
|
+
|
|
165
|
+
## Playing the user's own collection
|
|
166
|
+
|
|
167
|
+
When the user says **"play my favorites"**, the intent carries `reference: 'my'`. Resolve it however your collection lives:
|
|
168
|
+
|
|
169
|
+
**Local collection** — read `params.reference` in a callback and return your own tracks:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
configureBrowser({
|
|
173
|
+
search: async (params) => {
|
|
174
|
+
// getFavorites returns your local Track[]
|
|
175
|
+
if (params.reference === 'my') return getFavorites(params)
|
|
176
|
+
return searchByQuery(params)
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Server collection** — when you use the HTTP form, `reference=my` is appended to `request.query`, so branch in `transform` to route the request — for example, to a favorites endpoint, injecting locally-stored identifiers:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
configureBrowser({
|
|
185
|
+
search: {
|
|
186
|
+
baseUrl: 'https://api.example.com/search',
|
|
187
|
+
transform(request) {
|
|
188
|
+
// `transform` runs for every search — pass normal queries straight
|
|
189
|
+
// through to /search; only "play my favorites" is rewritten below.
|
|
190
|
+
if (request.query?.reference !== 'my') return request
|
|
191
|
+
return {
|
|
192
|
+
...request,
|
|
193
|
+
method: 'POST',
|
|
194
|
+
path: '/search/favorites',
|
|
195
|
+
contentType: 'application/json',
|
|
196
|
+
body: JSON.stringify({
|
|
197
|
+
q: request.query.q,
|
|
198
|
+
ids: getLocalFavoriteIds() // your stored identifiers
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`reference` is only ever `'my'` or `'unknown'` — "currently playing" ("play this") is resolved natively as a resume and never reaches search.
|
|
207
|
+
|
|
208
|
+
## Voice phrase → params
|
|
209
|
+
|
|
210
|
+
These mappings are **illustrative, not guaranteed.** The assistant (Siri / Google Assistant) decides how to parse a spoken phrase, and the same words can arrive structured differently — or as a bare `query` — depending on the platform, locale, and the assistant's own interpretation. Treat the table as _plausible_ shapes to handle, and always write your resolver against the fields actually present (see [Cross-platform differences](#cross-platform-differences)) rather than assuming a phrase produces a specific shape.
|
|
211
|
+
|
|
212
|
+
| Phrase | Resulting `SearchParams` |
|
|
213
|
+
| --------------------- | ------------------------------------------------------------- |
|
|
214
|
+
| "play something" | `{ query: 'something' }` |
|
|
215
|
+
| "play music" | `{ mode: 'any', query: '' }` _(iOS may send `mode: 'music'`)_ |
|
|
216
|
+
| "play jazz" | `{ genre: 'jazz', query: 'jazz' }` |
|
|
217
|
+
| "play The Stalk 88.5" | `{ mode: 'station', query: 'The Stalk 88.5' }` |
|
|
218
|
+
| "play a jazz podcast" | `{ mode: 'podcast', genre: 'jazz' }` |
|
|
219
|
+
| "play my favorites" | `{ reference: 'my', query: '' }` |
|
|
220
|
+
| "play my jazz" | `{ reference: 'my', genre: 'jazz' }` |
|
|
221
|
+
|
|
222
|
+
## Mixed audio/video
|
|
223
|
+
|
|
224
|
+
This is an **audio** library — its player streams audio and has no video surface, and CarPlay / Android Auto forbid video playback while driving. So video is fundamentally an **in-app** concern: a search can _signal_ a video request (via the video `mode` values), but the library will not render video.
|
|
225
|
+
|
|
226
|
+
To play video from your in-app search, intercept the load with [`handleTrackLoad`](/api/types/browser/#handletrackload). It runs whenever a track is loaded through [`navigate(track)`](/api/features/browser/#navigate) (or the library's own browse UI) — so route your in-app search-result taps through `navigate(track)` rather than calling `setQueue` / `play` yourself, or this hook never fires. When set, it is called **instead of** the library auto-playing the track — for **every** such load, not just video — so your handler must either route the track elsewhere or hand it back to the library to play:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import {
|
|
230
|
+
configureBrowser,
|
|
231
|
+
setQueue,
|
|
232
|
+
play,
|
|
233
|
+
type Track
|
|
234
|
+
} from 'react-native-audio-browser'
|
|
235
|
+
|
|
236
|
+
// You build the search-result tracks, so encode "this is video" however
|
|
237
|
+
// you like. `src` can be any string you recognise later — here, a scheme
|
|
238
|
+
// prefix your own player understands.
|
|
239
|
+
const isVideo = (track: Track) => track.src?.startsWith('video:') ?? false
|
|
240
|
+
|
|
241
|
+
configureBrowser({
|
|
242
|
+
handleTrackLoad: async ({ track, queue, startIndex }) => {
|
|
243
|
+
if (isVideo(track)) {
|
|
244
|
+
openVideoPlayer(track) // your own video surface
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
setQueue(queue, startIndex) // hand audio back to the library
|
|
248
|
+
play() // setQueue does not start playback, so play() is required
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`setQueue` and `play` are top-level named exports, like `configureBrowser` — not methods on a browser object. `setQueue` only loads the queue; it never changes play/pause state, which is why the audio branch calls `play()` after it.
|
|
254
|
+
|
|
255
|
+
The `mode` you saw at **search** time (`music-video` / `movie` / …) is not carried onto the `Track`, and `handleTrackLoad` runs at **load** time with no `mode`. The tracks your `search` source returns for a video request are _yours_, though — so tag them with a recognizable `src` (as above) when you build them, and read that tag in `isVideo` here. On external surfaces (CarPlay / Android Auto), a video request falls back to audio or is declined — there is no video playback path there.
|