mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
Merge pull request #9 from hamzenis/Sora-JSCore
Some checks failed
Build and Release IPA / Build IPA (push) Has been cancelled
Some checks failed
Build and Release IPA / Build IPA (push) Has been cancelled
This commit is contained in:
commit
c643e02b8e
4 changed files with 135 additions and 9 deletions
|
|
@ -2,10 +2,10 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,46 @@ class JSController: ObservableObject {
|
|||
print("JavaScript log: \(message)")
|
||||
}
|
||||
context.setObject(logFunction, forKeyedSubscript: "log" as NSString)
|
||||
|
||||
// Because fetch isnt available in JSContext, it simulates the fetch api for Javascript.
|
||||
// Performs network calls with URLSession
|
||||
let fetchNativeFunction: @convention(block) (String, JSValue, JSValue) -> Void = { urlString, resolve, reject in
|
||||
guard let url = URL(string: urlString) else {
|
||||
print("Invalid URL")
|
||||
reject.call(withArguments: ["Invalid URL"])
|
||||
return
|
||||
}
|
||||
let task = URLSession.shared.dataTask(with: url) { data, _, error in
|
||||
if let error = error {
|
||||
print("Network error in fetchNativeFunction: \(error.localizedDescription)")
|
||||
reject.call(withArguments: [error.localizedDescription])
|
||||
return
|
||||
}
|
||||
guard let data = data else {
|
||||
print("No data in response")
|
||||
reject.call(withArguments: ["No data"])
|
||||
return
|
||||
}
|
||||
if let text = String(data: data, encoding: .utf8) {
|
||||
resolve.call(withArguments: [text])
|
||||
} else {
|
||||
print("Unable to decode data to text")
|
||||
reject.call(withArguments: ["Unable to decode data"])
|
||||
}
|
||||
}
|
||||
task.resume()
|
||||
}
|
||||
context.setObject(fetchNativeFunction, forKeyedSubscript: "fetchNative" as NSString)
|
||||
|
||||
// Define fetch for JavaScript
|
||||
let fetchDefinition = """
|
||||
function fetch(url) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fetchNative(url, resolve, reject);
|
||||
});
|
||||
}
|
||||
"""
|
||||
context.evaluateScript(fetchDefinition)
|
||||
}
|
||||
|
||||
func loadScript(_ script: String) {
|
||||
|
|
@ -154,4 +194,81 @@ class JSController: ObservableObject {
|
|||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
/// Use Javascript to fetch search results
|
||||
func fetchJsSearchResults(keyword: String, module: ScrapingModule, completion: @escaping ([SearchItem]) -> Void) {
|
||||
if let exception = context.exception {
|
||||
print("JavaScript exception: \(exception)")
|
||||
completion([])
|
||||
return
|
||||
}
|
||||
|
||||
guard let searchResultsFunction = context.objectForKeyedSubscript("searchResults") else {
|
||||
print("No JavaScript function searchResults found")
|
||||
completion([])
|
||||
return
|
||||
}
|
||||
|
||||
// Call the JavaScript function, passing in the parameter
|
||||
let promiseValue = searchResultsFunction.call(withArguments: [keyword])
|
||||
guard let promise = promiseValue else {
|
||||
print("searchResults did not return a Promise")
|
||||
completion([])
|
||||
return
|
||||
}
|
||||
|
||||
// Handles successful promise resolution.
|
||||
let thenBlock: @convention(block) (JSValue) -> Void = { result in
|
||||
|
||||
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.map { item -> SearchItem in
|
||||
let title = item["title"] as? String ?? ""
|
||||
let imageUrl = item["image"] as? String ?? "https://s4.anilist.co/file/anilistcdn/character/large/default.jpg"
|
||||
let href = item["href"] as? String ?? ""
|
||||
return SearchItem(title: title, imageUrl: imageUrl, href: href)
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
completion(resultItems)
|
||||
}
|
||||
|
||||
} else {
|
||||
print("Failed to parse JSON")
|
||||
DispatchQueue.main.async {
|
||||
completion([])
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("JSON parsing error: \(error)")
|
||||
DispatchQueue.main.async {
|
||||
completion([])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("Result is not a string")
|
||||
DispatchQueue.main.async {
|
||||
completion([])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles promise rejection.
|
||||
let catchBlock: @convention(block) (JSValue) -> Void = { error in
|
||||
print("Promise rejected: \(String(describing: error.toString()))")
|
||||
DispatchQueue.main.async {
|
||||
completion([])
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the Swift blocks into JSValue functions
|
||||
let thenFunction = JSValue(object: thenBlock, in: context)
|
||||
let catchFunction = JSValue(object: catchBlock, in: context)
|
||||
|
||||
// Attach the 'then' and 'catch' callbacks to the Promise
|
||||
promise.invokeMethod("then", withArguments: [thenFunction])
|
||||
promise.invokeMethod("catch", withArguments: [catchFunction])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ struct ModuleMetadata: Codable, Hashable {
|
|||
let baseUrl: String
|
||||
let searchBaseUrl: String
|
||||
let scriptUrl: String
|
||||
let asyncJS: Bool?
|
||||
}
|
||||
|
||||
struct ScrapingModule: Codable, Identifiable, Hashable {
|
||||
|
|
|
|||
|
|
@ -154,10 +154,18 @@ struct SearchView: View {
|
|||
do {
|
||||
let jsContent = try moduleManager.getModuleContent(module)
|
||||
jsController.loadScript(jsContent)
|
||||
jsController.fetchSearchResults(keyword: searchText, module: module) { items in
|
||||
searchItems = items
|
||||
hasNoResults = items.isEmpty
|
||||
isSearching = false
|
||||
if(module.metadata.asyncJS == false || module.metadata.asyncJS == nil) {
|
||||
jsController.fetchSearchResults(keyword: searchText, module: module) { items in
|
||||
searchItems = items
|
||||
hasNoResults = items.isEmpty
|
||||
isSearching = false
|
||||
}
|
||||
} else {
|
||||
jsController.fetchJsSearchResults(keyword: searchText, module: module) { items in
|
||||
searchItems = items
|
||||
hasNoResults = items.isEmpty
|
||||
isSearching = false
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("Error loading module: \(error)")
|
||||
|
|
|
|||
Loading…
Reference in a new issue