94 lines
3.0 KiB
Swift
94 lines
3.0 KiB
Swift
import Foundation
|
|
import VelodyDomain
|
|
|
|
public protocol RemoteTrackDownloadStateStore: Actor {
|
|
func loadDownloadStates() async throws -> [RemoteTrackDownloadState]
|
|
func saveDownloadStates(_ states: [RemoteTrackDownloadState]) async throws
|
|
}
|
|
|
|
public extension RemoteTrackDownloadStateStore {
|
|
func saveDownloadState(_ state: RemoteTrackDownloadState) async throws {
|
|
var states = try await loadDownloadStates()
|
|
|
|
if let existingIndex = states.firstIndex(where: { $0.remoteTrackId == state.remoteTrackId }) {
|
|
states[existingIndex] = state
|
|
} else {
|
|
states.append(state)
|
|
}
|
|
|
|
try await saveDownloadStates(states)
|
|
}
|
|
}
|
|
|
|
public actor FileRemoteTrackDownloadStateStore: RemoteTrackDownloadStateStore {
|
|
private let fileURL: URL
|
|
private let fileManager: FileManager
|
|
private let encoder = JSONEncoder()
|
|
private let decoder = JSONDecoder()
|
|
|
|
public init(
|
|
fileURL: URL? = nil,
|
|
fileManager: FileManager = .default
|
|
) throws {
|
|
self.fileManager = fileManager
|
|
if let fileURL {
|
|
self.fileURL = fileURL
|
|
} else {
|
|
self.fileURL = try Self.defaultFileURL(fileManager: fileManager)
|
|
}
|
|
encoder.dateEncodingStrategy = .iso8601
|
|
decoder.dateDecodingStrategy = .iso8601
|
|
}
|
|
|
|
public func loadDownloadStates() async throws -> [RemoteTrackDownloadState] {
|
|
guard fileManager.fileExists(atPath: fileURL.path) else {
|
|
return []
|
|
}
|
|
|
|
let data = try Data(contentsOf: fileURL)
|
|
return try decoder.decode([RemoteTrackDownloadState].self, from: data)
|
|
}
|
|
|
|
public func saveDownloadStates(_ states: [RemoteTrackDownloadState]) async throws {
|
|
try fileManager.createDirectory(
|
|
at: fileURL.deletingLastPathComponent(),
|
|
withIntermediateDirectories: true
|
|
)
|
|
|
|
let sortedStates = states.sorted { lhs, rhs in
|
|
lhs.remoteTrackId.localizedCaseInsensitiveCompare(rhs.remoteTrackId) == .orderedAscending
|
|
}
|
|
let data = try encoder.encode(sortedStates)
|
|
try data.write(to: fileURL, options: .atomic)
|
|
}
|
|
|
|
private static func defaultFileURL(fileManager: FileManager) throws -> URL {
|
|
guard let applicationSupportURL = fileManager.urls(
|
|
for: .applicationSupportDirectory,
|
|
in: .userDomainMask
|
|
).first else {
|
|
throw CocoaError(.fileNoSuchFile)
|
|
}
|
|
|
|
return applicationSupportURL
|
|
.appendingPathComponent("Velody", isDirectory: true)
|
|
.appendingPathComponent("remote-download-states.json")
|
|
}
|
|
}
|
|
|
|
public actor InMemoryRemoteTrackDownloadStateStore: RemoteTrackDownloadStateStore {
|
|
private var states: [RemoteTrackDownloadState]
|
|
|
|
public init(states: [RemoteTrackDownloadState] = []) {
|
|
self.states = states
|
|
}
|
|
|
|
public func loadDownloadStates() async throws -> [RemoteTrackDownloadState] {
|
|
states
|
|
}
|
|
|
|
public func saveDownloadStates(_ states: [RemoteTrackDownloadState]) async throws {
|
|
self.states = states
|
|
}
|
|
}
|