react-native-audio-browser 0.2.0-next.8 → 0.2.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 (973) hide show
  1. package/AudioBrowser.podspec +1 -1
  2. package/README.md +2 -180
  3. package/android/build.gradle +1 -17
  4. package/android/gradle.properties +1 -1
  5. package/android/src/main/AndroidManifest.xml +0 -15
  6. package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +141 -401
  7. package/android/src/main/java/com/audiobrowser/Callbacks.kt +6 -4
  8. package/android/src/main/java/com/audiobrowser/Service.kt +18 -67
  9. package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +178 -442
  10. package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +4 -41
  11. package/android/src/main/java/com/audiobrowser/browser/TrackLoadHandler.kt +4 -4
  12. package/android/src/main/java/com/audiobrowser/http/RequestConfigBuilder.kt +97 -109
  13. package/android/src/main/java/com/audiobrowser/model/PlaybackMetadata.kt +10 -6
  14. package/android/src/main/java/com/audiobrowser/model/PlayerSetupOptions.kt +8 -40
  15. package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +46 -40
  16. package/android/src/main/java/com/audiobrowser/player/AutomaticBufferManager.kt +1 -4
  17. package/android/src/main/java/com/audiobrowser/player/DynamicLoadControl.kt +1 -18
  18. package/android/src/main/java/com/audiobrowser/player/MediaFactory.kt +3 -15
  19. package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +134 -422
  20. package/android/src/main/java/com/audiobrowser/player/MediaSessionCommandManager.kt +404 -248
  21. package/android/src/main/java/com/audiobrowser/player/PlaybackProgressUpdateManager.kt +59 -0
  22. package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +5 -25
  23. package/android/src/main/java/com/audiobrowser/player/Player.kt +582 -469
  24. package/android/src/main/java/com/audiobrowser/player/PlayerListener.kt +67 -190
  25. package/android/src/main/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicy.kt +53 -118
  26. package/android/src/main/java/com/audiobrowser/player/SleepTimer.kt +3 -37
  27. package/android/src/main/java/com/audiobrowser/player/TransformingDataSource.kt +40 -85
  28. package/android/src/main/java/com/audiobrowser/util/BrowserPathHelper.kt +0 -24
  29. package/android/src/main/java/com/audiobrowser/util/CoilBitmapLoader.kt +300 -23
  30. package/android/src/main/java/com/audiobrowser/util/EqualizerManager.kt +23 -13
  31. package/android/src/main/java/com/audiobrowser/util/MediaExtrasBuilder.kt +2 -2
  32. package/android/src/main/java/com/audiobrowser/util/MetadataAdapter.kt +3 -4
  33. package/android/src/main/java/com/audiobrowser/util/PlayingStateFactory.kt +1 -2
  34. package/android/src/main/java/com/audiobrowser/util/RatingFactory.kt +72 -0
  35. package/android/src/main/java/com/audiobrowser/util/ResolvedTrackFactory.kt +36 -0
  36. package/android/src/main/java/com/audiobrowser/util/SvgArtworkRenderer.kt +111 -6
  37. package/android/src/main/java/com/audiobrowser/util/SystemVolumeMonitor.kt +8 -22
  38. package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +80 -112
  39. package/android/src/main/res/values/strings.xml +0 -4
  40. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +0 -4
  41. package/android/src/test/java/com/audiobrowser/browser/SimpleRouterTest.kt +2 -1
  42. package/android/src/test/java/com/audiobrowser/http/RequestConfigBuilderTest.kt +0 -72
  43. package/android/src/test/java/com/audiobrowser/player/PlaybackProgressUpdateManagerTest.kt +146 -0
  44. package/ios/Browser/BrowserConfig.swift +11 -41
  45. package/ios/Browser/BrowserManager+URLResolution.swift +93 -213
  46. package/ios/Browser/BrowserManager.swift +110 -303
  47. package/ios/Browser/BrowserPathHelper.swift +3 -32
  48. package/ios/Browser/JsonModels.swift +66 -287
  49. package/ios/CarPlay/CarPlayArtworkResolver.swift +6 -6
  50. package/ios/CarPlay/CarPlayController.swift +149 -739
  51. package/ios/CarPlay/CarPlayImageLoader.swift +8 -57
  52. package/ios/CarPlay/CarPlayListItemFactory.swift +35 -44
  53. package/ios/CarPlay/CarPlayNowPlayingManager.swift +36 -180
  54. package/ios/CarPlay/RNABAudioBrowser.h +10 -15
  55. package/ios/CarPlay/RNABAudioBrowser.m +14 -15
  56. package/ios/CarPlay/RNABCarPlaySceneDelegate.m +0 -2
  57. package/ios/CarPlay/RNABMediaIntentHandler.swift +43 -53
  58. package/ios/CarPlay/Track+CarPlay.swift +63 -0
  59. package/ios/Extension/Capability+RemoteCommand.swift +9 -7
  60. package/ios/Extension/ChapterMetadata+AVFoundation.swift +1 -1
  61. package/ios/Extension/NavigationError+Conversion.swift +1 -8
  62. package/ios/Extension/ResolvedTrack+Copying.swift +4 -10
  63. package/ios/Extension/TimedMetadata+AVFoundation.swift +1 -1
  64. package/ios/Extension/Track+AVPlayer.swift +4 -4
  65. package/ios/Extension/Track+Copying.swift +3 -9
  66. package/ios/Extension/TrackMetadata+AVFoundation.swift +1 -1
  67. package/ios/HybridAudioBrowser.swift +173 -751
  68. package/ios/Model/NitroTypeStubs.swift +224 -299
  69. package/ios/Model/PlayerUpdateOptions.swift +23 -23
  70. package/ios/Model/RemoteCommand.swift +6 -24
  71. package/ios/Model/TrackPlayerError.swift +18 -82
  72. package/ios/NowPlayingInfo/NowPlayingInfoCenter.swift +9 -0
  73. package/ios/NowPlayingInfo/NowPlayingInfoController.swift +121 -99
  74. package/ios/Observer/PlayerItemNotificationObserver.swift +5 -45
  75. package/ios/Observer/PlayerItemPropertyObserver.swift +4 -27
  76. package/ios/Observer/PlayerTimeObserver.swift +53 -3
  77. package/ios/Option/SessionCategories.swift +11 -34
  78. package/ios/Player/LoadSeekCoordinator.swift +1 -16
  79. package/ios/Player/MediaLoader.swift +11 -31
  80. package/ios/Player/NowPlayingUpdater.swift +26 -119
  81. package/ios/Player/PlaybackCoordinator.swift +51 -285
  82. package/ios/Player/PlaybackCoordinatorCallbacks.swift +1 -6
  83. package/ios/Player/PlaybackEffectHandler.swift +9 -19
  84. package/ios/Player/PlaybackErrorHandler.swift +13 -97
  85. package/ios/Player/PlaybackProgressUpdateManager.swift +68 -0
  86. package/ios/Player/PlaybackStateMachine.swift +13 -18
  87. package/ios/Player/PlayingStateManager.swift +2 -3
  88. package/ios/Player/QueueManager.swift +61 -67
  89. package/ios/Player/RetryManager.swift +21 -98
  90. package/ios/Player/SleepTimerHandling.swift +0 -8
  91. package/ios/Player/SleepTimerManager.swift +11 -72
  92. package/ios/RemoteCommand/RemoteCommandController.swift +21 -40
  93. package/ios/Tests/BrowserPathHelperTests.swift +0 -28
  94. package/ios/Tests/CarPlayArtworkResolverTests.swift +39 -39
  95. package/ios/Tests/ChapterMetadataAVFoundationTests.swift +2 -2
  96. package/ios/Tests/LoadSeekCoordinatorTests.swift +7 -40
  97. package/ios/Tests/MediaLoaderTests.swift +20 -32
  98. package/ios/Tests/PlaybackCoordinatorTests.swift +13 -787
  99. package/ios/Tests/PlaybackErrorHandlerTests.swift +46 -380
  100. package/ios/Tests/PlaybackProgressUpdateManagerTests.swift +166 -0
  101. package/ios/Tests/PlaybackStateMachineTests.swift +8 -33
  102. package/ios/Tests/QueueManagerTests.swift +18 -149
  103. package/ios/Tests/ResolvedTrackCopyingTests.swift +8 -15
  104. package/ios/Tests/Support/TestTypes.swift +20 -61
  105. package/ios/Tests/TrackSelectorTests.swift +15 -15
  106. package/ios/TrackPlayer.swift +58 -380
  107. package/ios/TrackPlayerCallbacks.swift +44 -5
  108. package/ios/TrackSelector.swift +5 -5
  109. package/ios/Util/ArtworkImageFetcher.swift +2 -4
  110. package/ios/Util/NetworkMonitor.swift +3 -7
  111. package/ios/Util/OnceValue.swift +0 -34
  112. package/lib/commonjs/AudioBrowser.js +11 -0
  113. package/lib/commonjs/AudioBrowser.js.map +1 -1
  114. package/lib/commonjs/features/browser.js +158 -59
  115. package/lib/commonjs/features/browser.js.map +1 -1
  116. package/lib/commonjs/features/equalizer.js +1 -1
  117. package/lib/commonjs/features/errors.js +5 -37
  118. package/lib/commonjs/features/errors.js.map +1 -1
  119. package/lib/commonjs/features/favorites.js +1 -1
  120. package/lib/commonjs/features/index.js +0 -22
  121. package/lib/commonjs/features/index.js.map +1 -1
  122. package/lib/commonjs/features/metadata.js +3 -3
  123. package/lib/commonjs/features/metadata.js.map +1 -1
  124. package/lib/commonjs/features/network.js +1 -1
  125. package/lib/commonjs/features/nowPlaying.js +1 -36
  126. package/lib/commonjs/features/nowPlaying.js.map +1 -1
  127. package/lib/commonjs/features/output.js +23 -37
  128. package/lib/commonjs/features/output.js.map +1 -1
  129. package/lib/commonjs/features/playback/controls.js +0 -9
  130. package/lib/commonjs/features/playback/controls.js.map +1 -1
  131. package/lib/commonjs/features/playback/index.js +0 -11
  132. package/lib/commonjs/features/playback/index.js.map +1 -1
  133. package/lib/commonjs/features/playback/playWhenReady.js +1 -1
  134. package/lib/commonjs/features/playback/playing.js +1 -1
  135. package/lib/commonjs/features/playback/progress.js +13 -29
  136. package/lib/commonjs/features/playback/progress.js.map +1 -1
  137. package/lib/commonjs/features/playback/state.js +1 -1
  138. package/lib/commonjs/features/playback/volume.js +2 -2
  139. package/lib/commonjs/features/player/options.js +29 -79
  140. package/lib/commonjs/features/player/options.js.map +1 -1
  141. package/lib/commonjs/features/player/setup.js +3 -256
  142. package/lib/commonjs/features/player/setup.js.map +1 -1
  143. package/lib/commonjs/features/queue/activeTrack.js +1 -1
  144. package/lib/commonjs/features/queue/queue.js +5 -12
  145. package/lib/commonjs/features/queue/queue.js.map +1 -1
  146. package/lib/commonjs/features/queue/repeatMode.js +1 -1
  147. package/lib/commonjs/features/queue/shuffle.js +1 -1
  148. package/lib/commonjs/features/rating.js +2 -0
  149. package/lib/commonjs/features/rating.js.map +1 -0
  150. package/lib/commonjs/features/remoteControls.js +104 -14
  151. package/lib/commonjs/features/remoteControls.js.map +1 -1
  152. package/lib/commonjs/features/sleepTimer.js +5 -10
  153. package/lib/commonjs/features/sleepTimer.js.map +1 -1
  154. package/lib/commonjs/native.js +0 -5
  155. package/lib/commonjs/native.js.map +1 -1
  156. package/lib/commonjs/native.web.js +1 -4
  157. package/lib/commonjs/native.web.js.map +1 -1
  158. package/lib/commonjs/types/player.js +2 -0
  159. package/lib/commonjs/types/player.js.map +1 -0
  160. package/lib/commonjs/utils/LazyNativeEmitter.js +8 -8
  161. package/lib/commonjs/utils/LazyNativeEmitter.js.map +1 -1
  162. package/lib/commonjs/utils/NativeUpdatedValue.js +3 -0
  163. package/lib/commonjs/utils/NativeUpdatedValue.js.map +1 -1
  164. package/lib/commonjs/web/NativeAudioBrowser.js +124 -301
  165. package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
  166. package/lib/commonjs/web/SimpleRouter.test.js +174 -0
  167. package/lib/commonjs/web/SimpleRouter.test.js.map +1 -0
  168. package/lib/commonjs/web/TrackPlayer/Player.js +26 -90
  169. package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
  170. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js +293 -0
  171. package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js.map +1 -0
  172. package/lib/commonjs/web/TrackPlayer/RepeatMode.js +1 -1
  173. package/lib/commonjs/web/TrackPlayer/RepeatMode.js.map +1 -1
  174. package/lib/commonjs/web/TrackPlayer/SleepTimer.js +3 -34
  175. package/lib/commonjs/web/TrackPlayer/SleepTimer.js.map +1 -1
  176. package/lib/commonjs/web/TrackPlayer/index.js +5 -13
  177. package/lib/commonjs/web/TrackPlayer/index.js.map +1 -1
  178. package/lib/commonjs/web/browser/BrowserManager.js +50 -176
  179. package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
  180. package/lib/commonjs/web/browser/NavigationErrorManager.js +1 -3
  181. package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -1
  182. package/lib/commonjs/web/browser/SearchManager.js +9 -11
  183. package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
  184. package/lib/commonjs/web/http/HttpClient.js +23 -4
  185. package/lib/commonjs/web/http/HttpClient.js.map +1 -1
  186. package/lib/commonjs/web/http/RequestConfigBuilder.js +58 -107
  187. package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
  188. package/lib/commonjs/web/player/NowPlayingManager.js +5 -6
  189. package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
  190. package/lib/commonjs/web/player/OptionsManager.js +17 -9
  191. package/lib/commonjs/web/player/OptionsManager.js.map +1 -1
  192. package/lib/module/AudioBrowser.js +1 -4
  193. package/lib/module/AudioBrowser.js.map +1 -1
  194. package/lib/module/features/browser.js +156 -53
  195. package/lib/module/features/browser.js.map +1 -1
  196. package/lib/module/features/equalizer.js +1 -1
  197. package/lib/module/features/errors.js +5 -37
  198. package/lib/module/features/errors.js.map +1 -1
  199. package/lib/module/features/favorites.js +1 -1
  200. package/lib/module/features/index.js +0 -2
  201. package/lib/module/features/index.js.map +1 -1
  202. package/lib/module/features/metadata.js +3 -3
  203. package/lib/module/features/metadata.js.map +1 -1
  204. package/lib/module/features/network.js +1 -1
  205. package/lib/module/features/nowPlaying.js +1 -34
  206. package/lib/module/features/nowPlaying.js.map +1 -1
  207. package/lib/module/features/output.js +19 -32
  208. package/lib/module/features/output.js.map +1 -1
  209. package/lib/module/features/playback/controls.js +0 -8
  210. package/lib/module/features/playback/controls.js.map +1 -1
  211. package/lib/module/features/playback/index.js +0 -1
  212. package/lib/module/features/playback/index.js.map +1 -1
  213. package/lib/module/features/playback/playWhenReady.js +1 -1
  214. package/lib/module/features/playback/playing.js +1 -1
  215. package/lib/module/features/playback/progress.js +13 -29
  216. package/lib/module/features/playback/progress.js.map +1 -1
  217. package/lib/module/features/playback/state.js +1 -1
  218. package/lib/module/features/playback/volume.js +2 -2
  219. package/lib/module/features/player/options.js +29 -79
  220. package/lib/module/features/player/options.js.map +1 -1
  221. package/lib/module/features/player/setup.js +3 -256
  222. package/lib/module/features/player/setup.js.map +1 -1
  223. package/lib/module/features/queue/activeTrack.js +1 -1
  224. package/lib/module/features/queue/queue.js +5 -12
  225. package/lib/module/features/queue/queue.js.map +1 -1
  226. package/lib/module/features/queue/repeatMode.js +1 -1
  227. package/lib/module/features/queue/shuffle.js +1 -1
  228. package/lib/module/features/rating.js +2 -0
  229. package/lib/module/features/rating.js.map +1 -0
  230. package/lib/module/features/remoteControls.js +103 -13
  231. package/lib/module/features/remoteControls.js.map +1 -1
  232. package/lib/module/features/sleepTimer.js +5 -10
  233. package/lib/module/features/sleepTimer.js.map +1 -1
  234. package/lib/module/native.js +0 -5
  235. package/lib/module/native.js.map +1 -1
  236. package/lib/module/native.web.js +1 -4
  237. package/lib/module/native.web.js.map +1 -1
  238. package/lib/module/types/player.js +2 -0
  239. package/lib/module/types/player.js.map +1 -0
  240. package/lib/module/utils/LazyNativeEmitter.js +8 -8
  241. package/lib/module/utils/LazyNativeEmitter.js.map +1 -1
  242. package/lib/module/utils/NativeUpdatedValue.js +3 -0
  243. package/lib/module/utils/NativeUpdatedValue.js.map +1 -1
  244. package/lib/module/web/NativeAudioBrowser.js +125 -302
  245. package/lib/module/web/NativeAudioBrowser.js.map +1 -1
  246. package/lib/module/web/SimpleRouter.test.js +174 -0
  247. package/lib/module/web/SimpleRouter.test.js.map +1 -0
  248. package/lib/module/web/TrackPlayer/Player.js +26 -90
  249. package/lib/module/web/TrackPlayer/Player.js.map +1 -1
  250. package/lib/module/web/TrackPlayer/PlaylistPlayer.js +288 -0
  251. package/lib/module/web/TrackPlayer/PlaylistPlayer.js.map +1 -0
  252. package/lib/module/web/TrackPlayer/RepeatMode.js +1 -1
  253. package/lib/module/web/TrackPlayer/RepeatMode.js.map +1 -1
  254. package/lib/module/web/TrackPlayer/SleepTimer.js +3 -34
  255. package/lib/module/web/TrackPlayer/SleepTimer.js.map +1 -1
  256. package/lib/module/web/TrackPlayer/index.js +1 -2
  257. package/lib/module/web/TrackPlayer/index.js.map +1 -1
  258. package/lib/module/web/browser/BrowserManager.js +50 -176
  259. package/lib/module/web/browser/BrowserManager.js.map +1 -1
  260. package/lib/module/web/browser/NavigationErrorManager.js +1 -3
  261. package/lib/module/web/browser/NavigationErrorManager.js.map +1 -1
  262. package/lib/module/web/browser/SearchManager.js +9 -11
  263. package/lib/module/web/browser/SearchManager.js.map +1 -1
  264. package/lib/module/web/http/HttpClient.js +23 -4
  265. package/lib/module/web/http/HttpClient.js.map +1 -1
  266. package/lib/module/web/http/RequestConfigBuilder.js +58 -107
  267. package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
  268. package/lib/module/web/player/NowPlayingManager.js +5 -7
  269. package/lib/module/web/player/NowPlayingManager.js.map +1 -1
  270. package/lib/module/web/player/OptionsManager.js +17 -9
  271. package/lib/module/web/player/OptionsManager.js.map +1 -1
  272. package/lib/typescript/src/AudioBrowser.d.ts +1 -0
  273. package/lib/typescript/src/AudioBrowser.d.ts.map +1 -1
  274. package/lib/typescript/src/features/browser.d.ts +1 -40
  275. package/lib/typescript/src/features/browser.d.ts.map +1 -1
  276. package/lib/typescript/src/features/equalizer.d.ts +1 -1
  277. package/lib/typescript/src/features/errors.d.ts +6 -61
  278. package/lib/typescript/src/features/errors.d.ts.map +1 -1
  279. package/lib/typescript/src/features/favorites.d.ts +1 -1
  280. package/lib/typescript/src/features/index.d.ts +0 -2
  281. package/lib/typescript/src/features/index.d.ts.map +1 -1
  282. package/lib/typescript/src/features/metadata.d.ts +7 -9
  283. package/lib/typescript/src/features/metadata.d.ts.map +1 -1
  284. package/lib/typescript/src/features/network.d.ts +1 -1
  285. package/lib/typescript/src/features/nowPlaying.d.ts +1 -28
  286. package/lib/typescript/src/features/nowPlaying.d.ts.map +1 -1
  287. package/lib/typescript/src/features/output.d.ts +18 -28
  288. package/lib/typescript/src/features/output.d.ts.map +1 -1
  289. package/lib/typescript/src/features/playback/controls.d.ts +0 -5
  290. package/lib/typescript/src/features/playback/controls.d.ts.map +1 -1
  291. package/lib/typescript/src/features/playback/index.d.ts +0 -1
  292. package/lib/typescript/src/features/playback/index.d.ts.map +1 -1
  293. package/lib/typescript/src/features/playback/playWhenReady.d.ts +1 -1
  294. package/lib/typescript/src/features/playback/playing.d.ts +1 -1
  295. package/lib/typescript/src/features/playback/progress.d.ts +1 -4
  296. package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
  297. package/lib/typescript/src/features/playback/state.d.ts +1 -1
  298. package/lib/typescript/src/features/playback/volume.d.ts +2 -2
  299. package/lib/typescript/src/features/player/options.d.ts +127 -176
  300. package/lib/typescript/src/features/player/options.d.ts.map +1 -1
  301. package/lib/typescript/src/features/player/setup.d.ts +167 -213
  302. package/lib/typescript/src/features/player/setup.d.ts.map +1 -1
  303. package/lib/typescript/src/features/queue/activeTrack.d.ts +1 -1
  304. package/lib/typescript/src/features/queue/queue.d.ts +4 -11
  305. package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
  306. package/lib/typescript/src/features/queue/repeatMode.d.ts +1 -1
  307. package/lib/typescript/src/features/queue/shuffle.d.ts +1 -1
  308. package/lib/typescript/src/features/rating.d.ts +14 -0
  309. package/lib/typescript/src/features/rating.d.ts.map +1 -0
  310. package/lib/typescript/src/features/remoteControls.d.ts +35 -23
  311. package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
  312. package/lib/typescript/src/features/sleepTimer.d.ts +4 -19
  313. package/lib/typescript/src/features/sleepTimer.d.ts.map +1 -1
  314. package/lib/typescript/src/index.d.ts.map +1 -1
  315. package/lib/typescript/src/native.d.ts +2 -1
  316. package/lib/typescript/src/native.d.ts.map +1 -1
  317. package/lib/typescript/src/native.web.d.ts +3 -0
  318. package/lib/typescript/src/native.web.d.ts.map +1 -0
  319. package/lib/typescript/src/specs/audio-browser.nitro.d.ts +225 -17
  320. package/lib/typescript/src/specs/audio-browser.nitro.d.ts.map +1 -1
  321. package/lib/typescript/src/types/browser-native.d.ts +3 -9
  322. package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
  323. package/lib/typescript/src/types/browser-nodes.d.ts +13 -145
  324. package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
  325. package/lib/typescript/src/types/browser.d.ts +85 -349
  326. package/lib/typescript/src/types/browser.d.ts.map +1 -1
  327. package/lib/typescript/src/types/player.d.ts +64 -0
  328. package/lib/typescript/src/types/player.d.ts.map +1 -0
  329. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts +6 -8
  330. package/lib/typescript/src/utils/LazyNativeEmitter.d.ts.map +1 -1
  331. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts +2 -0
  332. package/lib/typescript/src/utils/NativeUpdatedValue.d.ts.map +1 -1
  333. package/lib/typescript/src/utils/resolveAssetSource.d.ts.map +1 -1
  334. package/lib/typescript/src/web/NativeAudioBrowser.d.ts +188 -0
  335. package/lib/typescript/src/web/NativeAudioBrowser.d.ts.map +1 -0
  336. package/lib/typescript/src/web/SimpleRouter.d.ts +40 -0
  337. package/lib/typescript/src/web/SimpleRouter.d.ts.map +1 -0
  338. package/lib/typescript/src/web/SimpleRouter.test.d.ts +2 -0
  339. package/lib/typescript/src/web/SimpleRouter.test.d.ts.map +1 -0
  340. package/lib/typescript/src/web/TrackPlayer/Event.d.ts +11 -0
  341. package/lib/typescript/src/web/TrackPlayer/Event.d.ts.map +1 -0
  342. package/lib/typescript/src/web/TrackPlayer/Player.d.ts +59 -0
  343. package/lib/typescript/src/web/TrackPlayer/Player.d.ts.map +1 -0
  344. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts +39 -0
  345. package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts.map +1 -0
  346. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts +6 -0
  347. package/lib/typescript/src/web/TrackPlayer/RepeatMode.d.ts.map +1 -0
  348. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts +4 -0
  349. package/lib/typescript/src/web/TrackPlayer/SetupNotCalledError.d.ts.map +1 -0
  350. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts +34 -0
  351. package/lib/typescript/src/web/TrackPlayer/SleepTimer.d.ts.map +1 -0
  352. package/lib/typescript/src/web/TrackPlayer/State.d.ts +13 -0
  353. package/lib/typescript/src/web/TrackPlayer/State.d.ts.map +1 -0
  354. package/lib/typescript/src/web/TrackPlayer/index.d.ts +5 -0
  355. package/lib/typescript/src/web/TrackPlayer/index.d.ts.map +1 -0
  356. package/lib/typescript/src/web/browser/BrowserManager.d.ts +154 -0
  357. package/lib/typescript/src/web/browser/BrowserManager.d.ts.map +1 -0
  358. package/lib/typescript/src/web/browser/FavoriteManager.d.ts +42 -0
  359. package/lib/typescript/src/web/browser/FavoriteManager.d.ts.map +1 -0
  360. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts +40 -0
  361. package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts.map +1 -0
  362. package/lib/typescript/src/web/browser/SearchManager.d.ts +22 -0
  363. package/lib/typescript/src/web/browser/SearchManager.d.ts.map +1 -0
  364. package/lib/typescript/src/web/http/HttpClient.d.ts +25 -0
  365. package/lib/typescript/src/web/http/HttpClient.d.ts.map +1 -0
  366. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts +98 -0
  367. package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts.map +1 -0
  368. package/lib/typescript/src/web/player/NowPlayingManager.d.ts +34 -0
  369. package/lib/typescript/src/web/player/NowPlayingManager.d.ts.map +1 -0
  370. package/lib/typescript/src/web/player/OptionsManager.d.ts +32 -0
  371. package/lib/typescript/src/web/player/OptionsManager.d.ts.map +1 -0
  372. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts +69 -0
  373. package/lib/typescript/src/web/util/BrowserPathHelper.d.ts.map +1 -0
  374. package/lib/typescript/src/web/util/shuffle.d.ts +8 -0
  375. package/lib/typescript/src/web/util/shuffle.d.ts.map +1 -0
  376. package/nitrogen/generated/android/AudioBrowser+autolinking.cmake +2 -4
  377. package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +10 -24
  378. package/nitrogen/generated/android/c++/JAndroidAudioContentType.hpp +6 -6
  379. package/nitrogen/generated/android/c++/JAndroidOptions.hpp +20 -10
  380. package/nitrogen/generated/android/c++/JAndroidUpdateOptions.hpp +86 -0
  381. package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +1 -33
  382. package/nitrogen/generated/android/c++/JBrowseResult.hpp +0 -5
  383. package/nitrogen/generated/android/c++/JCarPlayNowPlayingButton.hpp +3 -3
  384. package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +0 -6
  385. package/nitrogen/generated/android/c++/JFormattedNavigationError.hpp +2 -3
  386. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +0 -6
  387. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.hpp +147 -0
  388. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +0 -8
  389. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +0 -6
  390. package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +0 -6
  391. package/nitrogen/generated/android/c++/JFunc_void_IosOutput.hpp +80 -0
  392. package/nitrogen/generated/android/c++/JFunc_void_NowPlayingMetadata.hpp +10 -0
  393. package/nitrogen/generated/android/c++/JFunc_void_Options.hpp +9 -14
  394. package/nitrogen/generated/android/c++/JFunc_void_Playback.hpp +0 -2
  395. package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +0 -6
  396. package/nitrogen/generated/android/c++/JFunc_void_PlaybackErrorEvent.hpp +0 -2
  397. package/nitrogen/generated/android/c++/JFunc_void_RemoteSetRatingEvent.hpp +87 -0
  398. package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +10 -10
  399. package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +0 -6
  400. package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +0 -6
  401. package/nitrogen/generated/android/c++/JHeartRating.hpp +57 -0
  402. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +221 -245
  403. package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.hpp +22 -25
  404. package/nitrogen/generated/android/c++/JImageRowItem.hpp +3 -33
  405. package/nitrogen/generated/android/c++/{JOutput.hpp → JIosOutput.hpp} +15 -15
  406. package/nitrogen/generated/android/c++/JIosOutputType.hpp +85 -0
  407. package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +1 -33
  408. package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +28 -61
  409. package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +9 -20
  410. package/nitrogen/generated/android/c++/JNativeUpdateOptions.hpp +24 -21
  411. package/nitrogen/generated/android/c++/JNavigationErrorType.hpp +0 -6
  412. package/nitrogen/generated/android/c++/JNitroAndroidUpdateOptions.hpp +20 -10
  413. package/nitrogen/generated/android/c++/{JRemoteButton.hpp → JNotificationButton.hpp} +20 -20
  414. package/nitrogen/generated/android/c++/JNotificationButtonLayout.hpp +94 -0
  415. package/nitrogen/generated/android/c++/JNowPlayingMetadata.hpp +17 -3
  416. package/nitrogen/generated/android/c++/JNowPlayingUpdate.hpp +3 -7
  417. package/nitrogen/generated/android/c++/JOptions.hpp +14 -19
  418. package/nitrogen/generated/android/c++/{JNativeAndroidSetupOptions.hpp → JPartialAndroidSetupPlayerOptions.hpp} +10 -10
  419. package/nitrogen/generated/android/c++/{JNativeIOSSetupOptions.hpp → JPartialIOSSetupPlayerOptions.hpp} +10 -10
  420. package/nitrogen/generated/android/c++/JPartialSetupPlayerOptions.hpp +96 -0
  421. package/nitrogen/generated/android/c++/JPercentageRating.hpp +57 -0
  422. package/nitrogen/generated/android/c++/JPlayback.hpp +0 -2
  423. package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +0 -6
  424. package/nitrogen/generated/android/c++/JPlaybackError.hpp +3 -18
  425. package/nitrogen/generated/android/c++/JPlaybackErrorEvent.hpp +0 -2
  426. package/nitrogen/generated/android/c++/JPlaybackState.hpp +3 -3
  427. package/nitrogen/generated/android/c++/JPlayerCapabilities.hpp +5 -11
  428. package/nitrogen/generated/android/c++/JRatingType.hpp +73 -0
  429. package/nitrogen/generated/android/c++/JRemoteSetRatingEvent.hpp +66 -0
  430. package/nitrogen/generated/android/c++/JResolvedTrack.hpp +5 -23
  431. package/nitrogen/generated/android/c++/JRetryConfig.hpp +7 -11
  432. package/nitrogen/generated/android/c++/JSearchMode.hpp +9 -27
  433. package/nitrogen/generated/android/c++/JSearchParams.hpp +3 -9
  434. package/nitrogen/generated/android/c++/JStarRating.hpp +57 -0
  435. package/nitrogen/generated/android/c++/{JGateEvent.hpp → JThumbsRating.hpp} +15 -16
  436. package/nitrogen/generated/android/c++/JTrack.hpp +5 -23
  437. package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +0 -6
  438. package/nitrogen/generated/android/c++/JTransformableRequestConfig.hpp +1 -14
  439. package/nitrogen/generated/android/c++/JUpdateOptions.hpp +105 -0
  440. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.cpp +34 -0
  441. package/nitrogen/generated/android/c++/JVariant_HeartRating_ThumbsRating_StarRating_PercentageRating.hpp +105 -0
  442. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.cpp +26 -0
  443. package/nitrogen/generated/android/c++/JVariant_NullType_NotificationButtonLayout.hpp +75 -0
  444. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidAudioContentType.kt +4 -4
  445. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidOptions.kt +9 -3
  446. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/AndroidUpdateOptions.kt +50 -0
  447. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ArtworkRequestConfig.kt +4 -10
  448. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CarPlayNowPlayingButton.kt +3 -3
  449. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormattedNavigationError.kt +2 -2
  450. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.kt → Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.kt} +12 -12
  451. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_Output.kt → Func_void_IosOutput.kt} +9 -9
  452. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Func_void_GateEvent.kt → Func_void_RemoteSetRatingEvent.kt} +9 -9
  453. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_void_bool.kt +9 -9
  454. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Gate.kt → HeartRating.kt} +6 -9
  455. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/HybridAudioBrowserSpec.kt +109 -103
  456. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageRowItem.kt +3 -24
  457. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Output.kt → IosOutput.kt} +6 -6
  458. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IosOutputType.kt +32 -0
  459. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaRequestConfig.kt +4 -10
  460. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +6 -18
  461. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeRouteEntry.kt +4 -4
  462. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeUpdateOptions.kt +6 -6
  463. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NavigationErrorType.kt +1 -3
  464. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroAndroidUpdateOptions.kt +9 -3
  465. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoritesMatchMode.kt → NotificationButton.kt} +8 -5
  466. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NotificationButtonLayout.kt +50 -0
  467. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingMetadata.kt +6 -3
  468. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NowPlayingUpdate.kt +3 -6
  469. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Options.kt +3 -3
  470. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NativeAndroidSetupOptions.kt → PartialAndroidSetupPlayerOptions.kt} +5 -5
  471. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{NativeIOSSetupOptions.kt → PartialIOSSetupPlayerOptions.kt} +5 -5
  472. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PartialSetupPlayerOptions.kt +47 -0
  473. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{GateDecision.kt → PercentageRating.kt} +6 -9
  474. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackError.kt +3 -12
  475. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackState.kt +6 -6
  476. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlayerCapabilities.kt +2 -2
  477. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{GateReason.kt → RatingType.kt} +10 -5
  478. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{ArtworkVariants.kt → RemoteSetRatingEvent.kt} +6 -9
  479. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ResolvedTrack.kt +3 -12
  480. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RetryConfig.kt +4 -7
  481. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchMode.kt +6 -12
  482. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SearchParams.kt +3 -6
  483. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{GateEvent.kt → StarRating.kt} +6 -6
  484. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{FavoriteConfig.kt → ThumbsRating.kt} +6 -6
  485. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Track.kt +3 -12
  486. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TransformableRequestConfig.kt +4 -7
  487. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/UpdateOptions.kt +53 -0
  488. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.kt +77 -0
  489. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/{Variant_NullType_RemoteButtonLayout.kt → Variant_NullType_NotificationButtonLayout.kt} +9 -9
  490. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +28 -139
  491. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +489 -1125
  492. package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +45 -66
  493. package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +118 -176
  494. package/nitrogen/generated/ios/swift/AndroidAudioContentType.swift +8 -8
  495. package/nitrogen/generated/ios/swift/AndroidOptions.swift +22 -12
  496. package/nitrogen/generated/ios/swift/AndroidUpdateOptions.swift +119 -0
  497. package/nitrogen/generated/ios/swift/ArtworkRequestConfig.swift +1 -95
  498. package/nitrogen/generated/ios/swift/CarPlayNowPlayingButton.swift +4 -4
  499. package/nitrogen/generated/ios/swift/FormattedNavigationError.swift +4 -17
  500. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError______BrowserSourceCallbackParam.swift +68 -0
  501. package/nitrogen/generated/ios/swift/{Func_void_Output.swift → Func_void_IosOutput.swift} +10 -10
  502. package/nitrogen/generated/ios/swift/{Func_void_GateEvent.swift → Func_void_RemoteSetRatingEvent.swift} +10 -10
  503. package/nitrogen/generated/ios/swift/Func_void_bool.swift +5 -5
  504. package/nitrogen/generated/ios/swift/{Func_void_std__shared_ptr_Promise_GateDecision__.swift → Func_void_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError___.swift} +17 -17
  505. package/nitrogen/generated/ios/swift/HeartRating.swift +29 -0
  506. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec.swift +14 -20
  507. package/nitrogen/generated/ios/swift/HybridAudioBrowserSpec_cxx.swift +197 -230
  508. package/nitrogen/generated/ios/swift/ImageRowItem.swift +2 -121
  509. package/nitrogen/generated/ios/swift/{Output.swift → IosOutput.swift} +7 -7
  510. package/nitrogen/generated/ios/swift/IosOutputType.swift +76 -0
  511. package/nitrogen/generated/ios/swift/MediaRequestConfig.swift +1 -107
  512. package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +22 -161
  513. package/nitrogen/generated/ios/swift/NativeRouteEntry.swift +19 -19
  514. package/nitrogen/generated/ios/swift/NativeUpdateOptions.swift +25 -12
  515. package/nitrogen/generated/ios/swift/NavigationErrorType.swift +0 -8
  516. package/nitrogen/generated/ios/swift/NitroAndroidUpdateOptions.swift +41 -12
  517. package/nitrogen/generated/ios/swift/{RemoteButton.swift → NotificationButton.swift} +10 -10
  518. package/nitrogen/generated/ios/swift/NotificationButtonLayout.swift +92 -0
  519. package/nitrogen/generated/ios/swift/NowPlayingMetadata.swift +48 -1
  520. package/nitrogen/generated/ios/swift/NowPlayingUpdate.swift +1 -19
  521. package/nitrogen/generated/ios/swift/Options.swift +4 -10
  522. package/nitrogen/generated/ios/swift/{NativeAndroidSetupOptions.swift → PartialAndroidSetupPlayerOptions.swift} +5 -5
  523. package/nitrogen/generated/ios/swift/{NativeIOSSetupOptions.swift → PartialIOSSetupPlayerOptions.swift} +5 -5
  524. package/nitrogen/generated/ios/swift/PartialSetupPlayerOptions.swift +101 -0
  525. package/nitrogen/generated/ios/swift/PercentageRating.swift +29 -0
  526. package/nitrogen/generated/ios/swift/PlaybackError.swift +2 -43
  527. package/nitrogen/generated/ios/swift/PlaybackState.swift +4 -4
  528. package/nitrogen/generated/ios/swift/PlayerCapabilities.swift +8 -27
  529. package/nitrogen/generated/ios/swift/RatingType.swift +60 -0
  530. package/nitrogen/generated/ios/swift/RemoteSetRatingEvent.swift +58 -0
  531. package/nitrogen/generated/ios/swift/ResolvedTrack.swift +8 -74
  532. package/nitrogen/generated/ios/swift/RetryConfig.swift +4 -35
  533. package/nitrogen/generated/ios/swift/SearchMode.swift +12 -36
  534. package/nitrogen/generated/ios/swift/SearchParams.swift +2 -7
  535. package/nitrogen/generated/ios/swift/StarRating.swift +29 -0
  536. package/nitrogen/generated/ios/swift/ThumbsRating.swift +29 -0
  537. package/nitrogen/generated/ios/swift/Track.swift +8 -74
  538. package/nitrogen/generated/ios/swift/TransformableRequestConfig.swift +1 -60
  539. package/nitrogen/generated/ios/swift/UpdateOptions.swift +143 -0
  540. package/nitrogen/generated/ios/swift/Variant_HeartRating_ThumbsRating_StarRating_PercentageRating.swift +20 -0
  541. package/nitrogen/generated/ios/swift/{Variant_NullType_RemoteButtonLayout.swift → Variant_NullType_NotificationButtonLayout.swift} +3 -3
  542. package/nitrogen/generated/shared/c++/AndroidAudioContentType.hpp +10 -10
  543. package/nitrogen/generated/shared/c++/AndroidOptions.hpp +19 -8
  544. package/nitrogen/generated/shared/c++/AndroidUpdateOptions.hpp +109 -0
  545. package/nitrogen/generated/shared/c++/ArtworkRequestConfig.hpp +2 -10
  546. package/nitrogen/generated/shared/c++/CarPlayNowPlayingButton.hpp +6 -6
  547. package/nitrogen/generated/shared/c++/EqualizerSettings.hpp +2 -0
  548. package/nitrogen/generated/shared/c++/FormattedNavigationError.hpp +5 -6
  549. package/nitrogen/generated/shared/c++/{GateEvent.hpp → HeartRating.hpp} +17 -18
  550. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.cpp +18 -21
  551. package/nitrogen/generated/shared/c++/HybridAudioBrowserSpec.hpp +35 -44
  552. package/nitrogen/generated/shared/c++/ImageRowItem.hpp +2 -33
  553. package/nitrogen/generated/shared/c++/{Output.hpp → IosOutput.hpp} +18 -18
  554. package/nitrogen/generated/shared/c++/IosOutputType.hpp +112 -0
  555. package/nitrogen/generated/shared/c++/MediaRequestConfig.hpp +2 -10
  556. package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +15 -31
  557. package/nitrogen/generated/shared/c++/NativeRouteEntry.hpp +5 -5
  558. package/nitrogen/generated/shared/c++/NativeUpdateOptions.hpp +7 -9
  559. package/nitrogen/generated/shared/c++/NavigationErrorType.hpp +0 -8
  560. package/nitrogen/generated/shared/c++/NitroAndroidUpdateOptions.hpp +19 -8
  561. package/nitrogen/generated/shared/c++/{RemoteButton.hpp → NotificationButton.hpp} +25 -25
  562. package/nitrogen/generated/shared/c++/NotificationButtonLayout.hpp +102 -0
  563. package/nitrogen/generated/shared/c++/NowPlayingMetadata.hpp +19 -3
  564. package/nitrogen/generated/shared/c++/NowPlayingUpdate.hpp +2 -6
  565. package/nitrogen/generated/shared/c++/Options.hpp +8 -8
  566. package/nitrogen/generated/shared/c++/{NativeAndroidSetupOptions.hpp → PartialAndroidSetupPlayerOptions.hpp} +11 -11
  567. package/nitrogen/generated/shared/c++/{NativeIOSSetupOptions.hpp → PartialIOSSetupPlayerOptions.hpp} +11 -11
  568. package/nitrogen/generated/shared/c++/PartialSetupPlayerOptions.hpp +104 -0
  569. package/nitrogen/generated/shared/c++/{FavoriteConfig.hpp → PercentageRating.hpp} +17 -18
  570. package/nitrogen/generated/shared/c++/PlaybackError.hpp +3 -18
  571. package/nitrogen/generated/shared/c++/PlaybackState.hpp +9 -9
  572. package/nitrogen/generated/shared/c++/PlayerCapabilities.hpp +6 -9
  573. package/nitrogen/generated/shared/c++/RatingType.hpp +96 -0
  574. package/nitrogen/generated/shared/c++/RemoteSetRatingEvent.hpp +94 -0
  575. package/nitrogen/generated/shared/c++/ResolvedTrack.hpp +5 -24
  576. package/nitrogen/generated/shared/c++/RetryConfig.hpp +6 -10
  577. package/nitrogen/generated/shared/c++/SearchMode.hpp +15 -39
  578. package/nitrogen/generated/shared/c++/SearchParams.hpp +2 -9
  579. package/nitrogen/generated/shared/c++/{Gate.hpp → StarRating.hpp} +16 -21
  580. package/nitrogen/generated/shared/c++/{GateDecision.hpp → ThumbsRating.hpp} +17 -23
  581. package/nitrogen/generated/shared/c++/Track.hpp +5 -24
  582. package/nitrogen/generated/shared/c++/TransformableRequestConfig.hpp +2 -6
  583. package/nitrogen/generated/shared/c++/UpdateOptions.hpp +111 -0
  584. package/package.json +5 -12
  585. package/src/AudioBrowser.ts +1 -4
  586. package/src/features/browser.ts +201 -58
  587. package/src/features/equalizer.ts +1 -1
  588. package/src/features/errors.ts +5 -71
  589. package/src/features/favorites.ts +1 -1
  590. package/src/features/index.ts +0 -2
  591. package/src/features/metadata.ts +15 -9
  592. package/src/features/network.ts +1 -1
  593. package/src/features/nowPlaying.ts +1 -37
  594. package/src/features/output.ts +22 -35
  595. package/src/features/playback/controls.ts +0 -8
  596. package/src/features/playback/index.ts +0 -1
  597. package/src/features/playback/playWhenReady.ts +1 -1
  598. package/src/features/playback/playing.ts +1 -1
  599. package/src/features/playback/progress.ts +13 -32
  600. package/src/features/playback/state.ts +1 -1
  601. package/src/features/playback/volume.ts +2 -2
  602. package/src/features/player/options.ts +110 -217
  603. package/src/features/player/setup.ts +175 -397
  604. package/src/features/queue/activeTrack.ts +1 -1
  605. package/src/features/queue/queue.ts +6 -17
  606. package/src/features/queue/repeatMode.ts +1 -1
  607. package/src/features/queue/shuffle.ts +1 -1
  608. package/src/features/rating.ts +17 -0
  609. package/src/features/remoteControls.ts +115 -13
  610. package/src/features/sleepTimer.ts +5 -24
  611. package/src/native.ts +0 -5
  612. package/src/native.web.ts +1 -4
  613. package/src/specs/audio-browser.nitro.ts +41 -131
  614. package/src/types/browser-native.ts +6 -19
  615. package/src/types/browser-nodes.ts +13 -160
  616. package/src/types/browser.ts +89 -377
  617. package/src/types/player.ts +69 -0
  618. package/src/utils/LazyNativeEmitter.ts +8 -9
  619. package/src/utils/NativeUpdatedValue.ts +2 -0
  620. package/src/web/NativeAudioBrowser.ts +158 -365
  621. package/src/web/TrackPlayer/Player.ts +39 -113
  622. package/src/web/TrackPlayer/PlaylistPlayer.ts +346 -0
  623. package/src/web/TrackPlayer/RepeatMode.ts +1 -1
  624. package/src/web/TrackPlayer/SleepTimer.ts +3 -38
  625. package/src/web/TrackPlayer/index.ts +1 -2
  626. package/src/web/browser/BrowserManager.ts +62 -231
  627. package/src/web/browser/NavigationErrorManager.ts +1 -3
  628. package/src/web/browser/SearchManager.ts +15 -11
  629. package/src/web/http/HttpClient.ts +35 -5
  630. package/src/web/http/RequestConfigBuilder.ts +63 -120
  631. package/src/web/player/NowPlayingManager.ts +5 -6
  632. package/src/web/player/OptionsManager.ts +18 -12
  633. package/android/src/main/java/com/audiobrowser/browser/ArtworkResolutionRegistry.kt +0 -37
  634. package/android/src/main/java/com/audiobrowser/browser/BrowseArtworkRegistry.kt +0 -34
  635. package/android/src/main/java/com/audiobrowser/browser/BrowserUrlResolution.kt +0 -299
  636. package/android/src/main/java/com/audiobrowser/browser/JsonArtwork.kt +0 -79
  637. package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +0 -42
  638. package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +0 -35
  639. package/android/src/main/java/com/audiobrowser/player/CapabilityControls.kt +0 -172
  640. package/android/src/main/java/com/audiobrowser/player/EqualizerController.kt +0 -153
  641. package/android/src/main/java/com/audiobrowser/player/InterceptingPlayer.kt +0 -141
  642. package/android/src/main/java/com/audiobrowser/player/NowPlayingUpdater.kt +0 -347
  643. package/android/src/main/java/com/audiobrowser/player/PlaybackErrorClassifier.kt +0 -129
  644. package/android/src/main/java/com/audiobrowser/player/PlaybackStateMachine.kt +0 -85
  645. package/android/src/main/java/com/audiobrowser/player/PlaybackTimer.kt +0 -56
  646. package/android/src/main/java/com/audiobrowser/player/PlayerCapabilitiesExt.kt +0 -20
  647. package/android/src/main/java/com/audiobrowser/player/PlayerEngineFactory.kt +0 -130
  648. package/android/src/main/java/com/audiobrowser/player/SleepTimerManager.kt +0 -80
  649. package/android/src/main/java/com/audiobrowser/player/StuckRecoveryPolicy.kt +0 -50
  650. package/android/src/main/java/com/audiobrowser/player/VolumeFader.kt +0 -81
  651. package/android/src/main/java/com/audiobrowser/tls/AiaCertChaser.kt +0 -141
  652. package/android/src/main/java/com/audiobrowser/tls/AiaChasingTrustManager.kt +0 -37
  653. package/android/src/main/java/com/audiobrowser/tls/AiaTls.kt +0 -41
  654. package/android/src/main/java/com/audiobrowser/tls/CachingCertificateFetcher.kt +0 -209
  655. package/android/src/main/java/com/audiobrowser/util/ArtworkContentProvider.kt +0 -161
  656. package/android/src/main/java/com/audiobrowser/util/ArtworkUris.kt +0 -28
  657. package/android/src/main/java/com/audiobrowser/util/ArtworkVariant.kt +0 -24
  658. package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoader.kt +0 -49
  659. package/android/src/main/java/com/audiobrowser/util/CoilArtworkLoaderHolder.kt +0 -36
  660. package/android/src/main/java/com/audiobrowser/util/OutputMonitor.kt +0 -211
  661. package/android/src/main/java/com/audiobrowser/util/OutputSwitcher.kt +0 -58
  662. package/android/src/main/java/com/audiobrowser/util/RatingFavorites.kt +0 -14
  663. package/android/src/test/java/com/audiobrowser/TestFixtures.kt +0 -165
  664. package/android/src/test/java/com/audiobrowser/browser/ArtworkResolutionRegistryTest.kt +0 -46
  665. package/android/src/test/java/com/audiobrowser/browser/BrowseArtworkRegistryTest.kt +0 -39
  666. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerBuildApiRequestTest.kt +0 -114
  667. package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +0 -141
  668. package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +0 -177
  669. package/android/src/test/java/com/audiobrowser/browser/JsonArtworkTest.kt +0 -55
  670. package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +0 -94
  671. package/android/src/test/java/com/audiobrowser/player/CapabilityControlsTest.kt +0 -203
  672. package/android/src/test/java/com/audiobrowser/player/DynamicLoadControlTest.kt +0 -56
  673. package/android/src/test/java/com/audiobrowser/player/EqualizerControllerTest.kt +0 -216
  674. package/android/src/test/java/com/audiobrowser/player/MediaSessionSlotMappingTest.kt +0 -45
  675. package/android/src/test/java/com/audiobrowser/player/NowPlayingUpdaterTest.kt +0 -442
  676. package/android/src/test/java/com/audiobrowser/player/PlaybackErrorClassifierTest.kt +0 -190
  677. package/android/src/test/java/com/audiobrowser/player/PlaybackStateMachineTest.kt +0 -198
  678. package/android/src/test/java/com/audiobrowser/player/PlaybackTimerTest.kt +0 -137
  679. package/android/src/test/java/com/audiobrowser/player/PlayerEngineFactoryTest.kt +0 -36
  680. package/android/src/test/java/com/audiobrowser/player/RetryLoadErrorHandlingPolicyTest.kt +0 -268
  681. package/android/src/test/java/com/audiobrowser/player/SessionErrorMaskTest.kt +0 -68
  682. package/android/src/test/java/com/audiobrowser/player/StuckRecoveryPolicyTest.kt +0 -40
  683. package/android/src/test/java/com/audiobrowser/player/TransformingDataSourceTest.kt +0 -193
  684. package/android/src/test/java/com/audiobrowser/tls/AiaCertChaserTest.kt +0 -107
  685. package/android/src/test/java/com/audiobrowser/tls/AiaChasingTrustManagerTest.kt +0 -73
  686. package/android/src/test/java/com/audiobrowser/tls/CachingCertificateFetcherTest.kt +0 -75
  687. package/android/src/test/java/com/audiobrowser/tls/CertFixtures.kt +0 -18
  688. package/android/src/test/java/com/audiobrowser/util/ArtworkContentProviderTest.kt +0 -163
  689. package/android/src/test/java/com/audiobrowser/util/ArtworkUrisTest.kt +0 -34
  690. package/android/src/test/java/com/audiobrowser/util/BrowserPathHelperTest.kt +0 -28
  691. package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderHolderTest.kt +0 -53
  692. package/android/src/test/java/com/audiobrowser/util/CoilArtworkLoaderTest.kt +0 -58
  693. package/android/src/test/java/com/audiobrowser/util/FakeImageLoader.kt +0 -57
  694. package/android/src/test/java/com/audiobrowser/util/PlayingStateFactoryTest.kt +0 -40
  695. package/android/src/test/java/com/audiobrowser/util/RatingFavoritesTest.kt +0 -34
  696. package/android/src/test/java/com/audiobrowser/util/TrackFactoryBrowseTest.kt +0 -50
  697. package/android/src/test/java/com/audiobrowser/util/TrackFactoryMediaIdTest.kt +0 -63
  698. package/android/src/test/java/com/audiobrowser/util/TrackFactoryRatingTest.kt +0 -35
  699. package/android/src/test/resources/certs/isrg-root-x1.pem +0 -31
  700. package/android/src/test/resources/certs/letsencrypt-r13.der +0 -0
  701. package/android/src/test/resources/certs/letsencrypt-r13.pem +0 -29
  702. package/android/src/test/resources/certs/stationplaylist-leaf.pem +0 -34
  703. package/ios/Browser/MediaResolveComposer.swift +0 -114
  704. package/ios/Browser/SearchDrillIn.swift +0 -44
  705. package/ios/Browser/SectionScope.swift +0 -35
  706. package/ios/Browser/TabBarEntries.swift +0 -15
  707. package/ios/CarPlay/CPInterfaceController+Safe.swift +0 -126
  708. package/ios/CarPlay/ImageRowItem+CarPlay.swift +0 -19
  709. package/ios/CarPlay/RNABMediaAddHandler.swift +0 -75
  710. package/ios/CarPlay/RNABMediaAffinityHandler.swift +0 -69
  711. package/ios/Extension/PlayerCapabilities+Favorite.swift +0 -20
  712. package/ios/Extension/RequestConfig+Copying.swift +0 -31
  713. package/ios/Extension/Variant+Artwork.swift +0 -49
  714. package/ios/NowPlayingInfo/SilentAudioPrimer.swift +0 -73
  715. package/ios/Player/AirPlayStallJudgement.swift +0 -25
  716. package/ios/Player/EndOfTrackJudgement.swift +0 -32
  717. package/ios/Player/MediaIntentCriteria.swift +0 -139
  718. package/ios/Player/PersistedPlaybackState.swift +0 -11
  719. package/ios/Player/PlaybackStateStore.swift +0 -27
  720. package/ios/Player/PlaybackTimer.swift +0 -61
  721. package/ios/Player/VolumeFader.swift +0 -77
  722. package/ios/RemoteCommand/RemoteCommandCallbacks.swift +0 -47
  723. package/ios/Tests/AirPlayStallJudgementTests.swift +0 -52
  724. package/ios/Tests/EndOfTrackJudgementTests.swift +0 -35
  725. package/ios/Tests/JsonModelsTests.swift +0 -58
  726. package/ios/Tests/MediaIntentCriteriaTests.swift +0 -170
  727. package/ios/Tests/MediaLoaderAssetOptionsTests.swift +0 -46
  728. package/ios/Tests/MediaResolveTests.swift +0 -126
  729. package/ios/Tests/OnceValueTests.swift +0 -79
  730. package/ios/Tests/PlaybackStateStoreTests.swift +0 -48
  731. package/ios/Tests/PlaybackTimerTests.swift +0 -55
  732. package/ios/Tests/PlayingStateManagerTests.swift +0 -57
  733. package/ios/Tests/RemoteCommandControllerTests.swift +0 -156
  734. package/ios/Tests/RemoteCommandTests.swift +0 -67
  735. package/ios/Tests/RetryManagerTests.swift +0 -89
  736. package/ios/Tests/SearchDrillInTests.swift +0 -63
  737. package/ios/Tests/SectionScopeTests.swift +0 -82
  738. package/ios/Tests/SleepTimerManagerTests.swift +0 -107
  739. package/ios/Tests/Support/ManualSleepTimerScheduler.swift +0 -47
  740. package/ios/Tests/TabBarEntriesTests.swift +0 -71
  741. package/lib/commonjs/features/browser-config.js +0 -227
  742. package/lib/commonjs/features/browser-config.js.map +0 -1
  743. package/lib/commonjs/features/carConnection.js +0 -42
  744. package/lib/commonjs/features/carConnection.js.map +0 -1
  745. package/lib/commonjs/features/gate.js +0 -149
  746. package/lib/commonjs/features/gate.js.map +0 -1
  747. package/lib/commonjs/features/playback/trackPlaybackTime.js +0 -86
  748. package/lib/commonjs/features/playback/trackPlaybackTime.js.map +0 -1
  749. package/lib/commonjs/features/player/validateOptions.js +0 -24
  750. package/lib/commonjs/features/player/validateOptions.js.map +0 -1
  751. package/lib/commonjs/utils/artwork.js +0 -31
  752. package/lib/commonjs/utils/artwork.js.map +0 -1
  753. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js +0 -71
  754. package/lib/commonjs/web/TrackPlayer/PlaybackStateMachine.js.map +0 -1
  755. package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js +0 -34
  756. package/lib/commonjs/web/TrackPlayer/PlaybackTimer.js.map +0 -1
  757. package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js +0 -21
  758. package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js.map +0 -1
  759. package/lib/commonjs/web/TrackPlayer/QueueManager.js +0 -273
  760. package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +0 -1
  761. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js +0 -158
  762. package/lib/commonjs/web/TrackPlayer/QueuePlayer.js.map +0 -1
  763. package/lib/commonjs/web/TrackPlayer/VolumeFader.js +0 -75
  764. package/lib/commonjs/web/TrackPlayer/VolumeFader.js.map +0 -1
  765. package/lib/commonjs/web/browser/assertBrowsePageShape.js +0 -22
  766. package/lib/commonjs/web/browser/assertBrowsePageShape.js.map +0 -1
  767. package/lib/commonjs/web/browser/classifyTrackNavigation.js +0 -41
  768. package/lib/commonjs/web/browser/classifyTrackNavigation.js.map +0 -1
  769. package/lib/commonjs/web/browser/parseSearchResponse.js +0 -20
  770. package/lib/commonjs/web/browser/parseSearchResponse.js.map +0 -1
  771. package/lib/commonjs/web/playbackErrorKind.js +0 -57
  772. package/lib/commonjs/web/playbackErrorKind.js.map +0 -1
  773. package/lib/commonjs/web/player/MediaSessionManager.js +0 -121
  774. package/lib/commonjs/web/player/MediaSessionManager.js.map +0 -1
  775. package/lib/commonjs/web/player/RemoteCommandController.js +0 -96
  776. package/lib/commonjs/web/player/RemoteCommandController.js.map +0 -1
  777. package/lib/module/features/browser-config.js +0 -221
  778. package/lib/module/features/browser-config.js.map +0 -1
  779. package/lib/module/features/carConnection.js +0 -37
  780. package/lib/module/features/carConnection.js.map +0 -1
  781. package/lib/module/features/gate.js +0 -144
  782. package/lib/module/features/gate.js.map +0 -1
  783. package/lib/module/features/playback/trackPlaybackTime.js +0 -83
  784. package/lib/module/features/playback/trackPlaybackTime.js.map +0 -1
  785. package/lib/module/features/player/validateOptions.js +0 -20
  786. package/lib/module/features/player/validateOptions.js.map +0 -1
  787. package/lib/module/utils/artwork.js +0 -26
  788. package/lib/module/utils/artwork.js.map +0 -1
  789. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js +0 -67
  790. package/lib/module/web/TrackPlayer/PlaybackStateMachine.js.map +0 -1
  791. package/lib/module/web/TrackPlayer/PlaybackTimer.js +0 -29
  792. package/lib/module/web/TrackPlayer/PlaybackTimer.js.map +0 -1
  793. package/lib/module/web/TrackPlayer/PlayingStateFactory.js +0 -17
  794. package/lib/module/web/TrackPlayer/PlayingStateFactory.js.map +0 -1
  795. package/lib/module/web/TrackPlayer/QueueManager.js +0 -269
  796. package/lib/module/web/TrackPlayer/QueueManager.js.map +0 -1
  797. package/lib/module/web/TrackPlayer/QueuePlayer.js +0 -154
  798. package/lib/module/web/TrackPlayer/QueuePlayer.js.map +0 -1
  799. package/lib/module/web/TrackPlayer/VolumeFader.js +0 -70
  800. package/lib/module/web/TrackPlayer/VolumeFader.js.map +0 -1
  801. package/lib/module/web/browser/assertBrowsePageShape.js +0 -18
  802. package/lib/module/web/browser/assertBrowsePageShape.js.map +0 -1
  803. package/lib/module/web/browser/classifyTrackNavigation.js +0 -38
  804. package/lib/module/web/browser/classifyTrackNavigation.js.map +0 -1
  805. package/lib/module/web/browser/parseSearchResponse.js +0 -16
  806. package/lib/module/web/browser/parseSearchResponse.js.map +0 -1
  807. package/lib/module/web/playbackErrorKind.js +0 -52
  808. package/lib/module/web/playbackErrorKind.js.map +0 -1
  809. package/lib/module/web/player/MediaSessionManager.js +0 -116
  810. package/lib/module/web/player/MediaSessionManager.js.map +0 -1
  811. package/lib/module/web/player/RemoteCommandController.js +0 -92
  812. package/lib/module/web/player/RemoteCommandController.js.map +0 -1
  813. package/lib/typescript/src/features/browser-config.d.ts +0 -12
  814. package/lib/typescript/src/features/browser-config.d.ts.map +0 -1
  815. package/lib/typescript/src/features/carConnection.d.ts +0 -20
  816. package/lib/typescript/src/features/carConnection.d.ts.map +0 -1
  817. package/lib/typescript/src/features/gate.d.ts +0 -78
  818. package/lib/typescript/src/features/gate.d.ts.map +0 -1
  819. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts +0 -34
  820. package/lib/typescript/src/features/playback/trackPlaybackTime.d.ts.map +0 -1
  821. package/lib/typescript/src/features/player/validateOptions.d.ts +0 -2
  822. package/lib/typescript/src/features/player/validateOptions.d.ts.map +0 -1
  823. package/lib/typescript/src/utils/artwork.d.ts +0 -19
  824. package/lib/typescript/src/utils/artwork.d.ts.map +0 -1
  825. package/nitrogen/generated/android/c++/JArtworkVariants.hpp +0 -61
  826. package/nitrogen/generated/android/c++/JFavoriteConfig.hpp +0 -58
  827. package/nitrogen/generated/android/c++/JFavoritesMatchMode.hpp +0 -58
  828. package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +0 -100
  829. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.hpp +0 -112
  830. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.hpp +0 -121
  831. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +0 -123
  832. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +0 -133
  833. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +0 -121
  834. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.hpp +0 -139
  835. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.hpp +0 -134
  836. package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +0 -131
  837. package/nitrogen/generated/android/c++/JFunc_void_GateEvent.hpp +0 -79
  838. package/nitrogen/generated/android/c++/JFunc_void_Output.hpp +0 -80
  839. package/nitrogen/generated/android/c++/JGate.hpp +0 -62
  840. package/nitrogen/generated/android/c++/JGateDecision.hpp +0 -64
  841. package/nitrogen/generated/android/c++/JGateReason.hpp +0 -58
  842. package/nitrogen/generated/android/c++/JIOSOptions.hpp +0 -95
  843. package/nitrogen/generated/android/c++/JMediaReference.hpp +0 -58
  844. package/nitrogen/generated/android/c++/JNativeGateRequest.hpp +0 -74
  845. package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +0 -182
  846. package/nitrogen/generated/android/c++/JNitroIOSUpdateOptions.hpp +0 -96
  847. package/nitrogen/generated/android/c++/JOutputType.hpp +0 -82
  848. package/nitrogen/generated/android/c++/JPlaybackErrorKind.hpp +0 -76
  849. package/nitrogen/generated/android/c++/JRemoteButtonLayout.hpp +0 -86
  850. package/nitrogen/generated/android/c++/JStallReason.hpp +0 -58
  851. package/nitrogen/generated/android/c++/JTrackRequest.hpp +0 -93
  852. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.cpp +0 -26
  853. package/nitrogen/generated/android/c++/JVariant_Boolean_FavoriteConfig.hpp +0 -71
  854. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.cpp +0 -26
  855. package/nitrogen/generated/android/c++/JVariant_NullType_RemoteButtonLayout.hpp +0 -75
  856. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.cpp +0 -41
  857. package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +0 -121
  858. package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.cpp +0 -26
  859. package/nitrogen/generated/android/c++/JVariant_String_ArtworkVariants.hpp +0 -70
  860. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/FormatNowPlayingParams.kt +0 -50
  861. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.kt +0 -80
  862. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.kt +0 -80
  863. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_RequestConfig___Track.kt +0 -80
  864. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.kt +0 -80
  865. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__optional_std__string____Track.kt +0 -80
  866. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.kt +0 -80
  867. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.kt +0 -80
  868. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/IOSOptions.kt +0 -44
  869. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/MediaReference.kt +0 -23
  870. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeGateRequest.kt +0 -44
  871. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeSetupPlayerOptions.kt +0 -66
  872. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NitroIOSUpdateOptions.kt +0 -44
  873. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/OutputType.kt +0 -31
  874. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/PlaybackErrorKind.kt +0 -29
  875. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButton.kt +0 -26
  876. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/RemoteButtonLayout.kt +0 -44
  877. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/StallReason.kt +0 -23
  878. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackRequest.kt +0 -44
  879. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_Boolean_FavoriteConfig.kt +0 -53
  880. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.kt +0 -65
  881. package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Variant_String_ArtworkVariants.kt +0 -53
  882. package/nitrogen/generated/ios/swift/ArtworkVariants.swift +0 -34
  883. package/nitrogen/generated/ios/swift/FavoriteConfig.swift +0 -29
  884. package/nitrogen/generated/ios/swift/FavoritesMatchMode.swift +0 -40
  885. package/nitrogen/generated/ios/swift/FormatNowPlayingParams.swift +0 -67
  886. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___MediaTransformParams.swift +0 -54
  887. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___RequestConfig_std__optional_std__unordered_map_std__string__std__string__.swift +0 -69
  888. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_RequestConfig___Track.swift +0 -54
  889. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.swift +0 -60
  890. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__optional_std__string____Track.swift +0 -60
  891. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_GateDecision_____NativeGateRequest.swift +0 -61
  892. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__shared_ptr_Promise_TransformableRequestConfig____.swift +0 -61
  893. package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.swift +0 -77
  894. package/nitrogen/generated/ios/swift/Func_void_GateDecision.swift +0 -46
  895. package/nitrogen/generated/ios/swift/Func_void_TransformableRequestConfig.swift +0 -46
  896. package/nitrogen/generated/ios/swift/Func_void_std__optional_NowPlayingUpdate_.swift +0 -46
  897. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +0 -53
  898. package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_Promise_TransformableRequestConfig__.swift +0 -66
  899. package/nitrogen/generated/ios/swift/Func_void_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError____.swift +0 -81
  900. package/nitrogen/generated/ios/swift/Gate.swift +0 -47
  901. package/nitrogen/generated/ios/swift/GateDecision.swift +0 -40
  902. package/nitrogen/generated/ios/swift/GateEvent.swift +0 -29
  903. package/nitrogen/generated/ios/swift/GateReason.swift +0 -40
  904. package/nitrogen/generated/ios/swift/IOSOptions.swift +0 -51
  905. package/nitrogen/generated/ios/swift/MediaReference.swift +0 -40
  906. package/nitrogen/generated/ios/swift/NativeGateRequest.swift +0 -58
  907. package/nitrogen/generated/ios/swift/NativeSetupPlayerOptions.swift +0 -206
  908. package/nitrogen/generated/ios/swift/NitroIOSUpdateOptions.swift +0 -90
  909. package/nitrogen/generated/ios/swift/OutputType.swift +0 -72
  910. package/nitrogen/generated/ios/swift/PlaybackErrorKind.swift +0 -64
  911. package/nitrogen/generated/ios/swift/RemoteButtonLayout.swift +0 -57
  912. package/nitrogen/generated/ios/swift/StallReason.swift +0 -40
  913. package/nitrogen/generated/ios/swift/TrackRequest.swift +0 -106
  914. package/nitrogen/generated/ios/swift/Variant_Bool_FavoriteConfig.swift +0 -18
  915. package/nitrogen/generated/ios/swift/Variant_ResolvedTrack_BrowseError_Promise_BrowseResult_.swift +0 -19
  916. package/nitrogen/generated/ios/swift/Variant_String_ArtworkVariants.swift +0 -18
  917. package/nitrogen/generated/shared/c++/ArtworkVariants.hpp +0 -87
  918. package/nitrogen/generated/shared/c++/FavoritesMatchMode.hpp +0 -76
  919. package/nitrogen/generated/shared/c++/FormatNowPlayingParams.hpp +0 -110
  920. package/nitrogen/generated/shared/c++/GateReason.hpp +0 -76
  921. package/nitrogen/generated/shared/c++/IOSOptions.hpp +0 -91
  922. package/nitrogen/generated/shared/c++/MediaReference.hpp +0 -76
  923. package/nitrogen/generated/shared/c++/NativeGateRequest.hpp +0 -97
  924. package/nitrogen/generated/shared/c++/NativeSetupPlayerOptions.hpp +0 -138
  925. package/nitrogen/generated/shared/c++/NitroIOSUpdateOptions.hpp +0 -92
  926. package/nitrogen/generated/shared/c++/OutputType.hpp +0 -108
  927. package/nitrogen/generated/shared/c++/PlaybackErrorKind.hpp +0 -100
  928. package/nitrogen/generated/shared/c++/RemoteButtonLayout.hpp +0 -94
  929. package/nitrogen/generated/shared/c++/StallReason.hpp +0 -76
  930. package/nitrogen/generated/shared/c++/TrackRequest.hpp +0 -93
  931. package/src/codegen-no-config-variant.test.ts +0 -70
  932. package/src/features/browser-config.test.ts +0 -110
  933. package/src/features/browser-config.ts +0 -293
  934. package/src/features/browser.test.ts +0 -19
  935. package/src/features/carConnection.ts +0 -36
  936. package/src/features/gate.test.ts +0 -72
  937. package/src/features/gate.ts +0 -155
  938. package/src/features/playback/trackPlaybackTime.test.ts +0 -80
  939. package/src/features/playback/trackPlaybackTime.ts +0 -101
  940. package/src/features/player/options.test.ts +0 -38
  941. package/src/features/player/setup.test.ts +0 -125
  942. package/src/features/player/validateOptions.ts +0 -22
  943. package/src/features/queue/queue.test.ts +0 -36
  944. package/src/public-surface.test.ts +0 -116
  945. package/src/utils/artwork.ts +0 -30
  946. package/src/web/NativeAudioBrowser.playWhenReady.test.ts +0 -301
  947. package/src/web/TrackPlayer/PlaybackStateMachine.test.ts +0 -139
  948. package/src/web/TrackPlayer/PlaybackStateMachine.ts +0 -82
  949. package/src/web/TrackPlayer/PlaybackTimer.test.ts +0 -67
  950. package/src/web/TrackPlayer/PlaybackTimer.ts +0 -33
  951. package/src/web/TrackPlayer/Player.dispatch.test.ts +0 -229
  952. package/src/web/TrackPlayer/Player.load.test.ts +0 -86
  953. package/src/web/TrackPlayer/PlayingStateFactory.test.ts +0 -51
  954. package/src/web/TrackPlayer/PlayingStateFactory.ts +0 -24
  955. package/src/web/TrackPlayer/QueueManager.test.ts +0 -289
  956. package/src/web/TrackPlayer/QueueManager.ts +0 -302
  957. package/src/web/TrackPlayer/QueuePlayer.ts +0 -188
  958. package/src/web/TrackPlayer/VolumeFader.ts +0 -74
  959. package/src/web/browser/__tests__/layer-resolver.test.ts +0 -69
  960. package/src/web/browser/assertBrowsePageShape.test.ts +0 -27
  961. package/src/web/browser/assertBrowsePageShape.ts +0 -27
  962. package/src/web/browser/classifyTrackNavigation.test.ts +0 -41
  963. package/src/web/browser/classifyTrackNavigation.ts +0 -35
  964. package/src/web/browser/parseSearchResponse.test.ts +0 -25
  965. package/src/web/browser/parseSearchResponse.ts +0 -15
  966. package/src/web/http/RequestConfigBuilder.test.ts +0 -59
  967. package/src/web/playbackErrorKind.test.ts +0 -55
  968. package/src/web/playbackErrorKind.ts +0 -56
  969. package/src/web/player/MediaSessionManager.test.ts +0 -220
  970. package/src/web/player/MediaSessionManager.ts +0 -151
  971. package/src/web/player/NowPlayingManager.test.ts +0 -46
  972. package/src/web/player/RemoteCommandController.test.ts +0 -171
  973. package/src/web/player/RemoteCommandController.ts +0 -166
