use crate::Error;
use crate::TIDAL_API_BASE_URL;
use crate::TidalClient;
use crate::track::Track;
use crate::Order;
use crate::OrderDirection;
use crate::artist::ArtistSummary;
use crate::MediaMetadata;
use crate::List;
use reqwest::Method;
use serde_json::Value;
use serde::{Deserialize, Serialize};
use strum_macros::AsRefStr;
use crate::AudioQuality;
use strum_macros::EnumString;
#[derive(Default, Debug, Serialize, Deserialize, EnumString, AsRefStr, PartialEq, Eq, Copy, Clone)]
#[serde(rename_all = "UPPERCASE")]
#[strum(serialize_all = "UPPERCASE")]
pub enum AlbumType {
#[default]
ALBUM,
Lp,
Ep,
Single,
EpsAndSingles,
Compilations,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Album {
pub id: u64,
#[serde(default = "Default::default")]
pub artists: Vec<ArtistSummary>,
pub audio_quality: AudioQuality,
pub duration: u32,
pub explicit: bool,
pub title: String,
pub popularity: u32,
pub media_metadata: Option<MediaMetadata>,
pub cover: Option<String>,
pub video_cover: Option<String>,
pub vibrant_color: Option<String>,
pub release_date: Option<String>,
pub stream_start_date: Option<String>,
pub copyright: Option<String>,
pub number_of_tracks: u32,
pub number_of_videos: u32,
pub number_of_volumes: u32,
pub upc: Option<String>,
pub url: String,
pub version: Option<String>,
#[serde(rename = "type")]
pub album_type: AlbumType,
pub ad_supported_stream_ready: bool,
pub allow_streaming: bool,
pub dj_ready: bool,
pub pay_to_stream: bool,
pub premium_streaming_only: bool,
pub stem_ready: bool,
pub stream_ready: bool,
pub audio_modes: Vec<String>,
}
impl Album {
pub fn cover_url(&self, height: u16, width: u16) -> Option<String> {
self.cover.as_ref().map(|cover| {
let cover_path = cover.replace('-', "/");
format!("https://resources.tidal.com/images/{cover_path}/{height}x{width}.jpg")
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct FavoriteAlbum {
pub created: String,
pub item: Album,
}
impl TidalClient {
pub async fn album(
&self,
album_id: u64,
) -> Result<Album, Error> {
let url = format!("{TIDAL_API_BASE_URL}/albums/{album_id}");
let params = serde_json::json!({
"countryCode": self.get_country_code(),
"locale": self.get_locale(),
"deviceType": self.get_device_type().as_ref(),
});
let resp: Album = self.do_request(Method::GET, &url, Some(params), None).await?;
Ok(resp)
}
pub async fn album_tracks(
&self,
album_id: u64,
offset: Option<u32>,
limit: Option<u32>,
) -> Result<List<Track>, Error> {
let offset = offset.unwrap_or(0);
let limit = limit.unwrap_or(100);
let url = format!("{TIDAL_API_BASE_URL}/albums/{album_id}/tracks");
let params = serde_json::json!({
"offset": offset,
"limit": limit,
"countryCode": self.get_country_code(),
"locale": self.get_locale(),
"deviceType": self.get_device_type().as_ref(),
});
let resp: List<Track> = self.do_request(Method::GET, &url, Some(params), None).await?;
Ok(resp)
}
pub async fn favorite_albums(
&self,
offset: Option<u32>,
limit: Option<u32>,
order: Option<Order>,
order_direction: Option<OrderDirection>,
) -> Result<List<FavoriteAlbum>, Error> {
let user_id = self.get_user_id().ok_or(Error::UserAuthenticationRequired)?;
let offset = offset.unwrap_or(0);
let limit = limit.unwrap_or(100);
let url = format!("{TIDAL_API_BASE_URL}/users/{user_id}/favorites/albums");
let params = serde_json::json!({
"offset": offset,
"limit": limit,
"order": order.unwrap_or(Order::Date).as_ref(),
"orderDirection": order_direction.unwrap_or(OrderDirection::Desc).as_ref(),
"countryCode": self.get_country_code(),
"locale": self.get_locale(),
"deviceType": self.get_device_type().as_ref(),
});
let resp: List<FavoriteAlbum> = self.do_request(Method::GET, &url, Some(params), None).await?;
Ok(resp)
}
pub async fn add_favorite_album(
&self,
album_id: u64,
) -> Result<(), Error> {
let user_id = self.get_user_id().ok_or(Error::UserAuthenticationRequired)?;
let url = format!("{TIDAL_API_BASE_URL}/users/{user_id}/favorites/albums");
let params = serde_json::json!({
"albumId": album_id,
"countryCode": self.get_country_code(),
"locale": self.get_locale(),
"deviceType": self.get_device_type().as_ref(),
});
let _: Value = self.do_request(Method::POST, &url, Some(params), None).await?;
Ok(())
}
pub async fn remove_favorite_album(
&self,
album_id: u64,
) -> Result<(), Error> {
let user_id = self.get_user_id().ok_or(Error::UserAuthenticationRequired)?;
let url = format!("{TIDAL_API_BASE_URL}/users/{user_id}/favorites/albums/{album_id}");
let params = serde_json::json!({
"countryCode": self.get_country_code(),
"locale": self.get_locale(),
"deviceType": self.get_device_type().as_ref(),
});
let _: Value = self.do_request(Method::DELETE, &url, Some(params), None).await?;
Ok(())
}
}