Metadata-Version: 2.4
Name: pylrclib-cli
Version: 0.5.0
Summary: A multi-command CLI for searching, downloading, inspecting, cleansing, and publishing lyrics around LRCLIB.
Author: Harmonese
License-Expression: MIT
Keywords: lrclib,lyrics,lrc,karaoke,cli,music
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31.0
Requires-Dist: mutagen>=1.47.0
Requires-Dist: PyYAML>=6.0.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: coverage>=7.0; extra == "dev"
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Requires-Dist: ruff>=0.6.0; extra == "dev"
Requires-Dist: mypy>=1.10.0; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="https://raw.githubusercontent.com/Harmonese/pylrclib/main/docs/assets/pylrclib-logo-lockup.png" alt="pylrclib" width="720">
</p>

<p align="center">
  <a href="https://pypi.org/project/pylrclib-cli/"><img alt="PyPI version" src="https://img.shields.io/pypi/v/pylrclib-cli.svg"></a>
  <a href="https://pypi.org/project/pylrclib-cli/"><img alt="Python versions" src="https://img.shields.io/pypi/pyversions/pylrclib-cli.svg"></a>
  <a href="https://pypi.org/project/pylrclib-cli/"><img alt="License" src="https://img.shields.io/pypi/l/pylrclib-cli.svg"></a>
</p>

# pylrclib

A multi-command CLI for searching, downloading, inspecting, cleansing, and publishing lyrics around LRCLIB.

`pylrclib` is built for people who keep a real music library: audio files in one folder, plain lyrics in another, synced `.lrc` files somewhere else, and sometimes YAML metadata standing in for missing tags. It gives you one CLI for finding LRCLIB records, saving lyrics locally, checking local matches before upload, cleaning noisy LRC files, and publishing lyrics back to LRCLIB.

Package name: `pylrclib-cli`  
Command name: `pylrclib`

## Features

- Find LRCLIB records by text search, structured artist/title metadata, or exact LRCLIB id.
- Download lyrics as `.txt`, `.lrc`, or both, with predictable file naming.
- Upload local plain lyrics, synced lyrics, mixed records, or instrumental markers.
- Flag incorrect LRCLIB records by id.
- Match lyrics from audio filenames, YAML-declared files, and normalized `Artist - Title` names.
- Preview local matches before publishing so you can catch bad pairings.
- Clean `.lrc` files with default rules or fine-tuned `--keep-*` options.
- Diagnose a workspace before a large upload run.
- Switch CLI output between English and Chinese with `--lang auto|en_US|zh_CN`.

## Lyrics Model

The CLI treats lyrics as explicit states:

| State | Meaning |
| --- | --- |
| `plain` | Unsynced plain-text lyrics. |
| `synced` | Timed LRC lyrics. |
| `mixed` | Both plain and synced lyrics are available. |
| `instrumental` | Instrumental / no-vocals track. |

The same model is used by `search`, `down`, `up`, and `inspect`, while `cleanse` uses the same LRC parser to normalize synced lyric files.

## Installation

Install from PyPI:

```bash
pip install pylrclib-cli
```

Then check the CLI:

```bash
pylrclib --help
pylrclib --version
```

Install from source when developing:

```bash
pip install -e .
```

## Quick Start

Search LRCLIB:

```bash
pylrclib search --query "Aimer Ref:rain"
pylrclib search --artist "Aimer" --title "Ref:rain"
```

Download the best matching lyrics:

```bash
pylrclib down \
  --artist "Aimer" \
  --title "Ref:rain" \
  --output-dir ./lyrics
```

Download both plain text and synced LRC for an exact LRCLIB record:

```bash
pylrclib down \
  --lrclib-id 12345 \
  --output-dir ./lyrics \
  --save-mode both
```

Inspect local matches before uploading:

```bash
pylrclib inspect \
  --tracks ./music \
  --plain-dir ./lyrics_plain \
  --synced-dir ./lyrics_lrc \
  --show-all-candidates
```

