452 lines
13 KiB
Swift
452 lines
13 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 LocalTrackArtwork: Codable, Hashable, Sendable {
|
|
public var localFilePath: String
|
|
public var sha256: String
|
|
public var mimeType: String
|
|
public var width: Int?
|
|
public var height: Int?
|
|
|
|
public init(
|
|
localFilePath: String,
|
|
sha256: String,
|
|
mimeType: String,
|
|
width: Int? = nil,
|
|
height: Int? = nil
|
|
) {
|
|
self.localFilePath = localFilePath
|
|
self.sha256 = sha256
|
|
self.mimeType = mimeType
|
|
self.width = width
|
|
self.height = height
|
|
}
|
|
}
|
|
|
|
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 artwork: LocalTrackArtwork?
|
|
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,
|
|
artwork: LocalTrackArtwork? = 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.artwork = artwork
|
|
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 deviceAccessToken: String
|
|
public var bootstrapToken: String
|
|
public var serverTime: String
|
|
|
|
public init(
|
|
deviceId: String,
|
|
deviceAccessToken: String,
|
|
bootstrapToken: String,
|
|
serverTime: String
|
|
) {
|
|
self.deviceId = deviceId
|
|
self.deviceAccessToken = deviceAccessToken
|
|
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 struct ArtworkPayload: Codable, Hashable, Sendable {
|
|
public var dataBase64: String
|
|
public var sha256: String
|
|
public var mimeType: String
|
|
public var width: Int?
|
|
public var height: Int?
|
|
|
|
public init(
|
|
dataBase64: String,
|
|
sha256: String,
|
|
mimeType: String,
|
|
width: Int? = nil,
|
|
height: Int? = nil
|
|
) {
|
|
self.dataBase64 = dataBase64
|
|
self.sha256 = sha256
|
|
self.mimeType = mimeType
|
|
self.width = width
|
|
self.height = height
|
|
}
|
|
}
|
|
|
|
public var title: String
|
|
public var artist: String
|
|
public var album: String?
|
|
public var durationMs: Int?
|
|
public var artwork: ArtworkPayload?
|
|
|
|
public init(
|
|
title: String,
|
|
artist: String,
|
|
album: String? = nil,
|
|
durationMs: Int? = nil,
|
|
artwork: ArtworkPayload? = nil
|
|
) {
|
|
self.title = title
|
|
self.artist = artist
|
|
self.album = album
|
|
self.durationMs = durationMs
|
|
self.artwork = artwork
|
|
}
|
|
}
|
|
|
|
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 cursor: SyncCursor
|
|
public var entityType: String
|
|
public var entityId: String
|
|
public var action: String
|
|
public var track: RemoteTrack?
|
|
public var deletedTrackId: String?
|
|
public var createdAt: String
|
|
|
|
public init(
|
|
cursor: SyncCursor,
|
|
entityType: String,
|
|
entityId: String,
|
|
action: String,
|
|
track: RemoteTrack? = nil,
|
|
deletedTrackId: String? = nil,
|
|
createdAt: String
|
|
) {
|
|
self.cursor = cursor
|
|
self.entityType = entityType
|
|
self.entityId = entityId
|
|
self.action = action
|
|
self.track = track
|
|
self.deletedTrackId = deletedTrackId
|
|
self.createdAt = createdAt
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case cursor
|
|
case entityType
|
|
case entityId
|
|
case action
|
|
case track
|
|
case deletedTrackId
|
|
case createdAt
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
cursor = SyncCursor(
|
|
value: try container.decode(String.self, forKey: .cursor)
|
|
)
|
|
entityType = try container.decode(String.self, forKey: .entityType)
|
|
entityId = try container.decode(String.self, forKey: .entityId)
|
|
action = try container.decode(String.self, forKey: .action)
|
|
track = try container.decodeIfPresent(RemoteTrack.self, forKey: .track)
|
|
deletedTrackId = try container.decodeIfPresent(String.self, forKey: .deletedTrackId)
|
|
createdAt = try container.decode(String.self, forKey: .createdAt)
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(cursor.value, forKey: .cursor)
|
|
try container.encode(entityType, forKey: .entityType)
|
|
try container.encode(entityId, forKey: .entityId)
|
|
try container.encode(action, forKey: .action)
|
|
try container.encodeIfPresent(track, forKey: .track)
|
|
try container.encodeIfPresent(deletedTrackId, forKey: .deletedTrackId)
|
|
try container.encode(createdAt, forKey: .createdAt)
|
|
}
|
|
}
|
|
|
|
public struct SyncBootstrapResponse: Codable, Hashable, Sendable {
|
|
public var nextCursor: SyncCursor
|
|
public var tracks: [RemoteTrack]
|
|
public var serverTime: String
|
|
|
|
public init(
|
|
nextCursor: SyncCursor,
|
|
tracks: [RemoteTrack],
|
|
serverTime: String
|
|
) {
|
|
self.nextCursor = nextCursor
|
|
self.tracks = tracks
|
|
self.serverTime = serverTime
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case nextCursor
|
|
case tracks
|
|
case serverTime
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
nextCursor = SyncCursor(
|
|
value: try container.decode(String.self, forKey: .nextCursor)
|
|
)
|
|
tracks = try container.decode([RemoteTrack].self, forKey: .tracks)
|
|
serverTime = try container.decode(String.self, forKey: .serverTime)
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(nextCursor.value, forKey: .nextCursor)
|
|
try container.encode(tracks, forKey: .tracks)
|
|
try container.encode(serverTime, forKey: .serverTime)
|
|
}
|
|
}
|
|
|
|
public struct SyncChangesResponse: Codable, Hashable, Sendable {
|
|
public var nextCursor: SyncCursor
|
|
public var hasMore: Bool
|
|
public var requiresBootstrap: Bool
|
|
public var reason: String?
|
|
public var events: [SyncEvent]
|
|
public var serverTime: String
|
|
|
|
public init(
|
|
nextCursor: SyncCursor,
|
|
hasMore: Bool,
|
|
requiresBootstrap: Bool,
|
|
reason: String? = nil,
|
|
events: [SyncEvent],
|
|
serverTime: String
|
|
) {
|
|
self.nextCursor = nextCursor
|
|
self.hasMore = hasMore
|
|
self.requiresBootstrap = requiresBootstrap
|
|
self.reason = reason
|
|
self.events = events
|
|
self.serverTime = serverTime
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case nextCursor
|
|
case hasMore
|
|
case requiresBootstrap
|
|
case reason
|
|
case events
|
|
case serverTime
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
nextCursor = SyncCursor(
|
|
value: try container.decode(String.self, forKey: .nextCursor)
|
|
)
|
|
hasMore = try container.decode(Bool.self, forKey: .hasMore)
|
|
requiresBootstrap = try container.decode(Bool.self, forKey: .requiresBootstrap)
|
|
reason = try container.decodeIfPresent(String.self, forKey: .reason)
|
|
events = try container.decode([SyncEvent].self, forKey: .events)
|
|
serverTime = try container.decode(String.self, forKey: .serverTime)
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(nextCursor.value, forKey: .nextCursor)
|
|
try container.encode(hasMore, forKey: .hasMore)
|
|
try container.encode(requiresBootstrap, forKey: .requiresBootstrap)
|
|
try container.encodeIfPresent(reason, forKey: .reason)
|
|
try container.encode(events, forKey: .events)
|
|
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
|
|
}
|
|
}
|