react-native-audio-browser 0.2.0-next.10 → 0.2.0-next.12

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.
Files changed (345) hide show
  1. package/README.md +4 -4
  2. package/android/build.gradle +6 -5
  3. package/android/gradle.properties +1 -1
  4. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +110 -25
  5. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +254 -188
  6. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +23 -3
  7. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +77 -58
  8. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +68 -26
  9. package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +49 -0
  10. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +9 -7
  11. package/android/src/main/java/com/audiobrowser/extension/TappedTrack.kt +20 -0
  12. package/android/src/main/java/com/audiobrowser/extension/TrackIdentity.kt +15 -0
  13. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +120 -67
  14. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +21 -24
  15. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +93 -73
  16. package/android/src/main/java/com/audiobrowser/player/Player.kt +48 -30
  17. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +2 -15
  18. package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +46 -5
  19. package/android/src/main/java/com/audiobrowser/tls/AiaChasingExtendedTrustManager.kt +86 -27
  20. package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +58 -14
  21. package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +20 -6
  22. package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +249 -86
  23. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +44 -40
  24. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +28 -23
  25. package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +35 -0
  26. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +45 -53
  27. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +69 -30
  28. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerFavoritesTest.kt +153 -0
  29. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +6 -0
  30. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +345 -8
  31. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +38 -0
  32. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +1 -1
  33. package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +91 -0
  34. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +128 -53
  35. package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +87 -0
  36. package/android/src/test/java/com/audiobrowser/extension/TappedTrackTest.kt +43 -0
  37. package/android/src/test/java/com/audiobrowser/extension/TrackIdentityTest.kt +41 -0
  38. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +1 -2
  39. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +42 -2
  40. package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +55 -0
  41. package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +152 -8
  42. package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +218 -10
  43. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherCallSiteTest.kt +322 -0
  44. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +41 -16
  45. package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +161 -0
  46. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +7 -7
  47. package/ios/Browser/BrowserConfig.swift +102 -5
  48. package/ios/Browser/BrowserManager+URLResolution.swift +18 -14
  49. package/ios/Browser/BrowserManager.swift +131 -143
  50. package/ios/Browser/BrowserPathHelper.swift +44 -44
  51. package/ios/Browser/JsonModels.swift +187 -106
  52. package/ios/Browser/SearchDrillIn.swift +6 -6
  53. package/ios/Browser/SectionPresentation.swift +34 -0
  54. package/ios/Browser/SectionScope.swift +58 -24
  55. package/ios/Browser/StyleResolver.swift +47 -0
  56. package/ios/Browser/TabBarEntries.swift +2 -2
  57. package/ios/Browser/TrackIdTemplate.swift +37 -0
  58. package/ios/CarPlay/CarPlayArtworkResolver.swift +8 -7
  59. package/ios/CarPlay/CarPlayController.swift +105 -67
  60. package/ios/CarPlay/CarPlayImageLoader.swift +38 -18
  61. package/ios/CarPlay/CarPlayListItemFactory.swift +217 -84
  62. package/ios/CarPlay/CarPlayNowPlayingManager.swift +15 -12
  63. package/ios/Extension/Capability+RemoteCommand.swift +1 -1
  64. package/ios/Extension/PlayerCapabilities+Favorite.swift +1 -15
  65. package/ios/Extension/ResolvedTrack+Copying.swift +11 -15
  66. package/ios/Extension/Section+Copying.swift +31 -0
  67. package/ios/Extension/Track+Copying.swift +6 -12
  68. package/ios/Extension/Track+Identity.swift +18 -0
  69. package/ios/HybridAudioBrowser.swift +113 -35
  70. package/ios/Model/NitroTypeStubs.swift +64 -23
  71. package/ios/Model/PlayerUpdateOptions.swift +7 -0
  72. package/ios/Player/NowPlayingUpdater.swift +1 -1
  73. package/ios/Player/PlaybackCoordinator.swift +2 -2
  74. package/ios/Player/RetryManager.swift +4 -3
  75. package/ios/Tests/BrowserPathHelperTests.swift +50 -20
  76. package/ios/Tests/CarPlayArtworkResolverTests.swift +24 -41
  77. package/ios/Tests/JsonModelsTests.swift +75 -1
  78. package/ios/Tests/PlaybackStateStoreTests.swift +4 -3
  79. package/ios/Tests/QueueManagerTests.swift +4 -4
  80. package/ios/Tests/ResolvedTrackCopyingTests.swift +37 -35
  81. package/ios/Tests/SearchDrillInTests.swift +16 -16
  82. package/ios/Tests/SectionPresentationTests.swift +61 -0
  83. package/ios/Tests/SectionScopeTests.swift +87 -55
  84. package/ios/Tests/StyleResolverTests.swift +67 -0
  85. package/ios/Tests/TabBarEntriesTests.swift +18 -18
  86. package/ios/Tests/TrackIdTemplateTests.swift +67 -0
  87. package/ios/Tests/TrackIdentityTests.swift +20 -0
  88. package/ios/Tests/TrackSelectorTests.swift +168 -33
  89. package/ios/TrackSelector.swift +45 -30
  90. package/ios/Util/ArtworkImageFetcher.swift +16 -1
  91. package/lib/commonjs/AudioBrowser.js +11 -0
  92. package/lib/commonjs/AudioBrowser.js.map +1 -1
  93. package/lib/commonjs/features/browser-config.js +11 -8
  94. package/lib/commonjs/features/browser-config.js.map +1 -1
  95. package/lib/commonjs/features/browser.js +2 -2
  96. package/lib/commonjs/features/favorites.js +13 -10
  97. package/lib/commonjs/features/favorites.js.map +1 -1
  98. package/lib/commonjs/features/playback/controls.js +4 -2
  99. package/lib/commonjs/features/playback/controls.js.map +1 -1
  100. package/lib/commonjs/features/playback/state.js +2 -3
  101. package/lib/commonjs/features/playback/state.js.map +1 -1
  102. package/lib/commonjs/features/player/options.js.map +1 -1
  103. package/lib/commonjs/features/player/setup.js +3 -1
  104. package/lib/commonjs/features/player/setup.js.map +1 -1
  105. package/lib/commonjs/features/player/validateOptions.js +2 -0
  106. package/lib/commonjs/features/player/validateOptions.js.map +1 -1
  107. package/lib/commonjs/features/queue/activeTrack.js +14 -3
  108. package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
  109. package/lib/commonjs/features/queue/queue.js +11 -4
  110. package/lib/commonjs/features/queue/queue.js.map +1 -1
  111. package/lib/commonjs/utils/LazyNativeEmitter.js +21 -10
  112. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  113. package/lib/commonjs/utils/NativeUpdatedValue.js +31 -13
  114. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  115. package/lib/commonjs/utils/getTrackIdentity.js +24 -0
  116. package/lib/commonjs/utils/getTrackIdentity.js.map +1 -0
  117. package/lib/commonjs/utils/useNativeValueRefreshedBy.js +33 -0
  118. package/lib/commonjs/utils/useNativeValueRefreshedBy.js.map +1 -0
  119. package/lib/commonjs/web/NativeAudioBrowser.js +32 -24
  120. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  121. package/lib/commonjs/web/browser/BrowserManager.js +144 -87
  122. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  123. package/lib/commonjs/web/browser/FavoriteManager.js +30 -21
  124. package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
  125. package/lib/commonjs/web/browser/assertBrowsePageShape.js +2 -2
  126. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +1 -1
  127. package/lib/commonjs/web/browser/classifyTrackNavigation.js +9 -9
  128. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +1 -1
  129. package/lib/commonjs/web/browser/sections.js +89 -0
  130. package/lib/commonjs/web/browser/sections.js.map +1 -0
  131. package/lib/commonjs/web/player/NowPlayingManager.js +4 -3
  132. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  133. package/lib/commonjs/web/util/BrowserPathHelper.js +56 -11
  134. package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
  135. package/lib/module/AudioBrowser.js +1 -0
  136. package/lib/module/AudioBrowser.js.map +1 -1
  137. package/lib/module/features/browser-config.js +11 -8
  138. package/lib/module/features/browser-config.js.map +1 -1
  139. package/lib/module/features/browser.js +2 -2
  140. package/lib/module/features/favorites.js +13 -10
  141. package/lib/module/features/favorites.js.map +1 -1
  142. package/lib/module/features/playback/controls.js +4 -2
  143. package/lib/module/features/playback/controls.js.map +1 -1
  144. package/lib/module/features/playback/state.js +2 -3
  145. package/lib/module/features/playback/state.js.map +1 -1
  146. package/lib/module/features/player/options.js.map +1 -1
  147. package/lib/module/features/player/setup.js +3 -1
  148. package/lib/module/features/player/setup.js.map +1 -1
  149. package/lib/module/features/player/validateOptions.js +2 -0
  150. package/lib/module/features/player/validateOptions.js.map +1 -1
  151. package/lib/module/features/queue/activeTrack.js +14 -3
  152. package/lib/module/features/queue/activeTrack.js.map +1 -1
  153. package/lib/module/features/queue/queue.js +11 -4
  154. package/lib/module/features/queue/queue.js.map +1 -1
  155. package/lib/module/utils/LazyNativeEmitter.js +21 -10
  156. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  157. package/lib/module/utils/NativeUpdatedValue.js +31 -13
  158. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  159. package/lib/module/utils/getTrackIdentity.js +20 -0
  160. package/lib/module/utils/getTrackIdentity.js.map +1 -0
  161. package/lib/module/utils/useNativeValueRefreshedBy.js +29 -0
  162. package/lib/module/utils/useNativeValueRefreshedBy.js.map +1 -0
  163. package/lib/module/web/NativeAudioBrowser.js +32 -24
  164. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  165. package/lib/module/web/browser/BrowserManager.js +144 -87
  166. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  167. package/lib/module/web/browser/FavoriteManager.js +31 -21
  168. package/lib/module/web/browser/FavoriteManager.js.map +1 -1
  169. package/lib/module/web/browser/assertBrowsePageShape.js +2 -2
  170. package/lib/module/web/browser/assertBrowsePageShape.js.map +1 -1
  171. package/lib/module/web/browser/classifyTrackNavigation.js +9 -9
  172. package/lib/module/web/browser/classifyTrackNavigation.js.map +1 -1
  173. package/lib/module/web/browser/sections.js +83 -0
  174. package/lib/module/web/browser/sections.js.map +1 -0
  175. package/lib/module/web/player/NowPlayingManager.js +4 -3
  176. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  177. package/lib/module/web/util/BrowserPathHelper.js +56 -11
  178. package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
  179. package/lib/typescript/src/AudioBrowser.d.ts +1 -0
  180. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  181. package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
  182. package/lib/typescript/src/features/browser.d.ts +2 -2
  183. package/lib/typescript/src/features/favorites.d.ts +13 -10
  184. package/lib/typescript/src/features/favorites.d.ts.map +1 -1
  185. package/lib/typescript/src/features/playback/controls.d.ts +4 -2
  186. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  187. package/lib/typescript/src/features/playback/state.d.ts +2 -3
  188. package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
  189. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +1 -1
  190. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +1 -1
  191. package/lib/typescript/src/features/player/options.d.ts +21 -19
  192. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  193. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  194. package/lib/typescript/src/features/player/validateOptions.d.ts +1 -8
  195. package/lib/typescript/src/features/player/validateOptions.d.ts.map +1 -1
  196. package/lib/typescript/src/features/queue/activeTrack.d.ts +8 -1
  197. package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
  198. package/lib/typescript/src/features/queue/queue.d.ts +5 -2
  199. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  200. package/lib/typescript/src/types/browser-native.d.ts +2 -2
  201. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  202. package/lib/typescript/src/types/browser-nodes.d.ts +260 -127
  203. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  204. package/lib/typescript/src/types/browser.d.ts +33 -53
  205. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  206. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +16 -17
  207. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  208. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +24 -19
  209. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  210. package/lib/typescript/src/utils/getTrackIdentity.d.ts +18 -0
  211. package/lib/typescript/src/utils/getTrackIdentity.d.ts.map +1 -0
  212. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts +21 -0
  213. package/lib/typescript/src/utils/useNativeValueRefreshedBy.d.ts.map +1 -0
  214. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +0 -1
  215. package/nitrogen/generated/android/c++/{JFavoritesMatchMode.hpp → JArtworkRendering.hpp} +15 -15
  216. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +4 -3
  217. package/nitrogen/generated/android/c++/JBrowseResult.hpp +10 -4
  218. package/nitrogen/generated/android/c++/JCarPlayPlayingIndicatorLocation.hpp +58 -0
  219. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +4 -3
  220. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +4 -3
  221. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +4 -3
  222. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +4 -3
  223. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +4 -3
  224. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +4 -3
  225. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +4 -2
  226. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +4 -2
  227. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +10 -4
  228. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +4 -3
  229. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +2 -5
  230. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +4 -3
  231. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +9 -3
  232. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +4 -2
  233. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +21 -14
  234. package/nitrogen/generated/android/c++/JIOSOptions.hpp +9 -3
  235. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +4 -3
  236. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +16 -10
  237. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +8 -2
  238. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +6 -7
  239. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +2 -5
  240. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +9 -3
  241. package/nitrogen/generated/android/c++/JOptions.hpp +2 -5
  242. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +4 -3
  243. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
  244. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +49 -51
  245. package/nitrogen/generated/android/c++/JSection.hpp +114 -0
  246. package/nitrogen/generated/android/c++/JSectionStyle.hpp +69 -0
  247. package/nitrogen/generated/android/c++/JStyleDisplay.hpp +58 -0
  248. package/nitrogen/generated/android/c++/JTrack.hpp +19 -48
  249. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +4 -2
  250. package/nitrogen/generated/android/c++/JTrackStyle.hpp +25 -18
  251. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +10 -4
  252. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → ArtworkRendering.kt} +5 -5
  253. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayPlayingIndicatorLocation.kt +23 -0
  254. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +6 -3
  255. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +5 -5
  256. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +6 -3
  257. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  258. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +12 -18
  259. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Section.kt +50 -0
  260. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → SectionStyle.kt} +12 -6
  261. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StyleDisplay.kt +23 -0
  262. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +6 -15
  263. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +23 -5
  264. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +104 -82
  265. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +16 -10
  266. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +16 -10
  267. package/nitrogen/generated/ios/swift/ArtworkRendering.swift +40 -0
  268. package/nitrogen/generated/ios/swift/CarPlayPlayingIndicatorLocation.swift +40 -0
  269. package/nitrogen/generated/ios/swift/IOSOptions.swift +7 -2
  270. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +5 -5
  271. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +12 -1
  272. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
  273. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +54 -83
  274. package/nitrogen/generated/ios/swift/Section.swift +100 -0
  275. package/nitrogen/generated/ios/swift/SectionStyle.swift +64 -0
  276. package/nitrogen/generated/ios/swift/StyleDisplay.swift +40 -0
  277. package/nitrogen/generated/ios/swift/Track.swift +20 -73
  278. package/nitrogen/generated/ios/swift/TrackStyle.swift +28 -22
  279. package/nitrogen/generated/shared/c++/{FavoritesMatchMode.hpp → ArtworkRendering.hpp} +17 -17
  280. package/nitrogen/generated/shared/c++/CarPlayPlayingIndicatorLocation.hpp +76 -0
  281. package/nitrogen/generated/shared/c++/IOSOptions.hpp +9 -2
  282. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -6
  283. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +9 -2
  284. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
  285. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +30 -38
  286. package/nitrogen/generated/shared/c++/Section.hpp +106 -0
  287. package/nitrogen/generated/shared/c++/SectionStyle.hpp +96 -0
  288. package/nitrogen/generated/shared/c++/StyleDisplay.hpp +76 -0
  289. package/nitrogen/generated/shared/c++/Track.hpp +15 -31
  290. package/nitrogen/generated/shared/c++/TrackStyle.hpp +50 -34
  291. package/package.json +3 -2
  292. package/src/AudioBrowser.ts +1 -0
  293. package/src/features/browser-config.test.ts +21 -6
  294. package/src/features/browser-config.ts +11 -8
  295. package/src/features/browser.test.ts +1 -1
  296. package/src/features/browser.ts +2 -2
  297. package/src/features/favorites.ts +13 -10
  298. package/src/features/hook-wiring.test.ts +63 -15
  299. package/src/features/playback/controls.ts +4 -2
  300. package/src/features/playback/state.ts +2 -3
  301. package/src/features/playback/trackPlaybackTime.ts +1 -1
  302. package/src/features/player/options.ts +27 -19
  303. package/src/features/player/setup.test.ts +12 -3
  304. package/src/features/player/setup.ts +3 -1
  305. package/src/features/player/validateOptions.ts +2 -0
  306. package/src/features/queue/activeTrack.ts +14 -3
  307. package/src/features/queue/queue.ts +11 -4
  308. package/src/nitro-stub-drift.test.ts +277 -0
  309. package/src/public-surface.test.ts +40 -0
  310. package/src/specs/audio-browser.nitro.ts +4 -3
  311. package/src/style-resolution.test.ts +139 -0
  312. package/src/types/browser-native.ts +2 -2
  313. package/src/types/browser-nodes.ts +268 -136
  314. package/src/types/browser.ts +34 -55
  315. package/src/utils/LazyNativeEmitter.ts +20 -10
  316. package/src/utils/NativeUpdatedValue.ts +29 -13
  317. package/src/utils/getTrackIdentity.ts +20 -0
  318. package/src/utils/useNativeValueRefreshedBy.ts +38 -0
  319. package/src/web/NativeAudioBrowser.ts +37 -22
  320. package/src/web/browser/BrowserManager.ts +176 -95
  321. package/src/web/browser/FavoriteManager.test.ts +84 -0
  322. package/src/web/browser/FavoriteManager.ts +28 -23
  323. package/src/web/browser/assertBrowsePageShape.test.ts +1 -1
  324. package/src/web/browser/assertBrowsePageShape.ts +5 -3
  325. package/src/web/browser/classifyTrackNavigation.test.ts +10 -8
  326. package/src/web/browser/classifyTrackNavigation.ts +9 -9
  327. package/src/web/browser/parseSearchResponse.test.ts +8 -2
  328. package/src/web/browser/sections.test.ts +91 -0
  329. package/src/web/browser/sections.ts +72 -0
  330. package/src/web/player/NowPlayingManager.ts +4 -3
  331. package/src/web/util/BrowserPathHelper.test.ts +52 -0
  332. package/src/web/util/BrowserPathHelper.ts +61 -13
  333. package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
  334. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
  335. package/nitrogen/generated/android/c++/JImageRowItem.hpp +0 -105
  336. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
  337. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
  338. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +0 -68
  339. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
  340. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
  341. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
  342. package/nitrogen/generated/ios/swift/ImageRowItem.swift +0 -195
  343. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
  344. package/nitrogen/generated/shared/c++/FavoriteConfig.hpp +0 -84
  345. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +0 -129
