joscha gay

This commit is contained in:
uh wot 2021-07-15 00:10:10 +02:00
commit e30854304f
Signed by: uhwot
GPG Key ID: CB2454984587B781
24 changed files with 3073 additions and 0 deletions

1012
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

23
Cargo.toml Normal file
View File

@ -0,0 +1,23 @@
[package]
name = "dzlib-rs"
version = "0.1.0"
authors = ["uh_wot <uhwot@protonmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features = false }
serde = { version = "1.0", features = ["derive"] }
md-5 = "0.9"
hex = { version = "0.4", optional = true }
aes = { version = "0.7", optional = true }
block-modes = { version = "0.8", optional = true }
thiserror = "1.0"
[dev-dependencies]
tokio-test = "0.4"
[features]
default = ["stream"]
stream = ["hex", "aes", "block-modes"]

269
src/client.rs Normal file
View File

@ -0,0 +1,269 @@
use std::fmt::Display;
use std::str::FromStr;
use reqwest::Method;
use serde::{Deserialize, Deserializer, de};
use std::time::Instant;
use crate::objects::*;
use md5::{Digest, Md5};
use crate::errors::{ErrorKind, DzErr};
const API_URL: &str = "https://api.deezer.com";
// client id and secret are from deezer's chromecast app
const CLIENT_ID: &str = "119915";
const CLIENT_SECRET: &str = "2f5b4c9785ddc367975b83d90dc46f5c";
fn from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where T: FromStr,
T::Err: Display,
D: Deserializer<'de>
{
let s = String::deserialize(deserializer)?;
T::from_str(&s).map_err(de::Error::custom)
}
#[derive(Deserialize, Debug)]
struct TokenResp {
access_token: String,
#[serde(deserialize_with = "from_str")]
expires: u64,
}
// needed because deezer's API returns an integer on "expires" instead of a string when logging in
// thanks baguette assholes
#[derive(Deserialize, Debug)]
struct LoginTokenResp {
access_token: String,
expires: u64,
}
pub struct AccessToken {
pub token: String,
expires: u64,
instant: Instant
}
impl AccessToken {
fn new(token: String, expires: u64) -> AccessToken {
AccessToken {
token,
expires,
instant: Instant::now(),
}
}
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum DzRespType<T> {
Err { error: DzErr },
Ok(T),
}
pub struct Client {
client: reqwest::Client,
pub access_token: Option<AccessToken>,
}
impl Client {
pub fn new() -> Self {
Self {
access_token: None,
client: reqwest::Client::new(),
}
}
pub async fn get_token(&mut self) -> Result<(), ErrorKind> {
let resp = self.client.request(Method::GET, "https://connect.deezer.com/oauth/access_token.php")
.query(&[
("grant_type", "client_credentials"),
("client_id", CLIENT_ID),
("client_secret", CLIENT_SECRET),
("output", "json"),
])
.send().await.map_err(|e| ErrorKind::Reqwest(e))?
.json::<DzRespType<TokenResp>>().await.map_err(|e| ErrorKind::Reqwest(e))?;
match resp {
DzRespType::Ok(token) => {
self.access_token = Some(AccessToken::new(token.access_token, token.expires));
Ok(())
},
DzRespType::Err { error } => Err(ErrorKind::API(error)),
}
}
async fn check_token(&mut self) -> Result<(), ErrorKind> {
match &self.access_token {
Some(token) => {
if token.instant.elapsed().as_secs() >= token.expires && token.expires != 0 {
self.get_token().await
} else {
Ok(())
}
},
None => self.get_token().await
}
}
pub async fn login(&mut self, email: &str, password: &str) -> Result<(), ErrorKind> {
let password = format!("{:x}", Md5::digest(password.as_bytes()));
let hash = [
CLIENT_ID.as_bytes(),
email.as_bytes(),
password.as_bytes(),
CLIENT_SECRET.as_bytes(),
].concat();
let hash = format!("{:x}", Md5::digest(&hash));
let url = format!("{}/auth/token", API_URL);
let resp = self.client.request(Method::GET, &url)
.query(&[
("app_id", CLIENT_ID),
("login", email),
("password", &password),
("hash", &hash),
])
.send().await.map_err(|e| ErrorKind::Reqwest(e))?
.json::<DzRespType<LoginTokenResp>>().await.map_err(|e| ErrorKind::Reqwest(e))?;
match resp {
DzRespType::Ok(token) => {
self.access_token = Some(AccessToken::new(token.access_token, token.expires));
Ok(())
},
DzRespType::Err { error } => Err(ErrorKind::API(error)),
}
}
pub async fn login_token(&mut self, token: &str) -> Result<(), ErrorKind> {
self.access_token = Some(AccessToken::new(token.to_string(), 0));
match self.infos().await {
Err(e) => {
self.access_token = None;
Err(e)
},
Ok(i) => {
match i.user_token {
Some(_) => Ok(()),
None => {
self.access_token = None;
Err(ErrorKind::NotUserToken)
},
}
}
}
}
pub async fn api_call<T, Q>(&mut self, method: reqwest::Method, path: &str, query: &Q) -> Result<T, ErrorKind>
where T: de::DeserializeOwned,
Q: serde::ser::Serialize + ?Sized,
{
self.check_token().await.map_err(|e| ErrorKind::Token(Box::new(e)))?;
let url = format!("{}/{}", API_URL, path);
let resp = self.client.request(method, &url)
.query(&[("access_token", &self.access_token.as_ref().unwrap().token)])
.query(query)
.body("\n") // needed otherwise some POST requests fail
.send().await.map_err(|e| ErrorKind::Reqwest(e))?
.json::<DzRespType<T>>().await.map_err(|e| ErrorKind::Reqwest(e))?;
match resp {
DzRespType::Ok(r) => Ok(r),
DzRespType::Err { error } => Err(ErrorKind::API(error)),
}
}
pub async fn track(&mut self, id: i64) -> Result<Track, ErrorKind> {
Track::get(self, id).await
}
pub async fn album(&mut self, id: u64) -> Result<Album, ErrorKind> {
Album::get(self, id).await
}
pub async fn artist(&mut self, id: u64) -> Result<Artist, ErrorKind> {
Artist::get(self, id).await
}
pub async fn playlist(&mut self, id: u64) -> Result<Playlist, ErrorKind> {
Playlist::get(self, id).await
}
pub async fn podcast(&mut self, id: u64) -> Result<Podcast, ErrorKind> {
Podcast::get(self, id).await
}
pub async fn episode(&mut self, id: u64) -> Result<Episode, ErrorKind> {
Episode::get(self, id).await
}
pub async fn chart(&mut self, id: u64) -> Result<Chart, ErrorKind> {
Chart::get(self, id).await
}
pub async fn radio(&mut self, id: u64) -> Result<Radio, ErrorKind> {
Radio::get(self, id).await
}
pub async fn radio_list(&mut self) -> Result<DzArray<Radio>, ErrorKind> {
Radio::list(self).await
}
pub async fn search_track(&mut self, query: &str) -> Result<DzArray<Track>, ErrorKind> {
search::track(self, query).await
}
pub async fn search_album(&mut self, query: &str) -> Result<DzArray<Album>, ErrorKind> {
search::album(self, query).await
}
pub async fn search_artist(&mut self, query: &str) -> Result<DzArray<Artist>, ErrorKind> {
search::artist(self, query).await
}
pub async fn search_playlist(&mut self, query: &str) -> Result<DzArray<Playlist>, ErrorKind> {
search::playlist(self, query).await
}
pub async fn genre(&mut self, id: u64) -> Result<Genre, ErrorKind> {
Genre::get(self, id).await
}
pub async fn editorial(&mut self, id: u64) -> Result<Editorial, ErrorKind> {
Editorial::get(self, id).await
}
pub async fn lyrics(&mut self, id: u64) -> Result<Lyrics, ErrorKind> {
Lyrics::get(self, id).await
}
pub async fn user(&mut self, id: u64) -> Result<User, ErrorKind> {
User::get(self, id).await
}
pub async fn user_self(&mut self) -> Result<User, ErrorKind> {
User::get_self(self).await
}
pub async fn infos(&mut self) -> Result<Infos, ErrorKind> {
Infos::get(self).await
}
pub async fn options(&mut self) -> Result<Options, ErrorKind> {
Options::get(self).await
}
}
impl Default for Client {
fn default() -> Self {
Client::new()
}
}

43
src/crypto.rs Normal file
View File

@ -0,0 +1,43 @@
use md5::{Md5, Digest};
use aes::Aes128;
use block_modes::{BlockMode, Ecb};
use block_modes::block_padding::ZeroPadding;
pub fn stream_url(md5_origin: &str, format: u8, id: i64, media_version: u8) -> String {
// md5 origin + format num + id + media version
let metadata = [
md5_origin.as_bytes(),
&[b'\xa4'],
format.to_string().as_bytes(),
&[b'\xa4'],
id.to_string().as_bytes(),
&[b'\xa4'],
media_version.to_string().as_bytes(),
].concat();
let hash = format!("{:x}", Md5::digest(&metadata));
// md5 hash + previous metadata
let metadata_hash = [
hash.as_bytes(),
&[b'\xa4'],
&metadata,
&[b'\xa4'],
].concat();
// padding
/*
let len = metadata_hash.len();
if len % 16 > 0 {
metadata_hash.resize(len+(16-len%16), b'\x00');
}
*/
const AES_KEY: [u8; 16] = [106, 111, 54, 97, 101, 121, 54, 104, 97, 105, 100, 50, 84, 101, 105, 104];
type Aes128Ecb = Ecb<Aes128, ZeroPadding>;
let cipher = Aes128Ecb::new_from_slices(&AES_KEY, Default::default()).unwrap();
let ciphertext = cipher.encrypt_vec(&metadata_hash);
format!("https://cdns-proxy-{}.dzcdn.net/api/1/{}", md5_origin.chars().next().unwrap(), hex::encode(ciphertext))
}

33
src/errors.rs Normal file
View File

@ -0,0 +1,33 @@
use serde::Deserialize;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ErrorKind {
#[error("error from deezer api")]
API(DzErr),
#[error("error while calling api")]
Reqwest(reqwest::Error),
#[error("error while getting token")]
Token(Box<Self>),
#[error("token specified is not an account token")]
NotUserToken,
}
#[derive(Deserialize, Debug)]
pub struct DzErr {
#[serde(rename = "type")]
pub err_type: DzErrType,
pub message: String,
pub code: Option<u16>,
}
#[derive(Deserialize, Debug)]
pub enum DzErrType {
Exception,
OAuthException,
ParameterException,
MissingParameterException,
InvalidQueryException,
DataException,
IndividualAccountChangedNotAllowedException,
}

6
src/lib.rs Normal file
View File

@ -0,0 +1,6 @@
mod client;
pub mod objects;
pub mod errors;
#[cfg(feature = "stream")]
pub mod crypto;
pub use self::client::Client;

63
src/objects/album.rs Normal file
View File

@ -0,0 +1,63 @@
use serde::Deserialize;
use super::{DzArray, User, Track, ImgObject};
use crate::errors::ErrorKind;
use reqwest::Method;
// https://developers.deezer.com/api/album
#[derive(Deserialize, Debug)]
pub struct Album {
pub id: u64,
pub title: String,
pub upc: Option<String>,
pub link: Option<String>,
pub share: Option<String>,
pub cover: Option<String>,
pub cover_small: Option<String>,
pub cover_medium: Option<String>,
pub cover_big: Option<String>,
pub cover_xl: Option<String>,
pub md5_image: Option<String>,
pub genre_id: Option<i32>,
pub label: Option<String>,
pub nb_tracks: Option<u64>,
pub duration: Option<u64>,
pub fans: Option<u64>,
pub rating: Option<u32>,
pub release_date: Option<String>,
pub record_type: Option<String>,
pub available: Option<bool>,
pub tracklist: Option<String>,
pub explicit_lyrics: Option<bool>,
pub explicit_content_lyrics: Option<u8>,
pub explicit_content_cover: Option<u8>,
pub alternative: Option<Box<Album>>,
pub tracks: Option<DzArray<Track>>,
}
impl Album {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("album/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Result<Self, ErrorKind> {
Self::get(client, self.id).await
}
pub async fn fans(client: &mut crate::Client, id: u64) -> Result<DzArray<User>, ErrorKind> {
client.api_call(Method::GET, &format!("album/{}/fans", id), &()).await
}
pub async fn tracks(client: &mut crate::Client, id: u64) -> Result<DzArray<Track>, ErrorKind> {
client.api_call(Method::GET, &format!("album/{}/tracks", id), &()).await
}
}
impl ImgObject for Album {
fn md5_img(&self) -> Option<&str> {
Some(self.md5_image.as_ref()?)
}
fn img_type(&self) -> &'static str {
"cover"
}
}

67
src/objects/artist.rs Normal file
View File

@ -0,0 +1,67 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
use super::{DzArray, User, Track, Album, Playlist, ImgObject};
// https://developers.deezer.com/api/artist
#[derive(Deserialize, Debug)]
pub struct Artist {
pub id: Option<u64>,
pub name: String,
pub link: Option<String>,
pub picture: Option<String>,
pub picture_small: Option<String>,
pub picture_medium: Option<String>,
pub picture_big: Option<String>,
pub picture_xl: Option<String>,
pub md5_image: Option<String>,
pub nb_album: Option<u64>,
pub nb_fan: Option<u64>,
pub radio: Option<bool>,
pub tracklist: String,
}
impl Artist {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("artist/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Option<Result<Self, ErrorKind>> {
Some(Self::get(client, self.id?).await)
}
pub async fn top(client: &mut crate::Client, id: u64) -> Result<DzArray<Track>, ErrorKind> {
client.api_call(Method::GET, &format!("artist/{}/top", id), &()).await
}
pub async fn albums(client: &mut crate::Client, id: u64) -> Result<DzArray<Album>, ErrorKind> {
client.api_call(Method::GET, &format!("artist/{}/albums", id), &()).await
}
pub async fn fans(client: &mut crate::Client, id: u64) -> Result<DzArray<User>, ErrorKind> {
client.api_call(Method::GET, &format!("artist/{}/fans", id), &()).await
}
pub async fn related(client: &mut crate::Client, id: u64) -> Result<DzArray<Self>, ErrorKind> {
client.api_call(Method::GET, &format!("artist/{}/related", id), &()).await
}
pub async fn radio(client: &mut crate::Client, id: u64) -> Result<DzArray<Track>, ErrorKind> {
client.api_call(Method::GET, &format!("artist/{}/radio", id), &()).await
}
pub async fn playlists(client: &mut crate::Client, id: u64) -> Result<DzArray<Playlist>, ErrorKind> {
client.api_call(Method::GET, &format!("artist/{}/playlists", id), &()).await
}
}
impl ImgObject for Artist {
fn md5_img(&self) -> Option<&str> {
Some(self.md5_image.as_ref()?)
}
fn img_type(&self) -> &'static str {
"artist"
}
}

