react-native-nitro-player 0.5.3 → 0.5.5
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/HybridAudioDevices.kt +5 -3
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +4 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/connection/AndroidAutoConnectionDetector.kt +14 -13
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/NitroPlayerLogger.kt +31 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +142 -95
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +75 -29
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadManagerCore.kt +2 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadWorker.kt +1 -2
- package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +3 -2
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaBrowserService.kt +25 -24
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibraryManager.kt +3 -2
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +20 -19
- package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +119 -85
- package/android/src/main/java/com/margelo/nitro/nitroplayer/storage/NitroPlayerStorage.kt +50 -0
- package/ios/HybridAudioRoutePicker.swift +1 -1
- package/ios/HybridDownloadManager.swift +3 -3
- package/ios/HybridEqualizer.swift +3 -3
- package/ios/HybridTrackPlayer.swift +8 -4
- package/ios/core/NitroPlayerLogger.swift +22 -0
- package/ios/core/TrackPlayerCore.swift +195 -256
- package/ios/download/DownloadDatabase.swift +92 -62
- package/ios/download/DownloadFileManager.swift +17 -17
- package/ios/download/DownloadManagerCore.swift +80 -44
- package/ios/equalizer/EqualizerCore.swift +25 -20
- package/ios/playlist/PlaylistManager.swift +113 -82
- package/ios/queue/QueueManager.swift +1 -1
- package/ios/storage/NitroPlayerStorage.swift +44 -0
- package/lib/specs/TrackPlayer.nitro.d.ts +1 -0
- package/lib/types/PlayerQueue.d.ts +1 -1
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +5 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +1 -0
- package/nitrogen/generated/android/c++/JReason.hpp +3 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridTrackPlayerSpec.kt +4 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Reason.kt +2 -1
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +12 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +8 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +12 -0
- package/nitrogen/generated/ios/swift/Reason.swift +4 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +1 -0
- package/nitrogen/generated/shared/c++/Reason.hpp +4 -0
- package/package.json +1 -1
- package/src/specs/TrackPlayer.nitro.ts +1 -0
- package/src/types/PlayerQueue.ts +1 -1
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
package com.margelo.nitro.nitroplayer.download
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import android.content.SharedPreferences
|
|
5
|
-
import android.util.Log
|
|
6
4
|
import com.margelo.nitro.core.NullType
|
|
7
5
|
import com.margelo.nitro.nitroplayer.*
|
|
6
|
+
import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
|
|
8
7
|
import com.margelo.nitro.nitroplayer.playlist.PlaylistManager
|
|
8
|
+
import com.margelo.nitro.nitroplayer.storage.NitroPlayerStorage
|
|
9
9
|
import org.json.JSONArray
|
|
10
10
|
import org.json.JSONObject
|
|
11
11
|
import java.io.File
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Manages persistence of downloaded track metadata using
|
|
14
|
+
* Manages persistence of downloaded track metadata using file storage
|
|
15
15
|
*/
|
|
16
16
|
class DownloadDatabase private constructor(
|
|
17
17
|
private val context: Context,
|
|
18
18
|
) {
|
|
19
19
|
companion object {
|
|
20
20
|
private const val TAG = "DownloadDatabase"
|
|
21
|
-
|
|
22
|
-
private const val
|
|
23
|
-
private const val
|
|
21
|
+
// Legacy SharedPreferences keys (migration only)
|
|
22
|
+
private const val LEGACY_PREFS_NAME = "NitroPlayerDownloads"
|
|
23
|
+
private const val LEGACY_KEY_DOWNLOADED_TRACKS = "downloaded_tracks"
|
|
24
|
+
private const val LEGACY_KEY_PLAYLIST_TRACKS = "playlist_tracks"
|
|
24
25
|
|
|
25
26
|
@Volatile
|
|
26
27
|
private var instance: DownloadDatabase? = null
|
|
@@ -31,7 +32,6 @@ class DownloadDatabase private constructor(
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
35
35
|
private val downloadedTracks = mutableMapOf<String, DownloadedTrackRecord>()
|
|
36
36
|
private val playlistTracks = mutableMapOf<String, MutableSet<String>>()
|
|
37
37
|
private val fileManager = DownloadFileManager.getInstance(context)
|
|
@@ -253,13 +253,13 @@ class DownloadDatabase private constructor(
|
|
|
253
253
|
/** Validates all downloads and removes records for missing files */
|
|
254
254
|
fun syncDownloads(): Int {
|
|
255
255
|
synchronized(this) {
|
|
256
|
-
|
|
256
|
+
NitroPlayerLogger.log(TAG, "syncDownloads called")
|
|
257
257
|
|
|
258
258
|
val trackIdsToRemove = mutableListOf<String>()
|
|
259
259
|
|
|
260
260
|
for ((trackId, record) in downloadedTracks) {
|
|
261
261
|
if (!File(record.localPath).exists()) {
|
|
262
|
-
|
|
262
|
+
NitroPlayerLogger.log(TAG, "Missing file for track $trackId: ${record.localPath}")
|
|
263
263
|
trackIdsToRemove.add(trackId)
|
|
264
264
|
}
|
|
265
265
|
}
|
|
@@ -282,9 +282,9 @@ class DownloadDatabase private constructor(
|
|
|
282
282
|
|
|
283
283
|
if (trackIdsToRemove.isNotEmpty()) {
|
|
284
284
|
saveToDisk()
|
|
285
|
-
|
|
285
|
+
NitroPlayerLogger.log(TAG, "Cleaned up ${trackIdsToRemove.size} orphaned records")
|
|
286
286
|
} else {
|
|
287
|
-
|
|
287
|
+
NitroPlayerLogger.log(TAG, "All downloads are valid")
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
return trackIdsToRemove.size
|
|
@@ -294,39 +294,80 @@ class DownloadDatabase private constructor(
|
|
|
294
294
|
// Persistence
|
|
295
295
|
private fun saveToDisk() {
|
|
296
296
|
try {
|
|
297
|
-
// Save downloaded tracks
|
|
298
297
|
val tracksJson = JSONObject()
|
|
299
298
|
for ((trackId, record) in downloadedTracks) {
|
|
300
299
|
tracksJson.put(trackId, record.toJson())
|
|
301
300
|
}
|
|
302
|
-
prefs.edit().putString(KEY_DOWNLOADED_TRACKS, tracksJson.toString()).apply()
|
|
303
301
|
|
|
304
|
-
// Save playlist associations
|
|
305
302
|
val playlistJson = JSONObject()
|
|
306
303
|
for ((playlistId, trackIds) in playlistTracks) {
|
|
307
304
|
playlistJson.put(playlistId, JSONArray(trackIds.toList()))
|
|
308
305
|
}
|
|
309
|
-
|
|
306
|
+
|
|
307
|
+
val wrapper = JSONObject().apply {
|
|
308
|
+
put("downloadedTracks", tracksJson)
|
|
309
|
+
put("playlistTracks", playlistJson)
|
|
310
|
+
}
|
|
311
|
+
NitroPlayerStorage.write(context, "downloads.json", wrapper.toString())
|
|
310
312
|
} catch (e: Exception) {
|
|
311
313
|
e.printStackTrace()
|
|
312
314
|
}
|
|
313
315
|
}
|
|
314
316
|
|
|
315
317
|
private fun loadFromDisk() {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
318
|
+
// 1. Try new JSON file (post-migration)
|
|
319
|
+
val json = NitroPlayerStorage.read(context, "downloads.json")
|
|
320
|
+
if (json != null) {
|
|
321
|
+
try {
|
|
322
|
+
val wrapper = JSONObject(json)
|
|
323
|
+
|
|
324
|
+
val tracksJson = wrapper.optJSONObject("downloadedTracks")
|
|
325
|
+
if (tracksJson != null) {
|
|
326
|
+
for (trackId in tracksJson.keys()) {
|
|
327
|
+
val record = DownloadedTrackRecord.fromJson(tracksJson.getJSONObject(trackId))
|
|
328
|
+
downloadedTracks[trackId] = record
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
val playlistJson = wrapper.optJSONObject("playlistTracks")
|
|
333
|
+
if (playlistJson != null) {
|
|
334
|
+
for (playlistId in playlistJson.keys()) {
|
|
335
|
+
val trackIdsArray = playlistJson.getJSONArray(playlistId)
|
|
336
|
+
val trackIds = mutableSetOf<String>()
|
|
337
|
+
for (i in 0 until trackIdsArray.length()) {
|
|
338
|
+
trackIds.add(trackIdsArray.getString(i))
|
|
339
|
+
}
|
|
340
|
+
playlistTracks[playlistId] = trackIds
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
} catch (e: Exception) {
|
|
344
|
+
e.printStackTrace()
|
|
345
|
+
}
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 2. Migrate from SharedPreferences (one-time, existing installs)
|
|
350
|
+
val prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, Context.MODE_PRIVATE)
|
|
351
|
+
var didMigrate = false
|
|
352
|
+
|
|
353
|
+
val tracksString = prefs.getString(LEGACY_KEY_DOWNLOADED_TRACKS, null)
|
|
354
|
+
if (tracksString != null) {
|
|
355
|
+
try {
|
|
320
356
|
val tracksJson = JSONObject(tracksString)
|
|
321
357
|
for (trackId in tracksJson.keys()) {
|
|
322
358
|
val record = DownloadedTrackRecord.fromJson(tracksJson.getJSONObject(trackId))
|
|
323
359
|
downloadedTracks[trackId] = record
|
|
324
360
|
}
|
|
361
|
+
prefs.edit().remove(LEGACY_KEY_DOWNLOADED_TRACKS).apply()
|
|
362
|
+
didMigrate = true
|
|
363
|
+
} catch (e: Exception) {
|
|
364
|
+
e.printStackTrace()
|
|
325
365
|
}
|
|
366
|
+
}
|
|
326
367
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
368
|
+
val playlistString = prefs.getString(LEGACY_KEY_PLAYLIST_TRACKS, null)
|
|
369
|
+
if (playlistString != null) {
|
|
370
|
+
try {
|
|
330
371
|
val playlistJson = JSONObject(playlistString)
|
|
331
372
|
for (playlistId in playlistJson.keys()) {
|
|
332
373
|
val trackIdsArray = playlistJson.getJSONArray(playlistId)
|
|
@@ -336,9 +377,15 @@ class DownloadDatabase private constructor(
|
|
|
336
377
|
}
|
|
337
378
|
playlistTracks[playlistId] = trackIds
|
|
338
379
|
}
|
|
380
|
+
prefs.edit().remove(LEGACY_KEY_PLAYLIST_TRACKS).apply()
|
|
381
|
+
didMigrate = true
|
|
382
|
+
} catch (e: Exception) {
|
|
383
|
+
e.printStackTrace()
|
|
339
384
|
}
|
|
340
|
-
}
|
|
341
|
-
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (didMigrate) {
|
|
388
|
+
saveToDisk()
|
|
342
389
|
}
|
|
343
390
|
}
|
|
344
391
|
|
|
@@ -394,12 +441,11 @@ class DownloadDatabase private constructor(
|
|
|
394
441
|
}
|
|
395
442
|
|
|
396
443
|
private fun convertPlaylistManagerToNitro(playlist: com.margelo.nitro.nitroplayer.playlist.Playlist): Playlist {
|
|
397
|
-
// PlaylistManager already uses TrackItem from generated code with proper Variant types
|
|
398
444
|
return Playlist(
|
|
399
445
|
id = playlist.id,
|
|
400
446
|
name = playlist.name,
|
|
401
|
-
description = null,
|
|
402
|
-
artwork = null,
|
|
447
|
+
description = null,
|
|
448
|
+
artwork = null,
|
|
403
449
|
tracks = playlist.tracks.toTypedArray(),
|
|
404
450
|
)
|
|
405
451
|
}
|
|
@@ -432,7 +478,7 @@ internal data class DownloadedTrackRecord(
|
|
|
432
478
|
trackId = json.getString("trackId"),
|
|
433
479
|
originalTrack = TrackItemRecord.fromJson(json.getJSONObject("originalTrack")),
|
|
434
480
|
localPath = json.getString("localPath"),
|
|
435
|
-
localArtworkPath = json.
|
|
481
|
+
localArtworkPath = if (json.isNull("localArtworkPath")) null else json.getString("localArtworkPath"),
|
|
436
482
|
downloadedAt = json.getDouble("downloadedAt"),
|
|
437
483
|
fileSize = json.getDouble("fileSize"),
|
|
438
484
|
storageLocation = json.getString("storageLocation"),
|
|
@@ -469,7 +515,7 @@ internal data class TrackItemRecord(
|
|
|
469
515
|
album = json.getString("album"),
|
|
470
516
|
duration = json.getDouble("duration"),
|
|
471
517
|
url = json.getString("url"),
|
|
472
|
-
artwork = json.
|
|
518
|
+
artwork = if (json.isNull("artwork")) null else json.getString("artwork"),
|
|
473
519
|
)
|
|
474
520
|
}
|
|
475
521
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
+
NitroPlayerLogger.log("MediaBrowserService", "🎵 NitroPlayerMediaBrowserService: MediaSession token set successfully")
|
|
57
58
|
} else {
|
|
58
|
-
|
|
59
|
+
NitroPlayerLogger.log("MediaBrowserService", "⚠️ NitroPlayerMediaBrowserService: MediaSession not available yet")
|
|
59
60
|
}
|
|
60
61
|
} catch (e: Exception) {
|
|
61
|
-
|
|
62
|
+
NitroPlayerLogger.log("MediaBrowserService", "❌ NitroPlayerMediaBrowserService: Error setting session token - ${e.message}")
|
|
62
63
|
e.printStackTrace()
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
+
NitroPlayerLogger.log("MediaBrowserService", "📂 NitroPlayerMediaBrowserService: onGetRoot called from $clientPackageName")
|
|
81
82
|
|
|
82
83
|
// Check if Android Auto is enabled
|
|
83
84
|
if (!isAndroidAutoEnabled) {
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
106
|
+
NitroPlayerLogger.log("MediaBrowserService", "📂 NitroPlayerMediaBrowserService: onLoadChildren called for parentId: $parentId")
|
|
106
107
|
|
|
107
108
|
if (!isAndroidAutoEnabled) {
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
+
NitroPlayerLogger.log("MediaBrowserService", "✅ NitroPlayerMediaBrowserService: Returning ${mediaItems.size} root items")
|
|
137
138
|
result.sendResult(mediaItems)
|
|
138
139
|
} catch (e: Exception) {
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
192
|
+
NitroPlayerLogger.log("MediaBrowserService", "✅ NitroPlayerMediaBrowserService: Returning ${mediaItems.size} tracks from playlist '$playlistId'")
|
|
192
193
|
result.sendResult(mediaItems)
|
|
193
194
|
} catch (e: Exception) {
|
|
194
|
-
|
|
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
|
-
|
|
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
|
-
|
|
228
|
+
NitroPlayerLogger.log("MediaBrowserService", "✅ NitroPlayerMediaBrowserService: Returning ${mediaItems.size} items for parentId: $parentId")
|
|
228
229
|
result.sendResult(mediaItems)
|
|
229
230
|
} catch (e: Exception) {
|
|
230
|
-
|
|
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
|
-
|
|
243
|
+
NitroPlayerLogger.log("MediaBrowserService", "📢 NitroPlayerMediaBrowserService: Notified Android Auto of playlist update")
|
|
243
244
|
} catch (e: Exception) {
|
|
244
|
-
|
|
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
|
-
|
|
252
|
+
NitroPlayerLogger.log("MediaBrowserService", "📢 NitroPlayerMediaBrowserService: Notified Android Auto of playlist '$playlistId' update")
|
|
252
253
|
} catch (e: Exception) {
|
|
253
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
151
|
+
NitroPlayerLogger.log("MediaSessionManager", "✅ MediaSessionManager: Resolved track: ${track.title}")
|
|
151
152
|
} else {
|
|
152
|
-
|
|
153
|
+
NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Track $trackId not found in playlist")
|
|
153
154
|
updatedMediaItems.add(requestedMediaItem)
|
|
154
155
|
}
|
|
155
156
|
} else {
|
|
156
|
-
|
|
157
|
+
NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Playlist $playlistId not found")
|
|
157
158
|
updatedMediaItems.add(requestedMediaItem)
|
|
158
159
|
}
|
|
159
160
|
} else {
|
|
160
|
-
|
|
161
|
+
NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Invalid mediaId format: $mediaId")
|
|
161
162
|
updatedMediaItems.add(requestedMediaItem)
|
|
162
163
|
}
|
|
163
164
|
} catch (e: Exception) {
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
237
|
+
NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Track not found in playlist")
|
|
237
238
|
}
|
|
238
239
|
} else {
|
|
239
|
-
|
|
240
|
+
NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Playlist not found")
|
|
240
241
|
}
|
|
241
242
|
}
|
|
242
243
|
} catch (e: Exception) {
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
504
|
+
NitroPlayerLogger.log("MediaSessionManager", "⚠️ MediaSessionManager: Invalid artwork URI: $artworkUrl")
|
|
504
505
|
}
|
|
505
506
|
}
|
|
506
507
|
|