mirror of
https://github.com/Ferrite-iOS/Ferrite.git
synced 2026-01-11 20:10:27 +00:00
RealDebrid: Add batch torrent support
Batch torrents are torrents that have multiple files bundled within one torrent file. RealDebrid does support these, but it is difficult to get them to work. The main flow requires setting a specific combination in RealDebrid to allow for link generation. However, this is not intuitive to users and is bad API design on RealDebrid's part. Ferrite's implementation presents users with all the possible files from batches (duplicates deleted) and selects the user-chosen file to download. That way, only the user chosen file is presented to play on an external video player. This still needs work for optimization purposes, but this commit does produce a working build. Signed-off-by: kingbri <bdashore3@gmail.com>
This commit is contained in:
parent
47f713744b
commit
e9670ea118
11 changed files with 265 additions and 50 deletions
|
|
@ -32,6 +32,8 @@
|
|||
0CA148EB288903F000DE2211 /* SearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148D3288903F000DE2211 /* SearchResultsView.swift */; };
|
||||
0CA148EC288903F000DE2211 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148D4288903F000DE2211 /* ContentView.swift */; };
|
||||
0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 0CAF1C7A286F5C8600296F86 /* SwiftSoup */; };
|
||||
0CBC76FD288D914F0054BE44 /* BatchChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBC76FC288D914F0054BE44 /* BatchChoiceView.swift */; };
|
||||
0CBC76FF288DAAD00054BE44 /* NavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBC76FE288DAAD00054BE44 /* NavigationViewModel.swift */; };
|
||||
0CFEFCFD288A006200B3F490 /* GroupBoxStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFEFCFC288A006200B3F490 /* GroupBoxStyle.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
|
@ -43,7 +45,7 @@
|
|||
0CA148C1288903F000DE2211 /* NavView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavView.swift; sourceTree = "<group>"; };
|
||||
0CA148C2288903F000DE2211 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
0CA148C3288903F000DE2211 /* ScrapingViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrapingViewModel.swift; sourceTree = "<group>"; };
|
||||
0CA148C4288903F000DE2211 /* RealDebridModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealDebridModels.swift; sourceTree = "<group>"; };
|
||||
0CA148C4288903F000DE2211 /* RealDebridModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 3; lastKnownFileType = sourcecode.swift; path = RealDebridModels.swift; sourceTree = "<group>"; };
|
||||
0CA148C6288903F000DE2211 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
0CA148C7288903F000DE2211 /* FerriteApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FerriteApp.swift; sourceTree = "<group>"; };
|
||||
0CA148C9288903F000DE2211 /* Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -58,6 +60,8 @@
|
|||
0CA148D3288903F000DE2211 /* SearchResultsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchResultsView.swift; sourceTree = "<group>"; };
|
||||
0CA148D4288903F000DE2211 /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
0CAF1C68286F5C0E00296F86 /* Ferrite.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ferrite.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0CBC76FC288D914F0054BE44 /* BatchChoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchChoiceView.swift; sourceTree = "<group>"; };
|
||||
0CBC76FE288DAAD00054BE44 /* NavigationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewModel.swift; sourceTree = "<group>"; };
|
||||
0CFEFCFC288A006200B3F490 /* GroupBoxStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupBoxStyle.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
|
@ -128,6 +132,7 @@
|
|||
0CA148BB288903F000DE2211 /* SettingsView.swift */,
|
||||
0CA148BE288903F000DE2211 /* CardView.swift */,
|
||||
0CA148BC288903F000DE2211 /* LoginWebView.swift */,
|
||||
0CBC76FC288D914F0054BE44 /* BatchChoiceView.swift */,
|
||||
0CA148BD288903F000DE2211 /* MagnetChoiceView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
|
|
@ -139,6 +144,7 @@
|
|||
0CA148CD288903F000DE2211 /* DebridManager.swift */,
|
||||
0CA148C3288903F000DE2211 /* ScrapingViewModel.swift */,
|
||||
0CA148CF288903F000DE2211 /* ToastViewModel.swift */,
|
||||
0CBC76FE288DAAD00054BE44 /* NavigationViewModel.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -261,6 +267,7 @@
|
|||
files = (
|
||||
0CA148DB288903F000DE2211 /* NavView.swift in Sources */,
|
||||
0CA148E9288903F000DE2211 /* MainView.swift in Sources */,
|
||||
0CBC76FD288D914F0054BE44 /* BatchChoiceView.swift in Sources */,
|
||||
0CA148EC288903F000DE2211 /* ContentView.swift in Sources */,
|
||||
0CA148E1288903F000DE2211 /* Collection.swift in Sources */,
|
||||
0CA148E4288903F000DE2211 /* Keychain.swift in Sources */,
|
||||
|
|
@ -272,6 +279,7 @@
|
|||
0CA148E6288903F000DE2211 /* WebView.swift in Sources */,
|
||||
0CA148E2288903F000DE2211 /* Data.swift in Sources */,
|
||||
0CA148DE288903F000DE2211 /* RealDebridModels.swift in Sources */,
|
||||
0CBC76FF288DAAD00054BE44 /* NavigationViewModel.swift in Sources */,
|
||||
0CA148E8288903F000DE2211 /* RealDebridWrapper.swift in Sources */,
|
||||
0CA148D6288903F000DE2211 /* SettingsView.swift in Sources */,
|
||||
0CA148E5288903F000DE2211 /* DebridManager.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -51,10 +51,10 @@ public struct TokenResponse: Codable {
|
|||
// MARK: - instantAvailability endpoint
|
||||
|
||||
// Thanks Skitty!
|
||||
struct InstantAvailabilityResponse: Codable {
|
||||
public struct InstantAvailabilityResponse: Codable {
|
||||
var data: InstantAvailabilityData?
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
|
||||
if let data = try? container.decode(InstantAvailabilityData.self) {
|
||||
|
|
@ -72,6 +72,34 @@ struct InstantAvailabilityInfo: Codable {
|
|||
var filesize: Int
|
||||
}
|
||||
|
||||
// MARK: - Instant Availability client side structures
|
||||
public struct RealDebridIA: Codable, Hashable {
|
||||
let hash: String
|
||||
var files: [RealDebridIAFile] = []
|
||||
var batches: [RealDebridIABatch] = []
|
||||
}
|
||||
|
||||
public struct RealDebridIABatch: Codable, Hashable {
|
||||
let files: [RealDebridIABatchFile]
|
||||
}
|
||||
|
||||
public struct RealDebridIABatchFile: Codable, Hashable {
|
||||
let id: Int
|
||||
let fileName: String
|
||||
}
|
||||
|
||||
public struct RealDebridIAFile: Codable, Hashable {
|
||||
let name: String
|
||||
let batchIndex: Int
|
||||
let batchFileIndex: Int
|
||||
}
|
||||
|
||||
public enum RealDebridIAStatus: Codable, Hashable {
|
||||
case full
|
||||
case partial
|
||||
case none
|
||||
}
|
||||
|
||||
// MARK: - addMagnet endpoint
|
||||
public struct AddMagnetResponse: Codable {
|
||||
let id: String
|
||||
|
|
|
|||
|
|
@ -218,8 +218,8 @@ public class RealDebrid: ObservableObject {
|
|||
|
||||
// Checks if the magnet is streamable on RD
|
||||
// Currently does not work for batch links
|
||||
public func instantAvailability(magnetHashes: [String]) async throws -> [String] {
|
||||
var availableHashes: [String] = []
|
||||
public func instantAvailability(magnetHashes: [String]) async throws -> [RealDebridIA] {
|
||||
var availableHashes: [RealDebridIA] = []
|
||||
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/instantAvailability/\(magnetHashes.joined(separator: "/"))")!)
|
||||
|
||||
let data = try await performRequest(request: &request, requestName: #function)
|
||||
|
|
@ -232,9 +232,47 @@ public class RealDebrid: ObservableObject {
|
|||
continue
|
||||
}
|
||||
|
||||
// Do not include if a hash is a batch
|
||||
if !(data.rd.count > 1), !(data.rd[safe: 0]?.keys.count ?? 0 > 1) {
|
||||
availableHashes.append(hash)
|
||||
if data.rd.isEmpty {
|
||||
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: [RealDebridIABatchFile] = fileDict.map { (key, value) in
|
||||
// Force unwrapped ID. Is safe because ID is guaranteed on a successful response
|
||||
return RealDebridIABatchFile(id: Int(key)!, fileName: value.filename)
|
||||
}.sorted(by: { $0.id < $1.id })
|
||||
|
||||
return RealDebridIABatch(files: batchFiles)
|
||||
}
|
||||
|
||||
// RD files array
|
||||
// Possibly sort this in the future, but not sure how at the moment
|
||||
var files: [RealDebridIAFile] = []
|
||||
|
||||
for index in batches.indices {
|
||||
let batchFiles = batches[index].files
|
||||
|
||||
for batchFileIndex in batchFiles.indices {
|
||||
let batchFile = batchFiles[batchFileIndex]
|
||||
|
||||
if !files.contains(where: { $0.name == batchFile.fileName }) {
|
||||
files.append(
|
||||
RealDebridIAFile(
|
||||
name: batchFile.fileName,
|
||||
batchIndex: index,
|
||||
batchFileIndex: batchFileIndex
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
availableHashes.append(RealDebridIA(hash: hash, files: files, batches: batches))
|
||||
} else {
|
||||
availableHashes.append(RealDebridIA(hash: hash))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -259,13 +297,19 @@ public class RealDebrid: ObservableObject {
|
|||
}
|
||||
|
||||
// Queues the magnet link for downloading
|
||||
public func selectFiles(debridID: String) async throws {
|
||||
public func selectFiles(debridID: String, fileIds: [Int]) async throws {
|
||||
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/selectFiles/\(debridID)")!)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
var bodyComponents = URLComponents()
|
||||
bodyComponents.queryItems = [URLQueryItem(name: "files", value: "all")]
|
||||
|
||||
if fileIds.isEmpty {
|
||||
bodyComponents.queryItems = [URLQueryItem(name: "files", value: "all")]
|
||||
} else {
|
||||
let joinedIds = fileIds.map(String.init).joined(separator: ",")
|
||||
bodyComponents.queryItems = [URLQueryItem(name: "files", value: joinedIds)]
|
||||
}
|
||||
|
||||
request.httpBody = bodyComponents.query?.data(using: .utf8)
|
||||
|
||||
|
|
@ -273,13 +317,14 @@ public class RealDebrid: ObservableObject {
|
|||
}
|
||||
|
||||
// Fetches the info of a torrent
|
||||
public func torrentInfo(debridID: String) async throws -> String {
|
||||
public func torrentInfo(debridID: String, selectedIndex: Int?) async throws -> String {
|
||||
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/info/\(debridID)")!)
|
||||
|
||||
let data = try await performRequest(request: &request, requestName: #function)
|
||||
let rawResponse = try jsonDecoder.decode(TorrentInfoResponse.self, from: data)
|
||||
|
||||
if let torrentLink = rawResponse.links[safe: 0] {
|
||||
// Error out if no index is provided
|
||||
if let torrentLink = rawResponse.links[safe: selectedIndex ?? -1] {
|
||||
return torrentLink
|
||||
} else {
|
||||
throw RealDebridError.EmptyData
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ struct FerriteApp: App {
|
|||
@StateObject var scrapingModel: ScrapingViewModel = .init()
|
||||
@StateObject var toastModel: ToastViewModel = .init()
|
||||
@StateObject var debridManager: DebridManager = .init()
|
||||
@StateObject var navigationModel: NavigationViewModel = .init()
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
|
|
@ -23,6 +24,7 @@ struct FerriteApp: App {
|
|||
.environmentObject(debridManager)
|
||||
.environmentObject(scrapingModel)
|
||||
.environmentObject(toastModel)
|
||||
.environmentObject(navigationModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ public class DebridManager: ObservableObject {
|
|||
|
||||
@AppStorage("RealDebrid.Enabled") var realDebridEnabled = false
|
||||
|
||||
@Published var realDebridHashes: [String] = []
|
||||
@Published var realDebridHashes: [RealDebridIA] = []
|
||||
@Published var realDebridAuthUrl: String = ""
|
||||
@Published var realDebridDownloadUrl: String = ""
|
||||
@Published var selectedRealDebridItem: RealDebridIA?
|
||||
@Published var selectedRealDebridFile: RealDebridIAFile?
|
||||
|
||||
init() {
|
||||
realDebrid.parentManager = self
|
||||
|
|
@ -50,6 +52,38 @@ public class DebridManager: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
public func matchSearchResult(result: SearchResult?) -> RealDebridIAStatus {
|
||||
guard let result = result else {
|
||||
return .none
|
||||
}
|
||||
|
||||
guard let debridMatch = realDebridHashes.first(where: { result.magnetHash == $0.hash }) else {
|
||||
return .none
|
||||
}
|
||||
|
||||
if debridMatch.batches.isEmpty {
|
||||
return .full
|
||||
} else {
|
||||
return .partial
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public func setSelectedRdResult(result: SearchResult) -> Bool {
|
||||
guard let magnetHash = result.magnetHash else {
|
||||
toastModel?.toastDescription = "Could not find the torrent magnet hash"
|
||||
return false
|
||||
}
|
||||
|
||||
if let realDebridItem = realDebridHashes.first(where: { magnetHash == $0.hash }) {
|
||||
selectedRealDebridItem = realDebridItem
|
||||
return true
|
||||
} else {
|
||||
toastModel?.toastDescription = "Could not find the associated RealDebrid entry for magnet hash \(magnetHash)"
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func authenticateRd() async {
|
||||
do {
|
||||
let url = try await realDebrid.getVerificationInfo()
|
||||
|
|
@ -67,12 +101,23 @@ public class DebridManager: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
public func fetchRdDownload(searchResult: SearchResult) async {
|
||||
public func fetchRdDownload(searchResult: SearchResult, iaFile: RealDebridIAFile? = nil) async {
|
||||
do {
|
||||
let realDebridId = try await realDebrid.addMagnet(magnetLink: searchResult.magnetLink)
|
||||
try await realDebrid.selectFiles(debridID: realDebridId)
|
||||
|
||||
let torrentLink = try await realDebrid.torrentInfo(debridID: realDebridId)
|
||||
var fileIds: [Int] = []
|
||||
|
||||
if let iaFile = iaFile {
|
||||
guard let iaBatchFromFile = selectedRealDebridItem?.batches[safe: iaFile.batchIndex] else {
|
||||
return
|
||||
}
|
||||
|
||||
fileIds = iaBatchFromFile.files.map({ $0.id })
|
||||
}
|
||||
|
||||
try await realDebrid.selectFiles(debridID: realDebridId, fileIds: fileIds)
|
||||
|
||||
let torrentLink = try await realDebrid.torrentInfo(debridID: realDebridId, selectedIndex: iaFile == nil ? 0 : iaFile?.batchFileIndex)
|
||||
let downloadLink = try await realDebrid.unrestrictLink(debridDownloadLink: torrentLink)
|
||||
|
||||
Task { @MainActor in
|
||||
|
|
|
|||
21
Ferrite/Models/NavigationViewModel.swift
Normal file
21
Ferrite/Models/NavigationViewModel.swift
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// NavigationViewModel.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 7/24/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
class NavigationViewModel: ObservableObject {
|
||||
enum ChoiceSheetType: Identifiable {
|
||||
var id: Int {
|
||||
hashValue
|
||||
}
|
||||
|
||||
case magnet
|
||||
case batch
|
||||
}
|
||||
|
||||
@Published var currentChoiceSheet: ChoiceSheetType?
|
||||
}
|
||||
|
|
@ -54,9 +54,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
@Published var searchResults: [SearchResult] = []
|
||||
@Published var debridHashes: [String] = []
|
||||
@Published var searchText: String = ""
|
||||
|
||||
@Published var realDebridAuthUrl: String = ""
|
||||
@Published var showWebView: Bool = false
|
||||
@Published var selectedSearchResult: SearchResult?
|
||||
|
||||
// Fetches the HTML body for the source website
|
||||
@MainActor
|
||||
|
|
|
|||
61
Ferrite/Views/BatchChoiceView.swift
Normal file
61
Ferrite/Views/BatchChoiceView.swift
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// BatchChoiceView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 7/24/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct BatchChoiceView: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
@EnvironmentObject var scrapingModel: ScrapingViewModel
|
||||
@EnvironmentObject var navigationModel: NavigationViewModel
|
||||
|
||||
var body: some View {
|
||||
NavView {
|
||||
List {
|
||||
// To present this sheet, an RD item had to be set, this force unwrap is therefore safe
|
||||
ForEach(debridManager.selectedRealDebridItem!.files, id: \.self) { file in
|
||||
Button(file.name) {
|
||||
debridManager.selectedRealDebridFile = file
|
||||
|
||||
if let searchResult = scrapingModel.selectedSearchResult {
|
||||
Task {
|
||||
await debridManager.fetchRdDownload(searchResult: searchResult, iaFile: file)
|
||||
|
||||
// The download may complete before this sheet dismisses
|
||||
try? await Task.sleep(seconds: 1)
|
||||
navigationModel.currentChoiceSheet = .magnet
|
||||
|
||||
debridManager.selectedRealDebridFile = nil
|
||||
debridManager.selectedRealDebridItem = nil
|
||||
}
|
||||
}
|
||||
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Select a file")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Done") {
|
||||
debridManager.selectedRealDebridItem = nil
|
||||
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BatchChoiceView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
BatchChoiceView()
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ struct ContentView: View {
|
|||
@EnvironmentObject var scrapingModel: ScrapingViewModel
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
|
||||
@AppStorage("RealDebrid.Enabled") var realDebridEnabled = false
|
||||
|
||||
var body: some View {
|
||||
NavView {
|
||||
VStack {
|
||||
|
|
@ -25,7 +27,10 @@ struct ContentView: View {
|
|||
}
|
||||
|
||||
await scrapingModel.scrapeWebsite(source: source, html: html)
|
||||
await debridManager.populateDebridHashes(scrapingModel.searchResults)
|
||||
|
||||
if realDebridEnabled {
|
||||
await debridManager.populateDebridHashes(scrapingModel.searchResults)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,19 +11,18 @@ import ActivityView
|
|||
struct MagnetChoiceView: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
@EnvironmentObject var scrapingModel: ScrapingViewModel
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
|
||||
@AppStorage("RealDebrid.Enabled") var realDebridEnabled = false
|
||||
|
||||
@Binding var selectedResult: SearchResult?
|
||||
|
||||
@State private var showActivityView = false
|
||||
@State private var activityItem: ActivityItem?
|
||||
|
||||
var body: some View {
|
||||
NavView {
|
||||
Form {
|
||||
if realDebridEnabled, debridManager.realDebridHashes.contains(selectedResult?.magnetHash ?? "") {
|
||||
if realDebridEnabled, debridManager.matchSearchResult(result: scrapingModel.selectedSearchResult) != .none {
|
||||
Section("Real Debrid options") {
|
||||
Button("Play on Outplayer") {
|
||||
guard let downloadUrl = URL(string: "outplayer://\(debridManager.realDebridDownloadUrl)") else {
|
||||
|
|
@ -66,11 +65,11 @@ struct MagnetChoiceView: View {
|
|||
|
||||
Section("Magnet options") {
|
||||
Button("Copy magnet") {
|
||||
UIPasteboard.general.string = selectedResult?.magnetLink
|
||||
UIPasteboard.general.string = scrapingModel.selectedSearchResult?.magnetLink
|
||||
}
|
||||
|
||||
Button("Share magnet") {
|
||||
if let result = selectedResult, let url = URL(string: result.magnetLink) {
|
||||
if let result = scrapingModel.selectedSearchResult, let url = URL(string: result.magnetLink) {
|
||||
activityItem = ActivityItem(items: url)
|
||||
showActivityView.toggle()
|
||||
}
|
||||
|
|
@ -83,6 +82,8 @@ struct MagnetChoiceView: View {
|
|||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Done") {
|
||||
debridManager.realDebridDownloadUrl = ""
|
||||
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
|
@ -93,16 +94,6 @@ struct MagnetChoiceView: View {
|
|||
|
||||
struct MagnetChoiceView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MagnetChoiceView(
|
||||
selectedResult:
|
||||
.constant(
|
||||
SearchResult(
|
||||
title: "",
|
||||
source: "",
|
||||
size: "",
|
||||
magnetLink: "",
|
||||
magnetHash: nil)
|
||||
)
|
||||
)
|
||||
MagnetChoiceView()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,36 +13,42 @@ struct SearchResultsView: View {
|
|||
|
||||
@EnvironmentObject var scrapingModel: ScrapingViewModel
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
@EnvironmentObject var navigationModel: NavigationViewModel
|
||||
|
||||
@AppStorage("RealDebrid.Enabled") var realDebridEnabled = false
|
||||
|
||||
@State var selectedResult: SearchResult?
|
||||
|
||||
@State private var showExternalSheet = false
|
||||
@State private var resultUsesRd = false
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
ForEach(scrapingModel.searchResults, id: \.self) { result in
|
||||
VStack(alignment: .leading) {
|
||||
Button {
|
||||
selectedResult = result
|
||||
scrapingModel.selectedSearchResult = result
|
||||
|
||||
if debridManager.realDebridHashes.contains(result.magnetHash ?? ""), realDebridEnabled {
|
||||
switch debridManager.matchSearchResult(result: result) {
|
||||
case .full:
|
||||
Task {
|
||||
await debridManager.fetchRdDownload(searchResult: result)
|
||||
showExternalSheet.toggle()
|
||||
navigationModel.currentChoiceSheet = .magnet
|
||||
}
|
||||
} else {
|
||||
showExternalSheet.toggle()
|
||||
case .partial:
|
||||
if debridManager.setSelectedRdResult(result: result) {
|
||||
navigationModel.currentChoiceSheet = .batch
|
||||
}
|
||||
case .none:
|
||||
navigationModel.currentChoiceSheet = .magnet
|
||||
}
|
||||
} label: {
|
||||
Text(result.title)
|
||||
.font(.callout)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.sheet(isPresented: $showExternalSheet) {
|
||||
MagnetChoiceView(selectedResult: $selectedResult)
|
||||
.sheet(item: $navigationModel.currentChoiceSheet) { item in
|
||||
switch item {
|
||||
case .magnet:
|
||||
MagnetChoiceView()
|
||||
case .batch:
|
||||
BatchChoiceView()
|
||||
}
|
||||
}
|
||||
.tint(colorScheme == .light ? .black : .white)
|
||||
.padding(.bottom, 5)
|
||||
|
|
@ -59,11 +65,16 @@ struct SearchResultsView: View {
|
|||
.fontWeight(.bold)
|
||||
.padding(2)
|
||||
.background {
|
||||
if debridManager.realDebridHashes.contains(result.magnetHash ?? "") {
|
||||
switch debridManager.matchSearchResult(result: result) {
|
||||
case .full:
|
||||
Color.green
|
||||
.cornerRadius(4)
|
||||
.opacity(0.5)
|
||||
} else {
|
||||
case .partial:
|
||||
Color.orange
|
||||
.cornerRadius(4)
|
||||
.opacity(0.5)
|
||||
case .none:
|
||||
Color.red
|
||||
.cornerRadius(4)
|
||||
.opacity(0.5)
|
||||
|
|
|
|||
Loading…
Reference in a new issue