diff --git a/Ferrite/API/AllDebridWrapper.swift b/Ferrite/API/AllDebridWrapper.swift index 5b44fac..cf533f9 100644 --- a/Ferrite/API/AllDebridWrapper.swift +++ b/Ferrite/API/AllDebridWrapper.swift @@ -42,7 +42,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { // Validate the URL before doing anything else let rawResponse = try jsonDecoder.decode(ADResponse.self, from: data).data guard let userUrl = URL(string: rawResponse.userURL) else { - throw ADError.AuthQuery(description: "The login URL is invalid") + throw DebridError.AuthQuery(description: "The login URL is invalid") } // Spawn the polling task separately @@ -53,7 +53,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { return userUrl } catch { print("Couldn't get pin information!") - throw ADError.AuthQuery(description: error.localizedDescription) + throw DebridError.AuthQuery(description: error.localizedDescription) } } @@ -73,7 +73,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { while count < 12 { if Task.isCancelled { - throw ADError.AuthQuery(description: "Token request cancelled.") + throw DebridError.AuthQuery(description: "Token request cancelled.") } let (data, _) = try await URLSession.shared.data(for: request) @@ -92,7 +92,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { } } - throw ADError.AuthQuery(description: "Could not fetch the client ID and secret in time. Try logging in again.") + throw DebridError.AuthQuery(description: "Could not fetch the client ID and secret in time. Try logging in again.") } if case let .failure(error) = await authTask?.result { @@ -123,7 +123,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { // Wrapper request function which matches the responses and returns data @discardableResult private func performRequest(request: inout URLRequest, requestName: String) async throws -> Data { guard let token = getToken() else { - throw ADError.InvalidToken + throw DebridError.InvalidToken } request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") @@ -131,23 +131,23 @@ public class AllDebrid: PollingDebridSource, ObservableObject { let (data, response) = try await URLSession.shared.data(for: request) guard let response = response as? HTTPURLResponse else { - throw ADError.FailedRequest(description: "No HTTP response given") + throw DebridError.FailedRequest(description: "No HTTP response given") } if response.statusCode >= 200, response.statusCode <= 299 { return data } else if response.statusCode == 401 { logout() - throw ADError.FailedRequest(description: "The request \(requestName) failed because you were unauthorized. Please relogin to AllDebrid in Settings.") + throw DebridError.FailedRequest(description: "The request \(requestName) failed because you were unauthorized. Please relogin to AllDebrid in Settings.") } else { - throw ADError.FailedRequest(description: "The request \(requestName) failed with status code \(response.statusCode).") + throw DebridError.FailedRequest(description: "The request \(requestName) failed with status code \(response.statusCode).") } } // Builds a URL for further requests private func buildRequestURL(urlString: String, queryItems: [URLQueryItem] = []) throws -> URL { guard var components = URLComponents(string: urlString) else { - throw ADError.InvalidUrl + throw DebridError.InvalidUrl } components.queryItems = [ @@ -157,7 +157,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { if let url = components.url { return url } else { - throw ADError.InvalidUrl + throw DebridError.InvalidUrl } } @@ -234,7 +234,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { // Adds a magnet link to the user's AD account public func addMagnet(magnet: Magnet) async throws -> Int { guard let magnetLink = magnet.link else { - throw ADError.FailedRequest(description: "The magnet link is invalid") + throw DebridError.FailedRequest(description: "The magnet link is invalid") } var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/upload")) @@ -254,7 +254,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { if let magnet = rawResponse.magnets[safe: 0] { return magnet.id } else { - throw ADError.InvalidResponse + throw DebridError.InvalidResponse } } @@ -271,7 +271,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { if let linkWrapper = rawResponse.magnets[safe: 0]?.links[safe: selectedIndex ?? -1] { return linkWrapper.link } else { - throw ADError.EmptyTorrents + throw DebridError.EmptyTorrents } } @@ -306,7 +306,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { let rawResponse = try jsonDecoder.decode(ADResponse.self, from: data).data if rawResponse.magnets.isEmpty { - throw ADError.EmptyData + throw DebridError.EmptyData } cloudTorrents = rawResponse.magnets.map { magnetResponse in @@ -339,7 +339,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject { let rawResponse = try jsonDecoder.decode(ADResponse.self, from: data).data if rawResponse.links.isEmpty { - throw ADError.EmptyData + throw DebridError.EmptyData } // The link is also the ID diff --git a/Ferrite/API/PremiumizeWrapper.swift b/Ferrite/API/PremiumizeWrapper.swift index 1530ba8..fbbf6aa 100644 --- a/Ferrite/API/PremiumizeWrapper.swift +++ b/Ferrite/API/PremiumizeWrapper.swift @@ -40,7 +40,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject { if let url = urlComponents.url { return url } else { - throw PMError.InvalidUrl + throw DebridError.InvalidUrl } } @@ -48,14 +48,14 @@ public class Premiumize: OAuthDebridSource, ObservableObject { let callbackComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) guard let callbackFragment = callbackComponents?.fragment else { - throw PMError.InvalidResponse + throw DebridError.InvalidResponse } var fragmentComponents = URLComponents() fragmentComponents.query = callbackFragment guard let accessToken = fragmentComponents.queryItems?.first(where: { $0.name == "access_token" })?.value else { - throw PMError.InvalidToken + throw DebridError.InvalidToken } FerriteKeychain.shared.set(accessToken, forKey: "Premiumize.AccessToken") @@ -84,7 +84,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject { // Wrapper request function which matches the responses and returns data @discardableResult private func performRequest(request: inout URLRequest, requestName: String) async throws -> Data { guard let token = getToken() else { - throw PMError.InvalidToken + throw DebridError.InvalidToken } // Use the API query parameter if a manual API key is present @@ -93,7 +93,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject { let requestUrl = request.url, var components = URLComponents(url: requestUrl, resolvingAgainstBaseURL: false) else { - throw PMError.InvalidUrl + throw DebridError.InvalidUrl } let apiTokenItem = URLQueryItem(name: "apikey", value: token) @@ -112,16 +112,16 @@ public class Premiumize: OAuthDebridSource, ObservableObject { let (data, response) = try await URLSession.shared.data(for: request) guard let response = response as? HTTPURLResponse else { - throw PMError.FailedRequest(description: "No HTTP response given") + throw DebridError.FailedRequest(description: "No HTTP response given") } if response.statusCode >= 200, response.statusCode <= 299 { return data } else if response.statusCode == 401 { logout() - throw PMError.FailedRequest(description: "The request \(requestName) failed because you were unauthorized. Please relogin to Premiumize in Settings.") + throw DebridError.FailedRequest(description: "The request \(requestName) failed because you were unauthorized. Please relogin to Premiumize in Settings.") } else { - throw PMError.FailedRequest(description: "The request \(requestName) failed with status code \(response.statusCode).") + throw DebridError.FailedRequest(description: "The request \(requestName) failed with status code \(response.statusCode).") } } @@ -184,7 +184,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject { // Grabs DDL links func fetchDDL(magnet: Magnet) async throws -> DebridIA { if magnet.hash == nil { - throw PMError.EmptyData + throw DebridError.EmptyData } var request = URLRequest(url: URL(string: "\(baseApiUrl)/transfer/directdl")!) @@ -215,7 +215,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject { files: files ) } else { - throw PMError.EmptyData + throw DebridError.EmptyData } } @@ -245,7 +245,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject { var urlComponents = URLComponents(string: "\(baseApiUrl)/cache/check")! urlComponents.queryItems = magnets.map { URLQueryItem(name: "items[]", value: $0.hash) } guard let url = urlComponents.url else { - throw PMError.InvalidUrl + throw DebridError.InvalidUrl } var request = URLRequest(url: url) @@ -254,7 +254,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject { let rawResponse = try jsonDecoder.decode(CacheCheckResponse.self, from: data) if rawResponse.response.isEmpty { - throw PMError.EmptyData + throw DebridError.EmptyData } else { let availableMagnets = magnets.enumerated().compactMap { index, magnet in if rawResponse.response[safe: index] == true { @@ -280,13 +280,13 @@ public class Premiumize: OAuthDebridSource, ObservableObject { } else if let premiumizeItem = ia, let firstFile = premiumizeItem.files[safe: 0], let streamUrlString = firstFile.streamUrlString { return streamUrlString } else { - throw PMError.FailedRequest(description: "Could not fetch your file from the Premiumize API") + throw DebridError.FailedRequest(description: "Could not fetch your file from the Premiumize API") } } func createTransfer(magnet: Magnet) async throws { guard let magnetLink = magnet.link else { - throw PMError.FailedRequest(description: "The magnet link is invalid") + throw DebridError.FailedRequest(description: "The magnet link is invalid") } var request = URLRequest(url: URL(string: "\(baseApiUrl)/transfer/create")!) @@ -310,7 +310,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject { let rawResponse = try jsonDecoder.decode(AllItemsResponse.self, from: data) if rawResponse.files.isEmpty { - throw PMError.EmptyData + throw DebridError.EmptyData } // The "link" is the ID for Premiumize @@ -325,7 +325,7 @@ public class Premiumize: OAuthDebridSource, ObservableObject { var urlComponents = URLComponents(string: "\(baseApiUrl)/item/details")! urlComponents.queryItems = [URLQueryItem(name: "id", value: itemID)] guard let url = urlComponents.url else { - throw PMError.InvalidUrl + throw DebridError.InvalidUrl } var request = URLRequest(url: url) diff --git a/Ferrite/API/RealDebridWrapper.swift b/Ferrite/API/RealDebridWrapper.swift index f306a15..798bb68 100644 --- a/Ferrite/API/RealDebridWrapper.swift +++ b/Ferrite/API/RealDebridWrapper.swift @@ -7,7 +7,7 @@ import Foundation -public class RealDebrid: PollingDebridSource, ObservableObject { +public class RealDebrid: PollingDebridSource, ObservableObject { public let id = "RealDebrid" public let abbreviation = "RD" public let website = "https://real-debrid.com" @@ -52,7 +52,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject { ] guard let url = urlComponents.url else { - throw RDError.InvalidUrl + throw DebridError.InvalidUrl } let request = URLRequest(url: url) @@ -62,7 +62,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject { // Validate the URL before doing anything else let rawResponse = try jsonDecoder.decode(DeviceCodeResponse.self, from: data) guard let directVerificationUrl = URL(string: rawResponse.directVerificationURL) else { - throw RDError.AuthQuery(description: "The verification URL is invalid") + throw DebridError.AuthQuery(description: "The verification URL is invalid") } // Spawn the polling task separately @@ -73,7 +73,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject { return directVerificationUrl } catch { print("Couldn't get the new client creds!") - throw RDError.AuthQuery(description: error.localizedDescription) + throw DebridError.AuthQuery(description: error.localizedDescription) } } @@ -86,7 +86,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject { ] guard let url = urlComponents.url else { - throw RDError.InvalidUrl + throw DebridError.InvalidUrl } let request = URLRequest(url: url) @@ -96,7 +96,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject { while count < 12 { if Task.isCancelled { - throw RDError.AuthQuery(description: "Token request cancelled.") + throw DebridError.AuthQuery(description: "Token request cancelled.") } let (data, _) = try await URLSession.shared.data(for: request) @@ -118,17 +118,17 @@ public class RealDebrid: PollingDebridSource, ObservableObject { } } - throw RDError.AuthQuery(description: "Could not fetch the client ID and secret in time. Try logging in again.") + throw DebridError.AuthQuery(description: "Could not fetch the client ID and secret in time. Try logging in again.") } // Fetch all tokens for the user and store in FerriteKeychain.shared public func getApiTokens(deviceCode: String) async throws { guard let clientId = UserDefaults.standard.string(forKey: "RealDebrid.ClientId") else { - throw RDError.EmptyData + throw DebridError.EmptyData } guard let clientSecret = FerriteKeychain.shared.get("RealDebrid.ClientSecret") else { - throw RDError.EmptyData + throw DebridError.EmptyData } var request = URLRequest(url: URL(string: "\(baseAuthUrl)/token")!) @@ -208,7 +208,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject { // Wrapper request function which matches the responses and returns data @discardableResult private func performRequest(request: inout URLRequest, requestName: String) async throws -> Data { guard let token = await getToken() else { - throw RDError.InvalidToken + throw DebridError.InvalidToken } request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") @@ -216,16 +216,16 @@ public class RealDebrid: PollingDebridSource, ObservableObject { let (data, response) = try await URLSession.shared.data(for: request) guard let response = response as? HTTPURLResponse else { - throw RDError.FailedRequest(description: "No HTTP response given") + throw DebridError.FailedRequest(description: "No HTTP response given") } if response.statusCode >= 200, response.statusCode <= 299 { return data } else if response.statusCode == 401 { await logout() - throw RDError.FailedRequest(description: "The request \(requestName) failed because you were unauthorized. Please relogin to RealDebrid in Settings.") + throw DebridError.FailedRequest(description: "The request \(requestName) failed because you were unauthorized. Please relogin to RealDebrid in Settings.") } else { - throw RDError.FailedRequest(description: "The request \(requestName) failed with status code \(response.statusCode).") + throw DebridError.FailedRequest(description: "The request \(requestName) failed with status code \(response.statusCode).") } } @@ -345,7 +345,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject { return downloadLink } catch { - if case RealDebrid.RDError.EmptyTorrents = error, !selectedMagnetId.isEmpty { + if case DebridError.EmptyTorrents = error, !selectedMagnetId.isEmpty { try? await deleteTorrent(torrentId: selectedMagnetId) } @@ -357,7 +357,7 @@ public class RealDebrid: PollingDebridSource, ObservableObject { // Adds a magnet link to the user's RD account public func addMagnet(magnet: Magnet) async throws -> String { guard let magnetLink = magnet.link else { - throw RDError.FailedRequest(description: "The magnet link is invalid") + throw DebridError.FailedRequest(description: "The magnet link is invalid") } var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/addMagnet")!) @@ -408,9 +408,9 @@ public class RealDebrid: PollingDebridSource, ObservableObject { if let torrentLink = rawResponse.links[safe: linkIndex ?? -1], rawResponse.status == "downloaded" { return torrentLink } else if rawResponse.status == "downloading" || rawResponse.status == "queued" { - throw RDError.EmptyTorrents + throw DebridError.EmptyTorrents } else { - throw RDError.EmptyData + throw DebridError.EmptyData } } diff --git a/Ferrite/Models/AllDebridModels.swift b/Ferrite/Models/AllDebridModels.swift index 02b07b1..3ec1d0e 100644 --- a/Ferrite/Models/AllDebridModels.swift +++ b/Ferrite/Models/AllDebridModels.swift @@ -8,20 +8,6 @@ import Foundation public extension AllDebrid { - // MARK: - Errors - - // TODO: Hybridize debrid errors in one structure - enum ADError: Error { - case InvalidUrl - case InvalidPostBody - case InvalidResponse - case InvalidToken - case EmptyData - case EmptyTorrents - case FailedRequest(description: String) - case AuthQuery(description: String) - } - // MARK: - Generic AllDebrid response // Uses a generic parametr for whatever underlying response is present diff --git a/Ferrite/Models/DebridModels.swift b/Ferrite/Models/DebridModels.swift index 3eb4e5f..9bce883 100644 --- a/Ferrite/Models/DebridModels.swift +++ b/Ferrite/Models/DebridModels.swift @@ -43,3 +43,14 @@ public struct DebridCloudTorrent: Hashable, Sendable { let hash: String let links: [String] } + +public enum DebridError: Error { + case InvalidUrl + case InvalidPostBody + case InvalidResponse + case InvalidToken + case EmptyData + case EmptyTorrents + case FailedRequest(description: String) + case AuthQuery(description: String) +} diff --git a/Ferrite/Models/PremiumizeModels.swift b/Ferrite/Models/PremiumizeModels.swift index 4572526..6ec9606 100644 --- a/Ferrite/Models/PremiumizeModels.swift +++ b/Ferrite/Models/PremiumizeModels.swift @@ -8,20 +8,6 @@ import Foundation public extension Premiumize { - // MARK: - Errors - - // TODO: Hybridize debrid errors in one structure - enum PMError: Error { - case InvalidUrl - case InvalidPostBody - case InvalidResponse - case InvalidToken - case EmptyData - case EmptyTorrents - case FailedRequest(description: String) - case AuthQuery(description: String) - } - // MARK: - CacheCheckResponse struct CacheCheckResponse: Codable { diff --git a/Ferrite/Models/RealDebridModels.swift b/Ferrite/Models/RealDebridModels.swift index ba455b1..d72ad40 100644 --- a/Ferrite/Models/RealDebridModels.swift +++ b/Ferrite/Models/RealDebridModels.swift @@ -9,20 +9,6 @@ import Foundation public extension RealDebrid { - // MARK: - Errors - - // TODO: Hybridize debrid errors in one structure - enum RDError: Error { - case InvalidUrl - case InvalidPostBody - case InvalidResponse - case InvalidToken - case EmptyData - case EmptyTorrents - case FailedRequest(description: String) - case AuthQuery(description: String) - } - // MARK: - device code endpoint struct DeviceCodeResponse: Codable, Sendable { diff --git a/Ferrite/ViewModels/DebridManager.swift b/Ferrite/ViewModels/DebridManager.swift index 0b714f0..b811d0f 100644 --- a/Ferrite/ViewModels/DebridManager.swift +++ b/Ferrite/ViewModels/DebridManager.swift @@ -342,7 +342,7 @@ public class DebridManager: ObservableObject { try await realDebrid.authTask?.value return true } else { - throw RealDebrid.RDError.AuthQuery(description: "The verification URL was invalid") + throw DebridError.AuthQuery(description: "The verification URL was invalid") } } catch { await sendDebridError(error, prefix: "RealDebrid authentication error") @@ -361,7 +361,7 @@ public class DebridManager: ObservableObject { try await allDebrid.authTask?.value return true } else { - throw AllDebrid.ADError.AuthQuery(description: "The PIN URL was invalid") + throw DebridError.AuthQuery(description: "The PIN URL was invalid") } } catch { await sendDebridError(error, prefix: "AllDebrid authentication error") @@ -388,14 +388,14 @@ public class DebridManager: ObservableObject { public func handleCallback(url: URL?, error: Error?) async { do { if let error { - throw Premiumize.PMError.AuthQuery(description: "OAuth callback Error: \(error)") + throw DebridError.AuthQuery(description: "OAuth callback Error: \(error)") } if let callbackUrl = url { try premiumize.handleAuthCallback(url: callbackUrl) completeDebridAuth(.premiumize, success: true) } else { - throw Premiumize.PMError.AuthQuery(description: "The callback URL was invalid") + throw DebridError.AuthQuery(description: "The callback URL was invalid") } } catch { await sendDebridError(error, prefix: "Premiumize authentication error (callback)") @@ -476,7 +476,7 @@ public class DebridManager: ObservableObject { // Update the UI downloadUrl = downloadLink } else { - throw RealDebrid.RDError.FailedRequest(description: "Could not fetch your file from RealDebrid's cache or API") + throw DebridError.FailedRequest(description: "Could not fetch your file from RealDebrid's cache or API") } // Fetch one more time to add updated data into the RD cloud cache @@ -491,7 +491,7 @@ public class DebridManager: ObservableObject { func fetchRdDownload(magnet: Magnet?, cloudInfo: String?) async { do { guard let magnet else { - throw RealDebrid.RDError.FailedRequest(description: "Could not fetch your file from RealDebrid's cache or API") + throw DebridError.FailedRequest(description: "Could not fetch your file from RealDebrid's cache or API") } let downloadLink = try await realDebrid.getDownloadLink( @@ -505,7 +505,7 @@ public class DebridManager: ObservableObject { await fetchRdCloud(bypassTTL: true) } catch { switch error { - case RealDebrid.RDError.EmptyTorrents: + case DebridError.EmptyTorrents: showDeleteAlert.toggle() default: await sendDebridError(error, prefix: "RealDebrid download error", cancelString: "Download cancelled") @@ -565,7 +565,7 @@ public class DebridManager: ObservableObject { await fetchRdCloud(bypassTTL: true) } else { - throw RealDebrid.RDError.FailedRequest(description: "No torrent ID was provided") + throw DebridError.FailedRequest(description: "No torrent ID was provided") } } catch { await sendDebridError(error, prefix: "RealDebrid torrent delete error", presentError: presentError) @@ -582,7 +582,7 @@ public class DebridManager: ObservableObject { // Update UI downloadUrl = downloadLink } else { - throw AllDebrid.ADError.FailedRequest(description: "Could not fetch your file from AllDebrid's cache or API") + throw DebridError.FailedRequest(description: "Could not fetch your file from AllDebrid's cache or API") } // Fetch one more time to add updated data into the AD cloud cache @@ -641,7 +641,7 @@ public class DebridManager: ObservableObject { downloadUrl = downloadLink } else { - throw Premiumize.PMError.FailedRequest(description: "Could not fetch your file from Premiumize's cache or API") + throw DebridError.FailedRequest(description: "Could not fetch your file from Premiumize's cache or API") } // Fetch one more time to add updated data into the PM cloud cache