14
src/objects/auth.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::errors::ErrorKind;
use reqwest::Method;
pub async fn check(client: &mut crate::Client, field: &str, value: &str) -> Result<bool, ErrorKind> {
Ok(
client.api_call(Method::GET, "auth/check", &[("field", field), ("value", value)]).await?
)
}
pub async fn register(client: &mut crate::Client, email: &str, password: &str, name: &str, birthday: &str, genre: &str) -> Result<u64, ErrorKind> {
Ok(
client.api_call(Method::POST, "auth/register", &[("email", email), ("password", password), ("name", name), ("birthday", birthday), ("genre", genre)]).await?
)
}

63
src/objects/chart.rs Normal file
View File

@ -0,0 +1,63 @@
use serde::Deserialize;
use super::{DzArray, Track, Album, Artist, Playlist, Podcast};
use crate::errors::ErrorKind;
use reqwest::Method;
#[derive(Deserialize, Debug)]
pub struct Chart {
pub tracks: DzArray<ChartTrack>,
pub albums: DzArray<ChartAlbum>,
pub artists: DzArray<ChartArtist>,
pub playlists: DzArray<Playlist>,
pub podcasts: DzArray<Podcast>,
}
impl Chart {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("chart/{}", id), &()).await
}
pub async fn tracks(client: &mut crate::Client, id: u64) -> Result<DzArray<ChartTrack>, ErrorKind> {
client.api_call(Method::GET, &format!("chart/{}/tracks", id), &()).await
}
pub async fn albums(client: &mut crate::Client, id: u64) -> Result<DzArray<ChartAlbum>, ErrorKind> {
client.api_call(Method::GET, &format!("chart/{}/albums", id), &()).await
}
pub async fn artists(client: &mut crate::Client, id: u64) -> Result<DzArray<ChartArtist>, ErrorKind> {
client.api_call(Method::GET, &format!("chart/{}/artists", id), &()).await
}
pub async fn playlists(client: &mut crate::Client, id: u64) -> Result<DzArray<Playlist>, ErrorKind> {
client.api_call(Method::GET, &format!("chart/{}/playlists", id), &()).await
}
pub async fn podcasts(client: &mut crate::Client, id: u64) -> Result<DzArray<Podcast>, ErrorKind> {
client.api_call(Method::GET, &format!("chart/{}/podcasts", id), &()).await
}
}
#[derive(Deserialize, Debug)]
pub struct ChartTrack {
#[serde(flatten)]
pub track: Track,
pub position: u64,
}
#[derive(Deserialize, Debug)]
pub struct ChartAlbum {
#[serde(flatten)]
pub album: Album,
pub position: u64,
}
#[derive(Deserialize, Debug)]
pub struct ChartArtist {
#[serde(flatten)]
pub artist: Artist,
pub position: u64,
}

