Revert "test backup system"
Some checks are pending
Build and Release / Build IPA (push) Waiting to run
Build and Release / Build Mac Catalyst (push) Waiting to run

This reverts commit 18075308dd.
This commit is contained in:
Francesco 2025-05-25 13:26:48 +02:00
parent 18075308dd
commit 30aa66bf2a
6 changed files with 16 additions and 344 deletions

View file

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

View file

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

View file

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

View file

@ -13,5 +13,4 @@ extension Notification.Name {
static let ContinueWatchingDidUpdate = Notification.Name("ContinueWatchingDidUpdate")
static let DownloadManagerStatusUpdate = Notification.Name("DownloadManagerStatusUpdate")
static let modulesSyncDidComplete = Notification.Name("modulesSyncDidComplete")
static let backupRestored = Notification.Name("backupRestored")
}

View file

@ -6,7 +6,6 @@
//
import SwiftUI
import UniformTypeIdentifiers
struct SettingsViewData: View {
@State private var showEraseAppDataAlert = false
@ -19,41 +18,14 @@ struct SettingsViewData: View {
@State private var movPkgSize: Int64 = 0
@State private var showRemoveMovPkgAlert = false
// State bindings for cache settings
@State private var isMetadataCachingEnabled: Bool = true
@State private var isImageCachingEnabled: Bool = true
@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 {
Form {
Section(header: Text("Backup & Restore"), footer: Text("Create backups to transfer your data to another device or restore from a previous backup.")) {
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()
}
}
}
// New section for cache settings
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)
.onChange(of: isMetadataCachingEnabled) { newValue in
@ -76,6 +48,7 @@ struct SettingsViewData: View {
.onChange(of: isMemoryOnlyMode) { newValue in
MetadataCacheManager.shared.isMemoryOnlyMode = newValue
if newValue {
// Clear disk cache when switching to memory-only
MetadataCacheManager.shared.clearAllCache()
calculateCacheSize()
}
@ -145,42 +118,13 @@ struct SettingsViewData: View {
.navigationTitle("App Data")
.navigationViewStyle(StackNavigationViewStyle())
.onAppear {
// Initialize state with current values
isMetadataCachingEnabled = MetadataCacheManager.shared.isCachingEnabled
isImageCachingEnabled = KingfisherCacheManager.shared.isCachingEnabled
isMemoryOnlyMode = MetadataCacheManager.shared.isMemoryOnlyMode
calculateCacheSize()
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(
title: Text("Erase App Data"),
@ -213,51 +157,24 @@ struct SettingsViewData: View {
}
}
private func exportBackup() {
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
}
}
// Calculate and update the combined cache size
func calculateCacheSize() {
isCalculatingSize = true
cacheSizeText = "Calculating..."
// Group all cache size calculations
DispatchQueue.global(qos: .background).async {
var totalSize: Int64 = 0
// Get metadata cache size
let metadataSize = MetadataCacheManager.shared.getCacheSize()
totalSize += metadataSize
// Get image cache size asynchronously
KingfisherCacheManager.shared.calculateCacheSize { imageSize in
totalSize += Int64(imageSize)
// Update the UI on the main thread
DispatchQueue.main.async {
self.cacheSizeText = KingfisherCacheManager.formatCacheSize(UInt(totalSize))
self.isCalculatingSize = false
@ -266,9 +183,14 @@ struct SettingsViewData: View {
}
}
// Clear all caches (both metadata and images)
func clearAllCaches() {
// Clear metadata cache
MetadataCacheManager.shared.clearAllCache()
// Clear image cache
KingfisherCacheManager.shared.clearCache {
// Update cache size after clearing
calculateCacheSize()
}
@ -392,13 +314,3 @@ struct SettingsViewData: View {
return totalSize
}
}
struct ShareSheet: UIViewControllerRepresentable {
let items: [Any]
func makeUIViewController(context: Context) -> UIActivityViewController {
UIActivityViewController(activityItems: items, applicationActivities: nil)
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}

View file

@ -18,9 +18,6 @@
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 */; };
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 */; };
133D7C702D2BE2500075467E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6F2D2BE2500075467E /* ContentView.swift */; };
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>"; };
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>"; };
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; };
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>"; };
@ -219,15 +213,6 @@
path = Analytics;
sourceTree = "<group>";
};
132FC5B92DE333BD009A80F7 /* Backups */ = {
isa = PBXGroup;
children = (
132FC5BA2DE333D3009A80F7 /* BackupData.swift */,
132FC5BE2DE33410009A80F7 /* BackupManager.swift */,
);
path = Backups;
sourceTree = "<group>";
};
133D7C612D2BE2500075467E = {
isa = PBXGroup;
children = (
@ -309,10 +294,9 @@
133D7C852D2BE2640075467E /* Utils */ = {
isa = PBXGroup;
children = (
13D842532D45266900EBBFA6 /* Drops */,
7205AEDA2DCCEF9500943F3F /* Cache */,
13D842532D45266900EBBFA6 /* Drops */,
1399FAD12D3AB33D00E97C31 /* Logger */,
132FC5B92DE333BD009A80F7 /* Backups */,
133D7C882D2BE2640075467E /* Modules */,
133D7C8A2D2BE2640075467E /* JSLoader */,
1327FBA52D758CEA00FC6689 /* Analytics */,
@ -331,7 +315,6 @@
73D164D42D8B5B340011A360 /* JavaScriptCore+Extensions.swift */,
136BBE7F2DB1038000906B5E /* Notification+Name.swift */,
1327FBA82D758DEA00FC6689 /* UIDevice+Model.swift */,
132FC5BC2DE333F4009A80F7 /* DateFormatter.swift */,
13637B892DE0EA1100BDA2FC /* UserDefaults.swift */,
133D7C872D2BE2640075467E /* URLSession.swift */,
1359ED132D76F49900C13034 /* finTopView.swift */,
@ -637,7 +620,6 @@
13DB7CC32D7D99C0004371D3 /* SubtitleSettingsManager.swift in Sources */,
133D7C702D2BE2500075467E /* ContentView.swift in Sources */,
13D99CF72D4E73C300250A86 /* ModuleAdditionSettingsView.swift in Sources */,
132FC5BF2DE33410009A80F7 /* BackupManager.swift in Sources */,
13C0E5EC2D5F85F800E7F619 /* ContinueWatchingItem.swift in Sources */,
13CBA0882D60F19C00EFE70A /* VTTSubtitlesLoader.swift in Sources */,
13EA2BD62D32D97400C1EBD7 /* Double+Extension.swift in Sources */,
@ -680,8 +662,6 @@
72AC3A032DD4DAEB00C60B96 /* PerformanceMonitor.swift in Sources */,
72443C7F2DC8038300A61321 /* SettingsViewDownloads.swift in Sources */,
13DB46922D900BCE008CBC03 /* SettingsViewTrackers.swift in Sources */,
132FC5BB2DE333D3009A80F7 /* BackupData.swift in Sources */,
132FC5BD2DE333F4009A80F7 /* DateFormatter.swift in Sources */,
7222485F2DCBAA2C00CABE2D /* DownloadModels.swift in Sources */,
722248602DCBAA2C00CABE2D /* M3U8StreamExtractor.swift in Sources */,
13C0E5EA2D5F85EA00E7F619 /* ContinueWatchingManager.swift in Sources */,