92 lines
3.1 KiB
Swift
92 lines
3.1 KiB
Swift
import Foundation
|
|
|
|
// MARK: - JSON-RPC Client
|
|
|
|
final class MopidyClient {
|
|
static let shared = MopidyClient()
|
|
|
|
private let baseEndpoint: URL
|
|
|
|
private init() {
|
|
let credentials = "mopidy:\(AppSettings.shared.password)"
|
|
let encoded = Data(credentials.utf8).base64EncodedString()
|
|
self.baseEndpoint = URL(string: "http://\(AppSettings.shared.host):6680/mopidy/rpc")!
|
|
}
|
|
|
|
func request<T: Decodable>(method: String, params: [String: Any] = [:], as type: T.Type) async throws -> T {
|
|
var components = URLComponents(url: baseEndpoint, resolvingAgainstBaseURL: false)!
|
|
components.queryItems = [URLQueryItem(name: "jsonrpc", value: "2.0")]
|
|
guard let url = components.url else { throw MopidyError.invalidURL }
|
|
|
|
let body: [String: Any] = [
|
|
"jsonrpc": "2.0",
|
|
"method": method,
|
|
"params": params,
|
|
"id": 1
|
|
]
|
|
|
|
var request = URLRequest(url: url)
|
|
request.httpMethod = "POST"
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
request.httpBody = try JSONSerialization.data(withJSONObject: body)
|
|
|
|
let (data, response) = try await URLSession.shared.data(for: request)
|
|
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
|
|
throw MopidyError.badResponse
|
|
}
|
|
|
|
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
if let error = json?["error"] {
|
|
throw MopidyError.rpcError("\(error)")
|
|
}
|
|
guard let result = json?["result"] else {
|
|
throw MopidyError.noResult
|
|
}
|
|
|
|
let resultData = try JSONSerialization.data(withJSONObject: result, options: [])
|
|
return try JSONDecoder().decode(type, from: resultData)
|
|
}
|
|
|
|
func getPlaybackState() async throws -> MopidyPlaybackState {
|
|
try await request(method: "core.playback.getState", params: [:], as: MopidyPlaybackState.self)
|
|
}
|
|
|
|
func getCurrentTrack() async throws -> MopidyPlaybackState.TLTrack? {
|
|
try await request(method: "core.tracklist.getCurrentTrack", params: [:], as: MopidyPlaybackState.TLTrack?.self)
|
|
}
|
|
|
|
func getVolume() async throws -> Int {
|
|
try await request(method: "core.mixer.getVolume", params: [:], as: Int.self)
|
|
}
|
|
|
|
func setVolume(_ volume: Int) async throws {
|
|
try await request(method: "core.mixer.setVolume", params: ["volume": volume], as: Void.self)
|
|
}
|
|
|
|
func play() async throws {
|
|
try await request(method: "core.playback.play", params: [:], as: Void.self)
|
|
}
|
|
|
|
func pause() async throws {
|
|
try await request(method: "core.playback.pause", params: [:], as: Void.self)
|
|
}
|
|
}
|
|
|
|
// MARK: - Errors
|
|
|
|
enum MopidyError: LocalizedError {
|
|
case invalidURL
|
|
case badResponse
|
|
case noResult
|
|
case rpcError(String)
|
|
|
|
var errorDescription: String? {
|
|
switch self {
|
|
case .invalidURL: return "Invalid Mopidy URL"
|
|
case .badResponse: return "Bad server response"
|
|
case .noResult: return "No result in response"
|
|
case .rpcError(let msg): return "RPC error: \(msg)"
|
|
}
|
|
}
|
|
}
|