react-native-audio-browser 0.2.0-next.11 → 0.2.0-next.13
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 +14 -4
- package/android/build.gradle +20 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +115 -26
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +262 -188
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +23 -3
- package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +95 -58
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +68 -26
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +19 -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/model/PlayerUpdateOptions.kt +1 -1
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +120 -67
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -24
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +109 -74
- package/android/src/main/java/com/audiobrowser/player/Player.kt +51 -30
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
- package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +44 -40
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +52 -53
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +74 -41
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +229 -50
- 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 +1 -1
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -53
- 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/player/CapabilityControlsTest.kt +1 -2
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +161 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
- package/ios/Browser/BrowserConfig.swift +102 -5
- package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
- package/ios/Browser/BrowserManager.swift +229 -180
- package/ios/Browser/BrowserPathHelper.swift +44 -44
- package/ios/Browser/InertStyleDiagnostic.swift +279 -0
- package/ios/Browser/JsonModels.swift +258 -106
- package/ios/Browser/SearchDrillIn.swift +6 -6
- 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 +38 -18
- package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
- package/ios/Extension/ResolvedTrack+Copying.swift +11 -15
- package/ios/Extension/Section+Copying.swift +31 -0
- package/ios/Extension/Track+Copying.swift +6 -12
- package/ios/Extension/Track+Identity.swift +18 -0
- package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
- package/ios/Http/HttpClient.swift +8 -2
- package/ios/HybridAudioBrowser.swift +166 -45
- package/ios/Model/NitroTypeStubs.swift +106 -21
- package/ios/Model/PlayerUpdateOptions.swift +7 -0
- package/ios/Player/MediaIntentCriteria.swift +13 -5
- package/ios/Player/MediaLoader.swift +33 -9
- package/ios/Player/NowPlayingUpdater.swift +54 -6
- package/ios/Player/PlaybackCoordinator.swift +13 -6
- package/ios/Player/PlaybackTimer.swift +8 -2
- package/ios/Player/QueueManager.swift +36 -10
- package/ios/Player/RetryManager.swift +4 -3
- package/ios/Player/ShuffleOrder.swift +23 -0
- package/ios/Player/SleepTimerHandling.swift +13 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +53 -18
- 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/PlaybackStateStoreTests.swift +4 -3
- package/ios/Tests/QueueManagerTests.swift +72 -6
- package/ios/Tests/RemoteCommandControllerTests.swift +16 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +37 -35
- 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 +7 -0
- package/ios/Tests/TabBarEntriesTests.swift +18 -18
- package/ios/Tests/TrackIdTemplateTests.swift +67 -0
- package/ios/Tests/TrackIdentityTests.swift +20 -0
- package/ios/Tests/TrackSelectorTests.swift +168 -33
- package/ios/TrackPlayer.swift +57 -34
- package/ios/TrackSelector.swift +45 -30
- package/ios/Util/ArtworkImageFetcher.swift +16 -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 +11 -0
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/features/browser-config.js +11 -8
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/browser.js +2 -2
- package/lib/commonjs/features/favorites.js +13 -10
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/playback/controls.js +4 -2
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/progress.js +2 -1
- 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 +2 -3
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +3 -1
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +2 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -1
- package/lib/commonjs/features/queue/activeTrack.js +14 -3
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +11 -4
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/utils/LazyNativeEmitter.js +21 -10
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/getTrackIdentity.js +24 -0
- package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +32 -24
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +144 -87
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
- package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/commonjs/web/browser/sections.js +89 -0
- package/lib/commonjs/web/browser/sections.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
- package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/module/AudioBrowser.js +1 -0
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/features/browser-config.js +11 -8
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/browser.js +2 -2
- package/lib/module/features/favorites.js +13 -10
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/playback/controls.js +4 -2
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/progress.js +2 -1
- 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 +2 -3
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +3 -1
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +2 -0
- package/lib/module/features/player/validateOptions.js.map +1 -1
- package/lib/module/features/queue/activeTrack.js +14 -3
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +11 -4
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/utils/LazyNativeEmitter.js +21 -10
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +31 -13
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/getTrackIdentity.js +20 -0
- package/lib/module/utils/getTrackIdentity.js.map +1 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +32 -24
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +144 -87
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/FavoriteManager.js +31 -21
- package/lib/module/web/browser/FavoriteManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/module/web/browser/sections.js +83 -0
- package/lib/module/web/browser/sections.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +4 -3
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/util/BrowserPathHelper.js +56 -11
- package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/typescript/src/AudioBrowser.d.ts +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
- package/lib/typescript/src/features/browser.d.ts +2 -2
- package/lib/typescript/src/features/favorites.d.ts +13 -10
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +4 -2
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/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 +2 -3
- package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
- package/lib/typescript/src/features/player/options.d.ts +21 -19
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +1 -8
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts +8 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +5 -2
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-native.d.ts +2 -2
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +361 -126
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +33 -53
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +16 -17
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
- package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +0 -1
- package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +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__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 +2 -5
- 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 +33 -14
- 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 +22 -10
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +10 -7
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +55 -51
- package/nitrogen/generated/android/c++/JSection.hpp +120 -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 +23 -48
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
- 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 +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -18
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +18 -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 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +158 -82
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +25 -10
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +25 -10
- 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/GridTile.swift +44 -0
- 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 +5 -5
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +54 -83
- package/nitrogen/generated/ios/swift/Section.swift +100 -0
- package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
- package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +20 -73
- package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
- package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
- package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
- package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
- package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
- 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 +6 -6
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +30 -38
- package/nitrogen/generated/shared/c++/Section.hpp +106 -0
- package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
- package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +15 -31
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
- package/package.json +7 -2
- package/src/AudioBrowser.ts +1 -0
- package/src/features/browser-config.test.ts +21 -6
- package/src/features/browser-config.ts +11 -8
- package/src/features/browser.test.ts +1 -1
- package/src/features/browser.ts +2 -2
- package/src/features/favorites.ts +13 -10
- package/src/features/hook-wiring.test.ts +63 -15
- package/src/features/playback/controls.ts +4 -2
- package/src/features/playback/progress.ts +2 -1
- package/src/features/playback/rate.ts +8 -1
- package/src/features/playback/state.ts +2 -3
- package/src/features/playback/trackPlaybackTime.ts +1 -1
- package/src/features/player/options.ts +27 -19
- package/src/features/player/setup.test.ts +12 -3
- package/src/features/player/setup.ts +3 -1
- package/src/features/player/validateOptions.ts +2 -0
- package/src/features/queue/activeTrack.ts +14 -3
- package/src/features/queue/queue.ts +11 -4
- package/src/nitro-stub-drift.test.ts +40 -0
- package/src/public-surface.test.ts +40 -0
- package/src/specs/audio-browser.nitro.ts +4 -3
- package/src/style-resolution.test.ts +210 -0
- package/src/types/browser-native.ts +2 -2
- package/src/types/browser-nodes.ts +377 -135
- package/src/types/browser.ts +34 -55
- package/src/utils/LazyNativeEmitter.ts +20 -10
- package/src/utils/NativeUpdatedValue.ts +29 -13
- package/src/utils/getTrackIdentity.ts +20 -0
- package/src/utils/useNativeValueRefreshedBy.ts +38 -0
- package/src/web/NativeAudioBrowser.ts +37 -22
- package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
- package/src/web/TrackPlayer/QueueManager.ts +14 -0
- package/src/web/browser/BrowserManager.ts +176 -95
- package/src/web/browser/FavoriteManager.test.ts +84 -0
- package/src/web/browser/FavoriteManager.ts +28 -23
- package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
- package/src/web/browser/assertBrowsePageShape.ts +5 -3
- package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
- package/src/web/browser/classifyTrackNavigation.ts +9 -9
- package/src/web/browser/parseSearchResponse.test.ts +8 -2
- package/src/web/browser/sections.test.ts +91 -0
- package/src/web/browser/sections.ts +72 -0
- package/src/web/player/NowPlayingManager.ts +4 -3
- package/src/web/util/BrowserPathHelper.test.ts +52 -0
- package/src/web/util/BrowserPathHelper.ts +61 -13
- package/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 +246 -0
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
- package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Porting from react-native-track-player v4: what carries over unchanged, what was renamed or reshaped, and what is new.'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Migrating from react-native-track-player
|
|
6
|
+
|
|
7
|
+
If you're coming from [react-native-track-player](https://github.com/doublesymmetry/react-native-track-player/tree/v4)
|
|
8
|
+
(RNTP) v4, most of your playback and queue code ports over almost unchanged — the
|
|
9
|
+
function names and semantics were deliberately kept familiar. This page maps the
|
|
10
|
+
parts that _do_ change.
|
|
11
|
+
|
|
12
|
+
`react-native-audio-browser` started as a fork of RNTP v4, and we wrote much of
|
|
13
|
+
that original code. We've since rethought and rewritten it almost entirely. The
|
|
14
|
+
API stays familiar by lineage, not coincidence, but almost everything underneath
|
|
15
|
+
is new. RNTP has itself moved to a commercial license; `react-native-audio-browser`
|
|
16
|
+
stays MIT.
|
|
17
|
+
|
|
18
|
+
## What ports over unchanged
|
|
19
|
+
|
|
20
|
+
These are the same calls with the same meaning — `import` them from
|
|
21
|
+
`react-native-audio-browser` instead of `react-native-track-player`:
|
|
22
|
+
|
|
23
|
+
[`setupPlayer`](/api/features/player/#setupplayer), [`updateOptions`](/api/features/player/#updateoptions),
|
|
24
|
+
[`add`](/api/features/queue/#add), [`remove`](/api/features/queue/#remove), [`move`](/api/features/queue/#move),
|
|
25
|
+
[`skip`](/api/features/queue/#skip), [`skipToNext`](/api/features/queue/#skiptonext),
|
|
26
|
+
[`skipToPrevious`](/api/features/queue/#skiptoprevious), [`setQueue`](/api/features/queue/#setqueue),
|
|
27
|
+
[`getQueue`](/api/features/queue/#getqueue), [`getTrack`](/api/features/queue/#gettrack),
|
|
28
|
+
[`removeUpcomingTracks`](/api/features/queue/#removeupcomingtracks), [`load`](/api/features/queue/#load),
|
|
29
|
+
[`reset`](/api/features/playback/#reset), [`retry`](/api/features/playback/#retry),
|
|
30
|
+
[`play`](/api/features/playback/#play), [`pause`](/api/features/playback/#pause),
|
|
31
|
+
[`stop`](/api/features/playback/#stop), [`seekTo`](/api/features/playback/#seekto),
|
|
32
|
+
[`seekBy`](/api/features/playback/#seekby), [`setRate`](/api/features/playback/#setrate),
|
|
33
|
+
[`getRate`](/api/features/playback/#getrate), [`setVolume`](/api/features/playback/#setvolume),
|
|
34
|
+
[`getVolume`](/api/features/playback/#getvolume), [`setRepeatMode`](/api/features/queue/#setrepeatmode),
|
|
35
|
+
[`getRepeatMode`](/api/features/queue/#getrepeatmode), [`setPlayWhenReady`](/api/features/playback/#setplaywhenready),
|
|
36
|
+
[`getPlayWhenReady`](/api/features/playback/#getplaywhenready), [`getActiveTrack`](/api/features/queue/#getactivetrack),
|
|
37
|
+
[`getActiveTrackIndex`](/api/features/queue/#getactivetrackindex).
|
|
38
|
+
|
|
39
|
+
The differences below are what to watch for.
|
|
40
|
+
|
|
41
|
+
## Getters are synchronous — and there are hooks
|
|
42
|
+
|
|
43
|
+
RNTP getters return Promises; audio-browser getters return the value
|
|
44
|
+
**synchronously**, and most have a reactive **hook**. Drop the `await` (awaiting a
|
|
45
|
+
non-Promise still works, so this isn't urgent), and prefer the hook in React.
|
|
46
|
+
|
|
47
|
+
| RNTP v4 | audio-browser |
|
|
48
|
+
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `await getPosition()` | [`getProgress().position`](/api/features/playback/#position) |
|
|
50
|
+
| `await getDuration()` | [`getProgress().duration`](/api/features/playback/#duration) |
|
|
51
|
+
| `await getBufferedPosition()` | [`getProgress().buffered`](/api/features/playback/#buffered) |
|
|
52
|
+
| `await getProgress()` | [`getProgress()`](/api/features/playback/#getprogress) (sync) / [`usePolledProgress()`](/api/features/playback/#usepolledprogress) |
|
|
53
|
+
| `await getState()` / `getPlaybackState()` | [`getPlayback().state`](/api/features/playback/#state) / [`usePlayback()`](/api/features/playback/#useplayback) |
|
|
54
|
+
| `await getActiveTrack()` | [`getActiveTrack()`](/api/features/queue/#getactivetrack) / [`useActiveTrack()`](/api/features/queue/#useactivetrack) |
|
|
55
|
+
| `await getQueue()` | [`getQueue()`](/api/features/queue/#getqueue) / [`useQueue()`](/api/features/queue/#usequeue) |
|
|
56
|
+
| `await getRepeatMode()` | [`getRepeatMode()`](/api/features/queue/#getrepeatmode) / [`useRepeatMode()`](/api/features/queue/#userepeatmode) |
|
|
57
|
+
|
|
58
|
+
Two hook gotchas:
|
|
59
|
+
|
|
60
|
+
- **`useProgress` changed meaning.** RNTP's `useProgress(interval)` is polling;
|
|
61
|
+
its drop-in is [`usePolledProgress(intervalMs)`](/api/features/playback/#usepolledprogress).
|
|
62
|
+
audio-browser _also_ has a [`useProgress()`](/api/features/playback/#useprogress), but it's **event-based and takes no
|
|
63
|
+
interval** (and needs `progressUpdateEventInterval` enabled) — so copying
|
|
64
|
+
`useProgress(2000)` over compiles but silently ignores the arg. Use
|
|
65
|
+
`usePolledProgress` if you relied on the polling interval.
|
|
66
|
+
- **`useIsPlaying()` → [`usePlayingState()`](/api/features/playback/#useplayingstate)**,
|
|
67
|
+
but the field is renamed: RNTP's `{ playing, bufferingDuringPlay }` becomes
|
|
68
|
+
`{ playing, buffering }`.
|
|
69
|
+
|
|
70
|
+
## Events: per-event `on*` / `use*`, no `Event` enum
|
|
71
|
+
|
|
72
|
+
RNTP centralizes events behind the `Event` enum with `addEventListener` /
|
|
73
|
+
`useTrackPlayerEvents`. audio-browser exposes one emitter (and usually a hook)
|
|
74
|
+
per event, and `addListener` returns an **unsubscribe function** (not a
|
|
75
|
+
subscription with `.remove()`):
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// RNTP
|
|
79
|
+
import TrackPlayer, { Event } from 'react-native-track-player'
|
|
80
|
+
const sub = TrackPlayer.addEventListener(
|
|
81
|
+
Event.PlaybackActiveTrackChanged,
|
|
82
|
+
(e) => {}
|
|
83
|
+
)
|
|
84
|
+
sub.remove()
|
|
85
|
+
|
|
86
|
+
// audio-browser
|
|
87
|
+
import { onActiveTrackChanged } from 'react-native-audio-browser'
|
|
88
|
+
const unsubscribe = onActiveTrackChanged.addListener((e) => {})
|
|
89
|
+
unsubscribe()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| RNTP `Event.*` | audio-browser |
|
|
93
|
+
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| `PlaybackActiveTrackChanged` | [`onActiveTrackChanged`](/api/features/queue/#onactivetrackchanged) / [`useActiveTrack()`](/api/features/queue/#useactivetrack) |
|
|
95
|
+
| `PlaybackState` | [`onPlaybackChanged`](/api/features/playback/#onplaybackchanged) / [`usePlayback()`](/api/features/playback/#useplayback) |
|
|
96
|
+
| `PlaybackProgressUpdated` | [`onProgressUpdated`](/api/features/playback/#onprogressupdated) / [`useProgress()`](/api/features/playback/#useprogress) |
|
|
97
|
+
| `PlaybackQueueEnded` | [`onQueueEnded`](/api/features/queue/#onqueueended) |
|
|
98
|
+
| `PlaybackError` / `PlayerError` | [`onPlaybackError`](/api/features/errors/#onplaybackerror) / [`usePlaybackError()`](/api/features/errors/#useplaybackerror) |
|
|
99
|
+
| `PlaybackPlayWhenReadyChanged` | [`onPlayWhenReadyChanged`](/api/features/playback/#onplaywhenreadychanged) / [`usePlayWhenReady()`](/api/features/playback/#useplaywhenready) |
|
|
100
|
+
| `Remote*` (Play, Pause, Next, …) | `handleRemote*` to override, `onRemote*` to observe — see [Remote Controls](/guide/remote-controls) |
|
|
101
|
+
| `Metadata*Received` | [`onTimedMetadata`](/api/features/metadata/#ontimedmetadata) / [`onChapterMetadata`](/api/features/metadata/#onchaptermetadata) / [`onTrackMetadata`](/api/features/metadata/#ontrackmetadata) — see [Metadata](/guide/metadata) |
|
|
102
|
+
|
|
103
|
+
## Delete the playback service
|
|
104
|
+
|
|
105
|
+
RNTP requires `registerPlaybackService(() => require('./service'))` and a service
|
|
106
|
+
file that wires `Event.RemotePlay → TrackPlayer.play()`, etc. **audio-browser has
|
|
107
|
+
no playback service** — the library wires the native side itself, and remote
|
|
108
|
+
controls drive the player by default. Delete the service file and the
|
|
109
|
+
registration call. To customize a remote control, set a
|
|
110
|
+
[`handleRemote*`](/guide/remote-controls) override (pass `undefined` to restore
|
|
111
|
+
the default); to just observe, use `onRemote*`:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { handleRemoteNext, skipToNext } from 'react-native-audio-browser'
|
|
115
|
+
|
|
116
|
+
// Override "next" (call the default action yourself if you still want it):
|
|
117
|
+
handleRemoteNext(() => skipToNext())
|
|
118
|
+
handleRemoteNext(undefined) // restore the default
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## `capabilities`: an object, not an array
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
// RNTP
|
|
125
|
+
import { Capability } from 'react-native-track-player'
|
|
126
|
+
await TrackPlayer.updateOptions({
|
|
127
|
+
capabilities: [Capability.Play, Capability.Pause, Capability.SkipToNext]
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
// audio-browser — most are ON by default; specify only what differs
|
|
131
|
+
updateOptions({ capabilities: { skipToPrevious: false } })
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Pass `capabilities` to **either** [`setupPlayer`](/api/features/player/#setupplayer)
|
|
135
|
+
(initial) or [`updateOptions`](/api/features/player/#updateoptions) (later) — both
|
|
136
|
+
accept it. See
|
|
137
|
+
[Configuration → Capabilities](/guide/configuration#capabilities) for the
|
|
138
|
+
defaults (everything on except `jumpForward`, `jumpBackward`, `favorite`).
|
|
139
|
+
`Capability.Like` / `RatingType` map to the [`favorite`](/guide/favorites)
|
|
140
|
+
capability.
|
|
141
|
+
|
|
142
|
+
## Enums become string literals
|
|
143
|
+
|
|
144
|
+
audio-browser has no enum objects — use the string values directly:
|
|
145
|
+
|
|
146
|
+
| RNTP | audio-browser |
|
|
147
|
+
| ------------------------------------ | --------------------------------------------- |
|
|
148
|
+
| `State.Playing`, `State.Paused`, … | `'playing'`, `'paused'`, … (same strings) |
|
|
149
|
+
| `RepeatMode.Off` / `Track` / `Queue` | `'off'` / `'track'` / `'queue'` |
|
|
150
|
+
| `Capability.Play` (in an array) | `{ play: true }` (in the capabilities object) |
|
|
151
|
+
|
|
152
|
+
`State`'s string values are identical, so `state === 'playing'` checks port
|
|
153
|
+
directly — you just drop the enum import.
|
|
154
|
+
|
|
155
|
+
## `Track`: `url` → `src`
|
|
156
|
+
|
|
157
|
+
The playable URL field is renamed. In audio-browser, the **playable source**
|
|
158
|
+
is [`src`](/api/types/browser-nodes/#src); a track's **browse address** (for the
|
|
159
|
+
[browse tree](/guide/browser)) is a separate field, `path`:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
// RNTP
|
|
163
|
+
{
|
|
164
|
+
url: ('https://cdn.example.com/song.mp3', title, artist, artwork)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// audio-browser
|
|
168
|
+
{
|
|
169
|
+
src: ('https://cdn.example.com/song.mp3', title, artist, artwork)
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Other field changes: `isLiveStream` → [`live`](/api/types/browser-nodes/#live); per-track `headers`
|
|
174
|
+
move to a [media request config](/guide/browser); `rating` → favorites (see
|
|
175
|
+
above). `title` / `artist` / `album` / `artwork` / `duration` are unchanged.
|
|
176
|
+
|
|
177
|
+
## Now-playing metadata
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
// RNTP
|
|
181
|
+
TrackPlayer.updateNowPlayingMetadata({ title, artist })
|
|
182
|
+
TrackPlayer.clearNowPlayingMetadata()
|
|
183
|
+
|
|
184
|
+
// audio-browser
|
|
185
|
+
import { updateNowPlaying } from 'react-native-audio-browser'
|
|
186
|
+
updateNowPlaying({ title, artist })
|
|
187
|
+
updateNowPlaying(null) // clear
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
For live streams, prefer the [Now Playing formatter](/guide/now-playing) over
|
|
191
|
+
imperative updates — it re-derives the lock-screen lines on every metadata /
|
|
192
|
+
state change.
|
|
193
|
+
|
|
194
|
+
## Setup, before and after
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
// RNTP — index.js + startup
|
|
198
|
+
TrackPlayer.registerPlaybackService(() => require('./service'))
|
|
199
|
+
await TrackPlayer.setupPlayer()
|
|
200
|
+
await TrackPlayer.updateOptions({
|
|
201
|
+
capabilities: [Capability.Play, Capability.Pause, Capability.SkipToNext],
|
|
202
|
+
progressUpdateEventInterval: 2
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
// audio-browser — one declarative call, no service
|
|
208
|
+
import { setupPlayer } from 'react-native-audio-browser'
|
|
209
|
+
|
|
210
|
+
await setupPlayer({
|
|
211
|
+
progressUpdateEventInterval: 2
|
|
212
|
+
// capabilities are on by default; nothing to register
|
|
213
|
+
})
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
See [Configuration](/guide/configuration) for the full options surface and what's
|
|
217
|
+
setup-only vs runtime-updatable.
|
|
218
|
+
|
|
219
|
+
## What you gain
|
|
220
|
+
|
|
221
|
+
audio-browser adds capabilities RNTP doesn't have — worth adopting once you've
|
|
222
|
+
ported:
|
|
223
|
+
|
|
224
|
+
- A [**browse tree**](/guide/browser) that powers native **CarPlay** and
|
|
225
|
+
**Android Auto** menus (and voice [search](/guide/search)) — see the
|
|
226
|
+
[Automotive overview](/guide/automotive).
|
|
227
|
+
- A [**now-playing formatter**](/guide/now-playing) for live-stream metadata and
|
|
228
|
+
transient status lines.
|
|
229
|
+
- [**Content gating**](/guide/gate), a [**sleep timer**](/guide/sleep-timer),
|
|
230
|
+
an [**equalizer**](/guide/equalizer), and [**favorites**](/guide/favorites).
|
|
231
|
+
|
|
232
|
+
## Quick reference
|
|
233
|
+
|
|
234
|
+
| Area | RNTP v4 | audio-browser |
|
|
235
|
+
| --------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- |
|
|
236
|
+
| Getters | `await TrackPlayer.getX()` | `getX()` (sync) or `useX()` |
|
|
237
|
+
| Events | `Event` + `addEventListener` / `useTrackPlayerEvents` | per-event `onX` / `useX` |
|
|
238
|
+
| Unsubscribe | `subscription.remove()` | the function `addListener` returns |
|
|
239
|
+
| Remote handling | a registered playback **service** | default behavior + `handleRemote*` |
|
|
240
|
+
| Capabilities | `Capability[]` | `capabilities` object of booleans |
|
|
241
|
+
| Enums | `State.*`, `RepeatMode.*` | string literals (`'playing'`, `'queue'`) |
|
|
242
|
+
| Playable URL | `track.url` | [`track.src`](/api/types/browser-nodes/#src) |
|
|
243
|
+
| Live flag | `track.isLiveStream` | [`track.live`](/api/types/browser-nodes/#live) |
|
|
244
|
+
| Now playing | `updateNowPlayingMetadata` / `clearNowPlayingMetadata` | [`updateNowPlaying`](/api/features/nowPlaying/#updatenowplaying)`(update \| null)` |
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Why React Native's fetch hangs inside browse resolvers, search sources, and request transforms when the device is asleep, and what to use instead."
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Networking in native callbacks (don't use `fetch`)
|
|
6
|
+
|
|
7
|
+
**If a browse route resolver, search source, or request `transform` does its own
|
|
8
|
+
networking with React Native's built-in `fetch` — and it works in your app but
|
|
9
|
+
_hangs_ when the device is asleep (browsing on CarPlay or Android Auto with the
|
|
10
|
+
screen off, or answering a voice intent) — you are in the right place.** The
|
|
11
|
+
browse screen goes blank because the request never resolves. The fix is to fetch
|
|
12
|
+
with
|
|
13
|
+
[`react-native-nitro-fetch`](https://github.com/margelo/react-native-nitro-fetch),
|
|
14
|
+
or to let the library do the request natively.
|
|
15
|
+
|
|
16
|
+
## The problem
|
|
17
|
+
|
|
18
|
+
The library invokes the async callbacks you provide **from native code**,
|
|
19
|
+
whenever the system asks for content — including while your app is asleep:
|
|
20
|
+
|
|
21
|
+
- browse **route resolvers** (`configureBrowser({ routes })`),
|
|
22
|
+
- the **search source** (`configureBrowser({ search })`),
|
|
23
|
+
- request **`transform`** callbacks.
|
|
24
|
+
|
|
25
|
+
A locked iPhone browsing your library on CarPlay, or a "play jazz" voice command
|
|
26
|
+
with the screen dark, runs these callbacks with the app backgrounded. The first
|
|
27
|
+
native call into JS runs fine — but the moment your callback `await`s a `fetch`,
|
|
28
|
+
it hangs until the phone wakes.
|
|
29
|
+
|
|
30
|
+
### Why `fetch` hangs while asleep
|
|
31
|
+
|
|
32
|
+
React Native's `fetch` is the [`whatwg-fetch`](https://github.com/JakeChampion/fetch)
|
|
33
|
+
polyfill, and it resolves its promise with a **zero-delay timer**, not a
|
|
34
|
+
microtask:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
// inside whatwg-fetch
|
|
38
|
+
setTimeout(function () {
|
|
39
|
+
resolve(new Response(body, options))
|
|
40
|
+
}, 0)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
When the device is asleep, React Native's timer module is paused — on iOS,
|
|
44
|
+
`RCTTiming` pauses the `CADisplayLink` that drives JS timers — so `setTimeout`
|
|
45
|
+
callbacks don't fire. Native networking (`RCTNetworking`) **does** perform the
|
|
46
|
+
request and receive the response, but the `setTimeout(0)` that would hand it back
|
|
47
|
+
to your `await` never runs. The promise sits unresolved until the screen wakes,
|
|
48
|
+
and your browse list spins or shows blank. Android suspends JS timers the same
|
|
49
|
+
way while the app is backgrounded behind Android Auto, so the symptom and the fix
|
|
50
|
+
are identical there.
|
|
51
|
+
|
|
52
|
+
This isn't specific to this library — it affects any `fetch` on a sleeping
|
|
53
|
+
device, and upgrading doesn't help: current React Native still ships
|
|
54
|
+
`whatwg-fetch`.
|
|
55
|
+
|
|
56
|
+
## The fix: `react-native-nitro-fetch`
|
|
57
|
+
|
|
58
|
+
[`react-native-nitro-fetch`](https://github.com/margelo/react-native-nitro-fetch)
|
|
59
|
+
is a drop-in `fetch` replacement that runs the request on a native thread
|
|
60
|
+
(`URLSession` on iOS, Cronet on Android) and resolves through **Nitro's
|
|
61
|
+
microtask queue** — no `setTimeout`, no `whatwg-fetch`, no `RCTTiming`
|
|
62
|
+
dependency. So it completes even when the callback runs while the device is
|
|
63
|
+
asleep. Since this library is already a Nitro module, it's a natural fit.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install react-native-nitro-fetch react-native-nitro-modules
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
You already have `react-native-nitro-modules` (it's a peer dependency of this
|
|
70
|
+
library); you'll need a native rebuild (`pod install` + rebuild) after
|
|
71
|
+
installing.
|
|
72
|
+
|
|
73
|
+
Then add one import at the top of any module with a native-invoked callback — it
|
|
74
|
+
shadows the global `fetch`, so the call sites don't change:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { configureBrowser } from 'react-native-audio-browser'
|
|
78
|
+
import { fetch } from 'react-native-nitro-fetch'
|
|
79
|
+
|
|
80
|
+
configureBrowser({
|
|
81
|
+
tabs: [{ title: 'Browse', path: '/browse' }],
|
|
82
|
+
routes: {
|
|
83
|
+
'/browse': async ({ path }) => {
|
|
84
|
+
const res = await fetch('https://api.example.com/browse')
|
|
85
|
+
const { children } = await res.json()
|
|
86
|
+
return { path: path, title: 'Browse', children }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use the same import in your search source and request `transform`s.
|
|
93
|
+
|
|
94
|
+
## Even simpler: let the library fetch natively
|
|
95
|
+
|
|
96
|
+
If a route just maps a path to an HTTP request, you don't have to write a JS
|
|
97
|
+
callback at all. Give the browse source a `baseUrl` and the **library performs
|
|
98
|
+
the request natively** for each navigated path — no JS networking, nothing to
|
|
99
|
+
stall:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
configureBrowser({
|
|
103
|
+
// Each browse path is fetched over HTTP on the native side.
|
|
104
|
+
browse: { baseUrl: 'https://api.example.com' },
|
|
105
|
+
tabs: [{ title: 'Browse', path: '/browse' }]
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The navigated path is appended to `baseUrl`; to remap or shape the request, use a
|
|
110
|
+
[`transform`](/guide/browser) (still native). The search source accepts the same
|
|
111
|
+
request-config form, so it can be served natively too. Reach for
|
|
112
|
+
`react-native-nitro-fetch` only when you need logic a request config can't
|
|
113
|
+
express (combining sources, reading local storage, custom shaping).
|
|
114
|
+
|
|
115
|
+
## When it's safe to use plain `fetch`
|
|
116
|
+
|
|
117
|
+
This only affects code the library invokes from native while the app is asleep.
|
|
118
|
+
Networking driven by **your own app UI in the foreground** — a screen the user is
|
|
119
|
+
looking at — runs on the normal JS loop and is unaffected; plain `fetch` is fine
|
|
120
|
+
there. Rule of thumb: if the OS can run the code with the screen off, don't
|
|
121
|
+
depend on `fetch`.
|
|
122
|
+
|
|
123
|
+
## Further reading
|
|
124
|
+
|
|
125
|
+
- [`react-native-nitro-fetch`](https://github.com/margelo/react-native-nitro-fetch)
|
|
126
|
+
- [JS timers don't fire when the app is backgrounded (RN #38711)](https://github.com/facebook/react-native/issues/38711)
|
|
127
|
+
- [Timers & fetch freeze behind a native activity (RN #54534)](https://github.com/facebook/react-native/issues/54534)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Track the device''s validated internet connectivity with useOnline, getOnline, and onOnlineChanged, and what "online" means on each platform.'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Network
|
|
6
|
+
|
|
7
|
+
The library tracks the device's internet connectivity with a native monitor and
|
|
8
|
+
exposes it three ways:
|
|
9
|
+
|
|
10
|
+
- [`useOnline()`](/api/features/network/#useonline) — a reactive hook for your UI.
|
|
11
|
+
- [`getOnline()`](/api/features/network/#getonline) — a one-off snapshot.
|
|
12
|
+
- [`onOnlineChanged`](/api/features/network/#ononlinechanged) — subscribe to
|
|
13
|
+
changes outside React.
|
|
14
|
+
|
|
15
|
+
All three report the same boolean: `true` when the device has a working internet
|
|
16
|
+
connection. **"Online" means _validated_ internet, not just "connected to a
|
|
17
|
+
network".** Android is strict here — it requires the OS to confirm the network
|
|
18
|
+
actually reaches the internet, so Wi-Fi with no real connectivity reads as
|
|
19
|
+
`false`. iOS derives the signal from the system path monitor (`NWPathMonitor`),
|
|
20
|
+
which is slightly more permissive about such captive networks.
|
|
21
|
+
|
|
22
|
+
The UI snippet imports `Text` from `react-native`; everything else is from
|
|
23
|
+
`react-native-audio-browser`.
|
|
24
|
+
|
|
25
|
+
## In your UI
|
|
26
|
+
|
|
27
|
+
`useOnline` re-renders when connectivity flips — enough for an offline banner:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { Text } from 'react-native'
|
|
31
|
+
import { useOnline } from 'react-native-audio-browser'
|
|
32
|
+
|
|
33
|
+
function OfflineBanner() {
|
|
34
|
+
const online = useOnline()
|
|
35
|
+
if (online) return null
|
|
36
|
+
return <Text>You're offline</Text>
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Outside React
|
|
41
|
+
|
|
42
|
+
Read a snapshot with `getOnline()`, or subscribe with `onOnlineChanged`
|
|
43
|
+
(`addListener` returns an unsubscribe function):
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { getOnline, onOnlineChanged } from 'react-native-audio-browser'
|
|
47
|
+
|
|
48
|
+
getOnline() // → true | false
|
|
49
|
+
|
|
50
|
+
const unsubscribe = onOnlineChanged.addListener((online) => {
|
|
51
|
+
if (online) refetchSomething()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// later:
|
|
55
|
+
unsubscribe()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## You may not need this
|
|
59
|
+
|
|
60
|
+
The player already reacts to connectivity on its own, so you usually don't wire
|
|
61
|
+
these up for playback:
|
|
62
|
+
|
|
63
|
+
- When the connection drops mid-stream, the now-playing formatter receives
|
|
64
|
+
`stalled: 'offline'` (vs `'buffering'` when online) — see
|
|
65
|
+
[Now Playing → the formatter](/guide/now-playing#the-formatter-derived-continuous).
|
|
66
|
+
- When the connection returns while the stream sits **stalled** (state
|
|
67
|
+
`'buffering'` with play intent), the player re-establishes it automatically —
|
|
68
|
+
you don't need to listen for reconnect and re-`play()`. If the drop instead
|
|
69
|
+
surfaced as a playback **error**, recovery belongs to
|
|
70
|
+
[automatic retry](/guide/errors#automatic-retry) (`setupPlayer({ retry })`,
|
|
71
|
+
off by default); once retry gives up — or without it — the player stays in
|
|
72
|
+
`'error'` until the listener (or your code) calls `retry()` / `play()`.
|
|
73
|
+
|
|
74
|
+
Reach for `useOnline` / `onOnlineChanged` for **your own** UI and data — an
|
|
75
|
+
offline banner, disabling a control, refetching a list on reconnect.
|
|
76
|
+
|
|
77
|
+
## API summary
|
|
78
|
+
|
|
79
|
+
| API | Purpose |
|
|
80
|
+
| ----------------- | --------------------------------------------------------------------- |
|
|
81
|
+
| `useOnline()` | Reactive `boolean`; re-renders on connectivity change. |
|
|
82
|
+
| `getOnline()` | One-off snapshot `boolean`. |
|
|
83
|
+
| `onOnlineChanged` | Subscribe outside React; `addListener(cb)` returns an unsubscribe fn. |
|
|
84
|
+
|
|
85
|
+
`true` = validated internet connection; `false` = offline (including a network
|
|
86
|
+
with no real internet on Android).
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'What the system surfaces show: which metadata field lands where, the four layers that decide its value at any moment, and reading the result back in your own UI.'
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Now Playing
|
|
6
|
+
|
|
7
|
+
The now-playing surface is everything that displays the current track on the _system's_ surfaces: the iOS lock screen and Control Center, the Android notification, CarPlay and Android Auto's Now Playing screens, and Bluetooth head units. The library publishes one metadata dictionary and the _operating system_ renders it — each surface picks different fields, composes them differently, and ignores the rest. The same published metadata is also readable inside your own app — see [Reading now-playing in your own UI](#reading-now-playing-in-your-own-ui).
|
|
8
|
+
|
|
9
|
+
This guide covers: **which fields land where** (the static matrix), **the four layers that decide what those fields contain at any moment** (the dynamic model), and **reading the result back** in your own UI.
|
|
10
|
+
|
|
11
|
+
## Which field shows where
|
|
12
|
+
|
|
13
|
+
Apple and Google document very little of this; the table below reflects observed behavior.
|
|
14
|
+
|
|
15
|
+
| Field | iOS lock screen / Control Center | CarPlay Now Playing | Android notification | Android Auto Now Playing | Bluetooth (AVRCP) |
|
|
16
|
+
| --------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------- | ------------------------ | ----------------------- |
|
|
17
|
+
| `title` | Primary line | Primary line | Primary line | Primary line | Title |
|
|
18
|
+
| `artist` | **Secondary line (sole source — `album` is never used as a fallback)** | Second line | Secondary line | Secondary line | Artist |
|
|
19
|
+
| `album` | Not shown | **Third line — also the tappable album/artist button (see below)** | Not shown | Rarely shown | Album (some head units) |
|
|
20
|
+
| `artwork` | Shown | Shown | Shown | Shown | — |
|
|
21
|
+
| `live` | "LIVE" indicator replaces the time scrubber | "LIVE" indicator | — | — | — |
|
|
22
|
+
|
|
23
|
+
The time scrubber (elapsed, duration, playback rate) is not driven by track fields at all — every surface derives it from the player itself. `Track.duration` is informational metadata for your app (echoed back through now-playing events); it does not affect the scrubber.
|
|
24
|
+
|
|
25
|
+
`artwork` is the URL you _set_ on a track. (`Track.artworkSource` is a separate, output-only `ImageSource` the library populates for you to render in your own `<Image>` — it isn't published to surfaces.) Note that artwork isn't one of the overridable text fields: the formatter, override, and flash layers below carry only `title` / `artist` / `album`. Now-playing artwork comes from the track's `artwork`, optionally resolved through the [`nowPlayingArtwork`](#now-playing-artwork) config.
|
|
26
|
+
|
|
27
|
+
## The four metadata layers
|
|
28
|
+
|
|
29
|
+
What actually renders is decided by a fixed priority order. Each layer only overrides the fields it sets — anything it leaves `undefined` falls through to the layer below:
|
|
30
|
+
|
|
31
|
+
| Priority | Layer | API | Lifetime |
|
|
32
|
+
| ------------ | ---------------- | -------------------------------------- | ------------------------------------ |
|
|
33
|
+
| 1 (highest) | **Flash** | `flashNowPlaying(update, durationMs)` | `durationMs`, on a native timer |
|
|
34
|
+
| 2 | **Formatter** | `setupPlayer({ nowPlaying })` | as long as it's configured |
|
|
35
|
+
| 3 | **Override** | `updateNowPlaying(update)` | until cleared or the track changes |
|
|
36
|
+
| 4 (baseline) | **Track fields** | `Track.title` / `artist` / `album` / … | the track's time as the active track |
|
|
37
|
+
|
|
38
|
+
### Track fields — the baseline
|
|
39
|
+
|
|
40
|
+
With nothing else configured, the active track's own fields are published as-is. For on-demand content this is usually all you need.
|
|
41
|
+
|
|
42
|
+
### The formatter — derived, continuous
|
|
43
|
+
|
|
44
|
+
The [`nowPlaying`](/api/features/player/#formatnowplayingcallback) formatter hands you the now-playing text lines outright, re-invoked whenever they could change: on track change, on every timed-metadata update, on every playback transition (play / pause, a stall starting or recovering, an error), and on connectivity changes. It's the right layer for anything _derived from playback state_ — the live song from ICY/ID3 metadata, a "Reconnecting…" line during a stall, an error message:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
setupPlayer({
|
|
48
|
+
nowPlaying: ({ timedMetadata, playWhenReady, stalled, error }) => {
|
|
49
|
+
// `yourErrorLine` is your own kind→copy mapping — branch on `error.kind`,
|
|
50
|
+
// never `error.message`, which is developer English and never localized.
|
|
51
|
+
// This line is read by listeners, on the lock screen and in the car.
|
|
52
|
+
// See [Playback errors](/guide/errors#playback-errors).
|
|
53
|
+
if (error) return { artist: yourErrorLine(error.kind) }
|
|
54
|
+
if (stalled) {
|
|
55
|
+
return {
|
|
56
|
+
artist: stalled === 'offline' ? 'No connection' : 'Reconnecting…'
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// The live song, only while actually playing — a paused stream's last
|
|
60
|
+
// song is stale, so fall back to the track default (return undefined).
|
|
61
|
+
if (!playWhenReady || !timedMetadata?.title) return
|
|
62
|
+
return { artist: timedMetadata.title }
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The callback receives a single [`FormatNowPlayingParams`](/api/features/player/#formatnowplayingparams) object:
|
|
68
|
+
|
|
69
|
+
| Field | Type | Meaning |
|
|
70
|
+
| --------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
71
|
+
| `track` | `Track` | The currently playing track. |
|
|
72
|
+
| `timedMetadata` | `TimedMetadata?` | The ICY / ID3 "now playing song", if any. |
|
|
73
|
+
| `playWhenReady` | `boolean` | Play/pause intent — stays `true` through buffers, so the song line won't flicker. |
|
|
74
|
+
| `stalled` | `StallReason?` | Truthy only during a mid-stream stall: `'buffering'` (rebuffering while online) or `'offline'` (no connectivity). Use `if (stalled)`, compare `=== 'offline'` for the reason. |
|
|
75
|
+
| `error` | `PlaybackError?` | The current playback error, if playback failed. Switch on `error.kind` to pick your own localized line — see [Playback errors](/guide/errors#playback-errors). While [automatic retry](/guide/errors#automatic-retry) is still working, this carries the advisory error (`error.retrying === true`) — the formatter re-runs when it arrives, changes, and clears, so you can show provisional copy ("… — retrying") that hardens into the final line when the player gives up. |
|
|
76
|
+
|
|
77
|
+
It returns a [`NowPlayingUpdate`](/api/features/metadata/#nowplayingupdate) (`{ title?, artist?, album? }`) or `undefined`. The callback is synchronous and should stay cheap — it's a pure formatting function, no I/O. Each returned field falls back **independently** to the track's value when omitted; returning `undefined` (or `{}`) uses the track default entirely. Identical results across a rapid burst of transitions are de-duplicated natively, so they won't flicker the surface.
|
|
78
|
+
|
|
79
|
+
Timed metadata is never auto-applied: the library surfaces ICY (Shoutcast/Icecast) and in-band ID3 (HLS) frames to your code, and the formatter is where you decide what reaches the now-playing line. (To consume those frames outside the formatter, subscribe to [`onTimedMetadata`](/api/features/metadata/#ontimedmetadata).)
|
|
80
|
+
|
|
81
|
+
### The override — imperative, sticky
|
|
82
|
+
|
|
83
|
+
[`updateNowPlaying({ artist: '…' })`](/api/features/nowPlaying/#updatenowplaying) pins fields until you pass `null` or the track changes. It's the layer to reach for when you _don't_ configure a formatter.
|
|
84
|
+
|
|
85
|
+
**If a formatter is configured, it outranks the override** — the formatter's result is applied on top, falling back per-field to `override ?? track`. Mixing the two is rarely what you want: with a formatter in place, feed it state instead of calling `updateNowPlaying` around it. (The override predates the formatter; the formatter is the newer, preferred layer for anything derived from playback state.)
|
|
86
|
+
|
|
87
|
+
### The flash — transient, top priority
|
|
88
|
+
|
|
89
|
+
[`flashNowPlaying(update, durationMs)`](/api/features/nowPlaying/#flashnowplaying) is the toast of the now-playing world: it briefly replaces the fields it sets, then reverts to whatever the lower layers say. External surfaces have no notification primitive, so a transient metadata swap is the only way to give feedback there — the canonical use is answering a refused remote command:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// A radio product with an hourly skip allowance:
|
|
93
|
+
handleRemoteNext(() => {
|
|
94
|
+
if (skipsRemaining() > 0) {
|
|
95
|
+
skipToNext()
|
|
96
|
+
} else {
|
|
97
|
+
flashNowPlaying({ artist: 'Skip limit reached' }, 3000)
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Three properties make it a dedicated layer rather than sugar over `updateNowPlaying`:
|
|
103
|
+
|
|
104
|
+
- **It outranks the formatter.** While a flash is active the formatter pass is skipped entirely, so a live station's next metadata tick can't overwrite the message mid-window.
|
|
105
|
+
- **The revert runs on a native timer.** A JS `setTimeout` pauses with a backgrounded host on Android — and remote commands from the lock screen arrive exactly when the host is backgrounded. The native timer fires regardless.
|
|
106
|
+
- **A track change clears it early.** A flash is feedback about a moment; it never carries over to a new track.
|
|
107
|
+
|
|
108
|
+
Repeated calls restart the window. [`clearNowPlayingFlash()`](/api/features/nowPlaying/#clearnowplayingflash) cancels one imperatively — for example when the condition the flash complained about resolves mid-window.
|
|
109
|
+
|
|
110
|
+
## Reading now-playing in your own UI
|
|
111
|
+
|
|
112
|
+
The metadata you publish is also what your own app should render — a mini-player, a full-screen "now playing" view — so the in-app UI matches the lock screen exactly, formatter and all. Use the [`useNowPlaying`](/api/features/nowPlaying/#usenowplaying) hook:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { Text } from 'react-native'
|
|
116
|
+
import { useNowPlaying } from 'react-native-audio-browser'
|
|
117
|
+
|
|
118
|
+
function MiniPlayer() {
|
|
119
|
+
const nowPlaying = useNowPlaying()
|
|
120
|
+
if (!nowPlaying) return null
|
|
121
|
+
return (
|
|
122
|
+
<Text>
|
|
123
|
+
{nowPlaying.title} — {nowPlaying.artist}
|
|
124
|
+
</Text>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`useNowPlaying` returns the resolved [`NowPlayingMetadata`](/api/features/metadata/#nowplayingmetadata) — the same dictionary the system surfaces see, _after_ the four layers are applied — or `undefined` when nothing is playing. It carries `title` / `artist` / `album` / `artwork` (and `elapsedTime`). For the live scrubber position, pair it with [`useProgress`](/guide/playback#progress).
|
|
130
|
+
|
|
131
|
+
Outside React, [`getNowPlaying()`](/api/features/nowPlaying/#getnowplaying) reads a snapshot and [`onNowPlayingChanged`](/api/features/nowPlaying/#onnowplayingchanged) subscribes to changes (it fires when the override changes or the track changes).
|
|
132
|
+
|
|
133
|
+
To render artwork, don't use this `artwork` field directly — it's a raw URL string. Use the active track's ready-to-use `artworkSource` instead (`<Image source={track.artworkSource} />`) — see [Track](/guide/track).
|
|
134
|
+
|
|
135
|
+
## Gotchas worth knowing
|
|
136
|
+
|
|
137
|
+
**CarPlay's tappable line renders from `album`.** When a track has an `albumPath` (or `resolveAlbumPath` returns a path), the _album line_ becomes tappable — rendered as a third metadata line with a chevron, navigating the browse stack. A track without an `album` has no such line, so there is nothing to render or tap even though a destination exists. See the [CarPlay guide](/guide/carplay#album-line-navigation).
|
|
138
|
+
|
|
139
|
+
**The iOS lock screen never shows `album`.** Its secondary line comes from `artist` alone. If you move information from `artist` to `album` (for example to feed the CarPlay button line), it disappears from the lock screen.
|
|
140
|
+
|
|
141
|
+
**`artist` and `album` render as adjacent lines on CarPlay.** Giving both the same string displays it twice. If both fields must carry related context (a live stream's location, say), differentiate the copy — the album line reads well as an action since it can be tappable.
|
|
142
|
+
|
|
143
|
+
**One metadata dictionary feeds every surface.** There is no per-surface metadata: whatever you publish appears (or doesn't) everywhere simultaneously, per the table above. You cannot hide a field from one surface without hiding it from all of them.
|
|
144
|
+
|
|
145
|
+
**A formatter quietly disables `updateNowPlaying` for the fields it returns.** See [the override](#the-override--imperative-sticky) — the formatter's result wins per-field. If a line you set imperatively keeps reverting, a formatter is overwriting it; either move that logic into the formatter or use `flashNowPlaying` if it was meant to be transient.
|
|
146
|
+
|
|
147
|
+
**Browse lists are separate.** `subtitle` drives browse-list rows (CarPlay list detail text, Android Auto list subtitle) and is never shown on now-playing surfaces; `artist` drives now-playing and is never shown in browse lists. Neither falls back to the other.
|
|
148
|
+
|
|
149
|
+
## Now-playing artwork
|
|
150
|
+
|
|
151
|
+
Artwork is the one surface field the text layers (formatter / override / flash) can't touch — they carry only `title` / `artist` / `album`. The now-playing image comes from the active track's `artwork`.
|
|
152
|
+
|
|
153
|
+
By default that's the same image as the track's browse-list thumbnail. To resolve it differently — a larger, lock-screen-quality image without bloating list thumbnails — set the `nowPlayingArtwork` config kind, which builds the now-playing image from its own request (e.g. `{ path: '/artwork/{id}' }`):
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
configureBrowser({
|
|
157
|
+
nowPlayingArtwork: { path: '/artwork/{id}' }
|
|
158
|
+
// ...tabs, routes
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
See [`BrowserConfiguration.nowPlayingArtwork`](/api/types/browser/#browserconfiguration).
|
|
163
|
+
|
|
164
|
+
## API summary
|
|
165
|
+
|
|
166
|
+
| API | Purpose |
|
|
167
|
+
| ----------------------------------------- | ------------------------------------------------------------ |
|
|
168
|
+
| `setupPlayer({ nowPlaying })` | Configure the formatter — derived lines from playback state. |
|
|
169
|
+
| `updateNowPlaying(update \| null)` | Imperatively override fields; `null` clears. |
|
|
170
|
+
| `flashNowPlaying(update, durationMs)` | Transient, top-priority swap (refused-command feedback). |
|
|
171
|
+
| `clearNowPlayingFlash()` | Cancel an active flash early. |
|
|
172
|
+
| `useNowPlaying()` / `getNowPlaying()` | Read the resolved metadata in your own UI. |
|
|
173
|
+
| `onNowPlayingChanged` | Subscribe to metadata changes outside React. |
|
|
174
|
+
| `onTimedMetadata` | Subscribe to ICY / ID3 stream metadata directly. |
|
|
175
|
+
| `configureBrowser({ nowPlayingArtwork })` | Resolve now-playing artwork from its own request. |
|