react-native-nitro-player 0.7.1-alpha.0 → 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 (39) hide show
  1. package/README.md +47 -46
  2. package/android/src/main/AndroidManifest.xml +14 -1
  3. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt +5 -6
  4. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +68 -49
  5. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridDownloadManager.kt +67 -21
  6. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridEqualizer.kt +27 -5
  7. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +88 -49
  8. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +40 -10
  9. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ExoPlayerCore.kt +70 -29
  10. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ListenerRegistry.kt +4 -1
  11. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAndroidAuto.kt +38 -32
  12. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +70 -65
  13. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +23 -12
  14. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerNotify.kt +16 -4
  15. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +101 -72
  16. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueue.kt +64 -30
  17. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +54 -28
  18. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerSetup.kt +4 -3
  19. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerTempQueue.kt +73 -62
  20. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerUrlLoader.kt +51 -48
  21. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +3 -3
  22. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadManagerCore.kt +12 -3
  23. package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +169 -98
  24. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +30 -178
  25. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +40 -0
  26. package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +87 -85
  27. package/ios/core/TrackPlayerQueue.swift +27 -18
  28. package/ios/core/TrackPlayerQueueBuild.swift +16 -5
  29. package/ios/equalizer/EqualizerCore.swift +39 -34
  30. package/lib/hooks/useEqualizer.js +10 -5
  31. package/lib/specs/Equalizer.nitro.d.ts +1 -1
  32. package/lib/types/EqualizerTypes.d.ts +3 -3
  33. package/package.json +5 -5
  34. package/src/hooks/useEqualizer.ts +25 -17
  35. package/src/specs/AndroidAutoMediaLibrary.nitro.ts +3 -2
  36. package/src/specs/DownloadManager.nitro.ts +4 -2
  37. package/src/specs/Equalizer.nitro.ts +5 -3
  38. package/src/specs/TrackPlayer.nitro.ts +18 -6
  39. package/src/types/EqualizerTypes.ts +17 -13
@@ -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
  }
@@ -46,22 +46,33 @@ class HybridTrackPlayer : HybridTrackPlayerSpec() {
46
46
  private val listenerIds = mutableListOf<Pair<String, Long>>()
47
47
 
48
48
  init {
49
- val context = NitroModules.applicationContext
50
- ?: throw IllegalStateException("React Context is not initialized")
49
+ val context =
50
+ NitroModules.applicationContext
51
+ ?: throw IllegalStateException("React Context is not initialized")
51
52
  core = TrackPlayerCore.getInstance(context)
52
53
  }
53
54
 
54
55
  // ── Playback ─────────────────────────────────────────────────────────────
55
56
 
56
57
  override fun play(): Promise<Unit> = Promise.async { core.play() }
58
+
57
59
  override fun pause(): Promise<Unit> = Promise.async { core.pause() }
60
+
58
61
  override fun seek(position: Double): Promise<Unit> = Promise.async { core.seek(position) }
62
+
59
63
  override fun skipToNext(): Promise<Unit> = Promise.async { core.skipToNext() }
64
+
60
65
  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) }
66
+
67
+ override fun playSong(
68
+ songId: String,
69
+ fromPlaylist: String?,
70
+ ): Promise<Unit> = Promise.async { core.playSong(songId, fromPlaylist) }
71
+
62
72
  override fun skipToIndex(index: Double): Promise<Boolean> = Promise.async { core.skipToIndex(index.toInt()) }
63
73
 
64
74
  override fun setRepeatMode(mode: RepeatMode): Promise<Unit> = Promise.async { core.setRepeatMode(mode) }
75
+
65
76
  override fun getRepeatMode(): RepeatMode = core.getRepeatMode()
66
77
 
67
78
  override fun setVolume(volume: Double): Promise<Unit> = Promise.async { core.setVolume(volume) }
