react-native-nitro-player 1.4.1 → 1.4.3
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/core/TrackPlayerCore.kt +1 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +2 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +17 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/AuthAwareHttpDataSourceFactory.kt +48 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/ExoPlayerBuilder.kt +10 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +44 -4
- package/ios/HybridTrackPlayer.swift +0 -1
- package/ios/core/TrackPlayerQueueBuild.swift +33 -6
- package/lib/types/PlayerQueue.d.ts +1 -0
- package/nitrogen/generated/android/c++/JPlayerConfig.hpp +8 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/PlayerConfig.kt +6 -3
- package/nitrogen/generated/ios/swift/PlayerConfig.swift +19 -1
- package/nitrogen/generated/shared/c++/PlayerConfig.hpp +7 -2
- package/package.json +1 -1
- package/src/types/PlayerQueue.ts +2 -0
|
@@ -244,6 +244,7 @@ class TrackPlayerCore private constructor(
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
scope.cancel()
|
|
247
|
+
com.margelo.nitro.nitroplayer.media.AuthAwareHttpDataSourceFactory.clear()
|
|
247
248
|
// Do NOT stop the service — it owns the player.
|
|
248
249
|
// Unbind so Android can clean up if needed.
|
|
249
250
|
if (serviceBound) {
|
|
@@ -8,6 +8,7 @@ import com.margelo.nitro.nitroplayer.Reason
|
|
|
8
8
|
import com.margelo.nitro.nitroplayer.RepeatMode
|
|
9
9
|
import com.margelo.nitro.nitroplayer.TrackPlayerState
|
|
10
10
|
import com.margelo.nitro.nitroplayer.media.NitroPlayerMediaBrowserService
|
|
11
|
+
import com.margelo.nitro.nitroplayer.media.NitroPlayerPlaybackService
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Playback control — all public functions are suspend and execute on the player thread
|
|
@@ -95,6 +96,7 @@ suspend fun TrackPlayerCore.configure(config: PlayerConfig) =
|
|
|
95
96
|
withPlayerContext {
|
|
96
97
|
config.androidAutoEnabled?.let { NitroPlayerMediaBrowserService.isAndroidAutoEnabled = it }
|
|
97
98
|
config.lookaheadCount?.let { lookaheadCount = it.toInt() }
|
|
99
|
+
config.androidNotificationIcon?.let { NitroPlayerPlaybackService.notificationSmallIconResName = it }
|
|
98
100
|
mediaSessionManager?.configure(config.androidAutoEnabled, config.carPlayEnabled, config.showInNotification)
|
|
99
101
|
}
|
|
100
102
|
|
|
@@ -150,6 +150,23 @@ internal fun TrackPlayerCore.makeMediaItem(
|
|
|
150
150
|
|
|
151
151
|
val effectiveUrl = downloadManager.getEffectiveUrl(track)
|
|
152
152
|
|
|
153
|
+
// Register custom HTTP headers (e.g. Authorization) from extraPayload.headers so they are
|
|
154
|
+
// injected into the request for this URL. Applies to remote streams only — see ExoPlayerBuilder.
|
|
155
|
+
track.extraPayload?.let { payload ->
|
|
156
|
+
try {
|
|
157
|
+
val headersRaw = payload.toHashMap()["headers"]
|
|
158
|
+
if (headersRaw is Map<*, *>) {
|
|
159
|
+
val headers = headersRaw
|
|
160
|
+
.mapNotNull { (k, v) -> if (k is String && v is String) k to v else null }
|
|
161
|
+
.toMap()
|
|
162
|
+
if (headers.isNotEmpty()) {
|
|
163
|
+
com.margelo.nitro.nitroplayer.media.AuthAwareHttpDataSourceFactory
|
|
164
|
+
.setHeadersForUrl(effectiveUrl, headers)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch (_: Exception) {}
|
|
168
|
+
}
|
|
169
|
+
|
|
153
170
|
return MediaItem
|
|
154
171
|
.Builder()
|
|
155
172
|
.setMediaId(customMediaId ?: track.id)
|
package/android/src/main/java/com/margelo/nitro/nitroplayer/media/AuthAwareHttpDataSourceFactory.kt
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package com.margelo.nitro.nitroplayer.media
|
|
2
|
+
|
|
3
|
+
import androidx.media3.common.util.UnstableApi
|
|
4
|
+
import androidx.media3.datasource.DataSource
|
|
5
|
+
import androidx.media3.datasource.DefaultHttpDataSource
|
|
6
|
+
import androidx.media3.datasource.ResolvingDataSource
|
|
7
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* HTTP [DataSource.Factory] that injects per-URL request headers (e.g. `Authorization`)
|
|
11
|
+
* into ExoPlayer's HTTP requests, enabling playback from authenticated endpoints.
|
|
12
|
+
*
|
|
13
|
+
* Headers are registered per effective URL via [setHeadersForUrl] during MediaItem
|
|
14
|
+
* construction and applied through a [ResolvingDataSource] just before each request opens.
|
|
15
|
+
*
|
|
16
|
+
* The registry is a [ConcurrentHashMap] because it is written on the player thread but
|
|
17
|
+
* read on ExoPlayer's loader threads.
|
|
18
|
+
*/
|
|
19
|
+
@UnstableApi
|
|
20
|
+
class AuthAwareHttpDataSourceFactory : DataSource.Factory {
|
|
21
|
+
private val factory =
|
|
22
|
+
ResolvingDataSource.Factory(
|
|
23
|
+
DefaultHttpDataSource
|
|
24
|
+
.Factory()
|
|
25
|
+
.setAllowCrossProtocolRedirects(true)
|
|
26
|
+
.setConnectTimeoutMs(15_000)
|
|
27
|
+
.setReadTimeoutMs(15_000),
|
|
28
|
+
ResolvingDataSource.Resolver { dataSpec ->
|
|
29
|
+
val headers = headersMap[dataSpec.uri.toString()]
|
|
30
|
+
if (headers.isNullOrEmpty()) dataSpec else dataSpec.withAdditionalHeaders(headers)
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
override fun createDataSource(): DataSource = factory.createDataSource()
|
|
35
|
+
|
|
36
|
+
companion object {
|
|
37
|
+
private val headersMap = ConcurrentHashMap<String, Map<String, String>>()
|
|
38
|
+
|
|
39
|
+
fun setHeadersForUrl(url: String, headers: Map<String, String>) {
|
|
40
|
+
headersMap[url] = headers
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Releases all registered headers. Call when the player is torn down. */
|
|
44
|
+
fun clear() {
|
|
45
|
+
headersMap.clear()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -3,14 +3,18 @@ package com.margelo.nitro.nitroplayer.media
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import androidx.media3.common.AudioAttributes
|
|
5
5
|
import androidx.media3.common.C
|
|
6
|
+
import androidx.media3.common.util.UnstableApi
|
|
7
|
+
import androidx.media3.datasource.DefaultDataSource
|
|
6
8
|
import androidx.media3.exoplayer.DefaultLoadControl
|
|
7
9
|
import androidx.media3.exoplayer.ExoPlayer
|
|
10
|
+
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* Builds an [ExoPlayer] with the standard NitroPlayer configuration.
|
|
11
14
|
* The player uses the main application looper so that Media3's
|
|
12
15
|
* [MediaSessionService] notification system works without thread conflicts.
|
|
13
16
|
*/
|
|
17
|
+
@UnstableApi
|
|
14
18
|
object ExoPlayerBuilder {
|
|
15
19
|
fun build(context: Context): ExoPlayer {
|
|
16
20
|
val loadControl =
|
|
@@ -37,9 +41,15 @@ object ExoPlayerBuilder {
|
|
|
37
41
|
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
|
|
38
42
|
.build()
|
|
39
43
|
|
|
44
|
+
// DefaultDataSource routes http/https through AuthAwareHttpDataSourceFactory (so custom
|
|
45
|
+
// request headers can be injected) while handling file://, content:// and asset:// natively.
|
|
46
|
+
val dataSourceFactory = DefaultDataSource.Factory(context, AuthAwareHttpDataSourceFactory())
|
|
47
|
+
val mediaSourceFactory = DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
|
|
48
|
+
|
|
40
49
|
return ExoPlayer
|
|
41
50
|
.Builder(context)
|
|
42
51
|
.setLoadControl(loadControl)
|
|
52
|
+
.setMediaSourceFactory(mediaSourceFactory)
|
|
43
53
|
.setAudioAttributes(audioAttrs, /* handleAudioFocus */ true)
|
|
44
54
|
.setHandleAudioBecomingNoisy(true)
|
|
45
55
|
.setWakeMode(C.WAKE_MODE_NETWORK)
|
|
@@ -7,6 +7,7 @@ import android.os.IBinder
|
|
|
7
7
|
import android.os.Looper
|
|
8
8
|
import androidx.media3.common.util.UnstableApi
|
|
9
9
|
import androidx.media3.exoplayer.ExoPlayer
|
|
10
|
+
import androidx.media3.session.DefaultMediaNotificationProvider
|
|
10
11
|
import androidx.media3.session.MediaSession
|
|
11
12
|
import androidx.media3.session.MediaSessionService
|
|
12
13
|
import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
|
|
@@ -29,11 +30,24 @@ class NitroPlayerPlaybackService : MediaSessionService() {
|
|
|
29
30
|
|
|
30
31
|
companion object {
|
|
31
32
|
const val ACTION_LOCAL_BIND = "com.margelo.nitro.nitroplayer.LOCAL_BIND"
|
|
33
|
+
|
|
34
|
+
@Volatile
|
|
35
|
+
var notificationSmallIconResName: String? = null
|
|
36
|
+
set(value) {
|
|
37
|
+
field = value
|
|
38
|
+
instance?.let { service ->
|
|
39
|
+
service.mainHandler.post { service.applyNotificationSmallIcon() }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Volatile
|
|
44
|
+
private var instance: NitroPlayerPlaybackService? = null
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
// ── Created in onCreate ────────────────────────────────────────────────
|
|
35
48
|
private lateinit var player: ExoPlayer
|
|
36
49
|
private var mediaSession: MediaSession? = null
|
|
50
|
+
private var notificationProvider: DefaultMediaNotificationProvider? = null
|
|
37
51
|
private val mainHandler = Handler(Looper.getMainLooper())
|
|
38
52
|
|
|
39
53
|
@Volatile
|
|
@@ -54,6 +68,7 @@ class NitroPlayerPlaybackService : MediaSessionService() {
|
|
|
54
68
|
override fun onCreate() {
|
|
55
69
|
super.onCreate()
|
|
56
70
|
NitroPlayerLogger.log("PlaybackService") { "onCreate" }
|
|
71
|
+
instance = this
|
|
57
72
|
|
|
58
73
|
// Build ExoPlayer on main looper (default)
|
|
59
74
|
player = ExoPlayerBuilder.build(this)
|
|
@@ -65,12 +80,35 @@ class NitroPlayerPlaybackService : MediaSessionService() {
|
|
|
65
80
|
.setCallback(MediaSessionCallbackFactory.create(this, playlistManager))
|
|
66
81
|
.build()
|
|
67
82
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
83
|
+
val provider = DefaultMediaNotificationProvider.Builder(this).build()
|
|
84
|
+
notificationProvider = provider
|
|
85
|
+
applyNotificationSmallIcon()
|
|
86
|
+
setMediaNotificationProvider(provider)
|
|
87
|
+
|
|
71
88
|
addSession(mediaSession!!)
|
|
89
|
+
}
|
|
72
90
|
|
|
73
|
-
|
|
91
|
+
private fun applyNotificationSmallIcon() {
|
|
92
|
+
val name = notificationSmallIconResName ?: return
|
|
93
|
+
val provider = notificationProvider ?: return
|
|
94
|
+
val resId = resources.getIdentifier(name, "drawable", packageName)
|
|
95
|
+
if (resId == 0) {
|
|
96
|
+
NitroPlayerLogger.log("PlaybackService") {
|
|
97
|
+
"androidNotificationIcon '$name' not found in drawable resources — using default"
|
|
98
|
+
}
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
provider.setSmallIcon(resId)
|
|
102
|
+
|
|
103
|
+
// Live Refresh :)
|
|
104
|
+
val session = mediaSession
|
|
105
|
+
if (session != null && session.player.isPlaying) {
|
|
106
|
+
try {
|
|
107
|
+
onUpdateNotification(session, false)
|
|
108
|
+
} catch (e: Exception) {
|
|
109
|
+
NitroPlayerLogger.log("PlaybackService") { "notification refresh failed: ${e.message}" }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
74
112
|
}
|
|
75
113
|
|
|
76
114
|
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
|
|
@@ -93,6 +131,8 @@ class NitroPlayerPlaybackService : MediaSessionService() {
|
|
|
93
131
|
|
|
94
132
|
override fun onDestroy() {
|
|
95
133
|
NitroPlayerLogger.log("PlaybackService") { "onDestroy" }
|
|
134
|
+
if (instance === this) instance = null
|
|
135
|
+
notificationProvider = null
|
|
96
136
|
mediaSession?.release()
|
|
97
137
|
mediaSession = null
|
|
98
138
|
player.release()
|
|
@@ -358,6 +358,37 @@ extension TrackPlayerCore {
|
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
+
/// Extracts custom HTTP headers (e.g. `Authorization`) from `extraPayload.headers`.
|
|
362
|
+
/// These are passed to AVURLAsset so authenticated remote streams can be played.
|
|
363
|
+
/// Returns nil when no string headers are present.
|
|
364
|
+
func httpHeaders(for track: TrackItem) -> [String: String]? {
|
|
365
|
+
// AnyMap.toDictionary() returns nested objects as [String: Any?] (optional values),
|
|
366
|
+
// so cast to that and unwrap each value rather than [String: Any].
|
|
367
|
+
guard let payload = track.extraPayload?.toDictionary(),
|
|
368
|
+
let headersValue = payload["headers"],
|
|
369
|
+
let raw = headersValue as? [String: Any?]
|
|
370
|
+
else { return nil }
|
|
371
|
+
|
|
372
|
+
var headers: [String: String] = [:]
|
|
373
|
+
for (key, value) in raw {
|
|
374
|
+
if let stringValue = value as? String {
|
|
375
|
+
headers[key] = stringValue
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return headers.isEmpty ? nil : headers
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/// Builds AVURLAsset options, attaching custom HTTP headers for remote (non-local) tracks.
|
|
382
|
+
private func assetOptions(for track: TrackItem, isLocal: Bool) -> [String: Any] {
|
|
383
|
+
var options: [String: Any] = [AVURLAssetPreferPreciseDurationAndTimingKey: true]
|
|
384
|
+
if !isLocal, let headers = httpHeaders(for: track) {
|
|
385
|
+
// AVURLAssetHTTPHeaderFieldsKey is the de-facto mechanism for per-asset request
|
|
386
|
+
// headers (also used by react-native-track-player). Applies to non-DRM HTTP(S).
|
|
387
|
+
options["AVURLAssetHTTPHeaderFieldsKey"] = headers
|
|
388
|
+
}
|
|
389
|
+
return options
|
|
390
|
+
}
|
|
391
|
+
|
|
361
392
|
/// Creates a gapless-optimized AVPlayerItem with proper buffering configuration
|
|
362
393
|
func createGaplessPlayerItem(for track: TrackItem, isPreload: Bool = false) -> AVPlayerItem? {
|
|
363
394
|
let effectiveUrlString = DownloadManagerCore.shared.getEffectiveUrl(track: track)
|
|
@@ -396,9 +427,7 @@ extension TrackPlayerCore {
|
|
|
396
427
|
asset = preloadedAsset
|
|
397
428
|
NitroPlayerLogger.log("TrackPlayerCore", "🚀 Using preloaded asset for \(track.title)")
|
|
398
429
|
} else {
|
|
399
|
-
asset = AVURLAsset(url: url, options:
|
|
400
|
-
AVURLAssetPreferPreciseDurationAndTimingKey: true
|
|
401
|
-
])
|
|
430
|
+
asset = AVURLAsset(url: url, options: assetOptions(for: track, isLocal: isLocal))
|
|
402
431
|
}
|
|
403
432
|
|
|
404
433
|
let item = AVPlayerItem(asset: asset)
|
|
@@ -463,9 +492,7 @@ extension TrackPlayerCore {
|
|
|
463
492
|
url = remoteUrl
|
|
464
493
|
}
|
|
465
494
|
|
|
466
|
-
let asset = AVURLAsset(url: url, options:
|
|
467
|
-
AVURLAssetPreferPreciseDurationAndTimingKey: true
|
|
468
|
-
])
|
|
495
|
+
let asset = AVURLAsset(url: url, options: self.assetOptions(for: track, isLocal: isLocal))
|
|
469
496
|
|
|
470
497
|
asset.loadValuesAsynchronously(forKeys: Constants.preloadAssetKeys) { [weak self] in
|
|
471
498
|
var allKeysLoaded = true
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include "PlayerConfig.hpp"
|
|
12
12
|
|
|
13
13
|
#include <optional>
|
|
14
|
+
#include <string>
|
|
14
15
|
|
|
15
16
|
namespace margelo::nitro::nitroplayer {
|
|
16
17
|
|
|
@@ -39,11 +40,14 @@ namespace margelo::nitro::nitroplayer {
|
|
|
39
40
|
jni::local_ref<jni::JBoolean> showInNotification = this->getFieldValue(fieldShowInNotification);
|
|
40
41
|
static const auto fieldLookaheadCount = clazz->getField<jni::JDouble>("lookaheadCount");
|
|
41
42
|
jni::local_ref<jni::JDouble> lookaheadCount = this->getFieldValue(fieldLookaheadCount);
|
|
43
|
+
static const auto fieldAndroidNotificationIcon = clazz->getField<jni::JString>("androidNotificationIcon");
|
|
44
|
+
jni::local_ref<jni::JString> androidNotificationIcon = this->getFieldValue(fieldAndroidNotificationIcon);
|
|
42
45
|
return PlayerConfig(
|
|
43
46
|
androidAutoEnabled != nullptr ? std::make_optional(static_cast<bool>(androidAutoEnabled->value())) : std::nullopt,
|
|
44
47
|
carPlayEnabled != nullptr ? std::make_optional(static_cast<bool>(carPlayEnabled->value())) : std::nullopt,
|
|
45
48
|
showInNotification != nullptr ? std::make_optional(static_cast<bool>(showInNotification->value())) : std::nullopt,
|
|
46
|
-
lookaheadCount != nullptr ? std::make_optional(lookaheadCount->value()) : std::nullopt
|
|
49
|
+
lookaheadCount != nullptr ? std::make_optional(lookaheadCount->value()) : std::nullopt,
|
|
50
|
+
androidNotificationIcon != nullptr ? std::make_optional(androidNotificationIcon->toStdString()) : std::nullopt
|
|
47
51
|
);
|
|
48
52
|
}
|
|
49
53
|
|
|
@@ -53,7 +57,7 @@ namespace margelo::nitro::nitroplayer {
|
|
|
53
57
|
*/
|
|
54
58
|
[[maybe_unused]]
|
|
55
59
|
static jni::local_ref<JPlayerConfig::javaobject> fromCpp(const PlayerConfig& value) {
|
|
56
|
-
using JSignature = JPlayerConfig(jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JDouble>);
|
|
60
|
+
using JSignature = JPlayerConfig(jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JString>);
|
|
57
61
|
static const auto clazz = javaClassStatic();
|
|
58
62
|
static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
|
|
59
63
|
return create(
|
|
@@ -61,7 +65,8 @@ namespace margelo::nitro::nitroplayer {
|
|
|
61
65
|
value.androidAutoEnabled.has_value() ? jni::JBoolean::valueOf(value.androidAutoEnabled.value()) : nullptr,
|
|
62
66
|
value.carPlayEnabled.has_value() ? jni::JBoolean::valueOf(value.carPlayEnabled.value()) : nullptr,
|
|
63
67
|
value.showInNotification.has_value() ? jni::JBoolean::valueOf(value.showInNotification.value()) : nullptr,
|
|
64
|
-
value.lookaheadCount.has_value() ? jni::JDouble::valueOf(value.lookaheadCount.value()) : nullptr
|
|
68
|
+
value.lookaheadCount.has_value() ? jni::JDouble::valueOf(value.lookaheadCount.value()) : nullptr,
|
|
69
|
+
value.androidNotificationIcon.has_value() ? jni::make_jstring(value.androidNotificationIcon.value()) : nullptr
|
|
65
70
|
);
|
|
66
71
|
}
|
|
67
72
|
};
|
|
@@ -28,7 +28,10 @@ data class PlayerConfig(
|
|
|
28
28
|
val showInNotification: Boolean?,
|
|
29
29
|
@DoNotStrip
|
|
30
30
|
@Keep
|
|
31
|
-
val lookaheadCount: Double
|
|
31
|
+
val lookaheadCount: Double?,
|
|
32
|
+
@DoNotStrip
|
|
33
|
+
@Keep
|
|
34
|
+
val androidNotificationIcon: String?
|
|
32
35
|
) {
|
|
33
36
|
/* primary constructor */
|
|
34
37
|
|
|
@@ -40,8 +43,8 @@ data class PlayerConfig(
|
|
|
40
43
|
@Keep
|
|
41
44
|
@Suppress("unused")
|
|
42
45
|
@JvmStatic
|
|
43
|
-
private fun fromCpp(androidAutoEnabled: Boolean?, carPlayEnabled: Boolean?, showInNotification: Boolean?, lookaheadCount: Double?): PlayerConfig {
|
|
44
|
-
return PlayerConfig(androidAutoEnabled, carPlayEnabled, showInNotification, lookaheadCount)
|
|
46
|
+
private fun fromCpp(androidAutoEnabled: Boolean?, carPlayEnabled: Boolean?, showInNotification: Boolean?, lookaheadCount: Double?, androidNotificationIcon: String?): PlayerConfig {
|
|
47
|
+
return PlayerConfig(androidAutoEnabled, carPlayEnabled, showInNotification, lookaheadCount, androidNotificationIcon)
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
}
|
|
@@ -18,7 +18,7 @@ public extension PlayerConfig {
|
|
|
18
18
|
/**
|
|
19
19
|
* Create a new instance of `PlayerConfig`.
|
|
20
20
|
*/
|
|
21
|
-
init(androidAutoEnabled: Bool?, carPlayEnabled: Bool?, showInNotification: Bool?, lookaheadCount: Double?) {
|
|
21
|
+
init(androidAutoEnabled: Bool?, carPlayEnabled: Bool?, showInNotification: Bool?, lookaheadCount: Double?, androidNotificationIcon: String?) {
|
|
22
22
|
self.init({ () -> bridge.std__optional_bool_ in
|
|
23
23
|
if let __unwrappedValue = androidAutoEnabled {
|
|
24
24
|
return bridge.create_std__optional_bool_(__unwrappedValue)
|
|
@@ -43,6 +43,12 @@ public extension PlayerConfig {
|
|
|
43
43
|
} else {
|
|
44
44
|
return .init()
|
|
45
45
|
}
|
|
46
|
+
}(), { () -> bridge.std__optional_std__string_ in
|
|
47
|
+
if let __unwrappedValue = androidNotificationIcon {
|
|
48
|
+
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
|
|
49
|
+
} else {
|
|
50
|
+
return .init()
|
|
51
|
+
}
|
|
46
52
|
}())
|
|
47
53
|
}
|
|
48
54
|
|
|
@@ -93,4 +99,16 @@ public extension PlayerConfig {
|
|
|
93
99
|
}
|
|
94
100
|
}()
|
|
95
101
|
}
|
|
102
|
+
|
|
103
|
+
@inline(__always)
|
|
104
|
+
var androidNotificationIcon: String? {
|
|
105
|
+
return { () -> String? in
|
|
106
|
+
if bridge.has_value_std__optional_std__string_(self.__androidNotificationIcon) {
|
|
107
|
+
let __unwrapped = bridge.get_std__optional_std__string_(self.__androidNotificationIcon)
|
|
108
|
+
return String(__unwrapped)
|
|
109
|
+
} else {
|
|
110
|
+
return nil
|
|
111
|
+
}
|
|
112
|
+
}()
|
|
113
|
+
}
|
|
96
114
|
}
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
#include <optional>
|
|
34
|
+
#include <string>
|
|
34
35
|
|
|
35
36
|
namespace margelo::nitro::nitroplayer {
|
|
36
37
|
|
|
@@ -43,10 +44,11 @@ namespace margelo::nitro::nitroplayer {
|
|
|
43
44
|
std::optional<bool> carPlayEnabled SWIFT_PRIVATE;
|
|
44
45
|
std::optional<bool> showInNotification SWIFT_PRIVATE;
|
|
45
46
|
std::optional<double> lookaheadCount SWIFT_PRIVATE;
|
|
47
|
+
std::optional<std::string> androidNotificationIcon SWIFT_PRIVATE;
|
|
46
48
|
|
|
47
49
|
public:
|
|
48
50
|
PlayerConfig() = default;
|
|
49
|
-
explicit PlayerConfig(std::optional<bool> androidAutoEnabled, std::optional<bool> carPlayEnabled, std::optional<bool> showInNotification, std::optional<double> lookaheadCount): androidAutoEnabled(androidAutoEnabled), carPlayEnabled(carPlayEnabled), showInNotification(showInNotification), lookaheadCount(lookaheadCount) {}
|
|
51
|
+
explicit PlayerConfig(std::optional<bool> androidAutoEnabled, std::optional<bool> carPlayEnabled, std::optional<bool> showInNotification, std::optional<double> lookaheadCount, std::optional<std::string> androidNotificationIcon): androidAutoEnabled(androidAutoEnabled), carPlayEnabled(carPlayEnabled), showInNotification(showInNotification), lookaheadCount(lookaheadCount), androidNotificationIcon(androidNotificationIcon) {}
|
|
50
52
|
|
|
51
53
|
public:
|
|
52
54
|
friend bool operator==(const PlayerConfig& lhs, const PlayerConfig& rhs) = default;
|
|
@@ -65,7 +67,8 @@ namespace margelo::nitro {
|
|
|
65
67
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidAutoEnabled"))),
|
|
66
68
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "carPlayEnabled"))),
|
|
67
69
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "showInNotification"))),
|
|
68
|
-
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount")))
|
|
70
|
+
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount"))),
|
|
71
|
+
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon")))
|
|
69
72
|
);
|
|
70
73
|
}
|
|
71
74
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitroplayer::PlayerConfig& arg) {
|
|
@@ -74,6 +77,7 @@ namespace margelo::nitro {
|
|
|
74
77
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "carPlayEnabled"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.carPlayEnabled));
|
|
75
78
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "showInNotification"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.showInNotification));
|
|
76
79
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.lookaheadCount));
|
|
80
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.androidNotificationIcon));
|
|
77
81
|
return obj;
|
|
78
82
|
}
|
|
79
83
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
@@ -88,6 +92,7 @@ namespace margelo::nitro {
|
|
|
88
92
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "carPlayEnabled")))) return false;
|
|
89
93
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "showInNotification")))) return false;
|
|
90
94
|
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount")))) return false;
|
|
95
|
+
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon")))) return false;
|
|
91
96
|
return true;
|
|
92
97
|
}
|
|
93
98
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-player",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "A powerful audio player library for React Native with playlist management, playback controls, and support for Android Auto and CarPlay",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"module": "lib/index",
|