react-native-audio-browser 0.2.0-next.28 → 0.2.0-next.29
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/browser/BrowserUrlResolution.kt +3 -4
- package/android/src/test/java/com/audiobrowser/browser/BrowserUrlResolutionTest.kt +2 -2
- package/ios/AVPlayerEngine.swift +334 -0
- package/ios/AVPlayerEngineCallbacks.swift +15 -0
- package/ios/Browser/BrowseGate.swift +58 -0
- package/ios/Browser/BrowserConfig.swift +31 -89
- package/ios/Browser/BrowserManager.swift +68 -487
- package/ios/Browser/NavigationErrorState.swift +41 -0
- package/ios/Browser/PageResolver.swift +227 -0
- package/ios/Browser/RequestLayer.swift +90 -0
- package/ios/Browser/{BrowserManager+URLResolution.swift → RequestPipeline.swift} +175 -107
- package/ios/CarPlay/CPInterfaceController+Safe.swift +11 -24
- package/ios/CarPlay/CarPlayArtworkResolver.swift +35 -83
- package/ios/CarPlay/CarPlayController.swift +42 -1442
- package/ios/CarPlay/CarPlayImageLoader.swift +42 -83
- package/ios/CarPlay/CarPlayListItemFactory.swift +9 -23
- package/ios/CarPlay/CarPlayNowPlayingManager.swift +119 -129
- package/ios/CarPlay/CarPlaySession+Navigator.swift +114 -0
- package/ios/CarPlay/CarPlaySession+PageLoader.swift +92 -0
- package/ios/CarPlay/CarPlaySession+Refresher.swift +43 -0
- package/ios/CarPlay/CarPlaySession+TabBar.swift +193 -0
- package/ios/CarPlay/CarPlaySession.swift +209 -0
- package/ios/CarPlay/CarPlayTemplates.swift +63 -0
- package/ios/CarPlay/HybridAudioBrowser+IntentFavorite.swift +20 -0
- package/ios/CarPlay/MediaIntentPlayback.swift +74 -0
- package/ios/CarPlay/RNABMediaAddHandler.swift +2 -18
- package/ios/CarPlay/RNABMediaAffinityHandler.swift +5 -21
- package/ios/CarPlay/RNABMediaIntentHandler.swift +2 -3
- package/ios/Extension/NavigationError+Conversion.swift +7 -4
- package/ios/Extension/UIImage+Adaptive.swift +12 -0
- package/ios/Extension/URLError+Connectivity.swift +15 -0
- package/ios/Http/HttpClient.swift +63 -145
- package/ios/HybridAudioBrowser+Browser.swift +153 -0
- package/ios/HybridAudioBrowser+Options.swift +144 -0
- package/ios/HybridAudioBrowser+Player.swift +397 -0
- package/ios/HybridAudioBrowser+PlayerCallbacks.swift +190 -0
- package/ios/HybridAudioBrowser+Stubs.swift +46 -0
- package/ios/HybridAudioBrowser.swift +74 -1687
- package/ios/Model/NitroTypeStubs.swift +18 -1
- package/ios/Model/PlayerUpdateOptions.swift +1 -1
- package/ios/NowPlayingInfo/NowPlayingInfoController.swift +18 -53
- package/ios/Observer/ItemObserver.swift +90 -0
- package/ios/Observer/PlayerObserver.swift +59 -0
- package/ios/Option/SessionCategories.swift +41 -92
- package/ios/Player/AudioSessionController.swift +177 -0
- package/ios/Player/DeferredSeek.swift +70 -0
- package/ios/Player/ErrorStatus.swift +27 -0
- package/ios/Player/MediaLoader.swift +24 -11
- package/ios/Player/NowPlayingUpdater.swift +157 -58
- package/ios/Player/PlaybackFailure.swift +125 -0
- package/ios/Player/PlaybackPersistence.swift +73 -0
- package/ios/Player/Player.swift +1083 -0
- package/ios/Player/{PlaybackCoordinatorCallbacks.swift → PlayerCallbacks.swift} +3 -4
- package/ios/Player/PlayerEngine.swift +89 -0
- package/ios/Player/PlayerStatusTypes.swift +2 -6
- package/ios/Player/QueueManager.swift +52 -59
- package/ios/Player/RetryLoop.swift +261 -0
- package/ios/Player/SleepTimerManager.swift +1 -1
- package/ios/RemoteCommand/RemoteCommandCallbacks.swift +2 -3
- package/ios/RemoteCommand/RemoteCommandController.swift +85 -367
- package/ios/Tests/CarPlayArtworkResolverTests.swift +60 -356
- package/ios/Tests/DeferredSeekTests.swift +70 -0
- package/ios/Tests/EmitterTests.swift +29 -0
- package/ios/Tests/GenerationMemoTests.swift +121 -0
- package/ios/Tests/LRUCacheTests.swift +4 -5
- package/ios/Tests/MediaLoaderTests.swift +12 -33
- package/ios/Tests/ObserverTests.swift +88 -0
- package/ios/Tests/PlaybackErrorTests.swift +76 -0
- package/ios/Tests/PlaybackFailureTests.swift +189 -0
- package/ios/Tests/PlaybackPersistenceTests.swift +93 -0
- package/ios/Tests/PlayerPolicyTests.swift +405 -0
- package/ios/Tests/{PlaybackCoordinatorTests.swift → PlayerTests.swift} +298 -248
- package/ios/Tests/PlayingStateTests.swift +42 -0
- package/ios/Tests/QueueManagerTests.swift +76 -107
- package/ios/Tests/ReadyValueTests.swift +73 -0
- package/ios/Tests/RemoteCommandControllerTests.swift +10 -30
- package/ios/Tests/RemoteCommandTests.swift +2 -4
- package/ios/Tests/RequestLayerTests.swift +112 -0
- package/ios/Tests/RetryLoopTests.swift +270 -0
- package/ios/Tests/Support/TestTypes.swift +111 -112
- package/ios/Tests/TrackIdTemplateTests.swift +1 -1
- package/ios/Tests/URLErrorConnectivityTests.swift +20 -0
- package/ios/TrackSelector.swift +1 -1
- package/ios/Util/ArtworkImageFetcher.swift +42 -0
- package/ios/Util/Emitter.swift +42 -0
- package/ios/Util/GenerationMemo.swift +36 -0
- package/ios/Util/LRUCache.swift +23 -162
- package/ios/Util/NetworkMonitor.swift +1 -1
- package/ios/Util/ReadyValue.swift +56 -0
- package/ios/Util/SFSymbolRenderer.swift +35 -29
- package/ios/Util/SVGProcessor.swift +0 -10
- package/lib/commonjs/web/NativeAudioBrowser.js +1 -1
- package/lib/commonjs/web/TrackPlayer/PlayingStateFactory.js +1 -1
- package/lib/module/web/NativeAudioBrowser.js +1 -1
- package/lib/module/web/TrackPlayer/PlayingStateFactory.js +1 -1
- package/lib/typescript/src/types/browser.d.ts +3 -3
- package/package.json +1 -1
- package/src/types/browser.ts +3 -3
- package/src/web/NativeAudioBrowser.ts +1 -1
- package/src/web/TrackPlayer/PlayingStateFactory.test.ts +1 -1
- package/src/web/TrackPlayer/PlayingStateFactory.ts +1 -1
- package/ios/Bridge.h +0 -8
- package/ios/Browser/MediaResolveComposer.swift +0 -114
- package/ios/NowPlayingInfo/MediaItemProperty.swift +0 -78
- package/ios/NowPlayingInfo/NowPlayingInfoKeyValue.swift +0 -6
- package/ios/NowPlayingInfo/NowPlayingInfoProperty.swift +0 -220
- package/ios/Observer/PlayerItemNotificationObserver.swift +0 -119
- package/ios/Observer/PlayerItemPropertyObserver.swift +0 -133
- package/ios/Observer/PlayerStateObserver.swift +0 -50
- package/ios/Observer/PlayerTimeObserver.swift +0 -55
- package/ios/Option/PitchAlgorithms.swift +0 -17
- package/ios/Option/TimeEventFrequency.swift +0 -18
- package/ios/Player/LoadSeekCoordinator.swift +0 -99
- package/ios/Player/MediaLoaderDelegate.swift +0 -11
- package/ios/Player/PlaybackCoordinator.swift +0 -761
- package/ios/Player/PlaybackEffectHandler.swift +0 -54
- package/ios/Player/PlaybackErrorHandler.swift +0 -175
- package/ios/Player/PlaybackStateStore.swift +0 -27
- package/ios/Player/PlayingStateManager.swift +0 -33
- package/ios/Player/RetryManager.swift +0 -384
- package/ios/Player/SeekCompletionHandler.swift +0 -5
- package/ios/Player/SleepTimerHandling.swift +0 -28
- package/ios/Support/RNTrackPlayer-Bridging-Header.h +0 -7
- package/ios/Tests/LoadSeekCoordinatorTests.swift +0 -327
- package/ios/Tests/MediaResolveTests.swift +0 -126
- package/ios/Tests/OnceValueTests.swift +0 -79
- package/ios/Tests/PlaybackErrorHandlerTests.swift +0 -549
- package/ios/Tests/PlaybackStateStoreTests.swift +0 -49
- package/ios/Tests/PlayerTimeObserverTests.swift +0 -37
- package/ios/Tests/PlayingStateManagerTests.swift +0 -57
- package/ios/Tests/RetryManagerTests.swift +0 -178
- package/ios/TrackPlayer.swift +0 -1042
- package/ios/TrackPlayerCallbacks.swift +0 -53
- package/ios/Util/MetadataAdapter.swift +0 -131
- package/ios/Util/OnceValue.swift +0 -104
- /package/ios/{Player → CarPlay}/MediaIntentCriteria.swift +0 -0
- /package/ios/{Browser → CarPlay}/SectionPresentation.swift +0 -0
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import AVFoundation
|
|
2
|
-
import Foundation
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
Observes player state changes and invokes callbacks passed at initialization.
|
|
6
|
-
Uses modern block-based KVO for type-safe observation with automatic cleanup.
|
|
7
|
-
*/
|
|
8
|
-
@MainActor final class PlayerStateObserver {
|
|
9
|
-
private var observations: [NSKeyValueObservation] = []
|
|
10
|
-
|
|
11
|
-
weak var avPlayer: AVPlayer? {
|
|
12
|
-
willSet {
|
|
13
|
-
stopObserving()
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
private let onStatusChange: @MainActor (AVPlayer.Status) -> Void
|
|
18
|
-
private let onTimeControlStatusChange: @MainActor (AVPlayer.TimeControlStatus) -> Void
|
|
19
|
-
|
|
20
|
-
init(
|
|
21
|
-
onStatusChange: @escaping @MainActor (AVPlayer.Status) -> Void,
|
|
22
|
-
onTimeControlStatusChange: @escaping @MainActor (AVPlayer.TimeControlStatus) -> Void,
|
|
23
|
-
) {
|
|
24
|
-
self.onStatusChange = onStatusChange
|
|
25
|
-
self.onTimeControlStatusChange = onTimeControlStatusChange
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
Start receiving events from this observer.
|
|
30
|
-
*/
|
|
31
|
-
func startObserving() {
|
|
32
|
-
guard let avPlayer else { return }
|
|
33
|
-
stopObserving()
|
|
34
|
-
|
|
35
|
-
observations = [
|
|
36
|
-
avPlayer.observe(\.status, options: [.new, .initial]) { [weak self] player, _ in
|
|
37
|
-
let status = player.status
|
|
38
|
-
Task { @MainActor in self?.onStatusChange(status) }
|
|
39
|
-
},
|
|
40
|
-
avPlayer.observe(\.timeControlStatus, options: [.new]) { [weak self] player, _ in
|
|
41
|
-
let status = player.timeControlStatus
|
|
42
|
-
Task { @MainActor in self?.onTimeControlStatusChange(status) }
|
|
43
|
-
},
|
|
44
|
-
]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
func stopObserving() {
|
|
48
|
-
observations.removeAll()
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import AVFoundation
|
|
2
|
-
import Foundation
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
Observes the player's audio-start boundary and invokes the callback passed at initialization.
|
|
6
|
-
*/
|
|
7
|
-
@MainActor class PlayerTimeObserver {
|
|
8
|
-
/// The time to use as start boundary time. Cannot be zero.
|
|
9
|
-
private static let startBoundaryTime: CMTime = .init(value: 1, timescale: 1000)
|
|
10
|
-
|
|
11
|
-
var boundaryTimeStartObserverToken: Any?
|
|
12
|
-
|
|
13
|
-
weak var avPlayer: AVPlayer? {
|
|
14
|
-
willSet {
|
|
15
|
-
unregisterForBoundaryTimeEvents()
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
private let onAudioDidStart: @MainActor () -> Void
|
|
20
|
-
|
|
21
|
-
init(onAudioDidStart: @escaping @MainActor () -> Void) {
|
|
22
|
-
self.onAudioDidStart = onAudioDidStart
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
Will register for the AVPlayer BoundaryTimeEvents, to trigger the audio-start event.
|
|
27
|
-
*/
|
|
28
|
-
func registerForBoundaryTimeEvents() {
|
|
29
|
-
guard let avPlayer else {
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
unregisterForBoundaryTimeEvents()
|
|
33
|
-
boundaryTimeStartObserverToken = avPlayer.addBoundaryTimeObserver(
|
|
34
|
-
forTimes: [PlayerTimeObserver.startBoundaryTime].map {
|
|
35
|
-
NSValue(time: $0)
|
|
36
|
-
},
|
|
37
|
-
queue: nil,
|
|
38
|
-
using: { [weak self] in
|
|
39
|
-
Task { @MainActor in self?.onAudioDidStart() }
|
|
40
|
-
},
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
Unregister from the boundary events of the player.
|
|
46
|
-
*/
|
|
47
|
-
func unregisterForBoundaryTimeEvents() {
|
|
48
|
-
guard
|
|
49
|
-
let avPlayer,
|
|
50
|
-
let boundaryTimeStartObserverToken
|
|
51
|
-
else { return }
|
|
52
|
-
avPlayer.removeTimeObserver(boundaryTimeStartObserverToken)
|
|
53
|
-
self.boundaryTimeStartObserverToken = nil
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import AVFoundation
|
|
2
|
-
import Foundation
|
|
3
|
-
|
|
4
|
-
public enum PitchAlgorithm: String {
|
|
5
|
-
case linear
|
|
6
|
-
case music
|
|
7
|
-
case voice
|
|
8
|
-
|
|
9
|
-
/// The corresponding AVAudioTimePitchAlgorithm for use with AVPlayer
|
|
10
|
-
public var avAlgorithm: AVAudioTimePitchAlgorithm {
|
|
11
|
-
switch self {
|
|
12
|
-
case .linear: .varispeed
|
|
13
|
-
case .music: .spectral
|
|
14
|
-
case .voice: .timeDomain
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import AVFoundation
|
|
2
|
-
import Foundation
|
|
3
|
-
|
|
4
|
-
enum TimeEventFrequency {
|
|
5
|
-
case everySecond
|
|
6
|
-
case everyHalfSecond
|
|
7
|
-
case everyQuarterSecond
|
|
8
|
-
case custom(time: CMTime)
|
|
9
|
-
|
|
10
|
-
func getTime() -> CMTime {
|
|
11
|
-
switch self {
|
|
12
|
-
case .everySecond: CMTime(value: 1, timescale: 1)
|
|
13
|
-
case .everyHalfSecond: CMTime(value: 1, timescale: 2)
|
|
14
|
-
case .everyQuarterSecond: CMTime(value: 1, timescale: 4)
|
|
15
|
-
case let .custom(time): time
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import AVFoundation
|
|
2
|
-
import os.log
|
|
3
|
-
|
|
4
|
-
/// AVPlayer's `seek(to:)` is a no-op when there's no loaded `currentItem`,
|
|
5
|
-
/// so we must manually defer seeks that arrive during loading and execute them
|
|
6
|
-
/// once the AVPlayerItem is ready. This coordinator encapsulates the three
|
|
7
|
-
/// sub-problems (capture, execute, coordinate) that were previously spread
|
|
8
|
-
/// across `pendingSeek`, `loadSeekInFlight`, and guards in multiple callbacks.
|
|
9
|
-
@MainActor
|
|
10
|
-
final class LoadSeekCoordinator {
|
|
11
|
-
enum State {
|
|
12
|
-
case idle
|
|
13
|
-
case pendingSeek(time: TimeInterval)
|
|
14
|
-
case seekInFlight(time: TimeInterval)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
private let logger = Logger(subsystem: "com.audiobrowser", category: "LoadSeekCoordinator")
|
|
18
|
-
private(set) var state: State = .idle
|
|
19
|
-
|
|
20
|
-
/// Identifies the item epoch a seek was issued against. `reset()` (called on
|
|
21
|
-
/// track/queue changes) bumps it, so the completion of a seek issued against
|
|
22
|
-
/// the previous item can recognize itself as stale and be dropped — a
|
|
23
|
-
/// cancelled cross-item completion arriving after the new track's start
|
|
24
|
-
/// position was captured would otherwise consume or prematurely resolve it.
|
|
25
|
-
private(set) var generation = 0
|
|
26
|
-
|
|
27
|
-
func isCurrentGeneration(_ generation: Int) -> Bool {
|
|
28
|
-
generation == self.generation
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/// Used by `seekBy()` to offset from the deferred position.
|
|
32
|
-
var pendingTime: TimeInterval? {
|
|
33
|
-
switch state {
|
|
34
|
-
case let .pendingSeek(time): time
|
|
35
|
-
case let .seekInFlight(time): time
|
|
36
|
-
case .idle: nil
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
var shouldDeferReadyTransition: Bool {
|
|
41
|
-
switch state {
|
|
42
|
-
case .idle: false
|
|
43
|
-
case .pendingSeek, .seekInFlight: true
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
func capture(position: Double) {
|
|
48
|
-
if case .seekInFlight = state {
|
|
49
|
-
logger.debug("overriding in-flight seek with new target \(position)s")
|
|
50
|
-
}
|
|
51
|
-
state = .pendingSeek(time: position)
|
|
52
|
-
logger.debug("captured pending seek to \(position)s")
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/// Returns `true` if a seek was initiated -- caller should NOT transition to `.ready` yet.
|
|
56
|
-
func executeIfPending(on player: AVPlayer, delegate: (any SeekCompletionHandler)?) -> Bool {
|
|
57
|
-
guard case let .pendingSeek(time) = state else { return false }
|
|
58
|
-
|
|
59
|
-
logger.debug("executing deferred seek to \(time)s")
|
|
60
|
-
state = .seekInFlight(time: time)
|
|
61
|
-
|
|
62
|
-
let cmTime = CMTime(seconds: time, preferredTimescale: 1000)
|
|
63
|
-
let generation = generation
|
|
64
|
-
player
|
|
65
|
-
.seek(to: cmTime, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero) {
|
|
66
|
-
[weak self] finished in
|
|
67
|
-
Task { @MainActor in
|
|
68
|
-
guard let self, self.isCurrentGeneration(generation) else { return }
|
|
69
|
-
delegate?.handleSeekCompleted(to: time, didFinish: finished)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return true
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/// Returns `true` if done (caller should transition `.loading` -> `.ready`).
|
|
77
|
-
/// Returns `false` if a new seek was queued while in-flight (re-executes automatically).
|
|
78
|
-
func seekDidComplete(on player: AVPlayer, delegate: (any SeekCompletionHandler)?) -> Bool {
|
|
79
|
-
switch state {
|
|
80
|
-
case .seekInFlight:
|
|
81
|
-
logger.debug("seek landed, clearing coordinator")
|
|
82
|
-
state = .idle
|
|
83
|
-
return true
|
|
84
|
-
case .pendingSeek:
|
|
85
|
-
// A new seek arrived while the previous one was in-flight.
|
|
86
|
-
// Execute it now -- don't transition to .ready yet.
|
|
87
|
-
logger.debug("seek landed, but a new seek is pending -- re-executing")
|
|
88
|
-
_ = executeIfPending(on: player, delegate: delegate)
|
|
89
|
-
return false
|
|
90
|
-
case .idle:
|
|
91
|
-
return false
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
func reset() {
|
|
96
|
-
state = .idle
|
|
97
|
-
generation += 1
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import AVFoundation
|
|
2
|
-
|
|
3
|
-
@MainActor protocol MediaLoaderDelegate: AnyObject {
|
|
4
|
-
func mediaLoaderDidPrepareItem(_ item: AVPlayerItem)
|
|
5
|
-
func mediaLoaderDidFailWithUnplayableTrack()
|
|
6
|
-
func mediaLoaderDidFailWithRetryableError(_ error: Error)
|
|
7
|
-
func mediaLoaderDidFailWithError(_ error: TrackPlayerError.PlaybackError)
|
|
8
|
-
func mediaLoaderDidReceiveCommonMetadata(_ items: [AVMetadataItem])
|
|
9
|
-
func mediaLoaderDidReceiveChapterMetadata(_ groups: [AVTimedMetadataGroup])
|
|
10
|
-
func mediaLoaderDidReceiveTimedMetadata(_ groups: [AVTimedMetadataGroup])
|
|
11
|
-
}
|