@5stones/react-native-audio-browser 0.1.0

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 (1065) hide show
  1. package/AudioBrowser.podspec +43 -0
  2. package/LICENSE +21 -0
  3. package/README.md +5 -0
  4. package/android/CMakeLists.txt +32 -0
  5. package/android/build.gradle +210 -0
  6. package/android/fix-prefab.gradle +51 -0
  7. package/android/gradle.properties +5 -0
  8. package/android/src/main/AndroidManifest.xml +36 -0
  9. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  10. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +1191 -0
  11. package/android/src/main/java/com/audiobrowser/AudioBrowserPackage.kt +21 -0
  12. package/android/src/main/java/com/audiobrowser/Callbacks.kt +92 -0
  13. package/android/src/main/java/com/audiobrowser/HeadlessTaskService.kt +59 -0
  14. package/android/src/main/java/com/audiobrowser/Service.kt +336 -0
  15. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +1169 -0
  16. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +103 -0
  17. package/android/src/main/java/com/audiobrowser/browser/RouteMatch.kt +9 -0
  18. package/android/src/main/java/com/audiobrowser/browser/SimpleRouter.kt +150 -0
  19. package/android/src/main/java/com/audiobrowser/extension/EnumExtensions.kt +5 -0
  20. package/android/src/main/java/com/audiobrowser/extension/NumberExt.kt +13 -0
  21. package/android/src/main/java/com/audiobrowser/http/HttpClient.kt +171 -0
  22. package/android/src/main/java/com/audiobrowser/http/RequestConfigBuilder.kt +251 -0
  23. package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +80 -0
  24. package/android/src/main/java/com/audiobrowser/model/PlayerSetupOptions.kt +101 -0
  25. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +125 -0
  26. package/android/src/main/java/com/audiobrowser/player/AutomaticBufferManager.kt +256 -0
  27. package/android/src/main/java/com/audiobrowser/player/DynamicLoadControl.kt +163 -0
  28. package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +177 -0
  29. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +580 -0
  30. package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +578 -0
  31. package/android/src/main/java/com/audiobrowser/player/PlaybackProgressUpdateManager.kt +58 -0
  32. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +245 -0
  33. package/android/src/main/java/com/audiobrowser/player/Player.kt +1509 -0
  34. package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +230 -0
  35. package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +191 -0
  36. package/android/src/main/java/com/audiobrowser/player/SleepTimer.kt +80 -0
  37. package/android/src/main/java/com/audiobrowser/util/AndroidAudioContentTypeFactory.kt +17 -0
  38. package/android/src/main/java/com/audiobrowser/util/BatteryOptimizationHelper.kt +95 -0
  39. package/android/src/main/java/com/audiobrowser/util/BatteryWarningStore.kt +38 -0
  40. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +155 -0
  41. package/android/src/main/java/com/audiobrowser/util/CoilBitmapLoader.kt +390 -0
  42. package/android/src/main/java/com/audiobrowser/util/EqualizerManager.kt +197 -0
  43. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +75 -0
  44. package/android/src/main/java/com/audiobrowser/util/MetadataAdapter.kt +97 -0
  45. package/android/src/main/java/com/audiobrowser/util/NetworkConnectivityMonitor.kt +121 -0
  46. package/android/src/main/java/com/audiobrowser/util/PlayingStateFactory.kt +25 -0
  47. package/android/src/main/java/com/audiobrowser/util/RatingFactory.kt +72 -0
  48. package/android/src/main/java/com/audiobrowser/util/RepeatModeFactory.kt +22 -0
  49. package/android/src/main/java/com/audiobrowser/util/ResolvedTrackFactory.kt +36 -0
  50. package/android/src/main/java/com/audiobrowser/util/SvgArtworkRenderer.kt +130 -0
  51. package/android/src/main/java/com/audiobrowser/util/SystemVolumeMonitor.kt +50 -0
  52. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +125 -0
  53. package/android/src/main/res/values/strings.xml +6 -0
  54. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +273 -0
  55. package/android/src/test/java/com/audiobrowser/browser/SimpleBrowserTest.kt +33 -0
  56. package/android/src/test/java/com/audiobrowser/browser/SimpleRouterTest.kt +156 -0
  57. package/android/src/test/java/com/audiobrowser/http/RequestConfigBuilderTest.kt +240 -0
  58. package/ios/Bridge.h +8 -0
  59. package/ios/Browser/BrowserConfig.swift +85 -0
  60. package/ios/Browser/BrowserManager.swift +985 -0
  61. package/ios/Browser/BrowserPathHelper.swift +156 -0
  62. package/ios/Browser/JsonModels.swift +165 -0
  63. package/ios/Browser/SimpleRouter.swift +184 -0
  64. package/ios/CarPlay/CarPlayController.swift +1342 -0
  65. package/ios/CarPlay/RNABCarPlaySceneDelegate.h +15 -0
  66. package/ios/CarPlay/RNABCarPlaySceneDelegate.m +59 -0
  67. package/ios/CarPlay/Track+CarPlay.swift +49 -0
  68. package/ios/Extension/Capability+RemoteCommand.swift +76 -0
  69. package/ios/Extension/ChapterMetadata+AVFoundation.swift +37 -0
  70. package/ios/Extension/TimedMetadata+AVFoundation.swift +126 -0
  71. package/ios/Extension/Track+AVPlayer.swift +68 -0
  72. package/ios/Extension/TrackMetadata+AVFoundation.swift +128 -0
  73. package/ios/Http/HttpClient.swift +237 -0
  74. package/ios/HybridAudioBrowser.swift +1309 -0
  75. package/ios/Model/MediaURL.swift +66 -0
  76. package/ios/Model/PlayerUpdateOptions.swift +116 -0
  77. package/ios/Model/RemoteCommand.swift +71 -0
  78. package/ios/Model/SourceType.swift +6 -0
  79. package/ios/Model/TrackPlayerError.swift +69 -0
  80. package/ios/NowPlayingInfo/MediaItemProperty.swift +78 -0
  81. package/ios/NowPlayingInfo/NowPlayingInfoCenter.swift +9 -0
  82. package/ios/NowPlayingInfo/NowPlayingInfoController.swift +283 -0
  83. package/ios/NowPlayingInfo/NowPlayingInfoKeyValue.swift +6 -0
  84. package/ios/NowPlayingInfo/NowPlayingInfoProperty.swift +220 -0
  85. package/ios/Observer/PlayerItemNotificationObserver.swift +83 -0
  86. package/ios/Observer/PlayerItemPropertyObserver.swift +110 -0
  87. package/ios/Observer/PlayerStateObserver.swift +52 -0
  88. package/ios/Observer/PlayerTimeObserver.swift +109 -0
  89. package/ios/Option/PitchAlgorithms.swift +17 -0
  90. package/ios/Option/SessionCategories.swift +100 -0
  91. package/ios/Option/TimeEventFrequency.swift +18 -0
  92. package/ios/Player/PlaybackProgressUpdateManager.swift +60 -0
  93. package/ios/Player/PlayingStateManager.swift +29 -0
  94. package/ios/Player/RetryManager.swift +281 -0
  95. package/ios/Player/ShuffleOrder.swift +191 -0
  96. package/ios/Player/SleepTimerManager.swift +137 -0
  97. package/ios/RemoteCommand/RemoteCommandController.swift +400 -0
  98. package/ios/Support/RNTrackPlayer-Bridging-Header.h +7 -0
  99. package/ios/TrackPlayer.swift +1590 -0
  100. package/ios/TrackPlayerCallbacks.swift +121 -0
  101. package/ios/Util/Emitter.swift +46 -0
  102. package/ios/Util/LRUCache.swift +182 -0
  103. package/ios/Util/MetadataAdapter.swift +131 -0
  104. package/ios/Util/NetworkMonitor.swift +68 -0
  105. package/ios/Util/OnceValue.swift +70 -0
  106. package/ios/Util/SVGProcessor.swift +56 -0
  107. package/lib/commonjs/AudioBrowser.js +50 -0
  108. package/lib/commonjs/AudioBrowser.js.map +1 -0
  109. package/lib/commonjs/features/battery.js +154 -0
  110. package/lib/commonjs/features/battery.js.map +1 -0
  111. package/lib/commonjs/features/browser.js +271 -0
  112. package/lib/commonjs/features/browser.js.map +1 -0
  113. package/lib/commonjs/features/equalizer.js +69 -0
  114. package/lib/commonjs/features/equalizer.js.map +1 -0
  115. package/lib/commonjs/features/errors.js +165 -0
  116. package/lib/commonjs/features/errors.js.map +1 -0
  117. package/lib/commonjs/features/favorites.js +101 -0
  118. package/lib/commonjs/features/favorites.js.map +1 -0
  119. package/lib/commonjs/features/index.js +160 -0
  120. package/lib/commonjs/features/index.js.map +1 -0
  121. package/lib/commonjs/features/metadata.js +54 -0
  122. package/lib/commonjs/features/metadata.js.map +1 -0
  123. package/lib/commonjs/features/network.js +40 -0
  124. package/lib/commonjs/features/network.js.map +1 -0
  125. package/lib/commonjs/features/nowPlaying.js +61 -0
  126. package/lib/commonjs/features/nowPlaying.js.map +1 -0
  127. package/lib/commonjs/features/output.js +54 -0
  128. package/lib/commonjs/features/output.js.map +1 -0
  129. package/lib/commonjs/features/playback/controls.js +79 -0
  130. package/lib/commonjs/features/playback/controls.js.map +1 -0
  131. package/lib/commonjs/features/playback/index.js +83 -0
  132. package/lib/commonjs/features/playback/index.js.map +1 -0
  133. package/lib/commonjs/features/playback/playWhenReady.js +55 -0
  134. package/lib/commonjs/features/playback/playWhenReady.js.map +1 -0
  135. package/lib/commonjs/features/playback/playing.js +41 -0
  136. package/lib/commonjs/features/playback/playing.js.map +1 -0
  137. package/lib/commonjs/features/playback/progress.js +104 -0
  138. package/lib/commonjs/features/playback/progress.js.map +1 -0
  139. package/lib/commonjs/features/playback/rate.js +29 -0
  140. package/lib/commonjs/features/playback/rate.js.map +1 -0
  141. package/lib/commonjs/features/playback/state.js +62 -0
  142. package/lib/commonjs/features/playback/state.js.map +1 -0
  143. package/lib/commonjs/features/playback/volume.js +68 -0
  144. package/lib/commonjs/features/playback/volume.js.map +1 -0
  145. package/lib/commonjs/features/player/index.js +28 -0
  146. package/lib/commonjs/features/player/index.js.map +1 -0
  147. package/lib/commonjs/features/player/options.js +182 -0
  148. package/lib/commonjs/features/player/options.js.map +1 -0
  149. package/lib/commonjs/features/player/setup.js +143 -0
  150. package/lib/commonjs/features/player/setup.js.map +1 -0
  151. package/lib/commonjs/features/queue/activeTrack.js +52 -0
  152. package/lib/commonjs/features/queue/activeTrack.js.map +1 -0
  153. package/lib/commonjs/features/queue/index.js +50 -0
  154. package/lib/commonjs/features/queue/index.js.map +1 -0
  155. package/lib/commonjs/features/queue/queue.js +177 -0
  156. package/lib/commonjs/features/queue/queue.js.map +1 -0
  157. package/lib/commonjs/features/queue/repeatMode.js +58 -0
  158. package/lib/commonjs/features/queue/repeatMode.js.map +1 -0
  159. package/lib/commonjs/features/queue/shuffle.js +57 -0
  160. package/lib/commonjs/features/queue/shuffle.js.map +1 -0
  161. package/lib/commonjs/features/rating.js +2 -0
  162. package/lib/commonjs/features/rating.js.map +1 -0
  163. package/lib/commonjs/features/remoteControls.js +286 -0
  164. package/lib/commonjs/features/remoteControls.js.map +1 -0
  165. package/lib/commonjs/features/sleepTimer.js +164 -0
  166. package/lib/commonjs/features/sleepTimer.js.map +1 -0
  167. package/lib/commonjs/index.js +22 -0
  168. package/lib/commonjs/index.js.map +1 -0
  169. package/lib/commonjs/native.js +9 -0
  170. package/lib/commonjs/native.js.map +1 -0
  171. package/lib/commonjs/native.web.js +10 -0
  172. package/lib/commonjs/native.web.js.map +1 -0
  173. package/lib/commonjs/package.json +1 -0
  174. package/lib/commonjs/specs/audio-browser.nitro.js +6 -0
  175. package/lib/commonjs/specs/audio-browser.nitro.js.map +1 -0
  176. package/lib/commonjs/types/browser-native.js +6 -0
  177. package/lib/commonjs/types/browser-native.js.map +1 -0
  178. package/lib/commonjs/types/browser-nodes.js +6 -0
  179. package/lib/commonjs/types/browser-nodes.js.map +1 -0
  180. package/lib/commonjs/types/browser.js +6 -0
  181. package/lib/commonjs/types/browser.js.map +1 -0
  182. package/lib/commonjs/types/index.js +28 -0
  183. package/lib/commonjs/types/index.js.map +1 -0
  184. package/lib/commonjs/types/player.js +2 -0
  185. package/lib/commonjs/types/player.js.map +1 -0
  186. package/lib/commonjs/utils/LazyNativeEmitter.js +58 -0
  187. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -0
  188. package/lib/commonjs/utils/NativeUpdatedValue.js +59 -0
  189. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -0
  190. package/lib/commonjs/utils/resolveAssetSource.js +11 -0
  191. package/lib/commonjs/utils/resolveAssetSource.js.map +1 -0
  192. package/lib/commonjs/utils/useDebug.js +221 -0
  193. package/lib/commonjs/utils/useDebug.js.map +1 -0
  194. package/lib/commonjs/utils/useNativeUpdatedValue.js +44 -0
  195. package/lib/commonjs/utils/useNativeUpdatedValue.js.map +1 -0
  196. package/lib/commonjs/web/NativeAudioBrowser.js +668 -0
  197. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -0
  198. package/lib/commonjs/web/SimpleRouter.js +144 -0
  199. package/lib/commonjs/web/SimpleRouter.js.map +1 -0
  200. package/lib/commonjs/web/TrackPlayer/Event.js +19 -0
  201. package/lib/commonjs/web/TrackPlayer/Event.js.map +1 -0
  202. package/lib/commonjs/web/TrackPlayer/Player.js +276 -0
  203. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -0
  204. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js +284 -0
  205. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js.map +1 -0
  206. package/lib/commonjs/web/TrackPlayer/RepeatMode.js +12 -0
  207. package/lib/commonjs/web/TrackPlayer/RepeatMode.js.map +1 -0
  208. package/lib/commonjs/web/TrackPlayer/SetupNotCalledError.js +13 -0
  209. package/lib/commonjs/web/TrackPlayer/SetupNotCalledError.js.map +1 -0
  210. package/lib/commonjs/web/TrackPlayer/SleepTimer.js +75 -0
  211. package/lib/commonjs/web/TrackPlayer/SleepTimer.js.map +1 -0
  212. package/lib/commonjs/web/TrackPlayer/State.js +18 -0
  213. package/lib/commonjs/web/TrackPlayer/State.js.map +1 -0
  214. package/lib/commonjs/web/TrackPlayer/index.js +52 -0
  215. package/lib/commonjs/web/TrackPlayer/index.js.map +1 -0
  216. package/lib/commonjs/web/browser/BrowserManager.js +594 -0
  217. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -0
  218. package/lib/commonjs/web/browser/FavoriteManager.js +80 -0
  219. package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -0
  220. package/lib/commonjs/web/browser/NavigationErrorManager.js +106 -0
  221. package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -0
  222. package/lib/commonjs/web/browser/SearchManager.js +80 -0
  223. package/lib/commonjs/web/browser/SearchManager.js.map +1 -0
  224. package/lib/commonjs/web/http/HttpClient.js +81 -0
  225. package/lib/commonjs/web/http/HttpClient.js.map +1 -0
  226. package/lib/commonjs/web/http/RequestConfigBuilder.js +294 -0
  227. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -0
  228. package/lib/commonjs/web/player/NowPlayingManager.js +70 -0
  229. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -0
  230. package/lib/commonjs/web/player/OptionsManager.js +88 -0
  231. package/lib/commonjs/web/player/OptionsManager.js.map +1 -0
  232. package/lib/commonjs/web/util/BrowserPathHelper.js +138 -0
  233. package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -0
  234. package/lib/commonjs/web/util/shuffle.js +21 -0
  235. package/lib/commonjs/web/util/shuffle.js.map +1 -0
  236. package/lib/module/AudioBrowser.js +7 -0
  237. package/lib/module/AudioBrowser.js.map +1 -0
  238. package/lib/module/features/battery.js +144 -0
  239. package/lib/module/features/battery.js.map +1 -0
  240. package/lib/module/features/browser.js +257 -0
  241. package/lib/module/features/browser.js.map +1 -0
  242. package/lib/module/features/equalizer.js +60 -0
  243. package/lib/module/features/equalizer.js.map +1 -0
  244. package/lib/module/features/errors.js +156 -0
  245. package/lib/module/features/errors.js.map +1 -0
  246. package/lib/module/features/favorites.js +95 -0
  247. package/lib/module/features/favorites.js.map +1 -0
  248. package/lib/module/features/index.js +26 -0
  249. package/lib/module/features/index.js.map +1 -0
  250. package/lib/module/features/metadata.js +51 -0
  251. package/lib/module/features/metadata.js.map +1 -0
  252. package/lib/module/features/network.js +35 -0
  253. package/lib/module/features/network.js.map +1 -0
  254. package/lib/module/features/nowPlaying.js +54 -0
  255. package/lib/module/features/nowPlaying.js.map +1 -0
  256. package/lib/module/features/output.js +47 -0
  257. package/lib/module/features/output.js.map +1 -0
  258. package/lib/module/features/playback/controls.js +69 -0
  259. package/lib/module/features/playback/controls.js.map +1 -0
  260. package/lib/module/features/playback/index.js +10 -0
  261. package/lib/module/features/playback/index.js.map +1 -0
  262. package/lib/module/features/playback/playWhenReady.js +49 -0
  263. package/lib/module/features/playback/playWhenReady.js.map +1 -0
  264. package/lib/module/features/playback/playing.js +36 -0
  265. package/lib/module/features/playback/playing.js.map +1 -0
  266. package/lib/module/features/playback/progress.js +98 -0
  267. package/lib/module/features/playback/progress.js.map +1 -0
  268. package/lib/module/features/playback/rate.js +25 -0
  269. package/lib/module/features/playback/rate.js.map +1 -0
  270. package/lib/module/features/playback/state.js +57 -0
  271. package/lib/module/features/playback/state.js.map +1 -0
  272. package/lib/module/features/playback/volume.js +60 -0
  273. package/lib/module/features/playback/volume.js.map +1 -0
  274. package/lib/module/features/player/index.js +5 -0
  275. package/lib/module/features/player/index.js.map +1 -0
  276. package/lib/module/features/player/options.js +176 -0
  277. package/lib/module/features/player/options.js.map +1 -0
  278. package/lib/module/features/player/setup.js +140 -0
  279. package/lib/module/features/player/setup.js.map +1 -0
  280. package/lib/module/features/queue/activeTrack.js +46 -0
  281. package/lib/module/features/queue/activeTrack.js.map +1 -0
  282. package/lib/module/features/queue/index.js +7 -0
  283. package/lib/module/features/queue/index.js.map +1 -0
  284. package/lib/module/features/queue/queue.js +162 -0
  285. package/lib/module/features/queue/queue.js.map +1 -0
  286. package/lib/module/features/queue/repeatMode.js +52 -0
  287. package/lib/module/features/queue/repeatMode.js.map +1 -0
  288. package/lib/module/features/queue/shuffle.js +50 -0
  289. package/lib/module/features/queue/shuffle.js.map +1 -0
  290. package/lib/module/features/rating.js +2 -0
  291. package/lib/module/features/rating.js.map +1 -0
  292. package/lib/module/features/remoteControls.js +275 -0
  293. package/lib/module/features/remoteControls.js.map +1 -0
  294. package/lib/module/features/sleepTimer.js +155 -0
  295. package/lib/module/features/sleepTimer.js.map +1 -0
  296. package/lib/module/index.js +6 -0
  297. package/lib/module/index.js.map +1 -0
  298. package/lib/module/native.js +5 -0
  299. package/lib/module/native.js.map +1 -0
  300. package/lib/module/native.web.js +7 -0
  301. package/lib/module/native.web.js.map +1 -0
  302. package/lib/module/package.json +1 -0
  303. package/lib/module/specs/audio-browser.nitro.js +4 -0
  304. package/lib/module/specs/audio-browser.nitro.js.map +1 -0
  305. package/lib/module/types/browser-native.js +4 -0
  306. package/lib/module/types/browser-native.js.map +1 -0
  307. package/lib/module/types/browser-nodes.js +4 -0
  308. package/lib/module/types/browser-nodes.js.map +1 -0
  309. package/lib/module/types/browser.js +4 -0
  310. package/lib/module/types/browser.js.map +1 -0
  311. package/lib/module/types/index.js +5 -0
  312. package/lib/module/types/index.js.map +1 -0
  313. package/lib/module/types/player.js +2 -0
  314. package/lib/module/types/player.js.map +1 -0
  315. package/lib/module/utils/LazyNativeEmitter.js +53 -0
  316. package/lib/module/utils/LazyNativeEmitter.js.map +1 -0
  317. package/lib/module/utils/NativeUpdatedValue.js +54 -0
  318. package/lib/module/utils/NativeUpdatedValue.js.map +1 -0
  319. package/lib/module/utils/resolveAssetSource.js +7 -0
  320. package/lib/module/utils/resolveAssetSource.js.map +1 -0
  321. package/lib/module/utils/useDebug.js +218 -0
  322. package/lib/module/utils/useDebug.js.map +1 -0
  323. package/lib/module/utils/useNativeUpdatedValue.js +40 -0
  324. package/lib/module/utils/useNativeUpdatedValue.js.map +1 -0
  325. package/lib/module/web/NativeAudioBrowser.js +664 -0
  326. package/lib/module/web/NativeAudioBrowser.js.map +1 -0
  327. package/lib/module/web/SimpleRouter.js +139 -0
  328. package/lib/module/web/SimpleRouter.js.map +1 -0
  329. package/lib/module/web/TrackPlayer/Event.js +15 -0
  330. package/lib/module/web/TrackPlayer/Event.js.map +1 -0
  331. package/lib/module/web/TrackPlayer/Player.js +271 -0
  332. package/lib/module/web/TrackPlayer/Player.js.map +1 -0
  333. package/lib/module/web/TrackPlayer/PlaylistPlayer.js +279 -0
  334. package/lib/module/web/TrackPlayer/PlaylistPlayer.js.map +1 -0
  335. package/lib/module/web/TrackPlayer/RepeatMode.js +8 -0
  336. package/lib/module/web/TrackPlayer/RepeatMode.js.map +1 -0
  337. package/lib/module/web/TrackPlayer/SetupNotCalledError.js +8 -0
  338. package/lib/module/web/TrackPlayer/SetupNotCalledError.js.map +1 -0
  339. package/lib/module/web/TrackPlayer/SleepTimer.js +70 -0
  340. package/lib/module/web/TrackPlayer/SleepTimer.js.map +1 -0
  341. package/lib/module/web/TrackPlayer/State.js +14 -0
  342. package/lib/module/web/TrackPlayer/State.js.map +1 -0
  343. package/lib/module/web/TrackPlayer/index.js +7 -0
  344. package/lib/module/web/TrackPlayer/index.js.map +1 -0
  345. package/lib/module/web/browser/BrowserManager.js +590 -0
  346. package/lib/module/web/browser/BrowserManager.js.map +1 -0
  347. package/lib/module/web/browser/FavoriteManager.js +75 -0
  348. package/lib/module/web/browser/FavoriteManager.js.map +1 -0
  349. package/lib/module/web/browser/NavigationErrorManager.js +101 -0
  350. package/lib/module/web/browser/NavigationErrorManager.js.map +1 -0
  351. package/lib/module/web/browser/SearchManager.js +76 -0
  352. package/lib/module/web/browser/SearchManager.js.map +1 -0
  353. package/lib/module/web/http/HttpClient.js +76 -0
  354. package/lib/module/web/http/HttpClient.js.map +1 -0
  355. package/lib/module/web/http/RequestConfigBuilder.js +291 -0
  356. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -0
  357. package/lib/module/web/player/NowPlayingManager.js +65 -0
  358. package/lib/module/web/player/NowPlayingManager.js.map +1 -0
  359. package/lib/module/web/player/OptionsManager.js +83 -0
  360. package/lib/module/web/player/OptionsManager.js.map +1 -0
  361. package/lib/module/web/util/BrowserPathHelper.js +134 -0
  362. package/lib/module/web/util/BrowserPathHelper.js.map +1 -0
  363. package/lib/module/web/util/shuffle.js +17 -0
  364. package/lib/module/web/util/shuffle.js.map +1 -0
  365. package/lib/typescript/src/AudioBrowser.d.ts +5 -0
  366. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -0
  367. package/lib/typescript/src/features/battery.d.ts +112 -0
  368. package/lib/typescript/src/features/battery.d.ts.map +1 -0
  369. package/lib/typescript/src/features/browser.d.ts +68 -0
  370. package/lib/typescript/src/features/browser.d.ts.map +1 -0
  371. package/lib/typescript/src/features/equalizer.d.ts +35 -0
  372. package/lib/typescript/src/features/equalizer.d.ts.map +1 -0
  373. package/lib/typescript/src/features/errors.d.ts +136 -0
  374. package/lib/typescript/src/features/errors.d.ts.map +1 -0
  375. package/lib/typescript/src/features/favorites.d.ts +84 -0
  376. package/lib/typescript/src/features/favorites.d.ts.map +1 -0
  377. package/lib/typescript/src/features/index.d.ts +23 -0
  378. package/lib/typescript/src/features/index.d.ts.map +1 -0
  379. package/lib/typescript/src/features/metadata.d.ts +103 -0
  380. package/lib/typescript/src/features/metadata.d.ts.map +1 -0
  381. package/lib/typescript/src/features/network.d.ts +18 -0
  382. package/lib/typescript/src/features/network.d.ts.map +1 -0
  383. package/lib/typescript/src/features/nowPlaying.d.ts +36 -0
  384. package/lib/typescript/src/features/nowPlaying.d.ts.map +1 -0
  385. package/lib/typescript/src/features/output.d.ts +28 -0
  386. package/lib/typescript/src/features/output.d.ts.map +1 -0
  387. package/lib/typescript/src/features/playback/controls.d.ts +40 -0
  388. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -0
  389. package/lib/typescript/src/features/playback/index.d.ts +8 -0
  390. package/lib/typescript/src/features/playback/index.d.ts.map +1 -0
  391. package/lib/typescript/src/features/playback/playWhenReady.d.ts +30 -0
  392. package/lib/typescript/src/features/playback/playWhenReady.d.ts.map +1 -0
  393. package/lib/typescript/src/features/playback/playing.d.ts +21 -0
  394. package/lib/typescript/src/features/playback/playing.d.ts.map +1 -0
  395. package/lib/typescript/src/features/playback/progress.d.ts +51 -0
  396. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -0
  397. package/lib/typescript/src/features/playback/rate.d.ts +12 -0
  398. package/lib/typescript/src/features/playback/rate.d.ts.map +1 -0
  399. package/lib/typescript/src/features/playback/state.d.ts +43 -0
  400. package/lib/typescript/src/features/playback/state.d.ts.map +1 -0
  401. package/lib/typescript/src/features/playback/volume.d.ts +32 -0
  402. package/lib/typescript/src/features/playback/volume.d.ts.map +1 -0
  403. package/lib/typescript/src/features/player/index.d.ts +3 -0
  404. package/lib/typescript/src/features/player/index.d.ts.map +1 -0
  405. package/lib/typescript/src/features/player/options.d.ts +413 -0
  406. package/lib/typescript/src/features/player/options.d.ts.map +1 -0
  407. package/lib/typescript/src/features/player/setup.d.ts +471 -0
  408. package/lib/typescript/src/features/player/setup.d.ts.map +1 -0
  409. package/lib/typescript/src/features/queue/activeTrack.d.ts +38 -0
  410. package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -0
  411. package/lib/typescript/src/features/queue/index.d.ts +5 -0
  412. package/lib/typescript/src/features/queue/index.d.ts.map +1 -0
  413. package/lib/typescript/src/features/queue/queue.d.ts +111 -0
  414. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -0
  415. package/lib/typescript/src/features/queue/repeatMode.d.ts +34 -0
  416. package/lib/typescript/src/features/queue/repeatMode.d.ts.map +1 -0
  417. package/lib/typescript/src/features/queue/shuffle.d.ts +27 -0
  418. package/lib/typescript/src/features/queue/shuffle.d.ts.map +1 -0
  419. package/lib/typescript/src/features/rating.d.ts +14 -0
  420. package/lib/typescript/src/features/rating.d.ts.map +1 -0
  421. package/lib/typescript/src/features/remoteControls.d.ts +164 -0
  422. package/lib/typescript/src/features/remoteControls.d.ts.map +1 -0
  423. package/lib/typescript/src/features/sleepTimer.d.ts +80 -0
  424. package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -0
  425. package/lib/typescript/src/index.d.ts +4 -0
  426. package/lib/typescript/src/index.d.ts.map +1 -0
  427. package/lib/typescript/src/native.d.ts +3 -0
  428. package/lib/typescript/src/native.d.ts.map +1 -0
  429. package/lib/typescript/src/native.web.d.ts +3 -0
  430. package/lib/typescript/src/native.web.d.ts.map +1 -0
  431. package/lib/typescript/src/specs/audio-browser.nitro.d.ts +242 -0
  432. package/lib/typescript/src/specs/audio-browser.nitro.d.ts.map +1 -0
  433. package/lib/typescript/src/types/browser-native.d.ts +39 -0
  434. package/lib/typescript/src/types/browser-native.d.ts.map +1 -0
  435. package/lib/typescript/src/types/browser-nodes.d.ts +153 -0
  436. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -0
  437. package/lib/typescript/src/types/browser.d.ts +710 -0
  438. package/lib/typescript/src/types/browser.d.ts.map +1 -0
  439. package/lib/typescript/src/types/index.d.ts +3 -0
  440. package/lib/typescript/src/types/index.d.ts.map +1 -0
  441. package/lib/typescript/src/types/player.d.ts +64 -0
  442. package/lib/typescript/src/types/player.d.ts.map +1 -0
  443. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +35 -0
  444. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -0
  445. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +39 -0
  446. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -0
  447. package/lib/typescript/src/utils/resolveAssetSource.d.ts +4 -0
  448. package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -0
  449. package/lib/typescript/src/utils/useDebug.d.ts +65 -0
  450. package/lib/typescript/src/utils/useDebug.d.ts.map +1 -0
  451. package/lib/typescript/src/utils/useNativeUpdatedValue.d.ts +14 -0
  452. package/lib/typescript/src/utils/useNativeUpdatedValue.d.ts.map +1 -0
  453. package/lib/typescript/src/web/NativeAudioBrowser.d.ts +176 -0
  454. package/lib/typescript/src/web/NativeAudioBrowser.d.ts.map +1 -0
  455. package/lib/typescript/src/web/SimpleRouter.d.ts +40 -0
  456. package/lib/typescript/src/web/SimpleRouter.d.ts.map +1 -0
  457. package/lib/typescript/src/web/TrackPlayer/Event.d.ts +11 -0
  458. package/lib/typescript/src/web/TrackPlayer/Event.d.ts.map +1 -0
  459. package/lib/typescript/src/web/TrackPlayer/Player.d.ts +71 -0
  460. package/lib/typescript/src/web/TrackPlayer/Player.d.ts.map +1 -0
  461. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts +39 -0
  462. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts.map +1 -0
  463. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts +6 -0
  464. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts.map +1 -0
  465. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts +4 -0
  466. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts.map +1 -0
  467. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts +34 -0
  468. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts.map +1 -0
  469. package/lib/typescript/src/web/TrackPlayer/State.d.ts +13 -0
  470. package/lib/typescript/src/web/TrackPlayer/State.d.ts.map +1 -0
  471. package/lib/typescript/src/web/TrackPlayer/index.d.ts +5 -0
  472. package/lib/typescript/src/web/TrackPlayer/index.d.ts.map +1 -0
  473. package/lib/typescript/src/web/browser/BrowserManager.d.ts +154 -0
  474. package/lib/typescript/src/web/browser/BrowserManager.d.ts.map +1 -0
  475. package/lib/typescript/src/web/browser/FavoriteManager.d.ts +42 -0
  476. package/lib/typescript/src/web/browser/FavoriteManager.d.ts.map +1 -0
  477. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts +40 -0
  478. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts.map +1 -0
  479. package/lib/typescript/src/web/browser/SearchManager.d.ts +22 -0
  480. package/lib/typescript/src/web/browser/SearchManager.d.ts.map +1 -0
  481. package/lib/typescript/src/web/http/HttpClient.d.ts +25 -0
  482. package/lib/typescript/src/web/http/HttpClient.d.ts.map +1 -0
  483. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts +98 -0
  484. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts.map +1 -0
  485. package/lib/typescript/src/web/player/NowPlayingManager.d.ts +34 -0
  486. package/lib/typescript/src/web/player/NowPlayingManager.d.ts.map +1 -0
  487. package/lib/typescript/src/web/player/OptionsManager.d.ts +32 -0
  488. package/lib/typescript/src/web/player/OptionsManager.d.ts.map +1 -0
  489. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts +69 -0
  490. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts.map +1 -0
  491. package/lib/typescript/src/web/util/shuffle.d.ts +8 -0
  492. package/lib/typescript/src/web/util/shuffle.d.ts.map +1 -0
  493. package/nitro.json +24 -0
  494. package/nitrogen/generated/.gitattributes +1 -0
  495. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +88 -0
  496. package/nitrogen/generated/android/AudioBrowser+autolinking.gradle +27 -0
  497. package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +124 -0
  498. package/nitrogen/generated/android/AudioBrowserOnLoad.hpp +25 -0
  499. package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +68 -0
  500. package/nitrogen/generated/android/c++/JAndroidAudioOffloadSettings.hpp +61 -0
  501. package/nitrogen/generated/android/c++/JAndroidOptions.hpp +86 -0
  502. package/nitrogen/generated/android/c++/JAndroidPlayerWakeMode.hpp +62 -0
  503. package/nitrogen/generated/android/c++/JAndroidUpdateOptions.hpp +86 -0
  504. package/nitrogen/generated/android/c++/JAppKilledPlaybackBehavior.hpp +62 -0
  505. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +163 -0
  506. package/nitrogen/generated/android/c++/JBatteryOptimizationStatus.hpp +62 -0
  507. package/nitrogen/generated/android/c++/JBatteryOptimizationStatusChangedEvent.hpp +58 -0
  508. package/nitrogen/generated/android/c++/JBatteryWarningPendingChangedEvent.hpp +57 -0
  509. package/nitrogen/generated/android/c++/JBrowseError.hpp +57 -0
  510. package/nitrogen/generated/android/c++/JBrowseResult.cpp +26 -0
  511. package/nitrogen/generated/android/c++/JBrowseResult.hpp +83 -0
  512. package/nitrogen/generated/android/c++/JBrowserSourceCallbackParam.hpp +76 -0
  513. package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +65 -0
  514. package/nitrogen/generated/android/c++/JChapterMetadata.hpp +70 -0
  515. package/nitrogen/generated/android/c++/JEqualizerSettings.hpp +125 -0
  516. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +71 -0
  517. package/nitrogen/generated/android/c++/JFormatNavigationErrorParams.hpp +72 -0
  518. package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +61 -0
  519. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_FormattedNavigationError____FormatNavigationErrorParams.hpp +111 -0
  520. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____MediaTransformParams.hpp +134 -0
  521. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +143 -0
  522. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +136 -0
  523. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.hpp +143 -0
  524. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +157 -0
  525. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  526. package/nitrogen/generated/android/c++/JFunc_void_BatteryOptimizationStatusChangedEvent.hpp +79 -0
  527. package/nitrogen/generated/android/c++/JFunc_void_BatteryWarningPendingChangedEvent.hpp +77 -0
  528. package/nitrogen/generated/android/c++/JFunc_void_EqualizerSettings.hpp +80 -0
  529. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +88 -0
  530. package/nitrogen/generated/android/c++/JFunc_void_IosOutput.hpp +80 -0
  531. package/nitrogen/generated/android/c++/JFunc_void_NavigationErrorEvent.hpp +83 -0
  532. package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +89 -0
  533. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +98 -0
  534. package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +83 -0
  535. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +88 -0
  536. package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +81 -0
  537. package/nitrogen/generated/android/c++/JFunc_void_PlaybackPlayWhenReadyChangedEvent.hpp +77 -0
  538. package/nitrogen/generated/android/c++/JFunc_void_PlaybackProgressUpdatedEvent.hpp +77 -0
  539. package/nitrogen/generated/android/c++/JFunc_void_PlaybackQueueEndedEvent.hpp +77 -0
  540. package/nitrogen/generated/android/c++/JFunc_void_PlayingState.hpp +77 -0
  541. package/nitrogen/generated/android/c++/JFunc_void_RemoteJumpBackwardEvent.hpp +77 -0
  542. package/nitrogen/generated/android/c++/JFunc_void_RemoteJumpForwardEvent.hpp +77 -0
  543. package/nitrogen/generated/android/c++/JFunc_void_RemotePlayIdEvent.hpp +79 -0
  544. package/nitrogen/generated/android/c++/JFunc_void_RemotePlaySearchEvent.hpp +78 -0
  545. package/nitrogen/generated/android/c++/JFunc_void_RemoteSeekEvent.hpp +77 -0
  546. package/nitrogen/generated/android/c++/JFunc_void_RemoteSetRatingEvent.hpp +87 -0
  547. package/nitrogen/generated/android/c++/JFunc_void_RemoteSkipEvent.hpp +77 -0
  548. package/nitrogen/generated/android/c++/JFunc_void_RepeatModeChangedEvent.hpp +79 -0
  549. package/nitrogen/generated/android/c++/JFunc_void_TimedMetadata.hpp +79 -0
  550. package/nitrogen/generated/android/c++/JFunc_void_TrackMetadata.hpp +79 -0
  551. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +75 -0
  552. package/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
  553. package/nitrogen/generated/android/c++/JFunc_void_std__optional_FormattedNavigationError_.hpp +79 -0
  554. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +89 -0
  555. package/nitrogen/generated/android/c++/JFunc_void_std__optional_std__variant_nitro__NullType__SleepTimerTime__SleepTimerEndOfTrack__.hpp +84 -0
  556. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  557. package/nitrogen/generated/android/c++/JFunc_void_std__vector_ChapterMetadata_.hpp +98 -0
  558. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +105 -0
  559. package/nitrogen/generated/android/c++/JHeartRating.hpp +57 -0
  560. package/nitrogen/generated/android/c++/JHttpMethod.hpp +74 -0
  561. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +1811 -0
  562. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +250 -0
  563. package/nitrogen/generated/android/c++/JIOSCategory.hpp +71 -0
  564. package/nitrogen/generated/android/c++/JIOSCategoryMode.hpp +80 -0
  565. package/nitrogen/generated/android/c++/JIOSCategoryOptions.hpp +74 -0
  566. package/nitrogen/generated/android/c++/JIOSCategoryPolicy.hpp +62 -0
  567. package/nitrogen/generated/android/c++/JImageContext.hpp +61 -0
  568. package/nitrogen/generated/android/c++/JImageQueryParams.hpp +62 -0
  569. package/nitrogen/generated/android/c++/JImageSource.hpp +86 -0
  570. package/nitrogen/generated/android/c++/JIosOutput.hpp +67 -0
  571. package/nitrogen/generated/android/c++/JIosOutputType.hpp +86 -0
  572. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +153 -0
  573. package/nitrogen/generated/android/c++/JMediaTransformParams.hpp +69 -0
  574. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +196 -0
  575. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +147 -0
  576. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +105 -0
  577. package/nitrogen/generated/android/c++/JNavigationError.hpp +72 -0
  578. package/nitrogen/generated/android/c++/JNavigationErrorEvent.hpp +62 -0
  579. package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +68 -0
  580. package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +86 -0
  581. package/nitrogen/generated/android/c++/JNotificationButton.hpp +68 -0
  582. package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +94 -0
  583. package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +104 -0
  584. package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +62 -0
  585. package/nitrogen/generated/android/c++/JOptions.hpp +97 -0
  586. package/nitrogen/generated/android/c++/JPartialAndroidSetupPlayerOptions.hpp +104 -0
  587. package/nitrogen/generated/android/c++/JPartialIOSSetupPlayerOptions.hpp +100 -0
  588. package/nitrogen/generated/android/c++/JPartialSetupPlayerOptions.hpp +96 -0
  589. package/nitrogen/generated/android/c++/JPercentageRating.hpp +57 -0
  590. package/nitrogen/generated/android/c++/JPlayback.hpp +66 -0
  591. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +83 -0
  592. package/nitrogen/generated/android/c++/JPlaybackError.hpp +61 -0
  593. package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +60 -0
  594. package/nitrogen/generated/android/c++/JPlaybackPlayWhenReadyChangedEvent.hpp +57 -0
  595. package/nitrogen/generated/android/c++/JPlaybackProgressUpdatedEvent.hpp +69 -0
  596. package/nitrogen/generated/android/c++/JPlaybackQueueEndedEvent.hpp +61 -0
  597. package/nitrogen/generated/android/c++/JPlaybackState.hpp +80 -0
  598. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +101 -0
  599. package/nitrogen/generated/android/c++/JPlayingState.hpp +61 -0
  600. package/nitrogen/generated/android/c++/JProgress.hpp +65 -0
  601. package/nitrogen/generated/android/c++/JRatingType.hpp +74 -0
  602. package/nitrogen/generated/android/c++/JRemoteJumpBackwardEvent.hpp +57 -0
  603. package/nitrogen/generated/android/c++/JRemoteJumpForwardEvent.hpp +57 -0
  604. package/nitrogen/generated/android/c++/JRemotePlayIdEvent.hpp +62 -0
  605. package/nitrogen/generated/android/c++/JRemotePlaySearchEvent.hpp +57 -0
  606. package/nitrogen/generated/android/c++/JRemoteSeekEvent.hpp +57 -0
  607. package/nitrogen/generated/android/c++/JRemoteSetRatingEvent.hpp +66 -0
  608. package/nitrogen/generated/android/c++/JRemoteSkipEvent.hpp +57 -0
  609. package/nitrogen/generated/android/c++/JRepeatMode.hpp +62 -0
  610. package/nitrogen/generated/android/c++/JRepeatModeChangedEvent.hpp +58 -0
  611. package/nitrogen/generated/android/c++/JRequestConfig.hpp +115 -0
  612. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +154 -0
  613. package/nitrogen/generated/android/c++/JRetryConfig.hpp +61 -0
  614. package/nitrogen/generated/android/c++/JSearchMode.hpp +71 -0
  615. package/nitrogen/generated/android/c++/JSearchParams.hpp +84 -0
  616. package/nitrogen/generated/android/c++/JSleepTimer.cpp +30 -0
  617. package/nitrogen/generated/android/c++/JSleepTimer.hpp +88 -0
  618. package/nitrogen/generated/android/c++/JSleepTimerEndOfTrack.hpp +57 -0
  619. package/nitrogen/generated/android/c++/JSleepTimerTime.hpp +57 -0
  620. package/nitrogen/generated/android/c++/JStarRating.hpp +57 -0
  621. package/nitrogen/generated/android/c++/JThumbsRating.hpp +57 -0
  622. package/nitrogen/generated/android/c++/JTimedMetadata.hpp +74 -0
  623. package/nitrogen/generated/android/c++/JTrack.hpp +129 -0
  624. package/nitrogen/generated/android/c++/JTrackMetadata.hpp +118 -0
  625. package/nitrogen/generated/android/c++/JTrackStyle.hpp +59 -0
  626. package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +134 -0
  627. package/nitrogen/generated/android/c++/JUpdateOptions.hpp +105 -0
  628. package/nitrogen/generated/android/c++/JVariant_Boolean_AndroidAudioOffloadSettings.cpp +26 -0
  629. package/nitrogen/generated/android/c++/JVariant_Boolean_AndroidAudioOffloadSettings.hpp +70 -0
  630. package/nitrogen/generated/android/c++/JVariant_Boolean_RetryConfig.cpp +26 -0
  631. package/nitrogen/generated/android/c++/JVariant_Boolean_RetryConfig.hpp +70 -0
  632. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.cpp +34 -0
  633. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.hpp +105 -0
  634. package/nitrogen/generated/android/c++/JVariant_NullType_Double.cpp +26 -0
  635. package/nitrogen/generated/android/c++/JVariant_NullType_Double.hpp +69 -0
  636. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +26 -0
  637. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +75 -0
  638. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioContentType.kt +24 -0
  639. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioOffloadSettings.kt +41 -0
  640. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +50 -0
  641. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidPlayerWakeMode.kt +22 -0
  642. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidUpdateOptions.kt +50 -0
  643. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AppKilledPlaybackBehavior.kt +22 -0
  644. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkRequestConfig.kt +72 -0
  645. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AudioBrowserOnLoad.kt +35 -0
  646. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BatteryOptimizationStatus.kt +22 -0
  647. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BatteryOptimizationStatusChangedEvent.kt +38 -0
  648. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BatteryWarningPendingChangedEvent.kt +38 -0
  649. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BrowseError.kt +38 -0
  650. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BrowseResult.kt +59 -0
  651. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/BrowserSourceCallbackParam.kt +41 -0
  652. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayNowPlayingButton.kt +23 -0
  653. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ChapterMetadata.kt +47 -0
  654. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/EqualizerSettings.kt +59 -0
  655. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FavoriteChangedEvent.kt +41 -0
  656. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNavigationErrorParams.kt +44 -0
  657. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +41 -0
  658. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_FormattedNavigationError____FormatNavigationErrorParams.kt +80 -0
  659. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____MediaTransformParams.kt +80 -0
  660. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____RequestConfig_std__optional_std__unordered_map_std__string__std__string__.kt +80 -0
  661. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.kt +80 -0
  662. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.kt +80 -0
  663. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.kt +80 -0
  664. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void.kt +80 -0
  665. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_BatteryOptimizationStatusChangedEvent.kt +80 -0
  666. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_BatteryWarningPendingChangedEvent.kt +80 -0
  667. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_EqualizerSettings.kt +80 -0
  668. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_FavoriteChangedEvent.kt +80 -0
  669. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_IosOutput.kt +80 -0
  670. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_NavigationErrorEvent.kt +80 -0
  671. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_NowPlayingMetadata.kt +80 -0
  672. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_Options.kt +80 -0
  673. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_Playback.kt +80 -0
  674. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackActiveTrackChangedEvent.kt +80 -0
  675. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackErrorEvent.kt +80 -0
  676. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackPlayWhenReadyChangedEvent.kt +80 -0
  677. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackProgressUpdatedEvent.kt +80 -0
  678. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlaybackQueueEndedEvent.kt +80 -0
  679. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_PlayingState.kt +80 -0
  680. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteJumpBackwardEvent.kt +80 -0
  681. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteJumpForwardEvent.kt +80 -0
  682. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemotePlayIdEvent.kt +80 -0
  683. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemotePlaySearchEvent.kt +80 -0
  684. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteSeekEvent.kt +80 -0
  685. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteSetRatingEvent.kt +80 -0
  686. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RemoteSkipEvent.kt +80 -0
  687. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_RepeatModeChangedEvent.kt +80 -0
  688. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_TimedMetadata.kt +80 -0
  689. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_TrackMetadata.kt +80 -0
  690. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +80 -0
  691. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_double.kt +80 -0
  692. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__optional_FormattedNavigationError_.kt +80 -0
  693. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__optional_ResolvedTrack_.kt +80 -0
  694. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__optional_std__variant_nitro__NullType__SleepTimerTime__SleepTimerEndOfTrack__.kt +80 -0
  695. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__string.kt +80 -0
  696. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__vector_ChapterMetadata_.kt +80 -0
  697. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_std__vector_Track_.kt +80 -0
  698. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HeartRating.kt +38 -0
  699. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HttpMethod.kt +26 -0
  700. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +1137 -0
  701. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSCategory.kt +25 -0
  702. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSCategoryMode.kt +28 -0
  703. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSCategoryOptions.kt +26 -0
  704. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSCategoryPolicy.kt +22 -0
  705. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageContext.kt +41 -0
  706. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageQueryParams.kt +41 -0
  707. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageSource.kt +47 -0
  708. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IosOutput.kt +44 -0
  709. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IosOutputType.kt +30 -0
  710. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +69 -0
  711. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaTransformParams.kt +41 -0
  712. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +69 -0
  713. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +63 -0
  714. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +53 -0
  715. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationError.kt +47 -0
  716. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorEvent.kt +38 -0
  717. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorType.kt +24 -0
  718. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +50 -0
  719. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButton.kt +24 -0
  720. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +50 -0
  721. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +65 -0
  722. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +41 -0
  723. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +53 -0
  724. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialAndroidSetupPlayerOptions.kt +65 -0
  725. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialIOSSetupPlayerOptions.kt +50 -0
  726. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialSetupPlayerOptions.kt +47 -0
  727. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PercentageRating.kt +38 -0
  728. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Playback.kt +41 -0
  729. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackActiveTrackChangedEvent.kt +50 -0
  730. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +41 -0
  731. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorEvent.kt +38 -0
  732. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackPlayWhenReadyChangedEvent.kt +38 -0
  733. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackProgressUpdatedEvent.kt +47 -0
  734. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackQueueEndedEvent.kt +41 -0
  735. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackState.kt +28 -0
  736. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +71 -0
  737. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayingState.kt +41 -0
  738. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Progress.kt +44 -0
  739. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RatingType.kt +26 -0
  740. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteJumpBackwardEvent.kt +38 -0
  741. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteJumpForwardEvent.kt +38 -0
  742. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemotePlayIdEvent.kt +41 -0
  743. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemotePlaySearchEvent.kt +38 -0
  744. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteSeekEvent.kt +38 -0
  745. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteSetRatingEvent.kt +38 -0
  746. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteSkipEvent.kt +38 -0
  747. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RepeatMode.kt +22 -0
  748. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RepeatModeChangedEvent.kt +38 -0
  749. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RequestConfig.kt +59 -0
  750. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +89 -0
  751. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +41 -0
  752. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchMode.kt +25 -0
  753. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +56 -0
  754. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SleepTimer.kt +72 -0
  755. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SleepTimerEndOfTrack.kt +38 -0
  756. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SleepTimerTime.kt +38 -0
  757. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StarRating.kt +38 -0
  758. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ThumbsRating.kt +38 -0
  759. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TimedMetadata.kt +50 -0
  760. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +86 -0
  761. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackMetadata.kt +83 -0
  762. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +21 -0
  763. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +66 -0
  764. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/UpdateOptions.kt +53 -0
  765. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_AndroidAudioOffloadSettings.kt +59 -0
  766. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_RetryConfig.kt +59 -0
  767. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.kt +85 -0
  768. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_NullType_Double.kt +59 -0
  769. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_NullType_NotificationButtonLayout.kt +59 -0
  770. package/nitrogen/generated/ios/AudioBrowser+autolinking.rb +60 -0
  771. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +407 -0
  772. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +2822 -0
  773. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +300 -0
  774. package/nitrogen/generated/ios/AudioBrowserAutolinking.mm +33 -0
  775. package/nitrogen/generated/ios/AudioBrowserAutolinking.swift +25 -0
  776. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.cpp +11 -0
  777. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +1190 -0
  778. package/nitrogen/generated/ios/swift/AndroidAudioContentType.swift +52 -0
  779. package/nitrogen/generated/ios/swift/AndroidAudioOffloadSettings.swift +85 -0
  780. package/nitrogen/generated/ios/swift/AndroidOptions.swift +125 -0
  781. package/nitrogen/generated/ios/swift/AndroidPlayerWakeMode.swift +44 -0
  782. package/nitrogen/generated/ios/swift/AndroidUpdateOptions.swift +187 -0
  783. package/nitrogen/generated/ios/swift/AppKilledPlaybackBehavior.swift +44 -0
  784. package/nitrogen/generated/ios/swift/ArtworkRequestConfig.swift +445 -0
  785. package/nitrogen/generated/ios/swift/BatteryOptimizationStatus.swift +44 -0
  786. package/nitrogen/generated/ios/swift/BatteryOptimizationStatusChangedEvent.swift +36 -0
  787. package/nitrogen/generated/ios/swift/BatteryWarningPendingChangedEvent.swift +36 -0
  788. package/nitrogen/generated/ios/swift/BrowseError.swift +36 -0
  789. package/nitrogen/generated/ios/swift/BrowseResult.swift +18 -0
  790. package/nitrogen/generated/ios/swift/BrowserSourceCallbackParam.swift +86 -0
  791. package/nitrogen/generated/ios/swift/CarPlayNowPlayingButton.swift +48 -0
  792. package/nitrogen/generated/ios/swift/ChapterMetadata.swift +107 -0
  793. package/nitrogen/generated/ios/swift/EqualizerSettings.swift +168 -0
  794. package/nitrogen/generated/ios/swift/FavoriteChangedEvent.swift +47 -0
  795. package/nitrogen/generated/ios/swift/FormatNavigationErrorParams.swift +58 -0
  796. package/nitrogen/generated/ios/swift/FormattedNavigationError.swift +47 -0
  797. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_FormattedNavigationError____FormatNavigationErrorParams.swift +61 -0
  798. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____MediaTransformParams.swift +62 -0
  799. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____RequestConfig_std__optional_std__unordered_map_std__string__std__string__.swift +77 -0
  800. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.swift +62 -0
  801. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.swift +69 -0
  802. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.swift +68 -0
  803. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  804. package/nitrogen/generated/ios/swift/Func_void_BatteryOptimizationStatusChangedEvent.swift +47 -0
  805. package/nitrogen/generated/ios/swift/Func_void_BatteryWarningPendingChangedEvent.swift +47 -0
  806. package/nitrogen/generated/ios/swift/Func_void_EqualizerSettings.swift +47 -0
  807. package/nitrogen/generated/ios/swift/Func_void_FavoriteChangedEvent.swift +47 -0
  808. package/nitrogen/generated/ios/swift/Func_void_IosOutput.swift +47 -0
  809. package/nitrogen/generated/ios/swift/Func_void_NavigationErrorEvent.swift +47 -0
  810. package/nitrogen/generated/ios/swift/Func_void_NowPlayingMetadata.swift +47 -0
  811. package/nitrogen/generated/ios/swift/Func_void_Options.swift +47 -0
  812. package/nitrogen/generated/ios/swift/Func_void_Playback.swift +47 -0
  813. package/nitrogen/generated/ios/swift/Func_void_PlaybackActiveTrackChangedEvent.swift +47 -0
  814. package/nitrogen/generated/ios/swift/Func_void_PlaybackErrorEvent.swift +47 -0
  815. package/nitrogen/generated/ios/swift/Func_void_PlaybackPlayWhenReadyChangedEvent.swift +47 -0
  816. package/nitrogen/generated/ios/swift/Func_void_PlaybackProgressUpdatedEvent.swift +47 -0
  817. package/nitrogen/generated/ios/swift/Func_void_PlaybackQueueEndedEvent.swift +47 -0
  818. package/nitrogen/generated/ios/swift/Func_void_PlayingState.swift +47 -0
  819. package/nitrogen/generated/ios/swift/Func_void_RemoteJumpBackwardEvent.swift +47 -0
  820. package/nitrogen/generated/ios/swift/Func_void_RemoteJumpForwardEvent.swift +47 -0
  821. package/nitrogen/generated/ios/swift/Func_void_RemotePlayIdEvent.swift +47 -0
  822. package/nitrogen/generated/ios/swift/Func_void_RemotePlaySearchEvent.swift +47 -0
  823. package/nitrogen/generated/ios/swift/Func_void_RemoteSeekEvent.swift +47 -0
  824. package/nitrogen/generated/ios/swift/Func_void_RemoteSetRatingEvent.swift +47 -0
  825. package/nitrogen/generated/ios/swift/Func_void_RemoteSkipEvent.swift +47 -0
  826. package/nitrogen/generated/ios/swift/Func_void_RepeatModeChangedEvent.swift +47 -0
  827. package/nitrogen/generated/ios/swift/Func_void_RequestConfig.swift +47 -0
  828. package/nitrogen/generated/ios/swift/Func_void_TimedMetadata.swift +47 -0
  829. package/nitrogen/generated/ios/swift/Func_void_TrackMetadata.swift +47 -0
  830. package/nitrogen/generated/ios/swift/Func_void_bool.swift +47 -0
  831. package/nitrogen/generated/ios/swift/Func_void_double.swift +47 -0
  832. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  833. package/nitrogen/generated/ios/swift/Func_void_std__optional_FormattedNavigationError_.swift +47 -0
  834. package/nitrogen/generated/ios/swift/Func_void_std__optional_ResolvedTrack_.swift +47 -0
  835. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__variant_nitro__NullType__SleepTimerTime__SleepTimerEndOfTrack__.swift +69 -0
  836. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_RequestConfig__.swift +67 -0
  837. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError___.swift +67 -0
  838. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_std__vector_Track___.swift +67 -0
  839. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  840. package/nitrogen/generated/ios/swift/Func_void_std__variant_ResolvedTrack__BrowseError_.swift +59 -0
  841. package/nitrogen/generated/ios/swift/Func_void_std__vector_ChapterMetadata_.swift +47 -0
  842. package/nitrogen/generated/ios/swift/Func_void_std__vector_Track_.swift +47 -0
  843. package/nitrogen/generated/ios/swift/HeartRating.swift +36 -0
  844. package/nitrogen/generated/ios/swift/HttpMethod.swift +60 -0
  845. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +181 -0
  846. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +2352 -0
  847. package/nitrogen/generated/ios/swift/IOSCategory.swift +56 -0
  848. package/nitrogen/generated/ios/swift/IOSCategoryMode.swift +68 -0
  849. package/nitrogen/generated/ios/swift/IOSCategoryOptions.swift +60 -0
  850. package/nitrogen/generated/ios/swift/IOSCategoryPolicy.swift +44 -0
  851. package/nitrogen/generated/ios/swift/ImageContext.swift +71 -0
  852. package/nitrogen/generated/ios/swift/ImageQueryParams.swift +85 -0
  853. package/nitrogen/generated/ios/swift/ImageSource.swift +139 -0
  854. package/nitrogen/generated/ios/swift/IosOutput.swift +58 -0
  855. package/nitrogen/generated/ios/swift/IosOutputType.swift +76 -0
  856. package/nitrogen/generated/ios/swift/MediaRequestConfig.swift +434 -0
  857. package/nitrogen/generated/ios/swift/MediaTransformParams.swift +59 -0
  858. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +360 -0
  859. package/nitrogen/generated/ios/swift/NativeRouteEntry.swift +275 -0
  860. package/nitrogen/generated/ios/swift/NativeUpdateOptions.swift +215 -0
  861. package/nitrogen/generated/ios/swift/NavigationError.swift +100 -0
  862. package/nitrogen/generated/ios/swift/NavigationErrorEvent.swift +48 -0
  863. package/nitrogen/generated/ios/swift/NavigationErrorType.swift +52 -0
  864. package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +187 -0
  865. package/nitrogen/generated/ios/swift/NotificationButton.swift +52 -0
  866. package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +159 -0
  867. package/nitrogen/generated/ios/swift/NowPlayingMetadata.swift +351 -0
  868. package/nitrogen/generated/ios/swift/NowPlayingUpdate.swift +85 -0
  869. package/nitrogen/generated/ios/swift/Options.swift +148 -0
  870. package/nitrogen/generated/ios/swift/PartialAndroidSetupPlayerOptions.swift +328 -0
  871. package/nitrogen/generated/ios/swift/PartialIOSSetupPlayerOptions.swift +159 -0
  872. package/nitrogen/generated/ios/swift/PartialSetupPlayerOptions.swift +157 -0
  873. package/nitrogen/generated/ios/swift/PercentageRating.swift +36 -0
  874. package/nitrogen/generated/ios/swift/Playback.swift +59 -0
  875. package/nitrogen/generated/ios/swift/PlaybackActiveTrackChangedEvent.swift +128 -0
  876. package/nitrogen/generated/ios/swift/PlaybackError.swift +47 -0
  877. package/nitrogen/generated/ios/swift/PlaybackErrorEvent.swift +48 -0
  878. package/nitrogen/generated/ios/swift/PlaybackPlayWhenReadyChangedEvent.swift +36 -0
  879. package/nitrogen/generated/ios/swift/PlaybackProgressUpdatedEvent.swift +69 -0
  880. package/nitrogen/generated/ios/swift/PlaybackQueueEndedEvent.swift +47 -0
  881. package/nitrogen/generated/ios/swift/PlaybackState.swift +68 -0
  882. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +385 -0
  883. package/nitrogen/generated/ios/swift/PlayingState.swift +47 -0
  884. package/nitrogen/generated/ios/swift/Progress.swift +58 -0
  885. package/nitrogen/generated/ios/swift/RatingType.swift +60 -0
  886. package/nitrogen/generated/ios/swift/RemoteJumpBackwardEvent.swift +36 -0
  887. package/nitrogen/generated/ios/swift/RemoteJumpForwardEvent.swift +36 -0
  888. package/nitrogen/generated/ios/swift/RemotePlayIdEvent.swift +59 -0
  889. package/nitrogen/generated/ios/swift/RemotePlaySearchEvent.swift +36 -0
  890. package/nitrogen/generated/ios/swift/RemoteSeekEvent.swift +36 -0
  891. package/nitrogen/generated/ios/swift/RemoteSetRatingEvent.swift +76 -0
  892. package/nitrogen/generated/ios/swift/RemoteSkipEvent.swift +36 -0
  893. package/nitrogen/generated/ios/swift/RepeatMode.swift +44 -0
  894. package/nitrogen/generated/ios/swift/RepeatModeChangedEvent.swift +36 -0
  895. package/nitrogen/generated/ios/swift/RequestConfig.swift +298 -0
  896. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +511 -0
  897. package/nitrogen/generated/ios/swift/RetryConfig.swift +59 -0
  898. package/nitrogen/generated/ios/swift/SearchMode.swift +56 -0
  899. package/nitrogen/generated/ios/swift/SearchParams.swift +209 -0
  900. package/nitrogen/generated/ios/swift/SleepTimer.swift +19 -0
  901. package/nitrogen/generated/ios/swift/SleepTimerEndOfTrack.swift +36 -0
  902. package/nitrogen/generated/ios/swift/SleepTimerTime.swift +36 -0
  903. package/nitrogen/generated/ios/swift/StarRating.swift +36 -0
  904. package/nitrogen/generated/ios/swift/ThumbsRating.swift +36 -0
  905. package/nitrogen/generated/ios/swift/TimedMetadata.swift +175 -0
  906. package/nitrogen/generated/ios/swift/Track.swift +488 -0
  907. package/nitrogen/generated/ios/swift/TrackMetadata.swift +505 -0
  908. package/nitrogen/generated/ios/swift/TrackStyle.swift +40 -0
  909. package/nitrogen/generated/ios/swift/TransformableRequestConfig.swift +372 -0
  910. package/nitrogen/generated/ios/swift/UpdateOptions.swift +215 -0
  911. package/nitrogen/generated/ios/swift/Variant_Bool_AndroidAudioOffloadSettings.swift +18 -0
  912. package/nitrogen/generated/ios/swift/Variant_Bool_RetryConfig.swift +18 -0
  913. package/nitrogen/generated/ios/swift/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.swift +20 -0
  914. package/nitrogen/generated/ios/swift/Variant_NullType_Double.swift +18 -0
  915. package/nitrogen/generated/ios/swift/Variant_NullType_NotificationButtonLayout.swift +18 -0
  916. package/nitrogen/generated/shared/c++/AndroidAudioContentType.hpp +88 -0
  917. package/nitrogen/generated/shared/c++/AndroidAudioOffloadSettings.hpp +79 -0
  918. package/nitrogen/generated/shared/c++/AndroidOptions.hpp +101 -0
  919. package/nitrogen/generated/shared/c++/AndroidPlayerWakeMode.hpp +80 -0
  920. package/nitrogen/generated/shared/c++/AndroidUpdateOptions.hpp +101 -0
  921. package/nitrogen/generated/shared/c++/AppKilledPlaybackBehavior.hpp +80 -0
  922. package/nitrogen/generated/shared/c++/ArtworkRequestConfig.hpp +133 -0
  923. package/nitrogen/generated/shared/c++/BatteryOptimizationStatus.hpp +80 -0
  924. package/nitrogen/generated/shared/c++/BatteryOptimizationStatusChangedEvent.hpp +76 -0
  925. package/nitrogen/generated/shared/c++/BatteryWarningPendingChangedEvent.hpp +75 -0
  926. package/nitrogen/generated/shared/c++/BrowseError.hpp +75 -0
  927. package/nitrogen/generated/shared/c++/BrowserSourceCallbackParam.hpp +81 -0
  928. package/nitrogen/generated/shared/c++/CarPlayNowPlayingButton.hpp +84 -0
  929. package/nitrogen/generated/shared/c++/ChapterMetadata.hpp +88 -0
  930. package/nitrogen/generated/shared/c++/EqualizerSettings.hpp +105 -0
  931. package/nitrogen/generated/shared/c++/FavoriteChangedEvent.hpp +80 -0
  932. package/nitrogen/generated/shared/c++/FormatNavigationErrorParams.hpp +88 -0
  933. package/nitrogen/generated/shared/c++/FormattedNavigationError.hpp +79 -0
  934. package/nitrogen/generated/shared/c++/HeartRating.hpp +75 -0
  935. package/nitrogen/generated/shared/c++/HttpMethod.hpp +96 -0
  936. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +207 -0
  937. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +375 -0
  938. package/nitrogen/generated/shared/c++/IOSCategory.hpp +92 -0
  939. package/nitrogen/generated/shared/c++/IOSCategoryMode.hpp +104 -0
  940. package/nitrogen/generated/shared/c++/IOSCategoryOptions.hpp +96 -0
  941. package/nitrogen/generated/shared/c++/IOSCategoryPolicy.hpp +80 -0
  942. package/nitrogen/generated/shared/c++/ImageContext.hpp +79 -0
  943. package/nitrogen/generated/shared/c++/ImageQueryParams.hpp +80 -0
  944. package/nitrogen/generated/shared/c++/ImageSource.hpp +91 -0
  945. package/nitrogen/generated/shared/c++/IosOutput.hpp +85 -0
  946. package/nitrogen/generated/shared/c++/IosOutputType.hpp +112 -0
  947. package/nitrogen/generated/shared/c++/MediaRequestConfig.hpp +123 -0
  948. package/nitrogen/generated/shared/c++/MediaTransformParams.hpp +84 -0
  949. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +135 -0
  950. package/nitrogen/generated/shared/c++/NativeRouteEntry.hpp +131 -0
  951. package/nitrogen/generated/shared/c++/NativeUpdateOptions.hpp +103 -0
  952. package/nitrogen/generated/shared/c++/NavigationError.hpp +90 -0
  953. package/nitrogen/generated/shared/c++/NavigationErrorEvent.hpp +77 -0
  954. package/nitrogen/generated/shared/c++/NavigationErrorType.hpp +88 -0
  955. package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +101 -0
  956. package/nitrogen/generated/shared/c++/NotificationButton.hpp +88 -0
  957. package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +94 -0
  958. package/nitrogen/generated/shared/c++/NowPlayingMetadata.hpp +124 -0
  959. package/nitrogen/generated/shared/c++/NowPlayingUpdate.hpp +80 -0
  960. package/nitrogen/generated/shared/c++/Options.hpp +105 -0
  961. package/nitrogen/generated/shared/c++/PartialAndroidSetupPlayerOptions.hpp +121 -0
  962. package/nitrogen/generated/shared/c++/PartialIOSSetupPlayerOptions.hpp +103 -0
  963. package/nitrogen/generated/shared/c++/PartialSetupPlayerOptions.hpp +96 -0
  964. package/nitrogen/generated/shared/c++/PercentageRating.hpp +75 -0
  965. package/nitrogen/generated/shared/c++/Playback.hpp +84 -0
  966. package/nitrogen/generated/shared/c++/PlaybackActiveTrackChangedEvent.hpp +93 -0
  967. package/nitrogen/generated/shared/c++/PlaybackError.hpp +79 -0
  968. package/nitrogen/generated/shared/c++/PlaybackErrorEvent.hpp +77 -0
  969. package/nitrogen/generated/shared/c++/PlaybackPlayWhenReadyChangedEvent.hpp +75 -0
  970. package/nitrogen/generated/shared/c++/PlaybackProgressUpdatedEvent.hpp +87 -0
  971. package/nitrogen/generated/shared/c++/PlaybackQueueEndedEvent.hpp +79 -0
  972. package/nitrogen/generated/shared/c++/PlaybackState.hpp +104 -0
  973. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +119 -0
  974. package/nitrogen/generated/shared/c++/PlayingState.hpp +79 -0
  975. package/nitrogen/generated/shared/c++/Progress.hpp +83 -0
  976. package/nitrogen/generated/shared/c++/RatingType.hpp +96 -0
  977. package/nitrogen/generated/shared/c++/RemoteJumpBackwardEvent.hpp +75 -0
  978. package/nitrogen/generated/shared/c++/RemoteJumpForwardEvent.hpp +75 -0
  979. package/nitrogen/generated/shared/c++/RemotePlayIdEvent.hpp +80 -0
  980. package/nitrogen/generated/shared/c++/RemotePlaySearchEvent.hpp +75 -0
  981. package/nitrogen/generated/shared/c++/RemoteSeekEvent.hpp +75 -0
  982. package/nitrogen/generated/shared/c++/RemoteSetRatingEvent.hpp +86 -0
  983. package/nitrogen/generated/shared/c++/RemoteSkipEvent.hpp +75 -0
  984. package/nitrogen/generated/shared/c++/RepeatMode.hpp +80 -0
  985. package/nitrogen/generated/shared/c++/RepeatModeChangedEvent.hpp +76 -0
  986. package/nitrogen/generated/shared/c++/RequestConfig.hpp +107 -0
  987. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +153 -0
  988. package/nitrogen/generated/shared/c++/RetryConfig.hpp +79 -0
  989. package/nitrogen/generated/shared/c++/SearchMode.hpp +92 -0
  990. package/nitrogen/generated/shared/c++/SearchParams.hpp +102 -0
  991. package/nitrogen/generated/shared/c++/SleepTimerEndOfTrack.hpp +75 -0
  992. package/nitrogen/generated/shared/c++/SleepTimerTime.hpp +75 -0
  993. package/nitrogen/generated/shared/c++/StarRating.hpp +75 -0
  994. package/nitrogen/generated/shared/c++/ThumbsRating.hpp +75 -0
  995. package/nitrogen/generated/shared/c++/TimedMetadata.hpp +92 -0
  996. package/nitrogen/generated/shared/c++/Track.hpp +145 -0
  997. package/nitrogen/generated/shared/c++/TrackMetadata.hpp +136 -0
  998. package/nitrogen/generated/shared/c++/TrackStyle.hpp +76 -0
  999. package/nitrogen/generated/shared/c++/TransformableRequestConfig.hpp +116 -0
  1000. package/nitrogen/generated/shared/c++/UpdateOptions.hpp +103 -0
  1001. package/package.json +120 -0
  1002. package/src/AudioBrowser.ts +4 -0
  1003. package/src/features/battery.ts +176 -0
  1004. package/src/features/browser.ts +315 -0
  1005. package/src/features/equalizer.ts +64 -0
  1006. package/src/features/errors.ts +196 -0
  1007. package/src/features/favorites.ts +102 -0
  1008. package/src/features/index.ts +23 -0
  1009. package/src/features/metadata.ts +130 -0
  1010. package/src/features/network.ts +34 -0
  1011. package/src/features/nowPlaying.ts +56 -0
  1012. package/src/features/output.ts +50 -0
  1013. package/src/features/playback/controls.ts +66 -0
  1014. package/src/features/playback/index.ts +7 -0
  1015. package/src/features/playback/playWhenReady.ts +57 -0
  1016. package/src/features/playback/playing.ts +40 -0
  1017. package/src/features/playback/progress.ts +122 -0
  1018. package/src/features/playback/rate.ts +22 -0
  1019. package/src/features/playback/state.ts +72 -0
  1020. package/src/features/playback/volume.ts +59 -0
  1021. package/src/features/player/index.ts +2 -0
  1022. package/src/features/player/options.ts +479 -0
  1023. package/src/features/player/setup.ts +551 -0
  1024. package/src/features/queue/activeTrack.ts +59 -0
  1025. package/src/features/queue/index.ts +4 -0
  1026. package/src/features/queue/queue.ts +173 -0
  1027. package/src/features/queue/repeatMode.ts +58 -0
  1028. package/src/features/queue/shuffle.ts +49 -0
  1029. package/src/features/rating.ts +17 -0
  1030. package/src/features/remoteControls.ts +341 -0
  1031. package/src/features/sleepTimer.ts +186 -0
  1032. package/src/index.ts +5 -0
  1033. package/src/native.ts +5 -0
  1034. package/src/native.web.ts +4 -0
  1035. package/src/specs/audio-browser.nitro.ts +326 -0
  1036. package/src/types/browser-native.ts +66 -0
  1037. package/src/types/browser-nodes.ts +174 -0
  1038. package/src/types/browser.ts +792 -0
  1039. package/src/types/index.ts +2 -0
  1040. package/src/types/player.ts +69 -0
  1041. package/src/utils/LazyNativeEmitter.ts +58 -0
  1042. package/src/utils/NativeUpdatedValue.ts +56 -0
  1043. package/src/utils/resolveAssetSource.ts +4 -0
  1044. package/src/utils/useDebug.ts +283 -0
  1045. package/src/utils/useNativeUpdatedValue.ts +41 -0
  1046. package/src/web/NativeAudioBrowser.ts +781 -0
  1047. package/src/web/SimpleRouter.ts +177 -0
  1048. package/src/web/TrackPlayer/Event.ts +11 -0
  1049. package/src/web/TrackPlayer/Player.ts +323 -0
  1050. package/src/web/TrackPlayer/PlaylistPlayer.ts +337 -0
  1051. package/src/web/TrackPlayer/RepeatMode.ts +7 -0
  1052. package/src/web/TrackPlayer/SetupNotCalledError.ts +5 -0
  1053. package/src/web/TrackPlayer/SleepTimer.ts +71 -0
  1054. package/src/web/TrackPlayer/State.ts +15 -0
  1055. package/src/web/TrackPlayer/index.ts +4 -0
  1056. package/src/web/browser/BrowserManager.ts +642 -0
  1057. package/src/web/browser/FavoriteManager.ts +77 -0
  1058. package/src/web/browser/NavigationErrorManager.ts +112 -0
  1059. package/src/web/browser/SearchManager.ts +82 -0
  1060. package/src/web/http/HttpClient.ts +107 -0
  1061. package/src/web/http/RequestConfigBuilder.ts +347 -0
  1062. package/src/web/player/NowPlayingManager.ts +83 -0
  1063. package/src/web/player/OptionsManager.ts +101 -0
  1064. package/src/web/util/BrowserPathHelper.ts +147 -0
  1065. package/src/web/util/shuffle.ts +14 -0
