mirror of
https://github.com/Ferrite-iOS/Ferrite.git
synced 2026-01-11 20:10:27 +00:00
Debrid: Fix OffCloud single files and cloud population
Populate cloud lists when the app is launched to begin maintainence of a synced list. In addition, fix the errors when OffCloud tried fetching links for a single file. The explore endpoint only works when the file is a batch which is unknown until it's actually called. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
489da8e82e
commit
78f2aff25b
7 changed files with 117 additions and 26 deletions
|
|
@ -36,6 +36,14 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
|||
|
||||
private let jsonDecoder = JSONDecoder()
|
||||
|
||||
init() {
|
||||
// Populate user downloads and magnets
|
||||
Task {
|
||||
try? await getUserDownloads()
|
||||
try? await getUserMagnets()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Auth
|
||||
|
||||
// Fetches information for PIN auth
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ class OffCloud: DebridSource, ObservableObject {
|
|||
private let jsonDecoder = JSONDecoder()
|
||||
private let jsonEncoder = JSONEncoder()
|
||||
|
||||
init() {
|
||||
// Populate user downloads and magnets
|
||||
Task {
|
||||
try? await getUserMagnets()
|
||||
}
|
||||
}
|
||||
|
||||
func setApiKey(_ key: String) {
|
||||
FerriteKeychain.shared.set(key, forKey: "OffCloud.ApiKey")
|
||||
UserDefaults.standard.set(true, forKey: "OffCloud.UseManualKey")
|
||||
|
|
@ -133,11 +140,11 @@ class OffCloud: DebridSource, ObservableObject {
|
|||
|
||||
// Cloud in OffCloud's API
|
||||
func getRestrictedFile(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> (restrictedFile: DebridIAFile?, newIA: DebridIA?) {
|
||||
let selectedMagnet: DebridCloudMagnet
|
||||
let selectedCloudMagnet: DebridCloudMagnet
|
||||
|
||||
// Don't queue a new job if the magnet already exists in the user's account
|
||||
if let existingCloudMagnet = cloudMagnets.first(where: { $0.hash == magnet.hash && $0.status == "downloaded" }) {
|
||||
selectedMagnet = existingCloudMagnet
|
||||
selectedCloudMagnet = existingCloudMagnet
|
||||
} else {
|
||||
let cloudDownloadResponse = try await offcloudDownload(magnet: magnet)
|
||||
|
||||
|
|
@ -145,18 +152,19 @@ class OffCloud: DebridSource, ObservableObject {
|
|||
throw DebridError.IsCaching
|
||||
}
|
||||
|
||||
selectedMagnet = DebridCloudMagnet(
|
||||
selectedCloudMagnet = DebridCloudMagnet(
|
||||
id: cloudDownloadResponse.requestId,
|
||||
fileName: cloudDownloadResponse.fileName,
|
||||
status: cloudDownloadResponse.status,
|
||||
hash: "",
|
||||
links: []
|
||||
links: [cloudDownloadResponse.url]
|
||||
)
|
||||
}
|
||||
|
||||
let cloudExploreLinks = try await cloudExplore(requestId: selectedMagnet.id)
|
||||
let cloudExploreResponse = try await cloudExplore(requestId: selectedCloudMagnet.id)
|
||||
|
||||
if cloudExploreLinks.count > 1 {
|
||||
// Request will error if the file isn't a batch
|
||||
if case let .links(cloudExploreLinks) = cloudExploreResponse {
|
||||
var copiedIA = ia
|
||||
|
||||
copiedIA?.files = cloudExploreLinks.enumerated().compactMap { index, exploreLink in
|
||||
|
|
@ -172,11 +180,17 @@ class OffCloud: DebridSource, ObservableObject {
|
|||
}
|
||||
|
||||
return (nil, copiedIA)
|
||||
} else if let exploreLink = cloudExploreLinks.first {
|
||||
} else if case let .error(cloudExploreError) = cloudExploreResponse,
|
||||
cloudExploreError.error.lowercased() == "bad archive"
|
||||
{
|
||||
guard let selectedCloudLink = selectedCloudMagnet.links[safe: 0] else {
|
||||
throw DebridError.EmptyUserMagnets
|
||||
}
|
||||
|
||||
let restrictedFile = DebridIAFile(
|
||||
id: 0,
|
||||
name: selectedMagnet.fileName,
|
||||
streamUrlString: exploreLink
|
||||
name: selectedCloudMagnet.fileName,
|
||||
streamUrlString: "\(selectedCloudLink)/\(selectedCloudMagnet.fileName)"
|
||||
)
|
||||
|
||||
return (restrictedFile, nil)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ class Premiumize: OAuthDebridSource, ObservableObject {
|
|||
|
||||
private let jsonDecoder = JSONDecoder()
|
||||
|
||||
init() {
|
||||
// Populate user downloads and magnets
|
||||
Task {
|
||||
try? await getUserDownloads()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Auth
|
||||
|
||||
func getAuthUrl() throws -> URL {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,14 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
|||
UserDefaults.standard.removeObject(forKey: forKey)
|
||||
}
|
||||
|
||||
init() {
|
||||
// Populate user downloads and magnets
|
||||
Task {
|
||||
try? await getUserDownloads()
|
||||
try? await getUserMagnets()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Auth
|
||||
|
||||
// Fetches the device code from RD
|
||||
|
|
|
|||
|
|
@ -36,6 +36,13 @@ class TorBox: DebridSource, ObservableObject {
|
|||
private let jsonDecoder = JSONDecoder()
|
||||
private let jsonEncoder = JSONEncoder()
|
||||
|
||||
init() {
|
||||
// Populate user downloads and magnets
|
||||
Task {
|
||||
try? await getUserMagnets()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Auth
|
||||
|
||||
func setApiKey(_ key: String) {
|
||||
|
|
|
|||
|
|
@ -55,12 +55,14 @@ struct Magnet: Codable, Hashable, Sendable {
|
|||
if let hash, link == nil {
|
||||
self.hash = parseHash(hash)
|
||||
self.link = generateLink(hash: hash, title: title, trackers: trackers)
|
||||
} else if let parsedLink = parseLink(link), hash == nil {
|
||||
self.link = parsedLink
|
||||
self.hash = parseHash(extractHash(link: parsedLink))
|
||||
} else if let link, hash == nil {
|
||||
let (link, hash) = parseLink(link)
|
||||
|
||||
self.link = link
|
||||
self.hash = hash
|
||||
} else {
|
||||
self.hash = parseHash(hash)
|
||||
self.link = parseLink(link)
|
||||
self.link = parseLink(link).link
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,19 +110,35 @@ struct Magnet: Codable, Hashable, Sendable {
|
|||
}
|
||||
}
|
||||
|
||||
func parseLink(_ link: String?) -> String? {
|
||||
if let decodedLink = link?.removingPercentEncoding {
|
||||
let separator = "magnet:?xt=urn:btih:"
|
||||
if decodedLink.starts(with: separator) {
|
||||
return decodedLink
|
||||
} else if decodedLink.contains(separator) {
|
||||
let splitLink = decodedLink.components(separatedBy: separator)
|
||||
return splitLink.last.map { separator + $0 } ?? nil
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
func parseLink(_ link: String?, withHash: Bool = false) -> (link: String?, hash: String?) {
|
||||
let separator = "magnet:?xt=urn:btih:"
|
||||
|
||||
// Remove percent encoding from the link and ensure it's a magnet
|
||||
guard let decodedLink = link?.removingPercentEncoding, decodedLink.contains(separator) else {
|
||||
return (nil, nil)
|
||||
}
|
||||
|
||||
// Isolate the magnet link if it's bundled with another protocol
|
||||
let isolatedLink: String?
|
||||
if decodedLink.starts(with: separator) {
|
||||
isolatedLink = decodedLink
|
||||
} else {
|
||||
return nil
|
||||
let splitLink = decodedLink.components(separatedBy: separator)
|
||||
isolatedLink = splitLink.last.map { separator + $0 }
|
||||
}
|
||||
|
||||
guard let isolatedLink else {
|
||||
return (nil, nil)
|
||||
}
|
||||
|
||||
// If the hash can be extracted, decrypt it if necessary and return the revised link + hash
|
||||
if let originalHash = extractHash(link: isolatedLink),
|
||||
let parsedHash = parseHash(originalHash)
|
||||
{
|
||||
let replacedLink = isolatedLink.replacingOccurrences(of: originalHash, with: parsedHash)
|
||||
return (replacedLink, parsedHash)
|
||||
} else {
|
||||
return (decodedLink, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@
|
|||
import Foundation
|
||||
|
||||
extension OffCloud {
|
||||
struct ErrorResponse: Codable, Sendable {
|
||||
let error: String
|
||||
}
|
||||
|
||||
struct InstantAvailabilityRequest: Codable, Sendable {
|
||||
let hashes: [String]
|
||||
}
|
||||
|
|
@ -28,7 +32,32 @@ extension OffCloud {
|
|||
let url: String
|
||||
}
|
||||
|
||||
typealias CloudExploreResponse = [String]
|
||||
enum CloudExploreResponse: Codable {
|
||||
case links([String])
|
||||
case error(ErrorResponse)
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
|
||||
// Only continue if the data is a List which indicates a success
|
||||
if let linkArray = try? container.decode([String].self) {
|
||||
self = .links(linkArray)
|
||||
} else {
|
||||
let value = try container.decode(ErrorResponse.self)
|
||||
self = .error(value)
|
||||
}
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
switch self {
|
||||
case let .links(array):
|
||||
try container.encode(array)
|
||||
case let .error(value):
|
||||
try container.encode(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CloudHistoryResponse: Codable, Sendable {
|
||||
let requestId: String
|
||||
|
|
|
|||
Loading…
Reference in a new issue