From d6c6e4a1aaab46b1574d3d52b73081bf89fb31bf Mon Sep 17 00:00:00 2001 From: cranci1 <100066266+cranci1@users.noreply.github.com> Date: Mon, 14 Jul 2025 11:40:05 +0200 Subject: [PATCH] Error fixes, Player blur fixed, Crash fixed too? --- .../CustomPlayer/CustomPlayer.swift | 9 +- .../JSLoader/JSController-Streams.swift | 410 ++++++++++-------- Sora/Utlis & Misc/Modules/ModuleManager.swift | 2 + Sora/Views/MediaInfoView/MediaInfoView.swift | 2 +- 4 files changed, 240 insertions(+), 183 deletions(-) diff --git a/Sora/MediaUtils/CustomPlayer/CustomPlayer.swift b/Sora/MediaUtils/CustomPlayer/CustomPlayer.swift index a2380a3..176a048 100644 --- a/Sora/MediaUtils/CustomPlayer/CustomPlayer.swift +++ b/Sora/MediaUtils/CustomPlayer/CustomPlayer.swift @@ -997,9 +997,10 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele } private func createCircularBlurBackground(size: CGFloat) -> UIView { - let blurEffect = UIBlurEffect(style: .systemMaterial) + let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialDark) let blurView = UIVisualEffectView(effect: blurEffect) blurView.translatesAutoresizingMaskIntoConstraints = false + blurView.alpha = 0.7 blurView.layer.cornerRadius = size / 2 blurView.clipsToBounds = true @@ -1388,9 +1389,10 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele holdSpeedIndicator.setTitleColor(.white, for: .normal) holdSpeedIndicator.alpha = 0.0 - let blurEffect = UIBlurEffect(style: .systemUltraThinMaterial) + let blurEffect = UIBlurEffect(style: .systemUltraThinMaterialDark) let blurView = UIVisualEffectView(effect: blurEffect) blurView.translatesAutoresizingMaskIntoConstraints = false + blurView.alpha = 0.7 blurView.layer.cornerRadius = 21 blurView.clipsToBounds = true @@ -3804,8 +3806,9 @@ class GradientBlurButton: UIButton { } private func setupBlurAndGradient() { - let blur = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterial)) + let blur = UIVisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterialDark)) blur.isUserInteractionEnabled = false + blur.alpha = 0.7 blur.layer.cornerRadius = 21 blur.clipsToBounds = true blur.translatesAutoresizingMaskIntoConstraints = false diff --git a/Sora/Utlis & Misc/JSLoader/JSController-Streams.swift b/Sora/Utlis & Misc/JSLoader/JSController-Streams.swift index b245658..fd5e2ad 100644 --- a/Sora/Utlis & Misc/JSLoader/JSController-Streams.swift +++ b/Sora/Utlis & Misc/JSLoader/JSController-Streams.swift @@ -31,71 +31,89 @@ extension JSController { } Logger.shared.log(html, type: "HTMLStrings") - if let parseFunction = self.context.objectForKeyedSubscript("extractStreamUrl"), - let resultString = parseFunction.call(withArguments: [html]).toString() { - if let data = resultString.data(using: .utf8) { - do { - if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { - var streamUrls: [String]? = nil - var subtitleUrls: [String]? = nil - var streamUrlsAndHeaders : [[String:Any]]? = nil - if let streamSources = json["streams"] as? [[String:Any]] - { - streamUrlsAndHeaders = streamSources - Logger.shared.log("Found \(streamSources.count) streams and headers", type: "Stream") - } - else if let streamSource = json["stream"] as? [String:Any] - { - streamUrlsAndHeaders = [streamSource] - Logger.shared.log("Found single stream with headers", type: "Stream") - } - else if let streamsArray = json["streams"] as? [String] { - streamUrls = streamsArray - Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream") - } else if let streamUrl = json["stream"] as? String { - streamUrls = [streamUrl] - Logger.shared.log("Found single stream", type: "Stream") - } - - if let subsArray = json["subtitles"] as? [String] { - subtitleUrls = subsArray - Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream") - } else if let subtitleUrl = json["subtitles"] as? String { - subtitleUrls = [subtitleUrl] - Logger.shared.log("Found single subtitle track", type: "Stream") - } - - Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream") - DispatchQueue.main.async { - completion((streamUrls, subtitleUrls,streamUrlsAndHeaders)) - } - return + + guard let parseFunction = self.context.objectForKeyedSubscript("extractStreamUrl") else { + Logger.shared.log("extractStreamUrl function not found in JavaScript context", type: "Error") + DispatchQueue.main.async { completion((nil, nil, nil)) } + return + } + + let result = parseFunction.call(withArguments: [html]) + + if let exception = self.context.exception { + Logger.shared.log("JavaScript exception in extractStreamUrl: \(exception)", type: "Error") + self.context.exception = nil + DispatchQueue.main.async { completion((nil, nil, nil)) } + return + } + + guard let result = result, !result.isNull, !result.isUndefined else { + Logger.shared.log("extractStreamUrl returned null or undefined", type: "Error") + DispatchQueue.main.async { completion((nil, nil, nil)) } + return + } + + guard let resultString = result.toString() else { + Logger.shared.log("Failed to convert JavaScript result to string", type: "Error") + DispatchQueue.main.async { completion((nil, nil, nil)) } + return + } + + if resultString == "[object Promise]" { + Logger.shared.log("Received Promise object instead of resolved value, waiting for proper resolution", type: "Stream") + DispatchQueue.main.async { completion((nil, nil, nil)) } + return + } + + if let data = resultString.data(using: .utf8) { + do { + if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { + var streamUrls: [String]? = nil + var subtitleUrls: [String]? = nil + var streamUrlsAndHeaders : [[String:Any]]? = nil + + if let streamSources = json["streams"] as? [[String:Any]] { + streamUrlsAndHeaders = streamSources + Logger.shared.log("Found \(streamSources.count) streams and headers", type: "Stream") + } else if let streamSource = json["stream"] as? [String:Any] { + streamUrlsAndHeaders = [streamSource] + Logger.shared.log("Found single stream with headers", type: "Stream") + } else if let streamsArray = json["streams"] as? [String] { + streamUrls = streamsArray + Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream") + } else if let streamUrl = json["stream"] as? String { + streamUrls = [streamUrl] + Logger.shared.log("Found single stream", type: "Stream") } - if let streamsArray = try? JSONSerialization.jsonObject(with: data, options: []) as? [String] { - Logger.shared.log("Starting multi-stream with \(streamsArray.count) sources", type: "Stream") - DispatchQueue.main.async { completion((streamsArray, nil,nil)) } - return + if let subsArray = json["subtitles"] as? [String] { + subtitleUrls = subsArray + Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream") + } else if let subtitleUrl = json["subtitles"] as? String { + subtitleUrls = [subtitleUrl] + Logger.shared.log("Found single subtitle track", type: "Stream") } + + Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream") + DispatchQueue.main.async { + completion((streamUrls, subtitleUrls, streamUrlsAndHeaders)) + } + return } + + if let streamsArray = try JSONSerialization.jsonObject(with: data, options: []) as? [String] { + Logger.shared.log("Starting multi-stream with \(streamsArray.count) sources", type: "Stream") + DispatchQueue.main.async { completion((streamsArray, nil, nil)) } + return + } + } catch { + Logger.shared.log("JSON parsing error: \(error.localizedDescription)", type: "Error") } - - // Check if the result is a Promise object and handle it properly - if resultString == "[object Promise]" { - Logger.shared.log("Received Promise object instead of resolved value, waiting for proper resolution", type: "Stream") - // Skip this result - other methods will provide the resolved URL - let workItem = DispatchWorkItem { completion((nil, nil, nil)) } - DispatchQueue.main.async(execute: workItem) - return - } - - Logger.shared.log("Starting stream from: \(resultString)", type: "Stream") - let workItem = DispatchWorkItem { completion(([resultString], nil, nil)) } - DispatchQueue.main.async(execute: workItem) - } else { - Logger.shared.log("Failed to extract stream URL", type: "Error") - let workItem = DispatchWorkItem { completion((nil, nil, nil)) } - DispatchQueue.main.async(execute: workItem) + } + + Logger.shared.log("Starting stream from: \(resultString)", type: "Stream") + DispatchQueue.main.async { + completion(([resultString], nil, nil)) } }.resume() } @@ -123,81 +141,98 @@ extension JSController { let thenBlock: @convention(block) (JSValue) -> Void = { [weak self] result in guard self != nil else { return } - if let jsonString = result.toString(), - let data = jsonString.data(using: .utf8) { - do { - if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { - var streamUrls: [String]? = nil - var subtitleUrls: [String]? = nil - var streamUrlsAndHeaders : [[String:Any]]? = nil - if let streamSources = json["streams"] as? [[String:Any]] - { - streamUrlsAndHeaders = streamSources - Logger.shared.log("Found \(streamSources.count) streams and headers", type: "Stream") - } - else if let streamSource = json["stream"] as? [String:Any] - { - streamUrlsAndHeaders = [streamSource] - Logger.shared.log("Found single stream with headers", type: "Stream") - } - else if let streamsArray = json["streams"] as? [String] { - streamUrls = streamsArray - Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream") - } else if let streamUrl = json["stream"] as? String { - streamUrls = [streamUrl] - Logger.shared.log("Found single stream", type: "Stream") - } - - if let subsArray = json["subtitles"] as? [String] { - subtitleUrls = subsArray - Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream") - } else if let subtitleUrl = json["subtitles"] as? String { - subtitleUrls = [subtitleUrl] - Logger.shared.log("Found single subtitle track", type: "Stream") - } - - Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream") - DispatchQueue.main.async { - completion((streamUrls, subtitleUrls,streamUrlsAndHeaders)) - } - return - } - - if let streamsArray = try? JSONSerialization.jsonObject(with: data, options: []) as? [String] { - Logger.shared.log("Starting multi-stream with \(streamsArray.count) sources", type: "Stream") - DispatchQueue.main.async { completion((streamsArray, nil,nil)) } - return - } - } - } - - let streamUrl = result.toString() - Logger.shared.log("Starting stream from: \(streamUrl ?? "nil")", type: "Stream") - - // Check if the result is a Promise object and handle it properly - if streamUrl == "[object Promise]" { - Logger.shared.log("Received Promise object instead of resolved value, waiting for proper resolution", type: "Stream") - // Skip this result - other methods will provide the resolved URL + if result.isNull || result.isUndefined { + Logger.shared.log("Received null or undefined result from JavaScript", type: "Error") + DispatchQueue.main.async { completion((nil, nil, nil)) } return } + if let resultString = result.toString(), resultString == "[object Promise]" { + Logger.shared.log("Received Promise object instead of resolved value, waiting for proper resolution", type: "Stream") + return + } + + guard let jsonString = result.toString() else { + Logger.shared.log("Failed to convert JSValue to string", type: "Error") + DispatchQueue.main.async { completion((nil, nil, nil)) } + return + } + + guard let data = jsonString.data(using: .utf8) else { + Logger.shared.log("Failed to convert string to data", type: "Error") + DispatchQueue.main.async { completion((nil, nil, nil)) } + return + } + + do { + if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { + var streamUrls: [String]? = nil + var subtitleUrls: [String]? = nil + var streamUrlsAndHeaders : [[String:Any]]? = nil + + if let streamSources = json["streams"] as? [[String:Any]] { + streamUrlsAndHeaders = streamSources + Logger.shared.log("Found \(streamSources.count) streams and headers", type: "Stream") + } else if let streamSource = json["stream"] as? [String:Any] { + streamUrlsAndHeaders = [streamSource] + Logger.shared.log("Found single stream with headers", type: "Stream") + } else if let streamsArray = json["streams"] as? [String] { + streamUrls = streamsArray + Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream") + } else if let streamUrl = json["stream"] as? String { + streamUrls = [streamUrl] + Logger.shared.log("Found single stream", type: "Stream") + } + + if let subsArray = json["subtitles"] as? [String] { + subtitleUrls = subsArray + Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream") + } else if let subtitleUrl = json["subtitles"] as? String { + subtitleUrls = [subtitleUrl] + Logger.shared.log("Found single subtitle track", type: "Stream") + } + + Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream") + DispatchQueue.main.async { + completion((streamUrls, subtitleUrls, streamUrlsAndHeaders)) + } + return + } + + if let streamsArray = try JSONSerialization.jsonObject(with: data, options: []) as? [String] { + Logger.shared.log("Starting multi-stream with \(streamsArray.count) sources", type: "Stream") + DispatchQueue.main.async { completion((streamsArray, nil, nil)) } + return + } + } catch { + Logger.shared.log("JSON parsing error: \(error.localizedDescription)", type: "Error") + } + + Logger.shared.log("Starting stream from: \(jsonString)", type: "Stream") DispatchQueue.main.async { - completion((streamUrl != nil ? [streamUrl!] : nil, nil,nil)) + completion(([jsonString], nil, nil)) } } let catchBlock: @convention(block) (JSValue) -> Void = { error in - Logger.shared.log("Promise rejected: \(String(describing: error.toString()))", type: "Error") + let errorMessage = error.toString() ?? "Unknown JavaScript error" + Logger.shared.log("Promise rejected: \(errorMessage)", type: "Error") DispatchQueue.main.async { - completion((nil, nil,nil)) + completion((nil, nil, nil)) } } let thenFunction = JSValue(object: thenBlock, in: context) let catchFunction = JSValue(object: catchBlock, in: context) - promise.invokeMethod("then", withArguments: [thenFunction as Any]) - promise.invokeMethod("catch", withArguments: [catchFunction as Any]) + guard let thenFunction = thenFunction, let catchFunction = catchFunction else { + Logger.shared.log("Failed to create JSValue objects for Promise handling", type: "Error") + completion((nil, nil, nil)) + return + } + + promise.invokeMethod("then", withArguments: [thenFunction]) + promise.invokeMethod("catch", withArguments: [catchFunction]) } func fetchStreamUrlJSSecond(episodeUrl: String, softsub: Bool = false, module: ScrapingModule, completion: @escaping ((streams: [String]?, subtitles: [String]?,sources: [[String:Any]]? )) -> Void) { @@ -240,71 +275,82 @@ extension JSController { let thenBlock: @convention(block) (JSValue) -> Void = { [weak self] result in guard self != nil else { return } - if let jsonString = result.toString(), - let data = jsonString.data(using: .utf8) { - do { - if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { - var streamUrls: [String]? = nil - var subtitleUrls: [String]? = nil - var streamUrlsAndHeaders : [[String:Any]]? = nil - if let streamSources = json["streams"] as? [[String:Any]] - { - streamUrlsAndHeaders = streamSources - Logger.shared.log("Found \(streamSources.count) streams and headers", type: "Stream") - } - else if let streamSource = json["stream"] as? [String:Any] - { - streamUrlsAndHeaders = [streamSource] - Logger.shared.log("Found single stream with headers", type: "Stream") - } - else if let streamsArray = json["streams"] as? [String] { - streamUrls = streamsArray - Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream") - } else if let streamUrl = json["stream"] as? String { - streamUrls = [streamUrl] - Logger.shared.log("Found single stream", type: "Stream") - } - - if let subsArray = json["subtitles"] as? [String] { - subtitleUrls = subsArray - Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream") - } else if let subtitleUrl = json["subtitles"] as? String { - subtitleUrls = [subtitleUrl] - Logger.shared.log("Found single subtitle track", type: "Stream") - } - - Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream") - DispatchQueue.main.async { - completion((streamUrls, subtitleUrls, streamUrlsAndHeaders)) - } - return - } - - if let streamsArray = try? JSONSerialization.jsonObject(with: data, options: []) as? [String] { - Logger.shared.log("Starting multi-stream with \(streamsArray.count) sources", type: "Stream") - DispatchQueue.main.async { completion((streamsArray, nil, nil)) } - return - } - } - } - - let streamUrl = result.toString() - Logger.shared.log("Starting stream from: \(streamUrl ?? "nil")", type: "Stream") - - // Check if the result is a Promise object and handle it properly - if streamUrl == "[object Promise]" { - Logger.shared.log("Received Promise object instead of resolved value, waiting for proper resolution", type: "Stream") - // Skip this result - other methods will provide the resolved URL + if result.isNull || result.isUndefined { + Logger.shared.log("Received null or undefined result from JavaScript", type: "Error") + DispatchQueue.main.async { completion((nil, nil, nil)) } return } + if let resultString = result.toString(), resultString == "[object Promise]" { + Logger.shared.log("Received Promise object instead of resolved value, waiting for proper resolution", type: "Stream") + return + } + + guard let jsonString = result.toString() else { + Logger.shared.log("Failed to convert JSValue to string", type: "Error") + DispatchQueue.main.async { completion((nil, nil, nil)) } + return + } + + guard let data = jsonString.data(using: .utf8) else { + Logger.shared.log("Failed to convert string to data", type: "Error") + DispatchQueue.main.async { completion((nil, nil, nil)) } + return + } + + do { + if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { + var streamUrls: [String]? = nil + var subtitleUrls: [String]? = nil + var streamUrlsAndHeaders : [[String:Any]]? = nil + + if let streamSources = json["streams"] as? [[String:Any]] { + streamUrlsAndHeaders = streamSources + Logger.shared.log("Found \(streamSources.count) streams and headers", type: "Stream") + } else if let streamSource = json["stream"] as? [String:Any] { + streamUrlsAndHeaders = [streamSource] + Logger.shared.log("Found single stream with headers", type: "Stream") + } else if let streamsArray = json["streams"] as? [String] { + streamUrls = streamsArray + Logger.shared.log("Found \(streamsArray.count) streams", type: "Stream") + } else if let streamUrl = json["stream"] as? String { + streamUrls = [streamUrl] + Logger.shared.log("Found single stream", type: "Stream") + } + + if let subsArray = json["subtitles"] as? [String] { + subtitleUrls = subsArray + Logger.shared.log("Found \(subsArray.count) subtitle tracks", type: "Stream") + } else if let subtitleUrl = json["subtitles"] as? String { + subtitleUrls = [subtitleUrl] + Logger.shared.log("Found single subtitle track", type: "Stream") + } + + Logger.shared.log("Starting stream with \(streamUrls?.count ?? 0) sources and \(subtitleUrls?.count ?? 0) subtitles", type: "Stream") + DispatchQueue.main.async { + completion((streamUrls, subtitleUrls, streamUrlsAndHeaders)) + } + return + } + + if let streamsArray = try JSONSerialization.jsonObject(with: data, options: []) as? [String] { + Logger.shared.log("Starting multi-stream with \(streamsArray.count) sources", type: "Stream") + DispatchQueue.main.async { completion((streamsArray, nil, nil)) } + return + } + } catch { + Logger.shared.log("JSON parsing error: \(error.localizedDescription)", type: "Error") + } + + Logger.shared.log("Starting stream from: \(jsonString)", type: "Stream") DispatchQueue.main.async { - completion((streamUrl != nil ? [streamUrl!] : nil, nil, nil)) + completion(([jsonString], nil, nil)) } } let catchBlock: @convention(block) (JSValue) -> Void = { error in - Logger.shared.log("Promise rejected: \(String(describing: error.toString()))", type: "Error") + let errorMessage = error.toString() ?? "Unknown JavaScript error" + Logger.shared.log("Promise rejected: \(errorMessage)", type: "Error") DispatchQueue.main.async { completion((nil, nil, nil)) } @@ -313,8 +359,14 @@ extension JSController { let thenFunction = JSValue(object: thenBlock, in: self.context) let catchFunction = JSValue(object: catchBlock, in: self.context) - promise.invokeMethod("then", withArguments: [thenFunction as Any]) - promise.invokeMethod("catch", withArguments: [catchFunction as Any]) + guard let thenFunction = thenFunction, let catchFunction = catchFunction else { + Logger.shared.log("Failed to create JSValue objects for Promise handling", type: "Error") + completion((nil, nil, nil)) + return + } + + promise.invokeMethod("then", withArguments: [thenFunction]) + promise.invokeMethod("catch", withArguments: [catchFunction]) } } task.resume() diff --git a/Sora/Utlis & Misc/Modules/ModuleManager.swift b/Sora/Utlis & Misc/Modules/ModuleManager.swift index 68271f1..156cb15 100644 --- a/Sora/Utlis & Misc/Modules/ModuleManager.swift +++ b/Sora/Utlis & Misc/Modules/ModuleManager.swift @@ -9,6 +9,8 @@ import Foundation @MainActor class ModuleManager: ObservableObject { + static let shared = ModuleManager() + @Published var modules: [ScrapingModule] = [] @Published var selectedModuleChanged = false diff --git a/Sora/Views/MediaInfoView/MediaInfoView.swift b/Sora/Views/MediaInfoView/MediaInfoView.swift index d3f8e9e..5ebcde1 100644 --- a/Sora/Views/MediaInfoView/MediaInfoView.swift +++ b/Sora/Views/MediaInfoView/MediaInfoView.swift @@ -80,7 +80,7 @@ struct MediaInfoView: View { @AppStorage("selectedAppearance") private var selectedAppearance: Appearance = .system @ObservedObject private var jsController = JSController.shared - @EnvironmentObject var moduleManager: ModuleManager + @EnvironmentObject private var moduleManager: ModuleManager @EnvironmentObject private var libraryManager: LibraryManager @ObservedObject private var navigator = ChapterNavigator.shared