mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
Fixed PiP kinda + Crash issue
This commit is contained in:
parent
c42216f793
commit
39e366e896
3 changed files with 73 additions and 69 deletions
|
|
@ -12,7 +12,7 @@ import MediaPlayer
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
import MarqueeLabel
|
import MarqueeLabel
|
||||||
|
|
||||||
class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDelegate {
|
class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDelegate, AVPlayerViewControllerDelegate {
|
||||||
private var airplayButton: AVRoutePickerView!
|
private var airplayButton: AVRoutePickerView!
|
||||||
let module: ScrapingModule
|
let module: ScrapingModule
|
||||||
let streamURL: String
|
let streamURL: String
|
||||||
|
|
@ -68,13 +68,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
||||||
return UserDefaults.standard.bool(forKey: "doubleTapSeekEnabled")
|
return UserDefaults.standard.bool(forKey: "doubleTapSeekEnabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isPipButtonVisible: Bool {
|
|
||||||
if UserDefaults.standard.object(forKey: "pipButtonVisible") == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return UserDefaults.standard.bool(forKey: "pipButtonVisible")
|
|
||||||
}
|
|
||||||
|
|
||||||
private var isAutoplayEnabled: Bool {
|
private var isAutoplayEnabled: Bool {
|
||||||
if UserDefaults.standard.object(forKey: "autoplayNext") == nil {
|
if UserDefaults.standard.object(forKey: "autoplayNext") == nil {
|
||||||
return true
|
return true
|
||||||
|
|
@ -82,7 +75,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
||||||
return UserDefaults.standard.bool(forKey: "autoplayNext")
|
return UserDefaults.standard.bool(forKey: "autoplayNext")
|
||||||
}
|
}
|
||||||
private var pipController: AVPictureInPictureController?
|
private var pipController: AVPictureInPictureController?
|
||||||
private var pipButton: UIButton!
|
|
||||||
|
|
||||||
var portraitButtonVisibleConstraints: [NSLayoutConstraint] = []
|
var portraitButtonVisibleConstraints: [NSLayoutConstraint] = []
|
||||||
var portraitButtonHiddenConstraints: [NSLayoutConstraint] = []
|
var portraitButtonHiddenConstraints: [NSLayoutConstraint] = []
|
||||||
|
|
@ -187,7 +179,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
||||||
skip85Button,
|
skip85Button,
|
||||||
controlButtonsContainer,
|
controlButtonsContainer,
|
||||||
volumeSliderHostingView,
|
volumeSliderHostingView,
|
||||||
pipButton,
|
|
||||||
airplayButton,
|
airplayButton,
|
||||||
timeBatteryContainer,
|
timeBatteryContainer,
|
||||||
endTimeIcon,
|
endTimeIcon,
|
||||||
|
|
@ -207,7 +198,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
||||||
|
|
||||||
views.append(contentsOf: view.subviews.filter {
|
views.append(contentsOf: view.subviews.filter {
|
||||||
$0 is UIVisualEffectView ||
|
$0 is UIVisualEffectView ||
|
||||||
($0.layer.cornerRadius > 0 && $0 != dismissButton && $0 != lockButton && $0 != dimButton && $0 != pipButton && $0 != holdSpeedIndicator && $0 != volumeSliderHostingView)
|
($0.layer.cornerRadius > 0 && $0 != dismissButton && $0 != lockButton && $0 != dimButton && $0 != holdSpeedIndicator && $0 != volumeSliderHostingView)
|
||||||
})
|
})
|
||||||
|
|
||||||
return views
|
return views
|
||||||
|
|
@ -496,7 +487,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
||||||
view.addSubview(capsuleContainer)
|
view.addSubview(capsuleContainer)
|
||||||
capsuleContainer.alpha = isControlsVisible ? 1.0 : 0.0
|
capsuleContainer.alpha = isControlsVisible ? 1.0 : 0.0
|
||||||
|
|
||||||
let buttons: [UIView] = [airplayButton, pipButton, lockButton, dimButton]
|
let buttons: [UIView] = [airplayButton, lockButton, dimButton]
|
||||||
for btn in buttons {
|
for btn in buttons {
|
||||||
btn.removeFromSuperview()
|
btn.removeFromSuperview()
|
||||||
capsuleContainer.addSubview(btn)
|
capsuleContainer.addSubview(btn)
|
||||||
|
|
@ -728,7 +719,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
||||||
skipIntroButton?.removeFromSuperview()
|
skipIntroButton?.removeFromSuperview()
|
||||||
skipOutroButton?.removeFromSuperview()
|
skipOutroButton?.removeFromSuperview()
|
||||||
skip85Button?.removeFromSuperview()
|
skip85Button?.removeFromSuperview()
|
||||||
pipButton?.removeFromSuperview()
|
|
||||||
airplayButton?.removeFromSuperview()
|
airplayButton?.removeFromSuperview()
|
||||||
menuButton?.removeFromSuperview()
|
menuButton?.removeFromSuperview()
|
||||||
speedButton?.removeFromSuperview()
|
speedButton?.removeFromSuperview()
|
||||||
|
|
@ -807,6 +797,8 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
||||||
playerViewController = AVPlayerViewController()
|
playerViewController = AVPlayerViewController()
|
||||||
playerViewController.player = player
|
playerViewController.player = player
|
||||||
playerViewController.showsPlaybackControls = false
|
playerViewController.showsPlaybackControls = false
|
||||||
|
playerViewController.delegate = self
|
||||||
|
|
||||||
addChild(playerViewController)
|
addChild(playerViewController)
|
||||||
if playerViewController.view.superview == nil {
|
if playerViewController.view.superview == nil {
|
||||||
view.addSubview(playerViewController.view)
|
view.addSubview(playerViewController.view)
|
||||||
|
|
@ -2205,18 +2197,23 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func pipButtonTapped(_ sender: UIButton) {
|
|
||||||
guard let pip = pipController else { return }
|
|
||||||
if pip.isPictureInPictureActive {
|
|
||||||
pip.stopPictureInPicture()
|
|
||||||
} else {
|
|
||||||
pip.startPictureInPicture()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func startPipIfNeeded() {
|
@objc private func startPipIfNeeded() {
|
||||||
Logger.shared.log("PIP", type: "Genral")
|
guard let pipController = pipController else {
|
||||||
pipController!.startPictureInPicture()
|
Logger.shared.log("PiP controller not available", type: "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard AVPictureInPictureController.isPictureInPictureSupported() else {
|
||||||
|
Logger.shared.log("PiP not supported on this device", type: "Error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard !pipController.isPictureInPictureActive else {
|
||||||
|
Logger.shared.log("PiP already active", type: "Debug")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pipController.startPictureInPicture()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func lockTapped() {
|
@objc private func lockTapped() {
|
||||||
|
|
@ -3568,47 +3565,20 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
||||||
|
|
||||||
playerViewController.allowsPictureInPicturePlayback = true
|
playerViewController.allowsPictureInPicturePlayback = true
|
||||||
|
|
||||||
let playerLayerContainer = UIView()
|
if let playerLayer = playerViewController.view.layer.sublayers?.first(where: { $0 is AVPlayerLayer }) as? AVPlayerLayer {
|
||||||
playerLayerContainer.translatesAutoresizingMaskIntoConstraints = false
|
pipController = AVPictureInPictureController(playerLayer: playerLayer)
|
||||||
view.insertSubview(playerLayerContainer, at: 0)
|
} else {
|
||||||
|
pipController = AVPictureInPictureController(playerLayer: AVPlayerLayer(player: player))
|
||||||
NSLayoutConstraint.activate([
|
}
|
||||||
playerLayerContainer.topAnchor.constraint(equalTo: view.topAnchor),
|
|
||||||
playerLayerContainer.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
|
||||||
playerLayerContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
||||||
playerLayerContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
|
||||||
])
|
|
||||||
|
|
||||||
let pipPlayerLayer = AVPlayerLayer(player: playerViewController.player)
|
|
||||||
pipPlayerLayer.frame = playerViewController.view.layer.bounds
|
|
||||||
pipPlayerLayer.videoGravity = .resizeAspect
|
|
||||||
|
|
||||||
playerViewController.view.layer.insertSublayer(pipPlayerLayer, at: 0)
|
|
||||||
pipController = AVPictureInPictureController(playerLayer: pipPlayerLayer)
|
|
||||||
pipController?.delegate = self
|
pipController?.delegate = self
|
||||||
|
|
||||||
let Image = UIImage(systemName: "pip", withConfiguration: cfg)
|
|
||||||
pipButton = UIButton(type: .system)
|
|
||||||
pipButton.setImage(Image, for: .normal)
|
|
||||||
pipButton.tintColor = .white
|
|
||||||
pipButton.addTarget(self, action: #selector(pipButtonTapped(_:)), for: .touchUpInside)
|
|
||||||
|
|
||||||
controlsContainerView.addSubview(pipButton)
|
|
||||||
pipButton.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
pipButton.centerYAnchor.constraint(equalTo: dimButton.centerYAnchor),
|
airplayButton.centerYAnchor.constraint(equalTo: dimButton.centerYAnchor),
|
||||||
pipButton.trailingAnchor.constraint(equalTo: dimButton.leadingAnchor, constant: -8),
|
airplayButton.trailingAnchor.constraint(equalTo: dimButton.leadingAnchor, constant: -8),
|
||||||
pipButton.widthAnchor.constraint(equalToConstant: 30),
|
|
||||||
pipButton.heightAnchor.constraint(equalToConstant: 24),
|
|
||||||
airplayButton.centerYAnchor.constraint(equalTo: pipButton.centerYAnchor),
|
|
||||||
airplayButton.trailingAnchor.constraint(equalTo: pipButton.leadingAnchor, constant: -4),
|
|
||||||
airplayButton.widthAnchor.constraint(equalToConstant: 24),
|
airplayButton.widthAnchor.constraint(equalToConstant: 24),
|
||||||
airplayButton.heightAnchor.constraint(equalToConstant: 24)
|
airplayButton.heightAnchor.constraint(equalToConstant: 24)
|
||||||
])
|
])
|
||||||
|
|
||||||
pipButton.isHidden = !isPipButtonVisible
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(startPipIfNeeded), name: UIApplication.didEnterBackgroundNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(startPipIfNeeded), name: UIApplication.didEnterBackgroundNotification, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3701,11 +3671,11 @@ class GradientOverlayButton: UIButton {
|
||||||
|
|
||||||
extension CustomMediaPlayerViewController: AVPictureInPictureControllerDelegate {
|
extension CustomMediaPlayerViewController: AVPictureInPictureControllerDelegate {
|
||||||
func pictureInPictureControllerWillStartPictureInPicture(_ pipController: AVPictureInPictureController) {
|
func pictureInPictureControllerWillStartPictureInPicture(_ pipController: AVPictureInPictureController) {
|
||||||
pipButton.alpha = 0.5
|
// PiP will start
|
||||||
}
|
}
|
||||||
|
|
||||||
func pictureInPictureControllerDidStopPictureInPicture(_ pipController: AVPictureInPictureController) {
|
func pictureInPictureControllerDidStopPictureInPicture(_ pipController: AVPictureInPictureController) {
|
||||||
pipButton.alpha = 1.0
|
// PiP did stop
|
||||||
}
|
}
|
||||||
|
|
||||||
func pictureInPictureController(_ pipController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
|
func pictureInPictureController(_ pipController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
|
||||||
|
|
@ -3713,6 +3683,30 @@ extension CustomMediaPlayerViewController: AVPictureInPictureControllerDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - AVPlayerViewControllerDelegate
|
||||||
|
extension CustomMediaPlayerViewController {
|
||||||
|
func playerViewController(_ playerViewController: AVPlayerViewController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
|
||||||
|
let windowScene = UIApplication.shared.connectedScenes
|
||||||
|
.filter { $0.activationState == .foregroundActive }
|
||||||
|
.compactMap { $0 as? UIWindowScene }
|
||||||
|
.first
|
||||||
|
|
||||||
|
let window = windowScene?.windows.first(where: { $0.isKeyWindow })
|
||||||
|
|
||||||
|
if let topVC = window?.rootViewController?.topmostViewController() {
|
||||||
|
if topVC != self {
|
||||||
|
topVC.present(self, animated: true) {
|
||||||
|
completionHandler(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completionHandler(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completionHandler(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension CustomMediaPlayerViewController {
|
extension CustomMediaPlayerViewController {
|
||||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
return true
|
return true
|
||||||
|
|
@ -3897,3 +3891,21 @@ extension CustomMediaPlayerViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension UIViewController {
|
||||||
|
func topmostViewController() -> UIViewController {
|
||||||
|
if let presented = self.presentedViewController {
|
||||||
|
return presented.topmostViewController()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let navigation = self as? UINavigationController {
|
||||||
|
return navigation.visibleViewController?.topmostViewController() ?? navigation
|
||||||
|
}
|
||||||
|
|
||||||
|
if let tabBar = self as? UITabBarController {
|
||||||
|
return tabBar.selectedViewController?.topmostViewController() ?? tabBar
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ extension JSContext {
|
||||||
if httpMethod == "GET" && !bodyIsEmpty {
|
if httpMethod == "GET" && !bodyIsEmpty {
|
||||||
Logger.shared.log("GET request must not have a body", type: "Error")
|
Logger.shared.log("GET request must not have a body", type: "Error")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
reject.call(withArguments: ["GET request must not have a body"])
|
resolve.call(withArguments: ["GET request must not have a body"])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,6 @@ struct SettingsViewPlayer: View {
|
||||||
@AppStorage("skip85Visible") private var skip85Visible: Bool = true
|
@AppStorage("skip85Visible") private var skip85Visible: Bool = true
|
||||||
@AppStorage("doubleTapSeekEnabled") private var doubleTapSeekEnabled: Bool = false
|
@AppStorage("doubleTapSeekEnabled") private var doubleTapSeekEnabled: Bool = false
|
||||||
@AppStorage("skipIntroOutroVisible") private var skipIntroOutroVisible: Bool = true
|
@AppStorage("skipIntroOutroVisible") private var skipIntroOutroVisible: Bool = true
|
||||||
@AppStorage("pipButtonVisible") private var pipButtonVisible: Bool = true
|
|
||||||
@AppStorage("autoplayNext") private var autoplayNext: Bool = true
|
@AppStorage("autoplayNext") private var autoplayNext: Bool = true
|
||||||
|
|
||||||
@AppStorage("videoQualityWiFi") private var wifiQuality: String = VideoQualityPreference.defaultWiFiPreference.rawValue
|
@AppStorage("videoQualityWiFi") private var wifiQuality: String = VideoQualityPreference.defaultWiFiPreference.rawValue
|
||||||
|
|
@ -241,13 +240,6 @@ struct SettingsViewPlayer: View {
|
||||||
showDivider: true
|
showDivider: true
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsToggleRow(
|
|
||||||
icon: "pip",
|
|
||||||
title: NSLocalizedString("Show PiP Button", comment: ""),
|
|
||||||
isOn: $pipButtonVisible,
|
|
||||||
showDivider: true
|
|
||||||
)
|
|
||||||
|
|
||||||
SettingsToggleRow(
|
SettingsToggleRow(
|
||||||
icon: "play.circle.fill",
|
icon: "play.circle.fill",
|
||||||
title: NSLocalizedString("Autoplay Next", comment: ""),
|
title: NSLocalizedString("Autoplay Next", comment: ""),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue