Files
audioserver/GrooveAudio (iOS)/Mopidy/MopidyClient.swift
T

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)"
}
}
}