Plugins: Add option to use YAML for lists

YAML is a modern configuration format which is human readable
and supports more conventions than JSON.

However, some people may still prefer to write in JSON, so add the
option for users to write legacy JSON if they want to and to provide
backwards compatability with older plugins.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2023-04-01 23:11:30 -04:00
parent c8c7732575
commit 22bec5da52
3 changed files with 54 additions and 8 deletions

View file

@ -72,6 +72,7 @@
0C70E40228C3CE9C00A5C72D /* ConditionalContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C70E40128C3CE9C00A5C72D /* ConditionalContextMenu.swift */; };
0C70E40628C40C4E00A5C72D /* NotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C70E40528C40C4E00A5C72D /* NotificationCenter.swift */; };
0C733287289C4C820058D1FE /* SourceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C733286289C4C820058D1FE /* SourceSettingsView.swift */; };
0C748EDA29D9256D0049B8BE /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 0C748ED929D9256D0049B8BE /* Yams */; };
0C7506D728B1AC9A008BEE38 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 0C7506D628B1AC9A008BEE38 /* SwiftyJSON */; };
0C750744289B003E004B3906 /* SourceRssParser+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C750742289B003E004B3906 /* SourceRssParser+CoreDataClass.swift */; };
0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C750743289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift */; };
@ -299,6 +300,7 @@
0C4CFC462897030D00AD9FAD /* Regex in Frameworks */,
0C64A4B7288903880079976D /* KeychainSwift in Frameworks */,
0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */,
0C748EDA29D9256D0049B8BE /* Yams in Frameworks */,
0CDDDE052935235E006810B1 /* BetterSafariView in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -688,6 +690,7 @@
0C7506D628B1AC9A008BEE38 /* SwiftyJSON */,
0CDDDE042935235E006810B1 /* BetterSafariView */,
0C448BE829A135F100F4E266 /* Introspect-Static */,
0C748ED929D9256D0049B8BE /* Yams */,
);
productName = Torrenter;
productReference = 0CAF1C68286F5C0E00296F86 /* Ferrite.app */;
@ -725,6 +728,7 @@
0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
0CDDDE032935235E006810B1 /* XCRemoteSwiftPackageReference "BetterSafariView" */,
0C448BE729A135F100F4E266 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */,
0C748ED829D9256D0049B8BE /* XCRemoteSwiftPackageReference "Yams" */,
);
productRefGroup = 0CAF1C69286F5C0E00296F86 /* Products */;
projectDirPath = "";
@ -1152,6 +1156,14 @@
kind = branch;
};
};
0C748ED829D9256D0049B8BE /* XCRemoteSwiftPackageReference "Yams" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/jpsim/Yams";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 5.0.5;
};
};
0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON";
@ -1199,6 +1211,11 @@
package = 0C64A4B5288903880079976D /* XCRemoteSwiftPackageReference "keychain-swift" */;
productName = KeychainSwift;
};
0C748ED929D9256D0049B8BE /* Yams */ = {
isa = XCSwiftPackageProductDependency;
package = 0C748ED829D9256D0049B8BE /* XCRemoteSwiftPackageReference "Yams" */;
productName = Yams;
};
0C7506D628B1AC9A008BEE38 /* SwiftyJSON */ = {
isa = XCSwiftPackageProductDependency;
package = 0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */;

View file

@ -29,6 +29,7 @@ extension PluginManager {
enum PluginManagerError: Error {
case ListAddition(description: String)
case ActionAddition(description: String)
case PluginFetch(description: String)
}
struct AvailablePlugins {

View file

@ -7,6 +7,7 @@
import Foundation
import SwiftUI
import Yams
public class PluginManager: ObservableObject {
var logManager: LoggingManager?
@ -102,7 +103,18 @@ public class PluginManager: ObservableObject {
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData)
let (data, _) = try await URLSession.shared.data(for: request)
let pluginResponse = try JSONDecoder().decode(PluginListJson.self, from: data)
let pluginResponse: PluginListJson?
// If the URL is a yaml file, decode as such. Otherwise assume legacy JSON
if url.pathExtension == "yaml" || url.pathExtension == "yml" {
pluginResponse = try YAMLDecoder().decode(PluginListJson.self, from: data)
} else {
pluginResponse = try JSONDecoder().decode(PluginListJson.self, from: data)
}
guard let pluginResponse else {
throw PluginManagerError.PluginFetch(description: "Could not decode plugin list data")
}
if let sources = pluginResponse.sources {
// Faster and more performant to map instead of a for loop
@ -748,25 +760,41 @@ public class PluginManager: ObservableObject {
// Adds a plugin list
// Can move this to PersistenceController if needed
public func addPluginList(_ url: String, isSheet: Bool = false, existingPluginList: PluginList? = nil) async throws {
public func addPluginList(_ urlString: String, isSheet: Bool = false, existingPluginList: PluginList? = nil) async throws {
let backgroundContext = PersistenceController.shared.backgroundContext
if url.isEmpty || !url.starts(with: "https://") && !url.starts(with: "http://") {
if urlString.isEmpty || !urlString.starts(with: "https://") && !urlString.starts(with: "http://") {
throw PluginManagerError.ListAddition(description: "The provided source list is invalid. Please check if the URL is formatted properly.")
}
let (data, _) = try await URLSession.shared.data(for: URLRequest(url: URL(string: url)!))
let rawResponse = try JSONDecoder().decode(PluginListJson.self, from: data)
guard let url = URL(string: urlString) else {
throw PluginManagerError.ListAddition(description: "The provided source list is invalid. Please check if the URL is formatted properly.")
}
let (data, _) = try await URLSession.shared.data(for: URLRequest(url: url))
let rawResponse: PluginListJson?
// If the URL is a yaml file, decode as such. Otherwise assume legacy JSON
if url.pathExtension == "yaml" || url.pathExtension == "yml" {
rawResponse = try YAMLDecoder().decode(PluginListJson.self, from: data)
} else {
rawResponse = try JSONDecoder().decode(PluginListJson.self, from: data)
}
guard let rawResponse else {
throw PluginManagerError.ListAddition(description: "Could not decode the plugin list from URL \(urlString)")
}
if let existingPluginList {
existingPluginList.urlString = url
existingPluginList.urlString = urlString
existingPluginList.name = rawResponse.name
existingPluginList.author = rawResponse.author
try PersistenceController.shared.container.viewContext.save()
} else {
let pluginListRequest = PluginList.fetchRequest()
let urlPredicate = NSPredicate(format: "urlString == %@", url)
let urlPredicate = NSPredicate(format: "urlString == %@", urlString)
let infoPredicate = NSPredicate(format: "author == %@ AND name == %@", rawResponse.author, rawResponse.name)
pluginListRequest.predicate = NSCompoundPredicate(type: .or, subpredicates: [urlPredicate, infoPredicate])
pluginListRequest.fetchLimit = 1
@ -779,7 +807,7 @@ public class PluginManager: ObservableObject {
let newPluginList = PluginList(context: backgroundContext)
newPluginList.id = UUID()
newPluginList.urlString = url
newPluginList.urlString = urlString
newPluginList.name = rawResponse.name
newPluginList.author = rawResponse.author