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
|
@@ -10,7 +10,7 @@ struct SFSymbolTests {
|
|
|
10
10
|
let action = await CarPlayArtworkResolver.resolve(
|
|
11
11
|
artwork: "sf:heart.fill",
|
|
12
12
|
artworkSourceUri: nil,
|
|
13
|
-
|
|
13
|
+
stencil: false,
|
|
14
14
|
targetWidth: 40, targetHeight: 40,
|
|
15
15
|
displayScale: 2.0,
|
|
16
16
|
urlResolver: nil,
|
|
@@ -22,7 +22,7 @@ struct SFSymbolTests {
|
|
|
22
22
|
let action = await CarPlayArtworkResolver.resolve(
|
|
23
23
|
artwork: "sf:play.circle?bg=#000&fg=#fff",
|
|
24
24
|
artworkSourceUri: nil,
|
|
25
|
-
|
|
25
|
+
stencil: false,
|
|
26
26
|
targetWidth: 24, targetHeight: 24,
|
|
27
27
|
displayScale: 3.0,
|
|
28
28
|
urlResolver: nil,
|
|
@@ -35,7 +35,7 @@ struct SFSymbolTests {
|
|
|
35
35
|
let action = await CarPlayArtworkResolver.resolve(
|
|
36
36
|
artwork: "sf:music.note",
|
|
37
37
|
artworkSourceUri: nil,
|
|
38
|
-
|
|
38
|
+
stencil: false,
|
|
39
39
|
targetWidth: 40, targetHeight: 40,
|
|
40
40
|
displayScale: 2.0,
|
|
41
41
|
urlResolver: { _, _ in
|
|
@@ -51,7 +51,7 @@ struct SFSymbolTests {
|
|
|
51
51
|
let action = await CarPlayArtworkResolver.resolve(
|
|
52
52
|
artwork: nil,
|
|
53
53
|
artworkSourceUri: "sf:star.fill",
|
|
54
|
-
|
|
54
|
+
stencil: false,
|
|
55
55
|
targetWidth: 20, targetHeight: 20,
|
|
56
56
|
displayScale: 2.0,
|
|
57
57
|
urlResolver: nil,
|
|
@@ -68,7 +68,7 @@ struct NoArtworkTests {
|
|
|
68
68
|
let action = await CarPlayArtworkResolver.resolve(
|
|
69
69
|
artwork: nil,
|
|
70
70
|
artworkSourceUri: nil,
|
|
71
|
-
|
|
71
|
+
stencil: false,
|
|
72
72
|
targetWidth: 40, targetHeight: 40,
|
|
73
73
|
displayScale: 2.0,
|
|
74
74
|
urlResolver: nil,
|
|
@@ -80,7 +80,7 @@ struct NoArtworkTests {
|
|
|
80
80
|
let action = await CarPlayArtworkResolver.resolve(
|
|
81
81
|
artwork: nil,
|
|
82
82
|
artworkSourceUri: nil,
|
|
83
|
-
|
|
83
|
+
stencil: false,
|
|
84
84
|
targetWidth: 40, targetHeight: 40,
|
|
85
85
|
displayScale: 2.0,
|
|
86
86
|
urlResolver: { _, _ in nil },
|
|
@@ -97,7 +97,7 @@ struct URLResolutionTests {
|
|
|
97
97
|
let action = await CarPlayArtworkResolver.resolve(
|
|
98
98
|
artwork: "https://example.com/art.jpg",
|
|
99
99
|
artworkSourceUri: nil,
|
|
100
|
-
|
|
100
|
+
stencil: false,
|
|
101
101
|
targetWidth: 40, targetHeight: 40,
|
|
102
102
|
displayScale: 2.0,
|
|
103
103
|
urlResolver: { pixelWidth, pixelHeight in
|
|
@@ -121,7 +121,7 @@ struct URLResolutionTests {
|
|
|
121
121
|
let action = await CarPlayArtworkResolver.resolve(
|
|
122
122
|
artwork: "https://example.com/art.jpg",
|
|
123
123
|
artworkSourceUri: nil,
|
|
124
|
-
|
|
124
|
+
stencil: false,
|
|
125
125
|
targetWidth: 24, targetHeight: 24,
|
|
126
126
|
displayScale: 3.0,
|
|
127
127
|
urlResolver: { _, _ in
|
|
@@ -140,7 +140,7 @@ struct URLResolutionTests {
|
|
|
140
140
|
let action = await CarPlayArtworkResolver.resolve(
|
|
141
141
|
artwork: "https://example.com/art.jpg",
|
|
142
142
|
artworkSourceUri: nil,
|
|
143
|
-
|
|
143
|
+
stencil: false,
|
|
144
144
|
targetWidth: 40, targetHeight: 40,
|
|
145
145
|
displayScale: 2.0,
|
|
146
146
|
urlResolver: { _, _ in nil },
|
|
@@ -157,7 +157,7 @@ struct URLResolutionTests {
|
|
|
157
157
|
let action = await CarPlayArtworkResolver.resolve(
|
|
158
158
|
artwork: "https://example.com/fallback.jpg",
|
|
159
159
|
artworkSourceUri: nil,
|
|
160
|
-
|
|
160
|
+
stencil: false,
|
|
161
161
|
targetWidth: 40, targetHeight: 40,
|
|
162
162
|
displayScale: 2.0,
|
|
163
163
|
urlResolver: { _, _ in
|
|
@@ -180,7 +180,7 @@ struct URLResolutionTests {
|
|
|
180
180
|
_ = await CarPlayArtworkResolver.resolve(
|
|
181
181
|
artwork: "https://example.com/art.jpg",
|
|
182
182
|
artworkSourceUri: nil,
|
|
183
|
-
|
|
183
|
+
stencil: false,
|
|
184
184
|
targetWidth: 50, targetHeight: 30,
|
|
185
185
|
displayScale: 2.5,
|
|
186
186
|
urlResolver: { w, h in
|
|
@@ -202,7 +202,7 @@ struct DirectFallbackTests {
|
|
|
202
202
|
let action = await CarPlayArtworkResolver.resolve(
|
|
203
203
|
artwork: "https://example.com/artwork.png",
|
|
204
204
|
artworkSourceUri: nil,
|
|
205
|
-
|
|
205
|
+
stencil: false,
|
|
206
206
|
targetWidth: 40, targetHeight: 40,
|
|
207
207
|
displayScale: 2.0,
|
|
208
208
|
urlResolver: nil,
|
|
@@ -219,7 +219,7 @@ struct DirectFallbackTests {
|
|
|
219
219
|
let action = await CarPlayArtworkResolver.resolve(
|
|
220
220
|
artwork: nil,
|
|
221
221
|
artworkSourceUri: "https://example.com/source.jpg",
|
|
222
|
-
|
|
222
|
+
stencil: false,
|
|
223
223
|
targetWidth: 40, targetHeight: 40,
|
|
224
224
|
displayScale: 2.0,
|
|
225
225
|
urlResolver: nil,
|
|
@@ -236,7 +236,7 @@ struct DirectFallbackTests {
|
|
|
236
236
|
let action = await CarPlayArtworkResolver.resolve(
|
|
237
237
|
artwork: "https://example.com/artwork.jpg",
|
|
238
238
|
artworkSourceUri: "https://example.com/source.jpg",
|
|
239
|
-
|
|
239
|
+
stencil: false,
|
|
240
240
|
targetWidth: 40, targetHeight: 40,
|
|
241
241
|
displayScale: 2.0,
|
|
242
242
|
urlResolver: nil,
|
|
@@ -253,7 +253,7 @@ struct DirectFallbackTests {
|
|
|
253
253
|
let action = await CarPlayArtworkResolver.resolve(
|
|
254
254
|
artwork: "",
|
|
255
255
|
artworkSourceUri: nil,
|
|
256
|
-
|
|
256
|
+
stencil: false,
|
|
257
257
|
targetWidth: 40, targetHeight: 40,
|
|
258
258
|
displayScale: 2.0,
|
|
259
259
|
urlResolver: nil,
|
|
@@ -270,7 +270,7 @@ struct SVGDetectionTests {
|
|
|
270
270
|
let action = await CarPlayArtworkResolver.resolve(
|
|
271
271
|
artwork: "https://example.com/icon.svg",
|
|
272
272
|
artworkSourceUri: nil,
|
|
273
|
-
|
|
273
|
+
stencil: false,
|
|
274
274
|
targetWidth: 40, targetHeight: 40,
|
|
275
275
|
displayScale: 2.0,
|
|
276
276
|
urlResolver: nil,
|
|
@@ -287,7 +287,7 @@ struct SVGDetectionTests {
|
|
|
287
287
|
let action = await CarPlayArtworkResolver.resolve(
|
|
288
288
|
artwork: "https://example.com/icon.SVG",
|
|
289
289
|
artworkSourceUri: nil,
|
|
290
|
-
|
|
290
|
+
stencil: false,
|
|
291
291
|
targetWidth: 40, targetHeight: 40,
|
|
292
292
|
displayScale: 2.0,
|
|
293
293
|
urlResolver: nil,
|
|
@@ -304,7 +304,7 @@ struct SVGDetectionTests {
|
|
|
304
304
|
let action = await CarPlayArtworkResolver.resolve(
|
|
305
305
|
artwork: "https://example.com/art.jpg",
|
|
306
306
|
artworkSourceUri: nil,
|
|
307
|
-
|
|
307
|
+
stencil: false,
|
|
308
308
|
targetWidth: 40, targetHeight: 40,
|
|
309
309
|
displayScale: 2.0,
|
|
310
310
|
urlResolver: { _, _ in
|
|
@@ -323,7 +323,7 @@ struct SVGDetectionTests {
|
|
|
323
323
|
let action = await CarPlayArtworkResolver.resolve(
|
|
324
324
|
artwork: "https://example.com/photo.jpg",
|
|
325
325
|
artworkSourceUri: nil,
|
|
326
|
-
|
|
326
|
+
stencil: false,
|
|
327
327
|
targetWidth: 40, targetHeight: 40,
|
|
328
328
|
displayScale: 2.0,
|
|
329
329
|
urlResolver: nil,
|
|
@@ -341,11 +341,11 @@ struct SVGDetectionTests {
|
|
|
341
341
|
|
|
342
342
|
@Suite("tinting")
|
|
343
343
|
struct TintingTests {
|
|
344
|
-
@Test func
|
|
344
|
+
@Test func stencilTrue_setsShouldTint() async {
|
|
345
345
|
let action = await CarPlayArtworkResolver.resolve(
|
|
346
346
|
artwork: "https://example.com/icon.png",
|
|
347
347
|
artworkSourceUri: nil,
|
|
348
|
-
|
|
348
|
+
stencil: true,
|
|
349
349
|
targetWidth: 40, targetHeight: 40,
|
|
350
350
|
displayScale: 2.0,
|
|
351
351
|
urlResolver: nil,
|
|
@@ -358,28 +358,11 @@ struct TintingTests {
|
|
|
358
358
|
))
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
@Test func
|
|
361
|
+
@Test func stencilFalse_doesNotTint() async {
|
|
362
362
|
let action = await CarPlayArtworkResolver.resolve(
|
|
363
363
|
artwork: "https://example.com/photo.jpg",
|
|
364
364
|
artworkSourceUri: nil,
|
|
365
|
-
|
|
366
|
-
targetWidth: 40, targetHeight: 40,
|
|
367
|
-
displayScale: 2.0,
|
|
368
|
-
urlResolver: nil,
|
|
369
|
-
)
|
|
370
|
-
#expect(action == .fetch(
|
|
371
|
-
uri: "https://example.com/photo.jpg",
|
|
372
|
-
headers: nil,
|
|
373
|
-
shouldTint: false,
|
|
374
|
-
isSvg: false,
|
|
375
|
-
))
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
@Test func artworkCarPlayTintedNil_defaultsToFalse() async {
|
|
379
|
-
let action = await CarPlayArtworkResolver.resolve(
|
|
380
|
-
artwork: "https://example.com/photo.jpg",
|
|
381
|
-
artworkSourceUri: nil,
|
|
382
|
-
artworkCarPlayTinted: nil,
|
|
365
|
+
stencil: false,
|
|
383
366
|
targetWidth: 40, targetHeight: 40,
|
|
384
367
|
displayScale: 2.0,
|
|
385
368
|
urlResolver: nil,
|
|
@@ -396,7 +379,7 @@ struct TintingTests {
|
|
|
396
379
|
let action = await CarPlayArtworkResolver.resolve(
|
|
397
380
|
artwork: "https://example.com/icon.png",
|
|
398
381
|
artworkSourceUri: nil,
|
|
399
|
-
|
|
382
|
+
stencil: true,
|
|
400
383
|
targetWidth: 40, targetHeight: 40,
|
|
401
384
|
displayScale: 2.0,
|
|
402
385
|
urlResolver: { _, _ in
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import Testing
|
|
2
|
+
|
|
3
|
+
@testable import AudioBrowserTestable
|
|
4
|
+
|
|
5
|
+
// The dev diagnostic for declarations that can never render (ADR 0011): the
|
|
6
|
+
// recovery for the compile-time invalid-combination errors the block model
|
|
7
|
+
// deliberately gave up. Structural inertness only — a surface that can't draw
|
|
8
|
+
// a property it understands is intended usage, never a finding.
|
|
9
|
+
|
|
10
|
+
@Suite("InertStyleDiagnostic")
|
|
11
|
+
struct InertStyleDiagnosticTests {
|
|
12
|
+
private func findings(
|
|
13
|
+
_ sections: [Section], page: SectionStyle? = nil, path: String = "/home",
|
|
14
|
+
) -> [String] {
|
|
15
|
+
InertStyleDiagnostic.findings(path: path, pageStyle: page, sections: sections)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private func playable(_ src: String = "https://s/a.mp3", style: TrackStyle? = nil) -> Track {
|
|
19
|
+
Track(src: src, title: "T", style: style)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private func browsable(_ path: String = "/albums/1", style: TrackStyle? = nil) -> Track {
|
|
23
|
+
Track(path: path, title: "T", style: style)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// MARK: - Container properties
|
|
27
|
+
|
|
28
|
+
@Test func gridWrapOnAListSectionIsInert() {
|
|
29
|
+
let found = findings([Section(title: "Recent", style: SectionStyle(gridWrap: false), children: [])])
|
|
30
|
+
#expect(found.count == 1)
|
|
31
|
+
#expect(found[0].contains("section 'Recent'"))
|
|
32
|
+
#expect(found[0].contains("style.gridWrap"))
|
|
33
|
+
#expect(found[0].contains("display 'list'"))
|
|
34
|
+
#expect(found[0].contains("/home"))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Test func gridWrapInAGridIsLive() {
|
|
38
|
+
let style = SectionStyle(gridWrap: false, display: .grid)
|
|
39
|
+
#expect(findings([Section(style: style, children: [])]).isEmpty)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Test func aSectionInheritsThePagesGridDisplay() {
|
|
43
|
+
// `display` resolves by scope override, so the page's grid makes the
|
|
44
|
+
// section's `gridWrap` live without the section restating it.
|
|
45
|
+
let found = findings(
|
|
46
|
+
[Section(style: SectionStyle(gridWrap: false), children: [])],
|
|
47
|
+
page: SectionStyle(display: .grid),
|
|
48
|
+
)
|
|
49
|
+
#expect(found.isEmpty)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Test func gridTileOnAListSectionIsInert() {
|
|
53
|
+
let found = findings([Section(style: SectionStyle(gridTile: .card), children: [])])
|
|
54
|
+
#expect(found.count == 1)
|
|
55
|
+
#expect(found[0].contains("style.gridTile"))
|
|
56
|
+
#expect(found[0].contains("section #1"))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// MARK: - Item properties
|
|
60
|
+
|
|
61
|
+
@Test func cardPropertiesNeedTheCardTreatment() {
|
|
62
|
+
let plainGrid = SectionStyle(display: .grid, cardTint: "#1e3a8a", cardImage: .background)
|
|
63
|
+
let found = findings([Section(title: "Featured", style: plainGrid, children: [])])
|
|
64
|
+
#expect(found.count == 2)
|
|
65
|
+
#expect(found.contains { $0.contains("style.cardTint") })
|
|
66
|
+
#expect(found.contains { $0.contains("style.cardImage") })
|
|
67
|
+
#expect(found.allSatisfy { $0.contains("display 'grid', gridTile 'plain'") })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@Test func cardPropertiesInACardGridAreLive() {
|
|
71
|
+
let cards = SectionStyle(gridTile: .card, display: .grid, cardTint: "#1e3a8a", cardImage: .background)
|
|
72
|
+
#expect(findings([Section(style: cards, children: [])]).isEmpty)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Test func imageShapeNeedsAShapedTile() {
|
|
76
|
+
let list = SectionStyle(imageShape: .circular)
|
|
77
|
+
let cards = SectionStyle(gridTile: .card, display: .grid, imageShape: .circular)
|
|
78
|
+
let condensed = SectionStyle(gridTile: .condensed, display: .grid, imageShape: .circular)
|
|
79
|
+
#expect(findings([Section(style: list, children: [])]).count == 1)
|
|
80
|
+
#expect(findings([Section(style: cards, children: [])]).count == 1)
|
|
81
|
+
// Plain and condensed tiles both take a shape.
|
|
82
|
+
#expect(findings([Section(style: condensed, children: [])]).isEmpty)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Test func accessorySymbolIsInertOnlyOnCards() {
|
|
86
|
+
let rows = SectionStyle(accessorySymbol: "lock.fill")
|
|
87
|
+
let cards = SectionStyle(gridTile: .card, display: .grid, accessorySymbol: "lock.fill")
|
|
88
|
+
// List rows draw accessories; card elements have no slot for one.
|
|
89
|
+
#expect(findings([Section(style: rows, children: [])]).isEmpty)
|
|
90
|
+
let found = findings([Section(style: cards, children: [])])
|
|
91
|
+
#expect(found.count == 1)
|
|
92
|
+
#expect(found[0].contains("no accessory slot"))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@Test func artworkRenderingIsNeverInert() {
|
|
96
|
+
let stencilled = SectionStyle(gridTile: .card, display: .grid, artworkRendering: .stencil)
|
|
97
|
+
#expect(findings([Section(style: stencilled, children: [])]).isEmpty)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// MARK: - Track-level declarations
|
|
101
|
+
|
|
102
|
+
@Test func trackDeclarationsAreReportedPerSection() {
|
|
103
|
+
let section = Section(
|
|
104
|
+
title: "Results", style: SectionStyle(display: .grid),
|
|
105
|
+
children: [
|
|
106
|
+
playable(style: TrackStyle(cardTint: "#111")),
|
|
107
|
+
playable("https://s/b.mp3", style: TrackStyle(cardTint: "#222")),
|
|
108
|
+
playable("https://s/c.mp3"),
|
|
109
|
+
],
|
|
110
|
+
)
|
|
111
|
+
let found = findings([section])
|
|
112
|
+
#expect(found.count == 1)
|
|
113
|
+
#expect(found[0].contains("2 tracks in section 'Results'"))
|
|
114
|
+
#expect(found[0].contains("style.cardTint"))
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@Test func aTracksOwnShapeIsLiveInAPlainGrid() {
|
|
118
|
+
let section = Section(
|
|
119
|
+
style: SectionStyle(display: .grid),
|
|
120
|
+
children: [playable(style: TrackStyle(imageShape: .circular))],
|
|
121
|
+
)
|
|
122
|
+
#expect(findings([section]).isEmpty)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// MARK: - The page block
|
|
126
|
+
|
|
127
|
+
@Test func aPageDeclarationLiveInOneSectionIsNotReported() {
|
|
128
|
+
// Card tint reaches both sections; one renders cards. Live is live.
|
|
129
|
+
let found = findings(
|
|
130
|
+
[
|
|
131
|
+
Section(style: SectionStyle(gridTile: .card, display: .grid), children: []),
|
|
132
|
+
Section(style: SectionStyle(display: .grid), children: []),
|
|
133
|
+
],
|
|
134
|
+
page: SectionStyle(cardTint: "#1e3a8a"),
|
|
135
|
+
)
|
|
136
|
+
#expect(found.isEmpty)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@Test func aPageDeclarationNoSectionRendersIsReportedOnce() {
|
|
140
|
+
let found = findings(
|
|
141
|
+
[
|
|
142
|
+
Section(title: "A", style: SectionStyle(display: .grid), children: []),
|
|
143
|
+
Section(title: "B", children: []),
|
|
144
|
+
],
|
|
145
|
+
page: SectionStyle(cardTint: "#1e3a8a"),
|
|
146
|
+
)
|
|
147
|
+
#expect(found.count == 1)
|
|
148
|
+
#expect(found[0].contains("the page block"))
|
|
149
|
+
#expect(found[0].contains("no section that inherits it"))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Test func aSectionOverrideShadowsThePageValue() {
|
|
153
|
+
// Every section declares its own tint, so the page's is never resolved —
|
|
154
|
+
// shadowed, not inert; the diagnostic reports what can't render, not what
|
|
155
|
+
// isn't reached.
|
|
156
|
+
let found = findings(
|
|
157
|
+
[Section(style: SectionStyle(gridTile: .card, display: .grid, cardTint: "#000"), children: [])],
|
|
158
|
+
page: SectionStyle(cardTint: "#1e3a8a"),
|
|
159
|
+
)
|
|
160
|
+
#expect(found.isEmpty)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@Test func aPageWideGridMakesSectionDeclarationsLive() {
|
|
164
|
+
let found = findings(
|
|
165
|
+
[Section(style: SectionStyle(cardTint: "#111"), children: [])],
|
|
166
|
+
page: SectionStyle(gridTile: .card, display: .grid),
|
|
167
|
+
)
|
|
168
|
+
#expect(found.isEmpty)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// MARK: - The positional `display`
|
|
172
|
+
|
|
173
|
+
@Test func displayOnAPlayableTrackIsInert() {
|
|
174
|
+
let section = Section(
|
|
175
|
+
title: "Tracks",
|
|
176
|
+
children: [
|
|
177
|
+
playable(style: TrackStyle(display: .grid)),
|
|
178
|
+
browsable(style: TrackStyle(display: .grid)),
|
|
179
|
+
],
|
|
180
|
+
)
|
|
181
|
+
let found = findings([section])
|
|
182
|
+
#expect(found.count == 1)
|
|
183
|
+
#expect(found[0].contains("1 track in section 'Tracks'"))
|
|
184
|
+
#expect(found[0].contains("style.display"))
|
|
185
|
+
#expect(found[0].contains("opens none"))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@Test func displayOnASectionOrPageIsNeverInert() {
|
|
189
|
+
let found = findings(
|
|
190
|
+
[Section(style: SectionStyle(display: .list), children: [browsable()])],
|
|
191
|
+
page: SectionStyle(display: .grid),
|
|
192
|
+
)
|
|
193
|
+
#expect(found.isEmpty)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@Test func anUnstyledPageIsSilent() {
|
|
197
|
+
#expect(findings([Section(children: [playable(), browsable()])]).isEmpty)
|
|
198
|
+
#expect(findings([]).isEmpty)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -40,7 +40,7 @@ struct JsonTrackArtworkTests {
|
|
|
40
40
|
// The shape that shipped broken: a container decodes its children eagerly,
|
|
41
41
|
// so one unparseable row rejected the whole page rather than losing an image.
|
|
42
42
|
let json = """
|
|
43
|
-
{ "
|
|
43
|
+
{ "path": "/favorites", "title": "Favorites", "children": [
|
|
44
44
|
{ "title": "Playlist", "artwork": { "light": "https://e.example/l.png", "dark": "https://e.example/d.png" } }
|
|
45
45
|
] }
|
|
46
46
|
""".data(using: .utf8)!
|
|
@@ -56,3 +56,152 @@ struct JsonTrackArtworkTests {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
// MARK: - Style blocks (ADR 0011)
|
|
61
|
+
|
|
62
|
+
@Suite("JsonTrack.style")
|
|
63
|
+
struct JsonStyleTests {
|
|
64
|
+
@Test func decodesABlockOntoTrackStyle() throws {
|
|
65
|
+
let json = Data(
|
|
66
|
+
"""
|
|
67
|
+
{"title":"X","style":{"display":"grid","artworkRendering":"stencil"}}
|
|
68
|
+
""".utf8)
|
|
69
|
+
let track = try JSONDecoder().decode(JsonTrack.self, from: json).toNitro()
|
|
70
|
+
#expect(track.style?.display == .grid)
|
|
71
|
+
#expect(track.style?.artworkRendering == .stencil)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@Test func decodesABlockOntoSectionStyle() throws {
|
|
75
|
+
let json = Data(
|
|
76
|
+
"""
|
|
77
|
+
{"path":"/home","title":"Home","sections":[
|
|
78
|
+
{"style":{"display":"grid","gridWrap":false},"children":[{"title":"C","src":"s"}]}
|
|
79
|
+
]}
|
|
80
|
+
""".utf8)
|
|
81
|
+
let resolved = try JSONDecoder().decode(JsonResolvedTrack.self, from: json).toNitro()
|
|
82
|
+
let style = resolved.sections?.first?.style
|
|
83
|
+
#expect(style?.display == .grid)
|
|
84
|
+
#expect(style?.gridWrap == false)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@Test func staleStringStyleDecodesAsNoDeclaration() throws {
|
|
88
|
+
// The retired string vocabulary must never kill the page (tolerant
|
|
89
|
+
// decoding, ADR 0011) — it decodes as an empty block.
|
|
90
|
+
let json = Data(
|
|
91
|
+
"""
|
|
92
|
+
{"path":"/home","title":"Home","style":"rail","children":[
|
|
93
|
+
{"title":"C","src":"s","style":"grid"}
|
|
94
|
+
]}
|
|
95
|
+
""".utf8)
|
|
96
|
+
let resolved = try JSONDecoder().decode(JsonResolvedTrack.self, from: json).toNitro()
|
|
97
|
+
#expect(resolved.style == nil)
|
|
98
|
+
#expect(resolved.children?.first?.style == nil)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@Test func unknownEnumValuesDecodeAsNoDeclaration() throws {
|
|
102
|
+
let json = Data(
|
|
103
|
+
"""
|
|
104
|
+
{"title":"X","style":{"display":"carousel","artworkRendering":"embossed","gridWrap":"yes"}}
|
|
105
|
+
""".utf8)
|
|
106
|
+
let track = try JSONDecoder().decode(JsonTrack.self, from: json).toNitro()
|
|
107
|
+
// All-unknown declarations collapse to "no block" — one shape for "no
|
|
108
|
+
// declaration" on every platform.
|
|
109
|
+
#expect(track.style == nil)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@Test func decodesDisabled() throws {
|
|
113
|
+
let json = Data("""
|
|
114
|
+
{"title":"X","src":"s","disabled":true}
|
|
115
|
+
""".utf8)
|
|
116
|
+
let track = try JSONDecoder().decode(JsonTrack.self, from: json).toNitro()
|
|
117
|
+
#expect(track.disabled == true)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@Test func decodesImageShapeAndAccessorySymbol() throws {
|
|
121
|
+
let json = Data(
|
|
122
|
+
"""
|
|
123
|
+
{"title":"X","style":{"imageShape":"circular","accessorySymbol":"lock.fill"}}
|
|
124
|
+
""".utf8)
|
|
125
|
+
let track = try JSONDecoder().decode(JsonTrack.self, from: json).toNitro()
|
|
126
|
+
#expect(track.style?.imageShape == .circular)
|
|
127
|
+
#expect(track.style?.accessorySymbol == "lock.fill")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@Test func accessorySymbolNoneIsAValueNotEmptiness() throws {
|
|
131
|
+
// The inheritance-escape sentinel must survive the wire — collapsing it
|
|
132
|
+
// to "no declaration" would let the section value flow back in.
|
|
133
|
+
let json = Data(
|
|
134
|
+
"""
|
|
135
|
+
{"title":"X","style":{"accessorySymbol":"none"}}
|
|
136
|
+
""".utf8)
|
|
137
|
+
let track = try JSONDecoder().decode(JsonTrack.self, from: json).toNitro()
|
|
138
|
+
#expect(track.style?.accessorySymbol == "none")
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@Test func unknownImageShapeDecodesAsNoDeclaration() throws {
|
|
142
|
+
let json = Data(
|
|
143
|
+
"""
|
|
144
|
+
{"title":"X","style":{"imageShape":"hexagonal"}}
|
|
145
|
+
""".utf8)
|
|
146
|
+
let track = try JSONDecoder().decode(JsonTrack.self, from: json).toNitro()
|
|
147
|
+
#expect(track.style == nil)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@Test func decodesTheCardFamily() throws {
|
|
151
|
+
let json = Data(
|
|
152
|
+
"""
|
|
153
|
+
{"path":"/home","title":"Home","sections":[
|
|
154
|
+
{"style":{"display":"grid","gridTile":"card","cardTint":"#1e3a8a","cardImage":"background"},
|
|
155
|
+
"children":[{"title":"C","src":"s"}]}
|
|
156
|
+
]}
|
|
157
|
+
""".utf8)
|
|
158
|
+
let resolved = try JSONDecoder().decode(JsonResolvedTrack.self, from: json).toNitro()
|
|
159
|
+
let style = resolved.sections?.first?.style
|
|
160
|
+
#expect(style?.gridTile == .card)
|
|
161
|
+
#expect(style?.cardTint == "#1e3a8a")
|
|
162
|
+
#expect(style?.cardImage == .background)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@Test func unknownGridTileDecodesAsNoDeclaration() throws {
|
|
166
|
+
let json = Data(
|
|
167
|
+
"""
|
|
168
|
+
{"path":"/home","title":"Home","sections":[
|
|
169
|
+
{"style":{"gridTile":"hexagon"},"children":[{"title":"C","src":"s"}]}
|
|
170
|
+
]}
|
|
171
|
+
""".utf8)
|
|
172
|
+
let resolved = try JSONDecoder().decode(JsonResolvedTrack.self, from: json).toNitro()
|
|
173
|
+
#expect(resolved.sections?.first?.style == nil)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@Test func styleAndDisabledSurviveThePersistenceRoundTrip() throws {
|
|
177
|
+
// Restored queues keep their visible fidelity across process death.
|
|
178
|
+
let original = JsonTrack(
|
|
179
|
+
title: "X", src: "s",
|
|
180
|
+
style: JsonStyle(
|
|
181
|
+
display: "grid", artworkRendering: "stencil",
|
|
182
|
+
imageShape: "circular", accessorySymbol: "lock.fill",
|
|
183
|
+
),
|
|
184
|
+
disabled: true,
|
|
185
|
+
)
|
|
186
|
+
let encoded = try JSONEncoder().encode(original)
|
|
187
|
+
let decoded = try JSONDecoder().decode(JsonTrack.self, from: encoded)
|
|
188
|
+
#expect(decoded.style == original.style)
|
|
189
|
+
#expect(decoded.disabled == true)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@Test func liveBlockSnapshotsAllItemProperties() throws {
|
|
193
|
+
// The persistence snapshot (JsonStyle from a live TrackStyle) must not
|
|
194
|
+
// drop the newer item properties.
|
|
195
|
+
let snapshot = JsonStyle(
|
|
196
|
+
TrackStyle(
|
|
197
|
+
display: nil, artworkRendering: .stencil,
|
|
198
|
+
imageShape: .circular, accessorySymbol: "none",
|
|
199
|
+
cardTint: "#1e3a8a", cardImage: .background,
|
|
200
|
+
))
|
|
201
|
+
#expect(snapshot?.artworkRendering == "stencil")
|
|
202
|
+
#expect(snapshot?.imageShape == "circular")
|
|
203
|
+
#expect(snapshot?.accessorySymbol == "none")
|
|
204
|
+
#expect(snapshot?.cardTint == "#1e3a8a")
|
|
205
|
+
#expect(snapshot?.cardImage == "background")
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Testing
|
|
3
|
+
|
|
4
|
+
@testable import AudioBrowserTestable
|
|
5
|
+
|
|
6
|
+
@Suite("LRUCache")
|
|
7
|
+
struct LRUCacheTests {
|
|
8
|
+
/// A class so the tests can observe release through a weak reference.
|
|
9
|
+
private final class Box {
|
|
10
|
+
let id: Int
|
|
11
|
+
init(_ id: Int) { self.id = id }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@Test func evictsTheLeastRecentlyUsed() {
|
|
15
|
+
let cache = LRUCache<String, Int>(maxSize: 2)
|
|
16
|
+
cache.set("a", value: 1)
|
|
17
|
+
cache.set("b", value: 2)
|
|
18
|
+
cache.set("c", value: 3)
|
|
19
|
+
|
|
20
|
+
#expect(cache.get("a") == nil)
|
|
21
|
+
#expect(cache.get("b") == 2)
|
|
22
|
+
#expect(cache.get("c") == 3)
|
|
23
|
+
#expect(cache.count == 2)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// A read is a use: it must move the entry off the eviction end.
|
|
27
|
+
@Test func readingPromotesAnEntry() {
|
|
28
|
+
let cache = LRUCache<String, Int>(maxSize: 2)
|
|
29
|
+
cache.set("a", value: 1)
|
|
30
|
+
cache.set("b", value: 2)
|
|
31
|
+
_ = cache.get("a")
|
|
32
|
+
cache.set("c", value: 3)
|
|
33
|
+
|
|
34
|
+
#expect(cache.get("a") == 1)
|
|
35
|
+
#expect(cache.get("b") == nil)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Test func removeDropsOnlyItsOwnEntry() {
|
|
39
|
+
let cache = LRUCache<String, Int>(maxSize: 3)
|
|
40
|
+
cache.set("a", value: 1)
|
|
41
|
+
cache.set("b", value: 2)
|
|
42
|
+
cache.set("c", value: 3)
|
|
43
|
+
|
|
44
|
+
cache.remove("b")
|
|
45
|
+
|
|
46
|
+
#expect(cache.get("b") == nil)
|
|
47
|
+
#expect(cache.get("a") == 1)
|
|
48
|
+
#expect(cache.get("c") == 3)
|
|
49
|
+
#expect(cache.count == 2)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// The nodes form a doubly-linked list. If both directions were strong, adjacent nodes would
|
|
53
|
+
/// retain each other and clearing would drop only the dictionary and the two ends — the chain,
|
|
54
|
+
/// and everything it holds, would survive (#96).
|
|
55
|
+
@Test func clearReleasesEveryNode() {
|
|
56
|
+
let cache = LRUCache<String, Box>(maxSize: 10)
|
|
57
|
+
weak var first: Box?
|
|
58
|
+
weak var middle: Box?
|
|
59
|
+
weak var last: Box?
|
|
60
|
+
|
|
61
|
+
do {
|
|
62
|
+
let a = Box(1), b = Box(2), c = Box(3)
|
|
63
|
+
(first, middle, last) = (a, b, c)
|
|
64
|
+
cache.set("a", value: a)
|
|
65
|
+
cache.set("b", value: b)
|
|
66
|
+
cache.set("c", value: c)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#expect(first != nil, "the cache should still hold its entries")
|
|
70
|
+
|
|
71
|
+
cache.clear()
|
|
72
|
+
|
|
73
|
+
#expect(first == nil)
|
|
74
|
+
#expect(middle == nil)
|
|
75
|
+
#expect(last == nil)
|
|
76
|
+
#expect(cache.count == 0)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Same cycle, reached by dropping the cache itself rather than clearing it.
|
|
80
|
+
@Test func droppingTheCacheReleasesEveryNode() {
|
|
81
|
+
weak var first: Box?
|
|
82
|
+
weak var last: Box?
|
|
83
|
+
|
|
84
|
+
do {
|
|
85
|
+
let cache = LRUCache<String, Box>(maxSize: 10)
|
|
86
|
+
let a = Box(1), b = Box(2)
|
|
87
|
+
(first, last) = (a, b)
|
|
88
|
+
cache.set("a", value: a)
|
|
89
|
+
cache.set("b", value: b)
|
|
90
|
+
#expect(first != nil)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#expect(first == nil)
|
|
94
|
+
#expect(last == nil)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// Eviction has always unlinked the node it drops; this pins that down alongside the rest.
|
|
98
|
+
@Test func evictionReleasesTheEvictedNode() {
|
|
99
|
+
let cache = LRUCache<String, Box>(maxSize: 1)
|
|
100
|
+
weak var evicted: Box?
|
|
101
|
+
|
|
102
|
+
do {
|
|
103
|
+
let a = Box(1)
|
|
104
|
+
evicted = a
|
|
105
|
+
cache.set("a", value: a)
|
|
106
|
+
cache.set("b", value: Box(2))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#expect(evicted == nil)
|
|
110
|
+
}
|
|
111
|
+
}
|