Version one on the App Store

This commit is contained in:
2026-04-05 19:44:30 +02:00
parent c780be089d
commit 3ebf1763ed
26 changed files with 2088 additions and 842 deletions
@@ -16,6 +16,7 @@ final class MAPlayerManager {
// MARK: - Properties
private(set) var players: [String: MAPlayer] = [:]
private(set) var playerQueues: [String: MAPlayerQueue] = [:]
private(set) var queues: [String: [MAQueueItem]] = [:]
private weak var service: MAService?
@@ -77,36 +78,43 @@ final class MAPlayerManager {
private func handlePlayerUpdated(_ event: MAEvent) async {
guard let data = event.data else { return }
do {
let player = try data.decode(as: MAPlayer.self)
await MainActor.run {
players[player.playerId] = player
logger.debug("Updated player: \(player.name) - \(player.state.rawValue)")
logger.debug("Updated player: \(player.name) state=\(player.state.rawValue) item=\(player.currentItem?.name ?? "nil")")
}
} catch {
logger.error("Failed to decode player update: \(error.localizedDescription)")
logger.error("Failed to decode player_updated event: \(error)")
}
}
private func handleQueueUpdated(_ event: MAEvent) async {
guard let data = event.data,
let dict = data.value as? [String: Any],
let queueId = dict["queue_id"] as? String else {
guard let data = event.data else { return }
// The event data IS the PlayerQueue object decode it directly for current_item
if let queue = try? data.decode(as: MAPlayerQueue.self), !queue.queueId.isEmpty {
await MainActor.run {
playerQueues[queue.queueId] = queue
logger.debug("Updated queue state for player \(queue.queueId), current: \(queue.currentItem?.name ?? "nil")")
}
return
}
// Reload queue for this player
guard let service else { return }
// Fallback: extract queue_id and fetch from API
guard let dict = data.value as? [String: Any],
let queueId = dict["queue_id"] as? String,
let service else { return }
do {
let items = try await service.getQueue(playerId: queueId)
let queue = try await service.getPlayerQueue(playerId: queueId)
await MainActor.run {
queues[queueId] = items
logger.debug("Updated queue for player \(queueId): \(items.count) items")
playerQueues[queueId] = queue
logger.debug("Fetched queue state for player \(queueId), current: \(queue.currentItem?.name ?? "nil")")
}
} catch {
logger.error("Failed to reload queue: \(error.localizedDescription)")
logger.error("Failed to reload queue state: \(error.localizedDescription)")
}
}
@@ -117,18 +125,40 @@ final class MAPlayerManager {
// MARK: - Data Loading
/// Load all players
/// Load all players and their queue states
func loadPlayers() async throws {
guard let service else {
guard let service else {
throw MAWebSocketClient.ClientError.notConnected
}
logger.info("Loading players")
let playerList = try await service.getPlayers()
await MainActor.run {
players = Dictionary(uniqueKeysWithValues: playerList.map { ($0.playerId, $0) })
}
// Concurrently fetch queue state for each player to get current_item
var queueResults: [String: MAPlayerQueue] = [:]
await withTaskGroup(of: (String, MAPlayerQueue?).self) { group in
for player in playerList {
let pid = player.playerId
group.addTask {
let queue = try? await service.getPlayerQueue(playerId: pid)
return (pid, queue)
}
}
for await (pid, queue) in group {
if let queue { queueResults[pid] = queue }
}
}
await MainActor.run {
for (pid, queue) in queueResults {
playerQueues[pid] = queue
}
logger.info("Loaded queue states for \(queueResults.count) players")
}
}
/// Load queue for specific player
@@ -189,6 +219,16 @@ final class MAPlayerManager {
try await service.setVolume(playerId: playerId, level: level)
}
func syncPlayer(playerId: String, targetPlayerId: String) async throws {
guard let service else { throw MAWebSocketClient.ClientError.notConnected }
try await service.syncPlayer(playerId: playerId, targetPlayerId: targetPlayerId)
}
func unsyncPlayer(playerId: String) async throws {
guard let service else { throw MAWebSocketClient.ClientError.notConnected }
try await service.unsyncPlayer(playerId: playerId)
}
func playMedia(playerId: String, uri: String) async throws {
guard let service else {
throw MAWebSocketClient.ClientError.notConnected