@@ -71,10 +82,15 @@ class HybridTrackPlayer : HybridTrackPlayerSpec() {
71
82
  // ── Queue / state reads ───────────────────────────────────────────────────
72
83
 
73
84
  override fun getActualQueue(): Promise<Array<TrackItem>> = Promise.async { core.getActualQueue().toTypedArray() }
85
+
74
86
  override fun getState(): Promise<PlayerState> = Promise.async { core.getState() }
87
+
75
88
  override fun getTracksById(trackIds: Array<String>): Promise<Array<TrackItem>> = Promise.async { core.getTracksById(trackIds.toList()).toTypedArray() }
89
+
76
90
  override fun getTracksNeedingUrls(): Promise<Array<TrackItem>> = Promise.async { core.getTracksNeedingUrls().toTypedArray() }
91
+
77
92
  override fun getNextTracks(count: Double): Promise<Array<TrackItem>> = Promise.async { core.getNextTracks(count.toInt()).toTypedArray() }
93
+
78
94
  override fun getCurrentTrackIndex(): Promise<Double> = Promise.async { core.getCurrentTrackIndex().toDouble() }
79
95
 
80
96
  // ── URL updates ───────────────────────────────────────────────────────────
@@ -84,18 +100,30 @@ class HybridTrackPlayer : HybridTrackPlayerSpec() {
84
100
  // ── Temporary queue ───────────────────────────────────────────────────────
85
101
 
86
102
  override fun addToUpNext(trackId: String): Promise<Unit> = Promise.async { core.addToUpNext(trackId) }
103
+
87
104
  override fun playNext(trackId: String): Promise<Unit> = Promise.async { core.playNext(trackId) }
105
+
88
106
  override fun removeFromPlayNext(trackId: String): Promise<Boolean> = Promise.async { core.removeFromPlayNext(trackId) }
107
+
89
108
  override fun removeFromUpNext(trackId: String): Promise<Boolean> = Promise.async { core.removeFromUpNext(trackId) }
109
+
90
110
  override fun clearPlayNext(): Promise<Unit> = Promise.async { core.clearPlayNext() }
111
+
91
112
  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()) }
113
+
114
+ override fun reorderTemporaryTrack(
115
+ trackId: String,
116
+ newIndex: Double,
117
+ ): Promise<Boolean> = Promise.async { core.reorderTemporaryTrack(trackId, newIndex.toInt()) }
118
+
93
119
  override fun getPlayNextQueue(): Promise<Array<TrackItem>> = Promise.async { core.getPlayNextQueue().toTypedArray() }
120
+
94
121
  override fun getUpNextQueue(): Promise<Array<TrackItem>> = Promise.async { core.getUpNextQueue().toTypedArray() }
95
122
 
96
123
  // ── Playback speed ────────────────────────────────────────────────────────
97
124
 
98
125
  override fun setPlaybackSpeed(speed: Double): Promise<Unit> = Promise.async { core.setPlayBackSpeed(speed) }
126
+
99
127
  override fun getPlaybackSpeed(): Promise<Double> = Promise.async { core.getPlayBackSpeed() }
100
128
 
101
129
  // ── Android Auto ──────────────────────────────────────────────────────────
@@ -130,16 +158,18 @@ class HybridTrackPlayer : HybridTrackPlayerSpec() {
130
158
  }
131
159
 
132
160
  override fun onTracksNeedUpdate(callback: (tracks: Array<TrackItem>, lookahead: Double) -> Unit) {
133
- val id = core.addOnTracksNeedUpdateListener { tracks, lookahead ->
134
- callback(tracks.toTypedArray(), lookahead.toDouble())
135
- }
161
+ val id =
162
+ core.addOnTracksNeedUpdateListener { tracks, lookahead ->
163
+ callback(tracks.toTypedArray(), lookahead.toDouble())
164
+ }
136
165
  listenerIds += "onTracksNeedUpdate" to id
137
166
  }
138
167
 
139
168
  override fun onTemporaryQueueChange(callback: (playNextQueue: Array<TrackItem>, upNextQueue: Array<TrackItem>) -> Unit) {
140
- val id = core.addOnTemporaryQueueChangeListener { pn, un ->
141
- callback(pn.toTypedArray(), un.toTypedArray())
142
- }
169
+ val id =
170
+ core.addOnTemporaryQueueChangeListener { pn, un ->
171
+ callback(pn.toTypedArray(), un.toTypedArray())
172
+ }
143
173
  listenerIds += "onTemporaryQueueChange" to id
144
174
  }
145
175
 
@@ -1,7 +1,7 @@
1
1
  package com.margelo.nitro.nitroplayer.core
2
2
 
3
- import android.os.HandlerThread
4
3
  import android.content.Context
4
+ import android.os.HandlerThread
5
5
  import androidx.media3.common.AudioAttributes
6
6
  import androidx.media3.common.C
7
7
  import androidx.media3.common.MediaItem
@@ -9,63 +9,102 @@ import androidx.media3.common.Player
9
9
  import androidx.media3.exoplayer.DefaultLoadControl
10
10
  import androidx.media3.exoplayer.ExoPlayer
11
11
 
