1
1
mirror of https://github.com/danbee/persephone synced 2025-03-04 08:39:11 +00:00
persephone/Persephone/MPDClient/MPDClient.swift

203 lines
4.5 KiB
Swift

//
// MPDClient.swift
// Persephone
//
// Created by Daniel Barber on 2019/1/25.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
import mpdclient
class MPDClient {
var delegate: MPDClientDelegate?
let HOST = "localhost"
let PORT: UInt32 = 6600
private var connection: OpaquePointer?
private var status: Status?
private var queue: [Song] = []
private let commandQueue = DispatchQueue(label: "commandQueue")
enum Command {
case prevTrack, nextTrack, playPause, stop, fetchStatus, fetchQueue
}
struct Idle: OptionSet {
let rawValue: UInt32
static let database = Idle(rawValue: 0x1)
static let storedPlaylist = Idle(rawValue: 0x2)
static let queue = Idle(rawValue: 0x4)
static let player = Idle(rawValue: 0x8)
static let mixer = Idle(rawValue: 0x10)
static let output = Idle(rawValue: 0x20)
static let options = Idle(rawValue: 0x40)
static let update = Idle(rawValue: 0x80)
static let sticker = Idle(rawValue: 0x100)
static let subscription = Idle(rawValue: 0x200)
static let message = Idle(rawValue: 0x400)
}
init(withDelegate delegate: MPDClientDelegate?) {
self.delegate = delegate
}
func connect() {
guard let connection = mpd_connection_new(HOST, PORT, 0)
else { return }
guard let status = mpd_run_status(connection)
else { return }
self.connection = connection
self.status = Status(status)
fetchQueue()
self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state)
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song)
idle()
}
func disconnect() {
noIdle()
commandQueue.async { [unowned self] in
mpd_connection_free(self.connection)
}
}
func fetchStatus() {
sendCommand(command: .fetchStatus)
}
func fetchQueue() {
sendCommand(command: .fetchQueue)
}
func playPause() {
queueCommand(command: .playPause)
}
func stop() {
queueCommand(command: .stop)
}
func prevTrack() {
queueCommand(command: .prevTrack)
}
func nextTrack() {
queueCommand(command: .nextTrack)
}
func queueCommand(command: Command) {
noIdle()
commandQueue.async { [unowned self] in
self.sendCommand(command: command)
}
idle()
}
func sendCommand(command: Command) {
switch command {
// Transport commands
case .prevTrack:
sendPreviousTrack()
case .nextTrack:
sendNextTrack()
case .stop:
sendStop()
case .playPause:
sendPlay()
case .fetchStatus:
guard let status = mpd_run_status(connection) else { break }
self.status = Status(status)
case .fetchQueue:
self.queue = []
mpd_send_list_queue_meta(connection)
while let mpdSong = mpd_recv_song(connection) {
let song = Song(mpdSong)
self.queue.append(song)
}
}
}
func sendNextTrack() {
guard let state = status?.state,
state.isOneOf([.playing, .paused])
else { return }
mpd_run_next(connection)
}
func sendPreviousTrack() {
guard let state = status?.state,
state.isOneOf([.playing, .paused])
else { return }
mpd_run_previous(connection)
}
func sendStop() {
mpd_run_stop(connection)
}
func sendPlay() {
if status?.state == .stopped {
mpd_run_play(connection)
} else {
mpd_run_toggle_pause(connection)
}
}
func noIdle() {
mpd_send_noidle(connection)
}
func idle() {
commandQueue.async { [unowned self] in
mpd_send_idle(self.connection)
let result = mpd_recv_idle(self.connection, true)
self.handleIdleResult(result)
}
}
func handleIdleResult(_ result: mpd_idle) {
let mpdIdle = Idle(rawValue: result.rawValue)
if mpdIdle.contains(.queue) {
self.fetchQueue()
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
}
if mpdIdle.contains(.player) {
self.fetchStatus()
self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state)
self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song)
}
if !mpdIdle.isEmpty {
self.idle()
}
}
func getLastErrorMessage() -> String! {
if mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS {
return "no error"
}
if let errorMessage = mpd_connection_get_error_message(connection) {
return String(cString: errorMessage)
}
return "no error"
}
}