react-native-nitro-player 0.5.3 → 0.5.4

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 (43) hide show
  1. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +5 -3
  2. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +4 -0
  3. package/android/src/main/java/com/margelo/nitro/nitroplayer/connection/AndroidAutoConnectionDetector.kt +14 -13
  4. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/NitroPlayerLogger.kt +31 -0
  5. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +142 -95
  6. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +7 -6
  7. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadManagerCore.kt +2 -1
  8. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadWorker.kt +1 -2
  9. package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +3 -2
  10. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaBrowserService.kt +25 -24
  11. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibraryManager.kt +3 -2
  12. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +20 -19
  13. package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +16 -9
  14. package/ios/HybridAudioRoutePicker.swift +1 -1
  15. package/ios/HybridDownloadManager.swift +3 -3
  16. package/ios/HybridEqualizer.swift +3 -3
  17. package/ios/HybridTrackPlayer.swift +8 -4
  18. package/ios/core/NitroPlayerLogger.swift +22 -0
  19. package/ios/core/TrackPlayerCore.swift +195 -256
  20. package/ios/download/DownloadDatabase.swift +35 -39
  21. package/ios/download/DownloadFileManager.swift +17 -17
  22. package/ios/download/DownloadManagerCore.swift +29 -33
  23. package/ios/equalizer/EqualizerCore.swift +25 -20
  24. package/ios/playlist/PlaylistManager.swift +19 -9
  25. package/ios/queue/QueueManager.swift +1 -1
  26. package/lib/specs/TrackPlayer.nitro.d.ts +1 -0
  27. package/lib/types/PlayerQueue.d.ts +1 -1
  28. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +5 -0
  29. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +1 -0
  30. package/nitrogen/generated/android/c++/JReason.hpp +3 -0
  31. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridTrackPlayerSpec.kt +4 -0
  32. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Reason.kt +2 -1
  33. package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +12 -0
  34. package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +8 -0
  35. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +1 -0
  36. package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +12 -0
  37. package/nitrogen/generated/ios/swift/Reason.swift +4 -0
  38. package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +1 -0
  39. package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +1 -0
  40. package/nitrogen/generated/shared/c++/Reason.hpp +4 -0
  41. package/package.json +1 -1
  42. package/src/specs/TrackPlayer.nitro.ts +1 -0
  43. package/src/types/PlayerQueue.ts +1 -1
@@ -5,6 +5,7 @@ import android.content.SharedPreferences
5
5
  import android.util.Log
6
6
  import com.margelo.nitro.core.NullType
7
7
  import com.margelo.nitro.nitroplayer.*
8
+ import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
8
9
  import com.margelo.nitro.nitroplayer.playlist.PlaylistManager
9
10
  import org.json.JSONArray
10
11
  import org.json.JSONObject
