107 lines
3.1 KiB
Swift
107 lines
3.1 KiB
Swift
import Combine
|
|
import SwiftUI
|
|
|
|
@MainActor
|
|
final class NowPlayingController: ObservableObject {
|
|
@Published var currentTrack: MopidyPlaybackState.Track?
|
|
@Published var isPlaying = false
|
|
@Published var volume = 70
|
|
@Published var coverArt: UIImage?
|
|
@Published var connectionStatus: String = "Connecting..."
|
|
|
|
private let settings = AppSettings.shared
|
|
private let streamPlayer = StreamPlayer.shared
|
|
private var cancellables = Set<AnyCancellable>()
|
|
private var refreshTimer: Timer?
|
|
|
|
init() {
|
|
bindStreamPlayer()
|
|
startPolling()
|
|
fetchVolume()
|
|
}
|
|
|
|
func connect() {
|
|
streamPlayer.playStream(host: settings.host, password: settings.password)
|
|
}
|
|
|
|
func reconnect() {
|
|
streamPlayer.reconnect(host: settings.host, password: settings.password)
|
|
}
|
|
|
|
func togglePlay() {
|
|
if isPlaying {
|
|
streamPlayer.pause()
|
|
Task { try? await MopidyClient.shared.pause() }
|
|
} else {
|
|
streamPlayer.resume()
|
|
Task { try? await MopidyClient.shared.play() }
|
|
}
|
|
}
|
|
|
|
func setVolume(_ newVolume: Int) {
|
|
volume = newVolume
|
|
Task { [weak self] in
|
|
try? await MopidyClient.shared.setVolume(newVolume)
|
|
}
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func bindStreamPlayer() {
|
|
streamPlayer.$connectionStatus
|
|
.map { status in
|
|
switch status {
|
|
case .connected: return "Connected"
|
|
case .disconnected: return "Disconnected"
|
|
case .error(let msg): return "Error: \(msg)"
|
|
}
|
|
}
|
|
.assign(to: &$connectionStatus)
|
|
|
|
streamPlayer.$isPlaying
|
|
.assign(to: &$isPlaying)
|
|
}
|
|
|
|
private func startPolling() {
|
|
refreshTimer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: true) { [weak self] _ in
|
|
Task { [weak self] in
|
|
guard let self else { return }
|
|
do {
|
|
let state = try await MopidyClient.shared.getPlaybackState()
|
|
if let track = state.tlTrack?.track {
|
|
self.currentTrack = track
|
|
self.fetchCoverArt(track.coverArtURI)
|
|
}
|
|
self.isPlaying = (state.state == .playing)
|
|
} catch {
|
|
self.connectionStatus = "Poll error"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func fetchCoverArt(_ uris: [String]?) {
|
|
guard let firstURI = uris?.first, let url = URL(string: firstURI) else { return }
|
|
Task {
|
|
do {
|
|
let (data, _) = try await URLSession.shared.data(from: url)
|
|
if let image = UIImage(data: data) {
|
|
self.coverArt = image
|
|
}
|
|
} catch {
|
|
self.coverArt = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
private func fetchVolume() {
|
|
Task {
|
|
do {
|
|
self.volume = try await MopidyClient.shared.getVolume()
|
|
} catch {
|
|
print("Volume fetch error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
}
|