mirror of
https://github.com/cranci1/Sora.git
synced 2026-04-20 16:12:50 +00:00
parent
18075308dd
commit
30aa66bf2a
6 changed files with 16 additions and 344 deletions
|
|
@ -1,115 +0,0 @@
|
||||||
//
|
|
||||||
// BackupData.swift
|
|
||||||
// Sulfur
|
|
||||||
//
|
|
||||||
// Created by Francesco on 25/05/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct BackupData: Codable {
|
|
||||||
let version: String
|
|
||||||
let timestamp: Date
|
|
||||||
let userData: [String: Any]
|
|
||||||
|
|
||||||
init(userData: [String: Any]) {
|
|
||||||
self.version = "1.0"
|
|
||||||
self.timestamp = Date()
|
|
||||||
self.userData = userData
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
|
||||||
case version, timestamp, userData
|
|
||||||
}
|
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
||||||
version = try container.decode(String.self, forKey: .version)
|
|
||||||
timestamp = try container.decode(Date.self, forKey: .timestamp)
|
|
||||||
|
|
||||||
let userDataContainer = try container.nestedContainer(keyedBy: DynamicKey.self, forKey: .userData)
|
|
||||||
var userData: [String: Any] = [:]
|
|
||||||
|
|
||||||
for key in userDataContainer.allKeys {
|
|
||||||
userData[key.stringValue] = try userDataContainer.decode(AnyCodable.self, forKey: key).value
|
|
||||||
}
|
|
||||||
|
|
||||||
self.userData = userData
|
|
||||||
}
|
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws {
|
|
||||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
||||||
try container.encode(version, forKey: .version)
|
|
||||||
try container.encode(timestamp, forKey: .timestamp)
|
|
||||||
|
|
||||||
var userDataContainer = container.nestedContainer(keyedBy: DynamicKey.self, forKey: .userData)
|
|
||||||
for (key, value) in userData {
|
|
||||||
let dynamicKey = DynamicKey(stringValue: key)!
|
|
||||||
try userDataContainer.encode(AnyCodable(value), forKey: dynamicKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DynamicKey: CodingKey {
|
|
||||||
var stringValue: String
|
|
||||||
var intValue: Int?
|
|
||||||
|
|
||||||
init?(stringValue: String) {
|
|
||||||
self.stringValue = stringValue
|
|
||||||
}
|
|
||||||
|
|
||||||
init?(intValue: Int) {
|
|
||||||
self.intValue = intValue
|
|
||||||
self.stringValue = String(intValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AnyCodable: Codable {
|
|
||||||
let value: Any
|
|
||||||
|
|
||||||
init(_ value: Any) {
|
|
||||||
self.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
|
||||||
let container = try decoder.singleValueContainer()
|
|
||||||
|
|
||||||
if let bool = try? container.decode(Bool.self) {
|
|
||||||
value = bool
|
|
||||||
} else if let int = try? container.decode(Int.self) {
|
|
||||||
value = int
|
|
||||||
} else if let double = try? container.decode(Double.self) {
|
|
||||||
value = double
|
|
||||||
} else if let string = try? container.decode(String.self) {
|
|
||||||
value = string
|
|
||||||
} else if let array = try? container.decode([AnyCodable].self) {
|
|
||||||
value = array.map { $0.value }
|
|
||||||
} else if let dictionary = try? container.decode([String: AnyCodable].self) {
|
|
||||||
value = dictionary.mapValues { $0.value }
|
|
||||||
} else {
|
|
||||||
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unsupported type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encode(to encoder: Encoder) throws {
|
|
||||||
var container = encoder.singleValueContainer()
|
|
||||||
|
|
||||||
switch value {
|
|
||||||
case let bool as Bool:
|
|
||||||
try container.encode(bool)
|
|
||||||
case let int as Int:
|
|
||||||
try container.encode(int)
|
|
||||||
case let double as Double:
|
|
||||||
try container.encode(double)
|
|
||||||
case let string as String:
|
|
||||||
try container.encode(string)
|
|
||||||
case let array as [Any]:
|
|
||||||
try container.encode(array.map { AnyCodable($0) })
|
|
||||||
case let dictionary as [String: Any]:
|
|
||||||
try container.encode(dictionary.mapValues { AnyCodable($0) })
|
|
||||||
default:
|
|
||||||
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: container.codingPath, debugDescription: "Unsupported type"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
//
|
|
||||||
// BackupManager.swift
|
|
||||||
// Sulfur
|
|
||||||
//
|
|
||||||
// Created by Francesco on 25/05/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class BackupManager: ObservableObject {
|
|
||||||
static let shared = BackupManager()
|
|
||||||
|
|
||||||
private init() {}
|
|
||||||
|
|
||||||
func createBackup() -> BackupData {
|
|
||||||
var userData: [String: Any] = [:]
|
|
||||||
|
|
||||||
let userDefaults = UserDefaults.standard
|
|
||||||
let defaultsDict = userDefaults.dictionaryRepresentation()
|
|
||||||
|
|
||||||
let appKeys = defaultsDict.keys.filter { key in
|
|
||||||
!key.hasPrefix("Apple") &&
|
|
||||||
!key.hasPrefix("NS") &&
|
|
||||||
!key.hasPrefix("com.apple") &&
|
|
||||||
!key.contains("Keyboard")
|
|
||||||
}
|
|
||||||
|
|
||||||
for key in appKeys {
|
|
||||||
userData[key] = defaultsDict[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
return BackupData(userData: userData)
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportBackup() -> URL? {
|
|
||||||
let backup = createBackup()
|
|
||||||
|
|
||||||
do {
|
|
||||||
let encoder = JSONEncoder()
|
|
||||||
encoder.dateEncodingStrategy = .iso8601
|
|
||||||
encoder.outputFormatting = .prettyPrinted
|
|
||||||
|
|
||||||
let data = try encoder.encode(backup)
|
|
||||||
|
|
||||||
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
|
||||||
let fileName = "sora_backup_\(DateFormatter.backupFormatter.string(from: Date())).json"
|
|
||||||
let fileURL = documentsPath.appendingPathComponent(fileName)
|
|
||||||
|
|
||||||
try data.write(to: fileURL)
|
|
||||||
return fileURL
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
print("Failed to export backup: \(error)")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func importBackup(from url: URL) -> Bool {
|
|
||||||
do {
|
|
||||||
let data = try Data(contentsOf: url)
|
|
||||||
let decoder = JSONDecoder()
|
|
||||||
decoder.dateDecodingStrategy = .iso8601
|
|
||||||
|
|
||||||
let backup = try decoder.decode(BackupData.self, from: data)
|
|
||||||
|
|
||||||
let userDefaults = UserDefaults.standard
|
|
||||||
for (key, value) in backup.userData {
|
|
||||||
userDefaults.set(value, forKey: key)
|
|
||||||
}
|
|
||||||
|
|
||||||
userDefaults.synchronize()
|
|
||||||
|
|
||||||
|
|
||||||
NotificationCenter.default.post(name: .backupRestored, object: nil)
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
print("Failed to import backup: \(error)")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func shareBackup() -> URL? {
|
|
||||||
return exportBackup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
//
|
|
||||||
// DateFormatter.swift
|
|
||||||
// Sulfur
|
|
||||||
//
|
|
||||||
// Created by Francesco on 25/05/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension DateFormatter {
|
|
||||||
static let backupFormatter: DateFormatter = {
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.dateFormat = "yyyy-MM-dd_HH-mm-ss"
|
|
||||||
return formatter
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
@ -13,5 +13,4 @@ extension Notification.Name {
|
||||||
static let ContinueWatchingDidUpdate = Notification.Name("ContinueWatchingDidUpdate")
|
static let ContinueWatchingDidUpdate = Notification.Name("ContinueWatchingDidUpdate")
|
||||||
static let DownloadManagerStatusUpdate = Notification.Name("DownloadManagerStatusUpdate")
|
static let DownloadManagerStatusUpdate = Notification.Name("DownloadManagerStatusUpdate")
|
||||||
static let modulesSyncDidComplete = Notification.Name("modulesSyncDidComplete")
|
static let modulesSyncDidComplete = Notification.Name("modulesSyncDidComplete")
|
||||||
static let backupRestored = Notification.Name("backupRestored")
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UniformTypeIdentifiers
|
|
||||||
|
|
||||||
struct SettingsViewData: View {
|
struct SettingsViewData: View {
|
||||||
@State private var showEraseAppDataAlert = false
|
@State private var showEraseAppDataAlert = false
|
||||||
|
|
@ -19,41 +18,14 @@ struct SettingsViewData: View {
|
||||||
@State private var movPkgSize: Int64 = 0
|
@State private var movPkgSize: Int64 = 0
|
||||||
@State private var showRemoveMovPkgAlert = false
|
@State private var showRemoveMovPkgAlert = false
|
||||||
|
|
||||||
|
// State bindings for cache settings
|
||||||
@State private var isMetadataCachingEnabled: Bool = true
|
@State private var isMetadataCachingEnabled: Bool = true
|
||||||
@State private var isImageCachingEnabled: Bool = true
|
@State private var isImageCachingEnabled: Bool = true
|
||||||
@State private var isMemoryOnlyMode: Bool = false
|
@State private var isMemoryOnlyMode: Bool = false
|
||||||
|
|
||||||
@StateObject private var backupManager = BackupManager.shared
|
|
||||||
@State private var showingExportSuccess = false
|
|
||||||
@State private var showingImportSuccess = false
|
|
||||||
@State private var showingError = false
|
|
||||||
@State private var errorMessage = ""
|
|
||||||
@State private var showingFilePicker = false
|
|
||||||
@State private var showingShareSheet = false
|
|
||||||
@State private var backupURL: URL?
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section(header: Text("Backup & Restore"), footer: Text("Create backups to transfer your data to another device or restore from a previous backup.")) {
|
// New section for cache settings
|
||||||
Button(action: exportBackup) {
|
|
||||||
HStack {
|
|
||||||
Image(systemName: "square.and.arrow.up")
|
|
||||||
.foregroundColor(.blue)
|
|
||||||
Text("Create Backup")
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(action: { showingFilePicker = true }) {
|
|
||||||
HStack {
|
|
||||||
Image(systemName: "square.and.arrow.down")
|
|
||||||
.foregroundColor(.green)
|
|
||||||
Text("Restore from Backup")
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Section(header: Text("Cache Settings"), footer: Text("Caching helps reduce network usage and load content faster. You can disable it to save storage space.")) {
|
Section(header: Text("Cache Settings"), footer: Text("Caching helps reduce network usage and load content faster. You can disable it to save storage space.")) {
|
||||||
Toggle("Enable Metadata Caching", isOn: $isMetadataCachingEnabled)
|
Toggle("Enable Metadata Caching", isOn: $isMetadataCachingEnabled)
|
||||||
.onChange(of: isMetadataCachingEnabled) { newValue in
|
.onChange(of: isMetadataCachingEnabled) { newValue in
|
||||||
|
|
@ -76,6 +48,7 @@ struct SettingsViewData: View {
|
||||||
.onChange(of: isMemoryOnlyMode) { newValue in
|
.onChange(of: isMemoryOnlyMode) { newValue in
|
||||||
MetadataCacheManager.shared.isMemoryOnlyMode = newValue
|
MetadataCacheManager.shared.isMemoryOnlyMode = newValue
|
||||||
if newValue {
|
if newValue {
|
||||||
|
// Clear disk cache when switching to memory-only
|
||||||
MetadataCacheManager.shared.clearAllCache()
|
MetadataCacheManager.shared.clearAllCache()
|
||||||
calculateCacheSize()
|
calculateCacheSize()
|
||||||
}
|
}
|
||||||
|
|
@ -145,42 +118,13 @@ struct SettingsViewData: View {
|
||||||
.navigationTitle("App Data")
|
.navigationTitle("App Data")
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
// Initialize state with current values
|
||||||
isMetadataCachingEnabled = MetadataCacheManager.shared.isCachingEnabled
|
isMetadataCachingEnabled = MetadataCacheManager.shared.isCachingEnabled
|
||||||
isImageCachingEnabled = KingfisherCacheManager.shared.isCachingEnabled
|
isImageCachingEnabled = KingfisherCacheManager.shared.isCachingEnabled
|
||||||
isMemoryOnlyMode = MetadataCacheManager.shared.isMemoryOnlyMode
|
isMemoryOnlyMode = MetadataCacheManager.shared.isMemoryOnlyMode
|
||||||
calculateCacheSize()
|
calculateCacheSize()
|
||||||
updateSizes()
|
updateSizes()
|
||||||
}
|
}
|
||||||
.fileImporter(
|
|
||||||
isPresented: $showingFilePicker,
|
|
||||||
allowedContentTypes: [UTType.json],
|
|
||||||
allowsMultipleSelection: false
|
|
||||||
) { result in
|
|
||||||
handleFileImport(result)
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $showingShareSheet) {
|
|
||||||
if let url = backupURL {
|
|
||||||
ShareSheet(items: [url])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.alert("Backup Created", isPresented: $showingExportSuccess) {
|
|
||||||
Button("Share") {
|
|
||||||
showingShareSheet = true
|
|
||||||
}
|
|
||||||
Button("OK") { }
|
|
||||||
} message: {
|
|
||||||
Text("Your backup has been created successfully. You can share it or find it in your Files app.")
|
|
||||||
}
|
|
||||||
.alert("Backup Restored", isPresented: $showingImportSuccess) {
|
|
||||||
Button("OK") { }
|
|
||||||
} message: {
|
|
||||||
Text("Your data has been restored successfully. The app will refresh with your restored settings.")
|
|
||||||
}
|
|
||||||
.alert("Error", isPresented: $showingError) {
|
|
||||||
Button("OK") { }
|
|
||||||
} message: {
|
|
||||||
Text(errorMessage)
|
|
||||||
}
|
|
||||||
.alert(isPresented: $showEraseAppDataAlert) {
|
.alert(isPresented: $showEraseAppDataAlert) {
|
||||||
Alert(
|
Alert(
|
||||||
title: Text("Erase App Data"),
|
title: Text("Erase App Data"),
|
||||||
|
|
@ -213,51 +157,24 @@ struct SettingsViewData: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func exportBackup() {
|
// Calculate and update the combined cache size
|
||||||
guard let url = backupManager.exportBackup() else {
|
|
||||||
errorMessage = "Failed to create backup file"
|
|
||||||
showingError = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
backupURL = url
|
|
||||||
showingExportSuccess = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleFileImport(_ result: Result<[URL], Error>) {
|
|
||||||
switch result {
|
|
||||||
case .success(let urls):
|
|
||||||
guard let url = urls.first else { return }
|
|
||||||
|
|
||||||
if backupManager.importBackup(from: url) {
|
|
||||||
showingImportSuccess = true
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
||||||
isMetadataCachingEnabled = MetadataCacheManager.shared.isCachingEnabled
|
|
||||||
isImageCachingEnabled = KingfisherCacheManager.shared.isCachingEnabled
|
|
||||||
isMemoryOnlyMode = MetadataCacheManager.shared.isMemoryOnlyMode
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
errorMessage = "Failed to restore backup. Please check if the file is a valid Sora backup."
|
|
||||||
showingError = true
|
|
||||||
}
|
|
||||||
|
|
||||||
case .failure(let error):
|
|
||||||
errorMessage = "Failed to read backup file: \(error.localizedDescription)"
|
|
||||||
showingError = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func calculateCacheSize() {
|
func calculateCacheSize() {
|
||||||
isCalculatingSize = true
|
isCalculatingSize = true
|
||||||
cacheSizeText = "Calculating..."
|
cacheSizeText = "Calculating..."
|
||||||
|
|
||||||
|
// Group all cache size calculations
|
||||||
DispatchQueue.global(qos: .background).async {
|
DispatchQueue.global(qos: .background).async {
|
||||||
var totalSize: Int64 = 0
|
var totalSize: Int64 = 0
|
||||||
|
|
||||||
|
// Get metadata cache size
|
||||||
let metadataSize = MetadataCacheManager.shared.getCacheSize()
|
let metadataSize = MetadataCacheManager.shared.getCacheSize()
|
||||||
totalSize += metadataSize
|
totalSize += metadataSize
|
||||||
|
|
||||||
|
// Get image cache size asynchronously
|
||||||
KingfisherCacheManager.shared.calculateCacheSize { imageSize in
|
KingfisherCacheManager.shared.calculateCacheSize { imageSize in
|
||||||
totalSize += Int64(imageSize)
|
totalSize += Int64(imageSize)
|
||||||
|
|
||||||
|
// Update the UI on the main thread
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.cacheSizeText = KingfisherCacheManager.formatCacheSize(UInt(totalSize))
|
self.cacheSizeText = KingfisherCacheManager.formatCacheSize(UInt(totalSize))
|
||||||
self.isCalculatingSize = false
|
self.isCalculatingSize = false
|
||||||
|
|
@ -266,9 +183,14 @@ struct SettingsViewData: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear all caches (both metadata and images)
|
||||||
func clearAllCaches() {
|
func clearAllCaches() {
|
||||||
|
// Clear metadata cache
|
||||||
MetadataCacheManager.shared.clearAllCache()
|
MetadataCacheManager.shared.clearAllCache()
|
||||||
|
|
||||||
|
// Clear image cache
|
||||||
KingfisherCacheManager.shared.clearCache {
|
KingfisherCacheManager.shared.clearCache {
|
||||||
|
// Update cache size after clearing
|
||||||
calculateCacheSize()
|
calculateCacheSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,13 +314,3 @@ struct SettingsViewData: View {
|
||||||
return totalSize
|
return totalSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ShareSheet: UIViewControllerRepresentable {
|
|
||||||
let items: [Any]
|
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> UIActivityViewController {
|
|
||||||
UIActivityViewController(activityItems: items, applicationActivities: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,6 @@
|
||||||
132AF1232D9995C300A0140B /* JSController-Details.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132AF1222D9995C300A0140B /* JSController-Details.swift */; };
|
132AF1232D9995C300A0140B /* JSController-Details.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132AF1222D9995C300A0140B /* JSController-Details.swift */; };
|
||||||
132AF1252D9995F900A0140B /* JSController-Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132AF1242D9995F900A0140B /* JSController-Search.swift */; };
|
132AF1252D9995F900A0140B /* JSController-Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132AF1242D9995F900A0140B /* JSController-Search.swift */; };
|
||||||
132FC5B32DE31DAE009A80F7 /* SettingsViewAlternateAppIconPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132FC5B22DE31DAD009A80F7 /* SettingsViewAlternateAppIconPicker.swift */; };
|
132FC5B32DE31DAE009A80F7 /* SettingsViewAlternateAppIconPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132FC5B22DE31DAD009A80F7 /* SettingsViewAlternateAppIconPicker.swift */; };
|
||||||
132FC5BB2DE333D3009A80F7 /* BackupData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132FC5BA2DE333D3009A80F7 /* BackupData.swift */; };
|
|
||||||
132FC5BD2DE333F4009A80F7 /* DateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132FC5BC2DE333F4009A80F7 /* DateFormatter.swift */; };
|
|
||||||
132FC5BF2DE33410009A80F7 /* BackupManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 132FC5BE2DE33410009A80F7 /* BackupManager.swift */; };
|
|
||||||
133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6D2D2BE2500075467E /* SoraApp.swift */; };
|
133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6D2D2BE2500075467E /* SoraApp.swift */; };
|
||||||
133D7C702D2BE2500075467E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6F2D2BE2500075467E /* ContentView.swift */; };
|
133D7C702D2BE2500075467E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6F2D2BE2500075467E /* ContentView.swift */; };
|
||||||
133D7C722D2BE2520075467E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 133D7C712D2BE2520075467E /* Assets.xcassets */; };
|
133D7C722D2BE2520075467E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 133D7C712D2BE2520075467E /* Assets.xcassets */; };
|
||||||
|
|
@ -101,9 +98,6 @@
|
||||||
132AF1222D9995C300A0140B /* JSController-Details.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Details.swift"; sourceTree = "<group>"; };
|
132AF1222D9995C300A0140B /* JSController-Details.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Details.swift"; sourceTree = "<group>"; };
|
||||||
132AF1242D9995F900A0140B /* JSController-Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Search.swift"; sourceTree = "<group>"; };
|
132AF1242D9995F900A0140B /* JSController-Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSController-Search.swift"; sourceTree = "<group>"; };
|
||||||
132FC5B22DE31DAD009A80F7 /* SettingsViewAlternateAppIconPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewAlternateAppIconPicker.swift; sourceTree = "<group>"; };
|
132FC5B22DE31DAD009A80F7 /* SettingsViewAlternateAppIconPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewAlternateAppIconPicker.swift; sourceTree = "<group>"; };
|
||||||
132FC5BA2DE333D3009A80F7 /* BackupData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupData.swift; sourceTree = "<group>"; };
|
|
||||||
132FC5BC2DE333F4009A80F7 /* DateFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatter.swift; sourceTree = "<group>"; };
|
|
||||||
132FC5BE2DE33410009A80F7 /* BackupManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupManager.swift; sourceTree = "<group>"; };
|
|
||||||
133D7C6A2D2BE2500075467E /* Sulfur.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sulfur.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
133D7C6A2D2BE2500075467E /* Sulfur.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sulfur.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
133D7C6D2D2BE2500075467E /* SoraApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoraApp.swift; sourceTree = "<group>"; };
|
133D7C6D2D2BE2500075467E /* SoraApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoraApp.swift; sourceTree = "<group>"; };
|
||||||
133D7C6F2D2BE2500075467E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
133D7C6F2D2BE2500075467E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -219,15 +213,6 @@
|
||||||
path = Analytics;
|
path = Analytics;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
132FC5B92DE333BD009A80F7 /* Backups */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
132FC5BA2DE333D3009A80F7 /* BackupData.swift */,
|
|
||||||
132FC5BE2DE33410009A80F7 /* BackupManager.swift */,
|
|
||||||
);
|
|
||||||
path = Backups;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
133D7C612D2BE2500075467E = {
|
133D7C612D2BE2500075467E = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
@ -309,10 +294,9 @@
|
||||||
133D7C852D2BE2640075467E /* Utils */ = {
|
133D7C852D2BE2640075467E /* Utils */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
13D842532D45266900EBBFA6 /* Drops */,
|
|
||||||
7205AEDA2DCCEF9500943F3F /* Cache */,
|
7205AEDA2DCCEF9500943F3F /* Cache */,
|
||||||
|
13D842532D45266900EBBFA6 /* Drops */,
|
||||||
1399FAD12D3AB33D00E97C31 /* Logger */,
|
1399FAD12D3AB33D00E97C31 /* Logger */,
|
||||||
132FC5B92DE333BD009A80F7 /* Backups */,
|
|
||||||
133D7C882D2BE2640075467E /* Modules */,
|
133D7C882D2BE2640075467E /* Modules */,
|
||||||
133D7C8A2D2BE2640075467E /* JSLoader */,
|
133D7C8A2D2BE2640075467E /* JSLoader */,
|
||||||
1327FBA52D758CEA00FC6689 /* Analytics */,
|
1327FBA52D758CEA00FC6689 /* Analytics */,
|
||||||
|
|
@ -331,7 +315,6 @@
|
||||||
73D164D42D8B5B340011A360 /* JavaScriptCore+Extensions.swift */,
|
73D164D42D8B5B340011A360 /* JavaScriptCore+Extensions.swift */,
|
||||||
136BBE7F2DB1038000906B5E /* Notification+Name.swift */,
|
136BBE7F2DB1038000906B5E /* Notification+Name.swift */,
|
||||||
1327FBA82D758DEA00FC6689 /* UIDevice+Model.swift */,
|
1327FBA82D758DEA00FC6689 /* UIDevice+Model.swift */,
|
||||||
132FC5BC2DE333F4009A80F7 /* DateFormatter.swift */,
|
|
||||||
13637B892DE0EA1100BDA2FC /* UserDefaults.swift */,
|
13637B892DE0EA1100BDA2FC /* UserDefaults.swift */,
|
||||||
133D7C872D2BE2640075467E /* URLSession.swift */,
|
133D7C872D2BE2640075467E /* URLSession.swift */,
|
||||||
1359ED132D76F49900C13034 /* finTopView.swift */,
|
1359ED132D76F49900C13034 /* finTopView.swift */,
|
||||||
|
|
@ -637,7 +620,6 @@
|
||||||
13DB7CC32D7D99C0004371D3 /* SubtitleSettingsManager.swift in Sources */,
|
13DB7CC32D7D99C0004371D3 /* SubtitleSettingsManager.swift in Sources */,
|
||||||
133D7C702D2BE2500075467E /* ContentView.swift in Sources */,
|
133D7C702D2BE2500075467E /* ContentView.swift in Sources */,
|
||||||
13D99CF72D4E73C300250A86 /* ModuleAdditionSettingsView.swift in Sources */,
|
13D99CF72D4E73C300250A86 /* ModuleAdditionSettingsView.swift in Sources */,
|
||||||
132FC5BF2DE33410009A80F7 /* BackupManager.swift in Sources */,
|
|
||||||
13C0E5EC2D5F85F800E7F619 /* ContinueWatchingItem.swift in Sources */,
|
13C0E5EC2D5F85F800E7F619 /* ContinueWatchingItem.swift in Sources */,
|
||||||
13CBA0882D60F19C00EFE70A /* VTTSubtitlesLoader.swift in Sources */,
|
13CBA0882D60F19C00EFE70A /* VTTSubtitlesLoader.swift in Sources */,
|
||||||
13EA2BD62D32D97400C1EBD7 /* Double+Extension.swift in Sources */,
|
13EA2BD62D32D97400C1EBD7 /* Double+Extension.swift in Sources */,
|
||||||
|
|
@ -680,8 +662,6 @@
|
||||||
72AC3A032DD4DAEB00C60B96 /* PerformanceMonitor.swift in Sources */,
|
72AC3A032DD4DAEB00C60B96 /* PerformanceMonitor.swift in Sources */,
|
||||||
72443C7F2DC8038300A61321 /* SettingsViewDownloads.swift in Sources */,
|
72443C7F2DC8038300A61321 /* SettingsViewDownloads.swift in Sources */,
|
||||||
13DB46922D900BCE008CBC03 /* SettingsViewTrackers.swift in Sources */,
|
13DB46922D900BCE008CBC03 /* SettingsViewTrackers.swift in Sources */,
|
||||||
132FC5BB2DE333D3009A80F7 /* BackupData.swift in Sources */,
|
|
||||||
132FC5BD2DE333F4009A80F7 /* DateFormatter.swift in Sources */,
|
|
||||||
7222485F2DCBAA2C00CABE2D /* DownloadModels.swift in Sources */,
|
7222485F2DCBAA2C00CABE2D /* DownloadModels.swift in Sources */,
|
||||||
722248602DCBAA2C00CABE2D /* M3U8StreamExtractor.swift in Sources */,
|
722248602DCBAA2C00CABE2D /* M3U8StreamExtractor.swift in Sources */,
|
||||||
13C0E5EA2D5F85EA00E7F619 /* ContinueWatchingManager.swift in Sources */,
|
13C0E5EA2D5F85EA00E7F619 /* ContinueWatchingManager.swift in Sources */,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue