Merge pull request #765 from stonerl/partial-fix-for-503

Update now playing info when using system controls – Partial fix for 503
This commit is contained in:
Arkadiusz Fal 2024-08-31 13:21:41 +02:00 committed by GitHub
commit 2026201a5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 70 additions and 29 deletions

View File

@ -102,7 +102,7 @@ final class AVPlayerBackend: PlayerBackend {
private var frequentTimeObserver: Any? private var frequentTimeObserver: Any?
private var infrequentTimeObserver: Any? private var infrequentTimeObserver: Any?
private var playerTimeControlStatusObserver: Any? private var playerTimeControlStatusObserver: NSKeyValueObservation?
private var statusObservation: NSKeyValueObservation? private var statusObservation: NSKeyValueObservation?
@ -119,6 +119,26 @@ final class AVPlayerBackend: PlayerBackend {
#if os(iOS) #if os(iOS)
controller.player = avPlayer controller.player = avPlayer
#endif #endif
logger.info("AVPlayerBackend initialized.")
}
deinit {
// Invalidate any observers to avoid memory leaks
statusObservation?.invalidate()
playerTimeControlStatusObserver?.invalidate()
// Remove any time observers added to AVPlayer
if let frequentObserver = frequentTimeObserver {
avPlayer.removeTimeObserver(frequentObserver)
}
if let infrequentObserver = infrequentTimeObserver {
avPlayer.removeTimeObserver(infrequentObserver)
}
// Remove notification observers
removeItemDidPlayToEndTimeObserver()
logger.info("AVPlayerBackend deinitialized.")
} }
func canPlay(_ stream: Stream) -> Bool { func canPlay(_ stream: Stream) -> Bool {
@ -779,7 +799,7 @@ final class AVPlayerBackend: PlayerBackend {
opened = true opened = true
controller.startPictureInPicture() controller.startPictureInPicture()
} else { } else {
print("PiP not possible, waited \(delay) seconds") self.logger.info("PiP not possible, waited \(delay) seconds")
} }
} }
} }

View File

