add mobile androida and ios apps
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user