ios - 无法设置 MPMusicPlayerController.shuffleMode

标签 ios swift xcode mpmusicplayercontroller

您好,我有一个关于 Swift 中的 MPMusicPlayerController 的问题。我目前正在开发一个音乐应用程序,我想按歌曲随机播放音乐。因此,当应用程序启动时,它基本上会设置播放队列,然后设置随机播放模式。我可以成功设置队列(并播放歌曲),但在设置随机播放模式时出现错误:

musicPlayer.musicPlayer.shuffleMode = .songs

错误:

2018-07-03 15:01:36.450977+0200 Hitbeat[29053:8378883] [SDKPlayback] -[MPMusicPlayerController setShuffleMode:2] completed error: Error Domain=MPCPlayerRequestErrorDomain Code=1 "No commands provided." UserInfo={NSDebugDescription=No commands provided.}

这是什么意思? 我认为这可能是因为在设置 shuffleMode 时队列未完全设置,但我不确定,并且必须先设置歌曲队列才能按顺序设置模式是没有任何意义的要播放的歌曲。也许还有其他问题?

此外,一切都发生在主线程上。 (MPMusicPlayerController 始终必须在主线程中调用)

非常感谢,希望大家能帮助我。

这里是一些代码片段:

MusicPlayerManager.swift

import os.log
import MediaPlayer
import NotificationCenter

class MusicPlayerManager {

    let musicPlayer: MPMusicPlayerController
    lazy var musicPickerAndAdder = MusicPickerAndAdder()

    init() {
        // Instantiate a new music player
        musicPlayer = MPMusicPlayerApplicationController.applicationQueuePlayer

        // Add a playback queue containing all songs on the device

        switch MPMediaLibrary.authorizationStatus() {
        case .authorized:
            let catalogSongStoreID: String = ""
            let catalogQueueDescriptor = MPMusicPlayerStoreQueueDescriptor(storeIDs: [catalogSongStoreID])
            musicPlayer.setQueue(with: catalogQueueDescriptor)

        default:
            break
        }

        Timer.scheduledTimer(withTimeInterval: 15, repeats: false) {_ in
            print("shuffle mode setter")
            self.musicPlayer.shuffleMode = MPMusicShuffleMode.songs
        }
    }

    func updateOnlineMusicQueue() {
        var musicPickerIds = [String]()
        DispatchQueue.global(qos: .userInitiated).sync {
            musicPickerIds = musicPickerAndAdder.ids
        }
        if !musicPickerIds.isEmpty{
        musicPlayer.setQueue(with: musicPickerIds)
        }else {
            updateOfflineMusicQueue()
        }
        musicPlayer.pause()
    }

    func play() {

        if musicPlayer.playbackState == .playing {
            musicPlayer.pause()
            musicPlayer.skipToBeginning()
        }

        if !musicPlayer.isPreparedToPlay {
            musicPlayer.prepareToPlay { (error) in
                if error == nil {
                    self.musicPlayer.play()
                    self.startSongMasterTimer()
                }
            }
        }else {
            musicPlayer.play()
            startSongMasterTimer()
        }

    }

    func pauseAndSkip() {
//        if self.musicPlayer.shuffleMode.rawValue != 2 { // does not work here would work on pause and skip
//            self.musicPlayer.shuffleMode = MPMusicShuffleMode.songs
//        }
        //print("shuffler \(self.musicPlayer.shuffleMode.rawValue)")

        //print("At \(musicPlayer.currentPlaybackTime) of \((musicPlayer.nowPlayingItem?.playbackDuration!)")
        musicPlayer.pause()
        //if musicPlayer.nowPlayingItem != nil {
        musicPlayer.skipToNextItem()
        //}
        musicPlayer.prepareToPlay { (error) in
            if error == nil {
                self.musicPlayer.pause()
            }
        }
    }

    func currentSongInfo() -> SongInfo {

        let songTitle = musicPlayer.nowPlayingItem?.title?.replacingOccurrences(of: "-", with: " ") ?? "" // To guarantee there is only one - between Song and Artist
        let artistName = musicPlayer.nowPlayingItem?.artist?.replacingOccurrences(of: "-", with: " ") ?? ""

        let songInfo = SongInfo(title: songTitle, artist: artistName)

        return songInfo
    }

    func addSongToLibrary() {
        //print("Id of Item to Add: \(musicPlayer.nowPlayingItem?.playbackStoreID)")
        if musicPlayer.nowPlayingItem != nil {
            musicPickerAndAdder.addResourceToUserMusicLibrary(resourceId: (musicPlayer.nowPlayingItem?.playbackStoreID)!)
        }

        //ToDo add to myHitbeat Playlist
    }

}

class SongInfo {
    let title: String
    let artist: String

    init(title:String,artist:String) {
        self.title = title
        self.artist = artist
    }
}

MusicPickerAndAdder.swift

import Foundation

class MusicPickerAndAdder {

    lazy var authorizationManager: AuthorizationManager = {
        return AuthorizationManager(appleMusicManager: self.appleMusicManager)
    }()

    var appleMusicManager = AppleMusicManager()

    private var idsArraySize = 100

    static var idCategoriesStakes = ["Chart_Ids" : 0.10,
                                     "Recently_Played_Ids" : 0.10,
                                     "Experiment_Ids" : 0.30,
                                     "Recommendations_Ids" : 0.50,] // Addition of all Values must be 1 (100%)


