start work on explore content module addition, fork / modify "aniworld.to" script "https://devsforge.de/Sora/aniworld.json", modify jscontext / loading and update routines, tests

This commit is contained in:
Dominic Drees 2025-05-02 13:01:57 +02:00
parent 6f7b4ba092
commit 5f62e7f05e
5 changed files with 94 additions and 43 deletions

View file

@ -440,7 +440,7 @@
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Author:"
"value" : "Author: %@"
}
},
"en" : {

View file

@ -10,34 +10,32 @@ import JavaScriptCore
// TODO: implement and test
extension JSController {
func fetchExploreResults(module: ScrapingModule, completion: @escaping ([ExploreItem]) -> Void) {
completion([])
/*let searchUrl = module.metadata.searchBaseUrl.replacingOccurrences(of: "%s", with: keyword.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")
guard let url = URL(string: searchUrl) else {
guard let exploreUrl = module.metadata.exploreBaseUrl,
let url = URL(string: exploreUrl) else {
completion([])
return
}
URLSession.custom.dataTask(with: url) { [weak self] data, _, error in
guard let self = self else { return }
guard let self else { return }
if let error {
Logger.shared.log("Network error: \(error)",type: "Error")
Logger.shared.log("Network error: \(error)", type: .error)
DispatchQueue.main.async { completion([]) }
return
}
guard let data, let html = String(data: data, encoding: .utf8) else {
Logger.shared.log("Failed to decode HTML",type: "Error")
Logger.shared.log("Failed to decode HTML", type: .error)
DispatchQueue.main.async { completion([]) }
return
}
Logger.shared.log(html,type: "HTMLStrings")
if let parseFunction = self.context.objectForKeyedSubscript("searchResults"),
Logger.shared.log(html, type: .html)
if let parseFunction = self.context.objectForKeyedSubscript("exploreResults"),
let results = parseFunction.call(withArguments: [html]).toArray() as? [[String: String]] {
let resultItems = results.map { item in
SearchItem(
ExploreItem(
title: item["title"] ?? "",
imageUrl: item["image"] ?? "",
href: item["href"] ?? ""
@ -47,88 +45,82 @@ extension JSController {
completion(resultItems)
}
} else {
Logger.shared.log("Failed to parse results",type: "Error")
Logger.shared.log("Failed to parse results", type: .error)
DispatchQueue.main.async { completion([]) }
}
}.resume()
*/
}
func fetchJsExploreResults(module: ScrapingModule, completion: @escaping ([ExploreItem]) -> Void) {
completion([])
/*
if let exception = context.exception {
Logger.shared.log("JavaScript exception: \(exception)",type: "Error")
Logger.shared.log("JavaScript exception: \(exception)", type: .error)
completion([])
return
}
guard let searchResultsFunction = context.objectForKeyedSubscript("searchResults") else {
Logger.shared.log("No JavaScript function searchResults found",type: "Error")
guard let exploreResultsFunction = context.objectForKeyedSubscript("exploreResults") else {
Logger.shared.log("No JavaScript function exploreResults found", type: .error)
completion([])
return
}
let promiseValue = searchResultsFunction.call(withArguments: [keyword])
let promiseValue = exploreResultsFunction.call(withArguments: [])
guard let promise = promiseValue else {
Logger.shared.log("searchResults did not return a Promise",type: "Error")
Logger.shared.log("exploreResults did not return a Promise", type: .error)
completion([])
return
}
let thenBlock: @convention(block) (JSValue) -> Void = { result in
Logger.shared.log(result.toString(),type: "HTMLStrings")
Logger.shared.log(result.toString(), type: .html)
if let jsonString = result.toString(),
let data = jsonString.data(using: .utf8) {
do {
if let array = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] {
let resultItems = array.compactMap { item -> SearchItem? in
let resultItems = array.compactMap { item -> ExploreItem? in
guard let title = item["title"] as? String,
let imageUrl = item["image"] as? String,
let href = item["href"] as? String else {
Logger.shared.log("Missing or invalid data in search result item: \(item)", type: .error)
return nil
}
return SearchItem(title: title, imageUrl: imageUrl, href: href)
return ExploreItem(title: title, imageUrl: imageUrl, href: href)
}
DispatchQueue.main.async {
completion(resultItems)
}
} else {
Logger.shared.log("Failed to parse JSON",type: "Error")
Logger.shared.log("Failed to parse JSON", type: .error)
DispatchQueue.main.async {
completion([])
}
}
} catch {
Logger.shared.log("JSON parsing error: \(error)",type: "Error")
Logger.shared.log("JSON parsing error: \(error)", type: .error)
DispatchQueue.main.async {
completion([])
}
}
} else {
Logger.shared.log("Result is not a string",type: "Error")
Logger.shared.log("Result is not a string", type: .error)
DispatchQueue.main.async {
completion([])
}
}
}
let catchBlock: @convention(block) (JSValue) -> Void = { error in
Logger.shared.log("Promise rejected: \(String(describing: error.toString()))",type: "Error")
Logger.shared.log("Promise rejected: \(String(describing: error.toString()))", type: .error)
DispatchQueue.main.async {
completion([])
}
}
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])
*/
}
}

View file

@ -172,9 +172,26 @@ class ModuleManager: ObservableObject {
let localUrl = getDocumentsDirectory().appendingPathComponent(fileName)
try jsContent.write(to: localUrl, atomically: true, encoding: .utf8)
var exploreFileName: String?
if let exploreScriptValue = metadata.exploreScriptUrl {
guard let exploreScriptUrl = URL(string: exploreScriptValue) else {
throw NSError(domain: "Invalid script URL", code: -1)
}
let (exploreScriptData, _) = try await URLSession.custom.data(from: exploreScriptUrl)
guard let exploreJsContent = String(data: exploreScriptData, encoding: .utf8) else {
throw NSError(domain: "Invalid script encoding", code: -1)
}
exploreFileName = "\(UUID().uuidString).js"
let exploreLocalUrl = getDocumentsDirectory().appendingPathComponent(exploreFileName!)
try exploreJsContent.write(to: exploreLocalUrl, atomically: true, encoding: .utf8)
}
let module = ScrapingModule(
metadata: metadata,
localPath: fileName,
exploreLocalPath: exploreFileName,
metadataUrl: metadataUrl
)
@ -201,6 +218,15 @@ class ModuleManager: ObservableObject {
return try String(contentsOf: localUrl, encoding: .utf8)
}
func getModuleExploreContent(_ module: ScrapingModule) throws -> String? {
if let exploreLocalPath = module.exploreLocalPath {
let exploreLocalUrl = getDocumentsDirectory().appendingPathComponent(exploreLocalPath)
return try String(contentsOf: exploreLocalUrl, encoding: .utf8)
} else {
return nil
}
}
func refreshModules() async {
for (index, module) in modules.enumerated() {
do {
@ -220,10 +246,27 @@ class ModuleManager: ObservableObject {
let localUrl = getDocumentsDirectory().appendingPathComponent(module.localPath)
try jsContent.write(to: localUrl, atomically: true, encoding: .utf8)
var exploreFileName: String?
if let exploreScriptValue = newMetadata.exploreScriptUrl {
guard let exploreScriptUrl = URL(string: exploreScriptValue) else {
throw NSError(domain: "Invalid script URL", code: -1)
}
let (exploreScriptData, _) = try await URLSession.custom.data(from: exploreScriptUrl)
guard let exploreJsContent = String(data: exploreScriptData, encoding: .utf8) else {
throw NSError(domain: "Invalid script encoding", code: -1)
}
exploreFileName = module.exploreLocalPath ?? "\(UUID().uuidString).js"
let exploreLocalUrl = getDocumentsDirectory().appendingPathComponent(exploreFileName!)
try exploreJsContent.write(to: exploreLocalUrl, atomically: true, encoding: .utf8)
}
let updatedModule = ScrapingModule(
id: module.id,
metadata: newMetadata,
localPath: module.localPath,
exploreLocalPath: exploreFileName,
metadataUrl: module.metadataUrl,
isActive: module.isActive
)

View file

@ -18,6 +18,8 @@ struct ModuleMetadata: Codable, Hashable {
let quality: String
let searchBaseUrl: String
let scriptUrl: String
let exploreBaseUrl: String?
let exploreScriptUrl: String?
let asyncJS: Bool?
let streamAsyncJS: Bool?
let softsub: Bool?
@ -35,13 +37,22 @@ struct ScrapingModule: Codable, Identifiable, Hashable {
let id: UUID
let metadata: ModuleMetadata
let localPath: String
let exploreLocalPath: String?
let metadataUrl: String
var isActive: Bool
init(id: UUID = UUID(), metadata: ModuleMetadata, localPath: String, metadataUrl: String, isActive: Bool = false) {
init(
id: UUID = UUID(),
metadata: ModuleMetadata,
localPath: String,
exploreLocalPath: String?,
metadataUrl: String,
isActive: Bool = false
) {
self.id = id
self.metadata = metadata
self.localPath = localPath
self.exploreLocalPath = exploreLocalPath
self.metadataUrl = metadataUrl
self.isActive = isActive
}

View file

@ -284,7 +284,12 @@ struct ExploreView: View {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
Task {
do {
let jsContent = try moduleManager.getModuleContent(module)
guard let jsContent = try moduleManager.getModuleExploreContent(module) else {
isLoading = false
hasNoResults = true
return
}
jsController.loadScript(jsContent)
if module.metadata.asyncJS == true {
jsController.fetchJsExploreResults(module: module) { items in