Compare commits

...

2 commits

Author SHA1 Message Date
e20610ac53 refactor!: return client builder result
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
feat: add build_with_client function
fix: parsing translation status with source lang=null
tests: fix tests
2023-07-02 17:29:58 +02:00
cc44bf4bb6 fix: add error for non available lyrics 2023-06-24 00:03:04 +02:00
7 changed files with 50 additions and 18 deletions

View file

@ -145,7 +145,7 @@ async fn run(cli: Cli) -> Result<()> {
std::fs::create_dir_all(&storage_file)?; std::fs::create_dir_all(&storage_file)?;
storage_file.push("musixmatch_session.json"); 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 { match mxm.login().await {
Ok(_) => {} Ok(_) => {}

View file

@ -27,6 +27,7 @@ impl Musixmatch {
} }
let lyrics_body = self.execute_get_request::<LyricsBody>(&url).await?; let lyrics_body = self.execute_get_request::<LyricsBody>(&url).await?;
lyrics_body.validate()?;
Ok(lyrics_body.lyrics) Ok(lyrics_body.lyrics)
} }
@ -47,6 +48,7 @@ impl Musixmatch {
} }
let lyrics_body = self.execute_get_request::<LyricsBody>(&url).await?; let lyrics_body = self.execute_get_request::<LyricsBody>(&url).await?;
lyrics_body.validate()?;
Ok(lyrics_body.lyrics) Ok(lyrics_body.lyrics)
} }

View file

@ -30,6 +30,9 @@ pub enum Error {
/// Musixmatch content not found /// Musixmatch content not found
#[error("The requested content could not be found")] #[error("The requested content could not be found")]
NotFound, NotFound,
/// Musixmatch content not available
#[error("Unfortunately we're not authorized to show these lyrics")]
NotAvailable,
/// Error from the HTTP client /// Error from the HTTP client
#[error("http error: {0}")] #[error("http error: {0}")]
Http(#[from] reqwest::Error), Http(#[from] reqwest::Error),

View file

@ -19,7 +19,7 @@ use hmac::{Hmac, Mac};
use log::{error, info, warn}; use log::{error, info, warn};
use rand::Rng; use rand::Rng;
use reqwest::header::{self, HeaderMap}; use reqwest::header::{self, HeaderMap};
use reqwest::{Client, Url}; use reqwest::{Client, ClientBuilder, Url};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha1::Sha1; use sha1::Sha1;
@ -180,21 +180,25 @@ impl MusixmatchBuilder {
} }
/// Returns a new, configured Musixmatch client /// Returns a new, configured Musixmatch client
pub fn build(self) -> Musixmatch { pub fn build(self) -> Result<Musixmatch> {
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<Musixmatch> {
let storage = self.storage.or_default(|| Box::<FileStorage>::default()); let storage = self.storage.or_default(|| Box::<FileStorage>::default());
let stored_session = Musixmatch::retrieve_session(&storage); let stored_session = Musixmatch::retrieve_session(&storage);
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert(header::COOKIE, "AWSELBCORS=0; AWSELB=0".parse().unwrap()); 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())) .user_agent(self.user_agent.unwrap_or_else(|| DEFAULT_UA.to_owned()))
.gzip(true) .gzip(true)
.default_headers(headers) .default_headers(headers)
.build() .build()?;
.expect("http client could not be constructed");
Musixmatch { Ok(Musixmatch {
inner: MusixmatchRef { inner: MusixmatchRef {
http, http,
storage, storage,
@ -204,13 +208,15 @@ impl MusixmatchBuilder {
usertoken: Mutex::new(stored_session.map(|s| s.usertoken)), usertoken: Mutex::new(stored_session.map(|s| s.usertoken)),
} }
.into(), .into(),
} })
} }
} }
impl Default for Musixmatch { impl Default for Musixmatch {
fn default() -> Self { fn default() -> Self {
MusixmatchBuilder::new().build() MusixmatchBuilder::new()
.build()
.expect("http client could not be constructed")
} }
} }

View file