@@ -253,13 +254,13 @@ class DownloadDatabase private constructor(
253
254
  /** Validates all downloads and removes records for missing files */
254
255
  fun syncDownloads(): Int {
255
256
  synchronized(this) {
256
- Log.d(TAG, "syncDownloads called")
257
+ NitroPlayerLogger.log("DownloadDatabase", "syncDownloads called")
257
258
 
258
259
  val trackIdsToRemove = mutableListOf<String>()
259
260
 
260
261
  for ((trackId, record) in downloadedTracks) {
261
262
  if (!File(record.localPath).exists()) {
262
- Log.d(TAG, "Missing file for track $trackId: ${record.localPath}")
263
+ NitroPlayerLogger.log("DownloadDatabase", "Missing file for track $trackId: ${record.localPath}")
263
264
  trackIdsToRemove.add(trackId)
264
265
  }
265
266
  }
@@ -282,9 +283,9 @@ class DownloadDatabase private constructor(
282
283
 
283
284
  if (trackIdsToRemove.isNotEmpty()) {
284
285
  saveToDisk()
285
- Log.d(TAG, "Cleaned up ${trackIdsToRemove.size} orphaned records")
286
+ NitroPlayerLogger.log("DownloadDatabase", "Cleaned up ${trackIdsToRemove.size} orphaned records")
286
287
  } else {
287
- Log.d(TAG, "All downloads are valid")
288
+ NitroPlayerLogger.log("DownloadDatabase", "All downloads are valid")
288
289
  }
289
290
 
290
291
  return trackIdsToRemove.size
@@ -432,7 +433,7 @@ internal data class DownloadedTrackRecord(
432
433
  trackId = json.getString("trackId"),
433
434
  originalTrack = TrackItemRecord.fromJson(json.getJSONObject("originalTrack")),
434
435
  localPath = json.getString("localPath"),
435
- localArtworkPath = json.optString("localArtworkPath", null),
436
+ localArtworkPath = if (json.isNull("localArtworkPath")) null else json.getString("localArtworkPath"),
436
437
  downloadedAt = json.getDouble("downloadedAt"),
437
438
  fileSize = json.getDouble("fileSize"),
438
439
  storageLocation = json.getString("storageLocation"),
@@ -469,7 +470,7 @@ internal data class TrackItemRecord(
469
470
  album = json.getString("album"),
470
471
  duration = json.getDouble("duration"),
471
472
  url = json.getString("url"),
472
- artwork = json.optString("artwork", null),
473
+ artwork = if (json.isNull("artwork")) null else json.getString("artwork"),
473
474
  )
474
475
  }
475
476
  }
@@ -7,6 +7,7 @@ import android.util.Log
7
7
  import androidx.work.*
8
8
  import com.margelo.nitro.core.NullType
9
9
  import com.margelo.nitro.nitroplayer.*
10
+ import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
10
11
  import java.util.*
11
12
  import java.util.concurrent.ConcurrentHashMap
12
13
  import java.util.concurrent.CopyOnWriteArrayList
@@ -289,7 +290,7 @@ class DownloadManagerCore private constructor(
289
290
  fun syncDownloads(): Int {
290
291
  val removedCount = database.syncDownloads()
291
292
  val bytesFreed = fileManager.cleanupOrphanedFiles(database.getAllDownloadedTracks().map { it.trackId }.toSet())
292
- Log.d(TAG, "syncDownloads: removed $removedCount orphaned records, freed $bytesFreed bytes")
293
+ NitroPlayerLogger.log("DownloadManagerCore", "syncDownloads: removed $removedCount orphaned records, freed $bytesFreed bytes")
293
294
  return removedCount
294
295
  }
295
296
 
@@ -4,11 +4,11 @@ import android.app.NotificationChannel
4
4
  import android.app.NotificationManager
5
5
  import android.content.Context
6
6
  import android.os.Build
7
+ import android.webkit.MimeTypeMap
7
8
  import androidx.core.app.NotificationCompat
8
9
  import androidx.work.CoroutineWorker
9
10
  import androidx.work.ForegroundInfo
10
11
  import androidx.work.WorkerParameters
11
- import android.webkit.MimeTypeMap
12
12
  import com.margelo.nitro.nitroplayer.*
13
13
  import kotlinx.coroutines.Dispatchers
14
14
  import kotlinx.coroutines.withContext
@@ -149,7 +149,6 @@ class DownloadWorker(
149
149
 
150
150
  val finalExtension = if (extension.isNullOrEmpty()) "mp3" else extension
151
151
 
152
-
153
152
  // Create destination file
154
153
  val destinationFile = fileManager.createDownloadFile(trackId, storageLocation, finalExtension)
155
154
 
@@ -11,6 +11,7 @@ import com.margelo.nitro.nitroplayer.EqualizerState
11
11
  import com.margelo.nitro.nitroplayer.GainRange
12
12
  import com.margelo.nitro.nitroplayer.PresetType
13
13
  import com.margelo.nitro.nitroplayer.Variant_NullType_String
14
+ import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
14
15
  import org.json.JSONArray
15
16
  import org.json.JSONObject
16
17
  import java.lang.ref.WeakReference
@@ -99,7 +100,7 @@ class EqualizerCore private constructor(
99
100
  setupBandMapping()
100
101
  restoreSettings()
101
102
  } catch (e: Exception) {
102
- Log.e(TAG, "Failed to initialize equalizer: ${e.message}")
103
+ NitroPlayerLogger.log("EqualizerCore", "Failed to initialize equalizer: ${e.message}")
103
104
  }
104
105
  }
105
106
 
@@ -144,7 +145,7 @@ class EqualizerCore private constructor(
144
145
  saveEnabled(enabled)
145
146
  true
146
147
  } catch (e: Exception) {
147
- Log.e(TAG, "Failed to set enabled: ${e.message}")
148
+ NitroPlayerLogger.log("EqualizerCore", "Failed to set enabled: ${e.message}")
148
149
  false
149
150
  }
150
151
  }
@@ -10,6 +10,7 @@ import android.support.v4.media.MediaDescriptionCompat
10
10
  import androidx.media.MediaBrowserServiceCompat
11
11
  import androidx.media.utils.MediaConstants
12
12
  import com.margelo.nitro.nitroplayer.TrackItem
13
+ import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
13
14
  import com.margelo.nitro.nitroplayer.core.TrackPlayerCore
14
15
  import com.margelo.nitro.nitroplayer.playlist.Playlist
15
16
  import kotlinx.coroutines.CoroutineScope
@@ -53,23 +54,23 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
53
54
  sessionToken =
54
55
  android.support.v4.media.session.MediaSessionCompat.Token
55
56
  .fromToken(session.platformToken)
56
- println("🎵 NitroPlayerMediaBrowserService: MediaSession token set successfully")
57
+ NitroPlayerLogger.log("MediaBrowserService", "🎵 NitroPlayerMediaBrowserService: MediaSession token set successfully")
57
58
  } else {
58
- println("⚠️ NitroPlayerMediaBrowserService: MediaSession not available yet")
59
+ NitroPlayerLogger.log("MediaBrowserService", "⚠️ NitroPlayerMediaBrowserService: MediaSession not available yet")
59
60
  }
60
61
  } catch (e: Exception) {
61
- println("❌ NitroPlayerMediaBrowserService: Error setting session token - ${e.message}")
62
+ NitroPlayerLogger.log("MediaBrowserService", "❌ NitroPlayerMediaBrowserService: Error setting session token - ${e.message}")
62
63
  e.printStackTrace()
63
64
  }
64
65
 
65
- println("🚀 NitroPlayerMediaBrowserService: Service created")
66
+ NitroPlayerLogger.log("MediaBrowserService", "🚀 NitroPlayerMediaBrowserService: Service created")
66
67
  }