Upload local lyrics:

```bash
pylrclib up \
  --tracks ./music \
  --plain-dir ./lyrics_plain \
  --synced-dir ./lyrics_lrc \
  --lyrics-mode auto
```

Flag an incorrect LRCLIB record:

```bash
pylrclib flag --lrclib-id 12345 --content "wrong lyrics"
```

Clean LRC files, first as a preview and then with write-back:

```bash
pylrclib cleanse ./lyrics_lrc
pylrclib cleanse ./lyrics_lrc --write
```

Diagnose the workspace:

```bash
pylrclib doctor \
  --tracks ./music \
  --plain-dir ./lyrics_plain \
  --synced-dir ./lyrics_lrc
```

## Commands

### `search`

Use `search` to inspect remote LRCLIB records before downloading or uploading.

```bash
pylrclib search --query "artist title"
pylrclib search --title "Song Title" --artist "Artist Name"
pylrclib search --lrclib-id 12345
```

Common options:

```text
--query TEXT
--title TEXT
--artist TEXT
--album TEXT
--lrclib-id N
--limit N
--preview-lines N
--max-retries N
--user-agent TEXT
--api-base URL
--json
--yes
--non-interactive
```

### `down`

Use `down` to fetch lyrics from LRCLIB and write them locally.

Input modes:

1. Scan local audio or YAML metadata with `--tracks`.
2. Download one manually specified track with `--artist` and `--title`.
3. Fetch one exact LRCLIB record with `--lrclib-id`.

```bash
pylrclib down --tracks ./music --output-dir ./lyrics
pylrclib down --artist "Artist" --title "Song" --output-dir ./lyrics
pylrclib down --lrclib-id 12345 --output-dir ./lyrics --save-mode both
```

Save modes:

| Mode | Behavior |
| --- | --- |
| `auto` | Save `.lrc` when synced lyrics exist, otherwise save `.txt` when plain lyrics exist. |
| `plain` | Save only `.txt`. |
| `synced` | Save only `.lrc`. |
| `both` | Save both `.txt` and `.lrc` when available. |

Naming modes:

| Mode | Behavior |
| --- | --- |
| `auto` | Use local track basename when scanning files, otherwise use `Artist - Title`. |
| `track-basename` | Force the source audio/YAML basename when available. |
| `artist-title` | Use `Artist - Title`. |

Common options:

```text
--tracks PATH
--artist TEXT
--title TEXT
--album TEXT
--duration N
--lrclib-id N
--output-dir PATH
--plain-dir PATH
--synced-dir PATH
--save-mode auto|plain|synced|both
--naming auto|track-basename|artist-title
--skip-existing
--overwrite
--allow-derived-plain
--no-derived-plain
--preview-lines N
--max-retries N
--user-agent TEXT
--api-base URL
--yes
--non-interactive
```

### `up`

Use `up` to publish local lyrics to LRCLIB.

The upload workflow:

1. Discover audio files or YAML metadata.
2. Query LRCLIB external matches. The deprecated `/api/get-cached` path is skipped with a warning because current LRCLIB returns 404 for it.
3. Resolve local plain and synced lyric candidates.
4. Classify lyrics as `plain`, `synced`, `mixed`, `instrumental`, `invalid`, or `empty`.
5. Optionally normalize matched LRC content when `--cleanse` is enabled.
6. Build an upload plan from `--lyrics-mode`.
7. Publish lyrics or mark the track instrumental.
8. Optionally move or rename processed files.

Recommended split-directory workflow:

```bash
pylrclib up \
  --tracks ./music \
  --plain-dir ./lyrics_plain \
  --synced-dir ./lyrics_lrc \
  --cleanse \
  --cleanse-write \
  --done-lrc ./done_lyrics \
  --lyrics-mode auto
```

Upload modes:

