mirror of
https://github.com/Ferrite-iOS/Ferrite.git
synced 2026-01-11 20:10:27 +00:00
Debrid: Add split for download and unrestrict
Some debrid services aren't "rich", which means that they don't broadcast whether an instantly available torrent is a batch or a single file. This results in all torrents either having the green badge or red badge based on what hash is given. However, batches need to intercept the download itself which requires the download function to be split into download and unrestrict. In between, there's room for the batch sheet to act. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
e1eca593f3
commit
4beb953596
9 changed files with 164 additions and 81 deletions
|
|
@ -12,6 +12,9 @@
|
|||
0C03EB72296F619900162E9A /* PluginList+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C03EB70296F619900162E9A /* PluginList+CoreDataProperties.swift */; };
|
||||
0C0755C6293424A200ECA142 /* DebridLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0755C5293424A200ECA142 /* DebridLabelView.swift */; };
|
||||
0C0755C8293425B500ECA142 /* DebridManagerModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0755C7293425B500ECA142 /* DebridManagerModels.swift */; };
|
||||
0C07C6002C19FEBF00808A46 /* OffCloudWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C07C5FF2C19FEBF00808A46 /* OffCloudWrapper.swift */; };
|
||||
0C07C6022C1A016B00808A46 /* OffCloudModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C07C6012C1A016B00808A46 /* OffCloudModels.swift */; };
|
||||
0C07C6042C1A859B00808A46 /* FormDataBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C07C6032C1A859B00808A46 /* FormDataBody.swift */; };
|
||||
0C0974B029CCAAAF006DE7A3 /* OperatingSystemVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0974AF29CCAAAF006DE7A3 /* OperatingSystemVersion.swift */; };
|
||||
0C0D50E5288DFE7F0035ECC8 /* SourceModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */; };
|
||||
0C0D50E7288DFF850035ECC8 /* PluginAggregateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E6288DFF850035ECC8 /* PluginAggregateView.swift */; };
|
||||
|
|
@ -94,6 +97,8 @@
|
|||
0C84FCE729E4B61A00B0DFE4 /* FilterModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C84FCE629E4B61A00B0DFE4 /* FilterModels.swift */; };
|
||||
0C84FCE929E5ADEF00B0DFE4 /* FilterAmountLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C84FCE829E5ADEF00B0DFE4 /* FilterAmountLabelView.swift */; };
|
||||
0C871BDF29994D9D005279AC /* FilterLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C871BDE29994D9D005279AC /* FilterLabelView.swift */; };
|
||||
0C890E492C188808003B17B5 /* TorBoxWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C890E482C188808003B17B5 /* TorBoxWrapper.swift */; };
|
||||
0C890E4B2C188FA7003B17B5 /* TorBoxModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C890E4A2C188FA7003B17B5 /* TorBoxModels.swift */; };
|
||||
0C8AE2482C0FFB6600701675 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8AE2472C0FFB6600701675 /* Store.swift */; };
|
||||
0C8DC35229CE287E008A83AD /* PluginInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35129CE287E008A83AD /* PluginInfoView.swift */; };
|
||||
0C8DC35429CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35329CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift */; };
|
||||
|
|
@ -167,6 +172,9 @@
|
|||
0C03EB70296F619900162E9A /* PluginList+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginList+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0C0755C5293424A200ECA142 /* DebridLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebridLabelView.swift; sourceTree = "<group>"; };
|
||||
0C0755C7293425B500ECA142 /* DebridManagerModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebridManagerModels.swift; sourceTree = "<group>"; };
|
||||
0C07C5FF2C19FEBF00808A46 /* OffCloudWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffCloudWrapper.swift; sourceTree = "<group>"; };
|
||||
0C07C6012C1A016B00808A46 /* OffCloudModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OffCloudModels.swift; sourceTree = "<group>"; };
|
||||
0C07C6032C1A859B00808A46 /* FormDataBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormDataBody.swift; sourceTree = "<group>"; };
|
||||
0C0974AF29CCAAAF006DE7A3 /* OperatingSystemVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperatingSystemVersion.swift; sourceTree = "<group>"; };
|
||||
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceModels.swift; sourceTree = "<group>"; };
|
||||
0C0D50E6288DFF850035ECC8 /* PluginAggregateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginAggregateView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -244,6 +252,8 @@
|
|||
0C84FCE629E4B61A00B0DFE4 /* FilterModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterModels.swift; sourceTree = "<group>"; };
|
||||
0C84FCE829E5ADEF00B0DFE4 /* FilterAmountLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterAmountLabelView.swift; sourceTree = "<group>"; };
|
||||
0C871BDE29994D9D005279AC /* FilterLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterLabelView.swift; sourceTree = "<group>"; };
|
||||
0C890E482C188808003B17B5 /* TorBoxWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TorBoxWrapper.swift; sourceTree = "<group>"; };
|
||||
0C890E4A2C188FA7003B17B5 /* TorBoxModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TorBoxModels.swift; sourceTree = "<group>"; };
|
||||
0C8AE2472C0FFB6600701675 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
|
||||
0C8DC35129CE287E008A83AD /* PluginInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginInfoView.swift; sourceTree = "<group>"; };
|
||||
0C8DC35329CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsBaseUrlView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -410,6 +420,8 @@
|
|||
0C3E00D7296F5B9A00ECECB2 /* PluginModels.swift */,
|
||||
0C6771F929B3D1AE005D38D2 /* KodiModels.swift */,
|
||||
0C1A3E5129C8A7F500DA9730 /* SettingsModels.swift */,
|
||||
0C890E4A2C188FA7003B17B5 /* TorBoxModels.swift */,
|
||||
0C07C6012C1A016B00808A46 /* OffCloudModels.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -463,6 +475,7 @@
|
|||
0C1A3E5529C9488C00DA9730 /* CodableWrapper.swift */,
|
||||
0CD0265629FEFBF900A83D25 /* FerriteKeychain.swift */,
|
||||
0C8AE2472C0FFB6600701675 /* Store.swift */,
|
||||
0C07C6032C1A859B00808A46 /* FormDataBody.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -661,6 +674,8 @@
|
|||
0C422E7D293542EA00486D65 /* PremiumizeWrapper.swift */,
|
||||
0CA148D0288903F000DE2211 /* RealDebridWrapper.swift */,
|
||||
0C6771F329B3B4FD005D38D2 /* KodiWrapper.swift */,
|
||||
0C890E482C188808003B17B5 /* TorBoxWrapper.swift */,
|
||||
0C07C5FF2C19FEBF00808A46 /* OffCloudWrapper.swift */,
|
||||
);
|
||||
path = API;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -829,9 +844,11 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0C07C6042C1A859B00808A46 /* FormDataBody.swift in Sources */,
|
||||
0C7ED14328D65518009E29AD /* FileManager.swift in Sources */,
|
||||
0C03EB71296F619900162E9A /* PluginList+CoreDataClass.swift in Sources */,
|
||||
0C6771FA29B3D1AE005D38D2 /* KodiModels.swift in Sources */,
|
||||
0C07C6002C19FEBF00808A46 /* OffCloudWrapper.swift in Sources */,
|
||||
0C0D50E5288DFE7F0035ECC8 /* SourceModels.swift in Sources */,
|
||||
0CB6516528C5A5D700DCA721 /* InlinedList.swift in Sources */,
|
||||
0C8DC35429CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift in Sources */,
|
||||
|
|
@ -927,6 +944,7 @@
|
|||
0C84FCE929E5ADEF00B0DFE4 /* FilterAmountLabelView.swift in Sources */,
|
||||
0C10848B28BD9A38008F0BA6 /* SettingsAppVersionView.swift in Sources */,
|
||||
0CA05457288EE58200850554 /* SettingsPluginListView.swift in Sources */,
|
||||
0C07C6022C1A016B00808A46 /* OffCloudModels.swift in Sources */,
|
||||
0C78041D28BFB3EA001E8CA3 /* String.swift in Sources */,
|
||||
0C31133C28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift in Sources */,
|
||||
0CBC76FF288DAAD00054BE44 /* NavigationViewModel.swift in Sources */,
|
||||
|
|
@ -940,6 +958,7 @@
|
|||
0C6771F429B3B4FD005D38D2 /* KodiWrapper.swift in Sources */,
|
||||
0C3E00D0296F4DB200ECECB2 /* ActionModels.swift in Sources */,
|
||||
0C44E2AD28D51C63007711AE /* BackupManager.swift in Sources */,
|
||||
0C890E4B2C188FA7003B17B5 /* TorBoxModels.swift in Sources */,
|
||||
0C7075E429D374C50093DB2D /* Color.swift in Sources */,
|
||||
0C8DC35229CE287E008A83AD /* PluginInfoView.swift in Sources */,
|
||||
0C422E80293542F300486D65 /* PremiumizeModels.swift in Sources */,
|
||||
|
|
@ -956,6 +975,7 @@
|
|||
0CE1C4182981E8D700418F20 /* Plugin.swift in Sources */,
|
||||
0CEC8AB2299B3B57007BFE8F /* LibraryPickerView.swift in Sources */,
|
||||
0C6771F629B3B602005D38D2 /* SettingsKodiView.swift in Sources */,
|
||||
0C890E492C188808003B17B5 /* TorBoxWrapper.swift in Sources */,
|
||||
0CF1ABDC2C0C04B2009F6C26 /* Debrid.swift in Sources */,
|
||||
0C84F4842895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift in Sources */,
|
||||
0C32FB572890D1F2002BD219 /* ListRowViews.swift in Sources */,
|
||||
|
|
@ -1035,6 +1055,7 @@
|
|||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_STRICT_CONCURRENCY = minimal;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -1090,6 +1111,7 @@
|
|||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_STRICT_CONCURRENCY = minimal;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
|||
// MARK: - Downloading
|
||||
|
||||
// Wrapper function to fetch a download link from the API
|
||||
func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
|
||||
func getRestrictedFile(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> (restrictedFile: DebridIAFile?, newIA: DebridIA?) {
|
||||
let selectedMagnetId: String
|
||||
|
||||
if let existingMagnet = cloudTorrents.first(where: { $0.hash == magnet.hash && $0.status == "Ready" }) {
|
||||
|
|
@ -229,10 +229,7 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
|||
selectedIndex: iaFile?.fileId ?? 0
|
||||
)
|
||||
|
||||
try await saveLink(link: lockedLink)
|
||||
let downloadUrl = try await unlockLink(lockedLink: lockedLink)
|
||||
|
||||
return downloadUrl
|
||||
return (lockedLink, nil)
|
||||
}
|
||||
|
||||
// Adds a magnet link to the user's AD account
|
||||
|
|
@ -262,7 +259,7 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func fetchMagnetStatus(magnetId: String, selectedIndex: Int?) async throws -> String {
|
||||
func fetchMagnetStatus(magnetId: String, selectedIndex: Int?) async throws -> DebridIAFile {
|
||||
let queryItems = [
|
||||
URLQueryItem(name: "id", value: magnetId)
|
||||
]
|
||||
|
|
@ -272,20 +269,21 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
|||
let rawResponse = try jsonDecoder.decode(ADResponse<MagnetStatusResponse>.self, from: data).data
|
||||
|
||||
// Better to fetch no link at all than the wrong link
|
||||
if let linkWrapper = rawResponse.magnets[safe: 0]?.links[safe: selectedIndex ?? -1] {
|
||||
return linkWrapper.link
|
||||
if let torrentFile = rawResponse.magnets[safe: 0]?.links[safe: selectedIndex ?? -1] {
|
||||
return DebridIAFile(fileId: 0, name: torrentFile.filename, streamUrlString: torrentFile.link)
|
||||
} else {
|
||||
throw DebridError.EmptyTorrents
|
||||
}
|
||||
}
|
||||
|
||||
func unlockLink(lockedLink: String) async throws -> String {
|
||||
// Known as unlockLink in AD's API
|
||||
func unrestrictFile(_ restrictedFile: DebridIAFile) async throws -> String {
|
||||
let queryItems = [
|
||||
URLQueryItem(name: "link", value: lockedLink)
|
||||
URLQueryItem(name: "link", value: restrictedFile.streamUrlString)
|
||||
]
|
||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/link/unlock", queryItems: queryItems))
|
||||
|
||||
let data = try await performRequest(request: &request, requestName: #function)
|
||||
let data = try await performRequest(request: &request, requestName: "unlockLink")
|
||||
let rawResponse = try jsonDecoder.decode(ADResponse<UnlockLinkResponse>.self, from: data).data
|
||||
|
||||
return rawResponse.link
|
||||
|
|
|
|||
|
|
@ -277,20 +277,27 @@ class Premiumize: OAuthDebridSource, ObservableObject {
|
|||
|
||||
// MARK: - Downloading
|
||||
|
||||
// Wrapper function to fetch a DDL link from the API
|
||||
func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
|
||||
func getRestrictedFile(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> (restrictedFile: DebridIAFile?, newIA: DebridIA?) {
|
||||
// Store the item in PM cloud for later use
|
||||
try await createTransfer(magnet: magnet)
|
||||
|
||||
if let iaFile, let streamUrlString = iaFile.streamUrlString {
|
||||
return streamUrlString
|
||||
} else if let premiumizeItem = ia, let firstFile = premiumizeItem.files[safe: 0], let streamUrlString = firstFile.streamUrlString {
|
||||
return streamUrlString
|
||||
if let iaFile {
|
||||
return (iaFile, nil)
|
||||
} else if let premiumizeItem = ia, let firstFile = premiumizeItem.files[safe: 0] {
|
||||
return (firstFile, nil)
|
||||
} else {
|
||||
throw DebridError.FailedRequest(description: "Could not fetch your file from the Premiumize API")
|
||||
}
|
||||
}
|
||||
|
||||
func unrestrictFile(_ restrictedFile: DebridIAFile) async throws -> String {
|
||||
guard let streamUrlString = restrictedFile.streamUrlString else {
|
||||
throw DebridError.FailedRequest(description: "Could not get a streaming URL from the Premiumize API")
|
||||
}
|
||||
|
||||
return streamUrlString
|
||||
}
|
||||
|
||||
private func createTransfer(magnet: Magnet) async throws {
|
||||
guard let magnetLink = magnet.link else {
|
||||
throw DebridError.FailedRequest(description: "The magnet link is invalid")
|
||||
|
|
|
|||
|
|
@ -273,62 +273,50 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
|||
continue
|
||||
}
|
||||
|
||||
// Is this a batch?
|
||||
if data.rd.count > 1 || data.rd[0].count > 1 {
|
||||
// Batch array
|
||||
let batches = data.rd.map { fileDict in
|
||||
let batchFiles: [RealDebrid.IABatchFile] = fileDict.map { key, value in
|
||||
// Force unwrapped ID. Is safe because ID is guaranteed on a successful response
|
||||
RealDebrid.IABatchFile(id: Int(key)!, fileName: value.filename)
|
||||
}.sorted(by: { $0.id < $1.id })
|
||||
// Handle files array
|
||||
let batches = data.rd.map { fileDict in
|
||||
let batchFiles: [RealDebrid.IABatchFile] = fileDict.map { key, value in
|
||||
// Force unwrapped ID. Is safe because ID is guaranteed on a successful response
|
||||
RealDebrid.IABatchFile(id: Int(key)!, fileName: value.filename)
|
||||
}.sorted(by: { $0.id < $1.id })
|
||||
|
||||
return RealDebrid.IABatch(files: batchFiles)
|
||||
}
|
||||
return RealDebrid.IABatch(files: batchFiles)
|
||||
}
|
||||
|
||||
var files: [DebridIAFile] = []
|
||||
var files: [DebridIAFile] = []
|
||||
|
||||
for batch in batches {
|
||||
let batchFileIds = batch.files.map(\.id)
|
||||
for batch in batches {
|
||||
let batchFileIds = batch.files.map(\.id)
|
||||
|
||||
for batchFile in batch.files {
|
||||
if !files.contains(where: { $0.fileId == batchFile.id }) {
|
||||
files.append(
|
||||
DebridIAFile(
|
||||
fileId: batchFile.id,
|
||||
name: batchFile.fileName,
|
||||
batchIds: batchFileIds
|
||||
)
|
||||
for batchFile in batch.files {
|
||||
if !files.contains(where: { $0.fileId == batchFile.id }) {
|
||||
files.append(
|
||||
DebridIAFile(
|
||||
fileId: batchFile.id,
|
||||
name: batchFile.fileName,
|
||||
batchIds: batchFileIds
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TTL: 5 minutes
|
||||
IAValues.append(
|
||||
DebridIA(
|
||||
magnet: Magnet(hash: hash, link: nil),
|
||||
source: id,
|
||||
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
|
||||
files: files
|
||||
)
|
||||
)
|
||||
} else {
|
||||
IAValues.append(
|
||||
DebridIA(
|
||||
magnet: Magnet(hash: hash, link: nil),
|
||||
source: id,
|
||||
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
|
||||
files: []
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// TTL: 5 minutes
|
||||
IAValues.append(
|
||||
DebridIA(
|
||||
magnet: Magnet(hash: hash, link: nil),
|
||||
source: id,
|
||||
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
|
||||
files: files
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Downloading
|
||||
|
||||
// Wrapper function to fetch a download link from the API
|
||||
func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String {
|
||||
func getRestrictedFile(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> (restrictedFile: DebridIAFile?, newIA: DebridIA?) {
|
||||
var selectedMagnetId = ""
|
||||
|
||||
do {
|
||||
|
|
@ -342,13 +330,12 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
|||
}
|
||||
|
||||
// RealDebrid has 1 as the first ID for a file
|
||||
let torrentLink = try await torrentInfo(
|
||||
let torrentFile = try await torrentInfo(
|
||||
debridID: selectedMagnetId,
|
||||
selectedFileId: iaFile?.fileId ?? 1
|
||||
)
|
||||
let downloadLink = try await unrestrictLink(debridDownloadLink: torrentLink)
|
||||
|
||||
return downloadLink
|
||||
return (torrentFile, nil)
|
||||
} catch {
|
||||
if case DebridError.EmptyTorrents = error, !selectedMagnetId.isEmpty {
|
||||
try? await deleteTorrent(torrentId: selectedMagnetId)
|
||||
|
|
@ -401,7 +388,7 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
|||
}
|
||||
|
||||
// Gets the info of a torrent from a given ID
|
||||
func torrentInfo(debridID: String, selectedFileId: Int?) async throws -> String {
|
||||
func torrentInfo(debridID: String, selectedFileId: Int?) async throws -> DebridIAFile {
|
||||
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/info/\(debridID)")!)
|
||||
|
||||
let data = try await performRequest(request: &request, requestName: #function)
|
||||
|
|
@ -411,7 +398,11 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
|||
|
||||
// Let the user know if a torrent is downloading
|
||||
if let torrentLink = rawResponse.links[safe: linkIndex ?? -1], rawResponse.status == "downloaded" {
|
||||
return torrentLink
|
||||
return DebridIAFile(
|
||||
fileId: 0,
|
||||
name: rawResponse.filename,
|
||||
streamUrlString: torrentLink
|
||||
)
|
||||
} else if rawResponse.status == "downloading" || rawResponse.status == "queued" {
|
||||
throw DebridError.IsCaching
|
||||
} else {
|
||||
|
|
@ -420,13 +411,13 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
|||
}
|
||||
|
||||
// Downloads link from selectFiles for playback
|
||||
func unrestrictLink(debridDownloadLink: String) async throws -> String {
|
||||
func unrestrictFile(_ restrictedFile: DebridIAFile) async throws -> String {
|
||||
var request = URLRequest(url: URL(string: "\(baseApiUrl)/unrestrict/link")!)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
var bodyComponents = URLComponents()
|
||||
bodyComponents.queryItems = [URLQueryItem(name: "link", value: debridDownloadLink)]
|
||||
bodyComponents.queryItems = [URLQueryItem(name: "link", value: restrictedFile.streamUrlString)]
|
||||
|
||||
request.httpBody = bodyComponents.query?.data(using: .utf8)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,10 @@ protocol DebridSource: AnyObservableObject {
|
|||
// Fetches a download link from a source
|
||||
// Include the instant availability information with the args
|
||||
// Torrents also checked here
|
||||
func getDownloadLink(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> String
|
||||
func getRestrictedFile(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> (restrictedFile: DebridIAFile?, newIA: DebridIA?)
|
||||
|
||||
// Unrestricts a locked file
|
||||
func unrestrictFile(_ restrictedFile: DebridIAFile) async throws -> String
|
||||
|
||||
// User downloads functions
|
||||
func getUserDownloads() async throws
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ class DebridManager: ObservableObject {
|
|||
|
||||
var selectedDebridItem: DebridIA?
|
||||
var selectedDebridFile: DebridIAFile?
|
||||
var requiresUnrestrict: Bool = false
|
||||
|
||||
// TODO: Figure out a way to remove this var
|
||||
private var selectedOAuthDebridSource: OAuthDebridSource?
|
||||
|
|
@ -167,6 +168,11 @@ class DebridManager: ObservableObject {
|
|||
|
||||
if let IAItem = selectedSource.IAValues.first(where: { magnetHash == $0.magnet.hash }) {
|
||||
selectedDebridItem = IAItem
|
||||
|
||||
if IAItem.files.count == 1 {
|
||||
selectedDebridFile = IAItem.files[safe: 0]
|
||||
}
|
||||
|
||||
return true
|
||||
} else {
|
||||
logManager?.error("DebridManager: Could not find the associated \(selectedSource.id) entry for magnet hash \(magnetHash)")
|
||||
|
|
@ -311,8 +317,8 @@ class DebridManager: ObservableObject {
|
|||
// Cloudinfo is used for any extra information provided by debrid cloud
|
||||
func fetchDebridDownload(magnet: Magnet?, cloudInfo: String? = nil) async {
|
||||
defer {
|
||||
currentDebridTask = nil
|
||||
logManager?.hideIndeterminateToast()
|
||||
currentDebridTask = nil
|
||||
}
|
||||
|
||||
logManager?.updateIndeterminateToast("Loading content", cancelAction: {
|
||||
|
|
@ -331,12 +337,24 @@ class DebridManager: ObservableObject {
|
|||
}
|
||||
|
||||
if let magnet {
|
||||
let downloadLink = try await debridSource.getDownloadLink(
|
||||
let (restrictedFile, newIA) = try await debridSource.getRestrictedFile(
|
||||
magnet: magnet, ia: selectedDebridItem, iaFile: selectedDebridFile
|
||||
)
|
||||
|
||||
// Indicate that a link needs to be selected (batch)
|
||||
if let newIA {
|
||||
selectedDebridItem = newIA
|
||||
requiresUnrestrict = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
guard let restrictedFile else {
|
||||
throw DebridError.FailedRequest(description: "No files found for your request")
|
||||
}
|
||||
|
||||
// Update the UI
|
||||
downloadUrl = downloadLink
|
||||
downloadUrl = try await debridSource.unrestrictFile(restrictedFile)
|
||||
} else {
|
||||
throw DebridError.FailedRequest(description: "Could not fetch your file from \(debridSource.id)'s cache or API")
|
||||
}
|
||||
|
|
@ -350,8 +368,34 @@ class DebridManager: ObservableObject {
|
|||
default:
|
||||
await sendDebridError(error, prefix: "\(debridSource.id) download error", cancelString: "Download cancelled")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func unrestrictDownload() async {
|
||||
defer {
|
||||
logManager?.hideIndeterminateToast()
|
||||
requiresUnrestrict = false
|
||||
}
|
||||
|
||||
logManager?.updateIndeterminateToast("Loading content", cancelAction: {
|
||||
self.currentDebridTask?.cancel()
|
||||
self.currentDebridTask = nil
|
||||
})
|
||||
|
||||
guard let debridFile = selectedDebridFile, let debridSource = selectedDebridSource else {
|
||||
logManager?.error("DebridManager: Could not unrestrict the selected debrid file.")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let downloadLink = try await debridSource.unrestrictFile(debridFile)
|
||||
|
||||
downloadUrl = downloadLink
|
||||
} catch {
|
||||
await sendDebridError(error, prefix: "\(debridSource.id) unrestrict error", cancelString: "Unrestrict cancelled")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,14 @@ struct CloudTorrentView: View {
|
|||
if cloudTorrent.links.count == 1 {
|
||||
await debridManager.fetchDebridDownload(magnet: magnet)
|
||||
|
||||
// Bump to batch
|
||||
if debridManager.requiresUnrestrict {
|
||||
navModel.selectedHistoryInfo = historyInfo
|
||||
navModel.currentChoiceSheet = .batch
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !debridManager.downloadUrl.isEmpty {
|
||||
historyInfo.url = debridManager.downloadUrl
|
||||
PersistenceController.shared.createHistory(historyInfo, performSave: true)
|
||||
|
|
|
|||
|
|
@ -32,17 +32,23 @@ struct SearchResultButtonView: View {
|
|||
case .full:
|
||||
if debridManager.selectDebridResult(magnet: result.magnet) {
|
||||
debridManager.currentDebridTask = Task {
|
||||
let historyEntry = HistoryEntryJson(
|
||||
name: result.title,
|
||||
url: debridManager.downloadUrl,
|
||||
source: result.source
|
||||
)
|
||||
await debridManager.fetchDebridDownload(magnet: result.magnet)
|
||||
|
||||
// Bump to batch
|
||||
if debridManager.requiresUnrestrict {
|
||||
navModel.selectedHistoryInfo = historyEntry
|
||||
navModel.currentChoiceSheet = .batch
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !debridManager.downloadUrl.isEmpty {
|
||||
PersistenceController.shared.createHistory(
|
||||
HistoryEntryJson(
|
||||
name: result.title,
|
||||
url: debridManager.downloadUrl,
|
||||
source: result.source
|
||||
),
|
||||
performSave: true
|
||||
)
|
||||
PersistenceController.shared.createHistory(historyEntry, performSave: true)
|
||||
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: debridManager.downloadUrl,
|
||||
|
|
@ -133,9 +139,9 @@ struct SearchResultButtonView: View {
|
|||
Button("Cancel", role: .cancel) {}
|
||||
} message: {
|
||||
Text(
|
||||
"\(String(describing: debridManager.selectedDebridSource?.id)) is currently caching this file. " +
|
||||
"\(debridManager.selectedDebridSource?.id ?? "Unknown Debrid") is currently caching this file. " +
|
||||
"Would you like to delete it? \n\n" +
|
||||
"Progress can be checked on the RealDebrid website."
|
||||
"Progress can be checked on the \(debridManager.selectedDebridSource?.id ?? "Unknown Debrid") website."
|
||||
)
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .didDeleteBookmark)) { notification in
|
||||
|
|
|
|||
|
|
@ -60,7 +60,11 @@ struct BatchChoiceView: View {
|
|||
// Common function to communicate betwen VMs and queue/display a download
|
||||
func queueCommonDownload(fileName: String) {
|
||||
debridManager.currentDebridTask = Task {
|
||||
await debridManager.fetchDebridDownload(magnet: navModel.selectedMagnet)
|
||||
if debridManager.requiresUnrestrict {
|
||||
await debridManager.unrestrictDownload()
|
||||
} else {
|
||||
await debridManager.fetchDebridDownload(magnet: navModel.selectedMagnet)
|
||||
}
|
||||
|
||||
if !debridManager.downloadUrl.isEmpty {
|
||||
try? await Task.sleep(seconds: 1)
|
||||
|
|
|
|||
Loading…
Reference in a new issue