mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
Add basic PiP support
This commit is contained in:
parent
2dff68200d
commit
8d5a191779
@ -23,6 +23,8 @@ final class PlayerState: ObservableObject {
|
|||||||
let maxResolution: Stream.Resolution?
|
let maxResolution: Stream.Resolution?
|
||||||
var timeObserver: Any?
|
var timeObserver: Any?
|
||||||
|
|
||||||
|
var playingOutsideViewController = false
|
||||||
|
|
||||||
init(_ video: Video? = nil, maxResolution: Stream.Resolution? = nil) {
|
init(_ video: Video? = nil, maxResolution: Stream.Resolution? = nil) {
|
||||||
self.video = video
|
self.video = video
|
||||||
self.maxResolution = maxResolution
|
self.maxResolution = maxResolution
|
||||||
@ -222,6 +224,11 @@ final class PlayerState: ObservableObject {
|
|||||||
fileprivate func destroyPlayer() {
|
fileprivate func destroyPlayer() {
|
||||||
logger.critical("destroying player")
|
logger.critical("destroying player")
|
||||||
|
|
||||||
|
guard !playingOutsideViewController else {
|
||||||
|
logger.critical("cannot destroy, playing outside view controller")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
player?.currentItem?.tracks.forEach { $0.assetTrack?.asset?.cancelLoading() }
|
player?.currentItem?.tracks.forEach { $0.assetTrack?.asset?.cancelLoading() }
|
||||||
|
|
||||||
player?.replaceCurrentItem(with: nil)
|
player?.replaceCurrentItem(with: nil)
|
||||||
|
@ -232,6 +232,7 @@
|
|||||||
37977582268922F600DD52A8 /* InvidiousAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvidiousAPI.swift; sourceTree = "<group>"; };
|
37977582268922F600DD52A8 /* InvidiousAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvidiousAPI.swift; sourceTree = "<group>"; };
|
||||||
3797758A2689345500DD52A8 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
|
3797758A2689345500DD52A8 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
|
||||||
379775922689365600DD52A8 /* Array+Next.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Next.swift"; sourceTree = "<group>"; };
|
379775922689365600DD52A8 /* Array+Next.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Next.swift"; sourceTree = "<group>"; };
|
||||||
|
37992DC726CC50BC003D4C27 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
37AAF27D26737323007FC770 /* PopularView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopularView.swift; sourceTree = "<group>"; };
|
37AAF27D26737323007FC770 /* PopularView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopularView.swift; sourceTree = "<group>"; };
|
||||||
37AAF27F26737550007FC770 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
|
37AAF27F26737550007FC770 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
|
||||||
37AAF2892673AB89007FC770 /* ChannelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelView.swift; sourceTree = "<group>"; };
|
37AAF2892673AB89007FC770 /* ChannelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelView.swift; sourceTree = "<group>"; };
|
||||||
@ -356,6 +357,14 @@
|
|||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
37992DC826CC50CD003D4C27 /* iOS */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
37992DC726CC50BC003D4C27 /* Info.plist */,
|
||||||
|
);
|
||||||
|
path = iOS;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
37BE0BD826A214500092E2DB /* macOS */ = {
|
37BE0BD826A214500092E2DB /* macOS */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -378,6 +387,7 @@
|
|||||||
37D4B0BC2671614700C925CA = {
|
37D4B0BC2671614700C925CA = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
37992DC826CC50CD003D4C27 /* iOS */,
|
||||||
37BE0BD826A214500092E2DB /* macOS */,
|
37BE0BD826A214500092E2DB /* macOS */,
|
||||||
37D4B159267164AE00C925CA /* tvOS */,
|
37D4B159267164AE00C925CA /* tvOS */,
|
||||||
37D4B0C12671614700C925CA /* Shared */,
|
37D4B0C12671614700C925CA /* Shared */,
|
||||||
@ -1082,6 +1092,7 @@
|
|||||||
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = iOS/Info.plist;
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
@ -1112,6 +1123,7 @@
|
|||||||
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
DEVELOPMENT_TEAM = 78Z5H3M6RJ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = iOS/Info.plist;
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
@ -6,8 +6,6 @@ final class PlayerViewController: UIViewController {
|
|||||||
var video: Video!
|
var video: Video!
|
||||||
|
|
||||||
var playerLoaded = false
|
var playerLoaded = false
|
||||||
var playingFullScreen = false
|
|
||||||
|
|
||||||
var player = AVPlayer()
|
var player = AVPlayer()
|
||||||
var playerState: PlayerState! = PlayerState()
|
var playerState: PlayerState! = PlayerState()
|
||||||
var playerViewController = AVPlayerViewController()
|
var playerViewController = AVPlayerViewController()
|
||||||
@ -15,19 +13,19 @@ final class PlayerViewController: UIViewController {
|
|||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
if !playerLoaded {
|
|
||||||
loadPlayer()
|
loadPlayer()
|
||||||
}
|
|
||||||
|
|
||||||
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
|
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
|
||||||
try? AVAudioSession.sharedInstance().setActive(true)
|
try? AVAudioSession.sharedInstance().setActive(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidDisappear(_ animated: Bool) {
|
override func viewDidDisappear(_ animated: Bool) {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if !playingFullScreen {
|
if !playerState.playingOutsideViewController {
|
||||||
playerViewController.player?.replaceCurrentItem(with: nil)
|
playerViewController.player?.replaceCurrentItem(with: nil)
|
||||||
playerViewController.player = nil
|
playerViewController.player = nil
|
||||||
|
|
||||||
|
try? AVAudioSession.sharedInstance().setActive(false)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -35,6 +33,10 @@ final class PlayerViewController: UIViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadPlayer() {
|
func loadPlayer() {
|
||||||
|
guard !playerLoaded else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
playerState.player = player
|
playerState.player = player
|
||||||
playerViewController.player = playerState.player
|
playerViewController.player = playerState.player
|
||||||
playerState.loadVideo(video)
|
playerState.loadVideo(video)
|
||||||
@ -42,6 +44,16 @@ final class PlayerViewController: UIViewController {
|
|||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
present(playerViewController, animated: false)
|
present(playerViewController, animated: false)
|
||||||
#else
|
#else
|
||||||
|
embedViewController()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
playerViewController.allowsPictureInPicturePlayback = true
|
||||||
|
playerViewController.delegate = self
|
||||||
|
playerLoaded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
|
func embedViewController() {
|
||||||
playerViewController.exitsFullScreenWhenPlaybackEnds = true
|
playerViewController.exitsFullScreenWhenPlaybackEnds = true
|
||||||
playerViewController.view.frame = view.bounds
|
playerViewController.view.frame = view.bounds
|
||||||
|
|
||||||
@ -49,11 +61,8 @@ final class PlayerViewController: UIViewController {
|
|||||||
view.addSubview(playerViewController.view)
|
view.addSubview(playerViewController.view)
|
||||||
|
|
||||||
playerViewController.didMove(toParent: self)
|
playerViewController.didMove(toParent: self)
|
||||||
#endif
|
|
||||||
|
|
||||||
playerViewController.delegate = self
|
|
||||||
playerLoaded = true
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PlayerViewController: AVPlayerViewControllerDelegate {
|
extension PlayerViewController: AVPlayerViewControllerDelegate {
|
||||||
@ -61,8 +70,12 @@ extension PlayerViewController: AVPlayerViewControllerDelegate {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart(_: AVPlayerViewController) -> Bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
func playerViewControllerDidEndDismissalTransition(_: AVPlayerViewController) {
|
func playerViewControllerDidEndDismissalTransition(_: AVPlayerViewController) {
|
||||||
playingFullScreen = false
|
playerState.playingOutsideViewController = false
|
||||||
dismiss(animated: false)
|
dismiss(animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +83,7 @@ extension PlayerViewController: AVPlayerViewControllerDelegate {
|
|||||||
_: AVPlayerViewController,
|
_: AVPlayerViewController,
|
||||||
willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator
|
willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator
|
||||||
) {
|
) {
|
||||||
playingFullScreen = true
|
playerState.playingOutsideViewController = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func playerViewController(
|
func playerViewController(
|
||||||
@ -79,8 +92,16 @@ extension PlayerViewController: AVPlayerViewControllerDelegate {
|
|||||||
) {
|
) {
|
||||||
coordinator.animate(alongsideTransition: nil) { context in
|
coordinator.animate(alongsideTransition: nil) { context in
|
||||||
if !context.isCancelled {
|
if !context.isCancelled {
|
||||||
self.playingFullScreen = false
|
self.playerState.playingOutsideViewController = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func playerViewControllerWillStartPictureInPicture(_: AVPlayerViewController) {
|
||||||
|
playerState.playingOutsideViewController = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func playerViewControllerWillStopPictureInPicture(_: AVPlayerViewController) {
|
||||||
|
playerState.playingOutsideViewController = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
10
iOS/Info.plist
Normal file
10
iOS/Info.plist
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>audio</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -26,7 +26,7 @@ final class PlayerViewController: NSViewController {
|
|||||||
playerState.player = player
|
playerState.player = player
|
||||||
playerView.player = playerState.player
|
playerView.player = playerState.player
|
||||||
|
|
||||||
playerView.controlsStyle = .floating
|
playerView.allowsPictureInPicturePlayback = true
|
||||||
playerView.showsFullScreenToggleButton = true
|
playerView.showsFullScreenToggleButton = true
|
||||||
|
|
||||||
view = playerView
|
view = playerView
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>UIBackgroundModes</key>
|
||||||
<true/>
|
<array>
|
||||||
|
<string>audio</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
Loading…
Reference in New Issue
Block a user