12
-
13
- class ExoPlayerCore(context: Context, playerThread: HandlerThread) {
14
-
12
+ class ExoPlayerCore(
13
+ context: Context,
14
+ playerThread: HandlerThread,
15
+ ) {
15
16
  /** The underlying ExoPlayer instance — accessible for MediaSessionManager wiring. */
16
17
  internal val player: ExoPlayer = build(context, playerThread)
17
18
 
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()
19
+ private fun build(
20
+ context: Context,
21
+ playerThread: HandlerThread,
22
+ ): ExoPlayer {
23
+ val loadControl =
24
+ DefaultLoadControl
25
+ .Builder()
26
+ .setBufferDurationsMs(
27
+ // minBufferMs
28
+ 30_000,
29
+ // maxBufferMs
30
+ 120_000,
31
+ // bufferForPlayback
32
+ 2_500,
33
+ // bufferForRebuffer
34
+ 5_000,
35
+ ).setBackBuffer(30_000, /* retainBackBufferFromKeyframe */ true)
36
+ .setTargetBufferBytes(C.LENGTH_UNSET)
37
+ .setPrioritizeTimeOverSizeThresholds(true)
38
+ .build()
30
39
 
31
- val audioAttrs = AudioAttributes.Builder()
32
- .setUsage(C.USAGE_MEDIA)
33
- .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
34
- .build()
40
+ val audioAttrs =
41
+ AudioAttributes
42
+ .Builder()
43
+ .setUsage(C.USAGE_MEDIA)
44
+ .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
45
+ .build()
35
46
 
36
- return ExoPlayer.Builder(context)
47
+ return ExoPlayer
48
+ .Builder(context)
37
49
  .setLooper(playerThread.looper)
38
50
  .setLoadControl(loadControl)
39
51
  .setAudioAttributes(audioAttrs, /* handleAudioFocus */ true)
40
52
  .setHandleAudioBecomingNoisy(true)
53
+ .setWakeMode(C.WAKE_MODE_NETWORK)
41
54
  .setPauseAtEndOfMediaItems(false)
42
55
  .build()
43
56
  }
44
57
 
45
58
  // ── Playback ───────────────────────────────────────────────────────────
46
59
  fun play() = player.play()
60
+
47
61
  fun pause() = player.pause()
62
+
48
63
  fun seekTo(positionMs: Long) = player.seekTo(positionMs)
64
+
49
65
  fun seekToNext() = player.seekToNextMediaItem()
66
+
50
67
  fun hasNextMediaItem(): Boolean = player.hasNextMediaItem()
51
- fun setRepeatMode(mode: Int) { player.repeatMode = mode }
52
- fun setVolume(volume: Float) { player.volume = volume }
68
+
69
+ fun setRepeatMode(mode: Int) {
70
+ player.repeatMode = mode
71
+ }
72
+
73
+ fun setVolume(volume: Float) {
74
+ player.volume = volume
75
+ }
76
+
53
77
  fun setPlaybackSpeed(speed: Float) = player.setPlaybackSpeed(speed)
78
+
54
79
  fun getPlaybackSpeed(): Float = player.playbackParameters.speed
55
80
 
56
81
  // ── Queue mutations ────────────────────────────────────────────────────
57
82
  fun prepare() = player.prepare()
83
+
58
84
  fun seekToDefaultPosition(windowIndex: Int) = player.seekToDefaultPosition(windowIndex)
85
+
59
86
  fun clearMediaItems() = player.clearMediaItems()
60
- fun setMediaItems(items: List<MediaItem>, resetPosition: Boolean = false) =
61
- player.setMediaItems(items, resetPosition)
87
+
88
+ fun setMediaItems(
89
+ items: List<MediaItem>,
90
+ resetPosition: Boolean = false,
91
+ ) = player.setMediaItems(items, resetPosition)
92
+
62
93
  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)
94
+
95
+ fun removeMediaItems(
96
+ fromIndex: Int,
97
+ toIndex: Int,
98
+ ) = player.removeMediaItems(fromIndex, toIndex)
99
+
100
+ fun replaceMediaItem(
101
+ index: Int,
102
+ item: MediaItem,
103
+ ) = player.replaceMediaItem(index, item)
66
104
 
67
105
  // ── Listener wiring ────────────────────────────────────────────────────
68
106
  fun addListener(listener: Player.Listener) = player.addListener(listener)
107
+
69
108
  fun removeListener(listener: Player.Listener) = player.removeListener(listener)
70
109
 
71
110
  // ── State reads ────────────────────────────────────────────────────────
@@ -73,7 +112,9 @@ class ExoPlayerCore(context: Context, playerThread: HandlerThread) {
73
112
  val isPlaying: Boolean get() = player.isPlaying
74
113
  var playWhenReady: Boolean
75
114
  get() = player.playWhenReady
76
- set(value) { player.playWhenReady = value }
115
+ set(value) {
116
+ player.playWhenReady = value
117
+ }
77
118
  val currentMediaItem: MediaItem? get() = player.currentMediaItem
78
119
  val currentMediaItemIndex: Int get() = player.currentMediaItemIndex
79
120
  val currentPosition: Long get() = player.currentPosition
@@ -8,7 +8,10 @@ import java.util.concurrent.atomic.AtomicLong
8
8
  * Uses CopyOnWriteArrayList for lock-free iteration and AtomicLong for ID generation.
9
9
  */
10
10
  class ListenerRegistry<T> {
11
- private data class Entry<T>(val id: Long, val callback: T)
11
+ private data class Entry<T>(
12
+ val id: Long,
13
+ val callback: T,
14
+ )
12
15
 
13
16
  private val entries = CopyOnWriteArrayList<Entry<T>>()
14
17
  private val nextId = AtomicLong(0)
@@ -13,50 +13,56 @@ import com.margelo.nitro.nitroplayer.media.NitroPlayerMediaBrowserService
13
13
 
14
14
  /** Called on the main thread from TrackPlayerCore.init via handler.post. */
15
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)
16
+ androidAutoConnectionDetector =
17
+ AndroidAutoConnectionDetector(context).apply {
18
+ onConnectionChanged = { connected, _ ->
19
+ handler.post {
20
+ isAndroidAutoConnectedField = connected
21
+ NitroPlayerMediaBrowserService.isAndroidAutoConnected = connected
22
+ notifyAndroidAutoConnection(connected)
23
+ }
22
24
  }
25
+ registerCarConnectionReceiver()
23
26
  }
24
- registerCarConnectionReceiver()
25
- }
26
27
  }
