diff --git a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift index 03fba46..e047658 100644 --- a/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift +++ b/Sora/Utils/MediaPlayer/CustomPlayer/CustomPlayer.swift @@ -41,6 +41,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele var currentTimeVal: Double = 0.0 var duration: Double = 0.0 var isVideoLoaded = false + var detachedWindow: UIWindow? private var isHoldPauseEnabled: Bool { UserDefaults.standard.bool(forKey: "holdForPauseEnabled") @@ -1827,16 +1828,15 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele } @objc func dismissTapped() { - if let presentingViewController = self.presentingViewController { - presentingViewController.dismiss(animated: true, completion: nil) - } else { - dismiss(animated: true, completion: nil) + dismiss(animated: true) { [weak self] in + self?.detachedWindow = nil } } @objc func watchNextTapped() { player.pause() dismiss(animated: true) { [weak self] in + self?.detachedWindow = nil self?.onWatchNext() } } diff --git a/Sora/Utils/MediaPlayer/VideoPlayer.swift b/Sora/Utils/MediaPlayer/VideoPlayer.swift index 58a9a65..dd9c6e3 100644 --- a/Sora/Utils/MediaPlayer/VideoPlayer.swift +++ b/Sora/Utils/MediaPlayer/VideoPlayer.swift @@ -24,6 +24,7 @@ class VideoPlayerViewController: UIViewController { var episodeNumber: Int = 0 var episodeImageUrl: String = "" var mediaTitle: String = "" + var detachedWindow: UIWindow? init(module: ScrapingModule) { self.module = module diff --git a/Sora/Views/MediaInfoView/MediaInfoView.swift b/Sora/Views/MediaInfoView/MediaInfoView.swift index deaedb5..2a40645 100644 --- a/Sora/Views/MediaInfoView/MediaInfoView.swift +++ b/Sora/Views/MediaInfoView/MediaInfoView.swift @@ -1370,11 +1370,7 @@ struct MediaInfoView: View { videoPlayerViewController.aniListID = itemID ?? 0 videoPlayerViewController.modalPresentationStyle = .overFullScreen - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first, - let currentVC = window.rootViewController?.presentedViewController ?? window.rootViewController { - currentVC.present(videoPlayerViewController, animated: true, completion: nil) - } + presentPlayerWithDetachedContext(videoPlayerViewController: videoPlayerViewController) return default: break @@ -1412,11 +1408,7 @@ struct MediaInfoView: View { customMediaPlayer.modalPresentationStyle = .overFullScreen Logger.shared.log("Opening custom media player with stream URL: \(url), and subtitles URL: \(String(describing: subtitles))", type: "Stream") - if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.first, - let currentVC = window.rootViewController?.presentedViewController ?? window.rootViewController { - currentVC.present(customMediaPlayer, animated: true, completion: nil) - } + presentPlayerWithDetachedContext(customMediaPlayer: customMediaPlayer) } } } @@ -1999,4 +1991,34 @@ struct MediaInfoView: View { } }.resume() } + + private func presentPlayerWithDetachedContext(videoPlayerViewController: VideoPlayerViewController) { + guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return } + + let detachedWindow = UIWindow(windowScene: windowScene) + let hostingController = UIViewController() + hostingController.view.backgroundColor = .clear + detachedWindow.rootViewController = hostingController + detachedWindow.backgroundColor = .clear + detachedWindow.windowLevel = .normal + 1 + detachedWindow.makeKeyAndVisible() + + videoPlayerViewController.detachedWindow = detachedWindow + hostingController.present(videoPlayerViewController, animated: true, completion: nil) + } + + private func presentPlayerWithDetachedContext(customMediaPlayer: CustomMediaPlayerViewController) { + guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return } + + let detachedWindow = UIWindow(windowScene: windowScene) + let hostingController = UIViewController() + hostingController.view.backgroundColor = .clear + detachedWindow.rootViewController = hostingController + detachedWindow.backgroundColor = .clear + detachedWindow.windowLevel = .normal + 1 + detachedWindow.makeKeyAndVisible() + + customMediaPlayer.detachedWindow = detachedWindow + hostingController.present(customMediaPlayer, animated: true, completion: nil) + } } diff --git a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewLogger.swift b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewLogger.swift index e0d32ee..4a70dc4 100644 --- a/Sora/Views/SettingsView/SettingsSubViews/SettingsViewLogger.swift +++ b/Sora/Views/SettingsView/SettingsSubViews/SettingsViewLogger.swift @@ -62,8 +62,18 @@ fileprivate struct SettingsSection: View { struct SettingsViewLogger: View { @State private var logs: String = "" @State private var isLoading: Bool = true + @State private var showFullLogs: Bool = false @StateObject private var filterViewModel = LogFilterViewModel.shared + private let displayCharacterLimit = 50_000 + + var displayedLogs: String { + if showFullLogs || logs.count <= displayCharacterLimit { + return logs + } + return String(logs.suffix(displayCharacterLimit)) + } + var body: some View { ScrollView { VStack(spacing: 24) { @@ -79,13 +89,26 @@ struct SettingsViewLogger: View { .frame(maxWidth: .infinity, alignment: .center) .padding(.vertical, 20) } else { - Text(logs) - .font(.footnote) - .foregroundColor(.secondary) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 16) - .padding(.vertical, 12) - .textSelection(.enabled) + VStack(alignment: .leading, spacing: 8) { + Text(displayedLogs) + .font(.footnote) + .foregroundColor(.secondary) + .frame(maxWidth: .infinity, alignment: .leading) + .textSelection(.enabled) + + if logs.count > displayCharacterLimit && !showFullLogs { + Button(action: { + showFullLogs = true + }) { + Text("Show More (\(logs.count - displayCharacterLimit) more characters)") + .font(.footnote) + .foregroundColor(.accentColor) + } + .padding(.top, 8) + } + } + .padding(.horizontal, 16) + .padding(.vertical, 12) } } } @@ -139,6 +162,7 @@ struct SettingsViewLogger: View { await Logger.shared.clearLogsAsync() await MainActor.run { self.logs = "" + self.showFullLogs = false } } }