Skip to main content

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}