@@ -0,0 +1,1342 @@
1
+ import CarPlay
2
+ import Foundation
3
+ import Kingfisher
4
+ import NitroModules
5
+ import os.log
6
+
7
+ /// Controller managing CarPlay templates and navigation.
8
+ ///
9
+ /// Responsibilities:
10
+ /// - Creates and manages CPTabBarTemplate from browser tabs
11
+ /// - Converts browser content to CPListTemplate for navigation
12
+ /// - Handles item selection for playback and navigation
13
+ /// - Loads artwork for list items
14
+ /// - Integrates with CPNowPlayingTemplate
15
+ ///
16
+ /// This class is exposed to Objective-C for use by RNABCarPlaySceneDelegate.
17
+ @MainActor
18
+ @objc(RNABCarPlayController)
19
+ public final class RNABCarPlayController: NSObject {
20
+ private let logger = Logger(subsystem: "com.audiobrowser", category: "CarPlayController")
21
+
22
+ private let interfaceController: CPInterfaceController
23
+ private weak var audioBrowser: HybridAudioBrowser?
24
+
25
+ /// Track content subscriptions
26
+ private var isStarted = false
27
+
28
+ /// Current navigation stack paths (for back navigation context)
29
+ private var navigationStack: [String] = []
30
+
31
+ /// Helper object for CPNowPlayingTemplateObserver conformance
32
+ /// (kept separate to avoid exposing CarPlay protocols to Obj-C header)
33
+ private var nowPlayingObserver: NowPlayingObserver?
34
+
35
+ /// Helper object for CPInterfaceControllerDelegate conformance
36
+ private var interfaceDelegate: InterfaceControllerDelegate?
37
+
38
+ /// Reference to the Up Next template for updating when queue changes
39
+ private weak var upNextTemplate: CPListTemplate?
40
+
41
+ /// Convenience accessor for browser config
42
+ private var config: BrowserConfig {
43
+ audioBrowser?.browserManager.config ?? BrowserConfig()
44
+ }
45
+
46
+ /// Gets the current active track's favorited state
47
+ private var isActiveTrackFavorited: Bool {
48
+ (try? audioBrowser?.getActiveTrack())?.favorited ?? false
49
+ }
50
+
51
+ /// Checks if the given src matches the currently active (loaded) track
52
+ private func isActiveTrack(src: String) -> Bool {
53
+ audioBrowser?.getPlayer()?.currentTrack?.src == src
54
+ }
55
+
56
+ // MARK: - Initialization
57
+
58
+ @objc
59
+ public init(interfaceController: CPInterfaceController) {
60
+ self.interfaceController = interfaceController
61
+ audioBrowser = HybridAudioBrowser.shared
62
+ super.init()
63
+ }
64
+
65
+ // MARK: - Lifecycle
66
+
67
+ @objc
68
+ public func start() {
69
+ guard !isStarted else { return }
70
+ isStarted = true
71
+
72
+ logger.info("Starting CarPlay controller")
73
+
74
+ // Set up interface controller delegate for template lifecycle events
75
+ let delegate = InterfaceControllerDelegate(controller: self)
76
+ interfaceDelegate = delegate
77
+ interfaceController.delegate = delegate
78
+
79
+ // Show loading template while waiting
80
+ showLoadingTemplate()
81
+
82
+ // Wait for both browser and player to be ready
83
+ Task { @MainActor in
84
+ let (browser, _) = await playerAndConfiguredBrowser.wait()
85
+ guard self.isStarted else { return }
86
+ self.logger.debug("AudioBrowser and player ready, setting up CarPlay")
87
+ self.audioBrowser = browser
88
+ self.setupContentSubscriptions()
89
+ self.setupNowPlayingTemplate()
90
+ await self.buildInitialInterface()
91
+ }
92
+ }
93
+
94
+ @objc
95
+ public func stop() {
96
+ guard isStarted else { return }
97
+ isStarted = false
98
+
99
+ logger.info("Stopping CarPlay controller")
100
+
101
+ // Remove Now Playing observer
102
+ if let observer = nowPlayingObserver {
103
+ CPNowPlayingTemplate.shared.remove(observer)
104
+ nowPlayingObserver = nil
105
+ }
106
+
107
+ navigationStack.removeAll()
108
+ }
109
+
110
+ // MARK: - Content Subscriptions
111
+
112
+ private func setupContentSubscriptions() {
113
+ guard let audioBrowser else {
114
+ logger.warning("AudioBrowser not available for CarPlay")
115
+ return
116
+ }
117
+
118
+ // Subscribe to tab changes
119
+ audioBrowser.tabsChangedEmitter.addListener { [weak self] tabs in
120
+ Task { @MainActor in
121
+ self?.handleTabsChanged(tabs)
122
+ }
123
+ }
124
+
125
+ // Subscribe to content changes
126
+ audioBrowser.contentChangedEmitter.addListener { [weak self] content in
127
+ Task { @MainActor in
128
+ self?.handleContentChanged(content)
129
+ }
130
+ }
131
+
132
+ // Subscribe to config changes (for Now Playing buttons)
133
+ audioBrowser.browserManager.onConfigChanged = { [weak self] _ in
134
+ Task { @MainActor in
135
+ self?.setupNowPlayingButtons()
136
+ }
137
+ }
138
+
139
+ // Subscribe to favorite changes (for Now Playing button)
140
+ audioBrowser.favoriteChangedEmitter.addListener { [weak self] _ in
141
+ Task { @MainActor in
142
+ self?.updateFavoriteButtonState()
143
+ }
144
+ }
145
+
146
+ // Subscribe to external content changes (from notifyContentChanged)
147
+ audioBrowser.externalContentChangedEmitter.addListener { [weak self] path in
148
+ self?.notifyContentChanged(path: path)
149
+ }
150
+
151
+ // Subscribe to active track changes (for playing indicator in lists)
152
+ audioBrowser.activeTrackChangedEmitter.addListener { [weak self] event in
153
+ Task { @MainActor in
154
+ self?.handleActiveTrackChanged(event)
155
+ }
156
+ }
157
+
158
+ // Subscribe to queue changes (for Up Next list updates)
159
+ audioBrowser.queueChangedEmitter.addListener { [weak self] tracks in
160
+ Task { @MainActor in
161
+ self?.handleQueueChanged(tracks)
162
+ }
163
+ }
164
+
165
+ // Subscribe to navigation errors (from browser layer)
166
+ audioBrowser.navigationErrorEmitter.addListener { [weak self] event in
167
+ Task { @MainActor in
168
+ self?.handleNavigationError(event)
169
+ }
170
+ }
171
+ }
172
+
173
+ /// Handles navigation errors from the browser layer, displaying them in CarPlay
174
+ @MainActor
175
+ private func handleNavigationError(_ event: NavigationErrorEvent) {
176
+ guard let error = event.error else { return }
177
+ logger.warning("Navigation error: \(error.code.stringValue) - \(error.message)")
178
+ // Use current path from navigation stack, or "/" as fallback
179
+ let path = navigationStack.last ?? "/"
180
+ showNavigationError(error, path: path)
181
+ }
182
+
183
+ // MARK: - Initial Interface
184
+
185
+ @MainActor
186
+ private func buildInitialInterface() async {
187
+ guard let audioBrowser else {
188
+ logger.error("AudioBrowser not available")
189
+ showErrorTemplate(message: "Audio browser not initialized")
190
+ return
191
+ }
192
+
193
+ // Get tabs from browser manager
194
+ let tabs = audioBrowser.browserManager.getTabs()
195
+
196
+ if let tabs, !tabs.isEmpty {
197
+ await showTabBar(tabs: tabs)
198
+ } else {
199
+ // No tabs yet - query them
200
+ logger.info("No tabs available, querying...")
201
+ do {
202
+ let queriedTabs = try await audioBrowser.browserManager.queryTabs()
203
+ if !queriedTabs.isEmpty {
204
+ await showTabBar(tabs: queriedTabs)
205
+ } else {
206
+ showErrorTemplate(message: "No content available")
207
+ }
208
+ } catch {
209
+ logger.error("Failed to query tabs: \(error.localizedDescription)")
210
+ showErrorTemplate(message: "Failed to load content")
211
+ }
212
+ }
213
+ }
214
+
215
+ /// Shows a loading template while waiting for initialization
216
+ private func showLoadingTemplate() {
217
+ let template = CPListTemplate(
218
+ title: nil,
219
+ sections: [],
220
+ )
221
+ interfaceController.setRootTemplate(template, animated: false, completion: nil)
222
+ }
223
+
224
+ // MARK: - Tab Bar
225
+
226
+ @MainActor
227
+ private func showTabBar(tabs: [Track]) async {
228
+ logger.info("Building tab bar with \(tabs.count) tabs")
229
+
230
+ let maxTabs = CPTabBarTemplate.maximumTabCount
231
+
232
+ // Reserve one slot for search if configured
233
+ let hasSearch = config.hasSearch
234
+ let maxContentTabs = hasSearch ? maxTabs - 1 : maxTabs
235
+
236
+ // Create tab templates synchronously (empty shells) - don't block on content loading
237
+ let tabTemplates: [CPListTemplate] = tabs.prefix(maxContentTabs).map { tab in
238
+ createTabTemplate(for: tab)
239
+ }
240
+
241
+ // Set the tab bar immediately so UI appears fast
242
+ logger.info("Setting tab bar root template with \(tabTemplates.count) templates")
243
+ let tabBar = CPTabBarTemplate(templates: tabTemplates)
244
+ interfaceController.setRootTemplate(tabBar, animated: true, completion: nil)
245
+
246
+ // Load content for the first tab only - others load lazily when selected
247
+ if let firstTemplate = tabTemplates.first, let firstTab = tabs.first, let url = firstTab.url {
248
+ await loadContent(for: url, into: firstTemplate)
249
+ }
250
+ }
251
+
252
+ /// Creates a tab template shell without loading content (synchronous)
253
+ private func createTabTemplate(for track: Track) -> CPListTemplate {
254
+ let template = CPListTemplate(
255
+ title: track.title,
256
+ sections: [],
257
+ )
258
+
259
+ // Set tab title explicitly (required for tab bar display)
260
+ template.tabTitle = track.title
261
+
262
+ // Store path for lazy loading and refresh
263
+ if let url = track.url {
264
+ template.userInfo = ["path": url] as [String: Any]
265
+ }
266
+
267
+ // Set tab image - CarPlay requires an image for proper tab display
268
+ // Tab bar icons are 24pt x 24pt per CarPlay Developer Guide
269
+ // https://developer.apple.com/download/files/CarPlay-Developer-Guide.pdf
270
+ template.tabImage = defaultTabImage()
271
+
272
+ // Support SF Symbols via "sf:" prefix (e.g., "sf:heart.fill")
273
+ if let artwork = track.artwork, artwork.hasPrefix("sf:") {
274
+ let symbolName = String(artwork.dropFirst(3))
275
+ if let image = sfSymbolImage(symbolName) {
276
+ template.tabImage = image
277
+ }
278
+ } else if track.artwork != nil || track.artworkSource != nil {
279
+ // loadArtwork handles both artwork and artworkSource
280
+ let tabImageSize = CGSize(width: 24, height: 24)
281
+ loadArtwork(for: track, size: tabImageSize) { [weak template] image in
282
+ Task { @MainActor in
283
+ if let image {
284
+ template?.tabImage = image
285
+ }
286
+ }
287
+ }
288
+ }
289
+
290
+ return template
291
+ }
292
+
293
+ // MARK: - List Templates
294
+
295
+ @MainActor
296
+ private func createListTemplate(
297
+ for resolvedTrack: ResolvedTrack,
298
+ path: String,
299
+ ) -> CPListTemplate {
300
+ let template = CPListTemplate(
301
+ title: resolvedTrack.title,
302
+ sections: createSections(from: resolvedTrack),
303
+ )
304
+
305
+ template.userInfo = ["path": path] as [String: Any]
306
+
307
+ return template
308
+ }
309
+
310
+ /// Finds the path associated with a template, if any
311
+ private func getPath(from template: CPTemplate) -> String? {
312
+ (template.userInfo as? [String: Any])?["path"] as? String
313
+ }
314
+
315
+ private func createSections(from resolvedTrack: ResolvedTrack) -> [CPListSection] {
316
+ guard let children = resolvedTrack.children else {
317
+ return []
318
+ }
319
+
320
+ let maxSections = CPListTemplate.maximumSectionCount
321
+ let maxTotalItems = CPListTemplate.maximumItemCount
322
+
323
+ // Group by groupTitle if present
324
+ var groups: [String?: [Track]] = [:]
325
+ for track in children {
326
+ let groupKey = track.groupTitle
327
+ groups[groupKey, default: []].append(track)
328
+ }
329
+
330
+ // Create sections (respecting both section and total item limits)
331
+ var sections: [CPListSection] = []
332
+ var totalItemCount = 0
333
+
334
+ // Ungrouped items first
335
+ if let ungrouped = groups[nil], !ungrouped.isEmpty {
336
+ let availableSlots = maxTotalItems - totalItemCount
337
+ let items = ungrouped.prefix(availableSlots).map { createListItem(for: $0) }
338
+ if !items.isEmpty {
339
+ sections.append(CPListSection(items: items))
340
+ totalItemCount += items.count
341
+ }
342
+ }
343
+
344
+ // Then grouped items (respecting section and item limits)
345
+ for (groupTitle, tracks) in groups.sorted(by: { ($0.key ?? "") < ($1.key ?? "") }) {
346
+ guard sections.count < maxSections else { break }
347
+ guard totalItemCount < maxTotalItems else { break }
348
+ guard groupTitle != nil else { continue }
349
+
350
+ let availableSlots = maxTotalItems - totalItemCount
351
+ let items = tracks.prefix(availableSlots).map { createListItem(for: $0) }
352
+ if !items.isEmpty {
353
+ sections.append(CPListSection(items: items, header: groupTitle, sectionIndexTitle: nil))
354
+ totalItemCount += items.count
355
+ }
356
+ }
357
+
358
+ return sections
359
+ }
360
+
361
+ /// Creates a CPListItem for a track with common setup (userInfo, artwork, isPlaying).
362
+ /// - Parameters:
363
+ /// - track: The track to create the item for
364
+ /// - handler: Optional custom handler. If nil, uses default browse/play handling.
365
+ private func createListItem(
366
+ for track: Track,
367
+ handler: ((CPSelectableListItem, @escaping () -> Void) -> Void)? = nil,
368
+ ) -> CPListItem {
369
+ let item = CPListItem(
370
+ text: track.title,
371
+ detailText: track.subtitle ?? track.artist,
372
+ )
373
+
374
+ // Store track info for selection handling and updatePlayingIndicators()
375
+ item.userInfo = [
376
+ "url": track.url as Any,
377
+ "src": track.src as Any,
378
+ "hasSrc": track.src != nil,
379
+ "hasUrl": track.url != nil,
380
+ ]
381
+
382
+ // Set accessory type based on whether track is browsable or playable
383
+ if let src = track.src {
384
+ // Playable track - check if it's currently playing
385
+ item.accessoryType = .none
386
+ item.isPlaying = isActiveTrack(src: src)
387
+ if item.isPlaying {
388
+ logger.debug("Setting isPlaying=true for: \(track.title) (src: \(src))")
389
+ }
390
+ } else if track.url != nil {
391
+ // Browsable only - show disclosure indicator
392
+ item.accessoryType = .disclosureIndicator
393
+ }
394
+
395
+ // Load artwork with size context for proper CDN optimization
396
+ // Support SF Symbols via "sf:" prefix (e.g., "sf:heart.fill")
397
+ if let artwork = track.artwork, artwork.hasPrefix("sf:") {
398
+ let symbolName = String(artwork.dropFirst(3))
399
+ if let image = sfSymbolImageForListItem(symbolName) {
400
+ item.setImage(image)
401
+ }
402
+ } else if track.artwork != nil || track.artworkSource != nil {
403
+ // Set empty placeholder to reserve space while loading
404
+ item.setImage(placeholderImage)
405
+ loadArtwork(for: track, size: CPListItem.maximumImageSize) { [weak item] image in
406
+ Task { @MainActor in
407
+ item?.setImage(image)
408
+ }
409
+ }
410
+ }
411
+
412
+ // Set selection handler
413
+ if let handler {
414
+ item.handler = handler
415
+ } else {
416
+ item.handler = { [weak self] _, completion in
417
+ self?.handleItemSelection(track: track, completion: completion)
418
+ }
419
+ }
420
+
421
+ return item
422
+ }
423
+
424
+ // MARK: - Content Loading
425
+
426
+ @MainActor
427
+ private func loadContent(for path: String, into template: CPListTemplate) async {
428
+ guard let audioBrowser else { return }
429
+
430
+ do {
431
+ let resolved = try await audioBrowser.browserManager.resolve(path, useCache: true)
432
+ let sections = createSections(from: resolved)
433
+ template.updateSections(sections)
434
+ } catch {
435
+ logger.error("Failed to load content for \(path): \(error.localizedDescription)")
436
+ }
437
+ }
438
+
439
+ // MARK: - Selection Handling
440
+
441
+ private func handleItemSelection(track: Track, completion: @escaping () -> Void) {
442
+ logger.info("Selected track: \(track.title)")
443
+
444
+ guard let audioBrowser else {
445
+ completion()
446
+ return
447
+ }
448
+
449
+ // If this track is already loaded, resume playback and show Now Playing
450
+ if let src = track.src, isActiveTrack(src: src) {
451
+ try? audioBrowser.play()
452
+ showNowPlaying()
453
+ completion()
454
+ return
455
+ }
456
+
457
+ // Check if this is a contextual URL (playable-only track with queue context)
458
+ if let url = track.url, BrowserPathHelper.isContextual(url) {
459
+ let parentPath = BrowserPathHelper.stripTrackId(url)
460
+ let trackId = BrowserPathHelper.extractTrackId(url)
461
+ let player = audioBrowser.getPlayer()
462
+
463
+ // Check if queue already came from this parent path - just skip to the track
464
+ if let trackId,
465
+ parentPath == player?.queueSourcePath,
466
+ let index = player?.tracks.firstIndex(where: { $0.src == trackId })
467
+ {
468
+ logger.debug("Queue already from \(parentPath), skipping to index \(index)")
469
+ try? player?.skipTo(index, playWhenReady: true)
470
+ showNowPlaying()
471
+ completion()
472
+ return
473
+ }
474
+
475
+ Task {
476
+ do {
477
+ // Expand the queue from the contextual URL
478
+ if let expanded = try await audioBrowser.browserManager.expandQueueFromContextualUrl(url) {
479
+ let (tracks, startIndex) = expanded
480
+
481
+ await MainActor.run {
482
+ // Replace queue and start at the selected track
483
+ audioBrowser.getPlayer()?.setQueue(tracks, initialIndex: startIndex, playWhenReady: true, sourcePath: parentPath)
484
+
485
+ // Show now playing template
486
+ self.showNowPlaying()
487
+ completion()
488
+ }
489
+ } else {
490
+ // Fallback: just load the single track
491
+ await MainActor.run {
492
+ try? audioBrowser.load(track: track)
493
+ try? audioBrowser.play()
494
+ self.showNowPlaying()
495
+ completion()
496
+ }
497
+ }
498
+ } catch {
499
+ logger.error("Error expanding queue: \(error.localizedDescription)")
500
+ await MainActor.run {
501
+ try? audioBrowser.load(track: track)
502
+ try? audioBrowser.play()
503
+ self.showNowPlaying()
504
+ completion()
505
+ }
506
+ }
507
+ }
508
+ }
509
+ // If track has src, it's playable - load it
510
+ else if track.src != nil {
511
+ Task { @MainActor in
512
+ try? audioBrowser.load(track: track)
513
+ try? audioBrowser.play()
514
+ showNowPlaying()
515
+ completion()
516
+ }
517
+ }
518
+ // If track has url, it's browsable - navigate to it
519
+ else if let url = track.url {
520
+ navigateToUrl(url, completion: completion)
521
+ } else {
522
+ completion()
523
+ }
524
+ }
525
+
526
+ /// Navigates to a browsable URL path, showing error action sheet on failure with retry option.
527
+ private func navigateToUrl(_ url: String, completion: @escaping () -> Void) {
528
+ guard let audioBrowser else {
529
+ completion()
530
+ return
531
+ }
532
+
533
+ Task {
534
+ do {
535
+ let resolved = try await audioBrowser.browserManager.resolve(url, useCache: true)
536
+
537
+ await MainActor.run {
538
+ let listTemplate = self.createListTemplate(for: resolved, path: url)
539
+ self.navigationStack.append(url)
540
+ self.interfaceController.pushTemplate(listTemplate, animated: true, completion: nil)
541
+ completion()
542
+ }
543
+ } catch {
544
+ logger.error("Failed to navigate to \(url): \(error.localizedDescription)")
545
+ await MainActor.run {
546
+ let navError = self.toNavigationError(error)
547
+ self.showNavigationError(navError, path: url)
548
+ completion()
549
+ }
550
+ }
551
+ }
552
+ }
553
+
554
+ // MARK: - Now Playing
555
+
556
+ private func setupNowPlayingTemplate() {
557
+ let template = CPNowPlayingTemplate.shared
558
+
559
+ // Create and register observer for Up Next button
560
+ let observer = NowPlayingObserver(controller: self)
561
+ nowPlayingObserver = observer
562
+ template.add(observer)
563
+
564
+ // Setup custom Now Playing buttons from config
565
+ setupNowPlayingButtons()
566
+
567
+ // Update button states based on config
568
+ updateNowPlayingButtonStates()
569
+ }
570
+
571
+ /// Sets up custom Now Playing buttons based on configuration
572
+ private func setupNowPlayingButtons() {
573
+ let buttons = config.carPlayNowPlayingButtons
574
+ logger.info("Setting up Now Playing buttons: \(buttons.map(\.stringValue))")
575
+
576
+ guard !buttons.isEmpty else {
577
+ CPNowPlayingTemplate.shared.updateNowPlayingButtons([])
578
+ return
579
+ }
580
+
581
+ var nowPlayingButtons: [CPNowPlayingButton] = []
582
+
583
+ for buttonType in buttons {
584
+ switch buttonType {
585
+ case .shuffle:
586
+ let shuffleButton = CPNowPlayingShuffleButton { [weak self] _ in
587
+ self?.handleShuffleButtonTapped()
588
+ }
589
+ nowPlayingButtons.append(shuffleButton)
590
+
591
+ case .repeat:
592
+ let repeatButton = CPNowPlayingRepeatButton { [weak self] _ in
593
+ self?.handleRepeatButtonTapped()
594
+ }
595
+ nowPlayingButtons.append(repeatButton)
596
+
597
+ case .favorite:
598
+ let favoriteButton = CPNowPlayingImageButton(
599
+ image: favoriteButtonImage(isFavorited: isActiveTrackFavorited),
600
+ ) { [weak self] _ in
601
+ self?.handleFavoriteButtonTapped()
602
+ }
603
+ nowPlayingButtons.append(favoriteButton)
604
+
605
+ case .playbackRate:
606
+ let rateButton = CPNowPlayingPlaybackRateButton { [weak self] _ in
607
+ self?.handlePlaybackRateButtonTapped()
608
+ }
609
+ nowPlayingButtons.append(rateButton)
610
+ }
611
+ }
612
+
613
+ CPNowPlayingTemplate.shared.updateNowPlayingButtons(nowPlayingButtons)
614
+ logger.info("Updated Now Playing with \(nowPlayingButtons.count) custom button(s)")
615
+ }
616
+
617
+ /// Returns the appropriate image for the favorite button based on state
618
+ /// Sized to CPNowPlayingButtonMaximumImageSize per Apple docs
619
+ private func favoriteButtonImage(isFavorited: Bool) -> UIImage {
620
+ let symbolName = isFavorited ? "heart.fill" : "heart"
621
+ guard let image = UIImage(systemName: symbolName)?.resized(to: CPNowPlayingButtonMaximumImageSize) else {
622
+ return UIImage()
623
+ }
624
+ return image
625
+ }
626
+
627
+ /// Handles shuffle button tap - toggles shuffle mode
628
+ private func handleShuffleButtonTapped() {
629
+ guard let player = audioBrowser?.getPlayer() else { return }
630
+
631
+ let newEnabled = !player.shuffleEnabled
632
+ player.shuffleEnabled = newEnabled
633
+ logger.info("CarPlay shuffle mode changed: \(newEnabled)")
634
+ }
635
+
636
+ /// Handles repeat button tap - cycles through repeat modes
637
+ private func handleRepeatButtonTapped() {
638
+ guard let player = audioBrowser?.getPlayer() else { return }
639
+
640
+ let currentMode = player.getRepeatMode()
641
+ let newMode: RepeatMode = switch currentMode {
642
+ case .off:
643
+ .track
644
+ case .track:
645
+ .queue
646
+ case .queue:
647
+ .off
648
+ }
649
+
650
+ player.setRepeatMode(newMode)
651
+ logger.info("CarPlay repeat mode changed: \(currentMode.stringValue) → \(newMode.stringValue)")
652
+ }
653
+
654
+ /// Handles favorite button tap - toggles favorite state of current track
655
+ private func handleFavoriteButtonTapped() {
656
+ try? audioBrowser?.toggleActiveTrackFavorited()
657
+ logger.info("CarPlay favorite toggled")
658
+ // Button appearance is updated via onFavoriteChanged subscription
659
+ }
660
+
661
+ /// Handles playback rate button tap - cycles through available rates
662
+ private func handlePlaybackRateButtonTapped() {
663
+ guard let audioBrowser, let player = audioBrowser.getPlayer() else { return }
664
+
665
+ let rates = audioBrowser.playbackRates
666
+ guard !rates.isEmpty else { return }
667
+
668
+ let currentRate = Double(player.rate)
669
+ let nextRate: Double
670
+
671
+ // Find current rate index and cycle to next
672
+ if let currentIndex = rates.firstIndex(where: { (currentRate - $0).magnitude < 0.01 }) {
673
+ // Current rate is in the list - cycle to next
674
+ let nextIndex = (currentIndex + 1) % rates.count
675
+ nextRate = rates[nextIndex]
676
+ } else {
677
+ // Current rate not in list - find first rate greater than current, or wrap to first
678
+ nextRate = rates.first { $0 > currentRate } ?? rates[0]
679
+ }
680
+
681
+ player.rate = Float(nextRate)
682
+ logger.info("CarPlay playback rate changed: \(currentRate) → \(nextRate)")
683
+ }
684
+
685
+ /// Updates the favorite button appearance based on current track's favorite state
686
+ private func updateFavoriteButtonState() {
687
+ guard config.carPlayNowPlayingButtons.contains(.favorite) else { return }
688
+ let favorited = isActiveTrackFavorited
689
+ let buttons = CPNowPlayingTemplate.shared.nowPlayingButtons
690
+
691
+ // Find and update the favorite button (it's a CPNowPlayingImageButton)
692
+ for (index, button) in buttons.enumerated() {
693
+ if button is CPNowPlayingImageButton {
694
+ // Recreate the button with updated image
695
+ let newFavoriteButton = CPNowPlayingImageButton(
696
+ image: favoriteButtonImage(isFavorited: favorited),
697
+ ) { [weak self] _ in
698
+ self?.handleFavoriteButtonTapped()
699
+ }
700
+
701
+ var updatedButtons = buttons
702
+ updatedButtons[index] = newFavoriteButton
703
+ CPNowPlayingTemplate.shared.updateNowPlayingButtons(updatedButtons)
704
+ break
705
+ }
706
+ }
707
+ }
708
+
709
+ /// Updates Now Playing button states based on config and current queue
710
+ private func updateNowPlayingButtonStates() {
711
+ updateNowPlayingUpNextButton()
712
+ updateFavoriteButtonState()
713
+ }
714
+
715
+ /// Updates the Up Next button enabled state based on config and queue size
716
+ private func updateNowPlayingUpNextButton() {
717
+ let template = CPNowPlayingTemplate.shared
718
+ template.isUpNextButtonEnabled = config.carPlayUpNextButton && (audioBrowser?.getPlayer()?.tracks.count ?? 0) > 1
719
+ }
720
+
721
+ private func showNowPlaying() {
722
+ updateNowPlayingButtonStates()
723
+ let nowPlayingTemplate = CPNowPlayingTemplate.shared
724
+ interfaceController.pushTemplate(nowPlayingTemplate, animated: true, completion: nil)
725
+ }
726
+
727
+ // MARK: - Content Change Handlers
728
+
729
+ @MainActor
730
+ private func handleTabsChanged(_ tabs: [Track]) {
731
+ logger.debug("Tabs changed: \(tabs.count) tabs")
732
+ // Rebuild tab bar if we're at the root
733
+ Task {
734
+ await showTabBar(tabs: tabs)
735
+ }
736
+ }
737
+
738
+ @MainActor
739
+ private func handleContentChanged(_ content: ResolvedTrack?) {
740
+ // This callback fires when the main browser's content changes.
741
+ // For CarPlay-specific refreshes (e.g., favorites), use notifyContentChanged instead.
742
+ guard let content else { return }
743
+ refreshTemplatesForPath(content.url, with: content)
744
+ }
745
+
746
+ @MainActor
747
+ private func handleActiveTrackChanged(_ event: PlaybackActiveTrackChangedEvent) {
748
+ logger.debug("handleActiveTrackChanged: \(event.lastTrack?.src ?? "nil") → \(event.track?.src ?? "nil")")
749
+ updatePlayingIndicators()
750
+ // Update favorite button to reflect the new track's favorite state
751
+ updateFavoriteButtonState()
752
+ }
753
+
754
+ /// Updates the isPlaying state on all list items based on the current active track.
755
+ @MainActor
756
+ fileprivate func updatePlayingIndicators() {
757
+ var templates: [CPListTemplate] = []
758
+
759
+ if let tabBar = interfaceController.rootTemplate as? CPTabBarTemplate {
760
+ for template in tabBar.templates {
761
+ if let listTemplate = template as? CPListTemplate {
762
+ templates.append(listTemplate)
763
+ }
764
+ }
765
+ }
766
+
767
+ if let topTemplate = interfaceController.topTemplate as? CPListTemplate,
768
+ !templates.contains(where: { $0 === topTemplate })
769
+ {
770
+ templates.append(topTemplate)
771
+ }
772
+
773
+ for template in templates {
774
+ for section in template.sections {
775
+ for item in section.items {
776
+ guard let listItem = item as? CPListItem,
777
+ let userInfo = listItem.userInfo as? [String: Any],
778
+ let itemSrc = userInfo["src"] as? String
779
+ else { continue }
780
+
781
+ let isPlaying = isActiveTrack(src: itemSrc)
782
+ if listItem.isPlaying != isPlaying {
783
+ logger.debug("Updating isPlaying for \(itemSrc): \(listItem.isPlaying) → \(isPlaying)")
784
+ listItem.isPlaying = isPlaying
785
+ }
786
+ }
787
+ }
788
+ }
789
+ }
790
+
791
+ // MARK: - Public Content Notification
792
+
793
+ /// Notifies CarPlay that content at the given path has changed and should be refreshed.
794
+ /// Called from HybridAudioBrowser.notifyContentChanged() to update CarPlay lists.
795
+ ///
796
+ /// - Parameter path: The path where content has changed (e.g., "/favorites")
797
+ @objc
798
+ public func notifyContentChanged(path: String) {
799
+ guard isStarted else { return }
800
+
801
+ Task { @MainActor in
802
+ await refreshContentForPath(path)
803
+ }
804
+ }
805
+
806
+ /// Fetches fresh content for a path and updates any matching CarPlay templates.
807
+ @MainActor
808
+ private func refreshContentForPath(_ path: String) async {
809
+ guard let audioBrowser else { return }
810
+
811
+ logger.debug("Refreshing CarPlay content for path: \(path)")
812
+
813
+ // Fetch fresh content (bypassing cache since content changed)
814
+ do {
815
+ let resolved = try await audioBrowser.browserManager.resolve(path, useCache: false)
816
+ refreshTemplatesForPath(path, with: resolved)
817
+ } catch {
818
+ logger.error("Failed to refresh content for \(path): \(error.localizedDescription)")
819
+ }
820
+ }
821
+
822
+ /// Updates all CarPlay templates that are displaying the given path.
823
+ @MainActor
824
+ private func refreshTemplatesForPath(_ path: String, with content: ResolvedTrack) {
825
+ logger.debug("Content changed for path: \(path)")
826
+
827
+ // Check if the root template is a tab bar and refresh matching tabs
828
+ if let tabBar = interfaceController.rootTemplate as? CPTabBarTemplate {
829
+ for template in tabBar.templates {
830
+ guard let listTemplate = template as? CPListTemplate,
831
+ let templatePath = getPath(from: listTemplate),
832
+ templatePath == path
833
+ else { continue }
834
+
835
+ logger.info("Refreshing tab template for path: \(path)")
836
+ let sections = createSections(from: content)
837
+ listTemplate.updateSections(sections)
838
+ }
839
+ }
840
+
841
+ // Check if any template in the navigation stack matches the changed path
842
+ // The top template is the currently visible one
843
+ if let topTemplate = interfaceController.topTemplate as? CPListTemplate,
844
+ let templatePath = getPath(from: topTemplate),
845
+ templatePath == path
846
+ {
847
+ logger.info("Refreshing top template for path: \(path)")
848
+ let sections = createSections(from: content)
849
+ topTemplate.updateSections(sections)
850
+ }
851
+ }
852
+
853
+ // MARK: - Error Handling
854
+
855
+ /// Shows a navigation error using CPActionSheetTemplate.
856
+ /// - Parameters:
857
+ /// - error: The NavigationError to display
858
+ /// - path: The path that was being navigated to when the error occurred
859
+ private func showNavigationError(_ error: NavigationError, path: String) {
860
+ let defaultFormatted = defaultFormattedError(error)
861
+
862
+ // Check if custom formatter is configured
863
+ logger.debug("showNavigationError: formatNavigationError is \(self.config.formatNavigationError != nil ? "set" : "nil")")
864
+ if let formatter = config.formatNavigationError {
865
+ logger.debug("Calling formatNavigationError callback...")
866
+ // Call the JS callback and handle result
867
+ let params = FormatNavigationErrorParams(error: error, defaultFormatted: defaultFormatted, path: path)
868
+ formatter(params)
869
+ .then { [weak self] customDisplay in
870
+ self?.logger.debug("formatNavigationError returned: \(String(describing: customDisplay))")
871
+ self?.presentErrorActionSheet(customDisplay: customDisplay ?? defaultFormatted)
872
+ }
873
+ .catch { [weak self] callbackError in
874
+ self?.logger.error("formatNavigationError failed: \(callbackError)")
875
+ // On error, fall back to defaults
876
+ self?.presentErrorActionSheet(customDisplay: defaultFormatted)
877
+ }
878
+ } else {
879
+ presentErrorActionSheet(customDisplay: defaultFormatted)
880
+ }
881
+ }
882
+
883
+ /// Returns the default formatted error for the given navigation error
884
+ private func defaultFormattedError(_ error: NavigationError) -> FormattedNavigationError {
885
+ let title = switch error.code {
886
+ case .contentNotFound:
887
+ "Content Not Found"
888
+ case .networkError:
889
+ "Network Error"
890
+ case .httpError:
891
+ httpErrorTitle(statusCode: error.statusCode.map { Int($0) })
892
+ case .callbackError:
893
+ "Error"
894
+ case .unknownError:
895
+ "Error"
896
+ }
897
+ return FormattedNavigationError(title: title, message: error.message)
898
+ }
899
+
900
+ /// Presents the error action sheet with the given display info.
901
+ /// - Parameter customDisplay: The formatted error to display
902
+ private func presentErrorActionSheet(customDisplay: FormattedNavigationError) {
903
+ // If another template is already presented, dismiss it first
904
+ if interfaceController.presentedTemplate != nil {
905
+ interfaceController.dismissTemplate(animated: false) { [weak self] _, _ in
906
+ self?.showErrorActionSheet(customDisplay: customDisplay)
907
+ }
908
+ } else {
909
+ showErrorActionSheet(customDisplay: customDisplay)
910
+ }
911
+ }
912
+
913
+ /// Actually shows the error action sheet (called after safety checks)
914
+ private func showErrorActionSheet(customDisplay: FormattedNavigationError) {
915
+ // OK action - dismiss the action sheet (use system-localized "OK")
916
+ let okTitle = Bundle(for: UIAlertController.self).localizedString(forKey: "OK", value: "OK", table: nil)
917
+ let ok = CPAlertAction(title: okTitle, style: .cancel) { [weak self] _ in
918
+ self?.interfaceController.dismissTemplate(animated: true, completion: nil)
919
+ }
920
+
921
+ let actionSheet = CPActionSheetTemplate(
922
+ title: customDisplay.title,
923
+ message: customDisplay.message,
924
+ actions: [ok],
925
+ )
926
+
927
+ interfaceController.presentTemplate(actionSheet, animated: true, completion: nil)
928
+ }
929
+
930
+ /// Returns a localized title for HTTP errors based on status code
931
+ private func httpErrorTitle(statusCode: Int?) -> String {
932
+ guard let code = statusCode else { return "Server Error" }
933
+ return HTTPURLResponse.localizedString(forStatusCode: code).capitalized
934
+ }
935
+
936
+ /// Shows a simple error template as root (for initialization errors when no other template exists)
937
+ private func showErrorTemplate(message: String) {
938
+ // CPAlertTemplate cannot be set as root - use a list template instead
939
+ let errorItem = CPListItem(text: message, detailText: nil)
940
+ errorItem.isEnabled = false
941
+ let template = CPListTemplate(
942
+ title: "Error",
943
+ sections: [CPListSection(items: [errorItem])],
944
+ )
945
+ interfaceController.setRootTemplate(template, animated: true, completion: nil)
946
+ }
947
+
948
+ /// Converts a generic Error (typically BrowserError) to a NavigationError
949
+ private func toNavigationError(_ error: Error) -> NavigationError {
950
+ if let browserError = error as? BrowserError {
951
+ switch browserError {
952
+ case .contentNotFound:
953
+ NavigationError(code: .contentNotFound, message: browserError.localizedDescription, statusCode: nil, statusCodeSuccess: nil)
954
+ case let .httpError(code, _):
955
+ NavigationError(code: .httpError, message: browserError.localizedDescription, statusCode: Double(code), statusCodeSuccess: (200 ... 299).contains(code))
956
+ case .networkError:
957
+ NavigationError(code: .networkError, message: browserError.localizedDescription, statusCode: nil, statusCodeSuccess: nil)
958
+ case .invalidConfiguration:
959
+ NavigationError(code: .unknownError, message: browserError.localizedDescription, statusCode: nil, statusCodeSuccess: nil)
960
+ case .callbackError:
961
+ NavigationError(code: .callbackError, message: browserError.localizedDescription, statusCode: nil, statusCodeSuccess: nil)
962
+ }
963
+ } else if let httpError = error as? HttpClient.HttpException {
964
+ // HTTP error from HttpClient (non-2xx response)
965
+ NavigationError(code: .httpError, message: httpError.localizedDescription, statusCode: Double(httpError.code), statusCodeSuccess: (200 ... 299).contains(httpError.code))
966
+ } else if error is URLError {
967
+ // Network error (connection failed, timeout, no internet, etc.)
968
+ NavigationError(code: .networkError, message: error.localizedDescription, statusCode: nil, statusCodeSuccess: nil)
969
+ } else {
970
+ NavigationError(code: .unknownError, message: error.localizedDescription, statusCode: nil, statusCodeSuccess: nil)
971
+ }
972
+ }
973
+
974
+ // MARK: - Image Loading
975
+
976
+ /// Creates an SF Symbol image for tabs - plain systemName, CarPlay handles tinting
977
+ private func sfSymbolImage(_ symbolName: String) -> UIImage? {
978
+ UIImage(systemName: symbolName)
979
+ }
980
+
981
+ /// Creates an SF Symbol image for list items with light/dark mode support
982
+ private func sfSymbolImageForListItem(_ symbolName: String) -> UIImage? {
983
+ guard let symbol = UIImage(systemName: symbolName) else { return nil }
984
+
985
+ let size = symbol.size
986
+ let scale = symbol.scale
987
+
988
+ // Create both light and dark bitmap variants
989
+ let lightImage = renderSymbolToBitmap(symbol, tintColor: .black, size: size, scale: scale)
990
+ let darkImage = renderSymbolToBitmap(symbol, tintColor: .white, size: size, scale: scale)
991
+
992
+ // Combine with UIImageAsset for automatic light/dark switching
993
+ let asset = UIImageAsset()
994
+ asset.register(lightImage, with: UITraitCollection(userInterfaceStyle: .light))
995
+ asset.register(darkImage, with: UITraitCollection(userInterfaceStyle: .dark))
996
+
997
+ return asset.image(with: interfaceController.carTraitCollection)
998
+ }
999
+
1000
+ /// Renders an SF Symbol to a bitmap with the specified tint color
1001
+ private nonisolated func renderSymbolToBitmap(_ symbol: UIImage, tintColor: UIColor, size: CGSize, scale: CGFloat) -> UIImage {
1002
+ UIGraphicsBeginImageContextWithOptions(size, false, scale)
1003
+ defer { UIGraphicsEndImageContext() }
1004
+
1005
+ tintColor.set()
1006
+ symbol.withRenderingMode(.alwaysTemplate).draw(in: CGRect(origin: .zero, size: size))
1007
+
1008
+ guard let rendered = UIGraphicsGetImageFromCurrentImageContext() else {
1009
+ return symbol // Fallback to original if rendering fails
1010
+ }
1011
+ return rendered.withRenderingMode(.alwaysOriginal)
1012
+ }
1013
+
1014
+ private func defaultTabImage() -> UIImage? {
1015
+ sfSymbolImage("music.note.list")
1016
+ }
1017
+
1018
+ /// Cached empty placeholder image to reserve space while artwork loads
1019
+ private lazy var placeholderImage: UIImage? = {
1020
+ let size = CPListItem.maximumImageSize
1021
+ let scale = interfaceController.carTraitCollection.displayScale
1022
+ UIGraphicsBeginImageContextWithOptions(size, false, scale)
1023
+ defer { UIGraphicsEndImageContext() }
1024
+ return UIGraphicsGetImageFromCurrentImageContext()
1025
+ }()
1026
+
1027
+ /// Loads artwork for a track with size context, using the artwork transform if configured.
1028
+ /// - Parameters:
1029
+ /// - track: The track to load artwork for
1030
+ /// - size: The target size in points (will be multiplied by CarPlay display scale)
1031
+ /// - completion: Called with the loaded image, or nil on failure
1032
+ private func loadArtwork(for track: Track, size: CGSize, completion: @escaping @Sendable (UIImage?) -> Void) {
1033
+ guard let browserManager = audioBrowser?.browserManager else {
1034
+ // Fall back to direct URL loading
1035
+ loadArtworkDirect(track: track, completion: completion)
1036
+ return
1037
+ }
1038
+
1039
+ // Convert points to pixels using CarPlay display scale (not iPhone screen scale)
1040
+ let carTraits = interfaceController.carTraitCollection
1041
+ let scale = carTraits.displayScale
1042
+ let imageContext = ImageContext(width: size.width * scale, height: size.height * scale)
1043
+
1044
+ Task {
1045
+ // Resolve artwork URL with size context
1046
+ let imageSource = await browserManager.resolveArtworkUrl(
1047
+ track: track,
1048
+ perRouteConfig: nil,
1049
+ imageContext: imageContext,
1050
+ )
1051
+
1052
+ await MainActor.run {
1053
+ if let imageSource {
1054
+ // Check for SF Symbol URI (e.g., "sf:heart.fill")
1055
+ if imageSource.uri.hasPrefix("sf:") {
1056
+ let symbolName = String(imageSource.uri.dropFirst(3))
1057
+ completion(self.sfSymbolImageForListItem(symbolName))
1058
+ return
1059
+ }
1060
+
1061
+ // Parse URL - skip if invalid
1062
+ guard let url = URL(string: imageSource.uri) else {
1063
+ self.loadArtworkDirect(track: track, completion: completion)
1064
+ return
1065
+ }
1066
+
1067
+ // Capture trait collection before async call
1068
+ let carTraitCollection = self.interfaceController.carTraitCollection
1069
+
1070
+ // Load from resolved URL with any custom headers
1071
+ var options: KingfisherOptionsInfo = []
1072
+ if let headers = imageSource.headers, !headers.isEmpty {
1073
+ let modifier = AnyModifier { request in
1074
+ var request = request
1075
+ for (key, value) in headers {
1076
+ request.setValue(value, forHTTPHeaderField: key)
1077
+ }
1078
+ return request
1079
+ }
1080
+ options.append(.requestModifier(modifier))
1081
+ }
1082
+
1083
+ // Add SVG processor if URL is an SVG
1084
+ if url.pathExtension.lowercased() == "svg" {
1085
+ options.append(.processor(SVGProcessor(size: nil, scale: carTraitCollection.displayScale)))
1086
+ }
1087
+
1088
+ // Capture tinting preference before async call
1089
+ let shouldTint = track.artworkCarPlayTinted ?? false
1090
+
1091
+ KingfisherManager.shared.retrieveImage(with: url, options: options) { result in
1092
+ if case let .success(imageResult) = result {
1093
+ let image = imageResult.image
1094
+
1095
+ if shouldTint {
1096
+ // Apply light/dark tinting for monochrome icons
1097
+ completion(self.createAdaptiveImage(image, carTraitCollection: carTraitCollection))
1098
+ } else {
1099
+ // Regular images (photos, album art) - show as-is
1100
+ completion(image)
1101
+ }
1102
+ } else {
1103
+ completion(nil)
1104
+ }
1105
+ }
1106
+ } else {
1107
+ // No resolved URL - try direct loading as fallback
1108
+ self.loadArtworkDirect(track: track, completion: completion)
1109
+ }
1110
+ }
1111
+ }
1112
+ }
1113
+
1114
+ /// Loads artwork directly from track's artwork URL without transform.
1115
+ private func loadArtworkDirect(track: Track, completion: @escaping @Sendable (UIImage?) -> Void) {
1116
+ guard let artworkUrl = track.artwork ?? track.artworkSource?.uri else {
1117
+ completion(nil)
1118
+ return
1119
+ }
1120
+
1121
+ // Check for SF Symbol URI (e.g., "sf:heart.fill")
1122
+ if artworkUrl.hasPrefix("sf:") {
1123
+ let symbolName = String(artworkUrl.dropFirst(3))
1124
+ completion(sfSymbolImageForListItem(symbolName))
1125
+ return
1126
+ }
1127
+
1128
+ guard let url = URL(string: artworkUrl) else {
1129
+ completion(nil)
1130
+ return
1131
+ }
1132
+
1133
+ // Capture trait collection before async call
1134
+ let carTraitCollection = interfaceController.carTraitCollection
1135
+ let isSvg = url.pathExtension.lowercased() == "svg"
1136
+ let shouldTint = track.artworkCarPlayTinted ?? false
1137
+
1138
+ // Add SVG processor if URL is an SVG
1139
+ var options: KingfisherOptionsInfo = []
1140
+ if isSvg {
1141
+ options.append(.processor(SVGProcessor(size: nil, scale: carTraitCollection.displayScale)))
1142
+ }
1143
+
1144
+ KingfisherManager.shared.retrieveImage(with: url, options: options) { result in
1145
+ if case let .success(imageResult) = result {
1146
+ let image = imageResult.image
1147
+
1148
+ if shouldTint {
1149
+ // Apply light/dark tinting for monochrome icons
1150
+ completion(self.createAdaptiveImage(image, carTraitCollection: carTraitCollection))
1151
+ } else {
1152
+ // Regular images (photos, album art) - show as-is
1153
+ completion(image)
1154
+ }
1155
+ } else {
1156
+ completion(nil)
1157
+ }
1158
+ }
1159
+ }
1160
+
1161
+ /// Renders an image to a bitmap with the specified tint color (for monochrome icons).
1162
+ /// Thread-safe: UIGraphicsBeginImageContextWithOptions is safe to call from any thread (iOS 4+).
1163
+ private nonisolated func renderImageToBitmap(_ image: UIImage, tintColor: UIColor) -> UIImage {
1164
+ UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
1165
+ defer { UIGraphicsEndImageContext() }
1166
+
1167
+ tintColor.set()
1168
+ image.withRenderingMode(.alwaysTemplate).draw(in: CGRect(origin: .zero, size: image.size))
1169
+
1170
+ guard let rendered = UIGraphicsGetImageFromCurrentImageContext() else {
1171
+ return image // Fallback to original if rendering fails
1172
+ }
1173
+ return rendered.withRenderingMode(.alwaysOriginal)
1174
+ }
1175
+
1176
+ /// Creates light/dark tinted variants of an image and returns the appropriate one for current appearance
1177
+ private nonisolated func createAdaptiveImage(_ image: UIImage, carTraitCollection: UITraitCollection) -> UIImage {
1178
+ let lightImage = renderImageToBitmap(image, tintColor: .black)
1179
+ let darkImage = renderImageToBitmap(image, tintColor: .white)
1180
+
1181
+ let asset = UIImageAsset()
1182
+ asset.register(lightImage, with: UITraitCollection(userInterfaceStyle: .light))
1183
+ asset.register(darkImage, with: UITraitCollection(userInterfaceStyle: .dark))
1184
+
1185
+ return asset.image(with: carTraitCollection)
1186
+ }
1187
+ }
1188
+
1189
+ // MARK: - Now Playing Observer
1190
+
1191
+ /// Private helper class for CPNowPlayingTemplateObserver conformance.
1192
+ /// Kept separate from RNABCarPlayController to avoid exposing CarPlay protocols to Obj-C header.
1193
+ private final class NowPlayingObserver: NSObject, CPNowPlayingTemplateObserver, @unchecked Sendable {
1194
+ private let logger = Logger(subsystem: "com.audiobrowser", category: "NowPlayingObserver")
1195
+ private weak var controller: RNABCarPlayController?
1196
+
1197
+ @MainActor
1198
+ init(controller: RNABCarPlayController) {
1199
+ self.controller = controller
1200
+ super.init()
1201
+ }
1202
+
1203
+ func nowPlayingTemplateUpNextButtonTapped(_: CPNowPlayingTemplate) {
1204
+ Task { @MainActor in
1205
+ controller?.handleUpNextButtonTapped()
1206
+ }
1207
+ }
1208
+
1209
+ func nowPlayingTemplateAlbumArtistButtonTapped(_: CPNowPlayingTemplate) {
1210
+ // Album/Artist button functionality - can be implemented later
1211
+ logger.debug("Album/Artist button tapped (not implemented)")
1212
+ }
1213
+ }
1214
+
1215
+ // MARK: - Up Next Handler
1216
+
1217
+ private extension RNABCarPlayController {
1218
+ /// Handles the Up Next button tap from Now Playing screen
1219
+ func handleUpNextButtonTapped() {
1220
+ guard let player = audioBrowser?.getPlayer() else {
1221
+ logger.warning("Player not available for Up Next")
1222
+ return
1223
+ }
1224
+
1225
+ let tracks = player.tracks
1226
+
1227
+ guard !tracks.isEmpty else {
1228
+ logger.debug("No tracks in queue for Up Next")
1229
+ return
1230
+ }
1231
+
1232
+ logger.info("Showing Up Next queue with \(tracks.count) tracks")
1233
+
1234
+ let template = CPListTemplate(
1235
+ title: "Up Next",
1236
+ sections: [createUpNextSections(tracks: tracks, player: player)],
1237
+ )
1238
+
1239
+ // Store reference for queue change updates
1240
+ upNextTemplate = template
1241
+
1242
+ interfaceController.pushTemplate(template, animated: true, completion: nil)
1243
+ }
1244
+
1245
+ /// Creates list items for the Up Next queue
1246
+ func createUpNextSections(tracks: [Track], player: TrackPlayer) -> CPListSection {
1247
+ let items = tracks.enumerated().map { index, track -> CPListItem in
1248
+ createListItem(for: track) { [weak self] _, completion in
1249
+ self?.logger.info("Skipping to track at index \(index): \(track.title)")
1250
+ do {
1251
+ try player.skipTo(index, playWhenReady: true)
1252
+ } catch {
1253
+ self?.logger.error("Failed to skip to track: \(error.localizedDescription)")
1254
+ }
1255
+ completion()
1256
+ }
1257
+ }
1258
+ return CPListSection(items: items)
1259
+ }
1260
+
1261
+ /// Handles queue changes - updates Up Next list if visible
1262
+ @MainActor
1263
+ func handleQueueChanged(_ tracks: [Track]) {
1264
+ // Update the Up Next button enabled state
1265
+ updateNowPlayingUpNextButton()
1266
+
1267
+ // Update the Up Next template if it's currently visible
1268
+ guard let template = upNextTemplate,
1269
+ let player = audioBrowser?.getPlayer()
1270
+ else {
1271
+ return
1272
+ }
1273
+
1274
+ logger.debug("Queue changed, updating Up Next list with \(tracks.count) tracks")
1275
+ template.updateSections([createUpNextSections(tracks: tracks, player: player)])
1276
+ }
1277
+ }
1278
+
1279
+ // MARK: - CPInterfaceControllerDelegate
1280
+
1281
+ /// Separate delegate class to avoid exposing CPInterfaceControllerDelegate to Obj-C header
1282
+ private final class InterfaceControllerDelegate: NSObject, CPInterfaceControllerDelegate {
1283
+ private weak var controller: RNABCarPlayController?
1284
+
1285
+ init(controller: RNABCarPlayController) {
1286
+ self.controller = controller
1287
+ super.init()
1288
+ }
1289
+
1290
+ func templateDidAppear(_ aTemplate: CPTemplate, animated _: Bool) {
1291
+ guard let listTemplate = aTemplate as? CPListTemplate else { return }
1292
+
1293
+ // Update playing indicators when navigating back to a list template
1294
+ controller?.updatePlayingIndicators()
1295
+
1296
+ // Lazy load content for tabs that haven't been loaded yet
1297
+ controller?.loadContentIfNeeded(for: listTemplate)
1298
+ }
1299
+ }
1300
+
1301
+ // MARK: - Lazy Loading
1302
+
1303
+ private extension RNABCarPlayController {
1304
+ /// Loads content for a template if it hasn't been loaded yet (lazy loading for tabs)
1305
+ func loadContentIfNeeded(for template: CPListTemplate) {
1306
+ // Skip if already has content
1307
+ guard template.sections.isEmpty else { return }
1308
+
1309
+ // Get path from userInfo
1310
+ guard let path = getPath(from: template) else { return }
1311
+
1312
+ logger.debug("Lazy loading content for tab: \(path)")
1313
+
1314
+ Task {
1315
+ await loadContent(for: path, into: template)
1316
+ }
1317
+ }
1318
+ }
1319
+
1320
+ // MARK: - UIImage Resize
1321
+
1322
+ private extension UIImage {
1323
+ /// Draws the image centered within the target size, maintaining aspect ratio
1324
+ func resized(to targetSize: CGSize) -> UIImage? {
1325
+ UIGraphicsBeginImageContextWithOptions(targetSize, false, 0.0)
1326
+ defer { UIGraphicsEndImageContext() }
1327
+
1328
+ // Scale to fit while maintaining aspect ratio
1329
+ let widthRatio = targetSize.width / size.width
1330
+ let heightRatio = targetSize.height / size.height
1331
+ let scale = min(widthRatio, heightRatio)
1332
+
1333
+ let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)
1334
+ let origin = CGPoint(
1335
+ x: (targetSize.width - scaledSize.width) / 2,
1336
+ y: (targetSize.height - scaledSize.height) / 2,
1337
+ )
1338
+
1339
+ draw(in: CGRect(origin: origin, size: scaledSize))
1340
+ return UIGraphicsGetImageFromCurrentImageContext()?.withRenderingMode(.alwaysTemplate)
1341
+ }
1342
+ }