2022-05-20 21:23:14 +00:00
|
|
|
import AVKit
|
2021-09-25 08:18:22 +00:00
|
|
|
import Defaults
|
2021-07-18 22:32:46 +00:00
|
|
|
import SwiftUI
|
|
|
|
|
2023-05-20 20:49:10 +00:00
|
|
|
#if !os(macOS)
|
|
|
|
final class AppleAVPlayerViewControllerDelegate: NSObject, AVPlayerViewControllerDelegate {
|
|
|
|
var player: PlayerModel { .shared }
|
|
|
|
|
|
|
|
func playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart(_: AVPlayerViewController) -> Bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
func playerViewController(_: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator) {
|
|
|
|
Delay.by(0.5) { [weak self] in
|
|
|
|
self?.player.playingFullScreen = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func playerViewController(_: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
|
|
|
|
let wasPlaying = player.isPlaying
|
|
|
|
coordinator.animate(alongsideTransition: nil) { context in
|
|
|
|
#if os(iOS)
|
|
|
|
if wasPlaying {
|
|
|
|
self.player.play()
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if !context.isCancelled {
|
|
|
|
#if os(iOS)
|
|
|
|
self.player.lockedOrientation = nil
|
|
|
|
|
|
|
|
if Defaults[.rotateToPortraitOnExitFullScreen] {
|
|
|
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
|
|
|
}
|
|
|
|
|
|
|
|
if wasPlaying {
|
|
|
|
self.player.play()
|
|
|
|
}
|
|
|
|
|
|
|
|
self.player.playingFullScreen = false
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func playerViewControllerWillStartPictureInPicture(_: AVPlayerViewController) {}
|
|
|
|
|
|
|
|
func playerViewControllerWillStopPictureInPicture(_: AVPlayerViewController) {}
|
|
|
|
|
|
|
|
func playerViewControllerDidStartPictureInPicture(_: AVPlayerViewController) {
|
|
|
|
player.playingInPictureInPicture = true
|
|
|
|
player.avPlayerBackend.startPictureInPictureOnPlay = false
|
|
|
|
player.avPlayerBackend.startPictureInPictureOnSwitch = false
|
|
|
|
player.controls.objectWillChange.send()
|
|
|
|
|
|
|
|
if Defaults[.closePlayerOnOpeningPiP] { Delay.by(0.1) { self.player.hide() } }
|
|
|
|
}
|
|
|
|
|
|
|
|
func playerViewControllerDidStopPictureInPicture(_: AVPlayerViewController) {
|
|
|
|
player.playingInPictureInPicture = false
|
|
|
|
player.controls.objectWillChange.send()
|
|
|
|
}
|
|
|
|
|
|
|
|
func playerViewController(_: AVPlayerViewController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
|
|
|
|
player.presentingPlayer = true
|
|
|
|
withAnimation(.linear(duration: 0.3)) {
|
|
|
|
self.player.playingInPictureInPicture = false
|
|
|
|
Delay.by(0.5) {
|
|
|
|
completionHandler(true)
|
|
|
|
Delay.by(0.2) {
|
|
|
|
self.player.play()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func playerViewController(_: AVPlayerViewController, restoreUserInterfaceForFullScreenExitWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
|
|
|
|
withAnimation(nil) {
|
|
|
|
player.presentingPlayer = true
|
|
|
|
}
|
|
|
|
|
|
|
|
completionHandler(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-08-20 21:05:40 +00:00
|
|
|
#if os(iOS)
|
2023-05-20 20:49:10 +00:00
|
|
|
struct AppleAVPlayerView: UIViewControllerRepresentable {
|
|
|
|
@State private var controller = AVPlayerViewController()
|
|
|
|
|
|
|
|
func makeUIViewController(context _: Context) -> AVPlayerViewController {
|
|
|
|
setupController()
|
|
|
|
return controller
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateUIViewController(_: AVPlayerViewController, context _: Context) {
|
|
|
|
setupController()
|
|
|
|
}
|
|
|
|
|
|
|
|
func setupController() {
|
|
|
|
controller.delegate = PlayerModel.shared.appleAVPlayerViewControllerDelegate
|
|
|
|
controller.allowsPictureInPicturePlayback = true
|
|
|
|
if #available(iOS 14.2, *) {
|
|
|
|
controller.canStartPictureInPictureAutomaticallyFromInline = true
|
|
|
|
}
|
|
|
|
PlayerModel.shared.avPlayerBackend.controller = controller
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct AppleAVPlayerLayerView: UIViewRepresentable {
|
2022-08-20 21:05:40 +00:00
|
|
|
func makeUIView(context _: Context) -> some UIView {
|
2023-05-20 20:49:10 +00:00
|
|
|
PlayerLayerView(frame: .zero)
|
2022-08-20 21:05:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func updateUIView(_: UIViewType, context _: Context) {}
|
2021-07-18 22:32:46 +00:00
|
|
|
}
|
2023-05-20 20:49:10 +00:00
|
|
|
|
|
|
|
#elseif os(tvOS)
|
2022-08-20 21:05:40 +00:00
|
|
|
struct AppleAVPlayerView: UIViewControllerRepresentable {
|
|
|
|
func makeUIViewController(context _: Context) -> AppleAVPlayerViewController {
|
|
|
|
let controller = AppleAVPlayerViewController()
|
2022-11-24 20:36:05 +00:00
|
|
|
PlayerModel.shared.avPlayerBackend.controller = controller
|
2022-08-20 21:05:40 +00:00
|
|
|
|
|
|
|
return controller
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateUIViewController(_: AppleAVPlayerViewController, context _: Context) {
|
2022-11-24 20:36:05 +00:00
|
|
|
PlayerModel.shared.rebuildTVMenu()
|
2022-08-20 21:05:40 +00:00
|
|
|
}
|
|
|
|
}
|
2023-05-20 20:49:10 +00:00
|
|
|
#else
|
|
|
|
struct AppleAVPlayerView: NSViewRepresentable {
|
|
|
|
@State private var pictureInPictureDelegate = MacOSPiPDelegate()
|
|
|
|
|
|
|
|
func makeNSView(context _: Context) -> some NSView {
|
|
|
|
let view = AVPlayerView()
|
|
|
|
view.player = PlayerModel.shared.avPlayerBackend.avPlayer
|
|
|
|
view.showsFullScreenToggleButton = true
|
|
|
|
view.allowsPictureInPicturePlayback = true
|
|
|
|
view.pictureInPictureDelegate = pictureInPictureDelegate
|
|
|
|
return view
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateNSView(_: NSViewType, context _: Context) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct AppleAVPlayerLayerView: NSViewRepresentable {
|
|
|
|
func makeNSView(context _: Context) -> some NSView {
|
|
|
|
PlayerLayerView(frame: .zero)
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateNSView(_: NSViewType, context _: Context) {}
|
|
|
|
}
|
2022-08-20 21:05:40 +00:00
|
|
|
#endif
|