Files
ABS-Client/ABS Client/Audiobookshelf swift/Services/ProgressSyncManager.swift
2026-05-17 21:06:59 +02:00

95 lines
2.9 KiB
Swift

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