Tree: Cleanup access levels

Public should not be used in an app since it declares public to
additional modules. However, an app is one module. Some structs/
classes need to be left public to conform to CoreData's generation.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2024-06-10 23:55:16 -04:00
parent 3b771e5deb
commit ecdd0199f6
32 changed files with 258 additions and 267 deletions

View file

@ -7,19 +7,18 @@
import Foundation
// TODO: Fix errors
public class AllDebrid: PollingDebridSource, ObservableObject {
public let id = "AllDebrid"
public let abbreviation = "AD"
public let website = "https://alldebrid.com"
public var authTask: Task<Void, Error>?
class AllDebrid: PollingDebridSource, ObservableObject {
let id = "AllDebrid"
let abbreviation = "AD"
let website = "https://alldebrid.com"
var authTask: Task<Void, Error>?
public var authProcessing: Bool = false
public var isLoggedIn: Bool {
var authProcessing: Bool = false
var isLoggedIn: Bool {
getToken() != nil
}
public var manualToken: String? {
var manualToken: String? {
if UserDefaults.standard.bool(forKey: "AllDebrid.UseManualKey") {
return getToken()
} else {
@ -27,20 +26,20 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
}
}
@Published public var IAValues: [DebridIA] = []
@Published public var cloudDownloads: [DebridCloudDownload] = []
@Published public var cloudTorrents: [DebridCloudTorrent] = []
public var cloudTTL: Double = 0.0
@Published var IAValues: [DebridIA] = []
@Published var cloudDownloads: [DebridCloudDownload] = []
@Published var cloudTorrents: [DebridCloudTorrent] = []
var cloudTTL: Double = 0.0
let baseApiUrl = "https://api.alldebrid.com/v4"
let appName = "Ferrite"
private let baseApiUrl = "https://api.alldebrid.com/v4"
private let appName = "Ferrite"
let jsonDecoder = JSONDecoder()
private let jsonDecoder = JSONDecoder()
// MARK: - Auth
// Fetches information for PIN auth
public func getAuthUrl() async throws -> URL {
func getAuthUrl() async throws -> URL {
let url = try buildRequestURL(urlString: "\(baseApiUrl)/pin/get")
let request = URLRequest(url: url)
@ -66,7 +65,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
}
// Fetches API keys
public func getApiKey(checkID: String, pin: String) async throws {
func getApiKey(checkID: String, pin: String) async throws {
let queryItems = [
URLQueryItem(name: "agent", value: appName),
URLQueryItem(name: "check", value: checkID),
@ -109,17 +108,17 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
}
// Adds a manual API key instead of web auth
public func setApiKey(_ key: String) {
func setApiKey(_ key: String) {
FerriteKeychain.shared.set(key, forKey: "AllDebrid.ApiKey")
UserDefaults.standard.set(true, forKey: "AllDebrid.UseManualKey")
}
public func getToken() -> String? {
func getToken() -> String? {
FerriteKeychain.shared.get("AllDebrid.ApiKey")
}
// Clears tokens. No endpoint to deregister a device
public func logout() {
func logout() {
FerriteKeychain.shared.delete("AllDebrid.ApiKey")
UserDefaults.standard.removeObject(forKey: "AllDebrid.UseManualKey")
}
@ -150,7 +149,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
}
// Builds a URL for further requests
private func buildRequestURL(urlString: String, queryItems: [URLQueryItem] = []) throws -> URL {
func buildRequestURL(urlString: String, queryItems: [URLQueryItem] = []) throws -> URL {
guard var components = URLComponents(string: urlString) else {
throw DebridError.InvalidUrl
}
@ -168,7 +167,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
// MARK: - Instant availability
public func instantAvailability(magnets: [Magnet]) async throws {
func instantAvailability(magnets: [Magnet]) async throws {
let now = Date().timeIntervalSince1970
let sendMagnets = magnets.filter { magnet in
@ -215,7 +214,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
// MARK: - Downloading
// Wrapper function to fetch a download link from the API
public func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
let selectedMagnetId: String
if let existingMagnet = cloudTorrents.first(where: { $0.hash == magnet.hash && $0.status == "Ready" }) {
@ -237,7 +236,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
}
// Adds a magnet link to the user's AD account
public func addMagnet(magnet: Magnet) async throws -> Int {
func addMagnet(magnet: Magnet) async throws -> Int {
guard let magnetLink = magnet.link else {
throw DebridError.FailedRequest(description: "The magnet link is invalid")
}
@ -263,7 +262,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
}
}
public func fetchMagnetStatus(magnetId: String, selectedIndex: Int?) async throws -> String {
func fetchMagnetStatus(magnetId: String, selectedIndex: Int?) async throws -> String {
let queryItems = [
URLQueryItem(name: "id", value: magnetId)
]
@ -280,7 +279,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
}
}
public func unlockLink(lockedLink: String) async throws -> String {
func unlockLink(lockedLink: String) async throws -> String {
let queryItems = [
URLQueryItem(name: "link", value: lockedLink)
]
@ -292,7 +291,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
return rawResponse.link
}
public func saveLink(link: String) async throws {
func saveLink(link: String) async throws {
let queryItems = [
URLQueryItem(name: "links[]", value: link)
]
@ -304,7 +303,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
// MARK: - Cloud methods
// Referred to as "User magnets" in AllDebrid's API
public func getUserTorrents() async throws {
func getUserTorrents() async throws {
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/status"))
let data = try await performRequest(request: &request, requestName: #function)
@ -326,7 +325,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
}
}
public func deleteTorrent(torrentId: String?) async throws {
func deleteTorrent(torrentId: String?) async throws {
guard let torrentId else {
throw DebridError.FailedRequest(description: "The torrentID \(String(describing: torrentId)) is invalid")
}
@ -339,7 +338,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
try await performRequest(request: &request, requestName: #function)
}
public func getUserDownloads() async throws {
func getUserDownloads() async throws {
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/user/links"))
let data = try await performRequest(request: &request, requestName: #function)
@ -358,12 +357,12 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
}
// Not used
public func checkUserDownloads(link: String) async throws -> String? {
func checkUserDownloads(link: String) async throws -> String? {
nil
}
// The downloadId is actually the download link
public func deleteDownload(downloadId: String) async throws {
func deleteDownload(downloadId: String) async throws {
let queryItems = [
URLQueryItem(name: "link", value: downloadId)
]

View file

@ -7,8 +7,8 @@
import Foundation
public class Github {
public func fetchLatestRelease() async throws -> Release? {
class Github {
func fetchLatestRelease() async throws -> Release? {
let url = URL(string: "https://api.github.com/repos/Ferrite-iOS/Ferrite/releases/latest")!
let (data, _) = try await URLSession.shared.data(from: url)
@ -17,7 +17,7 @@ public class Github {
return rawResponse
}
public func fetchReleases() async throws -> [Release]? {
func fetchReleases() async throws -> [Release]? {
let url = URL(string: "https://api.github.com/repos/Ferrite-iOS/Ferrite/releases")!
let (data, _) = try await URLSession.shared.data(from: url)

View file

@ -7,11 +7,11 @@
import Foundation
public class Kodi {
let encoder = JSONEncoder()
class Kodi {
private let encoder = JSONEncoder()
// Used to add server to CoreData. Not part of API
public func addServer(urlString: String,
func addServer(urlString: String,
friendlyName: String?,
username: String?,
password: String?,
@ -65,7 +65,7 @@ public class Kodi {
try backgroundContext.save()
}
public func ping(server: KodiServer) async throws {
func ping(server: KodiServer) async throws {
var request = URLRequest(url: URL(string: "\(server.urlString)/jsonrpc")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@ -94,7 +94,7 @@ public class Kodi {
}
}
public func sendVideoUrl(urlString: String, server: KodiServer) async throws {
func sendVideoUrl(urlString: String, server: KodiServer) async throws {
if URL(string: urlString) == nil {
throw KodiError.InvalidPlaybackUrl
}

View file

@ -7,16 +7,17 @@
import Foundation
public class Premiumize: OAuthDebridSource, ObservableObject {
public let id = "Premiumize"
public let abbreviation = "PM"
public let website = "https://premiumize.me"
@Published public var authProcessing: Bool = false
public var isLoggedIn: Bool {
class Premiumize: OAuthDebridSource, ObservableObject {
let id = "Premiumize"
let abbreviation = "PM"
let website = "https://premiumize.me"
@Published var authProcessing: Bool = false
var isLoggedIn: Bool {
getToken() != nil
}
public var manualToken: String? {
var manualToken: String? {
if UserDefaults.standard.bool(forKey: "Premiumize.UseManualKey") {
return getToken()
} else {
@ -24,20 +25,20 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
}
}
@Published public var IAValues: [DebridIA] = []
@Published public var cloudDownloads: [DebridCloudDownload] = []
@Published public var cloudTorrents: [DebridCloudTorrent] = []
public var cloudTTL: Double = 0.0
@Published var IAValues: [DebridIA] = []
@Published var cloudDownloads: [DebridCloudDownload] = []
@Published var cloudTorrents: [DebridCloudTorrent] = []
var cloudTTL: Double = 0.0
let baseAuthUrl = "https://www.premiumize.me/authorize"
let baseApiUrl = "https://www.premiumize.me/api"
let clientId = "791565696"
private let baseAuthUrl = "https://www.premiumize.me/authorize"
private let baseApiUrl = "https://www.premiumize.me/api"
private let clientId = "791565696"
let jsonDecoder = JSONDecoder()
private let jsonDecoder = JSONDecoder()
// MARK: - Auth
public func getAuthUrl() throws -> URL {
func getAuthUrl() throws -> URL {
var urlComponents = URLComponents(string: baseAuthUrl)!
urlComponents.queryItems = [
URLQueryItem(name: "client_id", value: clientId),
@ -52,7 +53,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
}
}
public func handleAuthCallback(url: URL) throws {
func handleAuthCallback(url: URL) throws {
let callbackComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)
guard let callbackFragment = callbackComponents?.fragment else {
@ -70,17 +71,17 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
}
// Adds a manual API key instead of web auth
public func setApiKey(_ key: String) {
func setApiKey(_ key: String) {
FerriteKeychain.shared.set(key, forKey: "Premiumize.AccessToken")
UserDefaults.standard.set(true, forKey: "Premiumize.UseManualKey")
}
public func getToken() -> String? {
func getToken() -> String? {
FerriteKeychain.shared.get("Premiumize.AccessToken")
}
// Clears tokens. No endpoint to deregister a device
public func logout() {
func logout() {
FerriteKeychain.shared.delete("Premiumize.AccessToken")
UserDefaults.standard.removeObject(forKey: "Premiumize.UseManualKey")
}
@ -132,7 +133,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
// MARK: - Instant availability
public func instantAvailability(magnets: [Magnet]) async throws {
func instantAvailability(magnets: [Magnet]) async throws {
let now = Date().timeIntervalSince1970
// Remove magnets that don't have an associated link for PM along with existing TTL logic
@ -168,7 +169,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
// Function to divide and execute DDL endpoint requests in parallel
// Calls this for 10 requests at a time to not overwhelm API servers
public func divideDDLRequests(magnetChunk: [Magnet]) async throws -> [DebridIA] {
func divideDDLRequests(magnetChunk: [Magnet]) async throws -> [DebridIA] {
let tempIA = try await withThrowingTaskGroup(of: DebridIA.self) { group in
for magnet in magnetChunk {
group.addTask {
@ -187,7 +188,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
}
// Grabs DDL links
func fetchDDL(magnet: Magnet) async throws -> DebridIA {
private func fetchDDL(magnet: Magnet) async throws -> DebridIA {
if magnet.hash == nil {
throw DebridError.EmptyData
}
@ -227,7 +228,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
// Function to divide and execute cache endpoint requests in parallel
// Calls this for 100 hashes at a time due to API limits
public func divideCacheRequests(magnets: [Magnet]) async throws -> [Magnet] {
func divideCacheRequests(magnets: [Magnet]) async throws -> [Magnet] {
let availableMagnets = try await withThrowingTaskGroup(of: [Magnet].self) { group in
for chunk in magnets.chunked(into: 100) {
group.addTask {
@ -247,7 +248,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
}
// Parent function for initial checking of the cache
func checkCache(magnets: [Magnet]) async throws -> [Magnet] {
private func checkCache(magnets: [Magnet]) async throws -> [Magnet] {
var urlComponents = URLComponents(string: "\(baseApiUrl)/cache/check")!
urlComponents.queryItems = magnets.map { URLQueryItem(name: "items[]", value: $0.hash) }
guard let url = urlComponents.url else {
@ -277,7 +278,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
// MARK: - Downloading
// Wrapper function to fetch a DDL link from the API
public func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
// Store the item in PM cloud for later use
try await createTransfer(magnet: magnet)
@ -290,7 +291,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
}
}
func createTransfer(magnet: Magnet) async throws {
private func createTransfer(magnet: Magnet) async throws {
guard let magnetLink = magnet.link else {
throw DebridError.FailedRequest(description: "The magnet link is invalid")
}
@ -309,7 +310,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
// MARK: - Cloud methods
public func getUserDownloads() async throws {
func getUserDownloads() async throws {
var request = URLRequest(url: URL(string: "\(baseApiUrl)/item/listall")!)
let data = try await performRequest(request: &request, requestName: #function)
@ -325,7 +326,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
}
}
func itemDetails(itemID: String) async throws -> ItemDetailsResponse {
private func itemDetails(itemID: String) async throws -> ItemDetailsResponse {
var urlComponents = URLComponents(string: "\(baseApiUrl)/item/details")!
urlComponents.queryItems = [URLQueryItem(name: "id", value: itemID)]
guard let url = urlComponents.url else {
@ -340,12 +341,12 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
return rawResponse
}
public func checkUserDownloads(link: String) async throws -> String? {
func checkUserDownloads(link: String) async throws -> String? {
// Link is the cloud item ID
try await itemDetails(itemID: link).link
}
public func deleteDownload(downloadId: String) async throws {
func deleteDownload(downloadId: String) async throws {
var request = URLRequest(url: URL(string: "\(baseApiUrl)/item/delete")!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
@ -359,7 +360,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject {
}
// No user torrents for Premiumize
public func getUserTorrents() async throws {}
func getUserTorrents() async throws {}
public func deleteTorrent(torrentId: String?) async throws {}
func deleteTorrent(torrentId: String?) async throws {}
}

View file

@ -7,20 +7,20 @@
import Foundation
public class RealDebrid: PollingDebridSource, ObservableObject {
public let id = "RealDebrid"
public let abbreviation = "RD"
public let website = "https://real-debrid.com"
public var authTask: Task<Void, Error>?
class RealDebrid: PollingDebridSource, ObservableObject {
let id = "RealDebrid"
let abbreviation = "RD"
let website = "https://real-debrid.com"
var authTask: Task<Void, Error>?
@Published public var authProcessing: Bool = false
@Published var authProcessing: Bool = false
// Check the manual token since getTokens() is async
public var isLoggedIn: Bool {
var isLoggedIn: Bool {
FerriteKeychain.shared.get("RealDebrid.AccessToken") != nil
}
public var manualToken: String? {
var manualToken: String? {
if UserDefaults.standard.bool(forKey: "RealDebrid.UseManualKey") {
return FerriteKeychain.shared.get("RealDebrid.AccessToken")
} else {
@ -28,31 +28,31 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
}
@Published public var IAValues: [DebridIA] = []
@Published public var cloudDownloads: [DebridCloudDownload] = []
@Published public var cloudTorrents: [DebridCloudTorrent] = []
public var cloudTTL: Double = 0.0
@Published var IAValues: [DebridIA] = []
@Published var cloudDownloads: [DebridCloudDownload] = []
@Published var cloudTorrents: [DebridCloudTorrent] = []
var cloudTTL: Double = 0.0
let baseAuthUrl = "https://api.real-debrid.com/oauth/v2"
let baseApiUrl = "https://api.real-debrid.com/rest/1.0"
let openSourceClientId = "X245A4XAIBGVM"
private let baseAuthUrl = "https://api.real-debrid.com/oauth/v2"
private let baseApiUrl = "https://api.real-debrid.com/rest/1.0"
private let openSourceClientId = "X245A4XAIBGVM"
let jsonDecoder = JSONDecoder()
private let jsonDecoder = JSONDecoder()
@MainActor
func setUserDefaultsValue(_ value: Any, forKey: String) {
private func setUserDefaultsValue(_ value: Any, forKey: String) {
UserDefaults.standard.set(value, forKey: forKey)
}
@MainActor
func removeUserDefaultsValue(forKey: String) {
private func removeUserDefaultsValue(forKey: String) {
UserDefaults.standard.removeObject(forKey: forKey)
}
// MARK: - Auth
// Fetches the device code from RD
public func getAuthUrl() async throws -> URL {
func getAuthUrl() async throws -> URL {
var urlComponents = URLComponents(string: "\(baseAuthUrl)/device/code")!
urlComponents.queryItems = [
URLQueryItem(name: "client_id", value: openSourceClientId),
@ -86,7 +86,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Fetches the user's client ID and secret
public func getDeviceCredentials(deviceCode: String) async throws {
func getDeviceCredentials(deviceCode: String) async throws {
var urlComponents = URLComponents(string: "\(baseAuthUrl)/device/credentials")!
urlComponents.queryItems = [
URLQueryItem(name: "client_id", value: openSourceClientId),
@ -130,7 +130,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Fetch all tokens for the user and store in FerriteKeychain.shared
public func getApiTokens(deviceCode: String) async throws {
func getApiTokens(deviceCode: String) async throws {
guard let clientId = UserDefaults.standard.string(forKey: "RealDebrid.ClientId") else {
throw DebridError.EmptyData
}
@ -164,7 +164,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
await setUserDefaultsValue(accessTimestamp, forKey: "RealDebrid.AccessTokenStamp")
}
public func getToken() async -> String? {
func getToken() async -> String? {
let accessTokenStamp = UserDefaults.standard.double(forKey: "RealDebrid.AccessTokenStamp")
if Date().timeIntervalSince1970 > accessTokenStamp {
@ -183,7 +183,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
// Adds a manual API key instead of web auth
// Clear out existing refresh tokens and timestamps
public func setApiKey(_ key: String) {
func setApiKey(_ key: String) {
FerriteKeychain.shared.set(key, forKey: "RealDebrid.AccessToken")
FerriteKeychain.shared.delete("RealDebrid.RefreshToken")
FerriteKeychain.shared.delete("RealDebrid.AccessTokenStamp")
@ -192,7 +192,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Deletes tokens from device and RD's servers
public func logout() async {
func logout() async {
FerriteKeychain.shared.delete("RealDebrid.RefreshToken")
FerriteKeychain.shared.delete("RealDebrid.ClientSecret")
await removeUserDefaultsValue(forKey: "RealDebrid.ClientId")
@ -237,7 +237,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
// MARK: - Instant availability
// Checks if the magnet is streamable on RD
public func instantAvailability(magnets: [Magnet]) async throws {
func instantAvailability(magnets: [Magnet]) async throws {
let now = Date().timeIntervalSince1970
let sendMagnets = magnets.filter { magnet in
@ -328,7 +328,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
// MARK: - Downloading
// Wrapper function to fetch a download link from the API
public func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
var selectedMagnetId = ""
do {
@ -360,7 +360,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Adds a magnet link to the user's RD account
public func addMagnet(magnet: Magnet) async throws -> String {
func addMagnet(magnet: Magnet) async throws -> String {
guard let magnetLink = magnet.link else {
throw DebridError.FailedRequest(description: "The magnet link is invalid")
}
@ -381,7 +381,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Queues the magnet link for downloading
public func selectFiles(debridID: String, fileIds: [Int]) async throws {
func selectFiles(debridID: String, fileIds: [Int]) async throws {
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/selectFiles/\(debridID)")!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
@ -401,7 +401,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Gets the info of a torrent from a given ID
public func torrentInfo(debridID: String, selectedFileId: Int?) async throws -> String {
func torrentInfo(debridID: String, selectedFileId: Int?) async throws -> String {
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/info/\(debridID)")!)
let data = try await performRequest(request: &request, requestName: #function)
@ -420,7 +420,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Downloads link from selectFiles for playback
public func unrestrictLink(debridDownloadLink: String) async throws -> String {
func unrestrictLink(debridDownloadLink: String) async throws -> String {
var request = URLRequest(url: URL(string: "\(baseApiUrl)/unrestrict/link")!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
@ -439,7 +439,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
// MARK: - Cloud methods
// Gets the user's torrent library
public func getUserTorrents() async throws {
func getUserTorrents() async throws {
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents")!)
let data = try await performRequest(request: &request, requestName: #function)
@ -457,7 +457,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Deletes a torrent download from RD
public func deleteTorrent(torrentId: String?) async throws {
func deleteTorrent(torrentId: String?) async throws {
let deleteId: String
if let torrentId {
@ -480,7 +480,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Gets the user's downloads
public func getUserDownloads() async throws {
func getUserDownloads() async throws {
var request = URLRequest(url: URL(string: "\(baseApiUrl)/downloads")!)
let data = try await performRequest(request: &request, requestName: #function)
@ -491,11 +491,11 @@ public class RealDebrid: PollingDebridSource, ObservableObject {
}
// Not used
public func checkUserDownloads(link: String) -> String? {
func checkUserDownloads(link: String) -> String? {
nil
}
public func deleteDownload(downloadId: String) async throws {
func deleteDownload(downloadId: String) async throws {
var request = URLRequest(url: URL(string: "\(baseApiUrl)/downloads/delete/\(downloadId)")!)
request.httpMethod = "DELETE"

View file

@ -10,4 +10,4 @@ import CoreData
import Foundation
@objc(Bookmark)
public class Bookmark: NSManagedObject {}
class Bookmark: NSManagedObject {}

View file

@ -9,7 +9,7 @@
import CoreData
import Foundation
public extension Bookmark {
extension Bookmark {
@nonobjc class func fetchRequest() -> NSFetchRequest<Bookmark> {
NSFetchRequest<Bookmark>(entityName: "Bookmark")
}

View file

@ -7,7 +7,7 @@
import SwiftUI
public extension Color {
extension Color {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0

View file

@ -11,7 +11,7 @@ import SwiftUI
extension View {
// Modifies properties of a view. Works the same way as a ViewModifier
// From: https://github.com/SwiftUIX/SwiftUIX/blob/master/Sources/Intermodular/Extensions/SwiftUI/View%2B%2B.swift#L10
public func modifyViewProp(_ body: (inout Self) -> Void) -> Self {
func modifyViewProp(_ body: (inout Self) -> Void) -> Self {
var result = self
body(&result)

View file

@ -7,20 +7,20 @@
import Foundation
public struct ActionJson: Codable, Hashable, PluginJson {
public let name: String
public let version: Int16
struct ActionJson: Codable, Hashable, PluginJson {
let name: String
let version: Int16
let minVersion: String?
let about: String?
let website: String?
let requires: [ActionRequirement]
let deeplink: [DeeplinkActionJson]?
public let author: String?
public let listId: UUID?
public let listName: String?
public let tags: [PluginTagJson]?
let author: String?
let listId: UUID?
let listName: String?
let tags: [PluginTagJson]?
public init(name: String,
init(name: String,
version: Int16,
minVersion: String?,
about: String?,
@ -45,7 +45,7 @@ public struct ActionJson: Codable, Hashable, PluginJson {
self.tags = tags
}
public init(from decoder: Decoder) throws {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
version = try container.decode(Int16.self, forKey: .version)
@ -68,7 +68,7 @@ public struct ActionJson: Codable, Hashable, PluginJson {
}
}
public struct DeeplinkActionJson: Codable, Hashable {
struct DeeplinkActionJson: Codable, Hashable {
let os: [String]
let scheme: String
@ -77,7 +77,7 @@ public struct DeeplinkActionJson: Codable, Hashable {
self.scheme = scheme
}
public init(from decoder: Decoder) throws {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let os = try? container.decode(String.self, forKey: .os) {
@ -92,7 +92,7 @@ public struct DeeplinkActionJson: Codable, Hashable {
}
}
public extension ActionJson {
extension ActionJson {
// Fetches all tags without optional requirement
// Avoids the need for extra tag additions in DB
func getTags() -> [PluginTagJson] {
@ -100,7 +100,7 @@ public extension ActionJson {
}
}
public enum ActionRequirement: String, Codable {
enum ActionRequirement: String, Codable {
case magnet
case debrid
}

View file

@ -7,7 +7,7 @@
import Foundation
public extension AllDebrid {
extension AllDebrid {
// MARK: - Generic AllDebrid response
// Uses a generic parametr for whatever underlying response is present
@ -71,7 +71,7 @@ public extension AllDebrid {
struct MagnetStatusResponse: Codable {
let magnets: [MagnetStatusData]
public init(from decoder: Decoder) throws {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let data = try? container.decode(MagnetStatusData.self, forKey: .magnets) {

View file

@ -8,7 +8,7 @@
import Foundation
// Version is optional until v1 is phased out
public struct Backup: Codable {
struct Backup: Codable {
let version: Int?
var bookmarks: [BookmarkJson]?
var history: [HistoryJson]?

View file

@ -10,7 +10,7 @@ import Foundation
// MARK: - Universal IA enum (IA = InstantAvailability)
public enum IAStatus: String, Codable, Hashable, Sendable, CaseIterable {
enum IAStatus: String, Codable, Hashable, Sendable, CaseIterable {
case full = "Cached"
case partial = "Batch"
case none = "Uncached"
@ -18,7 +18,7 @@ public enum IAStatus: String, Codable, Hashable, Sendable, CaseIterable {
// MARK: - Enum for debrid differentiation. 0 is nil
public enum DebridType: Int, Codable, Hashable, CaseIterable {
enum DebridType: Int, Codable, Hashable, CaseIterable {
case realDebrid = 1
case allDebrid = 2
case premiumize = 3
@ -47,7 +47,7 @@ public enum DebridType: Int, Codable, Hashable, CaseIterable {
}
// Wrapper struct for magnet links to contain both the link and hash for easy access
public struct Magnet: Codable, Hashable, Sendable {
struct Magnet: Codable, Hashable, Sendable {
var hash: String?
var link: String?

View file

@ -7,14 +7,14 @@
import Foundation
public struct DebridIA: Hashable, Sendable {
struct DebridIA: Hashable, Sendable {
let magnet: Magnet
let source: String
let expiryTimeStamp: Double
var files: [DebridIAFile]
}
public struct DebridIAFile: Hashable, Sendable {
struct DebridIAFile: Hashable, Sendable {
let fileId: Int
let name: String
let streamUrlString: String?
@ -28,14 +28,14 @@ public struct DebridIAFile: Hashable, Sendable {
}
}
public struct DebridCloudDownload: Hashable, Sendable {
struct DebridCloudDownload: Hashable, Sendable {
let downloadId: String
let source: String
let fileName: String
let link: String
}
public struct DebridCloudTorrent: Hashable, Sendable {
struct DebridCloudTorrent: Hashable, Sendable {
let torrentId: String
let source: String
let fileName: String
@ -44,7 +44,7 @@ public struct DebridCloudTorrent: Hashable, Sendable {
let links: [String]
}
public enum DebridError: Error {
enum DebridError: Error {
case InvalidUrl
case InvalidPostBody
case InvalidResponse

View file

@ -7,7 +7,7 @@
import Foundation
public extension Github {
extension Github {
struct Release: Codable, Hashable, Sendable {
let htmlUrl: String
let tagName: String

View file

@ -7,7 +7,7 @@
import Foundation
public struct PluginListJson: Codable {
struct PluginListJson: Codable {
let name: String
let author: String
var sources: [SourceJson]?
@ -16,8 +16,8 @@ public struct PluginListJson: Codable {
// Color: Hex value
public struct PluginTagJson: Codable, Hashable, Sendable {
public let name: String
public let colorHex: String?
let name: String
let colorHex: String?
enum CodingKeys: String, CodingKey {
case name

View file

@ -7,7 +7,7 @@
import Foundation
public extension Premiumize {
extension Premiumize {
// MARK: - CacheCheckResponse
struct CacheCheckResponse: Codable {

View file

@ -8,7 +8,7 @@
import Foundation
public extension RealDebrid {
extension RealDebrid {
// MARK: - device code endpoint
struct DeviceCodeResponse: Codable, Sendable {
@ -58,7 +58,7 @@ public extension RealDebrid {
struct InstantAvailabilityResponse: Codable, Sendable {
var data: InstantAvailabilityData?
public init(from decoder: Decoder) throws {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let data = try? container.decode(InstantAvailabilityData.self) {

View file

@ -8,7 +8,7 @@
import Foundation
// A raw search result structure displayed on the UI
public struct SearchResult: Codable, Hashable, Sendable {
struct SearchResult: Codable, Hashable, Sendable {
let title: String?
let source: String
let size: String?

View file

@ -7,14 +7,14 @@
import Foundation
public enum ApiCredentialResponseType: String, Codable, Hashable, Sendable {
enum ApiCredentialResponseType: String, Codable, Hashable, Sendable {
case json
case text
}
public struct SourceJson: Codable, Hashable, Sendable, PluginJson {
public let name: String
public let version: Int16
struct SourceJson: Codable, Hashable, Sendable, PluginJson {
let name: String
let version: Int16
let minVersion: String?
let about: String?
let website: String?
@ -25,33 +25,33 @@ public struct SourceJson: Codable, Hashable, Sendable, PluginJson {
let jsonParser: SourceJsonParserJson?
let rssParser: SourceRssParserJson?
let htmlParser: SourceHtmlParserJson?
public let author: String?
public let listId: UUID?
public let listName: String?
public let tags: [PluginTagJson]?
let author: String?
let listId: UUID?
let listName: String?
let tags: [PluginTagJson]?
}
public extension SourceJson {
extension SourceJson {
// Fetches all tags without optional requirement
func getTags() -> [PluginTagJson] {
tags ?? []
}
}
public enum SourcePreferredParser: Int16, CaseIterable, Sendable {
enum SourcePreferredParser: Int16, CaseIterable, Sendable {
// case none = 0
case scraping = 1
case rss = 2
case siteApi = 3
}
public struct SourceApiJson: Codable, Hashable, Sendable {
struct SourceApiJson: Codable, Hashable, Sendable {
let apiUrl: String?
let clientId: SourceApiCredentialJson?
let clientSecret: SourceApiCredentialJson?
}
public struct SourceApiCredentialJson: Codable, Hashable, Sendable {
struct SourceApiCredentialJson: Codable, Hashable, Sendable {
let query: String?
let value: String?
let dynamic: Bool?
@ -60,7 +60,7 @@ public struct SourceApiCredentialJson: Codable, Hashable, Sendable {
let expiryLength: Double?
}
public struct SourceJsonParserJson: Codable, Hashable, Sendable {
struct SourceJsonParserJson: Codable, Hashable, Sendable {
let searchUrl: String
let request: SourceRequestJson?
let results: String?
@ -73,7 +73,7 @@ public struct SourceJsonParserJson: Codable, Hashable, Sendable {
let sl: SourceSLJson?
}
public struct SourceRssParserJson: Codable, Hashable, Sendable {
struct SourceRssParserJson: Codable, Hashable, Sendable {
let rssUrl: String?
let searchUrl: String
let request: SourceRequestJson?
@ -86,7 +86,7 @@ public struct SourceRssParserJson: Codable, Hashable, Sendable {
let sl: SourceSLJson?
}
public struct SourceHtmlParserJson: Codable, Hashable, Sendable {
struct SourceHtmlParserJson: Codable, Hashable, Sendable {
let searchUrl: String?
let request: SourceRequestJson?
let rows: String
@ -97,21 +97,21 @@ public struct SourceHtmlParserJson: Codable, Hashable, Sendable {
let sl: SourceSLJson?
}
public struct SourceComplexQueryJson: Codable, Hashable, Sendable {
struct SourceComplexQueryJson: Codable, Hashable, Sendable {
let query: String
let discriminator: String?
let attribute: String?
let regex: String?
}
public struct SourceMagnetJson: Codable, Hashable, Sendable {
struct SourceMagnetJson: Codable, Hashable, Sendable {
let query: String
let attribute: String
let regex: String?
let externalLinkQuery: String?
}
public struct SourceSLJson: Codable, Hashable, Sendable {
struct SourceSLJson: Codable, Hashable, Sendable {
let seeders: String?
let leechers: String?
let combined: String?
@ -121,7 +121,7 @@ public struct SourceSLJson: Codable, Hashable, Sendable {
let leecherRegex: String?
}
public struct SourceRequestJson: Codable, Hashable, Sendable {
struct SourceRequestJson: Codable, Hashable, Sendable {
let method: String?
let headers: [String: String]?
let body: String?

View file

@ -7,7 +7,7 @@
import Foundation
public protocol DebridSource: AnyObservableObject {
protocol DebridSource: AnyObservableObject {
// ID of the service
// var id: DebridInfo { get }
var id: String { get }
@ -51,7 +51,7 @@ public protocol DebridSource: AnyObservableObject {
func deleteTorrent(torrentId: String?) async throws
}
public protocol PollingDebridSource: DebridSource {
protocol PollingDebridSource: DebridSource {
// Task reference for polling
var authTask: Task<Void, Error>? { get set }
@ -59,7 +59,7 @@ public protocol PollingDebridSource: DebridSource {
func getAuthUrl() async throws -> URL
}
public protocol OAuthDebridSource: DebridSource {
protocol OAuthDebridSource: DebridSource {
// Fetches the auth URL
func getAuthUrl() throws -> URL

View file

@ -8,7 +8,7 @@
import CoreData
import Foundation
public protocol Plugin: ObservableObject, NSManagedObject {
protocol Plugin: ObservableObject, NSManagedObject {
var id: UUID { get set }
var listId: UUID? { get set }
var name: String { get set }
@ -27,7 +27,7 @@ extension Plugin {
}
}
public protocol PluginJson: Hashable {
protocol PluginJson: Hashable {
var name: String { get }
var version: Int16 { get }
var author: String? { get }

View file

@ -9,7 +9,7 @@
import Foundation
public class Application {
class Application {
static let shared = Application()
// OS name for Plugins to read. Lowercase for ease of use

View file

@ -27,7 +27,7 @@ class ErasedObservableObject: ObservableObject {
}
}
public protocol AnyObservableObject: AnyObject {
protocol AnyObservableObject: AnyObject {
var objectWillChange: ObservableObjectPublisher { get }
}
@ -59,14 +59,14 @@ public protocol AnyObservableObject: AnyObject {
/// Not all injected objects need this property wrapper. See the example projects for examples each
/// way.
@propertyWrapper
public struct Store<ObjectType> {
struct Store<ObjectType> {
/// The underlying object being stored.
public let wrappedValue: ObjectType
let wrappedValue: ObjectType
// See https://github.com/Tiny-Home-Consulting/Dependiject/issues/38
fileprivate var _observableObject: ObservedObject<ErasedObservableObject>
@MainActor internal var observableObject: ErasedObservableObject {
@MainActor var observableObject: ErasedObservableObject {
_observableObject.wrappedValue
}
@ -83,14 +83,14 @@ public struct Store<ObjectType> {
/// }
/// }
/// ```
public var projectedValue: Wrapper {
var projectedValue: Wrapper {
Wrapper(self)
}
/// Create a stored value on a custom scheduler.
///
/// Use this init to schedule updates on a specific scheduler other than `DispatchQueue.main`.
public init<S: Scheduler>(wrappedValue: ObjectType,
init<S: Scheduler>(wrappedValue: ObjectType,
on scheduler: S,
schedulerOptions: S.SchedulerOptions? = nil)
{
@ -112,7 +112,7 @@ public struct Store<ObjectType> {
/// Create a stored value which publishes on the main thread.
///
/// To control when updates are published, see ``init(wrappedValue:on:schedulerOptions:)``.
public init(wrappedValue: ObjectType) {
init(wrappedValue: ObjectType) {
self.init(wrappedValue: wrappedValue, on: DispatchQueue.main)
}
@ -120,15 +120,15 @@ public struct Store<ObjectType> {
/// [`ObservedObject.Wrapper`](https://developer.apple.com/documentation/swiftui/observedobject/wrapper)
/// type.
@dynamicMemberLookup
public struct Wrapper {
struct Wrapper {
private var store: Store
internal init(_ store: Store<ObjectType>) {
init(_ store: Store<ObjectType>) {
self.store = store
}
/// Returns a binding to the resulting value of a given key path.
public subscript<Subject>(
subscript<Subject>(
dynamicMember keyPath: ReferenceWritableKeyPath<ObjectType, Subject>
) -> Binding<Subject> {
Binding {
@ -141,7 +141,7 @@ public struct Store<ObjectType> {
}
extension Store: DynamicProperty {
public nonisolated mutating func update() {
nonisolated mutating func update() {
_observableObject.update()
}
}

View file

@ -7,9 +7,9 @@
import Foundation
public class BackupManager: ObservableObject {
class BackupManager: ObservableObject {
// Constant variable for backup versions
let latestBackupVersion: Int = 2
private let latestBackupVersion: Int = 2
var logManager: LoggingManager?
@ -21,17 +21,17 @@ public class BackupManager: ObservableObject {
@Published var selectedBackupUrl: URL?
@MainActor
func updateRestoreCompletedMessage(newString: String) {
private func updateRestoreCompletedMessage(newString: String) {
restoreCompletedMessage.append(newString)
}
@MainActor
func toggleRestoreCompletedAlert() {
private func toggleRestoreCompletedAlert() {
showRestoreCompletedAlert.toggle()
}
@MainActor
func updateBackupUrls(newUrl: URL) {
private func updateBackupUrls(newUrl: URL) {
backupUrls.append(newUrl)
}

View file

@ -9,7 +9,7 @@ import Foundation
import SwiftUI
@MainActor
public class DebridManager: ObservableObject {
class DebridManager: ObservableObject {
// Linked classes
var logManager: LoggingManager?
@Published var realDebrid: RealDebrid = .init()
@ -40,7 +40,7 @@ public class DebridManager: ObservableObject {
var selectedDebridFile: DebridIAFile?
// TODO: Figure out a way to remove this var
var selectedOAuthDebridSource: OAuthDebridSource?
private var selectedOAuthDebridSource: OAuthDebridSource?
@Published var filteredIAStatus: Set<IAStatus> = []
@ -48,17 +48,8 @@ public class DebridManager: ObservableObject {
var downloadUrl: String = ""
var authUrl: URL?
// RealDebrid auth variables
var realDebridAuthProcessing: Bool = false
@Published var showDeleteAlert: Bool = false
// AllDebrid auth variables
var allDebridAuthProcessing: Bool = false
// Premiumize auth variables
var premiumizeAuthProcessing: Bool = false
init() {
// Set the preferred service. Contains migration logic for earlier versions
if let rawPreferredService = UserDefaults.standard.string(forKey: "Debrid.PreferredService") {
@ -83,7 +74,7 @@ public class DebridManager: ObservableObject {
// TODO: Remove after v0.8.0
// Function to migrate the preferred service to the new string ID format
public func migratePreferredService(_ idInt: Int) -> String? {
private func migratePreferredService(_ idInt: Int) -> String? {
// Undo the EnabledDebrids key
UserDefaults.standard.removeObject(forKey: "Debrid.EnabledArray")
@ -92,7 +83,7 @@ public class DebridManager: ObservableObject {
// Wrapper function to match error descriptions
// Error can be suppressed to end user but must be printed in logs
func sendDebridError(
private func sendDebridError(
_ error: Error,
prefix: String,
presentError: Bool = true,
@ -119,20 +110,20 @@ public class DebridManager: ObservableObject {
}
// Cleans all cached IA values in the event of a full IA refresh
public func clearIAValues() {
func clearIAValues() {
for debridSource in debridSources {
debridSource.IAValues = []
}
}
// Clears all selected files and items
public func clearSelectedDebridItems() {
func clearSelectedDebridItems() {
selectedDebridItem = nil
selectedDebridFile = nil
}
// Common function to populate hashes for debrid services
public func populateDebridIA(_ resultMagnets: [Magnet]) async {
func populateDebridIA(_ resultMagnets: [Magnet]) async {
for debridSource in debridSources {
if !debridSource.isLoggedIn {
continue
@ -148,7 +139,7 @@ public class DebridManager: ObservableObject {
}
// Common function to match a magnet hash with a provided debrid service
public func matchMagnetHash(_ magnet: Magnet) -> IAStatus {
func matchMagnetHash(_ magnet: Magnet) -> IAStatus {
guard let magnetHash = magnet.hash else {
return .none
}
@ -162,7 +153,7 @@ public class DebridManager: ObservableObject {
}
}
public func selectDebridResult(magnet: Magnet) -> Bool {
func selectDebridResult(magnet: Magnet) -> Bool {
guard let magnetHash = magnet.hash else {
logManager?.error("DebridManager: Could not find the torrent magnet hash")
return false
@ -184,7 +175,7 @@ public class DebridManager: ObservableObject {
// MARK: - Authentication UI linked functions
// Common function to delegate what debrid service to authenticate with
public func authenticateDebrid(_ debridSource: some DebridSource, apiKey: String?) async {
func authenticateDebrid(_ debridSource: some DebridSource, apiKey: String?) async {
defer {
// Don't cancel processing if using OAuth
if !(debridSource is OAuthDebridSource) {
@ -253,7 +244,7 @@ public class DebridManager: ObservableObject {
}
// Wrapper function to validate and present an auth URL to the user
@discardableResult func validateAuthUrl(_ url: URL?, useAuthSession: Bool = false) -> Bool {
@discardableResult private func validateAuthUrl(_ url: URL?, useAuthSession: Bool = false) -> Bool {
guard let url else {
logManager?.error("DebridManager: Authentication: Invalid URL created: \(String(describing: url))")
return false
@ -270,7 +261,7 @@ public class DebridManager: ObservableObject {
}
// Currently handles Premiumize callback
public func handleAuthCallback(url: URL?, error: Error?) async {
func handleAuthCallback(url: URL?, error: Error?) async {
defer {
if enabledDebridCount == 1 {
selectedDebridSource = selectedOAuthDebridSource
@ -300,7 +291,7 @@ public class DebridManager: ObservableObject {
// MARK: - Logout UI functions
public func logout(_ debridSource: some DebridSource) async {
func logout(_ debridSource: some DebridSource) async {
await debridSource.logout()
if selectedDebridSource?.id == debridSource.id {
@ -312,7 +303,7 @@ public class DebridManager: ObservableObject {
// Common function to delegate what debrid service to fetch from
// Cloudinfo is used for any extra information provided by debrid cloud
public func fetchDebridDownload(magnet: Magnet?, cloudInfo: String? = nil) async {
func fetchDebridDownload(magnet: Magnet?, cloudInfo: String? = nil) async {
defer {
currentDebridTask = nil
logManager?.hideIndeterminateToast()
@ -359,7 +350,7 @@ public class DebridManager: ObservableObject {
}
// Wrapper to handle cloud fetching
public func fetchDebridCloud(bypassTTL: Bool = false) async {
func fetchDebridCloud(bypassTTL: Bool = false) async {
guard let selectedSource = selectedDebridSource else {
return
}
@ -381,7 +372,7 @@ public class DebridManager: ObservableObject {
}
}
public func deleteCloudDownload(_ download: DebridCloudDownload) async {
func deleteCloudDownload(_ download: DebridCloudDownload) async {
guard let selectedSource = selectedDebridSource else {
return
}
@ -395,7 +386,7 @@ public class DebridManager: ObservableObject {
}
}
public func deleteCloudTorrent(_ torrent: DebridCloudTorrent) async {
func deleteCloudTorrent(_ torrent: DebridCloudTorrent) async {
guard let selectedSource = selectedDebridSource else {
return
}

View file

@ -70,7 +70,7 @@ class LoggingManager: ObservableObject {
// TODO: Maybe append to a constant logfile?
public func info(_ message: String,
func info(_ message: String,
description: String? = nil)
{
let log = Log(
@ -88,7 +88,7 @@ class LoggingManager: ObservableObject {
print("LOG: \(log.toMessage())")
}
public func warn(_ message: String,
func warn(_ message: String,
description: String? = nil)
{
let log = Log(
@ -106,7 +106,7 @@ class LoggingManager: ObservableObject {
print("LOG: \(log.toMessage())")
}
public func error(_ message: String,
func error(_ message: String,
description: String? = nil,
showToast: Bool = true)
{
@ -132,7 +132,7 @@ class LoggingManager: ObservableObject {
// MARK: - Indeterminate functions
public func updateIndeterminateToast(_ description: String, cancelAction: (() -> Void)?) {
func updateIndeterminateToast(_ description: String, cancelAction: (() -> Void)?) {
indeterminateToastDescription = description
if let cancelAction {
@ -144,13 +144,13 @@ class LoggingManager: ObservableObject {
}
}
public func hideIndeterminateToast() {
func hideIndeterminateToast() {
showIndeterminateToast = false
indeterminateToastDescription = ""
indeterminateCancelAction = nil
}
public func exportLogs() {
func exportLogs() {
logFormatter.dateFormat = "yyyy-MM-dd-HHmmss"
let logFileName = "ferrite_session_\(logFormatter.string(from: Date())).txt"
let logFolderPath = FileManager.default.appDirectory.appendingPathComponent("Logs")

View file

@ -8,12 +8,12 @@
import SwiftUI
@MainActor
public class NavigationViewModel: ObservableObject {
class NavigationViewModel: ObservableObject {
var logManager: LoggingManager?
// Used between SearchResultsView and MagnetChoiceView
public enum ChoiceSheetType: Identifiable {
public var id: Int {
enum ChoiceSheetType: Identifiable {
var id: Int {
hashValue
}
@ -53,7 +53,7 @@ public class NavigationViewModel: ObservableObject {
@Published var currentSortFilter: SortFilter?
@Published var currentSortOrder: SortOrder = .forward
public func compareSearchResult(lhs: SearchResult, rhs: SearchResult) -> Bool {
func compareSearchResult(lhs: SearchResult, rhs: SearchResult) -> Bool {
switch currentSortFilter {
case .leechers:
guard let lhsLeechers = lhs.leechers, let rhsLeechers = rhs.leechers else {
@ -97,7 +97,7 @@ public class NavigationViewModel: ObservableObject {
@Published var searchPrompt: String = "Search"
@Published var lastSearchPromptIndex: Int = -1
let searchBarTextArray: [String] = [
private let searchBarTextArray: [String] = [
"What's on your mind?",
"Discover something interesting",
"Find an engaging show",

View file

@ -9,7 +9,7 @@ import Foundation
import SwiftUI
import Yams
public class PluginManager: ObservableObject {
class PluginManager: ObservableObject {
var logManager: LoggingManager?
let kodi: Kodi = .init()
@ -25,18 +25,18 @@ public class PluginManager: ObservableObject {
@Published var actionSuccessAlertMessage: String = ""
@MainActor
func cleanAvailablePlugins() {
private func cleanAvailablePlugins() {
availableSources = []
availableActions = []
}
@MainActor
func updateAvailablePlugins(_ newPlugins: AvailablePlugins) {
private func updateAvailablePlugins(_ newPlugins: AvailablePlugins) {
availableSources += newPlugins.availableSources
availableActions += newPlugins.availableActions
}
public func fetchPluginsFromUrl() async {
func fetchPluginsFromUrl() async {
let pluginListRequest = PluginList.fetchRequest()
guard let pluginLists = try? PersistenceController.shared.backgroundContext.fetch(pluginListRequest) else {
await logManager?.error("PluginManager: No plugin lists found")
@ -97,7 +97,7 @@ public class PluginManager: ObservableObject {
await logManager?.info("Plugin list fetch finished")
}
func fetchPluginList(pluginList: PluginList, url: URL) async throws -> AvailablePlugins? {
private func fetchPluginList(pluginList: PluginList, url: URL) async throws -> AvailablePlugins? {
var tempSources: [SourceJson] = []
var tempActions: [ActionJson] = []
@ -176,7 +176,7 @@ public class PluginManager: ObservableObject {
}
// Checks if a deeplink action is present and if there's a single action for the OS (or fallback)
func getFilteredDeeplinks(_ deeplinks: [DeeplinkActionJson]) -> [DeeplinkActionJson]? {
private func getFilteredDeeplinks(_ deeplinks: [DeeplinkActionJson]) -> [DeeplinkActionJson]? {
let osArray = deeplinks.filter { deeplink in
deeplink.os.contains(where: { $0.lowercased() == Application.shared.os.lowercased() })
}
@ -244,7 +244,7 @@ public class PluginManager: ObservableObject {
}
}
func fetchCastedPlugins<PJ: PluginJson>(_ forType: PJ.Type) -> [PJ] {
private func fetchCastedPlugins<PJ: PluginJson>(_ forType: PJ.Type) -> [PJ] {
switch String(describing: PJ.self) {
case "SourceJson":
return availableSources as? [PJ] ?? []
@ -256,7 +256,7 @@ public class PluginManager: ObservableObject {
}
// Checks if the current app version is supported by the source
func checkAppVersion(minVersion: String?) -> Bool {
private func checkAppVersion(minVersion: String?) -> Bool {
// If there's no min version, assume that every version is supported
guard let minVersion else {
return true
@ -266,7 +266,7 @@ public class PluginManager: ObservableObject {
}
// Fetches sources using the background context
public func fetchInstalledSources(searchResultsEmpty: Bool) -> [Source] {
func fetchInstalledSources(searchResultsEmpty: Bool) -> [Source] {
let backgroundContext = PersistenceController.shared.backgroundContext
if !filteredInstalledSources.isEmpty, !searchResultsEmpty {
@ -279,7 +279,7 @@ public class PluginManager: ObservableObject {
}
@MainActor
public func runDefaultAction(urlString: String?, navModel: NavigationViewModel) {
func runDefaultAction(urlString: String?, navModel: NavigationViewModel) {
let context = PersistenceController.shared.backgroundContext
guard let urlString else {
@ -332,7 +332,7 @@ public class PluginManager: ObservableObject {
// The iOS version of Ferrite only runs deeplink actions
@MainActor
public func runDeeplinkAction(_ action: Action, urlString: String?) {
func runDeeplinkAction(_ action: Action, urlString: String?) {
guard let deeplink = action.deeplink, let urlString else {
actionErrorAlertMessage = "Could not run action: \(action.name) since there is no deeplink to execute. Contact the action dev!"
showActionErrorAlert.toggle()
@ -355,7 +355,7 @@ public class PluginManager: ObservableObject {
}
@MainActor
public func sendToKodi(urlString: String?, server: KodiServer) async {
func sendToKodi(urlString: String?, server: KodiServer) async {
guard let urlString else {
actionErrorAlertMessage = "Could not send URL to Kodi since there is no playback URL to send"
showActionErrorAlert.toggle()
@ -380,7 +380,7 @@ public class PluginManager: ObservableObject {
}
}
public func installAction(actionJson: ActionJson?, doUpsert: Bool = false) async {
func installAction(actionJson: ActionJson?, doUpsert: Bool = false) async {
guard let actionJson else {
await logManager?.error("Action addition: No action present. Contact the app dev!")
return
@ -448,7 +448,7 @@ public class PluginManager: ObservableObject {
}
}
public func installSource(sourceJson: SourceJson?, doUpsert: Bool = false) async {
func installSource(sourceJson: SourceJson?, doUpsert: Bool = false) async {
guard let sourceJson else {
await logManager?.error("Source addition: No source present. Contact the app dev!")
return
@ -535,7 +535,7 @@ public class PluginManager: ObservableObject {
}
}
func addSourceApi(newSource: Source, apiJson: SourceApiJson) {
private func addSourceApi(newSource: Source, apiJson: SourceApiJson) {
let backgroundContext = PersistenceController.shared.backgroundContext
let newSourceApi = SourceApi(context: backgroundContext)
@ -571,7 +571,7 @@ public class PluginManager: ObservableObject {
}
// TODO: Migrate parser addition to a common protocol
func addJsonParser(newSource: Source, jsonParserJson: SourceJsonParserJson) {
private func addJsonParser(newSource: Source, jsonParserJson: SourceJsonParserJson) {
let backgroundContext = PersistenceController.shared.backgroundContext
let newSourceJsonParser = SourceJsonParser(context: backgroundContext)
@ -646,7 +646,7 @@ public class PluginManager: ObservableObject {
newSource.jsonParser = newSourceJsonParser
}
func addRssParser(newSource: Source, rssParserJson: SourceRssParserJson) {
private func addRssParser(newSource: Source, rssParserJson: SourceRssParserJson) {
let backgroundContext = PersistenceController.shared.backgroundContext
let newSourceRssParser = SourceRssParser(context: backgroundContext)
@ -725,7 +725,7 @@ public class PluginManager: ObservableObject {
newSource.rssParser = newSourceRssParser
}
func addHtmlParser(newSource: Source, htmlParserJson: SourceHtmlParserJson) {
private func addHtmlParser(newSource: Source, htmlParserJson: SourceHtmlParserJson) {
let backgroundContext = PersistenceController.shared.backgroundContext
let newSourceHtmlParser = SourceHtmlParser(context: backgroundContext)
@ -795,7 +795,7 @@ public class PluginManager: ObservableObject {
// Adds a plugin list
// Can move this to PersistenceController if needed
public func addPluginList(_ urlString: String, isSheet: Bool = false, existingPluginList: PluginList? = nil) async throws {
func addPluginList(_ urlString: String, isSheet: Bool = false, existingPluginList: PluginList? = nil) async throws {
let backgroundContext = PersistenceController.shared.backgroundContext
if urlString.isEmpty || !urlString.starts(with: "https://") && !urlString.starts(with: "http://") {

View file

@ -27,18 +27,18 @@ class ScrapingViewModel: ObservableObject {
// Only add results with valid magnet hashes to the search results array
@MainActor
func updateSearchResults(newResults: [SearchResult]) {
private func updateSearchResults(newResults: [SearchResult]) {
searchResults += newResults
}
@MainActor
func clearSearchResults() {
private func clearSearchResults() {
searchResults = []
}
@Published var currentSourceNames: Set<String> = []
@MainActor
func updateCurrentSourceNames(_ newName: String) {
private func updateCurrentSourceNames(_ newName: String) {
currentSourceNames.insert(newName)
logManager?.updateIndeterminateToast(
"Loading \(currentSourceNames.joined(separator: ", "))",
@ -47,7 +47,7 @@ class ScrapingViewModel: ObservableObject {
}
@MainActor
func removeCurrentSourceName(_ removedName: String) {
private func removeCurrentSourceName(_ removedName: String) {
currentSourceNames.remove(removedName)
logManager?.updateIndeterminateToast(
"Loading \(currentSourceNames.joined(separator: ", "))",
@ -56,18 +56,18 @@ class ScrapingViewModel: ObservableObject {
}
@MainActor
func clearCurrentSourceNames() {
private func clearCurrentSourceNames() {
currentSourceNames = []
logManager?.updateIndeterminateToast("Loading sources", cancelAction: nil)
}
// Utility function to print source specific errors
func sendSourceError(_ description: String) async {
private func sendSourceError(_ description: String) async {
await logManager?.error(description, showToast: false)
}
// Substitutes the given string with an arbitrary parameter dictionary
func substituteParams(_ input: String, with params: [String: String]) -> String {
private func substituteParams(_ input: String, with params: [String: String]) -> String {
let replaced = params.reduce(input) { result, param -> String in
result.replacingOccurrences(of: "{\(param.key)}", with: param.value)
}
@ -76,7 +76,7 @@ class ScrapingViewModel: ObservableObject {
}
// Cleans a SourceRequest's body and headers to be substituted
func cleanRequest(request: SourceRequest, params: [String: String]) -> SourceRequest {
private func cleanRequest(request: SourceRequest, params: [String: String]) -> SourceRequest {
if let body = request.body {
request.body = substituteParams(body, with: params)
}
@ -88,7 +88,7 @@ class ScrapingViewModel: ObservableObject {
return request
}
public func scanSources(sources: [Source], searchText: String, debridManager: DebridManager) async {
func scanSources(sources: [Source], searchText: String, debridManager: DebridManager) async {
await logManager?.info("Started scanning sources for query \"\(searchText)\"")
if sources.isEmpty {
@ -166,7 +166,7 @@ class ScrapingViewModel: ObservableObject {
}
}
func executeParser(source: Source) async -> SearchRequestResult? {
private func executeParser(source: Source) async -> SearchRequestResult? {
guard let website = source.website else {
await logManager?.error("Scraping: The base URL could not be found for source \(source.name)")
@ -294,7 +294,7 @@ class ScrapingViewModel: ObservableObject {
}
// Checks the base URL for any website data then iterates through the fallback URLs
func handleUrls(website: String, replacedSearchUrl: String?, fallbackUrls: [String]?, sourceName: String, requestParams: SourceRequest?) async -> Data? {
private func handleUrls(website: String, replacedSearchUrl: String?, fallbackUrls: [String]?, sourceName: String, requestParams: SourceRequest?) async -> Data? {
let fetchUrl = website + (replacedSearchUrl.map { $0 } ?? "")
if let data = await fetchWebsiteData(urlString: fetchUrl, sourceName: sourceName, requestParams: requestParams) {
return data
@ -312,7 +312,7 @@ class ScrapingViewModel: ObservableObject {
return nil
}
public func handleApiCredential(_ credential: SourceApiCredential,
private func handleApiCredential(_ credential: SourceApiCredential,
replacement: String,
searchUrl: String,
apiUrl: String?,
@ -353,7 +353,7 @@ class ScrapingViewModel: ObservableObject {
return nil
}
public func fetchApiCredential(urlString: String,
private func fetchApiCredential(urlString: String,
credential: SourceApiCredential,
sourceName: String) async -> String?
{
@ -399,7 +399,7 @@ class ScrapingViewModel: ObservableObject {
}
// Fetches the data for a URL
public func fetchWebsiteData(urlString: String, sourceName: String, requestParams: SourceRequest?) async -> Data? {
private func fetchWebsiteData(urlString: String, sourceName: String, requestParams: SourceRequest?) async -> Data? {
guard let url = URL(string: urlString.trimmingCharacters(in: .whitespacesAndNewlines)) else {
await sendSourceError("\(sourceName): Source doesn't contain a valid URL, contact the source dev!")
@ -446,7 +446,7 @@ class ScrapingViewModel: ObservableObject {
}
}
public func scrapeJson(source: Source, jsonData: Data) async -> SearchRequestResult? {
private func scrapeJson(source: Source, jsonData: Data) async -> SearchRequestResult? {
guard let jsonParser = source.jsonParser else {
return nil
}
@ -521,7 +521,7 @@ class ScrapingViewModel: ObservableObject {
}
// TODO: Add regex parsing for API
public func parseJsonResult(_ result: JSON,
private func parseJsonResult(_ result: JSON,
jsonParser: SourceJsonParser,
source: Source,
existingSearchResult: SearchResult? = nil) -> SearchResult?
@ -615,7 +615,7 @@ class ScrapingViewModel: ObservableObject {
}
// RSS feed scraper
public func scrapeRss(source: Source, rss: String) async -> SearchRequestResult? {
private func scrapeRss(source: Source, rss: String) async -> SearchRequestResult? {
guard let rssParser = source.rssParser else {
return nil
}
@ -750,7 +750,7 @@ class ScrapingViewModel: ObservableObject {
}
// Complex query parsing for RSS scraping
func runRssComplexQuery(item: Element,
private func runRssComplexQuery(item: Element,
query: String,
attribute: String,
discriminator: String?,
@ -783,7 +783,7 @@ class ScrapingViewModel: ObservableObject {
}
// HTML scraper
public func scrapeHtml(source: Source, website: String, html: String) async -> SearchRequestResult? {
private func scrapeHtml(source: Source, website: String, html: String) async -> SearchRequestResult? {
guard let htmlParser = source.htmlParser else {
return nil
}
@ -955,7 +955,7 @@ class ScrapingViewModel: ObservableObject {
}
// Complex query parsing for HTML scraping
func runHtmlComplexQuery(row: Element,
private func runHtmlComplexQuery(row: Element,
query: String,
attribute: String,
regexString: String?) throws -> String?
@ -980,7 +980,7 @@ class ScrapingViewModel: ObservableObject {
}
}
func runRegex(parsedValue: String, regexString: String) -> String? {
private func runRegex(parsedValue: String, regexString: String) -> String? {
// TODO: Maybe dynamically parse flags
let replacedRegexString = regexString
.replacingOccurrences(of: "{query}", with: cleanedSearchText)
@ -1003,7 +1003,7 @@ class ScrapingViewModel: ObservableObject {
}
}
func parseSizeString(sizeString: String) -> String? {
private func parseSizeString(sizeString: String) -> String? {
// Test if the string can be a full integer
guard let size = Int(sizeString) else {
return nil
@ -1025,7 +1025,7 @@ class ScrapingViewModel: ObservableObject {
}
}
func cleanApiCreds(api: SourceApi, sourceName: String) async {
private func cleanApiCreds(api: SourceApi, sourceName: String) async {
let backgroundContext = PersistenceController.shared.backgroundContext
let hasCredentials = api.clientId != nil || api.clientSecret != nil

View file

@ -56,7 +56,7 @@ struct HybridSecureField: View {
}
extension HybridSecureField {
public func fieldDisabled(_ isFieldDisabled: Bool) -> Self {
func fieldDisabled(_ isFieldDisabled: Bool) -> Self {
modifyViewProp { $0.isFieldDisabled = isFieldDisabled }
}
}

View file

@ -7,7 +7,7 @@
import SwiftUI
public extension View {
extension View {
// A dismissAction must be added in the parent view struct due to lifecycle issues
func expandedSearchable(text: Binding<String>,
isSearching: Binding<Bool>? = nil,