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.
- package/android/src/main/AndroidManifest.xml +14 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt +5 -6
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +68 -49
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridDownloadManager.kt +67 -21
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridEqualizer.kt +27 -5
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +88 -49
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +40 -10
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ExoPlayerCore.kt +70 -29
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ListenerRegistry.kt +4 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAndroidAuto.kt +38 -32
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +70 -65
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +23 -12
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerNotify.kt +16 -4
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +101 -72
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueue.kt +42 -22
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +40 -23
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerSetup.kt +4 -3
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerTempQueue.kt +73 -62
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerUrlLoader.kt +51 -48
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +3 -3
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadManagerCore.kt +12 -3
- package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +43 -34
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +30 -178
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +40 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +87 -85
- package/package.json +1 -1
- package/src/hooks/useEqualizer.ts +15 -12
- package/src/specs/AndroidAutoMediaLibrary.nitro.ts +3 -2
- package/src/specs/DownloadManager.nitro.ts +4 -2
- package/src/specs/Equalizer.nitro.ts +4 -2
- 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"
|
package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt
CHANGED
|
@@ -14,14 +14,13 @@ class HybridAndroidAutoMediaLibrary : HybridAndroidAutoMediaLibrarySpec() {
|
|
|
14
14
|
private val core: TrackPlayerCore
|
|
15
15
|
|
|
16
16
|
init {
|
|
17
|
-
val context =
|
|
18
|
-
|
|
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 =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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> =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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 =
|
|
19
|
-
|
|
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(
|
|
29
|
-
|
|
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)
|
|
42
|
-
|
|
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> =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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> =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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> =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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 =
|
|
17
|
-
|
|
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)
|
|
31
|
-
|
|
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
|
-
|
|
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 =
|
|
25
|
-
|
|
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(
|
|
36
|
-
|
|
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> =
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
override fun deletePlaylist(playlistId: String): Promise<Unit> =
|
|
43
|
+
Promise.async {
|
|
44
|
+
playlistManager.deletePlaylist(playlistId)
|
|
45
|
+
}
|
|
41
46
|
|
|
42
|
-
override fun updatePlaylist(
|
|
43
|
-
|
|
44
|
-
|
|
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)
|
|
50
|
-
|
|
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(
|
|
59
|
-
|
|
60
|
-
|
|
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(
|
|
64
|
-
|
|
65
|
-
|
|
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(
|
|
69
|
-
|
|
70
|
-
|
|
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(
|
|
74
|
-
|
|
75
|
-
|
|
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> =
|
|
81
|
-
|
|
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)
|
|
87
|
-
|
|
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 =
|
|
94
|
-
|
|
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 =
|
|
103
|
-
|
|
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 =
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
}
|