react-native-audio-browser 0.2.0-next.2 → 0.2.0-next.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -21
- package/android/build.gradle +35 -12
- package/android/gradle.properties +2 -2
- package/android/src/main/AndroidManifest.xml +3 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +389 -164
- package/android/src/main/java/com/audiobrowser/AudioBrowserLifecycleModule.kt +38 -0
- package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +18 -2
- package/android/src/main/java/com/audiobrowser/ServiceBinding.kt +48 -0
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +315 -213
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +24 -5
- package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +97 -58
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +70 -29
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +20 -7
- package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
- package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
- package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +20 -3
- package/android/src/main/java/com/audiobrowser/model/IcyTitle.kt +24 -0
- package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +10 -6
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +12 -12
- package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +111 -58
- package/android/src/main/java/com/audiobrowser/player/CrossOriginHeaderStripper.kt +77 -0
- package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +28 -10
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +509 -230
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +218 -252
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -25
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +113 -78
- package/android/src/main/java/com/audiobrowser/player/Player.kt +81 -38
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
- package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +1 -1
- package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +2 -1
- package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +67 -11
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +168 -0
- package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +58 -14
- package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +35 -7
- package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +278 -59
- package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +7 -9
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +56 -40
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +56 -52
- package/android/src/main/res/values/strings.xml +25 -5
- package/android/src/test/java/com/audiobrowser/ServiceBindingTest.kt +87 -0
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +119 -33
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +15 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +346 -8
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
- package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +3 -2
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -55
- package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -0
- package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
- package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
- package/android/src/test/java/com/audiobrowser/model/IcyTitleTest.kt +49 -0
- package/android/src/test/java/com/audiobrowser/player/BrowseTilesTest.kt +385 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +101 -58
- package/android/src/test/java/com/audiobrowser/player/CrossOriginHeaderStripperTest.kt +94 -0
- package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +45 -0
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
- package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +3 -4
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +3 -3
- package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
- package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +197 -7
- package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +325 -4
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherCallSiteTest.kt +322 -0
- package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +83 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +162 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
- package/ios/Browser/BrowserConfig.swift +102 -5
- package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
- package/ios/Browser/BrowserManager.swift +230 -205
- package/ios/Browser/BrowserPathHelper.swift +44 -44
- package/ios/Browser/InertStyleDiagnostic.swift +279 -0
- package/ios/Browser/JsonModels.swift +264 -106
- package/ios/Browser/SearchDrillIn.swift +7 -7
- package/ios/Browser/SectionPresentation.swift +64 -0
- package/ios/Browser/SectionScope.swift +58 -24
- package/ios/Browser/StyleResolver.swift +58 -0
- package/ios/Browser/TabBarEntries.swift +9 -3
- package/ios/Browser/TrackIdTemplate.swift +37 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
- package/ios/CarPlay/CarPlayController.swift +227 -85
- package/ios/CarPlay/CarPlayImageLoader.swift +42 -20
- package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
- package/ios/CarPlay/RNABMediaAddHandler.swift +6 -6
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +2 -2
- package/ios/CarPlay/RNABMediaIntentHandler.swift +16 -16
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
- package/ios/Extension/ResolvedTrack+Copying.swift +13 -15
- package/ios/Extension/Section+Copying.swift +29 -0
- package/ios/Extension/String+IcyCharset.swift +21 -0
- package/ios/Extension/TimedMetadata+AVFoundation.swift +6 -4
- package/ios/Extension/Track+Copying.swift +8 -12
- package/ios/Extension/Track+Identity.swift +18 -0
- package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
- package/ios/Http/HttpClient.swift +8 -2
- package/ios/HybridAudioBrowser.swift +253 -100
- package/ios/Model/NitroTypeStubs.swift +125 -23
- package/ios/Model/PlayerUpdateOptions.swift +7 -0
- package/ios/Model/RemoteCommand.swift +18 -0
- package/ios/Model/TrackPlayerError.swift +2 -2
- package/ios/Option/SessionCategories.swift +3 -2
- package/ios/Player/MediaIntentCriteria.swift +17 -9
- package/ios/Player/MediaLoader.swift +33 -9
- package/ios/Player/NowPlayingUpdater.swift +54 -6
- package/ios/Player/PlaybackCoordinator.swift +13 -6
- package/ios/Player/PlaybackErrorHandler.swift +4 -4
- package/ios/Player/PlaybackTimer.swift +8 -2
- package/ios/Player/QueueManager.swift +46 -13
- package/ios/Player/RetryManager.swift +80 -18
- package/ios/Player/SeekCompletionHandler.swift +3 -1
- package/ios/Player/ShuffleOrder.swift +33 -0
- package/ios/Player/SleepTimerHandling.swift +13 -0
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +47 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +62 -24
- package/ios/Tests/BrowserPathHelperTests.swift +50 -20
- package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
- package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
- package/ios/Tests/JsonModelsTests.swift +150 -1
- package/ios/Tests/LRUCacheTests.swift +111 -0
- package/ios/Tests/LoadSeekCoordinatorTests.swift +3 -1
- package/ios/Tests/MediaIntentCriteriaTests.swift +2 -2
- package/ios/Tests/PlaybackCoordinatorTests.swift +1 -1
- package/ios/Tests/PlaybackErrorHandlerTests.swift +3 -1
- package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
- package/ios/Tests/PlayerTimeObserverTests.swift +37 -0
- package/ios/Tests/QueueManagerTests.swift +122 -10
- package/ios/Tests/RemoteCommandControllerTests.swift +172 -0
- package/ios/Tests/RemoteCommandTests.swift +67 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +39 -35
- package/ios/Tests/RetryManagerTests.swift +89 -0
- package/ios/Tests/SearchDrillInTests.swift +16 -16
- package/ios/Tests/SectionPresentationTests.swift +90 -0
- package/ios/Tests/SectionScopeTests.swift +87 -55
- package/ios/Tests/StyleResolverTests.swift +127 -0
- package/ios/Tests/Support/TestTypes.swift +8 -1
- package/ios/Tests/TabBarEntriesTests.swift +19 -19
- package/ios/Tests/TimedMetadataAVFoundationTests.swift +42 -0
- package/ios/Tests/TrackIdTemplateTests.swift +67 -0
- package/ios/Tests/TrackIdentityTests.swift +20 -0
- package/ios/Tests/TrackSelectorTests.swift +168 -33
- package/ios/TrackPlayer.swift +63 -35
- package/ios/TrackPlayerCallbacks.swift +5 -38
- package/ios/TrackSelector.swift +45 -30
- package/ios/Util/ArtworkImageFetcher.swift +16 -1
- package/ios/Util/LRUCache.swift +4 -1
- package/ios/Util/NetworkMonitor.swift +43 -26
- package/ios/Util/SFSymbolRenderer.swift +24 -2
- package/ios/Util/SVGProcessor.swift +9 -2
- package/lib/commonjs/AudioBrowser.js +8 -8
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/CLAUDE.md +52 -0
- package/lib/commonjs/features/browser-config.js +12 -8
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/browser.js +11 -3
- package/lib/commonjs/features/browser.js.map +1 -1
- package/lib/commonjs/features/carConnection.js +1 -1
- package/lib/commonjs/features/equalizer.js +1 -1
- package/lib/commonjs/features/errors.js +3 -3
- package/lib/commonjs/features/favorites.js +14 -11
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/gate.js +16 -1
- package/lib/commonjs/features/gate.js.map +1 -1
- package/lib/commonjs/features/metadata.js +20 -3
- package/lib/commonjs/features/metadata.js.map +1 -1
- package/lib/commonjs/features/network.js +1 -1
- package/lib/commonjs/features/nowPlaying.js +1 -1
- package/lib/commonjs/features/output.js +1 -1
- package/lib/commonjs/features/playback/controls.js +4 -2
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/playWhenReady.js +1 -1
- package/lib/commonjs/features/playback/playing.js +1 -1
- package/lib/commonjs/features/playback/progress.js +3 -2
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/rate.js +8 -1
- package/lib/commonjs/features/playback/rate.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +3 -4
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -6
- package/lib/commonjs/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/commonjs/features/playback/volume.js +1 -1
- package/lib/commonjs/features/player/options.js +48 -34
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +18 -9
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +23 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -0
- package/lib/commonjs/features/queue/activeTrack.js +15 -4
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +13 -6
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/features/queue/repeatMode.js +1 -1
- package/lib/commonjs/features/queue/shuffle.js +1 -1
- package/lib/commonjs/features/remoteControls.js +13 -92
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/features/sleepTimer.js +13 -1
- package/lib/commonjs/features/sleepTimer.js.map +1 -1
- package/lib/commonjs/test-utils/reactNativeStub.js +65 -0
- package/lib/commonjs/test-utils/reactNativeStub.js.map +1 -0
- package/lib/commonjs/utils/LazyNativeEmitter.js +28 -14
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/getTrackIdentity.js +24 -0
- package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +200 -75
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js +37 -0
- package/lib/commonjs/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/Player.js +254 -39
- package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +175 -100
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
- package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
- package/lib/commonjs/web/browser/SearchManager.js +18 -23
- package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/commonjs/web/browser/sections.js +89 -0
- package/lib/commonjs/web/browser/sections.js.map +1 -0
- package/lib/commonjs/web/http/RequestConfigBuilder.js +110 -27
- package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/commonjs/web/http/followMediaRedirect.js +219 -0
- package/lib/commonjs/web/http/followMediaRedirect.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
- package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/module/AudioBrowser.js +2 -1
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/CLAUDE.md +52 -0
- package/lib/module/features/browser-config.js +12 -8
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/browser.js +11 -3
- package/lib/module/features/browser.js.map +1 -1
- package/lib/module/features/carConnection.js +1 -1
- package/lib/module/features/equalizer.js +1 -1
- package/lib/module/features/errors.js +3 -3
- package/lib/module/features/favorites.js +14 -11
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/gate.js +16 -1
- package/lib/module/features/gate.js.map +1 -1
- package/lib/module/features/metadata.js +20 -3
- package/lib/module/features/metadata.js.map +1 -1
- package/lib/module/features/network.js +1 -1
- package/lib/module/features/nowPlaying.js +1 -1
- package/lib/module/features/output.js +1 -1
- package/lib/module/features/playback/controls.js +4 -2
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/playWhenReady.js +1 -1
- package/lib/module/features/playback/playing.js +1 -1
- package/lib/module/features/playback/progress.js +3 -2
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/rate.js +8 -1
- package/lib/module/features/playback/rate.js.map +1 -1
- package/lib/module/features/playback/state.js +3 -4
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/playback/trackPlaybackTime.js +0 -5
- package/lib/module/features/playback/trackPlaybackTime.js.map +1 -1
- package/lib/module/features/playback/volume.js +1 -1
- package/lib/module/features/player/options.js +47 -32
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +17 -8
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +19 -0
- package/lib/module/features/player/validateOptions.js.map +1 -0
- package/lib/module/features/queue/activeTrack.js +15 -4
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +13 -6
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/features/queue/repeatMode.js +1 -1
- package/lib/module/features/queue/shuffle.js +1 -1
- package/lib/module/features/remoteControls.js +13 -92
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/features/sleepTimer.js +13 -1
- package/lib/module/features/sleepTimer.js.map +1 -1
- package/lib/module/test-utils/reactNativeStub.js +58 -0
- package/lib/module/test-utils/reactNativeStub.js.map +1 -0
- package/lib/module/utils/LazyNativeEmitter.js +28 -14
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +31 -13
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/getTrackIdentity.js +20 -0
- package/lib/module/utils/getTrackIdentity.js.map +1 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +200 -75
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/LoadAttempt.js +32 -0
- package/lib/module/web/TrackPlayer/LoadAttempt.js.map +1 -0
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +18 -2
- package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +1 -1
- package/lib/module/web/TrackPlayer/Player.js +254 -39
- package/lib/module/web/TrackPlayer/Player.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueuePlayer.js +33 -12
- package/lib/module/web/TrackPlayer/QueuePlayer.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +175 -100
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/FavoriteManager.js +31 -21
- package/lib/module/web/browser/FavoriteManager.js.map +1 -1
- package/lib/module/web/browser/SearchManager.js +18 -23
- package/lib/module/web/browser/SearchManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/module/web/browser/sections.js +83 -0
- package/lib/module/web/browser/sections.js.map +1 -0
- package/lib/module/web/http/RequestConfigBuilder.js +110 -27
- package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/module/web/http/followMediaRedirect.js +215 -0
- package/lib/module/web/http/followMediaRedirect.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +4 -3
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/util/BrowserPathHelper.js +56 -11
- package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts +1 -1
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
- package/lib/typescript/src/features/browser.d.ts +10 -2
- package/lib/typescript/src/features/browser.d.ts.map +1 -1
- package/lib/typescript/src/features/carConnection.d.ts +1 -1
- package/lib/typescript/src/features/equalizer.d.ts +1 -1
- package/lib/typescript/src/features/errors.d.ts +3 -3
- package/lib/typescript/src/features/favorites.d.ts +14 -11
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/gate.d.ts +18 -2
- package/lib/typescript/src/features/gate.d.ts.map +1 -1
- package/lib/typescript/src/features/metadata.d.ts +20 -3
- package/lib/typescript/src/features/metadata.d.ts.map +1 -1
- package/lib/typescript/src/features/network.d.ts +1 -1
- package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
- package/lib/typescript/src/features/output.d.ts +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +4 -2
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
- package/lib/typescript/src/features/playback/playing.d.ts +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/rate.d.ts +8 -1
- package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +3 -4
- package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/volume.d.ts +1 -1
- package/lib/typescript/src/features/player/options.d.ts +108 -56
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts +14 -4
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +2 -0
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -0
- package/lib/typescript/src/features/queue/activeTrack.d.ts +9 -2
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +7 -4
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
- package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
- package/lib/typescript/src/features/remoteControls.d.ts +23 -22
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts +1 -1
- package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts +36 -0
- package/lib/typescript/src/test-utils/reactNativeStub.d.ts.map +1 -0
- package/lib/typescript/src/types/browser-native.d.ts +3 -2
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +380 -126
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +82 -57
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +19 -16
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
- package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +1 -2
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JAndroidOptions.hpp +10 -10
- package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
- package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
- package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__string__.hpp +102 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +16 -4
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +7 -10
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
- package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +45 -25
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +36 -11
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +15 -12
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +7 -10
- package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +10 -10
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +7 -10
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
- package/nitrogen/generated/android/c++/{JNotificationButton.hpp → JRemoteButton.hpp} +19 -19
- package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +86 -0
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +57 -49
- package/nitrogen/generated/android/c++/JSection.hpp +116 -0
- package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
- package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +25 -46
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +75 -0
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +8 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NotificationButton.kt → RemoteButton.kt} +3 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +15 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -12
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_NotificationButtonLayout.kt → Variant_NullType_RemoteButtonLayout.kt} +9 -9
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +9 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +244 -133
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +31 -16
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +33 -18
- package/nitrogen/generated/ios/swift/AndroidOptions.swift +12 -12
- package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
- package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
- package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
- package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +2 -2
- package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
- package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +52 -5
- package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +12 -12
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
- package/nitrogen/generated/ios/swift/{NotificationButton.swift → RemoteButton.swift} +6 -6
- package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +57 -0
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +62 -73
- package/nitrogen/generated/ios/swift/Section.swift +82 -0
- package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
- package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +28 -63
- package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
- package/nitrogen/generated/ios/swift/{Variant_NullType_NotificationButtonLayout.swift → Variant_NullType_RemoteButtonLayout.swift} +3 -3
- package/nitrogen/generated/shared/c++/AndroidOptions.hpp +8 -8
- package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
- package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
- package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
- package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +1 -1
- package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +1 -1
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +10 -6
- package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +8 -8
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
- package/nitrogen/generated/shared/c++/{NotificationButton.hpp → RemoteButton.hpp} +19 -19
- package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +94 -0
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +33 -37
- package/nitrogen/generated/shared/c++/Section.hpp +102 -0
- package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
- package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +18 -30
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
- package/package.json +24 -11
- package/src/AudioBrowser.ts +2 -1
- package/src/CLAUDE.md +52 -0
- package/src/features/battery.test.ts +79 -0
- package/src/features/browser-config.test.ts +21 -6
- package/src/features/browser-config.ts +12 -8
- package/src/features/browser.test.ts +1 -1
- package/src/features/browser.ts +11 -3
- package/src/features/carConnection.ts +1 -1
- package/src/features/equalizer.ts +1 -1
- package/src/features/errors.ts +3 -3
- package/src/features/favorites.ts +14 -11
- package/src/features/gate.ts +16 -1
- package/src/features/hook-wiring.test.ts +302 -0
- package/src/features/metadata.ts +20 -3
- package/src/features/network.ts +1 -1
- package/src/features/nowPlaying.ts +1 -1
- package/src/features/output.ts +1 -1
- package/src/features/playback/controls.ts +4 -2
- package/src/features/playback/playWhenReady.ts +1 -1
- package/src/features/playback/playing.ts +1 -1
- package/src/features/playback/progress.test.ts +169 -0
- package/src/features/playback/progress.ts +3 -2
- package/src/features/playback/rate.ts +8 -1
- package/src/features/playback/state.ts +3 -4
- package/src/features/playback/trackPlaybackTime.test.ts +16 -6
- package/src/features/playback/trackPlaybackTime.ts +1 -6
- package/src/features/playback/volume.ts +1 -1
- package/src/features/player/options.ts +126 -76
- package/src/features/player/setup.test.ts +24 -7
- package/src/features/player/setup.ts +22 -10
- package/src/features/player/validateOptions.ts +21 -0
- package/src/features/queue/activeTrack.ts +15 -4
- package/src/features/queue/queue.ts +13 -6
- package/src/features/queue/repeatMode.ts +1 -1
- package/src/features/queue/shuffle.ts +1 -1
- package/src/features/remoteControls.ts +13 -92
- package/src/features/sleepTimer.test.ts +172 -0
- package/src/features/sleepTimer.ts +9 -1
- package/src/nitro-stub-drift.test.ts +277 -0
- package/src/public-surface.test.ts +147 -0
- package/src/specs/audio-browser.nitro.ts +5 -4
- package/src/style-resolution.test.ts +210 -0
- package/src/test-utils/reactNativeStub.ts +60 -0
- package/src/types/browser-native.ts +7 -3
- package/src/types/browser-nodes.ts +396 -134
- package/src/types/browser.ts +85 -59
- package/src/utils/LazyNativeEmitter.ts +28 -15
- package/src/utils/NativeUpdatedValue.ts +29 -13
- package/src/utils/getTrackIdentity.ts +20 -0
- package/src/utils/useDebug.test.ts +182 -0
- package/src/utils/useNativeUpdatedValue.test.ts +103 -0
- package/src/utils/useNativeValueRefreshedBy.ts +38 -0
- package/src/web/NativeAudioBrowser.playWhenReady.test.ts +232 -2
- package/src/web/NativeAudioBrowser.ts +239 -82
- package/src/web/TrackPlayer/LoadAttempt.ts +29 -0
- package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +60 -13
- package/src/web/TrackPlayer/PlaybackStateMachine.ts +22 -6
- package/src/web/TrackPlayer/Player.dispatch.test.ts +5 -3
- package/src/web/TrackPlayer/Player.load.test.ts +137 -1
- package/src/web/TrackPlayer/Player.ts +284 -37
- package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
- package/src/web/TrackPlayer/QueueManager.ts +14 -0
- package/src/web/TrackPlayer/QueuePlayer.ts +33 -14
- package/src/web/__tests__/lifecycle.test.ts +345 -0
- package/src/web/__tests__/lifecycleHarness.ts +351 -0
- package/src/web/__tests__/loadEdgeCases.test.ts +246 -0
- package/src/web/__tests__/loadSupersession.test.ts +175 -0
- package/src/web/__tests__/retry.test.ts +169 -0
- package/src/web/browser/BrowserManager.ts +209 -111
- package/src/web/browser/FavoriteManager.test.ts +84 -0
- package/src/web/browser/FavoriteManager.ts +28 -23
- package/src/web/browser/SearchManager.test.ts +75 -0
- package/src/web/browser/SearchManager.ts +25 -28
- package/src/web/browser/__tests__/artwork-resolution.test.ts +117 -0
- package/src/web/browser/__tests__/layer-resolver.test.ts +35 -0
- package/src/web/browser/__tests__/search-artwork.test.ts +46 -0
- package/src/web/browser/__tests__/search-navigation.test.ts +76 -0
- package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
- package/src/web/browser/assertBrowsePageShape.ts +5 -3
- package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
- package/src/web/browser/classifyTrackNavigation.ts +9 -9
- package/src/web/browser/parseSearchResponse.test.ts +8 -2
- package/src/web/browser/sections.test.ts +91 -0
- package/src/web/browser/sections.ts +72 -0
- package/src/web/http/RequestConfigBuilder.test.ts +241 -1
- package/src/web/http/RequestConfigBuilder.ts +140 -33
- package/src/web/http/followMediaRedirect.test.ts +289 -0
- package/src/web/http/followMediaRedirect.ts +253 -0
- package/src/web/player/NowPlayingManager.ts +4 -3
- package/src/web/util/BrowserPathHelper.test.ts +52 -0
- package/src/web/util/BrowserPathHelper.ts +61 -13
- package/website/guide/android-auto.md +114 -0
- package/website/guide/android-certificates.md +409 -0
- package/website/guide/artwork.md +255 -0
- package/website/guide/audio-output.md +135 -0
- package/website/guide/automotive.md +86 -0
- package/website/guide/basic-usage.md +151 -0
- package/website/guide/battery.md +115 -0
- package/website/guide/browser.md +581 -0
- package/website/guide/carplay.md +620 -0
- package/website/guide/configuration.md +173 -0
- package/website/guide/equalizer.md +231 -0
- package/website/guide/errors.md +310 -0
- package/website/guide/favorites.md +154 -0
- package/website/guide/gate.md +143 -0
- package/website/guide/getting-started.md +46 -0
- package/website/guide/hooks.md +238 -0
- package/website/guide/live-streams.md +125 -0
- package/website/guide/metadata.md +239 -0
- package/website/guide/migrating-from-track-player.md +244 -0
- package/website/guide/native-callback-fetch.md +127 -0
- package/website/guide/network.md +86 -0
- package/website/guide/now-playing.md +175 -0
- package/website/guide/playback.md +328 -0
- package/website/guide/queue.md +300 -0
- package/website/guide/remote-controls.md +256 -0
- package/website/guide/search.md +255 -0
- package/website/guide/sleep-timer.md +268 -0
- package/website/guide/track.md +247 -0
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/ios/Model/MediaURL.swift +0 -67
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
- package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +0 -94
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
- package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +0 -75
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +0 -50
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
- package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +0 -92
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
- package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
- package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +0 -102
|
@@ -3,23 +3,24 @@ import Foundation
|
|
|
3
3
|
/// Utility for handling browser paths and contextual URLs in the media browser system.
|
|
4
4
|
///
|
|
5
5
|
/// Handles two types of special paths:
|
|
6
|
-
/// 1. System paths (prefixed with `/__`):
|
|
6
|
+
/// 1. System paths (prefixed with `/__`): root, search, and offline paths
|
|
7
7
|
/// 2. Contextual URLs: Embed parent context in track identifiers for playback integration
|
|
8
8
|
///
|
|
9
|
-
/// Contextual URL format: `{parentPath}?__trackId={
|
|
10
|
-
/// Example: "/library/radio?__trackId=song.mp3"
|
|
9
|
+
/// Contextual URL format: `{parentPath}?__trackId={trackIdentity}&__index={childIndex}`
|
|
10
|
+
/// Example: "/library/radio?__trackId=song.mp3&__index=2"
|
|
11
|
+
///
|
|
12
|
+
/// `__trackId` is the identity check; `__index` (the child's position on the
|
|
13
|
+
/// page at stamp time) is only a tie-breaker between surfaces that carry the
|
|
14
|
+
/// same identity — a stale index never selects a different track.
|
|
11
15
|
///
|
|
12
16
|
/// This allows:
|
|
13
|
-
/// - Playable-only tracks (tracks with `src` but no `
|
|
17
|
+
/// - Playable-only tracks (tracks with `src` but no `path`) to be referenced
|
|
14
18
|
/// - Cache lookup to work consistently
|
|
15
19
|
/// - Parent context to be preserved for queue restoration
|
|
16
20
|
enum BrowserPathHelper {
|
|
17
21
|
/// Root path for media browsing
|
|
18
22
|
static let rootPath = "/__root"
|
|
19
23
|
|
|
20
|
-
/// Recent media path for playback resumption
|
|
21
|
-
static let recentPath = "/__recent"
|
|
22
|
-
|
|
23
24
|
/// Search path prefix (full path is /__search?q=query)
|
|
24
25
|
static let searchPathPrefix = "/__search"
|
|
25
26
|
|
|
@@ -38,10 +39,12 @@ enum BrowserPathHelper {
|
|
|
38
39
|
/// Query parameter name for contextual track identifiers
|
|
39
40
|
private static let contextualTrackParam = "__trackId"
|
|
40
41
|
|
|
42
|
+
/// Query parameter name for the tapped child's page position (tie-breaker)
|
|
43
|
+
private static let contextualIndexParam = "__index"
|
|
44
|
+
|
|
41
45
|
/// Check if a path is a special system path (not a regular navigation path)
|
|
42
46
|
static func isSpecialPath(_ path: String) -> Bool {
|
|
43
47
|
path == rootPath ||
|
|
44
|
-
path == recentPath ||
|
|
45
48
|
path.hasPrefix("\(searchPathPrefix)?")
|
|
46
49
|
}
|
|
47
50
|
|
|
@@ -61,13 +64,13 @@ enum BrowserPathHelper {
|
|
|
61
64
|
|| path.contains("&\(contextualTrackParam)=")
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
/// Strips the __trackId
|
|
65
|
-
/// If the URL is not contextual, returns it unchanged.
|
|
67
|
+
/// Strips the contextual parameters (__trackId and __index) from a contextual
|
|
68
|
+
/// URL to get the parent path. If the URL is not contextual, returns it unchanged.
|
|
66
69
|
///
|
|
67
70
|
/// - Parameter url: The URL to process
|
|
68
|
-
/// - Returns: The URL without the
|
|
71
|
+
/// - Returns: The URL without the contextual parameters
|
|
69
72
|
///
|
|
70
|
-
/// Example: "/library/radio?__trackId=song.mp3" → "/library/radio"
|
|
73
|
+
/// Example: "/library/radio?__trackId=song.mp3&__index=2" → "/library/radio"
|
|
71
74
|
/// Example: "/search?q=jazz&__trackId=song.mp3" → "/search?q=jazz"
|
|
72
75
|
static func stripTrackId(_ url: String) -> String {
|
|
73
76
|
guard isContextual(url) else {
|
|
@@ -78,8 +81,10 @@ enum BrowserPathHelper {
|
|
|
78
81
|
return url
|
|
79
82
|
}
|
|
80
83
|
|
|
81
|
-
// Filter out
|
|
82
|
-
components.queryItems = components.queryItems?.filter {
|
|
84
|
+
// Filter out the contextual parameters
|
|
85
|
+
components.queryItems = components.queryItems?.filter {
|
|
86
|
+
$0.name != contextualTrackParam && $0.name != contextualIndexParam
|
|
87
|
+
}
|
|
83
88
|
|
|
84
89
|
// If no query items left, clear the query string entirely
|
|
85
90
|
if components.queryItems?.isEmpty == true {
|
|
@@ -89,21 +94,24 @@ enum BrowserPathHelper {
|
|
|
89
94
|
return components.string ?? url
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
/// Builds a contextual URL by appending a track identifier
|
|
97
|
+
/// Builds a contextual URL by appending a track identifier — and optionally
|
|
98
|
+
/// the tapped child's page position — to a parent path.
|
|
93
99
|
/// Handles existing query parameters correctly.
|
|
94
100
|
///
|
|
95
101
|
/// - Parameters:
|
|
96
102
|
/// - parentPath: The parent container path
|
|
97
|
-
/// - trackId: The track
|
|
98
|
-
///
|
|
103
|
+
/// - trackId: The track identity (`id` when non-blank, else `src`)
|
|
104
|
+
/// - index: The child's position on the page at stamp time (tie-breaker)
|
|
105
|
+
/// - Returns: A contextual URL combining parent path, track ID, and index
|
|
99
106
|
///
|
|
100
|
-
/// Example: build("/library", "song.mp3") → "/library?__trackId=song.mp3"
|
|
107
|
+
/// Example: build("/library", "song.mp3", index: 2) → "/library?__trackId=song.mp3&__index=2"
|
|
101
108
|
/// Example: build("/search?q=jazz", "song.mp3") → "/search?q=jazz&__trackId=song.mp3"
|
|
102
|
-
static func build(parentPath: String, trackId: String) -> String {
|
|
109
|
+
static func build(parentPath: String, trackId: String, index: Int? = nil) -> String {
|
|
103
110
|
let encodedTrackId =
|
|
104
|
-
trackId.addingPercentEncoding(withAllowedCharacters:
|
|
111
|
+
trackId.addingPercentEncoding(withAllowedCharacters: queryComponentAllowed) ?? trackId
|
|
105
112
|
let separator = parentPath.contains("?") ? "&" : "?"
|
|
106
|
-
|
|
113
|
+
let indexParam = index.map { "&\(contextualIndexParam)=\($0)" } ?? ""
|
|
114
|
+
return "\(parentPath)\(separator)\(contextualTrackParam)=\(encodedTrackId)\(indexParam)"
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
/// Extracts the track ID from a contextual URL.
|
|
@@ -125,6 +133,21 @@ enum BrowserPathHelper {
|
|
|
125
133
|
return components.queryItems?.first { $0.name == contextualTrackParam }?.value
|
|
126
134
|
}
|
|
127
135
|
|
|
136
|
+
/// Extracts the stamped page index from a contextual URL, or nil when the
|
|
137
|
+
/// URL is not contextual or carries no (valid, non-negative) index.
|
|
138
|
+
///
|
|
139
|
+
/// Example: "/library/radio?__trackId=song.mp3&__index=2" → 2
|
|
140
|
+
static func extractIndex(_ path: String) -> Int? {
|
|
141
|
+
guard isContextual(path),
|
|
142
|
+
let components = URLComponents(string: path),
|
|
143
|
+
let raw = components.queryItems?.first(where: { $0.name == contextualIndexParam })?.value,
|
|
144
|
+
let index = Int(raw), index >= 0
|
|
145
|
+
else {
|
|
146
|
+
return nil
|
|
147
|
+
}
|
|
148
|
+
return index
|
|
149
|
+
}
|
|
150
|
+
|
|
128
151
|
/// Appends query parameters to a URL, handling `?` vs `&` separator and percent-encoding.
|
|
129
152
|
///
|
|
130
153
|
/// - Parameters:
|
|
@@ -193,27 +216,4 @@ enum BrowserPathHelper {
|
|
|
193
216
|
|
|
194
217
|
return "\(normalizedBase)/\(normalizedPath)"
|
|
195
218
|
}
|
|
196
|
-
|
|
197
|
-
/// True if `segment` appears in `path` as a complete path segment — bounded on
|
|
198
|
-
/// the left by the string start or `/`, and on the right by the string end or
|
|
199
|
-
/// one of `/`, `?`, `#`.
|
|
200
|
-
static func containsSegment(_ path: String, _ segment: String) -> Bool {
|
|
201
|
-
guard !segment.isEmpty else { return false }
|
|
202
|
-
var searchStart = path.startIndex
|
|
203
|
-
while let range = path.range(of: segment, range: searchStart ..< path.endIndex) {
|
|
204
|
-
let beforeOk =
|
|
205
|
-
range.lowerBound == path.startIndex
|
|
206
|
-
|| path[path.index(before: range.lowerBound)] == "/"
|
|
207
|
-
let afterOk: Bool
|
|
208
|
-
if range.upperBound == path.endIndex {
|
|
209
|
-
afterOk = true
|
|
210
|
-
} else {
|
|
211
|
-
let next = path[range.upperBound]
|
|
212
|
-
afterOk = next == "/" || next == "?" || next == "#"
|
|
213
|
-
}
|
|
214
|
-
if beforeOk, afterOk { return true }
|
|
215
|
-
searchStart = range.upperBound
|
|
216
|
-
}
|
|
217
|
-
return false
|
|
218
|
-
}
|
|
219
219
|
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import os.log
|
|
2
|
+
|
|
3
|
+
#if canImport(NitroModules)
|
|
4
|
+
import NitroModules
|
|
5
|
+
#endif
|
|
6
|
+
|
|
7
|
+
/// Dev diagnostic (ADR 0011): the declarations on a resolved page that can
|
|
8
|
+
/// never render.
|
|
9
|
+
///
|
|
10
|
+
/// The block model deliberately gave up compile-time invalid-combination
|
|
11
|
+
/// errors — combinations are not invalid, they are *inert*, the way
|
|
12
|
+
/// `grid-template` on a non-grid element is inert in CSS. This is the
|
|
13
|
+
/// recovery: at page resolution every declaration its own block makes
|
|
14
|
+
/// unreadable is reported once, with the effective context and the fix.
|
|
15
|
+
///
|
|
16
|
+
/// The line it draws, and never crosses: only **structural** inertness — a
|
|
17
|
+
/// declaration the resolved block itself renders unreadable, on every surface
|
|
18
|
+
/// (`gridWrap` outside a grid, `cardTint` with no card treatment in scope).
|
|
19
|
+
/// *Surface* inertness is never reported, because declarations are
|
|
20
|
+
/// aspirational: `imageShape` on Android Auto, or a card treatment on a
|
|
21
|
+
/// pre-26 CarPlay, is intended usage — the renderer drops what it can't draw.
|
|
22
|
+
///
|
|
23
|
+
/// Runs where every page passes regardless of its source (static config,
|
|
24
|
+
/// route callback, or fetched browse JSON), and only on a cache miss, so a
|
|
25
|
+
/// mistake is reported once per resolution rather than once per serve.
|
|
26
|
+
///
|
|
27
|
+
/// Debug builds only: `warn` is the whole seam, so a release build never even
|
|
28
|
+
/// walks the page (the trade is that a page served only to a Release build —
|
|
29
|
+
/// TestFlight, production browse JSON — reports nothing; reproduce it against
|
|
30
|
+
/// a debug build).
|
|
31
|
+
enum InertStyleDiagnostic {
|
|
32
|
+
/// Reports every inert declaration on a resolved page. The one gate — the
|
|
33
|
+
/// `#if` is here rather than at the call sites so callers read
|
|
34
|
+
/// unconditionally and the rule set stays free of build-configuration
|
|
35
|
+
/// knowledge.
|
|
36
|
+
static func warn(path: String, pageStyle: SectionStyle?, sections: [Section]) {
|
|
37
|
+
#if DEBUG
|
|
38
|
+
let logger = Logger(subsystem: "com.audiobrowser", category: "InertStyleDiagnostic")
|
|
39
|
+
for finding in findings(path: path, pageStyle: pageStyle, sections: sections) {
|
|
40
|
+
logger.warning("\(finding, privacy: .public)")
|
|
41
|
+
}
|
|
42
|
+
#endif
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// The declarations that carry a structural inertness condition.
|
|
46
|
+
///
|
|
47
|
+
/// Two keys of the block are deliberately absent — `display` and
|
|
48
|
+
/// `artworkRendering`, dispositioned below instead.
|
|
49
|
+
private enum Property: String, CaseIterable {
|
|
50
|
+
case gridWrap
|
|
51
|
+
case gridTile
|
|
52
|
+
case imageShape
|
|
53
|
+
case accessorySymbol
|
|
54
|
+
case cardTint
|
|
55
|
+
case cardImage
|
|
56
|
+
|
|
57
|
+
/// Item properties inherit (`track ?? section ?? page`) and so may also
|
|
58
|
+
/// be declared per track; container properties resolve by scope override
|
|
59
|
+
/// (`section ?? page`) and never reach an item.
|
|
60
|
+
var isItemProperty: Bool {
|
|
61
|
+
switch self {
|
|
62
|
+
case .gridWrap, .gridTile: false
|
|
63
|
+
case .imageShape, .accessorySymbol, .cardTint, .cardImage: true
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Whether a value of this property, resolved into `context`, has any
|
|
68
|
+
/// rendering to affect. Each case is the spec's own "inert unless" clause.
|
|
69
|
+
func isInert(in context: Context) -> Bool {
|
|
70
|
+
switch self {
|
|
71
|
+
case .gridWrap, .gridTile: !context.isGrid
|
|
72
|
+
case .imageShape: !context.isGrid || context.isCard
|
|
73
|
+
case .accessorySymbol: context.isCard
|
|
74
|
+
case .cardTint, .cardImage: !context.isCard
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// The rule the declaration falls foul of, stated positively.
|
|
79
|
+
var rule: String {
|
|
80
|
+
switch self {
|
|
81
|
+
case .gridWrap, .gridTile: "`\(rawValue)` renders only in a grid"
|
|
82
|
+
case .imageShape: "only plain and condensed tiles have a shape"
|
|
83
|
+
case .accessorySymbol: "card tiles have no accessory slot"
|
|
84
|
+
case .cardTint, .cardImage: "the card properties render only under `gridTile: 'card'`"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
var hint: String {
|
|
89
|
+
switch self {
|
|
90
|
+
case .gridWrap, .gridTile:
|
|
91
|
+
"Declare `display: 'grid'` on the section or the page, or drop `\(rawValue)`."
|
|
92
|
+
case .imageShape:
|
|
93
|
+
"Render the items as plain or condensed grid tiles, or drop `imageShape`."
|
|
94
|
+
case .accessorySymbol:
|
|
95
|
+
"Use `gridTile: 'plain'` or `'condensed'`, or drop `accessorySymbol`."
|
|
96
|
+
case .cardTint, .cardImage:
|
|
97
|
+
"Declare `display: 'grid'` with `gridTile: 'card'`, or drop `\(rawValue)`."
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// Iterated per section; `allCases` is computed, so read it once.
|
|
103
|
+
private static let allProperties = Property.allCases
|
|
104
|
+
|
|
105
|
+
/// The rest of the block's keys, dispositioned rather than ruled on:
|
|
106
|
+
/// `artworkRendering` has no structural inertness condition (artwork renders
|
|
107
|
+
/// in every layout), and `display` is positional — inert only in the one
|
|
108
|
+
/// position that opens no page (see `playableDisplayFinding`). Declared here
|
|
109
|
+
/// so the spec-level completeness test can hold this file to the whole
|
|
110
|
+
/// vocabulary: every key of the block is a rule, always renderable, or
|
|
111
|
+
/// positional, and a newly added property is a decision rather than an
|
|
112
|
+
/// omission.
|
|
113
|
+
static let alwaysRenderable = ["artworkRendering"]
|
|
114
|
+
static let positional = ["display"]
|
|
115
|
+
|
|
116
|
+
/// A section's effective container context: the two container values every
|
|
117
|
+
/// inertness condition is stated against.
|
|
118
|
+
private struct Context {
|
|
119
|
+
let isGrid: Bool
|
|
120
|
+
let tile: GridTile
|
|
121
|
+
|
|
122
|
+
/// Folded through `StyleResolver` rather than re-deriving the scope
|
|
123
|
+
/// override: the diagnostic predicts what a renderer will do with these
|
|
124
|
+
/// declarations, so it has to resolve them the way the renderers do.
|
|
125
|
+
init(section: SectionStyle?, page: SectionStyle?) {
|
|
126
|
+
let folded = StyleResolver.sectionStyle(section: section, page: page)
|
|
127
|
+
isGrid = folded.display == .grid
|
|
128
|
+
tile = folded.gridTile ?? .plain
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// Whether the card treatment is what this container renders — the
|
|
132
|
+
/// condition the `card*` family is named after.
|
|
133
|
+
var isCard: Bool { isGrid && tile == .card }
|
|
134
|
+
|
|
135
|
+
/// The effective style, for the message — in the wire spelling the author
|
|
136
|
+
/// types (`stringValue` is the generated enum's own mapping). `gridTile`
|
|
137
|
+
/// is quoted only where a grid gives it meaning: naming it on a list would
|
|
138
|
+
/// be advice to fix the wrong property.
|
|
139
|
+
var described: String {
|
|
140
|
+
isGrid ? "display 'grid', gridTile '\(tile.stringValue)'" : "display 'list'"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// Every inert declaration on a resolved page, as ready-to-log messages —
|
|
145
|
+
/// pure, so the whole rule set is testable off-device; the caller logs.
|
|
146
|
+
///
|
|
147
|
+
/// - Parameters:
|
|
148
|
+
/// - path: the resolved path, named in every message.
|
|
149
|
+
/// - pageStyle: the page's own block (`ResolvedTrack.style`).
|
|
150
|
+
/// - sections: the page's normalized sections, blocks *unfolded* — the
|
|
151
|
+
/// diagnostic attributes each finding to the level that declared it, so
|
|
152
|
+
/// it must see the levels before `StyleResolver` merges them.
|
|
153
|
+
static func findings(path: String, pageStyle: SectionStyle?, sections: [Section]) -> [String] {
|
|
154
|
+
var findings: [String] = []
|
|
155
|
+
// A page-level declaration is judged across the whole page: it is live if
|
|
156
|
+
// it renders in any section it reaches, however many others ignore it.
|
|
157
|
+
var pageReached: Set<Property> = []
|
|
158
|
+
var pageRendered: Set<Property> = []
|
|
159
|
+
|
|
160
|
+
for (index, section) in sections.enumerated() {
|
|
161
|
+
let context = Context(section: section.style, page: pageStyle)
|
|
162
|
+
let sectionLabel = label(section, index: index)
|
|
163
|
+
|
|
164
|
+
for property in allProperties {
|
|
165
|
+
let inert = property.isInert(in: context)
|
|
166
|
+
|
|
167
|
+
if declared(property, in: section.style) {
|
|
168
|
+
if inert {
|
|
169
|
+
findings.append(
|
|
170
|
+
message(
|
|
171
|
+
property, path: path, owner: sectionLabel,
|
|
172
|
+
scope: "its effective style is \(context.described)",
|
|
173
|
+
))
|
|
174
|
+
}
|
|
175
|
+
} else if declared(property, in: pageStyle) {
|
|
176
|
+
// The section takes the page's value (nothing closer overrode it).
|
|
177
|
+
pageReached.insert(property)
|
|
178
|
+
if !inert { pageRendered.insert(property) }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
guard property.isItemProperty, inert else { continue }
|
|
182
|
+
let tracks = section.children.count(where: { declared(property, in: $0.style) })
|
|
183
|
+
if tracks > 0 {
|
|
184
|
+
findings.append(
|
|
185
|
+
message(
|
|
186
|
+
property, path: path, owner: "\(trackCount(tracks)) in \(sectionLabel)",
|
|
187
|
+
scope: "the section's effective style is \(context.described)",
|
|
188
|
+
))
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if let finding = playableDisplayFinding(section, path: path, label: sectionLabel) {
|
|
193
|
+
findings.append(finding)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for property in allProperties
|
|
198
|
+
where pageReached.contains(property) && !pageRendered.contains(property)
|
|
199
|
+
{
|
|
200
|
+
findings.append(
|
|
201
|
+
message(
|
|
202
|
+
property, path: path, owner: "the page block",
|
|
203
|
+
scope: "no section that inherits it renders one",
|
|
204
|
+
))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return findings
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/// `display` is positional, so it is never inert *in* a container — but on
|
|
211
|
+
/// a track that renders playable (`src` wins the rendering) it promises the
|
|
212
|
+
/// layout of a page that never opens.
|
|
213
|
+
private static func playableDisplayFinding(
|
|
214
|
+
_ section: Section, path: String, label: String,
|
|
215
|
+
) -> String? {
|
|
216
|
+
let tracks = section.children.count(where: { $0.src != nil && $0.style?.display != nil })
|
|
217
|
+
guard tracks > 0 else { return nil }
|
|
218
|
+
return message(
|
|
219
|
+
path: path, owner: "\(trackCount(tracks)) in \(label)", key: "display",
|
|
220
|
+
reason: "`display` is the layout promise for the page a track opens, and a track "
|
|
221
|
+
+ "rendered playable opens none",
|
|
222
|
+
hint: "Drop `display`, or give the track a `path` if it should be browsable.",
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private static func message(
|
|
227
|
+
_ property: Property, path: String, owner: String, scope: String,
|
|
228
|
+
) -> String {
|
|
229
|
+
message(
|
|
230
|
+
path: path, owner: owner, key: property.rawValue,
|
|
231
|
+
reason: "\(property.rule), but \(scope)", hint: property.hint,
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/// The one message skeleton — every finding names the page, the declaration,
|
|
236
|
+
/// who declared it, why it can't render, and what to do about it.
|
|
237
|
+
private static func message(
|
|
238
|
+
path: String, owner: String, key: String, reason: String, hint: String,
|
|
239
|
+
) -> String {
|
|
240
|
+
"Inert style declaration on page '\(path)': `style.\(key)` on \(owner) "
|
|
241
|
+
+ "never renders — \(reason). \(hint)"
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private static func label(_ section: Section, index: Int) -> String {
|
|
245
|
+
if let title = section.title, !title.isEmpty { return "section '\(title)'" }
|
|
246
|
+
return "section #\(index + 1)"
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private static func trackCount(_ count: Int) -> String {
|
|
250
|
+
"\(count) track\(count == 1 ? "" : "s")"
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Nitro flattens the spec's `extends`, so the two blocks share no type —
|
|
254
|
+
// hence two lookups over the same key set rather than one protocol.
|
|
255
|
+
|
|
256
|
+
private static func declared(_ property: Property, in style: SectionStyle?) -> Bool {
|
|
257
|
+
guard let style else { return false }
|
|
258
|
+
switch property {
|
|
259
|
+
case .gridWrap: return style.gridWrap != nil
|
|
260
|
+
case .gridTile: return style.gridTile != nil
|
|
261
|
+
case .imageShape: return style.imageShape != nil
|
|
262
|
+
case .accessorySymbol: return style.accessorySymbol != nil
|
|
263
|
+
case .cardTint: return style.cardTint != nil
|
|
264
|
+
case .cardImage: return style.cardImage != nil
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private static func declared(_ property: Property, in style: TrackStyle?) -> Bool {
|
|
269
|
+
guard let style else { return false }
|
|
270
|
+
switch property {
|
|
271
|
+
// Container properties have no Track slot to be declared in.
|
|
272
|
+
case .gridWrap, .gridTile: return false
|
|
273
|
+
case .imageShape: return style.imageShape != nil
|
|
274
|
+
case .accessorySymbol: return style.accessorySymbol != nil
|
|
275
|
+
case .cardTint: return style.cardTint != nil
|
|
276
|
+
case .cardImage: return style.cardImage != nil
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|