MPMusicPlayerController.shuffleMode не может быть установлен - PullRequest
0 голосов
/ 03 июля 2018

Здравствуйте, у меня есть вопрос по поводу MPMusicPlayerController в Swift. В настоящее время я работаю над музыкальным приложением, в котором я хочу перемешивать музыку по песням. Поэтому, когда приложение запускается, оно в основном устанавливает очередь воспроизведения, а затем режим перемешивания. Я могу успешно установить очередь (и воспроизвести песни), но я получаю сообщение об ошибке при установке режима случайного воспроизведения:

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 может выглядеть немного грязно, хотя я не думаю, что проблема кроется там! В основном он извлекает некоторые данные из API Apple Music, который работает нормально, и добавляет песни в пользовательскую библиотеку, которая тоже работает.

1 Ответ

0 голосов
/ 11 августа 2018

Хорошо, попробовав все возможное, я нашел два решения, которые работают для меня. Как ни странно, я обнаружил, что остановка интерфейса происходит только тогда, когда ни одна песня не играла до сих пор. Если песня воспроизводится в данный момент или даже если песня воспроизводилась и после этого была приостановлена, пользовательский интерфейс не останавливается. Итак, я придумал эту функцию:

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...