i despise unclean merges 🙏 (also heres ur fidget spinner) (#83)

This commit is contained in:
Seiike 2025-04-17 16:59:26 +02:00 committed by GitHub
parent ad07457cf7
commit f512faba06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 79 additions and 50 deletions

View file

@ -61,23 +61,18 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
private var isDoubleTapSkipEnabled: Bool {
if UserDefaults.standard.object(forKey: "doubleTapSeekEnabled") == nil {
return true
return false
}
return UserDefaults.standard.bool(forKey: "doubleTapSeekEnabled")
}
var showWatchNextButton = true
var watchNextButtonTimer: Timer?
var isWatchNextRepositioned: Bool = false
var isWatchNextVisible: Bool = false
var lastDuration: Double = 0.0
var watchNextButtonAppearedAt: Double?
var portraitButtonVisibleConstraints: [NSLayoutConstraint] = []
var portraitButtonHiddenConstraints: [NSLayoutConstraint] = []
var landscapeButtonVisibleConstraints: [NSLayoutConstraint] = []
var landscapeButtonHiddenConstraints: [NSLayoutConstraint] = []
var currentMarqueeConstraints: [NSLayoutConstraint] = []
private var currentMenuButtonTrailing: NSLayoutConstraint!
var subtitleForegroundColor: String = "white"
var subtitleBackgroundEnabled: Bool = true
@ -287,6 +282,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
} else {
marqueeLabel.lineBreakMode = .byClipping
}
updateMenuButtonConstraints()
}
override func viewDidAppear(_ animated: Bool) {
@ -336,8 +332,17 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
if self.qualityButton.isHidden && self.isHLSStream {
// 1) reveal the quality button
self.qualityButton.isHidden = false
self.qualityButton.menu = self.qualitySelectionMenu()
// 2) update the trailing constraint for the menuButton
self.updateMenuButtonConstraints()
// 3) animate the shift
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut) {
self.view.layoutIfNeeded()
}
}
}
}
@ -420,7 +425,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
let playPauseTap = UITapGestureRecognizer(target: self, action: #selector(togglePlayPause))
playPauseTap.delaysTouchesBegan = false
playPauseTap.delegate = self // Ensure you return true in shouldRecognizeSimultaneouslyWith
playPauseTap.delegate = self
playPauseButton.addGestureRecognizer(playPauseTap)
@ -809,10 +814,11 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
NSLayoutConstraint.activate([
menuButton.topAnchor.constraint(equalTo: qualityButton.topAnchor),
menuButton.trailingAnchor.constraint(equalTo: qualityButton.leadingAnchor, constant: -6),
menuButton.widthAnchor.constraint(equalToConstant: 40),
menuButton.heightAnchor.constraint(equalToConstant: 40)
menuButton.heightAnchor.constraint(equalToConstant: 40),
])
currentMenuButtonTrailing = menuButton.trailingAnchor.constraint(equalTo: qualityButton.leadingAnchor, constant: -6)
}
func setupSpeedButton() {
@ -916,11 +922,11 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
qualityButton.menu = qualitySelectionMenu()
qualityButton.isHidden = true
speedButton.layer.shadowColor = UIColor.black.cgColor
speedButton.layer.shadowOffset = CGSize(width: 0, height: 2)
speedButton.layer.shadowOpacity = 0.6
speedButton.layer.shadowRadius = 4
speedButton.layer.masksToBounds = false
qualityButton.layer.shadowColor = UIColor.black.cgColor
qualityButton.layer.shadowOffset = CGSize(width: 0, height: 2)
qualityButton.layer.shadowOpacity = 0.6
qualityButton.layer.shadowRadius = 4
qualityButton.layer.masksToBounds = false
controlsContainerView.addSubview(qualityButton)
qualityButton.translatesAutoresizingMaskIntoConstraints = false
@ -1040,37 +1046,9 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
}
)
}
let isNearEnd = (self.duration - self.currentTimeVal) <= (self.duration * 0.10)
&& self.currentTimeVal != self.duration
&& self.showWatchNextButton
&& self.duration != 0
if isNearEnd {
if !self.isWatchNextVisible {
self.watchNextButtonAppearedAt = self.currentTimeVal
}
} else {
}
}
}
func repositionWatchNextButton() {
self.isWatchNextRepositioned = true
UIView.animate(withDuration: 0.3, animations: {
NSLayoutConstraint.deactivate(self.watchNextButtonNormalConstraints)
NSLayoutConstraint.activate(self.watchNextButtonControlsConstraints)
self.view.layoutIfNeeded()
self.watchNextButton.alpha = 0.0
}, completion: { _ in
self.watchNextButton.isHidden = true
})
self.watchNextButtonTimer?.invalidate()
self.watchNextButtonTimer = nil
}
func startUpdateTimer() {
updateTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
@ -1079,6 +1057,25 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
}
}
func updateMenuButtonConstraints() {
// tear down last one
currentMenuButtonTrailing.isActive = false
// pick the next visible control
let anchor: NSLayoutXAxisAnchor
if !qualityButton.isHidden {
anchor = qualityButton.leadingAnchor
} else if !speedButton.isHidden {
anchor = speedButton.leadingAnchor
} else {
anchor = controlsContainerView.trailingAnchor
}
// rebuild & activate
currentMenuButtonTrailing = menuButton.trailingAnchor.constraint(equalTo: anchor, constant: -6)
currentMenuButtonTrailing.isActive = true
}
@objc func toggleControls() {
isControlsVisible.toggle()
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseInOut, animations: {
@ -1096,6 +1093,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) { [weak self] finished in
guard self != nil else { return }
}
animateButtonRotation(backwardButton, clockwise: false)
}
}
@ -1107,6 +1105,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) { [weak self] finished in
guard self != nil else { return }
}
animateButtonRotation(forwardButton)
}
}
@ -1117,6 +1116,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) { [weak self] finished in
guard self != nil else { return }
}
animateButtonRotation(backwardButton, clockwise: false)
}
@objc func seekForward() {
@ -1125,6 +1125,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
currentTimeVal = min(currentTimeVal + finalSkip, duration)
player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) { [weak self] finished in
guard self != nil else { return } }
animateButtonRotation(forwardButton)
}
@objc func handleDoubleTap(_ gesture: UITapGestureRecognizer) {
@ -1240,6 +1241,30 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
}
}
private func animateButtonRotation(_ button: UIView, clockwise: Bool = true) {
if button.layer.animation(forKey: "rotate360") != nil {
return
}
button.superview?.layoutIfNeeded()
button.layer.shouldRasterize = true
button.layer.rasterizationScale = UIScreen.main.scale
button.layer.allowsEdgeAntialiasing = true
let rotation = CABasicAnimation(keyPath: "transform.rotation.z")
rotation.fromValue = 0
rotation.toValue = CGFloat.pi * 2 * (clockwise ? 1 : -1)
rotation.duration = 0.43
rotation.timingFunction = CAMediaTimingFunction(name: .linear)
button.layer.add(rotation, forKey: "rotate360")
DispatchQueue.main.asyncAfter(deadline: .now() + rotation.duration) {
button.layer.shouldRasterize = false
}
}
private func parseM3U8(url: URL, completion: @escaping () -> Void) {
var request = URLRequest(url: url)
request.addValue("\(module.metadata.baseUrl)", forHTTPHeaderField: "Referer")
@ -1403,19 +1428,23 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
parseM3U8(url: url) { [weak self] in
guard let self = self else { return }
if let lastSelectedQuality = UserDefaults.standard.string(forKey: "lastSelectedQuality"),
self.qualities.contains(where: { $0.1 == lastSelectedQuality }) {
self.switchToQuality(urlString: lastSelectedQuality)
if let last = UserDefaults.standard.string(forKey: "lastSelectedQuality"),
self.qualities.contains(where: { $0.1 == last }) {
self.switchToQuality(urlString: last)
}
// reveal + animate
self.qualityButton.isHidden = false
self.qualityButton.menu = self.qualitySelectionMenu()
self.updateMarqueeConstraints()
self.updateMenuButtonConstraints()
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut) {
self.view.layoutIfNeeded()
}
}
} else {
isHLSStream = false
qualityButton.isHidden = true
updateMenuButtonConstraints()
}
}

View file

@ -16,7 +16,7 @@ struct SettingsViewPlayer: View {
@AppStorage("skipIncrementHold") private var skipIncrementHold: Double = 30.0
@AppStorage("holdForPauseEnabled") private var holdForPauseEnabled = false
@AppStorage("skip85Visible") private var skip85Visible: Bool = true
@AppStorage("doubleTapSeekEnabled") private var doubleTapSeekEnabled: Bool = true
@AppStorage("doubleTapSeekEnabled") private var doubleTapSeekEnabled: Bool = false
private let mediaPlayers = ["Default", "VLC", "OutPlayer", "Infuse", "nPlayer", "Sora"]