Merge iOS and Mac app into one
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import Foundation
|
||||
import Observation
|
||||
|
||||
@Observable
|
||||
@MainActor
|
||||
final class ProgressSyncManager {
|
||||
private let client: ABSClient
|
||||
private(set) var queuedCount: Int = 0
|
||||
private(set) var lastSyncError: String?
|
||||
|
||||
/// Latest progress per itemId, persisted to disk.
|
||||
private var queue: [String: PlaybackProgress] = [:]
|
||||
|
||||
private let queueFile: URL
|
||||
|
||||
init(client: ABSClient) {
|
||||
self.client = client
|
||||
let dir = AppPaths.supportDirectory
|
||||
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
|
||||
self.queueFile = dir.appendingPathComponent("progress-queue.json")
|
||||
loadQueue()
|
||||
}
|
||||
|
||||
func report(itemId: String, episodeId: String? = nil, currentTime: Double, duration: Double, isFinished: Bool, isOnline: Bool) async {
|
||||
let progress = PlaybackProgress(
|
||||
itemId: itemId,
|
||||
episodeId: episodeId,
|
||||
currentTime: currentTime,
|
||||
duration: duration,
|
||||
isFinished: isFinished,
|
||||
updatedAt: Date()
|
||||
)
|
||||
let key = progress.syncKey
|
||||
|
||||
if isOnline {
|
||||
do {
|
||||
try await client.saveProgress(progress)
|
||||
queue.removeValue(forKey: key)
|
||||
persist()
|
||||
lastSyncError = nil
|
||||
return
|
||||
} catch {
|
||||
lastSyncError = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
queue[key] = progress
|
||||
persist()
|
||||
}
|
||||
|
||||
func drain() async {
|
||||
guard !queue.isEmpty else { return }
|
||||
let snapshot = queue
|
||||
for (id, progress) in snapshot {
|
||||
do {
|
||||
try await client.saveProgress(progress)
|
||||
queue.removeValue(forKey: id)
|
||||
} catch {
|
||||
lastSyncError = error.localizedDescription
|
||||
break
|
||||
}
|
||||
}
|
||||
persist()
|
||||
}
|
||||
|
||||
private func loadQueue() {
|
||||
guard let data = try? Data(contentsOf: queueFile),
|
||||
let decoded = try? JSONDecoder().decode([String: PlaybackProgress].self, from: data) else { return }
|
||||
queue = decoded
|
||||
queuedCount = decoded.count
|
||||
}
|
||||
|
||||
private func persist() {
|
||||
queuedCount = queue.count
|
||||
do {
|
||||
let data = try JSONEncoder().encode(queue)
|
||||
try data.write(to: queueFile, options: .atomic)
|
||||
} catch {
|
||||
lastSyncError = "Queue konnte nicht gespeichert werden: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum AppPaths {
|
||||
static var supportDirectory: URL {
|
||||
let base = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
|
||||
?? URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Library/Application Support")
|
||||
return base.appendingPathComponent("AudiobookshelfClient", isDirectory: true)
|
||||
}
|
||||
|
||||
static var downloadsDirectory: URL {
|
||||
supportDirectory.appendingPathComponent("downloads", isDirectory: true)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user