react-native-audio-browser 0.2.0-next.11 → 0.2.0-next.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -4
- package/android/build.gradle +20 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +115 -26
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +262 -188
- package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +23 -3
- package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +95 -58
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +68 -26
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +60 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +19 -7
- package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
- package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +1 -1
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +120 -67
- package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -24
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +109 -74
- package/android/src/main/java/com/audiobrowser/player/Player.kt +51 -30
- package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
- package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
- package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +44 -40
- package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +108 -0
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +52 -53
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +74 -41
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +229 -50
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
- package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +1 -1
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +147 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -53
- package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +154 -0
- package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
- package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
- package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +1 -2
- package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
- package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
- package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +161 -0
- package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
- package/ios/Browser/BrowserConfig.swift +102 -5
- package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
- package/ios/Browser/BrowserManager.swift +229 -180
- package/ios/Browser/BrowserPathHelper.swift +44 -44
- package/ios/Browser/InertStyleDiagnostic.swift +279 -0
- package/ios/Browser/JsonModels.swift +258 -106
- package/ios/Browser/SearchDrillIn.swift +6 -6
- package/ios/Browser/SectionPresentation.swift +64 -0
- package/ios/Browser/SectionScope.swift +58 -24
- package/ios/Browser/StyleResolver.swift +58 -0
- package/ios/Browser/TabBarEntries.swift +9 -3
- package/ios/Browser/TrackIdTemplate.swift +37 -0
- package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
- package/ios/CarPlay/CarPlayController.swift +227 -85
- package/ios/CarPlay/CarPlayImageLoader.swift +38 -18
- package/ios/CarPlay/CarPlayListItemFactory.swift +289 -84
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
- package/ios/Extension/Capability+RemoteCommand.swift +1 -1
- package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
- package/ios/Extension/ResolvedTrack+Copying.swift +11 -15
- package/ios/Extension/Section+Copying.swift +31 -0
- package/ios/Extension/Track+Copying.swift +6 -12
- package/ios/Extension/Track+Identity.swift +18 -0
- package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
- package/ios/Http/HttpClient.swift +8 -2
- package/ios/HybridAudioBrowser.swift +166 -45
- package/ios/Model/NitroTypeStubs.swift +106 -21
- package/ios/Model/PlayerUpdateOptions.swift +7 -0
- package/ios/Player/MediaIntentCriteria.swift +13 -5
- package/ios/Player/MediaLoader.swift +33 -9
- package/ios/Player/NowPlayingUpdater.swift +54 -6
- package/ios/Player/PlaybackCoordinator.swift +13 -6
- package/ios/Player/PlaybackTimer.swift +8 -2
- package/ios/Player/QueueManager.swift +36 -10
- package/ios/Player/RetryManager.swift +4 -3
- package/ios/Player/ShuffleOrder.swift +23 -0
- package/ios/Player/SleepTimerHandling.swift +13 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +53 -18
- package/ios/Tests/BrowserPathHelperTests.swift +50 -20
- package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
- package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
- package/ios/Tests/JsonModelsTests.swift +150 -1
- package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
- package/ios/Tests/QueueManagerTests.swift +72 -6
- package/ios/Tests/RemoteCommandControllerTests.swift +16 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +37 -35
- package/ios/Tests/SearchDrillInTests.swift +16 -16
- package/ios/Tests/SectionPresentationTests.swift +90 -0
- package/ios/Tests/SectionScopeTests.swift +87 -55
- package/ios/Tests/StyleResolverTests.swift +127 -0
- package/ios/Tests/Support/TestTypes.swift +7 -0
- package/ios/Tests/TabBarEntriesTests.swift +18 -18
- package/ios/Tests/TrackIdTemplateTests.swift +67 -0
- package/ios/Tests/TrackIdentityTests.swift +20 -0
- package/ios/Tests/TrackSelectorTests.swift +168 -33
- package/ios/TrackPlayer.swift +57 -34
- package/ios/TrackSelector.swift +45 -30
- package/ios/Util/ArtworkImageFetcher.swift +16 -1
- package/ios/Util/NetworkMonitor.swift +43 -26
- package/ios/Util/SFSymbolRenderer.swift +24 -2
- package/ios/Util/SVGProcessor.swift +9 -2
- package/lib/commonjs/AudioBrowser.js +11 -0
- package/lib/commonjs/AudioBrowser.js.map +1 -1
- package/lib/commonjs/features/browser-config.js +11 -8
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/browser.js +2 -2
- package/lib/commonjs/features/favorites.js +13 -10
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/playback/controls.js +4 -2
- package/lib/commonjs/features/playback/controls.js.map +1 -1
- package/lib/commonjs/features/playback/progress.js +2 -1
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/rate.js +8 -1
- package/lib/commonjs/features/playback/rate.js.map +1 -1
- package/lib/commonjs/features/playback/state.js +2 -3
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/player/setup.js +3 -1
- package/lib/commonjs/features/player/setup.js.map +1 -1
- package/lib/commonjs/features/player/validateOptions.js +2 -0
- package/lib/commonjs/features/player/validateOptions.js.map +1 -1
- package/lib/commonjs/features/queue/activeTrack.js +14 -3
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +11 -4
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/utils/LazyNativeEmitter.js +21 -10
- package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
- package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/commonjs/utils/getTrackIdentity.js +24 -0
- package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
- package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +32 -24
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +144 -87
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
- package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
- package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/commonjs/web/browser/sections.js +89 -0
- package/lib/commonjs/web/browser/sections.js.map +1 -0
- package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
- package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/module/AudioBrowser.js +1 -0
- package/lib/module/AudioBrowser.js.map +1 -1
- package/lib/module/features/browser-config.js +11 -8
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/browser.js +2 -2
- package/lib/module/features/favorites.js +13 -10
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/playback/controls.js +4 -2
- package/lib/module/features/playback/controls.js.map +1 -1
- package/lib/module/features/playback/progress.js +2 -1
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/rate.js +8 -1
- package/lib/module/features/playback/rate.js.map +1 -1
- package/lib/module/features/playback/state.js +2 -3
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/player/setup.js +3 -1
- package/lib/module/features/player/setup.js.map +1 -1
- package/lib/module/features/player/validateOptions.js +2 -0
- package/lib/module/features/player/validateOptions.js.map +1 -1
- package/lib/module/features/queue/activeTrack.js +14 -3
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +11 -4
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/utils/LazyNativeEmitter.js +21 -10
- package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
- package/lib/module/utils/NativeUpdatedValue.js +31 -13
- package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
- package/lib/module/utils/getTrackIdentity.js +20 -0
- package/lib/module/utils/getTrackIdentity.js.map +1 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
- package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +32 -24
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +144 -87
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/FavoriteManager.js +31 -21
- package/lib/module/web/browser/FavoriteManager.js.map +1 -1
- package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
- package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
- package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
- package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
- package/lib/module/web/browser/sections.js +83 -0
- package/lib/module/web/browser/sections.js.map +1 -0
- package/lib/module/web/player/NowPlayingManager.js +4 -3
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/util/BrowserPathHelper.js +56 -11
- package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/typescript/src/AudioBrowser.d.ts +1 -0
- package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
- package/lib/typescript/src/features/browser.d.ts +2 -2
- package/lib/typescript/src/features/favorites.d.ts +13 -10
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/controls.d.ts +4 -2
- package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/rate.d.ts +8 -1
- package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +2 -3
- package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
- package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
- package/lib/typescript/src/features/player/options.d.ts +21 -19
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
- package/lib/typescript/src/features/player/validateOptions.d.ts +1 -8
- package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts +8 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts +5 -2
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-native.d.ts +2 -2
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +361 -126
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +33 -53
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +16 -17
- package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
- package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
- package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
- package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
- package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
- package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +0 -1
- package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +16 -4
- package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
- package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +8 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +16 -4
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +2 -5
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +15 -3
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +8 -2
- package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +33 -14
- package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +8 -3
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +22 -10
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +14 -2
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +10 -7
- package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
- package/nitrogen/generated/android/c++/JOptions.hpp +2 -5
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +8 -3
- package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +55 -51
- package/nitrogen/generated/android/c++/JSection.hpp +120 -0
- package/nitrogen/generated/android/c++/JSectionStyle.hpp +96 -0
- package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
- package/nitrogen/generated/android/c++/JTrack.hpp +23 -48
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +8 -2
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +46 -18
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +16 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +5 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -18
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → Section.kt} +18 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -15
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +35 -5
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +158 -82
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +25 -10
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +25 -10
- package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
- package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
- package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
- package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
- package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
- package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +5 -5
- package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
- package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
- package/nitrogen/generated/ios/swift/ResolvedTrack.swift +54 -83
- package/nitrogen/generated/ios/swift/Section.swift +100 -0
- package/nitrogen/generated/ios/swift/SectionStyle.swift +133 -0
- package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
- package/nitrogen/generated/ios/swift/Track.swift +20 -73
- package/nitrogen/generated/ios/swift/TrackStyle.swift +86 -22
- package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
- package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
- package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
- package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
- package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -6
- package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
- package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
- package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +30 -38
- package/nitrogen/generated/shared/c++/Section.hpp +106 -0
- package/nitrogen/generated/shared/c++/SectionStyle.hpp +126 -0
- package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
- package/nitrogen/generated/shared/c++/Track.hpp +15 -31
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +73 -34
- package/package.json +7 -2
- package/src/AudioBrowser.ts +1 -0
- package/src/features/browser-config.test.ts +21 -6
- package/src/features/browser-config.ts +11 -8
- package/src/features/browser.test.ts +1 -1
- package/src/features/browser.ts +2 -2
- package/src/features/favorites.ts +13 -10
- package/src/features/hook-wiring.test.ts +63 -15
- package/src/features/playback/controls.ts +4 -2
- package/src/features/playback/progress.ts +2 -1
- package/src/features/playback/rate.ts +8 -1
- package/src/features/playback/state.ts +2 -3
- package/src/features/playback/trackPlaybackTime.ts +1 -1
- package/src/features/player/options.ts +27 -19
- package/src/features/player/setup.test.ts +12 -3
- package/src/features/player/setup.ts +3 -1
- package/src/features/player/validateOptions.ts +2 -0
- package/src/features/queue/activeTrack.ts +14 -3
- package/src/features/queue/queue.ts +11 -4
- package/src/nitro-stub-drift.test.ts +40 -0
- package/src/public-surface.test.ts +40 -0
- package/src/specs/audio-browser.nitro.ts +4 -3
- package/src/style-resolution.test.ts +210 -0
- package/src/types/browser-native.ts +2 -2
- package/src/types/browser-nodes.ts +377 -135
- package/src/types/browser.ts +34 -55
- package/src/utils/LazyNativeEmitter.ts +20 -10
- package/src/utils/NativeUpdatedValue.ts +29 -13
- package/src/utils/getTrackIdentity.ts +20 -0
- package/src/utils/useNativeValueRefreshedBy.ts +38 -0
- package/src/web/NativeAudioBrowser.ts +37 -22
- package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
- package/src/web/TrackPlayer/QueueManager.ts +14 -0
- package/src/web/browser/BrowserManager.ts +176 -95
- package/src/web/browser/FavoriteManager.test.ts +84 -0
- package/src/web/browser/FavoriteManager.ts +28 -23
- package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
- package/src/web/browser/assertBrowsePageShape.ts +5 -3
- package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
- package/src/web/browser/classifyTrackNavigation.ts +9 -9
- package/src/web/browser/parseSearchResponse.test.ts +8 -2
- package/src/web/browser/sections.test.ts +91 -0
- package/src/web/browser/sections.ts +72 -0
- package/src/web/player/NowPlayingManager.ts +4 -3
- package/src/web/util/BrowserPathHelper.test.ts +52 -0
- package/src/web/util/BrowserPathHelper.ts +61 -13
- package/website/guide/android-auto.md +114 -0
- package/website/guide/android-certificates.md +409 -0
- package/website/guide/artwork.md +255 -0
- package/website/guide/audio-output.md +135 -0
- package/website/guide/automotive.md +86 -0
- package/website/guide/basic-usage.md +151 -0
- package/website/guide/battery.md +115 -0
- package/website/guide/browser.md +581 -0
- package/website/guide/carplay.md +620 -0
- package/website/guide/configuration.md +173 -0
- package/website/guide/equalizer.md +231 -0
- package/website/guide/errors.md +310 -0
- package/website/guide/favorites.md +154 -0
- package/website/guide/gate.md +143 -0
- package/website/guide/getting-started.md +46 -0
- package/website/guide/hooks.md +238 -0
- package/website/guide/live-streams.md +125 -0
- package/website/guide/metadata.md +239 -0
- package/website/guide/migrating-from-track-player.md +244 -0
- package/website/guide/native-callback-fetch.md +127 -0
- package/website/guide/network.md +86 -0
- package/website/guide/now-playing.md +175 -0
- package/website/guide/playback.md +328 -0
- package/website/guide/queue.md +300 -0
- package/website/guide/remote-controls.md +256 -0
- package/website/guide/search.md +255 -0
- package/website/guide/sleep-timer.md +268 -0
- package/website/guide/track.md +246 -0
- package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
- package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
- package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
- package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
- package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
- package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
- package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
- package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
- package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
- package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
|
@@ -58,8 +58,10 @@ export function seekTo(position: number): void {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
* Jump to the live edge of the current track
|
|
62
|
-
* live
|
|
61
|
+
* Jump to the live edge of the current track; no-op unless the track
|
|
62
|
+
* declares `live: true`. A stream with a seekable live window (HLS) seeks
|
|
63
|
+
* to the window's edge; a non-seekable stream (e.g. ICY radio) reconnects
|
|
64
|
+
* to rejoin live — an expired URL re-resolves through `media.resolve`.
|
|
63
65
|
*/
|
|
64
66
|
export function seekToLiveEdge(): void {
|
|
65
67
|
nativeBrowser.seekToLiveEdge()
|
|
@@ -118,7 +118,8 @@ export function usePolledProgress(updateInterval = 1000): Progress {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
const stop = () => {
|
|
121
|
-
clearTimeout(
|
|
121
|
+
// Guarded: React Native types clearTimeout(handle: number) — no undefined.
|
|
122
|
+
if (timer !== undefined) clearTimeout(timer)
|
|
122
123
|
timer = undefined
|
|
123
124
|
unsubscribeState?.()
|
|
124
125
|
unsubscribeState = undefined
|
|
@@ -14,8 +14,15 @@ export function getRate(): number {
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Sets the playback rate.
|
|
17
|
+
*
|
|
18
|
+
* The rate is a speed multiplier, not a transport control — it is independent
|
|
19
|
+
* of play/pause, and setting it while paused never starts playback. Use
|
|
20
|
+
* {@link play} / {@link pause} for that.
|
|
21
|
+
*
|
|
17
22
|
* @param rate - The playback rate to change to, where 0.5 would be half speed,
|
|
18
|
-
* 1 would be regular speed, 2 would be double speed etc.
|
|
23
|
+
* 1 would be regular speed, 2 would be double speed etc. Must be greater than
|
|
24
|
+
* zero; platforms disagree on what a non-positive rate does (iOS ignores it,
|
|
25
|
+
* Android throws, web stalls the element), so treat it as unsupported.
|
|
19
26
|
*/
|
|
20
27
|
export function setRate(rate: number): void {
|
|
21
28
|
nativeBrowser.setRate(rate)
|
|
@@ -18,9 +18,8 @@ import { useNativeUpdatedValue } from '../../utils/useNativeUpdatedValue'
|
|
|
18
18
|
* - `'buffering'`: Indicates that the player is currently loading more data
|
|
19
19
|
* before it can continue playing or is ready to start playing.
|
|
20
20
|
* - `'error'`: Indicates that playback of the current item failed. Call
|
|
21
|
-
* `
|
|
22
|
-
*
|
|
23
|
-
* the item again.
|
|
21
|
+
* `getPlaybackError()` (or subscribe via `usePlaybackError()`) for the
|
|
22
|
+
* error details. Call `retry()` or `play()` to try to play the item again.
|
|
24
23
|
* - `'ended'`: Indicates that playback stopped due to the end of the queue
|
|
25
24
|
* being reached.
|
|
26
25
|
*/
|
|
@@ -9,7 +9,7 @@ export interface PlaybackElapsed {
|
|
|
9
9
|
total: number
|
|
10
10
|
/** Seconds played since the previous callback (≈ `period`). */
|
|
11
11
|
sinceLast: number
|
|
12
|
-
/** The currently playing track (id / src /
|
|
12
|
+
/** The currently playing track (id / src / path / title …), if any. */
|
|
13
13
|
track?: Track
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
CarPlayNowPlayingButton,
|
|
3
|
+
CarPlayPlayingIndicatorLocation
|
|
4
|
+
} from '../../types/browser'
|
|
3
5
|
import { nativeBrowser } from '../../native'
|
|
4
6
|
import { NativeUpdatedValue } from '../../utils/NativeUpdatedValue'
|
|
5
7
|
import { useNativeUpdatedValue } from '../../utils/useNativeUpdatedValue'
|
|
@@ -75,31 +77,22 @@ export interface PlayerCapabilities {
|
|
|
75
77
|
* Android Auto and the Android 13+ system media controls — and an
|
|
76
78
|
* (empty or filled) heart on playable browse rows.
|
|
77
79
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* - `false` / omitted: favoriting off everywhere.
|
|
83
|
-
* - `true`: on, with `'exact'` id matching.
|
|
84
|
-
* - `{ match }`: on, with the given match mode.
|
|
80
|
+
* The ids passed to `setFavorites` are matched exactly against a track's
|
|
81
|
+
* *identity* — its `id` when set, falling back to `src`. Store favorites as
|
|
82
|
+
* the same stable identifier you put in `Track.id` (or as the full `src`
|
|
83
|
+
* for tracks without an id).
|
|
85
84
|
*
|
|
86
85
|
* @example
|
|
87
86
|
* ```ts
|
|
88
|
-
*
|
|
89
|
-
* favorite: { match: 'exact' }
|
|
90
|
-
* setFavorites(['https://cdn.example.com/audio/track-42.mp3'])
|
|
91
|
-
* // → favorited when src === 'https://cdn.example.com/audio/track-42.mp3'
|
|
92
|
-
*
|
|
93
|
-
* // 'partial': a favorite id matches when it is a full path segment of src.
|
|
94
|
-
* favorite: { match: 'partial' }
|
|
87
|
+
* favorite: true
|
|
95
88
|
* setFavorites(['track-42'])
|
|
96
|
-
* // → favorited when
|
|
97
|
-
* //
|
|
89
|
+
* // → favorited when track.id === 'track-42'
|
|
90
|
+
* // (or, for id-less tracks, when track.src === 'track-42')
|
|
98
91
|
* ```
|
|
99
92
|
*
|
|
100
93
|
* @default false
|
|
101
94
|
*/
|
|
102
|
-
favorite?: boolean
|
|
95
|
+
favorite?: boolean
|
|
103
96
|
/**
|
|
104
97
|
* Enable shuffle mode toggle.
|
|
105
98
|
* @default true
|
|
@@ -295,6 +288,13 @@ export interface IOSOptions {
|
|
|
295
288
|
* @default []
|
|
296
289
|
*/
|
|
297
290
|
carPlayNowPlayingButtons: CarPlayNowPlayingButton[]
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Where the now-playing indicator draws on CarPlay list rows.
|
|
294
|
+
* See {@link CarPlayPlayingIndicatorLocation}.
|
|
295
|
+
* @default 'leading'
|
|
296
|
+
*/
|
|
297
|
+
carPlayPlayingIndicatorLocation: CarPlayPlayingIndicatorLocation
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
export interface AndroidOptions {
|
|
@@ -391,6 +391,13 @@ export interface IOSUpdateOptions {
|
|
|
391
391
|
* @default []
|
|
392
392
|
*/
|
|
393
393
|
carPlayNowPlayingButtons?: CarPlayNowPlayingButton[]
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Where the now-playing indicator draws on CarPlay list rows.
|
|
397
|
+
* See {@link CarPlayPlayingIndicatorLocation}.
|
|
398
|
+
* @default 'leading'
|
|
399
|
+
*/
|
|
400
|
+
carPlayPlayingIndicatorLocation?: CarPlayPlayingIndicatorLocation
|
|
394
401
|
}
|
|
395
402
|
|
|
396
403
|
/**
|
|
@@ -401,6 +408,7 @@ export interface NitroIOSUpdateOptions {
|
|
|
401
408
|
playbackRates?: number[]
|
|
402
409
|
carPlayUpNextButton?: boolean
|
|
403
410
|
carPlayNowPlayingButtons?: CarPlayNowPlayingButton[]
|
|
411
|
+
carPlayPlayingIndicatorLocation?: CarPlayPlayingIndicatorLocation
|
|
404
412
|
}
|
|
405
413
|
|
|
406
414
|
/**
|
|
@@ -74,14 +74,23 @@ describe('setupPlayer wire regrouping', () => {
|
|
|
74
74
|
})
|
|
75
75
|
})
|
|
76
76
|
|
|
77
|
-
it('moves ios
|
|
77
|
+
it('moves ios runtime fields into the runtime options', async () => {
|
|
78
78
|
await setupPlayer({
|
|
79
|
-
ios: {
|
|
79
|
+
ios: {
|
|
80
|
+
category: 'playback',
|
|
81
|
+
playbackRates: [0.5, 1, 2],
|
|
82
|
+
carPlayPlayingIndicatorLocation: 'trailing'
|
|
83
|
+
}
|
|
80
84
|
})
|
|
81
85
|
|
|
82
86
|
const sent = payload()
|
|
83
87
|
expect(sent.ios).toEqual({ category: 'playback' })
|
|
84
|
-
expect(sent.options).toEqual({
|
|
88
|
+
expect(sent.options).toEqual({
|
|
89
|
+
ios: {
|
|
90
|
+
playbackRates: [0.5, 1, 2],
|
|
91
|
+
carPlayPlayingIndicatorLocation: 'trailing'
|
|
92
|
+
}
|
|
93
|
+
})
|
|
85
94
|
})
|
|
86
95
|
|
|
87
96
|
it('omits a platform bag that only carried runtime fields', async () => {
|
|
@@ -741,12 +741,14 @@ export async function setupPlayer(
|
|
|
741
741
|
playbackRates,
|
|
742
742
|
carPlayUpNextButton,
|
|
743
743
|
carPlayNowPlayingButtons,
|
|
744
|
+
carPlayPlayingIndicatorLocation,
|
|
744
745
|
...iosSetup
|
|
745
746
|
} = ios
|
|
746
747
|
const iosUpdate = definedFields({
|
|
747
748
|
playbackRates,
|
|
748
749
|
carPlayUpNextButton,
|
|
749
|
-
carPlayNowPlayingButtons
|
|
750
|
+
carPlayNowPlayingButtons,
|
|
751
|
+
carPlayPlayingIndicatorLocation
|
|
750
752
|
})
|
|
751
753
|
validateIOSUpdateOptions(iosUpdate)
|
|
752
754
|
|
|
@@ -7,6 +7,8 @@ const MAX_CARPLAY_NOW_PLAYING_BUTTONS = 5
|
|
|
7
7
|
* Shared by `updateOptions` and `setupPlayer` (both can carry `ios` options).
|
|
8
8
|
* Kept out of `options.ts`, which the barrel re-exports — see
|
|
9
9
|
* public-surface.test.ts.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
10
12
|
*/
|
|
11
13
|
export function validateIOSUpdateOptions(ios?: IOSUpdateOptions): void {
|
|
12
14
|
const buttons = ios?.carPlayNowPlayingButtons
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Track } from '../../types'
|
|
2
2
|
import { nativeBrowser } from '../../native'
|
|
3
3
|
import { NativeUpdatedValue } from '../../utils/NativeUpdatedValue'
|
|
4
|
-
import {
|
|
4
|
+
import { useNativeValueRefreshedBy } from '../../utils/useNativeValueRefreshedBy'
|
|
5
|
+
import { onFavoriteChanged } from '../favorites'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Event data for when the active track changes.
|
|
@@ -40,6 +41,12 @@ export function getActiveTrackIndex(): number | undefined {
|
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Subscribes to active track change events.
|
|
44
|
+
*
|
|
45
|
+
* Fires on **transitions only** — selection, queue advance, skip. In-place
|
|
46
|
+
* metadata mutations of the active track (a favorite toggle) emit
|
|
47
|
+
* {@link onFavoriteChanged} instead; {@link useActiveTrack} subscribes to
|
|
48
|
+
* both, so UI bound through the hook still re-renders on a heart toggle.
|
|
49
|
+
*
|
|
43
50
|
* @param callback - Called when the active track changes
|
|
44
51
|
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
45
52
|
*/
|
|
@@ -50,10 +57,14 @@ export const onActiveTrackChanged =
|
|
|
50
57
|
|
|
51
58
|
// MARK: - Hooks
|
|
52
59
|
|
|
60
|
+
// Module-level so the hook's subscription identity is stable across renders.
|
|
61
|
+
const activeTrackInvalidators = [onActiveTrackChanged, onFavoriteChanged]
|
|
62
|
+
|
|
53
63
|
/**
|
|
54
|
-
* Hook that returns the current active track
|
|
64
|
+
* Hook that returns the current active track, updating on transitions AND on
|
|
65
|
+
* in-place mutations of the active track (favorite toggles).
|
|
55
66
|
* @returns The current active track or undefined
|
|
56
67
|
*/
|
|
57
68
|
export function useActiveTrack(): Track | undefined {
|
|
58
|
-
return
|
|
69
|
+
return useNativeValueRefreshedBy(getActiveTrack, activeTrackInvalidators)
|
|
59
70
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Track } from '../../types'
|
|
2
2
|
import { nativeBrowser } from '../../native'
|
|
3
3
|
import { NativeUpdatedValue } from '../../utils/NativeUpdatedValue'
|
|
4
|
-
import {
|
|
4
|
+
import { useNativeValueRefreshedBy } from '../../utils/useNativeValueRefreshedBy'
|
|
5
|
+
import { onFavoriteChanged } from '../favorites'
|
|
5
6
|
|
|
6
7
|
// MARK: - Types
|
|
7
8
|
|
|
@@ -165,7 +166,9 @@ export const onQueueEnded =
|
|
|
165
166
|
|
|
166
167
|
/**
|
|
167
168
|
* Subscribes to queue change events.
|
|
168
|
-
* Called when tracks are added, removed, reordered
|
|
169
|
+
* Called when tracks are added, removed, or reordered. An in-place mutation of
|
|
170
|
+
* a queued track's metadata (a favorite toggle) emits `onFavoriteChanged`
|
|
171
|
+
* instead; {@link useQueue} subscribes to both.
|
|
169
172
|
* @param callback - Called with the updated queue
|
|
170
173
|
* @returns An emitter — subscribe with `addListener(callback)`, which returns a cleanup function
|
|
171
174
|
*/
|
|
@@ -175,10 +178,14 @@ export const onQueueChanged = NativeUpdatedValue.emitterize<Track[]>(
|
|
|
175
178
|
|
|
176
179
|
// MARK: - Hooks
|
|
177
180
|
|
|
181
|
+
// Module-level so the hook's subscription identity is stable across renders.
|
|
182
|
+
const queueInvalidators = [onQueueChanged, onFavoriteChanged]
|
|
183
|
+
|
|
178
184
|
/**
|
|
179
|
-
* Hook that returns the current queue
|
|
185
|
+
* Hook that returns the current queue, updating on queue changes AND on
|
|
186
|
+
* in-place mutations of queued tracks (favorite toggles).
|
|
180
187
|
* @returns The current queue
|
|
181
188
|
*/
|
|
182
189
|
export function useQueue(): Track[] {
|
|
183
|
-
return
|
|
190
|
+
return useNativeValueRefreshedBy(getQueue, queueInvalidators)
|
|
184
191
|
}
|
|
@@ -234,4 +234,44 @@ describe('NitroTypeStubs mirror the generated Nitro types', () => {
|
|
|
234
234
|
drift.join('\n')
|
|
235
235
|
).toEqual([])
|
|
236
236
|
})
|
|
237
|
+
|
|
238
|
+
it('declares struct members in the generated initializer order', () => {
|
|
239
|
+
// Name/type parity is not enough: the stub struct's memberwise init must
|
|
240
|
+
// accept the same ARGUMENT ORDER as the generated labelled init, or shared
|
|
241
|
+
// sources compile under `swift test` and fail in the app build with
|
|
242
|
+
// "argument 'x' must precede argument 'y'" (nitrogen flattens `extends`
|
|
243
|
+
// own-properties-first, which is easy to get backwards — SectionStyle did).
|
|
244
|
+
// A stub may omit members; the ones it declares must be a subsequence of
|
|
245
|
+
// the generated order.
|
|
246
|
+
const misordered: string[] = []
|
|
247
|
+
|
|
248
|
+
for (const [name, stub] of stubs) {
|
|
249
|
+
if (name in NOT_GENERATED || stub.kind !== 'struct') continue
|
|
250
|
+
const real = parseGenerated(name)
|
|
251
|
+
if (real == null || real.kind !== 'struct') continue
|
|
252
|
+
|
|
253
|
+
const realOrder = [...real.members.keys()]
|
|
254
|
+
const stubOrder = [...stub.members.keys()].filter((member) =>
|
|
255
|
+
real.members.has(member)
|
|
256
|
+
)
|
|
257
|
+
let cursor = -1
|
|
258
|
+
for (const member of stubOrder) {
|
|
259
|
+
const index = realOrder.indexOf(member)
|
|
260
|
+
if (index < cursor) {
|
|
261
|
+
misordered.push(
|
|
262
|
+
`${name}: stub declares [${stubOrder.join(', ')}], ` +
|
|
263
|
+
`generated init order is [${realOrder.join(', ')}]`
|
|
264
|
+
)
|
|
265
|
+
break
|
|
266
|
+
}
|
|
267
|
+
cursor = index
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
expect(
|
|
272
|
+
misordered,
|
|
273
|
+
`Stub struct members are out of order relative to the generated init:\n` +
|
|
274
|
+
misordered.join('\n')
|
|
275
|
+
).toEqual([])
|
|
276
|
+
})
|
|
237
277
|
})
|
|
@@ -104,4 +104,44 @@ describe('public surface', () => {
|
|
|
104
104
|
it('does not re-export the raw Nitro object', () => {
|
|
105
105
|
expect(exports.map((e) => e.name)).not.toContain('nativeBrowser')
|
|
106
106
|
})
|
|
107
|
+
|
|
108
|
+
it('every public module is covered by the typedoc entry points', () => {
|
|
109
|
+
// The API reference documents entryPoints, not the export graph — a public
|
|
110
|
+
// module missing from website/typedoc.json ships undocumented.
|
|
111
|
+
const config = JSON.parse(
|
|
112
|
+
readFileSync(join(process.cwd(), 'website', 'typedoc.json'), 'utf8')
|
|
113
|
+
) as { entryPoints: string[] }
|
|
114
|
+
const entries = config.entryPoints.map(
|
|
115
|
+
(glob) =>
|
|
116
|
+
new RegExp(
|
|
117
|
+
'^' +
|
|
118
|
+
glob
|
|
119
|
+
.replace(/^\.\.\/src\//, '')
|
|
120
|
+
.replace(/[.]/g, '\\.')
|
|
121
|
+
.replace(/\*/g, '[^/]+') +
|
|
122
|
+
'$'
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
const covered = (file: string): boolean => {
|
|
126
|
+
if (entries.some((e) => e.test(file))) return true
|
|
127
|
+
// A directory barrel entry (…/index.ts) documents what it re-exports.
|
|
128
|
+
const dir = file.split('/').slice(0, -1)
|
|
129
|
+
while (dir.length > 0) {
|
|
130
|
+
if (entries.some((e) => e.test([...dir, 'index.ts'].join('/'))))
|
|
131
|
+
return true
|
|
132
|
+
dir.pop()
|
|
133
|
+
}
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
const uncovered = [...new Set(exports.map((e) => e.file))].filter(
|
|
137
|
+
(file) => !covered(file)
|
|
138
|
+
)
|
|
139
|
+
expect(
|
|
140
|
+
uncovered,
|
|
141
|
+
'Exported to consumers but absent from the API reference. Add each ' +
|
|
142
|
+
'module (or its directory barrel) to website/typedoc.json ' +
|
|
143
|
+
'entryPoints:\n' +
|
|
144
|
+
uncovered.join('\n')
|
|
145
|
+
).toEqual([])
|
|
146
|
+
})
|
|
107
147
|
})
|
|
@@ -229,9 +229,10 @@ export interface AudioBrowser extends HybridObject<{
|
|
|
229
229
|
seekTo(position: number): void
|
|
230
230
|
seekBy(offset: number): void
|
|
231
231
|
/**
|
|
232
|
-
* Jump to the live edge of the current track
|
|
233
|
-
*
|
|
234
|
-
* window
|
|
232
|
+
* Jump to the live edge of the current track; no-op unless the track
|
|
233
|
+
* declares `live: true`. A stream with a seekable live window (HLS) seeks
|
|
234
|
+
* to the window's edge; a non-seekable stream (e.g. ICY radio) reconnects
|
|
235
|
+
* to rejoin live — an expired URL re-resolves through `media.resolve`.
|
|
235
236
|
*/
|
|
236
237
|
seekToLiveEdge(): void
|
|
237
238
|
setVolume(level: number): void
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { describe, expect, it } from 'vitest'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Completeness guards for the style declaration block (ADR 0011) — the checks
|
|
7
|
+
* Nitro's `extends`-flattening took out of the type system, in two parts:
|
|
8
|
+
* every key must be *resolved* by each platform's `StyleResolver`, and every
|
|
9
|
+
* key must be *accounted for* by each platform's `InertStyleDiagnostic`.
|
|
10
|
+
*
|
|
11
|
+
* Part one — inheritance completeness.
|
|
12
|
+
*
|
|
13
|
+
* `SectionStyle extends TrackStyle` in the spec, but Nitro flattens `extends`
|
|
14
|
+
* into independent structs on every platform — so nothing in any type system
|
|
15
|
+
* forces a platform's `StyleResolver` to read a newly added style key. A key
|
|
16
|
+
* the resolver forgets is silently dead: declared by consumers, decoded on the
|
|
17
|
+
* wire, never rendered. This test is the compile-time check the flattening
|
|
18
|
+
* took away:
|
|
19
|
+
*
|
|
20
|
+
* - every `SectionStyle` key must appear in each platform's
|
|
21
|
+
* `sectionStyle(section, page)` merge;
|
|
22
|
+
* - every inherited `TrackStyle` key must appear in each platform's
|
|
23
|
+
* `trackStyle(track, section)` merge;
|
|
24
|
+
* - `display` is positional (the deny-list): the track merge must resolve it
|
|
25
|
+
* to nil/null and must not read it from any level.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const ROOT = process.cwd()
|
|
29
|
+
|
|
30
|
+
const RESOLVERS = [
|
|
31
|
+
{
|
|
32
|
+
file: 'ios/Browser/StyleResolver.swift',
|
|
33
|
+
sectionMarker: 'static func sectionStyle',
|
|
34
|
+
trackMarker: 'static func trackStyle',
|
|
35
|
+
positionalNil: 'display: nil'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
file: 'android/src/main/java/com/audiobrowser/browser/StyleResolver.kt',
|
|
39
|
+
sectionMarker: 'fun sectionStyle',
|
|
40
|
+
trackMarker: 'fun trackStyle',
|
|
41
|
+
positionalNil: 'display = null'
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
/** Keys the spec deliberately excludes from item inheritance. */
|
|
46
|
+
const POSITIONAL = ['display']
|
|
47
|
+
|
|
48
|
+
function interfaceKeys(source: string, name: string): string[] {
|
|
49
|
+
const start = source.indexOf(`export interface ${name} `)
|
|
50
|
+
expect(
|
|
51
|
+
start,
|
|
52
|
+
`interface ${name} not found in browser-nodes.ts`
|
|
53
|
+
).toBeGreaterThan(-1)
|
|
54
|
+
const end = source.indexOf('\n}', start)
|
|
55
|
+
const body = source.slice(start, end)
|
|
56
|
+
return [...body.matchAll(/^ {2}(\w+)\??:/gm)].flatMap((match) =>
|
|
57
|
+
match[1] === undefined ? [] : [match[1]]
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The function's text from `marker` to the next function declaration (or end
|
|
63
|
+
* of file) — brace-agnostic, since Kotlin expression bodies have no braces.
|
|
64
|
+
*/
|
|
65
|
+
function functionBody(source: string, marker: string, file: string): string {
|
|
66
|
+
const start = source.indexOf(marker)
|
|
67
|
+
expect(start, `${marker} not found in ${file}`).toBeGreaterThan(-1)
|
|
68
|
+
const rest = source.slice(start + marker.length)
|
|
69
|
+
const next = rest.search(/\n\s*(?:static func|func|fun)\s/)
|
|
70
|
+
return marker + (next === -1 ? rest : rest.slice(0, next))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Comment-free view, so prose about a key doesn't count as reading it. */
|
|
74
|
+
function stripComments(code: string): string {
|
|
75
|
+
return code
|
|
76
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
77
|
+
.split('\n')
|
|
78
|
+
.map((line) => line.replace(/\/\/.*$/, ''))
|
|
79
|
+
.join('\n')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const nodes = readFileSync(
|
|
83
|
+
join(ROOT, 'src', 'types', 'browser-nodes.ts'),
|
|
84
|
+
'utf8'
|
|
85
|
+
)
|
|
86
|
+
const trackKeys = interfaceKeys(nodes, 'TrackStyle')
|
|
87
|
+
const sectionKeys = [...trackKeys, ...interfaceKeys(nodes, 'SectionStyle')]
|
|
88
|
+
const inheritedKeys = trackKeys.filter((key) => !POSITIONAL.includes(key))
|
|
89
|
+
|
|
90
|
+
describe('style block shape', () => {
|
|
91
|
+
it('has the positional key and at least one inherited key', () => {
|
|
92
|
+
expect(trackKeys).toContain('display')
|
|
93
|
+
expect(inheritedKeys.length).toBeGreaterThan(0)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
for (const resolver of RESOLVERS) {
|
|
98
|
+
describe(resolver.file, () => {
|
|
99
|
+
const source = readFileSync(join(ROOT, resolver.file), 'utf8')
|
|
100
|
+
const sectionBody = stripComments(
|
|
101
|
+
functionBody(source, resolver.sectionMarker, resolver.file)
|
|
102
|
+
)
|
|
103
|
+
const trackBody = stripComments(
|
|
104
|
+
functionBody(source, resolver.trackMarker, resolver.file)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
// Containment of `holder?.key` for BOTH holders — `display =
|
|
108
|
+
// section?.display` with the page half dropped must fail — AND in
|
|
109
|
+
// precedence order: the narrower holder must be read first, or the
|
|
110
|
+
// scope-override/inheritance direction is inverted (`page ?? section`
|
|
111
|
+
// would pass a bare containment check).
|
|
112
|
+
it.each(sectionKeys)('sectionStyle merges %s (section ?? page)', (key) => {
|
|
113
|
+
const section = sectionBody.indexOf(`section?.${key}`)
|
|
114
|
+
const page = sectionBody.indexOf(`page?.${key}`)
|
|
115
|
+
expect(section, `section?.${key} not read`).toBeGreaterThan(-1)
|
|
116
|
+
expect(page, `page?.${key} not read`).toBeGreaterThan(-1)
|
|
117
|
+
expect(section, `section?.${key} must precede page?.${key}`).toBeLessThan(
|
|
118
|
+
page
|
|
119
|
+
)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it.each(inheritedKeys)(
|
|
123
|
+
'trackStyle inherits %s (track ?? section)',
|
|
124
|
+
(key) => {
|
|
125
|
+
const track = trackBody.indexOf(`track?.${key}`)
|
|
126
|
+
const section = trackBody.indexOf(`section?.${key}`)
|
|
127
|
+
expect(track, `track?.${key} not read`).toBeGreaterThan(-1)
|
|
128
|
+
expect(section, `section?.${key} not read`).toBeGreaterThan(-1)
|
|
129
|
+
expect(
|
|
130
|
+
track,
|
|
131
|
+
`track?.${key} must precede section?.${key}`
|
|
132
|
+
).toBeLessThan(section)
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
it('trackStyle excludes the positional display (deny-list)', () => {
|
|
137
|
+
expect(trackBody).toContain(resolver.positionalNil)
|
|
138
|
+
// The nil assignment must be the ONLY mention: reading display from any
|
|
139
|
+
// level here would item-inherit a positional key.
|
|
140
|
+
const mentions = trackBody.match(/display/g) ?? []
|
|
141
|
+
expect(mentions).toHaveLength(1)
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Part two — diagnostic completeness.
|
|
148
|
+
*
|
|
149
|
+
* The block model traded compile-time invalid-combination errors for
|
|
150
|
+
* inertness, and the dev-mode `InertStyleDiagnostic` is the recovery. It is
|
|
151
|
+
* exposed to the same flattening problem as the resolvers, one level up: a
|
|
152
|
+
* key added to the spec has no type-system tie to the diagnostic's rule set,
|
|
153
|
+
* so a property could ship whose inert combinations nobody is warned about.
|
|
154
|
+
*
|
|
155
|
+
* Each platform therefore dispositions the whole vocabulary — every key is a
|
|
156
|
+
* `Property` with an inertness rule, `alwaysRenderable`, or `positional` —
|
|
157
|
+
* and this test reads those three declarations and asserts set equality with
|
|
158
|
+
* the spec, both directions: a new key must be dispositioned, and a key
|
|
159
|
+
* deleted from the spec must not linger in the rule set.
|
|
160
|
+
*/
|
|
161
|
+
const DIAGNOSTICS = [
|
|
162
|
+
{
|
|
163
|
+
file: 'ios/Browser/InertStyleDiagnostic.swift',
|
|
164
|
+
// `case gridWrap`, up to the first member after the case list.
|
|
165
|
+
ruled: (source: string) =>
|
|
166
|
+
keysIn(
|
|
167
|
+
source,
|
|
168
|
+
/enum Property[\s\S]*?var isItemProperty/,
|
|
169
|
+
/^\s*case (\w+)$/gm
|
|
170
|
+
)
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
file: 'android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt',
|
|
174
|
+
// `GRID_WRAP("gridWrap", …)`, up to the `;` that ends the entry list.
|
|
175
|
+
ruled: (source: string) =>
|
|
176
|
+
keysIn(source, /enum class Property[\s\S]*?;/, /\w+\("(\w+)"/g)
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
/** The capture group of every `pattern` match within the first `region`. */
|
|
181
|
+
function keysIn(source: string, region: RegExp, pattern: RegExp): string[] {
|
|
182
|
+
const found = region.exec(source)
|
|
183
|
+
expect(found, `region ${region} not found`).not.toBeNull()
|
|
184
|
+
return [...found![0].matchAll(pattern)].flatMap((match) =>
|
|
185
|
+
match[1] === undefined ? [] : [match[1]]
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (const diagnostic of DIAGNOSTICS) {
|
|
190
|
+
describe(diagnostic.file, () => {
|
|
191
|
+
// Comment-free, so prose naming a key doesn't pass for dispositioning it.
|
|
192
|
+
const source = stripComments(
|
|
193
|
+
readFileSync(join(ROOT, diagnostic.file), 'utf8')
|
|
194
|
+
)
|
|
195
|
+
// The two lists are spelled `alwaysRenderable`/`positional` in Swift and
|
|
196
|
+
// SCREAMING_CASE in Kotlin; both hold bare string literals.
|
|
197
|
+
const listed = [
|
|
198
|
+
...source.matchAll(
|
|
199
|
+
/(?:alwaysRenderable|ALWAYS_RENDERABLE|positional|POSITIONAL)\s*=[^\n]*/gi
|
|
200
|
+
)
|
|
201
|
+
].flatMap((match) =>
|
|
202
|
+
[...match[0].matchAll(/"(\w+)"/g)].map((key) => key[1])
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
it('dispositions every key of the block, and only those', () => {
|
|
206
|
+
const dispositioned = [...diagnostic.ruled(source), ...listed]
|
|
207
|
+
expect(new Set(dispositioned)).toEqual(new Set(sectionKeys))
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type {
|
|
10
10
|
ArtworkRequestConfig,
|
|
11
11
|
BrowserSourceCallback,
|
|
12
|
-
|
|
12
|
+
ResolveAlbumPathCallback,
|
|
13
13
|
FormatNavigationErrorCallback,
|
|
14
14
|
HandleTrackLoadCallback,
|
|
15
15
|
MediaRequestConfig,
|
|
@@ -76,6 +76,6 @@ export interface NativeBrowserConfiguration {
|
|
|
76
76
|
|
|
77
77
|
// CarPlay options
|
|
78
78
|
carPlayLoadingTitle?: string
|
|
79
|
-
|
|
79
|
+
resolveAlbumPath?: ResolveAlbumPathCallback
|
|
80
80
|
formatNavigationError?: FormatNavigationErrorCallback
|
|
81
81
|
}
|