From d34099d49dabfd36f1059287578271f1691facca Mon Sep 17 00:00:00 2001 From: cranci <100066266+cranci1@users.noreply.github.com> Date: Sun, 27 Apr 2025 17:17:06 +0200 Subject: [PATCH] Little hotfix (#120) * fixed stuffs * OHHHH it was "CURRENT" * now they use UIStackView * subtitle delay maybe * nvm this better * better text --- .../Mutations/AniListPushUpdates.swift | 2 +- .../CustomPlayer/CustomPlayer.swift | 173 +++++++++++------- .../Helpers/SubtitleSettingsManager.swift | 1 + Sora/Utils/Modules/CommunityLib.swift | 2 +- .../SettingsSubViews/SettingsViewPlayer.swift | 23 +++ 5 files changed, 131 insertions(+), 70 deletions(-) diff --git a/Sora/Tracking Services/AniList/Mutations/AniListPushUpdates.swift b/Sora/Tracking Services/AniList/Mutations/AniListPushUpdates.swift index 4589bd9..4383e1f 100644 --- a/Sora/Tracking Services/AniList/Mutations/AniListPushUpdates.swift +++ b/Sora/Tracking Services/AniList/Mutations/AniListPushUpdates.swift @@ -57,7 +57,7 @@ class AniListMutation { let variables: [String: Any] = [ "mediaId": animeId, "progress": episodeNumber, - "status": "WATCHING" + "status": "CURRENT" ] let requestBody: [String: Any] = [ diff --git a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift index 5e385b7..174809d 100644 --- a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift +++ b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift @@ -6,11 +6,11 @@ // import UIKit -import MarqueeLabel import AVKit import SwiftUI -import AVFoundation import MediaPlayer +import AVFoundation +import MarqueeLabel class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDelegate { let module: ScrapingModule @@ -71,9 +71,11 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele var subtitleFontSize: Double = 20.0 var subtitleShadowRadius: Double = 1.0 var subtitlesLoader = VTTSubtitlesLoader() + var subtitleStackView: UIStackView! + var subtitleLabels: [UILabel] = [] var subtitlesEnabled: Bool = true { didSet { - subtitleLabel.isHidden = !subtitlesEnabled + subtitleStackView.isHidden = !subtitlesEnabled } } @@ -83,7 +85,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele var playPauseButton: UIImageView! var backwardButton: UIImageView! var forwardButton: UIImageView! - var subtitleLabel: UILabel! var topSubtitleLabel: UILabel! var dismissButton: UIButton! var menuButton: UIButton! @@ -166,6 +167,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele private var volumeValue: Double = 0.0 private var volumeViewModel = VolumeViewModel() var volumeSliderHostingView: UIView? + private var subtitleDelay: Double = 0.0 init(module: ScrapingModule, urlString: String, @@ -241,8 +243,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele updateSkipButtonsVisibility() setupHoldSpeedIndicator() - view.bringSubviewToFront(subtitleLabel) - view.bringSubviewToFront(topSubtitleLabel) + view.bringSubviewToFront(subtitleStackView) AniListMutation().fetchMalID(animeId: aniListID) { [weak self] result in switch result { @@ -717,48 +718,42 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele } func setupSubtitleLabel() { - subtitleLabel = UILabel() - subtitleLabel?.textAlignment = .center - subtitleLabel?.numberOfLines = 0 - subtitleLabel?.font = UIFont.systemFont(ofSize: CGFloat(subtitleFontSize)) - if let subtitleLabel = subtitleLabel { - view.addSubview(subtitleLabel) - subtitleLabel.translatesAutoresizingMaskIntoConstraints = false + subtitleStackView = UIStackView() + subtitleStackView.axis = .vertical + subtitleStackView.alignment = .center + subtitleStackView.distribution = .fill + subtitleStackView.spacing = 2 + + if let subtitleStackView = subtitleStackView { + view.addSubview(subtitleStackView) + subtitleStackView.translatesAutoresizingMaskIntoConstraints = false - subtitleBottomToSliderConstraint = subtitleLabel.bottomAnchor.constraint( + subtitleBottomToSliderConstraint = subtitleStackView.bottomAnchor.constraint( equalTo: sliderHostingController?.view.topAnchor ?? view.bottomAnchor, constant: -20 ) - subtitleBottomToSafeAreaConstraint = subtitleLabel.bottomAnchor.constraint( + subtitleBottomToSafeAreaConstraint = subtitleStackView.bottomAnchor.constraint( equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -subtitleBottomPadding ) NSLayoutConstraint.activate([ - subtitleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), - subtitleLabel.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 36), - subtitleLabel.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -36) + subtitleStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + subtitleStackView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 36), + subtitleStackView.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -36) ]) subtitleBottomToSafeAreaConstraint?.isActive = true } - topSubtitleLabel = UILabel() - topSubtitleLabel?.textAlignment = .center - topSubtitleLabel?.numberOfLines = 0 - topSubtitleLabel?.font = UIFont.systemFont(ofSize: CGFloat(subtitleFontSize)) - topSubtitleLabel?.isHidden = true - if let topSubtitleLabel = topSubtitleLabel { - view.addSubview(topSubtitleLabel) - topSubtitleLabel.translatesAutoresizingMaskIntoConstraints = false - - NSLayoutConstraint.activate([ - topSubtitleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), - topSubtitleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30), - topSubtitleLabel.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 36), - topSubtitleLabel.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -36) - ]) + for _ in 0..<2 { + let label = UILabel() + label.textAlignment = .center + label.numberOfLines = 0 + label.font = UIFont.systemFont(ofSize: CGFloat(subtitleFontSize)) + subtitleLabels.append(label) + subtitleStackView.addArrangedSubview(label) } updateSubtitleLabelAppearance() @@ -1305,7 +1300,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele } func updateSubtitleLabelAppearance() { - if let subtitleLabel = subtitleLabel { + for subtitleLabel in subtitleLabels { subtitleLabel.font = UIFont.systemFont(ofSize: CGFloat(subtitleFontSize)) subtitleLabel.textColor = subtitleUIColor() subtitleLabel.backgroundColor = subtitleBackgroundEnabled @@ -1318,20 +1313,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele subtitleLabel.layer.shadowOpacity = 1.0 subtitleLabel.layer.shadowOffset = .zero } - - if let topSubtitleLabel = topSubtitleLabel { - topSubtitleLabel.font = UIFont.systemFont(ofSize: CGFloat(subtitleFontSize)) - topSubtitleLabel.textColor = subtitleUIColor() - topSubtitleLabel.backgroundColor = subtitleBackgroundEnabled - ? UIColor.black.withAlphaComponent(0.6) - : .clear - topSubtitleLabel.layer.cornerRadius = 5 - topSubtitleLabel.clipsToBounds = true - topSubtitleLabel.layer.shadowColor = UIColor.black.cgColor - topSubtitleLabel.layer.shadowRadius = CGFloat(subtitleShadowRadius) - topSubtitleLabel.layer.shadowOpacity = 1.0 - topSubtitleLabel.layer.shadowOffset = .zero - } } func addTimeObserver() { @@ -1360,26 +1341,27 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele UserDefaults.standard.set(self.duration, forKey: "totalTime_\(self.fullUrl)") if self.subtitlesEnabled { - let cues = self.subtitlesLoader.cues.filter { self.currentTimeVal >= $0.startTime && self.currentTimeVal <= $0.endTime } + let adjustedTime = self.currentTimeVal - self.subtitleDelay + let cues = self.subtitlesLoader.cues.filter { adjustedTime >= $0.startTime && adjustedTime <= $0.endTime } if cues.count > 0 { - self.subtitleLabel.text = cues[0].text.strippedHTML - self.subtitleLabel.isHidden = false + self.subtitleLabels[0].text = cues[0].text.strippedHTML + self.subtitleLabels[0].isHidden = false } else { - self.subtitleLabel.text = "" - self.subtitleLabel.isHidden = !self.subtitlesEnabled + self.subtitleLabels[0].text = "" + self.subtitleLabels[0].isHidden = !self.subtitlesEnabled } if cues.count > 1 { - self.topSubtitleLabel.text = cues[1].text.strippedHTML - self.topSubtitleLabel.isHidden = false + self.subtitleLabels[1].text = cues[1].text.strippedHTML + self.subtitleLabels[1].isHidden = false } else { - self.topSubtitleLabel.text = "" - self.topSubtitleLabel.isHidden = true + self.subtitleLabels[1].text = "" + self.subtitleLabels[1].isHidden = true } } else { - self.subtitleLabel.text = "" - self.subtitleLabel.isHidden = true - self.topSubtitleLabel.text = "" - self.topSubtitleLabel.isHidden = true + self.subtitleLabels[0].text = "" + self.subtitleLabels[0].isHidden = true + self.subtitleLabels[1].text = "" + self.subtitleLabels[1].isHidden = true } let segmentsColor = self.getSegmentsColor() @@ -1639,21 +1621,17 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele isDimmed.toggle() dimButtonTimer?.invalidate() - // animate black overlay UIView.animate(withDuration: 0.25) { self.blackCoverView.alpha = self.isDimmed ? 1.0 : 0.4 } - // fade controls instead of hiding UIView.animate(withDuration: 0.25) { for view in self.controlsToHide { view.alpha = self.isDimmed ? 0 : 1 } - // keep the dim button visible/in front self.dimButton.alpha = self.isDimmed ? 0 : 1 } - // swap your trailing constraints on the dim‑button dimButtonToSlider.isActive = !isDimmed dimButtonToRight.isActive = isDimmed UIView.animate(withDuration: 0.25) { self.view.layoutIfNeeded() } @@ -1900,7 +1878,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele self.switchToQuality(urlString: last) } - // reveal + animate self.qualityButton.isHidden = false self.qualityButton.menu = self.qualitySelectionMenu() self.updateMenuButtonConstraints() @@ -2038,8 +2015,41 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele ] let paddingMenu = UIMenu(title: "Bottom Padding", children: paddingActions) + let delayActions = [ + UIAction(title: "-0.5s") { [weak self] _ in + guard let self = self else { return } + self.adjustSubtitleDelay(by: -0.5) + }, + UIAction(title: "-0.2s") { [weak self] _ in + guard let self = self else { return } + self.adjustSubtitleDelay(by: -0.2) + }, + UIAction(title: "+0.2s") { [weak self] _ in + guard let self = self else { return } + self.adjustSubtitleDelay(by: 0.2) + }, + UIAction(title: "+0.5s") { [weak self] _ in + guard let self = self else { return } + self.adjustSubtitleDelay(by: 0.5) + }, + UIAction(title: "Custom...") { [weak self] _ in + guard let self = self else { return } + self.presentCustomDelayAlert() + } + ] + + let resetDelayAction = UIAction(title: "Reset Timing") { [weak self] _ in + guard let self = self else { return } + SubtitleSettingsManager.shared.update { settings in settings.subtitleDelay = 0.0 } + self.subtitleDelay = 0.0 + self.loadSubtitleSettings() + DropManager.shared.showDrop(title: "Subtitle Timing Reset", subtitle: "", duration: 0.5, icon: UIImage(systemName: "clock.arrow.circlepath")) + } + + let delayMenu = UIMenu(title: "Subtitle Timing", children: delayActions + [resetDelayAction]) + let subtitleOptionsMenu = UIMenu(title: "Subtitle Options", children: [ - subtitlesToggleAction, colorMenu, fontSizeMenu, shadowMenu, backgroundMenu, paddingMenu + subtitlesToggleAction, colorMenu, fontSizeMenu, shadowMenu, backgroundMenu, paddingMenu, delayMenu ]) menuElements = [subtitleOptionsMenu] @@ -2048,6 +2058,32 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele return UIMenu(title: "", children: menuElements) } + func adjustSubtitleDelay(by amount: Double) { + let newValue = subtitleDelay + amount + let roundedValue = Double(round(newValue * 10) / 10) + SubtitleSettingsManager.shared.update { settings in settings.subtitleDelay = roundedValue } + self.subtitleDelay = roundedValue + self.loadSubtitleSettings() + } + + func presentCustomDelayAlert() { + let alert = UIAlertController(title: "Enter Custom Delay", message: nil, preferredStyle: .alert) + alert.addTextField { textField in + textField.placeholder = "Delay in seconds" + textField.keyboardType = .decimalPad + textField.text = String(format: "%.1f", self.subtitleDelay) + } + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) + alert.addAction(UIAlertAction(title: "Done", style: .default) { _ in + if let text = alert.textFields?.first?.text, let newDelay = Double(text) { + SubtitleSettingsManager.shared.update { settings in settings.subtitleDelay = newDelay } + self.subtitleDelay = newDelay + self.loadSubtitleSettings() + } + }) + present(alert, animated: true) + } + func presentCustomPaddingAlert() { let alert = UIAlertController(title: "Enter Custom Padding", message: nil, preferredStyle: .alert) alert.addTextField { textField in @@ -2095,6 +2131,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele self.subtitleShadowRadius = settings.shadowRadius self.subtitleBackgroundEnabled = settings.backgroundEnabled self.subtitleBottomPadding = settings.bottomPadding + self.subtitleDelay = settings.subtitleDelay } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { @@ -2178,7 +2215,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele self.holdSpeedIndicator.alpha = 0.8 } } - + private func endHoldSpeed() { player?.rate = originalRate diff --git a/Sora/Utils/MediaPlayer/CustomPlayer/Helpers/SubtitleSettingsManager.swift b/Sora/Utils/MediaPlayer/CustomPlayer/Helpers/SubtitleSettingsManager.swift index cb8f82a..57e96b3 100644 --- a/Sora/Utils/MediaPlayer/CustomPlayer/Helpers/SubtitleSettingsManager.swift +++ b/Sora/Utils/MediaPlayer/CustomPlayer/Helpers/SubtitleSettingsManager.swift @@ -13,6 +13,7 @@ struct SubtitleSettings: Codable { var shadowRadius: Double = 1.0 var backgroundEnabled: Bool = true var bottomPadding: CGFloat = 20.0 + var subtitleDelay: Double = 0.0 } class SubtitleSettingsManager { diff --git a/Sora/Utils/Modules/CommunityLib.swift b/Sora/Utils/Modules/CommunityLib.swift index 079f30a..10ae24f 100644 --- a/Sora/Utils/Modules/CommunityLib.swift +++ b/Sora/Utils/Modules/CommunityLib.swift @@ -6,7 +6,7 @@ // import SwiftUI -@preconcurrency import WebKit +import WebKit private struct ModuleLink: Identifiable { let id = UUID() diff --git a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewPlayer.swift b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewPlayer.swift index d07110d..dd5143f 100644 --- a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewPlayer.swift +++ b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewPlayer.swift @@ -117,6 +117,7 @@ struct SubtitleSettingsSection: View { @State private var shadowRadius: Double = SubtitleSettingsManager.shared.settings.shadowRadius @State private var backgroundEnabled: Bool = SubtitleSettingsManager.shared.settings.backgroundEnabled @State private var bottomPadding: CGFloat = SubtitleSettingsManager.shared.settings.bottomPadding + @State private var subtitleDelay: Double = SubtitleSettingsManager.shared.settings.subtitleDelay private let colors = ["white", "yellow", "green", "blue", "red", "purple"] private let shadowOptions = [0, 1, 3, 6] @@ -186,6 +187,28 @@ struct SubtitleSettingsSection: View { } } } + + VStack(alignment: .leading) { + Text("Subtitle Delay: \(String(format: "%.1fs", subtitleDelay))") + .padding(.bottom, 1) + + HStack { + Text("-10s") + .font(.system(size: 12)) + .foregroundColor(.secondary) + + Slider(value: $subtitleDelay, in: -10...10, step: 0.1) + .onChange(of: subtitleDelay) { newValue in + SubtitleSettingsManager.shared.update { settings in + settings.subtitleDelay = newValue + } + } + + Text("+10s") + .font(.system(size: 12)) + .foregroundColor(.secondary) + } + } } } }