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