27
28
 
28
29
  /** 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)
30
+ suspend fun TrackPlayerCore.playFromPlaylistTrack(mediaId: String) =
31
+ withPlayerContext {
32
+ try {
33
+ val colonIndex = mediaId.indexOf(':')
34
+ if (colonIndex <= 0 || colonIndex >= mediaId.length - 1) return@withPlayerContext
35
+ val playlistId = mediaId.substring(0, colonIndex)
36
+ val trackId = mediaId.substring(colonIndex + 1)
37
+ val playlist = playlistManager.getPlaylist(playlistId) ?: return@withPlayerContext
38
+ val trackIndex = playlist.tracks.indexOfFirst { it.id == trackId }
39
+ if (trackIndex < 0) return@withPlayerContext
40
+ if (currentPlaylistId != playlistId) {
41
+ loadPlaylistInternal(playlistId)
42
+ }
43
+ playFromIndexInternal(trackIndex)
44
+ } catch (_: Exception) {
40
45
  }
41
- playFromIndexInternal(trackIndex)
42
- } catch (_: Exception) {}
43
- }
46
+ }
44
47
 
45
48
  private fun TrackPlayerCore.loadPlaylistInternal(playlistId: String) {
46
- playNextStack.clear(); upNextQueue.clear()
49
+ playNextStack.clear()
50
+ upNextQueue.clear()
47
51
  currentTemporaryType = TrackPlayerCore.TemporaryType.NONE
48
52
  val playlist = playlistManager.getPlaylist(playlistId) ?: return
49
53
  currentPlaylistId = playlistId
50
54
  updatePlayerQueue(playlist.tracks)
51
55
  }
52
56
 
53
- suspend fun TrackPlayerCore.setAndroidAutoMediaLibrary(libraryJson: String) = withPlayerContext {
54
- val library = MediaLibraryParser.fromJson(libraryJson)
55
- mediaLibraryManager.setMediaLibrary(library)
56
- NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
57
- }
57
+ suspend fun TrackPlayerCore.setAndroidAutoMediaLibrary(libraryJson: String) =
58
+ withPlayerContext {
59
+ val library = MediaLibraryParser.fromJson(libraryJson)
60
+ mediaLibraryManager.setMediaLibrary(library)
61
+ NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
62
+ }
58
63
 
59
- suspend fun TrackPlayerCore.clearAndroidAutoMediaLibrary() = withPlayerContext {
60
- mediaLibraryManager.clear()
61
- NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
62
- }
64
+ suspend fun TrackPlayerCore.clearAndroidAutoMediaLibrary() =
65
+ withPlayerContext {
66
+ mediaLibraryManager.clear()
67
+ NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
68
+ }