@@ -2,59 +2,61 @@ package com.audiobrowser.player
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
- import android.os.Handler
6
- import android.os.Looper
7
- import androidx.core.net.toUri
5
+ import androidx.media3.common.AudioAttributes
8
6
  import androidx.media3.common.C
7
+ import androidx.media3.common.ForwardingPlayer
9
8
  import androidx.media3.common.HeartRating
10
- import androidx.media3.common.MediaMetadata
9
+ import androidx.media3.common.MediaItem
10
+ import androidx.media3.common.TrackSelectionParameters
11
11
  import androidx.media3.database.StandaloneDatabaseProvider
12
12
  import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor
13
13
  import androidx.media3.datasource.cache.SimpleCache
14
+ import androidx.media3.exoplayer.DefaultRenderersFactory
14
15
  import androidx.media3.exoplayer.ExoPlayer
16
+ import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
15
17
  import androidx.media3.session.MediaLibraryService
16
18
  import androidx.media3.session.MediaSession
17
19
  import com.audiobrowser.AudioBrowser
18
20
  import com.audiobrowser.Callbacks
19
- import com.audiobrowser.browser.displayArtworkSource
20
- import com.audiobrowser.browser.resolveArtworkUrl
21
- import com.audiobrowser.browser.unattributedArtworkSource
22
21
  import com.audiobrowser.extension.NumberExt.Companion.toSeconds