| Mode | Behavior |
| --- | --- |
| `auto` | Upload the best available local lyrics. Mixed wins over synced, synced wins over plain. |
| `plain` | Require plain lyrics. |
| `synced` | Require synced lyrics. Plain lyrics may be derived unless disabled. |
| `mixed` | Require both plain and synced lyrics, or derive plain from synced when allowed. |
| `instrumental` | Publish instrumental metadata. |

Local matching checks:

1. Same basename as the audio or YAML file.
2. YAML-declared filenames.
3. Normalized `Artist - Title.*` matches.
4. Interactive selection when multiple candidates exist.

Supported local lyric extensions:

| Type | Extensions |
| --- | --- |
| Plain | `.txt`, `.lyrics`, `.lyric` |
| Synced | `.lrc` |

Common options:

```text
--tracks PATH
--lyrics-dir PATH
--plain-dir PATH
--synced-dir PATH
--done-tracks PATH
--done-lrc PATH
-f, --follow
-r, --rename
-c, --cleanse
--cleanse-write
--keep-headers
--keep-credits
--keep-translations
--keep-instrumental-marker
--allow-non-lrc
--ignore-duration-mismatch
--lyrics-mode auto|plain|synced|mixed|instrumental
--allow-derived-plain
--no-derived-plain
-d, --default TRACKS_DIR LYRICS_DIR
-m, --match
--preview-lines N
--max-retries N
--user-agent TEXT
--api-base URL
--yes
--non-interactive
```

### `flag`

Use `flag` to report an incorrect LRCLIB record by id.

```bash
pylrclib flag --lrclib-id 12345 --content "wrong lyrics"
pylrclib flag --lrclib-id 12345 --yes
```

Common options:

```text
--lrclib-id N
--content TEXT
--preview-lines N
--max-retries N
--user-agent TEXT
--api-base URL
--yes
--non-interactive
```

### `inspect`

Use `inspect` to see what `up` would find locally without publishing anything.

```bash
pylrclib inspect \
  --tracks ./music \
  --plain-dir ./lyrics_plain \
  --synced-dir ./lyrics_lrc \
  --show-all-candidates
```

Common options:

```text
--tracks PATH
--lyrics-dir PATH
--plain-dir PATH
--synced-dir PATH
--lyrics-mode auto|plain|synced|mixed|instrumental
--preview-lines N
--show-all-candidates
```

### `cleanse`

Use `cleanse` to normalize `.lrc` files. Without `--write`, the command previews the cleansed output and leaves files unchanged.

```bash
pylrclib cleanse ./lyrics_lrc
pylrclib cleanse ./lyrics_lrc --write
pylrclib cleanse song.lrc another.lrc
```

`cleanse` removes common noisy content such as header tags, credit lines, duplicate CJK translation lines, and pure-music marker lines by default. Invalid files are not rewritten into empty files.

Every cleanse rule has a keep flag for precision:

```bash
pylrclib cleanse ./lyrics_lrc --write --keep-translations
pylrclib cleanse ./lyrics_lrc --write --keep-headers --keep-credits
```

Inside `pylrclib up`, `--cleanse` controls in-memory LRC normalization for preview and upload. Without it, matched `.lrc` files are uploaded as read from disk. Add `--cleanse-write` to write the normalized LRC content back to the matched file before upload. The same `--keep-*` flags refine upload-time cleansing. Use the standalone `cleanse` command when you want to bulk-normalize files without uploading.

Common options:

```text
paths ...
--lrc-dir PATH
--write
--keep-headers
--keep-credits
--keep-translations
--keep-instrumental-marker
--preview-lines N
```

### `doctor`

Use `doctor` to print resolved configuration, count local inputs, and catch obvious workflow conflicts.

```bash
pylrclib doctor \
  --tracks ./music \
  --plain-dir ./lyrics_plain \
  --synced-dir ./lyrics_lrc
```

Common options:

```text
--tracks PATH
--lyrics-dir PATH
--plain-dir PATH
--synced-dir PATH
--done-tracks PATH
--done-lrc PATH
-f, --follow
-r, --rename
-c, --cleanse
--lyrics-mode auto|plain|synced|mixed|instrumental
-d, --default TRACKS_DIR LYRICS_DIR
-m, --match
--preview-lines N
--max-retries N
--user-agent TEXT
--api-base URL
```

