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.
@@ -144,10 +144,6 @@ final class AVPlayerEngine: PlayerEngine {
144
144
  mediaLoader.resolveAndLoad(src: src, track: track)
145
145
  }
146
146
 
147
- func loadResolved() {
148
- mediaLoader.loadAsset()
149
- }
150
-
151
147
  func cancelLoading() {
152
148
  mediaLoader.cancelAll()
153
149
  }
@@ -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
- guard let url else {
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
  }
@@ -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. Falls back to
859
- /// `reload` when the track has no `src` to resolve.
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, let src = track.src else {
862
- reload(startFromCurrentTime: startFromCurrentTime)
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.loadResolvedCount == 1)
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 }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-browser",
3
- "version": "0.2.0-next.29",
3
+ "version": "0.2.0-next.30",
4
4
  "description": "react-native-audio-browser is a react native package built with Nitro",
5
5
  "keywords": [
6
6
  "react-native",