@@ -3,23 +3,24 @@ import Foundation
3
3
  /// Utility for handling browser paths and contextual URLs in the media browser system.
4
4
  ///
5
5
  /// Handles two types of special paths:
6
- /// 1. System paths (prefixed with `/__`): Root, recent, and search paths
6
+ /// 1. System paths (prefixed with `/__`): root, search, and offline paths
7
7
  /// 2. Contextual URLs: Embed parent context in track identifiers for playback integration
8
8
  ///
9
- /// Contextual URL format: `{parentPath}?__trackId={trackSrc}`
10
- /// Example: "/library/radio?__trackId=song.mp3"
9
+ /// Contextual URL format: `{parentPath}?__trackId={trackIdentity}&__index={childIndex}`
10
+ /// Example: "/library/radio?__trackId=song.mp3&__index=2"
11
+ ///
12
+ /// `__trackId` is the identity check; `__index` (the child's position on the
13
+ /// page at stamp time) is only a tie-breaker between surfaces that carry the
14
+ /// same identity — a stale index never selects a different track.
11
15
  ///
12
16
  /// This allows:
13
- /// - Playable-only tracks (tracks with `src` but no `url`) to be referenced
17
+ /// - Playable-only tracks (tracks with `src` but no `path`) to be referenced
14
18
  /// - Cache lookup to work consistently
