Compare commits

...

9 Commits

Author SHA1 Message Date
Jørgen Henrichsen 843ba9f450 Bumped version to 0.3.6 2018-10-29 18:53:26 +01:00
Jørgen Henrichsen 4305110867 Merge pull request #28 from dcvz/master
Add a Couple of Features and Fixes
2018-10-29 18:50:48 +01:00
David Chavez cd43ecc6f9 Update example and fix tests 2018-10-29 15:44:06 +01:00
David Chavez 274377af3f Address review comments 2018-10-29 14:21:09 +01:00
David Chavez 43824a9700 Add granularity for a track finishing playback between simple/queued 2018-10-28 13:37:02 +01:00
David Chavez 9e6683674b Fix issues with updating index upon queue mutations 2018-10-28 12:02:45 +01:00
David Chavez b27332aafb Add a couple of features and fix some issues 2018-10-28 10:35:44 +01:00
Jørgen Henrichsen 664c56b79c Update README
Add version to pod file instruction
2018-10-25 10:49:53 +02:00
Jørgen Henrichsen 7bb83e87e0 Merge pull request #25 from jorgenhenrichsen/dev
Update master
2018-10-23 10:12:46 +02:00
15 changed files with 159 additions and 80 deletions
+3 -3
View File
@@ -283,7 +283,7 @@
TargetAttributes = {
607FACCF1AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1;
DevelopmentTeam = HPNZWPB9JK;
DevelopmentTeam = B82TF96752;
LastSwiftMigration = 0900;
SystemCapabilities = {
com.apple.BackgroundModes = {
@@ -591,7 +591,7 @@
baseConfigurationReference = C344B34C66182CD1C5AD6DD2 /* Pods-SwiftAudio_Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = HPNZWPB9JK;
DEVELOPMENT_TEAM = B82TF96752;
INFOPLIST_FILE = SwiftAudio/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -608,7 +608,7 @@
baseConfigurationReference = CAD65BC49F7B60C24AB20FDA /* Pods-SwiftAudio_Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = HPNZWPB9JK;
DEVELOPMENT_TEAM = B82TF96752;
INFOPLIST_FILE = SwiftAudio/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+4 -4
View File
@@ -17,10 +17,10 @@ class AudioController {
let audioSessionController = AudioSessionController.shared
let sources: [AudioItem] = [
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/67b51d90ffddd6bb3f095059997021b589845f81?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "33 \"GOD\"", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/081447adc23dad4f79ba4f1082615d1c56edf5e1?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "8 (circle)", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/6f9999d909b017eabef97234dd7a206355720d9d?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "715 - CRΣΣKS", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/bf9bdd403c67fdbe06a582e7b292487c8cfd1f7e?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "____45_____", albumTitle: "22, A Million", sourceType: .stream, artwork: #imageLiteral(resourceName: "22AMI"))
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/67b51d90ffddd6bb3f095059997021b589845f81?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "33 \"GOD\"", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/081447adc23dad4f79ba4f1082615d1c56edf5e1?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "8 (circle)", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/6f9999d909b017eabef97234dd7a206355720d9d?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "715 - CRΣΣKS", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI")),
DefaultAudioItem(audioUrl: "https://p.scdn.co/mp3-preview/bf9bdd403c67fdbe06a582e7b292487c8cfd1f7e?cid=d8a5ed958d274c2e8ee717e6a4b0971d", artist: "Bon Iver", title: "____45_____", albumTitle: "22, A Million", sourceType: .stream, pitchAlgorithmType: .lowQualityZeroLatency, artwork: #imageLiteral(resourceName: "22AMI"))
]
init() {
+4 -4
View File
@@ -61,6 +61,10 @@ class ViewController: UIViewController {
}
extension ViewController: AudioPlayerDelegate {
func audioPlayer(itemPlaybackEndedWithReason reason: PlaybackEndedReason) {
}
func audioPlayer(playerDidChangeState state: AVPlayerWrapperState) {
playButton.setTitle(state == .playing ? "Pause" : "Play", for: .normal)
@@ -89,10 +93,6 @@ extension ViewController: AudioPlayerDelegate {
}
}
func audioPlayerItemDidComplete() {
}
func audioPlayer(secondsElapsed seconds: Double) {
if !isScrubbing {
slider.setValue(Float(seconds), animated: false)
+9 -14
View File
@@ -189,20 +189,18 @@ class AVPlayerWrapperTests: QuickSpec {
})
context("when seeking to a time", {
var passed = false
let holder = AVPlayerWrapperDelegateHolder()
let seekTime: TimeInterval = 0.5
beforeEach {
wrapper.delegate = holder
holder.stateUpdate = { (state) in
if state == .ready && wrapper.duration != 0 {
try? wrapper.seek(to: seekTime)
}
}
holder.seekCompletion = { passed = true }
try? wrapper.load(fromFilePath: Source.path, playWhenReady: false)
try? wrapper.seek(to: seekTime)
}
it("should eventually be equal to the seeked time", closure: {
expect(wrapper.currentTime).toEventually(equal(seekTime))
expect(passed).toEventually(beTrue())
})
})
})
@@ -214,12 +212,12 @@ class AVPlayerWrapperTests: QuickSpec {
}
class AVPlayerWrapperDelegateHolder: AVPlayerWrapperDelegate {
func AVWrapper(itemPlaybackDoneWithReason reason: PlaybackEndedReason) {
}
var state: AVPlayerWrapperState? {
didSet {
print(state)
if let state = state {
self.stateUpdate?(state)
}
@@ -233,10 +231,6 @@ class AVPlayerWrapperDelegateHolder: AVPlayerWrapperDelegate {
self.state = state
}
func AVWrapperItemDidComplete() {
}
func AVWrapper(secondsElapsed seconds: Double) {
}
@@ -245,8 +239,9 @@ class AVPlayerWrapperDelegateHolder: AVPlayerWrapperDelegate {
}
var seekCompletion: (() -> Void)?
func AVWrapper(seekTo seconds: Int, didFinish: Bool) {
seekCompletion?()
}
func AVWrapper(didUpdateDuration duration: Double) {
+10 -11
View File
@@ -105,20 +105,18 @@ class AudioPlayerTests: QuickSpec {
})
context("when seeking to a time", {
var passed = false
let holder = AudioPlayerDelegateHolder()
let seekTime: TimeInterval = 0.5
beforeEach {
audioPlayer.delegate = holder
holder.stateUpdate = { (state) in
if state == .ready && audioPlayer.duration != 0 {
try? audioPlayer.seek(to: seekTime)
}
}
holder.seekCompletion = { passed = true }
try? audioPlayer.loadItem(Source.getAudioItem(), playWhenReady: false)
try? audioPlayer.seek(to: seekTime)
}
it("should eventually be equal to the seeked time", closure: {
expect(audioPlayer.currentTime).toEventually(equal(seekTime))
expect(passed).toEventually(beTrue())
})
})
})
@@ -144,6 +142,10 @@ class AudioPlayerTests: QuickSpec {
}
class AudioPlayerDelegateHolder: AudioPlayerDelegate {
func audioPlayer(itemPlaybackEndedWithReason reason: PlaybackEndedReason) {
}
var stateUpdate: ((_ state: AudioPlayerState) -> Void)?
var state: AudioPlayerState? {
@@ -158,10 +160,6 @@ class AudioPlayerDelegateHolder: AudioPlayerDelegate {
self.state = state
}
func audioPlayerItemDidComplete() {
}
func audioPlayer(secondsElapsed seconds: Double) {
}
@@ -170,8 +168,9 @@ class AudioPlayerDelegateHolder: AudioPlayerDelegate {
}
var seekCompletion: (() -> Void)?
func audioPlayer(seekTo seconds: Int, didFinish: Bool) {
seekCompletion?()
}
func audioPlayer(didUpdateDuration duration: Double) {
+5 -5
View File
@@ -122,7 +122,7 @@ class QueueManagerTests: QuickSpec {
context("then removing the second item", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: 1)
removed = try? manager.removeItem(at: 1)
}
it("should have one less item", closure: {
@@ -134,7 +134,7 @@ class QueueManagerTests: QuickSpec {
context("then removing the last item", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: self.dummyItems.count - 1)
removed = try? manager.removeItem(at: self.dummyItems.count - 1)
}
it("should have one less item", closure: {
@@ -146,7 +146,7 @@ class QueueManagerTests: QuickSpec {
context("then removing the current item", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: manager.currentIndex)
removed = try? manager.removeItem(at: manager.currentIndex)
}
it("should not remove any items", closure: {
expect(removed).to(beNil())
@@ -157,7 +157,7 @@ class QueueManagerTests: QuickSpec {
context("then removing with too large index", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: self.dummyItems.count)
removed = try? manager.removeItem(at: self.dummyItems.count)
}
it("should not remove any items", closure: {
@@ -169,7 +169,7 @@ class QueueManagerTests: QuickSpec {
context("then removing with too small index", {
var removed: Int?
beforeEach {
removed = try? manager.remove(atIndex: -1)
removed = try? manager.removeItem(at: -1)
}
it("should not remove any items", closure: {
+1 -1
View File
@@ -70,7 +70,7 @@ class QueuedAudioPlayerTests: QuickSpec {
context("then removing one item", {
beforeEach {
try? audioPlayer.removeItem(atIndex: 1)
try? audioPlayer.removeItem(at: 1)
}
it("should be empty", closure: {
+2 -2
View File
@@ -13,7 +13,7 @@ struct Source {
static let path: String = Bundle.main.path(forResource: "TestSound", ofType: "m4a")!
static func getAudioItem() -> AudioItem {
return DefaultAudioItem(audioUrl: Source.path, sourceType: .file)
return DefaultAudioItem(audioUrl: Source.path, sourceType: .file, pitchAlgorithmType: .lowQualityZeroLatency)
}
}
@@ -21,6 +21,6 @@ struct ShortSource {
static let path: String = Bundle.main.path(forResource: "ShortTestSound", ofType: "m4a")!
static func getAudioItem() -> AudioItem {
return DefaultAudioItem(audioUrl: ShortSource.path, sourceType: .file)
return DefaultAudioItem(audioUrl: ShortSource.path, sourceType: .file, pitchAlgorithmType: .lowQualityZeroLatency)
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ SwiftAudio is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod 'SwiftAudio'
pod 'SwiftAudio', '~> 0.3.5'
```
## Usage
+1 -1
View File
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = 'SwiftAudio'
s.version = '0.3.5'
s.version = '0.3.6'
s.summary = 'Easy audio streaming for iOS'
# This description is used to generate tags and improve search results.
+17 -8
View File
@@ -11,10 +11,18 @@ import AVFoundation
import MediaPlayer
public enum PlaybackEndedReason: String {
case playedUntilEnd
case playerStopped
case skippedToNext
case skippedToPrevious
case jumpedToIndex
}
protocol AVPlayerWrapperDelegate: class {
func AVWrapper(didChangeState state: AVPlayerWrapperState)
func AVWrapperItemDidComplete()
func AVWrapper(itemPlaybackDoneWithReason reason: PlaybackEndedReason)
func AVWrapper(secondsElapsed seconds: Double)
func AVWrapper(failedWithError error: Error?)
func AVWrapper(seekTo seconds: Int, didFinish: Bool)
@@ -42,7 +50,7 @@ class AVPlayerWrapper {
*/
var playWhenReady: Bool { return _playWhenReady }
private var _playWhenReady: Bool = true
fileprivate var _playWhenReady: Bool = true
/**
The current `AudioPlayerState` of the player.
@@ -98,9 +106,11 @@ class AVPlayerWrapper {
/**
The rate of the AVPlayer
Default is 1.0
*/
var rate: Float {
return avPlayer.rate
get { return avPlayer.rate }
set { avPlayer.rate = newValue }
}
// MARK: - AVPlayer Config Properties
@@ -226,9 +236,8 @@ class AVPlayerWrapper {
guard currentItem != nil else {
throw APError.PlaybackError.noLoadedItem
}
let millis = Int64(max(min(seconds, duration), 0) * 1000)
let time = CMTime(value: millis, timescale: 1000)
avPlayer.seek(to: time) { (finished) in
avPlayer.seek(to: CMTimeMakeWithSeconds(seconds, 1)) { (finished) in
self.delegate?.AVWrapper(seekTo: Int(seconds), didFinish: finished)
}
}
@@ -240,7 +249,6 @@ class AVPlayerWrapper {
- parameter playWhenReady: Whether playback should start immediately when the item is ready. Default is `true`
*/
func load(fromUrlString urlString: String, playWhenReady: Bool = true) throws {
guard let url = URL(string: urlString) else {
throw APError.LoadError.invalidSourceUrl(urlString)
}
@@ -287,6 +295,7 @@ class AVPlayerWrapper {
if !soft {
avPlayer.replaceCurrentItem(with: nil)
}
playerTimeObserver.unregisterForBoundaryTimeEvents()
playerItemNotificationObserver.stopObservingCurrentItem()
}
@@ -353,7 +362,7 @@ extension AVPlayerWrapper: AVPlayerItemNotificationObserverDelegate {
// MARK: - AVPlayerItemNotificationObserverDelegate
func itemDidPlayToEndTime() {
delegate?.AVWrapperItemDidComplete()
delegate?.AVWrapper(itemPlaybackDoneWithReason: .playedUntilEnd)
}
}
+10 -2
View File
@@ -6,6 +6,7 @@
//
import Foundation
import AVFoundation
public enum SourceType {
case stream
@@ -19,6 +20,7 @@ public protocol AudioItem {
func getTitle() -> String?
func getAlbumTitle() -> String?
func getSourceType() -> SourceType
func getPitchAlgorithmType() -> AVAudioTimePitchAlgorithm
func getArtwork(_ handler: @escaping (UIImage?) -> Void)
}
@@ -36,14 +38,17 @@ public struct DefaultAudioItem: AudioItem {
public var sourceType: SourceType
public var pitchAlgorithmType: AVAudioTimePitchAlgorithm
public var artwork: UIImage?
public init(audioUrl: String, artist: String? = nil, title: String? = nil, albumTitle: String? = nil, sourceType: SourceType, artwork: UIImage? = nil) {
public init(audioUrl: String, artist: String? = nil, title: String? = nil, albumTitle: String? = nil, sourceType: SourceType, pitchAlgorithmType: AVAudioTimePitchAlgorithm, artwork: UIImage? = nil) {
self.audioUrl = audioUrl
self.artist = artist
self.title = title
self.albumTitle = albumTitle
self.sourceType = sourceType
self.pitchAlgorithmType = pitchAlgorithmType
self.artwork = artwork
}
@@ -67,9 +72,12 @@ public struct DefaultAudioItem: AudioItem {
return sourceType
}
public func getPitchAlgorithmType() -> AVAudioTimePitchAlgorithm {
return pitchAlgorithmType
}
public func getArtwork(_ handler: @escaping (UIImage?) -> Void) {
handler(artwork)
}
}
+16 -11
View File
@@ -14,7 +14,7 @@ public protocol AudioPlayerDelegate: class {
func audioPlayer(playerDidChangeState state: AudioPlayerState)
func audioPlayerItemDidComplete()
func audioPlayer(itemPlaybackEndedWithReason reason: PlaybackEndedReason)
func audioPlayer(secondsElapsed seconds: Double)
@@ -23,7 +23,7 @@ public protocol AudioPlayerDelegate: class {
func audioPlayer(seekTo seconds: Int, didFinish: Bool)
func audioPlayer(didUpdateDuration duration: Double)
}
/**
@@ -69,13 +69,6 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
return wrapper.duration
}
/**
The current rate of the underlying `AudioPlayer`.
*/
public var rate: Float {
return wrapper.rate
}
/**
The current state of the underlying `AudioPlayer`.
*/
@@ -123,6 +116,15 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
set { wrapper.volume = newValue }
}
/**
The player rate
Default is 1.0
*/
public var rate: Float {
get { return wrapper.rate }
set { wrapper.rate = newValue }
}
// MARK: - Init
/**
@@ -156,6 +158,8 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
try self.wrapper.load(fromFilePath: item.getSourceUrl(), playWhenReady: playWhenReady)
}
wrapper.currentItem?.audioTimePitchAlgorithm = item.getPitchAlgorithmType()
self._currentItem = item
set(item: item)
setArtwork(forItem: item)
@@ -187,6 +191,7 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
Stop playback, resetting the player.
*/
public func stop() {
AVWrapper(itemPlaybackDoneWithReason: .playerStopped)
self.reset()
self.wrapper.stop()
}
@@ -280,8 +285,8 @@ public class AudioPlayer: AVPlayerWrapperDelegate {
self.delegate?.audioPlayer(playerDidChangeState: state)
}
func AVWrapperItemDidComplete() {
self.delegate?.audioPlayerItemDidComplete()
func AVWrapper(itemPlaybackDoneWithReason reason: PlaybackEndedReason) {
self.delegate?.audioPlayer(itemPlaybackEndedWithReason: reason)
}
func AVWrapper(secondsElapsed seconds: Double) {
+47 -9
View File
@@ -20,10 +20,10 @@ class QueueManager<T> {
}
public var nextItems: [T] {
guard _currentIndex < _items.count else {
guard _currentIndex + 1 < _items.count else {
return []
}
return Array(_items[_currentIndex + 1..<items.count])
return Array(_items[_currentIndex + 1..<_items.count])
}
public var previousItems: [T] {
@@ -71,6 +71,21 @@ class QueueManager<T> {
_items.append(contentsOf: items)
}
/**
Add an array of items to the queue at a given index.
- parameter items: The `AudioItem`s to be added.
- 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))")
}
_items.insert(contentsOf: items, at: index)
if (_currentIndex >= index) { _currentIndex = _currentIndex + _items.count }
}
/**
Get the next item in the queue, if there are any.
Will update the current item.
@@ -119,9 +134,10 @@ class QueueManager<T> {
throw APError.QueueError.invalidIndex(index: index, message: "Cannot jump to the current item")
}
guard index >= 0 && items.count > index else {
throw APError.QueueError.invalidIndex(index: index, message: "The jump index has to be positive and smaller thant the count of current items (\(items.count))")
guard index >= 0 && _items.count > index else {
throw APError.QueueError.invalidIndex(index: index, message: "The jump index has to be positive and smaller thant the count of current items (\(_items.count))")
}
_currentIndex = index
return _items[index]
}
@@ -140,14 +156,15 @@ class QueueManager<T> {
}
guard fromIndex >= 0 && fromIndex < _items.count else {
throw APError.QueueError.invalidIndex(index: fromIndex, message: "The fromIndex has to be positive and smaller than the count of current items (\(items.count)).")
throw APError.QueueError.invalidIndex(index: fromIndex, message: "The fromIndex has to be positive and smaller than the count of current items (\(_items.count)).")
}
guard toIndex >= 0 && toIndex < _items.count else {
throw APError.QueueError.invalidIndex(index: toIndex, message: "The toIndex has to be positive and smaller than the count of current items (\(items.count)).")
throw APError.QueueError.invalidIndex(index: toIndex, message: "The toIndex has to be positive and smaller than the count of current items (\(_items.count)).")
}
_items.insert(_items.remove(at: fromIndex), at: toIndex)
let item = try removeItem(at: fromIndex)
try addItems([item], at: toIndex)
}
/**
@@ -158,16 +175,37 @@ class QueueManager<T> {
- returns: The removed item.
*/
@discardableResult
public func remove(atIndex index: Int) throws -> T {
public func removeItem(at index: Int) throws -> T {
guard index != _currentIndex else {
throw APError.QueueError.invalidIndex(index: index, message: "Cannot remove the current item!")
}
guard index >= 0 && _items.count > index else {
throw APError.QueueError.invalidIndex(index: index, message: "Index for removal has to be postivie and smaller than the count of current items (\(items.count)).")
throw APError.QueueError.invalidIndex(index: index, message: "Index for removal has to be positive and smaller than the count of current items (\(_items.count)).")
}
if index < _currentIndex {
_currentIndex = _currentIndex - 1
}
return _items.remove(at: index)
}
/**
Remove upcoming items.
*/
public func removeUpcomingItems() {
let nextIndex = _currentIndex + 1
guard nextIndex < _items.count else { return }
_items.removeSubrange(nextIndex..<_items.count)
}
/**
Removes all items for queue
*/
public func clearQueue() {
_currentIndex = 0
_items.removeAll()
}
}
+29 -4
View File
@@ -29,6 +29,14 @@ public class QueuedAudioPlayer: AudioPlayer {
return queueManager.current
}
/**
Stops the player and clears the queue.
*/
public override func stop() {
super.stop()
queueManager.clearQueue()
}
/**
The previous items held by the queue.
*/
@@ -77,12 +85,17 @@ public class QueuedAudioPlayer: AudioPlayer {
}
}
public func add(items: [AudioItem], at index: Int) throws {
try queueManager.addItems(items, at: index)
}
/**
Step to the next item in the queue.
- throws: `APError`
*/
public func next() throws {
AVWrapper(itemPlaybackDoneWithReason: .skippedToNext)
let nextItem = try queueManager.next()
try self.loadItem(nextItem, playWhenReady: true)
}
@@ -91,6 +104,7 @@ public class QueuedAudioPlayer: AudioPlayer {
Step to the previous item in the queue.
*/
public func previous() throws {
AVWrapper(itemPlaybackDoneWithReason: .skippedToPrevious)
let previousItem = try queueManager.previous()
try self.loadItem(previousItem, playWhenReady: true)
}
@@ -101,8 +115,8 @@ public class QueuedAudioPlayer: AudioPlayer {
- parameter index: The index of the item to remove.
- throws: `APError.QueueError`
*/
public func removeItem(atIndex index: Int) throws {
try queueManager.remove(atIndex: index)
public func removeItem(at index: Int) throws {
try queueManager.removeItem(at: index)
}
/**
@@ -113,6 +127,8 @@ public class QueuedAudioPlayer: AudioPlayer {
- throws: `APError`
*/
public func jumpToItem(atIndex index: Int, playWhenReady: Bool = true) throws {
AVWrapper(itemPlaybackDoneWithReason: .jumpedToIndex)
let item = try queueManager.jump(to: index)
try self.loadItem(item, playWhenReady: playWhenReady)
}
@@ -128,10 +144,19 @@ public class QueuedAudioPlayer: AudioPlayer {
try queueManager.moveItem(fromIndex: fromIndex, toIndex: toIndex)
}
/**
Remove all upcoming items, those returned by `next()`
*/
public func removeUpcomingItems() {
queueManager.removeUpcomingItems()
}
// MARK: - AVPlayerWrapperDelegate
override func AVWrapperItemDidComplete() {
super.AVWrapperItemDidComplete()
override func AVWrapper(itemPlaybackDoneWithReason reason: PlaybackEndedReason) {
super.AVWrapper(itemPlaybackDoneWithReason: reason)
guard reason == .playedUntilEnd else { return }
if automaticallyPlayNextSong {
try? self.next()
}