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 <bdashore3@proton.me>
This commit is contained in:
kingbri 2024-06-14 10:31:13 -04:00
parent 89367b72da
commit d0728e1a9b
8 changed files with 58 additions and 54 deletions

View file

@ -347,8 +347,8 @@ class AllDebrid: PollingDebridSource, ObservableObject {
} }
// Not used // Not used
func checkUserDownloads(link: String) async throws -> String? { func checkUserDownloads(link: String) -> String? {
nil link
} }
// The downloadId is actually the download link // The downloadId is actually the download link

View file

@ -17,7 +17,7 @@ class OffCloud: DebridSource, ObservableObject {
let id = "OffCloud" let id = "OffCloud"
let abbreviation = "OC" let abbreviation = "OC"
let website = "https://offcloud.com" 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" + "You must pay to access this service. \n\n" +
"This service does not inform if a torrent is a batch before downloading." "This service does not inform if a torrent is a batch before downloading."
@ -229,13 +229,13 @@ class OffCloud: DebridSource, ObservableObject {
return streamUrlString return streamUrlString
} }
func getUserDownloads() async throws {} func getUserDownloads() {}
func checkUserDownloads(link: String) async throws -> String? { func checkUserDownloads(link: String) -> String? {
nil link
} }
func deleteDownload(downloadId: String) async throws {} func deleteDownload(downloadId: String) {}
func getUserTorrents() async throws { func getUserTorrents() async throws {
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/cloud/history")) var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/cloud/history"))

View file

@ -11,7 +11,7 @@ class Premiumize: OAuthDebridSource, ObservableObject {
let id = "Premiumize" let id = "Premiumize"
let abbreviation = "PM" let abbreviation = "PM"
let website = "https://premiumize.me" 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." "You must pay to access the service."
@Published var authProcessing: Bool = false @Published var authProcessing: Bool = false
@ -369,7 +369,7 @@ class Premiumize: OAuthDebridSource, ObservableObject {
} }
// No user torrents for Premiumize // No user torrents for Premiumize
func getUserTorrents() async throws {} func getUserTorrents() {}
func deleteTorrent(torrentId: String?) async throws {} func deleteTorrent(torrentId: String?) {}
} }

View file

