MPMusicPlayerController.systemMusicPlayer setQueue 困难

MPMusicPlayerController.systemMusicPlayer setQueue difficulties

我的音乐应用程序有问题。这个想法是点击一个按钮并播放当前正在播放的艺术家的更多歌曲。当我在应用程序中并点击下一首歌曲时,它工作正常。但是,如果我让歌曲自然结束,应用程序会随机播放一首歌曲。所以我添加了逻辑来说明如果歌曲剩余时间为 0,则使用媒体播放器功能播放下一首歌曲,因为我知道这行得通。这解决了它,除非应用程序在后台。如果应用程序在后台运行,我试图让它保持活动状态,但我想这不起作用。

每次我认为我已经解决了问题,但问题又回来了。

我希望发生的事情 是锁定艺术家,将应用程序关闭到后台,当歌曲结束时播放另一首艺术家的歌曲。

实际发生的事情 是当我锁定艺术家并将应用程序关闭到后台时,歌曲将结束,有时它会播放正确的歌曲。有时它不会。 然而 当它播放错误的歌曲并且我重新打开应用程序时它会结束当前(错误)播放的歌曲并开始播放正确艺术家的歌曲

要清楚,我认为当歌曲剩余时间为 0 时需要运行逻辑以跳到下一首歌曲是不明智的,但无论出于何种原因我需要它

我已将功能设置为后台获取和音频 Airplay 以及画中画

我试过按顺序只post相关代码..

我有一个属性

let mediaPlayer = MPMusicPlayerController.systemMusicPlayer
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid

在我的 ViewDidLoad

try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
try? AVAudioSession.sharedInstance().setActive(true)


DispatchQueue.main.async {
  self.clearSongInfo()
  MediaManager.shared.getAllSongs { (songs) in
    guard let theSongs = songs else {
      return
    }
    self.mediaPlayer.nowPlayingItem = nil

    self.newSongs = theSongs.filter({ (item) -> Bool in
      return !MediaManager.shared.playedSongs.contains(item)
    })
    self.aSongIsInChamber = true
    self.mediaPlayer.setQueue(with: MPMediaItemCollection(items: self.newSongs.shuffled())
    )
    self.mediaPlayer.shuffleMode = .off
    self.mediaPlayer.repeatMode = .none

  }
  NotificationCenter.default.addObserver(self, selector: #selector(self.songChanged(_:)), name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange, object: self.mediaPlayer)
  self.mediaPlayer.beginGeneratingPlaybackNotifications()
}

我有一个函数可以更新播放时间和剩余时间,我有

if Int(self.songProgressSlider.maximumValue - self.songProgressSlider.value) < 1 {
  print("song ended naturally, skipped")
  mediaPlayer.prepareToPlay(completionHandler: { (error) in
    DispatchQueue.main.async {
      self.mediaPlayer.skipToNextItem()
    }
  })
}

当我播放音乐时我有一个布尔值 isPlaying

如果是,那么时间是 运行 我有这个

let app = UIApplication.shared
  var task: UIBackgroundTaskIdentifier?
  task  = app.beginBackgroundTask {
    app.endBackgroundTask(task!)
  }

我要让计时器在后台保持开启状态,以便在剩余时间为 0 时播放下一首歌曲

现在要锁定一位艺术家,我有以下在点击按钮时执行的代码

let artistPredicate: MPMediaPropertyPredicate  = MPMediaPropertyPredicate(value: nowPlaying.artist, forProperty: MPMediaItemPropertyArtist)
      let query: MPMediaQuery = MPMediaQuery.artists()
      let musicPlayerController: MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer

      query.addFilterPredicate(artistPredicate)
      musicPlayerController.setQueue(with: query)

我找到了一个不是 100% 我想要的解决方案,但现在我会接受它。

在我锁定艺术家的 IBAction 上,我最初只是

self.mediaPlayer.prepareToPlay { error in
  let artistPredicate: MPMediaPropertyPredicate  = MPMediaPropertyPredicate(value: nowPlaying.artist, forProperty: MPMediaItemPropertyArtist)
  let query: MPMediaQuery = MPMediaQuery.artists()
  let musicPlayerController: MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer

  query.addFilterPredicate(artistPredicate)
  musicPlayerController.setQueue(with: query)
}

添加了我为解决问题所做的工作

musicPlayerController.play()

设置队列后

这种方法的缺点是,如果我正在播放一首歌曲并点击按钮锁定艺术家,应用程序会立即结束正在播放的歌曲并转到该艺术家的歌曲,我宁愿歌曲完成播放然后播放我想要的歌曲。

如果有人能想出一种方法来使用 play() 并让歌曲自然结束,请将答案标记为正确...

注意 我从不想使用检查歌曲剩余时长的逻辑,也从不想使用后台代码。所以我摆脱了它,它仍然适用于新想法。

我发现设置队列不会 "take" 直到你说 prepareToPlay(字面上或隐含地说 play)。我的书的当前版本说:

My experience is that the player can behave in unexpected ways if you don't ask it to play, or at least prepareToPlay, immediately after setting the queue. Apparently the queue does not actually take effect until you do that.

你也说过(在评论中):

ideally I would like the song to finish first and then go onto the queue I have

好的,但也许您想要的是(iOS 11 中的新增功能)append 功能,它允许您修改队列而不完全替换它。