338 lines
9.1 KiB
Swift
338 lines
9.1 KiB
Swift
import Foundation
|
|
|
|
public enum DevicePlatform: String, Codable, Sendable, CaseIterable {
|
|
case macos = "MACOS"
|
|
case iphone = "IPHONE"
|
|
}
|
|
|
|
public enum LocalUploadStatus: String, Codable, Hashable, Sendable, CaseIterable {
|
|
case localOnly
|
|
case preparing
|
|
case uploading
|
|
case uploaded
|
|
case failed
|
|
}
|
|
|
|
public struct LibraryTrack: Identifiable, Codable, Hashable, Sendable {
|
|
public let id: String
|
|
public var title: String
|
|
public var artist: String
|
|
public var album: String?
|
|
public var durationSeconds: Double?
|
|
public var localFilePath: String
|
|
public var sha256: String?
|
|
public var uploadStatus: LocalUploadStatus?
|
|
public var remoteTrackId: String?
|
|
public var lastUploadError: String?
|
|
|
|
public init(
|
|
id: String = UUID().uuidString,
|
|
title: String,
|
|
artist: String = "Unknown Artist",
|
|
album: String? = nil,
|
|
durationSeconds: Double? = nil,
|
|
localFilePath: String = "",
|
|
sha256: String? = nil,
|
|
uploadStatus: LocalUploadStatus? = nil,
|
|
remoteTrackId: String? = nil,
|
|
lastUploadError: String? = nil
|
|
) {
|
|
self.id = id
|
|
self.title = title
|
|
self.artist = artist
|
|
self.album = album
|
|
self.durationSeconds = durationSeconds
|
|
self.localFilePath = localFilePath
|
|
self.sha256 = sha256
|
|
self.uploadStatus = uploadStatus
|
|
self.remoteTrackId = remoteTrackId
|
|
self.lastUploadError = lastUploadError
|
|
}
|
|
}
|
|
|
|
public struct DeviceRegistrationPayload: Codable, Hashable, Sendable {
|
|
public var platform: DevicePlatform
|
|
public var deviceName: String
|
|
public var appVersion: String
|
|
|
|
public init(
|
|
platform: DevicePlatform,
|
|
deviceName: String,
|
|
appVersion: String
|
|
) {
|
|
self.platform = platform
|
|
self.deviceName = deviceName
|
|
self.appVersion = appVersion
|
|
}
|
|
}
|
|
|
|
public struct DeviceRegistrationResponse: Codable, Hashable, Sendable {
|
|
public var deviceId: String
|
|
public var bootstrapToken: String
|
|
public var serverTime: String
|
|
|
|
public init(
|
|
deviceId: String,
|
|
bootstrapToken: String,
|
|
serverTime: String
|
|
) {
|
|
self.deviceId = deviceId
|
|
self.bootstrapToken = bootstrapToken
|
|
self.serverTime = serverTime
|
|
}
|
|
}
|
|
|
|
public struct DeviceHeartbeatPayload: Codable, Hashable, Sendable {
|
|
public var deviceId: String
|
|
public var appVersion: String
|
|
|
|
public init(
|
|
deviceId: String,
|
|
appVersion: String
|
|
) {
|
|
self.deviceId = deviceId
|
|
self.appVersion = appVersion
|
|
}
|
|
}
|
|
|
|
public struct DeviceHeartbeatResponse: Codable, Hashable, Sendable {
|
|
public var ok: Bool
|
|
public var serverTime: String
|
|
|
|
public init(
|
|
ok: Bool,
|
|
serverTime: String
|
|
) {
|
|
self.ok = ok
|
|
self.serverTime = serverTime
|
|
}
|
|
}
|
|
|
|
public enum UploadPrepareStatus: String, Codable, Hashable, Sendable {
|
|
case exists
|
|
case uploadRequired = "upload_required"
|
|
}
|
|
|
|
public struct UploadPrepareRequest: Codable, Hashable, Sendable {
|
|
public var deviceId: String
|
|
public var sha256: String
|
|
public var originalFilename: String
|
|
public var sizeBytes: Int
|
|
|
|
public init(
|
|
deviceId: String,
|
|
sha256: String,
|
|
originalFilename: String,
|
|
sizeBytes: Int
|
|
) {
|
|
self.deviceId = deviceId
|
|
self.sha256 = sha256
|
|
self.originalFilename = originalFilename
|
|
self.sizeBytes = sizeBytes
|
|
}
|
|
}
|
|
|
|
public struct UploadPrepareResponse: Codable, Hashable, Sendable {
|
|
public var status: UploadPrepareStatus
|
|
public var uploadId: String?
|
|
public var nextOffset: Int?
|
|
public var trackId: String?
|
|
public var assetId: String?
|
|
|
|
public init(
|
|
status: UploadPrepareStatus,
|
|
uploadId: String? = nil,
|
|
nextOffset: Int? = nil,
|
|
trackId: String? = nil,
|
|
assetId: String? = nil
|
|
) {
|
|
self.status = status
|
|
self.uploadId = uploadId
|
|
self.nextOffset = nextOffset
|
|
self.trackId = trackId
|
|
self.assetId = assetId
|
|
}
|
|
}
|
|
|
|
public enum UploadSessionState: String, Codable, Hashable, Sendable {
|
|
case pending = "PENDING"
|
|
case readyToUpload = "READY_TO_UPLOAD"
|
|
case completed = "COMPLETED"
|
|
case failed = "FAILED"
|
|
}
|
|
|
|
public struct UploadSessionStatusResponse: Codable, Hashable, Sendable {
|
|
public var uploadId: String
|
|
public var status: UploadSessionState
|
|
public var receivedBytes: String
|
|
public var expectedSizeBytes: String
|
|
public var nextOffset: String
|
|
public var finalizedAt: String?
|
|
|
|
public init(
|
|
uploadId: String,
|
|
status: UploadSessionState,
|
|
receivedBytes: String,
|
|
expectedSizeBytes: String,
|
|
nextOffset: String,
|
|
finalizedAt: String? = nil
|
|
) {
|
|
self.uploadId = uploadId
|
|
self.status = status
|
|
self.receivedBytes = receivedBytes
|
|
self.expectedSizeBytes = expectedSizeBytes
|
|
self.nextOffset = nextOffset
|
|
self.finalizedAt = finalizedAt
|
|
}
|
|
}
|
|
|
|
public struct UploadFinalizeRequest: Codable, Hashable, Sendable {
|
|
public var title: String
|
|
public var artist: String
|
|
public var album: String?
|
|
public var durationMs: Int?
|
|
|
|
public init(
|
|
title: String,
|
|
artist: String,
|
|
album: String? = nil,
|
|
durationMs: Int? = nil
|
|
) {
|
|
self.title = title
|
|
self.artist = artist
|
|
self.album = album
|
|
self.durationMs = durationMs
|
|
}
|
|
}
|
|
|
|
public struct UploadFinalizeResponse: Codable, Hashable, Sendable {
|
|
public var trackId: String
|
|
public var assetId: String
|
|
|
|
public init(
|
|
trackId: String,
|
|
assetId: String
|
|
) {
|
|
self.trackId = trackId
|
|
self.assetId = assetId
|
|
}
|
|
}
|
|
|
|
public struct SyncCursor: Codable, Hashable, Sendable {
|
|
public var value: String
|
|
|
|
public init(value: String = "0") {
|
|
self.value = value
|
|
}
|
|
}
|
|
|
|
public struct SyncEvent: Codable, Hashable, Sendable {
|
|
public var entityType: String
|
|
public var entityId: String
|
|
public var action: String
|
|
public var eventId: String
|
|
|
|
public init(
|
|
entityType: String,
|
|
entityId: String,
|
|
action: String,
|
|
eventId: String
|
|
) {
|
|
self.entityType = entityType
|
|
self.entityId = entityId
|
|
self.action = action
|
|
self.eventId = eventId
|
|
}
|
|
}
|
|
|
|
public struct SyncBootstrapResponse: Codable, Hashable, Sendable {
|
|
public var nextCursor: SyncCursor
|
|
public var tracks: [LibraryTrack]
|
|
public var events: [SyncEvent]
|
|
public var deletedTrackIds: [String]
|
|
public var serverTime: String
|
|
|
|
public init(
|
|
nextCursor: SyncCursor,
|
|
tracks: [LibraryTrack],
|
|
events: [SyncEvent],
|
|
deletedTrackIds: [String],
|
|
serverTime: String
|
|
) {
|
|
self.nextCursor = nextCursor
|
|
self.tracks = tracks
|
|
self.events = events
|
|
self.deletedTrackIds = deletedTrackIds
|
|
self.serverTime = serverTime
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case nextCursor
|
|
case tracks
|
|
case events
|
|
case deletedTrackIds
|
|
case serverTime
|
|
}
|
|
|
|
private struct WireTrack: Codable {
|
|
var id: String?
|
|
var title: String?
|
|
var artist: String?
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
let wireTracks = try container.decode([WireTrack].self, forKey: .tracks)
|
|
|
|
nextCursor = SyncCursor(
|
|
value: try container.decode(String.self, forKey: .nextCursor)
|
|
)
|
|
tracks = wireTracks.map { track in
|
|
LibraryTrack(
|
|
id: track.id ?? UUID().uuidString,
|
|
title: track.title ?? "Unknown Title",
|
|
artist: track.artist ?? "Unknown Artist",
|
|
album: nil,
|
|
durationSeconds: nil,
|
|
localFilePath: "",
|
|
sha256: nil
|
|
)
|
|
}
|
|
events = try container.decode([SyncEvent].self, forKey: .events)
|
|
deletedTrackIds = try container.decode([String].self, forKey: .deletedTrackIds)
|
|
serverTime = try container.decode(String.self, forKey: .serverTime)
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
let wireTracks = tracks.map { track in
|
|
WireTrack(
|
|
id: track.id,
|
|
title: track.title,
|
|
artist: track.artist
|
|
)
|
|
}
|
|
|
|
try container.encode(nextCursor.value, forKey: .nextCursor)
|
|
try container.encode(wireTracks, forKey: .tracks)
|
|
try container.encode(events, forKey: .events)
|
|
try container.encode(deletedTrackIds, forKey: .deletedTrackIds)
|
|
try container.encode(serverTime, forKey: .serverTime)
|
|
}
|
|
}
|
|
|
|
public struct ServerEnvironment: Codable, Hashable, Sendable {
|
|
public static let defaultLocalBaseURL = URL(string: "http://localhost:3007")!
|
|
|
|
public var baseURL: URL
|
|
public var appVersion: String
|
|
|
|
public init(
|
|
baseURL: URL = ServerEnvironment.defaultLocalBaseURL,
|
|
appVersion: String
|
|
) {
|
|
self.baseURL = baseURL
|
|
self.appVersion = appVersion
|
|
}
|
|
}
|