    private var chartIds: [String] {

        var chartsIds = [String]()
        let chartsIdsGroup = DispatchGroup()
        chartsIdsGroup.enter()

        let limit = Int(Double(idsArraySize) * MusicPickerAndAdder.idCategoriesStakes["Recently_Played_Ids"]!)
       appleMusicManager.performAppleMusicGetChartSongs(regionCode: Locale.current.regionCode?.lowercased() ?? "us", limit: limit) { (storeIds, error) in

        if error != nil {
            print("There was an Error getting Charts")
            chartsIdsGroup.leave()
            return
        }
        chartsIds = storeIds
        chartsIdsGroup.leave()
       }

        chartsIdsGroup.wait()
        print("Charts sucessfully fetched")
        return chartsIds
    }
    private var recentlyPlayedIds: [String] {

        var recentIds = [String]()
        let recentIdsGroup = DispatchGroup()
        recentIdsGroup.enter()

        let limit = Int(Double(idsArraySize) * MusicPickerAndAdder.idCategoriesStakes["Recently_Played_Ids"]!)
        appleMusicManager.performAppleMusicGetRecentlyPlayed(userToken: authorizationManager.userToken, limit: limit) {
            (storeIds, error) in
            if error != nil {
                print("There was an Error getting Recently Played")
                recentIdsGroup.leave()
                return
            }

            recentIds = storeIds
            recentIdsGroup.leave()
            }

        recentIdsGroup.wait()
        print("Recently Played sucessfully fetched: \(recentIds)")
        return recentIds
    }
    private var experimentIds: [String] {
        return ["pl.u-XkD04oZIY0Kxrl"]
    }
    private var recommendationsIds: [String] {
        return [String]()
    }

    // Never request Ids  in Main (UI) Thread
    var ids: [String] {

            var ids = [String]()

            ids += recentlyPlayedIds
            ids += chartIds
            ids += experimentIds
            ids += recommendationsIds

            print("Store Ids for Songs \(ids)")
            return ids.shuffled() // shuffles list of items
    }

    init() {
        requestAppleMusicAuthorization()
    }
    //MARK: Private Methods
    private func requestAppleMusicAuthorization() {
        UserDefaults.standard.register(defaults: ["tutorial": true])
        if !UserDefaults.standard.bool(forKey: "tutorial") {
            authorizationManager.requestCloudServiceAuthorization()
            authorizationManager.requestMediaLibraryAuthorization()
        }
    }

}

extension MusicPickerAndAdder { // to Add Songs
    func addResourceToUserMusicLibrary(resourceId: String) {
        appleMusicManager.performAddResourceToLibrary(resourceId: resourceId, userToken: authorizationManager.userToken)
    }
}

extension MutableCollection {
    /// Shuffles the contents of this collection.
    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }

        for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            // Change `Int` in the next line to `IndexDistance` in < Swift 4.1
            let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
            let i = index(firstUnshuffled, offsetBy: d)
            swapAt(firstUnshuffled, i)
        }
    }
}

extension Sequence {
    /// Returns an array with the contents of this sequence, shuffled.
    func shuffled() -> [Element] {
        var result = Array(self)
        result.shuffle()
        return result
    }
}

PS:MusicPickerAndAdder 可能看起来有点乱,但我不认为问题出在这里!它的基本功能是从 Apple Music API 中获取一些数据(工作正常),并将歌曲添加到用户库中(工作正常)。

最佳答案

好吧,在尝试了一切可能的方法之后,我想出了两个适合我的解决方案。奇怪的是,我发现只有当到目前为止还没有播放歌曲时才会出现界面卡住。如果当前正在播放一首歌曲,或者即使一首歌曲已播放但随后暂停,则不会出现用户界面卡住的情况。所以我想出了这个功能:

private func setShuffleMode() { // does work though startup and restarting takes longer

    musicPlayer.play()
    Timer.scheduledTimer(withTimeInterval: 1.5, repeats: false) {_ in
        print("shuffle mode setter")
        self.musicPlayer.pause()
        //self.musicPlayer.pause()// may stop interface freezing if occuring
        self.musicPlayer.shuffleMode = MPMusicShuffleMode.songs // freeze of ui only occurs when no song played before
    }
}

我尝试了几个时间间隔,有时仍然会失败,如果是一秒,则在 1.5 秒内从未失败,所以我将其留在那里

问题是启动应用程序以及重新启动它需要更长的时间。所以我想出了第二个解决方案

private func setShuffleMode2 () { // still in test if shuffle mode gets set fast or even ever set
    Timer.scheduledTimer(withTimeInterval: 5, repeats: true) {timer in
        if self.musicPlayer.playbackState == .playing && self.musicPlayer.currentPlaybackTime > 3{
            self.musicPlayer.shuffleMode = .songs
            print("shuffle mode setter")
            timer.invalidate()
        }
    }
}

这里我有一个重复计时器,它总是检查某个项目是否正在播放以及它是否已经播放一段时间,如果是,则设置随机播放模式并停止重复。我已经测试了第二个函数,它工作得很好,尽管存在这样的问题:它总是有可能在一段时间内没有被调用。多长时间取决于时间间隔和 currentPlaybackTime > someTime 值。

关于ios - 无法设置 MPMusicPlayerController.shuffleMode,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51155537/

相关文章:

xcode - Collection View :didSelectItemAtIndexPath - does not get called

ios - 如何为纯 native iOS 应用程序(无 Web 应用程序)配置 "App Domain"(Open Graph 操作需要)?

ios - 以编程方式切换 tabBar 导致 IBOutlet 为 nil

ios - Realm 迁移 : Update multiple objects and fallback strategy?

ios - 索引(其中 :) method in swift is producing the wrong index

iphone - 当我尝试 "Validate"或 "Submit"1.1 版本的应用程序时,Xcode 4.0 崩溃

ios - 不使用 Xcode 配置设备

objective-c - 检索用户铃声库

ios - 使用 PerformSegueWithIdentifier 展开 Segue - 不起作用 - Swift 2.1

ios - 无法在 Xcode 6 中仅针对 iPhone 5 和 4 进行应用程序设计