Improve PiP

Fix #186
Fix #196
This commit is contained in:
Arkadiusz Fal 2022-08-26 22:17:21 +02:00
parent 99881ccc23
commit 60f38a80aa
13 changed files with 173 additions and 104 deletions

View File

@ -59,6 +59,7 @@ final class AVPlayerBackend: PlayerBackend {
var controller: AppleAVPlayerViewController? var controller: AppleAVPlayerViewController?
#endif #endif
var startPictureInPictureOnPlay = false var startPictureInPictureOnPlay = false
var startPictureInPictureOnSwitch = false
private var asset: AVURLAsset? private var asset: AVURLAsset?
private var composition = AVMutableComposition() private var composition = AVMutableComposition()
@ -124,6 +125,7 @@ final class AVPlayerBackend: PlayerBackend {
} }
avPlayer.play() avPlayer.play()
model.objectWillChange.send()
} }
func pause() { func pause() {
@ -132,6 +134,7 @@ final class AVPlayerBackend: PlayerBackend {
} }
avPlayer.pause() avPlayer.pause()
model.objectWillChange.send()
} }
func togglePlay() { func togglePlay() {
@ -147,7 +150,7 @@ final class AVPlayerBackend: PlayerBackend {
avPlayer.seek( avPlayer.seek(
to: time, to: time,
toleranceBefore: .secondsInDefaultTimescale(1), toleranceBefore: .zero,
toleranceAfter: .zero, toleranceAfter: .zero,
completionHandler: completionHandler ?? { _ in } completionHandler: completionHandler ?? { _ in }
) )
@ -165,6 +168,8 @@ final class AVPlayerBackend: PlayerBackend {
func closeItem() { func closeItem() {
avPlayer.replaceCurrentItem(with: nil) avPlayer.replaceCurrentItem(with: nil)
video = nil
stream = nil
} }
func closePiP() { func closePiP() {
@ -294,6 +299,7 @@ final class AVPlayerBackend: PlayerBackend {
} }
if !preservingTime, if !preservingTime,
!self.model.transitioningToPiP,
let segment = self.model.sponsorBlock.segments.first, let segment = self.model.sponsorBlock.segments.first,
segment.start < 3, segment.start < 3,
self.model.lastSkipped.isNil self.model.lastSkipped.isNil
@ -434,12 +440,38 @@ final class AVPlayerBackend: PlayerBackend {
switch playerItem.status { switch playerItem.status {
case .readyToPlay: case .readyToPlay:
if self.model.playingInPictureInPicture {
self.startPictureInPictureOnSwitch = false
self.startPictureInPictureOnPlay = false
}
if self.model.activeBackend == .appleAVPlayer, if self.model.activeBackend == .appleAVPlayer,
self.isAutoplaying(playerItem) self.isAutoplaying(playerItem)
{ {
self.model.updateAspectRatio() self.model.updateAspectRatio()
if self.startPictureInPictureOnPlay,
let controller = self.model.pipController,
controller.isPictureInPicturePossible
{
self.tryStartingPictureInPicture()
} else {
self.model.play() self.model.play()
} }
} else if self.startPictureInPictureOnPlay {
self.startPictureInPictureOnPlay = false
self.model.stream = self.stream
self.model.streamSelection = self.stream
if self.model.activeBackend != .appleAVPlayer {
self.startPictureInPictureOnSwitch = true
let seconds = self.model.mpvBackend.currentTime?.seconds ?? 0
self.seek(to: seconds) { finished in
guard finished else { return }
self.model.pause()
self.model.changeActiveBackend(from: .mpv, to: .appleAVPlayer, changingStream: false)
}
}
}
case .failed: case .failed:
DispatchQueue.main.async { DispatchQueue.main.async {
self.model.playerError = item.error self.model.playerError = item.error
@ -483,7 +515,7 @@ final class AVPlayerBackend: PlayerBackend {
forInterval: interval, forInterval: interval,
queue: .main queue: .main
) { [weak self] _ in ) { [weak self] _ in
guard let self = self else { guard let self = self, self.model.activeBackend == .appleAVPlayer else {
return return
} }
@ -511,6 +543,7 @@ final class AVPlayerBackend: PlayerBackend {
if self.controlsUpdates { if self.controlsUpdates {
self.playerTime.duration = self.playerItemDuration ?? .zero self.playerTime.duration = self.playerItemDuration ?? .zero
self.playerTime.currentTime = self.currentTime ?? .zero self.playerTime.currentTime = self.currentTime ?? .zero
self.model.objectWillChange.send()
} }
} }
} }
@ -553,17 +586,6 @@ final class AVPlayerBackend: PlayerBackend {
} }
if player.timeControlStatus != .waitingToPlayAtSpecifiedRate { if player.timeControlStatus != .waitingToPlayAtSpecifiedRate {
if let controller = self.model.pipController {
if controller.isPictureInPicturePossible {
if self.startPictureInPictureOnPlay {
self.startPictureInPictureOnPlay = false
DispatchQueue.main.async {
self.model.pipController?.startPictureInPicture()
}
}
}
}
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
self?.model.objectWillChange.send() self?.model.objectWillChange.send()
} }
@ -598,10 +620,12 @@ final class AVPlayerBackend: PlayerBackend {
} }
logger.info("starting controls updates") logger.info("starting controls updates")
controlsUpdates = true controlsUpdates = true
model.objectWillChange.send()
} }
func stopControlsUpdates() { func stopControlsUpdates() {
controlsUpdates = false controlsUpdates = false
model.objectWillChange.send()
} }
func startMusicMode() { func startMusicMode() {
@ -633,13 +657,33 @@ final class AVPlayerBackend: PlayerBackend {
} }
func didChangeTo() { func didChangeTo() {
if model.musicMode { if startPictureInPictureOnSwitch {
startPictureInPictureOnSwitch = false
tryStartingPictureInPicture()
} else if model.musicMode {
startMusicMode() startMusicMode()
} else { } else {
stopMusicMode() stopMusicMode()
} }
} }
func tryStartingPictureInPicture() {
guard let controller = model.pipController else { return }
var opened = false
for delay in [0.1, 0.3, 0.5, 1, 2, 3, 5] {
Delay.by(delay) {
guard !opened else { return }
if controller.isPictureInPicturePossible {
opened = true
controller.startPictureInPicture()
} else {
print("PiP not possible, waited \(delay) seconds")
}
}
}
}
func setNeedsDrawing(_: Bool) {} func setNeedsDrawing(_: Bool) {}
func setSize(_: Double, _: Double) {} func setSize(_: Double, _: Double) {}
func setNeedsNetworkStateUpdates(_: Bool) {} func setNeedsNetworkStateUpdates(_: Bool) {}

View File

@ -169,7 +169,7 @@ final class MPVBackend: PlayerBackend {
stream.resolution != .unknown && stream.format != .av1 stream.resolution != .unknown && stream.format != .av1
} }
func playStream(_ stream: Stream, of video: Video, preservingTime: Bool, upgrading _: Bool) { func playStream(_ stream: Stream, of video: Video, preservingTime: Bool, upgrading: Bool) {
#if !os(macOS) #if !os(macOS)
if model.presentingPlayer { if model.presentingPlayer {
UIApplication.shared.isIdleTimerDisabled = true UIApplication.shared.isIdleTimerDisabled = true
@ -204,6 +204,7 @@ final class MPVBackend: PlayerBackend {
self.startClientUpdates() self.startClientUpdates()
if !preservingTime, if !preservingTime,
!upgrading,
let segment = self.model.sponsorBlock.segments.first, let segment = self.model.sponsorBlock.segments.first,
self.model.lastSkipped.isNil self.model.lastSkipped.isNil
{ {
@ -325,6 +326,8 @@ final class MPVBackend: PlayerBackend {
func closeItem() { func closeItem() {
client?.pause() client?.pause()
client?.stop() client?.stop()
self.video = nil
self.stream = nil
} }
func closePiP() {} func closePiP() {}

View File

@ -1,4 +1,5 @@
import AVKit import AVKit
import Defaults
import Foundation import Foundation
import SwiftUI import SwiftUI
@ -15,16 +16,21 @@ final class PiPDelegate: NSObject, AVPictureInPictureControllerDelegate {
func pictureInPictureControllerWillStartPictureInPicture(_: AVPictureInPictureController) {} func pictureInPictureControllerWillStartPictureInPicture(_: AVPictureInPictureController) {}
func pictureInPictureControllerDidStartPictureInPicture(_: AVPictureInPictureController) { func pictureInPictureControllerDidStartPictureInPicture(_: AVPictureInPictureController) {
player?.playingInPictureInPicture = true guard let player = player else { return }
player?.avPlayerBackend.startPictureInPictureOnPlay = false
player.playingInPictureInPicture = true
player.avPlayerBackend.startPictureInPictureOnPlay = false
player.avPlayerBackend.startPictureInPictureOnSwitch = false
player.controls.objectWillChange.send()
if Defaults[.closePlayerOnOpeningPiP] { Delay.by(0.1) { player.hide() } }
} }
func pictureInPictureControllerDidStopPictureInPicture(_: AVPictureInPictureController) { func pictureInPictureControllerDidStopPictureInPicture(_: AVPictureInPictureController) {
guard let player = player else { guard let player = player else { return }
return
}
player.playingInPictureInPicture = false player.playingInPictureInPicture = false
player.controls.objectWillChange.send()
} }
func pictureInPictureControllerWillStopPictureInPicture(_: AVPictureInPictureController) {} func pictureInPictureControllerWillStopPictureInPicture(_: AVPictureInPictureController) {}

View File

@ -126,26 +126,6 @@ final class PlayerControlsModel: ObservableObject {
} }
} }
func startPiP(startImmediately: Bool = true) {
player?.avPlayerBackend.startPictureInPictureOnPlay = 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
if startImmediately {
player?.pipController?.startPictureInPicture()
}
}
}
func removeTimer() { func removeTimer() {
timer?.invalidate() timer?.invalidate()
timer = nil timer = nil

View File

@ -272,6 +272,9 @@ final class PlayerModel: ObservableObject {
Orientation.lockOrientation(.allButUpsideDown) Orientation.lockOrientation(.allButUpsideDown)
} }
#endif #endif
#if os(macOS)
Windows.player.hide()
#endif
} }
func togglePlayer() { func togglePlayer() {
@ -280,6 +283,13 @@ final class PlayerModel: ObservableObject {
Windows.player.open() Windows.player.open()
} }
Windows.player.focus() Windows.player.focus()
if Windows.player.visible,
closePiPOnOpeningPlayer
{
closePiP()
}
#else #else
if presentingPlayer { if presentingPlayer {
hide() hide()
@ -398,7 +408,8 @@ final class PlayerModel: ObservableObject {
_ stream: Stream, _ stream: Stream,
of video: Video, of video: Video,
preservingTime: Bool = false, preservingTime: Bool = false,
upgrading: Bool = false upgrading: Bool = false,
withBackend: PlayerBackend? = nil
) { ) {
playerError = nil playerError = nil
if !upgrading { if !upgrading {
@ -420,9 +431,7 @@ final class PlayerModel: ObservableObject {
} }
} }
playerTime.reset() (withBackend ?? backend).playStream(
backend.playStream(
stream, stream,
of: video, of: video,
preservingTime: preservingTime, preservingTime: preservingTime,
@ -515,15 +524,13 @@ final class PlayerModel: ObservableObject {
} }
} }
func changeActiveBackend(from: PlayerBackendType, to: PlayerBackendType) { func changeActiveBackend(from: PlayerBackendType, to: PlayerBackendType, changingStream: Bool = true) {
guard activeBackend != to else { guard activeBackend != to else {
return return
} }
logger.info("changing backend from \(from.rawValue) to \(to.rawValue)") logger.info("changing backend from \(from.rawValue) to \(to.rawValue)")
pause()
if to == .mpv { if to == .mpv {
closePiP() closePiP()
} }
@ -531,15 +538,17 @@ final class PlayerModel: ObservableObject {
Defaults[.activeBackend] = to Defaults[.activeBackend] = to
self.activeBackend = to self.activeBackend = to
self.backend.didChangeTo()
guard var stream = stream else {
return
}
let fromBackend: PlayerBackend = from == .appleAVPlayer ? avPlayerBackend : mpvBackend let fromBackend: PlayerBackend = from == .appleAVPlayer ? avPlayerBackend : mpvBackend
let toBackend: PlayerBackend = to == .appleAVPlayer ? avPlayerBackend : mpvBackend let toBackend: PlayerBackend = to == .appleAVPlayer ? avPlayerBackend : mpvBackend
self.backend.didChangeTo()
fromBackend.pause()
guard var stream = stream, changingStream else {
return
}
if let stream = toBackend.stream, toBackend.video == fromBackend.video { if let stream = toBackend.stream, toBackend.video == fromBackend.video {
toBackend.seek(to: fromBackend.currentTime?.seconds ?? .zero) { finished in toBackend.seek(to: fromBackend.currentTime?.seconds ?? .zero) { finished in
guard finished else { guard finished else {
@ -610,11 +619,54 @@ final class PlayerModel: ObservableObject {
#endif #endif
} }
func startPiP() {
avPlayerBackend.startPictureInPictureOnPlay = false
avPlayerBackend.startPictureInPictureOnSwitch = false
if activeBackend == .appleAVPlayer {
avPlayerBackend.tryStartingPictureInPicture()
return
}
guard let video = currentVideo else { return }
guard let stream = avPlayerBackend.bestPlayable(availableStreams, maxResolution: .hd720p30) else { return }
exitFullScreen()
if avPlayerBackend.video == video {
if activeBackend != .appleAVPlayer {
avPlayerBackend.startPictureInPictureOnSwitch = true
changeActiveBackend(from: activeBackend, to: .appleAVPlayer)
}
} else {
avPlayerBackend.startPictureInPictureOnPlay = true
playStream(stream, of: video, preservingTime: true, upgrading: true, withBackend: avPlayerBackend)
}
controls.objectWillChange.send()
}
var transitioningToPiP: Bool {
avPlayerBackend.startPictureInPictureOnPlay || avPlayerBackend.startPictureInPictureOnSwitch
}
var pipPossible: Bool {
guard activeBackend == .appleAVPlayer else { return !transitioningToPiP }
guard let pipController = pipController else { return false }
guard !pipController.isPictureInPictureActive else { return true }
return pipController.isPictureInPicturePossible && !transitioningToPiP
}
func closePiP() { func closePiP() {
guard playingInPictureInPicture else { guard playingInPictureInPicture else {
return return
} }
avPlayerBackend.startPictureInPictureOnPlay = false
avPlayerBackend.startPictureInPictureOnSwitch = false
#if os(tvOS) #if os(tvOS)
show() show()
#endif #endif

View File

@ -127,6 +127,7 @@ extension Defaults.Keys {
#if !os(macOS) #if !os(macOS)
static let closePiPAndOpenPlayerOnEnteringForeground = Key<Bool>("closePiPAndOpenPlayerOnEnteringForeground", default: false) static let closePiPAndOpenPlayerOnEnteringForeground = Key<Bool>("closePiPAndOpenPlayerOnEnteringForeground", default: false)
#endif #endif
static let closePlayerOnOpeningPiP = Key<Bool>("closePlayerOnOpeningPiP", default: false)
static let recentlyOpened = Key<[RecentItem]>("recentlyOpened", default: []) static let recentlyOpened = Key<[RecentItem]>("recentlyOpened", default: [])

View File

@ -95,7 +95,7 @@ extension AppleAVPlayerViewController: AVPlayerViewControllerDelegate {
} }
func playerViewControllerWillBeginDismissalTransition(_: AVPlayerViewController) { func playerViewControllerWillBeginDismissalTransition(_: AVPlayerViewController) {
if Defaults[.pauseOnHidingPlayer] { if Defaults[.pauseOnHidingPlayer], !playerModel.playingInPictureInPicture {
playerModel.pause() playerModel.pause()
} }
dismiss(animated: false) dismiss(animated: false)
@ -121,15 +121,12 @@ extension AppleAVPlayerViewController: AVPlayerViewControllerDelegate {
self.playerModel.show() self.playerModel.show()
self.playerModel.setNeedsDrawing(true) self.playerModel.setNeedsDrawing(true)
#if os(tvOS)
if self.playerModel.playingInPictureInPicture { if self.playerModel.playingInPictureInPicture {
self.present(self.playerView, animated: false) { self.present(self.playerView, animated: false) {
completionHandler(true) completionHandler(true)
} }
} }
#else
completionHandler(true) completionHandler(true)
#endif
} }
} }

View File

@ -277,9 +277,11 @@ struct PlayerControls: View {
} }
private var pipButton: some View { private var pipButton: some View {
button("PiP", systemImage: "pip") { let image = player.transitioningToPiP ? "pip.fill" : player.pipController?.isPictureInPictureActive ?? false ? "pip.exit" : "pip.enter"
model.startPiP() return button("PiP", systemImage: image) {
(player.pipController?.isPictureInPictureActive ?? false) ? player.closePiP() : player.startPiP()
} }
.disabled(!player.pipPossible)
} }
#if os(iOS) #if os(iOS)

View File

@ -17,6 +17,7 @@ struct PlayerSettings: View {
#endif #endif
@Default(.closePiPOnNavigation) private var closePiPOnNavigation @Default(.closePiPOnNavigation) private var closePiPOnNavigation
@Default(.closePiPOnOpeningPlayer) private var closePiPOnOpeningPlayer @Default(.closePiPOnOpeningPlayer) private var closePiPOnOpeningPlayer
@Default(.closePlayerOnOpeningPiP) private var closePlayerOnOpeningPiP
#if !os(macOS) #if !os(macOS)
@Default(.closePlayerOnItemClose) private var closePlayerOnItemClose @Default(.closePlayerOnItemClose) private var closePlayerOnItemClose
@Default(.pauseOnEnteringBackground) private var pauseOnEnteringBackground @Default(.pauseOnEnteringBackground) private var pauseOnEnteringBackground
@ -96,6 +97,7 @@ struct PlayerSettings: View {
Section(header: SettingsHeader(text: "Picture in Picture")) { Section(header: SettingsHeader(text: "Picture in Picture")) {
closePiPOnNavigationToggle closePiPOnNavigationToggle
closePiPOnOpeningPlayerToggle closePiPOnOpeningPlayerToggle
closePlayerOnOpeningPiPToggle
#if !os(macOS) #if !os(macOS)
closePiPAndOpenPlayerOnEnteringForegroundToggle closePiPAndOpenPlayerOnEnteringForegroundToggle
#endif #endif
@ -201,6 +203,10 @@ struct PlayerSettings: View {
Toggle("Close PiP when player is opened", isOn: $closePiPOnOpeningPlayer) Toggle("Close PiP when player is opened", isOn: $closePiPOnOpeningPlayer)
} }
private var closePlayerOnOpeningPiPToggle: some View {
Toggle("Close player when starting PiP", isOn: $closePlayerOnOpeningPiP)
}
#if !os(macOS) #if !os(macOS)
private var closePiPAndOpenPlayerOnEnteringForegroundToggle: some View { private var closePiPAndOpenPlayerOnEnteringForegroundToggle: some View {
Toggle("Close PiP and open player when application enters foreground", isOn: $closePiPAndOpenPlayerOnEnteringForeground) Toggle("Close PiP and open player when application enters foreground", isOn: $closePiPAndOpenPlayerOnEnteringForeground)

View File

@ -167,7 +167,15 @@ struct VideoContextMenuView: View {
private var playNowInPictureInPictureButton: some View { private var playNowInPictureInPictureButton: some View {
Button { Button {
player.controls.startPiP(startImmediately: player.presentingPlayer && player.activeBackend == .appleAVPlayer) player.avPlayerBackend.startPictureInPictureOnPlay = true
#if !os(macOS)
player.exitFullScreen()
#endif
if player.activeBackend != PlayerBackendType.appleAVPlayer {
player.changeActiveBackend(from: .mpv, to: .appleAVPlayer)
}
player.hide() player.hide()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {

View File

@ -257,7 +257,6 @@
373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; }; 373CFAEF2697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; };
373CFAF02697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; }; 373CFAF02697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; };
373CFAF12697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; }; 373CFAF12697A78B003CB2C6 /* AddToPlaylistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */; };
374108D1272B11B2006C5CC8 /* PictureInPictureDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374108D0272B11B2006C5CC8 /* PictureInPictureDelegate.swift */; };
3741A32C27E7EFFD00D266D1 /* PlayerControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37030FFE27B04DCC00ECDDAA /* PlayerControls.swift */; }; 3741A32C27E7EFFD00D266D1 /* PlayerControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37030FFE27B04DCC00ECDDAA /* PlayerControls.swift */; };
3743B86927216D3600261544 /* ChannelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743B86727216D3600261544 /* ChannelCell.swift */; }; 3743B86927216D3600261544 /* ChannelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743B86727216D3600261544 /* ChannelCell.swift */; };
3743B86A27216D3600261544 /* ChannelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743B86727216D3600261544 /* ChannelCell.swift */; }; 3743B86A27216D3600261544 /* ChannelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3743B86727216D3600261544 /* ChannelCell.swift */; };
@ -1043,7 +1042,6 @@
373CFADA269663F1003CB2C6 /* Thumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbnail.swift; sourceTree = "<group>"; }; 373CFADA269663F1003CB2C6 /* Thumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbnail.swift; sourceTree = "<group>"; };
373CFAEA26975CBF003CB2C6 /* PlaylistFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistFormView.swift; sourceTree = "<group>"; }; 373CFAEA26975CBF003CB2C6 /* PlaylistFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistFormView.swift; sourceTree = "<group>"; };
373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToPlaylistView.swift; sourceTree = "<group>"; }; 373CFAEE2697A78B003CB2C6 /* AddToPlaylistView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToPlaylistView.swift; sourceTree = "<group>"; };
374108D0272B11B2006C5CC8 /* PictureInPictureDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PictureInPictureDelegate.swift; sourceTree = "<group>"; };
3743B86727216D3600261544 /* ChannelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelCell.swift; sourceTree = "<group>"; }; 3743B86727216D3600261544 /* ChannelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelCell.swift; sourceTree = "<group>"; };
3743CA4D270EFE3400E4D32B /* PlayerQueueRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerQueueRow.swift; sourceTree = "<group>"; }; 3743CA4D270EFE3400E4D32B /* PlayerQueueRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerQueueRow.swift; sourceTree = "<group>"; };
3743CA51270F284F00E4D32B /* View+Borders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Borders.swift"; sourceTree = "<group>"; }; 3743CA51270F284F00E4D32B /* View+Borders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Borders.swift"; sourceTree = "<group>"; };
@ -1917,7 +1915,6 @@
37BE0BDB26A2367F0092E2DB /* AppleAVPlayerView.swift */, 37BE0BDB26A2367F0092E2DB /* AppleAVPlayerView.swift */,
37FD43DB270470B70073EE42 /* InstancesSettings.swift */, 37FD43DB270470B70073EE42 /* InstancesSettings.swift */,
3751BA7D27E63F1D007B1A60 /* MPVOGLView.swift */, 3751BA7D27E63F1D007B1A60 /* MPVOGLView.swift */,
374108D0272B11B2006C5CC8 /* PictureInPictureDelegate.swift */,
37F7AB5428A951B200FB46B5 /* Power.swift */, 37F7AB5428A951B200FB46B5 /* Power.swift */,
37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */, 37E04C0E275940FB00172673 /* VerticalScrollingFix.swift */,
3751BA7F27E64244007B1A60 /* VideoLayer.swift */, 3751BA7F27E64244007B1A60 /* VideoLayer.swift */,
@ -3118,7 +3115,6 @@
375F7411289DC35A00747050 /* PlayerBackendView.swift in Sources */, 375F7411289DC35A00747050 /* PlayerBackendView.swift in Sources */,
37FEF11427EFD8580033912F /* PlaceholderCell.swift in Sources */, 37FEF11427EFD8580033912F /* PlaceholderCell.swift in Sources */,
37E64DD226D597EB00C71877 /* SubscriptionsModel.swift in Sources */, 37E64DD226D597EB00C71877 /* SubscriptionsModel.swift in Sources */,
374108D1272B11B2006C5CC8 /* PictureInPictureDelegate.swift in Sources */,
37F0F4EF286F734400C06C2E /* AdvancedSettings.swift in Sources */, 37F0F4EF286F734400C06C2E /* AdvancedSettings.swift in Sources */,
37C7A1D6267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */, 37C7A1D6267BFD9D0010EAD6 /* SponsorBlockSegment.swift in Sources */,
37319F0627103F94004ECCD0 /* PlayerQueue.swift in Sources */, 37319F0627103F94004ECCD0 /* PlayerQueue.swift in Sources */,

View File

@ -1,34 +0,0 @@
import AVKit
import Foundation
final class PictureInPictureDelegate: NSObject, AVPlayerViewPictureInPictureDelegate {
var playerModel: PlayerModel!
func playerViewShouldAutomaticallyDismissAtPicture(inPictureStart _: AVPlayerView) -> Bool {
false
}
func playerViewWillStartPicture(inPicture _: AVPlayerView) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
self?.playerModel.playingInPictureInPicture = true
self?.playerModel.hide()
}
}
func playerViewWillStopPicture(inPicture _: AVPlayerView) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
self?.playerModel.playingInPictureInPicture = false
self?.playerModel.show()
}
}
func playerView(
_: AVPlayerView,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: (Bool) -> Void
) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
self?.playerModel.show()
}
completionHandler(true)
}
}

View File

@ -47,9 +47,17 @@ enum Windows: String, CaseIterable {
} }
} }
func hide() {
window?.close()
}
func toggleFullScreen() { func toggleFullScreen() {
window?.toggleFullScreen(nil) window?.toggleFullScreen(nil)
} }
var visible: Bool {
window?.isVisible ?? false
}
} }
struct HostingWindowFinder: NSViewRepresentable { struct HostingWindowFinder: NSViewRepresentable {