51
src/objects/editorial.rs Normal file
View File

@ -0,0 +1,51 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
use super::{DzArray, Album, Chart, ImgObject};
#[derive(Deserialize, Debug)]
pub struct Editorial {
pub id: u64,
pub name: String,
pub picture: String,
pub picture_small: String,
pub picture_medium: String,
pub picture_big: String,
pub picture_xl: String,
}
impl Editorial {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("editorial/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Result<Self, ErrorKind> {
Self::get(client, self.id).await
}
pub async fn list(client: &mut crate::Client) -> Result<DzArray<Self>, ErrorKind> {
client.api_call(Method::GET, "editorial", &()).await
}
pub async fn selection(client: &mut crate::Client, id: u64) -> Result<DzArray<Album>, ErrorKind> {
client.api_call(Method::GET, &format!("editorial/{}/selection", id), &()).await
}
pub async fn charts(client: &mut crate::Client, id: u64) -> Result<Chart, ErrorKind> {
super::Chart::get(client, id).await
}
pub async fn releases(client: &mut crate::Client, id: u64) -> Result<DzArray<Album>, ErrorKind> {
client.api_call(Method::GET, &format!("editorial/{}/releases", id), &()).await
}
}
impl ImgObject for Editorial {
fn md5_img(&self) -> Option<&str> {
Some(self.picture_small.split('/').collect::<Vec<&str>>()[5])
}
fn img_type(&self) -> &'static str {
"misc"
}
}

58
src/objects/episode.rs Normal file
View File

@ -0,0 +1,58 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
use super::{Podcast, ImgObject};
#[derive(Deserialize, Debug)]
pub struct Episode {
pub id: u64,
pub title: String,
pub description: Option<String>,
pub available: Option<bool>,
pub release_date: String,
pub duration: u64,
pub link: Option<String>,
pub share: Option<String>,
pub picture: String,
pub picture_small: Option<String>,
pub picture_medium: Option<String>,
pub picture_big: Option<String>,
pub picture_xl: Option<String>,
pub md5_origin: String,
pub md5_32: String,
pub md5_64: String,
#[serde(deserialize_with = "super::from_str")]
pub filesize_32: u64,
#[serde(deserialize_with = "super::from_str")]
pub filesize_64: u64,
pub direct_stream_url: Option<String>,
pub podcast: Option<Podcast>,
}
impl Episode {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("episode/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Result<Self, ErrorKind> {
Self::get(client, self.id).await
}
pub async fn set_bookmark(client: &mut crate::Client, id: u64, offset: u8) -> Result<Self, ErrorKind> {
client.api_call(Method::POST, &format!("episode/{}/bookmark", id), &[("offset", &offset.to_string())]).await
}
pub async fn delete_bookmark(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::DELETE, &format!("episode/{}/bookmark", id), &()).await
}
}
impl ImgObject for Episode {
fn md5_img(&self) -> Option<&str> {
Some(self.picture_small.as_ref()?.split('/').collect::<Vec<&str>>()[5])
}
fn img_type(&self) -> &'static str {
"talk"
}
}

43
src/objects/genre.rs Normal file
View File

@ -0,0 +1,43 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
use super::{DzArray, Artist, Radio, ImgObject};
#[derive(Deserialize, Debug)]
pub struct Genre {
pub id: u64,
pub name: String,
pub picture: String,
pub picture_small: String,
pub picture_medium: String,
pub picture_big: String,
pub picture_xl: String,
}
impl Genre {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("genre/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Result<Self, ErrorKind> {
Self::get(client, self.id).await
}
pub async fn artists(client: &mut crate::Client, id: u64) -> Result<DzArray<Artist>, ErrorKind> {
client.api_call(Method::GET, &format!("genre/{}/artists", id), &()).await
}
pub async fn radios(client: &mut crate::Client, id: u64) -> Result<DzArray<Radio>, ErrorKind> {
client.api_call(Method::GET, &format!("genre/{}/radios", id), &()).await
}
}
impl ImgObject for Genre {
fn md5_img(&self) -> Option<&str> {
Some(self.picture_small.split('/').collect::<Vec<&str>>()[5])
}
fn img_type(&self) -> &'static str {
"misc"
}
}

