Debrid: Add common functions for existing magnets/downloads
This fixes cloud magnet fetching and also doesn't duplicate magnets inside the cloud service. Unrestricted links don't get duplicated, so no need to check against those. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
273403b711
commit
0caf8a8120
8 changed files with 88 additions and 94 deletions
|
|
@ -181,10 +181,18 @@ public class AllDebrid: PollingDebridSource {
|
||||||
// MARK: - Downloading
|
// MARK: - Downloading
|
||||||
|
|
||||||
// Wrapper function to fetch a download link from the API
|
// Wrapper function to fetch a download link from the API
|
||||||
public func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
|
public func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?, userTorrents: [DebridCloudTorrent] = []) async throws -> String {
|
||||||
let magnetID = try await addMagnet(magnet: magnet)
|
let selectedMagnetId: String
|
||||||
|
|
||||||
|
if let existingMagnet = userTorrents.first(where: { $0.hash == magnet.hash && $0.status == "Ready" }) {
|
||||||
|
selectedMagnetId = existingMagnet.torrentId
|
||||||
|
} else {
|
||||||
|
let magnetId = try await addMagnet(magnet: magnet)
|
||||||
|
selectedMagnetId = String(magnetId)
|
||||||
|
}
|
||||||
|
|
||||||
let lockedLink = try await fetchMagnetStatus(
|
let lockedLink = try await fetchMagnetStatus(
|
||||||
magnetId: magnetID,
|
magnetId: selectedMagnetId,
|
||||||
selectedIndex: iaFile?.fileId ?? 0
|
selectedIndex: iaFile?.fileId ?? 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -221,9 +229,9 @@ public class AllDebrid: PollingDebridSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchMagnetStatus(magnetId: Int, selectedIndex: Int?) async throws -> String {
|
public func fetchMagnetStatus(magnetId: String, selectedIndex: Int?) async throws -> String {
|
||||||
let queryItems = [
|
let queryItems = [
|
||||||
URLQueryItem(name: "id", value: String(magnetId))
|
URLQueryItem(name: "id", value: magnetId)
|
||||||
]
|
]
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/status", queryItems: queryItems))
|
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/status", queryItems: queryItems))
|
||||||
|
|
||||||
|
|
@ -315,6 +323,11 @@ public class AllDebrid: PollingDebridSource {
|
||||||
return downloads
|
return downloads
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not used
|
||||||
|
public func checkUserDownloads(link: String, userDownloads: [DebridCloudDownload]) async throws -> String? {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
// The downloadId is actually the download link
|
// The downloadId is actually the download link
|
||||||
public func deleteDownload(downloadId: String) async throws {
|
public func deleteDownload(downloadId: String) async throws {
|
||||||
let queryItems = [
|
let queryItems = [
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ public class Premiumize: OAuthDebridSource {
|
||||||
// MARK: - Downloading
|
// MARK: - Downloading
|
||||||
|
|
||||||
// Wrapper function to fetch a DDL link from the API
|
// Wrapper function to fetch a DDL link from the API
|
||||||
public func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
|
public func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?, userTorrents: [DebridCloudTorrent] = []) async throws -> String {
|
||||||
// Store the item in PM cloud for later use
|
// Store the item in PM cloud for later use
|
||||||
try await createTransfer(magnet: magnet)
|
try await createTransfer(magnet: magnet)
|
||||||
|
|
||||||
|
|
@ -316,6 +316,11 @@ public class Premiumize: OAuthDebridSource {
|
||||||
return rawResponse
|
return rawResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func checkUserDownloads(link: String, userDownloads: [DebridCloudDownload]) async throws -> String? {
|
||||||
|
// Link is the cloud item ID
|
||||||
|
try await itemDetails(itemID: link).link
|
||||||
|
}
|
||||||
|
|
||||||
public func deleteDownload(downloadId: String) async throws {
|
public func deleteDownload(downloadId: String) async throws {
|
||||||
var request = URLRequest(url: URL(string: "\(baseApiUrl)/item/delete")!)
|
var request = URLRequest(url: URL(string: "\(baseApiUrl)/item/delete")!)
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
|
|
|
||||||
|
|
@ -295,14 +295,22 @@ public class RealDebrid: PollingDebridSource {
|
||||||
// MARK: - Downloading
|
// MARK: - Downloading
|
||||||
|
|
||||||
// Wrapper function to fetch a download link from the API
|
// Wrapper function to fetch a download link from the API
|
||||||
public func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
|
public func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?, userTorrents: [DebridCloudTorrent] = []) async throws -> String {
|
||||||
let selectedMagnetId = try await addMagnet(magnet: magnet)
|
let selectedMagnetId: String
|
||||||
|
|
||||||
try await selectFiles(debridID: selectedMagnetId, fileIds: iaFile?.batchIds ?? [])
|
// Don't queue a new job if the torrent already exists
|
||||||
|
if let existingTorrent = userTorrents.first(where: { $0.hash == magnet.hash && $0.status == "downloaded" }) {
|
||||||
|
selectedMagnetId = existingTorrent.torrentId
|
||||||
|
} else {
|
||||||
|
selectedMagnetId = try await addMagnet(magnet: magnet)
|
||||||
|
|
||||||
|
try await selectFiles(debridID: selectedMagnetId, fileIds: iaFile?.batchIds ?? [])
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealDebrid has 1 as the first ID for a file
|
||||||
let torrentLink = try await torrentInfo(
|
let torrentLink = try await torrentInfo(
|
||||||
debridID: selectedMagnetId,
|
debridID: selectedMagnetId,
|
||||||
selectedIndex: iaFile?.fileId ?? 0
|
selectedFileId: iaFile?.fileId ?? 1
|
||||||
)
|
)
|
||||||
let downloadLink = try await unrestrictLink(debridDownloadLink: torrentLink)
|
let downloadLink = try await unrestrictLink(debridDownloadLink: torrentLink)
|
||||||
|
|
||||||
|
|
@ -351,13 +359,13 @@ public class RealDebrid: PollingDebridSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the info of a torrent from a given ID
|
// Gets the info of a torrent from a given ID
|
||||||
public func torrentInfo(debridID: String, selectedIndex: Int?) async throws -> String {
|
public func torrentInfo(debridID: String, selectedFileId: Int?) async throws -> String {
|
||||||
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/info/\(debridID)")!)
|
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/info/\(debridID)")!)
|
||||||
|
|
||||||
let data = try await performRequest(request: &request, requestName: #function)
|
let data = try await performRequest(request: &request, requestName: #function)
|
||||||
let rawResponse = try jsonDecoder.decode(TorrentInfoResponse.self, from: data)
|
let rawResponse = try jsonDecoder.decode(TorrentInfoResponse.self, from: data)
|
||||||
let filteredFiles = rawResponse.files.filter { $0.selected == 1 }
|
let filteredFiles = rawResponse.files.filter { $0.selected == 1 }
|
||||||
let linkIndex = filteredFiles.firstIndex(where: { $0.id == selectedIndex })
|
let linkIndex = filteredFiles.firstIndex(where: { $0.id == selectedFileId })
|
||||||
|
|
||||||
// Let the user know if a torrent is downloading
|
// Let the user know if a torrent is downloading
|
||||||
if let torrentLink = rawResponse.links[safe: linkIndex ?? -1], rawResponse.status == "downloaded" {
|
if let torrentLink = rawResponse.links[safe: linkIndex ?? -1], rawResponse.status == "downloaded" {
|
||||||
|
|
@ -429,6 +437,11 @@ public class RealDebrid: PollingDebridSource {
|
||||||
return downloads
|
return downloads
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not used
|
||||||
|
public func checkUserDownloads(link: String, userDownloads: [DebridCloudDownload]) -> String? {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
public func deleteDownload(downloadId: String) async throws {
|
public func deleteDownload(downloadId: String) async throws {
|
||||||
var request = URLRequest(url: URL(string: "\(baseApiUrl)/downloads/delete/\(downloadId)")!)
|
var request = URLRequest(url: URL(string: "\(baseApiUrl)/downloads/delete/\(downloadId)")!)
|
||||||
request.httpMethod = "DELETE"
|
request.httpMethod = "DELETE"
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,16 @@ public protocol DebridSource {
|
||||||
|
|
||||||
// Fetches a download link from a source
|
// Fetches a download link from a source
|
||||||
// Include the instant availability information with the args
|
// Include the instant availability information with the args
|
||||||
func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String
|
// Torrents also checked here
|
||||||
|
func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?, userTorrents: [DebridCloudTorrent]) async throws -> String
|
||||||
|
|
||||||
// Fetches cloud information from the service
|
// User downloads functions
|
||||||
func getUserDownloads() async throws -> [DebridCloudDownload]
|
func getUserDownloads() async throws -> [DebridCloudDownload]
|
||||||
func getUserTorrents() async throws -> [DebridCloudTorrent]
|
func checkUserDownloads(link: String, userDownloads: [DebridCloudDownload]) async throws -> String?
|
||||||
|
|
||||||
// Deletes information from the service
|
|
||||||
func deleteDownload(downloadId: String) async throws
|
func deleteDownload(downloadId: String) async throws
|
||||||
|
|
||||||
|
// User torrent functions
|
||||||
|
func getUserTorrents() async throws -> [DebridCloudTorrent]
|
||||||
func deleteTorrent(torrentId: String) async throws
|
func deleteTorrent(torrentId: String) async throws
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -559,42 +559,21 @@ public class DebridManager: ObservableObject {
|
||||||
|
|
||||||
switch selectedDebridType {
|
switch selectedDebridType {
|
||||||
case .realDebrid:
|
case .realDebrid:
|
||||||
await fetchRdDownload(magnet: magnet, existingLink: cloudInfo)
|
await fetchRdDownload(magnet: magnet, cloudInfo: cloudInfo)
|
||||||
case .allDebrid:
|
case .allDebrid:
|
||||||
await fetchAdDownload(magnet: magnet, existingLockedLink: cloudInfo)
|
await fetchAdDownload(magnet: magnet, cloudInfo: cloudInfo)
|
||||||
case .premiumize:
|
case .premiumize:
|
||||||
await fetchPmDownload(magnet: magnet, cloudItemId: cloudInfo)
|
await fetchPmDownload(magnet: magnet, cloudInfo: cloudInfo)
|
||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchRdDownload(magnet: Magnet?, existingLink: String?) async {
|
func fetchRdDownload(magnet: Magnet?, cloudInfo: String?) async {
|
||||||
// If an existing link is passed in args, set it to that. Otherwise, find one from RD cloud.
|
|
||||||
/*
|
|
||||||
let torrentLink: String?
|
|
||||||
if let existingLink {
|
|
||||||
torrentLink = existingLink
|
|
||||||
} else {
|
|
||||||
// Bypass the TTL for up to date information
|
|
||||||
await fetchRdCloud(bypassTTL: true)
|
|
||||||
|
|
||||||
let existingTorrent = realDebridCloudTorrents.first { $0.hash == selectedRealDebridItem?.magnet.hash && $0.status == "downloaded" }
|
|
||||||
torrentLink = existingTorrent?.links[safe: selectedRealDebridFile?.batchFileIndex ?? 0]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// If the links match from a user's downloads, no need to re-run a download
|
|
||||||
/*
|
|
||||||
if let torrentLink,
|
|
||||||
let downloadLink = await checkRdUserDownloads(userTorrentLink: torrentLink)
|
|
||||||
{
|
|
||||||
downloadUrl = downloadLink
|
|
||||||
} else */
|
|
||||||
if let magnet {
|
if let magnet {
|
||||||
let downloadLink = try await realDebrid.getDownloadLink(
|
let downloadLink = try await realDebrid.getDownloadLink(
|
||||||
magnet: magnet, ia: selectedRealDebridItem, iaFile: selectedRealDebridFile
|
magnet: magnet, ia: selectedRealDebridItem, iaFile: selectedRealDebridFile, userTorrents: realDebridCloudTorrents
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update the UI
|
// Update the UI
|
||||||
|
|
@ -612,7 +591,9 @@ public class DebridManager: ObservableObject {
|
||||||
default:
|
default:
|
||||||
await sendDebridError(error, prefix: "RealDebrid download error", cancelString: "Download cancelled")
|
await sendDebridError(error, prefix: "RealDebrid download error", cancelString: "Download cancelled")
|
||||||
|
|
||||||
// await deleteRdTorrent(torrentID: selectedRealDebridID, presentError: false)
|
if let torrentId = selectedRealDebridID {
|
||||||
|
try? await realDebrid.deleteTorrent(torrentId: torrentId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logManager?.hideIndeterminateToast()
|
logManager?.hideIndeterminateToast()
|
||||||
|
|
@ -685,32 +666,11 @@ public class DebridManager: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchAdDownload(magnet: Magnet?, existingLockedLink: String?) async {
|
func fetchAdDownload(magnet: Magnet?, cloudInfo: String?) async {
|
||||||
// If an existing link is passed in args, set it to that. Otherwise, find one from AD cloud.
|
|
||||||
/*
|
|
||||||
let lockedLink: String?
|
|
||||||
if let existingLockedLink {
|
|
||||||
lockedLink = existingLockedLink
|
|
||||||
} else {
|
|
||||||
// Bypass the TTL for up to date information
|
|
||||||
await fetchAdCloud(bypassTTL: true)
|
|
||||||
|
|
||||||
let existingMagnet = allDebridCloudMagnets.first { $0.hash == selectedAllDebridItem?.magnet.hash && $0.status == "Ready" }
|
|
||||||
lockedLink = existingMagnet?.links[safe: selectedAllDebridFile?.fileId ?? 0]?.link
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
/*
|
|
||||||
if let lockedLink,
|
|
||||||
let unlockedLink = await checkAdUserLinks(lockedLink: lockedLink)
|
|
||||||
{
|
|
||||||
downloadUrl = unlockedLink
|
|
||||||
} else if let magnet {
|
|
||||||
*/
|
|
||||||
if let magnet {
|
if let magnet {
|
||||||
let downloadLink = try await allDebrid.getDownloadLink(
|
let downloadLink = try await allDebrid.getDownloadLink(
|
||||||
magnet: magnet, ia: selectedAllDebridItem, iaFile: selectedAllDebridFile
|
magnet: magnet, ia: selectedAllDebridItem, iaFile: selectedAllDebridFile, userTorrents: allDebridCloudMagnets
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update UI
|
// Update UI
|
||||||
|
|
@ -777,11 +737,14 @@ public class DebridManager: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchPmDownload(magnet: Magnet?, cloudItemId: String? = nil) async {
|
func fetchPmDownload(magnet: Magnet?, cloudInfo: String? = nil) async {
|
||||||
do {
|
do {
|
||||||
if let cloudItemId {
|
if let cloudInfo {
|
||||||
downloadUrl = try await premiumize.itemDetails(itemID: cloudItemId).link
|
downloadUrl = try await premiumize.checkUserDownloads(link: cloudInfo, userDownloads: premiumizeCloudItems) ?? ""
|
||||||
} else if let magnet {
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let magnet {
|
||||||
let downloadLink = try await premiumize.getDownloadLink(
|
let downloadLink = try await premiumize.getDownloadLink(
|
||||||
magnet: magnet, ia: selectedPremiumizeItem, iaFile: selectedPremiumizeFile
|
magnet: magnet, ia: selectedPremiumizeItem, iaFile: selectedPremiumizeFile
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,14 @@ struct AllDebridCloudView: View {
|
||||||
)
|
)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
if cloudTorrent.links.count == 1 {
|
let magnet = Magnet(hash: cloudTorrent.hash, link: nil)
|
||||||
if let torrentLink = cloudTorrent.links[safe: 0] {
|
await debridManager.populateDebridIA([magnet])
|
||||||
await debridManager.fetchDebridDownload(magnet: nil, cloudInfo: torrentLink)
|
if debridManager.selectDebridResult(magnet: magnet) {
|
||||||
|
// Is this a batch?
|
||||||
|
|
||||||
|
if cloudTorrent.links.count == 1 {
|
||||||
|
await debridManager.fetchDebridDownload(magnet: magnet)
|
||||||
|
|
||||||
if !debridManager.downloadUrl.isEmpty {
|
if !debridManager.downloadUrl.isEmpty {
|
||||||
historyInfo.url = debridManager.downloadUrl
|
historyInfo.url = debridManager.downloadUrl
|
||||||
PersistenceController.shared.createHistory(historyInfo, performSave: true)
|
PersistenceController.shared.createHistory(historyInfo, performSave: true)
|
||||||
|
|
@ -79,14 +84,8 @@ struct AllDebridCloudView: View {
|
||||||
navModel: navModel
|
navModel: navModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
navModel.selectedMagnet = magnet
|
||||||
let magnet = Magnet(hash: cloudTorrent.hash, link: nil)
|
|
||||||
|
|
||||||
// Do not clear old IA values
|
|
||||||
await debridManager.populateDebridIA([magnet])
|
|
||||||
|
|
||||||
if debridManager.selectDebridResult(magnet: magnet) {
|
|
||||||
navModel.selectedHistoryInfo = historyInfo
|
navModel.selectedHistoryInfo = historyInfo
|
||||||
navModel.currentChoiceSheet = .batch
|
navModel.currentChoiceSheet = .batch
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,14 @@ struct RealDebridCloudView: View {
|
||||||
)
|
)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
if cloudTorrent.links.count == 1 {
|
let magnet = Magnet(hash: cloudTorrent.hash, link: nil)
|
||||||
if let torrentLink = cloudTorrent.links[safe: 0] {
|
await debridManager.populateDebridIA([magnet])
|
||||||
await debridManager.fetchDebridDownload(magnet: nil, cloudInfo: torrentLink)
|
if debridManager.selectDebridResult(magnet: magnet) {
|
||||||
|
// Is this a batch?
|
||||||
|
|
||||||
|
if cloudTorrent.links.count == 1 {
|
||||||
|
await debridManager.fetchDebridDownload(magnet: magnet)
|
||||||
|
|
||||||
if !debridManager.downloadUrl.isEmpty {
|
if !debridManager.downloadUrl.isEmpty {
|
||||||
historyInfo.url = debridManager.downloadUrl
|
historyInfo.url = debridManager.downloadUrl
|
||||||
PersistenceController.shared.createHistory(historyInfo, performSave: true)
|
PersistenceController.shared.createHistory(historyInfo, performSave: true)
|
||||||
|
|
@ -80,14 +85,8 @@ struct RealDebridCloudView: View {
|
||||||
navModel: navModel
|
navModel: navModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
navModel.selectedMagnet = magnet
|
||||||
let magnet = Magnet(hash: cloudTorrent.hash, link: nil)
|
|
||||||
|
|
||||||
// Do not clear old IA values
|
|
||||||
await debridManager.populateDebridIA([magnet])
|
|
||||||
|
|
||||||
if debridManager.selectDebridResult(magnet: magnet) {
|
|
||||||
navModel.selectedHistoryInfo = historyInfo
|
navModel.selectedHistoryInfo = historyInfo
|
||||||
navModel.currentChoiceSheet = .batch
|
navModel.currentChoiceSheet = .batch
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ struct BatchChoiceView: View {
|
||||||
// Common function to communicate betwen VMs and queue/display a download
|
// Common function to communicate betwen VMs and queue/display a download
|
||||||
func queueCommonDownload(fileName: String) {
|
func queueCommonDownload(fileName: String) {
|
||||||
debridManager.currentDebridTask = Task {
|
debridManager.currentDebridTask = Task {
|
||||||
await debridManager.fetchDebridDownload(magnet: navModel.resultFromCloud ? nil : navModel.selectedMagnet)
|
await debridManager.fetchDebridDownload(magnet: navModel.selectedMagnet)
|
||||||
|
|
||||||
if !debridManager.downloadUrl.isEmpty {
|
if !debridManager.downloadUrl.isEmpty {
|
||||||
try? await Task.sleep(seconds: 1)
|
try? await Task.sleep(seconds: 1)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue