Stemify iOS 1.1 and WebUI updates, repo restructure into desktop/ios/server/web
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
894dd5a695
commit
df5d847169
157 files changed
+3935
-742
No files matched your search
@@ -0,0 +1,80 @@
|
||||
import AVFoundation
|
||||
import Foundation
|
||||
import MediaPlayer
|
||||
|
||||
/// An internet radio stream the user saved in Browse > Web Radio.
|
||||
struct WebStation: Codable, Hashable, Identifiable {
|
||||
var id = UUID().uuidString
|
||||
var name: String
|
||||
var url: String
|
||||
}
|
||||
|
||||
/// Plays a web radio stream through AVPlayer, outside the stem engine. Starting a station pauses
|
||||
/// the queue, and playing the queue again stops the station. Main-thread only, like PlaybackManager.
|
||||
@Observable
|
||||
final class WebRadio: @unchecked Sendable {
|
||||
private let pm: PlaybackManager
|
||||
@ObservationIgnored private let player = AVPlayer()
|
||||
@ObservationIgnored weak var nowPlaying: NowPlaying?
|
||||
|
||||
private(set) var station: WebStation?
|
||||
private(set) var playing = false
|
||||
/// A station is loaded (playing or paused); the lock screen belongs to the radio.
|
||||
var active: Bool { station != nil }
|
||||
|
||||
init(pm: PlaybackManager) {
|
||||
self.pm = pm
|
||||
player.automaticallyWaitsToMinimizeStalling = true
|
||||
pm.addListener { [weak self] in
|
||||
guard let self, self.active, pm.playWhenReady else { return }
|
||||
self.stop()
|
||||
}
|
||||
}
|
||||
|
||||
func play(_ s: WebStation) {
|
||||
guard let url = URL(string: s.url) else { return }
|
||||
station = s
|
||||
if pm.playWhenReady { pm.pause() }
|
||||
let a = AVAudioSession.sharedInstance()
|
||||
try? a.setCategory(.playback, mode: .default, policy: .longFormAudio)
|
||||
try? a.setActive(true)
|
||||
player.replaceCurrentItem(with: AVPlayerItem(url: url))
|
||||
player.play()
|
||||
playing = true
|
||||
publish()
|
||||
}
|
||||
|
||||
func resume() {
|
||||
guard active else { return }
|
||||
// Live streams go stale while paused; rejoin at the live edge.
|
||||
if let s = station { play(s) }
|
||||
}
|
||||
|
||||
func pause() {
|
||||
player.pause()
|
||||
playing = false
|
||||
publish()
|
||||
}
|
||||
|
||||
func toggle() { if playing { pause() } else { resume() } }
|
||||
|
||||
func stop() {
|
||||
player.pause()
|
||||
player.replaceCurrentItem(with: nil)
|
||||
station = nil
|
||||
playing = false
|
||||
nowPlaying?.refresh()
|
||||
}
|
||||
|
||||
private func publish() {
|
||||
guard let s = station else { return }
|
||||
let c = MPNowPlayingInfoCenter.default()
|
||||
c.nowPlayingInfo = [
|
||||
MPMediaItemPropertyTitle: s.name,
|
||||
MPMediaItemPropertyArtist: "Web Radio",
|
||||
MPNowPlayingInfoPropertyIsLiveStream: true,
|
||||
MPNowPlayingInfoPropertyPlaybackRate: playing ? 1.0 : 0.0,
|
||||
]
|
||||
c.playbackState = playing ? .playing : .paused
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user