react-native-audio-browser 0.2.0-next.2 → 0.2.0-next.21
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
|
@@ -18,7 +18,7 @@ describe('toNativeConfig route classification', () => {
|
|
|
18
18
|
|
|
19
19
|
it('keeps a static page with a string artwork out of the RouteConfig branch', () => {
|
|
20
20
|
const page = {
|
|
21
|
-
|
|
21
|
+
path: '/x',
|
|
22
22
|
title: 'X',
|
|
23
23
|
artwork: 'https://img.example.com/x.png',
|
|
24
24
|
children: []
|
|
@@ -28,6 +28,21 @@ describe('toNativeConfig route classification', () => {
|
|
|
28
28
|
expect(entry?.artwork).toBeUndefined()
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
+
it('classifies a static page as static despite its top-level `path` key', () => {
|
|
32
|
+
// `path` is also a RequestConfig field; the page's required `title` must
|
|
33
|
+
// win the classification (a config with a `path` alone is not a page).
|
|
34
|
+
const page = { path: '/x', title: 'X', children: [] }
|
|
35
|
+
const entry = entryFor({ '/x': page }, '/x')
|
|
36
|
+
expect(entry?.browseStatic).toEqual(page)
|
|
37
|
+
expect(entry?.browseConfig).toBeUndefined()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('classifies a path-only request config as a request config', () => {
|
|
41
|
+
const entry = entryFor({ '/x': { path: '/remapped' } }, '/x')
|
|
42
|
+
expect(entry?.browseConfig).toEqual({ path: '/remapped' })
|
|
43
|
+
expect(entry?.browseStatic).toBeUndefined()
|
|
44
|
+
})
|
|
45
|
+
|
|
31
46
|
it('classifies a method-only request config as a request config', () => {
|
|
32
47
|
const entry = entryFor({ '/x': { method: 'POST' } }, '/x')
|
|
33
48
|
expect(entry?.browseConfig).toEqual({ method: 'POST' })
|
|
@@ -66,13 +81,13 @@ describe('validateBrowserConfiguration', () => {
|
|
|
66
81
|
it('is silent for a valid configuration', () => {
|
|
67
82
|
validateBrowserConfiguration({
|
|
68
83
|
routes: {
|
|
69
|
-
'/favorites': async () => ({
|
|
84
|
+
'/favorites': async () => ({ path: '/favorites', title: 'Favorites' }),
|
|
70
85
|
'/albums/{id}': { baseUrl: 'https://api.example.com' },
|
|
71
86
|
'*': { baseUrl: 'https://api.example.com' }
|
|
72
87
|
},
|
|
73
88
|
tabs: [
|
|
74
|
-
{ title: 'Home',
|
|
75
|
-
{ title: 'Search',
|
|
89
|
+
{ title: 'Home', path: '/' },
|
|
90
|
+
{ title: 'Search', path: '/search' }
|
|
76
91
|
]
|
|
77
92
|
})
|
|
78
93
|
expect(warnings).toEqual([])
|
|
@@ -96,14 +111,14 @@ describe('validateBrowserConfiguration', () => {
|
|
|
96
111
|
|
|
97
112
|
it('does not flag a valid static page as unclassifiable', () => {
|
|
98
113
|
validateBrowserConfiguration({
|
|
99
|
-
routes: { '/x': {
|
|
114
|
+
routes: { '/x': { path: '/x', title: 'X', children: [] } }
|
|
100
115
|
})
|
|
101
116
|
expect(warnings).toEqual([])
|
|
102
117
|
})
|
|
103
118
|
|
|
104
119
|
it('warns on more than 4 static tabs', () => {
|
|
105
120
|
validateBrowserConfiguration({
|
|
106
|
-
tabs: [1, 2, 3, 4, 5].map((n) => ({ title: `Tab ${n}`,
|
|
121
|
+
tabs: [1, 2, 3, 4, 5].map((n) => ({ title: `Tab ${n}`, path: `/${n}` }))
|
|
107
122
|
})
|
|
108
123
|
expect(warnings.some((w) => w.includes('at most 4'))).toBe(true)
|
|
109
124
|
})
|
|
@@ -38,9 +38,12 @@ function isTransformableRequestConfig(
|
|
|
38
38
|
source: unknown
|
|
39
39
|
): source is TransformableRequestConfig {
|
|
40
40
|
if (!isObjectValue(source)) return false
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
41
|
+
// A static ResolvedTrack page also carries a top-level `path`, so `path`
|
|
42
|
+
// alone cannot mark a request config. A page is recognised by its required
|
|
43
|
+
// `title` — no RequestConfig field is named `title`.
|
|
44
|
+
if ('title' in source) return false
|
|
45
|
+
// Any RequestConfig key marks a request config. (A static ResolvedTrack's
|
|
46
|
+
// per-track request override is nested under `request`, not top-level.)
|
|
44
47
|
return (
|
|
45
48
|
'baseUrl' in source ||
|
|
46
49
|
'path' in source ||
|
|
@@ -120,7 +123,7 @@ function searchSourceToRouteEntry(source: SearchSource): NativeRouteEntry {
|
|
|
120
123
|
*/
|
|
121
124
|
function wrapTracksAsResolvedTrack(tracks: Track[]): ResolvedTrack {
|
|
122
125
|
return {
|
|
123
|
-
|
|
126
|
+
path: TABS_ROUTE_PATH,
|
|
124
127
|
title: 'Tabs',
|
|
125
128
|
children: tracks
|
|
126
129
|
}
|
|
@@ -245,14 +248,14 @@ export function validateBrowserConfiguration(
|
|
|
245
248
|
!isRouteConfig(source) &&
|
|
246
249
|
!isTransformableRequestConfig(source)
|
|
247
250
|
) {
|
|
248
|
-
// Falls through to the static-page branch; a page needs
|
|
251
|
+
// Falls through to the static-page branch; a page needs path + title.
|
|
249
252
|
const page = source as Partial<ResolvedTrack>
|
|
250
|
-
if (typeof page.
|
|
253
|
+
if (typeof page.path !== 'string' || typeof page.title !== 'string') {
|
|
251
254
|
warn(
|
|
252
255
|
`route "${path}" matched no source shape — expected a callback, ` +
|
|
253
256
|
`a request config ({ baseUrl, path, headers, ... }), a ` +
|
|
254
257
|
`RouteConfig ({ browse, media, artwork }), or a static page ` +
|
|
255
|
-
`({
|
|
258
|
+
`({ path, title, children? }). It will be served as static ` +
|
|
256
259
|
`content and likely render a blank screen.`
|
|
257
260
|
)
|
|
258
261
|
}
|
|
@@ -286,8 +289,9 @@ export function toNativeConfig(
|
|
|
286
289
|
singleTrack: config.singleTrack,
|
|
287
290
|
handleTrackLoad: config.handleTrackLoad,
|
|
288
291
|
androidControllerOfflineError: config.androidControllerOfflineError,
|
|
292
|
+
viewMoreTitle: config.viewMoreTitle,
|
|
289
293
|
carPlayLoadingTitle: config.carPlayLoadingTitle,
|
|
290
|
-
|
|
294
|
+
resolveAlbumPath: config.resolveAlbumPath,
|
|
291
295
|
formatNavigationError: config.formatNavigationError
|
|
292
296
|
}
|
|
293
297
|
}
|
|
@@ -12,7 +12,7 @@ describe('getBrowserConfiguration', () => {
|
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
it('returns the last configuration in its public shape', () => {
|
|
15
|
-
const config = { tabs: [{ title: 'Home',
|
|
15
|
+
const config = { tabs: [{ title: 'Home', path: '/' }] }
|
|
16
16
|
configureBrowser(config)
|
|
17
17
|
expect(getBrowserConfiguration()).toBe(config)
|
|
18
18
|
})
|
package/src/features/browser.ts
CHANGED
|
@@ -43,8 +43,8 @@ let currentConfiguration: BrowserConfiguration | undefined
|
|
|
43
43
|
* }
|
|
44
44
|
* },
|
|
45
45
|
* tabs: [
|
|
46
|
-
* { title: 'Home',
|
|
47
|
-
* { title: 'Search',
|
|
46
|
+
* { title: 'Home', path: '/' },
|
|
47
|
+
* { title: 'Search', path: '/search' }
|
|
48
48
|
* ]
|
|
49
49
|
* })
|
|
50
50
|
* ```
|
|
@@ -157,8 +157,16 @@ export function hasSearch(): boolean {
|
|
|
157
157
|
/**
|
|
158
158
|
* Searches for tracks using the configured search source.
|
|
159
159
|
*
|
|
160
|
+
* Resolves `[]` only when the source found nothing; a failed search rejects,
|
|
161
|
+
* so a caller can tell "no results" from "the request failed". The rejection's
|
|
162
|
+
* message describes the failure — native platforms reject with the underlying
|
|
163
|
+
* throwable and JS sees only its message. On web the `Error` also carries a
|
|
164
|
+
* `code`, the {@link NavigationErrorType} the browse path reports as a
|
|
165
|
+
* {@link NavigationError} (`'http-error'`, `'network-error'`).
|
|
166
|
+
*
|
|
160
167
|
* @param query - The search query string
|
|
161
168
|
* @returns Promise resolving to an array of matching tracks
|
|
169
|
+
* @throws When the search source or its request fails
|
|
162
170
|
*
|
|
163
171
|
* @see {@link configureBrowser}
|
|
164
172
|
*
|
|
@@ -169,5 +177,5 @@ export function hasSearch(): boolean {
|
|
|
169
177
|
* ```
|
|
170
178
|
*/
|
|
171
179
|
export async function search(query: string): Promise<Track[]> {
|
|
172
|
-
return nativeBrowser.
|
|
180
|
+
return nativeBrowser.search(query)
|
|
173
181
|
}
|
|
@@ -19,7 +19,7 @@ export function isCarConnected(): boolean {
|
|
|
19
19
|
* Subscribes to car connection changes (CarPlay on iOS, Android Auto /
|
|
20
20
|
* Android Automotive on Android). Never fires on web.
|
|
21
21
|
* @param callback - Called with the new connection state
|
|
22
|
-
* @returns
|
|
22
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
23
23
|
*/
|
|
24
24
|
export const onCarConnectedChanged = NativeUpdatedValue.emitterize<boolean>(
|
|
25
25
|
(cb) => (nativeBrowser.onCarConnectedChanged = cb)
|
|
@@ -46,7 +46,7 @@ export function setEqualizerLevels(levels: number[]): void {
|
|
|
46
46
|
/**
|
|
47
47
|
* Subscribes to equalizer settings changes (Android only).
|
|
48
48
|
* @param callback - Called when equalizer settings change
|
|
49
|
-
* @returns
|
|
49
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
50
50
|
*/
|
|
51
51
|
export const onEqualizerChanged =
|
|
52
52
|
NativeUpdatedValue.emitterize<EqualizerSettings>(
|
package/src/features/errors.ts
CHANGED
|
@@ -134,7 +134,7 @@ export function getPlaybackError(): PlaybackError | undefined {
|
|
|
134
134
|
/**
|
|
135
135
|
* Subscribes to playback error events.
|
|
136
136
|
* @param callback - Called when a playback error occurs
|
|
137
|
-
* @returns
|
|
137
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
138
138
|
*/
|
|
139
139
|
export const onPlaybackError =
|
|
140
140
|
NativeUpdatedValue.emitterize<PlaybackErrorEvent>(
|
|
@@ -165,7 +165,7 @@ export function getNavigationError(): NavigationError | undefined {
|
|
|
165
165
|
/**
|
|
166
166
|
* Subscribes to navigation error events.
|
|
167
167
|
* @param callback - Called when a navigation error occurs
|
|
168
|
-
* @returns
|
|
168
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
169
169
|
*/
|
|
170
170
|
export const onNavigationError =
|
|
171
171
|
NativeUpdatedValue.emitterize<NavigationErrorEvent>(
|
|
@@ -198,7 +198,7 @@ export function getFormattedNavigationError():
|
|
|
198
198
|
/**
|
|
199
199
|
* Subscribes to formatted navigation error changes.
|
|
200
200
|
* @param callback - Called when the formatted navigation error changes
|
|
201
|
-
* @returns
|
|
201
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
202
202
|
*/
|
|
203
203
|
export const onFormattedNavigationError = NativeUpdatedValue.emitterize<
|
|
204
204
|
FormattedNavigationError | undefined
|
|
@@ -37,26 +37,29 @@ export function toggleActiveTrackFavorited(): void {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Sets the list of favorited track
|
|
40
|
+
* Sets the list of favorited track identifiers.
|
|
41
41
|
*
|
|
42
42
|
* This syncs your app's favorites with the native favorites cache, enabling
|
|
43
43
|
* the heart button in media controllers (notification, Android Auto, CarPlay)
|
|
44
44
|
* to show the correct state.
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
* Each identifier is matched **exactly** against a track's *identity* — its
|
|
47
|
+
* `id` when set, falling back to `src`. Store favorites as the same stable
|
|
48
|
+
* identifier you put in `Track.id` (or as the full `src` for id-less tracks).
|
|
49
49
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
50
|
+
* A track whose response already carries `favorited` keeps that value on
|
|
51
|
+
* display surfaces — a caller-set flag wins over cache hydration. The cache
|
|
52
|
+
* itself is written only by this call and by heart toggles
|
|
53
|
+
* (`setActiveTrackFavorited()` / media-controller taps), so you only need to
|
|
54
|
+
* call this on app launch — and again whenever the collection changes outside
|
|
55
|
+
* the player (a sync from another device, say).
|
|
53
56
|
*
|
|
54
|
-
* @param favorites - Array of favorited track `src`
|
|
57
|
+
* @param favorites - Array of favorited track identities (`id`, or `src` for id-less tracks)
|
|
55
58
|
*
|
|
56
59
|
* @example
|
|
57
60
|
* ```ts
|
|
58
|
-
* const
|
|
59
|
-
* setFavorites(
|
|
61
|
+
* const favoriteIds = await loadFavoritesFromStorage()
|
|
62
|
+
* setFavorites(favoriteIds)
|
|
60
63
|
* ```
|
|
61
64
|
*/
|
|
62
65
|
export function setFavorites(favorites: string[]): void {
|
|
@@ -82,7 +85,7 @@ export interface FavoriteChangedEvent {
|
|
|
82
85
|
* The native side has already updated the track's favorite state and UI.
|
|
83
86
|
*
|
|
84
87
|
* @param callback - Called with the track and its new favorite state
|
|
85
|
-
* @returns
|
|
88
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
86
89
|
*
|
|
87
90
|
* @example
|
|
88
91
|
* ```ts
|
package/src/features/gate.ts
CHANGED
|
@@ -120,12 +120,20 @@ nativeBrowser.resolveGate = async (
|
|
|
120
120
|
* some surfaces (CarPlay) any gate change pops pushed navigation back to root,
|
|
121
121
|
* so a page the user had drilled into is reset to the tab root. Set the gate
|
|
122
122
|
* before the car connects and it'll be there the moment it does.
|
|
123
|
+
*
|
|
124
|
+
* @platform ios android
|
|
125
|
+
* A no-op on web, which has no CarPlay or Android Auto surfaces for the gate to
|
|
126
|
+
* cover. Nothing is lost: the gate never covered in-app browse UI on any
|
|
127
|
+
* platform — that stays yours to gate.
|
|
123
128
|
*/
|
|
124
129
|
export function setGate(gate: Gate, resolve?: GateResolver): void
|
|
125
130
|
/**
|
|
126
131
|
* Raises the gate with a resolver only (no default chrome).
|
|
127
132
|
* The resolver must return a {@link Gate} or `true` for gated requests;
|
|
128
133
|
* native uses the built-in fallback chrome when `true` and no default is set.
|
|
134
|
+
*
|
|
135
|
+
* @platform ios android
|
|
136
|
+
* A no-op on web — the resolver is never consulted there.
|
|
129
137
|
*/
|
|
130
138
|
export function setGate(resolve: GateResolver): void
|
|
131
139
|
export function setGate(a: Gate | GateResolver, b?: GateResolver): void {
|
|
@@ -140,6 +148,9 @@ export function setGate(a: Gate | GateResolver, b?: GateResolver): void {
|
|
|
140
148
|
|
|
141
149
|
/**
|
|
142
150
|
* Drops the gate — the real content comes back, and the current tab is kept.
|
|
151
|
+
*
|
|
152
|
+
* @platform ios android
|
|
153
|
+
* A no-op on web, where no gate was ever raised.
|
|
143
154
|
*/
|
|
144
155
|
export function clearGate(): void {
|
|
145
156
|
resolver = undefined
|
|
@@ -148,7 +159,11 @@ export function clearGate(): void {
|
|
|
148
159
|
|
|
149
160
|
/**
|
|
150
161
|
* Subscribes to gate events (fired when a request is gated).
|
|
151
|
-
*
|
|
162
|
+
*
|
|
163
|
+
* @platform ios android
|
|
164
|
+
* Never fires on web — there are no gated serves to report.
|
|
165
|
+
*
|
|
166
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
152
167
|
*/
|
|
153
168
|
export const onGate = LazyNativeEmitter.emitterize<GateEvent>(
|
|
154
169
|
(cb) => (nativeBrowser.onGate = cb)
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/** @vitest-environment happy-dom */
|
|
2
|
+
|
|
3
|
+
import { renderHook, act, cleanup } from '@testing-library/react'
|
|
4
|
+
import { describe, it, expect, afterEach, vi } from 'vitest'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The hooks that are a single delegation to `useNativeUpdatedValue`. Their
|
|
8
|
+
* behaviour is covered once in `utils/useNativeUpdatedValue.test.ts`; what is
|
|
9
|
+
* per-hook and untestable there is the wiring — which native callback slot the
|
|
10
|
+
* emitter claims, and which `eventKey` is pulled out of the payload.
|
|
11
|
+
* (`useActiveTrack` and `useQueue` are getter-refreshed instead — they update
|
|
12
|
+
* on transitions AND favorite toggles — and are covered in their own block.)
|
|
13
|
+
*
|
|
14
|
+
* Both are easy to get wrong and silent when wrong: the hook simply never
|
|
15
|
+
* updates, or yields the whole event where a field was meant. Five slots
|
|
16
|
+
* already differ in name from the emitter that claims them
|
|
17
|
+
* (`onPlaybackPlayWhenReadyChanged` behind `onPlayWhenReadyChanged`, and
|
|
18
|
+
* friends), so the names cannot be derived — they have to be pinned.
|
|
19
|
+
*/
|
|
20
|
+
const slots = vi.hoisted(() => new Map<string, (event: unknown) => void>())
|
|
21
|
+
|
|
22
|
+
vi.mock('../native', () => ({
|
|
23
|
+
nativeBrowser: new Proxy({} as Record<string, unknown>, {
|
|
24
|
+
get(target, prop: string) {
|
|
25
|
+
// Getters are the pre-native fallback; undefined stands in for "native
|
|
26
|
+
// has not answered yet", which is what these tests start from.
|
|
27
|
+
target[prop] ??= vi.fn(() => undefined)
|
|
28
|
+
return target[prop]
|
|
29
|
+
},
|
|
30
|
+
set(target, prop: string, value) {
|
|
31
|
+
if (typeof value === 'function') slots.set(prop, value)
|
|
32
|
+
target[prop] = value
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
}))
|
|
37
|
+
|
|
38
|
+
const { useBatteryOptimizationStatus, useBatteryWarningPending } =
|
|
39
|
+
await import('./battery')
|
|
40
|
+
const { useContent, usePath, useTabs } = await import('./browser')
|
|
41
|
+
const { useCarConnected } = await import('./carConnection')
|
|
42
|
+
const { useEqualizerSettings } = await import('./equalizer')
|
|
43
|
+
const { useFormattedNavigationError, useNavigationError, usePlaybackError } =
|
|
44
|
+
await import('./errors')
|
|
45
|
+
const { useOnline } = await import('./network')
|
|
46
|
+
const { useNowPlaying } = await import('./nowPlaying')
|
|
47
|
+
const { useOutput } = await import('./output')
|
|
48
|
+
const { useOptions } = await import('./player/options')
|
|
49
|
+
const { usePlayingState } = await import('./playback/playing')
|
|
50
|
+
const { usePlayWhenReady } = await import('./playback/playWhenReady')
|
|
51
|
+
const { useProgress } = await import('./playback/progress')
|
|
52
|
+
const { usePlayback } = await import('./playback/state')
|
|
53
|
+
const { useSystemVolume } = await import('./playback/volume')
|
|
54
|
+
const { useActiveTrack } = await import('./queue/activeTrack')
|
|
55
|
+
const { useQueue } = await import('./queue/queue')
|
|
56
|
+
const { useRepeatMode } = await import('./queue/repeatMode')
|
|
57
|
+
const { useShuffle } = await import('./queue/shuffle')
|
|
58
|
+
|
|
59
|
+
afterEach(cleanup)
|
|
60
|
+
|
|
61
|
+
type Wiring = {
|
|
62
|
+
name: string
|
|
63
|
+
hook: () => unknown
|
|
64
|
+
/** The property on the Nitro object the emitter assigns its callback to. */
|
|
65
|
+
slot: string
|
|
66
|
+
event: unknown
|
|
67
|
+
/** What the hook returns — differs from `event` exactly when an eventKey applies. */
|
|
68
|
+
expected: unknown
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const wirings: Wiring[] = [
|
|
72
|
+
// Payload passed through whole (no eventKey).
|
|
73
|
+
{
|
|
74
|
+
name: 'useOutput',
|
|
75
|
+
hook: useOutput,
|
|
76
|
+
slot: 'onOutputChanged',
|
|
77
|
+
event: { type: 'bluetooth' },
|
|
78
|
+
expected: { type: 'bluetooth' }
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'useNowPlaying',
|
|
82
|
+
hook: useNowPlaying,
|
|
83
|
+
slot: 'onNowPlayingChanged',
|
|
84
|
+
event: { title: 'Song' },
|
|
85
|
+
expected: { title: 'Song' }
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'useOnline',
|
|
89
|
+
hook: useOnline,
|
|
90
|
+
slot: 'onOnlineChanged',
|
|
91
|
+
event: true,
|
|
92
|
+
expected: true
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'useCarConnected',
|
|
96
|
+
hook: useCarConnected,
|
|
97
|
+
slot: 'onCarConnectedChanged',
|
|
98
|
+
event: true,
|
|
99
|
+
expected: true
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'useEqualizerSettings',
|
|
103
|
+
hook: useEqualizerSettings,
|
|
104
|
+
slot: 'onEqualizerChanged',
|
|
105
|
+
event: { enabled: true },
|
|
106
|
+
expected: { enabled: true }
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'usePath',
|
|
110
|
+
hook: usePath,
|
|
111
|
+
slot: 'onPathChanged',
|
|
112
|
+
event: '/artists/1',
|
|
113
|
+
expected: '/artists/1'
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'useContent',
|
|
117
|
+
hook: useContent,
|
|
118
|
+
slot: 'onContentChanged',
|
|
119
|
+
event: { src: 'a' },
|
|
120
|
+
expected: { src: 'a' }
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'useTabs',
|
|
124
|
+
hook: useTabs,
|
|
125
|
+
slot: 'onTabsChanged',
|
|
126
|
+
event: [{ src: 'a' }],
|
|
127
|
+
expected: [{ src: 'a' }]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'usePlayback',
|
|
131
|
+
hook: usePlayback,
|
|
132
|
+
slot: 'onPlaybackChanged',
|
|
133
|
+
event: { state: 'playing' },
|
|
134
|
+
expected: { state: 'playing' }
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'useSystemVolume',
|
|
138
|
+
hook: useSystemVolume,
|
|
139
|
+
slot: 'onSystemVolumeChanged',
|
|
140
|
+
event: 0.4,
|
|
141
|
+
expected: 0.4
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'useProgress',
|
|
145
|
+
hook: useProgress,
|
|
146
|
+
slot: 'onPlaybackProgressUpdated',
|
|
147
|
+
event: { position: 1, duration: 2, buffered: 3 },
|
|
148
|
+
expected: { position: 1, duration: 2, buffered: 3 }
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: 'usePlayingState',
|
|
152
|
+
hook: usePlayingState,
|
|
153
|
+
slot: 'onPlaybackPlayingState',
|
|
154
|
+
event: { state: 'ready' },
|
|
155
|
+
expected: { state: 'ready' }
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'useShuffle',
|
|
159
|
+
hook: useShuffle,
|
|
160
|
+
slot: 'onPlaybackShuffleModeChanged',
|
|
161
|
+
event: true,
|
|
162
|
+
expected: true
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'useFormattedNavigationError',
|
|
166
|
+
hook: useFormattedNavigationError,
|
|
167
|
+
slot: 'onFormattedNavigationError',
|
|
168
|
+
event: { title: 'Offline' },
|
|
169
|
+
expected: { title: 'Offline' }
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'useOptions',
|
|
173
|
+
hook: useOptions,
|
|
174
|
+
slot: 'onOptionsChanged',
|
|
175
|
+
event: { playbackSpeed: 1.5 },
|
|
176
|
+
expected: { playbackSpeed: 1.5 }
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
// A field lifted out of the payload — the eventKey cases.
|
|
180
|
+
{
|
|
181
|
+
name: 'usePlaybackError',
|
|
182
|
+
hook: usePlaybackError,
|
|
183
|
+
slot: 'onPlaybackError',
|
|
184
|
+
event: { error: { code: 'network' } },
|
|
185
|
+
expected: { code: 'network' }
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'useNavigationError',
|
|
189
|
+
hook: useNavigationError,
|
|
190
|
+
slot: 'onNavigationError',
|
|
191
|
+
event: { error: { code: 'notFound' } },
|
|
192
|
+
expected: { code: 'notFound' }
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'usePlayWhenReady',
|
|
196
|
+
hook: usePlayWhenReady,
|
|
197
|
+
slot: 'onPlaybackPlayWhenReadyChanged',
|
|
198
|
+
event: { playWhenReady: true },
|
|
199
|
+
expected: true
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: 'useRepeatMode',
|
|
203
|
+
hook: useRepeatMode,
|
|
204
|
+
slot: 'onPlaybackRepeatModeChanged',
|
|
205
|
+
event: { repeatMode: 'queue' },
|
|
206
|
+
expected: 'queue'
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'useBatteryWarningPending',
|
|
210
|
+
hook: useBatteryWarningPending,
|
|
211
|
+
slot: 'onBatteryWarningPendingChanged',
|
|
212
|
+
event: { pending: true },
|
|
213
|
+
expected: true
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: 'useBatteryOptimizationStatus',
|
|
217
|
+
hook: useBatteryOptimizationStatus,
|
|
218
|
+
slot: 'onBatteryOptimizationStatusChanged',
|
|
219
|
+
event: { status: 'unrestricted' },
|
|
220
|
+
expected: 'unrestricted'
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
describe.each(wirings)('$name', ({ hook, slot, event, expected }) => {
|
|
225
|
+
it(`reads $slot`, () => {
|
|
226
|
+
// A slot nothing claimed means the emitter assigns a different property
|
|
227
|
+
// than the one named here — the hook would never update in production.
|
|
228
|
+
expect(slots.has(slot)).toBe(true)
|
|
229
|
+
|
|
230
|
+
const { result } = renderHook(() => hook())
|
|
231
|
+
act(() => slots.get(slot)!(event))
|
|
232
|
+
expect(result.current).toEqual(expected)
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
describe('hook wiring table', () => {
|
|
237
|
+
it('covers every delegating hook exactly once', () => {
|
|
238
|
+
const names = wirings.map((w) => w.name)
|
|
239
|
+
expect(new Set(names).size).toBe(names.length)
|
|
240
|
+
expect(new Set(wirings.map((w) => w.slot)).size).toBe(wirings.length)
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* `useActiveTrack` and `useQueue` are getter-refreshed: a favorite toggle
|
|
246
|
+
* mutates the active track in place WITHOUT a transition event —
|
|
247
|
+
* transitions and mutations are separate events — so
|
|
248
|
+
* these hooks re-read their native getter on the transition slot AND on
|
|
249
|
+
* `onFavoriteChanged`.
|
|
250
|
+
*/
|
|
251
|
+
const { nativeBrowser } = await import('../native')
|
|
252
|
+
|
|
253
|
+
describe.each([
|
|
254
|
+
{
|
|
255
|
+
name: 'useActiveTrack',
|
|
256
|
+
hook: useActiveTrack,
|
|
257
|
+
getter: 'getActiveTrack',
|
|
258
|
+
transitionSlot: 'onPlaybackActiveTrackChanged',
|
|
259
|
+
initial: { src: 'a', favorited: false },
|
|
260
|
+
mutated: { src: 'a', favorited: true }
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
name: 'useQueue',
|
|
264
|
+
hook: useQueue,
|
|
265
|
+
getter: 'getQueue',
|
|
266
|
+
transitionSlot: 'onPlaybackQueueChanged',
|
|
267
|
+
initial: [{ src: 'a', favorited: false }],
|
|
268
|
+
mutated: [{ src: 'a', favorited: true }]
|
|
269
|
+
}
|
|
270
|
+
])(
|
|
271
|
+
'$name (getter-refreshed)',
|
|
272
|
+
({ hook, getter, transitionSlot, initial, mutated }) => {
|
|
273
|
+
it(`re-reads ${getter} on ${transitionSlot} and onFavoriteChanged`, () => {
|
|
274
|
+
expect(slots.has(transitionSlot)).toBe(true)
|
|
275
|
+
expect(slots.has('onFavoriteChanged')).toBe(true)
|
|
276
|
+
|
|
277
|
+
const getterMock = (
|
|
278
|
+
nativeBrowser as unknown as Record<string, ReturnType<typeof vi.fn>>
|
|
279
|
+
)[getter]!
|
|
280
|
+
|
|
281
|
+
getterMock.mockReturnValue(initial)
|
|
282
|
+
const { result } = renderHook(() => hook())
|
|
283
|
+
expect(result.current).toEqual(initial)
|
|
284
|
+
|
|
285
|
+
// A transition re-reads the getter (the event payload is not consumed).
|
|
286
|
+
getterMock.mockReturnValue(mutated)
|
|
287
|
+
act(() => slots.get(transitionSlot)!({}))
|
|
288
|
+
expect(result.current).toEqual(mutated)
|
|
289
|
+
|
|
290
|
+
// A favorite toggle mutates in place — no transition event fires, but
|
|
291
|
+
// the hook must still refresh.
|
|
292
|
+
getterMock.mockReturnValue(initial)
|
|
293
|
+
act(() =>
|
|
294
|
+
slots.get('onFavoriteChanged')!({
|
|
295
|
+
track: { src: 'a' },
|
|
296
|
+
favorited: false
|
|
297
|
+
})
|
|
298
|
+
)
|
|
299
|
+
expect(result.current).toEqual(initial)
|
|
300
|
+
})
|
|
301
|
+
}
|
|
302
|
+
)
|
package/src/features/metadata.ts
CHANGED
|
@@ -98,8 +98,13 @@ export interface ChapterMetadata {
|
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
100
|
* Subscribes to chapter metadata events.
|
|
101
|
+
*
|
|
102
|
+
* @platform ios android
|
|
103
|
+
* Never fires on web — the web player does not read chapter markers. See
|
|
104
|
+
* {@link https://github.com/radio-garden/react-native-audio-browser/issues/118 | #118}.
|
|
105
|
+
*
|
|
101
106
|
* @param callback - Called when chapter metadata is received
|
|
102
|
-
* @returns
|
|
107
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
103
108
|
*/
|
|
104
109
|
export const onChapterMetadata = NativeUpdatedValue.emitterize<
|
|
105
110
|
ChapterMetadata[]
|
|
@@ -107,8 +112,13 @@ export const onChapterMetadata = NativeUpdatedValue.emitterize<
|
|
|
107
112
|
|
|
108
113
|
/**
|
|
109
114
|
* Subscribes to track metadata events.
|
|
115
|
+
*
|
|
116
|
+
* @platform ios android
|
|
117
|
+
* Never fires on web — the web player does not read embedded file tags. See
|
|
118
|
+
* {@link https://github.com/radio-garden/react-native-audio-browser/issues/118 | #118}.
|
|
119
|
+
*
|
|
110
120
|
* @param callback - Called when static track metadata is received
|
|
111
|
-
* @returns
|
|
121
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
112
122
|
*/
|
|
113
123
|
export const onTrackMetadata = NativeUpdatedValue.emitterize<TrackMetadata>(
|
|
114
124
|
(cb) => (nativeBrowser.onTrackMetadata = cb)
|
|
@@ -116,8 +126,15 @@ export const onTrackMetadata = NativeUpdatedValue.emitterize<TrackMetadata>(
|
|
|
116
126
|
|
|
117
127
|
/**
|
|
118
128
|
* Subscribes to timed metadata events (ICY/ID3 from live streams).
|
|
129
|
+
*
|
|
130
|
+
* @platform ios android
|
|
131
|
+
* Never fires on web. ICY metadata is stripped by the browser before the player
|
|
132
|
+
* can see it, and no browser API exposes it; in-band ID3 from HLS is reachable
|
|
133
|
+
* but not yet wired. See
|
|
134
|
+
* {@link https://github.com/radio-garden/react-native-audio-browser/issues/118 | #118}.
|
|
135
|
+
*
|
|
119
136
|
* @param callback - Called when stream metadata is received (title, artist from ICY/ID3 tags)
|
|
120
|
-
* @returns
|
|
137
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
121
138
|
*/
|
|
122
139
|
export const onTimedMetadata = NativeUpdatedValue.emitterize<TimedMetadata>(
|
|
123
140
|
(cb) => (nativeBrowser.onTimedMetadata = cb)
|
package/src/features/network.ts
CHANGED
|
@@ -17,7 +17,7 @@ export function getOnline(): boolean {
|
|
|
17
17
|
/**
|
|
18
18
|
* Subscribes to network connectivity changes.
|
|
19
19
|
* @param callback - Called when network state changes
|
|
20
|
-
* @returns
|
|
20
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
21
21
|
*/
|
|
22
22
|
export const onOnlineChanged = NativeUpdatedValue.emitterize<boolean>(
|
|
23
23
|
(cb) => (nativeBrowser.onOnlineChanged = cb)
|
|
@@ -74,7 +74,7 @@ export function clearNowPlayingFlash(): void {
|
|
|
74
74
|
* Subscribes to now playing metadata change events.
|
|
75
75
|
* Fires when updateNowPlaying is called or when the track changes.
|
|
76
76
|
* @param callback - Called when now playing metadata changes
|
|
77
|
-
* @returns
|
|
77
|
+
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
78
78
|
*/
|
|
79
79
|
export const onNowPlayingChanged =
|
|
80
80
|
NativeUpdatedValue.emitterize<NowPlayingMetadata>(
|