68 lines
1.8 KiB
Swift
68 lines
1.8 KiB
Swift
import AVFoundation
|
|
import Combine
|
|
|
|
final class StreamPlayer: ObservableObject {
|
|
static let shared = StreamPlayer()
|
|
|
|
private var player: AVPlayer
|
|
private var cancellables = Set<AnyCancellable>()
|
|
|
|
@Published var isPlaying = false
|
|
@Published var connectionStatus: ConnectionStatus = .disconnected
|
|
|
|
enum ConnectionStatus {
|
|
case connected, disconnected, error(String)
|
|
}
|
|
|
|
private init() {
|
|
self.player = AVPlayer()
|
|
setupNotifications()
|
|
}
|
|
|
|
func playStream(host: String, password: String) {
|
|
guard let url = URL(string: "http://\(host):8000/stream.flac") else {
|
|
connectionStatus = .error("Invalid URL")
|
|
return
|
|
}
|
|
|
|
let credentials = "listener:\(password)"
|
|
let encoded = Data(credentials.utf8).base64EncodedString()
|
|
let headers: [String: String] = ["Authorization": "Basic \(encoded)"]
|
|
let asset = AVURLAsset(url: url, options: [
|
|
AVURLAssetHTTPHeaderFieldsKey: headers
|
|
])
|
|
|
|
let item = AVPlayerItem(asset: asset)
|
|
item.preferredForwardBufferDuration = 10
|
|
|
|
player.replaceCurrentItem(with: item)
|
|
player.play()
|
|
isPlaying = true
|
|
connectionStatus = .connected
|
|
}
|
|
|
|
func pause() {
|
|
player.pause()
|
|
isPlaying = false
|
|
}
|
|
|
|
func resume() {
|
|
player.play()
|
|
isPlaying = true
|
|
}
|
|
|
|
func reconnect(host: String, password: String) {
|
|
pause()
|
|
connectionStatus = .disconnected
|
|
playStream(host: host, password: password)
|
|
}
|
|
|
|
private func setupNotifications() {
|
|
NotificationCenter.default.publisher(for: .AVPlayerItemFailedToPlayToEndTime)
|
|
.sink { [weak self] _ in
|
|
self?.connectionStatus = .error("Stream failed")
|
|
}
|
|
.store(in: &cancellables)
|
|
}
|
|
}
|