Ferrite: Switch to a Magnet struct

Magnets are expressed in two different ways: a hash and a link. Both
of these mean the same thing with a magnet link giving more information
if required.

However, there was a disconnect if a hash was present or a link was
present and required many steps to check which was available. Unify
magnets by creating a parent structure that attempts to extract
the hash or create a link in the event that either parameter isn't
provided.

Replace everything except bookmarks (to prevent CoreData complaints
and unnecessary abstraction) to use the new Magnet system.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2023-01-04 12:49:20 -05:00
parent 9f54397b77
commit 5a4e98f10d
23 changed files with 175 additions and 179 deletions

View file

@ -125,7 +125,11 @@ public class AllDebrid {
}
// Adds a magnet link to the user's AD account
public func addMagnet(magnetLink: String) async throws -> Int {
public func addMagnet(magnet: Magnet) async throws -> Int {
guard let magnetLink = magnet.link else {
throw ADError.FailedRequest(description: "The magnet link is invalid")
}
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/upload"))
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
@ -192,7 +196,7 @@ public class AllDebrid {
}
return IA(
hash: magnetResp.hash,
magnet: Magnet(hash: magnetResp.hash, link: magnetResp.magnet),
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
files: files
)

View file

@ -148,6 +148,10 @@ public class Premiumize {
// Grabs DDL links
func fetchDDL(magnet: Magnet) async throws -> IA {
if magnet.hash == nil {
throw PMError.EmptyData
}
var request = URLRequest(url: URL(string: "\(baseApiUrl)/transfer/directdl")!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
@ -169,7 +173,7 @@ public class Premiumize {
}
return IA(
hash: magnet.hash,
magnet: magnet,
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
files: files
)
@ -178,7 +182,11 @@ public class Premiumize {
}
}
func createTransfer(magnetLink: String) async throws {
func createTransfer(magnet: Magnet) async throws {
guard let magnetLink = magnet.link else {
throw PMError.FailedRequest(description: "The magnet link is invalid")
}
var request = URLRequest(url: URL(string: "\(baseApiUrl)/transfer/create")!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

View file

@ -188,7 +188,7 @@ public class RealDebrid {
// Currently does not work for batch links
public func instantAvailability(magnets: [Magnet]) async throws -> [IA] {
var availableHashes: [RealDebrid.IA] = []
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/instantAvailability/\(magnets.map(\.hash).joined(separator: "/"))")!)
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/instantAvailability/\(magnets.compactMap(\.hash).joined(separator: "/"))")!)
let data = try await performRequest(request: &request, requestName: #function)
@ -241,7 +241,7 @@ public class RealDebrid {
// TTL: 5 minutes
availableHashes.append(
RealDebrid.IA(
hash: hash,
magnet: Magnet(hash: hash, link: nil),
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
files: files,
batches: batches
@ -250,7 +250,7 @@ public class RealDebrid {
} else {
availableHashes.append(
RealDebrid.IA(
hash: hash,
magnet: Magnet(hash: hash, link: nil),
expiryTimeStamp: Date().timeIntervalSince1970 + 300
)
)
@ -261,7 +261,11 @@ public class RealDebrid {
}
// Adds a magnet link to the user's RD account
public func addMagnet(magnetLink: String) async throws -> String {
public func addMagnet(magnet: Magnet) async throws -> String {
guard let magnetLink = magnet.link else {
throw RDError.FailedRequest(description: "The magnet link is invalid")
}
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/addMagnet")!)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

View file

@ -16,8 +16,7 @@ public class Bookmark: NSManagedObject {
title: title,
source: source,
size: size,
magnetLink: magnetLink,
magnetHash: magnetHash,
magnet: Magnet(hash: magnetHash, link: magnetLink),
seeders: seeders,
leechers: leechers
)

View file

@ -145,7 +145,7 @@ public extension AllDebrid {
// MARK: - InstantAvailablity client side structures
struct IA: Codable, Hashable {
let hash: String
let magnet: Magnet
let expiryTimeStamp: Double
var files: [IAFile]
}

View file

@ -8,6 +8,7 @@
import Foundation
public struct Backup: Codable {
let version: Int
var bookmarks: [BookmarkJson]?
var history: [HistoryJson]?
var sourceNames: [String]?
@ -16,7 +17,16 @@ public struct Backup: Codable {
// MARK: - CoreData translation
typealias BookmarkJson = SearchResult
// Don't typealias to search result as this is a reflection of CoreData's struct
struct BookmarkJson: Codable {
let title: String?
let source: String
let size: String?
let magnetLink: String?
let magnetHash: String?
let seeders: String?
let leechers: String?
}
// Date is an epoch timestamp
struct HistoryJson: Codable {

View file

@ -6,6 +6,7 @@
//
import Foundation
import Base32
// MARK: - Universal IA enum (IA = InstantAvailability)
@ -36,6 +37,63 @@ public enum DebridType: Int, Codable, Hashable, CaseIterable {
// Wrapper struct for magnet links to contain both the link and hash for easy access
public struct Magnet: Codable, Hashable, Sendable {
let link: String?
let hash: String
var hash: String?
var link: String?
init(hash: String?, link: String?, title: String? = nil, trackers: [String]? = nil) {
if let hash = hash, link == nil {
self.hash = parseHash(hash)
self.link = generateLink(hash: hash, title: title, trackers: trackers)
} else if let link = link, hash == nil {
self.link = link
self.hash = parseHash(extractHash(link: link))
} else {
self.hash = parseHash(hash)
self.link = link
}
}
func generateLink(hash: String, title: String?, trackers: [String]?) -> String {
var magnetLinkArray = ["magnet:?xt=urn:btih:", hash]
if let title, let encodedTitle = title.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) {
magnetLinkArray.append("&dn=\(encodedTitle)")
}
if let trackers {
for trackerUrl in trackers {
if URL(string: trackerUrl) != nil,
let encodedUrlString = trackerUrl.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
{
magnetLinkArray.append("&tr=\(encodedUrlString)")
}
}
}
return magnetLinkArray.joined()
}
func extractHash(link: String) -> String? {
if let firstSplit = link.split(separator: ":")[safe: 3],
let tempHash = firstSplit.split(separator: "&")[safe: 0]
{
return String(tempHash)
} else {
return nil
}
}
// Is this a Base32hex hash?
func parseHash(_ magnetHash: String?) -> String? {
guard let magnetHash else {
return nil
}
if magnetHash.count == 32 {
let decryptedMagnetHash = base32DecodeToData(String(magnetHash))
return decryptedMagnetHash?.hexEncodedString()
} else {
return String(magnetHash).lowercased()
}
}
}

View file

@ -56,7 +56,7 @@ public extension Premiumize {
// MARK: - InstantAvailability client side structures
struct IA: Codable, Hashable {
let hash: String
let magnet: Magnet
let expiryTimeStamp: Double
let files: [IAFile]
}

View file

@ -93,7 +93,7 @@ public extension RealDebrid {
// MARK: - Instant Availability client side structures
struct IA: Codable, Hashable, Sendable {
let hash: String
let magnet: Magnet
let expiryTimeStamp: Double
var files: [IAFile] = []
var batches: [IABatch] = []

View file

@ -11,8 +11,7 @@ public struct SearchResult: Codable, Hashable, Sendable {
let title: String?
let source: String
let size: String?
let magnetLink: String?
let magnetHash: String?
let magnet: Magnet
let seeders: String?
let leechers: String?
}

View file

@ -8,6 +8,9 @@
import Foundation
public class BackupManager: ObservableObject {
// Constant variable for backup versions
let latestBackupVersion: Int = 1
var toastModel: ToastViewModel?
@Published var showRestoreAlert = false
@ -18,7 +21,7 @@ public class BackupManager: ObservableObject {
@Published var selectedBackupUrl: URL?
func createBackup() {
var backup = Backup()
var backup = Backup(version: latestBackupVersion)
let backgroundContext = PersistenceController.shared.backgroundContext
let bookmarkRequest = Bookmark.fetchRequest()

View file

@ -148,21 +148,21 @@ public class DebridManager: ObservableObject {
// If a hash isn't found in the IA, update it
// If the hash is expired, remove it and update it
let sendMagnets = resultMagnets.filter { magnet in
if let IAIndex = realDebridIAValues.firstIndex(where: { $0.hash == magnet.hash }), enabledDebrids.contains(.realDebrid) {
if let IAIndex = realDebridIAValues.firstIndex(where: { $0.magnet.hash == magnet.hash }), enabledDebrids.contains(.realDebrid) {
if now.timeIntervalSince1970 > realDebridIAValues[IAIndex].expiryTimeStamp {
realDebridIAValues.remove(at: IAIndex)
return true
} else {
return false
}
} else if let IAIndex = allDebridIAValues.firstIndex(where: { $0.hash == magnet.hash }), enabledDebrids.contains(.allDebrid) {
} else if let IAIndex = allDebridIAValues.firstIndex(where: { $0.magnet.hash == magnet.hash }), enabledDebrids.contains(.allDebrid) {
if now.timeIntervalSince1970 > allDebridIAValues[IAIndex].expiryTimeStamp {
allDebridIAValues.remove(at: IAIndex)
return true
} else {
return false
}
} else if let IAIndex = premiumizeIAValues.firstIndex(where: { $0.hash == magnet.hash }), enabledDebrids.contains(.premiumize) {
} else if let IAIndex = premiumizeIAValues.firstIndex(where: { $0.magnet.hash == magnet.hash }), enabledDebrids.contains(.premiumize) {
if now.timeIntervalSince1970 > premiumizeIAValues[IAIndex].expiryTimeStamp {
premiumizeIAValues.remove(at: IAIndex)
return true
@ -189,7 +189,7 @@ public class DebridManager: ObservableObject {
// Only strip magnets that don't have an associated link for PM
let strippedResultMagnets: [Magnet] = resultMagnets.compactMap {
if let magnetLink = $0.link {
return Magnet(link: magnetLink, hash: $0.hash)
return Magnet(hash: $0.hash, link: magnetLink)
} else {
return nil
}
@ -217,14 +217,14 @@ public class DebridManager: ObservableObject {
}
// Common function to match a magnet hash with a provided debrid service
public func matchMagnetHash(_ magnetHash: String?) -> IAStatus {
guard let magnetHash else {
public func matchMagnetHash(_ magnet: Magnet) -> IAStatus {
guard let magnetHash = magnet.hash else {
return .none
}
switch selectedDebridType {
case .realDebrid:
guard let realDebridMatch = realDebridIAValues.first(where: { magnetHash == $0.hash }) else {
guard let realDebridMatch = realDebridIAValues.first(where: { magnetHash == $0.magnet.hash }) else {
return .none
}
@ -234,7 +234,7 @@ public class DebridManager: ObservableObject {
return .partial
}
case .allDebrid:
guard let allDebridMatch = allDebridIAValues.first(where: { magnetHash == $0.hash }) else {
guard let allDebridMatch = allDebridIAValues.first(where: { magnetHash == $0.magnet.hash }) else {
return .none
}
@ -244,7 +244,7 @@ public class DebridManager: ObservableObject {
return .full
}
case .premiumize:
guard let premiumizeMatch = premiumizeIAValues.first(where: { magnetHash == $0.hash }) else {
guard let premiumizeMatch = premiumizeIAValues.first(where: { magnetHash == $0.magnet.hash }) else {
return .none
}
@ -258,15 +258,15 @@ public class DebridManager: ObservableObject {
}
}
public func selectDebridResult(magnetHash: String?) -> Bool {
guard let magnetHash = magnetHash else {
public func selectDebridResult(magnet: Magnet) -> Bool {
guard let magnetHash = magnet.hash else {
toastModel?.updateToastDescription("Could not find the torrent magnet hash")
return false
}
switch selectedDebridType {
case .realDebrid:
if let realDebridItem = realDebridIAValues.first(where: { magnetHash == $0.hash }) {
if let realDebridItem = realDebridIAValues.first(where: { magnetHash == $0.magnet.hash }) {
selectedRealDebridItem = realDebridItem
return true
} else {
@ -274,7 +274,7 @@ public class DebridManager: ObservableObject {
return false
}
case .allDebrid:
if let allDebridItem = allDebridIAValues.first(where: { magnetHash == $0.hash }) {
if let allDebridItem = allDebridIAValues.first(where: { magnetHash == $0.magnet.hash }) {
selectedAllDebridItem = allDebridItem
return true
} else {
@ -282,7 +282,7 @@ public class DebridManager: ObservableObject {
return false
}
case .premiumize:
if let premiumizeItem = premiumizeIAValues.first(where: { magnetHash == $0.hash }) {
if let premiumizeItem = premiumizeIAValues.first(where: { magnetHash == $0.magnet.hash }) {
selectedPremiumizeItem = premiumizeItem
return true
} else {
@ -471,7 +471,7 @@ public class DebridManager: ObservableObject {
// MARK: - Debrid fetch UI linked functions
// Common function to delegate what debrid service to fetch from
public func fetchDebridDownload(magnetLink: String?) async {
public func fetchDebridDownload(magnet: Magnet?) async {
defer {
currentDebridTask = nil
showLoadingProgress = false
@ -481,9 +481,9 @@ public class DebridManager: ObservableObject {
switch selectedDebridType {
case .realDebrid:
await fetchRdDownload(magnetLink: magnetLink)
await fetchRdDownload(magnet: magnet)
case .allDebrid:
await fetchAdDownload(magnetLink: magnetLink)
await fetchAdDownload(magnet: magnet)
case .premiumize:
await fetchPmDownload()
case .none:
@ -491,22 +491,22 @@ public class DebridManager: ObservableObject {
}
}
func fetchRdDownload(magnetLink: String?) async {
func fetchRdDownload(magnet: Magnet?) async {
do {
// Bypass the TTL since a download needs to be queried
await fetchRdCloud(bypassTTL: true)
// If there's an existing torrent, check for a download link. Otherwise check for an unrestrict link
let existingTorrents = realDebridCloudTorrents.filter { $0.hash == selectedRealDebridItem?.hash && $0.status == "downloaded" }
let existingTorrents = realDebridCloudTorrents.filter { $0.hash == selectedRealDebridItem?.magnet.hash && $0.status == "downloaded" }
// If the links match from a user's downloads, no need to re-run a download
if let existingTorrent = existingTorrents[safe: 0],
let torrentLink = existingTorrent.links[safe: selectedRealDebridFile?.batchFileIndex ?? 0]
{
try await checkRdUserDownloads(userTorrentLink: torrentLink)
} else if let magnetLink = magnetLink {
} else if let magnet {
// Add a magnet after all the cache checks fail
selectedRealDebridID = try await realDebrid.addMagnet(magnetLink: magnetLink)
selectedRealDebridID = try await realDebrid.addMagnet(magnet: magnet)
var fileIds: [Int] = []
if let iaFile = selectedRealDebridFile {
@ -611,16 +611,16 @@ public class DebridManager: ObservableObject {
}
}
func fetchAdDownload(magnetLink: String?) async {
guard let magnetLink = magnetLink else {
toastModel?.updateToastDescription("Could not run your action because the magnet link is invalid.")
print("AllDebrid error: Invalid magnet link")
func fetchAdDownload(magnet: Magnet?) async {
guard let magnet else {
toastModel?.updateToastDescription("Could not run your action because the magnet is invalid.")
print("AllDebrid error: Invalid magnet")
return
}
do {
let magnetID = try await allDebrid.addMagnet(magnetLink: magnetLink)
let magnetID = try await allDebrid.addMagnet(magnet: magnet)
let lockedLink = try await allDebrid.fetchMagnetStatus(
magnetId: magnetID,
selectedIndex: selectedAllDebridFile?.id ?? 0

View file

@ -32,8 +32,7 @@ class NavigationViewModel: ObservableObject {
@Published var isEditingSearch: Bool = false
@Published var isSearching: Bool = false
@Published var selectedSearchResult: SearchResult?
@Published var selectedMagnetLink: String?
@Published var selectedMagnet: Magnet?
@Published var selectedHistoryInfo: HistoryEntryJson?
@Published var resultFromCloud: Bool = false
@ -96,16 +95,18 @@ class NavigationViewModel: ObservableObject {
}
}
public func runMagnetAction(magnetString: String?, _ action: DefaultMagnetActionType? = nil) {
let selectedAction = action ?? defaultMagnetAction
guard let magnetLink = magnetString else {
public func runMagnetAction(magnet: Magnet?, _ action: DefaultMagnetActionType? = nil) {
// Fall back to selected magnet if the provided magnet is nil
let magnet = magnet ?? selectedMagnet
guard let magnetLink = magnet?.link else {
toastModel?.updateToastDescription("Could not run your action because the magnet link is invalid.")
print("Magnet action error: The magnet link is invalid.")
return
}
let selectedAction = action ?? defaultMagnetAction
switch selectedAction {
case .none:
currentChoiceSheet = .magnet
@ -126,24 +127,4 @@ class NavigationViewModel: ObservableObject {
}
}
}
/*
public func addToHistory(name: String?, source: String?, url: String?, subName: String? = nil) {
let backgroundContext = PersistenceController.shared.backgroundContext
// The timeStamp and date are nil because the create function will make them automatically
PersistenceController.shared.createHistory(
entryJson: HistoryEntryJson(
name: name ?? "",
subName: subName,
url: url ?? "",
timeStamp: nil,
source: source
),
date: nil
)
PersistenceController.shared.save(backgroundContext)
}
*/
}

View file

@ -353,7 +353,7 @@ class ScrapingViewModel: ObservableObject {
source: source,
existingSearchResult: searchResult
),
let magnetLink = newSearchResult.magnetLink,
let magnetLink = newSearchResult.magnet.link,
magnetLink.starts(with: "magnet:"),
!tempResults.contains(newSearchResult)
{
@ -362,7 +362,7 @@ class ScrapingViewModel: ObservableObject {
}
} else if
let searchResult,
let magnetLink = searchResult.magnetLink,
let magnetLink = searchResult.magnet.link,
magnetLink.starts(with: "magnet:"),
!tempResults.contains(searchResult)
{
@ -374,18 +374,16 @@ class ScrapingViewModel: ObservableObject {
}
public func parseJsonResult(_ result: JSON, jsonParser: SourceJsonParser, source: Source, existingSearchResult: SearchResult? = nil) -> SearchResult? {
var magnetHash: String? = existingSearchResult?.magnetHash
var magnetHash: String? = existingSearchResult?.magnet.hash
if let magnetHashParser = jsonParser.magnetHash {
let rawHash = result[magnetHashParser.query.components(separatedBy: ".")].rawValue
if !(rawHash is NSNull) {
magnetHash = fetchMagnetHash(existingHash: String(describing: rawHash))
magnetHash = String(describing: rawHash)
}
}
var title: String? = existingSearchResult?.title
if let titleParser = jsonParser.title {
if let existingTitle = existingSearchResult?.title,
let discriminatorQuery = titleParser.discriminator
@ -401,21 +399,13 @@ class ScrapingViewModel: ObservableObject {
}
}
var link: String? = existingSearchResult?.magnetLink
if let magnetLinkParser = jsonParser.magnetLink, existingSearchResult?.magnetLink == nil {
var link: String? = existingSearchResult?.magnet.link
if let magnetLinkParser = jsonParser.magnetLink, link == nil {
let rawLink = result[magnetLinkParser.query.components(separatedBy: ".")].rawValue
link = rawLink is NSNull ? nil : String(describing: rawLink)
} else if let magnetHash {
link = generateMagnetLink(magnetHash: magnetHash, title: title, trackers: source.trackers)
}
if magnetHash == nil, let href = link {
magnetHash = fetchMagnetHash(magnetLink: href)
}
var size: String? = existingSearchResult?.size
if let sizeParser = jsonParser.size, existingSearchResult?.size == nil {
let rawSize = result[sizeParser.query.components(separatedBy: ".")].rawValue
size = rawSize is NSNull ? nil : String(describing: rawSize)
@ -444,8 +434,7 @@ class ScrapingViewModel: ObservableObject {
title: title,
source: source.name,
size: size,
magnetLink: link,
magnetHash: magnetHash,
magnet: Magnet(hash: magnetHash, link: link, title: title, trackers: source.trackers),
seeders: seeders,
leechers: leechers
)
@ -476,15 +465,13 @@ class ScrapingViewModel: ObservableObject {
// Parse magnet link or translate hash
var magnetHash: String?
if let magnetHashParser = rssParser.magnetHash {
let tempHash = try? runRssComplexQuery(
magnetHash = try? runRssComplexQuery(
item: item,
query: magnetHashParser.query,
attribute: magnetHashParser.attribute,
discriminator: magnetHashParser.discriminator,
regexString: magnetHashParser.regex
)
magnetHash = fetchMagnetHash(existingHash: tempHash)
}
var title: String?
@ -507,8 +494,6 @@ class ScrapingViewModel: ObservableObject {
discriminator: magnetLinkParser.discriminator,
regexString: magnetLinkParser.regex
)
} else if let magnetHash {
link = generateMagnetLink(magnetHash: magnetHash, title: title, trackers: source.trackers)
} else {
continue
}
@ -517,10 +502,6 @@ class ScrapingViewModel: ObservableObject {
continue
}
if magnetHash == nil {
magnetHash = fetchMagnetHash(magnetLink: href)
}
var size: String?
if let sizeParser = rssParser.size {
size = try? runRssComplexQuery(
@ -564,8 +545,7 @@ class ScrapingViewModel: ObservableObject {
title: title ?? "No title",
source: source.name,
size: size ?? "",
magnetLink: href,
magnetHash: magnetHash,
magnet: Magnet(hash: magnetHash, link: href, title: title, trackers: source.trackers),
seeders: seeders,
leechers: leechers
)
@ -673,9 +653,6 @@ class ScrapingViewModel: ObservableObject {
continue
}
// Fetches the magnet hash
let magnetHash = fetchMagnetHash(magnetLink: href)
// Fetches the episode/movie title
var title: String?
if let titleParser = htmlParser.title {
@ -743,8 +720,7 @@ class ScrapingViewModel: ObservableObject {
title: title ?? "No title",
source: source.name,
size: size ?? "",
magnetLink: href,
magnetHash: magnetHash,
magnet: Magnet(hash: nil, link: href),
seeders: seeders,
leechers: leechers
)
@ -786,31 +762,6 @@ class ScrapingViewModel: ObservableObject {
}
}
// Fetches and possibly converts the magnet hash value to sha1
public func fetchMagnetHash(magnetLink: String? = nil, existingHash: String? = nil) -> String? {
var magnetHash: String
if let existingHash {
magnetHash = existingHash
} else if
let magnetLink,
let firstSplit = magnetLink.split(separator: ":")[safe: 3],
let tempHash = firstSplit.split(separator: "&")[safe: 0]
{
magnetHash = String(tempHash)
} else {
return nil
}
// Is this a Base32hex hash?
if magnetHash.count == 32 {
let decryptedMagnetHash = base32DecodeToData(String(magnetHash))
return decryptedMagnetHash?.hexEncodedString()
} else {
return String(magnetHash).lowercased()
}
}
func parseSizeString(sizeString: String) -> String? {
// Test if the string can be a full integer
guard let size = Int(sizeString) else {
@ -833,28 +784,6 @@ class ScrapingViewModel: ObservableObject {
}
}
public func generateMagnetLink(magnetHash: String, title: String?, trackers: [String]?) -> String {
var magnetLinkArray = ["magnet:?xt=urn:btih:"]
magnetLinkArray.append(magnetHash)
if let title, let encodedTitle = title.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) {
magnetLinkArray.append("&dn=\(encodedTitle)")
}
if let trackers {
for trackerUrl in trackers {
if URL(string: trackerUrl) != nil,
let encodedUrlString = trackerUrl.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
{
magnetLinkArray.append("&tr=\(encodedUrlString)")
}
}
}
return magnetLinkArray.joined()
}
func cleanApiCreds(api: SourceApi) async {
let backgroundContext = PersistenceController.shared.backgroundContext

View file

@ -11,7 +11,7 @@ struct DebridLabelView: View {
@EnvironmentObject var debridManager: DebridManager
@State var cloudLinks: [String] = []
var magnetHash: String?
var magnet: Magnet?
var body: some View {
if let selectedDebridType = debridManager.selectedDebridType {
@ -20,8 +20,8 @@ struct DebridLabelView: View {
.padding(2)
.background {
Group {
if cloudLinks.isEmpty {
switch debridManager.matchMagnetHash(magnetHash) {
if let magnet, cloudLinks.isEmpty {
switch debridManager.matchMagnetHash(magnet) {
case .full:
Color.green
case .partial:

View file

@ -56,7 +56,7 @@ struct BookmarksView: View {
viewTask = Task {
let magnets = bookmarks.compactMap {
if let magnetHash = $0.magnetHash {
return Magnet(link: $0.magnetLink, hash: magnetHash)
return Magnet(hash: magnetHash, link: $0.magnetLink)
} else {
return nil
}

View file

@ -20,12 +20,12 @@ struct RealDebridCloudView: View {
Button(downloadResponse.filename) {
navModel.resultFromCloud = true
navModel.selectedTitle = downloadResponse.filename
debridManager.downloadUrl = downloadResponse.link
debridManager.downloadUrl = downloadResponse.download
PersistenceController.shared.createHistory(
HistoryEntryJson(
name: downloadResponse.filename,
url: downloadResponse.link,
url: downloadResponse.download,
source: DebridType.realDebrid.toString()
)
)
@ -73,9 +73,10 @@ struct RealDebridCloudView: View {
}
} else {
debridManager.clearIAValues()
await debridManager.populateDebridIA([Magnet(link: nil, hash: torrentResponse.hash)])
let magnet = Magnet(hash: torrentResponse.hash, link: nil)
await debridManager.populateDebridIA([magnet])
if debridManager.selectDebridResult(magnetHash: torrentResponse.hash) {
if debridManager.selectDebridResult(magnet: magnet) {
navModel.selectedHistoryInfo = historyInfo
navModel.currentChoiceSheet = .batch
}

View file

@ -30,7 +30,7 @@ struct HistoryButtonView: View {
}
}
} else {
navModel.runMagnetAction(magnetString: url)
navModel.runMagnetAction(magnet: Magnet(hash: nil, link: url))
}
} else {
toastModel.updateToastDescription("URL invalid. Cannot load this history entry. Please delete it.")

View file

@ -22,15 +22,15 @@ struct SearchResultButtonView: View {
var body: some View {
Button {
if debridManager.currentDebridTask == nil {
navModel.selectedSearchResult = result
navModel.selectedMagnet = result.magnet
navModel.selectedTitle = result.title ?? ""
navModel.resultFromCloud = false
switch debridManager.matchMagnetHash(result.magnetHash) {
switch debridManager.matchMagnetHash(result.magnet) {
case .full:
if debridManager.selectDebridResult(magnetHash: result.magnetHash) {
if debridManager.selectDebridResult(magnet: result.magnet) {
debridManager.currentDebridTask = Task {
await debridManager.fetchDebridDownload(magnetLink: result.magnetLink)
await debridManager.fetchDebridDownload(magnet: result.magnet)
if !debridManager.downloadUrl.isEmpty {
PersistenceController.shared.createHistory(
@ -50,19 +50,19 @@ struct SearchResultButtonView: View {
}
}
case .partial:
if debridManager.selectDebridResult(magnetHash: result.magnetHash) {
if debridManager.selectDebridResult(magnet: result.magnet) {
navModel.currentChoiceSheet = .batch
}
case .none:
PersistenceController.shared.createHistory(
HistoryEntryJson(
name: result.title,
url: result.magnetLink,
url: result.magnet.link,
source: result.source
)
)
navModel.runMagnetAction(magnetString: result.magnetLink)
navModel.runMagnetAction(magnet: result.magnet)
}
}
} label: {
@ -95,8 +95,8 @@ struct SearchResultButtonView: View {
let newBookmark = Bookmark(context: backgroundContext)
newBookmark.title = result.title
newBookmark.source = result.source
newBookmark.magnetHash = result.magnetHash
newBookmark.magnetLink = result.magnetLink
newBookmark.magnetHash = result.magnet.hash
newBookmark.magnetLink = result.magnet.link
newBookmark.seeders = result.seeders
newBookmark.leechers = result.leechers
@ -139,8 +139,8 @@ struct SearchResultButtonView: View {
format: "title == %@ AND source == %@ AND magnetLink == %@ AND magnetHash = %@",
result.title ?? "",
result.source,
result.magnetLink ?? "",
result.magnetHash ?? ""
result.magnet.link ?? "",
result.magnet.hash ?? ""
)
bookmarkRequest.fetchLimit = 1

View file

@ -30,7 +30,7 @@ struct SearchResultInfoView: View {
Text(size)
}
DebridLabelView(magnetHash: result.magnetHash)
DebridLabelView(magnet: result.magnet)
}
.font(.caption)
}

View file

@ -91,8 +91,8 @@ struct ContentView: View {
// Remove magnets that don't have a hash
let magnets = scrapingModel.searchResults.compactMap {
if let magnetHash = $0.magnetHash {
return Magnet(link: $0.magnetLink, hash: magnetHash)
if let magnetHash = $0.magnet.hash {
return Magnet(hash: magnetHash, link: $0.magnet.link)
} else {
return nil
}

View file

@ -70,7 +70,7 @@ struct BatchChoiceView: View {
// Common function to communicate betwen VMs and queue/display a download
func queueCommonDownload(fileName: String) {
debridManager.currentDebridTask = Task {
await debridManager.fetchDebridDownload(magnetLink: navModel.resultFromCloud ? nil : navModel.selectedMagnetLink)
await debridManager.fetchDebridDownload(magnet: navModel.resultFromCloud ? nil : navModel.selectedMagnet)
if !debridManager.downloadUrl.isEmpty {
try? await Task.sleep(seconds: 1)

View file

@ -74,7 +74,7 @@ struct MagnetChoiceView: View {
if !navModel.resultFromCloud {
Section(header: "Magnet options") {
ListRowButtonView("Copy magnet", systemImage: "doc.on.doc.fill") {
UIPasteboard.general.string = navModel.selectedMagnetLink
UIPasteboard.general.string = navModel.selectedMagnet?.link
showMagnetCopyAlert.toggle()
}
.backport.alert(
@ -85,7 +85,7 @@ struct MagnetChoiceView: View {
)
ListRowButtonView("Share magnet", systemImage: "square.and.arrow.up.fill") {
if let magnetLink = navModel.selectedMagnetLink,
if let magnetLink = navModel.selectedMagnet?.link,
let url = URL(string: magnetLink)
{
navModel.activityItems = [url]
@ -94,7 +94,7 @@ struct MagnetChoiceView: View {
}
ListRowButtonView("Open in WebTor", systemImage: "arrow.up.forward.app.fill") {
navModel.runMagnetAction(magnetString: navModel.selectedMagnetLink, .webtor)
navModel.runMagnetAction(magnet: navModel.selectedMagnet, .webtor)
}
}
}