Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92554a187c | |||
| 473651f357 | |||
| db2f3e9af7 | |||
| a9f831a258 | |||
| cc3840d81e | |||
| 5307090ea3 | |||
| bdaee8b18f | |||
| 84d359bc4f |
@@ -47,9 +47,9 @@ class AVPlayerItemObserverTests: QuickSpec {
|
||||
}
|
||||
|
||||
class AVPlayerItemObserverDelegateHolder: AVPlayerItemObserverDelegate {
|
||||
var receivedMetadata: ((_ metadata: [AVMetadataItem]) -> Void)?
|
||||
|
||||
func item(didReceiveMetadata metadata: [AVMetadataItem]) {
|
||||
var receivedMetadata: ((_ metadata: [AVTimedMetadataGroup]) -> Void)?
|
||||
|
||||
func item(didReceiveMetadata metadata: [AVTimedMetadataGroup]) {
|
||||
receivedMetadata?(metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ class AVPlayerWrapperTests: XCTestCase {
|
||||
}
|
||||
|
||||
class AVPlayerWrapperDelegateHolder: AVPlayerWrapperDelegate {
|
||||
func AVWrapper(didReceiveMetadata metadata: [AVMetadataItem]) {
|
||||
func AVWrapper(didReceiveMetadata metadata: [AVTimedMetadataGroup]) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -52,11 +52,12 @@ class AudioSessionControllerTests: QuickSpec {
|
||||
}
|
||||
|
||||
describe("its delegate") {
|
||||
context("when a interruption arrives") {
|
||||
context("when a ended interruption arrives") {
|
||||
var delegate: AudioSessionControllerDelegateImplementation!
|
||||
beforeEach {
|
||||
let notification = Notification(name: AVAudioSession.interruptionNotification, object: nil, userInfo: [
|
||||
AVAudioSessionInterruptionTypeKey: UInt(0)
|
||||
AVAudioSessionInterruptionTypeKey: UInt(0),
|
||||
AVAudioSessionInterruptionOptionKey: UInt(1),
|
||||
])
|
||||
delegate = AudioSessionControllerDelegateImplementation()
|
||||
audioSessionController.delegate = delegate
|
||||
@@ -64,7 +65,23 @@ class AudioSessionControllerTests: QuickSpec {
|
||||
}
|
||||
|
||||
it("should eventually be updated with the interruption type") {
|
||||
expect(delegate.interruptionType).toEventuallyNot(beNil())
|
||||
expect(delegate.interruptionType).toEventually(equal(InterruptionType.ended(shouldResume: true)))
|
||||
}
|
||||
|
||||
}
|
||||
context("when a begin interruption arrives") {
|
||||
var delegate: AudioSessionControllerDelegateImplementation!
|
||||
beforeEach {
|
||||
let notification = Notification(name: AVAudioSession.interruptionNotification, object: nil, userInfo: [
|
||||
AVAudioSessionInterruptionTypeKey: UInt(1),
|
||||
])
|
||||
delegate = AudioSessionControllerDelegateImplementation()
|
||||
audioSessionController.delegate = delegate
|
||||
audioSessionController.handleInterruption(notification: notification)
|
||||
}
|
||||
|
||||
it("should eventually be updated with the interruption type") {
|
||||
expect(delegate.interruptionType).toEventually(equal(InterruptionType.began))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -91,10 +108,9 @@ class AudioSessionControllerTests: QuickSpec {
|
||||
}
|
||||
|
||||
class AudioSessionControllerDelegateImplementation: AudioSessionControllerDelegate {
|
||||
var interruptionType: InterruptionType? = nil
|
||||
|
||||
var interruptionType: AVAudioSession.InterruptionType? = nil
|
||||
|
||||
func handleInterruption(type: AVAudioSession.InterruptionType) {
|
||||
func handleInterruption(type: InterruptionType) {
|
||||
self.interruptionType = type
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,53 @@ class QueueManagerTests: QuickSpec {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
describe("when adding at index") {
|
||||
context("adding item at index 0 when queue is empty") {
|
||||
it("should add element successfully") {
|
||||
try manager.addItems([3], at: 0)
|
||||
expect(manager.current).to(equal(3))
|
||||
}
|
||||
}
|
||||
|
||||
context("adding item at index") {
|
||||
beforeEach {
|
||||
manager.addItems([3, 1])
|
||||
}
|
||||
|
||||
context("current [element count]") {
|
||||
it("should add element successfully") {
|
||||
try manager.addItems([5], at: manager.items.count)
|
||||
expect(manager.items.last).to(equal(5))
|
||||
}
|
||||
}
|
||||
|
||||
context("before the [current index]") {
|
||||
it("should add element successfully") {
|
||||
try manager.addItems([5], at: 0)
|
||||
expect(manager.current).to(equal(3))
|
||||
expect(manager.currentIndex).to(equal(1))
|
||||
}
|
||||
}
|
||||
|
||||
context("after the [current index]") {
|
||||
it("should add element successfully") {
|
||||
try manager.addItems([5], at: 1)
|
||||
expect(manager.current).to(equal(3))
|
||||
expect(manager.currentIndex).to(equal(0))
|
||||
}
|
||||
}
|
||||
|
||||
context("at [current index]") {
|
||||
it("should add element successfully") {
|
||||
try manager.next()
|
||||
try manager.addItems([5], at: 1)
|
||||
expect(manager.current).to(equal(1))
|
||||
expect(manager.currentIndex).to(equal(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("when adding one item") {
|
||||
|
||||
|
||||
@@ -167,10 +167,90 @@ class QueuedAudioPlayerTests: QuickSpec {
|
||||
}
|
||||
}
|
||||
|
||||
describe("onNext") {
|
||||
context("player was playing") {
|
||||
beforeEach {
|
||||
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()], playWhenReady: true)
|
||||
}
|
||||
|
||||
context("then calling next()") {
|
||||
beforeEach {
|
||||
try? audioPlayer.next()
|
||||
}
|
||||
|
||||
it("should go to next item and play") {
|
||||
expect(audioPlayer.nextItems.count).toEventually(equal(0))
|
||||
expect(audioPlayer.currentIndex).toEventually(equal(1))
|
||||
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("player was paused") {
|
||||
beforeEach {
|
||||
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()])
|
||||
audioPlayer.pause()
|
||||
|
||||
}
|
||||
|
||||
context("then calling next()") {
|
||||
beforeEach {
|
||||
try? audioPlayer.next()
|
||||
}
|
||||
|
||||
it("should go to next item and play") {
|
||||
expect(audioPlayer.nextItems.count).toEventually(equal(0))
|
||||
expect(audioPlayer.currentIndex).toEventually(equal(1))
|
||||
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.ready))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("onPrevious") {
|
||||
context("player was playing") {
|
||||
beforeEach {
|
||||
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()], playWhenReady: true)
|
||||
try? audioPlayer.next()
|
||||
}
|
||||
|
||||
context("then calling previous()") {
|
||||
beforeEach {
|
||||
try? audioPlayer.previous()
|
||||
}
|
||||
|
||||
it("should go to next item and play") {
|
||||
expect(audioPlayer.nextItems.count).toEventually(equal(1))
|
||||
expect(audioPlayer.currentIndex).toEventually(equal(0))
|
||||
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
|
||||
}
|
||||
}
|
||||
}
|
||||
context("player was paused") {
|
||||
beforeEach {
|
||||
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()])
|
||||
try? audioPlayer.next()
|
||||
audioPlayer.pause()
|
||||
|
||||
}
|
||||
|
||||
context("then calling previous()") {
|
||||
beforeEach {
|
||||
try? audioPlayer.previous()
|
||||
}
|
||||
|
||||
it("should go to next item and play") {
|
||||
expect(audioPlayer.nextItems.count).toEventually(equal(1))
|
||||
expect(audioPlayer.currentIndex).toEventually(equal(0))
|
||||
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.ready))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("its repeat mode") {
|
||||
context("when adding 2 items") {
|
||||
beforeEach {
|
||||
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()])
|
||||
try? audioPlayer.add(items: [ShortSource.getAudioItem(), ShortSource.getAudioItem()], playWhenReady: true)
|
||||
}
|
||||
|
||||
context("then setting repeat mode off") {
|
||||
@@ -244,9 +324,9 @@ class QueuedAudioPlayerTests: QuickSpec {
|
||||
try? audioPlayer.next()
|
||||
}
|
||||
|
||||
it("should move to next item but should not play") {
|
||||
it("should move to next item and should play") {
|
||||
expect(audioPlayer.nextItems.count).to(equal(0))
|
||||
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.ready))
|
||||
expect(audioPlayer.playerState).toEventually(equal(AudioPlayerState.playing))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ To see the audio player in action, run the example project!
|
||||
To run the example project, clone the repo, and run `pod install` from the Example directory first.
|
||||
|
||||
## Requirements
|
||||
iOS 10.0+
|
||||
iOS 11.0+
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'SwiftAudioEx'
|
||||
s.version = '0.14.7'
|
||||
s.version = '0.15.0'
|
||||
s.summary = 'Easy audio streaming for iOS'
|
||||
s.description = <<-DESC
|
||||
SwiftAudioEx is an audio player written in Swift, making it simpler to work with audio playback from streams and files.
|
||||
|
||||
@@ -89,6 +89,10 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol {
|
||||
get { return avPlayer.automaticallyWaitsToMinimizeStalling }
|
||||
set { avPlayer.automaticallyWaitsToMinimizeStalling = newValue }
|
||||
}
|
||||
|
||||
var willPlayWhenReady: Bool {
|
||||
return _playWhenReady
|
||||
}
|
||||
|
||||
var currentTime: TimeInterval {
|
||||
let seconds = avPlayer.currentTime().seconds
|
||||
@@ -218,8 +222,18 @@ class AVPlayerWrapper: AVPlayerWrapperProtocol {
|
||||
self.playerObserver.startObserving()
|
||||
self.playerItemNotificationObserver.startObserving(item: currentItem)
|
||||
self.playerItemObserver.startObserving(item: currentItem)
|
||||
for format in pendingAsset.availableMetadataFormats {
|
||||
self.delegate?.AVWrapper(didReceiveMetadata: pendingAsset.metadata(forFormat: format))
|
||||
|
||||
if pendingAsset.availableChapterLocales.count > 0 {
|
||||
for locale in pendingAsset.availableChapterLocales {
|
||||
let chapters = pendingAsset.chapterMetadataGroups(withTitleLocale: locale, containingItemsWithCommonKeys: nil)
|
||||
self.delegate?.AVWrapper(didReceiveMetadata: chapters)
|
||||
}
|
||||
} else {
|
||||
for format in pendingAsset.availableMetadataFormats {
|
||||
let timeRange = CMTimeRange(start: CMTime(seconds: 0, preferredTimescale: 1000), end: pendingAsset.duration)
|
||||
let group = AVTimedMetadataGroup(items: pendingAsset.metadata(forFormat: format), timeRange: timeRange)
|
||||
self.delegate?.AVWrapper(didReceiveMetadata: [group])
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
@@ -358,8 +372,8 @@ extension AVPlayerWrapper: AVPlayerItemObserverDelegate {
|
||||
func item(didUpdateDuration duration: Double) {
|
||||
self.delegate?.AVWrapper(didUpdateDuration: duration)
|
||||
}
|
||||
|
||||
func item(didReceiveMetadata metadata: [AVMetadataItem]) {
|
||||
|
||||
func item(didReceiveMetadata metadata: [AVTimedMetadataGroup]) {
|
||||
self.delegate?.AVWrapper(didReceiveMetadata: metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ import Foundation
|
||||
import MediaPlayer
|
||||
|
||||
|
||||
protocol AVPlayerWrapperDelegate: class {
|
||||
protocol AVPlayerWrapperDelegate: AnyObject {
|
||||
|
||||
func AVWrapper(didChangeState state: AVPlayerWrapperState)
|
||||
func AVWrapper(secondsElapsed seconds: Double)
|
||||
func AVWrapper(failedWithError error: Error?)
|
||||
func AVWrapper(seekTo seconds: Int, didFinish: Bool)
|
||||
func AVWrapper(didUpdateDuration duration: Double)
|
||||
func AVWrapper(didReceiveMetadata metadata: [AVMetadataItem])
|
||||
func AVWrapper(didReceiveMetadata metadata: [AVTimedMetadataGroup])
|
||||
func AVWrapperItemDidPlayToEndTime()
|
||||
func AVWrapperDidRecreateAVPlayer()
|
||||
|
||||
|
||||
@@ -9,9 +9,11 @@ import Foundation
|
||||
import AVFoundation
|
||||
|
||||
|
||||
protocol AVPlayerWrapperProtocol: class {
|
||||
protocol AVPlayerWrapperProtocol: AnyObject {
|
||||
|
||||
var state: AVPlayerWrapperState { get }
|
||||
|
||||
var willPlayWhenReady: Bool { get }
|
||||
|
||||
var currentItem: AVPlayerItem? { get }
|
||||
|
||||
|
||||
@@ -52,6 +52,10 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
|
||||
|
||||
// MARK: - Getters from AVPlayerWrapper
|
||||
|
||||
internal var willPlayWhenReady: Bool {
|
||||
return wrapper.willPlayWhenReady
|
||||
}
|
||||
|
||||
/**
|
||||
The elapsed playback time of the current item.
|
||||
@@ -365,8 +369,8 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
|
||||
func AVWrapper(didUpdateDuration duration: Double) {
|
||||
self.event.updateDuration.emit(data: duration)
|
||||
}
|
||||
|
||||
func AVWrapper(didReceiveMetadata metadata: [AVMetadataItem]) {
|
||||
|
||||
func AVWrapper(didReceiveMetadata metadata: [AVTimedMetadataGroup]) {
|
||||
self.event.receiveMetadata.emit(data: metadata)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,14 @@
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
|
||||
public protocol AudioSessionControllerDelegate: class {
|
||||
func handleInterruption(type: AVAudioSession.InterruptionType)
|
||||
public enum InterruptionType: Equatable {
|
||||
case began
|
||||
case ended(shouldResume: Bool)
|
||||
}
|
||||
|
||||
public protocol AudioSessionControllerDelegate: AnyObject {
|
||||
func handleInterruption(type: InterruptionType)
|
||||
}
|
||||
|
||||
/**
|
||||
Simple controller for the `AVAudioSession`. If you need more advanced options, just use the `AVAudioSession` directly.
|
||||
@@ -112,7 +115,19 @@ public class AudioSessionController {
|
||||
return
|
||||
}
|
||||
|
||||
self.delegate?.handleInterruption(type: type)
|
||||
switch type {
|
||||
case .began:
|
||||
self.delegate?.handleInterruption(type: .began)
|
||||
case .ended:
|
||||
guard let typeValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else {
|
||||
self.delegate?.handleInterruption(type: .ended(shouldResume: false))
|
||||
return
|
||||
}
|
||||
|
||||
let options = AVAudioSession.InterruptionOptions(rawValue: typeValue)
|
||||
self.delegate?.handleInterruption(type: .ended(shouldResume: options.contains(.shouldResume)))
|
||||
@unknown default: return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ extension AudioPlayer {
|
||||
public typealias FailEventData = (Error?)
|
||||
public typealias SeekEventData = (seconds: Int, didFinish: Bool)
|
||||
public typealias UpdateDurationEventData = (Double)
|
||||
public typealias MetadataEventData = ([AVMetadataItem])
|
||||
public typealias MetadataEventData = ([AVTimedMetadataGroup])
|
||||
public typealias DidRecreateAVPlayerEventData = ()
|
||||
public typealias QueueIndexEventData = (previousIndex: Int?, newIndex: Int?)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
protocol AVPlayerItemObserverDelegate: class {
|
||||
protocol AVPlayerItemObserverDelegate: AnyObject {
|
||||
|
||||
/**
|
||||
Called when the observed item updates the duration.
|
||||
@@ -18,7 +18,7 @@ protocol AVPlayerItemObserverDelegate: class {
|
||||
/**
|
||||
Called when the observed item receives metadata
|
||||
*/
|
||||
func item(didReceiveMetadata metadata: [AVMetadataItem])
|
||||
func item(didReceiveMetadata metadata: [AVTimedMetadataGroup])
|
||||
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ class AVPlayerItemObserver: NSObject {
|
||||
|
||||
private static var context = 0
|
||||
private let main: DispatchQueue = .main
|
||||
private let metadataOutput: AVPlayerItemMetadataOutput
|
||||
|
||||
private struct AVPlayerItemKeyPath {
|
||||
static let duration = #keyPath(AVPlayerItem.duration)
|
||||
static let loadedTimeRanges = #keyPath(AVPlayerItem.loadedTimeRanges)
|
||||
static let timedMetadata = #keyPath(AVPlayerItem.timedMetadata)
|
||||
}
|
||||
|
||||
private(set) var isObserving: Bool = false
|
||||
@@ -41,6 +41,13 @@ class AVPlayerItemObserver: NSObject {
|
||||
private(set) weak var observingItem: AVPlayerItem?
|
||||
weak var delegate: AVPlayerItemObserverDelegate?
|
||||
|
||||
override init() {
|
||||
metadataOutput = AVPlayerItemMetadataOutput()
|
||||
super.init()
|
||||
|
||||
metadataOutput.setDelegate(self, queue: main)
|
||||
}
|
||||
|
||||
deinit {
|
||||
stopObservingCurrentItem()
|
||||
}
|
||||
@@ -51,12 +58,12 @@ class AVPlayerItemObserver: NSObject {
|
||||
- parameter item: The player item to observe.
|
||||
*/
|
||||
func startObserving(item: AVPlayerItem) {
|
||||
self.stopObservingCurrentItem()
|
||||
self.isObserving = true
|
||||
self.observingItem = item
|
||||
stopObservingCurrentItem()
|
||||
isObserving = true
|
||||
observingItem = item
|
||||
item.addObserver(self, forKeyPath: AVPlayerItemKeyPath.duration, options: [.new], context: &AVPlayerItemObserver.context)
|
||||
item.addObserver(self, forKeyPath: AVPlayerItemKeyPath.loadedTimeRanges, options: [.new], context: &AVPlayerItemObserver.context)
|
||||
item.addObserver(self, forKeyPath: AVPlayerItemKeyPath.timedMetadata, options: [.new], context: &AVPlayerItemObserver.context)
|
||||
item.add(metadataOutput)
|
||||
}
|
||||
|
||||
func stopObservingCurrentItem() {
|
||||
@@ -65,8 +72,8 @@ class AVPlayerItemObserver: NSObject {
|
||||
}
|
||||
observingItem.removeObserver(self, forKeyPath: AVPlayerItemKeyPath.duration, context: &AVPlayerItemObserver.context)
|
||||
observingItem.removeObserver(self, forKeyPath: AVPlayerItemKeyPath.loadedTimeRanges, context: &AVPlayerItemObserver.context)
|
||||
observingItem.removeObserver(self, forKeyPath: AVPlayerItemKeyPath.timedMetadata, context: &AVPlayerItemObserver.context)
|
||||
self.isObserving = false
|
||||
observingItem.remove(metadataOutput)
|
||||
isObserving = false
|
||||
self.observingItem = nil
|
||||
}
|
||||
|
||||
@@ -79,21 +86,22 @@ class AVPlayerItemObserver: NSObject {
|
||||
switch observedKeyPath {
|
||||
case AVPlayerItemKeyPath.duration:
|
||||
if let duration = change?[.newKey] as? CMTime {
|
||||
self.delegate?.item(didUpdateDuration: duration.seconds)
|
||||
delegate?.item(didUpdateDuration: duration.seconds)
|
||||
}
|
||||
|
||||
case AVPlayerItemKeyPath.loadedTimeRanges:
|
||||
if let ranges = change?[.newKey] as? [NSValue], let duration = ranges.first?.timeRangeValue.duration {
|
||||
self.delegate?.item(didUpdateDuration: duration.seconds)
|
||||
delegate?.item(didUpdateDuration: duration.seconds)
|
||||
}
|
||||
|
||||
case AVPlayerItemKeyPath.timedMetadata:
|
||||
if let metadata = change?[.newKey] as? [AVMetadataItem] {
|
||||
self.delegate?.item(didReceiveMetadata: metadata)
|
||||
}
|
||||
default: break
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AVPlayerItemObserver: AVPlayerItemMetadataOutputPushDelegate {
|
||||
func metadataOutput(_ output: AVPlayerItemMetadataOutput, didOutputTimedMetadataGroups groups: [AVTimedMetadataGroup], from track: AVPlayerItemTrack?) {
|
||||
delegate?.item(didReceiveMetadata: groups)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,12 +94,13 @@ class QueueManager<T> {
|
||||
- parameter at: The index to insert the items at.
|
||||
*/
|
||||
public func addItems(_ items: [T], at index: Int) throws {
|
||||
guard index >= 0 && _items.count > index else {
|
||||
throw APError.QueueError.invalidIndex(index: index, message: "Index for addition has to be positive and smaller than the count of current items (\(_items.count))")
|
||||
guard index >= 0 && _items.count >= index else {
|
||||
throw APError.QueueError.invalidIndex(index: index, message: "Index to insert at has to be non-negative and equal to or smaller than the number of items: (\(_items.count))")
|
||||
}
|
||||
|
||||
|
||||
_items.insert(contentsOf: items, at: index)
|
||||
if (_currentIndex >= index) { _currentIndex = _currentIndex + items.count }
|
||||
|
||||
if (_currentIndex >= index && _items.count != 1) { _currentIndex = _currentIndex + items.count }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -123,14 +123,16 @@ public class QueuedAudioPlayer: AudioPlayer, QueueManagerDelegate {
|
||||
- throws: `APError`
|
||||
*/
|
||||
public func next() throws {
|
||||
let shouldPlayWhenReady = (playerState == .loading) ? willPlayWhenReady : [.buffering, .playing].contains(playerState)
|
||||
|
||||
do {
|
||||
let nextItem = try queueManager.next()
|
||||
event.playbackEnd.emit(data: .skippedToNext)
|
||||
try self.load(item: nextItem, playWhenReady: repeatMode != .track)
|
||||
try self.load(item: nextItem, playWhenReady: shouldPlayWhenReady)
|
||||
} catch APError.QueueError.noNextItem {
|
||||
if repeatMode == .queue {
|
||||
event.playbackEnd.emit(data: .skippedToNext)
|
||||
try jumpToItem(atIndex: 0, playWhenReady: true)
|
||||
try jumpToItem(atIndex: 0, playWhenReady: shouldPlayWhenReady)
|
||||
} else {
|
||||
throw APError.QueueError.noNextItem
|
||||
}
|
||||
@@ -143,9 +145,11 @@ public class QueuedAudioPlayer: AudioPlayer, QueueManagerDelegate {
|
||||
Step to the previous item in the queue.
|
||||
*/
|
||||
public func previous() throws {
|
||||
let shouldPlayWhenReady = (playerState == .loading) ? willPlayWhenReady : [.buffering, .playing].contains(playerState)
|
||||
|
||||
let previousItem = try queueManager.previous()
|
||||
event.playbackEnd.emit(data: .skippedToPrevious)
|
||||
try self.load(item: previousItem, playWhenReady: repeatMode != .track)
|
||||
try self.load(item: previousItem, playWhenReady: shouldPlayWhenReady)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,7 +209,7 @@ public class QueuedAudioPlayer: AudioPlayer, QueueManagerDelegate {
|
||||
case .off:
|
||||
do {
|
||||
let nextItem = try queueManager.next()
|
||||
try self.load(item: nextItem, playWhenReady: repeatMode != .track)
|
||||
try self.load(item: nextItem, playWhenReady: true)
|
||||
} catch { /* playback finished */ }
|
||||
case .track:
|
||||
seek(to: 0)
|
||||
@@ -213,7 +217,7 @@ public class QueuedAudioPlayer: AudioPlayer, QueueManagerDelegate {
|
||||
case .queue:
|
||||
do {
|
||||
let nextItem = try queueManager.next()
|
||||
try self.load(item: nextItem, playWhenReady: repeatMode != .track)
|
||||
try self.load(item: nextItem, playWhenReady: true)
|
||||
} catch {
|
||||
try? jumpToItem(atIndex: 0, playWhenReady: true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user