15
19
  /// - Parent context to be preserved for queue restoration
16
20
  enum BrowserPathHelper {
17
21
  /// Root path for media browsing
18
22
  static let rootPath = "/__root"
19
23
 
20
- /// Recent media path for playback resumption
21
- static let recentPath = "/__recent"
22
-
23
24
  /// Search path prefix (full path is /__search?q=query)
24
25
  static let searchPathPrefix = "/__search"
25
26
 
@@ -38,10 +39,12 @@ enum BrowserPathHelper {
38
39
  /// Query parameter name for contextual track identifiers
39
40
  private static let contextualTrackParam = "__trackId"
40
41
 
42
+ /// Query parameter name for the tapped child's page position (tie-breaker)
43
+ private static let contextualIndexParam = "__index"
44
+
41
45
  /// Check if a path is a special system path (not a regular navigation path)
42
46
  static func isSpecialPath(_ path: String) -> Bool {
43
47
  path == rootPath ||
44
- path == recentPath ||
45
48
  path.hasPrefix("\(searchPathPrefix)?")
46
49
  }
47
50
 
@@ -61,13 +64,13 @@ enum BrowserPathHelper {
61
64
  || path.contains("&\(contextualTrackParam)=")
62
65
  }
63
66
 
64
- /// Strips the __trackId parameter from a contextual URL to get the parent path.
65
- /// If the URL is not contextual, returns it unchanged.
67
+ /// Strips the contextual parameters (__trackId and __index) from a contextual
68
+ /// URL to get the parent path. If the URL is not contextual, returns it unchanged.
66
69
  ///
67
70
  /// - Parameter url: The URL to process
68
- /// - Returns: The URL without the __trackId parameter
71
+ /// - Returns: The URL without the contextual parameters
69
72
  ///
70
- /// Example: "/library/radio?__trackId=song.mp3" → "/library/radio"
73
+ /// Example: "/library/radio?__trackId=song.mp3&__index=2" → "/library/radio"
71
74
  /// Example: "/search?q=jazz&__trackId=song.mp3" → "/search?q=jazz"
72
75
  static func stripTrackId(_ url: String) -> String {
73
76
  guard isContextual(url) else {
@@ -78,8 +81,10 @@ enum BrowserPathHelper {
78
81
  return url
79
82
  }
80
83
 
81
- // Filter out __trackId parameter
82
- components.queryItems = components.queryItems?.filter { $0.name != contextualTrackParam }
84
+ // Filter out the contextual parameters
85
+ components.queryItems = components.queryItems?.filter {
86
+ $0.name != contextualTrackParam && $0.name != contextualIndexParam
87
+ }
83
88
 
84
89
  // If no query items left, clear the query string entirely
85
90
  if components.queryItems?.isEmpty == true {
@@ -89,21 +94,24 @@ enum BrowserPathHelper {
89
94
  return components.string ?? url
90
95
  }
91
96
 
92
- /// Builds a contextual URL by appending a track identifier to a parent path.
97
+ /// Builds a contextual URL by appending a track identifier — and optionally
98
+ /// the tapped child's page position — to a parent path.
93
99
  /// Handles existing query parameters correctly.
94
100
  ///
95
101
  /// - Parameters:
96
102
  /// - parentPath: The parent container path
97
- /// - trackId: The track identifier (typically the `src` value)
98
- /// - Returns: A contextual URL combining parent path and track ID
103
+ /// - trackId: The track identity (`id` when non-blank, else `src`)
104
+ /// - index: The child's position on the page at stamp time (tie-breaker)
105
+ /// - Returns: A contextual URL combining parent path, track ID, and index
99
106
  ///
100
- /// Example: build("/library", "song.mp3") → "/library?__trackId=song.mp3"
107
+ /// Example: build("/library", "song.mp3", index: 2) → "/library?__trackId=song.mp3&__index=2"
101
108
  /// Example: build("/search?q=jazz", "song.mp3") → "/search?q=jazz&__trackId=song.mp3"
102
- static func build(parentPath: String, trackId: String) -> String {
109
+ static func build(parentPath: String, trackId: String, index: Int? = nil) -> String {
103
110
  let encodedTrackId =
104
- trackId.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? trackId
111
+ trackId.addingPercentEncoding(withAllowedCharacters: queryComponentAllowed) ?? trackId
105
112
  let separator = parentPath.contains("?") ? "&" : "?"
106
- return "\(parentPath)\(separator)\(contextualTrackParam)=\(encodedTrackId)"
113
+ let indexParam = index.map { "&\(contextualIndexParam)=\($0)" } ?? ""
114
+ return "\(parentPath)\(separator)\(contextualTrackParam)=\(encodedTrackId)\(indexParam)"
107
115
  }
108
116
 
109
117
  /// Extracts the track ID from a contextual URL.
@@ -125,6 +133,21 @@ enum BrowserPathHelper {
125
133
  return components.queryItems?.first { $0.name == contextualTrackParam }?.value
126
134
  }
127
135
 
136
+ /// Extracts the stamped page index from a contextual URL, or nil when the
137
+ /// URL is not contextual or carries no (valid, non-negative) index.
138
+ ///
139
+ /// Example: "/library/radio?__trackId=song.mp3&__index=2" → 2
140
+ static func extractIndex(_ path: String) -> Int? {
141
+ guard isContextual(path),
142
+ let components = URLComponents(string: path),
143
+ let raw = components.queryItems?.first(where: { $0.name == contextualIndexParam })?.value,
144
+ let index = Int(raw), index >= 0
145
+ else {
146
+ return nil
147
+ }
148
+ return index
149
+ }
150
+
128
151
  /// Appends query parameters to a URL, handling `?` vs `&` separator and percent-encoding.
129
152
  ///
130
153
  /// - Parameters:
@@ -193,27 +216,4 @@ enum BrowserPathHelper {
193
216
 
194
217
  return "\(normalizedBase)/\(normalizedPath)"
195
218
  }
196
-
197
- /// True if `segment` appears in `path` as a complete path segment — bounded on
198
- /// the left by the string start or `/`, and on the right by the string end or
199
- /// one of `/`, `?`, `#`.
200
- static func containsSegment(_ path: String, _ segment: String) -> Bool {
201
- guard !segment.isEmpty else { return false }
202
- var searchStart = path.startIndex
203
- while let range = path.range(of: segment, range: searchStart ..< path.endIndex) {
204
- let beforeOk =
205
- range.lowerBound == path.startIndex
206
- || path[path.index(before: range.lowerBound)] == "/"
207
- let afterOk: Bool
208
- if range.upperBound == path.endIndex {
209
- afterOk = true
210
- } else {
211
- let next = path[range.upperBound]
212
- afterOk = next == "/" || next == "?" || next == "#"
213
- }
214
- if beforeOk, afterOk { return true }
215
- searchStart = range.upperBound
216
- }
217
- return false
218
- }
219
219
  }
@@ -62,41 +62,125 @@ extension JsonArtwork {
62
62
  }
63
63
  }
64
64
 
65
- /// JSON model for image row items (horizontal thumbnail row).
66
- struct JsonImageRowItem: Codable {
67
- let id: String?
68
- let url: String?
69
- let src: String?
70
- let artwork: String?
71
- let title: String
72
- let artist: String?
73
- let album: String?
74
- let albumUrl: String?
75
- let live: Bool?
76
- let request: JsonTrackRequest?
65
+ /// A style declaration block on the wire (ADR 0011):
66
+ /// `{ display?, gridWrap?, artworkRendering? }`.
67
+ ///
68
+ /// One JSON model serves both the `TrackStyle` and `SectionStyle` positions —
69
+ /// keys that don't exist at the narrower position simply don't map.
70
+ ///
71
+ /// Decoding is tolerant by design: a stale payload — the retired string
72
+ /// styles, a wrong-typed field, an unknown enum value — must degrade to "no
73
+ /// declaration", never fail the page or the persisted playback state (the
74
+ /// decoders above this are strict, so a throw here would kill the whole
75
+ /// payload). `init(from:)` therefore never throws, and unknown enum values
76
+ /// map to nil at `toNitro` time.
77
+ struct JsonStyle: Codable, Equatable {
78
+ let display: String?
79
+ let gridWrap: Bool?
80
+ let artworkRendering: String?
77
81
 
78
82
  init(
79
- id: String? = nil,
80
- url: String? = nil,
81
- src: String? = nil,
82
- artwork: String? = nil,
83
- title: String,
84
- artist: String? = nil,
85
- album: String? = nil,
86
- albumUrl: String? = nil,
87
- live: Bool? = nil,
88
- request: JsonTrackRequest? = nil,
83
+ display: String? = nil,
84
+ gridWrap: Bool? = nil,
85
+ artworkRendering: String? = nil,
86
+ ) {
87
+ self.display = display
88
+ self.gridWrap = gridWrap
89
+ self.artworkRendering = artworkRendering
90
+ }
91
+
92
+ init(from decoder: Decoder) throws {
93
+ // A non-object value (the retired `style: 'grid'` string) decodes as an
94
+ // empty block rather than failing the parent.
95
+ guard let container = try? decoder.container(keyedBy: CodingKeys.self) else {
96
+ display = nil
97
+ gridWrap = nil
98
+ artworkRendering = nil
99
+ return
100
+ }
101
+ display = (try? container.decodeIfPresent(String.self, forKey: .display)) ?? nil
102
+ gridWrap = (try? container.decodeIfPresent(Bool.self, forKey: .gridWrap)) ?? nil
103
+ artworkRendering = (try? container.decodeIfPresent(String.self, forKey: .artworkRendering)) ?? nil
104
+ }
105
+ }
106
+
107
+ extension JsonStyle {
108
+ /// The wire strings map to Nitro enums here, tolerantly: an unknown value
109
+ /// (a future `display` mode, say) is no declaration, not an error. A block
110
+ /// that resolves to no declarations at all — `{}`, the tolerant decoder's
111
+ /// empty result for the retired string vocabulary, or all-unknown values —
112
+ /// maps to nil, so "no declaration" has one shape on every platform
113
+ /// (Android's lenient mapper makes the same collapse).
114
+ func toTrackStyle() -> TrackStyle? {
115
+ let display = display.flatMap { StyleDisplay(fromString: $0.lowercased()) }
116
+ let artworkRendering = artworkRendering.flatMap { ArtworkRendering(fromString: $0.lowercased()) }
117
+ guard display != nil || artworkRendering != nil else { return nil }
118
+ return TrackStyle(display: display, artworkRendering: artworkRendering)
119
+ }
120
+
121
+ func toSectionStyle() -> SectionStyle? {
122
+ let display = display.flatMap { StyleDisplay(fromString: $0.lowercased()) }
123
+ let artworkRendering = artworkRendering.flatMap { ArtworkRendering(fromString: $0.lowercased()) }
124
+ guard display != nil || artworkRendering != nil || gridWrap != nil else { return nil }
125
+ // Argument order is the generated init's (own properties before
126
+ // inherited ones — Nitro flattens `extends` own-props-first).
127
+ return SectionStyle(
128
+ gridWrap: gridWrap,
129
+ display: display,
130
+ artworkRendering: artworkRendering,
131
+ )
132
+ }
133
+
134
+ /// Snapshot a live track block back to its JSON model (for persistence).
135
+ /// An all-nil block snapshots as no block — the decoders collapse it to
136
+ /// "no declaration" anyway, so never persist the distinction.
137
+ init?(_ style: TrackStyle?) {
138
+ guard let style, style.display != nil || style.artworkRendering != nil else { return nil }
139
+ self.init(
140
+ display: Self.string(style.display),
141
+ artworkRendering: Self.string(style.artworkRendering),
142
+ )
143
+ }
144
+
145
+ private static func string(_ display: StyleDisplay?) -> String? {
146
+ switch display {
147
+ case .list: "list"
148
+ case .grid: "grid"
149
+ case nil: nil
150
+ }
151
+ }
152
+
153
+ private static func string(_ rendering: ArtworkRendering?) -> String? {
154
+ switch rendering {
155
+ case .original: "original"
156
+ case .stencil: "stencil"
157
+ case nil: nil
158
+ }
159
+ }
160
+ }
161
+
162
+ /// JSON model for a page section (ADR 0010). Legacy `groupTitle`/`imageRow`
163
+ /// keys in payloads are simply unknown to these models and decode as
164
+ /// ignored dead weight.
165
+ struct JsonSection: Codable {
166
+ let title: String?
167
+ let subtitle: String?
168
+ let style: JsonStyle?
169
+ let path: String?
170
+ let children: [JsonTrack]
171
+
172
+ init(
173
+ title: String? = nil,
174
+ subtitle: String? = nil,
175
+ style: JsonStyle? = nil,
176
+ path: String? = nil,
177
+ children: [JsonTrack],
89
178
  ) {
90
- self.id = id
91
- self.url = url
92
- self.src = src
93
- self.artwork = artwork
94
179
  self.title = title
95
- self.artist = artist
96
- self.album = album
97
- self.albumUrl = albumUrl
98
- self.live = live
99
- self.request = request
180
+ self.subtitle = subtitle
181
+ self.style = style
182
+ self.path = path
183
+ self.children = children
100
184
  }
101
185
  }
102
186
 
@@ -116,59 +200,59 @@ struct JsonTrackRequest: Codable {
116
200
  /// JSON model for resolved track (container with children).
117
201
  struct JsonResolvedTrack: Codable {
118
202
  let id: String?
119
- let url: String
203
+ let path: String
120
204
  let title: String
121
205
  let subtitle: String?
122
206
  let artwork: JsonArtwork?
123
207
  let artist: String?
124
- let albumUrl: String?
208
+ let albumPath: String?
125
209
  let album: String?
126
210
  let description: String?
127
211
  let genre: String?
128
212
  let duration: Double?
213
+ let sections: [JsonSection]?
129
214
  let children: [JsonTrack]?
130
215
  let src: String?
131
- let style: String?
132
- let childrenStyle: String?
133
- let groupTitle: String?
216
+ let style: JsonStyle?
217
+ let disabled: Bool?
134
218
  let live: Bool?
135
219
  let carPlaySiriListButton: String?
136
220
 
137
221
  init(
138
222
  id: String? = nil,
139
- url: String,
223
+ path: String,
140
224
  title: String,
141
225
  subtitle: String? = nil,
142
226
  artwork: JsonArtwork? = nil,
143
227
  artist: String? = nil,
144
- albumUrl: String? = nil,
228
+ albumPath: String? = nil,
145
229
  album: String? = nil,
146
230
  description: String? = nil,
147
231
  genre: String? = nil,
148
232
  duration: Double? = nil,
233
+ sections: [JsonSection]? = nil,
149
234
  children: [JsonTrack]? = nil,
150
235
  src: String? = nil,
151
- style: String? = nil,
152
- childrenStyle: String? = nil,
153
- groupTitle: String? = nil,
236
+ style: JsonStyle? = nil,
237
+ disabled: Bool? = nil,
154
238
  live: Bool? = nil,
155
239
  carPlaySiriListButton: String? = nil,
156
240
  ) {
157
- self.url = url
241
+ self.path = path
158
242
  self.title = title
159
243
  self.subtitle = subtitle
160
244
  self.artwork = artwork
161
245
  self.artist = artist
162
- self.albumUrl = albumUrl
246
+ self.albumPath = albumPath
163
247
  self.album = album
164
248
  self.description = description
165
249
  self.genre = genre
166
250
  self.duration = duration
251
+ self.sections = sections
167
252
  self.children = children
168
253
  self.src = src
169
254
  self.style = style
170
- self.childrenStyle = childrenStyle
171
- self.groupTitle = groupTitle
255
+ self.disabled = disabled
172
256
  self.live = live
173
257
  self.carPlaySiriListButton = carPlaySiriListButton
174
258
  self.id = id
@@ -178,50 +262,46 @@ struct JsonResolvedTrack: Codable {
178
262
  /// JSON model for individual tracks.
179
263
  struct JsonTrack: Codable {
180
264
  let id: String?
181
- let url: String?
265
+ let path: String?
182
266
  let title: String
183
267
  let subtitle: String?
184
268
  let artwork: JsonArtwork?
185
269
  let artist: String?
186
- let albumUrl: String?
270
+ let albumPath: String?
187
271
  let album: String?
188
272
  let description: String?
189
273
  let genre: String?
190
274
  let duration: Double?
191
275
  let src: String?
192
276
  let request: JsonTrackRequest?
193
- let style: String?
194
- let childrenStyle: String?
195
- let groupTitle: String?
277
+ let style: JsonStyle?
278
+ let disabled: Bool?
196
279
  let live: Bool?
197
- let imageRow: [JsonImageRowItem]?
198
280
 
199
281
  init(
200
282
  id: String? = nil,
201
- url: String? = nil,
283
+ path: String? = nil,
202
284
  title: String,
203
285
  subtitle: String? = nil,
204
286
  artwork: JsonArtwork? = nil,
205
287
  artist: String? = nil,
206
- albumUrl: String? = nil,
288
+ albumPath: String? = nil,
207
289
  album: String? = nil,
208
290
  description: String? = nil,
209
291
  genre: String? = nil,
210
292
  duration: Double? = nil,
211
293
  src: String? = nil,
212
294
  request: JsonTrackRequest? = nil,
213
- style: String? = nil,
214
- childrenStyle: String? = nil,
215
- groupTitle: String? = nil,
295
+ style: JsonStyle? = nil,
296
+ disabled: Bool? = nil,
216
297
  live: Bool? = nil,
217
- imageRow: [JsonImageRowItem]? = nil,
218
298
  ) {
219
- self.url = url
299
+ self.path = path
220
300
  self.title = title
221
301
  self.subtitle = subtitle
222
302
  self.artwork = artwork
223
303
  self.artist = artist
224
- self.albumUrl = albumUrl
304
+ self.albumPath = albumPath
225
305
  self.album = album
226
306
  self.description = description
227
307
  self.genre = genre
@@ -229,10 +309,8 @@ struct JsonTrack: Codable {
229
309
  self.src = src
230
310
  self.request = request
231
311
  self.style = style
232
- self.childrenStyle = childrenStyle
233
- self.groupTitle = groupTitle
312
+ self.disabled = disabled
234
313
  self.live = live
235
- self.imageRow = imageRow
236
314
  self.id = id
237
315
  }
238
316
  }
@@ -250,26 +328,27 @@ struct JsonTrack: Codable {
250
328
 
251
329
  extension JsonTrack {
252
330
  /// Snapshot the persistable subset of a live Track (inverse of `toNitro()`).
331
+ /// The track's own declared style block and `disabled` persist (ADR 0011);
332
+ /// section/page-inherited values are never stamped onto tracks and
333
+ /// re-resolve at render, exactly as they do live.
253
334
  init(from track: Track) {
254
335
  self.init(
255
336
  id: track.id,
256
- url: track.url,
337
+ path: track.path,
257
338
  title: track.title,
258
339
  subtitle: track.subtitle,
259
340
  artwork: JsonArtwork(track.artwork),
260
341
  artist: track.artist,
261
- albumUrl: track.albumUrl,
342
+ albumPath: track.albumPath,
262
343
  album: track.album,
263
344
  description: track.description,
264
345
  genre: track.genre,
265
346
  duration: track.duration,
266
347
  src: track.src,
267
348
  request: track.request.map(JsonTrackRequest.init(from:)),
268
- style: nil,
269
- childrenStyle: nil,
270
- groupTitle: track.groupTitle,
349
+ style: JsonStyle(track.style),
350
+ disabled: track.disabled,
271
351
  live: track.live,
272
- imageRow: nil,
273
352
  )
274
353
  }
275
354
  }
@@ -289,14 +368,16 @@ struct JsonTrack: Codable {
289
368
  init(from track: Track) {
290
369
  self.init(
291
370
  id: track.id,
292
- url: track.url,
371
+ path: track.path,
293
372
  title: track.title,
294
373
  artwork: JsonArtwork(track.artwork),
295
374
  artist: track.artist,
296
- albumUrl: track.albumUrl,
375
+ albumPath: track.albumPath,
297
376
  album: track.album,
298
377
  src: track.src,
299
378
  request: track.request.map(JsonTrackRequest.init(from:)),
379
+ style: JsonStyle(track.style),
380
+ disabled: track.disabled,
300
381
  live: track.live,
301
382
  )
302
383
  }
@@ -308,26 +389,14 @@ struct JsonTrack: Codable {
308
389
 
309
390
  #if canImport(NitroModules)
310
391
 
311
- private extension String {
312
- func toTrackStyle() -> TrackStyle? {
313
- TrackStyle(fromString: lowercased())
314
- }
315
- }
316
-
317
- extension JsonImageRowItem {
318
- func toNitro() -> ImageRowItem {
319
- ImageRowItem(
320
- id: id,
321
- url: url,
322
- src: src,
323
- artwork: artwork,
324
- artworkSource: nil,
392
+ extension JsonSection {
393
+ func toNitro() -> Section {
394
+ Section(
325
395
  title: title,
326
- artist: artist,
327
- album: album,
328
- albumUrl: albumUrl,
329
- live: live,
330
- request: request?.toNitro(),
396
+ subtitle: subtitle,
397
+ style: style?.toSectionStyle(),
398
+ path: path,
399
+ children: children.map { $0.toNitro() },
331
400
  )
332
401
  }
333
402
  }
@@ -335,28 +404,26 @@ struct JsonTrack: Codable {
335
404
  extension JsonResolvedTrack {
336
405
  func toNitro() -> ResolvedTrack {
337
406
  ResolvedTrack(
338
- url: url,
407
+ path: path,
408
+ style: style?.toSectionStyle(),
409
+ sections: sections?.map { $0.toNitro() },
339
410
  children: children?.map { $0.toNitro() },
340
411
  carPlaySiriListButton: carPlaySiriListButton.flatMap { CarPlaySiriListButtonPosition(fromString: $0) },
341
412
  id: id,
342
413
  src: src,
343
414
  artwork: artwork?.toNitro(),
344
415
  artworkSource: nil, request: nil,
345
- artworkCarPlayTinted: nil,
346
416
  title: title,
347
417
  subtitle: subtitle,
348
418
  artist: artist,
349
- albumUrl: albumUrl,
419
+ albumPath: albumPath,
350
420
  album: album,
351
421
  description: description,
352
422
  genre: genre,
353
423
  duration: duration,
354
- style: style?.toTrackStyle(),
355
- childrenStyle: childrenStyle?.toTrackStyle(),
424
+ disabled: disabled,
356
425
  favorited: nil,
357
- groupTitle: groupTitle,
358
426
  live: live,
359
- imageRow: nil,
360
427
  )
361
428
  }
362
429
  }
@@ -365,26 +432,23 @@ struct JsonTrack: Codable {
365
432
  func toNitro() -> Track {
366
433
  Track(
367
434
  id: id,
368
- url: url,
435
+ path: path,
369
436
  src: src,
370
437
  artwork: artwork?.toNitro(),
371
438
  artworkSource: nil,
372
439
  request: request?.toNitro(),
373
- artworkCarPlayTinted: nil,
374
440
  title: title,
375
441
  subtitle: subtitle,
376
442
  artist: artist,
377
- albumUrl: albumUrl,
443
+ albumPath: albumPath,
378
444
  album: album,
379
445
  description: description,
380
446
  genre: genre,
381
447
  duration: duration,
382
448
  style: style?.toTrackStyle(),
383
- childrenStyle: childrenStyle?.toTrackStyle(),
449
+ disabled: disabled,
384
450
  favorited: nil,
385
- groupTitle: groupTitle,
386
451
  live: live,
387
- imageRow: imageRow?.map { $0.toNitro() },
388
452
  )
389
453
  }
390
454
  }
@@ -396,15 +460,29 @@ struct JsonTrack: Codable {
396
460
  func toNitro() -> Track {
397
461
  Track(
398
462
  id: id ?? "",
399
- url: url,
463
+ path: path,
400
464
  src: src,
465
+ artwork: artwork?.toNitro(),
401
466
  request: request.map { TrackRequest(userAgent: $0.userAgent, headers: $0.headers, query: $0.query) },
402
467
  title: title,
403
468
  artist: artist,
404
- albumUrl: albumUrl,
469
+ albumPath: albumPath,
405
470
  album: album,
471
+ style: style?.toTrackStyle(),
472
+ disabled: disabled,
406
473
  live: live,
407
- artwork: artwork?.toNitro(),
474
+ )
475
+ }
476
+ }
477
+
478
+ extension JsonSection {
479
+ func toNitro() -> Section {
480
+ Section(
481
+ title: title,
482
+ subtitle: subtitle,
483
+ style: style?.toSectionStyle(),
484
+ path: path,
485
+ children: children.map { $0.toNitro() },
408
486
  )
409
487
  }
410
488
  }
@@ -412,10 +490,13 @@ struct JsonTrack: Codable {
412
490
  extension JsonResolvedTrack {
413
491
  func toNitro() -> ResolvedTrack {
414
492
  ResolvedTrack(
415
- url: url,
493
+ path: path,
494
+ style: style?.toSectionStyle(),
495
+ sections: sections?.map { $0.toNitro() },
416
496
  children: children?.map { $0.toNitro() },
417
497
  artwork: artwork?.toNitro(),
418
498
  title: title,
499
+ disabled: disabled,
419
500
  )
420
501
  }
421
502
  }