react-native-audio-browser 0.2.0-next.29 → 0.2.0-next.30
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/ios/AVPlayerEngine.swift +0 -4
- package/ios/Player/MediaLoader.swift +6 -16
- package/ios/Player/Player.swift +5 -10
- package/ios/Player/PlayerEngine.swift +0 -2
- package/ios/Tests/MediaLoaderTests.swift +0 -15
- package/ios/Tests/PlayerPolicyTests.swift +15 -0
- package/ios/Tests/PlayerTests.swift +3 -1
- package/ios/Tests/Support/TestTypes.swift +0 -2
- package/package.json +1 -1
package/ios/AVPlayerEngine.swift
CHANGED
|
@@ -43,8 +43,6 @@ final class MediaLoader {
|
|
|
43
43
|
private(set) var mediaResolverTask: Task<Void, Never>?
|
|
44
44
|
private var metadataLoadTask: Task<Void, Never>?
|
|
45
45
|
private var playableLoadTask: Task<Void, Never>?
|
|
46
|
-
private var url: URL?
|
|
47
|
-
private var urlOptions: [String: Any]?
|
|
48
46
|
|
|
49
47
|
// MARK: - Public API
|
|
50
48
|
|
|
@@ -98,14 +96,8 @@ final class MediaLoader {
|
|
|
98
96
|
}
|
|
99
97
|
}
|
|
100
98
|
|
|
101
|
-
func loadAsset() {
|
|
102
|
-
|
|
103
|
-
// Callers (reload-from-terminal) have already transitioned to .loading;
|
|
104
|
-
// a silent return would strand that state with no load in flight.
|
|
105
|
-
onEvent?(.invalidSource("nil"))
|
|
106
|
-
return
|
|
107
|
-
}
|
|
108
|
-
let pendingAsset = AVURLAsset(url: url, options: urlOptions)
|
|
99
|
+
private func loadAsset(url: URL, options: [String: Any]?) {
|
|
100
|
+
let pendingAsset = AVURLAsset(url: url, options: options)
|
|
109
101
|
asset = pendingAsset
|
|
110
102
|
|
|
111
103
|
// Separate from playable loading to allow playback to start faster
|
|
@@ -205,8 +197,6 @@ final class MediaLoader {
|
|
|
205
197
|
playableLoadTask = nil
|
|
206
198
|
}
|
|
207
199
|
|
|
208
|
-
/// Preserves `url` and `urlOptions` so that `loadAsset()` can recreate the asset
|
|
209
|
-
/// (used by `reload()` → `loadAVPlayer()` without a preceding `resolveAndLoad`).
|
|
210
200
|
func clearAsset() {
|
|
211
201
|
guard let currentAsset = asset else { return }
|
|
212
202
|
|
|
@@ -243,12 +233,12 @@ final class MediaLoader {
|
|
|
243
233
|
}
|
|
244
234
|
|
|
245
235
|
let isLocalFile = mediaUrl.isFileURL
|
|
246
|
-
url = isLocalFile ? URL(fileURLWithPath: mediaUrl.path) : mediaUrl
|
|
247
|
-
urlOptions = Self.buildAssetOptions(headers: resolved.headers, userAgent: resolved.userAgent)
|
|
248
|
-
|
|
249
236
|
logger.debug(" final playbackUrl: \(mediaUrl.absoluteString)")
|
|
250
237
|
logger.debug(" isLocalFile: \(isLocalFile)")
|
|
251
238
|
|
|
252
|
-
loadAsset(
|
|
239
|
+
loadAsset(
|
|
240
|
+
url: isLocalFile ? URL(fileURLWithPath: mediaUrl.path) : mediaUrl,
|
|
241
|
+
options: Self.buildAssetOptions(headers: resolved.headers, userAgent: resolved.userAgent),
|
|
242
|
+
)
|
|
253
243
|
}
|
|
254
244
|
}
|
package/ios/Player/Player.swift
CHANGED
|
@@ -848,18 +848,13 @@ final class Player {
|
|
|
848
848
|
|
|
849
849
|
// MARK: - Reloading
|
|
850
850
|
|
|
851
|
-
/// Loads the current track again from its resolved URL.
|
|
852
|
-
func reload(startFromCurrentTime: Bool) {
|
|
853
|
-
prepareForReload(resumingAt: startFromCurrentTime ? resumePosition : nil)
|
|
854
|
-
engine.loadResolved()
|
|
855
|
-
}
|
|
856
|
-
|
|
857
851
|
/// Loads the current track again, re-running the media-URL resolver, so a consumer whose
|
|
858
|
-
/// resolver mints short-lived URLs or tokens recovers once they expire.
|
|
859
|
-
///
|
|
852
|
+
/// resolver mints short-lived URLs or tokens recovers once they expire. A track without a `src`
|
|
853
|
+
/// fails as an invalid source; with no current track, nothing loads.
|
|
860
854
|
func reloadResolving(startFromCurrentTime: Bool) {
|
|
861
|
-
guard let track = currentTrack
|
|
862
|
-
|
|
855
|
+
guard let track = currentTrack else { return }
|
|
856
|
+
guard let src = track.src else {
|
|
857
|
+
transition(.errorOccurred(.invalidSourceUrl("nil")))
|
|
863
858
|
return
|
|
864
859
|
}
|
|
865
860
|
prepareForReload(resumingAt: startFromCurrentTime ? resumePosition : nil)
|
|
@@ -54,8 +54,6 @@ enum EngineEvent {
|
|
|
54
54
|
/// Runs the media-URL resolver for `track`, so a short-lived URL is minted afresh, and loads
|
|
55
55
|
/// the result.
|
|
56
56
|
func resolveAndLoad(src: String, track: Track)
|
|
57
|
-
/// Loads the last resolved URL again.
|
|
58
|
-
func loadResolved()
|
|
59
57
|
func cancelLoading()
|
|
60
58
|
/// Drops the asset and stops observing the item, leaving it in place for the reload.
|
|
61
59
|
func dropAsset()
|
|
@@ -47,21 +47,6 @@ struct MediaLoaderInitialStateTests {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
// MARK: - loadAsset
|
|
51
|
-
|
|
52
|
-
@Suite("loadAsset")
|
|
53
|
-
@MainActor
|
|
54
|
-
struct LoadAssetTests {
|
|
55
|
-
/// A silent return here stranded the player: reload-from-terminal has
|
|
56
|
-
/// already transitioned to .loading, so no-oping leaves a spinner forever
|
|
57
|
-
/// with no load in flight and no error.
|
|
58
|
-
@Test func withoutResolvedUrl_reportsError() {
|
|
59
|
-
let (loader, spy) = makeLoader()
|
|
60
|
-
loader.loadAsset()
|
|
61
|
-
#expect(spy.playbackErrors.count == 1)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
50
|
// MARK: - resolveAndLoad
|
|
66
51
|
|
|
67
52
|
@Suite("resolveAndLoad")
|
|
@@ -329,6 +329,21 @@ struct PlayerReviewRegressionTests {
|
|
|
329
329
|
#expect(player.deferredSeek.target == 30)
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
/// Play on a track without a source fails as an invalid source; it never reloads the media
|
|
333
|
+
/// loader's last URL, which belongs to an earlier track.
|
|
334
|
+
@Test func playOnATrackWithoutASourceFailsInsteadOfReloading() {
|
|
335
|
+
let (player, engine, _) = makePlaying()
|
|
336
|
+
player.setQueue([Track(id: "t2", src: nil, title: "No source")])
|
|
337
|
+
#expect(player.state == .error)
|
|
338
|
+
let resolves = engine.resolveAndLoadCalls.count
|
|
339
|
+
|
|
340
|
+
player.play()
|
|
341
|
+
|
|
342
|
+
#expect(player.state == .error)
|
|
343
|
+
#expect(player.nitroPlaybackError?.code == TrackPlayerError.PlaybackError.invalidSourceUrl("nil").code)
|
|
344
|
+
#expect(engine.resolveAndLoadCalls.count == resolves)
|
|
345
|
+
}
|
|
346
|
+
|
|
332
347
|
/// While nothing plays, a transition alone rewrites the snapshot.
|
|
333
348
|
@Test func anErrorWhilePausedSavesWhereItStopped() {
|
|
334
349
|
UserDefaults(suiteName: "PlayerTests")!.removePersistentDomain(forName: "PlayerTests")
|
|
@@ -426,13 +426,15 @@ struct PlayWhenReadyTests {
|
|
|
426
426
|
@Test @MainActor
|
|
427
427
|
func true_fromStopped_triggersReload() {
|
|
428
428
|
let (c, eh, _, _) = makeCoordinator()
|
|
429
|
+
loadTrack(c)
|
|
429
430
|
c.transition(.stopped)
|
|
430
431
|
eh.resumePosition = 42
|
|
432
|
+
let resolvesBefore = eh.resolveAndLoadCalls.count
|
|
431
433
|
|
|
432
434
|
c.setPlayWhenReady(true)
|
|
433
435
|
|
|
434
436
|
#expect(eh.recreateCount == 0)
|
|
435
|
-
#expect(eh.
|
|
437
|
+
#expect(eh.resolveAndLoadCalls.count == resolvesBefore + 1)
|
|
436
438
|
#expect(c.deferredSeek.target == nil) // a stop restarts from the start
|
|
437
439
|
}
|
|
438
440
|
|
|
@@ -50,7 +50,6 @@ final class MockPlayerEngine: PlayerEngine {
|
|
|
50
50
|
|
|
51
51
|
// Loading
|
|
52
52
|
var resolveAndLoadCalls: [String] = []
|
|
53
|
-
var loadResolvedCount = 0
|
|
54
53
|
var cancelLoadingCount = 0
|
|
55
54
|
var dropAssetCount = 0
|
|
56
55
|
var clearItemCount = 0
|
|
@@ -58,7 +57,6 @@ final class MockPlayerEngine: PlayerEngine {
|
|
|
58
57
|
var recreateCount = 0
|
|
59
58
|
|
|
60
59
|
func resolveAndLoad(src: String, track _: Track) { resolveAndLoadCalls.append(src) }
|
|
61
|
-
func loadResolved() { loadResolvedCount += 1 }
|
|
62
60
|
func cancelLoading() { cancelLoadingCount += 1 }
|
|
63
61
|
func dropAsset() { dropAssetCount += 1 }
|
|
64
62
|
func clearItem() { clearItemCount += 1 }
|