react-native-nitro-player 0.7.0 → 0.7.1-alpha.1
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/README.md +47 -46
- 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 +179 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +170 -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 +150 -135
- 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 +221 -0
- package/ios/core/TrackPlayerQueueBuild.swift +493 -0
- package/ios/core/TrackPlayerTempQueue.swift +167 -0
- package/ios/core/TrackPlayerUrlLoader.swift +169 -0
- package/ios/equalizer/EqualizerCore.swift +63 -123
- 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 +22 -17
- 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 +10 -10
- package/lib/specs/TrackPlayer.nitro.d.ts +38 -16
- package/lib/types/EqualizerTypes.d.ts +3 -3
- 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 +5 -5
- package/src/hooks/useDownloadedTracks.ts +17 -13
- package/src/hooks/useEqualizer.ts +26 -21
- 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 +10 -10
- package/src/specs/TrackPlayer.nitro.ts +52 -16
- package/src/types/EqualizerTypes.ts +17 -13
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
@file:Suppress("ktlint:standard:max-line-length")
|
|
2
|
+
|
|
3
|
+
package com.margelo.nitro.nitroplayer.core
|
|
4
|
+
|
|
5
|
+
import com.margelo.nitro.nitroplayer.connection.AndroidAutoConnectionDetector
|
|
6
|
+
import com.margelo.nitro.nitroplayer.media.MediaLibraryParser
|
|
7
|
+
import com.margelo.nitro.nitroplayer.media.NitroPlayerMediaBrowserService
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Android Auto integration — detector setup runs on the main thread (receiver
|
|
11
|
+
* registration), playback commands are suspend and run on the player thread.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Called on the main thread from TrackPlayerCore.init via handler.post. */
|
|
15
|
+
internal fun TrackPlayerCore.setupAndroidAutoDetector() {
|
|
16
|
+
androidAutoConnectionDetector = AndroidAutoConnectionDetector(context).apply {
|
|
17
|
+
onConnectionChanged = { connected, _ ->
|
|
18
|
+
handler.post {
|
|
19
|
+
isAndroidAutoConnectedField = connected
|
|
20
|
+
NitroPlayerMediaBrowserService.isAndroidAutoConnected = connected
|
|
21
|
+
notifyAndroidAutoConnection(connected)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
registerCarConnectionReceiver()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Called by MediaBrowserService when the user picks a track in Android Auto. */
|
|
29
|
+
suspend fun TrackPlayerCore.playFromPlaylistTrack(mediaId: String) = withPlayerContext {
|
|
30
|
+
try {
|
|
31
|
+
val colonIndex = mediaId.indexOf(':')
|
|
32
|
+
if (colonIndex <= 0 || colonIndex >= mediaId.length - 1) return@withPlayerContext
|
|
33
|
+
val playlistId = mediaId.substring(0, colonIndex)
|
|
34
|
+
val trackId = mediaId.substring(colonIndex + 1)
|
|
35
|
+
val playlist = playlistManager.getPlaylist(playlistId) ?: return@withPlayerContext
|
|
36
|
+
val trackIndex = playlist.tracks.indexOfFirst { it.id == trackId }
|
|
37
|
+
if (trackIndex < 0) return@withPlayerContext
|
|
38
|
+
if (currentPlaylistId != playlistId) {
|
|
39
|
+
loadPlaylistInternal(playlistId)
|
|
40
|
+
}
|
|
41
|
+
playFromIndexInternal(trackIndex)
|
|
42
|
+
} catch (_: Exception) {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private fun TrackPlayerCore.loadPlaylistInternal(playlistId: String) {
|
|
46
|
+
playNextStack.clear(); upNextQueue.clear()
|
|
47
|
+
currentTemporaryType = TrackPlayerCore.TemporaryType.NONE
|
|
48
|
+
val playlist = playlistManager.getPlaylist(playlistId) ?: return
|
|
49
|
+
currentPlaylistId = playlistId
|
|
50
|
+
updatePlayerQueue(playlist.tracks)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
suspend fun TrackPlayerCore.setAndroidAutoMediaLibrary(libraryJson: String) = withPlayerContext {
|
|
54
|
+
val library = MediaLibraryParser.fromJson(libraryJson)
|
|
55
|
+
mediaLibraryManager.setMediaLibrary(library)
|
|
56
|
+
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
suspend fun TrackPlayerCore.clearAndroidAutoMediaLibrary() = withPlayerContext {
|
|
60
|
+
mediaLibraryManager.clear()
|
|
61
|
+
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
|
|
62
|
+
}
|