## YAML Metadata

YAML files can be used as track inputs when audio metadata is unavailable or inconvenient.

```yaml
track: Song Title
artist: Artist Name
album: Album Name
duration: 180
plain_file: song.txt
synced_file: song.lrc
lyrics_file: song.lrc
lrc_file: song.lrc
```

Required fields:

```text
track
artist
album
duration
```

Optional lyric filename fields:

```text
plain_file
synced_file
lyrics_file
lrc_file
```

## Environment Variables

Most directory, metadata, network, and preview options can be provided through environment variables:

```text
PYLRCLIB_TRACKS_DIR
PYLRCLIB_LYRICS_DIR
PYLRCLIB_PLAIN_DIR
PYLRCLIB_SYNCED_DIR
PYLRCLIB_DONE_TRACKS_DIR
PYLRCLIB_DONE_LRC_DIR
PYLRCLIB_OUTPUT_DIR
PYLRCLIB_ARTIST
PYLRCLIB_TITLE
PYLRCLIB_ALBUM
PYLRCLIB_DURATION
PYLRCLIB_LRCLIB_ID
PYLRCLIB_FLAG_CONTENT
PYLRCLIB_API_BASE
PYLRCLIB_USER_AGENT
PYLRCLIB_PREVIEW_LINES
PYLRCLIB_MAX_HTTP_RETRIES
PYLRCLIB_LIMIT
PYLRCLIB_LANG
```

CLI arguments take precedence over environment variables. Environment variables take precedence over hardcoded defaults.

## Language

`pylrclib` supports English and Chinese output:

```bash
pylrclib --lang en_US --help
pylrclib --lang zh_CN --help
pylrclib --lang auto --help
```

You can also set the language once for a shell session:

```bash
export PYLRCLIB_LANG=zh_CN
pylrclib --help
```

## Help Reference

The command help is the source of truth for current defaults and exact option names:

```bash
pylrclib -h
pylrclib search -h
pylrclib down -h
pylrclib up -h
pylrclib flag -h
pylrclib inspect -h
pylrclib cleanse -h
pylrclib doctor -h
```

## Development

Install development dependencies:

```bash
pip install -e ".[dev]"
```

Run tests:

```bash
python -m pytest -q
```

Run coverage:

```bash
python -m coverage run --source=pylrclib -m pytest -q
python -m coverage report -m
```

As of the 0.4.8 release prep, the local suite has 122 tests and reports 94% line coverage.

Run linting, type checking, and packaging checks before publishing:

```bash
ruff check .
mypy pylrclib
python -m build
twine check dist/*
```

When changing CLI arguments, defaults, or help text, refresh the README summaries against:

```bash
python -m pylrclib --lang en_US -h
python -m pylrclib --lang en_US search -h
python -m pylrclib --lang en_US down -h
python -m pylrclib --lang en_US up -h
python -m pylrclib --lang en_US flag -h
python -m pylrclib --lang en_US inspect -h
python -m pylrclib --lang en_US cleanse -h
python -m pylrclib --lang en_US doctor -h
```

## 中文速览

`pylrclib` 是一个围绕 LRCLIB 的歌词命令行工具，可以搜索、下载、检查、清洗和上传歌词。

常用命令：

```bash
pylrclib search --query "歌手 歌名"
pylrclib down --artist "歌手" --title "歌名" --output-dir ./lyrics
pylrclib inspect --tracks ./music --lyrics-dir ./lyrics --show-all-candidates
pylrclib up --tracks ./music --plain-dir ./plain --synced-dir ./lrc
pylrclib cleanse ./lrc --write
pylrclib doctor --tracks ./music --lyrics-dir ./lyrics
```

使用中文界面：

```bash
pylrclib --lang zh_CN --help
export PYLRCLIB_LANG=zh_CN
```

## License

MIT
