plexus_mono/types.rs
1//! Event types for the Monochrome music API activation
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6/// Events emitted by Monochrome API activation methods
7#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
8#[serde(tag = "type", rename_all = "snake_case")]
9pub enum MonoEvent {
10 /// Track metadata from /info/?id=
11 Track {
12 /// Tidal track ID
13 id: u64,
14 /// Track title (including version if present)
15 title: String,
16 /// Primary artist name
17 artist: String,
18 /// Album title
19 album: String,
20 /// Tidal album ID
21 album_id: u64,
22 /// Duration in seconds
23 duration_secs: u64,
24 /// Track number within the album
25 track_number: Option<u32>,
26 /// Release date (ISO 8601)
27 release_date: Option<String>,
28 /// Audio quality (e.g. "LOSSLESS", "HI_RES_LOSSLESS", "HIGH")
29 audio_quality: Option<String>,
30 /// Tidal cover UUID (use with cover_url to build image URLs)
31 cover_id: Option<String>,
32 },
33
34 /// Album metadata from /album/?id=
35 Album {
36 /// Tidal album ID
37 id: u64,
38 /// Album title
39 title: String,
40 /// Primary artist name
41 artist: String,
42 /// Release date (ISO 8601)
43 release_date: Option<String>,
44 /// Number of tracks in this album
45 track_count: u32,
46 /// Total album duration in seconds
47 duration_secs: Option<u64>,
48 /// Tidal cover UUID
49 cover_id: Option<String>,
50 },
51
52 /// Individual track within an album listing (follows Album event)
53 AlbumTrack {
54 /// 1-based position in the album
55 position: u32,
56 /// Tidal track ID
57 id: u64,
58 /// Track title
59 title: String,
60 /// Primary artist name
61 artist: String,
62 /// Duration in seconds
63 duration_secs: u64,
64 /// Audio quality
65 audio_quality: Option<String>,
66 },
67
68 /// Artist information from /artist/?id=
69 Artist {
70 /// Tidal artist ID
71 id: u64,
72 /// Artist name
73 name: String,
74 /// Tidal picture UUID (same format as track/album artist picture field)
75 picture_id: Option<String>,
76 /// Full cover image URL at 750x750 (directly from API response)
77 cover_url: Option<String>,
78 },
79
80 /// A track from search results (follows SearchStart)
81 SearchTrack {
82 /// 0-based rank in results
83 rank: u32,
84 /// Tidal track ID
85 id: u64,
86 /// Track title
87 title: String,
88 /// Primary artist name
89 artist: String,
90 /// Album title
91 album: String,
92 /// Duration in seconds
93 duration_secs: u64,
94 /// Audio quality
95 audio_quality: Option<String>,
96 },
97
98 /// An album from search results
99 SearchAlbum {
100 /// 0-based rank in results
101 rank: u32,
102 /// Tidal album ID
103 id: u64,
104 /// Album title
105 title: String,
106 /// Primary artist name
107 artist: String,
108 /// Number of tracks
109 track_count: u32,
110 /// Release date
111 release_date: Option<String>,
112 },
113
114 /// An artist from search results
115 SearchArtist {
116 /// 0-based rank in results
117 rank: u32,
118 /// Tidal artist ID
119 id: u64,
120 /// Artist name
121 name: String,
122 },
123
124 /// A single lyrics line from /lyrics/?id=
125 LyricLine {
126 /// Timestamp in milliseconds (None for unsynced lyrics)
127 timestamp_ms: Option<u64>,
128 /// Lyric text
129 text: String,
130 },
131
132 /// A recommended track from /recommendations/?id=
133 Recommendation {
134 /// 0-based rank in results
135 rank: u32,
136 /// Tidal track ID
137 id: u64,
138 /// Track title
139 title: String,
140 /// Primary artist name
141 artist: String,
142 /// Duration in seconds
143 duration_secs: u64,
144 },
145
146 /// A cover art URL from /cover/?id=
147 Cover {
148 /// Full HTTPS URL to the cover image
149 url: String,
150 /// Image dimension in pixels (width == height)
151 size: u32,
152 },
153
154 /// Resolved stream manifest from /track/?id= (use url immediately — it expires)
155 StreamManifest {
156 /// Tidal track ID
157 id: u64,
158 /// Pre-signed direct CDN URL — short-lived, use within seconds
159 url: String,
160 /// MIME type: "audio/flac", "audio/mp4", "audio/mpeg"
161 mime_type: String,
162 /// Codec string: "flac", "mp4a.40.2", etc.
163 codecs: String,
164 /// Quality tier used: "LOSSLESS", "HIGH", "LOW", "HI_RES_LOSSLESS"
165 quality: String,
166 /// Bit depth (present for LOSSLESS / HI_RES_LOSSLESS)
167 bit_depth: Option<u32>,
168 /// Sample rate in Hz (present for LOSSLESS / HI_RES_LOSSLESS)
169 sample_rate: Option<u32>,
170 /// File extension inferred from MIME type
171 extension: String,
172 },
173
174 /// Streaming download progress
175 DownloadProgress {
176 /// Absolute path of the file being written
177 path: String,
178 /// Bytes downloaded so far
179 bytes_downloaded: u64,
180 /// Total bytes (None if server didn't send Content-Length)
181 total_bytes: Option<u64>,
182 /// Completion percentage (None if total unknown)
183 percent: Option<f32>,
184 },
185
186 /// Download finished successfully
187 DownloadComplete {
188 /// Absolute path of the saved file
189 path: String,
190 /// Total bytes written
191 bytes: u64,
192 /// MIME type of the saved audio
193 mime_type: String,
194 },
195
196 /// Playback status event from mpv
197 PlaybackStatus {
198 /// Current player state
199 status: PlayStatus,
200 /// Elapsed seconds (available while playing)
201 elapsed_secs: Option<f32>,
202 /// Total duration in seconds (available while playing)
203 duration_secs: Option<f32>,
204 },
205
206 /// Current playback state — streamed ~1s via now_playing
207 NowPlaying {
208 /// Currently playing track ID
209 track_id: Option<u64>,
210 /// Track title
211 title: Option<String>,
212 /// Artist name
213 artist: Option<String>,
214 /// Album title
215 album: Option<String>,
216 /// Player state
217 status: PlayStatus,
218 /// Current position in seconds
219 position_secs: f32,
220 /// Total duration in seconds
221 duration_secs: f32,
222 /// Volume level 0.0–1.0
223 volume: f32,
224 /// Pre-amp gain 0.0–4.0 (>1.0 boosts)
225 preamp: f32,
226 /// Number of tracks in queue
227 queue_length: usize,
228 /// Monochrome web URL for the current track
229 url: Option<String>,
230 },
231
232 /// Queue contents snapshot
233 Queue {
234 /// Tracks in queue (current + upcoming)
235 tracks: Vec<QueuedTrack>,
236 /// Index of the currently playing track (if any)
237 current_index: Option<usize>,
238 },
239
240 /// Playlist summary info (from playlist list)
241 PlaylistInfo {
242 /// Playlist name
243 name: String,
244 /// Playlist description
245 description: String,
246 /// Number of tracks in the playlist
247 track_count: usize,
248 /// ISO 8601 creation timestamp
249 created_at: String,
250 /// ISO 8601 last-updated timestamp
251 updated_at: String,
252 },
253
254 /// Acknowledgement of a player action
255 PlayerAck {
256 /// Action that was performed
257 action: String,
258 /// Human-readable message
259 message: String,
260 },
261
262 /// Error from any method
263 Error {
264 /// Human-readable error description
265 message: String,
266 },
267}
268
269/// Playback lifecycle state
270#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
271#[serde(rename_all = "snake_case")]
272pub enum PlayStatus {
273 /// No track loaded
274 Idle,
275 /// Player process spawned, buffering/starting
276 Starting,
277 /// Buffering audio data from network
278 Buffering,
279 /// Currently playing
280 Playing,
281 /// Playback paused
282 Paused,
283 /// Playback stopped by user
284 Stopped,
285 /// Playback finished cleanly
286 Finished,
287 /// Player exited with an error
288 Failed,
289}
290
291/// A track in the playback queue
292#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
293pub struct QueuedTrack {
294 /// Tidal track ID
295 pub id: u64,
296 /// Track title
297 pub title: String,
298 /// Primary artist name
299 pub artist: String,
300 /// Album title
301 pub album: String,
302 /// Duration in seconds
303 pub duration_secs: u64,
304 /// Quality tier requested
305 pub quality: String,
306 /// Cover art UUID
307 pub cover_id: Option<String>,
308}
309
310/// Search target kind
311#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
312#[serde(rename_all = "snake_case")]
313pub enum SearchKind {
314 /// Search for tracks (default)
315 Tracks,
316 /// Search for albums
317 Albums,
318 /// Search for artists
319 Artists,
320}
321
322impl Default for SearchKind {
323 fn default() -> Self {
324 SearchKind::Tracks
325 }
326}