45
src/objects/infos.rs Normal file
View File

@ -0,0 +1,45 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
// https://developers.deezer.com/api/infos/
#[derive(Deserialize, Debug)]
pub struct Infos {
pub country_iso: String,
pub country: String,
pub open: bool,
pub pop: String,
pub upload_token: String,
pub upload_token_lifetime: u64,
pub user_token: Option<String>,
pub hosts: Hosts,
pub has_podcasts: bool,
pub offers: Vec<Offer>,
pub freemium_available: bool,
pub lyrics_available: bool,
}
impl Infos {
pub async fn get(client: &mut crate::Client) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, "infos", &()).await
}
}
#[derive(Deserialize, Debug)]
pub struct Hosts {
pub stream: String,
pub images: String,
}
#[derive(Deserialize, Debug)]
pub struct Offer {
pub id: u32,
pub name: String,
pub amount: String,
pub currency: String,
pub displayed_amount: String,
pub tc: String,
pub tc_html: String,
pub tc_txt: String,
pub try_and_buy: u32,
}

30
src/objects/lyrics.rs Normal file
View File

@ -0,0 +1,30 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
#[derive(Deserialize, Debug)]
pub struct Lyrics {
pub id: u64,
pub text: String,
pub sync_json: Option<Vec<LyricsSync>>,
pub copyrights: String,
pub writers: String,
}
impl Lyrics {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("lyrics/{}", id), &()).await
}
}
#[derive(Deserialize, Debug)]
pub struct LyricsSync {
pub lrc_timestamp: Option<String>,
#[serde(default)]
#[serde(deserialize_with = "super::from_str_option")]
pub milliseconds: Option<u64>,
#[serde(default)]
#[serde(deserialize_with = "super::from_str_option")]
pub duration: Option<u64>,
pub line: String
}

102
src/objects/mod.rs Normal file
View File

@ -0,0 +1,102 @@
mod track;
mod album;
mod artist;
mod playlist;
mod podcast;
mod episode;
mod chart;
mod radio;
pub mod search;
mod genre;
mod editorial;
mod lyrics;
mod user;
mod infos;
mod options;
pub mod auth;
pub use self::track::*;
pub use self::album::*;
pub use self::artist::*;
pub use self::playlist::*;
pub use self::podcast::*;
pub use self::episode::*;
pub use self::chart::*;
pub use self::radio::*;
pub use self::genre::*;
pub use self::editorial::*;
pub use self::lyrics::*;
pub use self::user::*;
pub use self::infos::*;
pub use self::options::*;
use serde::Deserialize;
use serde::de::{self, Deserializer};
use std::fmt::Display;
use std::str::FromStr;
#[derive(Deserialize, Debug)]
pub struct DzArray<T> {
pub data: Vec<T>,
pub checksum: Option<String>,
pub count: Option<u64>,
pub total: Option<u64>,
pub next: Option<String>,
}
pub enum ImgFormat {
JPG,
PNG,
}
pub trait ImgObject {
fn md5_img(&self) -> Option<&str>;
fn img_type(&self) -> &'static str;
fn img_url(&self, size: u16, format: ImgFormat, jpg_quality: u8) -> Option<String> {
let format = match format {
ImgFormat::JPG => "jpg",
ImgFormat::PNG => "png",
};
let jpg_quality = {
let quality: u8;
if format == "png" {
quality = 100;
} else {
quality = jpg_quality;
}
quality
};
Some(format!("https://cdns-images.dzcdn.net/images/{}/{}/{}x{}-000000-{}-0-0.{}", self.img_type(), self.md5_img()?, size, size, jpg_quality, format))
}
}
fn from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where T: FromStr,
T::Err: Display,
D: Deserializer<'de>
{
let mut s = String::deserialize(deserializer)?;
if s.is_empty() {
s = String::from("0");
}
T::from_str(&s).map_err(de::Error::custom)
}
fn from_str_option<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where T: FromStr,
T::Err: Display,
D: Deserializer<'de>
{
let s: Option<String> = Option::deserialize(deserializer)?;
if let Some(mut s) = s {
if s.is_empty() {
s = String::from("0");
}
return Ok(Some(
T::from_str(&s).map_err(de::Error::custom)?,
));
}
Ok(None)
}

26
src/objects/options.rs Normal file
View File

@ -0,0 +1,26 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
// https://developers.deezer.com/api/options/
#[derive(Deserialize, Debug)]
pub struct Options {
pub streaming: bool,
pub streaming_duration: u64,
pub offline: bool,
pub hq: bool,
pub ads_display: bool,
pub ads_audio: bool,
pub too_many_devices: bool,
pub can_subscribe: bool,
pub radio_skips: u32,
pub lossless: bool,
pub preview: bool,
pub radio: bool,
}
impl Options {
pub async fn get(client: &mut crate::Client) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, "options", &()).await
}
}

73
src/objects/playlist.rs Normal file
View File

@ -0,0 +1,73 @@
use serde::Deserialize;
use super::{DzArray, User, Track, ImgObject};
use crate::errors::ErrorKind;
use reqwest::Method;
// https://developers.deezer.com/api/playlist
#[derive(Deserialize, Debug)]
pub struct Playlist {
pub id: u64,
pub title: String,
pub description: Option<String>,
pub duration: Option<u64>,
pub public: bool,
pub is_loved_track: Option<bool>,
pub collaborative: Option<bool>,
pub nb_tracks: Option<u64>,
pub fans: Option<u64>,
pub link: Option<String>,
pub share: Option<String>,
pub picture: String,
pub picture_small: String,
pub picture_medium: String,
pub picture_big: String,
pub picture_xl: String,
pub checksum: String,
pub tracklist: String,
pub creation_date: String,
pub md5_image: String,
pub picture_type: String,
pub curator: Option<bool>,
pub creator: Option<User>,
pub tracks: Option<DzArray<PlaylistTrack>>,
}
impl Playlist {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("playlist/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Result<Self, ErrorKind> {
Self::get(client, self.id).await
}
pub async fn seen(client: &mut crate::Client, id: u64) -> Result<bool, ErrorKind> {
client.api_call(Method::POST, &format!("playlist/{}/seen", id), &()).await
}
pub async fn fans(client: &mut crate::Client, id: u64) -> Result<DzArray<User>, ErrorKind> {
client.api_call(Method::GET, &format!("playlist/{}/fans", id), &()).await
}
pub async fn tracks(client: &mut crate::Client, id: u64) -> Result<DzArray<PlaylistTrack>, ErrorKind> {
client.api_call(Method::GET, &format!("playlist/{}/tracks", id), &()).await
}
}
#[derive(Deserialize, Debug)]
pub struct PlaylistTrack {
#[serde(flatten)]
pub track: Track,
pub time_add: u64,
}
impl ImgObject for Playlist {
fn md5_img(&self) -> Option<&str> {
Some(&self.md5_image)
}
fn img_type(&self) -> &'static str {
"playlist"
}
}

45
src/objects/podcast.rs Normal file
View File