67
68
 
68
69
  override fun onDestroy() {
69
70
  super.onDestroy()
70
71
  instance = null
71
72
  serviceScope.cancel()
72
- println("🛑 NitroPlayerMediaBrowserService: Service destroyed")
73
+ NitroPlayerLogger.log("MediaBrowserService", "🛑 NitroPlayerMediaBrowserService: Service destroyed")
73
74
  }
74
75
 
75
76
  override fun onGetRoot(
@@ -77,11 +78,11 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
77
78
  clientUid: Int,
78
79
  rootHints: Bundle?,
79
80
  ): BrowserRoot? {
80
- println("📂 NitroPlayerMediaBrowserService: onGetRoot called from $clientPackageName")
81
+ NitroPlayerLogger.log("MediaBrowserService", "📂 NitroPlayerMediaBrowserService: onGetRoot called from $clientPackageName")
81
82
 
82
83
  // Check if Android Auto is enabled
83
84
  if (!isAndroidAutoEnabled) {
84
- println("⚠️ NitroPlayerMediaBrowserService: Android Auto not enabled")
85
+ NitroPlayerLogger.log("MediaBrowserService", "⚠️ NitroPlayerMediaBrowserService: Android Auto not enabled")
85
86
  return BrowserRoot(EMPTY_ROOT_ID, null)
86
87
  }
87
88
 
@@ -94,7 +95,7 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
94
95
  MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM,
95
96
  )
96
97
  }
97
- println("✅ NitroPlayerMediaBrowserService: Allowing connection from $clientPackageName with grid layout")
98
+ NitroPlayerLogger.log("MediaBrowserService", "✅ NitroPlayerMediaBrowserService: Allowing connection from $clientPackageName with grid layout")
98
99
  return BrowserRoot(ROOT_ID, extras)
99
100
  }
100
101
 
@@ -102,10 +103,10 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
102
103
  parentId: String,
103
104
  result: Result<MutableList<MediaBrowserCompat.MediaItem>>,
104
105
  ) {
105
- println("📂 NitroPlayerMediaBrowserService: onLoadChildren called for parentId: $parentId")
106
+ NitroPlayerLogger.log("MediaBrowserService", "📂 NitroPlayerMediaBrowserService: onLoadChildren called for parentId: $parentId")
106
107
 
107
108
  if (!isAndroidAutoEnabled) {
108
- println("⚠️ NitroPlayerMediaBrowserService: Android Auto not enabled, returning empty")
109
+ NitroPlayerLogger.log("MediaBrowserService", "⚠️ NitroPlayerMediaBrowserService: Android Auto not enabled, returning empty")
109
110
  result.sendResult(mutableListOf())
110
111
  return
111
112
  }
@@ -121,7 +122,7 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
121
122
 
122
123
  if (library == null) {
123
124
  // Fallback: show playlists if no media library is set
124
- println("⚠️ NitroPlayerMediaBrowserService: No media library set, using fallback playlists")
125
+ NitroPlayerLogger.log("MediaBrowserService", "⚠️ NitroPlayerMediaBrowserService: No media library set, using fallback playlists")
125
126
  val mediaItems = loadFallbackPlaylists()
126
127
  result.sendResult(mediaItems)
127
128
  return@launch
@@ -133,10 +134,10 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
133
134
  mediaItems.add(convertToMediaBrowserItem(item, library.layoutType))
134
135
  }
135
136
 
136
- println("✅ NitroPlayerMediaBrowserService: Returning ${mediaItems.size} root items")
137
+ NitroPlayerLogger.log("MediaBrowserService", "✅ NitroPlayerMediaBrowserService: Returning ${mediaItems.size} root items")
137
138
  result.sendResult(mediaItems)
138
139
  } catch (e: Exception) {
139
- println("❌ NitroPlayerMediaBrowserService: Error loading root items - ${e.message}")
140
+ NitroPlayerLogger.log("MediaBrowserService", "❌ NitroPlayerMediaBrowserService: Error loading root items - ${e.message}")
140
141
  e.printStackTrace()
141
142
  result.sendResult(mutableListOf())
142
143
  }
