Ferrite: Format and cleanup
Also add swipe to delete support in source lists Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
a774564212
commit
e063b91f3f
13 changed files with 158 additions and 149 deletions
|
|
@ -7,6 +7,7 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
0C0167DC29293FA900B65783 /* RealDebridModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0167DB29293FA900B65783 /* RealDebridModels.swift */; };
|
||||||
0C0D50E5288DFE7F0035ECC8 /* SourceModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */; };
|
0C0D50E5288DFE7F0035ECC8 /* SourceModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */; };
|
||||||
0C0D50E7288DFF850035ECC8 /* SourcesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E6288DFF850035ECC8 /* SourcesView.swift */; };
|
0C0D50E7288DFF850035ECC8 /* SourcesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E6288DFF850035ECC8 /* SourcesView.swift */; };
|
||||||
0C10848B28BD9A38008F0BA6 /* SettingsAppVersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */; };
|
0C10848B28BD9A38008F0BA6 /* SettingsAppVersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */; };
|
||||||
|
|
@ -68,7 +69,6 @@
|
||||||
0CA148DB288903F000DE2211 /* NavView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148C1288903F000DE2211 /* NavView.swift */; };
|
0CA148DB288903F000DE2211 /* NavView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148C1288903F000DE2211 /* NavView.swift */; };
|
||||||
0CA148DC288903F000DE2211 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0CA148C2288903F000DE2211 /* Assets.xcassets */; };
|
0CA148DC288903F000DE2211 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0CA148C2288903F000DE2211 /* Assets.xcassets */; };
|
||||||
0CA148DD288903F000DE2211 /* ScrapingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148C3288903F000DE2211 /* ScrapingViewModel.swift */; };
|
0CA148DD288903F000DE2211 /* ScrapingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148C3288903F000DE2211 /* ScrapingViewModel.swift */; };
|
||||||
0CA148DE288903F000DE2211 /* RealDebridModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148C4288903F000DE2211 /* RealDebridModels.swift */; };
|
|
||||||
0CA148DF288903F000DE2211 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0CA148C6288903F000DE2211 /* Preview Assets.xcassets */; };
|
0CA148DF288903F000DE2211 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0CA148C6288903F000DE2211 /* Preview Assets.xcassets */; };
|
||||||
0CA148E0288903F000DE2211 /* FerriteApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148C7288903F000DE2211 /* FerriteApp.swift */; };
|
0CA148E0288903F000DE2211 /* FerriteApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148C7288903F000DE2211 /* FerriteApp.swift */; };
|
||||||
0CA148E1288903F000DE2211 /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148C9288903F000DE2211 /* Collection.swift */; };
|
0CA148E1288903F000DE2211 /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148C9288903F000DE2211 /* Collection.swift */; };
|
||||||
|
|
@ -104,6 +104,7 @@
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
0C0167DB29293FA900B65783 /* RealDebridModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealDebridModels.swift; sourceTree = "<group>"; };
|
||||||
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceModels.swift; sourceTree = "<group>"; };
|
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceModels.swift; sourceTree = "<group>"; };
|
||||||
0C0D50E6288DFF850035ECC8 /* SourcesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesView.swift; sourceTree = "<group>"; };
|
0C0D50E6288DFF850035ECC8 /* SourcesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesView.swift; sourceTree = "<group>"; };
|
||||||
0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAppVersionView.swift; sourceTree = "<group>"; };
|
0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAppVersionView.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -160,7 +161,6 @@
|
||||||
0CA148C1288903F000DE2211 /* NavView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavView.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
0CA148C3288903F000DE2211 /* ScrapingViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrapingViewModel.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>"; };
|
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>"; };
|
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>"; };
|
0CA148C9288903F000DE2211 /* Collection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -243,7 +243,7 @@
|
||||||
children = (
|
children = (
|
||||||
0C7ED14028D61BBA009E29AD /* BackupModels.swift */,
|
0C7ED14028D61BBA009E29AD /* BackupModels.swift */,
|
||||||
0C68135128BC1A7C00FAD890 /* GithubModels.swift */,
|
0C68135128BC1A7C00FAD890 /* GithubModels.swift */,
|
||||||
0CA148C4288903F000DE2211 /* RealDebridModels.swift */,
|
0C0167DB29293FA900B65783 /* RealDebridModels.swift */,
|
||||||
0C41BC6428C2AEB900B47DD6 /* SearchModels.swift */,
|
0C41BC6428C2AEB900B47DD6 /* SearchModels.swift */,
|
||||||
0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */,
|
0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */,
|
||||||
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */,
|
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */,
|
||||||
|
|
@ -605,6 +605,7 @@
|
||||||
0C4CFC4E28970C8B00AD9FAD /* SourceComplexQuery+CoreDataProperties.swift in Sources */,
|
0C4CFC4E28970C8B00AD9FAD /* SourceComplexQuery+CoreDataProperties.swift in Sources */,
|
||||||
0CDCB91828C662640098B513 /* EmptyInstructionView.swift in Sources */,
|
0CDCB91828C662640098B513 /* EmptyInstructionView.swift in Sources */,
|
||||||
0CA148E2288903F000DE2211 /* Data.swift in Sources */,
|
0CA148E2288903F000DE2211 /* Data.swift in Sources */,
|
||||||
|
0C0167DC29293FA900B65783 /* RealDebridModels.swift in Sources */,
|
||||||
0C57D4CC289032ED008534E8 /* SearchResultRDView.swift in Sources */,
|
0C57D4CC289032ED008534E8 /* SearchResultRDView.swift in Sources */,
|
||||||
0C41BC6328C2AD0F00B47DD6 /* SearchResultButtonView.swift in Sources */,
|
0C41BC6328C2AD0F00B47DD6 /* SearchResultButtonView.swift in Sources */,
|
||||||
0CA05459288EE9E600850554 /* SourceManager.swift in Sources */,
|
0CA05459288EE9E600850554 /* SourceManager.swift in Sources */,
|
||||||
|
|
@ -615,7 +616,6 @@
|
||||||
0CA05457288EE58200850554 /* SettingsSourceListView.swift in Sources */,
|
0CA05457288EE58200850554 /* SettingsSourceListView.swift in Sources */,
|
||||||
0C78041D28BFB3EA001E8CA3 /* String.swift in Sources */,
|
0C78041D28BFB3EA001E8CA3 /* String.swift in Sources */,
|
||||||
0C31133C28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift in Sources */,
|
0C31133C28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift in Sources */,
|
||||||
0CA148DE288903F000DE2211 /* RealDebridModels.swift in Sources */,
|
|
||||||
0CBC76FF288DAAD00054BE44 /* NavigationViewModel.swift in Sources */,
|
0CBC76FF288DAAD00054BE44 /* NavigationViewModel.swift in Sources */,
|
||||||
0C44E2AD28D51C63007711AE /* BackupManager.swift in Sources */,
|
0C44E2AD28D51C63007711AE /* BackupManager.swift in Sources */,
|
||||||
0C4CFC4D28970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift in Sources */,
|
0C4CFC4D28970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift in Sources */,
|
||||||
|
|
@ -767,6 +767,7 @@
|
||||||
INFOPLIST_FILE = Ferrite/Info.plist;
|
INFOPLIST_FILE = Ferrite/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Ferrite;
|
INFOPLIST_KEY_CFBundleDisplayName = Ferrite;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
||||||
|
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||||
|
|
@ -801,6 +802,7 @@
|
||||||
INFOPLIST_FILE = Ferrite/Info.plist;
|
INFOPLIST_FILE = Ferrite/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Ferrite;
|
INFOPLIST_KEY_CFBundleDisplayName = Ferrite;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.entertainment";
|
||||||
|
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ struct PersistenceController {
|
||||||
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
|
||||||
|
|
||||||
container.loadPersistentStores { _, error in
|
container.loadPersistentStores { _, error in
|
||||||
if let error = error {
|
if let error {
|
||||||
fatalError("CoreData init error: \(error)")
|
fatalError("CoreData init error: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +203,7 @@ struct PersistenceController {
|
||||||
|
|
||||||
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "History")
|
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "History")
|
||||||
|
|
||||||
if let predicate = predicate {
|
if let predicate {
|
||||||
fetchRequest.predicate = predicate
|
fetchRequest.predicate = predicate
|
||||||
} else if range != .allTime {
|
} else if range != .allTime {
|
||||||
throw HistoryDeleteError.noDate("No history date range was provided and you weren't trying to clear everything! Try relaunching the app?")
|
throw HistoryDeleteError.noDate("No history date range was provided and you weren't trying to clear everything! Try relaunching the app?")
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,13 @@ extension View {
|
||||||
|
|
||||||
// MARK: Modifiers
|
// MARK: Modifiers
|
||||||
|
|
||||||
func conditionalContextMenu<InternalContent: View, ID: Hashable>(id: ID,
|
func conditionalContextMenu(id: some Hashable,
|
||||||
@ViewBuilder _ internalContent: @escaping () -> InternalContent) -> some View
|
@ViewBuilder _ internalContent: @escaping () -> some View) -> some View
|
||||||
{
|
{
|
||||||
modifier(ConditionalContextMenu(internalContent, id: id))
|
modifier(ConditionalContextMenu(internalContent, id: id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func conditionalId<ID: Hashable>(_ id: ID) -> some View {
|
func conditionalId(_ id: some Hashable) -> some View {
|
||||||
modifier(ConditionalId(id: id))
|
modifier(ConditionalId(id: id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// RealDebridModels.swift
|
// RealDebridModels.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 7/5/22.
|
// Created by Brian Dashore on 11/19/22.
|
||||||
//
|
//
|
||||||
// Structures generated from Quicktype
|
// Structures generated from Quicktype
|
||||||
|
|
||||||
|
|
@ -11,184 +11,184 @@ import Foundation
|
||||||
// MARK: - device code endpoint
|
// MARK: - device code endpoint
|
||||||
|
|
||||||
public struct DeviceCodeResponse: Codable, Sendable {
|
public struct DeviceCodeResponse: Codable, Sendable {
|
||||||
let deviceCode, userCode: String
|
let deviceCode, userCode: String
|
||||||
let interval, expiresIn: Int
|
let interval, expiresIn: Int
|
||||||
let verificationURL, directVerificationURL: String
|
let verificationURL, directVerificationURL: String
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case deviceCode = "device_code"
|
case deviceCode = "device_code"
|
||||||
case userCode = "user_code"
|
case userCode = "user_code"
|
||||||
case interval
|
case interval
|
||||||
case expiresIn = "expires_in"
|
case expiresIn = "expires_in"
|
||||||
case verificationURL = "verification_url"
|
case verificationURL = "verification_url"
|
||||||
case directVerificationURL = "direct_verification_url"
|
case directVerificationURL = "direct_verification_url"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - device credentials endpoint
|
// MARK: - device credentials endpoint
|
||||||
|
|
||||||
public struct DeviceCredentialsResponse: Codable, Sendable {
|
public struct DeviceCredentialsResponse: Codable, Sendable {
|
||||||
let clientID, clientSecret: String?
|
let clientID, clientSecret: String?
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case clientID = "client_id"
|
case clientID = "client_id"
|
||||||
case clientSecret = "client_secret"
|
case clientSecret = "client_secret"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - token endpoint
|
// MARK: - token endpoint
|
||||||
|
|
||||||
public struct TokenResponse: Codable, Sendable {
|
public struct TokenResponse: Codable, Sendable {
|
||||||
let accessToken: String
|
let accessToken: String
|
||||||
let expiresIn: Int
|
let expiresIn: Int
|
||||||
let refreshToken, tokenType: String
|
let refreshToken, tokenType: String
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case accessToken = "access_token"
|
case accessToken = "access_token"
|
||||||
case expiresIn = "expires_in"
|
case expiresIn = "expires_in"
|
||||||
case refreshToken = "refresh_token"
|
case refreshToken = "refresh_token"
|
||||||
case tokenType = "token_type"
|
case tokenType = "token_type"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - instantAvailability endpoint
|
// MARK: - instantAvailability endpoint
|
||||||
|
|
||||||
// Thanks Skitty!
|
// Thanks Skitty!
|
||||||
public struct InstantAvailabilityResponse: Codable, Sendable {
|
public struct InstantAvailabilityResponse: Codable, Sendable {
|
||||||
var data: InstantAvailabilityData?
|
var data: InstantAvailabilityData?
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
|
|
||||||
if let data = try? container.decode(InstantAvailabilityData.self) {
|
if let data = try? container.decode(InstantAvailabilityData.self) {
|
||||||
self.data = data
|
self.data = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct InstantAvailabilityData: Codable, Sendable {
|
|
||||||
var rd: [[String: InstantAvailabilityInfo]]
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InstantAvailabilityInfo: Codable, Sendable {
|
|
||||||
var filename: String
|
|
||||||
var filesize: Int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Instant Availability client side structures
|
// MARK: - Instant Availability client side structures
|
||||||
|
|
||||||
|
struct InstantAvailabilityData: Codable, Sendable {
|
||||||
|
var rd: [[String: InstantAvailabilityInfo]]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InstantAvailabilityInfo: Codable, Sendable {
|
||||||
|
var filename: String
|
||||||
|
var filesize: Int
|
||||||
|
}
|
||||||
|
|
||||||
public struct RealDebridIA: Codable, Hashable, Sendable {
|
public struct RealDebridIA: Codable, Hashable, Sendable {
|
||||||
let hash: String
|
let hash: String
|
||||||
let expiryTimeStamp: Double
|
let expiryTimeStamp: Double
|
||||||
var files: [RealDebridIAFile] = []
|
var files: [RealDebridIAFile] = []
|
||||||
var batches: [RealDebridIABatch] = []
|
var batches: [RealDebridIABatch] = []
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct RealDebridIABatch: Codable, Hashable, Sendable {
|
public struct RealDebridIABatch: Codable, Hashable, Sendable {
|
||||||
let files: [RealDebridIABatchFile]
|
let files: [RealDebridIABatchFile]
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct RealDebridIABatchFile: Codable, Hashable, Sendable {
|
public struct RealDebridIABatchFile: Codable, Hashable, Sendable {
|
||||||
let id: Int
|
let id: Int
|
||||||
let fileName: String
|
let fileName: String
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct RealDebridIAFile: Codable, Hashable, Sendable {
|
public struct RealDebridIAFile: Codable, Hashable, Sendable {
|
||||||
let name: String
|
let name: String
|
||||||
let batchIndex: Int
|
let batchIndex: Int
|
||||||
let batchFileIndex: Int
|
let batchFileIndex: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RealDebridIAStatus: Codable, Hashable, Sendable {
|
public enum RealDebridIAStatus: Codable, Hashable, Sendable {
|
||||||
case full
|
case full
|
||||||
case partial
|
case partial
|
||||||
case none
|
case none
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - addMagnet endpoint
|
// MARK: - addMagnet endpoint
|
||||||
|
|
||||||
public struct AddMagnetResponse: Codable, Sendable {
|
public struct AddMagnetResponse: Codable, Sendable {
|
||||||
let id: String
|
let id: String
|
||||||
let uri: String
|
let uri: String
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - torrentInfo endpoint
|
// MARK: - torrentInfo endpoint
|
||||||
|
|
||||||
struct TorrentInfoResponse: Codable, Sendable {
|
struct TorrentInfoResponse: Codable, Sendable {
|
||||||
let id, filename, originalFilename, hash: String
|
let id, filename, originalFilename, hash: String
|
||||||
let bytes, originalBytes: Int
|
let bytes, originalBytes: Int
|
||||||
let host: String
|
let host: String
|
||||||
let split, progress: Int
|
let split, progress: Int
|
||||||
let status, added: String
|
let status, added: String
|
||||||
let files: [TorrentInfoFile]
|
let files: [TorrentInfoFile]
|
||||||
let links: [String]
|
let links: [String]
|
||||||
let ended: String?
|
let ended: String?
|
||||||
let speed: Int?
|
let speed: Int?
|
||||||
let seeders: Int?
|
let seeders: Int?
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case id, filename
|
case id, filename
|
||||||
case originalFilename = "original_filename"
|
case originalFilename = "original_filename"
|
||||||
case hash, bytes
|
case hash, bytes
|
||||||
case originalBytes = "original_bytes"
|
case originalBytes = "original_bytes"
|
||||||
case host, split, progress, status, added, files, links, ended, speed, seeders
|
case host, split, progress, status, added, files, links, ended, speed, seeders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TorrentInfoFile: Codable, Sendable {
|
struct TorrentInfoFile: Codable, Sendable {
|
||||||
let id: Int
|
let id: Int
|
||||||
let path: String
|
let path: String
|
||||||
let bytes, selected: Int
|
let bytes, selected: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct UserTorrentsResponse: Codable, Sendable {
|
public struct UserTorrentsResponse: Codable, Sendable {
|
||||||
let id, filename, hash: String
|
let id, filename, hash: String
|
||||||
let bytes: Int
|
let bytes: Int
|
||||||
let host: String
|
let host: String
|
||||||
let split, progress: Int
|
let split, progress: Int
|
||||||
let status, added: String
|
let status, added: String
|
||||||
let links: [String]
|
let links: [String]
|
||||||
let speed, seeders: Int?
|
let speed, seeders: Int?
|
||||||
let ended: String?
|
let ended: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - unrestrictLink endpoint
|
// MARK: - unrestrictLink endpoint
|
||||||
|
|
||||||
struct UnrestrictLinkResponse: Codable, Sendable {
|
struct UnrestrictLinkResponse: Codable, Sendable {
|
||||||
let id, filename: String
|
let id, filename: String
|
||||||
let mimeType: String?
|
let mimeType: String?
|
||||||
let filesize: Int
|
let filesize: Int
|
||||||
let link: String
|
let link: String
|
||||||
let host: String
|
let host: String
|
||||||
let hostIcon: String
|
let hostIcon: String
|
||||||
let chunks, crc: Int
|
let chunks, crc: Int
|
||||||
let download: String
|
let download: String
|
||||||
let streamable: Int
|
let streamable: Int
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case id, filename, mimeType, filesize, link, host
|
case id, filename, mimeType, filesize, link, host
|
||||||
case hostIcon = "host_icon"
|
case hostIcon = "host_icon"
|
||||||
case chunks, crc, download, streamable
|
case chunks, crc, download, streamable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - User downloads list
|
// MARK: - User downloads list
|
||||||
|
|
||||||
public struct UserDownloadsResponse: Codable, Sendable {
|
public struct UserDownloadsResponse: Codable, Sendable {
|
||||||
let id, filename: String
|
let id, filename: String
|
||||||
let mimeType: String?
|
let mimeType: String?
|
||||||
let filesize: Int
|
let filesize: Int
|
||||||
let link: String
|
let link: String
|
||||||
let host: String
|
let host: String
|
||||||
let hostIcon: String
|
let hostIcon: String
|
||||||
let chunks: Int
|
let chunks: Int
|
||||||
let download: String
|
let download: String
|
||||||
let streamable: Int
|
let streamable: Int
|
||||||
let generated: String
|
let generated: String
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case id, filename, mimeType, filesize, link, host
|
case id, filename, mimeType, filesize, link, host
|
||||||
case hostIcon = "host_icon"
|
case hostIcon = "host_icon"
|
||||||
case chunks, download, streamable, generated
|
case chunks, download, streamable, generated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ public class BackupManager: ObservableObject {
|
||||||
do {
|
do {
|
||||||
try FileManager.default.removeItem(at: backupUrl)
|
try FileManager.default.removeItem(at: backupUrl)
|
||||||
|
|
||||||
if let index = index {
|
if let index {
|
||||||
backupUrls.remove(at: index)
|
backupUrls.remove(at: index)
|
||||||
} else {
|
} else {
|
||||||
backupUrls.removeAll(where: { $0 == backupUrl })
|
backupUrls.removeAll(where: { $0 == backupUrl })
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ public class DebridManager: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func matchSearchResult(result: SearchResult?) -> RealDebridIAStatus {
|
public func matchSearchResult(result: SearchResult?) -> RealDebridIAStatus {
|
||||||
guard let result = result else {
|
guard let result else {
|
||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class ScrapingViewModel: ObservableObject {
|
||||||
fallbackUrls: source.fallbackUrls
|
fallbackUrls: source.fallbackUrls
|
||||||
)
|
)
|
||||||
|
|
||||||
if let data = data,
|
if let data,
|
||||||
let html = String(data: data, encoding: .utf8)
|
let html = String(data: data, encoding: .utf8)
|
||||||
{
|
{
|
||||||
let sourceResults = await scrapeHtml(source: source, baseUrl: baseUrl, html: html)
|
let sourceResults = await scrapeHtml(source: source, baseUrl: baseUrl, html: html)
|
||||||
|
|
@ -99,7 +99,7 @@ class ScrapingViewModel: ObservableObject {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let data = data,
|
if let data,
|
||||||
let rss = String(data: data, encoding: .utf8)
|
let rss = String(data: data, encoding: .utf8)
|
||||||
{
|
{
|
||||||
let sourceResults = await scrapeRss(source: source, rss: rss)
|
let sourceResults = await scrapeRss(source: source, rss: rss)
|
||||||
|
|
@ -145,7 +145,7 @@ class ScrapingViewModel: ObservableObject {
|
||||||
fallbackUrls: source.fallbackUrls
|
fallbackUrls: source.fallbackUrls
|
||||||
)
|
)
|
||||||
|
|
||||||
if let data = data {
|
if let data {
|
||||||
let sourceResults = await scrapeJson(source: source, jsonData: data)
|
let sourceResults = await scrapeJson(source: source, jsonData: data)
|
||||||
tempResults += sourceResults
|
tempResults += sourceResults
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +170,7 @@ class ScrapingViewModel: ObservableObject {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
if let fallbackUrls = fallbackUrls {
|
if let fallbackUrls {
|
||||||
for fallbackUrl in fallbackUrls {
|
for fallbackUrl in fallbackUrls {
|
||||||
if let data = await fetchWebsiteData(urlString: fallbackUrl + replacedSearchUrl) {
|
if let data = await fetchWebsiteData(urlString: fallbackUrl + replacedSearchUrl) {
|
||||||
return data
|
return data
|
||||||
|
|
@ -359,7 +359,7 @@ class ScrapingViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if
|
} else if
|
||||||
let searchResult = searchResult,
|
let searchResult,
|
||||||
let magnetLink = searchResult.magnetLink,
|
let magnetLink = searchResult.magnetLink,
|
||||||
magnetLink.starts(with: "magnet:"),
|
magnetLink.starts(with: "magnet:"),
|
||||||
!tempResults.contains(searchResult)
|
!tempResults.contains(searchResult)
|
||||||
|
|
@ -404,7 +404,7 @@ class ScrapingViewModel: ObservableObject {
|
||||||
if let magnetLinkParser = jsonParser.magnetLink, existingSearchResult?.magnetLink == nil {
|
if let magnetLinkParser = jsonParser.magnetLink, existingSearchResult?.magnetLink == nil {
|
||||||
let rawLink = result[magnetLinkParser.query.components(separatedBy: ".")].rawValue
|
let rawLink = result[magnetLinkParser.query.components(separatedBy: ".")].rawValue
|
||||||
link = rawLink is NSNull ? nil : String(describing: rawLink)
|
link = rawLink is NSNull ? nil : String(describing: rawLink)
|
||||||
} else if let magnetHash = magnetHash {
|
} else if let magnetHash {
|
||||||
link = generateMagnetLink(magnetHash: magnetHash, title: title, trackers: source.trackers)
|
link = generateMagnetLink(magnetHash: magnetHash, title: title, trackers: source.trackers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -506,7 +506,7 @@ class ScrapingViewModel: ObservableObject {
|
||||||
discriminator: magnetLinkParser.discriminator,
|
discriminator: magnetLinkParser.discriminator,
|
||||||
regexString: magnetLinkParser.regex
|
regexString: magnetLinkParser.regex
|
||||||
)
|
)
|
||||||
} else if let magnetHash = magnetHash {
|
} else if let magnetHash {
|
||||||
link = generateMagnetLink(magnetHash: magnetHash, title: title, trackers: source.trackers)
|
link = generateMagnetLink(magnetHash: magnetHash, title: title, trackers: source.trackers)
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue
|
||||||
|
|
@ -586,7 +586,7 @@ class ScrapingViewModel: ObservableObject {
|
||||||
parsedValue = try item.getElementsByTag(query).first()?.text()
|
parsedValue = try item.getElementsByTag(query).first()?.text()
|
||||||
default:
|
default:
|
||||||
// If there's a key/value to lookup the attribute with, query it. Othewise assume the value is in the same attribute
|
// If there's a key/value to lookup the attribute with, query it. Othewise assume the value is in the same attribute
|
||||||
if let discriminator = discriminator {
|
if let discriminator {
|
||||||
let containerElement = try item.getElementsByAttributeValue(discriminator, query).first()
|
let containerElement = try item.getElementsByAttributeValue(discriminator, query).first()
|
||||||
parsedValue = try containerElement?.attr(attribute)
|
parsedValue = try containerElement?.attr(attribute)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -596,8 +596,8 @@ class ScrapingViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A capture group must be used in the provided regex
|
// A capture group must be used in the provided regex
|
||||||
if let regexString = regexString,
|
if let regexString,
|
||||||
let parsedValue = parsedValue,
|
let parsedValue,
|
||||||
let regexValue = try? Regex(regexString).firstMatch(in: parsedValue)?.groups[safe: 0]?.value
|
let regexValue = try? Regex(regexString).firstMatch(in: parsedValue)?.groups[safe: 0]?.value
|
||||||
{
|
{
|
||||||
return regexValue
|
return regexValue
|
||||||
|
|
@ -777,8 +777,8 @@ class ScrapingViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A capture group must be used in the provided regex
|
// A capture group must be used in the provided regex
|
||||||
if let regexString = regexString,
|
if let regexString,
|
||||||
let parsedValue = parsedValue,
|
let parsedValue,
|
||||||
let regexValue = try? Regex(regexString).firstMatch(in: parsedValue)?.groups[safe: 0]?.value
|
let regexValue = try? Regex(regexString).firstMatch(in: parsedValue)?.groups[safe: 0]?.value
|
||||||
{
|
{
|
||||||
return regexValue
|
return regexValue
|
||||||
|
|
@ -791,10 +791,10 @@ class ScrapingViewModel: ObservableObject {
|
||||||
public func fetchMagnetHash(magnetLink: String? = nil, existingHash: String? = nil) -> String? {
|
public func fetchMagnetHash(magnetLink: String? = nil, existingHash: String? = nil) -> String? {
|
||||||
var magnetHash: String
|
var magnetHash: String
|
||||||
|
|
||||||
if let existingHash = existingHash {
|
if let existingHash {
|
||||||
magnetHash = existingHash
|
magnetHash = existingHash
|
||||||
} else if
|
} else if
|
||||||
let magnetLink = magnetLink,
|
let magnetLink,
|
||||||
let firstSplit = magnetLink.split(separator: ":")[safe: 3],
|
let firstSplit = magnetLink.split(separator: ":")[safe: 3],
|
||||||
let tempHash = firstSplit.split(separator: "&")[safe: 0]
|
let tempHash = firstSplit.split(separator: "&")[safe: 0]
|
||||||
{
|
{
|
||||||
|
|
@ -839,11 +839,11 @@ class ScrapingViewModel: ObservableObject {
|
||||||
|
|
||||||
magnetLinkArray.append(magnetHash)
|
magnetLinkArray.append(magnetHash)
|
||||||
|
|
||||||
if let title = title, let encodedTitle = title.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) {
|
if let title, let encodedTitle = title.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) {
|
||||||
magnetLinkArray.append("&dn=\(encodedTitle)")
|
magnetLinkArray.append("&dn=\(encodedTitle)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if let trackers = trackers {
|
if let trackers {
|
||||||
for trackerUrl in trackers {
|
for trackerUrl in trackers {
|
||||||
if URL(string: trackerUrl) != nil,
|
if URL(string: trackerUrl) != nil,
|
||||||
let encodedUrlString = trackerUrl.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
|
let encodedUrlString = trackerUrl.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ public class SourceManager: ObservableObject {
|
||||||
// Checks if the current app version is supported by the source
|
// Checks if the current app version is supported by the source
|
||||||
func checkAppVersion(minVersion: String?) -> Bool {
|
func checkAppVersion(minVersion: String?) -> Bool {
|
||||||
// If there's no min version, assume that every version is supported
|
// If there's no min version, assume that every version is supported
|
||||||
guard let minVersion = minVersion else {
|
guard let minVersion else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -385,7 +385,7 @@ public class SourceManager: ObservableObject {
|
||||||
let (data, _) = try await URLSession.shared.data(for: URLRequest(url: URL(string: sourceUrl)!))
|
let (data, _) = try await URLSession.shared.data(for: URLRequest(url: URL(string: sourceUrl)!))
|
||||||
let rawResponse = try JSONDecoder().decode(SourceListJson.self, from: data)
|
let rawResponse = try JSONDecoder().decode(SourceListJson.self, from: data)
|
||||||
|
|
||||||
if let existingSourceList = existingSourceList {
|
if let existingSourceList {
|
||||||
existingSourceList.urlString = sourceUrl
|
existingSourceList.urlString = sourceUrl
|
||||||
existingSourceList.name = rawResponse.name
|
existingSourceList.name = rawResponse.name
|
||||||
existingSourceList.author = rawResponse.author
|
existingSourceList.author = rawResponse.author
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class ToastViewModel: ObservableObject {
|
||||||
@Published var showToast: Bool = false
|
@Published var showToast: Bool = false
|
||||||
|
|
||||||
public func updateToastDescription(_ description: String, newToastType: ToastType? = nil) {
|
public func updateToastDescription(_ description: String, newToastType: ToastType? = nil) {
|
||||||
if let newToastType = newToastType {
|
if let newToastType {
|
||||||
toastType = newToastType
|
toastType = newToastType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ struct AlertButton: Identifiable {
|
||||||
}
|
}
|
||||||
|
|
||||||
func toActionButton() -> Alert.Button {
|
func toActionButton() -> Alert.Button {
|
||||||
if let role = role {
|
if let role {
|
||||||
switch role {
|
switch role {
|
||||||
case .cancel:
|
case .cancel:
|
||||||
return .cancel(Text(label))
|
return .cancel(Text(label))
|
||||||
|
|
@ -57,7 +57,7 @@ struct AlertButton: Identifiable {
|
||||||
|
|
||||||
@available(iOS 15.0, *)
|
@available(iOS 15.0, *)
|
||||||
func toButtonRole(_ role: Role?) -> ButtonRole? {
|
func toButtonRole(_ role: Role?) -> ButtonRole? {
|
||||||
if let role = role {
|
if let role {
|
||||||
switch role {
|
switch role {
|
||||||
case .destructive:
|
case .destructive:
|
||||||
return .destructive
|
return .destructive
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ extension Backport where Content: View {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
if let message = message {
|
if let message {
|
||||||
Text(message)
|
Text(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +75,7 @@ extension Backport where Content: View {
|
||||||
button.toButtonView()
|
button.toButtonView()
|
||||||
}
|
}
|
||||||
} message: {
|
} message: {
|
||||||
if let message = message {
|
if let message {
|
||||||
Text(message)
|
Text(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ struct ListRowTextView: View {
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if let rightText = rightText {
|
if let rightText {
|
||||||
Text(rightText)
|
Text(rightText)
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: rightSymbol!)
|
Image(systemName: rightSymbol!)
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,13 @@ struct SettingsSourceListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onDelete { offsets in
|
||||||
|
for index in offsets {
|
||||||
|
if let list = sourceLists[safe: index] {
|
||||||
|
PersistenceController.shared.delete(list, context: backgroundContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.listStyle(.insetGrouped)
|
.listStyle(.insetGrouped)
|
||||||
.inlinedList()
|
.inlinedList()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue