react-native-nitro-player 0.7.1-alpha.1 → 0.7.1-alpha.2

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 (31) hide show
  1. package/android/src/main/AndroidManifest.xml +14 -1
  2. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt +5 -6
  3. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +68 -49
  4. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridDownloadManager.kt +67 -21
  5. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridEqualizer.kt +27 -5
  6. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +88 -49
  7. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +40 -10
  8. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ExoPlayerCore.kt +70 -29
  9. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ListenerRegistry.kt +4 -1
  10. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAndroidAuto.kt +38 -32
  11. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +70 -65
  12. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +23 -12
  13. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerNotify.kt +16 -4
  14. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +101 -72
  15. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueue.kt +42 -22
  16. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +40 -23
  17. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerSetup.kt +4 -3
  18. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerTempQueue.kt +73 -62
  19. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerUrlLoader.kt +51 -48
  20. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +3 -3
  21. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadManagerCore.kt +12 -3
  22. package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +43 -34
  23. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +30 -178
  24. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +40 -0
  25. package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +87 -85
  26. package/package.json +1 -1
  27. package/src/hooks/useEqualizer.ts +15 -12
  28. package/src/specs/AndroidAutoMediaLibrary.nitro.ts +3 -2
  29. package/src/specs/DownloadManager.nitro.ts +4 -2
  30. package/src/specs/Equalizer.nitro.ts +4 -2
  31. package/src/specs/TrackPlayer.nitro.ts +18 -6
@@ -4,8 +4,21 @@
4
4
  <uses-permission android:name="android.permission.INTERNET" />
5
5
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
6
6
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
7
-
7
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
8
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
9
+
8
10
  <application>
11
+ <!-- PlaybackService keeps music alive when screen is locked / app backgrounded -->
12
+ <service
13
+ android:name="com.margelo.nitro.nitroplayer.media.NitroPlayerPlaybackService"
14
+ android:foregroundServiceType="mediaPlayback"
15
+ android:exported="true"
16
+ tools:node="merge">
17
+ <intent-filter>
18
+ <action android:name="androidx.media3.session.MediaSessionService" />
19
+ </intent-filter>
20
+ </service>
21
+
9
22
  <!-- WorkManager's SystemForegroundService for download notifications -->
10
23
  <service
11
24
  android:name="androidx.work.impl.foreground.SystemForegroundService"
@@ -14,14 +14,13 @@ class HybridAndroidAutoMediaLibrary : HybridAndroidAutoMediaLibrarySpec() {
14
14
  private val core: TrackPlayerCore
15
15
 
16
16
  init {
17
- val context = NitroModules.applicationContext
18
- ?: throw IllegalStateException("React Context is not initialized")
17
+ val context =
18
+ NitroModules.applicationContext
19
+ ?: throw IllegalStateException("React Context is not initialized")
19
20
  core = TrackPlayerCore.getInstance(context)
20
21
  }
21
22
 
22
- override fun setMediaLibrary(libraryJson: String): Promise<Unit> =
23
- Promise.async { core.setAndroidAutoMediaLibrary(libraryJson) }
23
+ override fun setMediaLibrary(libraryJson: String): Promise<Unit> = Promise.async { core.setAndroidAutoMediaLibrary(libraryJson) }
24
24
 
25
- override fun clearMediaLibrary(): Promise<Unit> =
26
- Promise.async { core.clearAndroidAutoMediaLibrary() }
25
+ override fun clearMediaLibrary(): Promise<Unit> = Promise.async { core.clearAndroidAutoMediaLibrary() }
27
26
  }
@@ -19,12 +19,16 @@ class HybridAudioDevices : HybridAudioDevicesSpec() {
19
19
  private val audioManager = applicationContext?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
20
20
 
21
21
  private val validCommunicationDeviceTypes: Set<Int> by lazy {
22
- val types = mutableSetOf(
23
- AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
24
- AudioDeviceInfo.TYPE_WIRED_HEADSET, AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
25
- AudioDeviceInfo.TYPE_BLUETOOTH_SCO, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
26
- AudioDeviceInfo.TYPE_USB_HEADSET,
27
- )
22
+ val types =
23
+ mutableSetOf(
24
+ AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
25
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
26
+ AudioDeviceInfo.TYPE_WIRED_HEADSET,
27
+ AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
28
+ AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
29
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
30
+ AudioDeviceInfo.TYPE_USB_HEADSET,
31
+ )
28
32
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
29
33
  types.add(AudioDeviceInfo.TYPE_BLE_HEADSET)
30
34
  types.add(AudioDeviceInfo.TYPE_BLE_SPEAKER)
@@ -35,55 +39,70 @@ class HybridAudioDevices : HybridAudioDevicesSpec() {
35
39
  override fun getAudioDevices(): Array<TAudioDevice> {
36
40
  val devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
37
41
  val activeDevice: AudioDeviceInfo? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) audioManager.communicationDevice else null
38
- return devices.filter { validCommunicationDeviceTypes.contains(it.type) }.map { device ->
39
- TAudioDevice(
40
- id = device.id.toDouble(),
41
- name = device.productName?.toString() ?: getDeviceTypeName(device.type),
42
- type = device.type.toDouble(),
43
- isActive = device == activeDevice,
44
- )
45
- }.toTypedArray()
42
+ return devices
43
+ .filter { validCommunicationDeviceTypes.contains(it.type) }
44
+ .map { device ->
45
+ TAudioDevice(
46
+ id = device.id.toDouble(),
47
+ name = device.productName?.toString() ?: getDeviceTypeName(device.type),
48
+ type = device.type.toDouble(),
49
+ isActive = device == activeDevice,
50
+ )
51
+ }.toTypedArray()
46
52
  }
47
53
 
48
54
  /** v2: setAudioDevice now returns Promise<Unit> instead of Boolean */
49
- override fun setAudioDevice(deviceId: Double): Promise<Unit> = Promise.async {
50
- val device = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
51
- .firstOrNull { it.id == deviceId.toInt() }
52
- ?: throw IllegalArgumentException("Audio device $deviceId not found")
53
- if (!validCommunicationDeviceTypes.contains(device.type)) {
54
- throw IllegalArgumentException("Device type ${device.type} is not a valid communication device")
55
- }
56
- try {
57
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
58
- audioManager.setCommunicationDevice(device)
59
- } else {
60
- when (device.type) {
61
- AudioDeviceInfo.TYPE_BLUETOOTH_SCO, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP -> {
62
- audioManager.startBluetoothSco(); audioManager.isBluetoothScoOn = true
63
- }
64
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> audioManager.isSpeakerphoneOn = true
65
- AudioDeviceInfo.TYPE_WIRED_HEADSET, AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> {
66
- audioManager.isSpeakerphoneOn = false; audioManager.isBluetoothScoOn = false
55
+ override fun setAudioDevice(deviceId: Double): Promise<Unit> =
56
+ Promise.async {
57
+ val device =
58
+ audioManager
59
+ .getDevices(AudioManager.GET_DEVICES_OUTPUTS)
60
+ .firstOrNull { it.id == deviceId.toInt() }
61
+ ?: throw IllegalArgumentException("Audio device $deviceId not found")
62
+ if (!validCommunicationDeviceTypes.contains(device.type)) {
63
+ throw IllegalArgumentException("Device type ${device.type} is not a valid communication device")
64
+ }
65
+ try {
66
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
67
+ audioManager.setCommunicationDevice(device)
68
+ } else {
69
+ when (device.type) {
70
+ AudioDeviceInfo.TYPE_BLUETOOTH_SCO, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP -> {
71
+ audioManager.startBluetoothSco()
72
+ audioManager.isBluetoothScoOn = true
73
+ }
74
+
75
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> {
76
+ audioManager.isSpeakerphoneOn = true
77
+ }
78
+
79
+ AudioDeviceInfo.TYPE_WIRED_HEADSET, AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> {
80
+ audioManager.isSpeakerphoneOn = false
81
+ audioManager.isBluetoothScoOn = false
82
+ }
83
+
84
+ else -> {
85
+ throw IllegalArgumentException("Unsupported device type for pre-Android 12: ${device.type}")
86
+ }
67
87
  }
68
- else -> throw IllegalArgumentException("Unsupported device type for pre-Android 12: ${device.type}")
69
88
  }
89
+ } catch (e: Exception) {
90
+ NitroPlayerLogger.log("HybridAudioDevices", "Error setting audio device: ${e.message}")
91
+ throw e
70
92
  }
71
- } catch (e: Exception) {
72
- NitroPlayerLogger.log("HybridAudioDevices", "Error setting audio device: ${e.message}")
73
- throw e
74
93
  }
75
- }
76
94
 
77
- private fun getDeviceTypeName(type: Int): String = when (type) {
78
- AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> "Built-in Earpiece"
79
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> "Built-in Speaker"
80
- AudioDeviceInfo.TYPE_WIRED_HEADSET -> "Wired Headset"
81
- AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> "Wired Headphones"
82
- AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> "Bluetooth SCO"
83
- AudioDeviceInfo.TYPE_BLUETOOTH_A2DP -> "Bluetooth"
84
- AudioDeviceInfo.TYPE_USB_HEADSET -> "USB Headset"
85
- 26 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) "BLE Headset" else "Type 26"
86
- 27 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) "BLE Speaker" else "Type 27"
87
- else -> "Type $type"
88
- }
95
+ private fun getDeviceTypeName(type: Int): String =
96
+ when (type) {
97
+ AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> "Built-in Earpiece"
98
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> "Built-in Speaker"
99
+ AudioDeviceInfo.TYPE_WIRED_HEADSET -> "Wired Headset"
100
+ AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> "Wired Headphones"
101
+ AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> "Bluetooth SCO"
102
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP -> "Bluetooth"
103
+ AudioDeviceInfo.TYPE_USB_HEADSET -> "USB Headset"
104
+ 26 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) "BLE Headset" else "Type 26"
105
+ 27 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) "BLE Speaker" else "Type 27"
106
+ else -> "Type $type"
107
+ }
89
108
  }
