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:
kingbri 2022-11-19 11:58:02 -05:00
parent a774564212
commit e063b91f3f
13 changed files with 158 additions and 149 deletions

View file

@ -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;

View file

@ -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?")

View file

@ -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))
} }

View file

@ -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
} }
} }

View file

@ -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 })

View file

@ -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
} }

View file

@ -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)

View file

@ -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

View file

@ -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
} }

View file

@ -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

View file

@ -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)
} }
} }

View file

@ -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!)

View file

@ -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()