react-native-audio-browser 0.2.0-next.3 → 0.2.0-next.31
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 +36 -22
- 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 +541 -278
- 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 +52 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +315 -213
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +27 -9
- package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +288 -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 +62 -40
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +110 -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 +100 -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 +40 -2
- package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +301 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +3 -2
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +168 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -55
- package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +160 -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 +55 -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/AVPlayerEngine.swift +330 -0
- package/ios/AVPlayerEngineCallbacks.swift +15 -0
- package/ios/Browser/BrowseGate.swift +58 -0
- package/ios/Browser/BrowserConfig.swift +117 -78
- package/ios/Browser/BrowserManager.swift +209 -603
- package/ios/Browser/BrowserPathHelper.swift +50 -46
- package/ios/Browser/InertStyleDiagnostic.swift +296 -0
- package/ios/Browser/JsonModels.swift +264 -106
- package/ios/Browser/NavigationErrorState.swift +41 -0
- package/ios/Browser/PageResolver.swift +227 -0
- package/ios/Browser/RequestLayer.swift +90 -0
- package/ios/Browser/{BrowserManager+URLResolution.swift → RequestPipeline.swift} +193 -121
- package/ios/Browser/SearchDrillIn.swift +7 -7
- 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/CPInterfaceController+Safe.swift +100 -43
- package/ios/CarPlay/CarPlayArtworkResolver.swift +36 -83
- package/ios/CarPlay/CarPlayController.swift +42 -1296
- package/ios/CarPlay/CarPlayImageLoader.swift +78 -97
- package/ios/CarPlay/CarPlayListItemFactory.swift +303 -92
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +129 -136
- package/ios/CarPlay/CarPlaySession+Navigator.swift +114 -0
- package/ios/CarPlay/CarPlaySession+PageLoader.swift +92 -0
- package/ios/CarPlay/CarPlaySession+Refresher.swift +43 -0
- package/ios/CarPlay/CarPlaySession+TabBar.swift +193 -0
- package/ios/CarPlay/CarPlaySession.swift +209 -0
- package/ios/CarPlay/CarPlayTemplates.swift +63 -0
- package/ios/CarPlay/HybridAudioBrowser+IntentFavorite.swift +20 -0
- package/ios/{Player → CarPlay}/MediaIntentCriteria.swift +17 -9
- package/ios/CarPlay/MediaIntentPlayback.swift +74 -0
- package/ios/CarPlay/RNABMediaAddHandler.swift +8 -24
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +7 -23
- package/ios/CarPlay/RNABMediaIntentHandler.swift +18 -19
- package/ios/CarPlay/SectionPresentation.swift +77 -0
- package/ios/CarPlay/TopTemplateReplacer.swift +96 -0
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/NavigationError+Conversion.swift +14 -4
- 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/Extension/UIImage+Adaptive.swift +12 -0
- package/ios/Extension/URLError+Connectivity.swift +15 -0
- package/ios/Http/HttpClient.swift +66 -142
- package/ios/HybridAudioBrowser+Browser.swift +153 -0
- package/ios/HybridAudioBrowser+Options.swift +144 -0
- package/ios/HybridAudioBrowser+Player.swift +397 -0
- package/ios/HybridAudioBrowser+PlayerCallbacks.swift +189 -0
- package/ios/HybridAudioBrowser+Stubs.swift +46 -0
- package/ios/HybridAudioBrowser.swift +153 -1610
- package/ios/Model/NitroTypeStubs.swift +146 -24
- package/ios/Model/PlayerUpdateOptions.swift +8 -1
- package/ios/Model/RemoteCommand.swift +18 -0
- package/ios/Model/TrackPlayerError.swift +2 -2
- package/ios/NowPlayingInfo/NowPlayingInfoController.swift +18 -53
- package/ios/Observer/ItemObserver.swift +90 -0
- package/ios/Observer/PlayerObserver.swift +59 -0
- package/ios/Option/SessionCategories.swift +42 -92
- package/ios/Player/AudioSessionController.swift +189 -0
- package/ios/Player/DeferredSeek.swift +70 -0
- package/ios/Player/ErrorStatus.swift +27 -0
- package/ios/Player/MediaLoader.swift +62 -35
- package/ios/Player/NowPlayingUpdater.swift +197 -50
- package/ios/Player/PlaybackFailure.swift +125 -0
- package/ios/Player/PlaybackPersistence.swift +73 -0
- package/ios/Player/PlaybackTimer.swift +8 -2
- package/ios/Player/Player.swift +1078 -0
- package/ios/Player/{PlaybackCoordinatorCallbacks.swift → PlayerCallbacks.swift} +3 -4
- package/ios/Player/PlayerEngine.swift +87 -0
- package/ios/Player/PlayerStatusTypes.swift +2 -6
- package/ios/Player/QueueManager.swift +93 -67
- package/ios/Player/RetryLoop.swift +261 -0
- package/ios/Player/ShuffleOrder.swift +33 -0
- package/ios/Player/SleepTimerManager.swift +1 -1
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +46 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +87 -331
- package/ios/Tests/BrowserPathHelperTests.swift +56 -15
- package/ios/Tests/CarPlayArtworkResolverTests.swift +60 -373
- package/ios/Tests/DeferredSeekTests.swift +70 -0
- package/ios/Tests/EmitterTests.swift +29 -0
- package/ios/Tests/GenerationMemoTests.swift +121 -0
- package/ios/Tests/InertStyleDiagnosticTests.swift +209 -0
- package/ios/Tests/JsonModelsTests.swift +164 -1
- package/ios/Tests/LRUCacheTests.swift +110 -0
- package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
- package/ios/Tests/MediaLoaderTests.swift +12 -48
- package/ios/Tests/ObserverTests.swift +88 -0
- package/ios/Tests/PlaybackErrorTests.swift +76 -0
- package/ios/Tests/PlaybackFailureTests.swift +189 -0
- package/ios/Tests/PlaybackPersistenceTests.swift +93 -0
- package/ios/Tests/PlayerPolicyTests.swift +420 -0
- package/ios/Tests/{PlaybackCoordinatorTests.swift → PlayerTests.swift} +300 -248
- package/ios/Tests/PlayingStateTests.swift +42 -0
- package/ios/Tests/QueueManagerTests.swift +196 -115
- package/ios/Tests/ReadyValueTests.swift +73 -0
- package/ios/Tests/RemoteCommandControllerTests.swift +152 -0
- package/ios/Tests/RemoteCommandTests.swift +65 -0
- package/ios/Tests/RequestLayerTests.swift +112 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +39 -35
- package/ios/Tests/RetryLoopTests.swift +270 -0
- package/ios/Tests/SearchDrillInTests.swift +16 -16
- package/ios/Tests/SectionPresentationTests.swift +98 -0
- package/ios/Tests/SectionScopeTests.swift +87 -55
- package/ios/Tests/StyleResolverTests.swift +132 -0
- package/ios/Tests/Support/TestTypes.swift +108 -104
- package/ios/Tests/TabBarEntriesTests.swift +19 -19
- package/ios/Tests/TimedMetadataAVFoundationTests.swift +42 -0
- package/ios/Tests/TopTemplateReplacerTests.swift +172 -0
- package/ios/Tests/TrackIdTemplateTests.swift +67 -0
- package/ios/Tests/TrackIdentityTests.swift +20 -0
- package/ios/Tests/TrackSelectorTests.swift +168 -33
- package/ios/Tests/URLErrorConnectivityTests.swift +20 -0
- package/ios/TrackSelector.swift +46 -31
- package/ios/Util/ArtworkImageFetcher.swift +58 -1
- package/ios/Util/Emitter.swift +42 -0
- package/ios/Util/GenerationMemo.swift +36 -0
- package/ios/Util/LRUCache.swift +23 -159
- package/ios/Util/NetworkMonitor.swift +43 -26
- package/ios/Util/ReadyValue.swift +56 -0
- package/ios/Util/SFSymbolRenderer.swift +59 -31
- package/ios/Util/SVGProcessor.swift +9 -12
- 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 +201 -76
- 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/PlayingStateFactory.js +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/NavigationErrorManager.js +3 -1
- package/lib/commonjs/web/browser/NavigationErrorManager.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 +62 -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 +201 -76
- 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/PlayingStateFactory.js +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/NavigationErrorManager.js +3 -1
- package/lib/module/web/browser/NavigationErrorManager.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 +62 -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 +387 -126
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +93 -61
- 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/resolveAssetSource.d.ts +1 -2
- package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -1
- 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 +4 -2
- package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +2 -2
- package/nitrogen/generated/android/c++/JAndroidAudioOffloadSettings.hpp +2 -2
- package/nitrogen/generated/android/c++/JAndroidOptions.hpp +12 -12
- package/nitrogen/generated/android/c++/JAndroidPlayerWakeMode.hpp +2 -2
- package/nitrogen/generated/android/c++/JAppKilledPlaybackBehavior.hpp +2 -2
- package/nitrogen/generated/android/c++/JArtworkRendering.hpp +58 -0
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +10 -5
- package/nitrogen/generated/android/c++/JArtworkVariants.hpp +2 -2
- package/nitrogen/generated/android/c++/JBatteryOptimizationStatus.hpp +2 -2
- package/nitrogen/generated/android/c++/JBatteryOptimizationStatusChangedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JBatteryWarningPendingChangedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JBrowseError.hpp +2 -2
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +19 -7
- package/nitrogen/generated/android/c++/JBrowserSourceCallbackParam.hpp +2 -2
- package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +2 -2
- package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
- package/nitrogen/generated/android/c++/JCarPlaySiriListButtonPosition.hpp +2 -2
- package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
- package/nitrogen/generated/android/c++/JChapterMetadata.hpp +2 -2
- package/nitrogen/generated/android/c++/JEqualizerSettings.hpp +10 -10
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +10 -5
- package/nitrogen/generated/android/c++/JFormatNavigationErrorParams.hpp +11 -5
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +10 -5
- package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +9 -9
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +10 -5
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_FormattedNavigationError____FormatNavigationErrorParams.hpp +4 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +10 -5
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +10 -5
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____MediaTransformParams.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +9 -9
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +10 -5
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +18 -12
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +10 -4
- 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 +18 -6
- package/nitrogen/generated/android/c++/JFunc_void.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_BatteryOptimizationStatusChangedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_BatteryWarningPendingChangedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_EqualizerSettings.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +10 -5
- package/nitrogen/generated/android/c++/JFunc_void_GateEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_NavigationErrorEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +9 -12
- package/nitrogen/generated/android/c++/JFunc_void_Output.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +10 -5
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackPlayWhenReadyChangedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackProgressUpdatedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackQueueEndedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_PlayingState.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_RemoteJumpBackwardEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_RemoteJumpForwardEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_RemotePlayIdEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_RemotePlaySearchEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_RemoteSeekEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_RemoteSkipEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_RepeatModeChangedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_TimedMetadata.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_TrackMetadata.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_double.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_FormattedNavigationError_.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +17 -5
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_std__variant_nitro__NullType__SleepTimerTime__SleepTimerEndOfTrack__.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_ChapterMetadata_.hpp +16 -16
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +24 -18
- package/nitrogen/generated/android/c++/JGate.hpp +2 -2
- package/nitrogen/generated/android/c++/JGateDecision.hpp +2 -2
- package/nitrogen/generated/android/c++/JGateEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JGateReason.hpp +2 -2
- package/nitrogen/generated/android/c++/JGridTile.hpp +64 -0
- package/nitrogen/generated/android/c++/JHttpMethod.hpp +2 -2
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +77 -53
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +3 -3
- package/nitrogen/generated/android/c++/JIOSCategory.hpp +2 -2
- package/nitrogen/generated/android/c++/JIOSCategoryMode.hpp +2 -2
- package/nitrogen/generated/android/c++/JIOSCategoryOptions.hpp +2 -2
- package/nitrogen/generated/android/c++/JIOSCategoryPolicy.hpp +2 -2
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +17 -11
- package/nitrogen/generated/android/c++/JImageContext.hpp +2 -2
- package/nitrogen/generated/android/c++/JImageQueryParams.hpp +2 -2
- package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
- package/nitrogen/generated/android/c++/JImageSource.hpp +2 -2
- package/nitrogen/generated/android/c++/JMediaReference.hpp +2 -2
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +10 -5
- package/nitrogen/generated/android/c++/JMediaTransformParams.hpp +2 -2
- package/nitrogen/generated/android/c++/JNativeAndroidSetupOptions.hpp +2 -2
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +48 -21
- package/nitrogen/generated/android/c++/JNativeGateRequest.hpp +2 -2
- package/nitrogen/generated/android/c++/JNativeIOSSetupOptions.hpp +10 -10
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +16 -4
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +17 -14
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +9 -12
- package/nitrogen/generated/android/c++/JNavigationError.hpp +2 -2
- package/nitrogen/generated/android/c++/JNavigationErrorEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JNavigationErrorKind.hpp +58 -0
- package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +2 -2
- package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +12 -12
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +17 -11
- package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +2 -2
- package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +2 -2
- package/nitrogen/generated/android/c++/JOptions.hpp +9 -12
- package/nitrogen/generated/android/c++/JOutput.hpp +2 -2
- package/nitrogen/generated/android/c++/JOutputType.hpp +2 -2
- package/nitrogen/generated/android/c++/JPlayback.hpp +2 -2
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +10 -5
- package/nitrogen/generated/android/c++/JPlaybackError.hpp +2 -2
- package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JPlaybackErrorKind.hpp +2 -2
- package/nitrogen/generated/android/c++/JPlaybackPlayWhenReadyChangedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JPlaybackProgressUpdatedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JPlaybackQueueEndedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JPlaybackState.hpp +2 -2
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +7 -13
- package/nitrogen/generated/android/c++/JPlayingState.hpp +2 -2
- package/nitrogen/generated/android/c++/JProgress.hpp +2 -2
- 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++/JRemoteJumpBackwardEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JRemoteJumpForwardEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JRemotePlayIdEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JRemotePlaySearchEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JRemoteSeekEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JRemoteSkipEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JRepeatMode.hpp +2 -2
- package/nitrogen/generated/android/c++/JRepeatModeChangedEvent.hpp +2 -2
- package/nitrogen/generated/android/c++/JRequestConfig.hpp +2 -2
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +67 -59
- package/nitrogen/generated/android/c++/JRetryConfig.hpp +2 -2
- package/nitrogen/generated/android/c++/JSearchMode.hpp +2 -2
- package/nitrogen/generated/android/c++/JSearchParams.hpp +2 -2
- package/nitrogen/generated/android/c++/JSection.hpp +116 -0
- package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
- package/nitrogen/generated/android/c++/JSleepTimer.hpp +4 -4
- package/nitrogen/generated/android/c++/JSleepTimerEndOfTrack.hpp +2 -2
- package/nitrogen/generated/android/c++/JSleepTimerTime.hpp +2 -2
- package/nitrogen/generated/android/c++/JStallReason.hpp +2 -2
- package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
- package/nitrogen/generated/android/c++/JTimedMetadata.hpp +2 -2
- package/nitrogen/generated/android/c++/JTrack.hpp +27 -48
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +18 -12
- package/nitrogen/generated/android/c++/JTrackMetadata.hpp +2 -2
- package/nitrogen/generated/android/c++/JTrackRequest.hpp +2 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +47 -19
- package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +2 -2
- package/nitrogen/generated/android/c++/JVariant_Boolean_AndroidAudioOffloadSettings.hpp +3 -3
- package/nitrogen/generated/android/c++/JVariant_Boolean_RetryConfig.hpp +3 -3
- package/nitrogen/generated/android/c++/JVariant_NullType_Double.hpp +3 -3
- 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 +20 -8
- package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.hpp +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioOffloadSettings.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +20 -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/ArtworkRequestConfig.kt +37 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkVariants.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BatteryOptimizationStatusChangedEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BatteryWarningPendingChangedEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BrowseError.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BrowseResult.kt +16 -7
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BrowserSourceCallbackParam.kt +15 -0
- 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/ChapterMetadata.kt +19 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/EqualizerSettings.kt +27 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FavoriteChangedEvent.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNavigationErrorParams.kt +25 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNowPlayingParams.kt +21 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___Track.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_FormattedNavigationError____FormatNavigationErrorParams.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_std__string____Track.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____MediaTransformParams.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____RequestConfig_std__optional_std__unordered_map_std__string__std__string__.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +78 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_BatteryOptimizationStatusChangedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_BatteryWarningPendingChangedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_EqualizerSettings.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_FavoriteChangedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_GateEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_NavigationErrorEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_NowPlayingMetadata.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_Options.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_Output.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_Playback.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackActiveTrackChangedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackErrorEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackPlayWhenReadyChangedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackProgressUpdatedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackQueueEndedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlayingState.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteJumpBackwardEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteJumpForwardEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemotePlayIdEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemotePlaySearchEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteSeekEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteSkipEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RepeatModeChangedEvent.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_TimedMetadata.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_TrackMetadata.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_double.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__optional_FormattedNavigationError_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__optional_ResolvedTrack_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__optional_std__variant_nitro__NullType__SleepTimerTime__SleepTimerEndOfTrack__.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__string.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__vector_ChapterMetadata_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__vector_Track_.kt +0 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Gate.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GateDecision.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GateEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +25 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +3 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +25 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageContext.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageQueryParams.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageSource.kt +19 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaTransformParams.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeAndroidSetupOptions.kt +31 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +51 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeGateRequest.kt +17 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeIOSSetupOptions.kt +21 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +27 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeSetupPlayerOptions.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationError.kt +19 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorKind.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +20 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +25 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +29 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +17 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Output.kt +17 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Playback.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackActiveTrackChangedEvent.kt +21 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +21 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackPlayWhenReadyChangedEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackProgressUpdatedEvent.kt +19 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackQueueEndedEvent.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +37 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayingState.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Progress.kt +17 -0
- 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 +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteJumpBackwardEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteJumpForwardEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemotePlayIdEvent.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemotePlaySearchEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteSeekEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteSkipEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RepeatModeChangedEvent.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RequestConfig.kt +27 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +67 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +17 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +27 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Section.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +86 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SleepTimer.kt +18 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SleepTimerEndOfTrack.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SleepTimerTime.kt +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TimedMetadata.kt +21 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +55 -12
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackLoadEvent.kt +17 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackMetadata.kt +43 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackRequest.kt +17 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +58 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +31 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_AndroidAudioOffloadSettings.kt +16 -7
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_RetryConfig.kt +16 -7
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_NullType_Double.kt +16 -7
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +24 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.kt +18 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_String_ArtworkVariants.kt +16 -7
- package/nitrogen/generated/ios/AudioBrowser+autolinking.rb +2 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +38 -29
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +779 -665
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +34 -16
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +36 -18
- package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -12
- package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
- package/nitrogen/generated/ios/swift/BrowseResult.swift +12 -0
- package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
- package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
- package/nitrogen/generated/ios/swift/FormatNavigationErrorParams.swift +7 -2
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
- package/nitrogen/generated/ios/swift/GridTile.swift +48 -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/NavigationErrorKind.swift +40 -0
- 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/SleepTimer.swift +13 -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_Bool_AndroidAudioOffloadSettings.swift +12 -0
- package/nitrogen/generated/ios/swift/Variant_Bool_RetryConfig.swift +12 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_Double.swift +12 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_RemoteButtonLayout.swift +30 -0
- package/nitrogen/generated/ios/swift/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.swift +13 -0
- package/nitrogen/generated/ios/swift/Variant_String_ArtworkVariants.swift +12 -0
- 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++/FormatNavigationErrorParams.hpp +9 -2
- package/nitrogen/generated/shared/c++/GridTile.hpp +84 -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++/NavigationErrorKind.hpp +76 -0
- 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 +403 -134
- package/src/types/browser.ts +97 -63
- 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 +240 -83
- 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/PlayingStateFactory.test.ts +1 -1
- package/src/web/TrackPlayer/PlayingStateFactory.ts +1 -1
- 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/NavigationErrorManager.ts +3 -1
- 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 +68 -0
- package/src/web/util/BrowserPathHelper.ts +70 -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 +587 -0
- package/website/guide/carplay.md +650 -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/Bridge.h +0 -8
- package/ios/Browser/MediaResolveComposer.swift +0 -114
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/ios/Model/MediaURL.swift +0 -67
- package/ios/NowPlayingInfo/MediaItemProperty.swift +0 -78
- package/ios/NowPlayingInfo/NowPlayingInfoKeyValue.swift +0 -6
- package/ios/NowPlayingInfo/NowPlayingInfoProperty.swift +0 -220
- package/ios/Observer/PlayerItemNotificationObserver.swift +0 -119
- package/ios/Observer/PlayerItemPropertyObserver.swift +0 -133
- package/ios/Observer/PlayerStateObserver.swift +0 -50
- package/ios/Observer/PlayerTimeObserver.swift +0 -55
- package/ios/Option/PitchAlgorithms.swift +0 -17
- package/ios/Option/TimeEventFrequency.swift +0 -18
- package/ios/Player/LoadSeekCoordinator.swift +0 -99
- package/ios/Player/MediaLoaderDelegate.swift +0 -11
- package/ios/Player/PlaybackCoordinator.swift +0 -754
- package/ios/Player/PlaybackEffectHandler.swift +0 -54
- package/ios/Player/PlaybackErrorHandler.swift +0 -175
- package/ios/Player/PlaybackStateStore.swift +0 -27
- package/ios/Player/PlayingStateManager.swift +0 -33
- package/ios/Player/RetryManager.swift +0 -322
- package/ios/Player/SeekCompletionHandler.swift +0 -3
- package/ios/Player/SleepTimerHandling.swift +0 -15
- package/ios/Support/RNTrackPlayer-Bridging-Header.h +0 -7
- package/ios/Tests/LoadSeekCoordinatorTests.swift +0 -325
- package/ios/Tests/MediaResolveTests.swift +0 -126
- package/ios/Tests/OnceValueTests.swift +0 -79
- package/ios/Tests/PlaybackErrorHandlerTests.swift +0 -547
- package/ios/Tests/PlaybackStateStoreTests.swift +0 -48
- package/ios/Tests/PlayingStateManagerTests.swift +0 -57
- package/ios/Tests/RetryManagerTests.swift +0 -89
- package/ios/TrackPlayer.swift +0 -1014
- package/ios/TrackPlayerCallbacks.swift +0 -86
- package/ios/Util/MetadataAdapter.swift +0 -131
- package/ios/Util/OnceValue.swift +0 -104
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JFavoritesMatchMode.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/FavoriteConfig.kt +0 -38
- 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/ios/swift/Variant_NullType_NotificationButtonLayout.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
|
@@ -3,89 +3,49 @@ import NitroModules
|
|
|
3
3
|
import os.log
|
|
4
4
|
|
|
5
5
|
/// Custom browser errors.
|
|
6
|
-
enum BrowserError:
|
|
6
|
+
enum BrowserError: LocalizedError {
|
|
7
7
|
case contentNotFound(path: String)
|
|
8
|
-
case httpError(code: Int, body: String)
|
|
9
|
-
case networkError(Error)
|
|
10
8
|
case invalidConfiguration(String)
|
|
11
9
|
case callbackError(String)
|
|
12
10
|
|
|
13
|
-
var
|
|
11
|
+
var errorDescription: String? {
|
|
14
12
|
switch self {
|
|
15
|
-
case .contentNotFound:
|
|
16
|
-
|
|
17
|
-
case let .
|
|
18
|
-
// Try to extract error message from JSON response: { "error": "message" }
|
|
19
|
-
if let data = body.data(using: .utf8),
|
|
20
|
-
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
21
|
-
let errorMessage = json["error"] as? String
|
|
22
|
-
{
|
|
23
|
-
return errorMessage
|
|
24
|
-
}
|
|
25
|
-
// Fall back to localized HTTP status description
|
|
26
|
-
return HTTPURLResponse.localizedString(forStatusCode: code)
|
|
27
|
-
case let .networkError(error):
|
|
28
|
-
return "Network error: \(error.localizedDescription)"
|
|
29
|
-
case let .invalidConfiguration(message):
|
|
30
|
-
return "Invalid configuration: \(message)"
|
|
31
|
-
case let .callbackError(message):
|
|
32
|
-
return message
|
|
13
|
+
case .contentNotFound: "Content not found"
|
|
14
|
+
case let .invalidConfiguration(message): "Invalid configuration: \(message)"
|
|
15
|
+
case let .callbackError(message): message
|
|
33
16
|
}
|
|
34
17
|
}
|
|
35
18
|
}
|
|
36
19
|
|
|
37
|
-
///
|
|
38
|
-
///
|
|
39
|
-
/// This class contains the main business logic for:
|
|
40
|
-
/// - Route resolution and path matching with parameter extraction
|
|
41
|
-
/// - HTTP API requests and response processing
|
|
42
|
-
/// - JavaScript callback invocation
|
|
43
|
-
/// - Fallback handling and error management
|
|
20
|
+
/// The browser's state (path, content, tabs, config), its content and search caches, favorites
|
|
21
|
+
/// and events. Pages come from `PageResolver`; requests go through `RequestPipeline`.
|
|
44
22
|
@MainActor
|
|
45
23
|
final class BrowserManager {
|
|
46
24
|
// Allow creation from nonisolated contexts (e.g. HybridAudioBrowser property default)
|
|
47
|
-
nonisolated init() {
|
|
25
|
+
nonisolated init() {
|
|
26
|
+
pages = PageResolver(requests: requests)
|
|
27
|
+
}
|
|
48
28
|
|
|
49
29
|
let logger = Logger(subsystem: "com.audiobrowser", category: "BrowserManager")
|
|
50
30
|
|
|
51
|
-
// MARK: - Constants (match Kotlin companion object)
|
|
52
|
-
|
|
53
|
-
/// Internal path used for the default/root browse source
|
|
54
|
-
static let defaultRoutePath = "__default__"
|
|
55
|
-
|
|
56
|
-
/// Internal path used for navigation tabs
|
|
57
|
-
static let tabsRoutePath = "__tabs__"
|
|
58
|
-
|
|
59
|
-
/// Internal path used for search
|
|
60
|
-
static let searchRoutePath = "__search__"
|
|
61
|
-
|
|
62
31
|
// MARK: - Private Properties
|
|
63
32
|
|
|
64
|
-
|
|
65
|
-
private let
|
|
66
|
-
|
|
67
|
-
// LRU cache for individual tracks - keyed by both url and src for O(1) lookup
|
|
68
|
-
private let trackCache = LRUCache<String, Track>(maxSize: 3000)
|
|
33
|
+
let requests = RequestPipeline()
|
|
34
|
+
private let pages: PageResolver
|
|
69
35
|
|
|
70
36
|
// LRU cache for resolved content - keyed by path
|
|
71
37
|
private let contentCache = LRUCache<String, ResolvedTrack>(maxSize: 20)
|
|
72
38
|
|
|
73
|
-
//
|
|
74
|
-
private var
|
|
39
|
+
// Most recent search, keyed by `searchCacheKey`.
|
|
40
|
+
private var lastSearchKey: String?
|
|
75
41
|
private var lastSearchResults: [Track]?
|
|
76
42
|
|
|
77
|
-
//
|
|
78
|
-
private var layerGeneration = 0
|
|
79
|
-
private var resolvedLayerGeneration = -1
|
|
80
|
-
var resolvedRequestLayer: TransformableRequestConfig?
|
|
81
|
-
private var resolvedBrowseLayer: TransformableRequestConfig?
|
|
82
|
-
|
|
83
|
-
// Set of favorited track identifiers (src)
|
|
43
|
+
// Set of favorited track identities (id when non-blank, else src).
|
|
84
44
|
private var favoriteIds: Set<String> = []
|
|
85
45
|
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
private var
|
|
46
|
+
// Whether favoriting is enabled, propagated from the player's `favorite`
|
|
47
|
+
// capability. false = no row hearts. Set via setFavoriteEnabled.
|
|
48
|
+
private var favoriteEnabled = false
|
|
89
49
|
|
|
90
50
|
// Navigation tracking to prevent race conditions
|
|
91
51
|
private var currentNavigationId: Int = 0
|
|
@@ -95,7 +55,7 @@ final class BrowserManager {
|
|
|
95
55
|
private(set) var path: String = "/" {
|
|
96
56
|
didSet {
|
|
97
57
|
if oldValue != path {
|
|
98
|
-
|
|
58
|
+
pathChanged.emit(path)
|
|
99
59
|
}
|
|
100
60
|
}
|
|
101
61
|
}
|
|
@@ -103,7 +63,7 @@ final class BrowserManager {
|
|
|
103
63
|
private(set) var content: ResolvedTrack? {
|
|
104
64
|
didSet {
|
|
105
65
|
// Note: Can't compare ResolvedTrack directly, always fire callback
|
|
106
|
-
|
|
66
|
+
contentChanged.emit(content)
|
|
107
67
|
}
|
|
108
68
|
}
|
|
109
69
|
|
|
@@ -114,7 +74,7 @@ final class BrowserManager {
|
|
|
114
74
|
// titles in a new locale), and an unchanged result must not churn the
|
|
115
75
|
// CarPlay tab bar or reset its selection.
|
|
116
76
|
if let tabs, !TabBarEntries.same(oldValue, tabs) {
|
|
117
|
-
|
|
77
|
+
tabsChanged.emit(tabs)
|
|
118
78
|
}
|
|
119
79
|
}
|
|
120
80
|
}
|
|
@@ -124,35 +84,38 @@ final class BrowserManager {
|
|
|
124
84
|
/// Whether configureBrowser() has been called
|
|
125
85
|
private(set) var isConfigured = false
|
|
126
86
|
|
|
127
|
-
/// Browser configuration containing routes, search, tabs, and request settings.
|
|
128
|
-
|
|
129
|
-
|
|
87
|
+
/// Browser configuration containing routes, search, tabs, and request settings. Kept on
|
|
88
|
+
/// `requests`, which every request reads.
|
|
89
|
+
var config: BrowserConfig {
|
|
90
|
+
get { requests.config }
|
|
91
|
+
set {
|
|
130
92
|
isConfigured = true
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
onConfigChanged?(config)
|
|
93
|
+
requests.config = newValue
|
|
94
|
+
configChanged.emit(newValue)
|
|
134
95
|
}
|
|
135
96
|
}
|
|
136
97
|
|
|
137
|
-
// MARK: -
|
|
98
|
+
// MARK: - Events (emitted on the main actor)
|
|
138
99
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
///
|
|
144
|
-
///
|
|
145
|
-
|
|
146
|
-
/// events, never an external change routed through `setFavorites`.
|
|
147
|
-
var onFavoritesChanged: (() -> Void)?
|
|
100
|
+
let pathChanged = Emitter<String>()
|
|
101
|
+
let contentChanged = Emitter<ResolvedTrack?>()
|
|
102
|
+
let tabsChanged = Emitter<[Track]>()
|
|
103
|
+
let configChanged = Emitter<BrowserConfig>()
|
|
104
|
+
/// The favorite id set changed, however it changed (e.g. from the app or a webview), so
|
|
105
|
+
/// surfaces like the CarPlay now-playing heart can re-render.
|
|
106
|
+
let favoritesChanged = Emitter<Void>()
|
|
148
107
|
|
|
149
|
-
///
|
|
150
|
-
///
|
|
108
|
+
/// Runs the app's `handleTrackLoad` for `event` and waits for it, both promise layers: a JS
|
|
109
|
+
/// callback returning `Promise<void>` arrives as `Promise<Promise<Void>>`. False when the app
|
|
110
|
+
/// set none.
|
|
151
111
|
func awaitTrackLoadHandler(event: TrackLoadEvent) async -> Bool {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
112
|
+
guard let handler = config.handleTrackLoad else { return false }
|
|
113
|
+
do {
|
|
114
|
+
try await handler(event).await().await()
|
|
115
|
+
} catch {
|
|
116
|
+
logger.error("handleTrackLoad failed: \(error.localizedDescription)")
|
|
117
|
+
}
|
|
118
|
+
return true
|
|
156
119
|
}
|
|
157
120
|
|
|
158
121
|
// MARK: - Favorites
|
|
@@ -162,103 +125,75 @@ final class BrowserManager {
|
|
|
162
125
|
let newIds = Set(favorites)
|
|
163
126
|
guard newIds != favoriteIds else { return }
|
|
164
127
|
favoriteIds = newIds
|
|
165
|
-
|
|
128
|
+
favoritesChanged.emit(())
|
|
166
129
|
}
|
|
167
130
|
|
|
168
|
-
///
|
|
169
|
-
///
|
|
170
|
-
func
|
|
171
|
-
|
|
131
|
+
/// Enables/disables favoriting (propagated from the `favorite` capability).
|
|
132
|
+
/// false disables row-heart hydration.
|
|
133
|
+
func setFavoriteEnabled(_ enabled: Bool) {
|
|
134
|
+
favoriteEnabled = enabled
|
|
172
135
|
}
|
|
173
136
|
|
|
174
|
-
/// Optimistically reflects a local favorite toggle
|
|
175
|
-
///
|
|
176
|
-
///
|
|
177
|
-
///
|
|
178
|
-
///
|
|
179
|
-
|
|
180
|
-
/// aren't collaterally lost. The consumer reconciles `favoriteIds` to its
|
|
181
|
-
/// canonical ids on the next `setFavorites`; `isFavorite(src:)` gives the same
|
|
182
|
-
/// answer before and after, so the now-playing heart stays responsive.
|
|
183
|
-
func setFavorited(src: String, favorited: Bool) {
|
|
137
|
+
/// Optimistically reflects a local favorite toggle in the favorite set, keyed
|
|
138
|
+
/// by the track's identity (id when non-blank, else src). The consumer
|
|
139
|
+
/// reconciles `favoriteIds` to its canonical set on the next `setFavorites`;
|
|
140
|
+
/// `hydrateFavorite` gives the same answer before and after, so the
|
|
141
|
+
/// now-playing heart stays responsive.
|
|
142
|
+
func setFavorited(identity: String, favorited: Bool) {
|
|
184
143
|
if favorited {
|
|
185
|
-
favoriteIds.insert(
|
|
186
|
-
} else if favoriteMatch == .partial {
|
|
187
|
-
favoriteIds = favoriteIds.filter { !BrowserPathHelper.containsSegment(src, $0) }
|
|
144
|
+
favoriteIds.insert(identity)
|
|
188
145
|
} else {
|
|
189
|
-
favoriteIds.remove(
|
|
146
|
+
favoriteIds.remove(identity)
|
|
190
147
|
}
|
|
191
148
|
}
|
|
192
149
|
|
|
193
150
|
/// Hydrates the favorited field on a track based on the favoriteIds set.
|
|
194
151
|
/// No-op unless favoriting is enabled (the `favorite` capability). Only
|
|
195
|
-
///
|
|
196
|
-
/// false so non-favorited tracks still show an (empty) heart.
|
|
197
|
-
///
|
|
152
|
+
/// identity-bearing tracks (id or src) are favoritable; the flag is set to
|
|
153
|
+
/// true OR false so non-favorited tracks still show an (empty) heart. A
|
|
154
|
+
/// caller-set `favorited` wins — API/consumer-provided values are never
|
|
155
|
+
/// overwritten (matches Android and web).
|
|
198
156
|
func hydrateFavorite(_ track: Track) -> Track {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
let
|
|
202
|
-
|
|
203
|
-
// Only create a new track if the favorited state differs
|
|
204
|
-
if track.favorited == isFavorited { return track }
|
|
205
|
-
|
|
206
|
-
return track.copying(favorited: isFavorited)
|
|
157
|
+
// `identity` bridges a std::string out of the C++ track, so it is bound
|
|
158
|
+
// once here rather than computed again to answer "is it favorited".
|
|
159
|
+
guard favoriteEnabled, track.favorited == nil, let identity = track.identity else { return track }
|
|
160
|
+
return track.copying(favorited: favoriteIds.contains(identity))
|
|
207
161
|
}
|
|
208
162
|
|
|
209
|
-
///
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
case .exact:
|
|
213
|
-
favoriteIds.contains(src)
|
|
214
|
-
case .partial:
|
|
215
|
-
favoriteIds.contains { BrowserPathHelper.containsSegment(src, $0) }
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/// Hydrates favorites on all children of a ResolvedTrack.
|
|
163
|
+
/// Hydrates favorites on all tracks of a resolved page. Pages reach
|
|
164
|
+
/// hydration normalized (ADR 0010), so `sections` is the only structure
|
|
165
|
+
/// to walk.
|
|
220
166
|
private func hydrateChildren(_ resolvedTrack: ResolvedTrack) -> ResolvedTrack {
|
|
221
|
-
guard let
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// MARK: - Track Cache
|
|
227
|
-
|
|
228
|
-
/// Cache a track by both url and src for O(1) lookup from either key.
|
|
229
|
-
private func cacheTrack(_ track: Track) {
|
|
230
|
-
if let url = track.url {
|
|
231
|
-
trackCache.set(url, value: track)
|
|
232
|
-
}
|
|
233
|
-
if let src = track.src {
|
|
234
|
-
trackCache.set(src, value: track)
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
private func cacheChildren(_ resolvedTrack: ResolvedTrack) {
|
|
239
|
-
resolvedTrack.children?.forEach { cacheTrack($0) }
|
|
167
|
+
guard let sections = resolvedTrack.sections else { return resolvedTrack }
|
|
168
|
+
return resolvedTrack.copying(sections: sections.map { section in
|
|
169
|
+
section.copying(children: section.children.map { hydrateFavorite($0) })
|
|
170
|
+
})
|
|
240
171
|
}
|
|
241
172
|
|
|
242
173
|
// MARK: - Navigation
|
|
243
174
|
|
|
244
|
-
///
|
|
245
|
-
///
|
|
246
|
-
///
|
|
247
|
-
///
|
|
248
|
-
func
|
|
249
|
-
// Increment navigation ID and capture for this navigation
|
|
175
|
+
/// Loads `path` as the current page. `bypassCache` reloads it from the network and keeps the
|
|
176
|
+
/// content on screen meanwhile, for refreshing the current path. A newer load supersedes this one, and its
|
|
177
|
+
/// result or failure is dropped: every caller reports a throw, so a slow /a failing after a
|
|
178
|
+
/// fast /b succeeded would otherwise show /a's error over /b's content.
|
|
179
|
+
func load(_ path: String, bypassCache: Bool = false) async throws {
|
|
250
180
|
currentNavigationId += 1
|
|
251
181
|
let navigationId = currentNavigationId
|
|
252
|
-
|
|
253
182
|
self.path = path
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
183
|
+
if bypassCache {
|
|
184
|
+
// A failed reload must not leave the stale page to be served from the cache.
|
|
185
|
+
contentCache.remove(path)
|
|
186
|
+
} else {
|
|
187
|
+
content = nil
|
|
188
|
+
}
|
|
189
|
+
do {
|
|
190
|
+
let resolved = try await resolve(path, useCache: !bypassCache)
|
|
191
|
+
guard navigationId == currentNavigationId else { return }
|
|
192
|
+
content = resolved
|
|
193
|
+
} catch {
|
|
194
|
+
guard navigationId == currentNavigationId else { return }
|
|
195
|
+
throw error
|
|
196
|
+
}
|
|
262
197
|
}
|
|
263
198
|
|
|
264
199
|
/// Resolves content for a path with optional caching.
|
|
@@ -271,19 +206,27 @@ final class BrowserManager {
|
|
|
271
206
|
return hydrateChildren(cached)
|
|
272
207
|
}
|
|
273
208
|
|
|
274
|
-
let resolvedTrack = try await
|
|
209
|
+
let resolvedTrack = try await pages.resolve(normalizedPath)
|
|
275
210
|
|
|
276
211
|
// Cache the resolved content for future navigation
|
|
277
212
|
contentCache.set(normalizedPath, value: resolvedTrack)
|
|
278
213
|
|
|
279
|
-
// Cache children for track lookups
|
|
280
|
-
cacheChildren(resolvedTrack)
|
|
281
|
-
|
|
282
214
|
return hydrateChildren(resolvedTrack)
|
|
283
215
|
}
|
|
284
216
|
|
|
285
217
|
/// Invalidates the content cache for a specific path.
|
|
286
|
-
|
|
218
|
+
///
|
|
219
|
+
/// Expects a *container* path. A contextual path (`…?__trackId=…`) addresses
|
|
220
|
+
/// a child, not a cache entry — entries are keyed by the stripped parent — so
|
|
221
|
+
/// passing one used to remove nothing at all, silently. Android `require`s the
|
|
222
|
+
/// same precondition; this throws so the misuse surfaces identically on both
|
|
223
|
+
/// platforms instead of no-opping on one.
|
|
224
|
+
func invalidateContentCache(_ path: String) throws {
|
|
225
|
+
guard !BrowserPathHelper.isContextual(path) else {
|
|
226
|
+
throw BrowserError.invalidConfiguration(
|
|
227
|
+
"invalidateContentCache() expects a container path, not a contextual path: \(path)",
|
|
228
|
+
)
|
|
229
|
+
}
|
|
287
230
|
contentCache.remove(path)
|
|
288
231
|
}
|
|
289
232
|
|
|
@@ -291,324 +234,23 @@ final class BrowserManager {
|
|
|
291
234
|
/// Runs from `invalidateAllContent`, so re-resolve the layer thunks too.
|
|
292
235
|
func clearContentCache() {
|
|
293
236
|
contentCache.clear()
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
let
|
|
306
|
-
|
|
307
|
-
let
|
|
308
|
-
|
|
309
|
-
// Only apply result if this is still the current navigation
|
|
310
|
-
guard navigationId == currentNavigationId else { return }
|
|
311
|
-
|
|
312
|
-
content = resolved
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
private func resolveUncached(_ path: String) async throws -> ResolvedTrack {
|
|
316
|
-
// Match an explicit route (or the '*' default). With no match, fall back to
|
|
317
|
-
// the implicit default: fetch the path via the request + browse config.
|
|
318
|
-
let routeEntry = (config.routes).flatMap { findBestRouteMatch(path: path, routes: $0) }
|
|
319
|
-
|
|
320
|
-
let resolvedTrack: ResolvedTrack = if let (entry, routeMatch) = routeEntry {
|
|
321
|
-
try await resolveRouteEntry(entry, path: path, params: routeMatch.params)
|
|
322
|
-
} else {
|
|
323
|
-
// Implicit default — no route config, just request → browse → fetch path.
|
|
324
|
-
try await resolveFromConfig(
|
|
325
|
-
nil, path: path, params: ["path": path],
|
|
326
|
-
)
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Validate and transform children
|
|
330
|
-
if let children = resolvedTrack.children {
|
|
331
|
-
let transformed = try await transformChildren(
|
|
332
|
-
children, parentPath: path, routeEntry: routeEntry?.0,
|
|
333
|
-
)
|
|
334
|
-
return resolvedTrack.copying(children: transformed)
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return resolvedTrack
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// MARK: - Route Resolution
|
|
341
|
-
|
|
342
|
-
private func findBestRouteMatch(
|
|
343
|
-
path: String,
|
|
344
|
-
routes: [NativeRouteEntry],
|
|
345
|
-
) -> (NativeRouteEntry, RouteMatch)? {
|
|
346
|
-
// Filter out special routes
|
|
347
|
-
let browseRoutes = routes.filter {
|
|
348
|
-
$0.path != Self.tabsRoutePath &&
|
|
349
|
-
$0.path != Self.searchRoutePath &&
|
|
350
|
-
$0.path != Self.defaultRoutePath
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Try to find a matching route
|
|
354
|
-
if let match = router.findBestMatch(path: path, routes: browseRoutes) {
|
|
355
|
-
return match
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Fall back to __default__ if present
|
|
359
|
-
if let defaultRoute = routes.first(where: { $0.path == Self.defaultRoutePath }) {
|
|
360
|
-
return (defaultRoute, RouteMatch(params: ["path": path]))
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return nil
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
private func resolveRouteEntry(
|
|
367
|
-
_ entry: NativeRouteEntry,
|
|
368
|
-
path: String,
|
|
369
|
-
params: [String: String],
|
|
370
|
-
) async throws -> ResolvedTrack {
|
|
371
|
-
// Priority: callback > config > static
|
|
372
|
-
if let callback = entry.browseCallback {
|
|
373
|
-
let callbackParam = BrowserSourceCallbackParam(path: path, routeParams: params)
|
|
374
|
-
// BrowserSourceCallback may return a BrowseResult synchronously or via a
|
|
375
|
-
// Promise. Nitro flattens (ResolvedTrack | BrowseError) | Promise<BrowseResult>
|
|
376
|
-
// into a 3-arm variant: sync track, sync error, or a Promise resolving to a
|
|
377
|
-
// BrowseResult (which is itself a ResolvedTrack | BrowseError variant).
|
|
378
|
-
switch try await callback(callbackParam).await() {
|
|
379
|
-
case let .first(resolvedTrack):
|
|
380
|
-
return resolvedTrack
|
|
381
|
-
case let .second(browseError):
|
|
382
|
-
throw BrowserError.callbackError(browseError.error)
|
|
383
|
-
case let .third(promise):
|
|
384
|
-
switch try await promise.await() {
|
|
385
|
-
case let .first(resolvedTrack):
|
|
386
|
-
return resolvedTrack
|
|
387
|
-
case let .second(browseError):
|
|
388
|
-
throw BrowserError.callbackError(browseError.error)
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if let browseConfig = entry.browseConfig {
|
|
394
|
-
return try await resolveFromConfig(
|
|
395
|
-
browseConfig, path: path, params: params,
|
|
396
|
-
)
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if let staticContent = entry.browseStatic {
|
|
400
|
-
return staticContent
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
throw BrowserError.contentNotFound(path: path)
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/// Builds the HTTP request for an API-backed route by layering
|
|
407
|
-
/// request → kind → route configs. `initialQuery` seeds request-level query
|
|
408
|
-
/// params (e.g. search q/mode/…) onto the base before the route layer, so they
|
|
409
|
-
/// survive into `request.query` even when a layer's transform "wins
|
|
410
|
-
/// completely" (it is handed only the base, so the route config's own static
|
|
411
|
-
/// query would otherwise be dropped). `routeConfig` is nil for the implicit
|
|
412
|
-
/// browse default; `kindConfig` is nil for kinds with no per-kind config.
|
|
413
|
-
private func buildApiRequest(
|
|
414
|
-
kind kindConfig: TransformableRequestConfig?,
|
|
415
|
-
_ routeConfig: TransformableRequestConfig?,
|
|
416
|
-
path: String,
|
|
417
|
-
params: [String: String],
|
|
418
|
-
initialQuery: [String: String]? = nil,
|
|
419
|
-
) async throws -> HttpClient.HttpRequest {
|
|
420
|
-
try await ensureLayersResolved()
|
|
421
|
-
|
|
422
|
-
var merged = RequestConfig(
|
|
423
|
-
method: nil, path: path, baseUrl: nil, headers: nil,
|
|
424
|
-
query: nil, body: nil, contentType: nil, userAgent: nil,
|
|
425
|
-
)
|
|
426
|
-
merged = try await applyLayer(resolvedRequestLayer, to: merged, params: params)
|
|
427
|
-
if let initialQuery, !initialQuery.isEmpty {
|
|
428
|
-
merged = merged.copying(query: mergeDicts(merged.query, initialQuery))
|
|
429
|
-
}
|
|
430
|
-
merged = try await applyLayer(kindConfig, to: merged, params: params)
|
|
431
|
-
merged = try await applyLayer(routeConfig, to: merged, params: params)
|
|
432
|
-
|
|
433
|
-
guard let baseUrl = merged.baseUrl else {
|
|
434
|
-
throw BrowserError.invalidConfiguration("No URL configured for route")
|
|
435
|
-
}
|
|
436
|
-
var url = BrowserPathHelper.buildUrl(baseUrl: baseUrl, path: merged.path ?? path)
|
|
437
|
-
if let query = merged.query, !query.isEmpty {
|
|
438
|
-
url = BrowserPathHelper.appendQuery(query, to: url)
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return HttpClient.HttpRequest(
|
|
442
|
-
url: url,
|
|
443
|
-
method: merged.method?.stringValue ?? "GET",
|
|
444
|
-
headers: merged.headers,
|
|
445
|
-
body: merged.body,
|
|
446
|
-
contentType: merged.contentType ?? HttpClient.defaultContentType,
|
|
447
|
-
userAgent: merged.userAgent ?? HttpClient.defaultUserAgent,
|
|
237
|
+
requests.invalidateLayers()
|
|
238
|
+
// The search slot is content too: a locale switch or a re-auth changes
|
|
239
|
+
// titles, src, artwork and availability, and nothing else ever evicts it,
|
|
240
|
+
// so a repeated query returned pre-invalidation results forever.
|
|
241
|
+
lastSearchKey = nil
|
|
242
|
+
lastSearchResults = nil
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/// `error` as the app's `formatNavigationError` displays it, or its default display when
|
|
246
|
+
/// there is no formatter or it fails.
|
|
247
|
+
func formatted(_ error: NavigationError, path: String) async -> FormattedNavigationError {
|
|
248
|
+
let fallback = error.defaultFormatted()
|
|
249
|
+
guard let formatter = config.formatNavigationError else { return fallback }
|
|
250
|
+
let params = FormatNavigationErrorParams(
|
|
251
|
+
error: error, defaultFormatted: fallback, path: path, kind: NavigationErrorKind(path: path),
|
|
448
252
|
)
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
/// Resolves a browse route: a page object (`{title,url,children:[…]}`).
|
|
452
|
-
/// The browse kind layer is the cached resolved layer (`resolvedBrowseLayer`),
|
|
453
|
-
/// populated by `ensureLayersResolved()` inside `buildApiRequest`.
|
|
454
|
-
private func resolveFromConfig(
|
|
455
|
-
_ routeConfig: TransformableRequestConfig?,
|
|
456
|
-
path: String,
|
|
457
|
-
params: [String: String],
|
|
458
|
-
) async throws -> ResolvedTrack {
|
|
459
|
-
try await ensureLayersResolved()
|
|
460
|
-
let request = try await buildApiRequest(
|
|
461
|
-
kind: resolvedBrowseLayer, routeConfig, path: path, params: params,
|
|
462
|
-
)
|
|
463
|
-
|
|
464
|
-
logger.debug("Resolving content from API")
|
|
465
|
-
logger.debug(" path: \(path)")
|
|
466
|
-
logger.debug(" url: \(request.url)")
|
|
467
|
-
|
|
468
|
-
let result: JsonResolvedTrack = try await httpClient.requestJson(request, as: JsonResolvedTrack.self)
|
|
469
|
-
let nitroResult = result.toNitro()
|
|
470
|
-
|
|
471
|
-
logger.debug("Resolved: \(nitroResult.title)")
|
|
472
|
-
if let children = nitroResult.children {
|
|
473
|
-
logger.debug(" children: \(children.count) tracks")
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return nitroResult
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/// Applies one request-config layer (request / browse / route) onto a base.
|
|
480
|
-
/// A transform (async and/or sync) wins completely: it receives the base and its
|
|
481
|
-
/// result replaces it. When both are set they run as a pipeline — async first,
|
|
482
|
-
/// then sync. Otherwise the layer's static fields merge over the base. `path` is
|
|
483
|
-
/// carried from the base (only a transform may change it).
|
|
484
|
-
func applyLayer(
|
|
485
|
-
_ layer: TransformableRequestConfig?,
|
|
486
|
-
to base: RequestConfig,
|
|
487
|
-
params: [String: String],
|
|
488
|
-
) async throws -> RequestConfig {
|
|
489
|
-
guard let layer else { return base }
|
|
490
|
-
// Run-both transform: async first, then sync (each replaces the running config).
|
|
491
|
-
// The bridge await depth — the bug-prone part — is centralised in awaitAsync/SyncConfig.
|
|
492
|
-
if layer.transform != nil || layer.transformSync != nil {
|
|
493
|
-
var result = base
|
|
494
|
-
if let transform = layer.transform { result = try await awaitAsyncConfig(transform(result, params)) }
|
|
495
|
-
if let transformSync = layer.transformSync { result = try await awaitSyncConfig(transformSync(result, params)) }
|
|
496
|
-
return result
|
|
497
|
-
}
|
|
498
|
-
return RequestConfig(
|
|
499
|
-
method: layer.method ?? base.method,
|
|
500
|
-
path: base.path,
|
|
501
|
-
baseUrl: layer.baseUrl ?? base.baseUrl,
|
|
502
|
-
headers: mergeDicts(base.headers, layer.headers),
|
|
503
|
-
query: mergeDicts(base.query, layer.query),
|
|
504
|
-
body: layer.body ?? base.body,
|
|
505
|
-
contentType: layer.contentType ?? base.contentType,
|
|
506
|
-
userAgent: layer.userAgent ?? base.userAgent,
|
|
507
|
-
)
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/// Merges two optional dictionaries, with override values taking precedence.
|
|
511
|
-
/// Delegates to the pure, unit-tested `MediaResolveComposer.mergeDicts`.
|
|
512
|
-
func mergeDicts(
|
|
513
|
-
_ base: [String: String]?,
|
|
514
|
-
_ override: [String: String]?,
|
|
515
|
-
) -> [String: String]? {
|
|
516
|
-
MediaResolveComposer.mergeDicts(base, override)
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// MARK: - Layer Resolvers
|
|
520
|
-
|
|
521
|
-
/// Resolve one layer: invoke its resolver if present, else return the static
|
|
522
|
-
/// layer config unchanged. The resolver is Promise-only (the TS layer wraps a
|
|
523
|
-
/// sync-or-async thunk in Promise.resolve), so its result is the JS promise
|
|
524
|
-
/// behind the bridge promise — a double await.
|
|
525
|
-
private func resolveLayer(
|
|
526
|
-
config staticConfig: TransformableRequestConfig?,
|
|
527
|
-
resolver: (() -> Promise<Promise<TransformableRequestConfig>>)?,
|
|
528
|
-
) async throws -> TransformableRequestConfig? {
|
|
529
|
-
guard let resolver else { return staticConfig }
|
|
530
|
-
return try await resolver().await().await()
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
/// Ensure request/browse resolvers are resolved for the current generation,
|
|
534
|
-
/// caching the results. Re-resolves after a generation bump (config change /
|
|
535
|
-
/// invalidateAllContent). Idempotent within a generation.
|
|
536
|
-
func ensureLayersResolved() async throws {
|
|
537
|
-
guard resolvedLayerGeneration != layerGeneration else { return }
|
|
538
|
-
let generation = layerGeneration
|
|
539
|
-
let req = try await resolveLayer(config: config.request, resolver: config.requestResolver)
|
|
540
|
-
let brw = try await resolveLayer(config: config.browse, resolver: config.browseResolver)
|
|
541
|
-
// Only commit if still current (a newer generation may have started during await).
|
|
542
|
-
guard generation == layerGeneration else { return }
|
|
543
|
-
resolvedRequestLayer = req
|
|
544
|
-
resolvedBrowseLayer = brw
|
|
545
|
-
resolvedLayerGeneration = generation
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// MARK: - Child Transformation
|
|
549
|
-
|
|
550
|
-
private func transformChildren(
|
|
551
|
-
_ children: [Track],
|
|
552
|
-
parentPath: String,
|
|
553
|
-
routeEntry: NativeRouteEntry?,
|
|
554
|
-
) async throws -> [Track] {
|
|
555
|
-
var transformed: [Track] = []
|
|
556
|
-
|
|
557
|
-
for track in children {
|
|
558
|
-
// Validate track has stable identifier. A track carrying an imageRow is
|
|
559
|
-
// exempt: a url-less row is a pure preview — never selected, navigated
|
|
560
|
-
// to, or cached (its items carry their own identity).
|
|
561
|
-
if track.url == nil, track.src == nil, track.imageRow?.isEmpty != false {
|
|
562
|
-
throw BrowserError.invalidConfiguration(
|
|
563
|
-
"Track must have either 'url' or 'src' for stable identification: \(track.title)",
|
|
564
|
-
)
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
var transformedTrack = track
|
|
568
|
-
|
|
569
|
-
if track.src != nil, track.url == nil {
|
|
570
|
-
let contextualUrl = BrowserPathHelper.build(parentPath: parentPath, trackId: track.src!)
|
|
571
|
-
transformedTrack = track.copying(url: contextualUrl)
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// Resolve artwork URL at browse-time (no size context)
|
|
575
|
-
let artworkConfig = routeEntry?.artwork ?? config.artwork
|
|
576
|
-
if let imageSource = await resolveArtworkUrl(track: transformedTrack, perRouteConfig: artworkConfig) {
|
|
577
|
-
transformedTrack = transformedTrack.copying(artworkSource: imageSource)
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Resolve artwork for image row items
|
|
581
|
-
if let imageRowItems = transformedTrack.imageRow {
|
|
582
|
-
var resolvedItems: [ImageRowItem] = []
|
|
583
|
-
for item in imageRowItems {
|
|
584
|
-
let itemImageSource = await resolveArtworkUrl(track: item.toTrack(), perRouteConfig: artworkConfig)
|
|
585
|
-
// Playable items get a contextual URL like any list row, so a
|
|
586
|
-
// thumbnail tap expands into its section's queue instead of a
|
|
587
|
-
// queue of one (ADR 0006).
|
|
588
|
-
let itemUrl = item.url ?? item.src.map {
|
|
589
|
-
BrowserPathHelper.build(parentPath: parentPath, trackId: $0)
|
|
590
|
-
}
|
|
591
|
-
resolvedItems.append(ImageRowItem(
|
|
592
|
-
id: item.id,
|
|
593
|
-
url: itemUrl,
|
|
594
|
-
src: item.src,
|
|
595
|
-
artwork: item.artwork,
|
|
596
|
-
artworkSource: itemImageSource,
|
|
597
|
-
title: item.title,
|
|
598
|
-
artist: item.artist,
|
|
599
|
-
album: item.album,
|
|
600
|
-
albumUrl: item.albumUrl,
|
|
601
|
-
live: item.live,
|
|
602
|
-
request: item.request,
|
|
603
|
-
))
|
|
604
|
-
}
|
|
605
|
-
transformedTrack = transformedTrack.copying(imageRow: resolvedItems)
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
transformed.append(transformedTrack)
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
return transformed
|
|
253
|
+
return await (try? formatter(params).await()) ?? fallback
|
|
612
254
|
}
|
|
613
255
|
|
|
614
256
|
// MARK: - Search
|
|
@@ -621,71 +263,43 @@ final class BrowserManager {
|
|
|
621
263
|
)
|
|
622
264
|
}
|
|
623
265
|
|
|
624
|
-
///
|
|
625
|
-
///
|
|
626
|
-
|
|
266
|
+
/// The cache identity of a search. Sorted so key order is stable, and
|
|
267
|
+
/// `\u{1}`-delimited so two different param sets can't serialize alike.
|
|
268
|
+
private static func searchCacheKey(_ params: SearchParams) -> String {
|
|
269
|
+
PageResolver.searchQueryParams(params)
|
|
270
|
+
.sorted { $0.key < $1.key }
|
|
271
|
+
.map { "\($0.key)=\($0.value)" }
|
|
272
|
+
.joined(separator: "\u{1}")
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/// Structured search, cached by the full parameter set.
|
|
276
|
+
///
|
|
277
|
+
/// Voice intents reuse `query` across different structures —
|
|
278
|
+
/// `MediaIntentCriteria` falls back to the structured value when there is no
|
|
279
|
+
/// spoken name, so "play jazz" (genre) and a text search for "jazz" arrive
|
|
280
|
+
/// with an identical `query` — hence `searchCacheKey` rather than `query`.
|
|
627
281
|
func search(_ params: SearchParams) async throws -> ResolvedTrack {
|
|
628
282
|
let query = params.query
|
|
283
|
+
let cacheKey = Self.searchCacheKey(params)
|
|
629
284
|
|
|
630
|
-
// Check cache -
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
guard let routes = config.routes else {
|
|
637
|
-
throw BrowserError.contentNotFound(path: Self.searchRoutePath)
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
guard let searchEntry = routes.first(where: { $0.path == Self.searchRoutePath }) else {
|
|
641
|
-
throw BrowserError.contentNotFound(path: Self.searchRoutePath)
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
var results: [Track]
|
|
645
|
-
|
|
646
|
-
if let callback = searchEntry.searchCallback {
|
|
647
|
-
let outerPromise = callback(params)
|
|
648
|
-
let innerPromise = try await outerPromise.await()
|
|
649
|
-
results = try await innerPromise.await()
|
|
650
|
-
} else if let searchConfig = searchEntry.searchConfig {
|
|
651
|
-
// Search is its own kind — request → search route (no browse layer). The
|
|
652
|
-
// endpoint returns a bare Track array (unlike browse's page object). The
|
|
653
|
-
// search params go through `initialQuery` so they land in `request.query`
|
|
654
|
-
// (and the URL); a config with a transform only ever sees the base.
|
|
655
|
-
// Seed the search config's static path onto the base — a layer's static
|
|
656
|
-
// path never applies (carried from the base; only a transform may change
|
|
657
|
-
// it). Matches the web stub's fetchSearchResults and Android.
|
|
658
|
-
var queryParams: [String: String] = ["q": query]
|
|
659
|
-
if let mode = params.mode { queryParams["mode"] = mode.stringValue }
|
|
660
|
-
if params.reference == .my { queryParams["reference"] = "my" }
|
|
661
|
-
if let genre = params.genre { queryParams["genre"] = genre }
|
|
662
|
-
if let artist = params.artist { queryParams["artist"] = artist }
|
|
663
|
-
if let album = params.album { queryParams["album"] = album }
|
|
664
|
-
if let title = params.title { queryParams["title"] = title }
|
|
665
|
-
if let playlist = params.playlist { queryParams["playlist"] = playlist }
|
|
666
|
-
|
|
667
|
-
let request = try await buildApiRequest(
|
|
668
|
-
kind: nil, searchConfig, path: searchConfig.path ?? "", params: ["q": query],
|
|
669
|
-
initialQuery: queryParams,
|
|
670
|
-
)
|
|
671
|
-
logger.debug("Searching via API: \(request.url)")
|
|
672
|
-
let jsonTracks: [JsonTrack] = try await httpClient.requestJson(request, as: [JsonTrack].self)
|
|
673
|
-
results = jsonTracks.map { $0.toNitro() }
|
|
674
|
-
} else {
|
|
675
|
-
throw BrowserError.contentNotFound(path: Self.searchRoutePath)
|
|
285
|
+
// Check cache - hydrate favorites on the way out, since they may have
|
|
286
|
+
// changed since the search ran (the slot holds the raw results for exactly
|
|
287
|
+
// this reason — see below).
|
|
288
|
+
if cacheKey == lastSearchKey, let results = lastSearchResults {
|
|
289
|
+
return makeSearchResult(query: query, results: results)
|
|
676
290
|
}
|
|
677
291
|
|
|
678
|
-
|
|
679
|
-
let hydratedResults = results.map { hydrateFavorite($0) }
|
|
292
|
+
let results = try await pages.search(params)
|
|
680
293
|
|
|
681
|
-
// Cache results
|
|
682
|
-
|
|
683
|
-
|
|
294
|
+
// Cache the RAW results, never the hydrated ones: hydration is one-shot
|
|
295
|
+
// (`hydrateFavorite` skips a track whose `favorited` is already set), so a
|
|
296
|
+
// hydrated slot would serve the favorites as they were at first search
|
|
297
|
+
// forever. The content cache keys on the same rule — it stores the
|
|
298
|
+
// unhydrated resolve and hydrates in `resolve()`.
|
|
299
|
+
lastSearchKey = cacheKey
|
|
300
|
+
lastSearchResults = results
|
|
684
301
|
|
|
685
|
-
|
|
686
|
-
hydratedResults.forEach { cacheTrack($0) }
|
|
687
|
-
|
|
688
|
-
return makeSearchResult(query: query, results: hydratedResults)
|
|
302
|
+
return makeSearchResult(query: query, results: results)
|
|
689
303
|
}
|
|
690
304
|
|
|
691
305
|
/// Search and return playable tracks for "play «X»" voice intents, drilling
|
|
@@ -693,37 +307,44 @@ final class BrowserManager {
|
|
|
693
307
|
/// station. Decision in `SearchDrillIn.playable`; mirrors Android's
|
|
694
308
|
/// `searchPlayable`.
|
|
695
309
|
func searchPlayable(_ params: SearchParams) async throws -> [Track]? {
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
310
|
+
// Voice search never matches a disabled track (Track.disabled) — filtered
|
|
311
|
+
// before drill-in so an unavailable first result can't capture the play.
|
|
312
|
+
let children = try await ((search(params).flattenedChildren) ?? [])
|
|
313
|
+
.filter { $0.disabled != true }
|
|
314
|
+
return try await SearchDrillIn.playable(from: children) { [self] path in
|
|
315
|
+
logger.debug("First search result is browsable-only, resolving: \(path)")
|
|
316
|
+
return try await ((resolve(path).flattenedChildren) ?? []).filter { $0.disabled != true }
|
|
700
317
|
}
|
|
701
318
|
}
|
|
702
319
|
|
|
320
|
+
/// Wraps raw search results as a resolved page, hydrating favorites on the
|
|
321
|
+
/// way out — the single exit for `search`, so an unhydrated search page can't
|
|
322
|
+
/// be emitted (the same funnel `resolve()` gets from `hydrateChildren`).
|
|
703
323
|
private func makeSearchResult(query: String, results: [Track]) -> ResolvedTrack {
|
|
324
|
+
// Search results are a flat list; as a resolved page they are one
|
|
325
|
+
// untitled section (ADR 0010).
|
|
704
326
|
ResolvedTrack(
|
|
705
|
-
|
|
706
|
-
|
|
327
|
+
path: BrowserPathHelper.createSearchPath(query),
|
|
328
|
+
style: nil,
|
|
329
|
+
sections: [.untitled(results.map { hydrateFavorite($0) })],
|
|
330
|
+
children: nil,
|
|
707
331
|
carPlaySiriListButton: nil,
|
|
708
332
|
id: nil,
|
|
709
333
|
src: nil,
|
|
710
334
|
artwork: nil,
|
|
711
335
|
artworkSource: nil, request: nil,
|
|
712
|
-
artworkCarPlayTinted: nil,
|
|
713
336
|
title: "Search: \(query)",
|
|
714
337
|
subtitle: nil,
|
|
715
338
|
artist: nil,
|
|
716
|
-
|
|
339
|
+
albumPath: nil,
|
|
717
340
|
album: nil,
|
|
718
341
|
description: nil,
|
|
719
342
|
genre: nil,
|
|
720
343
|
duration: nil,
|
|
721
|
-
|
|
722
|
-
childrenStyle: nil,
|
|
344
|
+
disabled: nil,
|
|
723
345
|
favorited: nil,
|
|
724
|
-
groupTitle: nil,
|
|
725
346
|
live: nil,
|
|
726
|
-
|
|
347
|
+
icyCharset: nil,
|
|
727
348
|
)
|
|
728
349
|
}
|
|
729
350
|
|
|
@@ -731,55 +352,46 @@ final class BrowserManager {
|
|
|
731
352
|
|
|
732
353
|
/// Query navigation tabs.
|
|
733
354
|
func queryTabs() async throws -> [Track] {
|
|
734
|
-
guard let
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
guard let tabsEntry = routes.first(where: { $0.path == Self.tabsRoutePath }) else {
|
|
739
|
-
return []
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
let resolved = try await resolveRouteEntry(tabsEntry, path: Self.tabsRoutePath, params: [:])
|
|
743
|
-
let tabTracks = resolved.children ?? []
|
|
744
|
-
|
|
745
|
-
tabs = tabTracks
|
|
746
|
-
return tabTracks
|
|
355
|
+
guard let tabs = try await pages.tabs() else { return [] }
|
|
356
|
+
self.tabs = tabs
|
|
357
|
+
return tabs
|
|
747
358
|
}
|
|
748
359
|
|
|
749
360
|
// MARK: - Queue Expansion (for CarPlay/external controllers)
|
|
750
361
|
|
|
751
|
-
/// Expands a contextual
|
|
752
|
-
func
|
|
753
|
-
guard BrowserPathHelper.isContextual(
|
|
362
|
+
/// Expands a contextual path to a full queue of playable tracks.
|
|
363
|
+
func expandQueueFromContextualPath(_ path: String) async throws -> (tracks: [Track], selectedIndex: Int)? {
|
|
364
|
+
guard BrowserPathHelper.isContextual(path) else { return nil }
|
|
754
365
|
|
|
755
|
-
let parentPath = BrowserPathHelper.stripTrackId(
|
|
756
|
-
guard let trackId = BrowserPathHelper.extractTrackId(
|
|
366
|
+
let parentPath = BrowserPathHelper.stripTrackId(path)
|
|
367
|
+
guard let trackId = BrowserPathHelper.extractTrackId(path) else { return nil }
|
|
757
368
|
|
|
758
|
-
logger.debug("Expanding queue from contextual
|
|
759
|
-
logger.debug("
|
|
369
|
+
logger.debug("Expanding queue from contextual path")
|
|
370
|
+
logger.debug(" path: \(path)")
|
|
760
371
|
logger.debug(" parentPath: \(parentPath)")
|
|
761
372
|
logger.debug(" trackId: \(trackId)")
|
|
762
373
|
|
|
763
374
|
// Resolve the parent container
|
|
764
375
|
let resolved = try await resolve(parentPath, useCache: true)
|
|
765
|
-
guard let
|
|
766
|
-
|
|
767
|
-
// Queue scope is the tapped section, not the whole page (ADR 0006).
|
|
768
|
-
//
|
|
769
|
-
//
|
|
770
|
-
//
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
sectionTracks = tracks
|
|
777
|
-
case nil:
|
|
376
|
+
guard let sections = resolved.normalizedSections else { return nil }
|
|
377
|
+
|
|
378
|
+
// Queue scope is the tapped section, not the whole page (ADR 0006). The
|
|
379
|
+
// stamped flat index pins which section (and which copy) was tapped when
|
|
380
|
+
// the same identity appears more than once; it is only a tie-breaker —
|
|
381
|
+
// a stale index falls back to the first identity match. An id that no
|
|
382
|
+
// longer appears on the page at all aborts the expansion — the caller
|
|
383
|
+
// falls back to the stored single track (matching Android); silently
|
|
384
|
+
// queueing the changed list would resume the wrong station.
|
|
385
|
+
let tappedIndex = BrowserPathHelper.extractIndex(path)
|
|
386
|
+
guard let scoped = SectionScope.scoped(in: sections, containing: trackId, tappedIndex: tappedIndex) else {
|
|
778
387
|
return nil
|
|
779
388
|
}
|
|
389
|
+
let sectionTracks = scoped.section.children
|
|
390
|
+
let tappedOffset = scoped.tappedOffset
|
|
780
391
|
|
|
781
|
-
// Filter to playable tracks (have src)
|
|
782
|
-
|
|
392
|
+
// Filter to playable tracks (have src). A disabled track is unavailable —
|
|
393
|
+
// queue expansion excludes it, so auto-advance never meets one.
|
|
394
|
+
let playableTracks = sectionTracks.filter { $0.src != nil && $0.disabled != true }
|
|
783
395
|
guard !playableTracks.isEmpty else { return nil }
|
|
784
396
|
|
|
785
397
|
logger.debug("Found \(playableTracks.count) playable tracks")
|
|
@@ -787,10 +399,18 @@ final class BrowserManager {
|
|
|
787
399
|
logger.debug(" [\(index)] \(track.title) - src: \(track.src ?? "nil")")
|
|
788
400
|
}
|
|
789
401
|
|
|
790
|
-
// Find selected track index
|
|
791
|
-
//
|
|
792
|
-
//
|
|
793
|
-
|
|
402
|
+
// Find selected track index: the pinned copy when the stamp survived,
|
|
403
|
+
// else the first identity match. A miss must abort rather than silently
|
|
404
|
+
// start the queue at the wrong track (unreachable in practice — the
|
|
405
|
+
// section was found BY this id).
|
|
406
|
+
let selectedIndex: Int
|
|
407
|
+
if let tappedOffset, sectionTracks[tappedOffset].src != nil, sectionTracks[tappedOffset].disabled != true {
|
|
408
|
+
selectedIndex =
|
|
409
|
+
sectionTracks.prefix(through: tappedOffset)
|
|
410
|
+
.count(where: { $0.src != nil && $0.disabled != true }) - 1
|
|
411
|
+
} else if let index = playableTracks.firstIndex(where: { $0.identity == trackId }) {
|
|
412
|
+
selectedIndex = index
|
|
413
|
+
} else {
|
|
794
414
|
return nil
|
|
795
415
|
}
|
|
796
416
|
logger.debug("Selected track index: \(selectedIndex)")
|
|
@@ -802,20 +422,6 @@ final class BrowserManager {
|
|
|
802
422
|
|
|
803
423
|
return (tracks: playableTracks, selectedIndex: selectedIndex)
|
|
804
424
|
}
|
|
805
|
-
|
|
806
|
-
// MARK: - Accessors
|
|
807
|
-
|
|
808
|
-
func getPath() -> String {
|
|
809
|
-
path
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
func getContent() -> ResolvedTrack? {
|
|
813
|
-
content
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
func getTabs() -> [Track]? {
|
|
817
|
-
tabs
|
|
818
|
-
}
|
|
819
425
|
}
|
|
820
426
|
|
|
821
427
|
// MARK: - TrackSelectionBrowser
|