Compare commits
17 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4184cf1b9 | ||
|
|
cfc4a74afe | ||
|
|
7bb4ed5f7c | ||
|
|
f40f71bca3 | ||
|
|
68a7c60c2d | ||
|
|
8b00d11e44 | ||
|
|
9d7bc9b314 | ||
|
|
25bff02875 | ||
|
|
20dd00fa85 | ||
|
|
f9d2f38329 | ||
|
|
a7e20f30e6 | ||
|
|
ecf92239d2 | ||
|
|
dd54ec027b | ||
|
|
84357ea2c5 | ||
|
|
4fb5f77718 | ||
|
|
e5a872e09f | ||
|
|
1d6ac13e84 |
48 changed files with 304 additions and 436 deletions
|
|
@ -41,7 +41,6 @@
|
||||||
0C422E80293542F300486D65 /* PremiumizeModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C422E7F293542F300486D65 /* PremiumizeModels.swift */; };
|
0C422E80293542F300486D65 /* PremiumizeModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C422E7F293542F300486D65 /* PremiumizeModels.swift */; };
|
||||||
0C42B5982932F6DD008057A0 /* Set.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C42B5972932F6DD008057A0 /* Set.swift */; };
|
0C42B5982932F6DD008057A0 /* Set.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C42B5972932F6DD008057A0 /* Set.swift */; };
|
||||||
0C445C62293F9A0B0060744D /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C445C61293F9A0B0060744D /* Bundle.swift */; };
|
0C445C62293F9A0B0060744D /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C445C61293F9A0B0060744D /* Bundle.swift */; };
|
||||||
0C448BE929A135F100F4E266 /* Introspect-Static in Frameworks */ = {isa = PBXBuildFile; productRef = 0C448BE829A135F100F4E266 /* Introspect-Static */; };
|
|
||||||
0C44E2A828D4DDDC007711AE /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44E2A728D4DDDC007711AE /* Application.swift */; };
|
0C44E2A828D4DDDC007711AE /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44E2A728D4DDDC007711AE /* Application.swift */; };
|
||||||
0C44E2AD28D51C63007711AE /* BackupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44E2AC28D51C63007711AE /* BackupManager.swift */; };
|
0C44E2AD28D51C63007711AE /* BackupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44E2AC28D51C63007711AE /* BackupManager.swift */; };
|
||||||
0C44E2AF28D52E8A007711AE /* BackupsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44E2AE28D52E8A007711AE /* BackupsView.swift */; };
|
0C44E2AF28D52E8A007711AE /* BackupsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C44E2AE28D52E8A007711AE /* BackupsView.swift */; };
|
||||||
|
|
@ -69,7 +68,6 @@
|
||||||
0C6C7C9D29315292002DF910 /* AllDebridModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C6C7C9C29315292002DF910 /* AllDebridModels.swift */; };
|
0C6C7C9D29315292002DF910 /* AllDebridModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C6C7C9C29315292002DF910 /* AllDebridModels.swift */; };
|
||||||
0C7075E429D374C50093DB2D /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7075E329D374C50093DB2D /* Color.swift */; };
|
0C7075E429D374C50093DB2D /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7075E329D374C50093DB2D /* Color.swift */; };
|
||||||
0C7075E629D3845D0093DB2D /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7075E529D3845D0093DB2D /* ShareSheet.swift */; };
|
0C7075E629D3845D0093DB2D /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7075E529D3845D0093DB2D /* ShareSheet.swift */; };
|
||||||
0C70E40228C3CE9C00A5C72D /* ConditionalContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C70E40128C3CE9C00A5C72D /* ConditionalContextMenu.swift */; };
|
|
||||||
0C70E40628C40C4E00A5C72D /* NotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C70E40528C40C4E00A5C72D /* NotificationCenter.swift */; };
|
0C70E40628C40C4E00A5C72D /* NotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C70E40528C40C4E00A5C72D /* NotificationCenter.swift */; };
|
||||||
0C733287289C4C820058D1FE /* SourceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C733286289C4C820058D1FE /* SourceSettingsView.swift */; };
|
0C733287289C4C820058D1FE /* SourceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C733286289C4C820058D1FE /* SourceSettingsView.swift */; };
|
||||||
0C748EDA29D9256D0049B8BE /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 0C748ED929D9256D0049B8BE /* Yams */; };
|
0C748EDA29D9256D0049B8BE /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 0C748ED929D9256D0049B8BE /* Yams */; };
|
||||||
|
|
@ -82,6 +80,7 @@
|
||||||
0C794B6D289EFA2E00DD1CC8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C794B6C289EFA2E00DD1CC8 /* LaunchScreen.storyboard */; };
|
0C794B6D289EFA2E00DD1CC8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0C794B6C289EFA2E00DD1CC8 /* LaunchScreen.storyboard */; };
|
||||||
0C79DC072899AF3C003F1C5A /* SourceSeedLeech+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C79DC052899AF3C003F1C5A /* SourceSeedLeech+CoreDataClass.swift */; };
|
0C79DC072899AF3C003F1C5A /* SourceSeedLeech+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C79DC052899AF3C003F1C5A /* SourceSeedLeech+CoreDataClass.swift */; };
|
||||||
0C79DC082899AF3C003F1C5A /* SourceSeedLeech+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C79DC062899AF3C003F1C5A /* SourceSeedLeech+CoreDataProperties.swift */; };
|
0C79DC082899AF3C003F1C5A /* SourceSeedLeech+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C79DC062899AF3C003F1C5A /* SourceSeedLeech+CoreDataProperties.swift */; };
|
||||||
|
0C7B4A002CB051550048FA28 /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = 0C7B49FF2CB051550048FA28 /* SwiftUIIntrospect */; };
|
||||||
0C7C128628DAA3CD00381CD1 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7C128528DAA3CD00381CD1 /* URL.swift */; };
|
0C7C128628DAA3CD00381CD1 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7C128528DAA3CD00381CD1 /* URL.swift */; };
|
||||||
0C7D11FE28AA03FE00ED92DB /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7D11FD28AA03FE00ED92DB /* View.swift */; };
|
0C7D11FE28AA03FE00ED92DB /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7D11FD28AA03FE00ED92DB /* View.swift */; };
|
||||||
0C7ED14128D61BBA009E29AD /* BackupModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7ED14028D61BBA009E29AD /* BackupModels.swift */; };
|
0C7ED14128D61BBA009E29AD /* BackupModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7ED14028D61BBA009E29AD /* BackupModels.swift */; };
|
||||||
|
|
@ -104,6 +103,7 @@
|
||||||
0C8DC35429CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35329CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift */; };
|
0C8DC35429CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35329CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift */; };
|
||||||
0C8DC35629CE2ABF008A83AD /* SourceSettingsApiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35529CE2ABF008A83AD /* SourceSettingsApiView.swift */; };
|
0C8DC35629CE2ABF008A83AD /* SourceSettingsApiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35529CE2ABF008A83AD /* SourceSettingsApiView.swift */; };
|
||||||
0C8DC35829CE2ACA008A83AD /* SourceSettingsMethodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35729CE2ACA008A83AD /* SourceSettingsMethodView.swift */; };
|
0C8DC35829CE2ACA008A83AD /* SourceSettingsMethodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35729CE2ACA008A83AD /* SourceSettingsMethodView.swift */; };
|
||||||
|
0C93DED82CF80101009EA8D2 /* SettingsDebridLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C93DED72CF80101009EA8D2 /* SettingsDebridLinkView.swift */; };
|
||||||
0C95D8D828A55B03005E22B3 /* DefaultActionPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */; };
|
0C95D8D828A55B03005E22B3 /* DefaultActionPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */; };
|
||||||
0CA05457288EE58200850554 /* SettingsPluginListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05456288EE58200850554 /* SettingsPluginListView.swift */; };
|
0CA05457288EE58200850554 /* SettingsPluginListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05456288EE58200850554 /* SettingsPluginListView.swift */; };
|
||||||
0CA05459288EE9E600850554 /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05458288EE9E600850554 /* PluginManager.swift */; };
|
0CA05459288EE9E600850554 /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05458288EE9E600850554 /* PluginManager.swift */; };
|
||||||
|
|
@ -111,7 +111,6 @@
|
||||||
0CA148D6288903F000DE2211 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148BB288903F000DE2211 /* SettingsView.swift */; };
|
0CA148D6288903F000DE2211 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148BB288903F000DE2211 /* SettingsView.swift */; };
|
||||||
0CA148D7288903F000DE2211 /* LoginWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148BC288903F000DE2211 /* LoginWebView.swift */; };
|
0CA148D7288903F000DE2211 /* LoginWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148BC288903F000DE2211 /* LoginWebView.swift */; };
|
||||||
0CA148D8288903F000DE2211 /* ActionChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148BD288903F000DE2211 /* ActionChoiceView.swift */; };
|
0CA148D8288903F000DE2211 /* ActionChoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA148BD288903F000DE2211 /* ActionChoiceView.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 */; };
|
||||||
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 */; };
|
||||||
|
|
@ -135,9 +134,7 @@
|
||||||
0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 0CAF1C7A286F5C8600296F86 /* SwiftSoup */; };
|
0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 0CAF1C7A286F5C8600296F86 /* SwiftSoup */; };
|
||||||
0CB0115B29D36D9E009AFEDE /* SearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB0115A29D36D9E009AFEDE /* SearchResultsView.swift */; };
|
0CB0115B29D36D9E009AFEDE /* SearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB0115A29D36D9E009AFEDE /* SearchResultsView.swift */; };
|
||||||
0CB0AB5F29BD2A200015422C /* KodiServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB0AB5E29BD2A200015422C /* KodiServerView.swift */; };
|
0CB0AB5F29BD2A200015422C /* KodiServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB0AB5E29BD2A200015422C /* KodiServerView.swift */; };
|
||||||
0CB6516328C5A57300DCA721 /* ConditionalId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB6516228C5A57300DCA721 /* ConditionalId.swift */; };
|
|
||||||
0CB6516528C5A5D700DCA721 /* InlinedList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB6516428C5A5D700DCA721 /* InlinedList.swift */; };
|
0CB6516528C5A5D700DCA721 /* InlinedList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB6516428C5A5D700DCA721 /* InlinedList.swift */; };
|
||||||
0CB6516A28C5B4A600DCA721 /* InlineHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB6516928C5B4A600DCA721 /* InlineHeader.swift */; };
|
|
||||||
0CB725322C123E6F0047FC0B /* CloudDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB725312C123E6F0047FC0B /* CloudDownloadView.swift */; };
|
0CB725322C123E6F0047FC0B /* CloudDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB725312C123E6F0047FC0B /* CloudDownloadView.swift */; };
|
||||||
0CB725342C123E760047FC0B /* CloudMagnetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB725332C123E760047FC0B /* CloudMagnetView.swift */; };
|
0CB725342C123E760047FC0B /* CloudMagnetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB725332C123E760047FC0B /* CloudMagnetView.swift */; };
|
||||||
0CBAB83628D12ED500AC903E /* DisableInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBAB83528D12ED500AC903E /* DisableInteraction.swift */; };
|
0CBAB83628D12ED500AC903E /* DisableInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBAB83528D12ED500AC903E /* DisableInteraction.swift */; };
|
||||||
|
|
@ -226,7 +223,6 @@
|
||||||
0C6C7C9C29315292002DF910 /* AllDebridModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllDebridModels.swift; sourceTree = "<group>"; };
|
0C6C7C9C29315292002DF910 /* AllDebridModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllDebridModels.swift; sourceTree = "<group>"; };
|
||||||
0C7075E329D374C50093DB2D /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
|
0C7075E329D374C50093DB2D /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
|
||||||
0C7075E529D3845D0093DB2D /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
|
0C7075E529D3845D0093DB2D /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
|
||||||
0C70E40128C3CE9C00A5C72D /* ConditionalContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalContextMenu.swift; sourceTree = "<group>"; };
|
|
||||||
0C70E40528C40C4E00A5C72D /* NotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenter.swift; sourceTree = "<group>"; };
|
0C70E40528C40C4E00A5C72D /* NotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenter.swift; sourceTree = "<group>"; };
|
||||||
0C733286289C4C820058D1FE /* SourceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsView.swift; sourceTree = "<group>"; };
|
0C733286289C4C820058D1FE /* SourceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsView.swift; sourceTree = "<group>"; };
|
||||||
0C750742289B003E004B3906 /* SourceRssParser+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceRssParser+CoreDataClass.swift"; sourceTree = "<group>"; };
|
0C750742289B003E004B3906 /* SourceRssParser+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceRssParser+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||||
|
|
@ -259,6 +255,7 @@
|
||||||
0C8DC35329CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsBaseUrlView.swift; sourceTree = "<group>"; };
|
0C8DC35329CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsBaseUrlView.swift; sourceTree = "<group>"; };
|
||||||
0C8DC35529CE2ABF008A83AD /* SourceSettingsApiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsApiView.swift; sourceTree = "<group>"; };
|
0C8DC35529CE2ABF008A83AD /* SourceSettingsApiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsApiView.swift; sourceTree = "<group>"; };
|
||||||
0C8DC35729CE2ACA008A83AD /* SourceSettingsMethodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsMethodView.swift; sourceTree = "<group>"; };
|
0C8DC35729CE2ACA008A83AD /* SourceSettingsMethodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsMethodView.swift; sourceTree = "<group>"; };
|
||||||
|
0C93DED72CF80101009EA8D2 /* SettingsDebridLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDebridLinkView.swift; sourceTree = "<group>"; };
|
||||||
0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultActionPickerView.swift; sourceTree = "<group>"; };
|
0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultActionPickerView.swift; sourceTree = "<group>"; };
|
||||||
0CA05456288EE58200850554 /* SettingsPluginListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPluginListView.swift; sourceTree = "<group>"; };
|
0CA05456288EE58200850554 /* SettingsPluginListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPluginListView.swift; sourceTree = "<group>"; };
|
||||||
0CA05458288EE9E600850554 /* PluginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManager.swift; sourceTree = "<group>"; };
|
0CA05458288EE9E600850554 /* PluginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManager.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -266,7 +263,6 @@
|
||||||
0CA148BB288903F000DE2211 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
0CA148BB288903F000DE2211 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||||
0CA148BC288903F000DE2211 /* LoginWebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginWebView.swift; sourceTree = "<group>"; };
|
0CA148BC288903F000DE2211 /* LoginWebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginWebView.swift; sourceTree = "<group>"; };
|
||||||
0CA148BD288903F000DE2211 /* ActionChoiceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionChoiceView.swift; sourceTree = "<group>"; };
|
0CA148BD288903F000DE2211 /* ActionChoiceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionChoiceView.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>"; };
|
||||||
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>"; };
|
||||||
|
|
@ -290,9 +286,7 @@
|
||||||
0CAF1C68286F5C0E00296F86 /* Ferrite.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ferrite.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
0CAF1C68286F5C0E00296F86 /* Ferrite.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ferrite.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
0CB0115A29D36D9E009AFEDE /* SearchResultsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsView.swift; sourceTree = "<group>"; };
|
0CB0115A29D36D9E009AFEDE /* SearchResultsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsView.swift; sourceTree = "<group>"; };
|
||||||
0CB0AB5E29BD2A200015422C /* KodiServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KodiServerView.swift; sourceTree = "<group>"; };
|
0CB0AB5E29BD2A200015422C /* KodiServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KodiServerView.swift; sourceTree = "<group>"; };
|
||||||
0CB6516228C5A57300DCA721 /* ConditionalId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionalId.swift; sourceTree = "<group>"; };
|
|
||||||
0CB6516428C5A5D700DCA721 /* InlinedList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlinedList.swift; sourceTree = "<group>"; };
|
0CB6516428C5A5D700DCA721 /* InlinedList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlinedList.swift; sourceTree = "<group>"; };
|
||||||
0CB6516928C5B4A600DCA721 /* InlineHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineHeader.swift; sourceTree = "<group>"; };
|
|
||||||
0CB725312C123E6F0047FC0B /* CloudDownloadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudDownloadView.swift; sourceTree = "<group>"; };
|
0CB725312C123E6F0047FC0B /* CloudDownloadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudDownloadView.swift; sourceTree = "<group>"; };
|
||||||
0CB725332C123E760047FC0B /* CloudMagnetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudMagnetView.swift; sourceTree = "<group>"; };
|
0CB725332C123E760047FC0B /* CloudMagnetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudMagnetView.swift; sourceTree = "<group>"; };
|
||||||
0CBAB83528D12ED500AC903E /* DisableInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableInteraction.swift; sourceTree = "<group>"; };
|
0CBAB83528D12ED500AC903E /* DisableInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisableInteraction.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -327,13 +321,13 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0C7506D728B1AC9A008BEE38 /* SwiftyJSON in Frameworks */,
|
0C7506D728B1AC9A008BEE38 /* SwiftyJSON in Frameworks */,
|
||||||
0C448BE929A135F100F4E266 /* Introspect-Static in Frameworks */,
|
|
||||||
0C64A4B4288903680079976D /* Base32 in Frameworks */,
|
0C64A4B4288903680079976D /* Base32 in Frameworks */,
|
||||||
0C4CFC462897030D00AD9FAD /* Regex in Frameworks */,
|
0C4CFC462897030D00AD9FAD /* Regex in Frameworks */,
|
||||||
0C64A4B7288903880079976D /* KeychainSwift in Frameworks */,
|
0C64A4B7288903880079976D /* KeychainSwift in Frameworks */,
|
||||||
0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */,
|
0CAF1C7B286F5C8600296F86 /* SwiftSoup in Frameworks */,
|
||||||
0C748EDA29D9256D0049B8BE /* Yams in Frameworks */,
|
0C748EDA29D9256D0049B8BE /* Yams in Frameworks */,
|
||||||
0CDDDE052935235E006810B1 /* BetterSafariView in Frameworks */,
|
0CDDDE052935235E006810B1 /* BetterSafariView in Frameworks */,
|
||||||
|
0C7B4A002CB051550048FA28 /* SwiftUIIntrospect in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -483,8 +477,6 @@
|
||||||
0C44E2A928D4DFC4007711AE /* Modifiers */ = {
|
0C44E2A928D4DFC4007711AE /* Modifiers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0C70E40128C3CE9C00A5C72D /* ConditionalContextMenu.swift */,
|
|
||||||
0CB6516228C5A57300DCA721 /* ConditionalId.swift */,
|
|
||||||
0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */,
|
0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */,
|
||||||
0CBAB83528D12ED500AC903E /* DisableInteraction.swift */,
|
0CBAB83528D12ED500AC903E /* DisableInteraction.swift */,
|
||||||
0CB6516428C5A5D700DCA721 /* InlinedList.swift */,
|
0CB6516428C5A5D700DCA721 /* InlinedList.swift */,
|
||||||
|
|
@ -555,6 +547,7 @@
|
||||||
0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */,
|
0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */,
|
||||||
0C6771FD29B521F1005D38D2 /* SettingsDebridInfoView.swift */,
|
0C6771FD29B521F1005D38D2 /* SettingsDebridInfoView.swift */,
|
||||||
0C5708EA29B8F89300BE07F9 /* SettingsLogView.swift */,
|
0C5708EA29B8F89300BE07F9 /* SettingsLogView.swift */,
|
||||||
|
0C93DED72CF80101009EA8D2 /* SettingsDebridLinkView.swift */,
|
||||||
);
|
);
|
||||||
path = Settings;
|
path = Settings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -584,9 +577,7 @@
|
||||||
children = (
|
children = (
|
||||||
0C44E2A928D4DFC4007711AE /* Modifiers */,
|
0C44E2A928D4DFC4007711AE /* Modifiers */,
|
||||||
0CDCB91728C662640098B513 /* EmptyInstructionView.swift */,
|
0CDCB91728C662640098B513 /* EmptyInstructionView.swift */,
|
||||||
0CA148C1288903F000DE2211 /* NavView.swift */,
|
|
||||||
0CA3FB1F28B91D9500FA10A8 /* IndeterminateProgressView.swift */,
|
0CA3FB1F28B91D9500FA10A8 /* IndeterminateProgressView.swift */,
|
||||||
0CB6516928C5B4A600DCA721 /* InlineHeader.swift */,
|
|
||||||
0C32FB562890D1F2002BD219 /* ListRowViews.swift */,
|
0C32FB562890D1F2002BD219 /* ListRowViews.swift */,
|
||||||
0C2D9652299316CC00A504B6 /* Tag.swift */,
|
0C2D9652299316CC00A504B6 /* Tag.swift */,
|
||||||
0C6771FB29B3E0DB005D38D2 /* HybridSecureField.swift */,
|
0C6771FB29B3E0DB005D38D2 /* HybridSecureField.swift */,
|
||||||
|
|
@ -753,8 +744,8 @@
|
||||||
0C4CFC452897030D00AD9FAD /* Regex */,
|
0C4CFC452897030D00AD9FAD /* Regex */,
|
||||||
0C7506D628B1AC9A008BEE38 /* SwiftyJSON */,
|
0C7506D628B1AC9A008BEE38 /* SwiftyJSON */,
|
||||||
0CDDDE042935235E006810B1 /* BetterSafariView */,
|
0CDDDE042935235E006810B1 /* BetterSafariView */,
|
||||||
0C448BE829A135F100F4E266 /* Introspect-Static */,
|
|
||||||
0C748ED929D9256D0049B8BE /* Yams */,
|
0C748ED929D9256D0049B8BE /* Yams */,
|
||||||
|
0C7B49FF2CB051550048FA28 /* SwiftUIIntrospect */,
|
||||||
);
|
);
|
||||||
productName = Torrenter;
|
productName = Torrenter;
|
||||||
productReference = 0CAF1C68286F5C0E00296F86 /* Ferrite.app */;
|
productReference = 0CAF1C68286F5C0E00296F86 /* Ferrite.app */;
|
||||||
|
|
@ -791,8 +782,8 @@
|
||||||
0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */,
|
0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */,
|
||||||
0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
|
0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
|
||||||
0CDDDE032935235E006810B1 /* XCRemoteSwiftPackageReference "BetterSafariView" */,
|
0CDDDE032935235E006810B1 /* XCRemoteSwiftPackageReference "BetterSafariView" */,
|
||||||
0C448BE729A135F100F4E266 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */,
|
|
||||||
0C748ED829D9256D0049B8BE /* XCRemoteSwiftPackageReference "Yams" */,
|
0C748ED829D9256D0049B8BE /* XCRemoteSwiftPackageReference "Yams" */,
|
||||||
|
0C7B49FE2CB051550048FA28 /* XCRemoteSwiftPackageReference "swiftui-introspect" */,
|
||||||
);
|
);
|
||||||
productRefGroup = 0CAF1C69286F5C0E00296F86 /* Products */;
|
productRefGroup = 0CAF1C69286F5C0E00296F86 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
|
|
@ -852,13 +843,11 @@
|
||||||
0CB6516528C5A5D700DCA721 /* InlinedList.swift in Sources */,
|
0CB6516528C5A5D700DCA721 /* InlinedList.swift in Sources */,
|
||||||
0C8DC35429CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift in Sources */,
|
0C8DC35429CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift in Sources */,
|
||||||
0C32FB532890D19D002BD219 /* AboutView.swift in Sources */,
|
0C32FB532890D19D002BD219 /* AboutView.swift in Sources */,
|
||||||
0CB6516328C5A57300DCA721 /* ConditionalId.swift in Sources */,
|
|
||||||
0C70E40628C40C4E00A5C72D /* NotificationCenter.swift in Sources */,
|
0C70E40628C40C4E00A5C72D /* NotificationCenter.swift in Sources */,
|
||||||
0C84F4832895BFED0074B7C9 /* Source+CoreDataProperties.swift in Sources */,
|
0C84F4832895BFED0074B7C9 /* Source+CoreDataProperties.swift in Sources */,
|
||||||
0C2D9653299316CC00A504B6 /* Tag.swift in Sources */,
|
0C2D9653299316CC00A504B6 /* Tag.swift in Sources */,
|
||||||
0C84FCE129E4B41D00B0DFE4 /* SourceFilterView.swift in Sources */,
|
0C84FCE129E4B41D00B0DFE4 /* SourceFilterView.swift in Sources */,
|
||||||
0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */,
|
0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */,
|
||||||
0CA148DB288903F000DE2211 /* NavView.swift in Sources */,
|
|
||||||
0CA3B23C28C2AA5600616D3A /* Bookmark+CoreDataClass.swift in Sources */,
|
0CA3B23C28C2AA5600616D3A /* Bookmark+CoreDataClass.swift in Sources */,
|
||||||
0CD4030A29DA01B6008D9F03 /* PluginInfoMetaView.swift in Sources */,
|
0CD4030A29DA01B6008D9F03 /* PluginInfoMetaView.swift in Sources */,
|
||||||
0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */,
|
0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */,
|
||||||
|
|
@ -885,7 +874,6 @@
|
||||||
0C7D11FE28AA03FE00ED92DB /* View.swift in Sources */,
|
0C7D11FE28AA03FE00ED92DB /* View.swift in Sources */,
|
||||||
0CD0265729FEFBF900A83D25 /* FerriteKeychain.swift in Sources */,
|
0CD0265729FEFBF900A83D25 /* FerriteKeychain.swift in Sources */,
|
||||||
0CA3B23728C2660700616D3A /* HistoryView.swift in Sources */,
|
0CA3B23728C2660700616D3A /* HistoryView.swift in Sources */,
|
||||||
0C70E40228C3CE9C00A5C72D /* ConditionalContextMenu.swift in Sources */,
|
|
||||||
0C50B7D0299DF63C00A9FA3C /* UIDevice.swift in Sources */,
|
0C50B7D0299DF63C00A9FA3C /* UIDevice.swift in Sources */,
|
||||||
0C0D50E7288DFF850035ECC8 /* PluginAggregateView.swift in Sources */,
|
0C0D50E7288DFF850035ECC8 /* PluginAggregateView.swift in Sources */,
|
||||||
0CA3B23428C2658700616D3A /* LibraryView.swift in Sources */,
|
0CA3B23428C2658700616D3A /* LibraryView.swift in Sources */,
|
||||||
|
|
@ -912,7 +900,6 @@
|
||||||
0CA148DD288903F000DE2211 /* ScrapingViewModel.swift in Sources */,
|
0CA148DD288903F000DE2211 /* ScrapingViewModel.swift in Sources */,
|
||||||
0C445C62293F9A0B0060744D /* Bundle.swift in Sources */,
|
0C445C62293F9A0B0060744D /* Bundle.swift in Sources */,
|
||||||
0C0755C6293424A200ECA142 /* DebridLabelView.swift in Sources */,
|
0C0755C6293424A200ECA142 /* DebridLabelView.swift in Sources */,
|
||||||
0CB6516A28C5B4A600DCA721 /* InlineHeader.swift in Sources */,
|
|
||||||
0CA148D8288903F000DE2211 /* ActionChoiceView.swift in Sources */,
|
0CA148D8288903F000DE2211 /* ActionChoiceView.swift in Sources */,
|
||||||
0C41BC6528C2AEB900B47DD6 /* SearchModels.swift in Sources */,
|
0C41BC6528C2AEB900B47DD6 /* SearchModels.swift in Sources */,
|
||||||
0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */,
|
0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */,
|
||||||
|
|
@ -981,6 +968,7 @@
|
||||||
0C32FB572890D1F2002BD219 /* ListRowViews.swift in Sources */,
|
0C32FB572890D1F2002BD219 /* ListRowViews.swift in Sources */,
|
||||||
0CEC8AAE299B31B6007BFE8F /* SearchFilterHeaderView.swift in Sources */,
|
0CEC8AAE299B31B6007BFE8F /* SearchFilterHeaderView.swift in Sources */,
|
||||||
0C84F4822895BFED0074B7C9 /* Source+CoreDataClass.swift in Sources */,
|
0C84F4822895BFED0074B7C9 /* Source+CoreDataClass.swift in Sources */,
|
||||||
|
0C93DED82CF80101009EA8D2 /* SettingsDebridLinkView.swift in Sources */,
|
||||||
0C3DD43F29B6968D006429DB /* KodiEditorView.swift in Sources */,
|
0C3DD43F29B6968D006429DB /* KodiEditorView.swift in Sources */,
|
||||||
0CB725322C123E6F0047FC0B /* CloudDownloadView.swift in Sources */,
|
0CB725322C123E6F0047FC0B /* CloudDownloadView.swift in Sources */,
|
||||||
0C3DD44329B6ACD9006429DB /* KodiServer+CoreDataProperties.swift in Sources */,
|
0C3DD44329B6ACD9006429DB /* KodiServer+CoreDataProperties.swift in Sources */,
|
||||||
|
|
@ -1122,7 +1110,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 19;
|
CURRENT_PROJECT_VERSION = 21;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Ferrite/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Ferrite/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 8A74DBQ6S3;
|
DEVELOPMENT_TEAM = 8A74DBQ6S3;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
|
@ -1137,12 +1125,12 @@
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.7.2;
|
MARKETING_VERSION = 0.7.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = me.kingbri.Ferrite;
|
PRODUCT_BUNDLE_IDENTIFIER = me.kingbri.Ferrite;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
|
@ -1158,7 +1146,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 19;
|
CURRENT_PROJECT_VERSION = 21;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Ferrite/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Ferrite/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 8A74DBQ6S3;
|
DEVELOPMENT_TEAM = 8A74DBQ6S3;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
|
@ -1173,12 +1161,12 @@
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.7.2;
|
MARKETING_VERSION = 0.7.3;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = me.kingbri.Ferrite;
|
PRODUCT_BUNDLE_IDENTIFIER = me.kingbri.Ferrite;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
|
@ -1212,14 +1200,6 @@
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
0C448BE729A135F100F4E266 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/siteline/SwiftUI-Introspect/";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 0.2.3;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */ = {
|
0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/sindresorhus/Regex";
|
repositoryURL = "https://github.com/sindresorhus/Regex";
|
||||||
|
|
@ -1260,6 +1240,14 @@
|
||||||
kind = branch;
|
kind = branch;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
0C7B49FE2CB051550048FA28 /* XCRemoteSwiftPackageReference "swiftui-introspect" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/siteline/swiftui-introspect";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 1.3.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
0CAF1C79286F5C8600296F86 /* XCRemoteSwiftPackageReference "SwiftSoup" */ = {
|
0CAF1C79286F5C8600296F86 /* XCRemoteSwiftPackageReference "SwiftSoup" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/scinfu/SwiftSoup.git";
|
repositoryURL = "https://github.com/scinfu/SwiftSoup.git";
|
||||||
|
|
@ -1279,11 +1267,6 @@
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
0C448BE829A135F100F4E266 /* Introspect-Static */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = 0C448BE729A135F100F4E266 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */;
|
|
||||||
productName = "Introspect-Static";
|
|
||||||
};
|
|
||||||
0C4CFC452897030D00AD9FAD /* Regex */ = {
|
0C4CFC452897030D00AD9FAD /* Regex */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */;
|
package = 0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */;
|
||||||
|
|
@ -1309,6 +1292,11 @@
|
||||||
package = 0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
|
package = 0C7506D528B1AC9A008BEE38 /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
|
||||||
productName = SwiftyJSON;
|
productName = SwiftyJSON;
|
||||||
};
|
};
|
||||||
|
0C7B49FF2CB051550048FA28 /* SwiftUIIntrospect */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 0C7B49FE2CB051550048FA28 /* XCRemoteSwiftPackageReference "swiftui-introspect" */;
|
||||||
|
productName = SwiftUIIntrospect;
|
||||||
|
};
|
||||||
0CAF1C7A286F5C8600296F86 /* SwiftSoup */ = {
|
0CAF1C7A286F5C8600296F86 /* SwiftSoup */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 0CAF1C79286F5C8600296F86 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
|
package = 0CAF1C79286F5C8600296F86 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,13 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
let id = "AllDebrid"
|
let id = "AllDebrid"
|
||||||
let abbreviation = "AD"
|
let abbreviation = "AD"
|
||||||
let website = "https://alldebrid.com"
|
let website = "https://alldebrid.com"
|
||||||
|
let description: String? = "AllDebrid is a debrid service that is used for downloads and media playback. " +
|
||||||
|
"You must pay to access this service. \n\n" +
|
||||||
|
"It is not recommended to use this service since media cache checks are not possible via the API. " +
|
||||||
|
"Ferrite's instant availability solely looks at a user's magnet library. \n\n" +
|
||||||
|
"If you must use this service, it is recommended to download search results manually using the context menu. \n\n" +
|
||||||
|
"This service does not inform if a magnet link is a batch before downloading."
|
||||||
|
|
||||||
let cachedStatus: [String] = ["Ready"]
|
let cachedStatus: [String] = ["Ready"]
|
||||||
var authTask: Task<Void, Error>?
|
var authTask: Task<Void, Error>?
|
||||||
|
|
||||||
|
|
@ -81,7 +88,7 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
URLQueryItem(name: "pin", value: pin)
|
URLQueryItem(name: "pin", value: pin)
|
||||||
]
|
]
|
||||||
|
|
||||||
let request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/pin/check", queryItems: queryItems))
|
let request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/pin/check", queryItems: queryItems))
|
||||||
|
|
||||||
// Timer to poll AD API for key
|
// Timer to poll AD API for key
|
||||||
authTask = Task {
|
authTask = Task {
|
||||||
|
|
@ -192,31 +199,22 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sendMagnets.isEmpty {
|
// Fetch the user magnets to the latest version
|
||||||
return
|
try await getUserMagnets()
|
||||||
}
|
|
||||||
|
|
||||||
let queryItems = sendMagnets.map { URLQueryItem(name: "magnets[]", value: $0.hash) }
|
for cloudMagnet in cloudMagnets {
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/instant", queryItems: queryItems))
|
if cachedStatus.contains(cloudMagnet.status),
|
||||||
|
sendMagnets.contains(where: { $0.hash == cloudMagnet.hash })
|
||||||
let data = try await performRequest(request: &request, requestName: #function)
|
{
|
||||||
let rawResponse = try jsonDecoder.decode(ADResponse<InstantAvailabilityResponse>.self, from: data).data
|
IAValues.append(
|
||||||
|
DebridIA(
|
||||||
let filteredMagnets = rawResponse.magnets.filter { $0.instant == true && $0.files != nil }
|
magnet: Magnet(hash: cloudMagnet.hash, link: nil),
|
||||||
let availableHashes = filteredMagnets.map { magnetResp in
|
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
|
||||||
// Force unwrap is OK here since the filter caught any nil values
|
files: []
|
||||||
let files = magnetResp.files!.enumerated().map { index, magnetFile in
|
)
|
||||||
DebridIAFile(id: index, name: magnetFile.name)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return DebridIA(
|
|
||||||
magnet: Magnet(hash: magnetResp.hash, link: magnetResp.magnet),
|
|
||||||
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
|
|
||||||
files: files
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IAValues += availableHashes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Downloading
|
// MARK: - Downloading
|
||||||
|
|
@ -225,19 +223,49 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
func getRestrictedFile(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> (restrictedFile: DebridIAFile?, newIA: DebridIA?) {
|
func getRestrictedFile(magnet: Magnet, ia: DebridIA?, iaFile: DebridIAFile?) async throws -> (restrictedFile: DebridIAFile?, newIA: DebridIA?) {
|
||||||
let selectedMagnetId: String
|
let selectedMagnetId: String
|
||||||
|
|
||||||
if let existingMagnet = cloudMagnets.first(where: { $0.hash == magnet.hash && cachedStatus.contains($0.status) }) {
|
if let existingMagnet = cloudMagnets.first(where: {
|
||||||
|
$0.hash == magnet.hash && cachedStatus.contains($0.status)
|
||||||
|
}) {
|
||||||
selectedMagnetId = existingMagnet.id
|
selectedMagnetId = existingMagnet.id
|
||||||
} else {
|
} else {
|
||||||
let magnetId = try await addMagnet(magnet: magnet)
|
let magnetId = try await addMagnet(magnet: magnet)
|
||||||
selectedMagnetId = String(magnetId)
|
selectedMagnetId = String(magnetId)
|
||||||
}
|
}
|
||||||
|
|
||||||
let lockedLink = try await fetchMagnetStatus(
|
let rawResponse = try await fetchMagnetStatus(
|
||||||
magnetId: selectedMagnetId,
|
magnetId: selectedMagnetId,
|
||||||
selectedIndex: iaFile?.id ?? 0
|
selectedIndex: iaFile?.id ?? 0
|
||||||
)
|
)
|
||||||
|
guard let magnets = rawResponse.magnets[safe: 0] else {
|
||||||
|
throw DebridError.EmptyUserMagnets
|
||||||
|
}
|
||||||
|
|
||||||
return (lockedLink, nil)
|
// Batches require an unrestrict from the user
|
||||||
|
if magnets.links.count > 1, iaFile == nil {
|
||||||
|
var copiedIA = ia
|
||||||
|
|
||||||
|
copiedIA?.files = magnets.links.enumerated().compactMap { index, file in
|
||||||
|
DebridIAFile(
|
||||||
|
id: index,
|
||||||
|
name: file.filename,
|
||||||
|
streamUrlString: file.link
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (nil, copiedIA)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let cloudMagnetFile = magnets.links[safe: iaFile?.id ?? 0] {
|
||||||
|
let restrictedFile = DebridIAFile(
|
||||||
|
id: 0,
|
||||||
|
name: cloudMagnetFile.filename,
|
||||||
|
streamUrlString: cloudMagnetFile.link
|
||||||
|
)
|
||||||
|
|
||||||
|
return (restrictedFile, nil)
|
||||||
|
} else {
|
||||||
|
throw DebridError.EmptyUserMagnets
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a magnet link to the user's AD account
|
// Adds a magnet link to the user's AD account
|
||||||
|
|
@ -246,7 +274,7 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
throw DebridError.FailedRequest(description: "The magnet link is invalid")
|
throw DebridError.FailedRequest(description: "The magnet link is invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/upload"))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/magnet/upload"))
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
|
|
@ -261,27 +289,26 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
let rawResponse = try jsonDecoder.decode(ADResponse<AddMagnetResponse>.self, from: data).data
|
let rawResponse = try jsonDecoder.decode(ADResponse<AddMagnetResponse>.self, from: data).data
|
||||||
|
|
||||||
if let magnet = rawResponse.magnets[safe: 0] {
|
if let magnet = rawResponse.magnets[safe: 0] {
|
||||||
|
if !magnet.ready {
|
||||||
|
throw DebridError.IsCaching
|
||||||
|
}
|
||||||
|
|
||||||
return magnet.id
|
return magnet.id
|
||||||
} else {
|
} else {
|
||||||
throw DebridError.InvalidResponse
|
throw DebridError.InvalidResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchMagnetStatus(magnetId: String, selectedIndex: Int?) async throws -> DebridIAFile {
|
func fetchMagnetStatus(magnetId: String, selectedIndex: Int?) async throws -> MagnetStatusResponse {
|
||||||
let queryItems = [
|
let queryItems = [
|
||||||
URLQueryItem(name: "id", value: magnetId)
|
URLQueryItem(name: "id", value: magnetId)
|
||||||
]
|
]
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/status", queryItems: queryItems))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/magnet/status", queryItems: queryItems))
|
||||||
|
|
||||||
let data = try await performRequest(request: &request, requestName: #function)
|
let data = try await performRequest(request: &request, requestName: #function)
|
||||||
let rawResponse = try jsonDecoder.decode(ADResponse<MagnetStatusResponse>.self, from: data).data
|
let rawResponse = try jsonDecoder.decode(ADResponse<MagnetStatusResponse>.self, from: data).data
|
||||||
|
|
||||||
// Better to fetch no link at all than the wrong link
|
return rawResponse
|
||||||
if let cloudMagnetFile = rawResponse.magnets[safe: 0]?.links[safe: selectedIndex ?? -1] {
|
|
||||||
return DebridIAFile(id: 0, name: cloudMagnetFile.filename, streamUrlString: cloudMagnetFile.link)
|
|
||||||
} else {
|
|
||||||
throw DebridError.EmptyUserMagnets
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Known as unlockLink in AD's API
|
// Known as unlockLink in AD's API
|
||||||
|
|
@ -289,7 +316,7 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
let queryItems = [
|
let queryItems = [
|
||||||
URLQueryItem(name: "link", value: restrictedFile.streamUrlString)
|
URLQueryItem(name: "link", value: restrictedFile.streamUrlString)
|
||||||
]
|
]
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/link/unlock", queryItems: queryItems))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/link/unlock", queryItems: queryItems))
|
||||||
|
|
||||||
let data = try await performRequest(request: &request, requestName: "unlockLink")
|
let data = try await performRequest(request: &request, requestName: "unlockLink")
|
||||||
let rawResponse = try jsonDecoder.decode(ADResponse<UnlockLinkResponse>.self, from: data).data
|
let rawResponse = try jsonDecoder.decode(ADResponse<UnlockLinkResponse>.self, from: data).data
|
||||||
|
|
@ -301,7 +328,7 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
let queryItems = [
|
let queryItems = [
|
||||||
URLQueryItem(name: "links[]", value: link)
|
URLQueryItem(name: "links[]", value: link)
|
||||||
]
|
]
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/user/links/save", queryItems: queryItems))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/user/links/save", queryItems: queryItems))
|
||||||
|
|
||||||
try await performRequest(request: &request, requestName: #function)
|
try await performRequest(request: &request, requestName: #function)
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +336,7 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
// MARK: - Cloud methods
|
// MARK: - Cloud methods
|
||||||
|
|
||||||
func getUserMagnets() async throws {
|
func getUserMagnets() async throws {
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/status"))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/magnet/status"))
|
||||||
|
|
||||||
let data = try await performRequest(request: &request, requestName: #function)
|
let data = try await performRequest(request: &request, requestName: #function)
|
||||||
let rawResponse = try jsonDecoder.decode(ADResponse<MagnetStatusResponse>.self, from: data).data
|
let rawResponse = try jsonDecoder.decode(ADResponse<MagnetStatusResponse>.self, from: data).data
|
||||||
|
|
@ -333,13 +360,13 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
let queryItems = [
|
let queryItems = [
|
||||||
URLQueryItem(name: "id", value: cloudMagnetId)
|
URLQueryItem(name: "id", value: cloudMagnetId)
|
||||||
]
|
]
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/magnet/delete", queryItems: queryItems))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/magnet/delete", queryItems: queryItems))
|
||||||
|
|
||||||
try await performRequest(request: &request, requestName: #function)
|
try await performRequest(request: &request, requestName: #function)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserDownloads() async throws {
|
func getUserDownloads() async throws {
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/user/links"))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/user/links"))
|
||||||
|
|
||||||
let data = try await performRequest(request: &request, requestName: #function)
|
let data = try await performRequest(request: &request, requestName: #function)
|
||||||
let rawResponse = try jsonDecoder.decode(ADResponse<SavedLinksResponse>.self, from: data).data
|
let rawResponse = try jsonDecoder.decode(ADResponse<SavedLinksResponse>.self, from: data).data
|
||||||
|
|
@ -362,7 +389,7 @@ class AllDebrid: PollingDebridSource, ObservableObject {
|
||||||
let queryItems = [
|
let queryItems = [
|
||||||
URLQueryItem(name: "link", value: downloadId)
|
URLQueryItem(name: "link", value: downloadId)
|
||||||
]
|
]
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/user/links/delete", queryItems: queryItems))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/user/links/delete", queryItems: queryItems))
|
||||||
|
|
||||||
try await performRequest(request: &request, requestName: #function)
|
try await performRequest(request: &request, requestName: #function)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ class OffCloud: DebridSource, ObservableObject {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/cache"))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/cache"))
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
|
|
@ -202,7 +202,7 @@ class OffCloud: DebridSource, ObservableObject {
|
||||||
|
|
||||||
// Called as "cloud" in offcloud's API
|
// Called as "cloud" in offcloud's API
|
||||||
private func offcloudDownload(magnet: Magnet) async throws -> CloudDownloadResponse {
|
private func offcloudDownload(magnet: Magnet) async throws -> CloudDownloadResponse {
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/cloud"))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/cloud"))
|
||||||
request.httpMethod = "POST"
|
request.httpMethod = "POST"
|
||||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
|
|
@ -220,7 +220,7 @@ class OffCloud: DebridSource, ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func cloudExplore(requestId: String) async throws -> CloudExploreResponse {
|
private func cloudExplore(requestId: String) async throws -> CloudExploreResponse {
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/cloud/explore/\(requestId)"))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/cloud/explore/\(requestId)"))
|
||||||
|
|
||||||
let data = try await performRequest(request: &request, requestName: "cloudExplore")
|
let data = try await performRequest(request: &request, requestName: "cloudExplore")
|
||||||
let rawResponse = try jsonDecoder.decode(CloudExploreResponse.self, from: data)
|
let rawResponse = try jsonDecoder.decode(CloudExploreResponse.self, from: data)
|
||||||
|
|
@ -245,7 +245,7 @@ class OffCloud: DebridSource, ObservableObject {
|
||||||
func deleteUserDownload(downloadId: String) {}
|
func deleteUserDownload(downloadId: String) {}
|
||||||
|
|
||||||
func getUserMagnets() async throws {
|
func getUserMagnets() async throws {
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(baseApiUrl)/cloud/history"))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(baseApiUrl)/cloud/history"))
|
||||||
|
|
||||||
let data = try await performRequest(request: &request, requestName: "cloudHistory")
|
let data = try await performRequest(request: &request, requestName: "cloudHistory")
|
||||||
let rawResponse = try jsonDecoder.decode([CloudHistoryResponse].self, from: data)
|
let rawResponse = try jsonDecoder.decode([CloudHistoryResponse].self, from: data)
|
||||||
|
|
@ -271,7 +271,7 @@ class OffCloud: DebridSource, ObservableObject {
|
||||||
throw DebridError.InvalidPostBody
|
throw DebridError.InvalidPostBody
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = URLRequest(url: try buildRequestURL(urlString: "\(website)/cloud/remove/\(cloudMagnetId)"))
|
var request = try URLRequest(url: buildRequestURL(urlString: "\(website)/cloud/remove/\(cloudMagnetId)"))
|
||||||
try await performRequest(request: &request, requestName: "cloudRemove")
|
try await performRequest(request: &request, requestName: "cloudRemove")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,11 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
||||||
let website = "https://real-debrid.com"
|
let website = "https://real-debrid.com"
|
||||||
let description: String? = "RealDebrid is a debrid service that is used for downloads and media playback. " +
|
let description: String? = "RealDebrid is a debrid service that is used for downloads and media playback. " +
|
||||||
"You must pay to access this service. \n\n" +
|
"You must pay to access this service. \n\n" +
|
||||||
"This service does not inform if a magnet link in a user's cloud library is a batch before downloading."
|
"It is not recommended to use this service since media cache checks are not possible via the API. " +
|
||||||
|
"Ferrite's instant availability solely looks at a user's magnet library. \n\n" +
|
||||||
|
"If you must use this service, it is recommended to download search results manually using the context menu. \n\n" +
|
||||||
|
"This service does not inform if a magnet link is a batch before downloading."
|
||||||
|
|
||||||
let cachedStatus: [String] = ["downloaded"]
|
let cachedStatus: [String] = ["downloaded"]
|
||||||
var authTask: Task<Void, Error>?
|
var authTask: Task<Void, Error>?
|
||||||
|
|
||||||
|
|
@ -248,7 +252,8 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
||||||
|
|
||||||
// MARK: - Instant availability
|
// MARK: - Instant availability
|
||||||
|
|
||||||
// Checks if the magnet is streamable on RD
|
// Post-API changes
|
||||||
|
// Use user magnets to check for IA instead
|
||||||
func instantAvailability(magnets: [Magnet]) async throws {
|
func instantAvailability(magnets: [Magnet]) async throws {
|
||||||
let now = Date().timeIntervalSince1970
|
let now = Date().timeIntervalSince1970
|
||||||
|
|
||||||
|
|
@ -265,61 +270,21 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sendMagnets.isEmpty {
|
// Fetch the user magnets to the latest version
|
||||||
return
|
try await getUserMagnets()
|
||||||
}
|
|
||||||
|
|
||||||
var request = URLRequest(url: URL(string: "\(baseApiUrl)/torrents/instantAvailability/\(sendMagnets.compactMap(\.hash).joined(separator: "/"))")!)
|
for cloudMagnet in cloudMagnets {
|
||||||
|
if cachedStatus.contains(cloudMagnet.status),
|
||||||
let data = try await performRequest(request: &request, requestName: #function)
|
sendMagnets.contains(where: { $0.hash == cloudMagnet.hash })
|
||||||
|
{
|
||||||
let rawResponseDict = try jsonDecoder.decode([String: InstantAvailabilityResponse].self, from: data)
|
IAValues.append(
|
||||||
|
DebridIA(
|
||||||
for (hash, response) in rawResponseDict {
|
magnet: Magnet(hash: cloudMagnet.hash, link: nil),
|
||||||
guard let data = response.data else {
|
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
|
||||||
continue
|
files: []
|
||||||
}
|
)
|
||||||
|
|
||||||
if data.rd.isEmpty {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle files array
|
|
||||||
let batches = data.rd.map { fileDict in
|
|
||||||
let batchFiles: [RealDebrid.IABatchFile] = fileDict.map { key, value in
|
|
||||||
// Force unwrapped ID. Is safe because ID is guaranteed on a successful response
|
|
||||||
RealDebrid.IABatchFile(id: Int(key)!, fileName: value.filename)
|
|
||||||
}.sorted(by: { $0.id < $1.id })
|
|
||||||
|
|
||||||
return RealDebrid.IABatch(files: batchFiles)
|
|
||||||
}
|
|
||||||
|
|
||||||
var files: [DebridIAFile] = []
|
|
||||||
|
|
||||||
for batch in batches {
|
|
||||||
let batchFileIds = batch.files.map(\.id)
|
|
||||||
|
|
||||||
for batchFile in batch.files {
|
|
||||||
if !files.contains(where: { $0.id == batchFile.id }) {
|
|
||||||
files.append(
|
|
||||||
DebridIAFile(
|
|
||||||
id: batchFile.id,
|
|
||||||
name: batchFile.fileName,
|
|
||||||
batchIds: batchFileIds
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TTL: 5 minutes
|
|
||||||
IAValues.append(
|
|
||||||
DebridIA(
|
|
||||||
magnet: Magnet(hash: hash, link: nil),
|
|
||||||
expiryTimeStamp: Date().timeIntervalSince1970 + 300,
|
|
||||||
files: files
|
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -331,7 +296,9 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Don't queue a new job if the magnet already exists in the user's library
|
// Don't queue a new job if the magnet already exists in the user's library
|
||||||
if let existingCloudMagnet = cloudMagnets.first(where: { $0.hash == magnet.hash && $0.status == "downloaded" }) {
|
if let existingCloudMagnet = cloudMagnets.first(where: {
|
||||||
|
$0.hash == magnet.hash && cachedStatus.contains($0.status)
|
||||||
|
}) {
|
||||||
selectedMagnetId = existingCloudMagnet.id
|
selectedMagnetId = existingCloudMagnet.id
|
||||||
} else {
|
} else {
|
||||||
selectedMagnetId = try await addMagnet(magnet: magnet)
|
selectedMagnetId = try await addMagnet(magnet: magnet)
|
||||||
|
|
@ -342,9 +309,8 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
||||||
let response = try await torrentInfo(debridID: selectedMagnetId)
|
let response = try await torrentInfo(debridID: selectedMagnetId)
|
||||||
let filteredFiles = response.files.filter { $0.selected == 1 }
|
let filteredFiles = response.files.filter { $0.selected == 1 }
|
||||||
|
|
||||||
|
// Need to return this to the user
|
||||||
if filteredFiles.count > 1, iaFile == nil {
|
if filteredFiles.count > 1, iaFile == nil {
|
||||||
// Need to return this to the user
|
|
||||||
|
|
||||||
var copiedIA = ia
|
var copiedIA = ia
|
||||||
|
|
||||||
copiedIA?.files = response.files.enumerated().compactMap { index, file in
|
copiedIA?.files = response.files.enumerated().compactMap { index, file in
|
||||||
|
|
@ -435,22 +401,6 @@ class RealDebrid: PollingDebridSource, ObservableObject {
|
||||||
default:
|
default:
|
||||||
throw DebridError.EmptyUserMagnets
|
throw DebridError.EmptyUserMagnets
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
if rawResponse.status == "downloaded" {
|
|
||||||
//let cloudMagnetLink = rawResponse.links[safe: linkIndex ?? -1],
|
|
||||||
/*
|
|
||||||
return DebridIAFile(
|
|
||||||
id: 0,
|
|
||||||
name: rawResponse.filename,
|
|
||||||
streamUrlString: cloudMagnetLink
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
} else if rawResponse.status == "downloading" || rawResponse.status == "queued" {
|
|
||||||
throw DebridError.IsCaching
|
|
||||||
} else {
|
|
||||||
throw DebridError.EmptyUserMagnets
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Downloads link from selectFiles for playback
|
// Downloads link from selectFiles for playback
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Array.swift
|
// Set.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 11/26/22.
|
// Created by Brian Dashore on 11/26/22.
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ import UIKit
|
||||||
|
|
||||||
extension UIDevice {
|
extension UIDevice {
|
||||||
var hasNotch: Bool {
|
var hasNotch: Bool {
|
||||||
if #available(iOS 11.0, *) {
|
UIApplication.shared.currentUIWindow?.safeAreaInsets.bottom ?? 0 > 0
|
||||||
return UIApplication.shared.currentUIWindow?.safeAreaInsets.bottom ?? 0 > 0
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
// Created by Brian Dashore on 8/15/22.
|
// Created by Brian Dashore on 8/15/22.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Introspect
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
|
|
@ -20,16 +19,6 @@ extension View {
|
||||||
|
|
||||||
// MARK: Modifiers
|
// MARK: Modifiers
|
||||||
|
|
||||||
func conditionalContextMenu(id: some Hashable,
|
|
||||||
@ViewBuilder _ internalContent: @escaping () -> some View) -> some View
|
|
||||||
{
|
|
||||||
modifier(ConditionalContextMenuModifier(internalContent, id: id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func conditionalId(_ id: some Hashable) -> some View {
|
|
||||||
modifier(ConditionalIdModifier(id: id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func disabledAppearance(_ disabled: Bool, dimmedOpacity: Double? = nil, animation: Animation? = nil) -> some View {
|
func disabledAppearance(_ disabled: Bool, dimmedOpacity: Double? = nil, animation: Animation? = nil) -> some View {
|
||||||
modifier(DisabledAppearanceModifier(disabled: disabled, dimmedOpacity: dimmedOpacity, animation: animation))
|
modifier(DisabledAppearanceModifier(disabled: disabled, dimmedOpacity: dimmedOpacity, animation: animation))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// MultipartFormDataRequest.swift
|
// FormDataBody.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 6/12/24.
|
// Created by Brian Dashore on 6/12/24.
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ class DebridManager: ObservableObject {
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
logManager?.error("DebridManager: Could not find the associated \(selectedSource.id) entry for magnet hash \(magnetHash)")
|
logManager?.warn("DebridManager: Could not find the associated \(selectedSource.id) entry for magnet hash \(magnetHash)")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// ToastViewModel.swift
|
// LoggingManager.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 7/19/22.
|
// Created by Brian Dashore on 7/19/22.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// SourceManager.swift
|
// PluginManager.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 7/25/22.
|
// Created by Brian Dashore on 7/25/22.
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,12 @@ struct IndeterminateProgressView: View {
|
||||||
.foregroundColor(Color.accentColor)
|
.foregroundColor(Color.accentColor)
|
||||||
.frame(width: reader.size.width * 0.26, height: 6)
|
.frame(width: reader.size.width * 0.26, height: 6)
|
||||||
.clipShape(Capsule())
|
.clipShape(Capsule())
|
||||||
|
|
||||||
.offset(x: -reader.size.width * 0.6, y: 0)
|
.offset(x: -reader.size.width * 0.6, y: 0)
|
||||||
.offset(x: reader.size.width * 1.2 * self.offset, y: 0)
|
.offset(x: reader.size.width * 1.2 * offset, y: 0)
|
||||||
.animation(.default.repeatForever().speed(0.5), value: self.offset)
|
.animation(.default.repeatForever().speed(0.5), value: offset)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
withAnimation {
|
withAnimation {
|
||||||
self.offset = 1
|
offset = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
//
|
|
||||||
// InlineHeader.swift
|
|
||||||
// Ferrite
|
|
||||||
//
|
|
||||||
// Created by Brian Dashore on 9/5/22.
|
|
||||||
//
|
|
||||||
// For iOS 15's weird defaults regarding sectioned list padding
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct InlineHeader: View {
|
|
||||||
let title: String
|
|
||||||
|
|
||||||
init(_ title: String) {
|
|
||||||
self.title = title
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
if #available(iOS 16, *) {
|
|
||||||
Text(title)
|
|
||||||
} else {
|
|
||||||
Text(title)
|
|
||||||
.listRowInsets(EdgeInsets(top: 10, leading: 15, bottom: 0, trailing: 0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
//
|
|
||||||
// ConditionalContextMenu.swift
|
|
||||||
// Ferrite
|
|
||||||
//
|
|
||||||
// Created by Brian Dashore on 9/3/22.
|
|
||||||
//
|
|
||||||
// Used as a workaround for iOS 15 not updating context views with conditional variables
|
|
||||||
// A stateful ID is required for the contextMenu to update itself.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ConditionalContextMenuModifier<InternalContent: View, ID: Hashable>: ViewModifier {
|
|
||||||
let internalContent: () -> InternalContent
|
|
||||||
let id: ID
|
|
||||||
|
|
||||||
init(@ViewBuilder _ internalContent: @escaping () -> InternalContent, id: ID) {
|
|
||||||
self.internalContent = internalContent
|
|
||||||
self.id = id
|
|
||||||
}
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
if #available(iOS 16, *) {
|
|
||||||
content
|
|
||||||
.contextMenu {
|
|
||||||
internalContent()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content
|
|
||||||
.background {
|
|
||||||
Color.clear
|
|
||||||
.contextMenu {
|
|
||||||
internalContent()
|
|
||||||
}
|
|
||||||
.id(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
//
|
|
||||||
// ConditionalId.swift
|
|
||||||
// Ferrite
|
|
||||||
//
|
|
||||||
// Created by Brian Dashore on 9/4/22.
|
|
||||||
//
|
|
||||||
// Applies an ID below iOS 16
|
|
||||||
// This is due to ID workarounds making iOS 16 apps crash
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ConditionalIdModifier<ID: Hashable>: ViewModifier {
|
|
||||||
let id: ID
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
if #available(iOS 16, *) {
|
|
||||||
content
|
|
||||||
} else {
|
|
||||||
content
|
|
||||||
.id(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,26 +5,18 @@
|
||||||
// Created by Brian Dashore on 9/4/22.
|
// Created by Brian Dashore on 9/4/22.
|
||||||
//
|
//
|
||||||
// Removes the top padding on unsectioned lists
|
// Removes the top padding on unsectioned lists
|
||||||
// If a list is sectioned, see InlineHeader
|
|
||||||
//
|
//
|
||||||
|
|
||||||
import Introspect
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import SwiftUIIntrospect
|
||||||
|
|
||||||
struct InlinedListModifier: ViewModifier {
|
struct InlinedListModifier: ViewModifier {
|
||||||
let inset: CGFloat
|
let inset: CGFloat
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
if #available(iOS 16, *) {
|
content
|
||||||
content
|
.introspect(.list, on: .iOS(.v16, .v17, .v18)) { collectionView in
|
||||||
.introspectCollectionView { collectionView in
|
collectionView.contentInset.top = inset
|
||||||
collectionView.contentInset.top = inset
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content
|
|
||||||
.introspectTableView { tableView in
|
|
||||||
tableView.contentInset.top = inset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
//
|
|
||||||
// NavView.swift
|
|
||||||
// Ferrite
|
|
||||||
//
|
|
||||||
// Created by Brian Dashore on 7/4/22.
|
|
||||||
// Contributed by Mantton
|
|
||||||
//
|
|
||||||
// A wrapper that switches between NavigationStack and the legacy NavigationView
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct NavView<Content: View>: View {
|
|
||||||
@ViewBuilder var content: Content
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
// NavigationStack issues are fixed on iOS 17
|
|
||||||
if #available(iOS 17, *) {
|
|
||||||
NavigationStack {
|
|
||||||
content
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NavigationView {
|
|
||||||
content
|
|
||||||
}
|
|
||||||
.navigationViewStyle(.stack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -56,20 +56,27 @@ struct BookmarksView: View {
|
||||||
.frame(height: 15)
|
.frame(height: 15)
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
if !debridManager.enabledDebrids.isEmpty {
|
await matchAgainstIA()
|
||||||
let magnets = bookmarks.compactMap {
|
}
|
||||||
if let magnetHash = $0.magnetHash {
|
.refreshable {
|
||||||
return Magnet(hash: magnetHash, link: $0.magnetLink)
|
await matchAgainstIA()
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await debridManager.populateDebridIA(magnets)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchPredicate() {
|
func fetchPredicate() {
|
||||||
bookmarks.nsPredicate = searchText.isEmpty ? nil : NSPredicate(format: "title CONTAINS[cd] %@", searchText)
|
bookmarks.nsPredicate = searchText.isEmpty ? nil : NSPredicate(format: "title CONTAINS[cd] %@", searchText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func matchAgainstIA() async {
|
||||||
|
if !debridManager.enabledDebrids.isEmpty {
|
||||||
|
let magnets = bookmarks.compactMap {
|
||||||
|
if let magnetHash = $0.magnetHash {
|
||||||
|
return Magnet(hash: magnetHash, link: $0.magnetLink)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await debridManager.populateDebridIA(magnets)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ struct HistorySectionView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if compareGroup(historyGroup) > 0 {
|
if compareGroup(historyGroup) > 0 {
|
||||||
Section(header: InlineHeader(formatter.string(from: historyGroup[0].date ?? Date()))) {
|
Section(formatter.string(from: historyGroup[0].date ?? Date())) {
|
||||||
ForEach(historyGroup, id: \.self) { history in
|
ForEach(historyGroup, id: \.self) { history in
|
||||||
ForEach(history.entryArray.filter { allEntries.contains($0) }, id: \.self) { entry in
|
ForEach(history.entryArray.filter { allEntries.contains($0) }, id: \.self) { entry in
|
||||||
HistoryButtonView(entry: entry)
|
HistoryButtonView(entry: entry)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// InstalledSourceButtonView.swift
|
// InstalledPluginButtonView.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 8/5/22.
|
// Created by Brian Dashore on 8/5/22.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// SourceCatalogButtonView.swift
|
// PluginCatalogButtonView.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 8/5/22.
|
// Created by Brian Dashore on 8/5/22.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ struct PluginInfoAboutView<P: Plugin>: View {
|
||||||
@ObservedObject var selectedPlugin: P
|
@ObservedObject var selectedPlugin: P
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(header: InlineHeader("Description")) {
|
Section("Description") {
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
if let pluginAbout = selectedPlugin.about {
|
if let pluginAbout = selectedPlugin.about {
|
||||||
if pluginAbout.last == "\n" {
|
if pluginAbout.last == "\n" {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ struct PluginInfoMetaView<P: Plugin>: View {
|
||||||
) var pluginLists: FetchedResults<PluginList>
|
) var pluginLists: FetchedResults<PluginList>
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(header: InlineHeader("Metadata")) {
|
Section("Metadata") {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
VStack(alignment: .leading, spacing: 5) {
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
HStack(spacing: 5) {
|
HStack(spacing: 5) {
|
||||||
|
|
@ -32,8 +32,7 @@ struct PluginInfoMetaView<P: Plugin>: View {
|
||||||
Group {
|
Group {
|
||||||
Text("ID: \(selectedPlugin.id)")
|
Text("ID: \(selectedPlugin.id)")
|
||||||
|
|
||||||
if let pluginList = pluginLists.first(where: { $0.id == selectedPlugin.listId })
|
if let pluginList = pluginLists.first(where: { $0.id == selectedPlugin.listId }) {
|
||||||
{
|
|
||||||
Text("List: \(pluginList.name)")
|
Text("List: \(pluginList.name)")
|
||||||
Text("List ID: \(pluginList.id.uuidString)")
|
Text("List ID: \(pluginList.id.uuidString)")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ struct PluginAggregateView<P: Plugin, PJ: PluginJson>: View {
|
||||||
searchText: searchText
|
searchText: searchText
|
||||||
)
|
)
|
||||||
if !filteredUpdatedPlugins.isEmpty {
|
if !filteredUpdatedPlugins.isEmpty {
|
||||||
Section(header: InlineHeader("Updates")) {
|
Section("Updates") {
|
||||||
ForEach(filteredUpdatedPlugins, id: \.self) { (updatedPlugin: PJ) in
|
ForEach(filteredUpdatedPlugins, id: \.self) { (updatedPlugin: PJ) in
|
||||||
PluginCatalogButtonView(availablePlugin: updatedPlugin, needsUpdate: true)
|
PluginCatalogButtonView(availablePlugin: updatedPlugin, needsUpdate: true)
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +47,7 @@ struct PluginAggregateView<P: Plugin, PJ: PluginJson>: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !installedPlugins.isEmpty {
|
if !installedPlugins.isEmpty {
|
||||||
Section(header: InlineHeader("Installed")) {
|
Section("Installed") {
|
||||||
ForEach(installedPlugins, id: \.self) { installedPlugin in
|
ForEach(installedPlugins, id: \.self) { installedPlugin in
|
||||||
InstalledPluginButtonView(
|
InstalledPluginButtonView(
|
||||||
installedPlugin: installedPlugin,
|
installedPlugin: installedPlugin,
|
||||||
|
|
@ -64,7 +64,7 @@ struct PluginAggregateView<P: Plugin, PJ: PluginJson>: View {
|
||||||
searchText: searchText
|
searchText: searchText
|
||||||
)
|
)
|
||||||
if !filteredAvailablePlugins.isEmpty {
|
if !filteredAvailablePlugins.isEmpty {
|
||||||
Section(header: InlineHeader("Catalog")) {
|
Section("Catalog") {
|
||||||
ForEach(filteredAvailablePlugins, id: \.self) { availablePlugin in
|
ForEach(filteredAvailablePlugins, id: \.self) { availablePlugin in
|
||||||
PluginCatalogButtonView(availablePlugin: availablePlugin, needsUpdate: false)
|
PluginCatalogButtonView(availablePlugin: availablePlugin, needsUpdate: false)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ struct PluginInfoView<P: Plugin>: View {
|
||||||
@Binding var selectedPlugin: P?
|
@Binding var selectedPlugin: P?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
List {
|
List {
|
||||||
if let selectedPlugin {
|
if let selectedPlugin {
|
||||||
PluginInfoMetaView(selectedPlugin: selectedPlugin)
|
PluginInfoMetaView(selectedPlugin: selectedPlugin)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// PluginTagView.swift
|
// PluginTagsView.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 2/7/23.
|
// Created by Brian Dashore on 2/7/23.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ struct SourceSettingsApiView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(
|
Section(
|
||||||
header: InlineHeader("API credentials"),
|
header: Text("API credentials"),
|
||||||
footer: Text("Grab the required API credentials from the website. A client secret can be an API token.")
|
footer: Text("Grab the required API credentials from the website. A client secret can be an API token.")
|
||||||
) {
|
) {
|
||||||
if let clientId = selectedSourceApi.clientId, clientId.dynamic {
|
if let clientId = selectedSourceApi.clientId, clientId.dynamic {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ struct SourceSettingsBaseUrlView: View {
|
||||||
@State private var tempSite: String = ""
|
@State private var tempSite: String = ""
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(
|
Section(
|
||||||
header: InlineHeader("Base URL"),
|
header: Text("Base URL"),
|
||||||
footer: Text("Enter the base URL of your server.")
|
footer: Text("Enter the base URL of your server.")
|
||||||
) {
|
) {
|
||||||
TextField("https://...", text: $tempSite, onEditingChanged: { isFocused in
|
TextField("https://...", text: $tempSite, onEditingChanged: { isFocused in
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ struct SourceSettingsMethodView: View {
|
||||||
@ObservedObject var selectedSource: Source
|
@ObservedObject var selectedSource: Source
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section(header: InlineHeader("Fetch method")) {
|
Section("Fetch method") {
|
||||||
Picker("", selection: $selectedSource.preferredParser) {
|
Picker("", selection: $selectedSource.preferredParser) {
|
||||||
if selectedSource.jsonParser != nil {
|
if selectedSource.jsonParser != nil {
|
||||||
Text("Website API").tag(SourcePreferredParser.siteApi.rawValue)
|
Text("Website API").tag(SourcePreferredParser.siteApi.rawValue)
|
||||||
|
|
|
||||||
|
|
@ -37,29 +37,7 @@ struct SearchResultButtonView: View {
|
||||||
case .full:
|
case .full:
|
||||||
if debridManager.selectDebridResult(magnet: result.magnet) {
|
if debridManager.selectDebridResult(magnet: result.magnet) {
|
||||||
debridManager.currentDebridTask = Task {
|
debridManager.currentDebridTask = Task {
|
||||||
await debridManager.fetchDebridDownload(magnet: result.magnet)
|
await downloadToDebrid()
|
||||||
|
|
||||||
// Bump to batch
|
|
||||||
if debridManager.requiresUnrestrict {
|
|
||||||
navModel.selectedHistoryInfo = historyEntry
|
|
||||||
navModel.currentChoiceSheet = .batch
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !debridManager.downloadUrl.isEmpty {
|
|
||||||
historyEntry.url = debridManager.downloadUrl
|
|
||||||
PersistenceController.shared.createHistory(historyEntry, performSave: true)
|
|
||||||
|
|
||||||
pluginManager.runDefaultAction(
|
|
||||||
urlString: debridManager.downloadUrl,
|
|
||||||
navModel: navModel
|
|
||||||
)
|
|
||||||
|
|
||||||
if navModel.currentChoiceSheet != .action {
|
|
||||||
debridManager.downloadUrl = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .partial:
|
case .partial:
|
||||||
|
|
@ -90,7 +68,7 @@ struct SearchResultButtonView: View {
|
||||||
}
|
}
|
||||||
.disableInteraction(navModel.currentChoiceSheet != nil)
|
.disableInteraction(navModel.currentChoiceSheet != nil)
|
||||||
.tint(.primary)
|
.tint(.primary)
|
||||||
.conditionalContextMenu(id: existingBookmark) {
|
.contextMenu {
|
||||||
ZStack {
|
ZStack {
|
||||||
if let bookmark = existingBookmark {
|
if let bookmark = existingBookmark {
|
||||||
Button {
|
Button {
|
||||||
|
|
@ -121,6 +99,33 @@ struct SearchResultButtonView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
if debridManager.currentDebridTask == nil {
|
||||||
|
let foundIAResult = debridManager.selectDebridResult(magnet: result.magnet)
|
||||||
|
|
||||||
|
// Add a fake IA because we don't know if the magnet is cached at this point
|
||||||
|
if !foundIAResult {
|
||||||
|
debridManager.selectedDebridItem = DebridIA(
|
||||||
|
magnet: result.magnet,
|
||||||
|
expiryTimeStamp: Date().timeIntervalSince1970,
|
||||||
|
files: []
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
debridManager.currentDebridTask = Task {
|
||||||
|
await downloadToDebrid()
|
||||||
|
|
||||||
|
// Re-populate the IA cache if a result wasn't initially found
|
||||||
|
if !foundIAResult {
|
||||||
|
await debridManager.populateDebridIA([result.magnet])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Text("Download to Debrid")
|
||||||
|
Image(systemName: "arrow.down.circle")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.alert("Caching file", isPresented: $debridManager.showDeleteAlert) {
|
.alert("Caching file", isPresented: $debridManager.showDeleteAlert) {
|
||||||
Button("Yes", role: .destructive) {
|
Button("Yes", role: .destructive) {
|
||||||
|
|
@ -166,4 +171,35 @@ struct SearchResultButtonView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Common function to download
|
||||||
|
func downloadToDebrid() async {
|
||||||
|
var historyEntry = HistoryEntryJson(
|
||||||
|
name: result.title,
|
||||||
|
source: result.source
|
||||||
|
)
|
||||||
|
|
||||||
|
await debridManager.fetchDebridDownload(magnet: result.magnet)
|
||||||
|
navModel.selectedTitle = result.title ?? ""
|
||||||
|
|
||||||
|
if debridManager.requiresUnrestrict {
|
||||||
|
navModel.currentChoiceSheet = .batch
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !debridManager.downloadUrl.isEmpty {
|
||||||
|
historyEntry.url = debridManager.downloadUrl
|
||||||
|
PersistenceController.shared.createHistory(historyEntry, performSave: true)
|
||||||
|
|
||||||
|
pluginManager.runDefaultAction(
|
||||||
|
urlString: debridManager.downloadUrl,
|
||||||
|
navModel: navModel
|
||||||
|
)
|
||||||
|
|
||||||
|
if navModel.currentChoiceSheet != .action {
|
||||||
|
debridManager.downloadUrl = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// SearchResultRDView.swift
|
// SearchResultInfoView.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 7/26/22.
|
// Created by Brian Dashore on 7/26/22.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// DefaultActionsPickerViews.swift
|
// DefaultActionPickerView.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 8/11/22.
|
// Created by Brian Dashore on 8/11/22.
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,11 @@ struct KodiEditorView: View {
|
||||||
@State private var errorAlertText: String = ""
|
@State private var errorAlertText: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
Group {
|
Group {
|
||||||
Section(
|
Section(
|
||||||
header: InlineHeader("URL"),
|
header: Text("URL"),
|
||||||
footer: Text("Must follow the format http(s)://<ip>:<port>")
|
footer: Text("Must follow the format http(s)://<ip>:<port>")
|
||||||
) {
|
) {
|
||||||
TextField("Enter URL", text: $serverUrl)
|
TextField("Enter URL", text: $serverUrl)
|
||||||
|
|
@ -37,14 +37,14 @@ struct KodiEditorView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(
|
Section(
|
||||||
header: InlineHeader("Friendly name"),
|
header: Text("Friendly name"),
|
||||||
footer: Text("Defaults to the URL if not provided")
|
footer: Text("Defaults to the URL if not provided")
|
||||||
) {
|
) {
|
||||||
TextField("Friendly name", text: $friendlyName)
|
TextField("Friendly name", text: $friendlyName)
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(
|
Section(
|
||||||
header: InlineHeader("Credentials"),
|
header: Text("Credentials"),
|
||||||
footer: Text("Only use for clients with authentication")
|
footer: Text("Only use for clients with authentication")
|
||||||
) {
|
) {
|
||||||
TextField("Username", text: $username)
|
TextField("Username", text: $username)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ struct SettingsKodiView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
Section(header: InlineHeader("Description")) {
|
Section("Description") {
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
Text("Kodi is an external application that is used to manage a local media library and playback.")
|
Text("Kodi is an external application that is used to manage a local media library and playback.")
|
||||||
|
|
||||||
|
|
@ -27,7 +27,7 @@ struct SettingsKodiView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(
|
Section(
|
||||||
header: InlineHeader("Servers"),
|
header: Text("Servers"),
|
||||||
footer: Text("Edit a server by holding it and accessing the context menu")
|
footer: Text("Edit a server by holding it and accessing the context menu")
|
||||||
) {
|
) {
|
||||||
if kodiServers.isEmpty {
|
if kodiServers.isEmpty {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// SourceListEditorView.swift
|
// PluginListEditorView.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 7/25/22.
|
// Created by Brian Dashore on 7/25/22.
|
||||||
|
|
@ -25,7 +25,7 @@ struct PluginListEditorView: View {
|
||||||
@State private var loadedSelectedList = false
|
@State private var loadedSelectedList = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
TextField("Enter URL", text: $pluginListUrl)
|
TextField("Enter URL", text: $pluginListUrl)
|
||||||
.disableAutocorrection(true)
|
.disableAutocorrection(true)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// SettingsSourceListView.swift
|
// SettingsPluginListView.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 7/25/22.
|
// Created by Brian Dashore on 7/25/22.
|
||||||
|
|
@ -69,12 +69,8 @@ struct SettingsPluginListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $presentEditSheet) {
|
.sheet(isPresented: $presentEditSheet) {
|
||||||
if #available(iOS 16, *) {
|
PluginListEditorView()
|
||||||
PluginListEditorView()
|
.presentationDetents([.medium])
|
||||||
.presentationDetents([.medium])
|
|
||||||
} else {
|
|
||||||
PluginListEditorView()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationTitle("Plugin Lists")
|
.navigationTitle("Plugin Lists")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ struct SettingsAppVersionView: View {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
} else if !releases.isEmpty {
|
} else if !releases.isEmpty {
|
||||||
List {
|
List {
|
||||||
Section(header: InlineHeader("GitHub links")) {
|
Section("GitHub links") {
|
||||||
ForEach(releases, id: \.self) { release in
|
ForEach(releases, id: \.self) { release in
|
||||||
ListRowLinkView(text: release.tagName, link: release.htmlUrl)
|
ListRowLinkView(text: release.tagName, link: release.htmlUrl)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// DebridInfoView.swift
|
// SettingsDebridInfoView.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 3/5/23.
|
// Created by Brian Dashore on 3/5/23.
|
||||||
|
|
@ -16,7 +16,7 @@ struct SettingsDebridInfoView: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
Section(header: InlineHeader("Description")) {
|
Section("Description") {
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
Text(debridSource.description ??
|
Text(debridSource.description ??
|
||||||
"\(debridSource.id) is a debrid service that is used for downloads and media playback. You must pay to access the service."
|
"\(debridSource.id) is a debrid service that is used for downloads and media playback. You must pay to access the service."
|
||||||
|
|
@ -27,7 +27,7 @@ struct SettingsDebridInfoView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(
|
Section(
|
||||||
header: InlineHeader("Login status"),
|
header: Text("Login status"),
|
||||||
footer: Text("A WebView will show up to prompt you for credentials")
|
footer: Text("A WebView will show up to prompt you for credentials")
|
||||||
) {
|
) {
|
||||||
Button {
|
Button {
|
||||||
|
|
@ -58,7 +58,7 @@ struct SettingsDebridInfoView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(
|
Section(
|
||||||
header: InlineHeader("API key"),
|
header: Text("API key"),
|
||||||
footer: Text("Add a permanent API key here. Only use this if web authentication does not work!")
|
footer: Text("Add a permanent API key here. Only use this if web authentication does not work!")
|
||||||
) {
|
) {
|
||||||
HybridSecureField(
|
HybridSecureField(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
//
|
||||||
|
// SettingsDebridLinkView.swift
|
||||||
|
// Ferrite
|
||||||
|
//
|
||||||
|
// Created by Brian Dashore on 11/27/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SettingsDebridLinkView: View {
|
||||||
|
var debridSource: DebridSource
|
||||||
|
|
||||||
|
// TODO: Use a roundabout state for now
|
||||||
|
@State private var isLoggedIn = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationLink {
|
||||||
|
SettingsDebridInfoView(debridSource: debridSource)
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
Text(debridSource.id)
|
||||||
|
Spacer()
|
||||||
|
Text(isLoggedIn ? "Enabled" : "Disabled")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
isLoggedIn = debridSource.isLoggedIn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,7 @@ struct ContentView: View {
|
||||||
@State private var dismissAction: () -> Void = {}
|
@State private var dismissAction: () -> Void = {}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
List {
|
List {
|
||||||
SearchResultsView(searchText: $searchText)
|
SearchResultsView(searchText: $searchText)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ struct LibraryView: View {
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
ZStack {
|
ZStack {
|
||||||
switch navModel.libraryPickerSelection {
|
switch navModel.libraryPickerSelection {
|
||||||
case .bookmarks:
|
case .bookmarks:
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ struct LoginWebView: View {
|
||||||
var url: URL
|
var url: URL
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
WebView(url: url)
|
WebView(url: url)
|
||||||
.navigationTitle("Sign in")
|
.navigationTitle("Sign in")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,8 @@ struct MainView: View {
|
||||||
case .batch:
|
case .batch:
|
||||||
BatchChoiceView()
|
BatchChoiceView()
|
||||||
case .activity:
|
case .activity:
|
||||||
if #available(iOS 16, *) {
|
ShareSheet(activityItems: navModel.activityItems)
|
||||||
ShareSheet(activityItems: navModel.activityItems)
|
.presentationDetents([.medium, .large])
|
||||||
.presentationDetents([.medium, .large])
|
|
||||||
} else {
|
|
||||||
ShareSheet(activityItems: navModel.activityItems)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ struct PluginsView: View {
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
ZStack {
|
ZStack {
|
||||||
if checkedForPlugins {
|
if checkedForPlugins {
|
||||||
switch navModel.pluginPickerSelection {
|
switch navModel.pluginPickerSelection {
|
||||||
|
|
|
||||||
|
|
@ -212,10 +212,7 @@ struct SearchBar<ScopeContent: View>: UIViewControllerRepresentable {
|
||||||
private func setup() {
|
private func setup() {
|
||||||
parent?.navigationItem.searchController = searchController
|
parent?.navigationItem.searchController = searchController
|
||||||
parent?.navigationItem.hidesSearchBarWhenScrolling = false
|
parent?.navigationItem.hidesSearchBarWhenScrolling = false
|
||||||
|
parent?.navigationItem.preferredSearchBarPlacement = .stacked
|
||||||
if #available(iOS 16, *) {
|
|
||||||
parent?.navigationItem.preferredSearchBarPlacement = .stacked
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes search bar appear when application starts
|
// Makes search bar appear when application starts
|
||||||
parent?.navigationController?.navigationBar.sizeToFit()
|
parent?.navigationController?.navigationBar.sizeToFit()
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import BetterSafariView
|
import BetterSafariView
|
||||||
import Introspect
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
|
|
@ -43,24 +42,15 @@ struct SettingsView: View {
|
||||||
@FocusState private var focusedField: Field?
|
@FocusState private var focusedField: Field?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
Section(header: InlineHeader("Debrid services")) {
|
Section("Debrid services") {
|
||||||
ForEach(debridManager.debridSources, id: \.id) { (debridSource: DebridSource) in
|
ForEach(debridManager.debridSources, id: \.id) { (debridSource: DebridSource) in
|
||||||
NavigationLink {
|
SettingsDebridLinkView(debridSource: debridSource)
|
||||||
SettingsDebridInfoView(debridSource: debridSource)
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
Text(debridSource.id)
|
|
||||||
Spacer()
|
|
||||||
Text(debridSource.isLoggedIn ? "Enabled" : "Disabled")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: InlineHeader("Playback services")) {
|
Section("Playback services") {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
SettingsKodiView(kodiServers: kodiServers)
|
SettingsKodiView(kodiServers: kodiServers)
|
||||||
} label: {
|
} label: {
|
||||||
|
|
@ -74,7 +64,7 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(
|
Section(
|
||||||
header: InlineHeader("Behavior"),
|
header: Text("Behavior"),
|
||||||
footer: VStack(alignment: .leading, spacing: 8) {
|
footer: VStack(alignment: .leading, spacing: 8) {
|
||||||
Text("Temporarily disable ephemeral auth if you cannot log into a service")
|
Text("Temporarily disable ephemeral auth if you cannot log into a service")
|
||||||
Text("Only disable search timeout if results are slow to fetch")
|
Text("Only disable search timeout if results are slow to fetch")
|
||||||
|
|
@ -121,13 +111,13 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: InlineHeader("Plugin management")) {
|
Section("Plugin management") {
|
||||||
NavigationLink("Plugin lists") {
|
NavigationLink("Plugin lists") {
|
||||||
SettingsPluginListView()
|
SettingsPluginListView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: InlineHeader("Default actions")) {
|
Section("Default actions") {
|
||||||
if !debridManager.enabledDebrids.isEmpty {
|
if !debridManager.enabledDebrids.isEmpty {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
DefaultActionPickerView(
|
DefaultActionPickerView(
|
||||||
|
|
@ -185,13 +175,13 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: InlineHeader("Backups")) {
|
Section("Backups") {
|
||||||
NavigationLink("Backups") {
|
NavigationLink("Backups") {
|
||||||
BackupsView()
|
BackupsView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: InlineHeader("Updates")) {
|
Section("Updates") {
|
||||||
Toggle(isOn: $autoUpdateNotifs) {
|
Toggle(isOn: $autoUpdateNotifs) {
|
||||||
Text("Show update alerts")
|
Text("Show update alerts")
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +191,7 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: InlineHeader("Information")) {
|
Section("Information") {
|
||||||
ListRowLinkView(text: "Donate", link: "https://ko-fi.com/kingbri")
|
ListRowLinkView(text: "Donate", link: "https://ko-fi.com/kingbri")
|
||||||
ListRowLinkView(text: "Report issues", link: "https://github.com/bdashore3/Ferrite/issues")
|
ListRowLinkView(text: "Report issues", link: "https://github.com/bdashore3/Ferrite/issues")
|
||||||
|
|
||||||
|
|
@ -210,7 +200,7 @@ struct SettingsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: InlineHeader("Debug")) {
|
Section("Debug") {
|
||||||
NavigationLink("Logs") {
|
NavigationLink("Logs") {
|
||||||
SettingsLogView()
|
SettingsLogView()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// MagnetChoiceView.swift
|
// ActionChoiceView.swift
|
||||||
// Ferrite
|
// Ferrite
|
||||||
//
|
//
|
||||||
// Created by Brian Dashore on 7/20/22.
|
// Created by Brian Dashore on 7/20/22.
|
||||||
|
|
@ -29,9 +29,9 @@ struct ActionChoiceView: View {
|
||||||
@State private var showMagnetCopyAlert = false
|
@State private var showMagnetCopyAlert = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
Section(header: InlineHeader("Now Playing")) {
|
Section("Now Playing") {
|
||||||
VStack(alignment: .leading, spacing: 5) {
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
Text(navModel.selectedTitle)
|
Text(navModel.selectedTitle)
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
|
|
@ -46,7 +46,7 @@ struct ActionChoiceView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !debridManager.downloadUrl.isEmpty {
|
if !debridManager.downloadUrl.isEmpty {
|
||||||
Section(header: InlineHeader("Debrid options")) {
|
Section("Debrid options") {
|
||||||
ForEach(actions, id: \.id) { action in
|
ForEach(actions, id: \.id) { action in
|
||||||
if action.requires.contains(ActionRequirement.debrid.rawValue) {
|
if action.requires.contains(ActionRequirement.debrid.rawValue) {
|
||||||
ListRowButtonView(action.name, systemImage: "arrow.up.forward.app.fill") {
|
ListRowButtonView(action.name, systemImage: "arrow.up.forward.app.fill") {
|
||||||
|
|
@ -91,7 +91,7 @@ struct ActionChoiceView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !navModel.resultFromCloud {
|
if !navModel.resultFromCloud {
|
||||||
Section(header: InlineHeader("Magnet options")) {
|
Section("Magnet options") {
|
||||||
ForEach(actions, id: \.id) { action in
|
ForEach(actions, id: \.id) { action in
|
||||||
if action.requires.contains(ActionRequirement.magnet.rawValue) {
|
if action.requires.contains(ActionRequirement.magnet.rawValue) {
|
||||||
ListRowButtonView(action.name, systemImage: "arrow.up.forward.app.fill") {
|
ListRowButtonView(action.name, systemImage: "arrow.up.forward.app.fill") {
|
||||||
|
|
@ -123,13 +123,8 @@ struct ActionChoiceView: View {
|
||||||
}
|
}
|
||||||
.tint(.primary)
|
.tint(.primary)
|
||||||
.sheet(isPresented: $navModel.showLocalActivitySheet) {
|
.sheet(isPresented: $navModel.showLocalActivitySheet) {
|
||||||
// TODO: Fix share sheet
|
ShareSheet(activityItems: navModel.activityItems)
|
||||||
if #available(iOS 16, *) {
|
.presentationDetents([.medium, .large])
|
||||||
ShareSheet(activityItems: navModel.activityItems)
|
|
||||||
.presentationDetents([.medium, .large])
|
|
||||||
} else {
|
|
||||||
ShareSheet(activityItems: navModel.activityItems)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.alert("Action successful", isPresented: $pluginManager.showActionSuccessAlert) {
|
.alert("Action successful", isPresented: $pluginManager.showActionSuccessAlert) {
|
||||||
Button("OK", role: .cancel) {}
|
Button("OK", role: .cancel) {}
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,8 @@ struct BatchChoiceView: View {
|
||||||
|
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
|
|
||||||
// TODO: Make this generic for an IA protocol
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavView {
|
NavigationStack {
|
||||||
List {
|
List {
|
||||||
ForEach(debridManager.selectedDebridItem?.files ?? [], id: \.self) { file in
|
ForEach(debridManager.selectedDebridItem?.files ?? [], id: \.self) { file in
|
||||||
if file.name.lowercased().contains(searchText.lowercased()) || searchText.isEmpty {
|
if file.name.lowercased().contains(searchText.lowercased()) || searchText.isEmpty {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue