Debrid: Swap to common DebridError

Removes the redundant error types.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2024-06-05 22:50:40 -04:00 committed by Brian Dashore
parent aa739133be
commit fa8e5c19c3
8 changed files with 69 additions and 100 deletions

View file

@ -42,7 +42,7 @@ public class AllDebrid: PollingDebridSource, ObservableObject {
// Validate the URL before doing anything else
let rawResponse = try jsonDecoder.decode(ADResponse<PinResponse>.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<MagnetStatusResponse>.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<SavedLinksResponse>.self, from: data).data
if rawResponse.links.isEmpty {
throw ADError.EmptyData
throw DebridError.EmptyData
}
// The link is also the ID

View file

@ -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)

View file

@ -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
}
}

View file

@ -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

View file

@ -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)
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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