Plugins: Add website and about properties

These will serve as descriptions for a plugin which will be displayed
in the Plugin Info screen.

website has also replaced baseUrl and dynamicWebsite has replaced
dynamicBaseUrl

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2023-04-02 00:18:18 -04:00
parent 51366f3215
commit d918039810
14 changed files with 162 additions and 73 deletions

View file

@ -137,6 +137,8 @@
0CBC7705288DE7F40054BE44 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBC7704288DE7F40054BE44 /* PersistenceController.swift */; }; 0CBC7705288DE7F40054BE44 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBC7704288DE7F40054BE44 /* PersistenceController.swift */; };
0CC389532970AD900066D06F /* Action+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CC389512970AD900066D06F /* Action+CoreDataClass.swift */; }; 0CC389532970AD900066D06F /* Action+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CC389512970AD900066D06F /* Action+CoreDataClass.swift */; };
0CC389542970AD900066D06F /* Action+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CC389522970AD900066D06F /* Action+CoreDataProperties.swift */; }; 0CC389542970AD900066D06F /* Action+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CC389522970AD900066D06F /* Action+CoreDataProperties.swift */; };
0CD4030A29DA01B6008D9F03 /* PluginInfoMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD4030929DA01B6008D9F03 /* PluginInfoMetaView.swift */; };
0CD4030C29DA0222008D9F03 /* PluginInfoAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD4030B29DA0222008D9F03 /* PluginInfoAboutView.swift */; };
0CD4CAC628C980EB0046E1DC /* HistoryActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */; }; 0CD4CAC628C980EB0046E1DC /* HistoryActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */; };
0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */; }; 0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */; };
0CD5F1FB299BEFBE00476DDB /* CustomScopeBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5F1FA299BEFBE00476DDB /* CustomScopeBar.swift */; }; 0CD5F1FB299BEFBE00476DDB /* CustomScopeBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5F1FA299BEFBE00476DDB /* CustomScopeBar.swift */; };
@ -277,6 +279,8 @@
0CC389512970AD900066D06F /* Action+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Action+CoreDataClass.swift"; sourceTree = "<group>"; }; 0CC389512970AD900066D06F /* Action+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Action+CoreDataClass.swift"; sourceTree = "<group>"; };
0CC389522970AD900066D06F /* Action+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Action+CoreDataProperties.swift"; sourceTree = "<group>"; }; 0CC389522970AD900066D06F /* Action+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Action+CoreDataProperties.swift"; sourceTree = "<group>"; };
0CC6E4D428A45BA000AF2BCC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; 0CC6E4D428A45BA000AF2BCC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
0CD4030929DA01B6008D9F03 /* PluginInfoMetaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginInfoMetaView.swift; sourceTree = "<group>"; };
0CD4030B29DA0222008D9F03 /* PluginInfoAboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginInfoAboutView.swift; sourceTree = "<group>"; };
0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryActionsView.swift; sourceTree = "<group>"; }; 0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryActionsView.swift; sourceTree = "<group>"; };
0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisabledAppearance.swift; sourceTree = "<group>"; }; 0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisabledAppearance.swift; sourceTree = "<group>"; };
0CD5F1FA299BEFBE00476DDB /* CustomScopeBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomScopeBar.swift; sourceTree = "<group>"; }; 0CD5F1FA299BEFBE00476DDB /* CustomScopeBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomScopeBar.swift; sourceTree = "<group>"; };
@ -420,6 +424,7 @@
0C3E00D4296F560800ECECB2 /* Plugin */ = { 0C3E00D4296F560800ECECB2 /* Plugin */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0CD4030829DA01A3008D9F03 /* Info */,
0C44E2AA28D4E09B007711AE /* Buttons */, 0C44E2AA28D4E09B007711AE /* Buttons */,
0C794B65289DAC9F00DD1CC8 /* Source */, 0C794B65289DAC9F00DD1CC8 /* Source */,
0C0D50E6288DFF850035ECC8 /* PluginAggregateView.swift */, 0C0D50E6288DFF850035ECC8 /* PluginAggregateView.swift */,
@ -665,6 +670,15 @@
path = DataManagement; path = DataManagement;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
0CD4030829DA01A3008D9F03 /* Info */ = {
isa = PBXGroup;
children = (
0CD4030929DA01B6008D9F03 /* PluginInfoMetaView.swift */,
0CD4030B29DA0222008D9F03 /* PluginInfoAboutView.swift */,
);
path = Info;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -794,8 +808,10 @@
0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */, 0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */,
0CA148DB288903F000DE2211 /* NavView.swift in Sources */, 0CA148DB288903F000DE2211 /* NavView.swift in Sources */,
0CA3B23C28C2AA5600616D3A /* Bookmark+CoreDataClass.swift in Sources */, 0CA3B23C28C2AA5600616D3A /* Bookmark+CoreDataClass.swift in Sources */,
0CD4030A29DA01B6008D9F03 /* PluginInfoMetaView.swift in Sources */,
0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */, 0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */,
0CA3FB2028B91D9500FA10A8 /* IndeterminateProgressView.swift in Sources */, 0CA3FB2028B91D9500FA10A8 /* IndeterminateProgressView.swift in Sources */,
0CD4030C29DA0222008D9F03 /* PluginInfoAboutView.swift in Sources */,
0C50055A2992BA6A0064606A /* PluginTag+CoreDataClass.swift in Sources */, 0C50055A2992BA6A0064606A /* PluginTag+CoreDataClass.swift in Sources */,
0C1A3E5229C8A7F500DA9730 /* SettingsModels.swift in Sources */, 0C1A3E5229C8A7F500DA9730 /* SettingsModels.swift in Sources */,
0CBC7705288DE7F40054BE44 /* PersistenceController.swift in Sources */, 0CBC7705288DE7F40054BE44 /* PersistenceController.swift in Sources */,
@ -1129,7 +1145,7 @@
repositoryURL = "https://github.com/siteline/SwiftUI-Introspect/"; repositoryURL = "https://github.com/siteline/SwiftUI-Introspect/";
requirement = { requirement = {
kind = upToNextMajorVersion; kind = upToNextMajorVersion;
minimumVersion = 0.2.2; minimumVersion = 0.2.3;
}; };
}; };
0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */ = { 0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */ = {

View file

@ -19,6 +19,8 @@ public extension Action {
@NSManaged var name: String @NSManaged var name: String
@NSManaged var deeplink: String? @NSManaged var deeplink: String?
@NSManaged var version: Int16 @NSManaged var version: Int16
@NSManaged var about: String?
@NSManaged var website: String?
@NSManaged var requires: [String] @NSManaged var requires: [String]
@NSManaged var author: String @NSManaged var author: String
@NSManaged var enabled: Bool @NSManaged var enabled: Bool

View file

@ -15,9 +15,10 @@ public extension Source {
} }
@NSManaged var id: UUID @NSManaged var id: UUID
@NSManaged var baseUrl: String? @NSManaged var about: String?
@NSManaged var website: String?
@NSManaged var dynamicWebsite: Bool
@NSManaged var fallbackUrls: [String]? @NSManaged var fallbackUrls: [String]?
@NSManaged var dynamicBaseUrl: Bool
@NSManaged var enabled: Bool @NSManaged var enabled: Bool
@NSManaged var name: String @NSManaged var name: String
@NSManaged var author: String @NSManaged var author: String

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Action" representedClassName="Action" syncable="YES"> <entity name="Action" representedClassName="Action" syncable="YES">
<attribute name="about" optional="YES" attributeType="String"/>
<attribute name="author" attributeType="String" defaultValueString=""/> <attribute name="author" attributeType="String" defaultValueString=""/>
<attribute name="deeplink" optional="YES" attributeType="String"/> <attribute name="deeplink" optional="YES" attributeType="String"/>
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
@ -9,6 +10,7 @@
<attribute name="name" attributeType="String" defaultValueString=""/> <attribute name="name" attributeType="String" defaultValueString=""/>
<attribute name="requires" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[String]"/> <attribute name="requires" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[String]"/>
<attribute name="version" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="version" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="website" optional="YES" attributeType="String"/>
<relationship name="tags" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PluginTag" inverseName="parentAction" inverseEntity="PluginTag"/> <relationship name="tags" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PluginTag" inverseName="parentAction" inverseEntity="PluginTag"/>
</entity> </entity>
<entity name="Bookmark" representedClassName="Bookmark" syncable="YES"> <entity name="Bookmark" representedClassName="Bookmark" syncable="YES">
@ -53,9 +55,9 @@
<relationship name="parentSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="tags" inverseEntity="Source"/> <relationship name="parentSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="tags" inverseEntity="Source"/>
</entity> </entity>
<entity name="Source" representedClassName="Source" syncable="YES"> <entity name="Source" representedClassName="Source" syncable="YES">
<attribute name="about" optional="YES" attributeType="String"/>
<attribute name="author" attributeType="String" defaultValueString=""/> <attribute name="author" attributeType="String" defaultValueString=""/>
<attribute name="baseUrl" optional="YES" attributeType="String"/> <attribute name="dynamicWebsite" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="dynamicBaseUrl" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="fallbackUrls" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[String]"/> <attribute name="fallbackUrls" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[String]"/>
<attribute name="id" attributeType="UUID" usesScalarValueType="NO"/> <attribute name="id" attributeType="UUID" usesScalarValueType="NO"/>
@ -64,6 +66,7 @@
<attribute name="preferredParser" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="preferredParser" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="trackers" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[String]"/> <attribute name="trackers" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[String]"/>
<attribute name="version" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="version" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="website" optional="YES" attributeType="String"/>
<relationship name="api" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceApi" inverseName="parentSource" inverseEntity="SourceApi"/> <relationship name="api" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceApi" inverseName="parentSource" inverseEntity="SourceApi"/>
<relationship name="htmlParser" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceHtmlParser" inverseName="parentSource" inverseEntity="SourceHtmlParser"/> <relationship name="htmlParser" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceHtmlParser" inverseName="parentSource" inverseEntity="SourceHtmlParser"/>
<relationship name="jsonParser" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceJsonParser" inverseName="parentSource" inverseEntity="SourceJsonParser"/> <relationship name="jsonParser" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceJsonParser" inverseName="parentSource" inverseEntity="SourceJsonParser"/>

View file

@ -11,6 +11,8 @@ public struct ActionJson: Codable, Hashable, PluginJson {
public let name: String public let name: String
public let version: Int16 public let version: Int16
let minVersion: String? let minVersion: String?
let about: String?
let website: String?
let requires: [ActionRequirement] let requires: [ActionRequirement]
let deeplink: [DeeplinkActionJson]? let deeplink: [DeeplinkActionJson]?
public let author: String? public let author: String?
@ -21,6 +23,8 @@ public struct ActionJson: Codable, Hashable, PluginJson {
public init(name: String, public init(name: String,
version: Int16, version: Int16,
minVersion: String?, minVersion: String?,
about: String?,
website: String?,
requires: [ActionRequirement], requires: [ActionRequirement],
deeplink: [DeeplinkActionJson]?, deeplink: [DeeplinkActionJson]?,
author: String?, author: String?,
@ -31,6 +35,8 @@ public struct ActionJson: Codable, Hashable, PluginJson {
self.name = name self.name = name
self.version = version self.version = version
self.minVersion = minVersion self.minVersion = minVersion
self.about = about
self.website = website
self.requires = requires self.requires = requires
self.deeplink = deeplink self.deeplink = deeplink
self.author = author self.author = author
@ -44,6 +50,8 @@ public struct ActionJson: Codable, Hashable, PluginJson {
name = try container.decode(String.self, forKey: .name) name = try container.decode(String.self, forKey: .name)
version = try container.decode(Int16.self, forKey: .version) version = try container.decode(Int16.self, forKey: .version)
minVersion = try container.decodeIfPresent(String.self, forKey: .minVersion) minVersion = try container.decodeIfPresent(String.self, forKey: .minVersion)
about = try container.decodeIfPresent(String.self, forKey: .about)
website = try container.decodeIfPresent(String.self, forKey: .website)
requires = try container.decode([ActionRequirement].self, forKey: .requires) requires = try container.decode([ActionRequirement].self, forKey: .requires)
author = try container.decodeIfPresent(String.self, forKey: .author) author = try container.decodeIfPresent(String.self, forKey: .author)
listId = nil listId = nil

View file

@ -16,9 +16,10 @@ public struct SourceJson: Codable, Hashable, Sendable, PluginJson {
public let name: String public let name: String
public let version: Int16 public let version: Int16
let minVersion: String? let minVersion: String?
let baseUrl: String? let about: String?
let website: String?
let dynamicWebsite: Bool?
let fallbackUrls: [String]? let fallbackUrls: [String]?
let dynamicBaseUrl: Bool?
let trackers: [String]? let trackers: [String]?
let api: SourceApiJson? let api: SourceApiJson?
let jsonParser: SourceJsonParserJson? let jsonParser: SourceJsonParserJson?

View file

@ -14,6 +14,8 @@ public protocol Plugin: ObservableObject, NSManagedObject {
var name: String { get set } var name: String { get set }
var version: Int16 { get set } var version: Int16 { get set }
var author: String { get set } var author: String { get set }
var about: String? { get set }
var website: String? { get set }
var enabled: Bool { get set } var enabled: Bool { get set }
var tags: NSOrderedSet? { get set } var tags: NSOrderedSet? { get set }
func getTags() -> [PluginTagJson] func getTags() -> [PluginTagJson]

View file

@ -124,9 +124,10 @@ public class PluginManager: ObservableObject {
name: inputJson.name, name: inputJson.name,
version: inputJson.version, version: inputJson.version,
minVersion: inputJson.minVersion, minVersion: inputJson.minVersion,
baseUrl: inputJson.baseUrl, about: inputJson.about,
website: inputJson.website,
dynamicWebsite: inputJson.dynamicWebsite,
fallbackUrls: inputJson.fallbackUrls, fallbackUrls: inputJson.fallbackUrls,
dynamicBaseUrl: inputJson.dynamicBaseUrl,
trackers: inputJson.trackers, trackers: inputJson.trackers,
api: inputJson.api, api: inputJson.api,
jsonParser: inputJson.jsonParser, jsonParser: inputJson.jsonParser,
@ -154,6 +155,8 @@ public class PluginManager: ObservableObject {
name: inputJson.name, name: inputJson.name,
version: inputJson.version, version: inputJson.version,
minVersion: inputJson.minVersion, minVersion: inputJson.minVersion,
about: inputJson.about,
website: inputJson.website,
requires: inputJson.requires, requires: inputJson.requires,
deeplink: filteredDeeplinks, deeplink: filteredDeeplinks,
author: pluginList.author, author: pluginList.author,
@ -409,6 +412,8 @@ public class PluginManager: ObservableObject {
newAction.id = UUID() newAction.id = UUID()
newAction.name = actionJson.name newAction.name = actionJson.name
newAction.version = actionJson.version newAction.version = actionJson.version
newAction.website = actionJson.website
newAction.about = actionJson.about
newAction.author = actionJson.author ?? "Unknown" newAction.author = actionJson.author ?? "Unknown"
newAction.listId = actionJson.listId newAction.listId = actionJson.listId
newAction.requires = actionJson.requires.map(\.rawValue) newAction.requires = actionJson.requires.map(\.rawValue)
@ -448,9 +453,9 @@ public class PluginManager: ObservableObject {
let backgroundContext = PersistenceController.shared.backgroundContext let backgroundContext = PersistenceController.shared.backgroundContext
// If there's no base URL and it isn't dynamic, return before any transactions occur // If there's no base URL and it isn't dynamic, return before any transactions occur
let dynamicBaseUrl = sourceJson.dynamicBaseUrl ?? false let dynamicWebsite = sourceJson.dynamicWebsite ?? false
if !dynamicBaseUrl, sourceJson.baseUrl == nil { if !dynamicWebsite, sourceJson.website == nil {
await logManager?.error("Not adding this source because base URL parameters are malformed. Please contact the source dev.") await logManager?.error("Not adding this source because website parameters are malformed. Please contact the source dev.")
return return
} }
@ -472,9 +477,10 @@ public class PluginManager: ObservableObject {
newSource.id = UUID() newSource.id = UUID()
newSource.name = sourceJson.name newSource.name = sourceJson.name
newSource.version = sourceJson.version newSource.version = sourceJson.version
newSource.dynamicBaseUrl = dynamicBaseUrl newSource.about = sourceJson.about
newSource.baseUrl = sourceJson.baseUrl newSource.website = sourceJson.website
newSource.fallbackUrls = dynamicBaseUrl ? nil : sourceJson.fallbackUrls newSource.dynamicWebsite = dynamicWebsite
newSource.fallbackUrls = dynamicWebsite ? nil : sourceJson.fallbackUrls
newSource.author = sourceJson.author ?? "Unknown" newSource.author = sourceJson.author ?? "Unknown"
newSource.listId = sourceJson.listId newSource.listId = sourceJson.listId
newSource.trackers = sourceJson.trackers newSource.trackers = sourceJson.trackers

View file

@ -144,7 +144,7 @@ class ScrapingViewModel: ObservableObject {
} }
func executeParser(source: Source) async -> SearchRequestResult? { func executeParser(source: Source) async -> SearchRequestResult? {
guard let baseUrl = source.baseUrl else { guard let website = source.website else {
await logManager?.error("Scraping: The base URL could not be found for source \(source.name)") await logManager?.error("Scraping: The base URL could not be found for source \(source.name)")
return nil return nil
@ -167,7 +167,7 @@ class ScrapingViewModel: ObservableObject {
} }
let data = await handleUrls( let data = await handleUrls(
baseUrl: baseUrl, website: website,
replacedSearchUrl: replacedSearchUrl, replacedSearchUrl: replacedSearchUrl,
fallbackUrls: source.fallbackUrls, fallbackUrls: source.fallbackUrls,
sourceName: source.name sourceName: source.name
@ -176,7 +176,7 @@ class ScrapingViewModel: ObservableObject {
if let data, if let data,
let html = String(data: data, encoding: .utf8) let html = String(data: data, encoding: .utf8)
{ {
return await scrapeHtml(source: source, baseUrl: baseUrl, html: html) return await scrapeHtml(source: source, website: website, html: html)
} }
} }
case .rss: case .rss:
@ -194,7 +194,7 @@ class ScrapingViewModel: ObservableObject {
) )
} else { } else {
data = await handleUrls( data = await handleUrls(
baseUrl: baseUrl, website: website,
replacedSearchUrl: replacedSearchUrl, replacedSearchUrl: replacedSearchUrl,
fallbackUrls: source.fallbackUrls, fallbackUrls: source.fallbackUrls,
sourceName: source.name sourceName: source.name
@ -220,7 +220,7 @@ class ScrapingViewModel: ObservableObject {
replacement: "{clientId}", replacement: "{clientId}",
searchUrl: replacedSearchUrl, searchUrl: replacedSearchUrl,
apiUrl: sourceApi.apiUrl, apiUrl: sourceApi.apiUrl,
baseUrl: baseUrl, website: website,
sourceName: source.name) sourceName: source.name)
{ {
replacedSearchUrl = newSearchUrl replacedSearchUrl = newSearchUrl
@ -233,7 +233,7 @@ class ScrapingViewModel: ObservableObject {
replacement: "{secret}", replacement: "{secret}",
searchUrl: replacedSearchUrl, searchUrl: replacedSearchUrl,
apiUrl: sourceApi.apiUrl, apiUrl: sourceApi.apiUrl,
baseUrl: baseUrl, website: website,
sourceName: source.name) sourceName: source.name)
{ {
replacedSearchUrl = newSearchUrl replacedSearchUrl = newSearchUrl
@ -241,9 +241,9 @@ class ScrapingViewModel: ObservableObject {
} }
} }
let passedUrl = source.api?.apiUrl ?? baseUrl let passedUrl = source.api?.apiUrl ?? website
let data = await handleUrls( let data = await handleUrls(
baseUrl: passedUrl, website: passedUrl,
replacedSearchUrl: replacedSearchUrl, replacedSearchUrl: replacedSearchUrl,
fallbackUrls: source.fallbackUrls, fallbackUrls: source.fallbackUrls,
sourceName: source.name sourceName: source.name
@ -261,8 +261,8 @@ class ScrapingViewModel: ObservableObject {
} }
// Checks the base URL for any website data then iterates through the fallback URLs // Checks the base URL for any website data then iterates through the fallback URLs
func handleUrls(baseUrl: String, replacedSearchUrl: String?, fallbackUrls: [String]?, sourceName: String) async -> Data? { func handleUrls(website: String, replacedSearchUrl: String?, fallbackUrls: [String]?, sourceName: String) async -> Data? {
let fetchUrl = baseUrl + (replacedSearchUrl.map { $0 } ?? "") let fetchUrl = website + (replacedSearchUrl.map { $0 } ?? "")
if let data = await fetchWebsiteData(urlString: fetchUrl, sourceName: sourceName) { if let data = await fetchWebsiteData(urlString: fetchUrl, sourceName: sourceName) {
return data return data
} }
@ -283,7 +283,7 @@ class ScrapingViewModel: ObservableObject {
replacement: String, replacement: String,
searchUrl: String, searchUrl: String,
apiUrl: String?, apiUrl: String?,
baseUrl: String, website: String,
sourceName: String) async -> String? sourceName: String) async -> String?
{ {
// Is the credential expired // Is the credential expired
@ -302,7 +302,7 @@ class ScrapingViewModel: ObservableObject {
credential.value == nil || isExpired, credential.value == nil || isExpired,
let credentialUrl = credential.urlString, let credentialUrl = credential.urlString,
let newValue = await fetchApiCredential( let newValue = await fetchApiCredential(
urlString: (apiUrl ?? baseUrl) + credentialUrl, urlString: (apiUrl ?? website) + credentialUrl,
credential: credential, credential: credential,
sourceName: sourceName sourceName: sourceName
) )
@ -733,7 +733,7 @@ class ScrapingViewModel: ObservableObject {
} }
// HTML scraper // HTML scraper
public func scrapeHtml(source: Source, baseUrl: String, html: String) async -> SearchRequestResult? { public func scrapeHtml(source: Source, website: String, html: String) async -> SearchRequestResult? {
guard let htmlParser = source.htmlParser else { guard let htmlParser = source.htmlParser else {
return nil return nil
} }
@ -783,7 +783,7 @@ class ScrapingViewModel: ObservableObject {
continue continue
} }
let replacedMagnetUrl = externalMagnetUrl.starts(with: "/") ? baseUrl + externalMagnetUrl : externalMagnetUrl let replacedMagnetUrl = externalMagnetUrl.starts(with: "/") ? website + externalMagnetUrl : externalMagnetUrl
guard guard
let data = await fetchWebsiteData(urlString: replacedMagnetUrl, sourceName: source.name), let data = await fetchWebsiteData(urlString: replacedMagnetUrl, sourceName: source.name),
let magnetHtml = String(data: data, encoding: .utf8) let magnetHtml = String(data: data, encoding: .utf8)

View file

@ -0,0 +1,30 @@
//
// PluginInfoAboutView.swift
// Ferrite
//
// Created by Brian Dashore on 4/2/23.
//
import SwiftUI
struct PluginInfoAboutView<P: Plugin>: View {
@ObservedObject var selectedPlugin: P
var body: some View {
Section(header: InlineHeader("Description")) {
VStack(alignment: .leading, spacing: 10) {
if let pluginAbout = selectedPlugin.about {
if pluginAbout.last == "\n" {
Text(pluginAbout.dropLast())
} else {
Text(pluginAbout)
}
}
if let pluginWebsite = selectedPlugin.website {
Link("Website", destination: URL(string: pluginWebsite) ?? URL(string: "https://kingbri.dev/ferrite")!)
}
}
}
}
}

View file

@ -0,0 +1,54 @@
//
// PluginInfoMetaView.swift
// Ferrite
//
// Created by Brian Dashore on 4/2/23.
//
import SwiftUI
struct PluginInfoMetaView<P: Plugin>: View {
@ObservedObject var selectedPlugin: P
@FetchRequest(
entity: PluginList.entity(),
sortDescriptors: []
) var pluginLists: FetchedResults<PluginList>
var body: some View {
Section(header: InlineHeader("Metadata")) {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 5) {
HStack(spacing: 5) {
Text(selectedPlugin.name)
Text("v\(selectedPlugin.version)")
.foregroundColor(.secondary)
}
Text("by \(selectedPlugin.author)")
.foregroundColor(.secondary)
Group {
Text("ID: \(selectedPlugin.id)")
if let pluginList = pluginLists.first(where: { $0.id == selectedPlugin.listId })
{
Text("List: \(pluginList.name)")
Text("List ID: \(pluginList.id.uuidString)")
} else {
Text("No plugin list found. This source should be removed.")
}
}
.foregroundColor(.secondary)
.font(.caption)
}
if let tags = selectedPlugin.getTags(), !tags.isEmpty {
PluginTagsView(tags: tags)
}
}
.padding(.vertical, 2)
}
}
}

View file

@ -12,48 +12,14 @@ struct PluginInfoView<P: Plugin>: View {
@Binding var selectedPlugin: P? @Binding var selectedPlugin: P?
@FetchRequest(
entity: PluginList.entity(),
sortDescriptors: []
) var pluginLists: FetchedResults<PluginList>
var body: some View { var body: some View {
NavView { NavView {
List { List {
if let selectedPlugin { if let selectedPlugin {
Section(header: InlineHeader("Info")) { PluginInfoMetaView(selectedPlugin: selectedPlugin)
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 5) {
HStack(spacing: 5) {
Text(selectedPlugin.name)
Text("v\(selectedPlugin.version)") if selectedPlugin.about != nil || selectedPlugin.website != nil {
.foregroundColor(.secondary) PluginInfoAboutView(selectedPlugin: selectedPlugin)
}
Text("by \(selectedPlugin.author)")
.foregroundColor(.secondary)
Group {
Text("ID: \(selectedPlugin.id)")
if let pluginList = pluginLists.first(where: { $0.id == selectedPlugin.listId })
{
Text("List: \(pluginList.name)")
Text("List ID: \(pluginList.id.uuidString)")
} else {
Text("No plugin list found. This source should be removed.")
}
}
.foregroundColor(.secondary)
.font(.caption)
}
if let tags = selectedPlugin.getTags(), !tags.isEmpty {
PluginTagsView(tags: tags)
}
}
.padding(.vertical, 2)
} }
if let selectedSource = selectedPlugin as? Source { if let selectedSource = selectedPlugin as? Source {

View file

@ -10,18 +10,18 @@ import SwiftUI
struct SourceSettingsBaseUrlView: View { struct SourceSettingsBaseUrlView: View {
@ObservedObject var selectedSource: Source @ObservedObject var selectedSource: Source
@State private var tempBaseUrl: String = "" @State private var tempSite: String = ""
var body: some View { var body: some View {
Section( Section(
header: InlineHeader("Base URL"), header: InlineHeader("Base URL"),
footer: Text("Enter the base URL of your server.") footer: Text("Enter the base URL of your server.")
) { ) {
TextField("https://...", text: $tempBaseUrl, onEditingChanged: { isFocused in TextField("https://...", text: $tempSite, onEditingChanged: { isFocused in
if !isFocused { if !isFocused {
if tempBaseUrl.last == "/" { if tempSite.last == "/" {
selectedSource.baseUrl = String(tempBaseUrl.dropLast()) selectedSource.website = String(tempSite.dropLast())
} else { } else {
selectedSource.baseUrl = tempBaseUrl selectedSource.website = tempSite
} }
} }
}) })
@ -29,7 +29,7 @@ struct SourceSettingsBaseUrlView: View {
.autocorrectionDisabled(true) .autocorrectionDisabled(true)
.autocapitalization(.none) .autocapitalization(.none)
.onAppear { .onAppear {
tempBaseUrl = selectedSource.baseUrl ?? "" tempSite = selectedSource.website ?? ""
} }
} }
} }

View file

@ -11,7 +11,7 @@ struct SourceSettingsView: View {
@ObservedObject var selectedSource: Source @ObservedObject var selectedSource: Source
var body: some View { var body: some View {
if selectedSource.dynamicBaseUrl { if selectedSource.dynamicWebsite {
SourceSettingsBaseUrlView(selectedSource: selectedSource) SourceSettingsBaseUrlView(selectedSource: selectedSource)
} }