@@ -154,7 +155,7 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
154
155
  val playlist = trackPlayerCore?.getPlaylistManager()?.getPlaylist(playlistId)
155
156
 
156
157
  if (playlist == null) {
157
- println("⚠️ NitroPlayerMediaBrowserService: Playlist '$playlistId' not found")
158
+ NitroPlayerLogger.log("MediaBrowserService", "⚠️ NitroPlayerMediaBrowserService: Playlist '$playlistId' not found")
158
159
  result.sendResult(mutableListOf())
159
160
  return@launch
160
161
  }
@@ -188,10 +189,10 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
188
189
  )
189
190
  }
190
191
 
191
- println("✅ NitroPlayerMediaBrowserService: Returning ${mediaItems.size} tracks from playlist '$playlistId'")
192
+ NitroPlayerLogger.log("MediaBrowserService", "✅ NitroPlayerMediaBrowserService: Returning ${mediaItems.size} tracks from playlist '$playlistId'")
192
193
  result.sendResult(mediaItems)
193
194
  } catch (e: Exception) {
194
- println("❌ NitroPlayerMediaBrowserService: Error loading playlist tracks - ${e.message}")
195
+ NitroPlayerLogger.log("MediaBrowserService", "❌ NitroPlayerMediaBrowserService: Error loading playlist tracks - ${e.message}")
195
196
  e.printStackTrace()
196
197
  result.sendResult(mutableListOf())
197
198
  }
@@ -211,7 +212,7 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
211
212
  val children = mediaLibraryManager.getChildrenById(parentId)
212
213
 
213
214
  if (children == null) {
214
- println("⚠️ NitroPlayerMediaBrowserService: No children found for parentId: $parentId")
215
+ NitroPlayerLogger.log("MediaBrowserService", "⚠️ NitroPlayerMediaBrowserService: No children found for parentId: $parentId")
215
216
  result.sendResult(mutableListOf())
216
217
  return@launch
217
218
  }
@@ -224,10 +225,10 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
224
225
  convertToMediaBrowserItem(item, defaultLayout)
225
226
  }.toMutableList()
226
227
 
227
- println("✅ NitroPlayerMediaBrowserService: Returning ${mediaItems.size} items for parentId: $parentId")
228
+ NitroPlayerLogger.log("MediaBrowserService", "✅ NitroPlayerMediaBrowserService: Returning ${mediaItems.size} items for parentId: $parentId")
228
229
  result.sendResult(mediaItems)
229
230
  } catch (e: Exception) {
230
- println("❌ NitroPlayerMediaBrowserService: Error loading children - ${e.message}")
231
+ NitroPlayerLogger.log("MediaBrowserService", "❌ NitroPlayerMediaBrowserService: Error loading children - ${e.message}")
231
232
  e.printStackTrace()
232
233
  result.sendResult(mutableListOf())
233
234
  }
@@ -239,18 +240,18 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
239
240
  fun onPlaylistsUpdated() {
240
241
  try {
241
242
  notifyChildrenChanged(ROOT_ID)
242
- println("📢 NitroPlayerMediaBrowserService: Notified Android Auto of playlist update")
243
+ NitroPlayerLogger.log("MediaBrowserService", "📢 NitroPlayerMediaBrowserService: Notified Android Auto of playlist update")
243
244
  } catch (e: Exception) {
244
- println("⚠️ NitroPlayerMediaBrowserService: Error notifying children changed: ${e.message}")
245
+ NitroPlayerLogger.log("MediaBrowserService", "⚠️ NitroPlayerMediaBrowserService: Error notifying children changed: ${e.message}")
245
246
  }
246
247
  }
247
248
 
