import ExtensionFoundation import NowPlaying import Observation import Foundation @main struct MusicBridgeRemote: RemoteMediaSessionExtension { var configuration: RemoteMediaSessionExtensionConfiguration { RemoteMediaSessionExtensionConfiguration(extension: self) } func session(_ attributes: SystemPlayerAttributes) async throws -> PCMediaSession { _ = try SystemPlayerCredentials.load(attributes.id) return PCMediaSession(attributes) } } @MainActor @Observable final class PCMediaSession: RemoteMediaSessionRepresentable { let id: String var attributes: SystemPlayerAttributes private let transport = SystemPlayerTransport() init(_ attributes: SystemPlayerAttributes) { id = attributes.id; self.attributes = attributes } func update(_ attributes: SystemPlayerAttributes) { self.attributes = attributes } var content: (any MediaContentRepresentable)? { guard attributes.hasSession else { return nil } let artwork = attributes.artworkID.map { artworkID in Artwork(id: artworkID) { _ in let saved = try SystemPlayerCredentials.load(self.id) let client = BridgeClient(saved, timeout: 5) defer { client.close() } return try ArtworkRepresentation(data: await client.artwork(artworkID)) } } return MusicContent(id: attributes.source + "/" + attributes.title + "/" + attributes.artist, songTitle: attributes.title.isEmpty ? "Без названия" : attributes.title, artistName: attributes.artist, albumName: "", type: .audio, duration: attributes.duration > 0 ? .finite(attributes.duration) : nil, artwork: artwork) } var playbackSnapshot: MediaPlaybackSnapshot? { MediaPlaybackSnapshot(state: attributes.playing ? .playing(rate: Float(attributes.rate)) : .paused, elapsedTime: attributes.position, timestamp: attributes.timestamp) } var commands: [MediaCommand] { [ .play { try await self.send(.play) }.enabled(attributes.capabilities.play), .pause { try await self.send(.pause) }.enabled(attributes.capabilities.pause), .next { try await self.send(.next) }.enabled(attributes.capabilities.next), .previous { try await self.send(.previous) }.enabled(attributes.capabilities.previous), .seekToPosition { try await self.send(.seek, value: $0) }.enabled(attributes.capabilities.seek) ] } var devices: [MediaDevice] { let capabilities: [MediaDevice.Capability] = attributes.volume.map { volume in [.absoluteVolume(Float(volume)) { try await self.send(.volume, value: Double($0)) }] } ?? [] return [MediaDevice(id: id, name: "MusicBridge · ПК", type: .speaker, capabilities: capabilities)] } private func send(_ action: SystemPlayerAction, value: Double? = nil) async throws { let state = try await transport.send(action, value: value, sessionID: id) attributes = SystemPlayerAttributes(id: id, state: state) } }