@ -0,0 +1,45 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
use super::{DzArray, Episode, ImgObject};
#[derive(Deserialize, Debug)]
pub struct Podcast {
pub id: u64,
pub title: String,
pub description: Option<String>,
pub available: Option<bool>,
pub rating: Option<u32>,
pub fans: Option<u64>,
pub link: String,
pub share: Option<String>,
pub picture: String,
pub picture_small: String,
pub picture_medium: String,
pub picture_big: String,
pub picture_xl: String,
}
impl Podcast {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("podcast/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Result<Self, ErrorKind> {
Self::get(client, self.id).await
}
pub async fn episodes(client: &mut crate::Client, id: u64) -> Result<DzArray<Episode>, ErrorKind> {
client.api_call(Method::GET, &format!("podcast/{}/episodes", id), &()).await
}
}
impl ImgObject for Podcast {
fn md5_img(&self) -> Option<&str> {
Some(self.picture_small.split('/').collect::<Vec<&str>>()[5])
}
fn img_type(&self) -> &'static str {
"talk"
}
}

55
src/objects/radio.rs Normal file
View File

@ -0,0 +1,55 @@
use serde::Deserialize;
use super::{DzArray, ImgObject};
use crate::errors::ErrorKind;
use reqwest::Method;
#[derive(Deserialize, Debug)]
pub struct Radio {
pub id: u64,
pub title: String,
pub description: Option<String>,
pub share: Option<String>,
pub picture: String,
pub picture_small: String,
pub picture_medium: String,
pub picture_big: String,
pub picture_xl: String,
pub tracklist: String,
pub md5_image: String,
}
impl Radio {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("radio/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Result<Self, ErrorKind> {
Self::get(client, self.id).await
}
pub async fn list(client: &mut crate::Client) -> Result<DzArray<Self>, ErrorKind> {
client.api_call(Method::GET, "radio", &()).await
}
pub async fn genres(client: &mut crate::Client) -> Result<DzArray<RadioGenre>, ErrorKind> {
client.api_call(Method::GET, "radio/genres", &()).await
}
}
#[derive(Deserialize, Debug)]
pub struct RadioGenre {
pub id: u64,
pub title: String,
pub radios: Vec<Radio>,
}
impl ImgObject for Radio {
fn md5_img(&self) -> Option<&str> {
Some(&self.md5_image)
}
fn img_type(&self) -> &'static str {
"misc"
}
}

57
src/objects/search.rs Normal file
View File

@ -0,0 +1,57 @@
use serde::Deserialize;
use super::{DzArray, Track, Album, Artist, Playlist, Podcast, Radio, User};
use crate::errors::ErrorKind;
use reqwest::Method;
pub async fn track(client: &mut crate::client::Client, query: &str) -> Result<DzArray<Track>, ErrorKind> {
Ok(
client.api_call(Method::GET, "search/track", &[("q", query)]).await?
)
}
pub async fn album(client: &mut crate::client::Client, query: &str) -> Result<DzArray<Album>, ErrorKind> {
Ok(
client.api_call(Method::GET, "search/album", &[("q", query)]).await?
)
}
pub async fn artist(client: &mut crate::client::Client, query: &str) -> Result<DzArray<Artist>, ErrorKind> {
Ok(
client.api_call(Method::GET, "search/artist", &[("q", query)]).await?
)
}
pub async fn playlist(client: &mut crate::client::Client, query: &str) -> Result<DzArray<Playlist>, ErrorKind> {
Ok(
client.api_call(Method::GET, "search/playlist", &[("q", query)]).await?
)
}
pub async fn podcast(client: &mut crate::client::Client, query: &str) -> Result<DzArray<Podcast>, ErrorKind> {
Ok(
client.api_call(Method::GET, "search/podcast", &[("q", query)]).await?
)
}
pub async fn radio(client: &mut crate::client::Client, query: &str) -> Result<DzArray<Radio>, ErrorKind> {
Ok(
client.api_call(Method::GET, "search/radio", &[("q", query)]).await?
)
}
pub async fn user(client: &mut crate::client::Client, query: &str) -> Result<DzArray<User>, ErrorKind> {
Ok(
client.api_call(Method::GET, "search/user", &[("q", query)]).await?
)
}
pub async fn history(client: &mut crate::client::Client) -> Result<DzArray<SearchQuery>, ErrorKind> {
Ok(
client.api_call(Method::GET, "search/history", &()).await?
)
}
#[derive(Deserialize, Debug)]
pub struct SearchQuery {
pub query: String,
}

132
src/objects/track.rs Normal file
View File