248
249
  fun onPlaylistUpdated(playlistId: String) {
249
250
  try {
250
251
  notifyChildrenChanged("$PLAYLIST_PREFIX$playlistId")
251
- println("📢 NitroPlayerMediaBrowserService: Notified Android Auto of playlist '$playlistId' update")
252
+ NitroPlayerLogger.log("MediaBrowserService", "📢 NitroPlayerMediaBrowserService: Notified Android Auto of playlist '$playlistId' update")
252
253
  } catch (e: Exception) {
253
- println("⚠️ NitroPlayerMediaBrowserService: Error notifying playlist changed: ${e.message}")
254
+ NitroPlayerLogger.log("MediaBrowserService", "⚠️ NitroPlayerMediaBrowserService: Error notifying playlist changed: ${e.message}")
254
255
  }
255
256
  }
256
257
 
@@ -348,7 +349,7 @@ class NitroPlayerMediaBrowserService : MediaBrowserServiceCompat() {
348
349
  )
349
350
  }
350
351
 
351
- println("✅ NitroPlayerMediaBrowserService: Loaded ${mediaItems.size} playlists as fallback")
352
+ NitroPlayerLogger.log("MediaBrowserService", "✅ NitroPlayerMediaBrowserService: Loaded ${mediaItems.size} playlists as fallback")
352
353
  return mediaItems
353
354
  }
354
355
  }
@@ -1,6 +1,7 @@
1
1
  package com.margelo.nitro.nitroplayer.media
2
2
 
3
3
  import android.content.Context
4
+ import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
4
5
 
5
6
  /**
6
7
  * Manages the Android Auto media library structure
@@ -27,7 +28,7 @@ class MediaLibraryManager private constructor(
27
28
  */
28
29
  fun setMediaLibrary(library: MediaLibrary) {
29
30
  mediaLibrary = library
30
- println("📚 MediaLibraryManager: Media library set with ${library.rootItems.size} root items")
31
+ NitroPlayerLogger.log("MediaLibraryManager", "📚 MediaLibraryManager: Media library set with ${library.rootItems.size} root items")
31
32
  }
32
33
 
33
34
  /**
@@ -72,6 +73,6 @@ class MediaLibraryManager private constructor(
72
73
  */
73
74
  fun clear() {
74
75
  mediaLibrary = null
75
- println("📚 MediaLibraryManager: Media library cleared")
76
+ NitroPlayerLogger.log("MediaLibraryManager", "📚 MediaLibraryManager: Media library cleared")
76
77
  }
77
78
  }
@@ -23,6 +23,7 @@ import androidx.media3.session.SessionResult
23
23
  import com.google.common.util.concurrent.Futures
24
24
  import com.google.common.util.concurrent.ListenableFuture
25
25
  import com.margelo.nitro.nitroplayer.TrackItem
26
+ import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
26
27
  import com.margelo.nitro.nitroplayer.core.TrackPlayerCore
27
28
  import com.margelo.nitro.nitroplayer.media.NitroPlayerMediaBrowserService
28
29
  import com.margelo.nitro.nitroplayer.playlist.PlaylistManager
@@ -114,7 +115,7 @@ class MediaSessionManager(
114
115
  mediaItems: MutableList<MediaItem>,
115
116
  ): ListenableFuture<MutableList<MediaItem>> {
116
117
  // This is called when Android Auto requests to play a track
117
- println("🎵 MediaSessionManager: onAddMediaItems called with ${mediaItems.size} items")
118
+ NitroPlayerLogger.log("MediaSessionManager", "🎵 MediaSessionManager: onAddMediaItems called with ${mediaItems.size} items")
118
119
 
119
120
  if (mediaItems.isEmpty()) {
120
121
  return Futures.immediateFuture(mutableListOf())
@@ -128,7 +129,7 @@ class MediaSessionManager(
128
129
  requestedMediaItem.requestMetadata.mediaUri?.toString()
129
130
  ?: requestedMediaItem.mediaId
130
131
 
131
- println("🎵 MediaSessionManager: Processing mediaId: $mediaId")
132
+ NitroPlayerLogger.log("MediaSessionManager", "🎵 MediaSessionManager: Processing mediaId: $mediaId")
132
133
 
133
134
  try {
134
135
  // Parse mediaId format: "playlistId:trackId"
@@ -137,7 +138,7 @@ class MediaSessionManager(
137
138
  val playlistId = mediaId.substring(0, colonIndex)
138
139
  val trackId = mediaId.substring(colonIndex + 1)
139
140
 
140
- println("🎵 MediaSessionManager: Parsed playlistId: $playlistId, trackId: $trackId")
141
+ NitroPlayerLogger.log("MediaSessionManager", "🎵 MediaSessionManager: Parsed playlistId: $playlistId, trackId: $trackId")
141
142
 
142
143
  // Get the playlist and track
143
144
  val playlist = playlistManager.getPlaylist(playlistId)
@@ -147,27 +148,27 @@ class MediaSessionManager(
147
148
  // Create a proper MediaItem with all metadata
148
149
  val resolvedMediaItem = createMediaItem(track, mediaId)
149
150
  updatedMediaItems.add(resolvedMediaItem)
150
- println("✅ MediaSessionManager: Resolved track: ${track.title}")
151
+ NitroPlayerLogger.log("MediaSessionManager", "✅ MediaSessionManager: Resolved track: ${track.title}")
151
152
  } else {
152
- println("⚠️ MediaSessionManager: Track $trackId not found in playlist")
153
+ NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Track $trackId not found in playlist")
153
154
  updatedMediaItems.add(requestedMediaItem)
154
155
  }
155
156
  } else {
156
- println("⚠️ MediaSessionManager: Playlist $playlistId not found")
157
+ NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Playlist $playlistId not found")
157
158
  updatedMediaItems.add(requestedMediaItem)
158
159
  }
159
160
  } else {
160
- println("⚠️ MediaSessionManager: Invalid mediaId format: $mediaId")
161
+ NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Invalid mediaId format: $mediaId")
161
162
  updatedMediaItems.add(requestedMediaItem)
162
163
  }
163
164
  } catch (e: Exception) {
164
- println("❌ MediaSessionManager: Error processing mediaId - ${e.message}")
165
+ NitroPlayerLogger.log("MediaSessionManager", "❌ MediaSessionManager: Error processing mediaId - ${e.message}")
165
166
  e.printStackTrace()
166
167
  updatedMediaItems.add(requestedMediaItem)
167
168
  }
168
169
  }
169
170
 
170
- println("🎵 MediaSessionManager: Returning ${updatedMediaItems.size} resolved media items")
171
+ NitroPlayerLogger.log("MediaSessionManager", "🎵 MediaSessionManager: Returning ${updatedMediaItems.size} resolved media items")
171
172
  return Futures.immediateFuture(updatedMediaItems)
172
173
  }
