@5stones/react-native-audio-browser 0.1.4 → 0.1.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/audiobrowser/player/MediaFactory.kt +11 -71
- package/android/src/main/java/com/audiobrowser/player/TransformingDataSource.kt +125 -0
- package/ios/Browser/BrowserManager.swift +17 -2
- package/ios/TrackPlayer.swift +150 -31
- package/lib/commonjs/features/browser.js.map +1 -1
- package/lib/commonjs/features/equalizer.js.map +1 -1
- package/lib/commonjs/features/errors.js.map +1 -1
- package/lib/commonjs/features/favorites.js.map +1 -1
- package/lib/commonjs/features/metadata.js.map +1 -1
- package/lib/commonjs/features/nowPlaying.js.map +1 -1
- package/lib/commonjs/features/output.js.map +1 -1
- package/lib/commonjs/features/playback/state.js.map +1 -1
- package/lib/commonjs/features/player/options.js.map +1 -1
- package/lib/commonjs/features/queue/activeTrack.js.map +1 -1
- package/lib/commonjs/features/queue/queue.js +1 -1
- package/lib/commonjs/features/queue/queue.js.map +1 -1
- package/lib/commonjs/features/remoteControls.js.map +1 -1
- package/lib/commonjs/utils/useDebug.js.map +1 -1
- package/lib/commonjs/utils/validation.js +23 -0
- package/lib/commonjs/utils/validation.js.map +1 -0
- package/lib/commonjs/web/NativeAudioBrowser.js +53 -15
- package/lib/commonjs/web/NativeAudioBrowser.js.map +1 -1
- package/lib/commonjs/web/SimpleRouter.js +5 -4
- package/lib/commonjs/web/SimpleRouter.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/Player.js +49 -24
- package/lib/commonjs/web/TrackPlayer/Player.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js +54 -45
- package/lib/commonjs/web/TrackPlayer/PlaylistPlayer.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/State.js.map +1 -1
- package/lib/commonjs/web/browser/BrowserManager.js +10 -17
- package/lib/commonjs/web/browser/BrowserManager.js.map +1 -1
- package/lib/commonjs/web/browser/FavoriteManager.js.map +1 -1
- package/lib/commonjs/web/browser/NavigationErrorManager.js.map +1 -1
- package/lib/commonjs/web/browser/SearchManager.js.map +1 -1
- package/lib/commonjs/web/http/HttpClient.js +15 -2
- package/lib/commonjs/web/http/HttpClient.js.map +1 -1
- package/lib/commonjs/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/commonjs/web/player/NowPlayingManager.js +9 -0
- package/lib/commonjs/web/player/NowPlayingManager.js.map +1 -1
- package/lib/commonjs/web/player/OptionsManager.js +1 -1
- package/lib/commonjs/web/player/OptionsManager.js.map +1 -1
- package/lib/commonjs/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/module/features/browser.js.map +1 -1
- package/lib/module/features/equalizer.js.map +1 -1
- package/lib/module/features/errors.js.map +1 -1
- package/lib/module/features/favorites.js.map +1 -1
- package/lib/module/features/metadata.js.map +1 -1
- package/lib/module/features/nowPlaying.js +1 -0
- package/lib/module/features/nowPlaying.js.map +1 -1
- package/lib/module/features/output.js.map +1 -1
- package/lib/module/features/playback/state.js.map +1 -1
- package/lib/module/features/player/options.js.map +1 -1
- package/lib/module/features/queue/activeTrack.js.map +1 -1
- package/lib/module/features/queue/queue.js +1 -1
- package/lib/module/features/queue/queue.js.map +1 -1
- package/lib/module/features/remoteControls.js.map +1 -1
- package/lib/module/utils/useDebug.js.map +1 -1
- package/lib/module/utils/validation.js +18 -0
- package/lib/module/utils/validation.js.map +1 -0
- package/lib/module/web/NativeAudioBrowser.js +53 -15
- package/lib/module/web/NativeAudioBrowser.js.map +1 -1
- package/lib/module/web/SimpleRouter.js +5 -4
- package/lib/module/web/SimpleRouter.js.map +1 -1
- package/lib/module/web/TrackPlayer/Player.js +49 -24
- package/lib/module/web/TrackPlayer/Player.js.map +1 -1
- package/lib/module/web/TrackPlayer/PlaylistPlayer.js +54 -45
- package/lib/module/web/TrackPlayer/PlaylistPlayer.js.map +1 -1
- package/lib/module/web/TrackPlayer/State.js.map +1 -1
- package/lib/module/web/browser/BrowserManager.js +10 -17
- package/lib/module/web/browser/BrowserManager.js.map +1 -1
- package/lib/module/web/browser/FavoriteManager.js.map +1 -1
- package/lib/module/web/browser/NavigationErrorManager.js.map +1 -1
- package/lib/module/web/browser/SearchManager.js.map +1 -1
- package/lib/module/web/http/HttpClient.js +15 -2
- package/lib/module/web/http/HttpClient.js.map +1 -1
- package/lib/module/web/http/RequestConfigBuilder.js.map +1 -1
- package/lib/module/web/player/NowPlayingManager.js +9 -0
- package/lib/module/web/player/NowPlayingManager.js.map +1 -1
- package/lib/module/web/player/OptionsManager.js +1 -1
- package/lib/module/web/player/OptionsManager.js.map +1 -1
- package/lib/module/web/util/BrowserPathHelper.js.map +1 -1
- package/lib/typescript/src/features/browser.d.ts.map +1 -1
- package/lib/typescript/src/features/equalizer.d.ts.map +1 -1
- package/lib/typescript/src/features/errors.d.ts.map +1 -1
- package/lib/typescript/src/features/favorites.d.ts.map +1 -1
- package/lib/typescript/src/features/metadata.d.ts +1 -1
- package/lib/typescript/src/features/metadata.d.ts.map +1 -1
- package/lib/typescript/src/features/nowPlaying.d.ts +1 -1
- package/lib/typescript/src/features/nowPlaying.d.ts.map +1 -1
- package/lib/typescript/src/features/output.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/state.d.ts +1 -1
- package/lib/typescript/src/features/playback/state.d.ts.map +1 -1
- package/lib/typescript/src/features/player/options.d.ts +1 -1
- package/lib/typescript/src/features/player/options.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/activeTrack.d.ts.map +1 -1
- package/lib/typescript/src/features/queue/queue.d.ts.map +1 -1
- package/lib/typescript/src/features/remoteControls.d.ts.map +1 -1
- package/lib/typescript/src/specs/audio-browser.nitro.d.ts.map +1 -1
- package/lib/typescript/src/utils/useDebug.d.ts +1 -1
- package/lib/typescript/src/utils/useDebug.d.ts.map +1 -1
- package/lib/typescript/src/utils/validation.d.ts +3 -0
- package/lib/typescript/src/utils/validation.d.ts.map +1 -0
- package/lib/typescript/src/web/NativeAudioBrowser.d.ts +6 -1
- package/lib/typescript/src/web/NativeAudioBrowser.d.ts.map +1 -1
- package/lib/typescript/src/web/SimpleRouter.d.ts.map +1 -1
- package/lib/typescript/src/web/TrackPlayer/Player.d.ts +7 -19
- package/lib/typescript/src/web/TrackPlayer/Player.d.ts.map +1 -1
- package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts +3 -3
- package/lib/typescript/src/web/TrackPlayer/PlaylistPlayer.d.ts.map +1 -1
- package/lib/typescript/src/web/browser/BrowserManager.d.ts.map +1 -1
- package/lib/typescript/src/web/browser/FavoriteManager.d.ts.map +1 -1
- package/lib/typescript/src/web/browser/NavigationErrorManager.d.ts.map +1 -1
- package/lib/typescript/src/web/browser/SearchManager.d.ts +1 -1
- package/lib/typescript/src/web/browser/SearchManager.d.ts.map +1 -1
- package/lib/typescript/src/web/http/HttpClient.d.ts.map +1 -1
- package/lib/typescript/src/web/http/RequestConfigBuilder.d.ts.map +1 -1
- package/lib/typescript/src/web/player/NowPlayingManager.d.ts.map +1 -1
- package/lib/typescript/src/web/player/OptionsManager.d.ts.map +1 -1
- package/lib/typescript/src/web/util/BrowserPathHelper.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/features/browser.ts +1 -1
- package/src/features/equalizer.ts +1 -1
- package/src/features/errors.ts +1 -1
- package/src/features/favorites.ts +1 -1
- package/src/features/metadata.ts +1 -1
- package/src/features/nowPlaying.ts +1 -1
- package/src/features/output.ts +1 -1
- package/src/features/playback/state.ts +1 -1
- package/src/features/player/options.ts +2 -2
- package/src/features/queue/activeTrack.ts +1 -1
- package/src/features/queue/queue.ts +2 -2
- package/src/features/remoteControls.ts +2 -2
- package/src/specs/audio-browser.nitro.ts +0 -1
- package/src/utils/useDebug.ts +6 -6
- package/src/utils/validation.ts +27 -0
- package/src/web/NativeAudioBrowser.ts +137 -58
- package/src/web/SimpleRouter.ts +24 -9
- package/src/web/TrackPlayer/Player.ts +58 -30
- package/src/web/TrackPlayer/PlaylistPlayer.ts +72 -63
- package/src/web/TrackPlayer/RepeatMode.ts +1 -1
- package/src/web/TrackPlayer/State.ts +9 -9
- package/src/web/browser/BrowserManager.ts +124 -67
- package/src/web/browser/FavoriteManager.ts +5 -3
- package/src/web/browser/NavigationErrorManager.ts +15 -8
- package/src/web/browser/SearchManager.ts +17 -11
- package/src/web/http/HttpClient.ts +25 -7
- package/src/web/http/RequestConfigBuilder.ts +29 -13
- package/src/web/player/NowPlayingManager.ts +13 -7
- package/src/web/player/OptionsManager.ts +7 -6
- package/src/web/util/BrowserPathHelper.ts +3 -2
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
package com.audiobrowser.player
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import android.net.Uri
|
|
5
|
-
import androidx.core.net.toUri
|
|
6
4
|
import androidx.media3.common.MediaItem
|
|
7
5
|
import androidx.media3.datasource.DataSource
|
|
8
6
|
import androidx.media3.datasource.DefaultDataSource
|
|
@@ -87,91 +85,33 @@ class MediaFactory(
|
|
|
87
85
|
return mediaFactory.supportedTypes
|
|
88
86
|
}
|
|
89
87
|
|
|
90
|
-
/**
|
|
91
|
-
* Get the final request configuration for a media URL. Returns the URL, headers, and user-agent
|
|
92
|
-
* to use for streaming.
|
|
93
|
-
*/
|
|
94
|
-
private fun getMediaRequestConfig(
|
|
95
|
-
originalUrl: String
|
|
96
|
-
): Triple<String, Map<String, String>, String> {
|
|
97
|
-
return try {
|
|
98
|
-
val requestConfig = getRequestConfig(originalUrl)
|
|
99
|
-
Timber.d(
|
|
100
|
-
"Got media request config for URL '$originalUrl': path=${requestConfig?.path}, baseUrl=${requestConfig?.baseUrl}, headers=${requestConfig?.headers}, userAgent=${requestConfig?.userAgent}"
|
|
101
|
-
)
|
|
102
|
-
if (requestConfig != null) {
|
|
103
|
-
val path = requestConfig.path ?: ""
|
|
104
|
-
val baseUrl = requestConfig.baseUrl
|
|
105
|
-
|
|
106
|
-
// Build URL using the helper to ensure proper slash handling
|
|
107
|
-
val url = com.audiobrowser.util.BrowserPathHelper.buildUrl(baseUrl, path)
|
|
108
|
-
|
|
109
|
-
// Add query parameters if any
|
|
110
|
-
val finalUrl =
|
|
111
|
-
if (requestConfig.query?.isNotEmpty() == true) {
|
|
112
|
-
val uri = Uri.parse(url).buildUpon()
|
|
113
|
-
requestConfig.query.forEach { (key, value) -> uri.appendQueryParameter(key, value) }
|
|
114
|
-
uri.build().toString()
|
|
115
|
-
} else {
|
|
116
|
-
url
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
val headers = requestConfig.headers ?: emptyMap()
|
|
120
|
-
val userAgent = requestConfig.userAgent ?: DEFAULT_USER_AGENT
|
|
121
|
-
|
|
122
|
-
Triple(finalUrl, headers, userAgent)
|
|
123
|
-
} else {
|
|
124
|
-
Triple(originalUrl, emptyMap(), DEFAULT_USER_AGENT)
|
|
125
|
-
}
|
|
126
|
-
} catch (e: Exception) {
|
|
127
|
-
Timber.e(e, "Error getting media request config for URL: $originalUrl")
|
|
128
|
-
Triple(originalUrl, emptyMap(), DEFAULT_USER_AGENT)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
88
|
override fun createMediaSource(mediaItem: MediaItem): MediaSource {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
Timber.d("Media headers: $headers")
|
|
138
|
-
Timber.d("Media user-agent: $userAgent")
|
|
139
|
-
|
|
140
|
-
// DefaultDataSource.Factory handles all URI schemes (file://, content://, http://, https://,
|
|
141
|
-
// etc.)
|
|
142
|
-
// by routing to the appropriate implementation. We provide a configured HTTP factory for remote
|
|
143
|
-
// URLs.
|
|
89
|
+
// URL transformation is deferred to TransformingDataSource.open() which runs on
|
|
90
|
+
// ExoPlayer's IO thread. This prevents deadlocks when the JS thread is blocked by
|
|
91
|
+
// synchronous Nitro calls (e.g., seekTo) while a media transform callback needs the
|
|
92
|
+
// JS thread to resolve.
|
|
144
93
|
val httpFactory =
|
|
145
94
|
DefaultHttpDataSource.Factory().apply {
|
|
146
|
-
setUserAgent(
|
|
95
|
+
setUserAgent(DEFAULT_USER_AGENT)
|
|
147
96
|
setAllowCrossProtocolRedirects(true)
|
|
148
|
-
if (headers.isNotEmpty()) {
|
|
149
|
-
setDefaultRequestProperties(headers)
|
|
150
|
-
}
|
|
151
97
|
// Connect transfer listener for bandwidth measurement
|
|
152
98
|
transferListener?.let { setTransferListener(it) }
|
|
153
99
|
}
|
|
154
|
-
val
|
|
100
|
+
val baseFactory: DataSource.Factory = DefaultDataSource.Factory(context, httpFactory)
|
|
155
101
|
|
|
156
|
-
//
|
|
157
|
-
val
|
|
158
|
-
if (finalUrl != originalUri.toString()) {
|
|
159
|
-
Timber.d("Creating new MediaItem with transformed URL: $finalUrl")
|
|
160
|
-
mediaItem.buildUpon().setUri(finalUrl.toUri()).build()
|
|
161
|
-
} else {
|
|
162
|
-
mediaItem
|
|
163
|
-
}
|
|
102
|
+
// Wrap with TransformingDataSource to resolve URLs on the IO thread
|
|
103
|
+
val transformingFactory = TransformingDataSource.Factory(baseFactory, getRequestConfig)
|
|
164
104
|
|
|
165
105
|
// Configure data source factory with optional caching
|
|
166
106
|
val dataSourceFactory =
|
|
167
107
|
cache?.let {
|
|
168
108
|
CacheDataSource.Factory()
|
|
169
109
|
.setCache(it)
|
|
170
|
-
.setUpstreamDataSourceFactory(
|
|
110
|
+
.setUpstreamDataSourceFactory(transformingFactory)
|
|
171
111
|
.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
|
|
172
|
-
} ?:
|
|
112
|
+
} ?: transformingFactory
|
|
173
113
|
|
|
174
114
|
// Use DefaultMediaSourceFactory which supports HLS, DASH, and progressive media
|
|
175
|
-
return mediaFactory.setDataSourceFactory(dataSourceFactory).createMediaSource(
|
|
115
|
+
return mediaFactory.setDataSourceFactory(dataSourceFactory).createMediaSource(mediaItem)
|
|
176
116
|
}
|
|
177
117
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
package com.audiobrowser.player
|
|
2
|
+
|
|
3
|
+
import android.net.Uri
|
|
4
|
+
import androidx.core.net.toUri
|
|
5
|
+
import androidx.media3.datasource.DataSource
|
|
6
|
+
import androidx.media3.datasource.DataSpec
|
|
7
|
+
import androidx.media3.datasource.TransferListener
|
|
8
|
+
import com.audiobrowser.util.BrowserPathHelper
|
|
9
|
+
import com.margelo.nitro.audiobrowser.MediaRequestConfig
|
|
10
|
+
import timber.log.Timber
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A DataSource wrapper that defers media URL transformation to ExoPlayer's IO thread.
|
|
14
|
+
*
|
|
15
|
+
* ExoPlayer calls [MediaSource.Factory.createMediaSource] on the main thread during
|
|
16
|
+
* `addMediaItem()`, but calls [DataSource.open] on a background IO thread when it
|
|
17
|
+
* needs to fetch data. By deferring URL transformation (which may invoke a JS callback
|
|
18
|
+
* via `runBlocking`) to [open], we avoid blocking the main thread and prevent deadlocks
|
|
19
|
+
* when the JS thread is occupied by synchronous Nitro calls (e.g., `seekTo`).
|
|
20
|
+
*/
|
|
21
|
+
class TransformingDataSource(
|
|
22
|
+
private val upstream: DataSource,
|
|
23
|
+
private val getRequestConfig: (originalUrl: String) -> MediaRequestConfig?,
|
|
24
|
+
) : DataSource {
|
|
25
|
+
|
|
26
|
+
companion object {
|
|
27
|
+
private const val DEFAULT_USER_AGENT = "react-native-audio-browser"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private var resolvedUri: Uri? = null
|
|
31
|
+
|
|
32
|
+
override fun open(dataSpec: DataSpec): Long {
|
|
33
|
+
val originalUrl = dataSpec.uri.toString()
|
|
34
|
+
|
|
35
|
+
// Resolve the transform on the IO thread — safe to block here since
|
|
36
|
+
// the main thread and JS thread remain free for other work.
|
|
37
|
+
val (finalUrl, headers, userAgent) = resolveRequestConfig(originalUrl)
|
|
38
|
+
|
|
39
|
+
Timber.d("TransformingDataSource: $originalUrl -> $finalUrl")
|
|
40
|
+
|
|
41
|
+
// Build merged headers including user-agent override
|
|
42
|
+
val mergedHeaders = buildMap {
|
|
43
|
+
putAll(dataSpec.httpRequestHeaders)
|
|
44
|
+
putAll(headers)
|
|
45
|
+
if (userAgent != DEFAULT_USER_AGENT) {
|
|
46
|
+
put("User-Agent", userAgent)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Build a new DataSpec with the transformed URL and merged headers
|
|
51
|
+
val transformedSpec =
|
|
52
|
+
dataSpec.buildUpon().setUri(finalUrl.toUri()).setHttpRequestHeaders(mergedHeaders).build()
|
|
53
|
+
|
|
54
|
+
resolvedUri = transformedSpec.uri
|
|
55
|
+
return upstream.open(transformedSpec)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override fun read(buffer: ByteArray, offset: Int, length: Int): Int {
|
|
59
|
+
return upstream.read(buffer, offset, length)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
override fun addTransferListener(transferListener: TransferListener) {
|
|
63
|
+
upstream.addTransferListener(transferListener)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override fun getUri(): Uri? {
|
|
67
|
+
return resolvedUri ?: upstream.uri
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
override fun close() {
|
|
71
|
+
upstream.close()
|
|
72
|
+
resolvedUri = null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Resolves the media request configuration for the given URL. This calls the JS transform
|
|
77
|
+
* callback if configured. Safe to call from a background thread.
|
|
78
|
+
*
|
|
79
|
+
* @return Triple of (finalUrl, headers, userAgent)
|
|
80
|
+
*/
|
|
81
|
+
private fun resolveRequestConfig(
|
|
82
|
+
originalUrl: String
|
|
83
|
+
): Triple<String, Map<String, String>, String> {
|
|
84
|
+
return try {
|
|
85
|
+
val requestConfig = getRequestConfig(originalUrl)
|
|
86
|
+
if (requestConfig != null) {
|
|
87
|
+
val path = requestConfig.path ?: ""
|
|
88
|
+
val baseUrl = requestConfig.baseUrl
|
|
89
|
+
val url = BrowserPathHelper.buildUrl(baseUrl, path)
|
|
90
|
+
|
|
91
|
+
val finalUrl =
|
|
92
|
+
if (requestConfig.query?.isNotEmpty() == true) {
|
|
93
|
+
val uri = Uri.parse(url).buildUpon()
|
|
94
|
+
requestConfig.query!!.forEach { (key, value) -> uri.appendQueryParameter(key, value) }
|
|
95
|
+
uri.build().toString()
|
|
96
|
+
} else {
|
|
97
|
+
url
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
val headers = requestConfig.headers ?: emptyMap()
|
|
101
|
+
val userAgent = requestConfig.userAgent ?: DEFAULT_USER_AGENT
|
|
102
|
+
|
|
103
|
+
Triple(finalUrl, headers, userAgent)
|
|
104
|
+
} else {
|
|
105
|
+
Triple(originalUrl, emptyMap(), DEFAULT_USER_AGENT)
|
|
106
|
+
}
|
|
107
|
+
} catch (e: Exception) {
|
|
108
|
+
Timber.e(e, "Error resolving media request config for URL: $originalUrl")
|
|
109
|
+
Triple(originalUrl, emptyMap(), DEFAULT_USER_AGENT)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Factory that creates [TransformingDataSource] instances wrapping an upstream factory. */
|
|
114
|
+
class Factory(
|
|
115
|
+
private val upstreamFactory: DataSource.Factory,
|
|
116
|
+
private val getRequestConfig: (originalUrl: String) -> MediaRequestConfig?,
|
|
117
|
+
) : DataSource.Factory {
|
|
118
|
+
override fun createDataSource(): DataSource {
|
|
119
|
+
return TransformingDataSource(
|
|
120
|
+
upstream = upstreamFactory.createDataSource(),
|
|
121
|
+
getRequestConfig = getRequestConfig,
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -744,11 +744,26 @@ final class BrowserManager: @unchecked Sendable {
|
|
|
744
744
|
|
|
745
745
|
logger.debug("resolveMediaUrl: calling transform callback...")
|
|
746
746
|
// MediaRequestConfig.transform takes (request, routeParams) - pass nil for routeParams
|
|
747
|
+
// Call the JS transform outside the task group to avoid sending-parameter
|
|
748
|
+
// data-race warnings (transform and baseRequest aren't Sendable).
|
|
747
749
|
let outerPromise = transform(baseRequest, nil)
|
|
748
|
-
logger.debug("resolveMediaUrl: awaiting outer promise...")
|
|
749
750
|
let innerPromise = try await outerPromise.await()
|
|
750
751
|
logger.debug("resolveMediaUrl: awaiting inner promise...")
|
|
751
|
-
let transformedConfig = try await
|
|
752
|
+
let transformedConfig = try await withThrowingTaskGroup(of: RequestConfig.self) { group in
|
|
753
|
+
group.addTask { [innerPromise] in
|
|
754
|
+
return try await innerPromise.await()
|
|
755
|
+
}
|
|
756
|
+
group.addTask {
|
|
757
|
+
// Safety-net timeout: if the JS transform Promise doesn't resolve
|
|
758
|
+
// within 10s, bail out. This guards against Nitro bridge hangs when
|
|
759
|
+
// concurrent callbacks race.
|
|
760
|
+
try await Task.sleep(nanoseconds: 10_000_000_000)
|
|
761
|
+
throw CancellationError()
|
|
762
|
+
}
|
|
763
|
+
let result = try await group.next()!
|
|
764
|
+
group.cancelAll()
|
|
765
|
+
return result
|
|
766
|
+
}
|
|
752
767
|
logger.debug("resolveMediaUrl: transform complete")
|
|
753
768
|
|
|
754
769
|
// Extract values immediately to Swift native types to avoid
|
package/ios/TrackPlayer.swift
CHANGED
|
@@ -165,25 +165,87 @@ class TrackPlayer {
|
|
|
165
165
|
|
|
166
166
|
// MARK: - AVPlayer Properties (from AVPlayerWrapper)
|
|
167
167
|
|
|
168
|
-
///
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
168
|
+
/// Manages seeks that arrive while a track is loading.
|
|
169
|
+
/// Owns the full lifecycle: capture → execute → coordinate state transitions.
|
|
170
|
+
///
|
|
171
|
+
/// AVPlayer's `seek(to:)` is a no-op when there's no loaded `currentItem`,
|
|
172
|
+
/// so we must manually defer seeks that arrive during loading and execute them
|
|
173
|
+
/// once the AVPlayerItem is ready. This coordinator encapsulates the three
|
|
174
|
+
/// sub-problems (capture, execute, coordinate) that were previously spread
|
|
175
|
+
/// across `pendingSeek`, `loadSeekInFlight`, and guards in multiple callbacks.
|
|
176
|
+
final class LoadSeekCoordinator {
|
|
177
|
+
enum State {
|
|
178
|
+
case idle
|
|
179
|
+
case pendingSeek(time: TimeInterval, completion: (@Sendable (Bool) -> Void)?)
|
|
180
|
+
case seekInFlight
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private let logger = Logger(subsystem: "com.audiobrowser", category: "LoadSeekCoordinator")
|
|
184
|
+
private(set) var state: State = .idle
|
|
185
|
+
|
|
186
|
+
/// The pending seek time, if any. Used by `seekBy()` to offset from the deferred position.
|
|
187
|
+
var pendingTime: TimeInterval? {
|
|
188
|
+
if case let .pendingSeek(time, _) = state { return time }
|
|
189
|
+
return nil
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Whether to suppress state transitions to `.ready`.
|
|
193
|
+
/// True when a seek is pending or in-flight during the loading phase.
|
|
194
|
+
var shouldDeferReadyTransition: Bool {
|
|
195
|
+
switch state {
|
|
196
|
+
case .idle: return false
|
|
197
|
+
case .pendingSeek, .seekInFlight: return true
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// Called from `seekTo()` when `TrackPlayer.state == .loading`.
|
|
202
|
+
/// Cancels any previous pending seek before storing the new one.
|
|
203
|
+
func capture(position: Double, completion: (@Sendable (Bool) -> Void)?) {
|
|
204
|
+
// Cancel previous pending seek if any
|
|
205
|
+
if case let .pendingSeek(_, oldCompletion) = state {
|
|
206
|
+
oldCompletion?(false)
|
|
207
|
+
}
|
|
208
|
+
state = .pendingSeek(time: position, completion: completion)
|
|
209
|
+
logger.debug("captured pending seek to \(position)s")
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/// Called when AVPlayerItem is ready to accept a seek
|
|
213
|
+
/// (from `playableLoadTask` or `avItemDidUpdatePlaybackLikelyToKeepUp`).
|
|
214
|
+
///
|
|
215
|
+
/// Returns `true` if a seek was initiated — caller should NOT transition to `.ready` yet.
|
|
216
|
+
func executeIfPending(on player: AVPlayer, delegate: TrackPlayer?) -> Bool {
|
|
217
|
+
guard case let .pendingSeek(time, completion) = state else { return false }
|
|
218
|
+
|
|
219
|
+
logger.debug("executing deferred seek to \(time)s")
|
|
220
|
+
state = .seekInFlight
|
|
172
221
|
|
|
173
|
-
func execute(on player: AVPlayer, delegate: TrackPlayer?) {
|
|
174
222
|
let cmTime = CMTime(seconds: time, preferredTimescale: 1000)
|
|
175
|
-
let seekTime = time
|
|
176
223
|
player
|
|
177
224
|
.seek(to: cmTime, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero) { finished in
|
|
178
225
|
Task { @MainActor in
|
|
179
|
-
delegate?.handleSeekCompleted(to:
|
|
226
|
+
delegate?.handleSeekCompleted(to: time, didFinish: finished)
|
|
180
227
|
}
|
|
181
228
|
completion?(finished)
|
|
182
229
|
}
|
|
230
|
+
|
|
231
|
+
return true
|
|
183
232
|
}
|
|
184
233
|
|
|
185
|
-
|
|
186
|
-
|
|
234
|
+
/// Called from `handleSeekCompleted`.
|
|
235
|
+
/// Returns `true` if this was a load-seek (caller should transition `.loading` → `.ready`).
|
|
236
|
+
func seekDidComplete() -> Bool {
|
|
237
|
+
guard case .seekInFlight = state else { return false }
|
|
238
|
+
logger.debug("seek landed, clearing coordinator")
|
|
239
|
+
state = .idle
|
|
240
|
+
return true
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/// Reset when track changes or item is cleared.
|
|
244
|
+
func reset() {
|
|
245
|
+
if case let .pendingSeek(_, completion) = state {
|
|
246
|
+
completion?(false)
|
|
247
|
+
}
|
|
248
|
+
state = .idle
|
|
187
249
|
}
|
|
188
250
|
}
|
|
189
251
|
|
|
@@ -248,7 +310,8 @@ class TrackPlayer {
|
|
|
248
310
|
self?.callbacks?.playerDidChangePlayingState(state)
|
|
249
311
|
}
|
|
250
312
|
|
|
251
|
-
private
|
|
313
|
+
private let loadSeekCoordinator = LoadSeekCoordinator()
|
|
314
|
+
private var mediaResolverTask: Task<Void, Never>?
|
|
252
315
|
private var metadataLoadTask: Task<Void, Never>?
|
|
253
316
|
private var playableLoadTask: Task<Void, Never>?
|
|
254
317
|
private var asset: AVAsset?
|
|
@@ -627,11 +690,9 @@ class TrackPlayer {
|
|
|
627
690
|
- parameter completion: Called when the seek operation completes. The Bool parameter indicates whether the seek finished successfully (true) or was interrupted/deferred (false).
|
|
628
691
|
*/
|
|
629
692
|
func seekTo(_ seconds: TimeInterval, completion: @escaping @Sendable (Bool) -> Void) {
|
|
630
|
-
// If
|
|
693
|
+
// If a track is currently being loaded asynchronously, defer the seek until it's ready.
|
|
631
694
|
if state == .loading {
|
|
632
|
-
|
|
633
|
-
pendingSeek?.cancel()
|
|
634
|
-
pendingSeek = PendingSeek(time: seconds, completion: completion)
|
|
695
|
+
loadSeekCoordinator.capture(position: seconds, completion: completion)
|
|
635
696
|
} else if avPlayer.currentItem != nil {
|
|
636
697
|
let time = CMTime(seconds: seconds, preferredTimescale: 1000)
|
|
637
698
|
let seekSeconds = seconds
|
|
@@ -656,7 +717,7 @@ class TrackPlayer {
|
|
|
656
717
|
let targetTime: TimeInterval
|
|
657
718
|
if state == .loading {
|
|
658
719
|
// If loading, offset from pending seek (or 0 if no pending seek)
|
|
659
|
-
targetTime = (
|
|
720
|
+
targetTime = (loadSeekCoordinator.pendingTime ?? 0) + offset
|
|
660
721
|
} else if let currentItem = avPlayer.currentItem {
|
|
661
722
|
// If playing, offset from current position
|
|
662
723
|
targetTime = currentItem.currentTime().seconds + offset
|
|
@@ -846,8 +907,7 @@ class TrackPlayer {
|
|
|
846
907
|
}
|
|
847
908
|
|
|
848
909
|
asset = nil
|
|
849
|
-
|
|
850
|
-
pendingSeek = nil
|
|
910
|
+
loadSeekCoordinator.reset()
|
|
851
911
|
|
|
852
912
|
avPlayer.replaceCurrentItem(with: nil)
|
|
853
913
|
}
|
|
@@ -915,8 +975,6 @@ class TrackPlayer {
|
|
|
915
975
|
oldAsset.cancelLoading()
|
|
916
976
|
}
|
|
917
977
|
asset = nil
|
|
918
|
-
pendingSeek?.cancel()
|
|
919
|
-
pendingSeek = nil
|
|
920
978
|
}
|
|
921
979
|
if let url {
|
|
922
980
|
let pendingAsset = AVURLAsset(url: url, options: urlOptions)
|
|
@@ -994,10 +1052,21 @@ class TrackPlayer {
|
|
|
994
1052
|
self.startPlayback()
|
|
995
1053
|
}
|
|
996
1054
|
|
|
997
|
-
// Execute any pending seek
|
|
998
|
-
if
|
|
999
|
-
|
|
1000
|
-
|
|
1055
|
+
// Execute any pending seek, or transition state if already buffered
|
|
1056
|
+
if !self.loadSeekCoordinator.executeIfPending(on: self.avPlayer, delegate: self) {
|
|
1057
|
+
// No pending seek — if the item is already buffered (e.g., cached
|
|
1058
|
+
// HTTP response), KVO with [.new] won't fire because
|
|
1059
|
+
// isPlaybackLikelyToKeepUp never changed. Transition explicitly.
|
|
1060
|
+
if avItem.isPlaybackLikelyToKeepUp {
|
|
1061
|
+
self.avItemDidUpdatePlaybackLikelyToKeepUp(true)
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// If the item is already buffered (e.g., cached HTTP response), KVO
|
|
1066
|
+
// with [.new] won't fire because isPlaybackLikelyToKeepUp never
|
|
1067
|
+
// changed. Transition state explicitly so it doesn't stay .loading.
|
|
1068
|
+
if avItem.isPlaybackLikelyToKeepUp {
|
|
1069
|
+
self.avItemDidUpdatePlaybackLikelyToKeepUp(true)
|
|
1001
1070
|
}
|
|
1002
1071
|
}
|
|
1003
1072
|
} catch {
|
|
@@ -1036,6 +1105,11 @@ class TrackPlayer {
|
|
|
1036
1105
|
}
|
|
1037
1106
|
|
|
1038
1107
|
private func handleSeekCompleted(to seconds: Double, didFinish: Bool) {
|
|
1108
|
+
// If this was a deferred load-seek, transition out of .loading state
|
|
1109
|
+
if loadSeekCoordinator.seekDidComplete(), state == .loading {
|
|
1110
|
+
logger.debug("[loadSeek] seek landed at \(seconds)s (finished=\(didFinish)) → .ready")
|
|
1111
|
+
state = .ready
|
|
1112
|
+
}
|
|
1039
1113
|
setNowPlayingCurrentTime(seconds: seconds)
|
|
1040
1114
|
callbacks?.playerDidCompleteSeek(position: seconds, didFinish: didFinish)
|
|
1041
1115
|
}
|
|
@@ -1056,6 +1130,11 @@ class TrackPlayer {
|
|
|
1056
1130
|
// MARK: - Observer Callbacks
|
|
1057
1131
|
|
|
1058
1132
|
func avPlayerDidChangeTimeControlStatus(_ status: AVPlayer.TimeControlStatus) {
|
|
1133
|
+
// During loading, ignore stale timeControlStatus changes from old items.
|
|
1134
|
+
// State transitions during loading are managed by playableLoadTask
|
|
1135
|
+
// and avItemDidUpdatePlaybackLikelyToKeepUp.
|
|
1136
|
+
if state == .loading { return }
|
|
1137
|
+
|
|
1059
1138
|
switch status {
|
|
1060
1139
|
case .paused:
|
|
1061
1140
|
let currentState = state
|
|
@@ -1145,15 +1224,35 @@ class TrackPlayer {
|
|
|
1145
1224
|
}
|
|
1146
1225
|
|
|
1147
1226
|
func audioDidStart() {
|
|
1227
|
+
// Don't override loading state - playableLoadTask manages that transition
|
|
1228
|
+
if state == .loading { return }
|
|
1148
1229
|
state = .playing
|
|
1149
1230
|
}
|
|
1150
1231
|
|
|
1151
1232
|
func avItemDidUpdatePlaybackLikelyToKeepUp(_ playbackLikelyToKeepUp: Bool) {
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1233
|
+
guard playbackLikelyToKeepUp else { return }
|
|
1234
|
+
|
|
1235
|
+
// KVO callbacks are dispatched via Task { @MainActor }, so a stale callback
|
|
1236
|
+
// from a previous AVPlayerItem can arrive after the item was replaced/cleared.
|
|
1237
|
+
// Ignore these — there's nothing meaningful to do without a loaded item.
|
|
1238
|
+
guard avPlayer.currentItem != nil else { return }
|
|
1239
|
+
|
|
1240
|
+
// Execute any pending seek that arrived after playableLoadTask completed
|
|
1241
|
+
if loadSeekCoordinator.executeIfPending(on: avPlayer, delegate: self) {
|
|
1242
|
+
return
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// Only transition to .ready if no seek is in-flight — otherwise
|
|
1246
|
+
// handleSeekCompleted will handle the transition once the seek lands.
|
|
1247
|
+
if !loadSeekCoordinator.shouldDeferReadyTransition, state != .playing {
|
|
1248
|
+
logger.debug("avItemDidUpdatePlaybackLikelyToKeepUp → .ready")
|
|
1155
1249
|
state = .ready
|
|
1156
1250
|
}
|
|
1251
|
+
// Execute any pending seek that arrived after playableLoadTask completed
|
|
1252
|
+
if playbackLikelyToKeepUp, let pending = pendingSeek {
|
|
1253
|
+
pendingSeek = nil
|
|
1254
|
+
pending.execute(on: avPlayer, delegate: self)
|
|
1255
|
+
}
|
|
1157
1256
|
}
|
|
1158
1257
|
|
|
1159
1258
|
// MARK: - Queue Validation
|
|
@@ -1464,6 +1563,13 @@ class TrackPlayer {
|
|
|
1464
1563
|
// Reset end-of-track sleep timer when track changes
|
|
1465
1564
|
sleepTimerManager.onTrackChanged()
|
|
1466
1565
|
|
|
1566
|
+
// Cancel any in-flight media URL resolution from a previous track
|
|
1567
|
+
mediaResolverTask?.cancel()
|
|
1568
|
+
mediaResolverTask = nil
|
|
1569
|
+
|
|
1570
|
+
// Cancel any pending seek from a previous track
|
|
1571
|
+
loadSeekCoordinator.reset()
|
|
1572
|
+
|
|
1467
1573
|
// Cancel any pending retry and reset count when track changes
|
|
1468
1574
|
pendingRetryTask?.cancel()
|
|
1469
1575
|
pendingRetryTask = nil
|
|
@@ -1503,6 +1609,10 @@ class TrackPlayer {
|
|
|
1503
1609
|
logger.debug(" track.url: \(currentTrack.url ?? "nil")")
|
|
1504
1610
|
logger.debug(" track.src: \(src)")
|
|
1505
1611
|
|
|
1612
|
+
// Set loading state early so seekTo calls during async URL resolution
|
|
1613
|
+
// are captured as pending seeks rather than silently dropped
|
|
1614
|
+
state = .loading
|
|
1615
|
+
|
|
1506
1616
|
// Check if it's already a full URL (http/https) or local file
|
|
1507
1617
|
if src.hasPrefix("http://") || src.hasPrefix("https://") || src.hasPrefix("file://") {
|
|
1508
1618
|
// Already a full URL, use directly (may still need transform for headers)
|
|
@@ -1535,18 +1645,23 @@ class TrackPlayer {
|
|
|
1535
1645
|
// Don't use @MainActor for the entire Task - this can cause deadlocks
|
|
1536
1646
|
// when the JS callback needs to schedule work back on the main thread.
|
|
1537
1647
|
// Instead, only hop to main thread when we need to access/modify state.
|
|
1538
|
-
|
|
1648
|
+
mediaResolverTask?.cancel()
|
|
1649
|
+
mediaResolverTask = Task {
|
|
1539
1650
|
self.logger.debug("resolveAndLoadMedia: starting resolution for \(src)")
|
|
1540
1651
|
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
guard isCurrentTrack else {
|
|
1544
|
-
self.logger.debug("Track changed during media resolution, skipping load")
|
|
1652
|
+
guard !Task.isCancelled else {
|
|
1653
|
+
self.logger.debug("resolveAndLoadMedia: cancelled before start")
|
|
1545
1654
|
return
|
|
1546
1655
|
}
|
|
1547
1656
|
|
|
1548
1657
|
self.logger.debug("resolveAndLoadMedia: calling resolver...")
|
|
1549
1658
|
let resolved = await resolver(src)
|
|
1659
|
+
|
|
1660
|
+
guard !Task.isCancelled else {
|
|
1661
|
+
self.logger.debug("resolveAndLoadMedia: cancelled after resolver returned")
|
|
1662
|
+
return
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1550
1665
|
self.logger.debug("resolveAndLoadMedia: resolver returned, resolved URL: \(resolved.url)")
|
|
1551
1666
|
if let headers = resolved.headers {
|
|
1552
1667
|
self.logger.debug(" headers: \(headers)")
|
|
@@ -1557,6 +1672,10 @@ class TrackPlayer {
|
|
|
1557
1672
|
|
|
1558
1673
|
// Load on main thread
|
|
1559
1674
|
await MainActor.run {
|
|
1675
|
+
guard !Task.isCancelled else {
|
|
1676
|
+
self.logger.debug("resolveAndLoadMedia: cancelled before loadMediaWithResolvedUrl")
|
|
1677
|
+
return
|
|
1678
|
+
}
|
|
1560
1679
|
self.loadMediaWithResolvedUrl(resolved, track: track)
|
|
1561
1680
|
}
|
|
1562
1681
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_native","require","_NativeUpdatedValue","_useNativeUpdatedValue","isCallback","source","isTransformableRequestConfig","obj","isRouteConfig","flattenBrowseSource","browseCallback","browseConfig","browseStatic","searchSourceToRouteEntry","path","SEARCH_ROUTE_PATH","searchCallback","searchConfig","wrapTracksAsResolvedTrack","tracks","url","TABS_ROUTE_PATH","title","children","tabsSourceToRouteEntry","Array","isArray","wrappedCallback","flattenRouteEntry","browse","media","artwork","DEFAULT_ROUTE_PATH","flattenRoutes","routes","rootBrowse","tabs","search","entries","Object","push","length","undefined","toNativeConfig","config","request","singleTrack","androidControllerOfflineError","carPlayUpNextButton","carPlayNowPlayingButtons","formatNavigationError","configureBrowser","configuration","nativeBrowser","navigate","pathOrTrack","navigatePath","navigateTrack","getPath","onPathChanged","exports","NativeUpdatedValue","emitterize","cb","usePath","useNativeUpdatedValue","getContent","onContentChanged","useContent","getTabs","onTabsChanged","useTabs","notifyContentChanged","hasSearch","searchRoute","find","r","query","onSearch"],"sourceRoot":"../../../src","sources":["features/browser.ts"],"mappings":";;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"names":["_native","require","_NativeUpdatedValue","_useNativeUpdatedValue","isCallback","source","isTransformableRequestConfig","obj","isRouteConfig","flattenBrowseSource","browseCallback","browseConfig","browseStatic","searchSourceToRouteEntry","path","SEARCH_ROUTE_PATH","searchCallback","searchConfig","wrapTracksAsResolvedTrack","tracks","url","TABS_ROUTE_PATH","title","children","tabsSourceToRouteEntry","Array","isArray","wrappedCallback","flattenRouteEntry","browse","media","artwork","DEFAULT_ROUTE_PATH","flattenRoutes","routes","rootBrowse","tabs","search","entries","Object","push","length","undefined","toNativeConfig","config","request","singleTrack","androidControllerOfflineError","carPlayUpNextButton","carPlayNowPlayingButtons","formatNavigationError","configureBrowser","configuration","nativeBrowser","navigate","pathOrTrack","navigatePath","navigateTrack","getPath","onPathChanged","exports","NativeUpdatedValue","emitterize","cb","usePath","useNativeUpdatedValue","getContent","onContentChanged","useContent","getTabs","onTabsChanged","useTabs","notifyContentChanged","hasSearch","searchRoute","find","r","query","onSearch"],"sourceRoot":"../../../src","sources":["features/browser.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_native","require","_NativeUpdatedValue","_useNativeUpdatedValue","getEqualizerSettings","nativeBrowser","setEqualizerEnabled","enabled","setEqualizerPreset","preset","setEqualizerLevels","levels","onEqualizerChanged","exports","NativeUpdatedValue","emitterize","cb","useEqualizerSettings","useNativeUpdatedValue"],"sourceRoot":"../../../src","sources":["features/equalizer.ts"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"names":["_native","require","_NativeUpdatedValue","_useNativeUpdatedValue","getEqualizerSettings","nativeBrowser","setEqualizerEnabled","enabled","setEqualizerPreset","preset","setEqualizerLevels","levels","onEqualizerChanged","exports","NativeUpdatedValue","emitterize","cb","useEqualizerSettings","useNativeUpdatedValue"],"sourceRoot":"../../../src","sources":["features/equalizer.ts"],"mappings":";;;;;;;;;;;AACA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,mBAAA,GAAAD,OAAA;AACA,IAAAE,sBAAA,GAAAF,OAAA;AAIA;;AAEA;AACA;AACA;AACA;AACO,SAASG,oBAAoBA,CAAA,EAAkC;EACpE,OAAOC,qBAAa,CAACD,oBAAoB,CAAC,CAAC;AAC7C;;AAEA;;AAEA;AACA;AACA;AACA;AACO,SAASE,mBAAmBA,CAACC,OAAgB,EAAQ;EAC1DF,qBAAa,CAACC,mBAAmB,CAACC,OAAO,CAAC;AAC5C;;AAEA;AACA;AACA;AACA;AACO,SAASC,kBAAkBA,CAACC,MAAc,EAAQ;EACvDJ,qBAAa,CAACG,kBAAkB,CAACC,MAAM,CAAC;AAC1C;;AAEA;AACA;AACA;AACA;AACO,SAASC,kBAAkBA,CAACC,MAAgB,EAAQ;EACzDN,qBAAa,CAACK,kBAAkB,CAACC,MAAM,CAAC;AAC1C;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACO,MAAMC,kBAAkB,GAAAC,OAAA,CAAAD,kBAAA,GAC7BE,sCAAkB,CAACC,UAAU,CAC1BC,EAAE,IAAMX,qBAAa,CAACO,kBAAkB,GAAGI,EAC9C,CAAC;;AAEH;;AAEA;AACA;AACA;AACA;AACO,SAASC,oBAAoBA,CAAA,EAAkC;EACpE,OAAO,IAAAC,4CAAqB,EAACd,oBAAoB,EAAEQ,kBAAkB,CAAC;AACxE","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_native","require","_NativeUpdatedValue","_useNativeUpdatedValue","getPlaybackError","nativeBrowser","onPlaybackError","exports","NativeUpdatedValue","emitterize","cb","usePlaybackError","useNativeUpdatedValue","getNavigationError","error","undefined","onNavigationError","useNavigationError","getFormattedNavigationError","onFormattedNavigationError","useFormattedNavigationError"],"sourceRoot":"../../../src","sources":["features/errors.ts"],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"names":["_native","require","_NativeUpdatedValue","_useNativeUpdatedValue","getPlaybackError","nativeBrowser","onPlaybackError","exports","NativeUpdatedValue","emitterize","cb","usePlaybackError","useNativeUpdatedValue","getNavigationError","error","undefined","onNavigationError","useNavigationError","getFormattedNavigationError","onFormattedNavigationError","useFormattedNavigationError"],"sourceRoot":"../../../src","sources":["features/errors.ts"],"mappings":";;;;;;;;;;;;AACA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,mBAAA,GAAAD,OAAA;AACA,IAAAE,sBAAA,GAAAF,OAAA;AAEA;;AAQA;AACA;AACA;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAiBA;AACA;AACA;;AAKA;;AAEA;AACA;AACA;AACA;AACO,SAASG,gBAAgBA,CAAA,EAA8B;EAC5D,OAAOC,qBAAa,CAACD,gBAAgB,CAAC,CAAC;AACzC;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACO,MAAME,eAAe,GAAAC,OAAA,CAAAD,eAAA,GAC1BE,sCAAkB,CAACC,UAAU,CAC1BC,EAAE,IAAML,qBAAa,CAACC,eAAe,GAAGI,EAC3C,CAAC;;AAEH;;AAEA;AACA;AACA;AACA;AACO,SAASC,gBAAgBA,CAAA,EAA8B;EAC5D,OAAO,IAAAC,4CAAqB,EAACR,gBAAgB,EAAEE,eAAe,EAAE,OAAO,CAAC;AAC1E;;AAEA;;AAEA;AACA;AACA;AACA;AACO,SAASO,kBAAkBA,CAAA,EAAgC;EAChE,MAAMC,KAAK,GAAGT,qBAAa,CAACQ,kBAAkB,CAAC,CAAC;EAChD,OAAOC,KAAK,IAAIC,SAAS;AAC3B;;AAEA;AACA;AACA;AACA;AACA;AACO,MAAMC,iBAAiB,GAAAT,OAAA,CAAAS,iBAAA,GAC5BR,sCAAkB,CAACC,UAAU,CAC1BC,EAAE,IAAML,qBAAa,CAACW,iBAAiB,GAAGN,EAC7C,CAAC;;AAEH;AACA;AACA;AACA;AACO,SAASO,kBAAkBA,CAAA,EAAgC;EAChE,OAAO,IAAAL,4CAAqB,EAACC,kBAAkB,EAAEG,iBAAiB,EAAE,OAAO,CAAC;AAC9E;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASE,2BAA2BA,CAAA,EAE7B;EACZ,OAAOb,qBAAa,CAACa,2BAA2B,CAAC,CAAC,IAAIH,SAAS;AACjE;;AAEA;AACA;AACA;AACA;AACA;AACO,MAAMI,0BAA0B,GAAAZ,OAAA,CAAAY,0BAAA,GAAGX,sCAAkB,CAACC,UAAU,CAEpEC,EAAE,IAAML,qBAAa,CAACc,0BAA0B,GAAGT,EAAG,CAAC;;AAE1D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASU,2BAA2BA,CAAA,EAE7B;EACZ,OAAO,IAAAR,4CAAqB,EAC1BM,2BAA2B,EAC3BC,0BACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_native","require","_NativeUpdatedValue","setActiveTrackFavorited","favorited","nativeBrowser","toggleActiveTrackFavorited","setFavorites","favorites","onFavoriteChanged","exports","NativeUpdatedValue","emitterize","cb"],"sourceRoot":"../../../src","sources":["features/favorites.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"names":["_native","require","_NativeUpdatedValue","setActiveTrackFavorited","favorited","nativeBrowser","toggleActiveTrackFavorited","setFavorites","favorites","onFavoriteChanged","exports","NativeUpdatedValue","emitterize","cb"],"sourceRoot":"../../../src","sources":["features/favorites.ts"],"mappings":";;;;;;;;;AACA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,mBAAA,GAAAD,OAAA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASE,uBAAuBA,CAACC,SAAkB,EAAQ;EAChEC,qBAAa,CAACF,uBAAuB,CAACC,SAAS,CAAC;AAClD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASE,0BAA0BA,CAAA,EAAS;EACjDD,qBAAa,CAACC,0BAA0B,CAAC,CAAC;AAC5C;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,YAAYA,CAACC,SAAmB,EAAQ;EACtDH,qBAAa,CAACE,YAAY,CAACC,SAAS,CAAC;AACvC;;AAEA;;AAEA;AACA;AACA;AACA;;AAQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,iBAAiB,GAAAC,OAAA,CAAAD,iBAAA,GAC5BE,sCAAkB,CAACC,UAAU,CAC1BC,EAAE,IAAMR,qBAAa,CAACI,iBAAiB,GAAGI,EAC7C,CAAC","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_native","require","_NativeUpdatedValue","onChapterMetadata","exports","NativeUpdatedValue","emitterize","cb","nativeBrowser","onTrackMetadata","onTimedMetadata"],"sourceRoot":"../../../src","sources":["features/metadata.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"names":["_native","require","_NativeUpdatedValue","onChapterMetadata","exports","NativeUpdatedValue","emitterize","cb","nativeBrowser","onTrackMetadata","onTimedMetadata"],"sourceRoot":"../../../src","sources":["features/metadata.ts"],"mappings":";;;;;;AACA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,mBAAA,GAAAD,OAAA;AAEA;;AAoCA;AACA;AACA;AACA;AACA;;AAQA;AACA;AACA;AACA;;AASA;AACA;AACA;AACA;;AAoBA;AACA;AACA;AACA;;AAYA;;AAEA;AACA;AACA;AACA;AACA;AACO,MAAME,iBAAiB,GAAAC,OAAA,CAAAD,iBAAA,GAAGE,sCAAkB,CAACC,UAAU,CAE3DC,EAAE,IAAMC,qBAAa,CAACL,iBAAiB,GAAGI,EAAG,CAAC;;AAEjD;AACA;AACA;AACA;AACA;AACO,MAAME,eAAe,GAAAL,OAAA,CAAAK,eAAA,GAAGJ,sCAAkB,CAACC,UAAU,CACzDC,EAAE,IAAMC,qBAAa,CAACC,eAAe,GAAGF,EAC3C,CAAC;;AAED;AACA;AACA;AACA;AACA;AACO,MAAMG,eAAe,GAAAN,OAAA,CAAAM,eAAA,GAAGL,sCAAkB,CAACC,UAAU,CACzDC,EAAE,IAAMC,qBAAa,CAACE,eAAe,GAAGH,EAC3C,CAAC","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_native","require","_NativeUpdatedValue","_useNativeUpdatedValue","updateNowPlaying","update","nativeBrowser","undefined","getNowPlaying","onNowPlayingChanged","exports","NativeUpdatedValue","emitterize","cb","useNowPlaying","useNativeUpdatedValue"],"sourceRoot":"../../../src","sources":["features/nowPlaying.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"names":["_native","require","_NativeUpdatedValue","_useNativeUpdatedValue","updateNowPlaying","update","nativeBrowser","undefined","getNowPlaying","onNowPlayingChanged","exports","NativeUpdatedValue","emitterize","cb","useNowPlaying","useNativeUpdatedValue"],"sourceRoot":"../../../src","sources":["features/nowPlaying.ts"],"mappings":";;;;;;;;;AACA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,mBAAA,GAAAD,OAAA;AACA,IAAAE,sBAAA,GAAAF,OAAA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,gBAAgBA,CAACC,MAA+B,EAAQ;EACtEC,qBAAa,CAACF,gBAAgB,CAACC,MAAM,IAAIE,SAAS,CAAC;AACrD;;AAEA;AACA;AACA;AACO,SAASC,aAAaA,CAAA,EAAmC;EAC9D,OAAOF,qBAAa,CAACE,aAAa,CAAC,CAAC,IAAID,SAAS;AACnD;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,mBAAmB,GAAAC,OAAA,CAAAD,mBAAA,GAC9BE,sCAAkB,CAACC,UAAU,CAC1BC,EAAE,IAAMP,qBAAa,CAACG,mBAAmB,GAAGI,EAC/C,CAAC;;AAEH;;AAEA;AACA;AACA;AACA;AACO,SAASC,aAAaA,CAAA,EAAmC;EAC9D,OAAO,IAAAC,4CAAqB,EAACP,aAAa,EAAEC,mBAAmB,CAAC;AAClE","ignoreList":[]}
|