Implement artwork download and cache

This commit is contained in:
diyaa
2026-05-30 09:43:14 +02:00
parent 8caf29f186
commit 7b1952794c
23 changed files with 1261 additions and 66 deletions
@@ -1,6 +1,38 @@
import Foundation
import VelodyDomain
public struct RemoteArtworkDTO: Codable, Hashable, Sendable {
public var artworkId: String
public var sha256: String
public var mimeType: String
public var width: Int?
public var height: Int?
public init(
artworkId: String,
sha256: String,
mimeType: String,
width: Int? = nil,
height: Int? = nil
) {
self.artworkId = artworkId
self.sha256 = sha256
self.mimeType = mimeType
self.width = width
self.height = height
}
public var remoteArtwork: RemoteArtwork {
RemoteArtwork(
artworkId: artworkId,
sha256: sha256,
mimeType: mimeType,
width: width,
height: height
)
}
}
public struct RemoteTrackDTO: Codable, Hashable, Sendable {
public var trackId: String
public var title: String
@@ -10,6 +42,7 @@ public struct RemoteTrackDTO: Codable, Hashable, Sendable {
public var assetId: String
public var createdAt: String
public var updatedAt: String
public var artwork: RemoteArtworkDTO?
public init(
trackId: String,
@@ -19,7 +52,8 @@ public struct RemoteTrackDTO: Codable, Hashable, Sendable {
sha256: String,
assetId: String,
createdAt: String,
updatedAt: String
updatedAt: String,
artwork: RemoteArtworkDTO? = nil
) {
self.trackId = trackId
self.title = title
@@ -29,6 +63,7 @@ public struct RemoteTrackDTO: Codable, Hashable, Sendable {
self.assetId = assetId
self.createdAt = createdAt
self.updatedAt = updatedAt
self.artwork = artwork
}
public var remoteTrack: RemoteTrack {
@@ -40,7 +75,8 @@ public struct RemoteTrackDTO: Codable, Hashable, Sendable {
sha256: sha256,
assetId: assetId,
createdAt: createdAt,
updatedAt: updatedAt
updatedAt: updatedAt,
artwork: artwork?.remoteArtwork
)
}
}
@@ -48,6 +48,11 @@ public protocol VelodyAPIClient: Sendable {
deviceId: String
) async throws -> Data
func downloadArtwork(
artworkId: String,
deviceId: String
) async throws -> Data
func prepareUpload(
_ payload: UploadPrepareRequest
) async throws -> UploadPrepareResponse
@@ -145,6 +150,23 @@ public struct URLSessionVelodyAPIClient: VelodyAPIClient {
return try await executeData(request)
}
public func downloadArtwork(
artworkId: String,
deviceId: String
) async throws -> Data {
let request = try buildRequest(
method: "GET",
pathComponents: ["api", "v1", "artwork", artworkId, "download"],
queryItems: [
URLQueryItem(name: "deviceId", value: deviceId),
],
bodyData: nil,
acceptType: "image/*"
)
return try await executeData(request)
}
public func prepareUpload(
_ payload: UploadPrepareRequest
) async throws -> UploadPrepareResponse {
@@ -450,6 +472,18 @@ public struct StubVelodyAPIClient: VelodyAPIClient {
])
}
public func downloadArtwork(
artworkId: String,
deviceId: String
) async throws -> Data {
_ = artworkId
_ = deviceId
return Data([
0x89, 0x50, 0x4E, 0x47,
])
}
public func prepareUpload(
_ payload: UploadPrepareRequest
) async throws -> UploadPrepareResponse {
@@ -1,5 +1,6 @@
import Foundation
import XCTest
import VelodyDomain
@testable import VelodyNetworking
final class RemoteLibraryDTOTests: XCTestCase {
@@ -12,7 +13,14 @@ final class RemoteLibraryDTOTests: XCTestCase {
sha256: String(repeating: "a", count: 64),
assetId: "asset-456",
createdAt: "2026-05-29T08:00:00.000Z",
updatedAt: "2026-05-29T08:05:00.000Z"
updatedAt: "2026-05-29T08:05:00.000Z",
artwork: RemoteArtworkDTO(
artworkId: "artwork-789",
sha256: String(repeating: "b", count: 64),
mimeType: "image/png",
width: 512,
height: 512
)
)
let track = dto.remoteTrack
@@ -23,6 +31,16 @@ final class RemoteLibraryDTOTests: XCTestCase {
XCTAssertEqual(track.durationSeconds, 245)
XCTAssertEqual(track.sha256, String(repeating: "a", count: 64))
XCTAssertEqual(track.assetId, "asset-456")
XCTAssertEqual(
track.artwork,
RemoteArtwork(
artworkId: "artwork-789",
sha256: String(repeating: "b", count: 64),
mimeType: "image/png",
width: 512,
height: 512
)
)
}
func testRemoteLibraryResponseDTODecodesFromAPIResponse() throws {
@@ -38,7 +56,14 @@ final class RemoteLibraryDTOTests: XCTestCase {
"sha256": "\(String(repeating: "a", count: 64))",
"assetId": "asset-456",
"createdAt": "2026-05-29T08:00:00.000Z",
"updatedAt": "2026-05-29T08:05:00.000Z"
"updatedAt": "2026-05-29T08:05:00.000Z",
"artwork": {
"artworkId": "artwork-789",
"sha256": "\(String(repeating: "b", count: 64))",
"mimeType": "image/png",
"width": 512,
"height": 512
}
}
]
}
@@ -50,5 +75,6 @@ final class RemoteLibraryDTOTests: XCTestCase {
XCTAssertEqual(decoded.tracks.count, 1)
XCTAssertEqual(decoded.tracks.first?.trackId, "track-123")
XCTAssertEqual(decoded.tracks.first?.durationSeconds, 245)
XCTAssertEqual(decoded.tracks.first?.artwork?.artworkId, "artwork-789")
}
}