22
+ import com.margelo.nitro.audiobrowser.ImageContext
23
23
  import com.audiobrowser.model.PlayerSetupOptions
24
24
  import com.audiobrowser.model.PlayerUpdateOptions
25
+ import com.audiobrowser.util.AndroidAudioContentTypeFactory
25
26
  import com.audiobrowser.util.CoilBitmapLoader
27
+ import com.audiobrowser.util.EqualizerManager
26
28
  import com.audiobrowser.util.NetworkConnectivityMonitor
27
29
  import com.audiobrowser.util.PlayingStateFactory
28
30
  import com.audiobrowser.util.RepeatModeFactory
29
31
  import com.audiobrowser.util.TrackFactory
32
+ import com.margelo.nitro.audiobrowser.AndroidPlayerWakeMode
30
33
  import com.margelo.nitro.audiobrowser.AppKilledPlaybackBehavior
31
34
  import com.margelo.nitro.audiobrowser.FavoriteChangedEvent
32
- import com.margelo.nitro.audiobrowser.GateEvent
33
- import com.margelo.nitro.audiobrowser.GateReason
34
- import com.margelo.nitro.audiobrowser.ImageContext
35
- import com.margelo.nitro.audiobrowser.ImageSource
36
- import com.margelo.nitro.audiobrowser.NativeGateRequest
37
35
  import com.margelo.nitro.audiobrowser.NowPlayingMetadata
