Add Now Playing info center updates

This commit is contained in:
Arkadiusz Fal 2022-02-16 22:10:57 +01:00
parent 31a28a7cbd
commit 70ea098378
4 changed files with 109 additions and 53 deletions

View File

@ -40,7 +40,6 @@ final class AVPlayerBackend: PlayerBackend {
private var composition = AVMutableComposition()
private var loadedCompositionAssets = [AVMediaType]()
private var currentArtwork: MPMediaItemArtwork?
private var frequentTimeObserver: Any?
private var infrequentTimeObserver: Any?
private var playerTimeControlStatusObserver: Any?
@ -73,7 +72,7 @@ final class AVPlayerBackend: PlayerBackend {
_ stream: Stream,
of video: Video,
preservingTime: Bool,
upgrading: Bool
upgrading _: Bool
) {
if let url = stream.singleAssetURL {
model.logger.info("playing stream with one asset\(stream.kind == .hls ? " (HLS)" : ""): \(url)")
@ -85,10 +84,6 @@ final class AVPlayerBackend: PlayerBackend {
loadComposition(stream, of: video, preservingTime: preservingTime)
}
if !upgrading {
updateCurrentArtwork()
}
}
func play() {
@ -501,7 +496,7 @@ final class AVPlayerBackend: PlayerBackend {
self.controls.currentTime = self.currentTime ?? .zero
#if !os(tvOS)
self.updateNowPlayingInfo()
self.model.updateNowPlayingInfo()
#endif
if let currentTime = self.currentTime {
@ -566,48 +561,4 @@ final class AVPlayerBackend: PlayerBackend {
}
}
}
private func updateCurrentArtwork() {
guard let thumbnailData = try? Data(contentsOf: model.currentItem.video.thumbnailURL(quality: .medium)!) else {
return
}
#if os(macOS)
let image = NSImage(data: thumbnailData)
#else
let image = UIImage(data: thumbnailData)
#endif
if image.isNil {
return
}
currentArtwork = MPMediaItemArtwork(boundsSize: image!.size) { _ in image! }
}
fileprivate func updateNowPlayingInfo() {
var nowPlayingInfo: [String: AnyObject] = [
MPMediaItemPropertyTitle: model.currentItem.video.title as AnyObject,
MPMediaItemPropertyArtist: model.currentItem.video.author as AnyObject,
MPNowPlayingInfoPropertyIsLiveStream: model.currentItem.video.live as AnyObject,
MPNowPlayingInfoPropertyElapsedPlaybackTime: avPlayer.currentTime().seconds as AnyObject,
MPNowPlayingInfoPropertyPlaybackQueueCount: model.queue.count as AnyObject,
MPMediaItemPropertyMediaType: MPMediaType.anyVideo.rawValue as AnyObject
]
if !currentArtwork.isNil {
nowPlayingInfo[MPMediaItemPropertyArtwork] = currentArtwork as AnyObject
}
if !model.currentItem.video.live {
let itemDuration = model.currentItem.videoDuration ?? model.currentItem.duration
let duration = itemDuration.isFinite ? Double(itemDuration) : nil
if !duration.isNil {
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = duration as AnyObject
}
}
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
}

View File

@ -224,11 +224,13 @@ final class MPVBackend: PlayerBackend {
updateControls()
}
model.updateNowPlayingInfo()
if let currentTime = currentTime {
model.handleSegments(at: currentTime)
}
self.timeObserverThrottle.execute {
timeObserverThrottle.execute {
self.model.updateWatch()
}
}

View File

@ -97,6 +97,8 @@ final class PlayerModel: ObservableObject {
@Default(.closePiPAndOpenPlayerOnEnteringForeground) var closePiPAndOpenPlayerOnEnteringForeground
#endif
private var currentArtwork: MPMediaItemArtwork?
init(accounts: AccountsModel? = nil, comments: CommentsModel? = nil, controls: PlayerControlsModel? = nil) {
self.accounts = accounts ?? AccountsModel()
self.comments = comments ?? CommentsModel()
@ -232,6 +234,10 @@ final class PlayerModel: ObservableObject {
preservingTime: preservingTime,
upgrading: upgrading
)
if !upgrading {
updateCurrentArtwork()
}
}
func saveTime(completionHandler: @escaping () -> Void = {}) {
@ -418,4 +424,52 @@ final class PlayerModel: ObservableObject {
backend.exitFullScreen()
}
#endif
func updateNowPlayingInfo() {
let currentTime = (backend.currentTime?.seconds.isFinite ?? false) ? backend.currentTime!.seconds : 0
var nowPlayingInfo: [String: AnyObject] = [
MPMediaItemPropertyTitle: currentItem.video.title as AnyObject,
MPMediaItemPropertyArtist: currentItem.video.author as AnyObject,
MPNowPlayingInfoPropertyIsLiveStream: currentItem.video.live as AnyObject,
MPNowPlayingInfoPropertyElapsedPlaybackTime: currentTime as AnyObject,
MPNowPlayingInfoPropertyPlaybackQueueCount: queue.count as AnyObject,
MPNowPlayingInfoPropertyPlaybackQueueIndex: 1 as AnyObject,
MPMediaItemPropertyMediaType: MPMediaType.anyVideo.rawValue as AnyObject
]
if !currentArtwork.isNil {
nowPlayingInfo[MPMediaItemPropertyArtwork] = currentArtwork as AnyObject
}
if !currentItem.video.live {
let itemDuration = (backend.playerItemDuration ?? .zero).seconds
let duration = itemDuration.isFinite ? Double(itemDuration) : nil
if !duration.isNil {
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = duration as AnyObject
}
}
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
func updateCurrentArtwork() {
guard let video = currentVideo,
let thumbnailData = try? Data(contentsOf: video.thumbnailURL(quality: .medium)!)
else {
return
}
#if os(macOS)
let image = NSImage(data: thumbnailData)
#else
let image = UIImage(data: thumbnailData)
#endif
if image.isNil {
return
}
currentArtwork = MPMediaItemArtwork(boundsSize: image!.size) { _ in image! }
}
}

View File

@ -1,5 +1,6 @@
import AVFAudio
import Defaults
import MediaPlayer
import SDWebImage
import SDWebImagePINPlugin
import SDWebImageWebPCoder
@ -107,7 +108,7 @@ struct ContentView: View {
SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared)
SDWebImageManager.defaultImageCache = PINCache(name: "stream.yattee.app")
#if !os(macOS)
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
setupNowPlayingInfoCenter()
#endif
#if os(iOS)
@ -164,6 +165,54 @@ struct ContentView: View {
playlists.load()
}
func setupNowPlayingInfoCenter() {
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
UIApplication.shared.beginReceivingRemoteControlEvents()
MPRemoteCommandCenter.shared().playCommand.addTarget { _ in
player.play()
return .success
}
MPRemoteCommandCenter.shared().pauseCommand.addTarget { _ in
player.pause()
return .success
}
MPRemoteCommandCenter.shared().previousTrackCommand.isEnabled = false
MPRemoteCommandCenter.shared().nextTrackCommand.isEnabled = false
MPRemoteCommandCenter.shared().changePlaybackPositionCommand.addTarget { remoteEvent in
guard let event = remoteEvent as? MPChangePlaybackPositionCommandEvent
else {
return .commandFailed
}
player.backend.seek(to: event.positionTime)
return .success
}
let skipForwardCommand = MPRemoteCommandCenter.shared().skipForwardCommand
skipForwardCommand.isEnabled = true
skipForwardCommand.preferredIntervals = [10]
skipForwardCommand.addTarget { _ in
player.backend.seek(relative: .secondsInDefaultTimescale(10))
return .success
}
let skipBackwardCommand = MPRemoteCommandCenter.shared().skipBackwardCommand
skipBackwardCommand.isEnabled = true
skipBackwardCommand.preferredIntervals = [10]
skipBackwardCommand.addTarget { _ in
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
return .success
}
}
func openWelcomeScreenIfAccountEmpty() {
guard Defaults[.instances].isEmpty else {
return