@@ -15,77 +15,123 @@ class HybridDownloadManager : HybridDownloadManagerSpec() {
15
15
  private val core: DownloadManagerCore
16
16
 
17
17
  init {
18
- val context = NitroModules.applicationContext
19
- ?: throw IllegalStateException("React Context is not initialized")
18
+ val context =
19
+ NitroModules.applicationContext
20
+ ?: throw IllegalStateException("React Context is not initialized")
20
21
  core = DownloadManagerCore.getInstance(context)
21
22
  }
22
23
 
23
24
  // ── Configuration ─────────────────────────────────────────────────────────
24
25
  override fun configure(config: DownloadConfig) = core.configure(config)
26
+
25
27
  override fun getConfig(): DownloadConfig = core.getConfig()
26
28
 
27
29
  // ── Download operations ───────────────────────────────────────────────────
28
- override fun downloadTrack(track: TrackItem, playlistId: String?): Promise<String> = Promise.async { core.downloadTrack(track, playlistId) }
29
- override fun downloadPlaylist(playlistId: String, tracks: Array<TrackItem>): Promise<Array<String>> = Promise.async { core.downloadPlaylist(playlistId, tracks) }
30
+ override fun downloadTrack(
31
+ track: TrackItem,
32
+ playlistId: String?,
33
+ ): Promise<String> = Promise.async { core.downloadTrack(track, playlistId) }
34
+
35
+ override fun downloadPlaylist(
36
+ playlistId: String,
37
+ tracks: Array<TrackItem>,
38
+ ): Promise<Array<String>> = Promise.async { core.downloadPlaylist(playlistId, tracks) }
39
+
30
40
  override fun pauseDownload(downloadId: String): Promise<Unit> = Promise.async { core.pauseDownload(downloadId) }
41
+
31
42
  override fun resumeDownload(downloadId: String): Promise<Unit> = Promise.async { core.resumeDownload(downloadId) }
43
+
32
44
  override fun cancelDownload(downloadId: String): Promise<Unit> = Promise.async { core.cancelDownload(downloadId) }
45
+
33
46
  override fun retryDownload(downloadId: String): Promise<Unit> = Promise.async { core.retryDownload(downloadId) }
47
+
34
48
  override fun pauseAllDownloads(): Promise<Unit> = Promise.async { core.pauseAllDownloads() }
49
+
35
50
  override fun resumeAllDownloads(): Promise<Unit> = Promise.async { core.resumeAllDownloads() }
51
+
36
52
  override fun cancelAllDownloads(): Promise<Unit> = Promise.async { core.cancelAllDownloads() }
37
53
 
38
54
  // ── Download status (sync) ────────────────────────────────────────────────
39
55
  override fun getDownloadTask(downloadId: String): Variant_NullType_DownloadTask {
40
56
  val task = core.getDownloadTask(downloadId)
41
- return if (task != null) Variant_NullType_DownloadTask.create(task)
42
- else Variant_NullType_DownloadTask.create(NullType.NULL)
57
+ return if (task != null) {
58
+ Variant_NullType_DownloadTask.create(task)
59
+ } else {
60
+ Variant_NullType_DownloadTask.create(NullType.NULL)
61
+ }
43
62
  }
63
+
44
64
  override fun getActiveDownloads(): Array<DownloadTask> = core.getActiveDownloads()
65
+
45
66
  override fun getQueueStatus(): DownloadQueueStatus = core.getQueueStatus()
67
+
46
68
  override fun isDownloading(trackId: String): Boolean = core.isDownloading(trackId)
69
+
47
70
  override fun getDownloadState(trackId: String): DownloadState = core.getDownloadState(trackId)
48
71
 
49
72
  // ── Downloaded content queries (now async per spec) ───────────────────────
50
73
  override fun isTrackDownloaded(trackId: String): Promise<Boolean> = Promise.async { core.isTrackDownloaded(trackId) }
74
+
51
75
  override fun isPlaylistDownloaded(playlistId: String): Promise<Boolean> = Promise.async { core.isPlaylistDownloaded(playlistId) }
76
+
52
77
  override fun isPlaylistPartiallyDownloaded(playlistId: String): Promise<Boolean> = Promise.async { core.isPlaylistPartiallyDownloaded(playlistId) }
53
78
 
54
- override fun getDownloadedTrack(trackId: String): Promise<Variant_NullType_DownloadedTrack> = Promise.async {
55
- val track = core.getDownloadedTrack(trackId)
56
- if (track != null) Variant_NullType_DownloadedTrack.create(track)
57
- else Variant_NullType_DownloadedTrack.create(NullType.NULL)
58
- }
79
+ override fun getDownloadedTrack(trackId: String): Promise<Variant_NullType_DownloadedTrack> =
80
+ Promise.async {
81
+ val track = core.getDownloadedTrack(trackId)
82
+ if (track != null) {
83
+ Variant_NullType_DownloadedTrack.create(track)
84
+ } else {
85
+ Variant_NullType_DownloadedTrack.create(NullType.NULL)
86
+ }
87
+ }
88
+
59
89
  override fun getAllDownloadedTracks(): Promise<Array<DownloadedTrack>> = Promise.async { core.getAllDownloadedTracks() }
60
90
 
61
- override fun getDownloadedPlaylist(playlistId: String): Promise<Variant_NullType_DownloadedPlaylist> = Promise.async {
62
- val playlist = core.getDownloadedPlaylist(playlistId)
63
- if (playlist != null) Variant_NullType_DownloadedPlaylist.create(playlist)
64
- else Variant_NullType_DownloadedPlaylist.create(NullType.NULL)
65
- }
91
+ override fun getDownloadedPlaylist(playlistId: String): Promise<Variant_NullType_DownloadedPlaylist> =
92
+ Promise.async {
93
+ val playlist = core.getDownloadedPlaylist(playlistId)
94
+ if (playlist != null) {
95
+ Variant_NullType_DownloadedPlaylist.create(playlist)
96
+ } else {
97
+ Variant_NullType_DownloadedPlaylist.create(NullType.NULL)
98
+ }
99
+ }
100
+
66
101
  override fun getAllDownloadedPlaylists(): Promise<Array<DownloadedPlaylist>> = Promise.async { core.getAllDownloadedPlaylists() }
67
102
 
68
- override fun getLocalPath(trackId: String): Promise<Variant_NullType_String> = Promise.async {
69
- val path = core.getLocalPath(trackId)
70
- if (path != null) Variant_NullType_String.create(path)
71
- else Variant_NullType_String.create(NullType.NULL)
72
- }
103
+ override fun getLocalPath(trackId: String): Promise<Variant_NullType_String> =
104
+ Promise.async {
105
+ val path = core.getLocalPath(trackId)
106
+ if (path != null) {
107
+ Variant_NullType_String.create(path)
108
+ } else {
109
+ Variant_NullType_String.create(NullType.NULL)
110
+ }
111
+ }
73
112
 
74
113
  override fun syncDownloads(): Promise<Double> = Promise.async { core.syncDownloads().toDouble() }
114
+
75
115
  override fun getEffectiveUrl(track: TrackItem): Promise<String> = Promise.async { core.getEffectiveUrl(track) }
76
116
 
77
117
  // ── Deletion ──────────────────────────────────────────────────────────────
78
118
  override fun deleteDownloadedTrack(trackId: String): Promise<Unit> = Promise.async { core.deleteDownloadedTrack(trackId) }
119
+
79
120
  override fun deleteDownloadedPlaylist(playlistId: String): Promise<Unit> = Promise.async { core.deleteDownloadedPlaylist(playlistId) }
121
+
80
122
  override fun deleteAllDownloads(): Promise<Unit> = Promise.async { core.deleteAllDownloads() }
123
+
81
124
  override fun getStorageInfo(): Promise<DownloadStorageInfo> = Promise.async { core.getStorageInfo() }
82
125
 
83
126
  // ── Playback source ───────────────────────────────────────────────────────
84
127
  override fun setPlaybackSourcePreference(preference: PlaybackSource) = core.setPlaybackSourcePreference(preference)
128
+
85
129
  override fun getPlaybackSourcePreference(): PlaybackSource = core.getPlaybackSourcePreference()
86
130
 
87
131
  // ── Events ────────────────────────────────────────────────────────────────
88
132
  override fun onDownloadProgress(callback: (progress: DownloadProgress) -> Unit) = core.addProgressCallback(callback)
133
+
89
134
  override fun onDownloadStateChange(callback: (downloadId: String, trackId: String, state: DownloadState, error: DownloadError?) -> Unit) = core.addStateChangeCallback(callback)
135
+
90
136
  override fun onDownloadComplete(callback: (downloadedTrack: DownloadedTrack) -> Unit) = core.addCompleteCallback(callback)
91
137
  }
