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:
parent
9f54397b77
commit
5a4e98f10d
23 changed files with 175 additions and 179 deletions
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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] = []
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ struct SearchResultInfoView: View {
|
|||
Text(size)
|
||||
}
|
||||
|
||||
DebridLabelView(magnetHash: result.magnetHash)
|
||||
DebridLabelView(magnet: result.magnet)
|
||||
}
|
||||
.font(.caption)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue