import AppKit import Foundation import Observation @Observable @MainActor public final class AppModel { public let settings: SettingsStore public let library: StationLibrary public let playback: PlaybackController let artwork: StationArtworkLoader private let nowPlayingArtwork: NowPlayingArtworkResolver public convenience init() { self.init(settings: SettingsStore(), persistence: PersistenceStore()) } init( settings: SettingsStore = SettingsStore(), persistence: PersistenceStore = PersistenceStore() ) { self.settings = settings let client = RadioBrowserClient(baseURL: settings.baseURL, cacheDirectory: persistence.apiCacheDirectory) library = StationLibrary(client: client, persistence: persistence) let artworkLoader = StationArtworkLoader(cacheDirectory: persistence.cacheDirectory) artwork = artworkLoader nowPlayingArtwork = NowPlayingArtworkResolver(cacheDirectory: persistence.cacheDirectory, faviconLoader: artworkLoader) playback = PlaybackController(artworkProvider: { [weak nowPlayingArtwork] station, artist, title in nowPlayingArtwork?.resolve(artist: artist, title: title, station: station) ?? BearWaveAssets.image(named: "bearwave") ?? NSImage() }) nowPlayingArtwork.onArtworkLoaded = { [weak playback] in playback?.refreshNowPlaying() } playback.onMetadataChanged = { [weak nowPlayingArtwork] artist, title, station in nowPlayingArtwork?.fetchArtwork(artist: artist, title: title, station: station) } playback.volume = library.volume library.updateBaseURL(settings.baseURL) } public func start() async { await library.load(.top) if settings.resumeOnLaunch, let station = library.resumeStation() { play(station) } } public func play(_ station: RadioStation) { library.recordPlayback(station) playback.play(station) nowPlayingArtwork.fetchArtwork( artist: playback.currentTrackArtist.isEmpty ? nil : playback.currentTrackArtist, title: playback.currentTrackTitle.isEmpty ? nil : playback.currentTrackTitle, station: station ) } public func resumeLastStation() { guard let station = library.resumeStation() else { return } play(station) } public func setVolume(_ value: Double) { playback.volume = value library.saveVolume(value) } public func applySettings() { library.updateBaseURL(settings.baseURL) } public func quit() { playback.stop() nowPlayingArtwork.fetchArtwork(artist: nil, title: nil, station: nil) } }