@ -881,26 +881,29 @@ final class PlayerModel: ObservableObject {
} }
func updateRemoteCommandCenter() { func updateRemoteCommandCenter() {
let skipForwardCommand = MPRemoteCommandCenter.shared().skipForwardCommand let commandCenter = MPRemoteCommandCenter.shared()
let skipBackwardCommand = MPRemoteCommandCenter.shared().skipBackwardCommand let skipForwardCommand = commandCenter.skipForwardCommand
let previousTrackCommand = MPRemoteCommandCenter.shared().previousTrackCommand let skipBackwardCommand = commandCenter.skipBackwardCommand
let nextTrackCommand = MPRemoteCommandCenter.shared().nextTrackCommand let previousTrackCommand = commandCenter.previousTrackCommand
let nextTrackCommand = commandCenter.nextTrackCommand
if !remoteCommandCenterConfigured { if !remoteCommandCenterConfigured {
remoteCommandCenterConfigured = true remoteCommandCenterConfigured = true
#if !os(macOS)
try? AVAudioSession.sharedInstance().setCategory(
.playback,
mode: .moviePlayback
)
UIApplication.shared.beginReceivingRemoteControlEvents()
#endif
let interval = TimeInterval(systemControlsSeekDuration) ?? 10 let interval = TimeInterval(systemControlsSeekDuration) ?? 10
let preferredIntervals = [NSNumber(value: interval)] let preferredIntervals = [NSNumber(value: interval)]
// Remove existing targets to avoid duplicates
skipForwardCommand.removeTarget(nil)
skipBackwardCommand.removeTarget(nil)
previousTrackCommand.removeTarget(nil)
nextTrackCommand.removeTarget(nil)
commandCenter.playCommand.removeTarget(nil)
commandCenter.pauseCommand.removeTarget(nil)
commandCenter.togglePlayPauseCommand.removeTarget(nil)
commandCenter.changePlaybackPositionCommand.removeTarget(nil)
// Re-add targets for handling commands
skipForwardCommand.preferredIntervals = preferredIntervals skipForwardCommand.preferredIntervals = preferredIntervals
skipBackwardCommand.preferredIntervals = preferredIntervals skipBackwardCommand.preferredIntervals = preferredIntervals
@ -924,22 +927,22 @@ final class PlayerModel: ObservableObject {
return .success return .success
} }
MPRemoteCommandCenter.shared().playCommand.addTarget { [weak self] _ in commandCenter.playCommand.addTarget { [weak self] _ in
self?.play() self?.play()
return .success return .success
} }
MPRemoteCommandCenter.shared().pauseCommand.addTarget { [weak self] _ in commandCenter.pauseCommand.addTarget { [weak self] _ in
self?.pause() self?.pause()
return .success return .success
} }
MPRemoteCommandCenter.shared().togglePlayPauseCommand.addTarget { [weak self] _ in commandCenter.togglePlayPauseCommand.addTarget { [weak self] _ in
self?.togglePlay() self?.togglePlay()
return .success return .success
} }
MPRemoteCommandCenter.shared().changePlaybackPositionCommand.addTarget { [weak self] remoteEvent in commandCenter.changePlaybackPositionCommand.addTarget { [weak self] remoteEvent in
guard let event = remoteEvent as? MPChangePlaybackPositionCommandEvent else { return .commandFailed } guard let event = remoteEvent as? MPChangePlaybackPositionCommandEvent else { return .commandFailed }
self?.backend.seek(to: event.positionTime, seekType: .userInteracted) self?.backend.seek(to: event.positionTime, seekType: .userInteracted)
@ -1038,18 +1041,22 @@ final class PlayerModel: ObservableObject {
guard activeBackend == .mpv else { return } guard activeBackend == .mpv else { return }
#endif #endif
#if os(iOS)
if activeBackend == .appleAVPlayer, avPlayerUsesSystemControls {
return
}
#endif
guard let video = currentItem?.video else { guard let video = currentItem?.video else {
MPNowPlayingInfoCenter.default().nowPlayingInfo = .none MPNowPlayingInfoCenter.default().nowPlayingInfo = .none
return return
} }
let currentTime = (backend.currentTime?.seconds.isFinite ?? false) ? backend.currentTime!.seconds : 0 let currentTime = (backend.currentTime?.seconds.isFinite ?? false) ? backend.currentTime!.seconds : 0
// Determine the media type based on musicMode
let mediaType: NSNumber
if musicMode {
mediaType = MPMediaType.anyAudio.rawValue as NSNumber
} else {
mediaType = MPMediaType.anyVideo.rawValue as NSNumber
}
// Prepare the Now Playing info dictionary
var nowPlayingInfo: [String: AnyObject] = [ var nowPlayingInfo: [String: AnyObject] = [
MPMediaItemPropertyTitle: video.displayTitle as AnyObject, MPMediaItemPropertyTitle: video.displayTitle as AnyObject,
MPMediaItemPropertyArtist: video.displayAuthor as AnyObject, MPMediaItemPropertyArtist: video.displayAuthor as AnyObject,
@ -1057,7 +1064,7 @@ final class PlayerModel: ObservableObject {
MPNowPlayingInfoPropertyElapsedPlaybackTime: currentTime as AnyObject, MPNowPlayingInfoPropertyElapsedPlaybackTime: currentTime as AnyObject,
MPNowPlayingInfoPropertyPlaybackQueueCount: queue.count as AnyObject, MPNowPlayingInfoPropertyPlaybackQueueCount: queue.count as AnyObject,
MPNowPlayingInfoPropertyPlaybackQueueIndex: 1 as AnyObject, MPNowPlayingInfoPropertyPlaybackQueueIndex: 1 as AnyObject,
MPMediaItemPropertyMediaType: MPMediaType.anyVideo.rawValue as AnyObject MPMediaItemPropertyMediaType: mediaType
] ]
if !currentArtwork.isNil { if !currentArtwork.isNil {
@ -1261,7 +1268,7 @@ final class PlayerModel: ObservableObject {
} }
private func destroyKeyPressMonitor() { private func destroyKeyPressMonitor() {
if let keyPressMonitor = keyPressMonitor { if let keyPressMonitor {
NSEvent.removeMonitor(keyPressMonitor) NSEvent.removeMonitor(keyPressMonitor)
} }
} }

View File

@ -1,9 +1,12 @@
import AVFoundation
import Foundation import Foundation
import Logging
import UIKit import UIKit
final class AppDelegate: UIResponder, UIApplicationDelegate { final class AppDelegate: UIResponder, UIApplicationDelegate {
var orientationLock = UIInterfaceOrientationMask.all var orientationLock = UIInterfaceOrientationMask.all
private var logger = Logger(label: "stream.yattee.app.delegalate")
private(set) static var instance: AppDelegate! private(set) static var instance: AppDelegate!
func application(_: UIApplication, supportedInterfaceOrientationsFor _: UIWindow?) -> UIInterfaceOrientationMask { func application(_: UIApplication, supportedInterfaceOrientationsFor _: UIWindow?) -> UIInterfaceOrientationMask {
@ -12,11 +15,22 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // swiftlint:disable:this discouraged_optional_collection func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // swiftlint:disable:this discouraged_optional_collection
Self.instance = self Self.instance = self
#if os(iOS)
UIViewController.swizzleHomeIndicatorProperty()
#if !os(macOS)
UIViewController.swizzleHomeIndicatorProperty()
OrientationTracker.shared.startDeviceOrientationTracking() OrientationTracker.shared.startDeviceOrientationTracking()
// Configure the audio session for playback
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
} catch {
logger.error("Failed to set audio session category: \(error)")
}
// Begin receiving remote control events
UIApplication.shared.beginReceivingRemoteControlEvents()
#endif #endif
return true return true
} }