mirror of
https://github.com/yattee/yattee.git
synced 2024-12-14 22:30:32 +05:30
Add Now Playing info center updates
This commit is contained in:
parent
31a28a7cbd
commit
70ea098378
@ -40,7 +40,6 @@ final class AVPlayerBackend: PlayerBackend {
|
|||||||
private var composition = AVMutableComposition()
|
private var composition = AVMutableComposition()
|
||||||
private var loadedCompositionAssets = [AVMediaType]()
|
private var loadedCompositionAssets = [AVMediaType]()
|
||||||
|
|
||||||
private var currentArtwork: MPMediaItemArtwork?
|
|
||||||
private var frequentTimeObserver: Any?
|
private var frequentTimeObserver: Any?
|
||||||
private var infrequentTimeObserver: Any?
|
private var infrequentTimeObserver: Any?
|
||||||
private var playerTimeControlStatusObserver: Any?
|
private var playerTimeControlStatusObserver: Any?
|
||||||
@ -73,7 +72,7 @@ final class AVPlayerBackend: PlayerBackend {
|
|||||||
_ stream: Stream,
|
_ stream: Stream,
|
||||||
of video: Video,
|
of video: Video,
|
||||||
preservingTime: Bool,
|
preservingTime: Bool,
|
||||||
upgrading: Bool
|
upgrading _: Bool
|
||||||
) {
|
) {
|
||||||
if let url = stream.singleAssetURL {
|
if let url = stream.singleAssetURL {
|
||||||
model.logger.info("playing stream with one asset\(stream.kind == .hls ? " (HLS)" : ""): \(url)")
|
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)
|
loadComposition(stream, of: video, preservingTime: preservingTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !upgrading {
|
|
||||||
updateCurrentArtwork()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func play() {
|
func play() {
|
||||||
@ -501,7 +496,7 @@ final class AVPlayerBackend: PlayerBackend {
|
|||||||
self.controls.currentTime = self.currentTime ?? .zero
|
self.controls.currentTime = self.currentTime ?? .zero
|
||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
self.updateNowPlayingInfo()
|
self.model.updateNowPlayingInfo()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if let currentTime = self.currentTime {
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -224,11 +224,13 @@ final class MPVBackend: PlayerBackend {
|
|||||||
updateControls()
|
updateControls()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model.updateNowPlayingInfo()
|
||||||
|
|
||||||
if let currentTime = currentTime {
|
if let currentTime = currentTime {
|
||||||
model.handleSegments(at: currentTime)
|
model.handleSegments(at: currentTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.timeObserverThrottle.execute {
|
timeObserverThrottle.execute {
|
||||||
self.model.updateWatch()
|
self.model.updateWatch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,8 @@ final class PlayerModel: ObservableObject {
|
|||||||
@Default(.closePiPAndOpenPlayerOnEnteringForeground) var closePiPAndOpenPlayerOnEnteringForeground
|
@Default(.closePiPAndOpenPlayerOnEnteringForeground) var closePiPAndOpenPlayerOnEnteringForeground
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private var currentArtwork: MPMediaItemArtwork?
|
||||||
|
|
||||||
init(accounts: AccountsModel? = nil, comments: CommentsModel? = nil, controls: PlayerControlsModel? = nil) {
|
init(accounts: AccountsModel? = nil, comments: CommentsModel? = nil, controls: PlayerControlsModel? = nil) {
|
||||||
self.accounts = accounts ?? AccountsModel()
|
self.accounts = accounts ?? AccountsModel()
|
||||||
self.comments = comments ?? CommentsModel()
|
self.comments = comments ?? CommentsModel()
|
||||||
@ -232,6 +234,10 @@ final class PlayerModel: ObservableObject {
|
|||||||
preservingTime: preservingTime,
|
preservingTime: preservingTime,
|
||||||
upgrading: upgrading
|
upgrading: upgrading
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if !upgrading {
|
||||||
|
updateCurrentArtwork()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveTime(completionHandler: @escaping () -> Void = {}) {
|
func saveTime(completionHandler: @escaping () -> Void = {}) {
|
||||||
@ -418,4 +424,52 @@ final class PlayerModel: ObservableObject {
|
|||||||
backend.exitFullScreen()
|
backend.exitFullScreen()
|
||||||
}
|
}
|
||||||
#endif
|
#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! }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import AVFAudio
|
import AVFAudio
|
||||||
import Defaults
|
import Defaults
|
||||||
|
import MediaPlayer
|
||||||
import SDWebImage
|
import SDWebImage
|
||||||
import SDWebImagePINPlugin
|
import SDWebImagePINPlugin
|
||||||
import SDWebImageWebPCoder
|
import SDWebImageWebPCoder
|
||||||
@ -107,7 +108,7 @@ struct ContentView: View {
|
|||||||
SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared)
|
SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared)
|
||||||
SDWebImageManager.defaultImageCache = PINCache(name: "stream.yattee.app")
|
SDWebImageManager.defaultImageCache = PINCache(name: "stream.yattee.app")
|
||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
|
setupNowPlayingInfoCenter()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -164,6 +165,54 @@ struct ContentView: View {
|
|||||||
playlists.load()
|
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() {
|
func openWelcomeScreenIfAccountEmpty() {
|
||||||
guard Defaults[.instances].isEmpty else {
|
guard Defaults[.instances].isEmpty else {
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user