@ -483,7 +483,7 @@ class RealDebrid: PollingDebridSource, ObservableObject {
// Not used // Not used
func checkUserDownloads(link: String) -> String? { func checkUserDownloads(link: String) -> String? {
nil link
} }
func deleteDownload(downloadId: String) async throws { func deleteDownload(downloadId: String) async throws {

View file

@ -17,9 +17,8 @@ class TorBox: DebridSource, ObservableObject {
let id = "TorBox" let id = "TorBox"
let abbreviation = "TB" let abbreviation = "TB"
let website = "https://torbox.app" let website = "https://torbox.app"
let description = "TorBox is a debrid service that is used for downloads and media playback with seeding. " + 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. \n\n" + "Both free and paid plans are available."
"This service does not inform if a torrent is a batch before downloading."
@Published var authProcessing: Bool = false @Published var authProcessing: Bool = false
var isLoggedIn: Bool { var isLoggedIn: Bool {
@ -109,6 +108,7 @@ class TorBox: DebridSource, ObservableObject {
var components = URLComponents(string: "\(baseApiUrl)/torrents/checkcached")! var components = URLComponents(string: "\(baseApiUrl)/torrents/checkcached")!
components.queryItems = sendMagnets.map { URLQueryItem(name: "hash", value: $0.hash) } components.queryItems = sendMagnets.map { URLQueryItem(name: "hash", value: $0.hash) }
components.queryItems?.append(URLQueryItem(name: "format", value: "list")) components.queryItems?.append(URLQueryItem(name: "format", value: "list"))
components.queryItems?.append(URLQueryItem(name: "list_files", value: "true"))
guard let url = components.url else { guard let url = components.url else {
throw DebridError.InvalidUrl throw DebridError.InvalidUrl
@ -124,12 +124,21 @@ class TorBox: DebridSource, ObservableObject {
return return
} }
let availableHashes = iaObjects.map { let availableHashes = iaObjects.map { iaObject in
DebridIA( DebridIA(
magnet: Magnet(hash: $0.hash, link: nil), magnet: Magnet(hash: iaObject.hash, link: nil),
source: self.id, source: self.id,
expiryTimeStamp: Date().timeIntervalSince1970 + 300, 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 throw DebridError.IsCaching
} }
if filteredTorrent.files.count > 1 { guard let torrentFile = filteredTorrent.files[safe: iaFile?.fileId ?? 0] else {
var copiedIA = ia throw DebridError.EmptyTorrents
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)
} }
let restrictedFile = DebridIAFile(fileId: torrentFile.id, name: torrentFile.name, streamUrlString: String(torrentId))
return (restrictedFile, nil)
} }
private func createTorrent(magnet: Magnet) async throws -> Int { private func createTorrent(magnet: Magnet) async throws -> Int {
@ -233,13 +229,13 @@ class TorBox: DebridSource, ObservableObject {
// MARK: - Cloud methods // MARK: - Cloud methods
// Unused // Unused
func getUserDownloads() async throws {} func getUserDownloads() {}
func checkUserDownloads(link: String) async throws -> String? { func checkUserDownloads(link: String) -> String? {
nil link
} }
func deleteDownload(downloadId: String) async throws {} func deleteDownload(downloadId: String) {}
func getUserTorrents() async throws { func getUserTorrents() async throws {
let torrentList = try await myTorrentList() let torrentList = try await myTorrentList()
@ -252,7 +248,7 @@ class TorBox: DebridSource, ObservableObject {
fileName: torrent.name, fileName: torrent.name,
status: torrent.downloadState == "cached" || torrent.downloadState == "completed" ? "downloaded" : torrent.downloadState, status: torrent.downloadState == "cached" || torrent.downloadState == "completed" ? "downloaded" : torrent.downloadState,
hash: torrent.hash, hash: torrent.hash,
links: [String(torrent.id)] links: torrent.files.map { String($0.id) }
) )
} }
} }

View file

@ -20,7 +20,6 @@ extension Premiumize {
struct DDLResponse: Codable { struct DDLResponse: Codable {
let status: String let status: String
let content: [DDLData]? let content: [DDLData]?
let location: String
let filename: String let filename: String
let filesize: Int let filesize: Int
} }

View file

@ -47,6 +47,12 @@ extension TorBox {
let name: String let name: String
let size: Int let size: Int
let hash: String let hash: String
let files: [InstantAvailabilityFile]
}
struct InstantAvailabilityFile: Codable, Sendable {
let name: String
let size: Int
} }
struct InstantAvailabilityDataFailure: Codable, Sendable { struct InstantAvailabilityDataFailure: Codable, Sendable {

View file

@ -24,21 +24,24 @@ struct CloudDownloadView: View {
Button(cloudDownload.fileName) { Button(cloudDownload.fileName) {
navModel.resultFromCloud = true navModel.resultFromCloud = true
navModel.selectedTitle = cloudDownload.fileName navModel.selectedTitle = cloudDownload.fileName
debridManager.downloadUrl = cloudDownload.link var historyEntry = HistoryEntryJson(
name: cloudDownload.fileName,
PersistenceController.shared.createHistory( source: debridSource.id
HistoryEntryJson(
name: cloudDownload.fileName,
url: cloudDownload.link,
source: debridSource.id
),
performSave: true
) )
pluginManager.runDefaultAction( debridManager.currentDebridTask = Task {
urlString: debridManager.downloadUrl, await debridManager.fetchDebridDownload(magnet: nil, cloudInfo: cloudDownload.link)
navModel: navModel
) 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)) .disabledAppearance(navModel.currentChoiceSheet != nil, dimmedOpacity: 0.7, animation: .easeOut(duration: 0.2))
.tint(.primary) .tint(.primary)