Compare commits
26 commits
musixmatch
...
main
Author | SHA1 | Date | |
---|---|---|---|
e4cffa53ca | |||
|
4bfcb79173 | ||
|
5ef76f5a6b | ||
424a522708 | |||
38386c0132 | |||
a95f3fcf47 | |||
3b69b36ae6 | |||
df150d9ffd | |||
54235e6fb6 | |||
c4bfbe563a | |||
dc1bea13cc | |||
bc0dd99f7d | |||
c9fea762ec | |||
c120583bf8 | |||
05978665de | |||
f45ad3cefb | |||
348e9c5427 | |||
19e209e34f | |||
30e2afd367 | |||
c7d40a75ee | |||
dcc25bff20 | |||
1bc5ae4083 | |||
d2a7aed917 | |||
dc01542515 | |||
e72d2b4363 | |||
8afc43a097 |
15 changed files with 682 additions and 366 deletions
|
@ -4,6 +4,14 @@ on: [push, pull_request]
|
|||
jobs:
|
||||
Test:
|
||||
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:
|
||||
- name: 📦 Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
@ -17,3 +25,5 @@ jobs:
|
|||
|
||||
- name: 🧪 Test
|
||||
run: cargo test --workspace
|
||||
env:
|
||||
ALL_PROXY: "http://warpproxy:8124"
|
||||
|
|
|
@ -23,13 +23,8 @@ jobs:
|
|||
echo END_OF_FILE
|
||||
} >> "$GITHUB_ENV"
|
||||
|
||||
- name: 📤 Publish crate on code.thetadev.de
|
||||
run: |
|
||||
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 crate on crates.io
|
||||
run: cargo publish --token ${{ secrets.CARGO_TOKEN }} --package "${{ env.CRATE }}"
|
||||
|
||||
- name: 🎉 Publish release
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
|
|
34
Cargo.toml
34
Cargo.toml
|
@ -1,6 +1,7 @@
|
|||
[package]
|
||||
name = "musixmatch-inofficial"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
rust-version = "1.70.0"
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -8,22 +9,21 @@ repository.workspace = true
|
|||
keywords.workspace = true
|
||||
description = "Inofficial client for the Musixmatch API"
|
||||
|
||||
|
||||
include = ["/src", "README.md", "LICENSE"]
|
||||
include = ["/src", "README.md", "CHANGELOG.md", "LICENSE"]
|
||||
|
||||
[workspace]
|
||||
members = [".", "cli"]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
authors = ["ThetaDev <t.testboy@gmail.com>"]
|
||||
authors = ["ThetaDev <thetadev@magenta.de>"]
|
||||
license = "MIT"
|
||||
repository = "https://code.thetadev.de/ThetaDev/musixmatch-inofficial"
|
||||
repository = "https://codeberg.org/ThetaDev/musixmatch-inofficial"
|
||||
keywords = ["music", "lyrics"]
|
||||
categories = ["api-bindings", "multimedia"]
|
||||
|
||||
[workspace.dependencies]
|
||||
musixmatch-inofficial = { version = "0.1.0", path = ".", default-features = false }
|
||||
musixmatch-inofficial = { version = "0.1.1", path = ".", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["default-tls"]
|
||||
|
@ -41,30 +41,26 @@ reqwest = { version = "0.12.0", default-features = false, features = [
|
|||
"json",
|
||||
"gzip",
|
||||
] }
|
||||
tokio = { version = "1.20.0" }
|
||||
tokio = { version = "1.20.4" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.85"
|
||||
thiserror = "1.0.36"
|
||||
thiserror = "1.0.0"
|
||||
log = "0.4.17"
|
||||
time = { version = "0.3.15", features = [
|
||||
time = { version = "0.3.10", features = [
|
||||
"macros",
|
||||
"formatting",
|
||||
"serde",
|
||||
"serde-well-known",
|
||||
] }
|
||||
hmac = "0.12.1"
|
||||
sha1 = "0.10.5"
|
||||
rand = "0.8.5"
|
||||
hmac = "0.12.0"
|
||||
sha1 = "0.10.0"
|
||||
rand = "0.8.0"
|
||||
base64 = "0.22.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ctor = "0.2.0"
|
||||
rstest = { version = "0.18.0", default-features = false }
|
||||
env_logger = "0.11.0"
|
||||
rstest = { version = "0.23.0", default-features = false }
|
||||
dotenvy = "0.15.5"
|
||||
tokio = { version = "1.20.0", features = ["macros"] }
|
||||
tokio = { version = "1.20.4", features = ["macros"] }
|
||||
futures = "0.3.21"
|
||||
path_macro = "1.0.0"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
governor = "0.7.0"
|
||||
|
|
12
Justfile
12
Justfile
|
@ -10,12 +10,12 @@ release crate="musixmatch-inofficial":
|
|||
CHANGELOG="CHANGELOG.md"
|
||||
|
||||
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
|
||||
if [ ! -d "$CRATE" ]; then
|
||||
echo "$CRATE does not exist."; exit 1
|
||||
fi
|
||||
INCLUDES="$INCLUDES --include-path $CRATE/**"
|
||||
INCLUDES="$INCLUDES --include-path '$CRATE/**'"
|
||||
CHANGELOG="$CRATE/$CHANGELOG"
|
||||
CRATE="musixmatch-$CRATE" # Add crate name prefix
|
||||
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
|
||||
|
||||
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"
|
||||
if [ -f "$CHANGELOG" ]; then
|
||||
git-cliff $CLIFF_ARGS --prepend "$CHANGELOG"
|
||||
eval "git-cliff $CLIFF_ARGS --prepend '$CHANGELOG'"
|
||||
else
|
||||
git-cliff $CLIFF_ARGS --output "$CHANGELOG"
|
||||
eval "git-cliff $CLIFF_ARGS --output '$CHANGELOG'"
|
||||
fi
|
||||
|
||||
editor "$CHANGELOG"
|
||||
|
||||
git add "$CHANGELOG"
|
||||
git add .
|
||||
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"
|
||||
|
|
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
|
||||
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)>),
|
||||
[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.
|
||||
However, as of 2024, this requirement was removed and the API can be used anonymously.
|
||||
The client still allows you to supply credentials if Musixmatch decided to close the API
|
||||
down again.
|
||||
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 client still allows you to supply credentials if Musixmatch decides to
|
||||
close the API down again.
|
||||
|
||||
## ⚠️ Copyright disclaimer
|
||||
|
||||
|
|
39
cli/CHANGELOG.md
Normal file
39
cli/CHANGELOG.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
|
||||
## [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
|
||||
|
||||
<!-- generated by git-cliff -->
|
|
@ -1,6 +1,7 @@
|
|||
[package]
|
||||
name = "musixmatch-cli"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
rust-version = "1.70.0"
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -9,7 +10,7 @@ keywords.workspace = true
|
|||
description = "Inofficial command line interface for the Musixmatch API"
|
||||
|
||||
[features]
|
||||
default = ["rustls-tls-native-roots"]
|
||||
default = ["native-tls"]
|
||||
|
||||
# Reqwest TLS options
|
||||
native-tls = ["musixmatch-inofficial/native-tls"]
|
||||
|
@ -20,11 +21,9 @@ rustls-tls-native-roots = ["musixmatch-inofficial/rustls-tls-native-roots"]
|
|||
|
||||
[dependencies]
|
||||
musixmatch-inofficial.workspace = true
|
||||
tokio = { version = "1.20.0", features = ["macros", "rt-multi-thread"] }
|
||||
id3 = "1.3.0"
|
||||
mp3-duration = "0.1.10"
|
||||
clap = { version = "4.0.10", features = ["derive"] }
|
||||
anyhow = "1.0.65"
|
||||
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"
|
||||
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
|
||||
...
|
||||
```
|
395
cli/src/main.rs
395
cli/src/main.rs
|
@ -1,3 +1,6 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs, clippy::todo)]
|
||||
|
||||
use std::{
|
||||
io::{stdin, stdout, Write},
|
||||
path::PathBuf,
|
||||
|
@ -5,9 +8,8 @@ use std::{
|
|||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use id3::{Tag, TagLike};
|
||||
use musixmatch_inofficial::{
|
||||
models::{SubtitleFormat, Track, TrackId, TranslationMap},
|
||||
models::{AlbumId, ArtistId, SubtitleFormat, Track, TrackId, TranslationMap},
|
||||
Musixmatch,
|
||||
};
|
||||
|
||||
|
@ -20,14 +22,50 @@ struct Cli {
|
|||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
Get {
|
||||
#[command(subcommand)]
|
||||
command: GetCommands,
|
||||
/// Get lyrics text
|
||||
Lyrics {
|
||||
#[clap(flatten)]
|
||||
ident: TrackIdentifiers,
|
||||
/// Language
|
||||
#[clap(long)]
|
||||
lang: Option<String>,
|
||||
/// Bilingual
|
||||
#[clap(long)]
|
||||
bi: bool,
|
||||
},
|
||||
Mp3 {
|
||||
#[command(subcommand)]
|
||||
command: FileCommands,
|
||||
/// Get subtitles (time-synced lyrics)
|
||||
Subtitles {
|
||||
#[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)]
|
||||
Search {
|
||||
/// Track name
|
||||
|
@ -42,40 +80,8 @@ enum Commands {
|
|||
/// Search query
|
||||
query: Option<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,
|
||||
},
|
||||
/// Search for Musixmatch artists
|
||||
SearchArtist { query: Vec<String> },
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
@ -106,22 +112,38 @@ struct TrackIdentifiers {
|
|||
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)]
|
||||
enum FileCommands {
|
||||
/// Get lyrics text
|
||||
Lyrics {
|
||||
/// Music file
|
||||
#[clap(value_parser)]
|
||||
file: PathBuf,
|
||||
},
|
||||
/// Get subtitles (time-synced lyrics)
|
||||
Subtitles {
|
||||
/// Music file
|
||||
#[clap(value_parser)]
|
||||
file: PathBuf,
|
||||
/// Subtitle format
|
||||
#[clap(short, long, default_value = "lrc")]
|
||||
format: SubtitleFormatClap,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(clap::ValueEnum, Debug, Copy, Clone)]
|
||||
pub enum SubtitleFormatClap {
|
||||
enum SubtitleFormatClap {
|
||||
Lrc,
|
||||
Ttml,
|
||||
TtmlStructured,
|
||||
|
@ -175,164 +197,148 @@ async fn run(cli: Cli) -> Result<()> {
|
|||
};
|
||||
|
||||
match cli.command {
|
||||
Commands::Get { command } => match command {
|
||||
GetCommands::Lyrics { ident, lang, bi } => {
|
||||
let track_id = get_track_id(ident, &mxm).await?;
|
||||
let lyrics = mxm.track_lyrics(track_id.clone()).await?;
|
||||
Commands::Lyrics { ident, lang, bi } => {
|
||||
let track_id = get_track_id(ident, &mxm).await?;
|
||||
let lyrics = mxm.track_lyrics(track_id.clone()).await?;
|
||||
|
||||
eprintln!("Lyrics ID: {}", lyrics.lyrics_id);
|
||||
eprintln!(
|
||||
"Language: {}",
|
||||
lyrics.lyrics_language.as_deref().unwrap_or(NA_STR)
|
||||
);
|
||||
eprintln!(
|
||||
"Copyright: {}",
|
||||
lyrics
|
||||
.lyrics_copyright
|
||||
.as_deref()
|
||||
.map(|c| c.trim())
|
||||
.unwrap_or(NA_STR)
|
||||
);
|
||||
eprintln!("Lyrics ID: {}", lyrics.lyrics_id);
|
||||
eprintln!(
|
||||
"Language: {}",
|
||||
lyrics.lyrics_language.as_deref().unwrap_or(NA_STR)
|
||||
);
|
||||
eprintln!(
|
||||
"Copyright: {}",
|
||||
lyrics
|
||||
.lyrics_copyright
|
||||
.as_deref()
|
||||
.map(|c| c.trim())
|
||||
.unwrap_or(NA_STR)
|
||||
);
|
||||
|
||||
let mut lyrics_body = lyrics.lyrics_body;
|
||||
let mut lyrics_body = lyrics.lyrics_body;
|
||||
|
||||
if let Some(lang) = lang {
|
||||
if Some(&lang) != lyrics.lyrics_language.as_ref() {
|
||||
let tl = mxm.track_lyrics_translation(track_id, &lang).await?;
|
||||
if tl.is_empty() {
|
||||
eprintln!(
|
||||
"Translation not found. Returning lyrics in original language."
|
||||
);
|
||||
if let Some(lang) = lang {
|
||||
if Some(&lang) != lyrics.lyrics_language.as_ref() {
|
||||
let tl = mxm.track_lyrics_translation(track_id, &lang).await?;
|
||||
if tl.is_empty() {
|
||||
eprintln!("Translation not found. Returning lyrics in original language.");
|
||||
} else {
|
||||
eprintln!("Translated to: {}", tl.lang);
|
||||
let tm = TranslationMap::from(tl);
|
||||
let translated = tm.translate_lyrics(&lyrics_body);
|
||||
lyrics_body = if bi {
|
||||
lyrics_body
|
||||
.lines()
|
||||
.zip(translated.lines())
|
||||
.map(|(a, b)| {
|
||||
if a == b {
|
||||
a.to_string() + "\n"
|
||||
} else {
|
||||
format!("{a}\n> {b}\n")
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
eprintln!("Translated to: {}", tl.lang);
|
||||
let tm = TranslationMap::from(tl);
|
||||
let translated = tm.translate_lyrics(&lyrics_body);
|
||||
lyrics_body = if bi {
|
||||
lyrics_body
|
||||
.lines()
|
||||
.zip(translated.lines())
|
||||
.map(|(a, b)| {
|
||||
if a == b {
|
||||
a.to_string() + "\n"
|
||||
} else {
|
||||
format!("{a}\n> {b}\n")
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
translated
|
||||
};
|
||||
}
|
||||
translated
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!();
|
||||
println!("{}", lyrics_body);
|
||||
}
|
||||
Commands::Subtitles {
|
||||
ident,
|
||||
length,
|
||||
max_deviation,
|
||||
format,
|
||||
lang,
|
||||
} => {
|
||||
let track_id = get_track_id(ident, &mxm).await?;
|
||||
let subtitles = mxm
|
||||
.track_subtitle(
|
||||
track_id.clone(),
|
||||
if lang.is_some() {
|
||||
SubtitleFormat::Json
|
||||
} else {
|
||||
format.into()
|
||||
},
|
||||
length,
|
||||
max_deviation.or(Some(1.0)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
eprintln!("Subtitle ID: {}", subtitles.subtitle_id);
|
||||
eprintln!(
|
||||
"Language: {}",
|
||||
subtitles.subtitle_language.as_deref().unwrap_or(NA_STR)
|
||||
);
|
||||
eprintln!("Length: {}", subtitles.subtitle_length);
|
||||
eprintln!(
|
||||
"Copyright: {}",
|
||||
subtitles
|
||||
.lyrics_copyright
|
||||
.as_deref()
|
||||
.map(|s| s.trim())
|
||||
.unwrap_or(NA_STR)
|
||||
);
|
||||
|
||||
if let Some(lang) = lang {
|
||||
let mut lines = subtitles.to_lines()?;
|
||||
|
||||
if Some(&lang) != subtitles.subtitle_language.as_ref() {
|
||||
let tl = mxm.track_lyrics_translation(track_id, &lang).await?;
|
||||
if tl.is_empty() {
|
||||
bail!("Translation not found")
|
||||
} else {
|
||||
eprintln!("Translated to: {}", tl.lang);
|
||||
let tm = TranslationMap::from(tl);
|
||||
lines = tm.translate_subtitles(&lines);
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!();
|
||||
println!("{}", lyrics_body);
|
||||
}
|
||||
GetCommands::Subtitles {
|
||||
ident,
|
||||
length,
|
||||
max_deviation,
|
||||
format,
|
||||
lang,
|
||||
} => {
|
||||
let track_id = get_track_id(ident, &mxm).await?;
|
||||
let subtitles = mxm
|
||||
.track_subtitle(
|
||||
track_id.clone(),
|
||||
if lang.is_some() {
|
||||
SubtitleFormat::Json
|
||||
} else {
|
||||
format.into()
|
||||
},
|
||||
length,
|
||||
max_deviation.or(Some(1.0)),
|
||||
)
|
||||
.await?;
|
||||
|
||||
eprintln!("Subtitle ID: {}", subtitles.subtitle_id);
|
||||
eprintln!(
|
||||
"Language: {}",
|
||||
subtitles.subtitle_language.as_deref().unwrap_or(NA_STR)
|
||||
);
|
||||
eprintln!("Length: {}", subtitles.subtitle_length);
|
||||
eprintln!(
|
||||
"Copyright: {}",
|
||||
subtitles
|
||||
.lyrics_copyright
|
||||
.as_deref()
|
||||
.map(|s| s.trim())
|
||||
.unwrap_or(NA_STR)
|
||||
);
|
||||
|
||||
if let Some(lang) = lang {
|
||||
let mut lines = subtitles.to_lines()?;
|
||||
|
||||
if Some(&lang) != subtitles.subtitle_language.as_ref() {
|
||||
let tl = mxm.track_lyrics_translation(track_id, &lang).await?;
|
||||
if tl.is_empty() {
|
||||
bail!("Translation not found")
|
||||
} else {
|
||||
eprintln!("Translated to: {}", tl.lang);
|
||||
let tm = TranslationMap::from(tl);
|
||||
lines = tm.translate_subtitles(&lines);
|
||||
}
|
||||
let res = match format {
|
||||
SubtitleFormatClap::Lrc => lines.to_lrc(),
|
||||
SubtitleFormatClap::Ttml => lines.to_ttml(),
|
||||
SubtitleFormatClap::Json => lines.to_json()?,
|
||||
SubtitleFormatClap::TtmlStructured | SubtitleFormatClap::EbuStl => {
|
||||
bail!("subtitle format {format:?} cant be translated")
|
||||
}
|
||||
|
||||
eprintln!();
|
||||
let res = match format {
|
||||
SubtitleFormatClap::Lrc => lines.to_lrc(),
|
||||
SubtitleFormatClap::Ttml => lines.to_ttml(),
|
||||
SubtitleFormatClap::Json => lines.to_json()?,
|
||||
SubtitleFormatClap::TtmlStructured | SubtitleFormatClap::EbuStl => {
|
||||
bail!("subtitle format {format:?} cant be translated")
|
||||
}
|
||||
};
|
||||
println!("{}", res);
|
||||
} else {
|
||||
eprintln!();
|
||||
println!("{}", subtitles.subtitle_body);
|
||||
}
|
||||
}
|
||||
GetCommands::Track { ident } => {
|
||||
let track = get_track(ident, &mxm).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&track)?)
|
||||
}
|
||||
},
|
||||
Commands::Mp3 { command } => match command {
|
||||
FileCommands::Lyrics { file } => {
|
||||
let tag = Tag::read_from_path(&file)?;
|
||||
|
||||
let title = tag.title().ok_or(anyhow!("no title"))?;
|
||||
let artist = tag.artist().ok_or(anyhow!("no artist"))?;
|
||||
|
||||
let lyrics = mxm.matcher_lyrics(title, artist).await?;
|
||||
|
||||
println!(
|
||||
"Lyrics for {} by {}:\n\n{}",
|
||||
title, artist, lyrics.lyrics_body
|
||||
);
|
||||
}
|
||||
FileCommands::Subtitles { file } => {
|
||||
let tag = Tag::read_from_path(&file)?;
|
||||
let duration = mp3_duration::from_path(&file)?;
|
||||
|
||||
let title = tag.title().ok_or(anyhow!("no title"))?;
|
||||
let artist = tag.artist().ok_or(anyhow!("no artist"))?;
|
||||
|
||||
let subtitles = mxm
|
||||
.matcher_subtitle(
|
||||
title,
|
||||
artist,
|
||||
SubtitleFormat::Lrc,
|
||||
Some(duration.as_secs_f32()),
|
||||
Some(1.0),
|
||||
)
|
||||
.await?;
|
||||
|
||||
};
|
||||
println!("{}", res);
|
||||
} else {
|
||||
eprintln!();
|
||||
println!("{}", subtitles.subtitle_body);
|
||||
}
|
||||
},
|
||||
}
|
||||
Commands::Track { ident } => {
|
||||
let track = get_track(ident, &mxm).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&track)?)
|
||||
}
|
||||
Commands::Album { ident } => {
|
||||
let id = if let Some(id) = ident.mxm_id {
|
||||
AlbumId::AlbumId(id)
|
||||
} else if let Some(mb) = &ident.musicbrainz {
|
||||
AlbumId::Musicbrainz(mb)
|
||||
} else {
|
||||
bail!("no album ID specified")
|
||||
};
|
||||
let album = mxm.album(id).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&album)?)
|
||||
}
|
||||
Commands::Artist { ident } => {
|
||||
let id = if let Some(id) = ident.mxm_id {
|
||||
ArtistId::ArtistId(id)
|
||||
} else if let Some(mb) = &ident.musicbrainz {
|
||||
ArtistId::Musicbrainz(mb)
|
||||
} else {
|
||||
bail!("no artist ID specified")
|
||||
};
|
||||
let album = mxm.artist(id).await?;
|
||||
println!("{}", serde_json::to_string_pretty(&album)?)
|
||||
}
|
||||
Commands::Search {
|
||||
query,
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -14,12 +14,13 @@ All notable changes to this project will be documented in this file.\n
|
|||
# template for the changelog body
|
||||
# https://keats.github.io/tera/docs/#introduction
|
||||
body = """
|
||||
{% set repo_url = "https://code.thetadev.de/ThetaDev/rustypipe" %}\
|
||||
{% set repo_url = "https://codeberg.org/ThetaDev/musixmatch-inofficial" %}\
|
||||
{% if version %}\
|
||||
{%set vname = version | split(pat="/") | last %}
|
||||
{%if previous.version %}\
|
||||
## [{{ version }}]({{ repo_url }}/compare/{{ previous.version }}..{{ version }})\
|
||||
## [{{ vname }}]({{ repo_url }}/compare/{{ previous.version }}..{{ version }})\
|
||||
{% else %}\
|
||||
## {{ version }}\
|
||||
## [{{ vname }}]({{ repo_url }}/commits/tag/{{ version }})\
|
||||
{% endif %} - {{ timestamp | date(format="%Y-%m-%d") }}
|
||||
{% else %}\
|
||||
## [unreleased]
|
||||
|
@ -73,7 +74,7 @@ commit_parsers = [
|
|||
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
|
||||
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
|
||||
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
|
||||
{ message = "^chore\\(release\\): prepare for", skip = true },
|
||||
{ message = "^chore\\(release\\)", skip = true },
|
||||
{ message = "^chore\\(pr\\)", skip = true },
|
||||
{ message = "^chore\\(pull\\)", skip = true },
|
||||
{ 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)]
|
||||
pub struct Account {
|
||||
pub id: String,
|
||||
pub email: String,
|
||||
// pub id: String,
|
||||
// pub email: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
|
328
tests/tests.rs
328
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 rstest::rstest;
|
||||
use rstest::{fixture, rstest};
|
||||
use time::macros::{date, datetime};
|
||||
|
||||
use musixmatch_inofficial::{
|
||||
|
@ -9,19 +14,18 @@ use musixmatch_inofficial::{
|
|||
Error, Musixmatch,
|
||||
};
|
||||
|
||||
#[ctor::ctor]
|
||||
fn init() {
|
||||
let _ = dotenvy::dotenv();
|
||||
env_logger::init();
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(new_mxm().login())
|
||||
.unwrap();
|
||||
fn testfile<P: AsRef<Path>>(name: P) -> PathBuf {
|
||||
path!(env!("CARGO_MANIFEST_DIR") / "testfiles" / name)
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if let (Ok(email), Ok(password)) = (
|
||||
|
@ -31,11 +35,10 @@ fn new_mxm() -> Musixmatch {
|
|||
mxm = mxm.credentials(email, password);
|
||||
}
|
||||
|
||||
mxm.build().unwrap()
|
||||
}
|
||||
let mxm = mxm.build().unwrap();
|
||||
|
||||
fn testfile<P: AsRef<Path>>(name: P) -> PathBuf {
|
||||
path!(env!("CARGO_MANIFEST_DIR") / "testfiles" / name)
|
||||
LOGIN_LOCK.get_or_try_init(|| mxm.login()).await.unwrap();
|
||||
mxm
|
||||
}
|
||||
|
||||
mod album {
|
||||
|
@ -46,8 +49,8 @@ mod album {
|
|||
#[case::id(AlbumId::AlbumId(14248253))]
|
||||
#[case::musicbrainz(AlbumId::Musicbrainz("6c3cf9d8-88a8-43ed-850b-55813f01e451"))]
|
||||
#[tokio::test]
|
||||
async fn by_id(#[case] album_id: AlbumId<'_>) {
|
||||
let album = new_mxm().album(album_id).await.unwrap();
|
||||
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!(
|
||||
|
@ -56,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);
|
||||
|
@ -94,17 +97,20 @@ mod album {
|
|||
assert_imgurl(&album.album_coverart_500x500, "/26544045_500_500.jpg");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn album_ep() {
|
||||
let album = new_mxm().album(AlbumId::AlbumId(23976123)).await.unwrap();
|
||||
async fn album_ep(#[future] mxm: Musixmatch) {
|
||||
let album = mxm.await.album(AlbumId::AlbumId(23976123)).await.unwrap();
|
||||
assert_eq!(album.album_name, "Waldbrand EP");
|
||||
// assert_eq!(album.album_release_type, AlbumType::Ep);
|
||||
assert_eq!(album.album_release_date, Some(date!(2016 - 09 - 30)));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn by_id_missing() {
|
||||
let err = new_mxm()
|
||||
async fn by_id_missing(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.album(AlbumId::AlbumId(999999999999))
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
@ -112,9 +118,12 @@ mod album {
|
|||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn artist_albums() {
|
||||
let albums = new_mxm()
|
||||
#[ignore]
|
||||
async fn artist_albums(#[future] mxm: Musixmatch) {
|
||||
let albums = mxm
|
||||
.await
|
||||
.artist_albums(ArtistId::ArtistId(1039), None, 10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -122,9 +131,11 @@ mod album {
|
|||
assert_eq!(albums.len(), 10);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn artist_albums_missing() {
|
||||
let err = new_mxm()
|
||||
async fn artist_albums_missing(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.artist_albums(ArtistId::ArtistId(999999999999), None, 10, 1)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
@ -132,9 +143,10 @@ mod album {
|
|||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn charts() {
|
||||
let albums = new_mxm().chart_albums("US", 10, 1).await.unwrap();
|
||||
async fn charts(#[future] mxm: Musixmatch) {
|
||||
let albums = mxm.await.chart_albums("US", 10, 1).await.unwrap();
|
||||
|
||||
assert_eq!(albums.len(), 10);
|
||||
}
|
||||
|
@ -147,8 +159,8 @@ mod artist {
|
|||
#[case::id(ArtistId::ArtistId(410698))]
|
||||
#[case::musicbrainz(ArtistId::Musicbrainz("f99b7d67-4e63-4678-aa66-4c6ac0f7d24a"))]
|
||||
#[tokio::test]
|
||||
async fn by_id(#[case] artist_id: ArtistId<'_>) {
|
||||
let artist = new_mxm().artist(artist_id).await.unwrap();
|
||||
async fn by_id(#[case] artist_id: ArtistId<'_>, #[future] mxm: Musixmatch) {
|
||||
let artist = mxm.await.artist(artist_id).await.unwrap();
|
||||
|
||||
// dbg!(&artist);
|
||||
|
||||
|
@ -190,9 +202,11 @@ mod artist {
|
|||
assert_eq!(artist.end_date, None);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn by_id_missing() {
|
||||
let err = new_mxm()
|
||||
async fn by_id_missing(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.artist(ArtistId::ArtistId(999999999999))
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
@ -200,9 +214,11 @@ mod artist {
|
|||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn related() {
|
||||
let artists = new_mxm()
|
||||
async fn related(#[future] mxm: Musixmatch) {
|
||||
let artists = mxm
|
||||
.await
|
||||
.artist_related(ArtistId::ArtistId(26485840), 10, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -210,9 +226,11 @@ mod artist {
|
|||
assert_eq!(artists.len(), 10);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn related_missing() {
|
||||
let err = new_mxm()
|
||||
async fn related_missing(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.artist_related(ArtistId::ArtistId(999999999999), 10, 1)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
@ -220,20 +238,25 @@ mod artist {
|
|||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn search() {
|
||||
let artists = new_mxm().artist_search("psy", 5, 1).await.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 5);
|
||||
async fn search(#[future] mxm: Musixmatch) {
|
||||
let artists = mxm
|
||||
.await
|
||||
.artist_search("Snollebollekes", 5, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let artist = &artists[0];
|
||||
assert_eq!(artist.artist_id, 410698);
|
||||
assert_eq!(artist.artist_name, "PSY");
|
||||
assert_eq!(artist.artist_id, 25344078);
|
||||
assert_eq!(artist.artist_name, "Snollebollekes");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn search_empty() {
|
||||
let artists = new_mxm()
|
||||
async fn search_empty(#[future] mxm: Musixmatch) {
|
||||
let artists = mxm
|
||||
.await
|
||||
.artist_search(
|
||||
"Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz",
|
||||
5,
|
||||
|
@ -245,16 +268,18 @@ mod artist {
|
|||
assert_eq!(artists.len(), 0);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn charts() {
|
||||
let artists = new_mxm().chart_artists("US", 10, 1).await.unwrap();
|
||||
async fn charts(#[future] mxm: Musixmatch) {
|
||||
let artists = mxm.await.chart_artists("US", 10, 1).await.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 10);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn charts_no_country() {
|
||||
let artists = new_mxm().chart_artists("XY", 10, 1).await.unwrap();
|
||||
async fn charts_no_country(#[future] mxm: Musixmatch) {
|
||||
let artists = mxm.await.chart_artists("XY", 10, 1).await.unwrap();
|
||||
|
||||
assert_eq!(artists.len(), 10);
|
||||
}
|
||||
|
@ -269,8 +294,13 @@ mod track {
|
|||
#[case::translation_2c(true, false)]
|
||||
#[case::translation_3c(true, true)]
|
||||
#[tokio::test]
|
||||
async fn from_match(#[case] translation_status: bool, #[case] lang_3c: bool) {
|
||||
let track = new_mxm()
|
||||
async fn from_match(
|
||||
#[case] translation_status: bool,
|
||||
#[case] lang_3c: bool,
|
||||
#[future] mxm: Musixmatch,
|
||||
) {
|
||||
let track = mxm
|
||||
.await
|
||||
.matcher_track(
|
||||
"Poker Face",
|
||||
"Lady Gaga",
|
||||
|
@ -283,12 +313,12 @@ mod track {
|
|||
|
||||
// dbg!(&track);
|
||||
|
||||
assert_eq!(track.track_id, 15476784);
|
||||
assert_eq!(
|
||||
track.track_mbid.unwrap(),
|
||||
"080975b0-39b1-493c-ae64-5cb3292409bb"
|
||||
);
|
||||
assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
||||
assert_eq!(track.track_id, 85213841);
|
||||
// assert_eq!(
|
||||
// track.track_mbid.unwrap(),
|
||||
// "080975b0-39b1-493c-ae64-5cb3292409bb"
|
||||
// );
|
||||
// assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
||||
assert!(
|
||||
track.commontrack_isrcs[0]
|
||||
.iter()
|
||||
|
@ -296,7 +326,7 @@ mod track {
|
|||
"commontrack_isrcs: {:?}",
|
||||
&track.commontrack_isrcs[0],
|
||||
);
|
||||
assert_eq!(track.track_spotify_id.unwrap(), "5R8dQOPq8haW94K7mgERlO");
|
||||
assert_eq!(track.track_spotify_id.unwrap(), "1QV6tiMFM6fSOKOGLMHYYg");
|
||||
assert!(
|
||||
track
|
||||
.commontrack_spotify_ids
|
||||
|
@ -316,7 +346,7 @@ mod track {
|
|||
assert!(track.num_favourite > 50);
|
||||
assert!(track.lyrics_id.is_some());
|
||||
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.artist_id, 378462);
|
||||
assert_eq!(
|
||||
|
@ -324,9 +354,10 @@ mod track {
|
|||
"650e7db6-b795-4eb5-a702-5ea2fc46c848"
|
||||
);
|
||||
assert_eq!(track.artist_name, "Lady Gaga");
|
||||
assert_imgurl(&track.album_coverart_100x100, "/26319636.jpg");
|
||||
assert_imgurl(&track.album_coverart_350x350, "/26319636_350_350.jpg");
|
||||
assert_imgurl(&track.album_coverart_500x500, "/26319636_500_500.jpg");
|
||||
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));
|
||||
|
@ -374,8 +405,8 @@ mod track {
|
|||
#[case::isrc(TrackId::Isrc("KRA302000590".into()))]
|
||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))]
|
||||
#[tokio::test]
|
||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
||||
let track = new_mxm().track(track_id, true, false).await.unwrap();
|
||||
async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) {
|
||||
let track = mxm.await.track(track_id, true, false).await.unwrap();
|
||||
|
||||
// dbg!(&track);
|
||||
|
||||
|
@ -415,20 +446,25 @@ mod track {
|
|||
#[case::translation_2c(true, false)]
|
||||
#[case::translation_3c(true, true)]
|
||||
#[tokio::test]
|
||||
async fn from_id_translations(#[case] translation_status: bool, #[case] lang_3c: bool) {
|
||||
let track = new_mxm()
|
||||
.track(TrackId::TrackId(15476784), translation_status, lang_3c)
|
||||
async fn from_id_translations(
|
||||
#[case] translation_status: bool,
|
||||
#[case] lang_3c: bool,
|
||||
#[future] mxm: Musixmatch,
|
||||
) {
|
||||
let track = mxm
|
||||
.await
|
||||
.track(TrackId::Commontrack(47672612), translation_status, lang_3c)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// dbg!(&track);
|
||||
|
||||
assert_eq!(track.track_id, 15476784);
|
||||
assert_eq!(
|
||||
track.track_mbid.unwrap(),
|
||||
"080975b0-39b1-493c-ae64-5cb3292409bb"
|
||||
);
|
||||
assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
||||
assert_eq!(track.track_id, 85213841);
|
||||
// assert_eq!(
|
||||
// track.track_mbid.unwrap(),
|
||||
// "080975b0-39b1-493c-ae64-5cb3292409bb"
|
||||
// );
|
||||
// assert_eq!(track.track_isrc.unwrap(), "USUM70824409");
|
||||
assert!(
|
||||
track.commontrack_isrcs[0]
|
||||
.iter()
|
||||
|
@ -436,7 +472,7 @@ mod track {
|
|||
"commontrack_isrcs: {:?}",
|
||||
&track.commontrack_isrcs[0],
|
||||
);
|
||||
assert_eq!(track.track_spotify_id.unwrap(), "5R8dQOPq8haW94K7mgERlO");
|
||||
assert_eq!(track.track_spotify_id.unwrap(), "1QV6tiMFM6fSOKOGLMHYYg");
|
||||
assert!(
|
||||
track
|
||||
.commontrack_spotify_ids
|
||||
|
@ -456,7 +492,7 @@ mod track {
|
|||
assert!(track.num_favourite > 50);
|
||||
assert!(track.lyrics_id.is_some());
|
||||
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.artist_id, 378462);
|
||||
assert_eq!(
|
||||
|
@ -464,9 +500,10 @@ mod track {
|
|||
"650e7db6-b795-4eb5-a702-5ea2fc46c848"
|
||||
);
|
||||
assert_eq!(track.artist_name, "Lady Gaga");
|
||||
assert_imgurl(&track.album_coverart_100x100, "/26319636.jpg");
|
||||
assert_imgurl(&track.album_coverart_350x350, "/26319636_350_350.jpg");
|
||||
assert_imgurl(&track.album_coverart_500x500, "/26319636_500_500.jpg");
|
||||
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));
|
||||
|
@ -507,9 +544,11 @@ mod track {
|
|||
}
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn from_id_missing() {
|
||||
let err = new_mxm()
|
||||
async fn from_id_missing(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.track(TrackId::TrackId(999999999999), false, false)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
@ -517,9 +556,11 @@ mod track {
|
|||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn album_tracks() {
|
||||
let tracks = new_mxm()
|
||||
async fn album_tracks(#[future] mxm: Musixmatch) {
|
||||
let tracks = mxm
|
||||
.await
|
||||
.album_tracks(AlbumId::AlbumId(17118624), true, 20, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -556,9 +597,11 @@ mod track {
|
|||
});
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn album_missing() {
|
||||
let err = new_mxm()
|
||||
async fn album_missing(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.album_tracks(AlbumId::AlbumId(999999999999), false, 20, 1)
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
@ -570,8 +613,9 @@ mod track {
|
|||
#[case::top(ChartName::Top)]
|
||||
#[case::hot(ChartName::Hot)]
|
||||
#[tokio::test]
|
||||
async fn charts(#[case] chart_name: ChartName) {
|
||||
let tracks = new_mxm()
|
||||
async fn charts(#[case] chart_name: ChartName, #[future] mxm: Musixmatch) {
|
||||
let tracks = mxm
|
||||
.await
|
||||
.chart_tracks("US", chart_name, true, 20, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -579,9 +623,11 @@ mod track {
|
|||
assert_eq!(tracks.len(), 20);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn search() {
|
||||
let tracks = new_mxm()
|
||||
async fn search(#[future] mxm: Musixmatch) {
|
||||
let tracks = mxm
|
||||
.await
|
||||
.track_search()
|
||||
.q_artist("Lena")
|
||||
.q_track("Satellite")
|
||||
|
@ -600,9 +646,11 @@ mod track {
|
|||
assert_eq!(track.artist_name, "Lena");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn search_lyrics() {
|
||||
let tracks = new_mxm()
|
||||
async fn search_lyrics(#[future] mxm: Musixmatch) {
|
||||
let tracks = mxm
|
||||
.await
|
||||
.track_search()
|
||||
.q_lyrics("the whole world stops and stares for a while")
|
||||
.s_track_rating(SortOrder::Desc)
|
||||
|
@ -610,16 +658,18 @@ mod track {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(tracks.len(), 10);
|
||||
assert_gte(tracks.len(), 8, "tracks");
|
||||
|
||||
let track = &tracks[0];
|
||||
assert_eq!(track.track_name, "Just the Way You Are");
|
||||
assert_eq!(track.artist_name, "Bruno Mars");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn search_empty() {
|
||||
let artists = new_mxm()
|
||||
async fn search_empty(#[future] mxm: Musixmatch) {
|
||||
let artists = mxm
|
||||
.await
|
||||
.track_search()
|
||||
.q("Rindfleischettikettierungsüberwachungsaufgabenübertragungsgesetz")
|
||||
.send(10, 1)
|
||||
|
@ -629,16 +679,19 @@ mod track {
|
|||
assert_eq!(artists.len(), 0);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn genres() {
|
||||
let genres = new_mxm().genres().await.unwrap();
|
||||
async fn genres(#[future] mxm: Musixmatch) {
|
||||
let genres = mxm.await.genres().await.unwrap();
|
||||
assert!(genres.len() > 360);
|
||||
dbg!(&genres);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn snippet() {
|
||||
let snippet = new_mxm()
|
||||
async fn snippet(#[future] mxm: Musixmatch) {
|
||||
let snippet = mxm
|
||||
.await
|
||||
.track_snippet(TrackId::Commontrack(8874280))
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -660,9 +713,10 @@ mod lyrics {
|
|||
|
||||
use super::*;
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn from_match() {
|
||||
let lyrics = new_mxm().matcher_lyrics("Shine", "Spektrem").await.unwrap();
|
||||
async fn from_match(#[future] mxm: Musixmatch) {
|
||||
let lyrics = mxm.await.matcher_lyrics("Shine", "Spektrem").await.unwrap();
|
||||
|
||||
// dbg!(&lyrics);
|
||||
|
||||
|
@ -690,8 +744,8 @@ mod lyrics {
|
|||
#[case::isrc(TrackId::Isrc("KRA302000590".into()))]
|
||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))]
|
||||
#[tokio::test]
|
||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
||||
let lyrics = new_mxm().track_lyrics(track_id).await.unwrap();
|
||||
async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) {
|
||||
let lyrics = mxm.await.track_lyrics(track_id).await.unwrap();
|
||||
|
||||
// dbg!(&lyrics);
|
||||
|
||||
|
@ -707,9 +761,11 @@ mod lyrics {
|
|||
}
|
||||
|
||||
/// This track has no lyrics
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn instrumental() {
|
||||
let lyrics = new_mxm()
|
||||
async fn instrumental(#[future] mxm: Musixmatch) {
|
||||
let lyrics = mxm
|
||||
.await
|
||||
.matcher_lyrics("drivers license", "Bobby G")
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -725,9 +781,11 @@ mod lyrics {
|
|||
}
|
||||
|
||||
/// This track does not exist
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn missing() {
|
||||
let err = new_mxm()
|
||||
async fn missing(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.track_lyrics(TrackId::Spotify("674JwwTP7xCje81T0DRrLn".into()))
|
||||
.await
|
||||
.unwrap_err();
|
||||
|
@ -735,14 +793,16 @@ mod lyrics {
|
|||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn download_testdata() {
|
||||
async fn download_testdata(#[future] mxm: Musixmatch) {
|
||||
let mxm = mxm.await;
|
||||
let json_path = testfile("lyrics.json");
|
||||
if json_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let lyrics = new_mxm()
|
||||
let lyrics = mxm
|
||||
.track_lyrics(TrackId::Commontrack(18576954))
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -751,14 +811,16 @@ mod lyrics {
|
|||
serde_json::to_writer_pretty(BufWriter::new(json_file), &lyrics).unwrap();
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[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");
|
||||
if json_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let translations = new_mxm()
|
||||
let translations = mxm
|
||||
.track_lyrics_translation(TrackId::Commontrack(18576954), "de")
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -767,9 +829,10 @@ mod lyrics {
|
|||
serde_json::to_writer_pretty(BufWriter::new(json_file), &translations).unwrap();
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn concurrency() {
|
||||
let mxm = new_mxm();
|
||||
async fn concurrency(#[future] mxm: Musixmatch) {
|
||||
let mxm = mxm.await;
|
||||
|
||||
let album = mxm
|
||||
.album_tracks(
|
||||
|
@ -804,9 +867,11 @@ mod subtitles {
|
|||
use super::*;
|
||||
use musixmatch_inofficial::models::SubtitleFormat;
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn from_match() {
|
||||
let subtitle = new_mxm()
|
||||
async fn from_match(#[future] mxm: Musixmatch) {
|
||||
let subtitle = mxm
|
||||
.await
|
||||
.matcher_subtitle(
|
||||
"Shine",
|
||||
"Spektrem",
|
||||
|
@ -835,8 +900,9 @@ mod subtitles {
|
|||
#[case::isrc(TrackId::Isrc("KRA302000590".into()))]
|
||||
#[case::spotify(TrackId::Spotify("1t2qYCAjUAoGfeFeoBlK51".into()))]
|
||||
#[tokio::test]
|
||||
async fn from_id(#[case] track_id: TrackId<'_>) {
|
||||
let subtitle = new_mxm()
|
||||
async fn from_id(#[case] track_id: TrackId<'_>, #[future] mxm: Musixmatch) {
|
||||
let subtitle = mxm
|
||||
.await
|
||||
.track_subtitle(track_id, SubtitleFormat::Json, Some(175.0), Some(1.0))
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -856,9 +922,11 @@ mod subtitles {
|
|||
}
|
||||
|
||||
/// This track has no lyrics
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn instrumental() {
|
||||
let err = new_mxm()
|
||||
async fn instrumental(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.matcher_subtitle(
|
||||
"drivers license",
|
||||
"Bobby G",
|
||||
|
@ -873,9 +941,11 @@ mod subtitles {
|
|||
}
|
||||
|
||||
/// This track has not been synced
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn unsynced() {
|
||||
let err = new_mxm()
|
||||
async fn unsynced(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.track_subtitle(
|
||||
TrackId::Spotify("6oaWIABGL7eeiMILEDyGX1".into()),
|
||||
SubtitleFormat::Json,
|
||||
|
@ -889,9 +959,11 @@ mod subtitles {
|
|||
}
|
||||
|
||||
/// Try to get subtitles with wrong length parameter
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn wrong_length() {
|
||||
let err = new_mxm()
|
||||
async fn wrong_length(#[future] mxm: Musixmatch) {
|
||||
let err = mxm
|
||||
.await
|
||||
.track_subtitle(
|
||||
TrackId::Commontrack(118480583),
|
||||
SubtitleFormat::Json,
|
||||
|
@ -904,14 +976,16 @@ mod subtitles {
|
|||
assert!(matches!(err, Error::NotFound));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn download_testdata() {
|
||||
async fn download_testdata(#[future] mxm: Musixmatch) {
|
||||
let json_path = testfile("subtitles.json");
|
||||
if json_path.exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
let subtitle = new_mxm()
|
||||
let subtitle = mxm
|
||||
.await
|
||||
.track_subtitle(
|
||||
TrackId::Commontrack(18576954),
|
||||
SubtitleFormat::Json,
|
||||
|
@ -973,3 +1047,9 @@ fn assert_imgurl(url: &Option<String>, ends_with: &str) {
|
|||
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