Initial Commit
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// MAPlayerManager.swift
|
||||
// Mobile Music Assistant
|
||||
//
|
||||
// Created by Sven Hanold on 26.03.26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
private let logger = Logger(subsystem: "com.musicassistant.mobile", category: "PlayerManager")
|
||||
|
||||
/// Manages player state and real-time updates
|
||||
@Observable
|
||||
final class MAPlayerManager {
|
||||
// MARK: - Properties
|
||||
|
||||
private(set) var players: [String: MAPlayer] = [:]
|
||||
private(set) var queues: [String: [MAQueueItem]] = [:]
|
||||
|
||||
private weak var service: MAService?
|
||||
private var eventTask: Task<Void, Never>?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(service: MAService?) {
|
||||
self.service = service
|
||||
}
|
||||
|
||||
func setService(_ service: MAService) {
|
||||
self.service = service
|
||||
}
|
||||
|
||||
deinit {
|
||||
stopListening()
|
||||
}
|
||||
|
||||
// MARK: - Event Listening
|
||||
|
||||
/// Start listening to player events
|
||||
func startListening() {
|
||||
guard eventTask == nil, let service else { return }
|
||||
|
||||
logger.info("Starting event listener")
|
||||
|
||||
eventTask = Task {
|
||||
for await event in service.webSocketClient.eventStream {
|
||||
await handleEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stop listening to events
|
||||
func stopListening() {
|
||||
logger.info("Stopping event listener")
|
||||
eventTask?.cancel()
|
||||
eventTask = nil
|
||||
}
|
||||
|
||||
private func handleEvent(_ event: MAEvent) async {
|
||||
logger.debug("Handling event: \(event.event)")
|
||||
|
||||
switch event.event {
|
||||
case "player_updated":
|
||||
await handlePlayerUpdated(event)
|
||||
|
||||
case "queue_updated":
|
||||
await handleQueueUpdated(event)
|
||||
|
||||
case "queue_items_updated":
|
||||
await handleQueueItemsUpdated(event)
|
||||
|
||||
default:
|
||||
logger.debug("Unhandled event: \(event.event)")
|
||||
}
|
||||
}
|
||||
|
||||
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)")
|
||||
}
|
||||
} catch {
|
||||
logger.error("Failed to decode player update: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
// Reload queue for this player
|
||||
guard let service else { return }
|
||||
|
||||
do {
|
||||
let items = try await service.getQueue(playerId: queueId)
|
||||
await MainActor.run {
|
||||
queues[queueId] = items
|
||||
logger.debug("Updated queue for player \(queueId): \(items.count) items")
|
||||
}
|
||||
} catch {
|
||||
logger.error("Failed to reload queue: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
private func handleQueueItemsUpdated(_ event: MAEvent) async {
|
||||
// Similar to queue_updated
|
||||
await handleQueueUpdated(event)
|
||||
}
|
||||
|
||||
// MARK: - Data Loading
|
||||
|
||||
/// Load all players
|
||||
func loadPlayers() async throws {
|
||||
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) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Load queue for specific player
|
||||
func loadQueue(playerId: String) async throws {
|
||||
guard let service else {
|
||||
throw MAWebSocketClient.ClientError.notConnected
|
||||
}
|
||||
|
||||
logger.info("Loading queue for player \(playerId)")
|
||||
let items = try await service.getQueue(playerId: playerId)
|
||||
|
||||
await MainActor.run {
|
||||
queues[playerId] = items
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Player Control
|
||||
|
||||
func play(playerId: String) async throws {
|
||||
guard let service else {
|
||||
throw MAWebSocketClient.ClientError.notConnected
|
||||
}
|
||||
try await service.play(playerId: playerId)
|
||||
}
|
||||
|
||||
func pause(playerId: String) async throws {
|
||||
guard let service else {
|
||||
throw MAWebSocketClient.ClientError.notConnected
|
||||
}
|
||||
try await service.pause(playerId: playerId)
|
||||
}
|
||||
|
||||
func stop(playerId: String) async throws {
|
||||
guard let service else {
|
||||
throw MAWebSocketClient.ClientError.notConnected
|
||||
}
|
||||
try await service.stop(playerId: playerId)
|
||||
}
|
||||
|
||||
func nextTrack(playerId: String) async throws {
|
||||
guard let service else {
|
||||
throw MAWebSocketClient.ClientError.notConnected
|
||||
}
|
||||
try await service.nextTrack(playerId: playerId)
|
||||
}
|
||||
|
||||
func previousTrack(playerId: String) async throws {
|
||||
guard let service else {
|
||||
throw MAWebSocketClient.ClientError.notConnected
|
||||
}
|
||||
try await service.previousTrack(playerId: playerId)
|
||||
}
|
||||
|
||||
func setVolume(playerId: String, level: Int) async throws {
|
||||
guard let service else {
|
||||
throw MAWebSocketClient.ClientError.notConnected
|
||||
}
|
||||
try await service.setVolume(playerId: playerId, level: level)
|
||||
}
|
||||
|
||||
func playMedia(playerId: String, uri: String) async throws {
|
||||
guard let service else {
|
||||
throw MAWebSocketClient.ClientError.notConnected
|
||||
}
|
||||
try await service.playMedia(playerId: playerId, uri: uri)
|
||||
}
|
||||
|
||||
func playIndex(playerId: String, index: Int) async throws {
|
||||
guard let service else {
|
||||
throw MAWebSocketClient.ClientError.notConnected
|
||||
}
|
||||
try await service.playIndex(playerId: playerId, index: index)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user