@@ -13,37 +13,59 @@ class HybridEqualizer : HybridEqualizerSpec() {
13
13
  private val core: EqualizerCore
14
14
 
15
15
  init {
16
- val context = NitroModules.applicationContext
17
- ?: throw IllegalStateException("React Context is not initialized")
16
+ val context =
17
+ NitroModules.applicationContext
18
+ ?: throw IllegalStateException("React Context is not initialized")
18
19
  core = EqualizerCore.getInstance(context)
19
20
  core.ensureInitialized()
20
21
  }
21
22
 
22
23
  // ── Sync reads ────────────────────────────────────────────────────────────
23
24
  override fun isEnabled(): Boolean = core.isEnabled()
25
+
24
26
  override fun getBandRange(): GainRange = core.getBandRange()
27
+
25
28
  override fun getPresets(): Array<EqualizerPreset> = core.getPresets()
29
+
26
30
  override fun getBuiltInPresets(): Array<EqualizerPreset> = core.getBuiltInPresets()
31
+
27
32
  override fun getCustomPresets(): Array<EqualizerPreset> = core.getCustomPresets()
33
+
28
34
  override fun getCurrentPresetName(): Variant_NullType_String {
29
35
  val name = core.getCurrentPresetName()
30
- return if (name != null) Variant_NullType_String.create(name)
31
- else Variant_NullType_String.create(NullType.NULL)
36
+ return if (name != null) {
37
+ Variant_NullType_String.create(name)
38
+ } else {
39
+ Variant_NullType_String.create(NullType.NULL)
40
+ }
32
41
  }
33
42
 
34
43
  // ── Async mutations (per v2 spec) ─────────────────────────────────────────
35
44
  override fun setEnabled(enabled: Boolean): Promise<Unit> = Promise.async { core.setEnabled(enabled) }
45
+
36
46
  override fun getBands(): Promise<Array<EqualizerBand>> = Promise.async { core.getBands() }
37
- override fun setBandGain(bandIndex: Double, gainDb: Double): Promise<Unit> = Promise.async { core.setBandGain(bandIndex.toInt(), gainDb) }
47
+
48
+ override fun setBandGain(
49
+ bandIndex: Double,
50
+ gainDb: Double,
51
+ ): Promise<Unit> = Promise.async { core.setBandGain(bandIndex.toInt(), gainDb) }
52
+
38
53
  override fun setAllBandGains(gains: DoubleArray): Promise<Unit> = Promise.async { core.setAllBandGains(gains) }
54
+
39
55
  override fun applyPreset(presetName: String): Promise<Unit> = Promise.async { core.applyPreset(presetName) }
56
+
40
57
  override fun saveCustomPreset(name: String): Promise<Unit> = Promise.async { core.saveCustomPreset(name) }
58
+
41
59
  override fun deleteCustomPreset(name: String): Promise<Unit> = Promise.async { core.deleteCustomPreset(name) }
60
+
42
61
  override fun getState(): Promise<EqualizerState> = Promise.async { core.getState() }
62
+
43
63
  override fun reset(): Promise<Unit> = Promise.async { core.reset() }
44
64
 
45
65
  // ── Events ────────────────────────────────────────────────────────────────
46
66
  override fun onEnabledChange(callback: (enabled: Boolean) -> Unit) = core.addOnEnabledChangeListener(callback)
67
+
47
68
  override fun onBandChange(callback: (bands: Array<EqualizerBand>) -> Unit) = core.addOnBandChangeListener(callback)
69
+
48
70
  override fun onPresetChange(callback: (presetName: Variant_NullType_String?) -> Unit) = core.addOnPresetChangeListener(callback)
49
71
  }
