import Domain import Foundation public struct QueueItem: Identifiable, Hashable, Codable, Sendable { public let id: UUID public let track: Track public init(id: UUID = UUID(), track: Track) { self.id = id self.track = track } } public enum RepeatMode: String, Codable, Sendable, CaseIterable { case off, all, one } public enum PlaybackCommand: Codable, Sendable { case play case pause case togglePlayPause case seek(to: TimeInterval) case next case previous case setQueue(tracks: [Track], startIndex: Int, shuffled: Bool) case insertNext([Track]) case insertLast([Track]) case jump(to: QueueItem.ID) case move(item: QueueItem.ID, after: QueueItem.ID?) case remove(QueueItem.ID) case clearUpcoming case clearHistory case setShuffle(Bool) case setRepeat(RepeatMode) case setVolume(Double) } public struct PlaybackAnchor: Hashable, Sendable { public var position: TimeInterval public var instant: ContinuousClock.Instant public var rate: Double public init(position: TimeInterval, instant: ContinuousClock.Instant, rate: Double) { self.position = position self.instant = instant self.rate = rate } public func position(at now: ContinuousClock.Instant) -> TimeInterval { let elapsed = now - instant let seconds = Double(elapsed.components.seconds) + Double(elapsed.components.attoseconds) / 1e18 return position + seconds * rate } } public enum PlaybackStatus: String, Sendable { case stopped, paused, playing } public struct PlaybackState: Hashable, Sendable { public var history: [QueueItem] = [] public var current: QueueItem? public var upcoming: [QueueItem] = [] public var status: PlaybackStatus = .stopped public var anchor = PlaybackAnchor(position: 0, instant: .now, rate: 0) public var isShuffled = false public var repeatMode: RepeatMode = .off public var volume: Double = 1 public init() {} } public enum OutputEvent: Sendable { case finished(QueueItem.ID) case failed(QueueItem.ID) case anchor(QueueItem.ID, position: TimeInterval, rate: Double) } public protocol OutputEngine: Sendable { var events: AsyncStream { get } func load(_ item: QueueItem, at position: TimeInterval) async func prepare(_ item: QueueItem?) async func play() async func pause() async func seek(to position: TimeInterval) async func stop() async func setVolume(_ volume: Double) async } public extension OutputEngine { func prepare(_: QueueItem?) async {} func setVolume(_: Double) async {} }