36
+ import com.margelo.nitro.audiobrowser.NowPlayingUpdate
38
37
  import com.margelo.nitro.audiobrowser.Playback
39
38
  import com.margelo.nitro.audiobrowser.PlaybackActiveTrackChangedEvent
40
39
  import com.margelo.nitro.audiobrowser.PlaybackError
41
- import com.margelo.nitro.audiobrowser.PlaybackPlayWhenReadyChangedEvent
42
40
  import com.margelo.nitro.audiobrowser.PlaybackProgressUpdatedEvent
43
41
  import com.margelo.nitro.audiobrowser.PlaybackQueueEndedEvent
44
42
  import com.margelo.nitro.audiobrowser.PlaybackState
45
43
  import com.margelo.nitro.audiobrowser.PlayingState
44
+ import com.margelo.nitro.audiobrowser.RatingType
45
+ import com.margelo.nitro.audiobrowser.RemoteJumpBackwardEvent
46
+ import com.margelo.nitro.audiobrowser.RemoteJumpForwardEvent
47
+ import com.margelo.nitro.audiobrowser.RemoteSeekEvent
46
48
  import com.margelo.nitro.audiobrowser.RepeatMode
47
49
  import com.margelo.nitro.audiobrowser.SearchParams
50
+ import com.margelo.nitro.audiobrowser.SleepTimer as NitroSleepTimer
51
+ import com.margelo.nitro.audiobrowser.SleepTimerEndOfTrack
52
+ import com.margelo.nitro.audiobrowser.SleepTimerTime
48
53
  import com.margelo.nitro.audiobrowser.Track
