import AVFoundation import Combine final class StreamPlayer: ObservableObject { static let shared = StreamPlayer() private var player: AVPlayer private var cancellables = Set() @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) } }