mirror of
https://github.com/cranci1/Sora.git
synced 2026-01-11 20:10:24 +00:00
Media Info view
This commit is contained in:
parent
d519fc6110
commit
ee75542b2d
7 changed files with 131 additions and 12 deletions
|
|
@ -12,6 +12,7 @@
|
|||
1329D5CB2D298199008AEDA2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1329D5CA2D298199008AEDA2 /* Assets.xcassets */; };
|
||||
1329D5CE2D298199008AEDA2 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1329D5CD2D298199008AEDA2 /* Preview Assets.xcassets */; };
|
||||
133D7C1B2D2ADC430075467E /* URLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C1A2D2ADC430075467E /* URLSession.swift */; };
|
||||
133D7C1E2D2ADF110075467E /* MediaInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C1D2D2ADF110075467E /* MediaInfoView.swift */; };
|
||||
13AEE6192D2A75110096D953 /* Modules.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AEE6182D2A75110096D953 /* Modules.swift */; };
|
||||
13AEE61B2D2A78050096D953 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AEE61A2D2A78050096D953 /* SettingsView.swift */; };
|
||||
13AEE61D2D2A78160096D953 /* JSController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AEE61C2D2A78160096D953 /* JSController.swift */; };
|
||||
|
|
@ -30,6 +31,7 @@
|
|||
1329D5CD2D298199008AEDA2 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
1329D5DA2D29821B008AEDA2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
133D7C1A2D2ADC430075467E /* URLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSession.swift; sourceTree = "<group>"; };
|
||||
133D7C1D2D2ADF110075467E /* MediaInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaInfoView.swift; sourceTree = "<group>"; };
|
||||
13AEE6182D2A75110096D953 /* Modules.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modules.swift; sourceTree = "<group>"; };
|
||||
13AEE61A2D2A78050096D953 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
13AEE61C2D2A78160096D953 /* JSController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSController.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -107,9 +109,18 @@
|
|||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
133D7C1C2D2ADF060075467E /* MediaInfoView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
133D7C1D2D2ADF110075467E /* MediaInfoView.swift */,
|
||||
);
|
||||
path = MediaInfoView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
13AEE61E2D2AAD1E0096D953 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
133D7C1C2D2ADF060075467E /* MediaInfoView */,
|
||||
13AEE62A2D2ABCB40096D953 /* SettingsView */,
|
||||
13AEE6242D2AB1730096D953 /* HomeView.swift */,
|
||||
13AEE6262D2AB1990096D953 /* LibraryView.swift */,
|
||||
|
|
@ -237,6 +248,7 @@
|
|||
13AEE6252D2AB1730096D953 /* HomeView.swift in Sources */,
|
||||
13AEE61B2D2A78050096D953 /* SettingsView.swift in Sources */,
|
||||
1329D5C72D298198008AEDA2 /* Sora_JSApp.swift in Sources */,
|
||||
133D7C1E2D2ADF110075467E /* MediaInfoView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,17 +15,14 @@ struct ContentView: View {
|
|||
.tabItem {
|
||||
Label("Home", systemImage: "house")
|
||||
}
|
||||
|
||||
LibraryView()
|
||||
.tabItem {
|
||||
Label("Library", systemImage: "books.vertical")
|
||||
}
|
||||
|
||||
SearchView()
|
||||
.tabItem {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
}
|
||||
|
||||
SettingsView()
|
||||
.tabItem {
|
||||
Label("Settings", systemImage: "gear")
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class JSController: ObservableObject {
|
|||
context.evaluateScript(script)
|
||||
}
|
||||
|
||||
func searchContent(keyword: String, module: ScrapingModule, completion: @escaping ([MediaItem]) -> Void) {
|
||||
func fetchSearchResults(keyword: String, module: ScrapingModule, completion: @escaping ([MediaItem]) -> Void) {
|
||||
let searchUrl = module.metadata.searchBaseUrl.replacingOccurrences(of: "%s", with: keyword.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")
|
||||
|
||||
guard let url = URL(string: searchUrl) else {
|
||||
|
|
@ -51,17 +51,63 @@ class JSController: ObservableObject {
|
|||
return
|
||||
}
|
||||
|
||||
if let parseFunction = self.context.objectForKeyedSubscript("parseHTML"),
|
||||
if let parseFunction = self.context.objectForKeyedSubscript("searchResults"),
|
||||
let results = parseFunction.call(withArguments: [html]).toArray() as? [[String: String]] {
|
||||
let mediaItems = results.map { item in
|
||||
MediaItem(
|
||||
title: item["title"] ?? "",
|
||||
imageUrl: item["image"] ?? ""
|
||||
imageUrl: item["image"] ?? "",
|
||||
href: item["href"] ?? ""
|
||||
)
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
completion(mediaItems)
|
||||
}
|
||||
} else {
|
||||
print("Failed to parse results")
|
||||
DispatchQueue.main.async { completion([]) }
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
func fetchInfoContent(href: String, module: ScrapingModule, completion: @escaping ([MediaItem]) -> Void) {
|
||||
var baseUrl = module.metadata.baseUrl
|
||||
if !baseUrl.hasSuffix("/") && !href.hasPrefix("/") {
|
||||
baseUrl += "/"
|
||||
}
|
||||
baseUrl += href
|
||||
|
||||
guard let url = URL(string: baseUrl) else {
|
||||
completion([])
|
||||
return
|
||||
}
|
||||
|
||||
URLSession.custom.dataTask(with: url) { [weak self] data, response, error in
|
||||
guard let self = self else { return }
|
||||
|
||||
if let error = error {
|
||||
print("Network error: \(error)")
|
||||
DispatchQueue.main.async { completion([]) }
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data, let html = String(data: data, encoding: .utf8) else {
|
||||
print("Failed to decode HTML")
|
||||
DispatchQueue.main.async { completion([]) }
|
||||
return
|
||||
}
|
||||
|
||||
if let parseFunction = self.context.objectForKeyedSubscript("fetchInfo"),
|
||||
let results = parseFunction.call(withArguments: [html]).toArray() as? [[String: String]] {
|
||||
let mediaItems = results.map { item in
|
||||
MediaItem(
|
||||
title: item["title"] ?? "",
|
||||
imageUrl: item["image"] ?? "",
|
||||
href: item["href"] ?? ""
|
||||
)
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
completion(mediaItems)
|
||||
print(mediaItems)
|
||||
}
|
||||
} else {
|
||||
print("Failed to parse results")
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ struct ScrapingModule: Codable, Identifiable, Hashable {
|
|||
|
||||
class ModuleManager: ObservableObject {
|
||||
@Published var modules: [ScrapingModule] = []
|
||||
|
||||
private let fileManager = FileManager.default
|
||||
private let modulesFileName = "modules.json"
|
||||
|
||||
|
|
|
|||
53
Sora-JS/Views/MediaInfoView/MediaInfoView.swift
Normal file
53
Sora-JS/Views/MediaInfoView/MediaInfoView.swift
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// MediaInfoView.swift
|
||||
// Sora-JS
|
||||
//
|
||||
// Created by Francesco on 05/01/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Kingfisher
|
||||
|
||||
struct MediaInfoView: View {
|
||||
let title: String
|
||||
let imageUrl: String
|
||||
let href: String
|
||||
let module: ScrapingModule
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
KFImage(URL(string: imageUrl))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
|
||||
Text(title)
|
||||
.font(.largeTitle)
|
||||
.padding()
|
||||
|
||||
Button(action: {
|
||||
var finalHref = href
|
||||
if !href.starts(with: "http") {
|
||||
var baseUrl = module.metadata.baseUrl
|
||||
if !baseUrl.hasSuffix("/") && !href.hasPrefix("/") {
|
||||
baseUrl += "/"
|
||||
}
|
||||
finalHref = baseUrl + href
|
||||
}
|
||||
if let url = URL(string: finalHref) {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}) {
|
||||
Text("Open Link")
|
||||
.font(.headline)
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
.padding()
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.navigationTitle("Media Info")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,15 +12,18 @@ struct MediaItem: Identifiable {
|
|||
let id = UUID()
|
||||
let title: String
|
||||
let imageUrl: String
|
||||
let href: String
|
||||
}
|
||||
|
||||
struct SearchView: View {
|
||||
@AppStorage("selectedModuleId") private var selectedModuleId: String?
|
||||
@StateObject private var jsController = JSController()
|
||||
@EnvironmentObject var moduleManager: ModuleManager
|
||||
@State private var searchText = ""
|
||||
|
||||
@State private var mediaItems: [MediaItem] = []
|
||||
@State private var selectedMediaItem: MediaItem?
|
||||
@State private var isSearching = false
|
||||
@AppStorage("selectedModuleId") private var selectedModuleId: String?
|
||||
@State private var searchText = ""
|
||||
|
||||
private var selectedModule: ScrapingModule? {
|
||||
guard let id = selectedModuleId else { return nil }
|
||||
|
|
@ -69,6 +72,9 @@ struct SearchView: View {
|
|||
.padding([.leading, .bottom], 8)
|
||||
.lineLimit(1)
|
||||
}
|
||||
.onTapGesture {
|
||||
selectedMediaItem = item
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
|
@ -111,6 +117,9 @@ struct SearchView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.sheet(item: $selectedMediaItem) { item in
|
||||
MediaInfoView(title: item.title, imageUrl: item.imageUrl, href: item.href, module: selectedModule!)
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.onChange(of: selectedModuleId) { _ in
|
||||
|
|
@ -132,7 +141,7 @@ struct SearchView: View {
|
|||
do {
|
||||
let jsContent = try moduleManager.getModuleContent(module)
|
||||
jsController.loadScript(jsContent)
|
||||
jsController.searchContent(keyword: searchText, module: module) { items in
|
||||
jsController.fetchSearchResults(keyword: searchText, module: module) { items in
|
||||
mediaItems = items
|
||||
isSearching = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ import SwiftUI
|
|||
import Kingfisher
|
||||
|
||||
struct SettingsViewModule: View {
|
||||
@EnvironmentObject var moduleManager: ModuleManager
|
||||
@AppStorage("selectedModuleId") private var selectedModuleId: String?
|
||||
@State private var isLoading = false
|
||||
@EnvironmentObject var moduleManager: ModuleManager
|
||||
|
||||
@State private var errorMessage: String?
|
||||
@State private var isLoading = false
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
|
|
|||
Loading…
Reference in a new issue