mirror of
https://github.com/Ferrite-iOS/Ferrite.git
synced 2026-04-21 08:52:00 +00:00
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:
parent
c8c7732575
commit
22bec5da52
3 changed files with 54 additions and 8 deletions
|
|
@ -72,6 +72,7 @@
|
||||||
0C70E40228C3CE9C00A5C72D /* ConditionalContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C70E40128C3CE9C00A5C72D /* ConditionalContextMenu.swift */; };
|
0C70E40228C3CE9C00A5C72D /* ConditionalContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C70E40128C3CE9C00A5C72D /* ConditionalContextMenu.swift */; };
|
||||||
0C70E40628C40C4E00A5C72D /* NotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C70E40528C40C4E00A5C72D /* NotificationCenter.swift */; };
|
0C70E40628C40C4E00A5C72D /* NotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C70E40528C40C4E00A5C72D /* NotificationCenter.swift */; };
|
||||||
0C733287289C4C820058D1FE /* SourceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C733286289C4C820058D1FE /* SourceSettingsView.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 */; };
|
0C7506D728B1AC9A008BEE38 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 0C7506D628B1AC9A008BEE38 /* SwiftyJSON */; };
|
||||||
0C750744289B003E004B3906 /* SourceRssParser+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C750742289B003E004B3906 /* SourceRssParser+CoreDataClass.swift */; };
|
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 */; };
|
0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C750743289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift */; };
|
||||||
|
|
@ -299,6 +300,7 @@
|
||||||
0C4CFC462897030D00AD9FAD /* Regex in Frameworks */,
|
0C4CFC462897030D00AD9FAD /* Regex in Frameworks */,
|
||||||
0C64A4B7288903880079976D /* KeychainSwift in Frameworks */,
|
0C64A4B7288903880079976D /* KeychainSwift in Frameworks */,
|
||||||
0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */,
|
0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */,
|
||||||
|
0C748EDA29D9256D0049B8BE /* Yams in Frameworks */,
|
||||||
0CDDDE052935235E006810B1 /* BetterSafariView in Frameworks */,
|
0CDDDE052935235E006810B1 /* BetterSafariView in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
@ -688,6 +690,7 @@
|
||||||
0C7506D628B1AC9A008BEE38 /* SwiftyJSON */,
|
0C7506D628B1AC9A008BEE38 /* SwiftyJSON */,
|
||||||
0CDDDE042935235E006810B1 /* BetterSafariView */,
|
0CDDDE042935235E006810B1 /* BetterSafariView */,
|
||||||
0C448BE829A135F100F4E266 /* Introspect-Static */,
|
0C448BE829A135F100F4E266 /* Introspect-Static */,
|
||||||
|
0C748ED929D9256D0049B8BE /* Yams */,
|
||||||
);
|
);
|
||||||
productName = Torrenter;
|
productName = Torrenter;
|
||||||
productReference = 0CAF1C68286F5C0E00296F86 /* Ferrite.app */;
|
productReference = 0CAF1C68286F5C0E00296F86 /* Ferrite.app */;
|
||||||
|
|
@ -725,6 +728,7 @@
|
||||||
0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
|
0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
|
||||||
0CDDDE032935235E006810B1 /* XCRemoteSwiftPackageReference "BetterSafariView" */,
|
0CDDDE032935235E006810B1 /* XCRemoteSwiftPackageReference "BetterSafariView" */,
|
||||||
0C448BE729A135F100F4E266 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */,
|
0C448BE729A135F100F4E266 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */,
|
||||||
|
0C748ED829D9256D0049B8BE /* XCRemoteSwiftPackageReference "Yams" */,
|
||||||
);
|
);
|
||||||
productRefGroup = 0CAF1C69286F5C0E00296F86 /* Products */;
|
productRefGroup = 0CAF1C69286F5C0E00296F86 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
|
@ -1152,6 +1156,14 @@
|
||||||
kind = branch;
|
kind = branch;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
0C748ED829D9256D0049B8BE /* XCRemoteSwiftPackageReference "Yams" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/jpsim/Yams";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMinorVersion;
|
||||||
|
minimumVersion = 5.0.5;
|
||||||
|
};
|
||||||
|
};
|
||||||
0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = {
|
0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON";
|
repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON";
|
||||||
|
|
@ -1199,6 +1211,11 @@
|
||||||
package = 0C64A4B5288903880079976D /* XCRemoteSwiftPackageReference "keychain-swift" */;
|
package = 0C64A4B5288903880079976D /* XCRemoteSwiftPackageReference "keychain-swift" */;
|
||||||
productName = KeychainSwift;
|
productName = KeychainSwift;
|
||||||
};
|
};
|
||||||
|
0C748ED929D9256D0049B8BE /* Yams */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 0C748ED829D9256D0049B8BE /* XCRemoteSwiftPackageReference "Yams" */;
|
||||||
|
productName = Yams;
|
||||||
|
};
|
||||||
0C7506D628B1AC9A008BEE38 /* SwiftyJSON */ = {
|
0C7506D628B1AC9A008BEE38 /* SwiftyJSON */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
|
package = 0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ extension PluginManager {
|
||||||
enum PluginManagerError: Error {
|
enum PluginManagerError: Error {
|
||||||
case ListAddition(description: String)
|
case ListAddition(description: String)
|
||||||
case ActionAddition(description: String)
|
case ActionAddition(description: String)
|
||||||
|
case PluginFetch(description: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AvailablePlugins {
|
struct AvailablePlugins {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Yams
|
||||||
|
|
||||||
public class PluginManager: ObservableObject {
|
public class PluginManager: ObservableObject {
|
||||||
var logManager: LoggingManager?
|
var logManager: LoggingManager?
|
||||||
|
|
@ -102,7 +103,18 @@ public class PluginManager: ObservableObject {
|
||||||
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData)
|
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData)
|
||||||
|
|
||||||
let (data, _) = try await URLSession.shared.data(for: request)
|
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 {
|
if let sources = pluginResponse.sources {
|
||||||
// Faster and more performant to map instead of a for loop
|
// Faster and more performant to map instead of a for loop
|
||||||
|
|
@ -748,25 +760,41 @@ public class PluginManager: ObservableObject {
|
||||||
|
|
||||||
// Adds a plugin list
|
// Adds a plugin list
|
||||||
// Can move this to PersistenceController if needed
|
// 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
|
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.")
|
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)!))
|
guard let url = URL(string: urlString) else {
|
||||||
let rawResponse = try JSONDecoder().decode(PluginListJson.self, from: data)
|
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 {
|
if let existingPluginList {
|
||||||
existingPluginList.urlString = url
|
existingPluginList.urlString = urlString
|
||||||
existingPluginList.name = rawResponse.name
|
existingPluginList.name = rawResponse.name
|
||||||
existingPluginList.author = rawResponse.author
|
existingPluginList.author = rawResponse.author
|
||||||
|
|
||||||
try PersistenceController.shared.container.viewContext.save()
|
try PersistenceController.shared.container.viewContext.save()
|
||||||
} else {
|
} else {
|
||||||
let pluginListRequest = PluginList.fetchRequest()
|
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)
|
let infoPredicate = NSPredicate(format: "author == %@ AND name == %@", rawResponse.author, rawResponse.name)
|
||||||
pluginListRequest.predicate = NSCompoundPredicate(type: .or, subpredicates: [urlPredicate, infoPredicate])
|
pluginListRequest.predicate = NSCompoundPredicate(type: .or, subpredicates: [urlPredicate, infoPredicate])
|
||||||
pluginListRequest.fetchLimit = 1
|
pluginListRequest.fetchLimit = 1
|
||||||
|
|
@ -779,7 +807,7 @@ public class PluginManager: ObservableObject {
|
||||||
|
|
||||||
let newPluginList = PluginList(context: backgroundContext)
|
let newPluginList = PluginList(context: backgroundContext)
|
||||||
newPluginList.id = UUID()
|
newPluginList.id = UUID()
|
||||||
newPluginList.urlString = url
|
newPluginList.urlString = urlString
|
||||||
newPluginList.name = rawResponse.name
|
newPluginList.name = rawResponse.name
|
||||||
newPluginList.author = rawResponse.author
|
newPluginList.author = rawResponse.author
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue