From 5ef76f5a6b2a3b243f847cf86e72ebe176819d7a Mon Sep 17 00:00:00 2001 From: ThetaBot Date: Mon, 30 Sep 2024 00:07:35 +0000 Subject: [PATCH 01/38] chore(deps): update rust crate rstest to 0.23.0 (#2) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0a3b896..81e9352 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ rand = "0.8.0" base64 = "0.22.0" [dev-dependencies] -rstest = { version = "0.22.0", default-features = false } +rstest = { version = "0.23.0", default-features = false } dotenvy = "0.15.5" tokio = { version = "1.20.4", features = ["macros"] } futures = "0.3.21" From 4bfcb791733ce5ebd9d4e074c64eb23e9a768fc6 Mon Sep 17 00:00:00 2001 From: ThetaBot Date: Wed, 23 Oct 2024 20:08:45 +0000 Subject: [PATCH 02/38] chore(deps): update rust crate governor to 0.7.0 (#3) --- Cargo.toml | 2 +- tests/tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 81e9352..f1d68b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,4 +63,4 @@ dotenvy = "0.15.5" tokio = { version = "1.20.4", features = ["macros"] } futures = "0.3.21" path_macro = "1.0.0" -governor = "0.6.3" +governor = "0.7.0" diff --git a/tests/tests.rs b/tests/tests.rs index aa762f9..a6483c8 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -59,7 +59,7 @@ mod album { ); assert_eq!(album.album_name, "Gangnam Style (강남스타일)"); assert!(album.album_rating > 20); - assert_eq!(album.album_track_count, 1); + 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); From e4cffa53ca2e458ec9ab3607f036426e3c647166 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Wed, 23 Oct 2024 22:51:41 +0200 Subject: [PATCH 03/38] test: skip artist_albums test --- tests/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tests.rs b/tests/tests.rs index a6483c8..a308600 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -120,6 +120,7 @@ mod album { #[rstest] #[tokio::test] + #[ignore] async fn artist_albums(#[future] mxm: Musixmatch) { let albums = mxm .await From 368b46fa79d80460f6e4a7ba2dcce47c7590997d Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 15 Nov 2024 19:37:55 +0100 Subject: [PATCH 04/38] test: fix missing lyrics test --- tests/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests.rs b/tests/tests.rs index a308600..af71286 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -786,7 +786,7 @@ mod lyrics { async fn missing(#[future] mxm: Musixmatch) { let err = mxm .await - .track_lyrics(TrackId::Spotify("674JwwTP7xCje81T0DRrLn".into())) + .track_lyrics(TrackId::Spotify("2gwMMr1a4aXXN5L6KC80Pu".into())) .await .unwrap_err(); From 6a6ced16224c6ef3d05eb6ebd0aa0bdc40a34684 Mon Sep 17 00:00:00 2001 From: ThetaBot Date: Fri, 15 Nov 2024 18:56:06 +0000 Subject: [PATCH 05/38] fix(deps): update rust crate thiserror to v2 (#4) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f1d68b7..869d843 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ reqwest = { version = "0.12.0", default-features = false, features = [ tokio = { version = "1.20.4" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" -thiserror = "1.0.0" +thiserror = "2.0.0" log = "0.4.17" time = { version = "0.3.10", features = [ "macros", From adcd9baf120955bd3fbafa8674a0967d12d38a3e Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 15 Nov 2024 20:19:34 +0100 Subject: [PATCH 06/38] chore(release): release musixmatch-inofficial v0.1.2 --- CHANGELOG.md | 12 ++++++++++++ Cargo.toml | 2 +- cliff.toml | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c73e0..8d91e37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. +## [v0.1.2](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-inofficial/v0.1.1..musixmatch-inofficial/v0.1.2) - 2024-11-15 + +### 🐛 Bug Fixes + +- *(deps)* Update rust crate thiserror to v2 (#4) - ([6a6ced1](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/6a6ced16224c6ef3d05eb6ebd0aa0bdc40a34684)) + +### ⚙️ Miscellaneous Tasks + +- *(deps)* Update rust crate rstest to 0.23.0 (#2) - ([5ef76f5](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/5ef76f5a6b2a3b243f847cf86e72ebe176819d7a)) +- *(deps)* Update rust crate governor to 0.7.0 (#3) - ([4bfcb79](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/4bfcb791733ce5ebd9d4e074c64eb23e9a768fc6)) + + ## [v0.1.1](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-inofficial/v0.1.0..musixmatch-inofficial/v0.1.1) - 2024-08-18 ### 🚀 Features diff --git a/Cargo.toml b/Cargo.toml index 869d843..8377b1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "musixmatch-inofficial" -version = "0.1.1" +version = "0.1.2" rust-version = "1.70.0" edition.workspace = true authors.workspace = true diff --git a/cliff.toml b/cliff.toml index a12fc01..b456d2d 100644 --- a/cliff.toml +++ b/cliff.toml @@ -73,7 +73,7 @@ commit_parsers = [ { message = "^perf", group = "⚡ Performance" }, { message = "^refactor", group = "🚜 Refactor" }, { message = "^style", group = "🎨 Styling" }, - { message = "^test", group = "🧪 Testing" }, + { message = "^test", skip = true }, { message = "^chore\\(release\\)", skip = true }, { message = "^chore\\(pr\\)", skip = true }, { message = "^chore\\(pull\\)", skip = true }, From 693ff347552a693ae47e181489330c6b23f58a9a Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 6 Dec 2024 20:18:08 +0000 Subject: [PATCH 07/38] ci: add workdlow_dispatch --- .gitea/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index bc6e278..5aa75d1 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -1,5 +1,5 @@ name: CI -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch ] jobs: Test: From 26f4729738536d735cb808fce8a8e466f2e82449 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 7 Dec 2024 19:39:28 +0100 Subject: [PATCH 08/38] chore: fix clippy lints --- .gitea/workflows/ci.yaml | 5 ++++- src/api_model.rs | 12 ++++++------ src/models/id.rs | 6 +++--- tests/tests.rs | 8 ++++++-- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 5aa75d1..d03eb50 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -1,5 +1,8 @@ name: CI -on: [push, pull_request, workflow_dispatch ] +on: + push: + pull_request: + workflow_dispatch: jobs: Test: diff --git a/src/api_model.rs b/src/api_model.rs index 463e5d1..4d31076 100644 --- a/src/api_model.rs +++ b/src/api_model.rs @@ -117,7 +117,7 @@ where { struct BoolFromIntVisitor; - impl<'de> Visitor<'de> for BoolFromIntVisitor { + impl Visitor<'_> for BoolFromIntVisitor { type Value = bool; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -232,7 +232,7 @@ where n: PhantomData, } - impl<'de, N> Visitor<'de> for NullIfZeroVisitor + impl Visitor<'_> for NullIfZeroVisitor where N: TryFrom, { @@ -300,7 +300,7 @@ where { struct NullIfEmptyVisitor; - impl<'de> Visitor<'de> for NullIfEmptyVisitor { + impl Visitor<'_> for NullIfEmptyVisitor { type Value = Option; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -347,7 +347,7 @@ where n: PhantomData, } - impl<'de, N> Visitor<'de> for ParseIntVisitor + impl Visitor<'_> for ParseIntVisitor where N: FromStr + TryFrom, { @@ -441,7 +441,7 @@ pub mod optional_date { ) -> Result, D::Error> { struct OptionalDateVisitor; - impl<'de> Visitor<'de> for OptionalDateVisitor { + impl Visitor<'_> for OptionalDateVisitor { type Value = Option; fn expecting( @@ -501,7 +501,7 @@ pub mod optional_datetime { ) -> Result, D::Error> { struct OptionalDateVisitor; - impl<'de> Visitor<'de> for OptionalDateVisitor { + impl Visitor<'_> for OptionalDateVisitor { type Value = Option; fn expecting( diff --git a/src/models/id.rs b/src/models/id.rs index 2e8879a..9c84251 100644 --- a/src/models/id.rs +++ b/src/models/id.rs @@ -25,7 +25,7 @@ pub enum TrackId<'a> { Spotify(Cow<'a, str>), } -impl<'a> TrackId<'a> { +impl TrackId<'_> { pub(crate) fn to_param(&self) -> (&'static str, String) { match self { TrackId::Commontrack(id) => ("commontrack_id", id.to_string()), @@ -50,7 +50,7 @@ pub enum ArtistId<'a> { Musicbrainz(&'a str), } -impl<'a> ArtistId<'a> { +impl ArtistId<'_> { pub(crate) fn to_param(&self) -> (&'static str, String) { match self { ArtistId::ArtistId(id) => ("artist_id", id.to_string()), @@ -71,7 +71,7 @@ pub enum AlbumId<'a> { Musicbrainz(&'a str), } -impl<'a> AlbumId<'a> { +impl AlbumId<'_> { pub(crate) fn to_param(&self) -> (&'static str, String) { match self { AlbumId::AlbumId(id) => ("album_id", id.to_string()), diff --git a/tests/tests.rs b/tests/tests.rs index af71286..ea56b0b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -21,8 +21,12 @@ fn testfile>(name: P) -> PathBuf { #[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()))); + static MXM_LIMITER: LazyLock = LazyLock::new(|| { + RateLimiter::direct(Quota::per_second( + // limit 1 request per second for CI runs + NonZeroU32::new(if std::env::var("CI").is_ok() { 1 } else { 4 }).unwrap(), + )) + }); MXM_LIMITER.until_ready().await; From b136bb30040dc3ee849c26ff984884e706739235 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 8 Dec 2024 23:18:09 +0100 Subject: [PATCH 09/38] feat: add track performer tagging, artist images --- Cargo.toml | 1 + cli/src/main.rs | 44 +++++++++++--- src/api_model.rs | 66 ++++++++++++++++++++ src/apis/artist_api.rs | 1 + src/apis/track_api.rs | 24 +++++++- src/error.rs | 5 ++ src/lib.rs | 2 +- src/models/album.rs | 1 + src/models/artist.rs | 46 ++++++++++++++ src/models/id.rs | 133 ++++++++++++++++++++++++++++++++++++++++- src/models/mod.rs | 6 ++ src/models/snippet.rs | 1 + src/models/track.rs | 71 +++++++++++++++++++++- tests/tests.rs | 71 ++++++++++++++++++++-- 14 files changed, 454 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8377b1e..0f6609d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,3 +64,4 @@ tokio = { version = "1.20.4", features = ["macros"] } futures = "0.3.21" path_macro = "1.0.0" governor = "0.7.0" +test-log = "0.2.16" diff --git a/cli/src/main.rs b/cli/src/main.rs index 6a303da..c10e2a1 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -55,6 +55,11 @@ enum Commands { #[clap(flatten)] ident: TrackIdentifiers, }, + /// Get performer tagging + Performer { + #[clap(flatten)] + ident: TrackIdentifiers, + }, /// Get album metadata Album { #[clap(flatten)] @@ -314,9 +319,16 @@ async fn run(cli: Cli) -> Result<()> { } } Commands::Track { ident } => { - let track = get_track(ident, &mxm).await?; + let track = get_track(ident, &mxm, false).await?; println!("{}", serde_json::to_string_pretty(&track)?) } + Commands::Performer { ident } => { + let track = get_track(ident, &mxm, true).await?; + println!( + "{}", + serde_json::to_string_pretty(&track.performer_tagging)? + ) + } Commands::Album { ident } => { let id = if let Some(id) = ident.mxm_id { AlbumId::AlbumId(id) @@ -397,6 +409,7 @@ async fn get_track_or_id( ident: TrackIdentifiers, mxm: &Musixmatch, translation_status: bool, + performer_tagging: bool, ) -> Result> { Ok( match ( @@ -422,8 +435,15 @@ async fn get_track_or_id( } (_, _, _, _, _, _, _, Some(isrc)) => TrackOrId::TrackId(TrackId::Isrc(isrc.into())), (Some(name), Some(artist), _, _, _, _, _, _) => TrackOrId::Track(Box::new( - mxm.matcher_track(&name, &artist, "", translation_status, true) - .await?, + mxm.matcher_track( + &name, + &artist, + "", + translation_status, + true, + performer_tagging, + ) + .await?, )), _ => bail!("no track identifier given"), }, @@ -431,17 +451,23 @@ async fn get_track_or_id( } async fn get_track_id(ident: TrackIdentifiers, mxm: &Musixmatch) -> Result> { - Ok(match get_track_or_id(ident, mxm, false).await? { + Ok(match get_track_or_id(ident, mxm, false, false).await? { TrackOrId::Track(track) => TrackId::TrackId(track.track_id), TrackOrId::TrackId(id) => id, }) } -async fn get_track(ident: TrackIdentifiers, mxm: &Musixmatch) -> Result { - Ok(match get_track_or_id(ident, mxm, true).await? { - TrackOrId::Track(track) => *track, - TrackOrId::TrackId(id) => mxm.track(id, true, true).await?, - }) +async fn get_track( + ident: TrackIdentifiers, + mxm: &Musixmatch, + performer_tagging: bool, +) -> Result { + Ok( + match get_track_or_id(ident, mxm, true, performer_tagging).await? { + TrackOrId::Track(track) => *track, + TrackOrId::TrackId(id) => mxm.track(id, true, true, performer_tagging).await?, + }, + ) } fn input(prompt: &str) -> String { diff --git a/src/api_model.rs b/src/api_model.rs index 4d31076..1eb4760 100644 --- a/src/api_model.rs +++ b/src/api_model.rs @@ -543,6 +543,55 @@ pub mod optional_datetime { } } +pub fn single_or_vec<'de, D, T>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + struct SingleOrVecVisitor { + t: PhantomData, + } + + impl<'de, T> Visitor<'de> for SingleOrVecVisitor + where + T: Deserialize<'de>, + { + type Value = Vec; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("single object or list") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut res = Vec::new(); + while let Some(x) = seq.next_element()? { + res.push(x); + } + Ok(res) + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let (k1, val) = map + .next_entry::<&str, T>()? + .ok_or(serde::de::Error::missing_field("value"))?; + if let Some((k2, _)) = map.next_entry::<&str, serde::de::IgnoredAny>()? { + return Err(serde::de::Error::custom(format!( + "expected only 1 value, got keys `{k1}`, `{k2}`" + ))); + } + Ok(vec![val]) + } + } + + deserializer.deserialize_any(SingleOrVecVisitor { t: PhantomData }) +} + #[cfg(test)] mod tests { use time::Date; @@ -686,4 +735,21 @@ mod tests { let res = serde_json::from_str::(json_date).unwrap(); assert!(res.date.is_some()); } + + #[test] + fn deserialize_single_or_vec() { + #[derive(Deserialize, Debug)] + struct S { + #[serde(deserialize_with = "single_or_vec")] + vec: Vec, + } + + let res = serde_json::from_str::(r#"{"vec": [1, 2, 3]}"#).unwrap(); + assert_eq!(res.vec, [1, 2, 3]); + + let res = serde_json::from_str::(r#"{"vec": {"value": 1}}"#).unwrap(); + assert_eq!(res.vec, [1]); + + serde_json::from_str::(r#"{"vec": {"value": 1, "other": "xyz"}}"#).unwrap_err(); + } } diff --git a/src/apis/artist_api.rs b/src/apis/artist_api.rs index f2dde1f..1581a5f 100644 --- a/src/apis/artist_api.rs +++ b/src/apis/artist_api.rs @@ -18,6 +18,7 @@ impl Musixmatch { let id_param = id.to_param(); url_query.append_pair(id_param.0, &id_param.1); + url_query.append_pair("part", "artist_image"); url_query.finish(); } diff --git a/src/apis/track_api.rs b/src/apis/track_api.rs index 045779f..537c69b 100644 --- a/src/apis/track_api.rs +++ b/src/apis/track_api.rs @@ -26,6 +26,7 @@ impl Musixmatch { q_album: &str, translation_status: bool, lang_3c: bool, + performer_tagging: bool, ) -> Result { let mut url = self.new_url("matcher.track.get"); { @@ -40,8 +41,10 @@ impl Musixmatch { if !q_album.is_empty() { url_query.append_pair("q_album", q_album); } + + let mut part = Vec::new(); if translation_status { - url_query.append_pair("part", "track_lyrics_translation_status"); + part.push("track_lyrics_translation_status"); url_query.append_pair( "language_iso_code", match lang_3c { @@ -50,6 +53,13 @@ impl Musixmatch { }, ); } + if performer_tagging { + part.push("track_performer_tagging"); + } + if !part.is_empty() { + url_query.append_pair("part", &part.join(",")); + } + url_query.finish(); } @@ -73,6 +83,7 @@ impl Musixmatch { id: TrackId<'_>, translation_status: bool, lang_3c: bool, + performer_tagging: bool, ) -> Result { let mut url = self.new_url("track.get"); { @@ -80,8 +91,10 @@ impl Musixmatch { let id_param = id.to_param(); url_query.append_pair(id_param.0, &id_param.1); + + let mut part = Vec::new(); if translation_status { - url_query.append_pair("part", "track_lyrics_translation_status"); + part.push("track_lyrics_translation_status"); url_query.append_pair( "language_iso_code", match lang_3c { @@ -90,6 +103,13 @@ impl Musixmatch { }, ); } + if performer_tagging { + part.push("track_performer_tagging"); + } + if !part.is_empty() { + url_query.append_pair("part", &part.join(",")); + } + url_query.finish(); } diff --git a/src/error.rs b/src/error.rs index 17e03e0..f58312b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -52,3 +52,8 @@ impl From for Error { Self::InvalidData(value.to_string().into()) } } + +/// Could not parse Musixmatch FQID +#[derive(thiserror::Error, Debug)] +#[error("Could not parse Musixmatch FQID")] +pub struct IdError; diff --git a/src/lib.rs b/src/lib.rs index b494a13..0d14784 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ use std::fmt::Debug; use std::path::Path; use std::sync::{Arc, RwLock}; -pub use error::Error; +pub use error::{Error, IdError}; use base64::Engine; use hmac::{Hmac, Mac}; diff --git a/src/models/album.rs b/src/models/album.rs index afc0d59..9d4e8f7 100644 --- a/src/models/album.rs +++ b/src/models/album.rs @@ -15,6 +15,7 @@ pub(crate) struct AlbumListBody { /// Album: an album of songs in the Musixmatch database. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[non_exhaustive] pub struct Album { /// Unique Musixmatch Album ID pub album_id: u64, diff --git a/src/models/artist.rs b/src/models/artist.rs index e6d1a76..0d85dd7 100644 --- a/src/models/artist.rs +++ b/src/models/artist.rs @@ -15,6 +15,7 @@ pub(crate) struct ArtistListBody { /// Artist: an artist in the Musixmatch database. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[non_exhaustive] pub struct Artist { /// Musixmatch Artist ID pub artist_id: u64, @@ -85,10 +86,14 @@ pub struct Artist { /// End date of the artist's presence #[serde(default, with = "crate::api_model::optional_date")] pub end_date: Option, + /// Pictures of the artist + #[serde(default, deserialize_with = "crate::api_model::single_or_vec")] + pub artist_image: Vec, } /// Alternative artist name (e.g. different languages) #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +#[non_exhaustive] pub struct ArtistAlias { /// Alternative artist name pub artist_alias: String, @@ -96,6 +101,7 @@ pub struct ArtistAlias { /// Artist name in another language #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +#[non_exhaustive] pub struct ArtistNameTranslation { /// Artist name in another language pub artist_name_translation: ArtistNameTranslationInner, @@ -103,6 +109,7 @@ pub struct ArtistNameTranslation { /// Alternative artist name (e.g. different languages) #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +#[non_exhaustive] pub struct ArtistNameTranslationInner { /// Language code (e.g. "EN") /// @@ -111,3 +118,42 @@ pub struct ArtistNameTranslationInner { /// Translated name pub translation: String, } + +/// Artist image +#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +#[non_exhaustive] +pub struct ArtistImage { + /// ID of the image in the Musixmatch database + pub image_id: u64, + pub image_source_id: u32, + /// Author who created the image + #[serde(default, deserialize_with = "crate::api_model::null_if_empty")] + pub image_author: Option, + /// Copyright info for the image + #[serde(default, deserialize_with = "crate::api_model::null_if_empty")] + pub image_copyright: Option, + /// Image tags + #[serde(default, deserialize_with = "crate::api_model::null_if_empty")] + pub image_tags: Option, + // List of image files scaled to different sizes + pub image_format_list: Vec, +} + +/// Image file (wrapper struct) +#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +#[non_exhaustive] +pub struct ImageFormatWrap { + pub image_format: ImageFormat, +} + +/// Image file +#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +#[non_exhaustive] +pub struct ImageFormat { + /// URL to the image file + pub image_url: String, + /// Image width in pixels + pub width: u32, + /// Image height in pixels + pub height: u32, +} diff --git a/src/models/id.rs b/src/models/id.rs index 9c84251..3753c49 100644 --- a/src/models/id.rs +++ b/src/models/id.rs @@ -1,4 +1,8 @@ -use std::borrow::Cow; +use std::{borrow::Cow, convert::Infallible, fmt::Write, str::FromStr}; + +use serde::{de::Visitor, Deserialize, Serialize}; + +use crate::IdError; /// Track identifiers from different sources #[derive(Debug, Clone, PartialEq, Eq)] @@ -96,3 +100,130 @@ impl SortOrder { } } } + +/// Musixmatch fully qualified ID +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Fqid { + /// Numeric Musixmatch ID + pub id: u64, + /// Entity type + pub typ: MxmEntityType, +} + +/// Musixmatch entity type +#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +#[allow(missing_docs)] +#[non_exhaustive] +pub enum MxmEntityType { + Artist, + #[serde(other)] + Unknown, +} + +impl std::fmt::Display for MxmEntityType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + MxmEntityType::Artist => "artist", + MxmEntityType::Unknown => "unknown", + }; + f.write_str(s) + } +} + +impl FromStr for MxmEntityType { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + Ok(match s { + "artist" => Self::Artist, + _ => Self::Unknown, + }) + } +} + +impl std::fmt::Debug for MxmEntityType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self, f) + } +} + +impl std::fmt::Display for Fqid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "mxm:{}:{}", self.typ, self.id) + } +} + +impl std::fmt::Debug for Fqid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_char('"')?; + std::fmt::Display::fmt(&self, f)?; + f.write_char('"') + } +} + +impl FromStr for Fqid { + type Err = IdError; + + fn from_str(s: &str) -> Result { + let wo_pfx = s.strip_prefix("mxm:").ok_or(IdError)?; + let (typ_s, id_s) = wo_pfx.split_once(':').ok_or(IdError)?; + let id = id_s.parse().map_err(|_| IdError)?; + let typ = typ_s.parse().unwrap(); + Ok(Self { id, typ }) + } +} + +impl Serialize for Fqid { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Fqid { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct FqidVisitor; + + impl Visitor<'_> for FqidVisitor { + type Value = Fqid; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("Musixmatch FQID") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse().map_err(serde::de::Error::custom) + } + } + + deserializer.deserialize_str(FqidVisitor) + } +} + +#[cfg(test)] +mod tests { + use super::Fqid; + + #[test] + fn serialize_fqid() { + let json = r#""mxm:artist:27853427""#; + let id = serde_json::from_str::(json).unwrap(); + assert_eq!( + id, + Fqid { + id: 27853427, + typ: crate::models::id::MxmEntityType::Artist + } + ); + assert_eq!(serde_json::to_string(&id).unwrap(), json) + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 720d4e6..8aef72e 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -10,6 +10,8 @@ pub use subtitle::SubtitleTime; mod id; pub use id::AlbumId; pub use id::ArtistId; +pub use id::Fqid; +pub use id::MxmEntityType; pub use id::SortOrder; pub use id::TrackId; @@ -23,8 +25,12 @@ pub use translation::TranslationMap; pub(crate) mod track; pub use track::ChartName; +pub use track::Performer; +pub use track::PerformerTaggingPart; +pub use track::PerformerTaggingResources; pub use track::Track; pub use track::TrackLyricsTranslationStatus; +pub use track::TrackPerformerTagging; mod genre; pub use genre::Genre; diff --git a/src/models/snippet.rs b/src/models/snippet.rs index 9dc03a9..fa9944b 100644 --- a/src/models/snippet.rs +++ b/src/models/snippet.rs @@ -14,6 +14,7 @@ pub(crate) struct SnippetBody { /// /// Example: "There's not a thing that I would change" #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[allow(missing_docs)] pub struct Snippet { /// Unique Musixmatch Snippet ID pub snippet_id: u64, diff --git a/src/models/track.rs b/src/models/track.rs index 7c72a62..d62e2f7 100644 --- a/src/models/track.rs +++ b/src/models/track.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use time::OffsetDateTime; -use super::Genres; +use super::{Artist, Fqid, Genres}; #[derive(Debug, Deserialize)] pub(crate) struct TrackBody { @@ -140,6 +140,8 @@ pub struct Track { /// Status of lyrics translation #[serde(default)] pub track_lyrics_translation_status: Vec, + /// Lyrics parts marked with the performer who is singing them + pub performer_tagging: Option, } /// Status of lyrics translation (language + progress) @@ -156,6 +158,73 @@ pub struct TrackLyricsTranslationStatus { pub perc: f32, } +/// Lyrics parts marked with the performer who is singing them +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[allow(missing_docs)] +pub struct TrackPerformerTagging { + /// Musixmatch user ID of the user who added the performer tags + /// + /// Format: `mxm:<16 byte hex>` + pub user_id: String, + /// True if the lyrics are completely tagged + pub completed: bool, + /// True if the lyrics + pub has_unknown: bool, + /// True if the lyrics contain parts that are intended to be sung by the + /// audience during concerts + pub has_fan_chant: bool, + /// List of tagged lyrics parts + #[serde(default)] + pub content: Vec, + /// Artists (and possibly other objects) that are referenced by the tagged parts + #[serde(default)] + pub resources: PerformerTaggingResources, +} + +/// Performer-tagged lyrics part +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[allow(missing_docs)] +pub struct PerformerTaggingPart { + /// Part of the lyrics text + /// + /// Includes whitespace (spaces and newline characters). + pub snippet: String, + /// Unbekannt + /// + /// 0-3 + pub position: u32, + /// List of performers singing this part + pub performers: Vec, +} + +/// Lyrics performer +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[allow(missing_docs)] +pub struct Performer { + /// artist / unknown + #[serde(rename = "type")] + pub typ: String, + /// Fully-qualified performer ID + pub fqid: Option, + /// Unbekannt + /// + /// 9 + pub category_id: Option, + /// Unbekannt + /// + /// 405 + pub credit_role_id: Option, +} + +/// Artists (and possibly other objects) that are referenced by the tagged parts +#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(default)] +#[allow(missing_docs)] +pub struct PerformerTaggingResources { + /// List of artists tagged as performers + pub artists: Vec, +} + /// Available track charts #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ChartName { diff --git a/tests/tests.rs b/tests/tests.rs index ea56b0b..ffd77b7 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -204,6 +204,21 @@ mod artist { assert_eq!(artist.begin_date.unwrap(), date!(1977 - 12 - 31)); assert_eq!(artist.end_date_year, None); assert_eq!(artist.end_date, None); + let image = artist.artist_image.first().expect("artist image"); + assert_eq!(image.image_id, 20511); + let image_format = &image + .image_format_list + .iter() + .find(|img| img.image_format.height == 250 && img.image_format.width == 250) + .expect("image format 250px") + .image_format; + assert!( + image_format.image_url.starts_with( + "https://static.musixmatch.com/images-storage/mxmimages/1/1/5/0/2/20511_14.jpg?" + ), + "url: {}", + image_format.image_url + ); } #[rstest] @@ -290,8 +305,10 @@ mod artist { } mod track { + use std::collections::HashMap; + use super::*; - use musixmatch_inofficial::models::{ChartName, SortOrder}; + use musixmatch_inofficial::models::{ChartName, MxmEntityType, SortOrder}; #[rstest] #[case::no_translation(false, false)] @@ -311,6 +328,7 @@ mod track { "The Fame", translation_status, lang_3c, + false, ) .await .unwrap(); @@ -410,7 +428,7 @@ mod track { #[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(); + let track = mxm.await.track(track_id, true, false, false).await.unwrap(); // dbg!(&track); @@ -445,6 +463,46 @@ mod track { assert!(first_tstatus.perc >= 0.0 && first_tstatus.perc <= 1.0); } + #[rstest] + #[tokio::test] + #[test_log::test] + async fn performer(#[future] mxm: Musixmatch) { + let track = mxm + .await + .track(TrackId::TrackId(246372347), false, false, true) + .await + .unwrap(); + let perf = track.performer_tagging.expect("performer tagging"); + assert!(perf.completed); + assert!(!perf.has_unknown); + assert!(!perf.has_fan_chant); + + let artists = perf + .resources + .artists + .into_iter() + .map(|a| (a.artist_id, a)) + .collect::>(); + + assert_eq!(artists.len(), 2); + let sam_smith = &artists[&33491428]; + let kim_petras = &artists[&26796706]; + assert_eq!(sam_smith.artist_name, "Sam Smith"); + assert_eq!(sam_smith.artist_image.len(), 1); + assert_eq!(kim_petras.artist_name, "Kim Petras"); + assert_eq!(kim_petras.artist_image.len(), 1); + + for part in perf.content { + assert!(!part.snippet.trim().is_empty(), "empty snippet"); + assert_gte(part.performers.len(), 1, "part performers"); + for performer in &part.performers { + let pid = performer.fqid.expect("performer id"); + assert_eq!(pid.typ, MxmEntityType::Artist); + assert!(artists.contains_key(&pid.id)) + } + } + } + #[rstest] #[case::no_translation(false, false)] #[case::translation_2c(true, false)] @@ -457,7 +515,12 @@ mod track { ) { let track = mxm .await - .track(TrackId::Commontrack(47672612), translation_status, lang_3c) + .track( + TrackId::Commontrack(47672612), + translation_status, + lang_3c, + false, + ) .await .unwrap(); @@ -553,7 +616,7 @@ mod track { async fn from_id_missing(#[future] mxm: Musixmatch) { let err = mxm .await - .track(TrackId::TrackId(999999999999), false, false) + .track(TrackId::TrackId(999999999999), false, false, false) .await .unwrap_err(); From 4d26c4a72f617228a5e62d4d565e2c7a6f3d7f95 Mon Sep 17 00:00:00 2001 From: ThetaBot Date: Wed, 11 Dec 2024 00:05:24 +0000 Subject: [PATCH 10/38] chore(deps): update rust crate governor to 0.8.0 (#5) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0f6609d..cc76316 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,5 +63,5 @@ dotenvy = "0.15.5" tokio = { version = "1.20.4", features = ["macros"] } futures = "0.3.21" path_macro = "1.0.0" -governor = "0.7.0" +governor = "0.8.0" test-log = "0.2.16" From 2926455376ee2c24a25576528790f47713667f62 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 13 Jan 2025 05:04:21 +0100 Subject: [PATCH 11/38] test: fix tests --- src/models/track.rs | 2 +- tests/tests.rs | 64 ++++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/models/track.rs b/src/models/track.rs index d62e2f7..194c8f8 100644 --- a/src/models/track.rs +++ b/src/models/track.rs @@ -203,7 +203,7 @@ pub struct PerformerTaggingPart { pub struct Performer { /// artist / unknown #[serde(rename = "type")] - pub typ: String, + pub typ: Option, /// Fully-qualified performer ID pub fqid: Option, /// Unbekannt diff --git a/tests/tests.rs b/tests/tests.rs index ffd77b7..c360e5c 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -421,45 +421,44 @@ mod track { } #[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()))] + #[case::trackid(TrackId::TrackId(167254015))] + #[case::commontrack(TrackId::Commontrack(93933821))] + #[case::vanity(TrackId::CommontrackVanity("Nightbirde-2/Girl-in-a-Bubble".into()))] + #[case::isrc(TrackId::Isrc("QZDA41918667".into()))] + #[case::spotify(TrackId::Spotify("2roGy5AYlaJpmL9CuXj6tT".into()))] #[tokio::test] async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) { let track = mxm.await.track(track_id, true, false, 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_eq!(track.track_id, 167254015); + assert_eq!(track.track_isrc.unwrap(), "QZDA41918667"); + assert_eq!(track.track_spotify_id.unwrap(), "2roGy5AYlaJpmL9CuXj6tT"); + assert_eq!(track.track_name, "Girl in a Bubble"); + assert!(track.track_rating > 40); + assert_eq!(track.track_length, 238); 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"); + assert!(track.num_favourite > 1); + assert_eq!(track.lyrics_id.unwrap(), 25830616); + assert_eq!(track.subtitle_id.unwrap(), 34307878); + assert_eq!(track.album_id, 31842378); + assert_eq!(track.album_name, "Girl in a Bubble"); + assert_eq!(track.artist_id, 38035381); + assert_eq!(track.artist_name, "Nightbirde"); + assert_imgurl(&track.album_coverart_100x100, "/43015335.jpg"); + assert_imgurl(&track.album_coverart_350x350, "/43015335_350_350.jpg"); + assert_imgurl(&track.album_coverart_500x500, "/43015335_500_500.jpg"); + assert_eq!(track.commontrack_vanity_id, "Nightbirde-2/Girl-in-a-Bubble"); let release_date = track.first_release_date.unwrap(); - assert_eq!(release_date.date(), date!(2020 - 11 - 17)); + assert_eq!(release_date.date(), date!(2019 - 03 - 22)); 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_eq!(first_tstatus.from.as_deref(), Some("en")); assert!(first_tstatus.perc >= 0.0 && first_tstatus.perc <= 1.0); } @@ -469,7 +468,7 @@ mod track { async fn performer(#[future] mxm: Musixmatch) { let track = mxm .await - .track(TrackId::TrackId(246372347), false, false, true) + .track(TrackId::TrackId(206591653), false, false, true) .await .unwrap(); let perf = track.performer_tagging.expect("performer tagging"); @@ -485,12 +484,11 @@ mod track { .collect::>(); assert_eq!(artists.len(), 2); - let sam_smith = &artists[&33491428]; - let kim_petras = &artists[&26796706]; - assert_eq!(sam_smith.artist_name, "Sam Smith"); - assert_eq!(sam_smith.artist_image.len(), 1); - assert_eq!(kim_petras.artist_name, "Kim Petras"); - assert_eq!(kim_petras.artist_image.len(), 1); + let jhayco = &artists[&53077263]; + let bad_bunny = &artists[&33491954]; + assert_eq!(jhayco.artist_name, "Jhayco"); + assert_eq!(bad_bunny.artist_name, "Bad Bunny"); + assert_eq!(bad_bunny.artist_image.len(), 1); for part in perf.content { assert!(!part.snippet.trim().is_empty(), "empty snippet"); @@ -816,7 +814,7 @@ mod lyrics { // dbg!(&lyrics); - assert_eq!(lyrics.lyrics_id, 30126001); + assert_eq!(lyrics.lyrics_id, 36846057); assert_eq!(lyrics.lyrics_language.unwrap(), "ko"); assert_eq!(lyrics.lyrics_language_description.unwrap(), "Korean"); let copyright = lyrics.lyrics_copyright.unwrap(); From 6942d0eaaa6dfa15846c7f1a09ca4165a5a4b3c3 Mon Sep 17 00:00:00 2001 From: ThetaBot Date: Mon, 13 Jan 2025 04:09:14 +0000 Subject: [PATCH 12/38] chore(deps): update rust crate rstest to 0.24.0 (#6) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cc76316..a04c11b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ rand = "0.8.0" base64 = "0.22.0" [dev-dependencies] -rstest = { version = "0.23.0", default-features = false } +rstest = { version = "0.24.0", default-features = false } dotenvy = "0.15.5" tokio = { version = "1.20.4", features = ["macros"] } futures = "0.3.21" From 319dabeee018f8b5b633cf91e792b12fa18e7775 Mon Sep 17 00:00:00 2001 From: ThetaBot Date: Mon, 13 Jan 2025 04:14:03 +0000 Subject: [PATCH 13/38] fix(deps): update rust crate dirs to v6 (#7) --- cli/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 8b77e98..759db40 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,5 +25,5 @@ tokio = { version = "1.20.4", features = ["macros", "rt-multi-thread"] } clap = { version = "4.0.0", features = ["derive"] } anyhow = "1.0.0" rpassword = "7.0.0" -dirs = "5.0.0" +dirs = "6.0.0" serde_json = "1.0.85" From 2c22f2aa3303b49b1bdeef6f454ab84c859b8470 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 13 Jan 2025 05:20:31 +0100 Subject: [PATCH 14/38] ci: increase rate limit --- tests/tests.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/tests.rs b/tests/tests.rs index c360e5c..45f1f2f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -22,10 +22,11 @@ fn testfile>(name: P) -> PathBuf { 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( - // limit 1 request per second for CI runs - NonZeroU32::new(if std::env::var("CI").is_ok() { 1 } else { 4 }).unwrap(), - )) + RateLimiter::direct(if std::env::var("CI").is_ok() { + Quota::with_period(std::time::Duration::from_millis(1500)).unwrap() + } else { + Quota::per_second(NonZeroU32::new(4).unwrap()) + }) }); MXM_LIMITER.until_ready().await; From 62e0308dda39559daaf7f53d94e7d5b59e9d1fd4 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 16 Jan 2025 01:47:30 +0100 Subject: [PATCH 15/38] chore(release): release musixmatch-inofficial v0.2.0 --- CHANGELOG.md | 13 +++++++++++++ Cargo.toml | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d91e37..8570d22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. +## [v0.2.0](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-inofficial/v0.1.2..musixmatch-inofficial/v0.2.0) - 2025-01-16 + +### 🚀 Features + +- Add track performer tagging, artist images - ([b136bb3](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/b136bb30040dc3ee849c26ff984884e706739235)) + +### ⚙️ Miscellaneous Tasks + +- Fix clippy lints - ([26f4729](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/26f4729738536d735cb808fce8a8e466f2e82449)) +- *(deps)* Update rust crate governor to 0.8.0 (#5) - ([4d26c4a](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/4d26c4a72f617228a5e62d4d565e2c7a6f3d7f95)) +- *(deps)* Update rust crate rstest to 0.24.0 (#6) - ([6942d0e](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/6942d0eaaa6dfa15846c7f1a09ca4165a5a4b3c3)) + + ## [v0.1.2](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-inofficial/v0.1.1..musixmatch-inofficial/v0.1.2) - 2024-11-15 ### 🐛 Bug Fixes diff --git a/Cargo.toml b/Cargo.toml index a04c11b..beb0777 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "musixmatch-inofficial" -version = "0.1.2" +version = "0.2.0" rust-version = "1.70.0" edition.workspace = true authors.workspace = true @@ -23,7 +23,7 @@ keywords = ["music", "lyrics"] categories = ["api-bindings", "multimedia"] [workspace.dependencies] -musixmatch-inofficial = { version = "0.1.1", path = ".", default-features = false } +musixmatch-inofficial = { version = "0.2.0", path = ".", default-features = false } [features] default = ["default-tls"] From 7b5a6e2e5050438c7d80dce6451e11839edc56b7 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 16 Jan 2025 01:48:11 +0100 Subject: [PATCH 16/38] chore(release): release musixmatch-cli v0.3.0 --- cli/CHANGELOG.md | 19 +++++++++++++++++++ cli/Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index c8a2e23..ac7b1a4 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. +## [v0.3.0](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-cli/v0.2.0..musixmatch-cli/v0.3.0) - 2025-01-16 + +### 🚀 Features + +- Add track performer tagging, artist images - ([b136bb3](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/b136bb30040dc3ee849c26ff984884e706739235)) + +### 🐛 Bug Fixes + +- *(deps)* Update rust crate thiserror to v2 (#4) - ([6a6ced1](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/6a6ced16224c6ef3d05eb6ebd0aa0bdc40a34684)) +- *(deps)* Update rust crate dirs to v6 (#7) - ([319dabe](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/319dabeee018f8b5b633cf91e792b12fa18e7775)) + +### ⚙️ Miscellaneous Tasks + +- *(deps)* Update rust crate rstest to 0.23.0 (#2) - ([5ef76f5](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/5ef76f5a6b2a3b243f847cf86e72ebe176819d7a)) +- *(deps)* Update rust crate governor to 0.7.0 (#3) - ([4bfcb79](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/4bfcb791733ce5ebd9d4e074c64eb23e9a768fc6)) +- *(deps)* Update rust crate governor to 0.8.0 (#5) - ([4d26c4a](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/4d26c4a72f617228a5e62d4d565e2c7a6f3d7f95)) +- *(deps)* Update rust crate rstest to 0.24.0 (#6) - ([6942d0e](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/6942d0eaaa6dfa15846c7f1a09ca4165a5a4b3c3)) + + ## [v0.2.0](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-cli/v0.1.0..musixmatch-cli/v0.2.0) - 2024-08-18 ### 🚀 Features diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 759db40..6228dda 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "musixmatch-cli" -version = "0.2.0" +version = "0.3.0" rust-version = "1.70.0" edition.workspace = true authors.workspace = true From bf68f94682773a778c5ce21cd177886ff6988126 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 3 Feb 2025 03:27:45 +0100 Subject: [PATCH 17/38] ci: renovate: preserveSemverRanges --- .gitea/workflows/renovate.yaml | 2 +- renovate.json | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/renovate.yaml b/.gitea/workflows/renovate.yaml index e91c636..013b185 100644 --- a/.gitea/workflows/renovate.yaml +++ b/.gitea/workflows/renovate.yaml @@ -17,7 +17,7 @@ jobs: renovate: runs-on: docker container: - image: renovate/renovate:latest + image: renovate/renovate:39 steps: - name: Load renovate repo cache diff --git a/renovate.json b/renovate.json index 3ff20b1..1ec2687 100644 --- a/renovate.json +++ b/renovate.json @@ -1,8 +1,6 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:best-practices" - ], + "extends": ["config:best-practices", ":preserveSemverRanges"], "semanticCommits": "enabled", "automerge": true, "automergeStrategy": "squash", From 0bb886adab2593c81667105aca3c22b3c191b774 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 22 Feb 2025 23:01:18 +0000 Subject: [PATCH 18/38] ci: disable renovate --- .gitea/workflows/{renovate.yaml => renovate.yaml.bak} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .gitea/workflows/{renovate.yaml => renovate.yaml.bak} (100%) diff --git a/.gitea/workflows/renovate.yaml b/.gitea/workflows/renovate.yaml.bak similarity index 100% rename from .gitea/workflows/renovate.yaml rename to .gitea/workflows/renovate.yaml.bak From c90bfc647c6b62d30530fa97ec830c1e16d7a75c Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 3 Feb 2025 03:47:15 +0100 Subject: [PATCH 19/38] test: update translation track id --- tests/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests.rs b/tests/tests.rs index 45f1f2f..02ebc61 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -336,7 +336,7 @@ mod track { // dbg!(&track); - assert_eq!(track.track_id, 85213841); + assert_eq!(track.track_id, 9060927); // assert_eq!( // track.track_mbid.unwrap(), // "080975b0-39b1-493c-ae64-5cb3292409bb" From 87859e629f3c236ba450872b29beb7876be7ef0b Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 3 Apr 2025 13:41:30 +0200 Subject: [PATCH 20/38] chore(deps): update rust crate governor to 0.10.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index beb0777..3fd6b92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,5 +63,5 @@ dotenvy = "0.15.5" tokio = { version = "1.20.4", features = ["macros"] } futures = "0.3.21" path_macro = "1.0.0" -governor = "0.8.0" +governor = "0.10.0" test-log = "0.2.16" From a3f2ffc5d99ddddf777b4de306bd215bd3bbf5ce Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 3 Apr 2025 13:42:36 +0200 Subject: [PATCH 21/38] chore(deps): update rust crate rstest to 0.25.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3fd6b92..bc489af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ rand = "0.8.0" base64 = "0.22.0" [dev-dependencies] -rstest = { version = "0.24.0", default-features = false } +rstest = { version = "0.25.0", default-features = false } dotenvy = "0.15.5" tokio = { version = "1.20.4", features = ["macros"] } futures = "0.3.21" From 7c325c4af779e32059680c1cfb874f83896d7649 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 4 Apr 2025 13:25:02 +0200 Subject: [PATCH 22/38] chore(deps): update rust crate rand to 0.9.0 --- Cargo.toml | 2 +- src/lib.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bc489af..e3b3518 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ time = { version = "0.3.10", features = [ ] } hmac = "0.12.0" sha1 = "0.10.0" -rand = "0.8.0" +rand = "0.9.0" base64 = "0.22.0" [dev-dependencies] diff --git a/src/lib.rs b/src/lib.rs index 0d14784..8a6f508 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -449,20 +449,20 @@ impl Musixmatch { } fn random_guid() -> String { - let mut rng = rand::thread_rng(); - let n = rng.gen::(); + let mut rng = rand::rng(); + let n = rng.random::(); format!("{:016x}", n) } fn random_uuid() -> String { - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); format!( "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}", - rng.gen::(), - rng.gen::(), - rng.gen::(), - rng.gen::(), - rng.gen::() & 0xffffffffffff, + rng.random::(), + rng.random::(), + rng.random::(), + rng.random::(), + rng.random::() & 0xffffffffffff, ) } From 6f90033cf4284eff5c12a30aafb21943c1575b92 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 4 Apr 2025 13:55:44 +0200 Subject: [PATCH 23/38] fix: parsing unset has_fan_chant field --- src/models/track.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/models/track.rs b/src/models/track.rs index 194c8f8..12831ed 100644 --- a/src/models/track.rs +++ b/src/models/track.rs @@ -167,11 +167,14 @@ pub struct TrackPerformerTagging { /// Format: `mxm:<16 byte hex>` pub user_id: String, /// True if the lyrics are completely tagged + #[serde(default)] pub completed: bool, - /// True if the lyrics + /// True if the lyrics have unknown performers + #[serde(default)] pub has_unknown: bool, /// True if the lyrics contain parts that are intended to be sung by the /// audience during concerts + #[serde(default)] pub has_fan_chant: bool, /// List of tagged lyrics parts #[serde(default)] From 4a46e7bb1d83c6261660d403c009cdb640b301d7 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 4 Apr 2025 13:58:45 +0200 Subject: [PATCH 24/38] docs: fix docs --- src/models/track.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/models/track.rs b/src/models/track.rs index 12831ed..bfb1980 100644 --- a/src/models/track.rs +++ b/src/models/track.rs @@ -160,7 +160,6 @@ pub struct TrackLyricsTranslationStatus { /// Lyrics parts marked with the performer who is singing them #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[allow(missing_docs)] pub struct TrackPerformerTagging { /// Musixmatch user ID of the user who added the performer tags /// @@ -186,15 +185,14 @@ pub struct TrackPerformerTagging { /// Performer-tagged lyrics part #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[allow(missing_docs)] pub struct PerformerTaggingPart { /// Part of the lyrics text /// /// Includes whitespace (spaces and newline characters). pub snippet: String, - /// Unbekannt + /// Unknown /// - /// 0-3 + /// Values: 0-3 pub position: u32, /// List of performers singing this part pub performers: Vec, @@ -202,7 +200,6 @@ pub struct PerformerTaggingPart { /// Lyrics performer #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[allow(missing_docs)] pub struct Performer { /// artist / unknown #[serde(rename = "type")] @@ -222,7 +219,6 @@ pub struct Performer { /// Artists (and possibly other objects) that are referenced by the tagged parts #[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(default)] -#[allow(missing_docs)] pub struct PerformerTaggingResources { /// List of artists tagged as performers pub artists: Vec, From 04a0544ad5b56c1e5c1fcec8214f7f8a4cd9fe81 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 4 Apr 2025 14:07:23 +0200 Subject: [PATCH 25/38] test: fix tests --- tests/tests.rs | 81 +++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/tests/tests.rs b/tests/tests.rs index 02ebc61..7a45f64 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -324,9 +324,9 @@ mod track { let track = mxm .await .matcher_track( - "Poker Face", - "Lady Gaga", - "The Fame", + "Unter der Oberfläche", + "Silbermond", + "Himmel auf", translation_status, lang_3c, false, @@ -336,55 +336,58 @@ mod track { // dbg!(&track); - assert_eq!(track.track_id, 9060927); - // assert_eq!( - // track.track_mbid.unwrap(), - // "080975b0-39b1-493c-ae64-5cb3292409bb" - // ); - // assert_eq!(track.track_isrc.unwrap(), "USUM70824409"); + assert_eq!(track.track_id, 17633247); + assert_eq!( + track.track_mbid.unwrap(), + "1b97675a-537e-4599-82de-b4ef801e4a69" + ); + assert_eq!(track.track_isrc.unwrap(), "DEE861200084"); assert!( track.commontrack_isrcs[0] .iter() - .any(|isrc| isrc == "USUM70824409"), + .any(|isrc| isrc == "DEE861200084"), "commontrack_isrcs: {:?}", &track.commontrack_isrcs[0], ); - assert_eq!(track.track_spotify_id.unwrap(), "1QV6tiMFM6fSOKOGLMHYYg"); + assert_eq!(track.track_spotify_id.unwrap(), "4kebfr96W2JTvb7xjf4lDV"); assert!( track .commontrack_spotify_ids .iter() - .any(|spid| spid == "1QV6tiMFM6fSOKOGLMHYYg"), + .any(|spid| spid == "4kebfr96W2JTvb7xjf4lDV"), "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_eq!(track.track_name, "Unter der Oberfläche"); + assert!(track.track_rating > 30); + assert_eq!(track.commontrack_id, 10514077); assert!(!track.instrumental); - assert!(track.explicit); + 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!(!track.has_track_structure); + assert!(track.num_favourite > 10); + assert_eq!(track.lyrics_id.unwrap(), 7401716); + assert_eq!(track.subtitle_id.unwrap(), 35487435); + assert_eq!(track.album_id, 14122870); + assert_eq!(track.album_name, "Himmel auf"); + assert_eq!(track.artist_id, 84849); assert_eq!( track.artist_mbid.unwrap(), - "650e7db6-b795-4eb5-a702-5ea2fc46c848" + "34d42823-6b56-4861-a675-1565bf40d557" + ); + assert_eq!(track.artist_name, "Silbermond"); + assert_imgurl(&track.album_coverart_100x100, "/30913494.jpg"); + assert_imgurl(&track.album_coverart_350x350, "/30913494_350_350.jpg"); + assert_imgurl(&track.album_coverart_500x500, "/30913494_500_500.jpg"); + assert_imgurl(&track.album_coverart_800x800, "/30913494_800_800.jpg"); + assert_eq!( + track.commontrack_vanity_id, + "Silbermond/Unter-der-Oberfläche" ); - 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)); + assert_eq!(first_release.date(), date!(2012 - 3 - 23)); + assert!(track.updated_time > datetime!(2013-4-26 13:11:50 UTC)); let first_pri_genre = &track.primary_genres.music_genre_list[0].music_genre; assert_eq!(first_pri_genre.music_genre_id, 14); @@ -393,23 +396,13 @@ mod track { 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") + tl.from.as_deref() == Some("deu") } else { - tl.from.as_deref() == Some("en") + tl.from.as_deref() == Some("de") }) && tl.perc >= 0.0 && tl.perc <= 1.0 }), From f73dcdd13488eea9ff9d043ba46e8c647e0881a3 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 4 Apr 2025 14:12:48 +0200 Subject: [PATCH 26/38] chore(release): release musixmatch-inofficial v0.2.1 --- CHANGELOG.md | 17 +++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8570d22..4f099be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. +## [v0.2.1](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-inofficial/v0.2.0..musixmatch-inofficial/v0.2.1) - 2025-04-04 + +### 🐛 Bug Fixes + +- Parsing unset has_fan_chant field - ([6f90033](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/6f90033cf4284eff5c12a30aafb21943c1575b92)) + +### 📚 Documentation + +- Fix docs - ([4a46e7b](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/4a46e7bb1d83c6261660d403c009cdb640b301d7)) + +### ⚙️ Miscellaneous Tasks + +- *(deps)* Update rust crate governor to 0.10.0 - ([87859e6](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/87859e629f3c236ba450872b29beb7876be7ef0b)) +- *(deps)* Update rust crate rstest to 0.25.0 - ([a3f2ffc](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/a3f2ffc5d99ddddf777b4de306bd215bd3bbf5ce)) +- *(deps)* Update rust crate rand to 0.9.0 - ([7c325c4](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/7c325c4af779e32059680c1cfb874f83896d7649)) + + ## [v0.2.0](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-inofficial/v0.1.2..musixmatch-inofficial/v0.2.0) - 2025-01-16 ### 🚀 Features diff --git a/Cargo.toml b/Cargo.toml index e3b3518..c1071e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "musixmatch-inofficial" -version = "0.2.0" +version = "0.2.1" rust-version = "1.70.0" edition.workspace = true authors.workspace = true From 59dee61a2fdbda6a7643aa209b9baaf25a6de0f8 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 9 Aug 2025 15:24:02 +0200 Subject: [PATCH 27/38] fix: update signature secret --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8a6f508..667435a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,7 @@ const YMD_FORMAT: &[time::format_description::FormatItem] = const APP_ID: &str = "android-player-v1.0"; const API_URL: &str = "https://apic.musixmatch.com/ws/1.1/"; -const SIGNATURE_SECRET: &[u8; 20] = b"967Pn4)N3&R_GBg5$b('"; +const SIGNATURE_SECRET: &[u8; 29] = b"mNdca@6W7TeEcFn6*3.s97sJ*yPMd"; const DEFAULT_UA: &str = "Dalvik/2.1.0 (Linux; U; Android 13; Pixel 6 Build/T3B2.230316.003)"; const DEFAULT_BRAND: &str = "Google"; @@ -503,6 +503,6 @@ mod tests { fn t_sign_url() { let mut url = Url::parse("https://apic.musixmatch.com/ws/1.1/track.subtitle.get?app_id=android-player-v1.0&usertoken=22092860c49e8d783b569a7bd847cd5b289bbec306f8a0bb2d3771&format=json&track_spotify_id=7Ga0ByppmSXWuKXdsD8JGL&subtitle_format=mxm").unwrap(); sign_url_with_date(&mut url, datetime!(2022-09-28 0:00 UTC)); - assert_eq!(url.as_str(), "https://apic.musixmatch.com/ws/1.1/track.subtitle.get?app_id=android-player-v1.0&usertoken=22092860c49e8d783b569a7bd847cd5b289bbec306f8a0bb2d3771&format=json&track_spotify_id=7Ga0ByppmSXWuKXdsD8JGL&subtitle_format=mxm&signature=cvXbedVvGneT7o4k8QG6jfk9pAM%3D%0A&signature_protocol=sha1") + assert_eq!(url.as_str(), "https://apic.musixmatch.com/ws/1.1/track.subtitle.get?app_id=android-player-v1.0&usertoken=22092860c49e8d783b569a7bd847cd5b289bbec306f8a0bb2d3771&format=json&track_spotify_id=7Ga0ByppmSXWuKXdsD8JGL&subtitle_format=mxm&signature=78ywxkeXlazpevI%2BbD8E3YluLPc%3D%0A&signature_protocol=sha1") } } From 74d5359547e0b3ffa75f5f7384ed3b7ab0e2ea11 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 9 Aug 2025 15:24:44 +0200 Subject: [PATCH 28/38] feat!: removed artist_related endpoint (discontinued) --- src/apis/artist_api.rs | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/src/apis/artist_api.rs b/src/apis/artist_api.rs index 1581a5f..024c6bc 100644 --- a/src/apis/artist_api.rs +++ b/src/apis/artist_api.rs @@ -26,40 +26,6 @@ impl Musixmatch { Ok(artist_body.artist) } - /// Get a list of artists somehow related to the one specified by its ID. - /// - /// # Parameters - /// - `id`: [Artist ID](crate::models::ArtistId) - /// - `page_size`: Define the page size for paginated results. Range is 1 to 100. - /// - `page`: Define the page number for paginated results, starting from 1. - /// - /// # Reference - /// - pub async fn artist_related( - &self, - id: ArtistId<'_>, - page_size: u8, - page: u32, - ) -> Result> { - let mut url = self.new_url("artist.related.get"); - { - let mut url_query = url.query_pairs_mut(); - - let id_param = id.to_param(); - url_query.append_pair(id_param.0, &id_param.1); - url_query.append_pair("page_size", &page_size.to_string()); - url_query.append_pair("page", &page.to_string()); - url_query.finish(); - } - - let artist_list_body = self.execute_get_request::(&url).await?; - Ok(artist_list_body - .artist_list - .into_iter() - .map(|a| a.artist) - .collect()) - } - /// Search for artists in the Musixmatch database. /// /// # Parameters From 528c6f7eb10319bf6bb99ddb0239c61fce7e23e9 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 9 Aug 2025 15:25:23 +0200 Subject: [PATCH 29/38] test: fix tests --- src/apis/track_api.rs | 2 +- tests/tests.rs | 63 ++++++++++++------------------------------- 2 files changed, 18 insertions(+), 47 deletions(-) diff --git a/src/apis/track_api.rs b/src/apis/track_api.rs index 537c69b..1c25975 100644 --- a/src/apis/track_api.rs +++ b/src/apis/track_api.rs @@ -226,7 +226,7 @@ impl Musixmatch { /// /// # Reference /// - pub fn track_search(&self) -> TrackSearchQuery { + pub fn track_search(&self) -> TrackSearchQuery<'_> { TrackSearchQuery { mxm: self.clone(), q_track: None, diff --git a/tests/tests.rs b/tests/tests.rs index 7a45f64..492f957 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -63,7 +63,7 @@ mod album { "6c3cf9d8-88a8-43ed-850b-55813f01e451" ); assert_eq!(album.album_name, "Gangnam Style (강남스타일)"); - assert!(album.album_rating > 20); + assert!(album.album_rating > 12); 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); @@ -184,7 +184,7 @@ mod artist { artist.artist_name_translation_list ); assert_eq!(artist.artist_country.unwrap(), "KR"); - assert!(artist.artist_rating > 50); + assert!(artist.artist_rating > 40); 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); @@ -234,30 +234,6 @@ mod artist { 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) { @@ -324,7 +300,7 @@ mod track { let track = mxm .await .matcher_track( - "Unter der Oberfläche", + "Du fehlst hier", "Silbermond", "Himmel auf", translation_status, @@ -336,39 +312,37 @@ mod track { // dbg!(&track); - assert_eq!(track.track_id, 17633247); + assert_eq!(track.track_id, 17633259); assert_eq!( track.track_mbid.unwrap(), - "1b97675a-537e-4599-82de-b4ef801e4a69" + "746af8c4-703e-4461-a40d-74ecdbcd755e" ); - assert_eq!(track.track_isrc.unwrap(), "DEE861200084"); + assert_eq!(track.track_isrc.unwrap(), "DEE861200095"); assert!( track.commontrack_isrcs[0] .iter() - .any(|isrc| isrc == "DEE861200084"), + .any(|isrc| isrc == "DEE861200095"), "commontrack_isrcs: {:?}", &track.commontrack_isrcs[0], ); - assert_eq!(track.track_spotify_id.unwrap(), "4kebfr96W2JTvb7xjf4lDV"); + assert_eq!(track.track_spotify_id.unwrap(), "3wZwbYSozyMLnJJcT3e51Q"); assert!( track .commontrack_spotify_ids .iter() - .any(|spid| spid == "4kebfr96W2JTvb7xjf4lDV"), + .any(|spid| spid == "7lsi0kAFXHUCRjJzKlCZdA"), "commontrack_spotify_ids: {:?}", track.commontrack_spotify_ids, ); - assert_eq!(track.track_name, "Unter der Oberfläche"); - assert!(track.track_rating > 30); - assert_eq!(track.commontrack_id, 10514077); + assert_eq!(track.track_name, "Du fehlst hier"); + assert!(track.track_rating > 25); + assert_eq!(track.commontrack_id, 10514065); assert!(!track.instrumental); assert!(!track.explicit); + assert!(track.has_lyrics); assert!(track.has_subtitles); - assert!(track.has_richsync); - assert!(!track.has_track_structure); - assert!(track.num_favourite > 10); - assert_eq!(track.lyrics_id.unwrap(), 7401716); - assert_eq!(track.subtitle_id.unwrap(), 35487435); + assert_eq!(track.lyrics_id.unwrap(), 34727375); + assert_eq!(track.subtitle_id.unwrap(), 15014656); assert_eq!(track.album_id, 14122870); assert_eq!(track.album_name, "Himmel auf"); assert_eq!(track.artist_id, 84849); @@ -381,13 +355,10 @@ mod track { assert_imgurl(&track.album_coverart_350x350, "/30913494_350_350.jpg"); assert_imgurl(&track.album_coverart_500x500, "/30913494_500_500.jpg"); assert_imgurl(&track.album_coverart_800x800, "/30913494_800_800.jpg"); - assert_eq!( - track.commontrack_vanity_id, - "Silbermond/Unter-der-Oberfläche" - ); + assert_eq!(track.commontrack_vanity_id, "Silbermond/Du-fehlst-hier"); let first_release = track.first_release_date.unwrap(); assert_eq!(first_release.date(), date!(2012 - 3 - 23)); - assert!(track.updated_time > datetime!(2013-4-26 13:11:50 UTC)); + assert!(track.updated_time > datetime!(2024-2-22 11:00 UTC)); let first_pri_genre = &track.primary_genres.music_genre_list[0].music_genre; assert_eq!(first_pri_genre.music_genre_id, 14); From bfe6fec115b9ba3b58f1e949ac7b583a95041f8f Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 9 Aug 2025 18:34:40 +0200 Subject: [PATCH 30/38] fix: clippy lints --- Cargo.toml | 2 +- cli/src/main.rs | 2 +- src/lib.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c1071e0..0f55dc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ rand = "0.9.0" base64 = "0.22.0" [dev-dependencies] -rstest = { version = "0.25.0", default-features = false } +rstest = { version = "0.26.0", default-features = false } dotenvy = "0.15.5" tokio = { version = "1.20.4", features = ["macros"] } futures = "0.3.21" diff --git a/cli/src/main.rs b/cli/src/main.rs index c10e2a1..fe9b844 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -175,7 +175,7 @@ async fn main() { let cli = Cli::parse(); run(cli).await.unwrap_or_else(|e| { - println!("Error: {}", e); + eprintln!("Error: {}", e); std::process::exit(1); }); } diff --git a/src/lib.rs b/src/lib.rs index 667435a..435c806 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -247,7 +247,7 @@ impl Musixmatch { // Get user token // The get_token endpoint seems to be rate limited for 2 requests per minute let mut url = Url::parse_with_params( - &format!("{}{}", API_URL, "token.get"), + &format!("{API_URL}token.get"), &[ ("adv_id", adv_id.as_str()), ("root", "0"), @@ -374,7 +374,7 @@ impl Musixmatch { fn new_url(&self, endpoint: &str) -> reqwest::Url { Url::parse_with_params( - &format!("{}{}", API_URL, endpoint), + &format!("{API_URL}{endpoint}"), &[("app_id", APP_ID), ("format", "json")], ) .unwrap() @@ -451,7 +451,7 @@ impl Musixmatch { fn random_guid() -> String { let mut rng = rand::rng(); let n = rng.random::(); - format!("{:016x}", n) + format!("{n:016x}") } fn random_uuid() -> String { @@ -468,7 +468,7 @@ fn random_uuid() -> String { fn new_url_from_token(endpoint: &str, usertoken: &str) -> reqwest::Url { Url::parse_with_params( - &format!("{}{}", API_URL, endpoint), + &format!("{API_URL}{endpoint}"), &[ ("app_id", APP_ID), ("usertoken", usertoken), From 09c6004ed13c2f9039490c083b0e6b61dccaa884 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 9 Aug 2025 18:36:45 +0200 Subject: [PATCH 31/38] fix: clippy lints (2) --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 435c806..448d8d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -343,7 +343,7 @@ impl Musixmatch { match serde_json::to_string(&to_store) { Ok(json) => storage.write(&json), - Err(e) => error!("Could not serialize session. Error: {}", e), + Err(e) => error!("Could not serialize session. Error: {e}"), } } } @@ -355,7 +355,7 @@ impl Musixmatch { .and_then(|json| match serde_json::from_str::(&json) { Ok(session) => Some(session), Err(e) => { - error!("Could not deserialize session. Error: {}", e); + error!("Could not deserialize session. Error: {e}"); None } }) From 39bbd5070a62d865c0e276904047bb687f1ba8a8 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 9 Aug 2025 18:39:20 +0200 Subject: [PATCH 32/38] fix: clippy lints (3) --- cli/src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index fe9b844..84e26b9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -175,7 +175,7 @@ async fn main() { let cli = Cli::parse(); run(cli).await.unwrap_or_else(|e| { - eprintln!("Error: {}", e); + eprintln!("Error: {e}"); std::process::exit(1); }); } @@ -251,7 +251,7 @@ async fn run(cli: Cli) -> Result<()> { } eprintln!(); - println!("{}", lyrics_body); + println!("{lyrics_body}"); } Commands::Subtitles { ident, @@ -312,7 +312,7 @@ async fn run(cli: Cli) -> Result<()> { bail!("subtitle format {format:?} cant be translated") } }; - println!("{}", res); + println!("{res}"); } else { eprintln!(); println!("{}", subtitles.subtitle_body); @@ -471,7 +471,7 @@ async fn get_track( } fn input(prompt: &str) -> String { - print!("{}", prompt); + print!("{prompt}"); stdout().flush().expect("Failed to flush stdout!"); @@ -483,7 +483,7 @@ fn input(prompt: &str) -> String { } fn input_pwd(prompt: &str) -> String { - print!("{}", prompt); + print!("{prompt}"); stdout().flush().expect("Failed to flush stdout!"); rpassword::read_password().expect("Failed to read password") From 80c3c38c414b32013e6b1e7304b60e4ff0572f92 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 9 Aug 2025 23:45:59 +0200 Subject: [PATCH 33/38] ci: use cargo-nextest --- .gitea/workflows/ci.yaml | 21 ++++++++++++++++++++- Justfile | 2 +- tests/tests.rs | 18 +++++++++--------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index d03eb50..dc15c86 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -27,6 +27,25 @@ jobs: run: cargo clippy --all -- -D warnings - name: 🧪 Test - run: cargo test --workspace + run: cargo nextest run --config-file ~/.config/nextest.toml --profile ci --retries 2 --workspace env: ALL_PROXY: "http://warpproxy:8124" + + - name: Move test report + if: always() + run: mv target/nextest/ci/junit.xml junit.xml || true + + - name: 💌 Upload test report + if: always() + uses: https://code.forgejo.org/forgejo/upload-artifact@v4 + with: + name: test + path: | + junit.xml + + - name: 🔗 Artifactview PR comment + if: ${{ always() && github.event_name == 'pull_request' }} + run: | + if [[ "$GITEA_ACTIONS" == "true" ]]; then RUN_NUMBER="$GITHUB_RUN_NUMBER"; else RUN_NUMBER="$GITHUB_RUN_ID"; fi + curl -SsL --fail-with-body -w "\n" -X POST https://av.thetadev.de/.well-known/api/prComment -H "Content-Type: application/json" \ + --data '{"url": "'"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$RUN_NUMBER"'", "pr": ${{ github.event.number }}, "artifact_titles": {"test":"🧪 Test report"}, "artifact_paths": {"test":"/junit.xml?viewer=1"}}' diff --git a/Justfile b/Justfile index 7456066..a97e4a5 100644 --- a/Justfile +++ b/Justfile @@ -1,5 +1,5 @@ test: - cargo test + cargo nextest run --workspace --no-fail-fast --retries 1 release crate="musixmatch-inofficial": #!/usr/bin/env bash diff --git a/tests/tests.rs b/tests/tests.rs index 492f957..ebd9669 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -120,7 +120,7 @@ mod album { .await .unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, Error::NotFound), "got: {err:?}"); } #[rstest] @@ -145,7 +145,7 @@ mod album { .await .unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, Error::NotFound), "got: {err:?}"); } #[rstest] @@ -231,7 +231,7 @@ mod artist { .await .unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, Error::NotFound), "got: {err:?}"); } #[rstest] @@ -583,7 +583,7 @@ mod track { .await .unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, Error::NotFound), "got: {err:?}"); } #[rstest] @@ -636,7 +636,7 @@ mod track { .await .unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, Error::NotFound), "got: {err:?}"); } #[rstest] @@ -820,7 +820,7 @@ mod lyrics { .await .unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, Error::NotFound), "got: {err:?}"); } #[rstest] @@ -967,7 +967,7 @@ mod subtitles { .await .unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, Error::NotFound), "got: {err:?}"); } /// This track has not been synced @@ -985,7 +985,7 @@ mod subtitles { .await .unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, Error::NotFound), "got: {err:?}"); } /// Try to get subtitles with wrong length parameter @@ -1003,7 +1003,7 @@ mod subtitles { .await .unwrap_err(); - assert!(matches!(err, Error::NotFound)); + assert!(matches!(err, Error::NotFound), "got: {err:?}"); } #[rstest] From e087752dd0d331093a3434e18b5ff276bf4dc7c1 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 8 Dec 2025 10:26:47 +0100 Subject: [PATCH 34/38] fix: remove serde::__private::fmt --- src/api_model.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/api_model.rs b/src/api_model.rs index 1eb4760..d69aaa8 100644 --- a/src/api_model.rs +++ b/src/api_model.rs @@ -444,10 +444,7 @@ pub mod optional_date { impl Visitor<'_> for OptionalDateVisitor { type Value = Option; - fn expecting( - &self, - formatter: &mut serde::__private::fmt::Formatter, - ) -> serde::__private::fmt::Result { + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("date or empty string") } @@ -504,10 +501,7 @@ pub mod optional_datetime { impl Visitor<'_> for OptionalDateVisitor { type Value = Option; - fn expecting( - &self, - formatter: &mut serde::__private::fmt::Formatter, - ) -> serde::__private::fmt::Result { + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("timestamp or empty string") } From c6b33829482149d73f341d3dc51093df9d6917bc Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 8 Dec 2025 10:39:26 +0100 Subject: [PATCH 35/38] test: fix tests --- .gitea/workflows/ci.yaml | 2 +- Justfile | 2 +- tests/tests.rs | 34 +++++++++++++++++++--------------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index dc15c86..5ebadac 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -27,7 +27,7 @@ jobs: run: cargo clippy --all -- -D warnings - name: 🧪 Test - run: cargo nextest run --config-file ~/.config/nextest.toml --profile ci --retries 2 --workspace + run: cargo nextest run --config-file ~/.config/nextest.toml --profile ci --retries 2 -j 1 --workspace env: ALL_PROXY: "http://warpproxy:8124" diff --git a/Justfile b/Justfile index a97e4a5..f288b43 100644 --- a/Justfile +++ b/Justfile @@ -1,5 +1,5 @@ test: - cargo nextest run --workspace --no-fail-fast --retries 1 + cargo nextest run --workspace --no-fail-fast --retries 1 -j 1 release crate="musixmatch-inofficial": #!/usr/bin/env bash diff --git a/tests/tests.rs b/tests/tests.rs index ebd9669..f514b17 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,7 +1,7 @@ use std::{ - num::NonZeroU32, path::{Path, PathBuf}, sync::LazyLock, + time::Duration, }; use governor::{DefaultDirectRateLimiter, Quota, RateLimiter}; @@ -23,9 +23,9 @@ async fn mxm() -> Musixmatch { static LOGIN_LOCK: tokio::sync::OnceCell<()> = tokio::sync::OnceCell::const_new(); static MXM_LIMITER: LazyLock = LazyLock::new(|| { RateLimiter::direct(if std::env::var("CI").is_ok() { - Quota::with_period(std::time::Duration::from_millis(1500)).unwrap() + Quota::with_period(Duration::from_millis(2000)).unwrap() } else { - Quota::per_second(NonZeroU32::new(4).unwrap()) + Quota::with_period(Duration::from_millis(500)).unwrap() }) }); @@ -52,19 +52,19 @@ mod album { #[rstest] #[case::id(AlbumId::AlbumId(14248253))] - #[case::musicbrainz(AlbumId::Musicbrainz("6c3cf9d8-88a8-43ed-850b-55813f01e451"))] + // #[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_mbid.expect("mbid"), + // "6c3cf9d8-88a8-43ed-850b-55813f01e451" + // ); assert_eq!(album.album_name, "Gangnam Style (강남스타일)"); assert!(album.album_rating > 12); - assert_eq!(album.album_track_count, 0); + 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); @@ -162,7 +162,7 @@ mod artist { #[rstest] #[case::id(ArtistId::ArtistId(410698))] - #[case::musicbrainz(ArtistId::Musicbrainz("f99b7d67-4e63-4678-aa66-4c6ac0f7d24a"))] + // #[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(); @@ -170,10 +170,10 @@ mod artist { // dbg!(&artist); assert_eq!(artist.artist_id, 410698); - assert_eq!( - artist.artist_mbid.unwrap(), - "f99b7d67-4e63-4678-aa66-4c6ac0f7d24a" - ); + // assert_eq!( + // artist.artist_mbid.expect("mbid"), + // "f99b7d67-4e63-4678-aa66-4c6ac0f7d24a" + // ); assert_eq!(artist.artist_name, "PSY"); assert!( artist.artist_name_translation_list.iter().any(|tl| { @@ -184,7 +184,11 @@ mod artist { artist.artist_name_translation_list ); assert_eq!(artist.artist_country.unwrap(), "KR"); - assert!(artist.artist_rating > 40); + assert!( + artist.artist_rating > 10, + "rating: {}", + artist.artist_rating + ); 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); From 4782f4905e1eaa85d448766f2ab1ec81478fa8ba Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 8 Dec 2025 10:47:12 +0100 Subject: [PATCH 36/38] test: add back artist_mbid assertion --- tests/tests.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/tests.rs b/tests/tests.rs index f514b17..70d1fc8 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -56,6 +56,7 @@ mod album { #[tokio::test] async fn by_id(#[case] album_id: AlbumId<'_>, #[future] mxm: Musixmatch) { let album = mxm.await.album(album_id).await.unwrap(); + // dbg!(&album); assert_eq!(album.album_id, 14248253); // assert_eq!( @@ -170,10 +171,10 @@ mod artist { // dbg!(&artist); assert_eq!(artist.artist_id, 410698); - // assert_eq!( - // artist.artist_mbid.expect("mbid"), - // "f99b7d67-4e63-4678-aa66-4c6ac0f7d24a" - // ); + assert_eq!( + artist.artist_mbid.expect("mbid"), + "f99b7d67-4e63-4678-aa66-4c6ac0f7d24a" + ); assert_eq!(artist.artist_name, "PSY"); assert!( artist.artist_name_translation_list.iter().any(|tl| { From f6fe442e3c23a6bc23a6a66776c69579fcb6307d Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 8 Dec 2025 10:48:24 +0100 Subject: [PATCH 37/38] chore(release): release musixmatch-inofficial v0.3.0 --- CHANGELOG.md | 14 ++++++++++++++ Cargo.toml | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f099be..0801b22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. +## [v0.3.0](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-inofficial/v0.2.1..musixmatch-inofficial/v0.3.0) - 2025-12-08 + +### 🚀 Features + +- [**breaking**] Removed artist_related endpoint (discontinued) - ([74d5359](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/74d5359547e0b3ffa75f5f7384ed3b7ab0e2ea11)) + +### 🐛 Bug Fixes + +- Update signature secret - ([59dee61](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/59dee61a2fdbda6a7643aa209b9baaf25a6de0f8)) +- Clippy lints - ([bfe6fec](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/bfe6fec115b9ba3b58f1e949ac7b583a95041f8f)) +- Clippy lints (2) - ([09c6004](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/09c6004ed13c2f9039490c083b0e6b61dccaa884)) +- Remove serde::__private::fmt - ([e087752](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/e087752dd0d331093a3434e18b5ff276bf4dc7c1)) + + ## [v0.2.1](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-inofficial/v0.2.0..musixmatch-inofficial/v0.2.1) - 2025-04-04 ### 🐛 Bug Fixes diff --git a/Cargo.toml b/Cargo.toml index 0f55dc4..0d3ead1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "musixmatch-inofficial" -version = "0.2.1" +version = "0.3.0" rust-version = "1.70.0" edition.workspace = true authors.workspace = true @@ -23,7 +23,7 @@ keywords = ["music", "lyrics"] categories = ["api-bindings", "multimedia"] [workspace.dependencies] -musixmatch-inofficial = { version = "0.2.0", path = ".", default-features = false } +musixmatch-inofficial = { version = "0.3.0", path = ".", default-features = false } [features] default = ["default-tls"] From 5e5942beb8ac9c5251fe94a3c74f722974544220 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 8 Dec 2025 10:49:57 +0100 Subject: [PATCH 38/38] chore(release): release musixmatch-cli v0.3.1 --- cli/CHANGELOG.md | 15 +++++++++++++++ cli/Cargo.toml | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index ac7b1a4..9c882bc 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file. +## [v0.3.1](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-cli/v0.3.0..musixmatch-cli/v0.3.1) - 2025-12-08 + +### 🐛 Bug Fixes + +- Clippy lints - ([bfe6fec](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/bfe6fec115b9ba3b58f1e949ac7b583a95041f8f)) +- Clippy lints (3) - ([39bbd50](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/39bbd5070a62d865c0e276904047bb687f1ba8a8)) + +### ⚙️ Miscellaneous Tasks + +- *(deps)* Update rust crate governor to 0.10.0 - ([87859e6](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/87859e629f3c236ba450872b29beb7876be7ef0b)) +- *(deps)* Update rust crate rstest to 0.25.0 - ([a3f2ffc](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/a3f2ffc5d99ddddf777b4de306bd215bd3bbf5ce)) +- *(deps)* Update rust crate rand to 0.9.0 - ([7c325c4](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/7c325c4af779e32059680c1cfb874f83896d7649)) +- *(deps)* Update musixmatch to 0.3.0 + + ## [v0.3.0](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-cli/v0.2.0..musixmatch-cli/v0.3.0) - 2025-01-16 ### 🚀 Features diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 6228dda..6543bc5 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "musixmatch-cli" -version = "0.3.0" +version = "0.3.1" rust-version = "1.70.0" edition.workspace = true authors.workspace = true