Ferrite-backup/Ferrite/ViewModels/DebridManager.swift
kingbri 664c57b751 Ferrite: Add updater
Updates are sent via an alert on starting the app. This can be
disabled in the settings menu.

A full version struct has been completed for flexible comparisons.

Version history can also be viewed in settings in case a user wants
to download an earlier version of the app.

Updates track Github releases.

Signed-off-by: kingbri <bdashore3@gmail.com>
2022-08-31 18:47:02 -04:00

189 lines
5.9 KiB
Swift

//
// DebridManager.swift
// Ferrite
//
// Created by Brian Dashore on 7/20/22.
//
import Foundation
import SwiftUI
@MainActor
public class DebridManager: ObservableObject {
// UI Variables
var toastModel: ToastViewModel?
@Published var showWebView: Bool = false
@Published var showLoadingProgress: Bool = false
// Service agnostic variables
@Published var currentDebridTask: Task<Void, Never>?
// RealDebrid auth variables
let realDebrid: RealDebrid = .init()
@Published var realDebridEnabled: Bool = false {
didSet {
UserDefaults.standard.set(realDebridEnabled, forKey: "RealDebrid.Enabled")
}
}
@Published var realDebridAuthProcessing: Bool = false
@Published var realDebridAuthUrl: String = ""
// RealDebrid fetch variables
@Published var realDebridHashes: [RealDebridIA] = []
@Published var realDebridDownloadUrl: String = ""
@Published var selectedRealDebridItem: RealDebridIA?
@Published var selectedRealDebridFile: RealDebridIAFile?
init() {
realDebridEnabled = UserDefaults.standard.bool(forKey: "RealDebrid.Enabled")
}
public func populateDebridHashes(_ searchResults: [SearchResult]) async {
var hashes: [String] = []
for result in searchResults {
if let hash = result.magnetHash {
hashes.append(hash)
}
}
do {
let debridHashes = try await realDebrid.instantAvailability(magnetHashes: hashes)
realDebridHashes = debridHashes
} catch {
let error = error as NSError
if error.code != -999 {
toastModel?.updateToastDescription("RealDebrid hash error: \(error)")
}
print("RealDebrid hash error: \(error)")
}
}
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
}
}
public func setSelectedRdResult(result: SearchResult) -> Bool {
guard let magnetHash = result.magnetHash else {
toastModel?.updateToastDescription("Could not find the torrent magnet hash")
return false
}
if let realDebridItem = realDebridHashes.first(where: { magnetHash == $0.hash }) {
selectedRealDebridItem = realDebridItem
return true
} else {
toastModel?.updateToastDescription("Could not find the associated RealDebrid entry for magnet hash \(magnetHash)")
return false
}
}
public func authenticateRd() async {
do {
realDebridAuthProcessing = true
let verificationResponse = try await realDebrid.getVerificationInfo()
realDebridAuthUrl = verificationResponse.directVerificationURL
showWebView.toggle()
try await realDebrid.getDeviceCredentials(deviceCode: verificationResponse.deviceCode)
realDebridEnabled = true
} catch {
toastModel?.updateToastDescription("RealDebrid authentication error: \(error)")
realDebrid.authTask?.cancel()
print("RealDebrid authentication error: \(error)")
}
}
public func logoutRd() async {
do {
try await realDebrid.deleteTokens()
realDebridEnabled = false
realDebridAuthProcessing = false
} catch {
toastModel?.updateToastDescription("RealDebrid logout error: \(error)")
print("RealDebrid logout error: \(error)")
}
}
public func fetchRdDownload(searchResult: SearchResult, iaFile: RealDebridIAFile? = nil) async {
defer {
currentDebridTask = nil
showLoadingProgress = false
}
showLoadingProgress = true
guard let magnetLink = searchResult.magnetLink else {
toastModel?.updateToastDescription("Could not run your action because the magnet link is invalid.")
print("RealDebrid error: Invalid magnet link")
return
}
var realDebridId: String?
do {
realDebridId = try await realDebrid.addMagnet(magnetLink: magnetLink)
var fileIds: [Int] = []
if let iaFile = iaFile {
guard let iaBatchFromFile = selectedRealDebridItem?.batches[safe: iaFile.batchIndex] else {
return
}
fileIds = iaBatchFromFile.files.map(\.id)
}
if let realDebridId = realDebridId {
try await realDebrid.selectFiles(debridID: realDebridId, fileIds: fileIds)
let torrentLink = try await realDebrid.torrentInfo(debridID: realDebridId, selectedIndex: iaFile?.batchFileIndex ?? 0)
let downloadLink = try await realDebrid.unrestrictLink(debridDownloadLink: torrentLink)
realDebridDownloadUrl = downloadLink
} else {
toastModel?.updateToastDescription("Could not cache this torrent. Aborting.")
}
} catch {
let error = error as NSError
switch error.code {
case -999:
toastModel?.updateToastDescription("Download cancelled", newToastType: .info)
default:
toastModel?.updateToastDescription("RealDebrid download error: \(error)")
}
// Delete the torrent download if it exists
if let realDebridId = realDebridId {
try? await realDebrid.deleteTorrent(debridID: realDebridId)
}
showLoadingProgress = false
print("RealDebrid download error: \(error)")
}
}
}