@ -1,11 +1,30 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use time::OffsetDateTime; use time::OffsetDateTime;
use crate::{error::Result, Error};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub(crate) struct LyricsBody { pub(crate) struct LyricsBody {
pub lyrics: Lyrics, 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. /// Lyrics from the Musixmatch database.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive] #[non_exhaustive]

View file

@ -147,7 +147,7 @@ pub struct Track {
#[non_exhaustive] #[non_exhaustive]
pub struct TrackLyricsTranslationStatus { pub struct TrackLyricsTranslationStatus {
/// Source language code (e.g. "ko") /// Source language code (e.g. "ko")
pub from: String, pub from: Option<String>,
/// Target language code (e.g. "en") /// Target language code (e.g. "en")
pub to: String, pub to: String,
/// Translation ratio from 0 (untranslated) - 1 (fully translated) /// Translation ratio from 0 (untranslated) - 1 (fully translated)

View file

@ -28,6 +28,7 @@ fn new_mxm() -> Musixmatch {
std::env::var("MUSIXMATCH_PASSWORD").unwrap(), std::env::var("MUSIXMATCH_PASSWORD").unwrap(),
) )
.build() .build()
.unwrap()
} }
fn testfile<P: AsRef<Path>>(name: P) -> PathBuf { fn testfile<P: AsRef<Path>>(name: P) -> PathBuf {
@ -51,7 +52,7 @@ mod album {
"6c3cf9d8-88a8-43ed-850b-55813f01e451" "6c3cf9d8-88a8-43ed-850b-55813f01e451"
); );
assert_eq!(album.album_name, "Gangnam Style (강남스타일)"); 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_track_count, 1);
assert_eq!(album.album_release_date.unwrap(), date!(2012 - 01 - 01)); assert_eq!(album.album_release_date.unwrap(), date!(2012 - 01 - 01));
assert_eq!(album.album_release_type, AlbumType::Single); assert_eq!(album.album_release_type, AlbumType::Single);
@ -366,9 +367,9 @@ mod track {
assert!( assert!(
track.track_lyrics_translation_status.iter().all(|tl| { track.track_lyrics_translation_status.iter().all(|tl| {
(if lang_3c { (if lang_3c {
tl.from == "eng" tl.from.as_deref() == Some("eng")
} else { } else {
tl.from == "en" tl.from.as_deref() == Some("en")
}) && tl.perc >= 0.0 }) && tl.perc >= 0.0
&& tl.perc <= 1.0 && tl.perc <= 1.0
}), }),
@ -428,7 +429,7 @@ mod track {
assert!(track.updated_time > datetime!(2022-8-27 0:00 UTC)); assert!(track.updated_time > datetime!(2022-8-27 0:00 UTC));
let first_tstatus = &track.track_lyrics_translation_status[0]; 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); assert!(first_tstatus.perc >= 0.0 && first_tstatus.perc <= 1.0);
} }
@ -524,9 +525,9 @@ mod track {
assert!( assert!(
track.track_lyrics_translation_status.iter().all(|tl| { track.track_lyrics_translation_status.iter().all(|tl| {
(if lang_3c { (if lang_3c {
tl.from == "eng" tl.from.as_deref() == Some("eng")
} else { } else {
tl.from == "en" tl.from.as_deref() == Some("en")
}) && tl.perc >= 0.0 }) && tl.perc >= 0.0
&& tl.perc <= 1.0 && tl.perc <= 1.0
}), }),
@ -626,7 +627,7 @@ mod track {
assert_eq!(tracks.len(), 1); assert_eq!(tracks.len(), 1);
let track = &tracks[0]; 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.track_name, "Satellite");
assert_eq!(track.artist_name, "Lena"); assert_eq!(track.artist_name, "Lena");
} }
@ -664,6 +665,7 @@ mod track {
async fn genres() { async fn genres() {
let genres = new_mxm().genres().await.unwrap(); let genres = new_mxm().genres().await.unwrap();
assert!(genres.len() > 360); assert!(genres.len() > 360);
dbg!(&genres);
} }
#[tokio::test] #[tokio::test]
@ -990,7 +992,7 @@ mod translation {
#[tokio::test] #[tokio::test]
async fn no_credentials() { async fn no_credentials() {
let mxm = Musixmatch::builder().no_storage().build(); let mxm = Musixmatch::builder().no_storage().build().unwrap();
let err = mxm let err = mxm
.track_lyrics(TrackId::TrackId(205688271)) .track_lyrics(TrackId::TrackId(205688271))
.await .await