react-native-nitro-player 0.7.0 → 0.7.1-alpha.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.
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt +9 -13
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +45 -90
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridDownloadManager.kt +48 -182
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridEqualizer.kt +21 -77
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +55 -104
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +113 -123
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ExoPlayerCore.kt +82 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ListenerRegistry.kt +48 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAndroidAuto.kt +62 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +153 -1887
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +122 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerNotify.kt +44 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +162 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueue.kt +165 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +161 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerSetup.kt +28 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerTempQueue.kt +121 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerUrlLoader.kt +98 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +27 -18
- package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +11 -58
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +13 -30
- package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +102 -162
- package/ios/HybridDownloadManager.swift +32 -26
- package/ios/HybridEqualizer.swift +48 -35
- package/ios/HybridTrackPlayer.swift +127 -102
- package/ios/core/ListenerRegistry.swift +60 -0
- package/ios/core/TrackPlayerCore.swift +130 -2356
- package/ios/core/TrackPlayerListener.swift +395 -0
- package/ios/core/TrackPlayerNotify.swift +52 -0
- package/ios/core/TrackPlayerPlayback.swift +274 -0
- package/ios/core/TrackPlayerQueue.swift +212 -0
- package/ios/core/TrackPlayerQueueBuild.swift +482 -0
- package/ios/core/TrackPlayerTempQueue.swift +167 -0
- package/ios/core/TrackPlayerUrlLoader.swift +169 -0
- package/ios/equalizer/EqualizerCore.swift +24 -89
- package/ios/media/MediaSessionManager.swift +32 -49
- package/ios/playlist/PlaylistManager.swift +2 -9
- package/ios/queue/HybridPlayerQueue.swift +69 -66
- package/lib/hooks/useDownloadedTracks.js +16 -13
- package/lib/hooks/useEqualizer.d.ts +4 -4
- package/lib/hooks/useEqualizer.js +12 -12
- package/lib/hooks/useEqualizerPresets.d.ts +3 -3
- package/lib/hooks/useEqualizerPresets.js +12 -18
- package/lib/specs/AndroidAutoMediaLibrary.nitro.d.ts +2 -2
- package/lib/specs/AudioDevices.nitro.d.ts +2 -2
- package/lib/specs/DownloadManager.nitro.d.ts +10 -10
- package/lib/specs/Equalizer.nitro.d.ts +9 -9
- package/lib/specs/TrackPlayer.nitro.d.ts +38 -16
- package/nitrogen/generated/android/NitroPlayerOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_TrackItem__std__vector_TrackItem_.hpp +122 -0
- package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.cpp +31 -6
- package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.hpp +2 -2
- package/nitrogen/generated/android/c++/JHybridAudioDevicesSpec.cpp +16 -3
- package/nitrogen/generated/android/c++/JHybridAudioDevicesSpec.hpp +1 -1
- package/nitrogen/generated/android/c++/JHybridDownloadManagerSpec.cpp +154 -44
- package/nitrogen/generated/android/c++/JHybridDownloadManagerSpec.hpp +10 -10
- package/nitrogen/generated/android/c++/JHybridEqualizerSpec.cpp +130 -34
- package/nitrogen/generated/android/c++/JHybridEqualizerSpec.hpp +9 -9
- package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.cpp +115 -24
- package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.hpp +8 -8
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +243 -24
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +16 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_std__vector_TrackItem__std__vector_TrackItem_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrarySpec.kt +3 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridAudioDevicesSpec.kt +2 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridDownloadManagerSpec.kt +10 -10
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridEqualizerSpec.kt +10 -9
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridPlayerQueueSpec.kt +9 -8
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridTrackPlayerSpec.kt +45 -8
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.cpp +74 -18
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +380 -151
- package/nitrogen/generated/ios/c++/HybridDownloadManagerSpecSwift.hpp +10 -10
- package/nitrogen/generated/ios/c++/HybridEqualizerSpecSwift.hpp +12 -9
- package/nitrogen/generated/ios/c++/HybridPlayerQueueSpecSwift.hpp +23 -8
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +82 -8
- package/nitrogen/generated/ios/swift/Func_void_EqualizerState.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__DownloadedPlaylist_.swift +58 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__DownloadedTrack_.swift +58 -0
- package/nitrogen/generated/ios/swift/Func_void_std__variant_nitro__NullType__std__string_.swift +58 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_DownloadedPlaylist_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_DownloadedTrack_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_EqualizerBand_.swift +5 -5
- package/nitrogen/generated/ios/swift/Func_void_std__vector_TrackItem__std__vector_TrackItem_.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridDownloadManagerSpec.swift +10 -10
- package/nitrogen/generated/ios/swift/HybridDownloadManagerSpec_cxx.swift +141 -71
- package/nitrogen/generated/ios/swift/HybridEqualizerSpec.swift +9 -9
- package/nitrogen/generated/ios/swift/HybridEqualizerSpec_cxx.swift +105 -41
- package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec.swift +8 -8
- package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec_cxx.swift +95 -32
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +16 -8
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +267 -32
- package/nitrogen/generated/shared/c++/HybridAndroidAutoMediaLibrarySpec.hpp +3 -2
- package/nitrogen/generated/shared/c++/HybridAudioDevicesSpec.hpp +2 -1
- package/nitrogen/generated/shared/c++/HybridDownloadManagerSpec.hpp +10 -10
- package/nitrogen/generated/shared/c++/HybridEqualizerSpec.hpp +10 -9
- package/nitrogen/generated/shared/c++/HybridPlayerQueueSpec.hpp +9 -8
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +8 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +16 -8
- package/package.json +1 -1
- package/src/hooks/useDownloadedTracks.ts +17 -13
- package/src/hooks/useEqualizer.ts +16 -16
- package/src/hooks/useEqualizerPresets.ts +15 -21
- package/src/specs/AndroidAutoMediaLibrary.nitro.ts +2 -2
- package/src/specs/AudioDevices.nitro.ts +2 -2
- package/src/specs/DownloadManager.nitro.ts +10 -10
- package/src/specs/Equalizer.nitro.ts +9 -9
- package/src/specs/TrackPlayer.nitro.ts +52 -16
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
@file:Suppress("ktlint:standard:max-line-length")
|
|
2
|
+
|
|
1
3
|
package com.margelo.nitro.nitroplayer
|
|
2
4
|
|
|
3
5
|
import androidx.annotation.Keep
|
|
4
|
-
import com.facebook.jni.HybridData
|
|
5
6
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
6
7
|
import com.margelo.nitro.NitroModules
|
|
7
8
|
import com.margelo.nitro.core.NullType
|
|
9
|
+
import com.margelo.nitro.core.Promise
|
|
8
10
|
import com.margelo.nitro.nitroplayer.core.TrackPlayerCore
|
|
11
|
+
import com.margelo.nitro.nitroplayer.core.loadPlaylist
|
|
12
|
+
import com.margelo.nitro.nitroplayer.core.updatePlaylist
|
|
9
13
|
import com.margelo.nitro.nitroplayer.playlist.PlaylistManager
|
|
10
14
|
import java.util.UUID
|
|
11
15
|
import com.margelo.nitro.nitroplayer.playlist.Playlist as InternalPlaylist
|
|
@@ -17,7 +21,8 @@ class HybridPlayerQueue : HybridPlayerQueueSpec() {
|
|
|
17
21
|
private val playlistManager: PlaylistManager
|
|
18
22
|
|
|
19
23
|
init {
|
|
20
|
-
val context = NitroModules.applicationContext
|
|
24
|
+
val context = NitroModules.applicationContext
|
|
25
|
+
?: throw IllegalStateException("React Context is not initialized")
|
|
21
26
|
core = TrackPlayerCore.getInstance(context)
|
|
22
27
|
playlistManager = core.getPlaylistManager()
|
|
23
28
|
}
|
|
@@ -25,143 +30,89 @@ class HybridPlayerQueue : HybridPlayerQueueSpec() {
|
|
|
25
30
|
private val playlistsChangeListeners = java.util.concurrent.CopyOnWriteArrayList<() -> Unit>()
|
|
26
31
|
private val playlistChangeListeners = java.util.concurrent.ConcurrentHashMap<String, () -> Unit>()
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
override fun createPlaylist(
|
|
31
|
-
name
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
): String = playlistManager.createPlaylist(name, description, artwork)
|
|
35
|
-
|
|
36
|
-
@DoNotStrip
|
|
37
|
-
@Keep
|
|
38
|
-
override fun deletePlaylist(playlistId: String) {
|
|
33
|
+
// ── Playlist CRUD ─────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
override fun createPlaylist(name: String, description: String?, artwork: String?): Promise<String> =
|
|
36
|
+
Promise.async { playlistManager.createPlaylist(name, description, artwork) }
|
|
37
|
+
|
|
38
|
+
override fun deletePlaylist(playlistId: String): Promise<Unit> = Promise.async {
|
|
39
39
|
playlistManager.deletePlaylist(playlistId)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
@Keep
|
|
44
|
-
override fun updatePlaylist(
|
|
45
|
-
playlistId: String,
|
|
46
|
-
name: String?,
|
|
47
|
-
description: String?,
|
|
48
|
-
artwork: String?,
|
|
49
|
-
) {
|
|
42
|
+
override fun updatePlaylist(playlistId: String, name: String?, description: String?, artwork: String?): Promise<Unit> = Promise.async {
|
|
50
43
|
playlistManager.updatePlaylist(playlistId, name, description, artwork)
|
|
44
|
+
core.updatePlaylist(playlistId)
|
|
51
45
|
}
|
|
52
46
|
|
|
53
|
-
@DoNotStrip
|
|
54
|
-
@Keep
|
|
55
47
|
override fun getPlaylist(playlistId: String): Variant_NullType_Playlist {
|
|
56
48
|
val playlist = playlistManager.getPlaylist(playlistId)
|
|
57
|
-
return if (playlist != null)
|
|
58
|
-
|
|
59
|
-
} else {
|
|
60
|
-
Variant_NullType_Playlist.create(NullType.NULL)
|
|
61
|
-
}
|
|
49
|
+
return if (playlist != null) Variant_NullType_Playlist.create(playlist.toPlaylist())
|
|
50
|
+
else Variant_NullType_Playlist.create(NullType.NULL)
|
|
62
51
|
}
|
|
63
52
|
|
|
64
|
-
@DoNotStrip
|
|
65
|
-
@Keep
|
|
66
53
|
override fun getAllPlaylists(): Array<Playlist> =
|
|
67
|
-
playlistManager
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@Keep
|
|
75
|
-
override fun addTrackToPlaylist(
|
|
76
|
-
playlistId: String,
|
|
77
|
-
track: TrackItem,
|
|
78
|
-
index: Double?,
|
|
79
|
-
) {
|
|
80
|
-
val insertIndex = index?.toInt()
|
|
81
|
-
playlistManager.addTrackToPlaylist(playlistId, track, insertIndex)
|
|
54
|
+
playlistManager.getAllPlaylists().map { it.toPlaylist() }.toTypedArray()
|
|
55
|
+
|
|
56
|
+
// ── Track mutations ───────────────────────────────────────────────────────
|
|
57
|
+
|
|
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)
|
|
82
61
|
}
|
|
83
62
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
playlistId: String,
|
|
88
|
-
tracks: Array<TrackItem>,
|
|
89
|
-
index: Double?,
|
|
90
|
-
) {
|
|
91
|
-
val insertIndex = index?.toInt()
|
|
92
|
-
playlistManager.addTracksToPlaylist(playlistId, tracks.toList(), insertIndex)
|
|
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)
|
|
93
66
|
}
|
|
94
67
|
|
|
95
|
-
|
|
96
|
-
@Keep
|
|
97
|
-
override fun removeTrackFromPlaylist(
|
|
98
|
-
playlistId: String,
|
|
99
|
-
trackId: String,
|
|
100
|
-
) {
|
|
68
|
+
override fun removeTrackFromPlaylist(playlistId: String, trackId: String): Promise<Unit> = Promise.async {
|
|
101
69
|
playlistManager.removeTrackFromPlaylist(playlistId, trackId)
|
|
70
|
+
core.updatePlaylist(playlistId)
|
|
102
71
|
}
|
|
103
72
|
|
|
104
|
-
|
|
105
|
-
@Keep
|
|
106
|
-
override fun reorderTrackInPlaylist(
|
|
107
|
-
playlistId: String,
|
|
108
|
-
trackId: String,
|
|
109
|
-
newIndex: Double,
|
|
110
|
-
) {
|
|
73
|
+
override fun reorderTrackInPlaylist(playlistId: String, trackId: String, newIndex: Double): Promise<Unit> = Promise.async {
|
|
111
74
|
playlistManager.reorderTrackInPlaylist(playlistId, trackId, newIndex.toInt())
|
|
75
|
+
core.updatePlaylist(playlistId)
|
|
112
76
|
}
|
|
113
77
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
override fun loadPlaylist(playlistId: String) {
|
|
117
|
-
|
|
78
|
+
// ── Playback control ──────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
override fun loadPlaylist(playlistId: String): Promise<Unit> = Promise.async {
|
|
81
|
+
core.loadPlaylist(playlistId)
|
|
118
82
|
}
|
|
119
83
|
|
|
120
|
-
@DoNotStrip
|
|
121
|
-
@Keep
|
|
122
84
|
override fun getCurrentPlaylistId(): Variant_NullType_String {
|
|
123
|
-
val
|
|
124
|
-
return if (
|
|
125
|
-
|
|
126
|
-
} else {
|
|
127
|
-
Variant_NullType_String.create(NullType.NULL)
|
|
128
|
-
}
|
|
85
|
+
val id = core.getCurrentPlaylistId()
|
|
86
|
+
return if (id != null) Variant_NullType_String.create(id)
|
|
87
|
+
else Variant_NullType_String.create(NullType.NULL)
|
|
129
88
|
}
|
|
130
89
|
|
|
131
|
-
|
|
132
|
-
|
|
90
|
+
// ── Events ────────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
133
92
|
override fun onPlaylistsChanged(callback: (playlists: Array<Playlist>, operation: QueueOperation?) -> Unit) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
callback(playlists.map { it.toPlaylist() }.toTypedArray(), operation)
|
|
138
|
-
}
|
|
93
|
+
val removeListener = playlistManager.addPlaylistsChangeListener { playlists, operation ->
|
|
94
|
+
callback(playlists.map { it.toPlaylist() }.toTypedArray(), operation)
|
|
95
|
+
}
|
|
139
96
|
playlistsChangeListeners.add(removeListener)
|
|
140
97
|
}
|
|
141
98
|
|
|
142
|
-
@DoNotStrip
|
|
143
|
-
@Keep
|
|
144
99
|
override fun onPlaylistChanged(callback: (playlistId: String, playlist: Playlist, operation: QueueOperation?) -> Unit) {
|
|
145
|
-
// Listen to all playlists and filter by playlistId
|
|
146
100
|
val listenerId = UUID.randomUUID().toString()
|
|
147
|
-
|
|
148
|
-
// For each playlist, add a listener
|
|
149
101
|
playlistManager.getAllPlaylists().forEach { internalPlaylist ->
|
|
150
|
-
val removeListener =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
102
|
+
val removeListener = playlistManager.addPlaylistChangeListener(internalPlaylist.id) { playlist, operation ->
|
|
103
|
+
callback(playlist.id, playlist.toPlaylist(), operation)
|
|
104
|
+
}
|
|
154
105
|
playlistChangeListeners[listenerId] = removeListener
|
|
155
106
|
}
|
|
156
107
|
}
|
|
157
108
|
|
|
158
|
-
// Helper
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
109
|
+
// ── Helper ────────────────────────────────────────────────────────────────
|
|
110
|
+
|
|
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
|
+
)
|
|
167
118
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@file:Suppress("ktlint:standard:max-line-length")
|
|
2
|
+
|
|
1
3
|
package com.margelo.nitro.nitroplayer
|
|
2
4
|
|
|
3
5
|
import androidx.annotation.Keep
|
|
@@ -5,169 +7,157 @@ import com.facebook.proguard.annotations.DoNotStrip
|
|
|
5
7
|
import com.margelo.nitro.NitroModules
|
|
6
8
|
import com.margelo.nitro.core.Promise
|
|
7
9
|
import com.margelo.nitro.nitroplayer.core.TrackPlayerCore
|
|
10
|
+
import com.margelo.nitro.nitroplayer.core.addToUpNext
|
|
11
|
+
import com.margelo.nitro.nitroplayer.core.clearPlayNext
|
|
12
|
+
import com.margelo.nitro.nitroplayer.core.clearUpNext
|
|
13
|
+
import com.margelo.nitro.nitroplayer.core.configure
|
|
14
|
+
import com.margelo.nitro.nitroplayer.core.getActualQueue
|
|
15
|
+
import com.margelo.nitro.nitroplayer.core.getCurrentTrackIndex
|
|
16
|
+
import com.margelo.nitro.nitroplayer.core.getNextTracks
|
|
17
|
+
import com.margelo.nitro.nitroplayer.core.getPlayBackSpeed
|
|
18
|
+
import com.margelo.nitro.nitroplayer.core.getPlayNextQueue
|
|
19
|
+
import com.margelo.nitro.nitroplayer.core.getRepeatMode
|
|
20
|
+
import com.margelo.nitro.nitroplayer.core.getState
|
|
21
|
+
import com.margelo.nitro.nitroplayer.core.getTracksById
|
|
22
|
+
import com.margelo.nitro.nitroplayer.core.getTracksNeedingUrls
|
|
23
|
+
import com.margelo.nitro.nitroplayer.core.getUpNextQueue
|
|
24
|
+
import com.margelo.nitro.nitroplayer.core.pause
|
|
25
|
+
import com.margelo.nitro.nitroplayer.core.play
|
|
26
|
+
import com.margelo.nitro.nitroplayer.core.playNext
|
|
27
|
+
import com.margelo.nitro.nitroplayer.core.playSong
|
|
28
|
+
import com.margelo.nitro.nitroplayer.core.removeFromPlayNext
|
|
29
|
+
import com.margelo.nitro.nitroplayer.core.removeFromUpNext
|
|
30
|
+
import com.margelo.nitro.nitroplayer.core.reorderTemporaryTrack
|
|
31
|
+
import com.margelo.nitro.nitroplayer.core.seek
|
|
32
|
+
import com.margelo.nitro.nitroplayer.core.setPlayBackSpeed
|
|
33
|
+
import com.margelo.nitro.nitroplayer.core.setRepeatMode
|
|
34
|
+
import com.margelo.nitro.nitroplayer.core.setVolume
|
|
35
|
+
import com.margelo.nitro.nitroplayer.core.skipToIndex
|
|
36
|
+
import com.margelo.nitro.nitroplayer.core.skipToNext
|
|
37
|
+
import com.margelo.nitro.nitroplayer.core.skipToPrevious
|
|
38
|
+
import com.margelo.nitro.nitroplayer.core.updateTracks
|
|
8
39
|
|
|
9
40
|
@DoNotStrip
|
|
10
41
|
@Keep
|
|
11
42
|
class HybridTrackPlayer : HybridTrackPlayerSpec() {
|
|
12
43
|
private val core: TrackPlayerCore
|
|
13
44
|
|
|
45
|
+
// Track listener IDs for cleanup in dispose()
|
|
46
|
+
private val listenerIds = mutableListOf<Pair<String, Long>>()
|
|
47
|
+
|
|
14
48
|
init {
|
|
15
|
-
val context =
|
|
16
|
-
|
|
49
|
+
val context = NitroModules.applicationContext
|
|
50
|
+
?: throw IllegalStateException("React Context is not initialized")
|
|
17
51
|
core = TrackPlayerCore.getInstance(context)
|
|
18
52
|
}
|
|
19
53
|
|
|
20
|
-
|
|
21
|
-
@Keep
|
|
22
|
-
override fun play() {
|
|
23
|
-
core.play()
|
|
24
|
-
}
|
|
54
|
+
// ── Playback ─────────────────────────────────────────────────────────────
|
|
25
55
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
override fun
|
|
29
|
-
|
|
30
|
-
}
|
|
56
|
+
override fun play(): Promise<Unit> = Promise.async { core.play() }
|
|
57
|
+
override fun pause(): Promise<Unit> = Promise.async { core.pause() }
|
|
58
|
+
override fun seek(position: Double): Promise<Unit> = Promise.async { core.seek(position) }
|
|
59
|
+
override fun skipToNext(): Promise<Unit> = Promise.async { core.skipToNext() }
|
|
60
|
+
override fun skipToPrevious(): Promise<Unit> = Promise.async { core.skipToPrevious() }
|
|
61
|
+
override fun playSong(songId: String, fromPlaylist: String?): Promise<Unit> = Promise.async { core.playSong(songId, fromPlaylist) }
|
|
62
|
+
override fun skipToIndex(index: Double): Promise<Boolean> = Promise.async { core.skipToIndex(index.toInt()) }
|
|
31
63
|
|
|
32
|
-
override fun
|
|
33
|
-
|
|
34
|
-
fromPlaylist: String?,
|
|
35
|
-
): Promise<Unit> =
|
|
36
|
-
Promise.async {
|
|
37
|
-
core.playSong(songId, fromPlaylist)
|
|
38
|
-
}
|
|
64
|
+
override fun setRepeatMode(mode: RepeatMode): Promise<Unit> = Promise.async { core.setRepeatMode(mode) }
|
|
65
|
+
override fun getRepeatMode(): RepeatMode = core.getRepeatMode()
|
|
39
66
|
|
|
40
|
-
|
|
41
|
-
@Keep
|
|
42
|
-
override fun skipToNext() {
|
|
43
|
-
core.skipToNext()
|
|
44
|
-
}
|
|
67
|
+
override fun setVolume(volume: Double): Promise<Unit> = Promise.async { core.setVolume(volume) }
|
|
45
68
|
|
|
46
|
-
|
|
47
|
-
@Keep
|
|
48
|
-
override fun skipToPrevious() {
|
|
49
|
-
core.skipToPrevious()
|
|
50
|
-
}
|
|
69
|
+
override fun configure(config: PlayerConfig): Promise<Unit> = Promise.async { core.configure(config) }
|
|
51
70
|
|
|
52
|
-
|
|
53
|
-
@Keep
|
|
54
|
-
override fun seek(position: Double) {
|
|
55
|
-
core.seek(position)
|
|
56
|
-
}
|
|
71
|
+
// ── Queue / state reads ───────────────────────────────────────────────────
|
|
57
72
|
|
|
58
|
-
override fun
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
override fun getActualQueue(): Promise<Array<TrackItem>> = Promise.async { core.getActualQueue().toTypedArray() }
|
|
74
|
+
override fun getState(): Promise<PlayerState> = Promise.async { core.getState() }
|
|
75
|
+
override fun getTracksById(trackIds: Array<String>): Promise<Array<TrackItem>> = Promise.async { core.getTracksById(trackIds.toList()).toTypedArray() }
|
|
76
|
+
override fun getTracksNeedingUrls(): Promise<Array<TrackItem>> = Promise.async { core.getTracksNeedingUrls().toTypedArray() }
|
|
77
|
+
override fun getNextTracks(count: Double): Promise<Array<TrackItem>> = Promise.async { core.getNextTracks(count.toInt()).toTypedArray() }
|
|
78
|
+
override fun getCurrentTrackIndex(): Promise<Double> = Promise.async { core.getCurrentTrackIndex().toDouble() }
|
|
62
79
|
|
|
63
|
-
|
|
64
|
-
Promise.async {
|
|
65
|
-
core.playNext(trackId)
|
|
66
|
-
}
|
|
80
|
+
// ── URL updates ───────────────────────────────────────────────────────────
|
|
67
81
|
|
|
68
|
-
override fun
|
|
69
|
-
Promise.async {
|
|
70
|
-
core.getActualQueue().toTypedArray()
|
|
71
|
-
}
|
|
82
|
+
override fun updateTracks(tracks: Array<TrackItem>): Promise<Unit> = Promise.async { core.updateTracks(tracks.toList()) }
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
Promise.async {
|
|
75
|
-
core.getState()
|
|
76
|
-
}
|
|
84
|
+
// ── Temporary queue ───────────────────────────────────────────────────────
|
|
77
85
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
override fun
|
|
86
|
+
override fun addToUpNext(trackId: String): Promise<Unit> = Promise.async { core.addToUpNext(trackId) }
|
|
87
|
+
override fun playNext(trackId: String): Promise<Unit> = Promise.async { core.playNext(trackId) }
|
|
88
|
+
override fun removeFromPlayNext(trackId: String): Promise<Boolean> = Promise.async { core.removeFromPlayNext(trackId) }
|
|
89
|
+
override fun removeFromUpNext(trackId: String): Promise<Boolean> = Promise.async { core.removeFromUpNext(trackId) }
|
|
90
|
+
override fun clearPlayNext(): Promise<Unit> = Promise.async { core.clearPlayNext() }
|
|
91
|
+
override fun clearUpNext(): Promise<Unit> = Promise.async { core.clearUpNext() }
|
|
92
|
+
override fun reorderTemporaryTrack(trackId: String, newIndex: Double): Promise<Boolean> = Promise.async { core.reorderTemporaryTrack(trackId, newIndex.toInt()) }
|
|
93
|
+
override fun getPlayNextQueue(): Promise<Array<TrackItem>> = Promise.async { core.getPlayNextQueue().toTypedArray() }
|
|
94
|
+
override fun getUpNextQueue(): Promise<Array<TrackItem>> = Promise.async { core.getUpNextQueue().toTypedArray() }
|
|
81
95
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
override fun
|
|
96
|
+
// ── Playback speed ────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
override fun setPlaybackSpeed(speed: Double): Promise<Unit> = Promise.async { core.setPlayBackSpeed(speed) }
|
|
99
|
+
override fun getPlaybackSpeed(): Promise<Double> = Promise.async { core.getPlayBackSpeed() }
|
|
100
|
+
|
|
101
|
+
// ── Android Auto ──────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
override fun isAndroidAutoConnected(): Boolean = core.isAndroidAutoConnected()
|
|
104
|
+
|
|
105
|
+
// ── Event listeners ───────────────────────────────────────────────────────
|
|
85
106
|
|
|
86
107
|
override fun onChangeTrack(callback: (track: TrackItem, reason: Reason?) -> Unit) {
|
|
87
|
-
core.addOnChangeTrackListener(callback)
|
|
108
|
+
val id = core.addOnChangeTrackListener(callback)
|
|
109
|
+
listenerIds += "onChangeTrack" to id
|
|
88
110
|
}
|
|
89
111
|
|
|
90
112
|
override fun onPlaybackStateChange(callback: (state: TrackPlayerState, reason: Reason?) -> Unit) {
|
|
91
|
-
core.addOnPlaybackStateChangeListener(callback)
|
|
113
|
+
val id = core.addOnPlaybackStateChangeListener(callback)
|
|
114
|
+
listenerIds += "onPlaybackStateChange" to id
|
|
92
115
|
}
|
|
93
116
|
|
|
94
117
|
override fun onSeek(callback: (position: Double, totalDuration: Double) -> Unit) {
|
|
95
|
-
core.addOnSeekListener(callback)
|
|
118
|
+
val id = core.addOnSeekListener(callback)
|
|
119
|
+
listenerIds += "onSeek" to id
|
|
96
120
|
}
|
|
97
121
|
|
|
98
122
|
override fun onPlaybackProgressChange(callback: (position: Double, totalDuration: Double, isManuallySeeked: Boolean?) -> Unit) {
|
|
99
|
-
core.addOnPlaybackProgressChangeListener(callback)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
@DoNotStrip
|
|
103
|
-
@Keep
|
|
104
|
-
override fun configure(config: PlayerConfig) {
|
|
105
|
-
core.configure(
|
|
106
|
-
androidAutoEnabled = config.androidAutoEnabled,
|
|
107
|
-
carPlayEnabled = config.carPlayEnabled,
|
|
108
|
-
showInNotification = config.showInNotification,
|
|
109
|
-
lookaheadCount = config.lookaheadCount?.toInt(),
|
|
110
|
-
)
|
|
123
|
+
val id = core.addOnPlaybackProgressChangeListener(callback)
|
|
124
|
+
listenerIds += "onPlaybackProgressChange" to id
|
|
111
125
|
}
|
|
112
126
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
127
|
+
override fun onAndroidAutoConnectionChange(callback: (connected: Boolean) -> Unit) {
|
|
128
|
+
val id = core.addOnAndroidAutoConnectionListener(callback)
|
|
129
|
+
listenerIds += "onAndroidAutoConnectionChange" to id
|
|
116
130
|
}
|
|
117
131
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
@DoNotStrip
|
|
122
|
-
@Keep
|
|
123
|
-
override fun setVolume(volume: Double): Boolean = core.setVolume(volume)
|
|
124
|
-
|
|
125
|
-
@DoNotStrip
|
|
126
|
-
@Keep
|
|
127
|
-
override fun skipToIndex(index: Double): Promise<Boolean> =
|
|
128
|
-
Promise.async {
|
|
129
|
-
core.skipToIndex(index.toInt())
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
override fun updateTracks(tracks: Array<TrackItem>): Promise<Unit> =
|
|
133
|
-
Promise.async {
|
|
134
|
-
core.updateTracks(tracks.toList())
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
override fun getTracksById(trackIds: Array<String>): Promise<Array<TrackItem>> =
|
|
138
|
-
Promise.async {
|
|
139
|
-
core.getTracksById(trackIds.toList()).toTypedArray()
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
override fun getTracksNeedingUrls(): Promise<Array<TrackItem>> =
|
|
143
|
-
Promise.async {
|
|
144
|
-
core.getTracksNeedingUrls().toTypedArray()
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
override fun getNextTracks(count: Double): Promise<Array<TrackItem>> =
|
|
148
|
-
Promise.async {
|
|
149
|
-
core.getNextTracks(count.toInt()).toTypedArray()
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
override fun getCurrentTrackIndex(): Promise<Double> =
|
|
153
|
-
Promise.async {
|
|
154
|
-
core.getCurrentTrackIndex().toDouble()
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
override fun setPlaybackSpeed(speed: Double): Promise<Unit> =
|
|
158
|
-
Promise.async {
|
|
159
|
-
core.setPlayBackSpeed(speed)
|
|
160
|
-
Unit
|
|
132
|
+
override fun onTracksNeedUpdate(callback: (tracks: Array<TrackItem>, lookahead: Double) -> Unit) {
|
|
133
|
+
val id = core.addOnTracksNeedUpdateListener { tracks, lookahead ->
|
|
134
|
+
callback(tracks.toTypedArray(), lookahead.toDouble())
|
|
161
135
|
}
|
|
136
|
+
listenerIds += "onTracksNeedUpdate" to id
|
|
137
|
+
}
|
|
162
138
|
|
|
163
|
-
override fun
|
|
164
|
-
|
|
165
|
-
|
|
139
|
+
override fun onTemporaryQueueChange(callback: (playNextQueue: Array<TrackItem>, upNextQueue: Array<TrackItem>) -> Unit) {
|
|
140
|
+
val id = core.addOnTemporaryQueueChangeListener { pn, un ->
|
|
141
|
+
callback(pn.toTypedArray(), un.toTypedArray())
|
|
166
142
|
}
|
|
143
|
+
listenerIds += "onTemporaryQueueChange" to id
|
|
144
|
+
}
|
|
167
145
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
146
|
+
// ── Cleanup ───────────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
override fun dispose() {
|
|
149
|
+
super.dispose()
|
|
150
|
+
listenerIds.forEach { (type, id) ->
|
|
151
|
+
when (type) {
|
|
152
|
+
"onChangeTrack" -> core.removeOnChangeTrackListener(id)
|
|
153
|
+
"onPlaybackStateChange" -> core.removeOnPlaybackStateChangeListener(id)
|
|
154
|
+
"onSeek" -> core.removeOnSeekListener(id)
|
|
155
|
+
"onPlaybackProgressChange" -> core.removeOnPlaybackProgressChangeListener(id)
|
|
156
|
+
"onAndroidAutoConnectionChange" -> core.removeOnAndroidAutoConnectionListener(id)
|
|
157
|
+
"onTracksNeedUpdate" -> core.removeOnTracksNeedUpdateListener(id)
|
|
158
|
+
"onTemporaryQueueChange" -> core.removeOnTemporaryQueueChangeListener(id)
|
|
159
|
+
}
|
|
171
160
|
}
|
|
161
|
+
listenerIds.clear()
|
|
172
162
|
}
|
|
173
163
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
package com.margelo.nitro.nitroplayer.core
|
|
2
|
+
|
|
3
|
+
import android.os.HandlerThread
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import androidx.media3.common.AudioAttributes
|
|
6
|
+
import androidx.media3.common.C
|
|
7
|
+
import androidx.media3.common.MediaItem
|
|
8
|
+
import androidx.media3.common.Player
|
|
9
|
+
import androidx.media3.exoplayer.DefaultLoadControl
|
|
10
|
+
import androidx.media3.exoplayer.ExoPlayer
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ExoPlayerCore(context: Context, playerThread: HandlerThread) {
|
|
14
|
+
|
|
15
|
+
/** The underlying ExoPlayer instance — accessible for MediaSessionManager wiring. */
|
|
16
|
+
internal val player: ExoPlayer = build(context, playerThread)
|
|
17
|
+
|
|
18
|
+
private fun build(context: Context, playerThread: HandlerThread): ExoPlayer {
|
|
19
|
+
val loadControl = DefaultLoadControl.Builder()
|
|
20
|
+
.setBufferDurationsMs(
|
|
21
|
+
/* minBufferMs */ 30_000,
|
|
22
|
+
/* maxBufferMs */ 120_000,
|
|
23
|
+
/* bufferForPlayback */ 2_500,
|
|
24
|
+
/* bufferForRebuffer */ 5_000,
|
|
25
|
+
)
|
|
26
|
+
.setBackBuffer(30_000, /* retainBackBufferFromKeyframe */ true)
|
|
27
|
+
.setTargetBufferBytes(C.LENGTH_UNSET)
|
|
28
|
+
.setPrioritizeTimeOverSizeThresholds(true)
|
|
29
|
+
.build()
|
|
30
|
+
|
|
31
|
+
val audioAttrs = AudioAttributes.Builder()
|
|
32
|
+
.setUsage(C.USAGE_MEDIA)
|
|
33
|
+
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
|
|
34
|
+
.build()
|
|
35
|
+
|
|
36
|
+
return ExoPlayer.Builder(context)
|
|
37
|
+
.setLooper(playerThread.looper)
|
|
38
|
+
.setLoadControl(loadControl)
|
|
39
|
+
.setAudioAttributes(audioAttrs, /* handleAudioFocus */ true)
|
|
40
|
+
.setHandleAudioBecomingNoisy(true)
|
|
41
|
+
.setPauseAtEndOfMediaItems(false)
|
|
42
|
+
.build()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── Playback ───────────────────────────────────────────────────────────
|
|
46
|
+
fun play() = player.play()
|
|
47
|
+
fun pause() = player.pause()
|
|
48
|
+
fun seekTo(positionMs: Long) = player.seekTo(positionMs)
|
|
49
|
+
fun seekToNext() = player.seekToNextMediaItem()
|
|
50
|
+
fun hasNextMediaItem(): Boolean = player.hasNextMediaItem()
|
|
51
|
+
fun setRepeatMode(mode: Int) { player.repeatMode = mode }
|
|
52
|
+
fun setVolume(volume: Float) { player.volume = volume }
|
|
53
|
+
fun setPlaybackSpeed(speed: Float) = player.setPlaybackSpeed(speed)
|
|
54
|
+
fun getPlaybackSpeed(): Float = player.playbackParameters.speed
|
|
55
|
+
|
|
56
|
+
// ── Queue mutations ────────────────────────────────────────────────────
|
|
57
|
+
fun prepare() = player.prepare()
|
|
58
|
+
fun seekToDefaultPosition(windowIndex: Int) = player.seekToDefaultPosition(windowIndex)
|
|
59
|
+
fun clearMediaItems() = player.clearMediaItems()
|
|
60
|
+
fun setMediaItems(items: List<MediaItem>, resetPosition: Boolean = false) =
|
|
61
|
+
player.setMediaItems(items, resetPosition)
|
|
62
|
+
fun addMediaItems(items: List<MediaItem>) = player.addMediaItems(items)
|
|
63
|
+
fun removeMediaItems(fromIndex: Int, toIndex: Int) =
|
|
64
|
+
player.removeMediaItems(fromIndex, toIndex)
|
|
65
|
+
fun replaceMediaItem(index: Int, item: MediaItem) = player.replaceMediaItem(index, item)
|
|
66
|
+
|
|
67
|
+
// ── Listener wiring ────────────────────────────────────────────────────
|
|
68
|
+
fun addListener(listener: Player.Listener) = player.addListener(listener)
|
|
69
|
+
fun removeListener(listener: Player.Listener) = player.removeListener(listener)
|
|
70
|
+
|
|
71
|
+
// ── State reads ────────────────────────────────────────────────────────
|
|
72
|
+
val playbackState: Int get() = player.playbackState
|
|
73
|
+
val isPlaying: Boolean get() = player.isPlaying
|
|
74
|
+
var playWhenReady: Boolean
|
|
75
|
+
get() = player.playWhenReady
|
|
76
|
+
set(value) { player.playWhenReady = value }
|
|
77
|
+
val currentMediaItem: MediaItem? get() = player.currentMediaItem
|
|
78
|
+
val currentMediaItemIndex: Int get() = player.currentMediaItemIndex
|
|
79
|
+
val currentPosition: Long get() = player.currentPosition
|
|
80
|
+
val duration: Long get() = player.duration
|
|
81
|
+
val mediaItemCount: Int get() = player.mediaItemCount
|
|
82
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package com.margelo.nitro.nitroplayer.core
|
|
2
|
+
|
|
3
|
+
import java.util.concurrent.CopyOnWriteArrayList
|
|
4
|
+
import java.util.concurrent.atomic.AtomicLong
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Thread-safe listener registry with stable numeric IDs for add/remove.
|
|
8
|
+
* Uses CopyOnWriteArrayList for lock-free iteration and AtomicLong for ID generation.
|
|
9
|
+
*/
|
|
10
|
+
class ListenerRegistry<T> {
|
|
11
|
+
private data class Entry<T>(val id: Long, val callback: T)
|
|
12
|
+
|
|
13
|
+
private val entries = CopyOnWriteArrayList<Entry<T>>()
|
|
14
|
+
private val nextId = AtomicLong(0)
|
|
15
|
+
|
|
16
|
+
/** Register a callback and return its stable ID for later removal. */
|
|
17
|
+
fun add(callback: T): Long {
|
|
18
|
+
val id = nextId.incrementAndGet()
|
|
19
|
+
entries.add(Entry(id, callback))
|
|
20
|
+
return id
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Remove the callback with the given ID. Returns true if found. */
|
|
24
|
+
fun remove(id: Long): Boolean {
|
|
25
|
+
val iterator = entries.iterator()
|
|
26
|
+
while (iterator.hasNext()) {
|
|
27
|
+
val entry = iterator.next()
|
|
28
|
+
if (entry.id == id) {
|
|
29
|
+
entries.remove(entry)
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Remove all registered callbacks. */
|
|
37
|
+
fun clear() = entries.clear()
|
|
38
|
+
|
|
39
|
+
/** Invoke action for every registered callback (snapshot iteration — safe under mutation). */
|
|
40
|
+
fun forEach(action: (T) -> Unit) {
|
|
41
|
+
for (entry in entries) {
|
|
42
|
+
action(entry.callback)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** True when no callbacks are registered. */
|
|
47
|
+
val isEmpty: Boolean get() = entries.isEmpty()
|
|
48
|
+
}
|