173
174
 
@@ -179,7 +180,7 @@ class MediaSessionManager(
179
180
  startPositionMs: Long,
180
181
  ): ListenableFuture<MediaSession.MediaItemsWithStartPosition> {
181
182
  // This is called when Android Auto wants to set and play media items
182
- println("🎵 MediaSessionManager: onSetMediaItems called with ${mediaItems.size} items, startIndex: $startIndex")
183
+ NitroPlayerLogger.log("MediaSessionManager", "🎵 MediaSessionManager: onSetMediaItems called with ${mediaItems.size} items, startIndex: $startIndex")
183
184
 
184
185
  if (mediaItems.isEmpty()) {
185
186
  return Futures.immediateFuture(
@@ -194,7 +195,7 @@ class MediaSessionManager(
194
195
  try {
195
196
  // Get the first item's mediaId to determine the playlist
196
197
  val firstMediaId = mediaItems[0].mediaId
197
- println("🎵 MediaSessionManager: First mediaId: $firstMediaId")
198
+ NitroPlayerLogger.log("MediaSessionManager", "🎵 MediaSessionManager: First mediaId: $firstMediaId")
198
199
 
199
200
  // Parse mediaId format: "playlistId:trackId"
200
201
  if (firstMediaId.contains(':')) {
@@ -202,7 +203,7 @@ class MediaSessionManager(
202
203
  val playlistId = firstMediaId.substring(0, colonIndex)
203
204
  val trackId = firstMediaId.substring(colonIndex + 1)
204
205
 
205
- println("🎵 MediaSessionManager: Loading full playlist: $playlistId, starting at track: $trackId")
206
+ NitroPlayerLogger.log("MediaSessionManager", "🎵 MediaSessionManager: Loading full playlist: $playlistId, starting at track: $trackId")
206
207
 
207
208
  // Get the full playlist
208
209
  val playlist = playlistManager.getPlaylist(playlistId)
@@ -222,7 +223,7 @@ class MediaSessionManager(
222
223
  createMediaItem(track, trackMediaId)
223
224
  }.toMutableList()
224
225
 
225
- println("✅ MediaSessionManager: Loaded ${playlistMediaItems.size} tracks, starting at index $trackIndex")
226
+ NitroPlayerLogger.log("MediaSessionManager", "✅ MediaSessionManager: Loaded ${playlistMediaItems.size} tracks, starting at index $trackIndex")
226
227
 
227
228
  // Return the full playlist with the correct start index
228
229
  return Futures.immediateFuture(
@@ -233,19 +234,19 @@ class MediaSessionManager(
233
234
  ),
234
235
  )
235
236
  } else {
236
- println("⚠️ MediaSessionManager: Track not found in playlist")
237
+ NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Track not found in playlist")
237
238
  }
238
239
  } else {
239
- println("⚠️ MediaSessionManager: Playlist not found")
240
+ NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Playlist not found")
240
241
  }
241
242
  }
242
243
  } catch (e: Exception) {
243
- println("❌ MediaSessionManager: Error in onSetMediaItems - ${e.message}")
244
+ NitroPlayerLogger.log("MediaSessionManager", "❌ MediaSessionManager: Error in onSetMediaItems - ${e.message}")
244
245
  e.printStackTrace()
245
246
  }
246
247
 
247
248
  // Fallback: use the provided media items
248
- println("🎵 MediaSessionManager: Using fallback - provided media items")
249
+ NitroPlayerLogger.log("MediaSessionManager", "🎵 MediaSessionManager: Using fallback - provided media items")
249
250
  return Futures.immediateFuture(
250
251
  MediaSession.MediaItemsWithStartPosition(
251
252
  mediaItems,
@@ -391,7 +392,7 @@ class MediaSessionManager(
391
392
  .setShowActionsInCompactView(0, 1, 2),
392
393
  )
393
394
  } catch (e: Exception) {
394
- Log.w("MediaSessionManager", "Failed to set media session token: ${e.message}")
395
+ NitroPlayerLogger.log("MediaSessionManager", "Failed to set media session token: ${e.message}")
395
396
  }
396
397
 
397
398
  // Add action buttons
@@ -500,7 +501,7 @@ class MediaSessionManager(
500
501
  try {
501
502
  metadataBuilder.setArtworkUri(Uri.parse(artworkUrl))
502
503
  } catch (e: Exception) {
503
- println("⚠️ MediaSessionManager: Invalid artwork URI: $artworkUrl")
504
+ NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Invalid artwork URI: $artworkUrl")
504
505
  }
505
506
  }
506
507
 
@@ -2,6 +2,7 @@ package com.margelo.nitro.nitroplayer.playlist
2
2
 
3
3
  import android.content.Context
4
4
  import android.content.SharedPreferences
5
+ import com.margelo.nitro.core.AnyMap
5
6
  import com.margelo.nitro.core.NullType
6
7
  import com.margelo.nitro.nitroplayer.QueueOperation
7
8
  import com.margelo.nitro.nitroplayer.TrackItem
@@ -13,8 +14,6 @@ import org.json.JSONObject
13
14
  import java.util.UUID
14
15
  import java.util.concurrent.CopyOnWriteArrayList
15
16
 
16
- import com.margelo.nitro.core.AnyMap
17
-
18
17
  /**
19
18
  * Manages multiple playlists using ExoPlayer's native playlist functionality
20
19
  * Based on: https://developer.android.com/media/media3/exoplayer/playlists
@@ -30,6 +29,14 @@ class PlaylistManager private constructor(
30
29
  private val sharedPreferences: SharedPreferences =
31
30
  context.getSharedPreferences("NitroPlayerPlaylists", Context.MODE_PRIVATE)
32
31
 
32
+ private val saveHandler = android.os.Handler(android.os.Looper.getMainLooper())
33
+ private val saveRunnable = Runnable { savePlaylistsToPreferences() }
34
+
35
+ private fun scheduleSave() {
36
+ saveHandler.removeCallbacks(saveRunnable)
37
+ saveHandler.postDelayed(saveRunnable, 300)
38
+ }
39
+
33
40
  companion object {
34
41
  @Volatile
35
42
  @Suppress("ktlint:standard:property-naming")
@@ -63,7 +70,7 @@ class PlaylistManager private constructor(
63
70
 
64
71
  // Only cache for Android Auto if connected
65
72
  if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
66
- savePlaylistsToPreferences()
73
+ scheduleSave()
67
74
  }
68
75
  notifyPlaylistsChanged(QueueOperation.ADD)
69
76
  NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
@@ -87,7 +94,7 @@ class PlaylistManager private constructor(
87
94
  playlistListeners.remove(playlistId)
88
95
  // Only cache for Android Auto if connected
89
96
  if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
90
- savePlaylistsToPreferences()
97
+ scheduleSave()
91
98
  }
92
99
  notifyPlaylistsChanged(QueueOperation.REMOVE)
93
100
  NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
@@ -122,7 +129,7 @@ class PlaylistManager private constructor(
122
129
 
123
130
  // Only cache for Android Auto if connected
124
131
  if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
125
- savePlaylistsToPreferences()
132
+ scheduleSave()
126
133
  }
127
134
  notifyPlaylistChanged(playlistId, QueueOperation.UPDATE)
128
135
  notifyPlaylistsChanged(QueueOperation.UPDATE)
@@ -172,7 +179,7 @@ class PlaylistManager private constructor(
172
179
 
173
180
  // Only cache for Android Auto if connected
174
181
  if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
175
- savePlaylistsToPreferences()
182
+ scheduleSave()
176
183
  }
177
184
  notifyPlaylistChanged(playlistId, QueueOperation.ADD)
178
185
  NitroPlayerMediaBrowserService.getInstance()?.onPlaylistUpdated(playlistId)
@@ -210,7 +217,7 @@ class PlaylistManager private constructor(
210
217
 
211
218
  // Only cache for Android Auto if connected
212
219
  if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
213
- savePlaylistsToPreferences()
220
+ scheduleSave()
214
221
  }
215
222
  notifyPlaylistChanged(playlistId, QueueOperation.ADD)
216
223
  NitroPlayerMediaBrowserService.getInstance()?.onPlaylistUpdated(playlistId)
@@ -246,7 +253,7 @@ class PlaylistManager private constructor(
246
253
  }
247
254
 
248
255
  if (removed) {
249
- savePlaylistsToPreferences()
256
+ scheduleSave()
250
257
  notifyPlaylistChanged(playlistId, QueueOperation.REMOVE)
251
258
  NitroPlayerMediaBrowserService.getInstance()?.onPlaylistUpdated(playlistId)
252
259
 
@@ -284,7 +291,7 @@ class PlaylistManager private constructor(
284
291
  playlists[playlistId] = playlist.copy(tracks = tracks)
285
292
  }
286
293
 
287
- savePlaylistsToPreferences()
294
+ scheduleSave()
288
295
  notifyPlaylistChanged(playlistId, QueueOperation.UPDATE)
289
296
  NitroPlayerMediaBrowserService.getInstance()?.onPlaylistUpdated(playlistId)
290
297
 
@@ -30,7 +30,7 @@ class HybridAudioRoutePicker: HybridAudioRoutePickerSpec {
30
30
  .flatMap({ $0.windows })
31
31
  .first(where: { $0.isKeyWindow })
32
32
  else {
33
- print("HybridAudioRoutePicker: Could not find key window")
33
+ NitroPlayerLogger.log("HybridAudioRoutePicker", "Could not find key window")
34
34
  return
35
35
  }
36
36
 
@@ -208,19 +208,19 @@ final class HybridDownloadManager: HybridDownloadManagerSpec {
208
208
  // MARK: - Event Callbacks
209
209
 
210
210
  func onDownloadProgress(callback: @escaping (DownloadProgress) -> Void) throws {
211
- print("🎯 HybridDownloadManager: onDownloadProgress callback registered")
211
+ NitroPlayerLogger.log("HybridDownloadManager", "onDownloadProgress callback registered")
212
212
  core.addProgressCallback(callback)
213
213
  }
214
214
 
215
215
  func onDownloadStateChange(
216
216
  callback: @escaping (String, String, DownloadState, DownloadError?) -> Void
217
217
  ) throws {
218
- print("🎯 HybridDownloadManager: onDownloadStateChange callback registered")
218
+ NitroPlayerLogger.log("HybridDownloadManager", "onDownloadStateChange callback registered")
219
219
  core.addStateChangeCallback(callback)
220
220
  }
221
221
 
222
222
  func onDownloadComplete(callback: @escaping (DownloadedTrack) -> Void) throws {
223
- print("🎯 HybridDownloadManager: onDownloadComplete callback registered")
223
+ NitroPlayerLogger.log("HybridDownloadManager", "onDownloadComplete callback registered")
224
224
  core.addCompleteCallback(callback)
225
225
  }
226
226
  }
@@ -95,17 +95,17 @@ final class HybridEqualizer: HybridEqualizerSpec {
95
95
  // MARK: - Event Callbacks
96
96
 
97
97
  func onEnabledChange(callback: @escaping (Bool) -> Void) throws {
98
- print("🎯 HybridEqualizer: onEnabledChange callback registered")
98
+ NitroPlayerLogger.log("HybridEqualizer", "onEnabledChange callback registered")
99
99
  core.addOnEnabledChangeListener(owner: self, callback)
100
100
  }
101
101
 
102
102
  func onBandChange(callback: @escaping ([EqualizerBand]) -> Void) throws {
103
- print("🎯 HybridEqualizer: onBandChange callback registered")
103
+ NitroPlayerLogger.log("HybridEqualizer", "onBandChange callback registered")
104
104
  core.addOnBandChangeListener(owner: self, callback)
105
105
  }
106
106
 
107
107
  func onPresetChange(callback: @escaping (Variant_NullType_String?) -> Void) throws {
108
- print("🎯 HybridEqualizer: onPresetChange callback registered")
108
+ NitroPlayerLogger.log("HybridEqualizer", "onPresetChange callback registered")
109
109
  core.addOnPresetChangeListener(owner: self, callback)
110
110
  }
111
111
  }