@ -0,0 +1,132 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
use super::{DzArray, Artist, Album, Genre};
// https://developers.deezer.com/api/track
#[derive(Deserialize, Debug)]
pub struct Track {
pub id: i64,
pub readable: Option<bool>,
pub title: String,
pub title_short: Option<String>,
pub title_version: Option<String>,
pub isrc: Option<String>,
pub link: Option<String>,
pub share: Option<String>,
pub duration: u64,
pub track_position: Option<u64>,
pub disk_number: Option<u64>,
pub rank: u32,
pub release_date: Option<String>,
pub lyrics_id: u64,
pub explicit_lyrics: bool,
pub explicit_content_lyrics: u8,
pub explicit_content_cover: u8,
pub preview: Option<String>,
pub alternative: Option<Box<Track>>,
pub bpm: Option<f32>,
pub gain: Option<f32>,
pub available_countries: Option<Vec<String>>,
pub contributors: Option<Vec<Contributor>>,
pub md5_image: String,
pub user_id: Option<u64>,
pub artist: Option<Artist>,
pub album: Option<Album>,
// stream url gen stuff
pub md5_origin: String,
pub media_version: u8,
// stream filesizes
#[serde(deserialize_with = "super::from_str")]
pub filesize_128: u64,
#[serde(deserialize_with = "super::from_str")]
pub filesize_320: u64,
#[serde(default)]
#[serde(deserialize_with = "super::from_str_option")]
pub filesize_flac: Option<u64>,
#[serde(default)]
#[serde(deserialize_with = "super::from_str_option")]
pub filesize_mhm1_ra3: Option<u64>,
#[serde(deserialize_with = "super::from_str")]
pub filesize_misc: u64,
}
pub enum TrkFormat {
AAC96,
MP364,
MP3128,
MP3320,
FLAC,
MP4RA1,
MP4RA2,
MP4RA3,
SBC256,
Misc,
}
pub fn format_to_num(format: TrkFormat) -> u8 {
match format {
TrkFormat::AAC96 => 8,
TrkFormat::MP364 => 10,
TrkFormat::MP3128 => 1,
TrkFormat::MP3320 => 3,
TrkFormat::FLAC => 9,
TrkFormat::MP4RA1 => 13,
TrkFormat::MP4RA2 => 14,
TrkFormat::MP4RA3 => 15,
TrkFormat::SBC256 => 12,
TrkFormat::Misc => 0,
}
}
impl Track {
pub async fn get(client: &mut crate::Client, id: i64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("track/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Result<Self, ErrorKind> {
Self::get(client, self.id).await
}
pub fn format_to_filesize(&self, format: &TrkFormat) -> Option<u64> {
match format {
TrkFormat::AAC96 => None,
TrkFormat::MP364 => None,
TrkFormat::MP3128 => Some(self.filesize_128),
TrkFormat::MP3320 => Some(self.filesize_320),
TrkFormat::FLAC => self.filesize_flac,
TrkFormat::MP4RA1 => None,
TrkFormat::MP4RA2 => None,
TrkFormat::MP4RA3 => None,
TrkFormat::SBC256 => None,
TrkFormat::Misc => Some(self.filesize_misc),
}
}
#[cfg(feature = "stream")]
pub fn stream_url(&self, format: TrkFormat) -> Option<String> {
if let Some(size) = self.format_to_filesize(&format) {
if size == 0 {
return None;
}
}
let format = format_to_num(format);
Some(crate::crypto::stream_url(&self.md5_origin, format, self.id, self.media_version))
}
pub async fn genres(client: &mut crate::Client, id: i64) -> Result<DzArray<Genre>, ErrorKind> {
client.api_call(Method::GET, &format!("track/{}/genres", id), &()).await
}
}
#[derive(Deserialize, Debug)]
pub struct Contributor {
#[serde(flatten)]
pub artist: Artist,
pub role: String,
}

193
src/objects/user.rs Normal file
View File

@ -0,0 +1,193 @@
use serde::Deserialize;
use crate::errors::ErrorKind;
use reqwest::Method;
use super::{DzArray, Album, Artist, Track, Playlist, Podcast, Radio, ImgObject};
// https://developers.deezer.com/api/user
#[derive(Deserialize, Debug)]
pub struct User {
pub id: u64,
pub name: String,
pub lastname: Option<String>,
pub firstname: Option<String>,
pub email: Option<String>,
pub status: Option<u8>,
pub birthday: Option<String>,
pub inscription_date: Option<String>,
pub gender: Option<String>,
pub link: Option<String>,
pub picture: Option<String>,
pub picture_small: Option<String>,
pub picture_medium: Option<String>,
pub picture_big: Option<String>,
pub picture_xl: Option<String>,
pub country: Option<String>,
pub lang: Option<String>,
pub explicit_content_level: Option<String>,
pub explicit_content_levels_available: Option<Vec<String>>,
pub tracklist: String,
}
impl User {
pub async fn get(client: &mut crate::Client, id: u64) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}", id), &()).await
}
pub async fn get_full(&self, client: &mut crate::Client) -> Result<Self, ErrorKind> {
Self::get(client, self.id).await
}
pub async fn get_self(client: &mut crate::Client) -> Result<Self, ErrorKind> {
client.api_call(Method::GET, "user/me", &()).await
}
pub async fn albums(client: &mut crate::Client, id: u64) -> Result<DzArray<UserAlbum>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/albums", id), &()).await
}
pub async fn artists(client: &mut crate::Client, id: u64) -> Result<DzArray<UserArtist>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/artists", id), &()).await
}
pub async fn tracks(client: &mut crate::Client, id: u64) -> Result<DzArray<UserTrack>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/tracks", id), &()).await
}
pub async fn playlists(client: &mut crate::Client, id: u64) -> Result<DzArray<UserPlaylist>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/playlists", id), &()).await
}
pub async fn personal_songs(client: &mut crate::Client) -> Result<DzArray<Track>, ErrorKind> {
client.api_call(Method::GET, "user/me/personal_songs", &()).await
}
pub async fn album_chart(client: &mut crate::Client, id: u64) -> Result<DzArray<Album>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/charts/albums", id), &()).await
}
pub async fn artist_chart(client: &mut crate::Client, id: u64) -> Result<DzArray<Artist>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/charts/artists", id), &()).await
}
pub async fn track_chart(client: &mut crate::Client, id: u64) -> Result<DzArray<Track>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/charts/tracks", id), &()).await
}
pub async fn playlist_chart(client: &mut crate::Client, id: u64) -> Result<DzArray<Playlist>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/charts/playlists", id), &()).await
}
pub async fn flow(client: &mut crate::Client, id: u64) -> Result<DzArray<Track>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/flow", id), &()).await
}
pub async fn history(client: &mut crate::Client) -> Result<DzArray<Track>, ErrorKind> {
client.api_call(Method::GET, "user/me/history", &()).await
}
pub async fn followings(client: &mut crate::Client, id: u64) -> Result<DzArray<Self>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/followings", id), &()).await
}
pub async fn follow(client: &mut crate::Client, id: u64) -> Result<bool, ErrorKind> {
client.api_call(Method::POST, "user/me/followings", &[("user_id", &id.to_string())]).await
}
pub async fn unfollow(client: &mut crate::Client, id: u64) -> Result<bool, ErrorKind> {
client.api_call(Method::DELETE, "user/me/followings", &[("user_id", &id.to_string())]).await
}
pub async fn followers(client: &mut crate::Client, id: u64) -> Result<DzArray<Self>, ErrorKind> {
client.api_call(Method::GET, &format!("user/{}/followers", id), &()).await
}
pub async fn album_recommendations(client: &mut crate::Client) -> Result<DzArray<RecommendationAlbum>, ErrorKind> {
client.api_call(Method::GET, "user/me/recommendations/albums", &()).await
}
pub async fn release_recommendations(client: &mut crate::Client) -> Result<DzArray<RecommendationAlbum>, ErrorKind> {
client.api_call(Method::GET, "user/me/recommendations/releases", &()).await
}
pub async fn artist_recommendations(client: &mut crate::Client) -> Result<DzArray<Artist>, ErrorKind> {
client.api_call(Method::GET, "user/me/recommendations/artists", &()).await
}
pub async fn playlist_recommendations(client: &mut crate::Client) -> Result<DzArray<Playlist>, ErrorKind> {
client.api_call(Method::GET, "user/me/recommendations/playlists", &()).await
}
pub async fn track_recommendations(client: &mut crate::Client) -> Result<DzArray<Track>, ErrorKind> {
client.api_call(Method::GET, "user/me/recommendations/tracks", &()).await
}
pub async fn radio_recommendations(client: &mut crate::Client) -> Result<DzArray<Radio>, ErrorKind> {
client.api_call(Method::GET, "user/me/recommendations/radios", &()).await
}
}
#[derive(Deserialize, Debug)]
pub struct UserAlbum {
#[serde(flatten)]
pub album: Album,
pub time_add: u64,
}
#[derive(Deserialize, Debug)]
pub struct UserArtist {
#[serde(flatten)]
pub artist: Artist,
pub time_add: u64,
}
#[derive(Deserialize, Debug)]
pub struct UserTrack {
#[serde(flatten)]
pub track: Track,
pub time_add: u64,
}
#[derive(Deserialize, Debug)]
pub struct UserPlaylist {
#[serde(flatten)]
pub playlist: Playlist,
pub time_add: u64,
}
#[derive(Deserialize, Debug)]
pub struct UserPodcast {
#[serde(flatten)]
pub podcast: Podcast,
pub time_add: u64,
}
#[derive(Deserialize, Debug)]
pub struct UserRadio {
#[serde(flatten)]
pub radio: Radio,
pub time_add: u64,
}
#[derive(Deserialize, Debug)]
pub struct RecommendationAlbum {
#[serde(flatten)]
pub album: Album,
pub timestamp: u64,
}
impl ImgObject for User {
fn md5_img(&self) -> Option<&str> {
Some(self.picture_small.as_ref()?.split('/').collect::<Vec<&str>>()[5])
}
fn img_type(&self) -> &'static str {
"user"
}
}

