Fixed PiP kinda + Crash issue
Some checks failed
Build and Release / Build IPA (push) Has been cancelled
Build and Release / Build macOS App (push) Has been cancelled

This commit is contained in:
cranci1 2025-09-26 15:33:47 +02:00
parent c42216f793
commit 39e366e896
3 changed files with 73 additions and 69 deletions

View file

@ -12,7 +12,7 @@ import MediaPlayer
import AVFoundation
import MarqueeLabel
class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDelegate {
class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDelegate, AVPlayerViewControllerDelegate {
private var airplayButton: AVRoutePickerView!
let module: ScrapingModule
let streamURL: String
@ -68,13 +68,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
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 {
if UserDefaults.standard.object(forKey: "autoplayNext") == nil {
return true
@ -82,7 +75,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
return UserDefaults.standard.bool(forKey: "autoplayNext")
}
private var pipController: AVPictureInPictureController?
private var pipButton: UIButton!
var portraitButtonVisibleConstraints: [NSLayoutConstraint] = []
var portraitButtonHiddenConstraints: [NSLayoutConstraint] = []
@ -187,7 +179,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
skip85Button,
controlButtonsContainer,
volumeSliderHostingView,
pipButton,
airplayButton,
timeBatteryContainer,
endTimeIcon,
@ -207,7 +198,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
views.append(contentsOf: view.subviews.filter {
$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
@ -496,7 +487,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
view.addSubview(capsuleContainer)
capsuleContainer.alpha = isControlsVisible ? 1.0 : 0.0
let buttons: [UIView] = [airplayButton, pipButton, lockButton, dimButton]
let buttons: [UIView] = [airplayButton, lockButton, dimButton]
for btn in buttons {
btn.removeFromSuperview()
capsuleContainer.addSubview(btn)
@ -728,7 +719,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
skipIntroButton?.removeFromSuperview()
skipOutroButton?.removeFromSuperview()
skip85Button?.removeFromSuperview()
pipButton?.removeFromSuperview()
airplayButton?.removeFromSuperview()
menuButton?.removeFromSuperview()
speedButton?.removeFromSuperview()
@ -807,6 +797,8 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
playerViewController = AVPlayerViewController()
playerViewController.player = player
playerViewController.showsPlaybackControls = false
playerViewController.delegate = self
addChild(playerViewController)
if playerViewController.view.superview == nil {
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() {
Logger.shared.log("PIP", type: "Genral")
pipController!.startPictureInPicture()
guard let pipController = pipController else {
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() {
@ -3568,47 +3565,20 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
playerViewController.allowsPictureInPicturePlayback = true
let playerLayerContainer = UIView()
playerLayerContainer.translatesAutoresizingMaskIntoConstraints = false
view.insertSubview(playerLayerContainer, at: 0)
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)
if let playerLayer = playerViewController.view.layer.sublayers?.first(where: { $0 is AVPlayerLayer }) as? AVPlayerLayer {
pipController = AVPictureInPictureController(playerLayer: playerLayer)
} else {
pipController = AVPictureInPictureController(playerLayer: AVPlayerLayer(player: player))
}
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([
pipButton.centerYAnchor.constraint(equalTo: dimButton.centerYAnchor),
pipButton.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.centerYAnchor.constraint(equalTo: dimButton.centerYAnchor),
airplayButton.trailingAnchor.constraint(equalTo: dimButton.leadingAnchor, constant: -8),
airplayButton.widthAnchor.constraint(equalToConstant: 24),
airplayButton.heightAnchor.constraint(equalToConstant: 24)
])
pipButton.isHidden = !isPipButtonVisible
NotificationCenter.default.addObserver(self, selector: #selector(startPipIfNeeded), name: UIApplication.didEnterBackgroundNotification, object: nil)
}
@ -3701,11 +3671,11 @@ class GradientOverlayButton: UIButton {
extension CustomMediaPlayerViewController: AVPictureInPictureControllerDelegate {
func pictureInPictureControllerWillStartPictureInPicture(_ pipController: AVPictureInPictureController) {
pipButton.alpha = 0.5
// PiP will start
}
func pictureInPictureControllerDidStopPictureInPicture(_ pipController: AVPictureInPictureController) {
pipButton.alpha = 1.0
// PiP did stop
}
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 {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
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
}
}

View file

@ -165,7 +165,7 @@ extension JSContext {
if httpMethod == "GET" && !bodyIsEmpty {
Logger.shared.log("GET request must not have a body", type: "Error")
DispatchQueue.main.async {
reject.call(withArguments: ["GET request must not have a body"])
resolve.call(withArguments: ["GET request must not have a body"])
}
return
}

View file

@ -204,7 +204,6 @@ struct SettingsViewPlayer: View {
@AppStorage("skip85Visible") private var skip85Visible: Bool = true
@AppStorage("doubleTapSeekEnabled") private var doubleTapSeekEnabled: Bool = false
@AppStorage("skipIntroOutroVisible") private var skipIntroOutroVisible: Bool = true
@AppStorage("pipButtonVisible") private var pipButtonVisible: Bool = true
@AppStorage("autoplayNext") private var autoplayNext: Bool = true
@AppStorage("videoQualityWiFi") private var wifiQuality: String = VideoQualityPreference.defaultWiFiPreference.rawValue
@ -241,13 +240,6 @@ struct SettingsViewPlayer: View {
showDivider: true
)
SettingsToggleRow(
icon: "pip",
title: NSLocalizedString("Show PiP Button", comment: ""),
isOn: $pipButtonVisible,
showDivider: true
)
SettingsToggleRow(
icon: "play.circle.fill",
title: NSLocalizedString("Autoplay Next", comment: ""),