uta
A command-line music search / download tool written in Rust, supporting QQ Music and NetEase Cloud Music. It ships as a single static executable — no Python, ffmpeg, or other runtime required — and prefers lossless audio.
The logic is ported from QQMusicClient / NeteaseMusicClient in musicdl,
keeping only the "fast lossless" path.
⚠️ For learning and research only. The original project is licensed under PolyForm Noncommercial 1.0.0; as a derivative work, this project is likewise not for commercial use. Please obtain paid content through official channels.
Install
- From crates.io:
cargo install uta - Prebuilt binaries: each release also builds Linux x86_64 (fully static musl), macOS (Apple Silicon / Intel) and Windows x64 archives; they are attached to the project's GitHub Releases (the repository is currently private).
Then create a config file (see Configuration) — without third-party endpoints you usually won't get lossless audio.
Shell completion (bash / zsh / fish / powershell / elvish):
Usage
|| |
Sources
# the same song found on both is kept once, in the better quality (--no-merge lists all)
-s works for search, get, album, artist and playlist; only search accepts multiple sources.
Cross-source fallback: when a track can't be resolved on its own source (e.g. a greyed-out song in a QQ album),
uta searches the other source for the same song (same title and first artist, duration within 5s) and downloads that audio,
while keeping the original title / artist / album / track number / lyrics / cover. Such rows are marked (补) in the table
and carry "fallback" in JSON. Disable with --no-fallback.
Output: <output>/<title> - <artist>.flac plus a matching .lrc, with title / artist / album / lyrics / cover tags embedded.
Downloads are resumable: after an interruption (Ctrl-C, network loss, failure) the partial file is kept as .part,
and running the same command again continues from where it stopped. Exit code is 130 on Ctrl-C and 1 if any track failed.
Configuration
The config file lives at ~/.config/uta/config.toml on Linux (the platform config directory elsewhere), or pass --config <path>.
Third-party endpoint URLs are only read from the config file and are never compiled into the binary.
[vkeys]/[tang]: QQ Music third-party endpoints. vkeys is the primary one; tang is a rate-limited fallback when vkeys can't provide lossless. Without them only the official endpoint is used (usually no lossless).[netease.tmetu]/[netease.chksz]/[netease.jfjt]: NetEase third-party endpoints (no keys needed). Without them only the official endpoint is used, which gives 320k for free tracks and nothing for paid ones when not logged in.[defaults]: defaultsource,output,max_quality,jobs,download_jobs.source = "163"makes NetEase the default;source = ["qq", "163"]makessearchuse both (other commands use the first). Priority: command line > config file > built-in defaults.
Design
| Aspect | Choice |
|---|---|
| Search | QQ official musicu.fcg DoSearchForQQMusicMobile; NetEase cloudsearch/pc |
| Download links | QQ: third-party (vkeys → tang) first, official GetVkey as fallback; NetEase: third-party (tmetu → chksz → jfjt) first, official eapi as fallback |
| Concurrency | tokio; all results are resolved in parallel |
| TLS | rustls (no OpenSSL, fully static musl builds) |
| Tags | lofty |
| Interaction | inquire |
Protocol notes (in Chinese): docs/qq-protocol.md, docs/netease-protocol.md.
The full config template: config.example.toml.
Building
# Fully static Linux binary (x86_64-unknown-linux-musl, no dynamic dependencies)
# output: target/x86_64-unknown-linux-musl/release/uta
ring (used by rustls) contains C code, so the musl build needs a C compiler targeting musl. The script looks for, in order:
the CC_x86_64_unknown_linux_musl environment variable → x86_64-linux-musl-gcc / musl-gcc
(Debian/Ubuntu: apt install musl-tools) → zig.
NixOS / Nix
The repository ships a flake.nix (rust-overlay stable + rust-analyzer + musl target + musl gcc); with direnv it loads automatically:
The toolchain version is pinned by flake.lock; upgrade with nix flake update rust-overlay.
CI and releases
.github/workflows/ci.yml: on pushes to main and PRs —fmt --check,clippy -D warnings(also with--no-default-features),cargo test,cargo package, and a static musl build. Network tests are marked#[ignore]and don't run in CI..github/workflows/release.yml: onv*tags — tests and builds for Linux musl, macOS (arm64 / x86_64) and Windows x64, publishes all archives (with.sha256) to GitHub Releases, then publishes to crates.io (token from theCRATE_TOKENsecret of thecrateenvironment). Manual runs only build, without publishing. The tag must match the version inCargo.toml:
# bump `version` in Cargo.toml (e.g. 0.2.0) and commit first
&&
Out of scope (for now)
- Other platforms (e.g. Kugou: download links are blocked by risk control without login)
- QMCv2 decryption of encrypted VIP formats (
.mflac/.mgg) - Login cookies / VIP credentials
- The slow and unstable third-party endpoints from musicdl's lower tiers
License
PolyForm Noncommercial 1.0.0 — non-commercial use only.