570
tests/test.rs Normal file
View File

@ -0,0 +1,570 @@
use dzlib_rs::{Client, objects::*, errors::ErrorKind};
use tokio_test::block_on;
const EMAIL: &str = "email";
const PASSWORD: &str = "password";
#[test]
fn check_access_token() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.get_token())?;
let token = client.access_token.unwrap().token;
println!("{}", token);
assert_eq!(token.len(), 51);
Ok(())
}
#[test]
fn check_track() -> Result<(), ErrorKind> {
let mut client = Client::new();
let track = block_on(client.track(3135556))?;
println!("{:#?}", track);
Ok(())
}
#[test]
fn check_track_genres() -> Result<(), ErrorKind> {
let mut client = Client::new();
let genres = block_on(Track::genres(&mut client, 3135556))?;
println!("{:#?}", genres);
Ok(())
}
#[test]
fn check_album() -> Result<(), ErrorKind> {
let mut client = Client::new();
let album = block_on(client.album(302127))?;
println!("{:#?}", album);
Ok(())
}
#[test]
fn check_album_fans() -> Result<(), ErrorKind> {
let mut client = Client::new();
let fans = block_on(Album::fans(&mut client, 302127))?;
println!("{:#?}", fans);
Ok(())
}
#[test]
fn check_album_tracks() -> Result<(), ErrorKind> {
let mut client = Client::new();
let tracks = block_on(Album::tracks(&mut client, 302127))?;
println!("{:#?}", tracks);
Ok(())
}
#[test]
fn check_artist() -> Result<(), ErrorKind> {
let mut client = Client::new();
let artist = block_on(client.artist(27))?;
println!("{:#?}", artist);
Ok(())
}
#[test]
fn check_artist_top() -> Result<(), ErrorKind> {
let mut client = Client::new();
let top = block_on(Artist::top(&mut client, 27))?;
println!("{:#?}", top);
Ok(())
}
#[test]
fn check_artist_albums() -> Result<(), ErrorKind> {
let mut client = Client::new();
let albums = block_on(Artist::fans(&mut client, 27))?;
println!("{:#?}", albums);
Ok(())
}
#[test]
fn check_artist_fans() -> Result<(), ErrorKind> {
let mut client = Client::new();
let fans = block_on(Artist::fans(&mut client, 27))?;
println!("{:#?}", fans);
Ok(())
}
#[test]
fn check_artist_related() -> Result<(), ErrorKind> {
let mut client = Client::new();
let related = block_on(Artist::related(&mut client, 27))?;
println!("{:#?}", related);
Ok(())
}
#[test]
fn check_artist_radio() -> Result<(), ErrorKind> {
let mut client = Client::new();
let radio = block_on(Artist::radio(&mut client, 27))?;
println!("{:#?}", radio);
Ok(())
}
#[test]
fn check_artist_playlists() -> Result<(), ErrorKind> {
let mut client = Client::new();
let playlists = block_on(Artist::playlists(&mut client, 27))?;
println!("{:#?}", playlists);
Ok(())
}
#[test]
fn check_playlist() -> Result<(), ErrorKind> {
let mut client = Client::new();
let playlist = block_on(client.playlist(2021502402))?;
println!("{:#?}", playlist);
Ok(())
}
#[test]
fn check_playlist_fans() -> Result<(), ErrorKind> {
let mut client = Client::new();
let playlist = block_on(Playlist::fans(&mut client, 2021502402))?;
println!("{:#?}", playlist);
Ok(())
}
#[test]
fn check_playlist_tracks() -> Result<(), ErrorKind> {
let mut client = Client::new();
let tracks = block_on(Playlist::tracks(&mut client, 2021502402))?;
println!("{:#?}", tracks);
Ok(())
}
#[test]
fn check_playlist_seen() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let seen = block_on(Playlist::seen(&mut client, 2021502402))?;
println!("{:#?}", seen);
Ok(())
}
#[test]
fn check_user() -> Result<(), ErrorKind> {
let mut client = Client::new();
let user = block_on(client.user(2741487522))?;
println!("{:#?}", user);
Ok(())
}
#[test]
fn check_user_self() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let user = block_on(client.user_self())?;
println!("{:#?}", user);
Ok(())
}
#[test]
fn check_user_albums() -> Result<(), ErrorKind> {
let mut client = Client::new();
let albums = block_on(User::albums(&mut client, 5))?;
println!("{:#?}", albums);
Ok(())
}
#[test]
fn check_user_artists() -> Result<(), ErrorKind> {
let mut client = Client::new();
let artists = block_on(User::artists(&mut client, 5))?;
println!("{:#?}", artists);
Ok(())
}
#[test]
fn check_user_tracks() -> Result<(), ErrorKind> {
let mut client = Client::new();
let tracks = block_on(User::tracks(&mut client, 5))?;
println!("{:#?}", tracks);
Ok(())
}
#[test]
fn check_user_playlists() -> Result<(), ErrorKind> {
let mut client = Client::new();
let playlists = block_on(User::playlists(&mut client, 5))?;
println!("{:#?}", playlists);
Ok(())
}
#[test]
fn check_user_personal_songs() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let songs = block_on(User::personal_songs(&mut client))?;
println!("{:#?}", songs);
Ok(())
}
#[test]
fn check_user_album_chart() -> Result<(), ErrorKind> {
let mut client = Client::new();
let chart = block_on(User::album_chart(&mut client, 5))?;
println!("{:#?}", chart);
Ok(())
}
#[test]
fn check_user_artist_chart() -> Result<(), ErrorKind> {
let mut client = Client::new();
let chart = block_on(User::artist_chart(&mut client, 5))?;
println!("{:#?}", chart);
Ok(())
}
#[test]
fn check_user_track_chart() -> Result<(), ErrorKind> {
let mut client = Client::new();
let chart = block_on(User::track_chart(&mut client, 5))?;
println!("{:#?}", chart);
Ok(())
}
#[test]
fn check_user_playlist_chart() -> Result<(), ErrorKind> {
let mut client = Client::new();
let chart = block_on(User::playlist_chart(&mut client, 5))?;
println!("{:#?}", chart);
Ok(())
}
#[test]
fn check_user_flow() -> Result<(), ErrorKind> {
let mut client = Client::new();
let flow = block_on(User::flow(&mut client, 5))?;
println!("{:#?}", flow);
Ok(())
}
#[test]
fn check_user_history() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let history = block_on(User::history(&mut client))?;
println!("{:#?}", history);
Ok(())
}
#[test]
fn check_user_followings() -> Result<(), ErrorKind> {
let mut client = Client::new();
let followings = block_on(User::followings(&mut client, 5))?;
println!("{:#?}", followings);
Ok(())
}
#[test]
fn check_user_followers() -> Result<(), ErrorKind> {
let mut client = Client::new();
let followers = block_on(User::followers(&mut client, 5))?;
println!("{:#?}", followers);
Ok(())
}
#[test]
fn check_user_album_rec() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let rec = block_on(User::album_recommendations(&mut client))?;
println!("{:#?}", rec);
Ok(())
}
#[test]
fn check_user_release_rec() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let rec = block_on(User::release_recommendations(&mut client))?;
println!("{:#?}", rec);
Ok(())
}
#[test]
fn check_user_artist_rec() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let rec = block_on(User::artist_recommendations(&mut client))?;
println!("{:#?}", rec);
Ok(())
}
#[test]
fn check_user_playlist_rec() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let rec = block_on(User::playlist_recommendations(&mut client))?;
println!("{:#?}", rec);
Ok(())
}
#[test]
fn check_user_track_rec() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let rec = block_on(User::track_recommendations(&mut client))?;
println!("{:#?}", rec);
Ok(())
}
#[test]
fn check_user_radio_rec() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let rec = block_on(User::radio_recommendations(&mut client))?;
println!("{:#?}", rec);
Ok(())
}
#[test]
fn check_infos() -> Result<(), ErrorKind> {
let mut client = Client::new();
let infos = block_on(client.infos())?;
println!("{:#?}", infos);
Ok(())
}
#[test]
fn check_genre() -> Result<(), ErrorKind> {
let mut client = Client::new();
let genre = block_on(client.genre(0))?;
println!("{:#?}", genre);
Ok(())
}
#[test]
fn check_genre_artists() -> Result<(), ErrorKind> {
let mut client = Client::new();
let artists = block_on(Genre::artists(&mut client, 0))?;
println!("{:#?}", artists);
Ok(())
}
#[test]
fn check_genre_radios() -> Result<(), ErrorKind> {
let mut client = Client::new();
let radios = block_on(Genre::radios(&mut client, 0))?;
println!("{:#?}", radios);
Ok(())
}
#[test]
fn check_options() -> Result<(), ErrorKind> {
let mut client = Client::new();
let options = block_on(client.options())?;
println!("{:#?}", options);
Ok(())
}
#[test]
fn check_editorial() -> Result<(), ErrorKind> {
let mut client = Client::new();
let editorial = block_on(client.editorial(0))?;
println!("{:#?}", editorial);
Ok(())
}
#[test]
fn check_editorial_list() -> Result<(), ErrorKind> {
let mut client = Client::new();
let list = block_on(Editorial::list(&mut client))?;
println!("{:#?}", list);
Ok(())
}
#[test]
fn check_editorial_selection() -> Result<(), ErrorKind> {
let mut client = Client::new();
let selection = block_on(Editorial::selection(&mut client, 0))?;
println!("{:#?}", selection);
Ok(())
}
#[test]
fn check_editorial_charts() -> Result<(), ErrorKind> {
let mut client = Client::new();
let charts = block_on(Editorial::charts(&mut client, 0))?;
println!("{:#?}", charts);
Ok(())
}
#[test]
fn check_lyrics() -> Result<(), ErrorKind> {
let mut client = Client::new();
let lyrics = block_on(client.lyrics(2780622))?;
println!("{:#?}", lyrics);
Ok(())
}
#[test]
fn check_radio() -> Result<(), ErrorKind> {
let mut client = Client::new();
let radio = block_on(client.radio(6))?;
println!("{:#?}", radio);
Ok(())
}
#[test]
fn check_radio_list() -> Result<(), ErrorKind> {
let mut client = Client::new();
let radio_list = block_on(client.radio_list())?;
println!("{:#?}", radio_list);
Ok(())
}
#[test]
fn check_radio_genres() -> Result<(), ErrorKind> {
let mut client = Client::new();
let genres = block_on(Radio::genres(&mut client))?;
println!("{:#?}", genres);
Ok(())
}
#[test]
fn check_chart() -> Result<(), ErrorKind> {
let mut client = Client::new();
let chart = block_on(client.chart(0))?;
println!("{:#?}", chart);
Ok(())
}
#[test]
fn check_chart_tracks() -> Result<(), ErrorKind> {
let mut client = Client::new();
let tracks = block_on(Chart::tracks(&mut client, 0))?;
println!("{:#?}", tracks);
Ok(())
}
#[test]
fn check_chart_albums() -> Result<(), ErrorKind> {
let mut client = Client::new();
let albums = block_on(Chart::albums(&mut client, 0))?;
println!("{:#?}", albums);
Ok(())
}
#[test]
fn check_chart_artists() -> Result<(), ErrorKind> {
let mut client = Client::new();
let artists = block_on(Chart::artists(&mut client, 0))?;
println!("{:#?}", artists);
Ok(())
}
#[test]
fn check_chart_playlists() -> Result<(), ErrorKind> {
let mut client = Client::new();
let playlists = block_on(Chart::playlists(&mut client, 0))?;
println!("{:#?}", playlists);
Ok(())
}
#[test]
fn check_chart_podcasts() -> Result<(), ErrorKind> {
let mut client = Client::new();
let playlists = block_on(Chart::podcasts(&mut client, 0))?;
println!("{:#?}", playlists);
Ok(())
}
#[test]
fn check_search_track() -> Result<(), ErrorKind> {
let mut client = Client::new();
let search = block_on(client.search_track("eminem"))?;
println!("{:#?}", search);
Ok(())
}
#[test]
fn check_search_album() -> Result<(), ErrorKind> {
let mut client = Client::new();
let search = block_on(client.search_album("eminem"))?;
println!("{:#?}", search);
Ok(())
}
#[test]
fn check_search_artist() -> Result<(), ErrorKind> {
let mut client = Client::new();
let search = block_on(client.search_artist("eminem"))?;
println!("{:#?}", search);
Ok(())
}
#[test]
fn check_search_playlist() -> Result<(), ErrorKind> {
let mut client = Client::new();
let search = block_on(client.search_playlist("eminem"))?;
println!("{:#?}", search);
Ok(())
}
#[test]
fn check_search_podcast() -> Result<(), ErrorKind> {
let mut client = Client::new();
let search = block_on(search::podcast(&mut client, "eminem"))?;
println!("{:#?}", search);
Ok(())
}
#[test]
fn check_search_radio() -> Result<(), ErrorKind> {
let mut client = Client::new();
let search = block_on(search::radio(&mut client, "classic"))?;
println!("{:#?}", search);
Ok(())
}
#[test]
fn check_search_user() -> Result<(), ErrorKind> {
let mut client = Client::new();
let search = block_on(search::user(&mut client, "eminem"))?;
println!("{:#?}", search);
Ok(())
}
#[test]
fn check_search_history() -> Result<(), ErrorKind> {
let mut client = Client::new();
block_on(client.login(EMAIL, PASSWORD))?;
let search = block_on(search::history(&mut client))?;
println!("{:#?}", search);
Ok(())
}
#[test]
fn check_podcast() -> Result<(), ErrorKind> {
let mut client = Client::new();
let podcast = block_on(client.podcast(2027))?;
println!("{:#?}", podcast);
Ok(())
}
#[test]
fn check_podcast_episodes() -> Result<(), ErrorKind> {
let mut client = Client::new();
let episodes = block_on(Podcast::episodes(&mut client, 2027))?;
println!("{:#?}", episodes);
Ok(())
}
#[test]
fn check_episode() -> Result<(), ErrorKind> {
let mut client = Client::new();
let episode = block_on(client.episode(56469))?;
println!("{:#?}", episode);
Ok(())
}
#[test]
fn check_register() -> Result<(), ErrorKind> {
let mut client = Client::new();
let user_id = block_on(auth::register(&mut client, "email", "password", "name", "0000-00-00", "M"))?;
println!("{:#?}", user_id);
Ok(())
}