mirror of
				https://github.com/yattee/yattee.git
				synced 2025-11-03 22:22:02 +00:00 
			
		
		
		
	Add Open in PiP option (fix #137)
This commit is contained in:
		@@ -37,6 +37,9 @@ final class PiPDelegate: NSObject, AVPictureInPictureControllerDelegate {
 | 
				
			|||||||
        _: AVPictureInPictureController,
 | 
					        _: AVPictureInPictureController,
 | 
				
			||||||
        restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
 | 
					        restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
 | 
					        player?.show()
 | 
				
			||||||
 | 
					        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
 | 
				
			||||||
            completionHandler(true)
 | 
					            completionHandler(true)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -109,6 +109,29 @@ final class PlayerControlsModel: ObservableObject {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func startPiP(startImmediately: Bool = true) {
 | 
				
			||||||
 | 
					        if player.activeBackend == .mpv {
 | 
				
			||||||
 | 
					            player.avPlayerBackend.switchToMPVOnPipClose = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #if !os(macOS)
 | 
				
			||||||
 | 
					            player.exitFullScreen()
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if player.activeBackend != PlayerBackendType.appleAVPlayer {
 | 
				
			||||||
 | 
					            player.saveTime { [weak player] in
 | 
				
			||||||
 | 
					                player?.changeActiveBackend(from: .mpv, to: .appleAVPlayer)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [weak player] in
 | 
				
			||||||
 | 
					            player?.avPlayerBackend.startPictureInPictureOnPlay = true
 | 
				
			||||||
 | 
					            if startImmediately {
 | 
				
			||||||
 | 
					                player?.pipController?.startPictureInPicture()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func removeTimer() {
 | 
					    func removeTimer() {
 | 
				
			||||||
        timer?.invalidate()
 | 
					        timer?.invalidate()
 | 
				
			||||||
        timer = nil
 | 
					        timer = nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -205,7 +205,7 @@ final class PlayerModel: ObservableObject {
 | 
				
			|||||||
        backend.pause()
 | 
					        backend.pause()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func play(_ video: Video, at time: CMTime? = nil) {
 | 
					    func play(_ video: Video, at time: CMTime? = nil, showingPlayer: Bool = true) {
 | 
				
			||||||
        var delay = 0.0
 | 
					        var delay = 0.0
 | 
				
			||||||
        #if !os(macOS)
 | 
					        #if !os(macOS)
 | 
				
			||||||
            delay = 0.3
 | 
					            delay = 0.3
 | 
				
			||||||
@@ -223,8 +223,10 @@ final class PlayerModel: ObservableObject {
 | 
				
			|||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if showingPlayer {
 | 
				
			||||||
            show()
 | 
					            show()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func playStream(
 | 
					    func playStream(
 | 
				
			||||||
        _ stream: Stream,
 | 
					        _ stream: Stream,
 | 
				
			||||||
@@ -268,6 +270,7 @@ final class PlayerModel: ObservableObject {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    func saveTime(completionHandler: @escaping () -> Void = {}) {
 | 
					    func saveTime(completionHandler: @escaping () -> Void = {}) {
 | 
				
			||||||
        guard let currentTime = backend.currentTime, currentTime.seconds > 0 else {
 | 
					        guard let currentTime = backend.currentTime, currentTime.seconds > 0 else {
 | 
				
			||||||
 | 
					            completionHandler()
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -341,6 +344,10 @@ final class PlayerModel: ObservableObject {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func changeActiveBackend(from: PlayerBackendType, to: PlayerBackendType) {
 | 
					    func changeActiveBackend(from: PlayerBackendType, to: PlayerBackendType) {
 | 
				
			||||||
 | 
					        guard activeBackend != to else {
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Defaults[.activeBackend] = to
 | 
					        Defaults[.activeBackend] = to
 | 
				
			||||||
        self.activeBackend = to
 | 
					        self.activeBackend = to
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -265,25 +265,7 @@ struct PlayerControls: View {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private var pipButton: some View {
 | 
					    private var pipButton: some View {
 | 
				
			||||||
        button("PiP", systemImage: "pip") {
 | 
					        button("PiP", systemImage: "pip") {
 | 
				
			||||||
            if player.activeBackend == .mpv {
 | 
					            model.startPiP()
 | 
				
			||||||
                player.avPlayerBackend.switchToMPVOnPipClose = true
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            #if !os(macOS)
 | 
					 | 
				
			||||||
                player.exitFullScreen()
 | 
					 | 
				
			||||||
            #endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if player.activeBackend != PlayerBackendType.appleAVPlayer {
 | 
					 | 
				
			||||||
                player.saveTime {
 | 
					 | 
				
			||||||
                    player.changeActiveBackend(from: .mpv, to: .appleAVPlayer)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
 | 
					 | 
				
			||||||
                print(player.pipController?.isPictureInPicturePossible ?? false ? "possible" : "NOT possible")
 | 
					 | 
				
			||||||
                player.avPlayerBackend.startPictureInPictureOnPlay = true
 | 
					 | 
				
			||||||
                player.pipController?.startPictureInPicture()
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import CoreData
 | 
					import CoreData
 | 
				
			||||||
 | 
					import CoreMedia
 | 
				
			||||||
import Defaults
 | 
					import Defaults
 | 
				
			||||||
import SwiftUI
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -53,6 +54,9 @@ struct VideoContextMenuView: View {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Section {
 | 
					        Section {
 | 
				
			||||||
            playNowButton
 | 
					            playNowButton
 | 
				
			||||||
 | 
					            #if os(iOS)
 | 
				
			||||||
 | 
					                playNowInPictureInPictureButton
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Section {
 | 
					        Section {
 | 
				
			||||||
@@ -133,6 +137,26 @@ struct VideoContextMenuView: View {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var playNowInPictureInPictureButton: some View {
 | 
				
			||||||
 | 
					        Button {
 | 
				
			||||||
 | 
					            player.controls.startPiP(startImmediately: false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var time: CMTime?
 | 
				
			||||||
 | 
					            if saveHistory,
 | 
				
			||||||
 | 
					               let stoppedAt = watch?.stoppedAt,
 | 
				
			||||||
 | 
					               !watch!.finished
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                time = .secondsInDefaultTimescale(stoppedAt)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
 | 
				
			||||||
 | 
					                player.play(video, at: time, showingPlayer: false)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } label: {
 | 
				
			||||||
 | 
					            Label("Play in PiP", systemImage: "pip")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var playNextButton: some View {
 | 
					    private var playNextButton: some View {
 | 
				
			||||||
        Button {
 | 
					        Button {
 | 
				
			||||||
            player.playNext(video)
 | 
					            player.playNext(video)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user