mirror of
https://github.com/cranci1/Sora.git
synced 2026-01-11 20:10:24 +00:00
test crash fix + layout fix
This commit is contained in:
parent
d707858ad7
commit
09b1d9b0b1
4 changed files with 167 additions and 77 deletions
|
|
@ -9,7 +9,6 @@ import Foundation
|
|||
import JavaScriptCore
|
||||
|
||||
extension JSController {
|
||||
|
||||
func fetchDetails(url: String, completion: @escaping ([MediaItem], [EpisodeLink]) -> Void) {
|
||||
guard let url = URL(string: url) else {
|
||||
completion([], [])
|
||||
|
|
@ -94,41 +93,64 @@ extension JSController {
|
|||
let dispatchGroup = DispatchGroup()
|
||||
|
||||
dispatchGroup.enter()
|
||||
var hasLeftDetailsGroup = false
|
||||
let detailsGroupQueue = DispatchQueue(label: "details.group")
|
||||
|
||||
let promiseValueDetails = extractDetailsFunction.call(withArguments: [url.absoluteString])
|
||||
guard let promiseDetails = promiseValueDetails else {
|
||||
Logger.shared.log("extractDetails did not return a Promise", type: "Error")
|
||||
dispatchGroup.leave()
|
||||
detailsGroupQueue.sync {
|
||||
guard !hasLeftDetailsGroup else { return }
|
||||
hasLeftDetailsGroup = true
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
completion([], [])
|
||||
return
|
||||
}
|
||||
|
||||
let thenBlockDetails: @convention(block) (JSValue) -> Void = { result in
|
||||
if let jsonOfDetails = result.toString(),
|
||||
let dataDetails = jsonOfDetails.data(using: .utf8) {
|
||||
do {
|
||||
if let array = try JSONSerialization.jsonObject(with: dataDetails, options: []) as? [[String: Any]] {
|
||||
resultItems = array.map { item -> MediaItem in
|
||||
MediaItem(
|
||||
description: item["description"] as? String ?? "",
|
||||
aliases: item["aliases"] as? String ?? "",
|
||||
airdate: item["airdate"] as? String ?? ""
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse JSON of extractDetails", type: "Error")
|
||||
}
|
||||
} catch {
|
||||
Logger.shared.log("JSON parsing error of extract details: \(error)", type: "Error")
|
||||
detailsGroupQueue.sync {
|
||||
guard !hasLeftDetailsGroup else {
|
||||
Logger.shared.log("extractDetails: thenBlock called but group already left", type: "Debug")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Result is not a string of extractDetails", type: "Error")
|
||||
hasLeftDetailsGroup = true
|
||||
|
||||
if let jsonOfDetails = result.toString(),
|
||||
let dataDetails = jsonOfDetails.data(using: .utf8) {
|
||||
do {
|
||||
if let array = try JSONSerialization.jsonObject(with: dataDetails, options: []) as? [[String: Any]] {
|
||||
resultItems = array.map { item -> MediaItem in
|
||||
MediaItem(
|
||||
description: item["description"] as? String ?? "",
|
||||
aliases: item["aliases"] as? String ?? "",
|
||||
airdate: item["airdate"] as? String ?? ""
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse JSON of extractDetails", type: "Error")
|
||||
}
|
||||
} catch {
|
||||
Logger.shared.log("JSON parsing error of extract details: \(error)", type: "Error")
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Result is not a string of extractDetails", type: "Error")
|
||||
}
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
let catchBlockDetails: @convention(block) (JSValue) -> Void = { error in
|
||||
Logger.shared.log("Promise rejected of extractDetails: \(String(describing: error.toString()))", type: "Error")
|
||||
dispatchGroup.leave()
|
||||
detailsGroupQueue.sync {
|
||||
guard !hasLeftDetailsGroup else {
|
||||
Logger.shared.log("extractDetails: catchBlock called but group already left", type: "Debug")
|
||||
return
|
||||
}
|
||||
hasLeftDetailsGroup = true
|
||||
|
||||
Logger.shared.log("Promise rejected of extractDetails: \(String(describing: error.toString()))", type: "Error")
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
}
|
||||
|
||||
let thenFunctionDetails = JSValue(object: thenBlockDetails, in: context)
|
||||
|
|
@ -140,50 +162,80 @@ extension JSController {
|
|||
dispatchGroup.enter()
|
||||
let promiseValueEpisodes = extractEpisodesFunction.call(withArguments: [url.absoluteString])
|
||||
|
||||
var hasLeftEpisodesGroup = false
|
||||
let episodesGroupQueue = DispatchQueue(label: "episodes.group")
|
||||
|
||||
let timeoutWorkItem = DispatchWorkItem {
|
||||
Logger.shared.log("Timeout for extractEpisodes", type: "Warning")
|
||||
dispatchGroup.leave()
|
||||
episodesGroupQueue.sync {
|
||||
guard !hasLeftEpisodesGroup else {
|
||||
Logger.shared.log("extractEpisodes: timeout called but group already left", type: "Debug")
|
||||
return
|
||||
}
|
||||
hasLeftEpisodesGroup = true
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0, execute: timeoutWorkItem)
|
||||
|
||||
guard let promiseEpisodes = promiseValueEpisodes else {
|
||||
Logger.shared.log("extractEpisodes did not return a Promise", type: "Error")
|
||||
timeoutWorkItem.cancel()
|
||||
dispatchGroup.leave()
|
||||
episodesGroupQueue.sync {
|
||||
guard !hasLeftEpisodesGroup else { return }
|
||||
hasLeftEpisodesGroup = true
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
completion([], [])
|
||||
return
|
||||
}
|
||||
|
||||
let thenBlockEpisodes: @convention(block) (JSValue) -> Void = { result in
|
||||
timeoutWorkItem.cancel()
|
||||
if let jsonOfEpisodes = result.toString(),
|
||||
let dataEpisodes = jsonOfEpisodes.data(using: .utf8) {
|
||||
do {
|
||||
if let array = try JSONSerialization.jsonObject(with: dataEpisodes, options: []) as? [[String: Any]] {
|
||||
episodeLinks = array.map { item -> EpisodeLink in
|
||||
EpisodeLink(
|
||||
number: item["number"] as? Int ?? 0,
|
||||
title: "",
|
||||
href: item["href"] as? String ?? "",
|
||||
duration: nil
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse JSON of extractEpisodes", type: "Error")
|
||||
}
|
||||
} catch {
|
||||
Logger.shared.log("JSON parsing error of extractEpisodes: \(error)", type: "Error")
|
||||
episodesGroupQueue.sync {
|
||||
guard !hasLeftEpisodesGroup else {
|
||||
Logger.shared.log("extractEpisodes: thenBlock called but group already left", type: "Debug")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Result is not a string of extractEpisodes", type: "Error")
|
||||
hasLeftEpisodesGroup = true
|
||||
|
||||
if let jsonOfEpisodes = result.toString(),
|
||||
let dataEpisodes = jsonOfEpisodes.data(using: .utf8) {
|
||||
do {
|
||||
if let array = try JSONSerialization.jsonObject(with: dataEpisodes, options: []) as? [[String: Any]] {
|
||||
episodeLinks = array.map { item -> EpisodeLink in
|
||||
EpisodeLink(
|
||||
number: item["number"] as? Int ?? 0,
|
||||
title: "",
|
||||
href: item["href"] as? String ?? "",
|
||||
duration: nil
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Failed to parse JSON of extractEpisodes", type: "Error")
|
||||
}
|
||||
} catch {
|
||||
Logger.shared.log("JSON parsing error of extractEpisodes: \(error)", type: "Error")
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("Result is not a string of extractEpisodes", type: "Error")
|
||||
}
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
let catchBlockEpisodes: @convention(block) (JSValue) -> Void = { error in
|
||||
timeoutWorkItem.cancel()
|
||||
Logger.shared.log("Promise rejected of extractEpisodes: \(String(describing: error.toString()))", type: "Error")
|
||||
dispatchGroup.leave()
|
||||
episodesGroupQueue.sync {
|
||||
guard !hasLeftEpisodesGroup else {
|
||||
Logger.shared.log("extractEpisodes: catchBlock called but group already left", type: "Debug")
|
||||
return
|
||||
}
|
||||
hasLeftEpisodesGroup = true
|
||||
|
||||
Logger.shared.log("Promise rejected of extractEpisodes: \(String(describing: error.toString()))", type: "Error")
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
}
|
||||
|
||||
let thenFunctionEpisodes = JSValue(object: thenBlockEpisodes, in: context)
|
||||
|
|
|
|||
|
|
@ -59,30 +59,48 @@ extension JSController {
|
|||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
var chaptersArr: [[String: Any]] = []
|
||||
var hasLeftGroup = false
|
||||
let groupQueue = DispatchQueue(label: "extractChapters.group")
|
||||
|
||||
let thenBlock: @convention(block) (JSValue) -> Void = { jsValue in
|
||||
Logger.shared.log("extractChapters thenBlock: \(jsValue)", type: "Debug")
|
||||
if let arr = jsValue.toArray() as? [[String: Any]] {
|
||||
Logger.shared.log("extractChapters: parsed as array, count = \(arr.count)", type: "Debug")
|
||||
chaptersArr = arr
|
||||
} else if let jsonString = jsValue.toString(), let data = jsonString.data(using: .utf8) {
|
||||
do {
|
||||
if let arr = try JSONSerialization.jsonObject(with: data) as? [[String: Any]] {
|
||||
Logger.shared.log("extractChapters: parsed as JSON string, count = \(arr.count)", type: "Debug")
|
||||
chaptersArr = arr
|
||||
} else {
|
||||
Logger.shared.log("extractChapters: JSON string did not parse to array", type: "Error")
|
||||
}
|
||||
} catch {
|
||||
Logger.shared.log("JSON parsing error of extractChapters: \(error)", type: "Error")
|
||||
groupQueue.sync {
|
||||
guard !hasLeftGroup else {
|
||||
Logger.shared.log("extractChapters: thenBlock called but group already left", type: "Debug")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("extractChapters: could not parse result", type: "Error")
|
||||
hasLeftGroup = true
|
||||
|
||||
if let arr = jsValue.toArray() as? [[String: Any]] {
|
||||
Logger.shared.log("extractChapters: parsed as array, count = \(arr.count)", type: "Debug")
|
||||
chaptersArr = arr
|
||||
} else if let jsonString = jsValue.toString(), let data = jsonString.data(using: .utf8) {
|
||||
do {
|
||||
if let arr = try JSONSerialization.jsonObject(with: data) as? [[String: Any]] {
|
||||
Logger.shared.log("extractChapters: parsed as JSON string, count = \(arr.count)", type: "Debug")
|
||||
chaptersArr = arr
|
||||
} else {
|
||||
Logger.shared.log("extractChapters: JSON string did not parse to array", type: "Error")
|
||||
}
|
||||
} catch {
|
||||
Logger.shared.log("JSON parsing error of extractChapters: \(error)", type: "Error")
|
||||
}
|
||||
} else {
|
||||
Logger.shared.log("extractChapters: could not parse result", type: "Error")
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
let catchBlock: @convention(block) (JSValue) -> Void = { jsValue in
|
||||
Logger.shared.log("extractChapters catchBlock: \(jsValue)", type: "Error")
|
||||
group.leave()
|
||||
groupQueue.sync {
|
||||
guard !hasLeftGroup else {
|
||||
Logger.shared.log("extractChapters: catchBlock called but group already left", type: "Debug")
|
||||
return
|
||||
}
|
||||
hasLeftGroup = true
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
result.invokeMethod("then", withArguments: [thenBlock])
|
||||
result.invokeMethod("catch", withArguments: [catchBlock])
|
||||
|
|
@ -182,24 +200,42 @@ extension JSController {
|
|||
group.enter()
|
||||
var extractedText = ""
|
||||
var extractError: Error? = nil
|
||||
var hasLeftGroup = false
|
||||
let groupQueue = DispatchQueue(label: "extractText.group")
|
||||
|
||||
let thenBlock: @convention(block) (JSValue) -> Void = { jsValue in
|
||||
Logger.shared.log("extractText thenBlock: received value", type: "Debug")
|
||||
if let text = jsValue.toString(), !text.isEmpty {
|
||||
Logger.shared.log("extractText: successfully extracted text", type: "Debug")
|
||||
extractedText = text
|
||||
} else {
|
||||
extractError = JSError.emptyContent
|
||||
groupQueue.sync {
|
||||
guard !hasLeftGroup else {
|
||||
Logger.shared.log("extractText: thenBlock called but group already left", type: "Debug")
|
||||
return
|
||||
}
|
||||
hasLeftGroup = true
|
||||
|
||||
if let text = jsValue.toString(), !text.isEmpty {
|
||||
Logger.shared.log("extractText: successfully extracted text", type: "Debug")
|
||||
extractedText = text
|
||||
} else {
|
||||
extractError = JSError.emptyContent
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
|
||||
let catchBlock: @convention(block) (JSValue) -> Void = { jsValue in
|
||||
Logger.shared.log("extractText catchBlock: \(jsValue)", type: "Error")
|
||||
if extractedText.isEmpty {
|
||||
extractError = JSError.jsException(jsValue.toString() ?? "Unknown error")
|
||||
groupQueue.sync {
|
||||
guard !hasLeftGroup else {
|
||||
Logger.shared.log("extractText: catchBlock called but group already left", type: "Debug")
|
||||
return
|
||||
}
|
||||
hasLeftGroup = true
|
||||
|
||||
if extractedText.isEmpty {
|
||||
extractError = JSError.jsException(jsValue.toString() ?? "Unknown error")
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
group.leave()
|
||||
}
|
||||
|
||||
result.invokeMethod("then", withArguments: [thenBlock])
|
||||
|
|
@ -277,8 +313,8 @@ extension JSController {
|
|||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse,
|
||||
(200...299).contains(httpResponse.statusCode) else {
|
||||
guard let httpResponse = response as? HTTPURLResponse,
|
||||
(200...299).contains(httpResponse.statusCode) else {
|
||||
Logger.shared.log("Direct fetch failed with status code: \((response as? HTTPURLResponse)?.statusCode ?? -1)", type: "Error")
|
||||
throw JSError.invalidResponse
|
||||
}
|
||||
|
|
@ -317,4 +353,4 @@ extension JSController {
|
|||
Logger.shared.log("Direct fetch successful, content length: \(content.count)", type: "Debug")
|
||||
return content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,6 +341,8 @@ struct SettingsViewGeneral: View {
|
|||
.font(.caption)
|
||||
.foregroundStyle(.gray)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding(.top, -6)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
.environment(\.editMode, .constant(.active))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -634,8 +634,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
134A387B2DE4B5B90041B687 /* Downloads */,
|
||||
04536F702E04BA3B00A11248 /* JSController-Novel.swift */,
|
||||
133D7C8B2D2BE2640075467E /* JSController.swift */,
|
||||
04536F702E04BA3B00A11248 /* JSController-Novel.swift */,
|
||||
132AF1202D99951700A0140B /* JSController-Streams.swift */,
|
||||
132AF1222D9995C300A0140B /* JSController-Details.swift */,
|
||||
132AF1242D9995F900A0140B /* JSController-Search.swift */,
|
||||
|
|
|
|||
Loading…
Reference in a new issue