Compare commits
22 commits
musixmatch
...
main
Author | SHA1 | Date | |
---|---|---|---|
424a522708 | |||
38386c0132 | |||
a95f3fcf47 | |||
3b69b36ae6 | |||
df150d9ffd | |||
54235e6fb6 | |||
c4bfbe563a | |||
dc1bea13cc | |||
bc0dd99f7d | |||
c9fea762ec | |||
c120583bf8 | |||
05978665de | |||
f45ad3cefb | |||
348e9c5427 | |||
19e209e34f | |||
30e2afd367 | |||
c7d40a75ee | |||
dcc25bff20 | |||
1bc5ae4083 | |||
d2a7aed917 | |||
dc01542515 | |||
e72d2b4363 |
15 changed files with 672 additions and 366 deletions
|
@ -4,6 +4,14 @@ on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
Test:
|
Test:
|
||||||
runs-on: cimaster-latest
|
runs-on: cimaster-latest
|
||||||
|
services:
|
||||||
|
warpproxy:
|
||||||
|
image: thetadev256/warpproxy
|
||||||
|
env:
|
||||||
|
WARP_DEVICE_ID: ${{ secrets.WARP_DEVICE_ID }}
|
||||||
|
WARP_ACCESS_TOKEN: ${{ secrets.WARP_ACCESS_TOKEN }}
|
||||||
|
WARP_LICENSE_KEY: ${{ secrets.WARP_LICENSE_KEY }}
|
||||||
|
WARP_PRIVATE_KEY: ${{ secrets.WARP_PRIVATE_KEY }}
|
||||||
steps:
|
steps:
|
||||||
- name: 📦 Checkout repository
|
- name: 📦 Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
@ -17,3 +25,5 @@ jobs:
|
||||||
|
|
||||||
- name: 🧪 Test
|
- name: 🧪 Test
|
||||||
run: cargo test --workspace
|
run: cargo test --workspace
|
||||||
|
env:
|
||||||
|
ALL_PROXY: "http://warpproxy:8124"
|
||||||
|
|
|
@ -23,13 +23,8 @@ jobs:
|
||||||
echo END_OF_FILE
|
echo END_OF_FILE
|
||||||
} >> "$GITHUB_ENV"
|
} >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: 📤 Publish crate on code.thetadev.de
|
- name: 📤 Publish crate on crates.io
|
||||||
run: |
|
run: cargo publish --token ${{ secrets.CARGO_TOKEN }} --package "${{ env.CRATE }}"
|
||||||
mkdir -p ~/.cargo
|
|
||||||
printf '[registries.thetadev]\nindex = "https://code.thetadev.de/ThetaDev/_cargo-index.git"\ntoken = "Bearer ${{ secrets.TOKEN_GITEA }}"\n' >> ~/.cargo/config.toml
|
|
||||||
sed -i "s/^musixmatch-.*=\s*{/\0 registry = \"thetadev\",/g" Cargo.toml
|
|
||||||
cargo publish --registry thetadev --package "${{ env.CRATE }}" --allow-dirty
|
|
||||||
git restore Cargo.toml
|
|
||||||
|
|
||||||
- name: 🎉 Publish release
|
- name: 🎉 Publish release
|
||||||
uses: https://gitea.com/actions/release-action@main
|
uses: https://gitea.com/actions/release-action@main
|
||||||
|
|
63
.gitea/workflows/renovate.yaml
Normal file
63
.gitea/workflows/renovate.yaml
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
name: renovate
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
paths:
|
||||||
|
- ".forgejo/workflows/renovate.yaml"
|
||||||
|
- "renovate.json"
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
RENOVATE_REPOSITORIES: ${{ github.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
renovate:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: renovate/renovate:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Load renovate repo cache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
.tmp/cache/renovate/repository
|
||||||
|
.tmp/cache/renovate/renovate-cache-sqlite
|
||||||
|
.tmp/osv
|
||||||
|
key: repo-cache-${{ github.run_id }}
|
||||||
|
restore-keys: |
|
||||||
|
repo-cache-
|
||||||
|
|
||||||
|
- name: Run renovate
|
||||||
|
run: renovate
|
||||||
|
env:
|
||||||
|
LOG_LEVEL: debug
|
||||||
|
RENOVATE_BASE_DIR: ${{ github.workspace }}/.tmp
|
||||||
|
RENOVATE_ENDPOINT: ${{ github.server_url }}
|
||||||
|
RENOVATE_PLATFORM: gitea
|
||||||
|
RENOVATE_REPOSITORY_CACHE: 'enabled'
|
||||||
|
RENOVATE_TOKEN: ${{ secrets.FORGEJO_CI_BOT_TOKEN }}
|
||||||
|
GITHUB_COM_TOKEN: ${{ secrets.GH_PUBLIC_TOKEN }}
|
||||||
|
RENOVATE_GIT_AUTHOR: 'Renovate Bot <forgejo-renovate-action@forgejo.org>'
|
||||||
|
|
||||||
|
RENOVATE_X_SQLITE_PACKAGE_CACHE: true
|
||||||
|
|
||||||
|
GIT_AUTHOR_NAME: 'Renovate Bot'
|
||||||
|
GIT_AUTHOR_EMAIL: 'forgejo-renovate-action@forgejo.org'
|
||||||
|
GIT_COMMITTER_NAME: 'Renovate Bot'
|
||||||
|
GIT_COMMITTER_EMAIL: 'forgejo-renovate-action@forgejo.org'
|
||||||
|
|
||||||
|
OSV_OFFLINE_ROOT_DIR: ${{ github.workspace }}/.tmp/osv
|
||||||
|
|
||||||
|
- name: Save renovate repo cache
|
||||||
|
if: always() && env.RENOVATE_DRY_RUN != 'full'
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
.tmp/cache/renovate/repository
|
||||||
|
.tmp/cache/renovate/renovate-cache-sqlite
|
||||||
|
.tmp/osv
|
||||||
|
key: repo-cache-${{ github.run_id }}
|
26
CHANGELOG.md
26
CHANGELOG.md
|
@ -2,7 +2,31 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## v0.1.0 - 2024-03-23
|
|
||||||
|
## [v0.1.1](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-inofficial/v0.1.0..musixmatch-inofficial/v0.1.1) - 2024-08-18
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- Add msrv - ([a95f3fc](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/a95f3fcf478f1acda9fad12741604b6793e128c1))
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- Update readme - ([348e9c5](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/348e9c5427e59c488d7e2f7cef9e7006a12864f2))
|
||||||
|
|
||||||
|
### 🧪 Testing
|
||||||
|
|
||||||
|
- Fix tests - ([d2a7aed](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/d2a7aed917bfcec75ce00bb49d380fbc31c47384))
|
||||||
|
- Fix tests - ([c120583](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/c120583bf861cc74fbce686b2bd88bc575270130))
|
||||||
|
- Fix tests - ([c9fea76](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/c9fea762ec97a1c594e60a3b1cbc72bb786d0957))
|
||||||
|
- Add rate limiter - ([3b69b36](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/3b69b36ae6c945d786534e0eaa353fb737b1fb54))
|
||||||
|
|
||||||
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- Update justfile - ([1bc5ae4](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/1bc5ae408343e6755e390909e7017647efcf59a1))
|
||||||
|
- Update dependencies - ([dcc25bf](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/dcc25bff202becdec7101c5ce1825cd75e445f99))
|
||||||
|
- Change repo to codeberg - ([30e2afd](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/30e2afd3679d2c17a49afd523c8b8bad70f291e5))
|
||||||
|
|
||||||
|
## [v0.1.0](https://codeberg.org/ThetaDev/musixmatch-inofficial/commits/tag/musixmatch-inofficial/v0.1.0) - 2024-03-23
|
||||||
|
|
||||||
Initial release
|
Initial release
|
||||||
|
|
||||||
|
|
34
Cargo.toml
34
Cargo.toml
|
@ -1,6 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "musixmatch-inofficial"
|
name = "musixmatch-inofficial"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
|
rust-version = "1.70.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
@ -8,22 +9,21 @@ repository.workspace = true
|
||||||
keywords.workspace = true
|
keywords.workspace = true
|
||||||
description = "Inofficial client for the Musixmatch API"
|
description = "Inofficial client for the Musixmatch API"
|
||||||
|
|
||||||
|
include = ["/src", "README.md", "CHANGELOG.md", "LICENSE"]
|
||||||
include = ["/src", "README.md", "LICENSE"]
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [".", "cli"]
|
members = [".", "cli"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["ThetaDev <t.testboy@gmail.com>"]
|
authors = ["ThetaDev <thetadev@magenta.de>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://code.thetadev.de/ThetaDev/musixmatch-inofficial"
|
repository = "https://codeberg.org/ThetaDev/musixmatch-inofficial"
|
||||||
keywords = ["music", "lyrics"]
|
keywords = ["music", "lyrics"]
|
||||||
categories = ["api-bindings", "multimedia"]
|
categories = ["api-bindings", "multimedia"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
musixmatch-inofficial = { version = "0.1.0", path = ".", default-features = false }
|
musixmatch-inofficial = { version = "0.1.1", path = ".", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["default-tls"]
|
default = ["default-tls"]
|
||||||
|
@ -41,30 +41,26 @@ reqwest = { version = "0.12.0", default-features = false, features = [
|
||||||
"json",
|
"json",
|
||||||
"gzip",
|
"gzip",
|
||||||
] }
|
] }
|
||||||
tokio = { version = "1.20.0" }
|
tokio = { version = "1.20.4" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.85"
|
serde_json = "1.0.85"
|
||||||
thiserror = "1.0.36"
|
thiserror = "1.0.0"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
time = { version = "0.3.15", features = [
|
time = { version = "0.3.10", features = [
|
||||||
"macros",
|
"macros",
|
||||||
"formatting",
|
"formatting",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-well-known",
|
"serde-well-known",
|
||||||
] }
|
] }
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.0"
|
||||||
sha1 = "0.10.5"
|
sha1 = "0.10.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.0"
|
||||||
base64 = "0.22.0"
|
base64 = "0.22.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ctor = "0.2.0"
|
rstest = { version = "0.22.0", default-features = false }
|
||||||
rstest = { version = "0.18.0", default-features = false }
|
|
||||||
env_logger = "0.11.0"
|
|
||||||
dotenvy = "0.15.5"
|
dotenvy = "0.15.5"
|
||||||
tokio = { version = "1.20.0", features = ["macros"] }
|
tokio = { version = "1.20.4", features = ["macros"] }
|
||||||
futures = "0.3.21"
|
futures = "0.3.21"
|
||||||
path_macro = "1.0.0"
|
path_macro = "1.0.0"
|
||||||
|
governor = "0.6.3"
|
||||||
[profile.release]
|
|
||||||
strip = true
|
|
||||||
|
|
12
Justfile
12
Justfile
|
@ -10,12 +10,12 @@ release crate="musixmatch-inofficial":
|
||||||
CHANGELOG="CHANGELOG.md"
|
CHANGELOG="CHANGELOG.md"
|
||||||
|
|
||||||
if [ "$CRATE" = "musixmatch-inofficial" ]; then
|
if [ "$CRATE" = "musixmatch-inofficial" ]; then
|
||||||
INCLUDES="$INCLUDES --include-path src/** --include-path tests/** --include-path testfiles/**"
|
INCLUDES="$INCLUDES --include-path 'src/**' --include-path 'tests/**' --include-path 'testfiles/**'"
|
||||||
else
|
else
|
||||||
if [ ! -d "$CRATE" ]; then
|
if [ ! -d "$CRATE" ]; then
|
||||||
echo "$CRATE does not exist."; exit 1
|
echo "$CRATE does not exist."; exit 1
|
||||||
fi
|
fi
|
||||||
INCLUDES="$INCLUDES --include-path $CRATE/**"
|
INCLUDES="$INCLUDES --include-path '$CRATE/**'"
|
||||||
CHANGELOG="$CRATE/$CHANGELOG"
|
CHANGELOG="$CRATE/$CHANGELOG"
|
||||||
CRATE="musixmatch-$CRATE" # Add crate name prefix
|
CRATE="musixmatch-$CRATE" # Add crate name prefix
|
||||||
fi
|
fi
|
||||||
|
@ -26,17 +26,17 @@ release crate="musixmatch-inofficial":
|
||||||
|
|
||||||
if git rev-parse "$TAG" >/dev/null 2>&1; then echo "version tag $TAG already exists"; exit 1; fi
|
if git rev-parse "$TAG" >/dev/null 2>&1; then echo "version tag $TAG already exists"; exit 1; fi
|
||||||
|
|
||||||
CLIFF_ARGS="--tag v${VERSION} --tag-pattern ${CRATE}/* --unreleased $INCLUDES"
|
CLIFF_ARGS="--tag '${TAG}' --tag-pattern '${CRATE}/v*' --unreleased $INCLUDES"
|
||||||
echo "git-cliff $CLIFF_ARGS"
|
echo "git-cliff $CLIFF_ARGS"
|
||||||
if [ -f "$CHANGELOG" ]; then
|
if [ -f "$CHANGELOG" ]; then
|
||||||
git-cliff $CLIFF_ARGS --prepend "$CHANGELOG"
|
eval "git-cliff $CLIFF_ARGS --prepend '$CHANGELOG'"
|
||||||
else
|
else
|
||||||
git-cliff $CLIFF_ARGS --output "$CHANGELOG"
|
eval "git-cliff $CLIFF_ARGS --output '$CHANGELOG'"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
editor "$CHANGELOG"
|
editor "$CHANGELOG"
|
||||||
|
|
||||||
git add "$CHANGELOG"
|
git add .
|
||||||
git commit -m "chore(release): release $CRATE v$VERSION"
|
git commit -m "chore(release): release $CRATE v$VERSION"
|
||||||
|
|
||||||
awk 'BEGIN{RS="(^|\n)## [^\n]+\n*"} NR==2 { print }' "$CHANGELOG" | git tag -as -F - --cleanup whitespace "$TAG"
|
awk 'BEGIN{RS="(^|\n)## [^\n]+\n*"} NR==2 { print }' "$CHANGELOG" | git tag -as -F - --cleanup whitespace "$TAG"
|
||||||
|
|
14
README.md
14
README.md
|
@ -1,4 +1,8 @@
|
||||||
# Musixmatch-Inofficial
|
# musixmatch-inofficial
|
||||||
|
|
||||||
|
[![Current crates.io version](https://img.shields.io/crates/v/musixmatch-inofficial.svg)](https://crates.io/crates/musixmatch-inofficial)
|
||||||
|
[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat)](http://opensource.org/licenses/MIT)
|
||||||
|
[![CI status](https://codeberg.org/ThetaDev/musixmatch-inofficial/actions/workflows/ci.yaml/badge.svg?style=flat&label=CI)](https://codeberg.org/ThetaDev/musixmatch-inofficial/actions/?workflow=ci.yaml)
|
||||||
|
|
||||||
This is an inofficial client for the Musixmatch API that uses the key embedded in the
|
This is an inofficial client for the Musixmatch API that uses the key embedded in the
|
||||||
Musixmatch Android app.
|
Musixmatch Android app.
|
||||||
|
@ -7,10 +11,10 @@ It allows you to obtain synchronized lyrics in different formats
|
||||||
([LRC](<https://en.wikipedia.org/wiki/LRC_(file_format)>),
|
([LRC](<https://en.wikipedia.org/wiki/LRC_(file_format)>),
|
||||||
[DFXP](https://www.w3.org/TR/ttml1/), JSON) for almost any song.
|
[DFXP](https://www.w3.org/TR/ttml1/), JSON) for almost any song.
|
||||||
|
|
||||||
The Musixmatch API required a free account on <https://www.musixmatch.com> to be used.
|
The Musixmatch API used to require a free account on <https://www.musixmatch.com> to be
|
||||||
However, as of 2024, this requirement was removed and the API can be used anonymously.
|
used. However, as of 2024, this requirement was removed and the API can be used
|
||||||
The client still allows you to supply credentials if Musixmatch decided to close the API
|
anonymously. The client still allows you to supply credentials if Musixmatch decides to
|
||||||
down again.
|
close the API down again.
|
||||||
|
|
||||||
## ⚠️ Copyright disclaimer
|
## ⚠️ Copyright disclaimer
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,37 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## v0.1.0 - 2024-03-23
|
|
||||||
|
## [v0.2.0](https://codeberg.org/ThetaDev/musixmatch-inofficial/compare/musixmatch-cli/v0.1.0..musixmatch-cli/v0.2.0) - 2024-08-18
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- Add format option to mp3 subtitles cmd - ([19e209e](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/19e209e34f4d129a4223930bfd41e1ccf117f231))
|
||||||
|
- Add get album, get artist, search artist - ([c4bfbe5](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/c4bfbe563a00d399b3645dd68f03c1215ee51fdb))
|
||||||
|
- [**breaking**] Remove MP3 feature, refactor cmd structure - ([54235e6](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/54235e6fb61084823a6583aaa7d59b1799deb07f))
|
||||||
|
- Add msrv - ([a95f3fc](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/a95f3fcf478f1acda9fad12741604b6793e128c1))
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- Use native TLS for CLI - ([dc1bea1](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/dc1bea13cc2a37eae7f3727dc72f865a01430a2e))
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- Update readme - ([348e9c5](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/348e9c5427e59c488d7e2f7cef9e7006a12864f2))
|
||||||
|
|
||||||
|
### 🧪 Testing
|
||||||
|
|
||||||
|
- Fix tests - ([d2a7aed](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/d2a7aed917bfcec75ce00bb49d380fbc31c47384))
|
||||||
|
- Add rate limiter - ([3b69b36](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/3b69b36ae6c945d786534e0eaa353fb737b1fb54))
|
||||||
|
|
||||||
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- Fix changelogs - ([e72d2b4](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/e72d2b4363a3a9a48dec8f2be9389f6cc239035c))
|
||||||
|
- Update justfile - ([1bc5ae4](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/1bc5ae408343e6755e390909e7017647efcf59a1))
|
||||||
|
- Update dependencies - ([dcc25bf](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/dcc25bff202becdec7101c5ce1825cd75e445f99))
|
||||||
|
- Change repo to codeberg - ([30e2afd](https://codeberg.org/ThetaDev/musixmatch-inofficial/commit/30e2afd3679d2c17a49afd523c8b8bad70f291e5))
|
||||||
|
|
||||||
|
## [v0.1.0](https://codeberg.org/ThetaDev/musixmatch-inofficial/commits/tag/musixmatch-cli/v0.1.0) - 2024-03-23
|
||||||
|
|
||||||
Initial release
|
Initial release
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "musixmatch-cli"
|
name = "musixmatch-cli"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
|
rust-version = "1.70.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
@ -9,7 +10,7 @@ keywords.workspace = true
|
||||||
description = "Inofficial command line interface for the Musixmatch API"
|
description = "Inofficial command line interface for the Musixmatch API"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rustls-tls-native-roots"]
|
default = ["native-tls"]
|
||||||
|
|
||||||
# Reqwest TLS options
|
# Reqwest TLS options
|
||||||
native-tls = ["musixmatch-inofficial/native-tls"]
|
native-tls = ["musixmatch-inofficial/native-tls"]
|
||||||
|
@ -20,11 +21,9 @@ rustls-tls-native-roots = ["musixmatch-inofficial/rustls-tls-native-roots"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
musixmatch-inofficial.workspace = true
|
musixmatch-inofficial.workspace = true
|
||||||
tokio = { version = "1.20.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.20.4", features = ["macros", "rt-multi-thread"] }
|
||||||
id3 = "1.3.0"
|
clap = { version = "4.0.0", features = ["derive"] }
|
||||||
mp3-duration = "0.1.10"
|
anyhow = "1.0.0"
|
||||||
clap = { version = "4.0.10", features = ["derive"] }
|
|
||||||
anyhow = "1.0.65"
|
|
||||||
rpassword = "7.0.0"
|
rpassword = "7.0.0"
|
||||||
dirs = "5.0.0"
|
dirs = "5.0.0"
|
||||||
serde_json = "1.0.91"
|
serde_json = "1.0.85"
|
||||||
|
|
77
cli/README.md
Normal file
77
cli/README.md
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
# musixmatch-cli
|
||||||
|
|
||||||
|
[![Current crates.io version](https://img.shields.io/crates/v/musixmatch-cli.svg)](https://crates.io/crates/musixmatch-cli)
|
||||||
|
[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat)](http://opensource.org/licenses/MIT)
|
||||||
|
[![CI status](https://codeberg.org/ThetaDev/musixmatch-inofficial/actions/workflows/ci.yaml/badge.svg?style=flat&label=CI)](https://codeberg.org/ThetaDev/musixmatch-inofficial/actions/?workflow=ci.yaml)
|
||||||
|
|
||||||
|
The Musixmatch CLI allows you to fetch lyrics, subtitles and track metadata from the
|
||||||
|
command line using the Musixmatch API.
|
||||||
|
|
||||||
|
The Musixmatch API used to require a free account on <https://www.musixmatch.com> to be
|
||||||
|
used. However, as of 2024, this requirement was removed and the API can be used
|
||||||
|
anonymously. The CLI still allows you to supply credentials if Musixmatch decides to
|
||||||
|
close the API down again.
|
||||||
|
|
||||||
|
### Get lyrics
|
||||||
|
|
||||||
|
```txt
|
||||||
|
musixmatch-cli lyrics -n shine -a spektrem
|
||||||
|
Lyrics ID: 34583240
|
||||||
|
Language: en
|
||||||
|
Copyright: Writer(s): Jesse Warren
|
||||||
|
Copyright: Ncs Music
|
||||||
|
|
||||||
|
Eyes in the sky gazing far into the night
|
||||||
|
I raise my hand to the fire, but it's no use
|
||||||
|
'Cause you can't stop it from shining through
|
||||||
|
It's true
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get translated lyrics
|
||||||
|
|
||||||
|
Musixmatch also offers translated lyrics. You have to select a language using the
|
||||||
|
`--lang` flag. You can also set the `--bi` flag to output both the original and
|
||||||
|
translated lines.
|
||||||
|
|
||||||
|
```txt
|
||||||
|
musixmatch-cli lyrics -n shine -a spektrem --lang de --bi
|
||||||
|
Lyrics ID: 34583240
|
||||||
|
Language: en
|
||||||
|
Copyright: Writer(s): Jesse Warren
|
||||||
|
Copyright: Ncs Music
|
||||||
|
Translated to: de
|
||||||
|
|
||||||
|
Eyes in the sky gazing far into the night
|
||||||
|
> Augen starren in die weite Nacht
|
||||||
|
I raise my hand to the fire, but it's no use
|
||||||
|
> Ich hebe meine Hand in das Feuer, doch ihr geschieht nichts
|
||||||
|
'Cause you can't stop it from shining through
|
||||||
|
> Denn du kannst es nicht daran hindern, hindurch zu scheinen
|
||||||
|
It's true
|
||||||
|
> Es ist wahr
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get subtitles (synchronized lyrics)
|
||||||
|
|
||||||
|
For most lyrics Musixmatch provides timestamps for the individual lines so you can
|
||||||
|
display them in sync during playback.
|
||||||
|
|
||||||
|
Musixmatch offers multiple subtitle formats you can select using the `--format` flag.
|
||||||
|
The available formats are: `lrc`, `ttml`, `ttml-structured`, `json`, `ebu-stl`
|
||||||
|
|
||||||
|
```txt
|
||||||
|
musixmatch-cli subtitles -n shine -a spektrem
|
||||||
|
Subtitle ID: 35340319
|
||||||
|
Language: en
|
||||||
|
Length: 316
|
||||||
|
Copyright: Writer(s): Jesse Warren
|
||||||
|
Copyright: Ncs Music
|
||||||
|
|
||||||
|
[00:59.84] Eyes in the sky gazing far into the night
|
||||||
|
[01:06.55] I raise my hand to the fire, but it's no use
|
||||||
|
[01:11.97] 'Cause you can't stop it from shining through
|
||||||
|
[01:16.07] It's true
|
||||||
|
...
|
||||||
|
```
|
181
cli/src/main.rs
181
cli/src/main.rs
|
@ -1,3 +1,6 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![warn(missing_docs, clippy::todo)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io::{stdin, stdout, Write},
|
io::{stdin, stdout, Write},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -5,9 +8,8 @@ use std::{
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
use id3::{Tag, TagLike};
|
|
||||||
use musixmatch_inofficial::{
|
use musixmatch_inofficial::{
|
||||||
models::{SubtitleFormat, Track, TrackId, TranslationMap},
|
models::{AlbumId, ArtistId, SubtitleFormat, Track, TrackId, TranslationMap},
|
||||||
Musixmatch,
|
Musixmatch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,14 +22,50 @@ struct Cli {
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
enum Commands {
|
enum Commands {
|
||||||
Get {
|
/// Get lyrics text
|
||||||
#[command(subcommand)]
|
Lyrics {
|
||||||
command: GetCommands,
|
#[clap(flatten)]
|
||||||
|
ident: TrackIdentifiers,
|
||||||
|
/// Language
|
||||||
|
#[clap(long)]
|
||||||
|
lang: Option<String>,
|
||||||
|
/// Bilingual
|
||||||
|
#[clap(long)]
|
||||||
|
bi: bool,
|
||||||
},
|
},
|
||||||
Mp3 {
|
/// Get subtitles (time-synced lyrics)
|
||||||
#[command(subcommand)]
|
Subtitles {
|
||||||
command: FileCommands,
|
#[clap(flatten)]
|
||||||
|
ident: TrackIdentifiers,
|
||||||
|
/// Track length
|
||||||
|
#[clap(short, long)]
|
||||||
|
length: Option<f32>,
|
||||||
|
/// Maximum deviation from track length (Default: 1s)
|
||||||
|
#[clap(long)]
|
||||||
|
max_deviation: Option<f32>,
|
||||||
|
/// Subtitle format
|
||||||
|
#[clap(short, long, default_value = "lrc")]
|
||||||
|
format: SubtitleFormatClap,
|
||||||
|
/// Language
|
||||||
|
#[clap(long)]
|
||||||
|
lang: Option<String>,
|
||||||
},
|
},
|
||||||
|
/// Get track metadata
|
||||||
|
Track {
|
||||||
|
#[clap(flatten)]
|
||||||
|
ident: TrackIdentifiers,
|
||||||
|
},
|
||||||
|
/// Get album metadata
|
||||||
|
Album {
|
||||||
|
#[clap(flatten)]
|
||||||
|
ident: AlbumArtistIdentifiers,
|
||||||
|
},
|
||||||
|
/// Get artist metadata
|
||||||
|
Artist {
|
||||||
|
#[clap(flatten)]
|
||||||
|
ident: AlbumArtistIdentifiers,
|
||||||
|
},
|
||||||
|
/// Search for Musixmatch tracks
|
||||||
#[group(required = true)]
|
#[group(required = true)]
|
||||||
Search {
|
Search {
|
||||||
/// Track name
|
/// Track name
|
||||||
|
@ -42,40 +80,8 @@ enum Commands {
|
||||||
/// Search query
|
/// Search query
|
||||||
query: Option<Vec<String>>,
|
query: Option<Vec<String>>,
|
||||||
},
|
},
|
||||||
}
|
/// Search for Musixmatch artists
|
||||||
|
SearchArtist { query: Vec<String> },
|
||||||
#[derive(Subcommand)]
|
|
||||||
enum GetCommands {
|
|
||||||
Lyrics {
|
|
||||||
#[clap(flatten)]
|
|
||||||
ident: TrackIdentifiers,
|
|
||||||
/// Language
|
|
||||||
#[clap(long)]
|
|
||||||
lang: Option<String>,
|
|
||||||
/// Bilingual
|
|
||||||
#[clap(long)]
|
|
||||||
bi: bool,
|
|
||||||
},
|
|
||||||
Subtitles {
|
|
||||||
#[clap(flatten)]
|
|
||||||
ident: TrackIdentifiers,
|
|
||||||
/// Track length
|
|
||||||
#[clap(long, short)]
|
|
||||||
length: Option<f32>,
|
|
||||||
/// Maximum deviation from track length (Default: 1s)
|
|
||||||
#[clap(long)]
|
|
||||||
max_deviation: Option<f32>,
|
|
||||||
/// Subtitle format
|
|
||||||
#[clap(long, default_value = "lrc")]
|
|
||||||
format: SubtitleFormatClap,
|
|
||||||
/// Language
|
|
||||||
#[clap(long)]
|
|
||||||
lang: Option<String>,
|
|
||||||
},
|
|
||||||
Track {
|
|
||||||
#[clap(flatten)]
|
|
||||||
ident: TrackIdentifiers,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
|
@ -106,22 +112,38 @@ struct TrackIdentifiers {
|
||||||
isrc: Option<String>,
|
isrc: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Args)]
|
||||||
|
#[group(multiple = false)]
|
||||||
|
struct AlbumArtistIdentifiers {
|
||||||
|
/// Musixmatch-ID
|
||||||
|
#[clap(long)]
|
||||||
|
mxm_id: Option<u64>,
|
||||||
|
/// Musicbrainz-ID
|
||||||
|
#[clap(long)]
|
||||||
|
musicbrainz: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
enum FileCommands {
|
enum FileCommands {
|
||||||
|
/// Get lyrics text
|
||||||
Lyrics {
|
Lyrics {
|
||||||
/// Music file
|
/// Music file
|
||||||
#[clap(value_parser)]
|
#[clap(value_parser)]
|
||||||
file: PathBuf,
|
file: PathBuf,
|
||||||
},
|
},
|
||||||
|
/// Get subtitles (time-synced lyrics)
|
||||||
Subtitles {
|
Subtitles {
|
||||||
/// Music file
|
/// Music file
|
||||||
#[clap(value_parser)]
|
#[clap(value_parser)]
|
||||||
file: PathBuf,
|
file: PathBuf,
|
||||||
|
/// Subtitle format
|
||||||
|
#[clap(short, long, default_value = "lrc")]
|
||||||
|
format: SubtitleFormatClap,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::ValueEnum, Debug, Copy, Clone)]
|
#[derive(clap::ValueEnum, Debug, Copy, Clone)]
|
||||||
pub enum SubtitleFormatClap {
|
enum SubtitleFormatClap {
|
||||||
Lrc,
|
Lrc,
|
||||||
Ttml,
|
Ttml,
|
||||||
TtmlStructured,
|
TtmlStructured,
|
||||||
|
@ -175,8 +197,7 @@ async fn run(cli: Cli) -> Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Commands::Get { command } => match command {
|
Commands::Lyrics { ident, lang, bi } => {
|
||||||
GetCommands::Lyrics { ident, lang, bi } => {
|
|
||||||
let track_id = get_track_id(ident, &mxm).await?;
|
let track_id = get_track_id(ident, &mxm).await?;
|
||||||
let lyrics = mxm.track_lyrics(track_id.clone()).await?;
|
let lyrics = mxm.track_lyrics(track_id.clone()).await?;
|
||||||
|
|
||||||
|
@ -200,9 +221,7 @@ async fn run(cli: Cli) -> Result<()> {
|
||||||
if Some(&lang) != lyrics.lyrics_language.as_ref() {
|
if Some(&lang) != lyrics.lyrics_language.as_ref() {
|
||||||
let tl = mxm.track_lyrics_translation(track_id, &lang).await?;
|
let tl = mxm.track_lyrics_translation(track_id, &lang).await?;
|
||||||
if tl.is_empty() {
|
if tl.is_empty() {
|
||||||
eprintln!(
|
eprintln!("Translation not found. Returning lyrics in original language.");
|
||||||
"Translation not found. Returning lyrics in original language."
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Translated to: {}", tl.lang);
|
eprintln!("Translated to: {}", tl.lang);
|
||||||
let tm = TranslationMap::from(tl);
|
let tm = TranslationMap::from(tl);
|
||||||
|
@ -229,7 +248,7 @@ async fn run(cli: Cli) -> Result<()> {
|
||||||
eprintln!();
|
eprintln!();
|
||||||
println!("{}", lyrics_body);
|
println!("{}", lyrics_body);
|
||||||
}
|
}
|
||||||
GetCommands::Subtitles {
|
Commands::Subtitles {
|
||||||
ident,
|
ident,
|
||||||
length,
|
length,
|
||||||
max_deviation,
|
max_deviation,
|
||||||
|
@ -294,45 +313,32 @@ async fn run(cli: Cli) -> Result<()> {
|
||||||
println!("{}", subtitles.subtitle_body);
|
println!("{}", subtitles.subtitle_body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GetCommands::Track { ident } => {
|
Commands::Track { ident } => {
|
||||||
let track = get_track(ident, &mxm).await?;
|
let track = get_track(ident, &mxm).await?;
|
||||||
println!("{}", serde_json::to_string_pretty(&track)?)
|
println!("{}", serde_json::to_string_pretty(&track)?)
|
||||||
}
|
}
|
||||||
},
|
Commands::Album { ident } => {
|
||||||
Commands::Mp3 { command } => match command {
|
let id = if let Some(id) = ident.mxm_id {
|
||||||
FileCommands::Lyrics { file } => {
|
AlbumId::AlbumId(id)
|
||||||
let tag = Tag::read_from_path(&file)?;
|
} else if let Some(mb) = &ident.musicbrainz {
|
||||||
|
AlbumId::Musicbrainz(mb)
|
||||||
let title = tag.title().ok_or(anyhow!("no title"))?;
|
} else {
|
||||||
let artist = tag.artist().ok_or(anyhow!("no artist"))?;
|
bail!("no album ID specified")
|
||||||
|
};
|
||||||
let lyrics = mxm.matcher_lyrics(title, artist).await?;
|
let album = mxm.album(id).await?;
|
||||||
|
println!("{}", serde_json::to_string_pretty(&album)?)
|
||||||
println!(
|
|
||||||
"Lyrics for {} by {}:\n\n{}",
|
|
||||||
title, artist, lyrics.lyrics_body
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
FileCommands::Subtitles { file } => {
|
Commands::Artist { ident } => {
|
||||||
let tag = Tag::read_from_path(&file)?;
|
let id = if let Some(id) = ident.mxm_id {
|
||||||
let duration = mp3_duration::from_path(&file)?;
|
ArtistId::ArtistId(id)
|
||||||
|
} else if let Some(mb) = &ident.musicbrainz {
|
||||||
let title = tag.title().ok_or(anyhow!("no title"))?;
|
ArtistId::Musicbrainz(mb)
|
||||||
let artist = tag.artist().ok_or(anyhow!("no artist"))?;
|
} else {
|
||||||
|
bail!("no artist ID specified")
|
||||||
let subtitles = mxm
|
};
|
||||||
.matcher_subtitle(
|
let album = mxm.artist(id).await?;
|
||||||
title,
|
println!("{}", serde_json::to_string_pretty(&album)?)
|
||||||
artist,
|
|
||||||
SubtitleFormat::Lrc,
|
|
||||||
Some(duration.as_secs_f32()),
|
|
||||||
Some(1.0),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
println!("{}", subtitles.subtitle_body);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Commands::Search {
|
Commands::Search {
|
||||||
query,
|
query,
|
||||||
name,
|
name,
|
||||||
|
@ -369,6 +375,15 @@ async fn run(cli: Cli) -> Result<()> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Commands::SearchArtist { query } => {
|
||||||
|
let artists = mxm.artist_search(&query.join(" "), 20, 0).await?;
|
||||||
|
for a in artists {
|
||||||
|
println!(
|
||||||
|
"{} <https://musixmatch.com/artist/{}>",
|
||||||
|
a.artist_name, a.artist_vanity_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,13 @@ All notable changes to this project will be documented in this file.\n
|
||||||
# template for the changelog body
|
# template for the changelog body
|
||||||
# https://keats.github.io/tera/docs/#introduction
|
# https://keats.github.io/tera/docs/#introduction
|
||||||
body = """
|
body = """
|
||||||
{% set repo_url = "https://code.thetadev.de/ThetaDev/rustypipe" %}\
|
{% set repo_url = "https://codeberg.org/ThetaDev/musixmatch-inofficial" %}\
|
||||||
{% if version %}\
|
{% if version %}\
|
||||||
|
{%set vname = version | split(pat="/") | last %}
|
||||||
{%if previous.version %}\
|
{%if previous.version %}\
|
||||||
## [{{ version }}]({{ repo_url }}/compare/{{ previous.version }}..{{ version }})\
|
## [{{ vname }}]({{ repo_url }}/compare/{{ previous.version }}..{{ version }})\
|
||||||
{% else %}\
|
{% else %}\
|
||||||
## {{ version }}\
|
## [{{ vname }}]({{ repo_url }}/commits/tag/{{ version }})\
|
||||||
{% endif %} - {{ timestamp | date(format="%Y-%m-%d") }}
|
{% endif %} - {{ timestamp | date(format="%Y-%m-%d") }}
|
||||||
{% else %}\
|
{% else %}\
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
@ -73,7 +74,7 @@ commit_parsers = [
|
||||||
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
|
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
|
||||||
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
|
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
|
||||||
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
|
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
|
||||||
{ message = "^chore\\(release\\): prepare for", skip = true },
|
{ message = "^chore\\(release\\)", skip = true },
|
||||||
{ message = "^chore\\(pr\\)", skip = true },
|
{ message = "^chore\\(pr\\)", skip = true },
|
||||||
{ message = "^chore\\(pull\\)", skip = true },
|
{ message = "^chore\\(pull\\)", skip = true },
|
||||||
{ message = "^chore", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
|
{ message = "^chore", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
|
||||||
|
|
13
renovate.json
Normal file
13
renovate.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:best-practices"
|
||||||
|
],
|
||||||
|
"semanticCommits": "enabled",
|
||||||
|
"automerge": true,
|
||||||
|
"automergeStrategy": "squash",
|
||||||
|
"osvVulnerabilityAlerts": true,
|
||||||
|
"labels": ["dependency-upgrade"],
|
||||||
|
"enabledManagers": ["cargo"],
|
||||||
|
"prHourlyLimit": 5
|
||||||
|
}
|
|
@ -99,8 +99,8 @@ pub enum LoginCredential {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Account {
|
pub struct Account {
|
||||||
pub id: String,
|
// pub id: String,
|
||||||
pub email: String,
|
// pub email: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
325
tests/tests.rs
325
tests/tests.rs
|
@ -1,7 +1,12 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::{
|
||||||
|
num::NonZeroU32,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::LazyLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
|
||||||
use path_macro::path;
|
use path_macro::path;
|
||||||
use rstest::rstest;
|
use rstest::{fixture, rstest};
|
||||||
use time::macros::{date, datetime};
|
use time::macros::{date, datetime};
|
||||||
|
|
||||||
use musixmatch_inofficial::{
|
use musixmatch_inofficial::{
|
||||||
|
@ -9,19 +14,18 @@ use musixmatch_inofficial::{
|
||||||
Error, Musixmatch,
|
Error, Musixmatch,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[ctor::ctor]
|
fn testfile<P: AsRef<Path>>(name: P) -> PathBuf {
|
||||||
fn init() {
|
path!(env!("CARGO_MANIFEST_DIR") / "testfiles" / name)
|
||||||
let _ = dotenvy::dotenv();
|
|
||||||
env_logger::init();
|
|
||||||
tokio::runtime::Builder::new_current_thread()
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
.block_on(new_mxm().login())
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_mxm() -> Musixmatch {
|
#[fixture]
|
||||||
|
async fn mxm() -> Musixmatch {
|
||||||
|
static LOGIN_LOCK: tokio::sync::OnceCell<()> = tokio::sync::OnceCell::const_new();
|
||||||
|
static MXM_LIMITER: LazyLock<DefaultDirectRateLimiter> =
|
||||||
|
LazyLock::new(|| RateLimiter::direct(Quota::per_second(NonZeroU32::new(1).unwrap())));
|
||||||
|
|
||||||
|
MXM_LIMITER.until_ready().await;
|
||||||
|
|
||||||
let mut mxm = Musixmatch::builder();
|
let mut mxm = Musixmatch::builder();
|
||||||
|
|
||||||
if let (Ok(email), Ok(password)) = (
|
if let (Ok(email), Ok(password)) = (
|
||||||
|
@ -31,11 +35,10 @@ fn new_mxm() -> Musixmatch {
|
||||||
mxm = mxm.credentials(email, password);
|
mxm = mxm.credentials(email, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
mxm.build().unwrap()
|
let mxm = mxm.build().unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
fn testfile<P: AsRef<Path>>(name: P) -> PathBuf {
|
LOGIN_LOCK.get_or_try_init(|| mxm.login()).await.unwrap();
|
||||||
path!(env!("CARGO_MANIFEST_DIR") / "testfiles" / name)
|
mxm
|
||||||
}
|
}
|
||||||
|
|
||||||
mod album {
|
mod album {
|
||||||
|
@ -46,8 +49,8 @@ mod album {
|
||||||
#[case::id(AlbumId::AlbumId(14248253))]
|
#[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]
|
#[tokio::test]
|
||||||
async fn by_id(#[case] album_id: AlbumId<'_>) {
|
async fn by_id(#[case] album_id: AlbumId<'_>, #[future] mxm: Musixmatch) {
|
||||||
let album = new_mxm().album(album_id).await.unwrap();
|
let album = mxm.await.album(album_id).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(album.album_id, 14248253);
|
assert_eq!(album.album_id, 14248253);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -94,17 +97,20 @@ mod album {
|
||||||
assert_imgurl(&album.album_coverart_500x500, "/26544045_500_500.jpg");
|
assert_imgurl(&album.album_coverart_500x500, "/26544045_500_500.jpg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn album_ep() {
|
async fn album_ep(#[future] mxm: Musixmatch) {
|
||||||
let album = new_mxm().album(AlbumId::AlbumId(23976123)).await.unwrap();
|
let album = mxm.await.album(AlbumId::AlbumId(23976123)).await.unwrap();
|
||||||
assert_eq!(album.album_name, "Waldbrand EP");
|
assert_eq!(album.album_name, "Waldbrand EP");
|
||||||
// assert_eq!(album.album_release_type, AlbumType::Ep);
|
// assert_eq!(album.album_release_type, AlbumType::Ep);
|
||||||
assert_eq!(album.album_release_date, Some(date!(2016 - 09 - 30)));
|
assert_eq!(album.album_release_date, Some(date!(2016 - 09 - 30)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn by_id_missing() {
|
async fn by_id_missing(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.album(AlbumId::AlbumId(999999999999))
|
.album(AlbumId::AlbumId(999999999999))
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
@ -112,9 +118,11 @@ mod album {
|
||||||
assert!(matches!(err, Error::NotFound));
|
assert!(matches!(err, Error::NotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn artist_albums() {
|
async fn artist_albums(#[future] mxm: Musixmatch) {
|
||||||
let albums = new_mxm()
|
let albums = mxm
|
||||||
|
.await
|
||||||
.artist_albums(ArtistId::ArtistId(1039), None, 10, 1)
|
.artist_albums(ArtistId::ArtistId(1039), None, 10, 1)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -122,9 +130,11 @@ mod album {
|
||||||
assert_eq!(albums.len(), 10);
|
assert_eq!(albums.len(), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn artist_albums_missing() {
|
async fn artist_albums_missing(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.artist_albums(ArtistId::ArtistId(999999999999), None, 10, 1)
|
.artist_albums(ArtistId::ArtistId(999999999999), None, 10, 1)
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
@ -132,9 +142,10 @@ mod album {
|
||||||
assert!(matches!(err, Error::NotFound));
|
assert!(matches!(err, Error::NotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn charts() {
|
async fn charts(#[future] mxm: Musixmatch) {
|
||||||
let albums = new_mxm().chart_albums("US", 10, 1).await.unwrap();
|
let albums = mxm.await.chart_albums("US", 10, 1).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(albums.len(), 10);
|
assert_eq!(albums.len(), 10);
|
||||||
}
|
}
|
||||||
|
@ -147,8 +158,8 @@ mod artist {
|
||||||
#[case::id(ArtistId::ArtistId(410698))]
|
#[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]
|
#[tokio::test]
|
||||||
async fn by_id(#[case] artist_id: ArtistId<'_>) {
|
async fn by_id(#[case] artist_id: ArtistId<'_>, #[future] mxm: Musixmatch) {
|
||||||
let artist = new_mxm().artist(artist_id).await.unwrap();
|
let artist = mxm.await.artist(artist_id).await.unwrap();
|
||||||
|
|
||||||
// dbg!(&artist);
|
// dbg!(&artist);
|
||||||
|
|
||||||
|
@ -190,9 +201,11 @@ mod artist {
|
||||||
assert_eq!(artist.end_date, None);
|
assert_eq!(artist.end_date, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn by_id_missing() {
|
async fn by_id_missing(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.artist(ArtistId::ArtistId(999999999999))
|
.artist(ArtistId::ArtistId(999999999999))
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
@ -200,9 +213,11 @@ mod artist {
|
||||||
assert!(matches!(err, Error::NotFound));
|
assert!(matches!(err, Error::NotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn related() {
|
async fn related(#[future] mxm: Musixmatch) {
|
||||||
let artists = new_mxm()
|
let artists = mxm
|
||||||
|
.await
|
||||||
.artist_related(ArtistId::ArtistId(26485840), 10, 1)
|
.artist_related(ArtistId::ArtistId(26485840), 10, 1)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -210,9 +225,11 @@ mod artist {
|
||||||
assert_eq!(artists.len(), 10);
|
assert_eq!(artists.len(), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn related_missing() {
|
async fn related_missing(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.artist_related(ArtistId::ArtistId(999999999999), 10, 1)
|
.artist_related(ArtistId::ArtistId(999999999999), 10, 1)
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
@ -220,20 +237,25 @@ mod artist {
|
||||||
assert!(matches!(err, Error::NotFound));
|
assert!(matches!(err, Error::NotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn search() {
|
async fn search(#[future] mxm: Musixmatch) {
|
||||||
let artists = new_mxm().artist_search("psy", 5, 1).await.unwrap();
|
let artists = mxm
|
||||||
|
.await
|
||||||
assert_eq!(artists.len(), 5);
|
.artist_search("Snollebollekes", 5, 1)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let artist = &artists[0];
|
let artist = &artists[0];
|
||||||
assert_eq!(artist.artist_id, 410698);
|
assert_eq!(artist.artist_id, 25344078);
|
||||||
assert_eq!(artist.artist_name, "PSY");
|
assert_eq!(artist.artist_name, "Snollebollekes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn search_empty() {
|
async fn search_empty(#[future] mxm: Musixmatch) {
|
||||||
let artists = new_mxm()
|
let artists = mxm
|
||||||
|
.await
|
||||||
.artist_search(
|
.artist_search(
|
||||||
"Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz",
|
"Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz",
|
||||||
5,
|
5,
|
||||||
|
@ -245,16 +267,18 @@ mod artist {
|
||||||
assert_eq!(artists.len(), 0);
|
assert_eq!(artists.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn charts() {
|
async fn charts(#[future] mxm: Musixmatch) {
|
||||||
let artists = new_mxm().chart_artists("US", 10, 1).await.unwrap();
|
let artists = mxm.await.chart_artists("US", 10, 1).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(artists.len(), 10);
|
assert_eq!(artists.len(), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn charts_no_country() {
|
async fn charts_no_country(#[future] mxm: Musixmatch) {
|
||||||
let artists = new_mxm().chart_artists("XY", 10, 1).await.unwrap();
|
let artists = mxm.await.chart_artists("XY", 10, 1).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(artists.len(), 10);
|
assert_eq!(artists.len(), 10);
|
||||||
}
|
}
|
||||||
|
@ -269,8 +293,13 @@ mod track {
|
||||||
#[case::translation_2c(true, false)]
|
#[case::translation_2c(true, false)]
|
||||||
#[case::translation_3c(true, true)]
|
#[case::translation_3c(true, true)]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn from_match(#[case] translation_status: bool, #[case] lang_3c: bool) {
|
async fn from_match(
|
||||||
let track = new_mxm()
|
#[case] translation_status: bool,
|
||||||
|
#[case] lang_3c: bool,
|
||||||
|
#[future] mxm: Musixmatch,
|
||||||
|
) {
|
||||||
|
let track = mxm
|
||||||
|
.await
|
||||||
.matcher_track(
|
.matcher_track(
|
||||||
"Poker Face",
|
"Poker Face",
|
||||||
"Lady Gaga",
|
"Lady Gaga",
|
||||||
|
@ -283,12 +312,12 @@ mod track {
|
||||||
|
|
||||||
// dbg!(&track);
|
// dbg!(&track);
|
||||||
|
|
||||||
assert_eq!(track.track_id, 15476784);
|
assert_eq!(track.track_id, 85213841);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
track.track_mbid.unwrap(),
|
// track.track_mbid.unwrap(),
|
||||||
"080975b0-39b1-493c-ae64-5cb3292409bb"
|
// "080975b0-39b1-493c-ae64-5cb3292409bb"
|
||||||
);
|
// );
|
||||||
assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
// assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
||||||
assert!(
|
assert!(
|
||||||
track.commontrack_isrcs[0]
|
track.commontrack_isrcs[0]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -296,7 +325,7 @@ mod track {
|
||||||
"commontrack_isrcs: {:?}",
|
"commontrack_isrcs: {:?}",
|
||||||
&track.commontrack_isrcs[0],
|
&track.commontrack_isrcs[0],
|
||||||
);
|
);
|
||||||
assert_eq!(track.track_spotify_id.unwrap(), "5R8dQOPq8haW94K7mgERlO");
|
assert_eq!(track.track_spotify_id.unwrap(), "1QV6tiMFM6fSOKOGLMHYYg");
|
||||||
assert!(
|
assert!(
|
||||||
track
|
track
|
||||||
.commontrack_spotify_ids
|
.commontrack_spotify_ids
|
||||||
|
@ -316,7 +345,7 @@ mod track {
|
||||||
assert!(track.num_favourite > 50);
|
assert!(track.num_favourite > 50);
|
||||||
assert!(track.lyrics_id.is_some());
|
assert!(track.lyrics_id.is_some());
|
||||||
assert_eq!(track.subtitle_id.unwrap(), 36450705);
|
assert_eq!(track.subtitle_id.unwrap(), 36450705);
|
||||||
assert_eq!(track.album_id, 13810402);
|
assert_eq!(track.album_id, 20960801);
|
||||||
assert_eq!(track.album_name, "The Fame");
|
assert_eq!(track.album_name, "The Fame");
|
||||||
assert_eq!(track.artist_id, 378462);
|
assert_eq!(track.artist_id, 378462);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -324,9 +353,10 @@ mod track {
|
||||||
"650e7db6-b795-4eb5-a702-5ea2fc46c848"
|
"650e7db6-b795-4eb5-a702-5ea2fc46c848"
|
||||||
);
|
);
|
||||||
assert_eq!(track.artist_name, "Lady Gaga");
|
assert_eq!(track.artist_name, "Lady Gaga");
|
||||||
assert_imgurl(&track.album_coverart_100x100, "/26319636.jpg");
|
assert_imgurl(&track.album_coverart_100x100, "/32133892.jpg");
|
||||||
assert_imgurl(&track.album_coverart_350x350, "/26319636_350_350.jpg");
|
assert_imgurl(&track.album_coverart_350x350, "/32133892_350_350.jpg");
|
||||||
assert_imgurl(&track.album_coverart_500x500, "/26319636_500_500.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");
|
assert_eq!(track.commontrack_vanity_id, "Lady-Gaga/poker-face-1");
|
||||||
let first_release = track.first_release_date.unwrap();
|
let first_release = track.first_release_date.unwrap();
|
||||||
assert_eq!(first_release.date(), date!(2008 - 1 - 1));
|
assert_eq!(first_release.date(), date!(2008 - 1 - 1));
|
||||||
|
@ -374,8 +404,8 @@ mod track {
|
||||||
#[case::isrc(TrackId::Isrc("KRA302000590".into()))]
|
#[case::isrc(TrackId::Isrc("KRA302000590".into()))]
|
||||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))]
|
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) {
|
||||||
let track = new_mxm().track(track_id, true, false).await.unwrap();
|
let track = mxm.await.track(track_id, true, false).await.unwrap();
|
||||||
|
|
||||||
// dbg!(&track);
|
// dbg!(&track);
|
||||||
|
|
||||||
|
@ -415,20 +445,25 @@ mod track {
|
||||||
#[case::translation_2c(true, false)]
|
#[case::translation_2c(true, false)]
|
||||||
#[case::translation_3c(true, true)]
|
#[case::translation_3c(true, true)]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn from_id_translations(#[case] translation_status: bool, #[case] lang_3c: bool) {
|
async fn from_id_translations(
|
||||||
let track = new_mxm()
|
#[case] translation_status: bool,
|
||||||
.track(TrackId::TrackId(15476784), translation_status, lang_3c)
|
#[case] lang_3c: bool,
|
||||||
|
#[future] mxm: Musixmatch,
|
||||||
|
) {
|
||||||
|
let track = mxm
|
||||||
|
.await
|
||||||
|
.track(TrackId::Commontrack(47672612), translation_status, lang_3c)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// dbg!(&track);
|
// dbg!(&track);
|
||||||
|
|
||||||
assert_eq!(track.track_id, 15476784);
|
assert_eq!(track.track_id, 85213841);
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
track.track_mbid.unwrap(),
|
// track.track_mbid.unwrap(),
|
||||||
"080975b0-39b1-493c-ae64-5cb3292409bb"
|
// "080975b0-39b1-493c-ae64-5cb3292409bb"
|
||||||
);
|
// );
|
||||||
assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
// assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
||||||
assert!(
|
assert!(
|
||||||
track.commontrack_isrcs[0]
|
track.commontrack_isrcs[0]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -436,7 +471,7 @@ mod track {
|
||||||
"commontrack_isrcs: {:?}",
|
"commontrack_isrcs: {:?}",
|
||||||
&track.commontrack_isrcs[0],
|
&track.commontrack_isrcs[0],
|
||||||
);
|
);
|
||||||
assert_eq!(track.track_spotify_id.unwrap(), "5R8dQOPq8haW94K7mgERlO");
|
assert_eq!(track.track_spotify_id.unwrap(), "1QV6tiMFM6fSOKOGLMHYYg");
|
||||||
assert!(
|
assert!(
|
||||||
track
|
track
|
||||||
.commontrack_spotify_ids
|
.commontrack_spotify_ids
|
||||||
|
@ -456,7 +491,7 @@ mod track {
|
||||||
assert!(track.num_favourite > 50);
|
assert!(track.num_favourite > 50);
|
||||||
assert!(track.lyrics_id.is_some());
|
assert!(track.lyrics_id.is_some());
|
||||||
assert_eq!(track.subtitle_id.unwrap(), 36450705);
|
assert_eq!(track.subtitle_id.unwrap(), 36450705);
|
||||||
assert_eq!(track.album_id, 13810402);
|
assert_eq!(track.album_id, 20960801);
|
||||||
assert_eq!(track.album_name, "The Fame");
|
assert_eq!(track.album_name, "The Fame");
|
||||||
assert_eq!(track.artist_id, 378462);
|
assert_eq!(track.artist_id, 378462);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -464,9 +499,10 @@ mod track {
|
||||||
"650e7db6-b795-4eb5-a702-5ea2fc46c848"
|
"650e7db6-b795-4eb5-a702-5ea2fc46c848"
|
||||||
);
|
);
|
||||||
assert_eq!(track.artist_name, "Lady Gaga");
|
assert_eq!(track.artist_name, "Lady Gaga");
|
||||||
assert_imgurl(&track.album_coverart_100x100, "/26319636.jpg");
|
assert_imgurl(&track.album_coverart_100x100, "/32133892.jpg");
|
||||||
assert_imgurl(&track.album_coverart_350x350, "/26319636_350_350.jpg");
|
assert_imgurl(&track.album_coverart_350x350, "/32133892_350_350.jpg");
|
||||||
assert_imgurl(&track.album_coverart_500x500, "/26319636_500_500.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");
|
assert_eq!(track.commontrack_vanity_id, "Lady-Gaga/poker-face-1");
|
||||||
let first_release = track.first_release_date.unwrap();
|
let first_release = track.first_release_date.unwrap();
|
||||||
assert_eq!(first_release.date(), date!(2008 - 1 - 1));
|
assert_eq!(first_release.date(), date!(2008 - 1 - 1));
|
||||||
|
@ -507,9 +543,11 @@ mod track {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn from_id_missing() {
|
async fn from_id_missing(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.track(TrackId::TrackId(999999999999), false, false)
|
.track(TrackId::TrackId(999999999999), false, false)
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
@ -517,9 +555,11 @@ mod track {
|
||||||
assert!(matches!(err, Error::NotFound));
|
assert!(matches!(err, Error::NotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn album_tracks() {
|
async fn album_tracks(#[future] mxm: Musixmatch) {
|
||||||
let tracks = new_mxm()
|
let tracks = mxm
|
||||||
|
.await
|
||||||
.album_tracks(AlbumId::AlbumId(17118624), true, 20, 1)
|
.album_tracks(AlbumId::AlbumId(17118624), true, 20, 1)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -556,9 +596,11 @@ mod track {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn album_missing() {
|
async fn album_missing(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.album_tracks(AlbumId::AlbumId(999999999999), false, 20, 1)
|
.album_tracks(AlbumId::AlbumId(999999999999), false, 20, 1)
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
@ -570,8 +612,9 @@ mod track {
|
||||||
#[case::top(ChartName::Top)]
|
#[case::top(ChartName::Top)]
|
||||||
#[case::hot(ChartName::Hot)]
|
#[case::hot(ChartName::Hot)]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn charts(#[case] chart_name: ChartName) {
|
async fn charts(#[case] chart_name: ChartName, #[future] mxm: Musixmatch) {
|
||||||
let tracks = new_mxm()
|
let tracks = mxm
|
||||||
|
.await
|
||||||
.chart_tracks("US", chart_name, true, 20, 1)
|
.chart_tracks("US", chart_name, true, 20, 1)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -579,9 +622,11 @@ mod track {
|
||||||
assert_eq!(tracks.len(), 20);
|
assert_eq!(tracks.len(), 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn search() {
|
async fn search(#[future] mxm: Musixmatch) {
|
||||||
let tracks = new_mxm()
|
let tracks = mxm
|
||||||
|
.await
|
||||||
.track_search()
|
.track_search()
|
||||||
.q_artist("Lena")
|
.q_artist("Lena")
|
||||||
.q_track("Satellite")
|
.q_track("Satellite")
|
||||||
|
@ -600,9 +645,11 @@ mod track {
|
||||||
assert_eq!(track.artist_name, "Lena");
|
assert_eq!(track.artist_name, "Lena");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn search_lyrics() {
|
async fn search_lyrics(#[future] mxm: Musixmatch) {
|
||||||
let tracks = new_mxm()
|
let tracks = mxm
|
||||||
|
.await
|
||||||
.track_search()
|
.track_search()
|
||||||
.q_lyrics("the whole world stops and stares for a while")
|
.q_lyrics("the whole world stops and stares for a while")
|
||||||
.s_track_rating(SortOrder::Desc)
|
.s_track_rating(SortOrder::Desc)
|
||||||
|
@ -610,16 +657,18 @@ mod track {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(tracks.len(), 10);
|
assert_gte(tracks.len(), 8, "tracks");
|
||||||
|
|
||||||
let track = &tracks[0];
|
let track = &tracks[0];
|
||||||
assert_eq!(track.track_name, "Just the Way You Are");
|
assert_eq!(track.track_name, "Just the Way You Are");
|
||||||
assert_eq!(track.artist_name, "Bruno Mars");
|
assert_eq!(track.artist_name, "Bruno Mars");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn search_empty() {
|
async fn search_empty(#[future] mxm: Musixmatch) {
|
||||||
let artists = new_mxm()
|
let artists = mxm
|
||||||
|
.await
|
||||||
.track_search()
|
.track_search()
|
||||||
.q("Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz")
|
.q("Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz")
|
||||||
.send(10, 1)
|
.send(10, 1)
|
||||||
|
@ -629,16 +678,19 @@ mod track {
|
||||||
assert_eq!(artists.len(), 0);
|
assert_eq!(artists.len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn genres() {
|
async fn genres(#[future] mxm: Musixmatch) {
|
||||||
let genres = new_mxm().genres().await.unwrap();
|
let genres = mxm.await.genres().await.unwrap();
|
||||||
assert!(genres.len() > 360);
|
assert!(genres.len() > 360);
|
||||||
dbg!(&genres);
|
dbg!(&genres);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn snippet() {
|
async fn snippet(#[future] mxm: Musixmatch) {
|
||||||
let snippet = new_mxm()
|
let snippet = mxm
|
||||||
|
.await
|
||||||
.track_snippet(TrackId::Commontrack(8874280))
|
.track_snippet(TrackId::Commontrack(8874280))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -660,9 +712,10 @@ mod lyrics {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn from_match() {
|
async fn from_match(#[future] mxm: Musixmatch) {
|
||||||
let lyrics = new_mxm().matcher_lyrics("Shine", "Spektrem").await.unwrap();
|
let lyrics = mxm.await.matcher_lyrics("Shine", "Spektrem").await.unwrap();
|
||||||
|
|
||||||
// dbg!(&lyrics);
|
// dbg!(&lyrics);
|
||||||
|
|
||||||
|
@ -690,8 +743,8 @@ mod lyrics {
|
||||||
#[case::isrc(TrackId::Isrc("KRA302000590".into()))]
|
#[case::isrc(TrackId::Isrc("KRA302000590".into()))]
|
||||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))]
|
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) {
|
||||||
let lyrics = new_mxm().track_lyrics(track_id).await.unwrap();
|
let lyrics = mxm.await.track_lyrics(track_id).await.unwrap();
|
||||||
|
|
||||||
// dbg!(&lyrics);
|
// dbg!(&lyrics);
|
||||||
|
|
||||||
|
@ -707,9 +760,11 @@ mod lyrics {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This track has no lyrics
|
/// This track has no lyrics
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn instrumental() {
|
async fn instrumental(#[future] mxm: Musixmatch) {
|
||||||
let lyrics = new_mxm()
|
let lyrics = mxm
|
||||||
|
.await
|
||||||
.matcher_lyrics("drivers license", "Bobby G")
|
.matcher_lyrics("drivers license", "Bobby G")
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -725,9 +780,11 @@ mod lyrics {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This track does not exist
|
/// This track does not exist
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn missing() {
|
async fn missing(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.track_lyrics(TrackId::Spotify("674JwwTP7xCje81T0DRrLn".into()))
|
.track_lyrics(TrackId::Spotify("674JwwTP7xCje81T0DRrLn".into()))
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
@ -735,14 +792,16 @@ mod lyrics {
|
||||||
assert!(matches!(err, Error::NotFound));
|
assert!(matches!(err, Error::NotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn download_testdata() {
|
async fn download_testdata(#[future] mxm: Musixmatch) {
|
||||||
|
let mxm = mxm.await;
|
||||||
let json_path = testfile("lyrics.json");
|
let json_path = testfile("lyrics.json");
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let lyrics = new_mxm()
|
let lyrics = mxm
|
||||||
.track_lyrics(TrackId::Commontrack(18576954))
|
.track_lyrics(TrackId::Commontrack(18576954))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -751,14 +810,16 @@ mod lyrics {
|
||||||
serde_json::to_writer_pretty(BufWriter::new(json_file), &lyrics).unwrap();
|
serde_json::to_writer_pretty(BufWriter::new(json_file), &lyrics).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn download_testdata_translation() {
|
async fn download_testdata_translation(#[future] mxm: Musixmatch) {
|
||||||
|
let mxm = mxm.await;
|
||||||
let json_path = testfile("translation.json");
|
let json_path = testfile("translation.json");
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let translations = new_mxm()
|
let translations = mxm
|
||||||
.track_lyrics_translation(TrackId::Commontrack(18576954), "de")
|
.track_lyrics_translation(TrackId::Commontrack(18576954), "de")
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -767,9 +828,10 @@ mod lyrics {
|
||||||
serde_json::to_writer_pretty(BufWriter::new(json_file), &translations).unwrap();
|
serde_json::to_writer_pretty(BufWriter::new(json_file), &translations).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn concurrency() {
|
async fn concurrency(#[future] mxm: Musixmatch) {
|
||||||
let mxm = new_mxm();
|
let mxm = mxm.await;
|
||||||
|
|
||||||
let album = mxm
|
let album = mxm
|
||||||
.album_tracks(
|
.album_tracks(
|
||||||
|
@ -804,9 +866,11 @@ mod subtitles {
|
||||||
use super::*;
|
use super::*;
|
||||||
use musixmatch_inofficial::models::SubtitleFormat;
|
use musixmatch_inofficial::models::SubtitleFormat;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn from_match() {
|
async fn from_match(#[future] mxm: Musixmatch) {
|
||||||
let subtitle = new_mxm()
|
let subtitle = mxm
|
||||||
|
.await
|
||||||
.matcher_subtitle(
|
.matcher_subtitle(
|
||||||
"Shine",
|
"Shine",
|
||||||
"Spektrem",
|
"Spektrem",
|
||||||
|
@ -835,8 +899,9 @@ mod subtitles {
|
||||||
#[case::isrc(TrackId::Isrc("KRA302000590".into()))]
|
#[case::isrc(TrackId::Isrc("KRA302000590".into()))]
|
||||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))]
|
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) {
|
||||||
let subtitle = new_mxm()
|
let subtitle = mxm
|
||||||
|
.await
|
||||||
.track_subtitle(track_id, SubtitleFormat::Json, Some(175.0), Some(1.0))
|
.track_subtitle(track_id, SubtitleFormat::Json, Some(175.0), Some(1.0))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -856,9 +921,11 @@ mod subtitles {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This track has no lyrics
|
/// This track has no lyrics
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn instrumental() {
|
async fn instrumental(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.matcher_subtitle(
|
.matcher_subtitle(
|
||||||
"drivers license",
|
"drivers license",
|
||||||
"Bobby G",
|
"Bobby G",
|
||||||
|
@ -873,9 +940,11 @@ mod subtitles {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This track has not been synced
|
/// This track has not been synced
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn unsynced() {
|
async fn unsynced(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.track_subtitle(
|
.track_subtitle(
|
||||||
TrackId::Spotify("6oaWIABGL7eeiMILEDyGX1".into()),
|
TrackId::Spotify("6oaWIABGL7eeiMILEDyGX1".into()),
|
||||||
SubtitleFormat::Json,
|
SubtitleFormat::Json,
|
||||||
|
@ -889,9 +958,11 @@ mod subtitles {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to get subtitles with wrong length parameter
|
/// Try to get subtitles with wrong length parameter
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn wrong_length() {
|
async fn wrong_length(#[future] mxm: Musixmatch) {
|
||||||
let err = new_mxm()
|
let err = mxm
|
||||||
|
.await
|
||||||
.track_subtitle(
|
.track_subtitle(
|
||||||
TrackId::Commontrack(118480583),
|
TrackId::Commontrack(118480583),
|
||||||
SubtitleFormat::Json,
|
SubtitleFormat::Json,
|
||||||
|
@ -904,14 +975,16 @@ mod subtitles {
|
||||||
assert!(matches!(err, Error::NotFound));
|
assert!(matches!(err, Error::NotFound));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn download_testdata() {
|
async fn download_testdata(#[future] mxm: Musixmatch) {
|
||||||
let json_path = testfile("subtitles.json");
|
let json_path = testfile("subtitles.json");
|
||||||
if json_path.exists() {
|
if json_path.exists() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let subtitle = new_mxm()
|
let subtitle = mxm
|
||||||
|
.await
|
||||||
.track_subtitle(
|
.track_subtitle(
|
||||||
TrackId::Commontrack(18576954),
|
TrackId::Commontrack(18576954),
|
||||||
SubtitleFormat::Json,
|
SubtitleFormat::Json,
|
||||||
|
@ -973,3 +1046,9 @@ fn assert_imgurl(url: &Option<String>, ends_with: &str) {
|
||||||
url
|
url
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Assert that number A is greater than or equal to number B
|
||||||
|
#[track_caller]
|
||||||
|
fn assert_gte<T: PartialOrd + std::fmt::Display>(a: T, b: T, msg: &str) {
|
||||||
|
assert!(a >= b, "expected >= {b} {msg}, got {a}");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue