1pub mod codec {
2 pub mod io {
3 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4 use std::io::{self, Cursor, Read, Write};
5 pub const V1: u8 = 1;
6 pub const V2: u8 = 2;
7 pub const V3: u8 = 3;
8 pub struct BinaryBuffer<T>(Cursor<T>);
9 impl BinaryBuffer<Vec<u8>> {
10 pub fn new() -> Self {
11 Self(Cursor::new(Vec::new()))
12 }
13 pub fn with_capacity(capacity: usize) -> Self {
14 Self(Cursor::new(Vec::with_capacity(capacity)))
15 }
16 pub fn into_inner(self) -> Vec<u8> {
17 self.0.into_inner()
18 }
19 }
20 impl Default for BinaryBuffer<Vec<u8>> {
21 fn default() -> Self {
22 Self::new()
23 }
24 }
25 impl<T: AsRef<[u8]>> BinaryBuffer<T> {
26 pub fn from_data(data: T) -> Self {
27 Self(Cursor::new(data))
28 }
29 pub fn read_string(&mut self) -> io::Result<String> {
30 let size = self.0.read_u16::<BigEndian>()? as usize;
31 let mut content = vec![0u8; size];
32 self.0.read_exact(&mut content)?;
33 String::from_utf8(content)
34 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
35 }
36 pub fn read_nullable_string(&mut self) -> io::Result<Option<String>> {
37 if self.0.read_u8()? != 0 {
38 self.read_string().map(Some)
39 } else {
40 Ok(None)
41 }
42 }
43 pub fn read_json<V: serde::de::DeserializeOwned>(&mut self) -> io::Result<V> {
44 let s = self.read_string()?;
45 serde_json::from_str(&s).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
46 }
47 }
48 impl BinaryBuffer<Vec<u8>> {
49 pub fn write_string(&mut self, text: &str) -> io::Result<()> {
50 let raw = text.as_bytes();
51 self.0.write_u16::<BigEndian>(raw.len() as u16)?;
52 self.0.write_all(raw)
53 }
54 pub fn write_nullable_string(&mut self, text: Option<&str>) -> io::Result<()> {
55 match text {
56 Some(s) => {
57 self.0.write_u8(1)?;
58 self.write_string(s)
59 }
60 None => self.0.write_u8(0),
61 }
62 }
63 pub fn write_json<V: serde::Serialize>(&mut self, value: &V) -> io::Result<()> {
64 let s = serde_json::to_string(value).map_err(io::Error::other)?;
65 self.write_string(&s)
66 }
67 }
68 impl<T> std::ops::Deref for BinaryBuffer<T> {
69 type Target = Cursor<T>;
70 fn deref(&self) -> &Self::Target {
71 &self.0
72 }
73 }
74 impl<T> std::ops::DerefMut for BinaryBuffer<T> {
75 fn deref_mut(&mut self) -> &mut Self::Target {
76 &mut self.0
77 }
78 }
79 }
80 pub mod decode {
81 use crate::protocol::{
82 CodecError, PlaylistInfo, Track, TrackInfo,
83 codec::io::{BinaryBuffer, V1, V2, V3},
84 };
85 use base64::{Engine, prelude::BASE64_STANDARD};
86 use byteorder::{BigEndian, ReadBytesExt};
87 pub fn decode_track(encoded: &str) -> Result<Track, CodecError> {
88 if encoded.is_empty() {
89 return Err(CodecError::EmptyInput);
90 }
91 let raw_payload = BASE64_STANDARD.decode(encoded)?;
92 if raw_payload.len() < 4 {
93 return Err(CodecError::TruncatedBuffer("header".into()));
94 }
95 let mut stream = BinaryBuffer::from_data(raw_payload);
96 let envelope = stream.read_u32::<BigEndian>()?;
97 let flags = (envelope >> 30) & 0x03;
98 let ver = if flags & 1 != 0 {
99 stream.read_u8()?
100 } else {
101 V1
102 };
103 if !(V1..=V3).contains(&ver) {
104 return Err(CodecError::UnknownVersion(ver));
105 }
106 let title = stream.read_string()?;
107 let author = stream.read_string()?;
108 let length = stream.read_u64::<BigEndian>()?;
109 let identifier = stream.read_string()?;
110 let is_stream = stream.read_u8()? != 0;
111 let (uri, artwork_url, isrc) = match ver {
112 V2 => (stream.read_nullable_string()?, None, None),
113 V3 => (
114 stream.read_nullable_string()?,
115 stream.read_nullable_string()?,
116 stream.read_nullable_string()?,
117 ),
118 _ => (None, None, None),
119 };
120 let source_name = stream.read_string()?;
121 let position = stream.read_u64::<BigEndian>()?;
122 let user_data = stream.read_json().unwrap_or_else(|_| serde_json::json!({}));
123 Ok(Track {
124 encoded: encoded.to_owned(),
125 info: TrackInfo {
126 identifier,
127 is_seekable: !is_stream,
128 author,
129 length,
130 is_stream,
131 position,
132 title,
133 uri,
134 artwork_url,
135 isrc,
136 source_name,
137 },
138 plugin_info: serde_json::json!({}),
139 user_data,
140 })
141 }
142 pub fn decode_playlist_info(encoded: &str) -> Result<PlaylistInfo, CodecError> {
143 let raw = BASE64_STANDARD.decode(encoded)?;
144 let mut stream = BinaryBuffer::from_data(raw);
145 Ok(PlaylistInfo {
146 name: stream.read_string()?,
147 selected_track: stream.read_i32::<BigEndian>()?,
148 })
149 }
150 }
151 pub mod encode {
152 use crate::protocol::{
153 CodecError, PlaylistInfo, TrackInfo,
154 codec::io::{BinaryBuffer, V3},
155 };
156 use base64::{Engine, prelude::BASE64_STANDARD};
157 use byteorder::{BigEndian, WriteBytesExt};
158 use std::io::Write;
159 pub fn encode_track(
160 metadata: &TrackInfo,
161 user_data: &serde_json::Value,
162 ) -> Result<String, CodecError> {
163 let mut blob = BinaryBuffer::with_capacity(128);
164 blob.write_u8(V3)?;
165 blob.write_string(&metadata.title)?;
166 blob.write_string(&metadata.author)?;
167 blob.write_u64::<BigEndian>(metadata.length)?;
168 blob.write_string(&metadata.identifier)?;
169 blob.write_u8(u8::from(metadata.is_stream))?;
170 blob.write_nullable_string(metadata.uri.as_deref())?;
171 blob.write_nullable_string(metadata.artwork_url.as_deref())?;
172 blob.write_nullable_string(metadata.isrc.as_deref())?;
173 blob.write_string(&metadata.source_name)?;
174 blob.write_u64::<BigEndian>(metadata.position)?;
175 if let Some(obj) = user_data.as_object() {
176 if !obj.is_empty() {
177 blob.write_json(user_data)?;
178 }
179 } else if !user_data.is_null() {
180 blob.write_json(user_data)?;
181 }
182 let inner = blob.into_inner();
183 let header = (inner.len() as u32) | (1u32 << 30);
184 let mut out = Vec::with_capacity(4 + inner.len());
185 out.write_u32::<BigEndian>(header)?;
186 out.write_all(&inner)?;
187 Ok(BASE64_STANDARD.encode(&out))
188 }
189 pub fn encode_playlist_info(info: &PlaylistInfo) -> Result<String, CodecError> {
190 let mut blob = BinaryBuffer::with_capacity(64);
191 blob.write_string(&info.name)?;
192 blob.write_i32::<BigEndian>(info.selected_track)?;
193 Ok(BASE64_STANDARD.encode(blob.into_inner()))
194 }
195 }
196 pub use decode::{decode_playlist_info, decode_track};
197 pub use encode::{encode_playlist_info, encode_track};
198}
199pub mod info {
200 use serde::Serialize;
201 #[derive(Debug, Serialize)]
202 #[serde(rename_all = "camelCase")]
203 pub struct Info {
204 pub version: Version,
205 pub build_time: u64,
206 pub git: GitInfo,
207 pub jvm: String,
208 pub lavaplayer: String,
209 pub source_managers: Vec<String>,
210 pub filters: Vec<String>,
211 pub plugins: Vec<Plugin>,
212 }
213 #[derive(Debug, Serialize)]
214 #[serde(rename_all = "camelCase")]
215 pub struct Version {
216 pub semver: String,
217 pub major: u32,
218 pub minor: u32,
219 pub patch: u32,
220 pub pre_release: Option<String>,
221 }
222 #[derive(Debug, Serialize)]
223 #[serde(rename_all = "camelCase")]
224 pub struct GitInfo {
225 pub branch: String,
226 pub commit: String,
227 pub commit_time: u64,
228 }
229 #[derive(Debug, Serialize)]
230 pub struct Plugin {
231 pub name: String,
232 pub version: String,
233 }
234}
235pub mod stats {
236 use serde::Serialize;
237 #[derive(Debug, Clone, Serialize)]
238 #[serde(rename_all = "camelCase")]
239 pub struct Stats {
240 pub players: u64,
241 pub playing_players: u64,
242 pub uptime: u64,
243 pub memory: Memory,
244 pub cpu: Cpu,
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub frame_stats: Option<FrameStats>,
247 }
248 #[derive(Debug, Clone, Serialize)]
249 #[serde(rename_all = "camelCase")]
250 pub struct Memory {
251 pub free: u64,
252 pub used: u64,
253 pub allocated: u64,
254 pub reservable: u64,
255 }
256 #[derive(Debug, Clone, Serialize)]
257 #[serde(rename_all = "camelCase")]
258 pub struct Cpu {
259 pub cores: i32,
260 pub system_load: f64,
261 pub lavalink_load: f64,
262 }
263 #[derive(Debug, Clone, Serialize)]
264 #[serde(rename_all = "camelCase")]
265 pub struct FrameStats {
266 pub sent: i32,
267 pub nulled: i32,
268 pub deficit: i32,
269 }
270}
271pub mod routeplanner {
272 use serde::Serialize;
273 #[derive(Debug, Serialize, Clone)]
274 #[serde(tag = "class", content = "details")]
275 pub enum RoutePlannerStatus {
276 RotatingIpRoutePlanner(RotatingIpDetails),
277 NanoIpRoutePlanner(NanoIpDetails),
278 RotatingNanoIpRoutePlanner(RotatingNanoIpDetails),
279 BalancingIpRoutePlanner(BalancingIpDetails),
280 }
281 #[derive(Debug, Serialize, Clone)]
282 #[serde(rename_all = "camelCase")]
283 pub struct RotatingIpDetails {
284 pub ip_block: IpBlock,
285 pub failing_addresses: Vec<FailingAddress>,
286 pub rotate_index: String,
287 pub ip_index: String,
288 pub current_address: String,
289 }
290 #[derive(Debug, Serialize, Clone)]
291 #[serde(rename_all = "camelCase")]
292 pub struct NanoIpDetails {
293 pub ip_block: IpBlock,
294 pub failing_addresses: Vec<FailingAddress>,
295 pub current_address: String,
296 }
297 #[derive(Debug, Serialize, Clone)]
298 #[serde(rename_all = "camelCase")]
299 pub struct RotatingNanoIpDetails {
300 pub ip_block: IpBlock,
301 pub failing_addresses: Vec<FailingAddress>,
302 pub block_index: String,
303 pub current_address_index: String,
304 }
305 #[derive(Debug, Serialize, Clone)]
306 #[serde(rename_all = "camelCase")]
307 pub struct BalancingIpDetails {
308 pub ip_block: IpBlock,
309 pub failing_addresses: Vec<FailingAddress>,
310 }
311 #[derive(Debug, Serialize, Clone)]
312 #[serde(rename_all = "camelCase")]
313 pub struct IpBlock {
314 #[serde(rename = "type")]
315 pub block_type: String,
316 pub size: String,
317 }
318 #[derive(Debug, Serialize, Clone)]
319 #[serde(rename_all = "camelCase")]
320 pub struct FailingAddress {
321 pub failing_address: String,
322 pub failing_timestamp: u64,
323 pub failing_time: String,
324 }
325 #[derive(Debug, serde::Deserialize)]
326 pub struct FreeAddressRequest {
327 pub address: String,
328 }
329}
330pub mod tracks {
331 use crate::protocol::codec::{decode_track, encode_track};
332 use serde::{Deserialize, Serialize};
333 #[derive(Debug, Clone, Serialize, Deserialize)]
334 #[serde(rename_all = "camelCase")]
335 pub struct Track {
336 pub encoded: String,
337 pub info: TrackInfo,
338 #[serde(default = "serde_json::Value::default")]
339 pub plugin_info: serde_json::Value,
340 #[serde(default = "serde_json::Value::default")]
341 pub user_data: serde_json::Value,
342 }
343 impl Track {
344 pub fn new(info: TrackInfo) -> Self {
345 let mut track = Self {
346 encoded: String::new(),
347 info,
348 plugin_info: serde_json::json!({}),
349 user_data: serde_json::json!({}),
350 };
351 track.encoded = track.encode();
352 track
353 }
354 pub fn encode(&self) -> String {
355 encode_track(&self.info, &self.user_data).unwrap_or_else(|_| self.encoded.clone())
356 }
357 pub fn decode(encoded: &str) -> Option<Self> {
358 decode_track(encoded).ok()
359 }
360 }
361 #[derive(Debug, Clone, Serialize, Deserialize, Default)]
362 #[serde(rename_all = "camelCase")]
363 pub struct TrackInfo {
364 pub identifier: String,
365 pub is_seekable: bool,
366 pub author: String,
367 pub length: u64,
368 pub is_stream: bool,
369 pub position: u64,
370 pub title: String,
371 pub uri: Option<String>,
372 pub artwork_url: Option<String>,
373 pub isrc: Option<String>,
374 pub source_name: String,
375 }
376 #[derive(Debug, Serialize, Deserialize)]
377 #[serde(tag = "loadType", content = "data", rename_all = "camelCase")]
378 pub enum LoadResult {
379 Track(Track),
380 Playlist(PlaylistData),
381 Search(Vec<Track>),
382 Empty {},
383 Error(LoadError),
384 }
385 #[derive(Debug, Clone, Serialize, Deserialize)]
386 #[serde(rename_all = "camelCase")]
387 pub struct PlaylistData {
388 pub info: PlaylistInfo,
389 pub plugin_info: serde_json::Value,
390 pub tracks: Vec<Track>,
391 }
392 #[derive(Debug, Clone, Serialize, Deserialize)]
393 #[serde(rename_all = "camelCase")]
394 pub struct TextData {
395 pub text: String,
396 pub plugin: serde_json::Value,
397 }
398 #[derive(Debug, Clone, Serialize, Deserialize)]
399 #[serde(rename_all = "camelCase")]
400 pub struct SearchResult {
401 pub tracks: Vec<Track>,
402 pub albums: Vec<PlaylistData>,
403 pub artists: Vec<PlaylistData>,
404 pub playlists: Vec<PlaylistData>,
405 pub texts: Vec<TextData>,
406 pub plugin: serde_json::Value,
407 }
408 #[derive(Debug, Clone, Serialize, Deserialize)]
409 #[serde(rename_all = "camelCase")]
410 pub struct PlaylistInfo {
411 pub name: String,
412 pub selected_track: i32,
413 }
414 #[derive(Debug, Serialize, Deserialize)]
415 #[serde(rename_all = "camelCase")]
416 pub struct LoadError {
417 pub message: Option<String>,
418 pub severity: crate::common::Severity,
419 pub cause: String,
420 #[serde(skip_serializing_if = "Option::is_none")]
421 pub cause_stack_trace: Option<String>,
422 }
423}
424pub mod events {
425 use crate::protocol::tracks::Track;
426 use serde::{Deserialize, Serialize};
427 #[derive(Debug, Clone, Serialize, Deserialize)]
428 #[serde(rename_all = "camelCase")]
429 pub struct PlayerState {
430 pub time: u64,
431 pub position: u64,
432 pub connected: bool,
433 pub ping: i64,
434 }
435 #[derive(Debug, Serialize)]
436 #[serde(tag = "op", rename_all = "camelCase")]
437 pub enum OutgoingMessage {
438 Ready {
439 resumed: bool,
440 #[serde(rename = "sessionId")]
441 session_id: crate::common::types::SessionId,
442 },
443 #[serde(rename = "playerUpdate")]
444 PlayerUpdate {
445 #[serde(rename = "guildId")]
446 guild_id: crate::common::types::GuildId,
447 state: PlayerState,
448 },
449 #[serde(rename = "stats")]
450 Stats {
451 #[serde(flatten)]
452 stats: super::stats::Stats,
453 },
454 #[serde(rename = "event")]
455 Event {
456 #[serde(flatten)]
457 event: Box<LavendeEvent>,
458 },
459 }
460 #[derive(Debug, Serialize)]
461 #[serde(tag = "type", rename_all = "camelCase")]
462 pub enum LavendeEvent {
463 #[serde(rename = "TrackStartEvent")]
464 TrackStart {
465 #[serde(rename = "guildId")]
466 guild_id: crate::common::types::GuildId,
467 track: Track,
468 },
469 #[serde(rename = "TrackEndEvent")]
470 TrackEnd {
471 #[serde(rename = "guildId")]
472 guild_id: crate::common::types::GuildId,
473 track: Track,
474 reason: TrackEndReason,
475 },
476 #[serde(rename = "TrackExceptionEvent")]
477 TrackException {
478 #[serde(rename = "guildId")]
479 guild_id: crate::common::types::GuildId,
480 track: Track,
481 exception: TrackException,
482 },
483 #[serde(rename = "TrackStuckEvent")]
484 TrackStuck {
485 #[serde(rename = "guildId")]
486 guild_id: crate::common::types::GuildId,
487 track: Track,
488 #[serde(rename = "thresholdMs")]
489 threshold_ms: u64,
490 },
491 #[serde(rename = "LyricsFoundEvent")]
492 LyricsFound {
493 #[serde(rename = "guildId")]
494 guild_id: crate::common::types::GuildId,
495 lyrics: super::models::LavendeLyrics,
496 },
497 #[serde(rename = "LyricsNotFoundEvent")]
498 LyricsNotFound {
499 #[serde(rename = "guildId")]
500 guild_id: crate::common::types::GuildId,
501 },
502 #[serde(rename = "LyricsLineEvent")]
503 LyricsLine {
504 #[serde(rename = "guildId")]
505 guild_id: crate::common::types::GuildId,
506 line_index: i32,
507 line: super::models::LavendeLyricsLine,
508 skipped: bool,
509 },
510 #[serde(rename = "WebSocketClosedEvent")]
511 WebSocketClosed {
512 #[serde(rename = "guildId")]
513 guild_id: crate::common::types::GuildId,
514 code: u16,
515 reason: String,
516 #[serde(rename = "byRemote")]
517 by_remote: bool,
518 },
519 }
520 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
521 #[serde(rename_all = "camelCase")]
522 pub enum TrackEndReason {
523 #[serde(rename = "finished")]
524 Finished,
525 #[serde(rename = "loadFailed")]
526 LoadFailed,
527 #[serde(rename = "stopped")]
528 Stopped,
529 #[serde(rename = "replaced")]
530 Replaced,
531 #[serde(rename = "cleanup")]
532 Cleanup,
533 }
534 #[derive(Debug, Clone, Serialize, Deserialize)]
535 #[serde(rename_all = "camelCase")]
536 pub struct TrackException {
537 pub message: Option<String>,
538 pub severity: crate::common::Severity,
539 pub cause: String,
540 pub cause_stack_trace: Option<String>,
541 }
542}
543pub mod models {
544 use serde::{Deserialize, Serialize};
545 #[derive(Deserialize)]
546 pub struct LoadTracksQuery {
547 pub identifier: String,
548 }
549 #[derive(Deserialize)]
550 pub struct LoadSearchQuery {
551 pub query: String,
552 pub types: Option<String>,
553 }
554 #[derive(Deserialize)]
555 #[serde(rename_all = "camelCase")]
556 pub struct DecodeTrackQuery {
557 pub encoded_track: Option<String>,
558 pub track: Option<String>,
559 }
560 #[derive(Deserialize)]
561 pub struct EncodedTracks(pub Vec<String>);
562 #[derive(Serialize)]
563 pub struct Tracks {
564 pub tracks: Vec<crate::protocol::tracks::Track>,
565 }
566 #[derive(Serialize)]
567 pub struct Exception {
568 pub message: String,
569 pub severity: String,
570 pub cause: String,
571 }
572 #[derive(Serialize, Deserialize, Debug, Clone)]
573 #[serde(rename_all = "camelCase")]
574 pub struct LyricsLine {
575 pub text: String,
576 pub timestamp: u64,
577 pub duration: u64,
578 }
579 #[derive(Serialize, Deserialize, Debug, Clone)]
580 #[serde(rename_all = "camelCase")]
581 pub struct LyricsData {
582 pub name: String,
583 pub author: String,
584 pub provider: String,
585 pub text: String,
586 pub lines: Option<Vec<LyricsLine>>,
587 }
588 #[derive(Debug, Serialize, Deserialize)]
589 #[serde(tag = "loadType", content = "data", rename_all = "camelCase")]
590 pub enum LyricsLoadResult {
591 Lyrics(LyricsResultData),
592 Text(LyricsTextData),
593 Empty {},
594 Error(LyricsLoadError),
595 }
596 #[derive(Debug, Clone, Serialize, Deserialize)]
597 #[serde(rename_all = "camelCase")]
598 pub struct LyricsResultData {
599 pub name: String,
600 pub synced: bool,
601 pub lines: Vec<LyricsLine>,
602 }
603 #[derive(Debug, Clone, Serialize, Deserialize)]
604 #[serde(rename_all = "camelCase")]
605 pub struct LyricsTextData {
606 pub text: String,
607 }
608 #[derive(Debug, Serialize, Deserialize)]
609 #[serde(rename_all = "camelCase")]
610 pub struct LyricsLoadError {
611 pub message: String,
612 pub severity: crate::common::Severity,
613 }
614 #[derive(Serialize, Deserialize, Debug, Clone)]
615 #[serde(rename_all = "camelCase")]
616 pub struct LavendeLyrics {
617 pub source_name: String,
618 pub provider: Option<String>,
619 pub text: Option<String>,
620 pub lines: Option<Vec<LavendeLyricsLine>>,
621 pub plugin: serde_json::Value,
622 }
623 #[derive(Serialize, Deserialize, Debug, Clone)]
624 #[serde(rename_all = "camelCase")]
625 pub struct LavendeLyricsLine {
626 pub timestamp: u64,
627 pub duration: Option<u64>,
628 pub line: String,
629 pub plugin: serde_json::Value,
630 }
631 #[derive(Deserialize)]
632 #[serde(rename_all = "camelCase")]
633 pub struct GetLyricsQuery {
634 pub track: String,
635 #[serde(default)]
636 pub skip_track_source: bool,
637 }
638 #[derive(Deserialize)]
639 #[serde(rename_all = "camelCase")]
640 pub struct GetPlayerLyricsQuery {
641 #[serde(default)]
642 pub skip_track_source: bool,
643 }
644}
645use thiserror::Error;
646#[derive(Debug, Error)]
647pub enum CodecError {
648 #[error("Empty input")]
649 EmptyInput,
650 #[error("Corrupt buffer: {0}")]
651 CorruptBuffer(String),
652 #[error("Truncated buffer: {0}")]
653 TruncatedBuffer(String),
654 #[error("Unknown track version: {0}")]
655 UnknownVersion(u8),
656 #[error("Base64 error: {0}")]
657 Base64(#[from] base64::DecodeError),
658 #[error("IO error: {0}")]
659 Io(#[from] std::io::Error),
660 #[error("UTF-8 error: {0}")]
661 Utf8(#[from] std::string::FromUtf8Error),
662}
663pub use codec::*;
664pub use events::*;
665pub use info::*;
666pub use models::*;
667pub use routeplanner::*;
668pub use stats::*;
669pub use tracks::*;