use std::{ num::NonZeroU32, path::{Path, PathBuf}, sync::LazyLock, }; use governor::{DefaultDirectRateLimiter, Quota, RateLimiter}; use path_macro::path; use rstest::{fixture, rstest}; use time::macros::{date, datetime}; use musixmatch_inofficial::{ models::{AlbumId, ArtistId, TrackId}, Error, Musixmatch, }; fn testfile>(name: P) -> PathBuf { path!(env!("CARGO_MANIFEST_DIR") / "testfiles" / name) } #[fixture] async fn mxm() -> Musixmatch { static LOGIN_LOCK: tokio::sync::OnceCell<()> = tokio::sync::OnceCell::const_new(); static MXM_LIMITER: LazyLock = LazyLock::new(|| RateLimiter::direct(Quota::per_second(NonZeroU32::new(1).unwrap()))); MXM_LIMITER.until_ready().await; let mut mxm = Musixmatch::builder(); if let (Ok(email), Ok(password)) = ( std::env::var("MUSIXMATCH_EMAIL"), std::env::var("MUSIXMATCH_PASSWORD"), ) { mxm = mxm.credentials(email, password); } let mxm = mxm.build().unwrap(); LOGIN_LOCK.get_or_try_init(|| mxm.login()).await.unwrap(); mxm } 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<'_>, #[future] mxm: Musixmatch) { let album = mxm.await.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 > 20); assert_eq!(album.album_track_count, 0); 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_imgurl(&album.album_coverart_100x100, "/26544045.jpg"); assert_imgurl(&album.album_coverart_350x350, "/26544045_350_350.jpg"); assert_imgurl(&album.album_coverart_500x500, "/26544045_500_500.jpg"); } #[rstest] #[tokio::test] async fn album_ep(#[future] mxm: Musixmatch) { let album = mxm.await.album(AlbumId::AlbumId(23976123)).await.unwrap(); assert_eq!(album.album_name, "Waldbrand EP"); // assert_eq!(album.album_release_type, AlbumType::Ep); assert_eq!(album.album_release_date, Some(date!(2016 - 09 - 30))); } #[rstest] #[tokio::test] async fn by_id_missing(#[future] mxm: Musixmatch) { let err = mxm .await .album(AlbumId::AlbumId(999999999999)) .await .unwrap_err(); assert!(matches!(err, Error::NotFound)); } #[rstest] #[tokio::test] #[ignore] async fn artist_albums(#[future] mxm: Musixmatch) { let albums = mxm .await .artist_albums(ArtistId::ArtistId(1039), None, 10, 1) .await .unwrap(); assert_eq!(albums.len(), 10); } #[rstest] #[tokio::test] async fn artist_albums_missing(#[future] mxm: Musixmatch) { let err = mxm .await .artist_albums(ArtistId::ArtistId(999999999999), None, 10, 1) .await .unwrap_err(); assert!(matches!(err, Error::NotFound)); } #[rstest] #[tokio::test] async fn charts(#[future] mxm: Musixmatch) { let albums = mxm.await.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<'_>, #[future] mxm: Musixmatch) { let artist = mxm.await.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); } #[rstest] #[tokio::test] async fn by_id_missing(#[future] mxm: Musixmatch) { let err = mxm .await .artist(ArtistId::ArtistId(999999999999)) .await .unwrap_err(); assert!(matches!(err, Error::NotFound)); } #[rstest] #[tokio::test] async fn related(#[future] mxm: Musixmatch) { let artists = mxm .await .artist_related(ArtistId::ArtistId(26485840), 10, 1) .await .unwrap(); assert_eq!(artists.len(), 10); } #[rstest] #[tokio::test] async fn related_missing(#[future] mxm: Musixmatch) { let err = mxm .await .artist_related(ArtistId::ArtistId(999999999999), 10, 1) .await .unwrap_err(); assert!(matches!(err, Error::NotFound)); } #[rstest] #[tokio::test] async fn search(#[future] mxm: Musixmatch) { let artists = mxm .await .artist_search("Snollebollekes", 5, 1) .await .unwrap(); let artist = &artists[0]; assert_eq!(artist.artist_id, 25344078); assert_eq!(artist.artist_name, "Snollebollekes"); } #[rstest] #[tokio::test] async fn search_empty(#[future] mxm: Musixmatch) { let artists = mxm .await .artist_search( "Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz", 5, 1, ) .await .unwrap(); assert_eq!(artists.len(), 0); } #[rstest] #[tokio::test] async fn charts(#[future] mxm: Musixmatch) { let artists = mxm.await.chart_artists("US", 10, 1).await.unwrap(); assert_eq!(artists.len(), 10); } #[rstest] #[tokio::test] async fn charts_no_country(#[future] mxm: Musixmatch) { let artists = mxm.await.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, #[future] mxm: Musixmatch, ) { let track = mxm .await .matcher_track( "Poker Face", "Lady Gaga", "The Fame", translation_status, lang_3c, ) .await .unwrap(); // dbg!(&track); assert_eq!(track.track_id, 85213841); // 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(), "1QV6tiMFM6fSOKOGLMHYYg"); 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!(track.lyrics_id.is_some()); assert_eq!(track.subtitle_id.unwrap(), 36450705); assert_eq!(track.album_id, 20960801); 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_imgurl(&track.album_coverart_100x100, "/32133892.jpg"); assert_imgurl(&track.album_coverart_350x350, "/32133892_350_350.jpg"); assert_imgurl(&track.album_coverart_500x500, "/32133892_500_500.jpg"); assert_imgurl(&track.album_coverart_800x800, "/32133892_800_800.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.as_deref() == Some("eng") } else { tl.from.as_deref() == Some("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".into()))] #[case::isrc(TrackId::Isrc("KRA302000590".into()))] #[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))] #[tokio::test] async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) { let track = mxm.await.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!(track.lyrics_id.is_some()); assert_eq!(track.subtitle_id.unwrap(), 36476905); assert_eq!(track.album_id, 41035954); assert_eq!(track.album_name, "Black Mamba"); assert_eq!(track.artist_id, 46970441); assert_eq!(track.artist_name, "aespa"); assert_imgurl(&track.album_coverart_100x100, "/52156772.jpg"); assert_imgurl(&track.album_coverart_350x350, "/52156772_350_350.jpg"); assert_imgurl(&track.album_coverart_500x500, "/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.as_deref(), Some("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, #[future] mxm: Musixmatch, ) { let track = mxm .await .track(TrackId::Commontrack(47672612), translation_status, lang_3c) .await .unwrap(); // dbg!(&track); assert_eq!(track.track_id, 85213841); // 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(), "1QV6tiMFM6fSOKOGLMHYYg"); 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!(track.lyrics_id.is_some()); assert_eq!(track.subtitle_id.unwrap(), 36450705); assert_eq!(track.album_id, 20960801); 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_imgurl(&track.album_coverart_100x100, "/32133892.jpg"); assert_imgurl(&track.album_coverart_350x350, "/32133892_350_350.jpg"); assert_imgurl(&track.album_coverart_500x500, "/32133892_500_500.jpg"); assert_imgurl(&track.album_coverart_800x800, "/32133892_800_800.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.as_deref() == Some("eng") } else { tl.from.as_deref() == Some("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] #[tokio::test] async fn from_id_missing(#[future] mxm: Musixmatch) { let err = mxm .await .track(TrackId::TrackId(999999999999), false, false) .await .unwrap_err(); assert!(matches!(err, Error::NotFound)); } #[rstest] #[tokio::test] async fn album_tracks(#[future] mxm: Musixmatch) { let tracks = mxm .await .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::>(); 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); }); } #[rstest] #[tokio::test] async fn album_missing(#[future] mxm: Musixmatch) { let err = mxm .await .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, #[future] mxm: Musixmatch) { let tracks = mxm .await .chart_tracks("US", chart_name, true, 20, 1) .await .unwrap(); assert_eq!(tracks.len(), 20); } #[rstest] #[tokio::test] async fn search(#[future] mxm: Musixmatch) { let tracks = mxm .await .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, 72643758); assert_eq!(track.track_name, "Satellite"); assert_eq!(track.artist_name, "Lena"); } #[rstest] #[tokio::test] async fn search_lyrics(#[future] mxm: Musixmatch) { let tracks = mxm .await .track_search() .q_lyrics("the whole world stops and stares for a while") .s_track_rating(SortOrder::Desc) .send(10, 1) .await .unwrap(); assert_gte(tracks.len(), 8, "tracks"); let track = &tracks[0]; assert_eq!(track.track_name, "Just the Way You Are"); assert_eq!(track.artist_name, "Bruno Mars"); } #[rstest] #[tokio::test] async fn search_empty(#[future] mxm: Musixmatch) { let artists = mxm .await .track_search() .q("Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz") .send(10, 1) .await .unwrap(); assert_eq!(artists.len(), 0); } #[rstest] #[tokio::test] async fn genres(#[future] mxm: Musixmatch) { let genres = mxm.await.genres().await.unwrap(); assert!(genres.len() > 360); dbg!(&genres); } #[rstest] #[tokio::test] async fn snippet(#[future] mxm: Musixmatch) { let snippet = mxm .await .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}; use super::*; #[rstest] #[tokio::test] async fn from_match(#[future] mxm: Musixmatch) { let lyrics = mxm.await.matcher_lyrics("Shine", "Spektrem").await.unwrap(); // dbg!(&lyrics); assert_eq!(lyrics.lyrics_id, 34583240); assert!(!lyrics.instrumental); assert!(!lyrics.explicit); assert!( lyrics .lyrics_body .starts_with("Eyes in the sky gazing far into the night\n"), "got: {}", lyrics.lyrics_body ); assert_eq!(lyrics.lyrics_language.unwrap(), "en"); assert_eq!(lyrics.lyrics_language_description.unwrap(), "English"); let copyright = lyrics.lyrics_copyright.unwrap(); assert!(copyright.contains("Jesse Warren"), "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".into()))] #[case::isrc(TrackId::Isrc("KRA302000590".into()))] #[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))] #[tokio::test] async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) { let lyrics = mxm.await.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 #[rstest] #[tokio::test] async fn instrumental(#[future] mxm: Musixmatch) { let lyrics = mxm .await .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 #[rstest] #[tokio::test] async fn missing(#[future] mxm: Musixmatch) { let err = mxm .await .track_lyrics(TrackId::Spotify("2gwMMr1a4aXXN5L6KC80Pu".into())) .await .unwrap_err(); assert!(matches!(err, Error::NotFound)); } #[rstest] #[tokio::test] async fn download_testdata(#[future] mxm: Musixmatch) { let mxm = mxm.await; let json_path = testfile("lyrics.json"); if json_path.exists() { return; } let lyrics = 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(); } #[rstest] #[tokio::test] async fn download_testdata_translation(#[future] mxm: Musixmatch) { let mxm = mxm.await; let json_path = testfile("translation.json"); if json_path.exists() { return; } let translations = 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(); } #[rstest] #[tokio::test] async fn concurrency(#[future] mxm: Musixmatch) { let mxm = mxm.await; 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::>() .await .into_iter() .map(Result::unwrap) .collect::>(); dbg!(x); } } mod subtitles { use std::{fs::File, io::BufWriter}; use super::*; use musixmatch_inofficial::models::SubtitleFormat; #[rstest] #[tokio::test] async fn from_match(#[future] mxm: Musixmatch) { let subtitle = mxm .await .matcher_subtitle( "Shine", "Spektrem", SubtitleFormat::Json, Some(315.0), Some(1.0), ) .await .unwrap(); // dbg!(&subtitle); assert_eq!(subtitle.subtitle_id, 35340319); assert_eq!(subtitle.subtitle_language.unwrap(), "en"); assert_eq!(subtitle.subtitle_language_description.unwrap(), "English"); let copyright = subtitle.lyrics_copyright.unwrap(); assert!(copyright.contains("Jesse Warren"), "copyright: {copyright}",); assert_eq!(subtitle.subtitle_length, 316); 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".into()))] #[case::isrc(TrackId::Isrc("KRA302000590".into()))] #[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))] #[tokio::test] async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) { let subtitle = mxm .await .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 #[rstest] #[tokio::test] async fn instrumental(#[future] mxm: Musixmatch) { let err = mxm .await .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 #[rstest] #[tokio::test] async fn unsynced(#[future] mxm: Musixmatch) { let err = mxm .await .track_subtitle( TrackId::Spotify("6oaWIABGL7eeiMILEDyGX1".into()), SubtitleFormat::Json, Some(213.0), Some(1.0), ) .await .unwrap_err(); assert!(matches!(err, Error::NotFound)); } /// Try to get subtitles with wrong length parameter #[rstest] #[tokio::test] async fn wrong_length(#[future] mxm: Musixmatch) { let err = mxm .await .track_subtitle( TrackId::Commontrack(118480583), SubtitleFormat::Json, Some(200.0), Some(1.0), ) .await .unwrap_err(); assert!(matches!(err, Error::NotFound)); } #[rstest] #[tokio::test] async fn download_testdata(#[future] mxm: Musixmatch) { let json_path = testfile("subtitles.json"); if json_path.exists() { return; } let subtitle = mxm .await .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}; use musixmatch_inofficial::models::{Lyrics, Subtitle, TranslationList, TranslationMap}; use crate::testfile; #[test] fn translation_test() { let lyrics_path = testfile("lyrics.json"); let subtitles_path = testfile("subtitles.json"); let translation_path = testfile("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(testfile("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(testfile("translated_subtitles.lrc")).unwrap(); let expected_ttml = std::fs::read_to_string(testfile("translated_subtitles.xml")).unwrap(); assert_eq!(subtitles_trans.to_lrc().trim(), expected_lrc.trim()); assert_eq!(subtitles_trans.to_ttml().trim(), expected_ttml.trim()); } } #[track_caller] fn assert_imgurl(url: &Option, ends_with: &str) { assert!( url.as_deref().is_some_and( |url| url.starts_with("https://s.mxmcdn.net/images-storage/") && url.ends_with(ends_with) ), "expected url ending with {ends_with}\ngot {:?}", url ); } /// Assert that number A is greater than or equal to number B #[track_caller] fn assert_gte(a: T, b: T, msg: &str) { assert!(a >= b, "expected >= {b} {msg}, got {a}"); }