Error fixes, Player blur fixed, Crash fixed too?

This commit is contained in:
cranci1 2025-07-14 11:40:05 +02:00
parent 5835a6ef8f
commit d6c6e4a1aa
4 changed files with 240 additions and 183 deletions

View file

@ -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

View file

@ -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()

View file

@ -9,6 +9,8 @@ import Foundation
@MainActor
class ModuleManager: ObservableObject {
static let shared = ModuleManager()
@Published var modules: [ScrapingModule] = []
@Published var selectedModuleChanged = false

View file

@ -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