Skip to main content

snapcast_proto/
status.rs

1//! Snapcast status types matching the JSON-RPC wire format.
2//!
3//! Shared between the embedded server (serialize) and the process backend
4//! talking to C++ snapserver (deserialize). Also used by embedders like SnapDog.
5
6use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9
10/// Result of `Server.GetStatus`.
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct ServerStatus {
13    /// Full server state.
14    pub server: Server,
15}
16
17/// Top-level server state.
18#[derive(Debug, Clone, Default, Serialize, Deserialize)]
19pub struct Server {
20    /// Server host and version info.
21    pub server: ServerInfo,
22    /// All groups (each containing its clients).
23    pub groups: Vec<Group>,
24    /// All configured streams.
25    pub streams: Vec<Stream>,
26}
27
28/// Server host and software information.
29#[derive(Debug, Clone, Default, Serialize, Deserialize)]
30pub struct ServerInfo {
31    /// Server host details.
32    pub host: Host,
33    /// Snapserver software info.
34    pub snapserver: Snapserver,
35}
36
37/// Snapserver software information.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct Snapserver {
40    /// Software name.
41    pub name: String,
42    /// Binary protocol version.
43    #[serde(rename = "protocolVersion")]
44    pub protocol_version: u32,
45    /// JSON-RPC control protocol version.
46    #[serde(rename = "controlProtocolVersion")]
47    pub control_protocol_version: u32,
48    /// Software version string.
49    pub version: String,
50}
51
52impl Default for Snapserver {
53    fn default() -> Self {
54        Self {
55            name: "snapcast-rs".into(),
56            protocol_version: 2,
57            control_protocol_version: 1,
58            version: env!("CARGO_PKG_VERSION").into(),
59        }
60    }
61}
62
63// ── Host ──────────────────────────────────────────────────────
64
65/// Host identification and platform info.
66#[derive(Debug, Clone, Default, Serialize, Deserialize)]
67pub struct Host {
68    /// CPU architecture.
69    #[serde(default)]
70    pub arch: String,
71    /// IP address.
72    #[serde(default)]
73    pub ip: String,
74    /// MAC address.
75    #[serde(default)]
76    pub mac: String,
77    /// Hostname.
78    #[serde(default)]
79    pub name: String,
80    /// Operating system.
81    #[serde(default)]
82    pub os: String,
83}
84
85// ── Client ────────────────────────────────────────────────────
86
87/// A Snapcast client (speaker endpoint).
88#[derive(Debug, Clone, Default, Serialize, Deserialize)]
89pub struct Client {
90    /// Unique client ID.
91    pub id: String,
92    /// Whether currently connected.
93    pub connected: bool,
94    /// Client configuration (persisted).
95    pub config: ClientConfig,
96    /// Host information.
97    pub host: Host,
98    /// Snapclient software info.
99    #[serde(default)]
100    pub snapclient: Snapclient,
101    /// Last-seen timestamp.
102    #[serde(default, rename = "lastSeen")]
103    pub last_seen: LastSeen,
104}
105
106/// Client configuration (persisted across restarts).
107#[derive(Debug, Clone, Default, Serialize, Deserialize)]
108pub struct ClientConfig {
109    /// Multi-instance identifier.
110    #[serde(default)]
111    pub instance: u32,
112    /// Additional latency in milliseconds.
113    pub latency: i32,
114    /// Display name.
115    pub name: String,
116    /// Volume settings.
117    pub volume: Volume,
118}
119
120/// Volume state.
121#[derive(Debug, Clone, Default, Serialize, Deserialize)]
122pub struct Volume {
123    /// Mute state.
124    pub muted: bool,
125    /// Volume percentage (0–100).
126    pub percent: u16,
127}
128
129/// Snapclient software information.
130#[derive(Debug, Clone, Default, Serialize, Deserialize)]
131pub struct Snapclient {
132    /// Software name.
133    #[serde(default)]
134    pub name: String,
135    /// Protocol version.
136    #[serde(default, rename = "protocolVersion")]
137    pub protocol_version: u32,
138    /// Software version string.
139    #[serde(default)]
140    pub version: String,
141}
142
143/// Last-seen timestamp (seconds + microseconds since epoch).
144#[derive(Debug, Clone, Default, Serialize, Deserialize)]
145pub struct LastSeen {
146    /// Seconds since epoch.
147    #[serde(default)]
148    pub sec: u64,
149    /// Microseconds.
150    #[serde(default)]
151    pub usec: u64,
152}
153
154// ── Group ─────────────────────────────────────────────────────
155
156/// A group of clients sharing the same stream.
157#[derive(Debug, Clone, Default, Serialize, Deserialize)]
158pub struct Group {
159    /// Unique group ID.
160    pub id: String,
161    /// Display name.
162    pub name: String,
163    /// Stream ID this group is playing.
164    pub stream_id: String,
165    /// Group mute state.
166    pub muted: bool,
167    /// Clients in this group.
168    pub clients: Vec<Client>,
169}
170
171// ── Stream ────────────────────────────────────────────────────
172
173/// An audio stream source.
174#[derive(Debug, Clone, Default, Serialize, Deserialize)]
175pub struct Stream {
176    /// Stream ID.
177    pub id: String,
178    /// Stream properties (MPRIS-style metadata).
179    #[serde(default, skip_serializing_if = "Option::is_none")]
180    pub properties: Option<StreamProperties>,
181    /// Playback status.
182    pub status: StreamStatus,
183    /// Source URI.
184    pub uri: StreamUri,
185}
186
187/// Stream playback status.
188#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
189#[serde(rename_all = "lowercase")]
190pub enum StreamStatus {
191    /// No audio data flowing.
192    #[default]
193    Idle,
194    /// Audio data actively streaming.
195    Playing,
196    /// Stream disabled by configuration.
197    Disabled,
198    /// Status not recognized.
199    Unknown,
200}
201
202impl From<&str> for StreamStatus {
203    fn from(s: &str) -> Self {
204        match s {
205            "playing" => Self::Playing,
206            "idle" => Self::Idle,
207            "disabled" => Self::Disabled,
208            _ => Self::Unknown,
209        }
210    }
211}
212
213/// Parsed stream URI components.
214#[derive(Debug, Clone, Default, Serialize, Deserialize)]
215pub struct StreamUri {
216    /// URI fragment.
217    #[serde(default)]
218    pub fragment: String,
219    /// Host component.
220    #[serde(default)]
221    pub host: String,
222    /// Path component.
223    #[serde(default)]
224    pub path: String,
225    /// Query parameters.
226    #[serde(default)]
227    pub query: HashMap<String, String>,
228    /// Raw URI string.
229    pub raw: String,
230    /// URI scheme (pipe, tcp, process, etc.).
231    #[serde(default)]
232    pub scheme: String,
233}
234
235/// Stream properties (MPRIS-style metadata and capabilities).
236#[derive(Debug, Clone, Default, Serialize, Deserialize)]
237pub struct StreamProperties {
238    /// Playback status (Playing, Paused, Stopped).
239    #[serde(default)]
240    pub playback_status: Option<String>,
241    /// Loop status (None, Track, Playlist).
242    #[serde(default)]
243    pub loop_status: Option<String>,
244    /// Shuffle mode.
245    #[serde(default)]
246    pub shuffle: Option<bool>,
247    /// Volume (0–100).
248    #[serde(default)]
249    pub volume: Option<u16>,
250    /// Mute state.
251    #[serde(default)]
252    pub mute: Option<bool>,
253    /// Playback rate.
254    #[serde(default)]
255    pub rate: Option<f64>,
256    /// Position in seconds.
257    #[serde(default)]
258    pub position: Option<f64>,
259    /// Can skip to next track.
260    #[serde(default)]
261    pub can_go_next: bool,
262    /// Can skip to previous track.
263    #[serde(default)]
264    pub can_go_previous: bool,
265    /// Can start playback.
266    #[serde(default)]
267    pub can_play: bool,
268    /// Can pause playback.
269    #[serde(default)]
270    pub can_pause: bool,
271    /// Can seek within track.
272    #[serde(default)]
273    pub can_seek: bool,
274    /// Can control playback at all.
275    #[serde(default)]
276    pub can_control: bool,
277    /// Track metadata (artist, title, album, etc.).
278    #[serde(default)]
279    pub metadata: Option<serde_json::Value>,
280}