54
+ import com.margelo.nitro.core.NullType
49
55
  import java.io.File
50
- import java.io.IOException
51
56
  import java.util.concurrent.TimeUnit
52
57
  import kotlin.time.Duration.Companion.seconds
53
58
  import kotlinx.coroutines.CompletableDeferred
54
- import kotlinx.coroutines.Dispatchers
55
- import kotlinx.coroutines.MainScope
56
59
  import kotlinx.coroutines.TimeoutCancellationException
57
- import kotlinx.coroutines.withContext
58
60
  import kotlinx.coroutines.withTimeout
59
61
  import timber.log.Timber
60
62
 
@@ -64,50 +66,25 @@ class Player(internal val context: Context) {
64
66
  get() = options.appKilledPlaybackBehavior
65
67
 
66
68
  private var options = PlayerUpdateOptions()
67
- private val mainHandler = Handler(Looper.getMainLooper())
68
69
  internal var callbacks: Callbacks? = null
69
70
  private lateinit var mediaSession: MediaSession
70
71
  val networkMonitor: NetworkConnectivityMonitor = NetworkConnectivityMonitor(context)
71
- internal val equalizer =
72
- EqualizerController(onSettingsChanged = { settings -> callbacks?.onEqualizerChanged(settings) })
72
+ private var equalizerManager: EqualizerManager? = null
73
73
  private val mediaSessionCallback = MediaSessionCallback(this)
74
74
  internal val playbackStateStore = PlaybackStateStore(this)
75
- internal val volumeFader = VolumeFader(getVolume = { volume }, setVolume = { volume = it })
76
- internal val sleepTimer =
77
- SleepTimerManager(
78
- volumeFader,
79
- pause = { pause() },
80
- onChanged = { callbacks?.onSleepTimerChanged(it) },
81
- )
75
+ private val sleepTimer =
76
+ object : SleepTimer() {
77
+ override fun onComplete() {
78
+ Timber.d("Sleep timer completed, stopping playback")
79
+ stop()
80
+ callbacks?.onSleepTimerChanged(NitroSleepTimer.create(NullType.NULL))
81
+ }
82
+ }
82
83
 
83
84
  lateinit var exoPlayer: ExoPlayer
84
85
  lateinit var forwardingPlayer: androidx.media3.common.Player
85
86
  /** Thread-safe cache of playWhenReady for access from non-main threads (e.g., retry policy) */
86
87
  @Volatile internal var playWhenReadyCache = false
87
-
88
- /**
89
- * Whether the current load has ever produced audio — selects between the retry policy's
90
- * first-connect and recovery budgets. Volatile: read from ExoPlayer's loader thread. Set when
91
- * playback starts, cleared on media-item transition (a new load starts unproven); retry reloads
92
- * of the same load keep it.
93
- */
94
- @Volatile internal var hasPlayedCache = false
95
-
96
- /** Last playWhenReady value emitted to JS — see [emitPlayWhenReadyChanged]. */
97
- private var lastEmittedPlayWhenReady: Boolean? = null
98
-
99
- /**
100
- * Emits the playWhenReady change to JS exactly once per value. Two producers exist: the
101
- * synchronous clear at ENDED in [setPlaybackState] (ordered before the state / queue-ended
102
- * callbacks, matching iOS) and the async media3 listener (authoritative for changes ExoPlayer
103
- * makes on its own, e.g. audio focus); the guard deduplicates their overlap.
104
- */
105
- internal fun emitPlayWhenReadyChanged(value: Boolean) {
106
- if (lastEmittedPlayWhenReady == value) return
107
- lastEmittedPlayWhenReady = value
108
- callbacks?.onPlaybackPlayWhenReadyChanged(PlaybackPlayWhenReadyChangedEvent(value))
109
- }
110
-
111
88
  private lateinit var mediaFactory: MediaFactory
112
89
  private lateinit var loadControl: DynamicLoadControl
113
90
  private var automaticBufferManager: AutomaticBufferManager? = null
@@ -118,39 +95,49 @@ class Player(internal val context: Context) {
118
95
  */
119
96
  @Volatile private var pendingNetworkRetry = false
120
97
 
98
+ private var _browser: AudioBrowser? = null
121
99
  private var browserRegistered = CompletableDeferred<AudioBrowser>()
122
100
  private var _coilBitmapLoader: CoilBitmapLoader? = null
123
101
 
124
102
  /**
125
- * Coil ImageLoader for SVG pre-rendering in Android Auto browse items. Set by Service after
126
- * creation.
103
+ * Coil ImageLoader for SVG pre-rendering in Android Auto browse items.
104
+ * Set by Service after creation.
127
105
  */
128
106
  var imageLoader: coil3.ImageLoader? = null
129
107
 
130
- /** Registry that maps content:// tokens to artwork URLs for the browse content provider. */
131
- val browseArtworkRegistry = com.audiobrowser.browser.BrowseArtworkRegistry()
132
-
133
108
  /**
134
- * Set the CoilBitmapLoader for display-time bitmap loading. Called from Service after creation.
109
+ * Set the CoilBitmapLoader for artwork URL transformation. Called from Service after creation.
135
110
  */
136
111
  var coilBitmapLoader: CoilBitmapLoader?
137
112
  get() = _coilBitmapLoader
138
113
  set(value) {
139
114
  _coilBitmapLoader = value
115
+ // If browser is already set, wire up the resolver
116
+ _browser?.let { audioBrowser -> wireUpArtworkResolver(audioBrowser, value) }
140
117
  }
141
118
 
142
- // NOTE: setting the browser does NOT complete browserRegistered.
143
- // Configuration (routes/tabs) must be set before Android Auto can browse.
144
- // Call notifyBrowserConfigurationReady() after configuration is set.
145
- var browser: AudioBrowser? = null
119
+ var browser: AudioBrowser?
120
+ get() = _browser
121
+ set(value) {
122
+ _browser = value
123
+ value?.let { audioBrowser ->
124
+ // Wire up artwork URL resolver
125
+ // This ensures the resolver is available when onGetChildren resumes
126
+ wireUpArtworkResolver(audioBrowser, _coilBitmapLoader)
127
+
128
+ // NOTE: We do NOT complete browserRegistered here.
129
+ // Configuration (routes/tabs) must be set before Android Auto can browse.
130
+ // Call notifyBrowserConfigurationReady() after configuration is set.
131
+ }
132
+ }
146
133
 
147
134
  /**
148
- * Notifies that the browser configuration is ready (routes/tabs are configured). This should be
149
- * called after setting the browser AND its configuration. Only then will Android Auto be able to
150
- * browse content.
135
+ * Notifies that the browser configuration is ready (routes/tabs are configured).
136
+ * This should be called after setting the browser AND its configuration.
137
+ * Only then will Android Auto be able to browse content.
151
138
  */
152
139
  fun notifyBrowserConfigurationReady() {
153
- val audioBrowser = browser ?: return
140
+ val audioBrowser = _browser ?: return
154
141
 
155
142
  if (!browserRegistered.isCompleted) {
156
143
  Timber.d("Browser configuration ready - completing deferred")
@@ -168,15 +155,34 @@ class Player(internal val context: Context) {
168
155
  mediaSessionCallback.updateMediaSession(
169
156
  mediaSession,
170
157
  options.capabilities,
171
- options.remoteButtonLayout,
158
+ options.notificationButtons,
172
159
  searchAvailable,
173
- options.forwardJumpInterval,
174
- options.backwardJumpInterval,
175
160
  )
176
161
  }
177
162
  }
178
163
  }
179
164
 
165
+ /**
166
+ * Wires up the artwork URL resolver between BrowserManager and CoilBitmapLoader. This enables
167
+ * artwork URL transformation during browse-time (Phase 2).
168
+ */
169
+ private fun wireUpArtworkResolver(audioBrowser: AudioBrowser, loader: CoilBitmapLoader?) {
170
+ val browserManager = audioBrowser.browserManager
171
+ if (loader != null) {
172
+ Timber.d("Wiring up artwork URL resolver")
173
+ browserManager.artworkUrlResolver = { track, perRouteConfig, imageContext ->
174
+ loader.transformArtworkUrlForTrack(track, perRouteConfig, imageContext)
175
+ }
176
+ // Clear content cache so that content resolved before the resolver was wired up
177
+ // will be re-fetched with artwork transformation applied
178
+ browserManager.clearContentCache()
179
+ Timber.d("Cleared content cache after wiring artwork resolver")
180
+ } else {
181
+ Timber.d("CoilBitmapLoader not available - artwork URL transformation disabled")
182
+ browserManager.artworkUrlResolver = null
183
+ }
184
+ }
185
+
180
186
  /**
181
187
  * Suspends until the browser is registered, with a timeout.
182
188
  *
@@ -194,16 +200,133 @@ class Player(internal val context: Context) {
194
200
  throw e
195
201
  }
196
202
 
203
+ /**
204
+ * ForwardingPlayer that intercepts external player actions and dispatches them to callbacks.
205
+ *
206
+ * This class blocks all external media item modifications to delegate control to RNAB. These
207
+ * overrides prevent media controllers (like Android Auto, notifications) from directly modifying
208
+ * the queue, ensuring all queue changes go through the RNAB API for proper state management and
209
+ * event handling.
210
+ */
211
+ private inner class InterceptingPlayer(player: ExoPlayer) : ForwardingPlayer(player) {
212
+
213
+ override fun setMediaItems(mediaItems: MutableList<MediaItem>, resetPosition: Boolean) {
214
+ return super.setMediaItems(mediaItems, resetPosition)
215
+ }
216
+
217
+ override fun addMediaItems(mediaItems: MutableList<MediaItem>) {
218
+ return super.addMediaItems(mediaItems)
219
+ }
220
+
221
+ override fun addMediaItems(index: Int, mediaItems: MutableList<MediaItem>) {
222
+ return super.addMediaItems(index, mediaItems)
223
+ }
224
+
225
+ override fun setMediaItems(
226
+ mediaItems: MutableList<MediaItem>,
227
+ startIndex: Int,
228
+ startPositionMs: Long,
229
+ ) {
230
+ return super.setMediaItems(mediaItems, startIndex, startPositionMs)
231
+ }
232
+
233
+ override fun setMediaItems(mediaItems: MutableList<MediaItem>) {
234
+ return super.setMediaItems(mediaItems)
235
+ }
236
+
237
+ // Intercept playback controls and dispatch to callbacks or fall back to default behavior
238
+ override fun play() {
239
+ if (callbacks?.handleRemotePlay() != true) {
240
+ super.play()
241
+ }
242
+ }
243
+
244
+ override fun pause() {
245
+ if (callbacks?.handleRemotePause() != true) {
246
+ super.pause()
247
+ }
248
+ }
249
+
250
+ override fun seekToNext() {
251
+ Timber.Forest.d("InterceptingPlayer.seekToNext() called")
252
+ if (callbacks?.handleRemoteNext() != true) {
253
+ super.seekToNext()
254
+ }
255
+ }
256
+
257
+ override fun seekToNextMediaItem() {
258
+ Timber.Forest.d("InterceptingPlayer.seekToNextMediaItem() called")
259
+ if (callbacks?.handleRemoteNext() != true) {
260
+ super.seekToNextMediaItem()
261
+ }
262
+ }
263
+
264
+ override fun seekToPrevious() {
265
+ Timber.Forest.d("InterceptingPlayer.seekToPrevious() called")
266
+ if (callbacks?.handleRemotePrevious() != true) {
267
+ super.seekToPrevious()
268
+ }
269
+ }
270
+
271
+ override fun seekToPreviousMediaItem() {
272
+ Timber.Forest.d("InterceptingPlayer.seekToPreviousMediaItem() called")
273
+ if (callbacks?.handleRemotePrevious() != true) {
274
+ super.seekToPreviousMediaItem()
275
+ }
276
+ }
277
+
278
+ override fun seekForward() {
279
+ Timber.Forest.d("InterceptingPlayer.seekForward() called")
280
+ if (
281
+ callbacks?.handleRemoteJumpForward(
282
+ RemoteJumpForwardEvent(interval = options.forwardJumpInterval)
283
+ ) != true
284
+ ) {
285
+ super.seekForward()
286
+ }
287
+ }
288
+
289
+ override fun seekBack() {
290
+ if (
291
+ callbacks?.handleRemoteJumpBackward(
292
+ RemoteJumpBackwardEvent(interval = options.backwardJumpInterval)
293
+ ) != true
294
+ ) {
295
+ super.seekBack()
296
+ }
297
+ }
298
+
299
+ override fun stop() {
300
+ if (callbacks?.handleRemoteStop() != true) {
301
+ super.stop()
302
+ }
303
+ }
304
+
305
+ override fun seekTo(mediaItemIndex: Int, positionMs: Long) {
306
+ if (
307
+ callbacks?.handleRemoteSeek(RemoteSeekEvent(position = positionMs.toDouble() / 1000.0)) !=
308
+ true
309
+ ) {
310
+ super.seekTo(mediaItemIndex, positionMs)
311
+ }
312
+ }
313
+
314
+ override fun seekTo(positionMs: Long) {
315
+ if (
316
+ callbacks?.handleRemoteSeek(RemoteSeekEvent(position = positionMs.toDouble() / 1000.0)) !=
317
+ true
318
+ ) {
319
+ super.seekTo(positionMs)
320
+ }
321
+ }
322
+ }
323
+
197
324
  private lateinit var playerListener: PlayerListener
198
325
  private var cache: SimpleCache? = null
199
326
 
200
- private val progressTimer: PlaybackTimer by lazy {
201
- PlaybackTimer(
202
- isActive = {
203
- it == PlaybackState.LOADING || it == PlaybackState.BUFFERING || it == PlaybackState.PLAYING
204
- }
205
- ) {
206
- val index = currentIndex ?: return@PlaybackTimer
327
+ private val progressUpdateManager: PlaybackProgressUpdateManager by lazy {
328
+ PlaybackProgressUpdateManager {
329
+ val index = currentIndex ?: return@PlaybackProgressUpdateManager
207
330
  val event =
208
331
  PlaybackProgressUpdatedEvent(
209
332
  position = position.toSeconds(),
@@ -215,12 +338,7 @@ class Player(internal val context: Context) {
215
338
  }
216
339
  }
217
340
 
218
- private val intervalTimer: PlaybackTimer by lazy {
219
- PlaybackTimer(isActive = { it == PlaybackState.PLAYING }) { callbacks?.onPlaybackInterval() }
220
- }
221
-
222
341
  internal var playingState: PlayingState = PlayingState(false, false)
223
- private set
224
342
 
225
343
  val currentTrack: Track?
226
344
  get() = exoPlayer.currentMediaItem?.let { TrackFactory.fromMedia3(it) }
@@ -234,6 +352,9 @@ class Player(internal val context: Context) {
234
352
  internal var playbackState: PlaybackState = PlaybackState.NONE
235
353
  private set
236
354
 
355
+ /** Current now playing metadata override (null = use track metadata) */
356
+ private var nowPlayingOverride: NowPlayingUpdate? = null
357
+
237
358
  fun getPlayback(): Playback {
238
359
  return Playback(playbackState, playbackError)
239
360
  }
@@ -242,33 +363,9 @@ class Player(internal val context: Context) {
242
363
  return playingState
243
364
  }
244
365
 
245
- /**
246
- * Re-derives [playingState] from the current `playWhenReady` + [playbackState] and emits
247
- * `onPlaybackPlayingState` when it changed. The only writer of [playingState] — called from the
248
- * state machine ([setPlaybackState]) and from [PlayerListener.onPlayWhenReadyChanged], the change
249
- * points of its two inputs.
250
- */
251
- internal fun refreshPlayingState() {
252
- val newPlayingState = PlayingStateFactory.derive(playWhenReady, playbackState)
253
- if (newPlayingState == playingState) return
254
- Timber.d(
255
- "PlayingState changed: playing=${newPlayingState.playing}, buffering=${newPlayingState.buffering}"
256
- )
257
- playingState = newPlayingState
258
- callbacks?.onPlaybackPlayingState(playingState)
259
- }
260
-
261
366
  var playWhenReady: Boolean
262
367
  get() = exoPlayer.playWhenReady
263
368
  set(value) {
264
- // Raising the intent at STATE_ENDED must replay, not silently no-op:
265
- // ExoPlayer's setPlayWhenReady(true) never restarts an ended player (the
266
- // ENDED→seekToDefaultPosition replay lives in media3's media-button
267
- // path, not the Player API). In the setter so every writer gets it —
268
- // play(), togglePlayback() and JS setPlayWhenReady alike.
269
- if (value && exoPlayer.playbackState == ExoPlayer.STATE_ENDED) {
270
- exoPlayer.seekToDefaultPosition()
271
- }
272
369
  playWhenReadyCache = value
273
370
  exoPlayer.playWhenReady = value
274
371
  }
@@ -301,6 +398,8 @@ class Player(internal val context: Context) {
301
398
  val isPlaying
302
399
  get() = exoPlayer.isPlaying
303
400
 
401
+ var ratingType: RatingType = RatingType.NONE
402
+
304
403
  var repeatMode: RepeatMode
305
404
  get() = RepeatModeFactory.fromMedia3(exoPlayer.repeatMode)
306
405
  internal set(value) {
@@ -330,14 +429,8 @@ class Player(internal val context: Context) {
330
429
  .map { index -> TrackFactory.fromMedia3(exoPlayer.getMediaItemAt(index)) }
331
430
  .toTypedArray()
332
431
 
333
- /**
334
- * Whether nothing follows the current item in *playback order* — shuffle- and repeat-aware via
335
- * media3's own order computation (the linear `currentMediaItemIndex == count - 1` check missed a
336
- * shuffled queue whose playback order ends on a different linear index). Matches iOS's
337
- * `isLastInPlaybackOrder`.
338
- */
339
- val isLastInPlaybackOrder: Boolean
340
- get() = exoPlayer.nextMediaItemIndex == C.INDEX_UNSET
432
+ val isLastTrack: Boolean
433
+ get() = exoPlayer.currentMediaItemIndex == exoPlayer.mediaItemCount - 1
341
434
 
342
435
  /**
343
436
  * The source path from which the current queue was expanded (e.g., from a contextual URL). Used
@@ -374,12 +467,6 @@ class Player(internal val context: Context) {
374
467
  fun setup(setupOptions: PlayerSetupOptions) {
375
468
  Timber.Forest.d("Setting up player with new options")
376
469
 
377
- nowPlaying.enabled = setupOptions.autoUpdateNowPlayingMetadata
378
- // Wrap the Nitro Func into a plain suspend call so the updater (and its tests) never touch
379
- // bridge types; the double-await depth is unchanged (invoke -> bridge Promise -> JS promise).
380
- nowPlaying.formatter =
381
- setupOptions.nowPlayingMetadataFormatter?.let { f -> { params -> f.invoke(params).await() } }
382
-
383
470
  val isInitialSetup = !::exoPlayer.isInitialized
384
471
 
385
472
  if (!isInitialSetup) {
@@ -389,9 +476,6 @@ class Player(internal val context: Context) {
389
476
  }
390
477
 
391
478
  if (setupOptions.maxCacheSize > 0) {
392
- // SimpleCache locks its folder — re-setup must release the previous
393
- // instance first or the constructor throws.
394
- cache?.release()
395
479
  cache =
396
480
  SimpleCache(
397
481
  File(context.cacheDir, "RNAB"),
@@ -405,46 +489,94 @@ class Player(internal val context: Context) {
405
489
  cache = null
406
490
  }
407
491
 
408
- // One engine generation: load control, media factory, ExoPlayer (pure construction —
409
- // see buildPlayerEngine; lifecycle and wiring stay here).
410
- val engine =
411
- buildPlayerEngine(
492
+ // Recreate ExoPlayer with new setup options
493
+ val renderer = DefaultRenderersFactory(context)
494
+ renderer.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER)
495
+
496
+ // Create bandwidth meter for adaptive bitrate selection in HLS/DASH
497
+ val bandwidthMeter = DefaultBandwidthMeter.Builder(context).build()
498
+
499
+ loadControl = run {
500
+ val minBuffer = setupOptions.minBuffer.toInt()
501
+ val maxBuffer = setupOptions.maxBuffer.toInt()
502
+ val playBuffer = setupOptions.playBuffer.toInt()
503
+ // When automatic (rebufferBuffer is null), start at playBuffer and let AutomaticBufferManager
504
+ // adjust
505
+ // When fixed (rebufferBuffer is set), use that value
506
+ val playAfterRebuffer = setupOptions.rebufferBuffer?.toInt() ?: playBuffer
507
+ val backBuffer = setupOptions.backBuffer.toInt()
508
+ val config =
509
+ BufferConfig(
510
+ minBufferMs = minBuffer,
511
+ maxBufferMs = maxBuffer,
512
+ bufferForPlaybackMs = playBuffer,
513
+ bufferForPlaybackAfterRebufferMs = playAfterRebuffer,
514
+ backBufferMs = backBuffer,
515
+ )
516
+ DynamicLoadControl(initialConfig = config)
517
+ }
518
+ // Create MediaFactory with reference to browser for media URL transformation
519
+ // shouldRetry checks playWhenReady to avoid retrying when paused (e.g., another app took audio
520
+ // focus). Uses thread-safe cache since this is called from ExoPlayer's playback thread.
521
+ // isOnline and onRetryPending enable network-aware retry acceleration.
522
+ mediaFactory =
523
+ MediaFactory(
412
524
  context,
413
- setupOptions,
414
525
  cache,
526
+ setupOptions.retryPolicy,
415
527
  shouldRetry = { playWhenReadyCache },
416
528
  isOnline = { networkMonitor.getOnline() },
417
- hasPlayed = { hasPlayedCache },
418
- onRetryPending = { exception, isNetworkError ->
529
+ onRetryPending = { isNetworkError ->
419
530
  // Track pending network retries for acceleration when connectivity returns
420
531
  pendingNetworkRetry = isNetworkError
421
532
  if (isNetworkError) {
422
533
  Timber.d("Network retry pending, will accelerate on connectivity restoration")
423
534
  }
424
- reportRetryingError(exception)
425
535
  },
426
- resolveMediaConfig = { url -> browser?.getMediaRequestConfig(url) },
427
- )
428
- loadControl = engine.loadControl
429
- mediaFactory = engine.mediaFactory
430
- exoPlayer = engine.exoPlayer
536
+ transferListener = bandwidthMeter,
537
+ ) { url ->
538
+ browser?.getMediaRequestConfig(url)
539
+ }
431
540
 
432
- // A rebuilt engine starts paused and fires no change event — sync the
433
- // eager cache and tell JS, or the stale true sticks (and the emit dedupe
434
- // would swallow the next real rise).
435
- if (!isInitialSetup) {
436
- playWhenReadyCache = false
437
- emitPlayWhenReadyChanged(false)
541
+ exoPlayer =
542
+ ExoPlayer.Builder(context)
543
+ .setRenderersFactory(renderer)
544
+ .setBandwidthMeter(bandwidthMeter)
545
+ .setHandleAudioBecomingNoisy(setupOptions.handleAudioBecomingNoisy)
546
+ .setMediaSourceFactory(mediaFactory)
547
+ .setWakeMode(
548
+ when (setupOptions.wakeMode) {
549
+ AndroidPlayerWakeMode.NONE -> C.WAKE_MODE_NONE
550
+ AndroidPlayerWakeMode.LOCAL -> C.WAKE_MODE_LOCAL
551
+ AndroidPlayerWakeMode.NETWORK -> C.WAKE_MODE_NETWORK
552
+ }
553
+ )
554
+ .setLoadControl(loadControl)
555
+ .setName("AudioBrowser")
556
+ .build()
557
+ exoPlayer.setAudioAttributes(
558
+ AudioAttributes.Builder()
559
+ .setUsage(C.USAGE_MEDIA)
560
+ .setContentType(AndroidAudioContentTypeFactory.toMedia3(setupOptions.audioContentType))
561
+ .build(),
562
+ true, // handle audio focus
563
+ )
564
+
565
+ // Apply setup-specific options
566
+ setupOptions.audioOffload?.let {
567
+ val audioOffloadPreferences =
568
+ TrackSelectionParameters.AudioOffloadPreferences.Builder()
569
+ .setAudioOffloadMode(
570
+ TrackSelectionParameters.AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_ENABLED
571
+ )
572
+ .setIsGaplessSupportRequired(it.gaplessSupportRequired)
573
+ .setIsSpeedChangeSupportRequired(it.rateChangeSupportRequired)
574
+ .build()
575
+ exoPlayer.trackSelectionParameters.audioOffloadPreferences = audioOffloadPreferences
438
576
  }
439
577
 
440
578
  // Recreate forwarding player with new ExoPlayer
441
- forwardingPlayer =
442
- InterceptingPlayer(
443
- exoPlayer,
444
- { callbacks },
445
- { options },
446
- keepSessionAliveOnError = setupOptions.keepSessionAliveOnError,
447
- )
579
+ forwardingPlayer = InterceptingPlayer(exoPlayer)
448
580
 
449
581
  if (isInitialSetup) {
450
582
  // Initial setup - create player listener and emit initial state
@@ -453,7 +585,7 @@ class Player(internal val context: Context) {
453
585
  callbacks?.onPlaybackChanged(Playback(PlaybackState.NONE, null))
454
586
 
455
587
  // Initialize equalizer with audio session ID
456
- equalizer.initialize(exoPlayer.audioSessionId)
588
+ initializeEqualizer()
457
589
  } else {
458
590
  // Re-setup - re-add listener and update MediaSession
459
591
  forwardingPlayer.addListener(playerListener)
@@ -488,8 +620,8 @@ class Player(internal val context: Context) {
488
620
  }
489
621
 
490
622
  /**
491
- * Starts observing network connectivity changes and invokes the callback when state changes. Also
492
- * handles accelerating pending network retries when connectivity is restored.
623
+ * Starts observing network connectivity changes and invokes the callback when state changes.
624
+ * Also handles accelerating pending network retries when connectivity is restored.
493
625
  *
494
626
  * @param scope The coroutine scope to use for observation
495
627
  */
@@ -497,10 +629,6 @@ class Player(internal val context: Context) {
497
629
  networkMonitor.observeOnline(scope) { isOnline ->
498
630
  callbacks?.onOnlineChanged(isOnline)
499
631
 
500
- // Re-render now-playing so the formatter's stall classification (buffering vs offline) tracks
501
- // connectivity immediately, not only on the next playback transition.
502
- nowPlaying.render()
503
-
504
632
  // Accelerate pending network retry when connectivity is restored
505
633
  if (isOnline && pendingNetworkRetry) {
506
634
  Timber.d("Network restored with pending retry, triggering immediate retry")
@@ -676,18 +804,36 @@ class Player(internal val context: Context) {
676
804
  * controllers without interrupting playback.
677
805
  */
678
806
  fun setActiveTrackFavorited(favorited: Boolean) {
807
+ val index = exoPlayer.currentMediaItemIndex
808
+ if (index == C.INDEX_UNSET) return
809
+
679
810
  val currentTrack = this.currentTrack ?: return
680
811
 
681
- // Persist to the native favorites cache regardless of player state — the
682
- // user's gesture is durable even when there's no active media item to
683
- // update (queue tear-down, between-track gaps, etc.).
812
+ // Update native favorites cache (only tracks with src can be favorited)
684
813
  currentTrack.src?.let { src -> browser?.browserManager?.updateFavorite(src, favorited) }
685
814
 
686
- val index = exoPlayer.currentMediaItemIndex
687
- if (index == C.INDEX_UNSET) return
688
-
689
815
  // Create updated Track with new favorited state
690
- val updatedTrack = currentTrack.copy(favorited = favorited)
816
+ val updatedTrack =
817
+ Track(
818
+ url = currentTrack.url,
819
+ src = currentTrack.src,
820
+ artwork = currentTrack.artwork,
821
+ artworkSource = currentTrack.artworkSource,
822
+ artworkCarPlayTinted = currentTrack.artworkCarPlayTinted,
823
+ title = currentTrack.title,
824
+ subtitle = currentTrack.subtitle,
825
+ artist = currentTrack.artist,
826
+ album = currentTrack.album,
827
+ description = currentTrack.description,
828
+ genre = currentTrack.genre,
829
+ duration = currentTrack.duration,
830
+ style = currentTrack.style,
831
+ childrenStyle = currentTrack.childrenStyle,
832
+ favorited = favorited,
833
+ groupTitle = currentTrack.groupTitle,
834
+ live = currentTrack.live,
835
+ imageRow = currentTrack.imageRow,
836
+ )
691
837
 
692
838
  // Use buildUpon() on the existing MediaItem to update only the metadata
693
839
  // This preserves internal references and avoids playback interruption
@@ -735,152 +881,78 @@ class Player(internal val context: Context) {
735
881
  // MARK: - Now Playing Metadata
736
882
 
737
883
  /**
738
- * Now Playing rendering (flash/override/formatter precedence, dedupe, stale guards, artwork
739
- * keying) lives in [NowPlayingUpdater]; this object is its platform surface — current playback
740
- * reads plus MediaItem stamping via [replaceMediaItem], and artwork resolution via the browser.
884
+ * Updates the now playing notification metadata. Pass null to clear overrides and revert to track
885
+ * metadata.
741
886
  */
742
- val nowPlaying: NowPlayingUpdater =
743
- NowPlayingUpdater(
744
- object : NowPlayingSurface {
745
- override val currentIndex
746
- get() = this@Player.currentIndex
747
-
748
- override val currentTrack
749
- get() = this@Player.currentTrack
750
-
751
- override val playbackState
752
- get() = this@Player.playbackState
753
-
754
- override val playbackError
755
- get() = this@Player.playbackError
756
-
757
- override val playWhenReady
758
- get() = exoPlayer.playWhenReady
759
-
760
- override val isRebuffering
761
- get() = loadControl.isRebuffering
762
-
763
- override val isOnline
764
- get() = networkMonitor.getOnline()
765
-
766
- override val hasNowPlayingArtworkConfig
767
- get() = browser?.browserManager?.config?.nowPlayingArtwork != null
768
-
769
- override fun stampFields(
770
- index: Int,
771
- track: Track,
772
- title: String?,
773
- secondaryLine: String?,
774
- album: String?,
775
- ) =
776
- restampMediaItem(index, track) {
777
- setTitle(title)
778
- .setDisplayTitle(title)
779
- .setArtist(secondaryLine)
780
- // Android Auto reads DISPLAY_SUBTITLE (not ARTIST) once DISPLAY_TITLE is set, so
781
- // mirror the line into `subtitle`; `artist` still drives the lock screen /
782
- // notification.
783
- .setSubtitle(secondaryLine)
784
- .setAlbumTitle(album)
785
- }
786
-
787
- override fun stampArtwork(index: Int, track: Track, uri: String) =
788
- restampMediaItem(index, track) { setArtworkUri(uri.toUri()) }
789
-
790
- override suspend fun resolveNowPlayingArtwork(track: Track, sizePx: Double): String? {
791
- val browserManager = browser?.browserManager ?: return null
792
- val config = browserManager.config.nowPlayingArtwork ?: return null
793
- val resolved =
794
- try {
795
- browserManager.resolveArtworkUrl(track, config, ImageContext(sizePx, sizePx))
796
- } catch (e: Exception) {
797
- Timber.e(e, "Failed to resolve now-playing artwork for track id=${track.id}")
798
- null
799
- }
800
- val uri = resolved?.uri?.takeIf { it.isNotEmpty() } ?: return null
801
- // Remember how this URI was produced: Media3 hands it back to the bitmap loader, which
802
- // re-resolves Track-first (with the nowPlayingArtwork kind, not the global artwork
803
- // config).
804
- browserManager.artworkResolutions.register(uri, track, config)
805
- return uri
806
- }
887
+ fun updateNowPlaying(update: NowPlayingUpdate?) {
888
+ nowPlayingOverride = update
889
+ applyNowPlayingMetadata()
890
+ }
807
891
 
808
- override fun emitNowPlayingChanged(metadata: NowPlayingMetadata) {
809
- callbacks?.onNowPlayingChanged(metadata)
810
- }
811
- },
812
- MainScope(),
892
+ /** Gets the current now playing metadata (override if set, else track metadata). */
893
+ fun getNowPlaying(): NowPlayingMetadata? {
894
+ val track = currentTrack ?: return null
895
+ val override = nowPlayingOverride
896
+
897
+ return NowPlayingMetadata(
898
+ elapsedTime = null,
899
+ title = override?.title ?: track.title,
900
+ album = track.album,
901
+ artist = override?.artist ?: track.artist,
902
+ duration = track.duration,
903
+ artwork = track.artwork,
904
+ description = track.description,
905
+ mediaId = track.src ?: track.url,
906
+ genre = track.genre,
907
+ rating = null, // TODO: map track.favorited to rating if needed
813
908
  )
814
-
815
- /** Republishes the item at [index] with mutated metadata, preserving uri and Track tag. */
816
- private fun restampMediaItem(
817
- index: Int,
818
- track: Track,
819
- mutate: MediaMetadata.Builder.() -> MediaMetadata.Builder,
820
- ) {
821
- val currentMediaItem = exoPlayer.getMediaItemAt(index)
822
- val updatedMediaItem =
823
- currentMediaItem
824
- .buildUpon()
825
- .setUri(currentMediaItem.localConfiguration?.uri)
826
- .setMediaMetadata(currentMediaItem.mediaMetadata.buildUpon().mutate().build())
827
- .setTag(track)
828
- .build()
829
- exoPlayer.replaceMediaItem(index, updatedMediaItem)
830
909
  }
831
910
 
832
911
  /**
833
- * Resets the retry timer when track changes. Called from PlayerListener.onMediaItemTransition.
834
- */
835
- /**
836
- * A restart from a terminal error begins a new load of the same track: fresh retry budgets and
837
- * unproven playback, so the user's tap yields a visible first-connect retry window instead of a
838
- * single silent attempt — the same behavior as re-selecting the track (ADR 0004). Hooked off the
839
- * player error clearing: every recovery path (retry(), play(), the transport controls' idle-error
840
- * recovery) goes through exoPlayer.prepare(), which clears the error.
912
+ * Clears the now playing override when track changes. Called from
913
+ * PlayerListener.onMediaItemTransition.
841
914
  */
842
- internal fun resetForNewLoad() {
843
- resetRetryTimer()
844
- hasPlayedCache = false
915
+ internal fun clearNowPlayingOverride() {
916
+ nowPlayingOverride = null
845
917
  }
846
918
 
919
+ /**
920
+ * Resets the retry timer when track changes. Called from PlayerListener.onMediaItemTransition.
921
+ */
847
922
  internal fun resetRetryTimer() {
848
923
  mediaFactory.resetRetryTimer()
849
924
  }
850
925
 
851
926
  /**
852
- * Finds the queue Track whose published artwork URI matches [uri], for display-time artwork
853
- * resolution of app-supplied queue tracks that never went through browse (so they are not in the
854
- * resolution registry). Must run on the main thread (ExoPlayer access).
927
+ * Applies the current now playing metadata to the media notification. Uses the override if set,
928
+ * otherwise uses track metadata.
855
929
  */
856
- private fun findQueueTrackByArtworkUri(uri: String): Track? {
857
- for (i in 0 until exoPlayer.mediaItemCount) {
858
- val item = exoPlayer.getMediaItemAt(i)
859
- if (item.mediaMetadata.artworkUri?.toString() == uri) {
860
- return item.localConfiguration?.tag as? Track
861
- }
862
- }
863
- return null
864
- }
930
+ private fun applyNowPlayingMetadata() {
931
+ val index = currentIndex ?: return
932
+ val track = currentTrack ?: return
933
+ val override = nowPlayingOverride
865
934
 
866
- /**
867
- * Track-first display-time artwork resolution for [CoilBitmapLoader]: registry hit
868
- * (browse/now-playing-resolved URIs) → queue-tag lookup (app-supplied tracks with raw artwork) →
869
- * header-only fallback for unattributable URIs (registry eviction / process-death restore). Null
870
- * means "fetch the URI as-is".
871
- */
872
- suspend fun resolveDisplayArtwork(uri: String, sizeHintPixels: Int?): ImageSource? {
873
- val browserManager = browser?.browserManager ?: return null
874
- val imageContext =
875
- sizeHintPixels?.takeIf { it > 0 }?.let { ImageContext(it.toDouble(), it.toDouble()) }
876
- browserManager.displayArtworkSource(uri, imageContext)?.let {
877
- return it
878
- }
879
- withContext(Dispatchers.Main) { findQueueTrackByArtworkUri(uri) }
880
- ?.let { track ->
881
- return browserManager.resolveArtworkUrl(track, null, imageContext)
882
- }
883
- return browserManager.unattributedArtworkSource(uri)
935
+ val currentMediaItem = exoPlayer.getMediaItemAt(index)
936
+ val updatedMetadata =
937
+ currentMediaItem.mediaMetadata
938
+ .buildUpon()
939
+ .setTitle(override?.title ?: track.title)
940
+ .setDisplayTitle(override?.title ?: track.title)
941
+ .setArtist(override?.artist ?: track.artist)
942
+ .build()
943
+
944
+ val updatedMediaItem =
945
+ currentMediaItem
946
+ .buildUpon()
947
+ .setUri(currentMediaItem.localConfiguration?.uri)
948
+ .setMediaMetadata(updatedMetadata)
949
+ .setTag(track)
950
+ .build()
951
+
952
+ exoPlayer.replaceMediaItem(index, updatedMediaItem)
953
+
954
+ // Notify JS of the now playing metadata change
955
+ getNowPlaying()?.let { callbacks?.onNowPlayingChanged(it) }
884
956
  }
885
957
 
886
958
  /**
@@ -903,25 +975,12 @@ class Player(internal val context: Context) {
903
975
  }
904
976
 
905
977
  fun play() {
906
- // Through the setter so the STATE_ENDED replay handling applies
907
- // (ExoPlayer.play() is only setPlayWhenReady(true)).
908
- playWhenReady = true
978
+ exoPlayer.play()
909
979
  if (currentTrack != null) {
910
- // No-op unless the player is STATE_IDLE (ExoPlayer.prepare early-returns
911
- // otherwise), so this only reconnects after a stop() or error and never
912
- // re-buffers a healthy stream. Reconnecting is also how live streams
913
- // rejoin the live edge on resume.
914
980
  exoPlayer.prepare()
915
981
  }
916
982
  }
917
983
 
918
- /** Jump to the live edge (default position) of a live item; no-op otherwise. */
919
- fun seekToLiveEdge() {
920
- if (exoPlayer.isCurrentMediaItemLive) {
921
- exoPlayer.seekToDefaultPosition()
922
- }
923
- }
924
-
925
984
  /**
926
985
  * Executes a search and plays the results. Used for voice commands with structured search
927
986
  * parameters.
@@ -931,20 +990,7 @@ class Player(internal val context: Context) {
931
990
  */
932
991
  suspend fun playFromSearch(params: SearchParams): Boolean {
933
992
  return try {
934
- val audioBrowser = awaitBrowser()
935
- // A gate blocks external-surface "play" search too — otherwise voice search is a way around
936
- // the gate (mirrors the iOS play-media intent guard). Resolved per request; a gated decision
937
- // fires onGate(search) and refuses.
938
- val gateOutcome =
939
- audioBrowser.gateDecision(
940
- NativeGateRequest(reason = GateReason.SEARCH, path = null, search = params)
941
- )
942
- if (gateOutcome.gated) {
943
- Timber.i("playFromSearch refused — request is gated")
944
- audioBrowser.onGate(GateEvent(GateReason.SEARCH))
945
- return false
946
- }
947
- val browserManager = audioBrowser.browserManager
993
+ val browserManager = awaitBrowser().browserManager
948
994
 
949
995
  Timber.d(
950
996
  "Executing voice search: mode=${params.mode}, query='${params.query}', artist='${params.artist}', album='${params.album}'"
@@ -973,10 +1019,7 @@ class Player(internal val context: Context) {
973
1019
  }
974
1020
 
975
1021
  fun pause() {
976
- // Through the setter so the eager playWhenReadyCache write holds for the
977
- // drop direction too — the retry policy reads it off-main before
978
- // ExoPlayer's listener round-trip syncs it.
979
- playWhenReady = false
1022
+ exoPlayer.pause()
980
1023
  }
981
1024
 
982
1025
  fun togglePlayback() {
@@ -993,11 +1036,8 @@ class Player(internal val context: Context) {
993
1036
  * required for playback.
994
1037
  */
995
1038
  fun stop() {
996
- // State first so the machine's STOPPED guard suppresses the PAUSED transition
997
- // from the playWhenReady drop below; through setPlaybackState so the change
998
- // is emitted (direct field assignment bypasses events — see its docstring).
999
- setPlaybackState(PlaybackState.STOPPED)
1000
- playWhenReady = false
1039
+ playbackState = PlaybackState.STOPPED
1040
+ exoPlayer.playWhenReady = false
1001
1041
  exoPlayer.stop()
1002
1042
  }
1003
1043
 
@@ -1011,15 +1051,7 @@ class Player(internal val context: Context) {
1011
1051
  * use [pause].
1012
1052
  */
1013
1053
  fun destroy() {
1014
- // Cancel without a final save: the loop would tick against the released
1015
- // player and persist position 0 over the real resumption position.
1016
- playbackStateStore.cancelPeriodicSave()
1017
- // An armed timer's Runnable would outlive the release and fire pause() into the
1018
- // released player. Cleared here, while a mid-ramp fade can still restore volume.
1019
- sleepTimer.clear()
1020
1054
  stop()
1021
- nowPlaying.destroy()
1022
- mediaSessionCallback.destroy()
1023
1055
  forwardingPlayer.removeListener(playerListener)
1024
1056
  automaticBufferManager?.detach()
1025
1057
  automaticBufferManager = null
@@ -1027,7 +1059,8 @@ class Player(internal val context: Context) {
1027
1059
  cache?.release()
1028
1060
  cache = null
1029
1061
  networkMonitor.destroy()
1030
- equalizer.release()
1062
+ equalizerManager?.release()
1063
+ equalizerManager = null
1031
1064
  }
1032
1065
 
1033
1066
  fun seekTo(duration: Long, unit: TimeUnit) {
@@ -1040,57 +1073,6 @@ class Player(internal val context: Context) {
1040
1073
  exoPlayer.seekTo(positionMs)
1041
1074
  }
1042
1075
 
1043
- /**
1044
- * Invalidates in-flight [reportRetryingError] posts: bumped on track change so a report
1045
- * classified for the outgoing track can't land on the incoming one's spinner. Written on main,
1046
- * read on ExoPlayer's loader thread.
1047
- */
1048
- @Volatile private var retryingErrorGeneration = 0
1049
-
1050
- /**
1051
- * Surfaces a load failure the retry policy is still working on: attached to the current
1052
- * (non-terminal) state via [Callbacks.onPlaybackChanged], so UIs can show the cause over the
1053
- * spinner. Not an ERROR transition, and not a [Callbacks.onPlaybackError] — that event stays
1054
- * reserved for terminal errors. Called from ExoPlayer's loader thread, hence the main hop.
1055
- */
1056
- private fun reportRetryingError(exception: IOException) {
1057
- val error = PlaybackErrorClassifier.retryingLoadError(exception, networkMonitor.getOnline())
1058
- val generation = retryingErrorGeneration
1059
- mainHandler.post {
1060
- // The track changed while this post was in flight — the error belongs to the old one.
1061
- if (generation != retryingErrorGeneration) return@post
1062
- // Only attach while the failure is what the listener is looking at. During PLAYING/READY
1063
- // buffered audio is still audibly fine, and ExoPlayer's in-place retry can succeed without
1064
- // any state change — an advisory attached there would never clear.
1065
- if (
1066
- playbackState != PlaybackState.LOADING &&
1067
- playbackState != PlaybackState.BUFFERING &&
1068
- playbackState != PlaybackState.PAUSED
1069
- ) {
1070
- return@post
1071
- }
1072
- // Each failed attempt re-reports; identical repeats add nothing (and would
1073
- // re-render now-playing every backoff tick).
1074
- if (playbackError == error) return@post
1075
- playbackError = error
1076
- callbacks?.onPlaybackChanged(Playback(playbackState, error))
1077
- nowPlaying.render()
1078
- }
1079
- }
1080
-
1081
- /**
1082
- * Drops a retrying error whose track is gone. The new track may keep the state on
1083
- * LOADING/BUFFERING — no state-change emit — so the clear is emitted here, or the old track's
1084
- * error would linger over the new one's spinner.
1085
- */
1086
- internal fun clearRetryingError() {
1087
- retryingErrorGeneration++
1088
- if (playbackError?.retrying != true) return
1089
- playbackError = null
1090
- callbacks?.onPlaybackChanged(Playback(playbackState, null))
1091
- nowPlaying.render()
1092
- }
1093
-
1094
1076
  /**
1095
1077
  * Updates the player state and emits a state change event if the state has changed. Only emits an
1096
1078
  * event if the new state differs from the current state.
@@ -1106,78 +1088,39 @@ class Player(internal val context: Context) {
1106
1088
  val oldState = playbackState
1107
1089
  playbackState = state
1108
1090
 
1109
- // Audio rendered: this load has proven itself — retries from here on get
1110
- // the retry policy's full recovery budget, not the short first-connect one.
1111
- if (state == PlaybackState.PLAYING) {
1112
- hasPlayedCache = true
1113
- }
1114
-
1115
- // A natural end exhausts the play intent — nothing is left to play. Keeping
1116
- // playWhenReady true inverted the play/pause toggle, held audio focus forever
1117
- // (ExoPlayer abandons it only at IDLE or on the intent dropping), and kept the
1118
- // periodic position save running. Cleared and emitted before the state /
1119
- // queue-ended callbacks so JS observes the native order (intent → state →
1120
- // queueEnded); the media3 listener's later echo is deduplicated by
1121
- // emitPlayWhenReadyChanged. The state machine's ENDED guard keeps the drop
1122
- // from re-reporting the state as PAUSED.
1123
- if (state == PlaybackState.ENDED && playWhenReady) {
1124
- playWhenReady = false
1125
- emitPlayWhenReadyChanged(false)
1126
- }
1127
-
1128
- // The last track fires no AUTO media-item transition, so an armed
1129
- // end-of-track sleep timer would stay set forever and pause the next
1130
- // track played instead.
1131
- if (state == PlaybackState.ENDED) {
1132
- sleepTimer.onTrackEnd()
1133
- }
1134
-
1135
1091
  // Clear error when transitioning away from error state
1136
1092
  if (oldState == PlaybackState.ERROR) {
1137
1093
  playbackError = null
1138
1094
  callbacks?.onPlaybackError(null)
1139
1095
  }
1140
1096
 
1141
- // A retrying error rides along through LOADING/BUFFERING/PAUSED; a state that
1142
- // proves recovery (READY/PLAYING — data flowed) or abandons the attempt clears
1143
- // it, before the Playback below is built so the emitted event is already clean.
1144
- if (
1145
- playbackError?.retrying == true &&
1146
- state != PlaybackState.LOADING &&
1147
- state != PlaybackState.BUFFERING &&
1148
- state != PlaybackState.PAUSED
1149
- ) {
1150
- playbackError = null
1151
- }
1152
-
1153
1097
  val playback = Playback(state, playbackError)
1154
1098
  callbacks?.onPlaybackChanged(playback)
1155
1099
 
1156
- // Re-render the now-playing on every state change: the metadata formatter receives
1157
- // `playbackState` (plus isRebuffering / isOnline / error), so any transition can change its
1158
- // output — the live song while paused, a "Reconnecting…" line on a rebuffer, an offline/error
1159
- // line. The publish-dedupe in applyNowPlayingFields drops redundant updates, so re-running
1160
- // through the rapid startup sequence (none→loading→buffering→ready→playing) is cheap.
1161
- nowPlaying.render()
1162
-
1163
- // Emit queue ended event when playback ends on the last track. Repeat modes never
1164
- // conceptually end the queue (matches web's endsQueue()): an ENDED that surfaces while
1165
- // repeating (e.g. a seek to the end) must not read as "playlist over".
1100
+ // Emit queue ended event when playback ends on the last track
1166
1101
  // This coupling ensures queue ended events are always triggered consistently with state
1167
1102
  // changes
1168
- if (state == PlaybackState.ENDED && isLastInPlaybackOrder && repeatMode == RepeatMode.OFF) {
1103
+ if (state == PlaybackState.ENDED && isLastTrack) {
1169
1104
  currentIndex?.let { index ->
1170
1105
  val event =
1171
1106
  PlaybackQueueEndedEvent(track = index.toDouble(), position = position.toSeconds())
1172
1107
  callbacks?.onPlaybackQueueEnded(event)
1173
1108
  }
1174
- // Reset saved position to 0 so resumption starts from beginning
1175
- playbackStateStore.savePositionZero()
1109
+ // Reset saved position to 0 so resumption starts from beginning (only when not repeating)
1110
+ if (repeatMode == RepeatMode.OFF) {
1111
+ playbackStateStore.savePositionZero()
1112
+ }
1176
1113
  }
1177
1114
 
1178
- progressTimer.onPlaybackStateChanged(state)
1179
- intervalTimer.onPlaybackStateChanged(state)
1180
- refreshPlayingState()
1115
+ progressUpdateManager.onPlaybackStateChanged(state)
1116
+ val newPlayingState = PlayingStateFactory.derive(playWhenReady, state)
1117
+ if (newPlayingState != playingState) {
1118
+ Timber.d(
1119
+ "PlayingState changed: playing=${newPlayingState.playing}, buffering=${newPlayingState.buffering}"
1120
+ )
1121
+ playingState = newPlayingState
1122
+ callbacks?.onPlaybackPlayingState(playingState)
1123
+ }
1181
1124
  }
1182
1125
  }
1183
1126
 
@@ -1187,11 +1130,7 @@ class Player(internal val context: Context) {
1187
1130
  * @param interval The interval in seconds, or null to disable progress updates
1188
1131
  */
1189
1132
  fun setProgressUpdateInterval(interval: Double?) {
1190
- progressTimer.setInterval(interval)
1191
- }
1192
-
1193
- fun setPlaybackIntervalEnabled(enabled: Boolean) {
1194
- intervalTimer.setInterval(if (enabled) 1.0 else null)
1133
+ progressUpdateManager.setUpdateInterval(interval)
1195
1134
  }
1196
1135
 
1197
1136
  /**
@@ -1210,6 +1149,8 @@ class Player(internal val context: Context) {
1210
1149
 
1211
1150
  // Check what changed
1212
1151
  val skipSilenceChanged = previousOptions.skipSilence != options.skipSilence
1152
+ val ratingTypeChanged = previousOptions.ratingType != options.ratingType
1153
+ val shuffleChanged = previousOptions.shuffle != options.shuffle
1213
1154
  val progressUpdateEventIntervalChanged =
1214
1155
  previousOptions.progressUpdateEventInterval != options.progressUpdateEventInterval
1215
1156
  val forwardJumpIntervalChanged =
@@ -1217,18 +1158,20 @@ class Player(internal val context: Context) {
1217
1158
  val backwardJumpIntervalChanged =
1218
1159
  previousOptions.backwardJumpInterval != options.backwardJumpInterval
1219
1160
  val capabilitiesChanged = previousOptions.capabilities != options.capabilities
1220
- val remoteButtonLayoutChanged =
1221
- !previousOptions.remoteButtonLayout.sameAs(options.remoteButtonLayout)
1161
+ val notificationButtonsChanged =
1162
+ previousOptions.notificationButtons != options.notificationButtons
1222
1163
  val appKilledPlaybackBehaviorChanged =
1223
1164
  previousOptions.appKilledPlaybackBehavior != options.appKilledPlaybackBehavior
1224
1165
 
1225
1166
  val hasChanged =
1226
1167
  skipSilenceChanged ||
1168
+ ratingTypeChanged ||
1169
+ shuffleChanged ||
1227
1170
  progressUpdateEventIntervalChanged ||
1228
1171
  forwardJumpIntervalChanged ||
1229
1172
  backwardJumpIntervalChanged ||
1230
1173
  capabilitiesChanged ||
1231
- remoteButtonLayoutChanged ||
1174
+ notificationButtonsChanged ||
1232
1175
  appKilledPlaybackBehaviorChanged
1233
1176
 
1234
1177
  // Apply only changed properties
@@ -1236,32 +1179,25 @@ class Player(internal val context: Context) {
1236
1179
  skipSilence = options.skipSilence
1237
1180
  }
1238
1181
 
1239
- if (capabilitiesChanged) {
1240
- // The `favorite` capability is the single favoriting switch — propagate
1241
- // its match mode to the browser so it can hydrate row hearts.
1242
- browser?.browserManager?.setFavoriteMatch(options.capabilities.favoriteMatch)
1182
+ if (ratingTypeChanged) {
1183
+ options.ratingType?.let { ratingType = it }
1184
+ }
1185
+
1186
+ if (shuffleChanged) {
1187
+ shuffleMode = options.shuffle
1243
1188
  }
1244
1189
 
1245
1190
  if (progressUpdateEventIntervalChanged) {
1246
1191
  setProgressUpdateInterval(options.progressUpdateEventInterval)
1247
1192
  }
1248
1193
 
1249
- // The jump intervals pick the button icons (ICON_SKIP_BACK_15 and friends), so a changed
1250
- // interval has to rebuild the layout even when capabilities and placement are untouched.
1251
- if (
1252
- capabilitiesChanged ||
1253
- remoteButtonLayoutChanged ||
1254
- forwardJumpIntervalChanged ||
1255
- backwardJumpIntervalChanged
1256
- ) {
1194
+ if (capabilitiesChanged || notificationButtonsChanged) {
1257
1195
  val searchAvailable = browser?.browserManager?.config?.hasSearch ?: false
1258
1196
  mediaSessionCallback.updateMediaSession(
1259
1197
  mediaSession,
1260
1198
  options.capabilities,
1261
- options.remoteButtonLayout,
1199
+ options.notificationButtons,
1262
1200
  searchAvailable,
1263
- options.forwardJumpInterval,
1264
- options.backwardJumpInterval,
1265
1201
  )
1266
1202
  }
1267
1203
 
@@ -1305,6 +1241,187 @@ class Player(internal val context: Context) {
1305
1241
  return networkMonitor.getOnline()
1306
1242
  }
1307
1243
 
1244
+ /** Initializes the equalizer with the player's audio session ID. */
1245
+ private fun initializeEqualizer() {
1246
+ val audioSessionId = exoPlayer.audioSessionId
1247
+ if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) {
1248
+ Timber.d("Skipping equalizer init - no audio session yet, will init on first playback")
1249
+ return
1250
+ }
1251
+
1252
+ try {
1253
+ equalizerManager =
1254
+ EqualizerManager(audioSessionId).apply {
1255
+ setOnSettingsChanged { settings -> callbacks?.onEqualizerChanged(settings) }
1256
+ }
1257
+ Timber.d("Equalizer initialized with session ID: $audioSessionId")
1258
+ } catch (e: Exception) {
1259
+ Timber.e(e, "Failed to initialize equalizer")
1260
+ equalizerManager = null
1261
+ }
1262
+ }
1263
+
1264
+ /**
1265
+ * Reinitializes the equalizer when the audio session ID changes. Preserves current settings
1266
+ * (enabled state, preset, or custom levels). Also handles first-time initialization when the
1267
+ * equalizer was skipped at startup due to unset audio session ID.
1268
+ */
1269
+ internal fun reinitializeEqualizer(newAudioSessionId: Int) {
1270
+ val oldManager = equalizerManager
1271
+
1272
+ // First-time initialization (skipped at startup because session was unset)
1273
+ if (oldManager == null) {
1274
+ Timber.d("First-time equalizer initialization with session ID: $newAudioSessionId")
1275
+ try {
1276
+ equalizerManager =
1277
+ EqualizerManager(newAudioSessionId).apply {
1278
+ setOnSettingsChanged { settings -> callbacks?.onEqualizerChanged(settings) }
1279
+ }
1280
+ Timber.d("Equalizer initialized with session ID: $newAudioSessionId")
1281
+ } catch (e: Exception) {
1282
+ Timber.e(e, "Failed to initialize equalizer")
1283
+ equalizerManager = null
1284
+ }
1285
+ return
1286
+ }
1287
+
1288
+ // Same session ID, no need to reinitialize
1289
+ if (oldManager.audioSessionId == newAudioSessionId) {
1290
+ return
1291
+ }
1292
+
1293
+ try {
1294
+ // Capture current settings before releasing old equalizer
1295
+ val currentSettings = oldManager.getSettings()
1296
+
1297
+ // Release old equalizer
1298
+ oldManager.release()
1299
+
1300
+ // Create new equalizer with new session ID
1301
+ equalizerManager =
1302
+ EqualizerManager(newAudioSessionId).apply {
1303
+ setOnSettingsChanged { settings -> callbacks?.onEqualizerChanged(settings) }
1304
+ }
1305
+
1306
+ // Restore previous settings if available
1307
+ currentSettings?.let { settings ->
1308
+ if (settings.activePreset != null) {
1309
+ // Restore preset
1310
+ equalizerManager?.setPreset(settings.activePreset)
1311
+ } else if (settings.enabled) {
1312
+ // Restore custom levels
1313
+ equalizerManager?.setLevels(settings.bandLevels)
1314
+ }
1315
+ // Restore enabled state
1316
+ equalizerManager?.setEnabled(settings.enabled)
1317
+ }
1318
+
1319
+ Timber.d("Equalizer reinitialized for new session ID: $newAudioSessionId")
1320
+ } catch (e: Exception) {
1321
+ Timber.e(e, "Failed to reinitialize equalizer")
1322
+ equalizerManager = null
1323
+ }
1324
+ }
1325
+
1326
+ /**
1327
+ * Gets the current equalizer settings.
1328
+ *
1329
+ * @return Current equalizer settings or null if not available
1330
+ */
1331
+ fun getEqualizerSettings(): com.margelo.nitro.audiobrowser.EqualizerSettings? {
1332
+ return equalizerManager?.getSettings()
1333
+ }
1334
+
1335
+ /**
1336
+ * Enables or disables the equalizer.
1337
+ *
1338
+ * @param enabled true to enable, false to disable
1339
+ */
1340
+ fun setEqualizerEnabled(enabled: Boolean) {
1341
+ equalizerManager?.setEnabled(enabled)
1342
+ }
1343
+
1344
+ /**
1345
+ * Applies a preset to the equalizer.
1346
+ *
1347
+ * @param preset Name of the preset to apply
1348
+ */
1349
+ fun setEqualizerPreset(preset: String) {
1350
+ equalizerManager?.setPreset(preset)
1351
+ }
1352
+
1353
+ /**
1354
+ * Sets custom band levels for the equalizer.
1355
+ *
1356
+ * @param levels Array of level values in millibels for each band
1357
+ */
1358
+ fun setEqualizerLevels(levels: DoubleArray) {
1359
+ equalizerManager?.setLevels(levels)
1360
+ }
1361
+
1362
+ // MARK: - Sleep Timer
1363
+
1364
+ /**
1365
+ * Gets the current sleep timer state.
1366
+ *
1367
+ * @return Sleep timer state or null if no timer is active
1368
+ */
1369
+ fun getSleepTimer(): NitroSleepTimer {
1370
+ return when {
1371
+ sleepTimer.time != null -> {
1372
+ NitroSleepTimer.create(SleepTimerTime(sleepTimer.time!!))
1373
+ }
1374
+ sleepTimer.sleepWhenPlayedToEnd -> {
1375
+ NitroSleepTimer.create(SleepTimerEndOfTrack(true))
1376
+ }
1377
+ else -> {
1378
+ NitroSleepTimer.create(NullType.NULL)
1379
+ }
1380
+ }
1381
+ }
1382
+
1383
+ /**
1384
+ * Sets a sleep timer to stop playback after the specified duration.
1385
+ *
1386
+ * @param seconds Number of seconds until playback stops
1387
+ */
1388
+ fun setSleepTimer(seconds: Double) {
1389
+ sleepTimer.sleepAfter(seconds)
1390
+ callbacks?.onSleepTimerChanged(getSleepTimer())
1391
+ }
1392
+
1393
+ /** Sets a sleep timer to stop playback when the current track finishes playing. */
1394
+ fun setSleepTimerToEndOfTrack() {
1395
+ sleepTimer.sleepWhenPlayedToEnd()
1396
+ callbacks?.onSleepTimerChanged(getSleepTimer())
1397
+ }
1398
+
1399
+ /**
1400
+ * Clears the active sleep timer.
1401
+ *
1402
+ * @return true if a timer was cleared, false if no timer was active
1403
+ */
1404
+ fun clearSleepTimer(): Boolean {
1405
+ val wasRunning = sleepTimer.clear()
1406
+ if (wasRunning) {
1407
+ callbacks?.onSleepTimerChanged(NitroSleepTimer.create(NullType.NULL))
1408
+ }
1409
+ return wasRunning
1410
+ }
1411
+
1412
+ /**
1413
+ * Checks if the sleep timer is set to end on track completion and stops playback if so. Called
1414
+ * when a track naturally finishes playing.
1415
+ */
1416
+ internal fun checkSleepTimerOnTrackEnd() {
1417
+ if (sleepTimer.sleepWhenPlayedToEnd) {
1418
+ Timber.d("Sleep timer triggered on track end, stopping playback")
1419
+ sleepTimer.clear()
1420
+ stop()
1421
+ callbacks?.onSleepTimerChanged(NitroSleepTimer.create(NullType.NULL))
1422
+ }
1423
+ }
1424
+
1308
1425
  /**
1309
1426
  * Validates that an index is within bounds [0, trackCount).
1310
1427
  *
@@ -1352,10 +1469,6 @@ class Player(internal val context: Context) {
1352
1469
  mediaSessionCallback.notifyContentChanged(path)
1353
1470
  }
1354
1471
 
1355
- fun invalidateAllContent() {
1356
- mediaSessionCallback.invalidateAllContent()
1357
- }
1358
-
1359
1472
  /**
1360
1473
  * Returns the recommended artwork size in pixels from the connected media browser (e.g., Android
1361
1474
  * Auto), or null if not provided.