From d0728e1a9b3629aca5b78586ce2f1981502b0f82 Mon Sep 17 00:00:00 2001 From: kingbri Date: Fri, 14 Jun 2024 10:31:13 -0400 Subject: [PATCH] Debrid: Make TorBox a rich service and fix cloud downloads TorBox can now show if there's a batch before loading a file. Cloud downloads should check the server in case there's a different method to fetch a download link. Signed-off-by: kingbri --- Ferrite/API/AllDebridWrapper.swift | 4 +- Ferrite/API/OffCloudWrapper.swift | 10 ++-- Ferrite/API/PremiumizeWrapper.swift | 6 +-- Ferrite/API/RealDebridWrapper.swift | 2 +- Ferrite/API/TorBoxWrapper.swift | 54 +++++++++---------- Ferrite/Models/PremiumizeModels.swift | 1 - Ferrite/Models/TorBoxModels.swift | 6 +++ .../Library/Cloud/CloudDownloadView.swift | 29 +++++----- 8 files changed, 58 insertions(+), 54 deletions(-) diff --git a/Ferrite/API/AllDebridWrapper.swift b/Ferrite/API/AllDebridWrapper.swift index 4f47f07..75be7f3 100644 --- a/Ferrite/API/AllDebridWrapper.swift +++ b/Ferrite/API/AllDebridWrapper.swift @@ -347,8 +347,8 @@ class AllDebrid: PollingDebridSource, ObservableObject { } // Not used - func checkUserDownloads(link: String) async throws -> String? { - nil + func checkUserDownloads(link: String) -> String? { + link } // The downloadId is actually the download link diff --git a/Ferrite/API/OffCloudWrapper.swift b/Ferrite/API/OffCloudWrapper.swift index 62e1e08..9785d08 100644 --- a/Ferrite/API/OffCloudWrapper.swift +++ b/Ferrite/API/OffCloudWrapper.swift @@ -17,7 +17,7 @@ class OffCloud: DebridSource, ObservableObject { let id = "OffCloud" let abbreviation = "OC" let website = "https://offcloud.com" - let description = "OffCloud is a debrid service that is used for downloads and media playback. " + + let description: String? = "OffCloud is a debrid service that is used for downloads and media playback. " + "You must pay to access this service. \n\n" + "This service does not inform if a torrent is a batch before downloading." @@ -229,13 +229,13 @@ class OffCloud: DebridSource, ObservableObject { return streamUrlString } - func getUserDownloads() async throws {} + func getUserDownloads() {} - func checkUserDownloads(link: String) async throws -> String? { - nil + func checkUserDownloads(link: String) -> String? { + link } - func deleteDownload(downloadId: String) async throws {} + func deleteDownload(downloadId: String) {} func getUserTorrents() async throws { var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/cloud/history")) diff --git a/Ferrite/API/PremiumizeWrapper.swift b/Ferrite/API/PremiumizeWrapper.swift index ad4aedd..3f6d478 100644 --- a/Ferrite/API/PremiumizeWrapper.swift +++ b/Ferrite/API/PremiumizeWrapper.swift @@ -11,7 +11,7 @@ class Premiumize: OAuthDebridSource, ObservableObject { let id = "Premiumize" let abbreviation = "PM" let website = "https://premiumize.me" - let description = "Premiumize is a debrid service that is used for downloads and media playback with seeding. " + + let description: String? = "Premiumize is a debrid service that is used for downloads and media playback with seeding. " + "You must pay to access the service." @Published var authProcessing: Bool = false @@ -369,7 +369,7 @@ class Premiumize: OAuthDebridSource, ObservableObject { } // No user torrents for Premiumize - func getUserTorrents() async throws {} + func getUserTorrents() {} - func deleteTorrent(torrentId: String?) async throws {} + func deleteTorrent(torrentId: String?) {} } diff --git a/Ferrite/API/RealDebridWrapper.swift b/Ferrite/API/RealDebridWrapper.swift index 975f8a2..bd65250 100644 --- a/Ferrite/API/RealDebridWrapper.swift +++ b/Ferrite/API/RealDebridWrapper.swift @@ -483,7 +483,7 @@ class RealDebrid: PollingDebridSource, ObservableObject { // Not used func checkUserDownloads(link: String) -> String? { - nil + link } func deleteDownload(downloadId: String) async throws { diff --git a/Ferrite/API/TorBoxWrapper.swift b/Ferrite/API/TorBoxWrapper.swift index 9037db6..7abdf33 100644 --- a/Ferrite/API/TorBoxWrapper.swift +++ b/Ferrite/API/TorBoxWrapper.swift @@ -17,9 +17,8 @@ class TorBox: DebridSource, ObservableObject { let id = "TorBox" let abbreviation = "TB" let website = "https://torbox.app" - let description = "TorBox is a debrid service that is used for downloads and media playback with seeding. " + - "Both free and paid plans are available. \n\n" + - "This service does not inform if a torrent is a batch before downloading." + let description: String? = "TorBox is a debrid service that is used for downloads and media playback with seeding. " + + "Both free and paid plans are available." @Published var authProcessing: Bool = false var isLoggedIn: Bool { @@ -109,6 +108,7 @@ class TorBox: DebridSource, ObservableObject { var components = URLComponents(string: "\(baseApiUrl)/torrents/checkcached")! components.queryItems = sendMagnets.map { URLQueryItem(name: "hash", value: $0.hash) } components.queryItems?.append(URLQueryItem(name: "format", value: "list")) + components.queryItems?.append(URLQueryItem(name: "list_files", value: "true")) guard let url = components.url else { throw DebridError.InvalidUrl @@ -124,12 +124,21 @@ class TorBox: DebridSource, ObservableObject { return } - let availableHashes = iaObjects.map { + let availableHashes = iaObjects.map { iaObject in DebridIA( - magnet: Magnet(hash: $0.hash, link: nil), + magnet: Magnet(hash: iaObject.hash, link: nil), source: self.id, expiryTimeStamp: Date().timeIntervalSince1970 + 300, - files: [] + files: iaObject.files.enumerated().compactMap { index, iaFile in + guard let fileName = iaFile.name.split(separator: "/").last else { + return nil + } + + return DebridIAFile( + fileId: index, + name: String(fileName) + ) + } ) } @@ -150,25 +159,12 @@ class TorBox: DebridSource, ObservableObject { throw DebridError.IsCaching } - if filteredTorrent.files.count > 1 { - var copiedIA = ia - - copiedIA?.files = filteredTorrent.files.map { torrentFile in - DebridIAFile( - fileId: torrentFile.id, - name: torrentFile.shortName, - streamUrlString: String(torrentId) - ) - } - - return (nil, copiedIA) - } else if let torrentFile = filteredTorrent.files.first { - let restrictedFile = DebridIAFile(fileId: torrentFile.id, name: torrentFile.name, streamUrlString: String(torrentId)) - - return (restrictedFile, nil) - } else { - return (nil, nil) + guard let torrentFile = filteredTorrent.files[safe: iaFile?.fileId ?? 0] else { + throw DebridError.EmptyTorrents } + + let restrictedFile = DebridIAFile(fileId: torrentFile.id, name: torrentFile.name, streamUrlString: String(torrentId)) + return (restrictedFile, nil) } private func createTorrent(magnet: Magnet) async throws -> Int { @@ -233,13 +229,13 @@ class TorBox: DebridSource, ObservableObject { // MARK: - Cloud methods // Unused - func getUserDownloads() async throws {} + func getUserDownloads() {} - func checkUserDownloads(link: String) async throws -> String? { - nil + func checkUserDownloads(link: String) -> String? { + link } - func deleteDownload(downloadId: String) async throws {} + func deleteDownload(downloadId: String) {} func getUserTorrents() async throws { let torrentList = try await myTorrentList() @@ -252,7 +248,7 @@ class TorBox: DebridSource, ObservableObject { fileName: torrent.name, status: torrent.downloadState == "cached" || torrent.downloadState == "completed" ? "downloaded" : torrent.downloadState, hash: torrent.hash, - links: [String(torrent.id)] + links: torrent.files.map { String($0.id) } ) } } diff --git a/Ferrite/Models/PremiumizeModels.swift b/Ferrite/Models/PremiumizeModels.swift index 927702c..a3fdf97 100644 --- a/Ferrite/Models/PremiumizeModels.swift +++ b/Ferrite/Models/PremiumizeModels.swift @@ -20,7 +20,6 @@ extension Premiumize { struct DDLResponse: Codable { let status: String let content: [DDLData]? - let location: String let filename: String let filesize: Int } diff --git a/Ferrite/Models/TorBoxModels.swift b/Ferrite/Models/TorBoxModels.swift index be9845f..d2c0af8 100644 --- a/Ferrite/Models/TorBoxModels.swift +++ b/Ferrite/Models/TorBoxModels.swift @@ -47,6 +47,12 @@ extension TorBox { let name: String let size: Int let hash: String + let files: [InstantAvailabilityFile] + } + + struct InstantAvailabilityFile: Codable, Sendable { + let name: String + let size: Int } struct InstantAvailabilityDataFailure: Codable, Sendable { diff --git a/Ferrite/Views/ComponentViews/Library/Cloud/CloudDownloadView.swift b/Ferrite/Views/ComponentViews/Library/Cloud/CloudDownloadView.swift index 9f2b488..f68f27c 100644 --- a/Ferrite/Views/ComponentViews/Library/Cloud/CloudDownloadView.swift +++ b/Ferrite/Views/ComponentViews/Library/Cloud/CloudDownloadView.swift @@ -24,21 +24,24 @@ struct CloudDownloadView: View { Button(cloudDownload.fileName) { navModel.resultFromCloud = true navModel.selectedTitle = cloudDownload.fileName - debridManager.downloadUrl = cloudDownload.link - - PersistenceController.shared.createHistory( - HistoryEntryJson( - name: cloudDownload.fileName, - url: cloudDownload.link, - source: debridSource.id - ), - performSave: true + var historyEntry = HistoryEntryJson( + name: cloudDownload.fileName, + source: debridSource.id ) - pluginManager.runDefaultAction( - urlString: debridManager.downloadUrl, - navModel: navModel - ) + debridManager.currentDebridTask = Task { + await debridManager.fetchDebridDownload(magnet: nil, cloudInfo: cloudDownload.link) + + if !debridManager.downloadUrl.isEmpty { + historyEntry.url = debridManager.downloadUrl + PersistenceController.shared.createHistory(historyEntry, performSave: true) + + pluginManager.runDefaultAction( + urlString: debridManager.downloadUrl, + navModel: navModel + ) + } + } } .disabledAppearance(navModel.currentChoiceSheet != nil, dimmedOpacity: 0.7, animation: .easeOut(duration: 0.2)) .tint(.primary)