@@ -21,8 +21,9 @@ class HybridPlayerQueue : HybridPlayerQueueSpec() {
21
21
  private val playlistManager: PlaylistManager
22
22
 
23
23
  init {
24
- val context = NitroModules.applicationContext
25
- ?: throw IllegalStateException("React Context is not initialized")
24
+ val context =
25
+ NitroModules.applicationContext
26
+ ?: throw IllegalStateException("React Context is not initialized")
26
27
  core = TrackPlayerCore.getInstance(context)
27
28
  playlistManager = core.getPlaylistManager()
28
29
  }
@@ -32,87 +33,125 @@ class HybridPlayerQueue : HybridPlayerQueueSpec() {
32
33
 
33
34
  // ── Playlist CRUD ─────────────────────────────────────────────────────────
34
35
 
35
- override fun createPlaylist(name: String, description: String?, artwork: String?): Promise<String> =
36
- Promise.async { playlistManager.createPlaylist(name, description, artwork) }
36
+ override fun createPlaylist(
37
+ name: String,
38
+ description: String?,
39
+ artwork: String?,
40
+ ): Promise<String> = Promise.async { playlistManager.createPlaylist(name, description, artwork) }
37
41
 
38
- override fun deletePlaylist(playlistId: String): Promise<Unit> = Promise.async {
39
- playlistManager.deletePlaylist(playlistId)
40
- }
42
+ override fun deletePlaylist(playlistId: String): Promise<Unit> =
43
+ Promise.async {
44
+ playlistManager.deletePlaylist(playlistId)
45
+ }
41
46
 
42
- override fun updatePlaylist(playlistId: String, name: String?, description: String?, artwork: String?): Promise<Unit> = Promise.async {
43
- playlistManager.updatePlaylist(playlistId, name, description, artwork)
44
- core.updatePlaylist(playlistId)
45
- }
47
+ override fun updatePlaylist(
48
+ playlistId: String,
49
+ name: String?,
50
+ description: String?,
51
+ artwork: String?,
52
+ ): Promise<Unit> =
53
+ Promise.async {
54
+ playlistManager.updatePlaylist(playlistId, name, description, artwork)
55
+ core.updatePlaylist(playlistId)
56
+ }
46
57
 
47
58
  override fun getPlaylist(playlistId: String): Variant_NullType_Playlist {
48
59
  val playlist = playlistManager.getPlaylist(playlistId)
49
- return if (playlist != null) Variant_NullType_Playlist.create(playlist.toPlaylist())
50
- else Variant_NullType_Playlist.create(NullType.NULL)
60
+ return if (playlist != null) {
61
+ Variant_NullType_Playlist.create(playlist.toPlaylist())
62
+ } else {
63
+ Variant_NullType_Playlist.create(NullType.NULL)
64
+ }
51
65
  }
52
66
 
53
- override fun getAllPlaylists(): Array<Playlist> =
54
- playlistManager.getAllPlaylists().map { it.toPlaylist() }.toTypedArray()
67
+ override fun getAllPlaylists(): Array<Playlist> = playlistManager.getAllPlaylists().map { it.toPlaylist() }.toTypedArray()
55
68
 
56
69
  // ── Track mutations ───────────────────────────────────────────────────────
57
70
 
58
- override fun addTrackToPlaylist(playlistId: String, track: TrackItem, index: Double?): Promise<Unit> = Promise.async {
59
- playlistManager.addTrackToPlaylist(playlistId, track, index?.toInt())
60
- core.updatePlaylist(playlistId)
61
- }
71
+ override fun addTrackToPlaylist(
72
+ playlistId: String,
73
+ track: TrackItem,
74
+ index: Double?,
75
+ ): Promise<Unit> =
76
+ Promise.async {
77
+ playlistManager.addTrackToPlaylist(playlistId, track, index?.toInt())
78
+ core.updatePlaylist(playlistId)
79
+ }
62
80
 
63
- override fun addTracksToPlaylist(playlistId: String, tracks: Array<TrackItem>, index: Double?): Promise<Unit> = Promise.async {
64
- playlistManager.addTracksToPlaylist(playlistId, tracks.toList(), index?.toInt())
65
- core.updatePlaylist(playlistId)
66
- }
81
+ override fun addTracksToPlaylist(
82
+ playlistId: String,
83
+ tracks: Array<TrackItem>,
84
+ index: Double?,
85
+ ): Promise<Unit> =
86
+ Promise.async {
87
+ playlistManager.addTracksToPlaylist(playlistId, tracks.toList(), index?.toInt())
88
+ core.updatePlaylist(playlistId)
89
+ }
67
90
 
68
- override fun removeTrackFromPlaylist(playlistId: String, trackId: String): Promise<Unit> = Promise.async {
69
- playlistManager.removeTrackFromPlaylist(playlistId, trackId)
70
- core.updatePlaylist(playlistId)
71
- }
91
+ override fun removeTrackFromPlaylist(
92
+ playlistId: String,
93
+ trackId: String,
94
+ ): Promise<Unit> =
95
+ Promise.async {
96
+ playlistManager.removeTrackFromPlaylist(playlistId, trackId)
97
+ core.updatePlaylist(playlistId)
98
+ }
72
99
 
73
- override fun reorderTrackInPlaylist(playlistId: String, trackId: String, newIndex: Double): Promise<Unit> = Promise.async {
74
- playlistManager.reorderTrackInPlaylist(playlistId, trackId, newIndex.toInt())
75
- core.updatePlaylist(playlistId)
76
- }
100
+ override fun reorderTrackInPlaylist(
101
+ playlistId: String,
102
+ trackId: String,
103
+ newIndex: Double,
104
+ ): Promise<Unit> =
105
+ Promise.async {
106
+ playlistManager.reorderTrackInPlaylist(playlistId, trackId, newIndex.toInt())
107
+ core.updatePlaylist(playlistId)
108
+ }
77
109
 
78
110
  // ── Playback control ──────────────────────────────────────────────────────
79
111
 
80
- override fun loadPlaylist(playlistId: String): Promise<Unit> = Promise.async {
81
- core.loadPlaylist(playlistId)
82
- }
112
+ override fun loadPlaylist(playlistId: String): Promise<Unit> =
113
+ Promise.async {
114
+ core.loadPlaylist(playlistId)
115
+ }
83
116
 
84
117
  override fun getCurrentPlaylistId(): Variant_NullType_String {
85
118
  val id = core.getCurrentPlaylistId()
86
- return if (id != null) Variant_NullType_String.create(id)
87
- else Variant_NullType_String.create(NullType.NULL)
119
+ return if (id != null) {
120
+ Variant_NullType_String.create(id)
121
+ } else {
122
+ Variant_NullType_String.create(NullType.NULL)
123
+ }
88
124
  }
89
125
 
90
126
  // ── Events ────────────────────────────────────────────────────────────────
91
127
 
92
128
  override fun onPlaylistsChanged(callback: (playlists: Array<Playlist>, operation: QueueOperation?) -> Unit) {
93
- val removeListener = playlistManager.addPlaylistsChangeListener { playlists, operation ->
94
- callback(playlists.map { it.toPlaylist() }.toTypedArray(), operation)
95
- }
129
+ val removeListener =
130
+ playlistManager.addPlaylistsChangeListener { playlists, operation ->
131
+ callback(playlists.map { it.toPlaylist() }.toTypedArray(), operation)
132
+ }
96
133
  playlistsChangeListeners.add(removeListener)
97
134
  }
98
135
 
99
136
  override fun onPlaylistChanged(callback: (playlistId: String, playlist: Playlist, operation: QueueOperation?) -> Unit) {
100
137
  val listenerId = UUID.randomUUID().toString()
101
138
  playlistManager.getAllPlaylists().forEach { internalPlaylist ->
102
- val removeListener = playlistManager.addPlaylistChangeListener(internalPlaylist.id) { playlist, operation ->
103
- callback(playlist.id, playlist.toPlaylist(), operation)
104
- }
139
+ val removeListener =
140
+ playlistManager.addPlaylistChangeListener(internalPlaylist.id) { playlist, operation ->
141
+ callback(playlist.id, playlist.toPlaylist(), operation)
142
+ }
105
143
  playlistChangeListeners[listenerId] = removeListener
106
144
  }
107
145
  }
108
146
 
109
147
  // ── Helper ────────────────────────────────────────────────────────────────
110
148
 
111
- private fun InternalPlaylist.toPlaylist(): Playlist = Playlist(
112
- id = this.id,
113
- name = this.name,
114
- description = this.description?.let { Variant_NullType_String.create(it) },
115
- artwork = this.artwork?.let { Variant_NullType_String.create(it) },
116
- tracks = this.tracks.toTypedArray(),
117
- )
149
+ private fun InternalPlaylist.toPlaylist(): Playlist =
150
+ Playlist(
151
+ id = this.id,
152
+ name = this.name,
153
+ description = this.description?.let { Variant_NullType_String.create(it) },
154
+ artwork = this.artwork?.let { Variant_NullType_String.create(it) },
155
+ tracks = this.tracks.toTypedArray(),
156
+ )
118
157
  }