Compare commits
3 commits
a31e089ff4
...
df69f4160a
Author | SHA1 | Date | |
---|---|---|---|
df69f4160a | |||
199827e776 | |||
603e178e17 |
19 changed files with 1486 additions and 1084 deletions
|
@ -156,6 +156,8 @@ impl From<SubtitleFormatClap> for SubtitleFormat {
|
|||
}
|
||||
}
|
||||
|
||||
const NA_STR: &str = "n/a";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let cli = Cli::parse();
|
||||
|
@ -235,8 +237,14 @@ async fn run(cli: Cli) -> Result<()> {
|
|||
};
|
||||
|
||||
eprintln!("Lyrics ID: {}", lyrics.lyrics_id);
|
||||
eprintln!("Language: {}", lyrics.lyrics_language);
|
||||
eprintln!("Copyright: {}", lyrics.lyrics_copyright);
|
||||
eprintln!(
|
||||
"Language: {}",
|
||||
lyrics.lyrics_language.as_deref().unwrap_or(NA_STR)
|
||||
);
|
||||
eprintln!(
|
||||
"Copyright: {}",
|
||||
lyrics.lyrics_copyright.as_deref().unwrap_or(NA_STR)
|
||||
);
|
||||
eprintln!();
|
||||
|
||||
println!("{}", lyrics.lyrics_body);
|
||||
|
@ -298,9 +306,15 @@ async fn run(cli: Cli) -> Result<()> {
|
|||
};
|
||||
|
||||
eprintln!("Subtitle ID: {}", subtitles.subtitle_id);
|
||||
eprintln!("Language: {}", subtitles.subtitle_language);
|
||||
eprintln!(
|
||||
"Language: {}",
|
||||
subtitles.subtitle_language.as_deref().unwrap_or(NA_STR)
|
||||
);
|
||||
eprintln!("Length: {}", subtitles.subtitle_length);
|
||||
eprintln!("Copyright: {}", subtitles.lyrics_copyright);
|
||||
eprintln!(
|
||||
"Copyright: {}",
|
||||
subtitles.lyrics_copyright.as_deref().unwrap_or(NA_STR)
|
||||
);
|
||||
eprintln!();
|
||||
|
||||
println!("{}", subtitles.subtitle_body);
|
||||
|
|
378
src/api_model.rs
378
src/api_model.rs
|
@ -1,3 +1,5 @@
|
|||
use std::{marker::PhantomData, str::FromStr};
|
||||
|
||||
use serde::{de::Visitor, Deserialize, Deserializer, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
|
@ -227,7 +229,269 @@ where
|
|||
deserializer.deserialize_any(BoolFromIntVisitor)
|
||||
}
|
||||
|
||||
/**
|
||||
* The Musixmatch API returns zero as a default value for numeric IDs.
|
||||
* These values should be deserialized as [`None`].
|
||||
*/
|
||||
pub fn null_if_zero<'de, D, N>(deserializer: D) -> Result<Option<N>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
N: TryFrom<u64>,
|
||||
{
|
||||
struct NullIfZeroVisitor<N> {
|
||||
n: PhantomData<N>,
|
||||
}
|
||||
|
||||
impl<'de, N> Visitor<'de> for NullIfZeroVisitor<N>
|
||||
where
|
||||
N: TryFrom<u64>,
|
||||
{
|
||||
type Value = Option<N>;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("unsigned int or None")
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if v < 1 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(v.try_into().ok())
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_u64(v.into())
|
||||
}
|
||||
|
||||
fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_u64(v.into())
|
||||
}
|
||||
|
||||
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_u64(v.into())
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(NullIfZeroVisitor { n: PhantomData })
|
||||
}
|
||||
|
||||
/// The Musixmatch API returns an empty string as a default value for string fields.
|
||||
/// These values should be deserialized as [`None`].
|
||||
pub fn null_if_empty<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct NullIfEmptyVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for NullIfEmptyVisitor {
|
||||
type Value = Option<String>;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("string or None")
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if v.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(v.to_owned()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(NullIfEmptyVisitor)
|
||||
}
|
||||
|
||||
/// Deserialize a numeric string into an integer.
|
||||
/// Return None if the string is empty/null or if there was a parse error.
|
||||
pub fn parse_int<'de, D, N>(deserializer: D) -> Result<Option<N>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
N: FromStr + TryFrom<u64>,
|
||||
{
|
||||
struct ParseIntVisitor<N> {
|
||||
n: PhantomData<N>,
|
||||
}
|
||||
|
||||
impl<'de, N> Visitor<'de> for ParseIntVisitor<N>
|
||||
where
|
||||
N: FromStr + TryFrom<u64>,
|
||||
{
|
||||
type Value = Option<N>;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("numeric string or None")
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(v.try_into().ok())
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_u64(v.into())
|
||||
}
|
||||
|
||||
fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_u64(v.into())
|
||||
}
|
||||
|
||||
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
self.visit_u64(v.into())
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if v.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(v.parse().ok())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(ParseIntVisitor { n: PhantomData })
|
||||
}
|
||||
|
||||
pub mod optional_date {
|
||||
use super::*;
|
||||
use serde::ser::Error as _;
|
||||
use serde::Serializer;
|
||||
use time::{macros::format_description, Date};
|
||||
|
||||
const DATE_FORMAT: &[time::format_description::FormatItem] =
|
||||
format_description!("[year]-[month]-[day]");
|
||||
|
||||
pub fn serialize<S: Serializer>(
|
||||
value: &Option<Date>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
match value {
|
||||
Some(date) => date
|
||||
.format(&DATE_FORMAT)
|
||||
.map_err(S::Error::custom)?
|
||||
.serialize(serializer),
|
||||
None => serializer.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<Date>, D::Error> {
|
||||
struct OptionalDateVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for OptionalDateVisitor {
|
||||
type Value = Option<Date>;
|
||||
|
||||
fn expecting(
|
||||
&self,
|
||||
formatter: &mut serde::__private::fmt::Formatter,
|
||||
) -> serde::__private::fmt::Result {
|
||||
formatter.write_str("date or empty string")
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if v.is_empty() || v == "0000-00-00" {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Date::parse(v, &DATE_FORMAT).ok())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(OptionalDateVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod optional_datetime {
|
||||
use super::*;
|
||||
use serde::Serializer;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
|
@ -257,6 +521,20 @@ pub mod optional_date {
|
|||
formatter.write_str("timestamp or empty string")
|
||||
}
|
||||
|
||||
fn visit_none<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
|
@ -271,12 +549,14 @@ pub mod optional_date {
|
|||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(OptionalDateVisitor)
|
||||
deserializer.deserialize_any(OptionalDateVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use time::Date;
|
||||
|
||||
use super::*;
|
||||
use crate::models::subtitle::SubtitleBody;
|
||||
|
||||
|
@ -338,17 +618,113 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_null_if_zero() {
|
||||
#[derive(Deserialize)]
|
||||
struct S {
|
||||
#[serde(default, deserialize_with = "null_if_zero")]
|
||||
n: Option<u64>,
|
||||
}
|
||||
|
||||
let json = r#"{"n": 0}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().n, None);
|
||||
|
||||
let json = r#"{"n": 1}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().n, Some(1));
|
||||
|
||||
let json = r#"{"n": null}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().n, None);
|
||||
|
||||
let json = r#"{}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().n, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_null_if_empty() {
|
||||
#[derive(Deserialize)]
|
||||
struct S {
|
||||
#[serde(default, deserialize_with = "null_if_empty")]
|
||||
s: Option<String>,
|
||||
}
|
||||
|
||||
let json = r#"{"s": ""}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().s, None);
|
||||
|
||||
let json = r#"{"s": "a"}"#;
|
||||
assert_eq!(
|
||||
serde_json::from_str::<S>(json).unwrap().s,
|
||||
Some("a".to_owned())
|
||||
);
|
||||
|
||||
let json = r#"{"n": null}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().s, None);
|
||||
|
||||
let json = r#"{}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().s, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_parse_int() {
|
||||
#[derive(Deserialize)]
|
||||
struct S {
|
||||
#[serde(default, deserialize_with = "parse_int")]
|
||||
n: Option<u64>,
|
||||
}
|
||||
|
||||
let json = r#"{"n": 0}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().n, Some(0));
|
||||
|
||||
let json = r#"{"n": "0"}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().n, Some(0));
|
||||
|
||||
let json = r#"{"n": null}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().n, None);
|
||||
|
||||
let json = r#"{}"#;
|
||||
assert_eq!(serde_json::from_str::<S>(json).unwrap().n, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_optional_date() {
|
||||
#[derive(Deserialize)]
|
||||
struct S {
|
||||
#[serde(with = "optional_date")]
|
||||
date: Option<Date>,
|
||||
}
|
||||
|
||||
let json_null_string = r#"{"date": null}"#;
|
||||
let json_empty_string = r#"{"date": ""}"#;
|
||||
let json_zero_date = r#"{"date": "0000-00-00"}"#;
|
||||
let json_date = r#"{"date": "2022-08-27"}"#;
|
||||
|
||||
let res = serde_json::from_str::<S>(json_null_string).unwrap();
|
||||
assert!(res.date.is_none());
|
||||
|
||||
let res = serde_json::from_str::<S>(json_empty_string).unwrap();
|
||||
assert!(res.date.is_none());
|
||||
|
||||
let res = serde_json::from_str::<S>(json_zero_date).unwrap();
|
||||
assert!(res.date.is_none());
|
||||
|
||||
let res = serde_json::from_str::<S>(json_date).unwrap();
|
||||
assert!(res.date.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_optional_datetime() {
|
||||
#[derive(Deserialize)]
|
||||
struct S {
|
||||
#[serde(with = "optional_datetime")]
|
||||
date: Option<OffsetDateTime>,
|
||||
}
|
||||
|
||||
let json_null_string = r#"{"date": null}"#;
|
||||
let json_empty_string = r#"{"date": ""}"#;
|
||||
let json_date = r#"{"date": "2022-08-27T23:47:20Z"}"#;
|
||||
|
||||
let res = serde_json::from_str::<S>(json_null_string).unwrap();
|
||||
assert!(res.date.is_none());
|
||||
|
||||
let res = serde_json::from_str::<S>(json_empty_string).unwrap();
|
||||
assert!(res.date.is_none());
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ impl Musixmatch {
|
|||
|
||||
/// Create a new query builder for searching tracks in the Musixmatch database.
|
||||
///
|
||||
/// **Note:** The search results are unsorted the by default. You probably want
|
||||
/// **Note:** The search results are unordered the by default. You probably want
|
||||
/// to sort by popularity (`.s_track_rating(SortOrder::Desc)`) to get relevant results.
|
||||
///
|
||||
/// # Example
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
use time::{Date, OffsetDateTime};
|
||||
|
||||
use super::Genres;
|
||||
|
||||
|
@ -19,10 +19,8 @@ pub struct Album {
|
|||
/// Unique Musixmatch Album ID
|
||||
pub album_id: u64,
|
||||
/// Musicbrainz album ID
|
||||
///
|
||||
/// **Note:** most albums dont have this entry set
|
||||
#[serde(default)]
|
||||
pub album_mbid: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_mbid: Option<String>,
|
||||
/// Album name
|
||||
pub album_name: String,
|
||||
/// Popularity of the album from 0 to 100
|
||||
|
@ -30,14 +28,14 @@ pub struct Album {
|
|||
pub album_rating: u8,
|
||||
/// Number of tracks on the album
|
||||
pub album_track_count: u16,
|
||||
/// Album release date (e.g. "2009-07-07")
|
||||
/// Album release date
|
||||
#[serde(default, with = "crate::api_model::optional_date")]
|
||||
pub album_release_date: Option<Date>,
|
||||
/// Album type
|
||||
#[serde(default)]
|
||||
pub album_release_date: String,
|
||||
/// Album type (Single / EP / Album)
|
||||
pub album_release_type: String,
|
||||
pub album_release_type: AlbumType,
|
||||
|
||||
/// Musixmatch artist ID
|
||||
#[serde(default)]
|
||||
pub artist_id: u64,
|
||||
/// Artist name
|
||||
pub artist_name: String,
|
||||
|
@ -48,19 +46,15 @@ pub struct Album {
|
|||
/// Secondary genres / Subgenres
|
||||
///
|
||||
/// Example: primary_genres: Pop, secondary_genres: K-Pop / Mandopop
|
||||
///
|
||||
/// Note that this schema is not applied to all tracks on Musixmatch.
|
||||
/// There are for example many K-Pop tracks with both Pop and K-Pop
|
||||
/// tagged as primary genre and this field empty.
|
||||
#[serde(default)]
|
||||
pub secondary_genres: Genres,
|
||||
|
||||
/// Album copyright text
|
||||
#[serde(default)]
|
||||
pub album_copyright: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_copyright: Option<String>,
|
||||
/// Album label / recording company
|
||||
#[serde(default)]
|
||||
pub album_label: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_label: Option<String>,
|
||||
/// Human-readable URL-safe Album ID
|
||||
///
|
||||
/// Example: `LMFAO/Party-Rock-5`
|
||||
|
@ -71,17 +65,31 @@ pub struct Album {
|
|||
pub updated_time: OffsetDateTime,
|
||||
|
||||
/// Album cover URL (100x100px)
|
||||
#[serde(default)]
|
||||
pub album_coverart_100x100: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_coverart_100x100: Option<String>,
|
||||
/// Album cover URL (350x350px)
|
||||
#[serde(default)]
|
||||
pub album_coverart_350x350: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_coverart_350x350: Option<String>,
|
||||
/// Album cover URL (500x500px)
|
||||
#[serde(default)]
|
||||
pub album_coverart_500x500: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_coverart_500x500: Option<String>,
|
||||
/// Album cover URL (800x800px)
|
||||
///
|
||||
/// **Note:** not present on a lot of albums
|
||||
#[serde(default)]
|
||||
pub album_coverart_800x800: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_coverart_800x800: Option<String>,
|
||||
}
|
||||
|
||||
/// Album type
|
||||
///
|
||||
/// Source: <https://developer.musixmatch.com/documentation/music-meta-data>
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum AlbumType {
|
||||
#[default]
|
||||
Album,
|
||||
Single,
|
||||
Compilation,
|
||||
Remix,
|
||||
Live,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
use time::{Date, OffsetDateTime};
|
||||
|
||||
use super::Genres;
|
||||
|
||||
|
@ -19,10 +19,8 @@ pub struct Artist {
|
|||
/// Musixmatch Artist ID
|
||||
pub artist_id: u64,
|
||||
/// Musicbrainz Artist ID
|
||||
///
|
||||
/// **Note:** most tracks dont have this entry set
|
||||
#[serde(default)]
|
||||
pub artist_mbid: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub artist_mbid: Option<String>,
|
||||
|
||||
/// Artist name
|
||||
pub artist_name: String,
|
||||
|
@ -30,8 +28,8 @@ pub struct Artist {
|
|||
#[serde(default)]
|
||||
pub artist_name_translation_list: Vec<ArtistNameTranslation>,
|
||||
/// Artist origin as a 2-letter country code (e.g. "US")
|
||||
#[serde(default)]
|
||||
pub artist_country: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub artist_country: Option<String>,
|
||||
/// Alternative names for the artist (e.g. in different languages)
|
||||
#[serde(default)]
|
||||
pub artist_alias_list: Vec<ArtistAlias>,
|
||||
|
@ -53,20 +51,20 @@ pub struct Artist {
|
|||
pub secondary_genres: Genres,
|
||||
|
||||
/// URL of the artist's Twitter profile
|
||||
#[serde(default)]
|
||||
pub artist_twitter_url: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub artist_twitter_url: Option<String>,
|
||||
/// URL of the artist's website
|
||||
#[serde(default)]
|
||||
pub artist_website_url: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub artist_website_url: Option<String>,
|
||||
/// URL of the artist's Instagram profile
|
||||
#[serde(default)]
|
||||
pub artist_instagram_url: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub artist_instagram_url: Option<String>,
|
||||
/// URL of the artist's TikTok profile
|
||||
#[serde(default)]
|
||||
pub artist_tiktok_url: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub artist_tiktok_url: Option<String>,
|
||||
/// URL of the artist's Facebook profile
|
||||
#[serde(default)]
|
||||
pub artist_facebook_url: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub artist_facebook_url: Option<String>,
|
||||
|
||||
/// URL-safe human-readable artist ID
|
||||
///
|
||||
|
@ -75,22 +73,18 @@ pub struct Artist {
|
|||
/// Date and time when the artist was last updated
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
pub updated_time: OffsetDateTime,
|
||||
/// Year of the start of the artist's presence
|
||||
#[serde(default)]
|
||||
pub begin_date_year: String,
|
||||
/// Start date of the artist's presence in YYYY-MM-DD format
|
||||
///
|
||||
/// **Info:** the default value is `"0000-00-00"`
|
||||
#[serde(default)]
|
||||
pub begin_date: String,
|
||||
/// Year of the end of the artist's presence
|
||||
#[serde(default)]
|
||||
pub end_date_year: String,
|
||||
/// End date of the artist's presence in YYYY-MM-DD format
|
||||
///
|
||||
/// **Info:** the default value is `"0000-00-00"`
|
||||
#[serde(default)]
|
||||
pub end_date: String,
|
||||
/// Start year of the artist's presence
|
||||
#[serde(default, deserialize_with = "crate::api_model::parse_int")]
|
||||
pub begin_date_year: Option<u16>,
|
||||
/// Start date of the artist's presence
|
||||
#[serde(default, with = "crate::api_model::optional_date")]
|
||||
pub begin_date: Option<Date>,
|
||||
/// End year of the artist's presence
|
||||
#[serde(default, deserialize_with = "crate::api_model::parse_int")]
|
||||
pub end_date_year: Option<u16>,
|
||||
/// End date of the artist's presence
|
||||
#[serde(default, with = "crate::api_model::optional_date")]
|
||||
pub end_date: Option<Date>,
|
||||
}
|
||||
|
||||
/// Alternative artist name (e.g. different languages)
|
||||
|
|
|
@ -21,11 +21,11 @@ pub struct Lyrics {
|
|||
/// Lyrics text
|
||||
pub lyrics_body: String,
|
||||
/// Language code (e.g. "en")
|
||||
#[serde(default)]
|
||||
pub lyrics_language: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub lyrics_language: Option<String>,
|
||||
/// Language name (e.g. "English")
|
||||
#[serde(default)]
|
||||
pub lyrics_language_description: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub lyrics_language_description: Option<String>,
|
||||
/// Copyright text of the lyrics
|
||||
///
|
||||
/// Ends with a newline.
|
||||
|
@ -35,8 +35,8 @@ pub struct Lyrics {
|
|||
/// Writer(s): David Hodges
|
||||
/// Copyright: Emi Blackwood Music Inc., 12.06 Publishing, Hipgnosis Sfh I Limited, Hifi Music Ip Issuer L.p.
|
||||
/// ```
|
||||
#[serde(default)]
|
||||
pub lyrics_copyright: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub lyrics_copyright: Option<String>,
|
||||
/// Date and time when the lyrics were last updated
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
pub updated_time: OffsetDateTime,
|
||||
|
|
|
@ -39,6 +39,7 @@ pub use artist::ArtistNameTranslationInner;
|
|||
|
||||
pub(crate) mod album;
|
||||
pub use album::Album;
|
||||
pub use album::AlbumType;
|
||||
|
||||
pub(crate) mod snippet;
|
||||
pub use snippet::Snippet;
|
||||
|
|
|
@ -18,8 +18,8 @@ pub struct Snippet {
|
|||
/// Unique Musixmatch Snippet ID
|
||||
pub snippet_id: u64,
|
||||
/// Snippet language code (e.g. "en")
|
||||
#[serde(default)]
|
||||
pub snippet_language: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub snippet_language: Option<String>,
|
||||
/// True if the track is instrumental
|
||||
#[serde(default, deserialize_with = "crate::api_model::bool_from_int")]
|
||||
pub instrumental: bool,
|
||||
|
|
|
@ -196,11 +196,11 @@ pub struct Subtitle {
|
|||
/// Subtitle / synchronized lyrics in the requested format
|
||||
pub subtitle_body: String,
|
||||
/// Language code (e.g. "en")
|
||||
#[serde(default)]
|
||||
pub subtitle_language: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub subtitle_language: Option<String>,
|
||||
/// Language name (e.g. "English")
|
||||
#[serde(default)]
|
||||
pub subtitle_language_description: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub subtitle_language_description: Option<String>,
|
||||
/// Copyright text of the lyrics
|
||||
///
|
||||
/// Ends with a newline.
|
||||
|
@ -210,8 +210,8 @@ pub struct Subtitle {
|
|||
/// Writer(s): David Hodges
|
||||
/// Copyright: Emi Blackwood Music Inc., 12.06 Publishing, Hipgnosis Sfh I Limited, Hifi Music Ip Issuer L.p.
|
||||
/// ```
|
||||
#[serde(default)]
|
||||
pub lyrics_copyright: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub lyrics_copyright: Option<String>,
|
||||
/// Duration of the subtitle in seconds
|
||||
pub subtitle_length: u32,
|
||||
/// Date and time when the subtitle was last updated
|
||||
|
@ -239,7 +239,7 @@ pub struct SubtitleLines {
|
|||
/// List of subtitle lines
|
||||
pub lines: Vec<SubtitleLine>,
|
||||
/// Language code (e.g. "en")
|
||||
pub lang: String,
|
||||
pub lang: Option<String>,
|
||||
/// Duration of the subtitle in seconds
|
||||
pub length: u32,
|
||||
}
|
||||
|
@ -268,10 +268,10 @@ impl SubtitleLines {
|
|||
pub fn to_ttml(&self) -> String {
|
||||
let mut ttml = format!(
|
||||
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:tts="http://www.w3.org/ns/ttml" xml:lang="{}">
|
||||
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:tts="http://www.w3.org/ns/ttml" xml:lang="{0}">
|
||||
<body>
|
||||
<div xml:lang="{}">"#,
|
||||
self.lang, self.lang
|
||||
<div xml:lang="{0}">"#,
|
||||
self.lang.as_deref().unwrap_or_default(),
|
||||
);
|
||||
|
||||
for i in 0..self.lines.len() {
|
||||
|
|
|
@ -20,28 +20,26 @@ pub struct Track {
|
|||
/// Unique Musixmatch Track ID
|
||||
pub track_id: u64,
|
||||
/// Track Musicbrainz ID
|
||||
///
|
||||
/// **Note:** most tracks dont have this entry set
|
||||
#[serde(default)]
|
||||
pub track_mbid: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub track_mbid: Option<String>,
|
||||
/// [International Standard Recording Code](https://en.wikipedia.org/wiki/International_Standard_Recording_Code)
|
||||
#[serde(default)]
|
||||
pub track_isrc: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub track_isrc: Option<String>,
|
||||
/// [ISRCs](https://en.wikipedia.org/wiki/International_Standard_Recording_Code) of equivalent tracks (e.g. on different albums)
|
||||
#[serde(default)]
|
||||
pub commontrack_isrcs: Vec<Vec<String>>,
|
||||
/// Track ID on Spotify
|
||||
#[serde(default)]
|
||||
pub track_spotify_id: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub track_spotify_id: Option<String>,
|
||||
/// Spotify IDs of equivalent tracks (e.g. on different albums)
|
||||
#[serde(default)]
|
||||
pub commontrack_spotify_ids: Vec<String>,
|
||||
/// Track ID on Soundcloud
|
||||
#[serde(default)]
|
||||
pub track_soundcloud_id: u64,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_zero")]
|
||||
pub track_soundcloud_id: Option<u64>,
|
||||
/// Track ID on XBox Music
|
||||
#[serde(default)]
|
||||
pub track_xboxmusic_id: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub track_xboxmusic_id: Option<String>,
|
||||
|
||||
/// Title of the track
|
||||
pub track_name: String,
|
||||
|
@ -78,44 +76,39 @@ pub struct Track {
|
|||
pub num_favourite: u32,
|
||||
|
||||
/// Musixmatch lyrics ID
|
||||
#[serde(default)]
|
||||
pub lyrics_id: u64,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_zero")]
|
||||
pub lyrics_id: Option<u64>,
|
||||
/// Musixmatch subtitle ID
|
||||
#[serde(default)]
|
||||
pub subtitle_id: u64,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_zero")]
|
||||
pub subtitle_id: Option<u64>,
|
||||
|
||||
/// Musixmatch album ID
|
||||
#[serde(default)]
|
||||
pub album_id: u64,
|
||||
/// Album name
|
||||
#[serde(default)]
|
||||
pub album_name: String,
|
||||
|
||||
/// Musixmatch artist ID
|
||||
pub artist_id: u64,
|
||||
/// Musicbrainz artist ID
|
||||
///
|
||||
/// **Note:** most tracks dont have this entry set
|
||||
#[serde(default)]
|
||||
pub artist_mbid: String,
|
||||
/// Artist name
|
||||
#[serde(default)]
|
||||
pub artist_name: String,
|
||||
/// Musicbrainz artist ID
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub artist_mbid: Option<String>,
|
||||
|
||||
/// Album cover URL (100x100px)
|
||||
#[serde(default)]
|
||||
pub album_coverart_100x100: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_coverart_100x100: Option<String>,
|
||||
/// Album cover URL (350x350px)
|
||||
#[serde(default)]
|
||||
pub album_coverart_350x350: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_coverart_350x350: Option<String>,
|
||||
/// Album cover URL (500x500px)
|
||||
#[serde(default)]
|
||||
pub album_coverart_500x500: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_coverart_500x500: Option<String>,
|
||||
/// Album cover URL (800x800px)
|
||||
///
|
||||
/// **Note:** not present on a lot of albums
|
||||
#[serde(default)]
|
||||
pub album_coverart_800x800: String,
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub album_coverart_800x800: Option<String>,
|
||||
|
||||
/// Human-readable Musixmatch ID
|
||||
///
|
||||
|
@ -125,7 +118,7 @@ pub struct Track {
|
|||
pub commontrack_vanity_id: String,
|
||||
|
||||
/// Track release date
|
||||
#[serde(default, with = "crate::api_model::optional_date")]
|
||||
#[serde(default, with = "crate::api_model::optional_datetime")]
|
||||
pub first_release_date: Option<OffsetDateTime>,
|
||||
/// Date and time when the track was last updated
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
|
|
|
@ -19,9 +19,9 @@ pub(crate) struct TranslationBody {
|
|||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub struct Translation {
|
||||
/// 2-character source language code (e.g. "en")
|
||||
#[serde(default)]
|
||||
pub language_from: String,
|
||||
/// source language code (e.g. "en")
|
||||
#[serde(default, deserialize_with = "crate::api_model::null_if_empty")]
|
||||
pub language_from: Option<String>,
|
||||
/// Translated line
|
||||
#[serde(default)]
|
||||
pub description: String,
|
||||
|
@ -102,7 +102,7 @@ impl TranslationMap {
|
|||
time: line.time,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
lang: self.lang.to_owned(),
|
||||
lang: Some(self.lang.to_owned()),
|
||||
length: subtitles.length,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
use musixmatch_inofficial::{storage::FileStorage, Musixmatch};
|
||||
|
||||
#[ctor::ctor]
|
||||
fn init() {
|
||||
let _ = dotenvy::dotenv();
|
||||
env_logger::init();
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(new_mxm().login())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn new_mxm() -> Musixmatch {
|
||||
Musixmatch::new(
|
||||
&std::env::var("MUSIXMATCH_EMAIL").unwrap(),
|
||||
&std::env::var("MUSIXMATCH_PASSWORD").unwrap(),
|
||||
Some(Box::new(FileStorage::default())),
|
||||
)
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
mod _fixtures;
|
||||
use _fixtures::*;
|
||||
|
||||
use musixmatch_inofficial::{
|
||||
models::{AlbumId, ArtistId},
|
||||
Error,
|
||||
};
|
||||
use rstest::rstest;
|
||||
use time::macros::datetime;
|
||||
|
||||
#[rstest]
|
||||
#[case::id(AlbumId::AlbumId(14248253))]
|
||||
#[case::musicbrainz(AlbumId::Musicbrainz("6c3cf9d8-88a8-43ed-850b-55813f01e451"))]
|
||||
#[tokio::test]
|
||||
async fn by_id(#[case] album_id: AlbumId<'_>) {
|
||||
let album = new_mxm().album(album_id).await.unwrap();
|
||||
|
||||
assert_eq!(album.album_id, 14248253);
|
||||
assert_eq!(album.album_mbid, "6c3cf9d8-88a8-43ed-850b-55813f01e451");
|
||||
assert_eq!(album.album_name, "Gangnam Style (강남스타일)");
|
||||
assert!(album.album_rating > 25);
|
||||
assert_eq!(album.album_track_count, 1);
|
||||
assert_eq!(album.album_release_date, "2012-01-01");
|
||||
assert_eq!(album.album_release_type, "Single");
|
||||
assert_eq!(album.artist_id, 410698);
|
||||
assert_eq!(album.artist_name, "PSY");
|
||||
|
||||
let first_pri_genre = &album.primary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_pri_genre.music_genre_id, 14);
|
||||
assert_eq!(first_pri_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_pri_genre.music_genre_name, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_name_extended, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_vanity.as_ref().unwrap(), "Pop");
|
||||
|
||||
let first_sec_genre = &album.secondary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_sec_genre.music_genre_id, 17);
|
||||
assert_eq!(first_sec_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_sec_genre.music_genre_name, "Dance");
|
||||
assert_eq!(first_sec_genre.music_genre_name_extended, "Dance");
|
||||
assert_eq!(
|
||||
first_sec_genre.music_genre_vanity.as_ref().unwrap(),
|
||||
"Dance"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
album.album_copyright,
|
||||
"© 2012 Schoolboy/Universal Republic Records, a division of UMG Recordings, Inc."
|
||||
);
|
||||
assert_eq!(
|
||||
album.album_label,
|
||||
"Silent Records/Universal Republic Records"
|
||||
);
|
||||
assert_eq!(album.album_vanity_id, "410698/Gangnam-Style-Single");
|
||||
assert!(album.updated_time > datetime!(2022-6-3 0:00 UTC));
|
||||
assert_eq!(
|
||||
album.album_coverart_100x100,
|
||||
"https://s.mxmcdn.net/images-storage/albums/5/4/0/4/4/5/26544045.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
album.album_coverart_350x350,
|
||||
"https://s.mxmcdn.net/images-storage/albums/5/4/0/4/4/5/26544045_350_350.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
album.album_coverart_500x500,
|
||||
"https://s.mxmcdn.net/images-storage/albums/5/4/0/4/4/5/26544045_500_500.jpg"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn by_id_missing() {
|
||||
let err = new_mxm()
|
||||
.album(AlbumId::AlbumId(999999999999))
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artist_albums() {
|
||||
let albums = new_mxm()
|
||||
.artist_albums(ArtistId::ArtistId(1039), None, 10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(albums.len(), 10);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artist_albums_missing() {
|
||||
let err = new_mxm()
|
||||
.artist_albums(ArtistId::ArtistId(999999999999), None, 10, 1)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn charts() {
|
||||
let albums = new_mxm().chart_albums("US", 10, 1).await.unwrap();
|
||||
|
||||
assert_eq!(albums.len(), 10);
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
mod _fixtures;
|
||||
use _fixtures::*;
|
||||
|
||||
use musixmatch_inofficial::{models::ArtistId, Error};
|
||||
use rstest::rstest;
|
||||
use time::macros::datetime;
|
||||
|
||||
#[rstest]
|
||||
#[case::id(ArtistId::ArtistId(410698))]
|
||||
#[case::musicbrainz(ArtistId::Musicbrainz("f99b7d67-4e63-4678-aa66-4c6ac0f7d24a"))]
|
||||
#[tokio::test]
|
||||
async fn by_id(#[case] artist_id: ArtistId<'_>) {
|
||||
let artist = new_mxm().artist(artist_id).await.unwrap();
|
||||
|
||||
// dbg!(&artist);
|
||||
|
||||
assert_eq!(artist.artist_id, 410698);
|
||||
assert_eq!(artist.artist_mbid, "f99b7d67-4e63-4678-aa66-4c6ac0f7d24a");
|
||||
assert_eq!(artist.artist_name, "PSY");
|
||||
assert!(
|
||||
artist.artist_name_translation_list.iter().any(|tl| {
|
||||
tl.artist_name_translation.language == "KO"
|
||||
&& tl.artist_name_translation.translation == "싸이"
|
||||
}),
|
||||
"missing Korean translation in: {:?}",
|
||||
artist.artist_name_translation_list
|
||||
);
|
||||
assert_eq!(artist.artist_country, "KR");
|
||||
assert!(artist.artist_rating > 50);
|
||||
let first_genre = &artist.primary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_genre.music_genre_id, 14);
|
||||
assert_eq!(first_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_genre.music_genre_name, "Pop");
|
||||
assert_eq!(first_genre.music_genre_name_extended, "Pop");
|
||||
assert_eq!(first_genre.music_genre_vanity.as_ref().unwrap(), "Pop");
|
||||
assert_eq!(artist.artist_twitter_url, "https://twitter.com/psy_oppa");
|
||||
assert_eq!(
|
||||
artist.artist_facebook_url,
|
||||
"https://www.facebook.com/officialpsy"
|
||||
);
|
||||
assert_eq!(artist.artist_vanity_id, "410698");
|
||||
assert!(artist.updated_time > datetime!(2016-6-30 0:00 UTC));
|
||||
assert_eq!(artist.begin_date_year, "1977");
|
||||
assert_eq!(artist.begin_date, "1977-12-31");
|
||||
assert_eq!(artist.end_date_year, "");
|
||||
assert_eq!(artist.end_date, "0000-00-00");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn by_id_missing() {
|
||||
let err = new_mxm()
|
||||
.artist(ArtistId::ArtistId(999999999999))
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn related() {
|
||||
let artists = new_mxm()
|
||||
.artist_related(ArtistId::ArtistId(26485840), 10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 10);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn related_missing() {
|
||||
let err = new_mxm()
|
||||
.artist_related(ArtistId::ArtistId(999999999999), 10, 1)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search() {
|
||||
let artists = new_mxm().artist_search("psy", 5, 1).await.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 5);
|
||||
|
||||
let artist = &artists[0];
|
||||
assert_eq!(artist.artist_id, 410698);
|
||||
assert_eq!(artist.artist_name, "PSY");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search_empty() {
|
||||
let artists = new_mxm()
|
||||
.artist_search(
|
||||
"Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz",
|
||||
5,
|
||||
1,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn charts() {
|
||||
let artists = new_mxm().chart_artists("US", 10, 1).await.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 10);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn charts_no_country() {
|
||||
let artists = new_mxm().chart_artists("XY", 10, 1).await.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 10);
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
mod _fixtures;
|
||||
use _fixtures::*;
|
||||
|
||||
use std::{fs::File, io::BufWriter, path::Path};
|
||||
|
||||
use futures::stream::{self, StreamExt};
|
||||
use musixmatch_inofficial::{models::TrackId, Error};
|
||||
use rstest::rstest;
|
||||
use time::macros::datetime;
|
||||
|
||||
#[tokio::test]
|
||||
async fn from_match() {
|
||||
let lyrics = new_mxm().matcher_lyrics("Shine", "Spektrem").await.unwrap();
|
||||
|
||||
// dbg!(&lyrics);
|
||||
|
||||
assert_eq!(lyrics.lyrics_id, 25947036);
|
||||
assert!(!lyrics.instrumental);
|
||||
assert!(!lyrics.explicit);
|
||||
assert!(lyrics
|
||||
.lyrics_body
|
||||
.starts_with("Eyes, in the sky, gazing far into the night\n"));
|
||||
assert_eq!(lyrics.lyrics_language, "en");
|
||||
assert_eq!(lyrics.lyrics_language_description, "English");
|
||||
assert!(
|
||||
lyrics.lyrics_copyright.contains("Kim Jeffeson"),
|
||||
"copyright: {}",
|
||||
lyrics.lyrics_copyright,
|
||||
);
|
||||
assert!(lyrics.updated_time > datetime!(2021-6-3 0:00 UTC));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::trackid(TrackId::TrackId(205688271))]
|
||||
#[case::commontrack(TrackId::Commontrack(118480583))]
|
||||
#[case::vanity(TrackId::CommontrackVanity("aespa/Black-Mamba"))]
|
||||
#[case::isrc(TrackId::Isrc("KRA302000590"))]
|
||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51"))]
|
||||
#[tokio::test]
|
||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
||||
let lyrics = new_mxm().track_lyrics(track_id).await.unwrap();
|
||||
|
||||
// dbg!(&lyrics);
|
||||
|
||||
assert_eq!(lyrics.lyrics_id, 30126001);
|
||||
assert_eq!(lyrics.lyrics_language, "ko");
|
||||
assert_eq!(lyrics.lyrics_language_description, "Korean");
|
||||
assert!(
|
||||
lyrics.lyrics_copyright.contains("Kenneth Scott Chesak"),
|
||||
"copyright: {}",
|
||||
lyrics.lyrics_copyright,
|
||||
);
|
||||
assert!(lyrics.updated_time > datetime!(2022-8-27 0:00 UTC));
|
||||
}
|
||||
|
||||
/// This track has no lyrics
|
||||
#[tokio::test]
|
||||
async fn instrumental() {
|
||||
let lyrics = new_mxm()
|
||||
.matcher_lyrics("drivers license", "Bobby G")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(lyrics.lyrics_id, 25891609);
|
||||
assert!(lyrics.instrumental);
|
||||
assert!(!lyrics.explicit);
|
||||
assert_eq!(lyrics.lyrics_body, "");
|
||||
assert_eq!(lyrics.lyrics_language, "");
|
||||
assert_eq!(lyrics.lyrics_language_description, "");
|
||||
assert_eq!(lyrics.lyrics_copyright, "");
|
||||
assert!(lyrics.updated_time > datetime!(2021-6-21 0:00 UTC));
|
||||
}
|
||||
|
||||
/// This track does not exist
|
||||
#[tokio::test]
|
||||
async fn missing() {
|
||||
let err = new_mxm()
|
||||
.track_lyrics(TrackId::Spotify("674JwwTP7xCje81T0DRrLn"))
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn download_testdata() {
|
||||
let json_path = Path::new("testfiles/lyrics.json");
|
||||
if json_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let lyrics = new_mxm()
|
||||
.track_lyrics(TrackId::Commontrack(18576954))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let json_file = File::create(json_path).unwrap();
|
||||
serde_json::to_writer_pretty(BufWriter::new(json_file), &lyrics).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn download_testdata_translation() {
|
||||
let json_path = Path::new("testfiles/translation.json");
|
||||
if json_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let translations = new_mxm()
|
||||
.track_lyrics_translation(TrackId::Commontrack(18576954), "de")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let json_file = File::create(json_path).unwrap();
|
||||
serde_json::to_writer_pretty(BufWriter::new(json_file), &translations).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn concurrency() {
|
||||
let mxm = new_mxm();
|
||||
|
||||
let album = mxm
|
||||
.album_tracks(
|
||||
musixmatch_inofficial::models::AlbumId::AlbumId(17118624),
|
||||
true,
|
||||
20,
|
||||
1,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let x = stream::iter(album)
|
||||
.map(|track| {
|
||||
mxm.track_lyrics(musixmatch_inofficial::models::TrackId::TrackId(
|
||||
track.track_id,
|
||||
))
|
||||
})
|
||||
.buffered(8)
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(Result::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
dbg!(x);
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
mod _fixtures;
|
||||
use _fixtures::*;
|
||||
|
||||
use std::{fs::File, io::BufWriter, path::Path};
|
||||
|
||||
use musixmatch_inofficial::{
|
||||
models::{SubtitleFormat, TrackId},
|
||||
Error,
|
||||
};
|
||||
use rstest::rstest;
|
||||
use time::macros::datetime;
|
||||
|
||||
#[tokio::test]
|
||||
async fn from_match() {
|
||||
let subtitle = new_mxm()
|
||||
.matcher_subtitle(
|
||||
"Shine",
|
||||
"Spektrem",
|
||||
SubtitleFormat::Json,
|
||||
Some(315.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&subtitle);
|
||||
|
||||
assert_eq!(subtitle.subtitle_id, 36913312);
|
||||
assert_eq!(subtitle.subtitle_language, "en");
|
||||
assert_eq!(subtitle.subtitle_language_description, "English");
|
||||
assert!(
|
||||
subtitle.lyrics_copyright.contains("Kim Jeffeson"),
|
||||
"copyright: {}",
|
||||
subtitle.lyrics_copyright,
|
||||
);
|
||||
assert_eq!(subtitle.subtitle_length, 315);
|
||||
assert!(subtitle.updated_time > datetime!(2021-6-30 0:00 UTC));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::trackid(TrackId::TrackId(205688271))]
|
||||
#[case::commontrack(TrackId::Commontrack(118480583))]
|
||||
#[case::vanity(TrackId::CommontrackVanity("aespa/Black-Mamba"))]
|
||||
#[case::isrc(TrackId::Isrc("KRA302000590"))]
|
||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51"))]
|
||||
#[tokio::test]
|
||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
||||
let subtitle = new_mxm()
|
||||
.track_subtitle(track_id, SubtitleFormat::Json, Some(175.0), Some(1.0))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&subtitle);
|
||||
|
||||
assert_eq!(subtitle.subtitle_id, 36476905);
|
||||
assert_eq!(subtitle.subtitle_language, "ko");
|
||||
assert_eq!(subtitle.subtitle_language_description, "Korean");
|
||||
assert!(
|
||||
subtitle.lyrics_copyright.contains("Kenneth Scott Chesak"),
|
||||
"copyright: {}",
|
||||
subtitle.lyrics_copyright,
|
||||
);
|
||||
assert_eq!(subtitle.subtitle_length, 175);
|
||||
assert!(subtitle.updated_time > datetime!(2022-8-27 0:00 UTC));
|
||||
}
|
||||
|
||||
/// This track has no lyrics
|
||||
#[tokio::test]
|
||||
async fn instrumental() {
|
||||
let err = new_mxm()
|
||||
.matcher_subtitle(
|
||||
"drivers license",
|
||||
"Bobby G",
|
||||
SubtitleFormat::Json,
|
||||
Some(246.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
/// This track has not been synced
|
||||
#[tokio::test]
|
||||
async fn unsynced() {
|
||||
let err = new_mxm()
|
||||
.track_subtitle(
|
||||
TrackId::Spotify("6oaWIABGL7eeiMILEDyGX1"),
|
||||
SubtitleFormat::Json,
|
||||
Some(213.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
/// Try to get subtitles with wrong length parameter
|
||||
#[tokio::test]
|
||||
async fn wrong_length() {
|
||||
let err = new_mxm()
|
||||
.track_subtitle(
|
||||
TrackId::Commontrack(118480583),
|
||||
SubtitleFormat::Json,
|
||||
Some(200.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn download_testdata() {
|
||||
let json_path = Path::new("testfiles/subtitles.json");
|
||||
if json_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let subtitle = new_mxm()
|
||||
.track_subtitle(
|
||||
TrackId::Commontrack(18576954),
|
||||
SubtitleFormat::Json,
|
||||
Some(259.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let json_file = File::create(json_path).unwrap();
|
||||
serde_json::to_writer_pretty(BufWriter::new(json_file), &subtitle).unwrap();
|
||||
}
|
980
tests/tests.rs
Normal file
980
tests/tests.rs
Normal file
|
@ -0,0 +1,980 @@
|
|||
use rstest::rstest;
|
||||
use time::macros::{date, datetime};
|
||||
|
||||
use musixmatch_inofficial::{
|
||||
models::{AlbumId, ArtistId, TrackId},
|
||||
storage::FileStorage,
|
||||
Error, Musixmatch,
|
||||
};
|
||||
|
||||
#[ctor::ctor]
|
||||
fn init() {
|
||||
let _ = dotenvy::dotenv();
|
||||
env_logger::init();
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(new_mxm().login())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn new_mxm() -> Musixmatch {
|
||||
Musixmatch::new(
|
||||
&std::env::var("MUSIXMATCH_EMAIL").unwrap(),
|
||||
&std::env::var("MUSIXMATCH_PASSWORD").unwrap(),
|
||||
Some(Box::<FileStorage>::default()),
|
||||
)
|
||||
}
|
||||
|
||||
mod album {
|
||||
use super::*;
|
||||
use musixmatch_inofficial::models::AlbumType;
|
||||
|
||||
#[rstest]
|
||||
#[case::id(AlbumId::AlbumId(14248253))]
|
||||
#[case::musicbrainz(AlbumId::Musicbrainz("6c3cf9d8-88a8-43ed-850b-55813f01e451"))]
|
||||
#[tokio::test]
|
||||
async fn by_id(#[case] album_id: AlbumId<'_>) {
|
||||
let album = new_mxm().album(album_id).await.unwrap();
|
||||
|
||||
assert_eq!(album.album_id, 14248253);
|
||||
assert_eq!(
|
||||
album.album_mbid.unwrap(),
|
||||
"6c3cf9d8-88a8-43ed-850b-55813f01e451"
|
||||
);
|
||||
assert_eq!(album.album_name, "Gangnam Style (강남스타일)");
|
||||
assert!(album.album_rating > 25);
|
||||
assert_eq!(album.album_track_count, 1);
|
||||
assert_eq!(album.album_release_date.unwrap(), date!(2012 - 01 - 01));
|
||||
assert_eq!(album.album_release_type, AlbumType::Single);
|
||||
assert_eq!(album.artist_id, 410698);
|
||||
assert_eq!(album.artist_name, "PSY");
|
||||
|
||||
let first_pri_genre = &album.primary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_pri_genre.music_genre_id, 14);
|
||||
assert_eq!(first_pri_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_pri_genre.music_genre_name, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_name_extended, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_vanity.as_ref().unwrap(), "Pop");
|
||||
|
||||
let first_sec_genre = &album.secondary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_sec_genre.music_genre_id, 17);
|
||||
assert_eq!(first_sec_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_sec_genre.music_genre_name, "Dance");
|
||||
assert_eq!(first_sec_genre.music_genre_name_extended, "Dance");
|
||||
assert_eq!(
|
||||
first_sec_genre.music_genre_vanity.as_ref().unwrap(),
|
||||
"Dance"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
album.album_copyright.unwrap(),
|
||||
"© 2012 Schoolboy/Universal Republic Records, a division of UMG Recordings, Inc."
|
||||
);
|
||||
assert_eq!(
|
||||
album.album_label.unwrap(),
|
||||
"Silent Records/Universal Republic Records"
|
||||
);
|
||||
assert_eq!(album.album_vanity_id, "410698/Gangnam-Style-Single");
|
||||
assert!(album.updated_time > datetime!(2022-6-3 0:00 UTC));
|
||||
assert_eq!(
|
||||
album.album_coverart_100x100.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums/5/4/0/4/4/5/26544045.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
album.album_coverart_350x350.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums/5/4/0/4/4/5/26544045_350_350.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
album.album_coverart_500x500.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums/5/4/0/4/4/5/26544045_500_500.jpg"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn album_tst() {
|
||||
let album = new_mxm().album(AlbumId::AlbumId(23976123)).await.unwrap();
|
||||
println!("type: {:?}", album.album_release_type);
|
||||
println!("date: {:?}", album.album_release_date);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn by_id_missing() {
|
||||
let err = new_mxm()
|
||||
.album(AlbumId::AlbumId(999999999999))
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artist_albums() {
|
||||
let albums = new_mxm()
|
||||
.artist_albums(ArtistId::ArtistId(1039), None, 10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(albums.len(), 10);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn artist_albums_missing() {
|
||||
let err = new_mxm()
|
||||
.artist_albums(ArtistId::ArtistId(999999999999), None, 10, 1)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn charts() {
|
||||
let albums = new_mxm().chart_albums("US", 10, 1).await.unwrap();
|
||||
|
||||
assert_eq!(albums.len(), 10);
|
||||
}
|
||||
}
|
||||
|
||||
mod artist {
|
||||
use super::*;
|
||||
|
||||
#[rstest]
|
||||
#[case::id(ArtistId::ArtistId(410698))]
|
||||
#[case::musicbrainz(ArtistId::Musicbrainz("f99b7d67-4e63-4678-aa66-4c6ac0f7d24a"))]
|
||||
#[tokio::test]
|
||||
async fn by_id(#[case] artist_id: ArtistId<'_>) {
|
||||
let artist = new_mxm().artist(artist_id).await.unwrap();
|
||||
|
||||
// dbg!(&artist);
|
||||
|
||||
assert_eq!(artist.artist_id, 410698);
|
||||
assert_eq!(
|
||||
artist.artist_mbid.unwrap(),
|
||||
"f99b7d67-4e63-4678-aa66-4c6ac0f7d24a"
|
||||
);
|
||||
assert_eq!(artist.artist_name, "PSY");
|
||||
assert!(
|
||||
artist.artist_name_translation_list.iter().any(|tl| {
|
||||
tl.artist_name_translation.language == "KO"
|
||||
&& tl.artist_name_translation.translation == "싸이"
|
||||
}),
|
||||
"missing Korean translation in: {:?}",
|
||||
artist.artist_name_translation_list
|
||||
);
|
||||
assert_eq!(artist.artist_country.unwrap(), "KR");
|
||||
assert!(artist.artist_rating > 50);
|
||||
let first_genre = &artist.primary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_genre.music_genre_id, 14);
|
||||
assert_eq!(first_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_genre.music_genre_name, "Pop");
|
||||
assert_eq!(first_genre.music_genre_name_extended, "Pop");
|
||||
assert_eq!(first_genre.music_genre_vanity.as_ref().unwrap(), "Pop");
|
||||
assert_eq!(
|
||||
artist.artist_twitter_url.unwrap(),
|
||||
"https://twitter.com/psy_oppa"
|
||||
);
|
||||
assert_eq!(
|
||||
artist.artist_facebook_url.unwrap(),
|
||||
"https://www.facebook.com/officialpsy"
|
||||
);
|
||||
assert_eq!(artist.artist_vanity_id, "410698");
|
||||
assert!(artist.updated_time > datetime!(2016-6-30 0:00 UTC));
|
||||
assert_eq!(artist.begin_date_year.unwrap(), 1977);
|
||||
assert_eq!(artist.begin_date.unwrap(), date!(1977 - 12 - 31));
|
||||
assert_eq!(artist.end_date_year, None);
|
||||
assert_eq!(artist.end_date, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn by_id_missing() {
|
||||
let err = new_mxm()
|
||||
.artist(ArtistId::ArtistId(999999999999))
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn related() {
|
||||
let artists = new_mxm()
|
||||
.artist_related(ArtistId::ArtistId(26485840), 10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 10);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn related_missing() {
|
||||
let err = new_mxm()
|
||||
.artist_related(ArtistId::ArtistId(999999999999), 10, 1)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search() {
|
||||
let artists = new_mxm().artist_search("psy", 5, 1).await.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 5);
|
||||
|
||||
let artist = &artists[0];
|
||||
assert_eq!(artist.artist_id, 410698);
|
||||
assert_eq!(artist.artist_name, "PSY");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search_empty() {
|
||||
let artists = new_mxm()
|
||||
.artist_search(
|
||||
"Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz",
|
||||
5,
|
||||
1,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn charts() {
|
||||
let artists = new_mxm().chart_artists("US", 10, 1).await.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 10);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn charts_no_country() {
|
||||
let artists = new_mxm().chart_artists("XY", 10, 1).await.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 10);
|
||||
}
|
||||
}
|
||||
|
||||
mod track {
|
||||
use super::*;
|
||||
use musixmatch_inofficial::models::{ChartName, SortOrder};
|
||||
|
||||
#[rstest]
|
||||
#[case::no_translation(false, false)]
|
||||
#[case::translation_2c(true, false)]
|
||||
#[case::translation_3c(true, true)]
|
||||
#[tokio::test]
|
||||
async fn from_match(#[case] translation_status: bool, #[case] lang_3c: bool) {
|
||||
let track = new_mxm()
|
||||
.matcher_track(
|
||||
"Poker Face",
|
||||
"Lady Gaga",
|
||||
"The Fame",
|
||||
translation_status,
|
||||
lang_3c,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&track);
|
||||
|
||||
assert_eq!(track.track_id, 15476784);
|
||||
assert_eq!(
|
||||
track.track_mbid.unwrap(),
|
||||
"080975b0-39b1-493c-ae64-5cb3292409bb"
|
||||
);
|
||||
assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
||||
assert!(
|
||||
track.commontrack_isrcs[0]
|
||||
.iter()
|
||||
.any(|isrc| isrc == "USUM70824409"),
|
||||
"commontrack_isrcs: {:?}",
|
||||
&track.commontrack_isrcs[0],
|
||||
);
|
||||
assert_eq!(track.track_spotify_id.unwrap(), "5R8dQOPq8haW94K7mgERlO");
|
||||
assert!(
|
||||
track
|
||||
.commontrack_spotify_ids
|
||||
.iter()
|
||||
.any(|spid| spid == "1QV6tiMFM6fSOKOGLMHYYg"),
|
||||
"commontrack_spotify_ids: {:?}",
|
||||
track.commontrack_spotify_ids,
|
||||
);
|
||||
assert_eq!(track.track_name, "Poker Face");
|
||||
assert!(track.track_rating > 50);
|
||||
assert_eq!(track.commontrack_id, 47672612);
|
||||
assert!(!track.instrumental);
|
||||
assert!(track.explicit);
|
||||
assert!(track.has_subtitles);
|
||||
assert!(track.has_richsync);
|
||||
assert!(track.has_track_structure);
|
||||
assert!(track.num_favourite > 50);
|
||||
assert_eq!(track.lyrics_id.unwrap(), 30678771);
|
||||
assert_eq!(track.subtitle_id.unwrap(), 36450705);
|
||||
assert_eq!(track.album_id, 13810402);
|
||||
assert_eq!(track.album_name, "The Fame");
|
||||
assert_eq!(track.artist_id, 378462);
|
||||
assert_eq!(
|
||||
track.artist_mbid.unwrap(),
|
||||
"650e7db6-b795-4eb5-a702-5ea2fc46c848"
|
||||
);
|
||||
assert_eq!(track.artist_name, "Lady Gaga");
|
||||
assert_eq!(
|
||||
track.album_coverart_100x100.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_350x350.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636_350_350.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_500x500.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636_500_500.jpg"
|
||||
);
|
||||
assert_eq!(track.commontrack_vanity_id, "Lady-Gaga/poker-face-1");
|
||||
let first_release = track.first_release_date.unwrap();
|
||||
assert_eq!(first_release.date(), date!(2008 - 1 - 1));
|
||||
assert!(track.updated_time > datetime!(2023-1-17 0:00 UTC));
|
||||
|
||||
let first_pri_genre = &track.primary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_pri_genre.music_genre_id, 14);
|
||||
assert_eq!(first_pri_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_pri_genre.music_genre_name, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_name_extended, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_vanity.as_ref().unwrap(), "Pop");
|
||||
|
||||
let first_sec_genre = &track.secondary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_sec_genre.music_genre_id, 17);
|
||||
assert_eq!(first_sec_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_sec_genre.music_genre_name, "Dance");
|
||||
assert_eq!(first_sec_genre.music_genre_name_extended, "Dance");
|
||||
assert_eq!(
|
||||
first_sec_genre.music_genre_vanity.as_ref().unwrap(),
|
||||
"Dance"
|
||||
);
|
||||
|
||||
if translation_status {
|
||||
assert!(
|
||||
track.track_lyrics_translation_status.iter().all(|tl| {
|
||||
(if lang_3c {
|
||||
tl.from == "eng"
|
||||
} else {
|
||||
tl.from == "en"
|
||||
}) && tl.perc >= 0.0
|
||||
&& tl.perc <= 1.0
|
||||
}),
|
||||
"translation: {:?}",
|
||||
track.track_lyrics_translation_status
|
||||
);
|
||||
} else {
|
||||
assert!(track.track_lyrics_translation_status.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::trackid(TrackId::TrackId(205688271))]
|
||||
#[case::commontrack(TrackId::Commontrack(118480583))]
|
||||
#[case::vanity(TrackId::CommontrackVanity("aespa/Black-Mamba"))]
|
||||
#[case::isrc(TrackId::Isrc("KRA302000590"))]
|
||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51"))]
|
||||
#[tokio::test]
|
||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
||||
let track = new_mxm().track(track_id, true, false).await.unwrap();
|
||||
|
||||
// dbg!(&track);
|
||||
|
||||
assert_eq!(track.track_id, 205688271);
|
||||
assert_eq!(track.track_isrc.unwrap(), "KRA302000590");
|
||||
assert_eq!(track.track_spotify_id.unwrap(), "1t2qYCAjUAoGfeFeoBlK51");
|
||||
assert_eq!(track.track_name, "Black Mamba");
|
||||
assert!(track.track_rating > 50);
|
||||
assert_eq!(track.track_length, 175);
|
||||
assert!(!track.explicit);
|
||||
assert!(track.has_lyrics);
|
||||
assert!(track.has_subtitles);
|
||||
assert!(track.has_richsync);
|
||||
assert!(track.num_favourite > 200);
|
||||
assert_eq!(track.lyrics_id.unwrap(), 30126001);
|
||||
assert_eq!(track.subtitle_id.unwrap(), 36476905);
|
||||
assert_eq!(track.album_id, 41035954);
|
||||
assert_eq!(track.album_name, "Black Mamba - Single");
|
||||
assert_eq!(track.artist_id, 46970441);
|
||||
assert_eq!(track.artist_name, "aespa");
|
||||
assert_eq!(
|
||||
track.album_coverart_100x100.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums5/2/7/7/6/5/1/52156772.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_350x350.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums5/2/7/7/6/5/1/52156772_350_350.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_500x500.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums5/2/7/7/6/5/1/52156772_500_500.jpg"
|
||||
);
|
||||
assert_eq!(track.commontrack_vanity_id, "aespa/Black-Mamba");
|
||||
|
||||
let release_date = track.first_release_date.unwrap();
|
||||
assert_eq!(release_date.date(), date!(2020 - 11 - 17));
|
||||
assert!(track.updated_time > datetime!(2022-8-27 0:00 UTC));
|
||||
|
||||
let first_tstatus = &track.track_lyrics_translation_status[0];
|
||||
assert_eq!(first_tstatus.from, "ko");
|
||||
assert!(first_tstatus.perc >= 0.0 && first_tstatus.perc <= 1.0);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::no_translation(false, false)]
|
||||
#[case::translation_2c(true, false)]
|
||||
#[case::translation_3c(true, true)]
|
||||
#[tokio::test]
|
||||
async fn from_id_translations(#[case] translation_status: bool, #[case] lang_3c: bool) {
|
||||
let track = new_mxm()
|
||||
.track(TrackId::TrackId(15476784), translation_status, lang_3c)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&track);
|
||||
|
||||
assert_eq!(track.track_id, 15476784);
|
||||
assert_eq!(
|
||||
track.track_mbid.unwrap(),
|
||||
"080975b0-39b1-493c-ae64-5cb3292409bb"
|
||||
);
|
||||
assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
||||
assert!(
|
||||
track.commontrack_isrcs[0]
|
||||
.iter()
|
||||
.any(|isrc| isrc == "USUM70824409"),
|
||||
"commontrack_isrcs: {:?}",
|
||||
&track.commontrack_isrcs[0],
|
||||
);
|
||||
assert_eq!(track.track_spotify_id.unwrap(), "5R8dQOPq8haW94K7mgERlO");
|
||||
assert!(
|
||||
track
|
||||
.commontrack_spotify_ids
|
||||
.iter()
|
||||
.any(|spid| spid == "1QV6tiMFM6fSOKOGLMHYYg"),
|
||||
"commontrack_spotify_ids: {:?}",
|
||||
track.commontrack_spotify_ids,
|
||||
);
|
||||
assert_eq!(track.track_name, "Poker Face");
|
||||
assert!(track.track_rating > 50);
|
||||
assert_eq!(track.commontrack_id, 47672612);
|
||||
assert!(!track.instrumental);
|
||||
assert!(track.explicit);
|
||||
assert!(track.has_subtitles);
|
||||
assert!(track.has_richsync);
|
||||
assert!(track.has_track_structure);
|
||||
assert!(track.num_favourite > 50);
|
||||
assert_eq!(track.lyrics_id.unwrap(), 30678771);
|
||||
assert_eq!(track.subtitle_id.unwrap(), 36450705);
|
||||
assert_eq!(track.album_id, 13810402);
|
||||
assert_eq!(track.album_name, "The Fame");
|
||||
assert_eq!(track.artist_id, 378462);
|
||||
assert_eq!(
|
||||
track.artist_mbid.unwrap(),
|
||||
"650e7db6-b795-4eb5-a702-5ea2fc46c848"
|
||||
);
|
||||
assert_eq!(track.artist_name, "Lady Gaga");
|
||||
assert_eq!(
|
||||
track.album_coverart_100x100.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_350x350.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636_350_350.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_500x500.unwrap(),
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636_500_500.jpg"
|
||||
);
|
||||
assert_eq!(track.commontrack_vanity_id, "Lady-Gaga/poker-face-1");
|
||||
let first_release = track.first_release_date.unwrap();
|
||||
assert_eq!(first_release.date(), date!(2008 - 1 - 1));
|
||||
assert!(track.updated_time > datetime!(2023-1-17 0:00 UTC));
|
||||
|
||||
let first_pri_genre = &track.primary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_pri_genre.music_genre_id, 14);
|
||||
assert_eq!(first_pri_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_pri_genre.music_genre_name, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_name_extended, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_vanity.as_ref().unwrap(), "Pop");
|
||||
|
||||
let first_sec_genre = &track.secondary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_sec_genre.music_genre_id, 17);
|
||||
assert_eq!(first_sec_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_sec_genre.music_genre_name, "Dance");
|
||||
assert_eq!(first_sec_genre.music_genre_name_extended, "Dance");
|
||||
assert_eq!(
|
||||
first_sec_genre.music_genre_vanity.as_ref().unwrap(),
|
||||
"Dance"
|
||||
);
|
||||
|
||||
if translation_status {
|
||||
assert!(
|
||||
track.track_lyrics_translation_status.iter().all(|tl| {
|
||||
(if lang_3c {
|
||||
tl.from == "eng"
|
||||
} else {
|
||||
tl.from == "en"
|
||||
}) && tl.perc >= 0.0
|
||||
&& tl.perc <= 1.0
|
||||
}),
|
||||
"translation: {:?}",
|
||||
track.track_lyrics_translation_status
|
||||
);
|
||||
} else {
|
||||
assert!(track.track_lyrics_translation_status.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn from_id_missing() {
|
||||
let err = new_mxm()
|
||||
.track(TrackId::TrackId(999999999999), false, false)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn album_tracks() {
|
||||
let tracks = new_mxm()
|
||||
.album_tracks(AlbumId::AlbumId(17118624), true, 20, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&tracks);
|
||||
|
||||
let track_names = tracks
|
||||
.iter()
|
||||
.map(|t| t.track_name.to_owned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
track_names,
|
||||
vec![
|
||||
"Gäa",
|
||||
"Vergiss mein nicht",
|
||||
"Orome",
|
||||
"Falke flieg",
|
||||
"Minne",
|
||||
"Das Lied der Ahnen",
|
||||
"Hörst du den Wind",
|
||||
"Nan Úye",
|
||||
"Faolan",
|
||||
"Hymne der Nacht",
|
||||
"Avalon",
|
||||
"Tolo Nan",
|
||||
"Oonagh",
|
||||
]
|
||||
);
|
||||
|
||||
tracks.iter().for_each(|t| {
|
||||
assert!(t.has_lyrics);
|
||||
assert!(t.has_subtitles);
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn album_missing() {
|
||||
let err = new_mxm()
|
||||
.album_tracks(AlbumId::AlbumId(999999999999), false, 20, 1)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::top(ChartName::Top)]
|
||||
#[case::hot(ChartName::Hot)]
|
||||
#[tokio::test]
|
||||
async fn charts(#[case] chart_name: ChartName) {
|
||||
let tracks = new_mxm()
|
||||
.chart_tracks("US", chart_name, true, 20, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(tracks.len(), 20);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search() {
|
||||
let tracks = new_mxm()
|
||||
.track_search()
|
||||
.q_artist("Lena")
|
||||
.q_track("Satellite")
|
||||
.s_track_rating(SortOrder::Desc)
|
||||
.send(1, 0)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&tracks);
|
||||
|
||||
assert_eq!(tracks.len(), 1);
|
||||
|
||||
let track = &tracks[0];
|
||||
assert_eq!(track.commontrack_id, 12426476);
|
||||
assert_eq!(track.track_name, "Satellite");
|
||||
assert_eq!(track.artist_name, "Lena");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search_lyrics() {
|
||||
let tracks = new_mxm()
|
||||
.track_search()
|
||||
.q_lyrics("the whole world stops and stares for a while")
|
||||
.s_track_rating(SortOrder::Desc)
|
||||
.send(10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(tracks.len(), 10);
|
||||
|
||||
let track = &tracks[0];
|
||||
assert_eq!(track.track_name, "Just the Way You Are");
|
||||
assert_eq!(track.artist_name, "Bruno Mars");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search_empty() {
|
||||
let artists = new_mxm()
|
||||
.track_search()
|
||||
.q("Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz")
|
||||
.send(10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn genres() {
|
||||
let genres = new_mxm().genres().await.unwrap();
|
||||
assert!(genres.len() > 360);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn snippet() {
|
||||
let snippet = new_mxm()
|
||||
.track_snippet(TrackId::Commontrack(8874280))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(snippet.snippet_id, 23036767);
|
||||
assert_eq!(snippet.snippet_language.unwrap(), "en");
|
||||
assert!(!snippet.instrumental);
|
||||
assert!(snippet.updated_time > datetime!(2022-8-29 0:00 UTC));
|
||||
assert_eq!(
|
||||
snippet.snippet_body,
|
||||
"There's not a thing that I would change"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod lyrics {
|
||||
use futures::stream::{self, StreamExt};
|
||||
use std::{fs::File, io::BufWriter, path::Path};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn from_match() {
|
||||
let lyrics = new_mxm().matcher_lyrics("Shine", "Spektrem").await.unwrap();
|
||||
|
||||
// dbg!(&lyrics);
|
||||
|
||||
assert_eq!(lyrics.lyrics_id, 25947036);
|
||||
assert!(!lyrics.instrumental);
|
||||
assert!(!lyrics.explicit);
|
||||
assert!(lyrics
|
||||
.lyrics_body
|
||||
.starts_with("Eyes, in the sky, gazing far into the night\n"));
|
||||
assert_eq!(lyrics.lyrics_language.unwrap(), "en");
|
||||
assert_eq!(lyrics.lyrics_language_description.unwrap(), "English");
|
||||
let copyright = lyrics.lyrics_copyright.unwrap();
|
||||
assert!(copyright.contains("Kim Jeffeson"), "copyright: {copyright}",);
|
||||
assert!(lyrics.updated_time > datetime!(2021-6-3 0:00 UTC));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::trackid(TrackId::TrackId(205688271))]
|
||||
#[case::commontrack(TrackId::Commontrack(118480583))]
|
||||
#[case::vanity(TrackId::CommontrackVanity("aespa/Black-Mamba"))]
|
||||
#[case::isrc(TrackId::Isrc("KRA302000590"))]
|
||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51"))]
|
||||
#[tokio::test]
|
||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
||||
let lyrics = new_mxm().track_lyrics(track_id).await.unwrap();
|
||||
|
||||
// dbg!(&lyrics);
|
||||
|
||||
assert_eq!(lyrics.lyrics_id, 30126001);
|
||||
assert_eq!(lyrics.lyrics_language.unwrap(), "ko");
|
||||
assert_eq!(lyrics.lyrics_language_description.unwrap(), "Korean");
|
||||
let copyright = lyrics.lyrics_copyright.unwrap();
|
||||
assert!(
|
||||
copyright.contains("Kenneth Scott Chesak"),
|
||||
"copyright: {copyright}",
|
||||
);
|
||||
assert!(lyrics.updated_time > datetime!(2022-8-27 0:00 UTC));
|
||||
}
|
||||
|
||||
/// This track has no lyrics
|
||||
#[tokio::test]
|
||||
async fn instrumental() {
|
||||
let lyrics = new_mxm()
|
||||
.matcher_lyrics("drivers license", "Bobby G")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(lyrics.lyrics_id, 25891609);
|
||||
assert!(lyrics.instrumental);
|
||||
assert!(!lyrics.explicit);
|
||||
assert_eq!(lyrics.lyrics_body, "");
|
||||
assert_eq!(lyrics.lyrics_language, None);
|
||||
assert_eq!(lyrics.lyrics_language_description, None);
|
||||
assert_eq!(lyrics.lyrics_copyright, None);
|
||||
assert!(lyrics.updated_time > datetime!(2021-6-21 0:00 UTC));
|
||||
}
|
||||
|
||||
/// This track does not exist
|
||||
#[tokio::test]
|
||||
async fn missing() {
|
||||
let err = new_mxm()
|
||||
.track_lyrics(TrackId::Spotify("674JwwTP7xCje81T0DRrLn"))
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn download_testdata() {
|
||||
let json_path = Path::new("testfiles/lyrics.json");
|
||||
if json_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let lyrics = new_mxm()
|
||||
.track_lyrics(TrackId::Commontrack(18576954))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let json_file = File::create(json_path).unwrap();
|
||||
serde_json::to_writer_pretty(BufWriter::new(json_file), &lyrics).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn download_testdata_translation() {
|
||||
let json_path = Path::new("testfiles/translation.json");
|
||||
if json_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let translations = new_mxm()
|
||||
.track_lyrics_translation(TrackId::Commontrack(18576954), "de")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let json_file = File::create(json_path).unwrap();
|
||||
serde_json::to_writer_pretty(BufWriter::new(json_file), &translations).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn concurrency() {
|
||||
let mxm = new_mxm();
|
||||
|
||||
let album = mxm
|
||||
.album_tracks(
|
||||
musixmatch_inofficial::models::AlbumId::AlbumId(17118624),
|
||||
true,
|
||||
20,
|
||||
1,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let x = stream::iter(album)
|
||||
.map(|track| {
|
||||
mxm.track_lyrics(musixmatch_inofficial::models::TrackId::TrackId(
|
||||
track.track_id,
|
||||
))
|
||||
})
|
||||
.buffered(8)
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(Result::unwrap)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
dbg!(x);
|
||||
}
|
||||
}
|
||||
|
||||
mod subtitles {
|
||||
use std::{fs::File, io::BufWriter, path::Path};
|
||||
|
||||
use super::*;
|
||||
use musixmatch_inofficial::models::SubtitleFormat;
|
||||
|
||||
#[tokio::test]
|
||||
async fn from_match() {
|
||||
let subtitle = new_mxm()
|
||||
.matcher_subtitle(
|
||||
"Shine",
|
||||
"Spektrem",
|
||||
SubtitleFormat::Json,
|
||||
Some(315.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&subtitle);
|
||||
|
||||
assert_eq!(subtitle.subtitle_id, 36913312);
|
||||
assert_eq!(subtitle.subtitle_language.unwrap(), "en");
|
||||
assert_eq!(subtitle.subtitle_language_description.unwrap(), "English");
|
||||
let copyright = subtitle.lyrics_copyright.unwrap();
|
||||
assert!(copyright.contains("Kim Jeffeson"), "copyright: {copyright}",);
|
||||
assert_eq!(subtitle.subtitle_length, 315);
|
||||
assert!(subtitle.updated_time > datetime!(2021-6-30 0:00 UTC));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::trackid(TrackId::TrackId(205688271))]
|
||||
#[case::commontrack(TrackId::Commontrack(118480583))]
|
||||
#[case::vanity(TrackId::CommontrackVanity("aespa/Black-Mamba"))]
|
||||
#[case::isrc(TrackId::Isrc("KRA302000590"))]
|
||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51"))]
|
||||
#[tokio::test]
|
||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
||||
let subtitle = new_mxm()
|
||||
.track_subtitle(track_id, SubtitleFormat::Json, Some(175.0), Some(1.0))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&subtitle);
|
||||
|
||||
assert_eq!(subtitle.subtitle_id, 36476905);
|
||||
assert_eq!(subtitle.subtitle_language.unwrap(), "ko");
|
||||
assert_eq!(subtitle.subtitle_language_description.unwrap(), "Korean");
|
||||
let copyright = subtitle.lyrics_copyright.unwrap();
|
||||
assert!(
|
||||
copyright.contains("Kenneth Scott Chesak"),
|
||||
"copyright: {copyright}",
|
||||
);
|
||||
assert_eq!(subtitle.subtitle_length, 175);
|
||||
assert!(subtitle.updated_time > datetime!(2022-8-27 0:00 UTC));
|
||||
}
|
||||
|
||||
/// This track has no lyrics
|
||||
#[tokio::test]
|
||||
async fn instrumental() {
|
||||
let err = new_mxm()
|
||||
.matcher_subtitle(
|
||||
"drivers license",
|
||||
"Bobby G",
|
||||
SubtitleFormat::Json,
|
||||
Some(246.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
/// This track has not been synced
|
||||
#[tokio::test]
|
||||
async fn unsynced() {
|
||||
let err = new_mxm()
|
||||
.track_subtitle(
|
||||
TrackId::Spotify("6oaWIABGL7eeiMILEDyGX1"),
|
||||
SubtitleFormat::Json,
|
||||
Some(213.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
/// Try to get subtitles with wrong length parameter
|
||||
#[tokio::test]
|
||||
async fn wrong_length() {
|
||||
let err = new_mxm()
|
||||
.track_subtitle(
|
||||
TrackId::Commontrack(118480583),
|
||||
SubtitleFormat::Json,
|
||||
Some(200.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn download_testdata() {
|
||||
let json_path = Path::new("testfiles/subtitles.json");
|
||||
if json_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let subtitle = new_mxm()
|
||||
.track_subtitle(
|
||||
TrackId::Commontrack(18576954),
|
||||
SubtitleFormat::Json,
|
||||
Some(259.0),
|
||||
Some(1.0),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let json_file = File::create(json_path).unwrap();
|
||||
serde_json::to_writer_pretty(BufWriter::new(json_file), &subtitle).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
mod translation {
|
||||
use std::{fs::File, io::BufReader, path::Path};
|
||||
|
||||
use musixmatch_inofficial::models::{Lyrics, Subtitle, TranslationList, TranslationMap};
|
||||
|
||||
#[test]
|
||||
fn translation_test() {
|
||||
let lyrics_path = Path::new("testfiles/lyrics.json");
|
||||
let subtitles_path = Path::new("testfiles/subtitles.json");
|
||||
let translation_path = Path::new("testfiles/translation.json");
|
||||
|
||||
let lyrics: Lyrics =
|
||||
serde_json::from_reader(BufReader::new(File::open(lyrics_path).unwrap())).unwrap();
|
||||
let subtitle: Subtitle =
|
||||
serde_json::from_reader(BufReader::new(File::open(subtitles_path).unwrap())).unwrap();
|
||||
let translations: TranslationList =
|
||||
serde_json::from_reader(BufReader::new(File::open(translation_path).unwrap())).unwrap();
|
||||
|
||||
let t_map = TranslationMap::from(translations);
|
||||
|
||||
let lyrics_trans = t_map.translate_lyrics(&lyrics.lyrics_body);
|
||||
let expected_lyrics = std::fs::read_to_string("testfiles/translated_lyrics.txt").unwrap();
|
||||
assert_eq!(lyrics_trans.trim(), expected_lyrics.trim());
|
||||
|
||||
let subtitles_trans = t_map.translate_subtitles(&subtitle.to_lines().unwrap());
|
||||
|
||||
let expected_lrc = std::fs::read_to_string("testfiles/translated_subtitles.lrc").unwrap();
|
||||
let expected_ttml = std::fs::read_to_string("testfiles/translated_subtitles.xml").unwrap();
|
||||
|
||||
assert_eq!(subtitles_trans.to_lrc().trim(), expected_lrc.trim());
|
||||
assert_eq!(subtitles_trans.to_ttml().trim(), expected_ttml.trim());
|
||||
}
|
||||
}
|
|
@ -1,412 +0,0 @@
|
|||
mod _fixtures;
|
||||
use _fixtures::*;
|
||||
|
||||
use musixmatch_inofficial::{
|
||||
models::{AlbumId, ChartName, SortOrder, TrackId},
|
||||
Error,
|
||||
};
|
||||
use rstest::rstest;
|
||||
use time::macros::{date, datetime};
|
||||
|
||||
#[rstest]
|
||||
#[case::no_translation(false, false)]
|
||||
#[case::translation_2c(true, false)]
|
||||
#[case::translation_3c(true, true)]
|
||||
#[tokio::test]
|
||||
async fn from_match(#[case] translation_status: bool, #[case] lang_3c: bool) {
|
||||
let track = new_mxm()
|
||||
.matcher_track(
|
||||
"Poker Face",
|
||||
"Lady Gaga",
|
||||
"The Fame",
|
||||
translation_status,
|
||||
lang_3c,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&track);
|
||||
|
||||
assert_eq!(track.track_id, 15476784);
|
||||
assert_eq!(track.track_mbid, "080975b0-39b1-493c-ae64-5cb3292409bb");
|
||||
assert_eq!(track.track_isrc, "USUM70824409");
|
||||
assert!(
|
||||
track.commontrack_isrcs[0]
|
||||
.iter()
|
||||
.any(|isrc| isrc == "USUM70824409"),
|
||||
"commontrack_isrcs: {:?}",
|
||||
&track.commontrack_isrcs[0],
|
||||
);
|
||||
assert_eq!(track.track_spotify_id, "5R8dQOPq8haW94K7mgERlO");
|
||||
assert!(
|
||||
track
|
||||
.commontrack_spotify_ids
|
||||
.iter()
|
||||
.any(|spid| spid == "1QV6tiMFM6fSOKOGLMHYYg"),
|
||||
"commontrack_spotify_ids: {:?}",
|
||||
track.commontrack_spotify_ids,
|
||||
);
|
||||
assert_eq!(track.track_name, "Poker Face");
|
||||
assert!(track.track_rating > 50);
|
||||
assert_eq!(track.commontrack_id, 47672612);
|
||||
assert!(!track.instrumental);
|
||||
assert!(track.explicit);
|
||||
assert!(track.has_subtitles);
|
||||
assert!(track.has_richsync);
|
||||
assert!(track.has_track_structure);
|
||||
assert!(track.num_favourite > 50);
|
||||
assert_eq!(track.lyrics_id, 30678771);
|
||||
assert_eq!(track.subtitle_id, 36450705);
|
||||
assert_eq!(track.album_id, 13810402);
|
||||
assert_eq!(track.album_name, "The Fame");
|
||||
assert_eq!(track.artist_id, 378462);
|
||||
assert_eq!(track.artist_mbid, "650e7db6-b795-4eb5-a702-5ea2fc46c848");
|
||||
assert_eq!(track.artist_name, "Lady Gaga");
|
||||
assert_eq!(
|
||||
track.album_coverart_100x100,
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_350x350,
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636_350_350.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_500x500,
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636_500_500.jpg"
|
||||
);
|
||||
assert_eq!(track.commontrack_vanity_id, "Lady-Gaga/poker-face-1");
|
||||
let first_release = track.first_release_date.unwrap();
|
||||
assert_eq!(first_release.date(), date!(2008 - 1 - 1));
|
||||
assert!(track.updated_time > datetime!(2023-1-17 0:00 UTC));
|
||||
|
||||
let first_pri_genre = &track.primary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_pri_genre.music_genre_id, 14);
|
||||
assert_eq!(first_pri_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_pri_genre.music_genre_name, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_name_extended, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_vanity.as_ref().unwrap(), "Pop");
|
||||
|
||||
let first_sec_genre = &track.secondary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_sec_genre.music_genre_id, 17);
|
||||
assert_eq!(first_sec_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_sec_genre.music_genre_name, "Dance");
|
||||
assert_eq!(first_sec_genre.music_genre_name_extended, "Dance");
|
||||
assert_eq!(
|
||||
first_sec_genre.music_genre_vanity.as_ref().unwrap(),
|
||||
"Dance"
|
||||
);
|
||||
|
||||
if translation_status {
|
||||
assert!(
|
||||
track.track_lyrics_translation_status.iter().all(|tl| {
|
||||
(if lang_3c {
|
||||
tl.from == "eng"
|
||||
} else {
|
||||
tl.from == "en"
|
||||
}) && tl.perc >= 0.0
|
||||
&& tl.perc <= 1.0
|
||||
}),
|
||||
"translation: {:?}",
|
||||
track.track_lyrics_translation_status
|
||||
);
|
||||
} else {
|
||||
assert!(track.track_lyrics_translation_status.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::trackid(TrackId::TrackId(205688271))]
|
||||
#[case::commontrack(TrackId::Commontrack(118480583))]
|
||||
#[case::vanity(TrackId::CommontrackVanity("aespa/Black-Mamba"))]
|
||||
#[case::isrc(TrackId::Isrc("KRA302000590"))]
|
||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51"))]
|
||||
#[tokio::test]
|
||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
||||
let track = new_mxm().track(track_id, true, false).await.unwrap();
|
||||
|
||||
// dbg!(&track);
|
||||
|
||||
assert_eq!(track.track_id, 205688271);
|
||||
assert_eq!(track.track_isrc, "KRA302000590");
|
||||
assert_eq!(track.track_spotify_id, "1t2qYCAjUAoGfeFeoBlK51");
|
||||
assert_eq!(track.track_name, "Black Mamba");
|
||||
assert!(track.track_rating > 50);
|
||||
assert_eq!(track.track_length, 175);
|
||||
assert!(!track.explicit);
|
||||
assert!(track.has_lyrics);
|
||||
assert!(track.has_subtitles);
|
||||
assert!(track.has_richsync);
|
||||
assert!(track.num_favourite > 200);
|
||||
assert_eq!(track.lyrics_id, 30126001);
|
||||
assert_eq!(track.subtitle_id, 36476905);
|
||||
assert_eq!(track.album_id, 41035954);
|
||||
assert_eq!(track.album_name, "Black Mamba - Single");
|
||||
assert_eq!(track.artist_id, 46970441);
|
||||
assert_eq!(track.artist_name, "aespa");
|
||||
assert_eq!(
|
||||
track.album_coverart_100x100,
|
||||
"https://s.mxmcdn.net/images-storage/albums5/2/7/7/6/5/1/52156772.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_350x350,
|
||||
"https://s.mxmcdn.net/images-storage/albums5/2/7/7/6/5/1/52156772_350_350.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_500x500,
|
||||
"https://s.mxmcdn.net/images-storage/albums5/2/7/7/6/5/1/52156772_500_500.jpg"
|
||||
);
|
||||
assert_eq!(track.commontrack_vanity_id, "aespa/Black-Mamba");
|
||||
|
||||
let release_date = track.first_release_date.unwrap();
|
||||
assert_eq!(release_date.date(), date!(2020 - 11 - 17));
|
||||
assert!(track.updated_time > datetime!(2022-8-27 0:00 UTC));
|
||||
|
||||
let first_tstatus = &track.track_lyrics_translation_status[0];
|
||||
assert_eq!(first_tstatus.from, "ko");
|
||||
assert!(first_tstatus.perc >= 0.0 && first_tstatus.perc <= 1.0);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::no_translation(false, false)]
|
||||
#[case::translation_2c(true, false)]
|
||||
#[case::translation_3c(true, true)]
|
||||
#[tokio::test]
|
||||
async fn from_id_translations(#[case] translation_status: bool, #[case] lang_3c: bool) {
|
||||
let track = new_mxm()
|
||||
.track(TrackId::TrackId(15476784), translation_status, lang_3c)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&track);
|
||||
|
||||
assert_eq!(track.track_id, 15476784);
|
||||
assert_eq!(track.track_mbid, "080975b0-39b1-493c-ae64-5cb3292409bb");
|
||||
assert_eq!(track.track_isrc, "USUM70824409");
|
||||
assert!(
|
||||
track.commontrack_isrcs[0]
|
||||
.iter()
|
||||
.any(|isrc| isrc == "USUM70824409"),
|
||||
"commontrack_isrcs: {:?}",
|
||||
&track.commontrack_isrcs[0],
|
||||
);
|
||||
assert_eq!(track.track_spotify_id, "5R8dQOPq8haW94K7mgERlO");
|
||||
assert!(
|
||||
track
|
||||
.commontrack_spotify_ids
|
||||
.iter()
|
||||
.any(|spid| spid == "1QV6tiMFM6fSOKOGLMHYYg"),
|
||||
"commontrack_spotify_ids: {:?}",
|
||||
track.commontrack_spotify_ids,
|
||||
);
|
||||
assert_eq!(track.track_name, "Poker Face");
|
||||
assert!(track.track_rating > 50);
|
||||
assert_eq!(track.commontrack_id, 47672612);
|
||||
assert!(!track.instrumental);
|
||||
assert!(track.explicit);
|
||||
assert!(track.has_subtitles);
|
||||
assert!(track.has_richsync);
|
||||
assert!(track.has_track_structure);
|
||||
assert!(track.num_favourite > 50);
|
||||
assert_eq!(track.lyrics_id, 30678771);
|
||||
assert_eq!(track.subtitle_id, 36450705);
|
||||
assert_eq!(track.album_id, 13810402);
|
||||
assert_eq!(track.album_name, "The Fame");
|
||||
assert_eq!(track.artist_id, 378462);
|
||||
assert_eq!(track.artist_mbid, "650e7db6-b795-4eb5-a702-5ea2fc46c848");
|
||||
assert_eq!(track.artist_name, "Lady Gaga");
|
||||
assert_eq!(
|
||||
track.album_coverart_100x100,
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_350x350,
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636_350_350.jpg"
|
||||
);
|
||||
assert_eq!(
|
||||
track.album_coverart_500x500,
|
||||
"https://s.mxmcdn.net/images-storage/albums/6/3/6/9/1/3/26319636_500_500.jpg"
|
||||
);
|
||||
assert_eq!(track.commontrack_vanity_id, "Lady-Gaga/poker-face-1");
|
||||
let first_release = track.first_release_date.unwrap();
|
||||
assert_eq!(first_release.date(), date!(2008 - 1 - 1));
|
||||
assert!(track.updated_time > datetime!(2023-1-17 0:00 UTC));
|
||||
|
||||
let first_pri_genre = &track.primary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_pri_genre.music_genre_id, 14);
|
||||
assert_eq!(first_pri_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_pri_genre.music_genre_name, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_name_extended, "Pop");
|
||||
assert_eq!(first_pri_genre.music_genre_vanity.as_ref().unwrap(), "Pop");
|
||||
|
||||
let first_sec_genre = &track.secondary_genres.music_genre_list[0].music_genre;
|
||||
assert_eq!(first_sec_genre.music_genre_id, 17);
|
||||
assert_eq!(first_sec_genre.music_genre_parent_id, 34);
|
||||
assert_eq!(first_sec_genre.music_genre_name, "Dance");
|
||||
assert_eq!(first_sec_genre.music_genre_name_extended, "Dance");
|
||||
assert_eq!(
|
||||
first_sec_genre.music_genre_vanity.as_ref().unwrap(),
|
||||
"Dance"
|
||||
);
|
||||
|
||||
if translation_status {
|
||||
assert!(
|
||||
track.track_lyrics_translation_status.iter().all(|tl| {
|
||||
(if lang_3c {
|
||||
tl.from == "eng"
|
||||
} else {
|
||||
tl.from == "en"
|
||||
}) && tl.perc >= 0.0
|
||||
&& tl.perc <= 1.0
|
||||
}),
|
||||
"translation: {:?}",
|
||||
track.track_lyrics_translation_status
|
||||
);
|
||||
} else {
|
||||
assert!(track.track_lyrics_translation_status.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn from_id_missing() {
|
||||
let err = new_mxm()
|
||||
.track(TrackId::TrackId(999999999999), false, false)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn album_tracks() {
|
||||
let tracks = new_mxm()
|
||||
.album_tracks(AlbumId::AlbumId(17118624), true, 20, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&tracks);
|
||||
|
||||
let track_names = tracks
|
||||
.iter()
|
||||
.map(|t| t.track_name.to_owned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
track_names,
|
||||
vec![
|
||||
"Gäa",
|
||||
"Vergiss mein nicht",
|
||||
"Orome",
|
||||
"Falke flieg",
|
||||
"Minne",
|
||||
"Das Lied der Ahnen",
|
||||
"Hörst du den Wind",
|
||||
"Nan Úye",
|
||||
"Faolan",
|
||||
"Hymne der Nacht",
|
||||
"Avalon",
|
||||
"Tolo Nan",
|
||||
"Oonagh",
|
||||
]
|
||||
);
|
||||
|
||||
tracks.iter().for_each(|t| {
|
||||
assert!(t.has_lyrics);
|
||||
assert!(t.has_subtitles);
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn album_missing() {
|
||||
let err = new_mxm()
|
||||
.album_tracks(AlbumId::AlbumId(999999999999), false, 20, 1)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::top(ChartName::Top)]
|
||||
#[case::hot(ChartName::Hot)]
|
||||
#[tokio::test]
|
||||
async fn charts(#[case] chart_name: ChartName) {
|
||||
let tracks = new_mxm()
|
||||
.chart_tracks("US", chart_name, true, 20, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(tracks.len(), 20);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search() {
|
||||
let tracks = new_mxm()
|
||||
.track_search()
|
||||
.q_artist("Lena")
|
||||
.q_track("Satellite")
|
||||
.s_track_rating(SortOrder::Desc)
|
||||
.send(1, 0)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&tracks);
|
||||
|
||||
assert_eq!(tracks.len(), 1);
|
||||
|
||||
let track = &tracks[0];
|
||||
assert_eq!(track.commontrack_id, 12426476);
|
||||
assert_eq!(track.track_name, "Satellite");
|
||||
assert_eq!(track.artist_name, "Lena");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search_lyrics() {
|
||||
let tracks = new_mxm()
|
||||
.track_search()
|
||||
.q_lyrics("the whole world stops and stares for a while")
|
||||
.s_track_rating(SortOrder::Desc)
|
||||
.send(10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(tracks.len(), 10);
|
||||
|
||||
let track = &tracks[0];
|
||||
assert_eq!(track.track_name, "Just the Way You Are");
|
||||
assert_eq!(track.artist_name, "Bruno Mars");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn search_empty() {
|
||||
let artists = new_mxm()
|
||||
.track_search()
|
||||
.q("Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz")
|
||||
.send(10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn genres() {
|
||||
let genres = new_mxm().genres().await.unwrap();
|
||||
assert!(genres.len() > 360);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn snippet() {
|
||||
let snippet = new_mxm()
|
||||
.track_snippet(TrackId::Commontrack(8874280))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(snippet.snippet_id, 23036767);
|
||||
assert_eq!(snippet.snippet_language, "en");
|
||||
assert!(!snippet.instrumental);
|
||||
assert!(snippet.updated_time > datetime!(2022-8-29 0:00 UTC));
|
||||
assert_eq!(
|
||||
snippet.snippet_body,
|
||||
"There's not a thing that I would change"
|
||||
);
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
use std::{fs::File, io::BufReader, path::Path};
|
||||
|
||||
use musixmatch_inofficial::models::{Lyrics, Subtitle, TranslationList, TranslationMap};
|
||||
|
||||
#[test]
|
||||
fn translation_test() {
|
||||
let lyrics_path = Path::new("testfiles/lyrics.json");
|
||||
let subtitles_path = Path::new("testfiles/subtitles.json");
|
||||
let translation_path = Path::new("testfiles/translation.json");
|
||||
|
||||
let lyrics: Lyrics =
|
||||
serde_json::from_reader(BufReader::new(File::open(lyrics_path).unwrap())).unwrap();
|
||||
let subtitle: Subtitle =
|
||||
serde_json::from_reader(BufReader::new(File::open(subtitles_path).unwrap())).unwrap();
|
||||
let translations: TranslationList =
|
||||
serde_json::from_reader(BufReader::new(File::open(translation_path).unwrap())).unwrap();
|
||||
|
||||
let t_map = TranslationMap::from(translations);
|
||||
|
||||
let lyrics_trans = t_map.translate_lyrics(&lyrics.lyrics_body);
|
||||
let expected_lyrics = std::fs::read_to_string("testfiles/translated_lyrics.txt").unwrap();
|
||||
assert_eq!(lyrics_trans.trim(), expected_lyrics.trim());
|
||||
|
||||
let subtitles_trans = t_map.translate_subtitles(&subtitle.to_lines().unwrap());
|
||||
|
||||
let expected_lrc = std::fs::read_to_string("testfiles/translated_subtitles.lrc").unwrap();
|
||||
let expected_ttml = std::fs::read_to_string("testfiles/translated_subtitles.xml").unwrap();
|
||||
|
||||
assert_eq!(subtitles_trans.to_lrc().trim(), expected_lrc.trim());
|
||||
assert_eq!(subtitles_trans.to_ttml().trim(), expected_ttml.trim());
|
||||
}
|
Loading…
Reference in a new issue