diff --git a/cli/src/main.rs b/cli/src/main.rs index 65f353d..b80813f 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -145,7 +145,7 @@ async fn run(cli: Cli) -> Result<()> { std::fs::create_dir_all(&storage_file)?; storage_file.push("musixmatch_session.json"); - let mxm = Musixmatch::builder().storage_file(storage_file).build(); + let mxm = Musixmatch::builder().storage_file(storage_file).build()?; match mxm.login().await { Ok(_) => {} diff --git a/src/apis/lyrics_api.rs b/src/apis/lyrics_api.rs index 1bb6c8e..d9a5038 100644 --- a/src/apis/lyrics_api.rs +++ b/src/apis/lyrics_api.rs @@ -27,6 +27,7 @@ impl Musixmatch { } let lyrics_body = self.execute_get_request::(&url).await?; + lyrics_body.validate()?; Ok(lyrics_body.lyrics) } @@ -47,6 +48,7 @@ impl Musixmatch { } let lyrics_body = self.execute_get_request::(&url).await?; + lyrics_body.validate()?; Ok(lyrics_body.lyrics) } diff --git a/src/error.rs b/src/error.rs index a2bffe3..f6a84fb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,6 +30,9 @@ pub enum Error { /// Musixmatch content not found #[error("The requested content could not be found")] NotFound, + /// Musixmatch content not available + #[error("Unfortunately we're not authorized to show these lyrics")] + NotAvailable, /// Error from the HTTP client #[error("http error: {0}")] Http(#[from] reqwest::Error), diff --git a/src/lib.rs b/src/lib.rs index ae76f22..8029bcf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ use hmac::{Hmac, Mac}; use log::{error, info, warn}; use rand::Rng; use reqwest::header::{self, HeaderMap}; -use reqwest::{Client, Url}; +use reqwest::{Client, ClientBuilder, Url}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use sha1::Sha1; @@ -180,21 +180,25 @@ impl MusixmatchBuilder { } /// Returns a new, configured Musixmatch client - pub fn build(self) -> Musixmatch { + pub fn build(self) -> Result { + self.build_with_client(ClientBuilder::new()) + } + + /// Returns a new, configured Musixmatch client using a Reqwest client builder + pub fn build_with_client(self, client_builder: ClientBuilder) -> Result { let storage = self.storage.or_default(|| Box::::default()); let stored_session = Musixmatch::retrieve_session(&storage); let mut headers = HeaderMap::new(); headers.insert(header::COOKIE, "AWSELBCORS=0; AWSELB=0".parse().unwrap()); - let http = Client::builder() + let http = client_builder .user_agent(self.user_agent.unwrap_or_else(|| DEFAULT_UA.to_owned())) .gzip(true) .default_headers(headers) - .build() - .expect("http client could not be constructed"); + .build()?; - Musixmatch { + Ok(Musixmatch { inner: MusixmatchRef { http, storage, @@ -204,13 +208,15 @@ impl MusixmatchBuilder { usertoken: Mutex::new(stored_session.map(|s| s.usertoken)), } .into(), - } + }) } } impl Default for Musixmatch { fn default() -> Self { - MusixmatchBuilder::new().build() + MusixmatchBuilder::new() + .build() + .expect("http client could not be constructed") } } diff --git a/src/models/lyrics.rs b/src/models/lyrics.rs index dd9053f..0070249 100644 --- a/src/models/lyrics.rs +++ b/src/models/lyrics.rs @@ -1,11 +1,30 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; +use crate::{error::Result, Error}; + #[derive(Debug, Deserialize)] pub(crate) struct LyricsBody { pub lyrics: Lyrics, } +impl LyricsBody { + pub(crate) fn validate(&self) -> Result<()> { + if self.lyrics.lyrics_body.is_empty() + && self + .lyrics + .lyrics_copyright + .as_deref() + .map(|c| c.contains("not authorized")) + .unwrap_or_default() + { + Err(Error::NotAvailable) + } else { + Ok(()) + } + } +} + /// Lyrics from the Musixmatch database. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[non_exhaustive] diff --git a/src/models/track.rs b/src/models/track.rs index 35f5f03..7c72a62 100644 --- a/src/models/track.rs +++ b/src/models/track.rs @@ -147,7 +147,7 @@ pub struct Track { #[non_exhaustive] pub struct TrackLyricsTranslationStatus { /// Source language code (e.g. "ko") - pub from: String, + pub from: Option, /// Target language code (e.g. "en") pub to: String, /// Translation ratio from 0 (untranslated) - 1 (fully translated) diff --git a/tests/tests.rs b/tests/tests.rs index 0eb16f5..e351bbe 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -28,6 +28,7 @@ fn new_mxm() -> Musixmatch { std::env::var("MUSIXMATCH_PASSWORD").unwrap(), ) .build() + .unwrap() } fn testfile>(name: P) -> PathBuf { @@ -51,7 +52,7 @@ mod album { "6c3cf9d8-88a8-43ed-850b-55813f01e451" ); assert_eq!(album.album_name, "Gangnam Style (강남스타일)"); - assert!(album.album_rating > 25); + assert!(album.album_rating > 20); 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); @@ -366,9 +367,9 @@ mod track { assert!( track.track_lyrics_translation_status.iter().all(|tl| { (if lang_3c { - tl.from == "eng" + tl.from.as_deref() == Some("eng") } else { - tl.from == "en" + tl.from.as_deref() == Some("en") }) && tl.perc >= 0.0 && tl.perc <= 1.0 }), @@ -428,7 +429,7 @@ mod track { 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_eq!(first_tstatus.from.as_deref(), Some("ko")); assert!(first_tstatus.perc >= 0.0 && first_tstatus.perc <= 1.0); } @@ -524,9 +525,9 @@ mod track { assert!( track.track_lyrics_translation_status.iter().all(|tl| { (if lang_3c { - tl.from == "eng" + tl.from.as_deref() == Some("eng") } else { - tl.from == "en" + tl.from.as_deref() == Some("en") }) && tl.perc >= 0.0 && tl.perc <= 1.0 }), @@ -626,7 +627,7 @@ mod track { assert_eq!(tracks.len(), 1); let track = &tracks[0]; - assert_eq!(track.commontrack_id, 12426476); + assert_eq!(track.commontrack_id, 72643758); assert_eq!(track.track_name, "Satellite"); assert_eq!(track.artist_name, "Lena"); } @@ -664,6 +665,7 @@ mod track { async fn genres() { let genres = new_mxm().genres().await.unwrap(); assert!(genres.len() > 360); + dbg!(&genres); } #[tokio::test] @@ -990,7 +992,7 @@ mod translation { #[tokio::test] async fn no_credentials() { - let mxm = Musixmatch::builder().no_storage().build(); + let mxm = Musixmatch::builder().no_storage().build().unwrap(); let err = mxm .track_lyrics(TrackId::TrackId(205688271)) .await