无法设置MPMusicPlayerController.shuffleMode

时间:2018-07-03 13:12:02

标签: 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中获取一些可以正常工作的数据,并将歌曲添加到也可以工作的用户库中。<​​/ p>

1 个答案:

答案 0 :(得分:0)

好吧,在尝试了一切可能之后,我想出了两个对我有用的解决方案。奇怪的是,我发现只有当到目前为止没有歌曲播放时,才会发生界面冻结。如果当前正在播放一首歌曲,或者即使歌曲已播放并且随后被暂停,也不会冻结用户界面。所以我想出了这个功能:

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值。