mirror of
				https://github.com/yattee/yattee.git
				synced 2025-10-31 04:31:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			164 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
| import AVKit
 | |
| import Defaults
 | |
| import SwiftUI
 | |
| 
 | |
| #if !os(macOS)
 | |
|     final class AppleAVPlayerViewControllerDelegate: NSObject, AVPlayerViewControllerDelegate {
 | |
|         #if os(iOS)
 | |
|             @Default(.rotateToLandscapeOnEnterFullScreen) private var rotateToLandscapeOnEnterFullScreen
 | |
|             @Default(.avPlayerUsesSystemControls) private var avPlayerUsesSystemControls
 | |
|         #endif
 | |
| 
 | |
|         var player: PlayerModel { .shared }
 | |
| 
 | |
|         func playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart(_: AVPlayerViewController) -> Bool {
 | |
|             false
 | |
|         }
 | |
| 
 | |
|         #if os(iOS)
 | |
|             func playerViewController(_: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator) {
 | |
|                 guard rotateToLandscapeOnEnterFullScreen.isRotating else { return }
 | |
|                 if PlayerModel.shared.currentVideoIsLandscape {
 | |
|                     let delay = PlayerModel.shared.activeBackend == .appleAVPlayer && avPlayerUsesSystemControls ? 0.8 : 0
 | |
|                     // not sure why but first rotation call is ignore so doing rotate to same orientation first
 | |
|                     Delay.by(delay) {
 | |
|                         let orientation = OrientationTracker.shared.currentDeviceOrientation.isLandscape ? OrientationTracker.shared.currentInterfaceOrientation : self.rotateToLandscapeOnEnterFullScreen.interaceOrientation
 | |
|                         Orientation.lockOrientation(.allButUpsideDown, andRotateTo: OrientationTracker.shared.currentInterfaceOrientation)
 | |
|                         Orientation.lockOrientation(.allButUpsideDown, andRotateTo: orientation)
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             func playerViewController(_: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
 | |
|                 let wasPlaying = player.isPlaying
 | |
|                 coordinator.animate(alongsideTransition: nil) { context in
 | |
|                     if wasPlaying {
 | |
|                         self.player.play()
 | |
|                     }
 | |
|                     if !context.isCancelled {
 | |
|                         #if os(iOS)
 | |
|                             self.player.lockedOrientation = nil
 | |
| 
 | |
|                             if Constants.isIPhone {
 | |
|                                 Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
 | |
|                             }
 | |
| 
 | |
|                             if wasPlaying {
 | |
|                                 self.player.play()
 | |
|                             }
 | |
| 
 | |
|                             self.player.playingFullScreen = false
 | |
|                         #endif
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             func playerViewController(_: AVPlayerViewController, restoreUserInterfaceForFullScreenExitWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
 | |
|                 withAnimation(nil) {
 | |
|                     player.presentingPlayer = true
 | |
|                 }
 | |
| 
 | |
|                 completionHandler(true)
 | |
|             }
 | |
|         #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()
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| #endif
 | |
| 
 | |
| #if os(iOS)
 | |
|     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 {
 | |
|         func makeUIView(context _: Context) -> some UIView {
 | |
|             PlayerLayerView(frame: .zero)
 | |
|         }
 | |
| 
 | |
|         func updateUIView(_: UIViewType, context _: Context) {}
 | |
|     }
 | |
| 
 | |
| #elseif os(tvOS)
 | |
|     struct AppleAVPlayerView: UIViewControllerRepresentable {
 | |
|         func makeUIViewController(context _: Context) -> AppleAVPlayerViewController {
 | |
|             let controller = AppleAVPlayerViewController()
 | |
|             PlayerModel.shared.avPlayerBackend.controller = controller
 | |
| 
 | |
|             return controller
 | |
|         }
 | |
| 
 | |
|         func updateUIViewController(_: AppleAVPlayerViewController, context _: Context) {
 | |
|             PlayerModel.shared.rebuildTVMenu()
 | |
|         }
 | |
|     }
 | |
| #else
 | |
|     struct AppleAVPlayerView: NSViewRepresentable {
 | |
|         func makeNSView(context _: Context) -> some NSView {
 | |
|             let view = AVPlayerView()
 | |
|             view.player = PlayerModel.shared.avPlayerBackend.avPlayer
 | |
|             view.showsFullScreenToggleButton = true
 | |
|             view.allowsPictureInPicturePlayback = true
 | |
|             view.pictureInPictureDelegate = MacOSPiPDelegate.shared
 | |
|             return view
 | |
|         }
 | |
| 
 | |
|         func updateNSView(_: NSViewType, context _: Context) {}
 | |
|     }
 | |
| 
 | |
|     struct AppleAVPlayerLayerView: NSViewRepresentable {
 | |
|         func makeNSView(context _: Context) -> some NSView {
 | |
|             PlayerLayerView(frame: .zero)
 | |
|         }
 | |
| 
 | |
|         func updateNSView(_: NSViewType, context _: Context) {}
 | |
|     }
 | |
| #endif
 | 
