lyriq


Namelyriq JSON
Version 1.4.0 PyPI version JSON
download
home_pageNone
SummaryA lightweight Python library designed to effortlessly fetch song lyrics.
upload_time2025-07-24 13:50:46
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords lyrics music synchronization
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Lyriq

A lightweight Python library designed to effortlessly fetch and display song lyrics, with support for synchronized lyrics and an accompanying CLI tool.

[![PyPI version](https://img.shields.io/pypi/v/lyriq.svg)](https://pypi.org/project/lyriq/)
[![Python Versions](https://img.shields.io/pypi/pyversions/lyriq.svg)](https://pypi.org/project/lyriq/)
[![License](https://img.shields.io/github/license/tn3w/lyriq.svg)](https://github.com/tn3w/lyriq/blob/main/LICENSE)

## Features

- Fetch lyrics from the LRCLib API
- Support for both plain and synchronized lyrics
- Built-in caching to reduce API calls
- CLI tool with synchronized lyrics playback
- Support for saving/loading lyrics in JSON and plain text formats
- Colorful terminal output with synchronized highlighting
- Database dump management with download functionality
- Progress tracking for large file downloads

## Installation

### From PyPI

```bash
pip install lyriq
```

### Development Installation

```bash
git clone https://github.com/tn3w/lyriq.git
cd lyriq
pip install -e ".[dev]"
```

## Usage

### Basic Usage

```python
import sys
from lyriq import get_lyrics

lyrics = get_lyrics("Circles", "Post Malone")
if not lyrics:
    print("No lyrics found for 'Circles' by Post Malone")
    sys.exit(0)

print(f"ID: {lyrics.id}")
print(f"Name: {lyrics.name}")
print(f"Track: {lyrics.track_name}")
print(f"Artist: {lyrics.artist_name}")
print(f"Album: {lyrics.album_name}")
print(f"Duration: {lyrics.duration} seconds")
print(f"Instrumental: {'Yes' if lyrics.synced_lyrics else 'No'}")
print("\nPlain Lyrics:")
print("-" * 40)
print(lyrics.plain_lyrics)
print("\nSynchronized Lyrics (timestamp: lyric):")
print("-" * 40)

for timestamp, line in sorted(lyrics.lyrics.items()):
    print(f"[{timestamp}] {line}")

print("-" * 40)
```

### Fetch by ID

```python
from lyriq import get_lyrics_by_id

# Fetch lyrics by ID
lyrics = get_lyrics_by_id("449")  # ID for "Circles" by Post Malone

if lyrics:
    print(f"Found: {lyrics.track_name} by {lyrics.artist_name}")
```

### Convert to Plain Text

```python
from lyriq import to_plain_lyrics, get_lyrics

lyrics = get_lyrics("Circles", "Post Malone")
plain_text = to_plain_lyrics(lyrics)
print(plain_text)
```

### Search for Lyrics

```python
from lyriq import search_lyrics

# Search by general query
results = search_lyrics(q="Circles Post Malone")

# Or search by song and artist name
results = search_lyrics(song_name="Circles", artist_name="Post Malone")

if results:
    print(f"Found {len(results)} results:")
    for i, lyrics in enumerate(results[:3]):  # Display first 3 results
        print(f"{i+1}. {lyrics.track_name} by {lyrics.artist_name} ({lyrics.album_name})")
else:
    print("No results found")
```

### Save and Load Lyrics

```python
from lyriq import get_lyrics

# Fetch and save lyrics
lyrics = get_lyrics("Circles", "Post Malone")

# Save to plain text file
lyrics.to_plain_file("circles.txt")

# Save to JSON file
lyrics.to_json_file("circles.json")

# Load from JSON file
loaded_lyrics = lyrics.from_json_file("circles.json")
```

### Database Dumps

```python
from lyriq import get_database_dumps, get_latest_database_dump, download_database_dump

# Get all available database dumps
dumps = get_database_dumps()
if dumps:
    print(f"Found {len(dumps)} database dumps:")
    for i, dump in enumerate(dumps, 1):
        size_mb = dump.size / (1024 * 1024)
        print(f"{i}. {dump.filename} ({size_mb:.1f} MB)")

# Get the latest database dump
latest_dump = get_latest_database_dump()
if latest_dump:
    print(f"Latest dump: {latest_dump.filename}")
    print(f"Size: {latest_dump.size / (1024 * 1024):.1f} MB")
    print(f"Uploaded: {latest_dump.uploaded}")

# Download a database dump with progress tracking
def progress_callback(downloaded, total):
    if total > 0:
        percent = (downloaded / total) * 100
        print(f"Progress: {percent:.1f}%")

if latest_dump:
    file_path = download_database_dump(
        latest_dump, 
        download_path="./my_dump.sqlite3.gz",
        progress_callback=progress_callback
    )
    if file_path:
        print(f"Downloaded to: {file_path}")
```

## Command Line Interface

The library comes with a command-line interface for quick access to lyrics with synchronized lyrics playback:
```
usage: lyriq [-h] [-v] [--id ID] [--duration [DURATION]] [--search [SEARCH]]
             [--search-index SEARCH_INDEX] [--none-char NONE_CHAR] [--no-info]
             [--plain [{plain,lrc,json}]] [--file FILE] [--file-format {plain,lrc,json}]
             [--load LOAD]
             [song_name] [artist_name] [album_name]

Fetch and display song lyrics

positional arguments:
  song_name             Name of the song (optional)
  artist_name           Name of the artist (optional)
  album_name            Name of the album (optional)

options:
  -h, --help            show this help message and exit
  -v, --version         show version message and exit
  --id ID               ID of the song
  --duration [DURATION]
                        Duration of the song (optional)
  --search [SEARCH]     Search for lyrics by song name and artist name. Optionally provide a search query.
  --search-index SEARCH_INDEX
                        Select search result at specified index directly (1-based)
  --none-char NONE_CHAR
                        Character to use for empty lines
  --no-info             Do not display track information
  --plain [{plain,lrc,json}]
                        Display only plain lyrics (default), or specify 'lrc' or 'json' for other formats
  --file FILE           File to save lyrics to and exit
  --file-format {plain,lrc,json}
                        Format to save lyrics to
  --load LOAD           Load lyrics from file
  --dumps               List and download database dumps
  --dumps-index DUMPS_INDEX
                        Select database dump at specified index directly (1-based)
```

### Usage Examples

```bash
# Basic usage
lyriq "Circles" "Post Malone"

# With album name (optional)
lyriq "Circles" "Post Malone" "Hollywood's Bleeding"

# With duration (optional)
lyriq "Circles" "Post Malone" --duration 210

# Fetch lyrics by ID
lyriq --id 449

# Custom character for empty lines
lyriq "Circles" "Post Malone" --none-char "*"

# Display no track information
lyriq "Circles" "Post Malone" --no-info

# Display only plain lyrics
lyriq "Circles" "Post Malone" --plain

# Display plain lyrics in other formats
lyriq "Circles" "Post Malone" --plain lrc
lyriq "Circles" "Post Malone" --plain json

# Save lyrics to file and exit
lyriq "Circles" "Post Malone" --file Circles-Post-Malone.txt

# Save lyrics to JSON file and exit
lyriq "Circles" "Post Malone" --file Circles-Post-Malone.json --file-format json

# Save lyrics to LRC file and exit
lyriq "Circles" "Post Malone" --file Circles-Post-Malone.json --file-format lrc

# Load lyrics from file
lyriq --load Circles-Post-Malone.json
lyriq --load Circles-Post-Malone.lrc

# Search for lyrics using song name and artist name fields with interactive UI
lyriq "Circles" "Post Malone" --search

# Search with general query
lyriq --search "Circles Post Malone"

# Search and select result at specific index
lyriq --search "Circles Post Malone" --search-index 1

# List and download database dumps with interactive UI
lyriq --dumps

# Download database dump at specific index directly
lyriq --dumps --dumps-index 1
```

### CLI Features

- Display song metadata with colored highlighting of differences
- Synchronized lyrics playback (if available)
- Interactive controls:
    - Press `SPACE` (or custom character): Play/Pause
    - Press `←` / `→` arrows: Rewind/Fast-forward by 5 seconds
    - Press `r`: Toggle repeat
    - Press `q`: Quit
- Interactive search UI:
    - Navigate results with `↑` / `↓` arrow keys
    - Select with `Enter` or number keys `1-9`
    - Pagination with 4 results per page
    - Shows synchronized lyrics availability with color indicators

## API Reference

### Main Functions

#### `get_lyrics(song_name, artist_name, album_name=None, duration=None, none_char="♪")`

Fetches lyrics for a song by artist name and song name.

- **Parameters**:
    - `song_name`: Name of the song
    - `artist_name`: Name of the artist
    - `album_name`: (Optional) Album name to improve search accuracy
    - `duration`: (Optional) Duration of the song in seconds
    - `none_char`: Character to use for empty lines in synchronized lyrics
- **Returns**: A `Lyrics` object if found, `None` otherwise

#### `get_lyrics_by_id(lyrics_id, none_char="♪")`

Fetches lyrics for a song by its LRCLib ID.

- **Parameters**:
    - `lyrics_id`: The LRCLib ID of the song
    - `none_char`: Character to use for empty lines in synchronized lyrics
- **Returns**: A `Lyrics` object if found, `None` otherwise

#### `search_lyrics(q=None, song_name=None, artist_name=None, album_name=None, none_char="♪")`

Searches for lyrics by query or song/artist information.

- **Parameters**:
    - `q`: General search query string
    - `song_name`: Optional song name for searching
    - `artist_name`: Optional artist name for searching
    - `album_name`: Optional album name for better matching
    - `none_char`: Character to use for empty lines in synchronized lyrics
- **Returns**: A list of `Lyrics` objects if found, `None` otherwise

#### `to_plain_lyrics(lyrics, none_char="♪")`

Converts a `Lyrics` object or lyrics dictionary to plain text.

- **Parameters**:
    - `lyrics`: A `Lyrics` object or dictionary containing lyrics data
    - `none_char`: Character to use for empty lines
- **Returns**: Plain text lyrics as a string

#### `request_challenge()`

Requests a cryptographic challenge from the API for generating a publish token.

- **Returns**: A tuple containing `(prefix, target)` for the proof-of-work challenge
- **Raises**: `LyriqError` if the API returns an error

#### `verify_nonce(result_bytes, target_bytes)`

Verifies if a nonce satisfies the target requirement for the proof-of-work challenge.

- **Parameters**:
    - `result_bytes`: The hashed result as bytes
    - `target_bytes`: The target as bytes
- **Returns**: `True` if the nonce satisfies the target, `False` otherwise

#### `generate_publish_token(prefix, target)`

Generates a valid publish token by solving a proof-of-work challenge.

- **Parameters**:
    - `prefix`: The prefix string provided by the challenge
    - `target`: The target string in hexadecimal format provided by the challenge
- **Returns**: A valid publish token in the format `{prefix}:{nonce}`
- **Raises**: `LyriqError` if there is an error with the token generation

#### `publish_lyrics(track_name, artist_name, album_name, duration, plain_lyrics="", synced_lyrics="")`

Publishes lyrics to the LRCLIB API.

- **Parameters**:
    - `track_name`: Name of the track
    - `artist_name`: Name of the artist
    - `album_name`: Name of the album
    - `duration`: Duration of the track in seconds
    - `plain_lyrics`: Plain text lyrics (optional)
    - `synced_lyrics`: Synchronized lyrics (optional)
- **Returns**: `True` if the publish was successful, `False` otherwise
- **Raises**: `LyriqError` if there is an error publishing the lyrics

#### `get_database_dumps()`

Fetches the list of available database dumps from the LRCLib database dumps API.

- **Returns**: A list of `DatabaseDump` objects if successful, `None` otherwise
- **Raises**: `LyriqError` if there is an error fetching the database dumps

#### `get_latest_database_dump()`

Gets the latest database dump from the LRCLib database dumps API.

- **Returns**: The latest `DatabaseDump` object if found, `None` otherwise

#### `download_database_dump(dump, download_path=None, progress_callback=None)`

Downloads a database dump file.

- **Parameters**:
    - `dump`: The `DatabaseDump` object to download
    - `download_path`: Optional path to save the file. If not provided, saves to cache directory
    - `progress_callback`: Optional callback function to track download progress. Called with `(bytes_downloaded, total_bytes)`
- **Returns**: The path to the downloaded file if successful, `None` otherwise
- **Raises**: `LyriqError` if there is an error downloading the file

### Lyrics Class

#### Properties

- `lyrics`: Dictionary mapping timestamps to lyric lines
- `synced_lyrics`: Raw synchronized lyrics string
- `plain_lyrics`: Plain lyrics string
- `id`: LRCLib ID of the song
- `name`: Name of the song
- `track_name`: Name of the track
- `artist_name`: Name of the artist
- `album_name`: Name of the album
- `duration`: Duration of the song in seconds
- `instrumental`: Whether the song is instrumental (True/False)

#### Methods

##### `from_dict(data, none_char="♪")`

Create a `Lyrics` instance from a dictionary.

- **Parameters**:
    - `data`: Raw lyrics data dictionary from the API
    - `none_char`: Character to use for empty lines
- **Returns**: A new `Lyrics` instance

##### `to_dict()`

Convert the `Lyrics` instance to a dictionary.

- **Returns**: Dictionary representation of the lyrics

##### `to_plain_string(none_char=None)`

Convert the lyrics to a plain string representation.

- **Parameters**:
    - `none_char`: Character to use for empty lines (optional)
- **Returns**: Plain string representation of lyrics or None if empty
- **Raises**: `EmptyLyricsError` if lyrics are empty

##### `to_plain_file(file_path, none_char=None)`

Write the lyrics to a plain text file.

- **Parameters**:
    - `file_path`: Path to the output file
    - `none_char`: Character to use for empty lines (optional)
- **Raises**: `EmptyLyricsError` if lyrics are empty

##### `to_lrc_string()`

Convert the lyrics to LRC format string.

- **Returns**: LRC format string with metadata and timestamps

##### `to_lrc_file(file_path)`

Write the lyrics to a LRC file.

- **Parameters**:
    - `file_path`: Path to the output file

##### `from_lrc_string(lrc_string, none_char="♪")`

Create a `Lyrics` instance from a LRC format string.

- **Parameters**:
    - `lrc_string`: LRC format string with metadata and timestamps
    - `none_char`: Character to use for empty lines
- **Returns**: A new `Lyrics` instance

##### `from_lrc_file(file_path, none_char="♪")`

Read lyrics from a LRC file.

- **Parameters**:
    - `file_path`: Path to the LRC file
    - `none_char`: Character to use for empty lines
- **Returns**: A new `Lyrics` instance

##### `to_json_file(file_path)`

Write the lyrics to a JSON file.

- **Parameters**:
    - `file_path`: Path to the output file

##### `from_json_file(file_path, none_char="♪")`

Read lyrics from a JSON file.

- **Parameters**:
    - `file_path`: Path to the JSON file
    - `none_char`: Character to use for empty lines
- **Returns**: A new `Lyrics` instance

### DatabaseDump Class

#### Properties

- `storage_class`: Storage class of the dump file (e.g., "Standard")
- `uploaded`: Upload datetime as a `datetime` object
- `checksums`: Dictionary of checksums for the file
- `http_etag`: HTTP ETag header value
- `etag`: ETag value
- `size`: Size of the dump file in bytes
- `version`: Version identifier of the dump
- `key`: Key/filename of the dump file
- `filename`: Extracted filename from the key (property)
- `download_url`: Full download URL for the dump (property)

#### Methods

##### `from_dict(data)`

Create a `DatabaseDump` instance from a dictionary.

- **Parameters**:
    - `data`: Raw database dump data dictionary from the API
- **Returns**: A new `DatabaseDump` instance

## Examples

See the examples directory for practical usage examples:

- `examples/basic_usage.py` - Basic usage of the library
- `examples/fetch_by_id.py` - Fetching lyrics by ID
- `examples/search_lyrics.py` - Searching for lyrics
- `examples/synced_lyrics.py` - Working with synchronized lyrics
- `examples/format_conversion.py` - Converting between formats
- `examples/publishing_lyrics.py` - Publishing lyrics to the API
- `examples/database_dumps.py` - Working with database dumps

## Development

### Project Structure

```
lyriq/
├── .github/
│   ├── workflows/
│   │   ├── python-tests.yml
│   │   └── python-publish.yml
├── environment.yml      # Environment file
├── examples/            # Example files demonstrating usage
│   ├── basic_usage.py
│   ├── fetch_by_id.py
│   ├── search_lyrics.py
│   ├── synced_lyrics.py
│   ├── format_conversion.py
│   └── publishing_lyrics.py
├── lyriq/
│   ├── __init__.py      # Package exports
│   ├── __main__.py      # CLI entry point
│   ├── lyriq.py         # Core functionality
│   ├── cli.py           # Command line interface
│   └── cache/           # Auto-generated cache directory
│       ├── lyrics.json  # Lyrics cache
│       └── search.json  # Search cache
├── tests/
│   ├── __init__.py
│   └── test_lyriq.py    # Test suite
├── pyproject.toml       # Project configuration
├── setup.py             # Setup script
├── main.py              # Example usage
├── README.md            # This file
├── LICENSE              # License file
└── .gitignore           # Git ignore file
```

### Running Tests

```bash
# Install development dependencies
pip install -e ".[dev]"

# Run the test suite
pytest

# Run with coverage report
pytest --cov=lyriq --cov-report=term-missing
```

### Adding Features

1. Make changes to the core functionality in `lyriq.py`
2. Add appropriate tests in `test_lyriq.py`
3. Update documentation in docstrings and README.md
4. Run tests to ensure everything works

## Technical Details

### API

Lyriq uses the LRCLib API (https://lrclib.net/api) to fetch lyrics data. The API provides both synchronized and plain lyrics.

### Caching

To reduce API calls and improve performance, Lyriq caches all retrieved lyrics in JSON files located in the cache directory:

```
<package_location>/cache/
```

The cache is thread-safe and automatically writes to disk when new lyrics are added.

### Synchronized Lyrics Format

Synchronized lyrics are stored in LRC format with timestamps in the format `[MM:SS.ms]`. The CLI tool interprets these timestamps to display the lyrics at the right moment during playback.

## License

Copyright 2025 TN3W

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "lyriq",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "lyrics, music, synchronization",
    "author": null,
    "author_email": "TN3W <tn3w@protonmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/77/7b/d1d436bda78d6ee8e5bf0f13f1375fbc79de930d2d875eafd867dab9ae8d/lyriq-1.4.0.tar.gz",
    "platform": null,
    "description": "# Lyriq\n\nA lightweight Python library designed to effortlessly fetch and display song lyrics, with support for synchronized lyrics and an accompanying CLI tool.\n\n[![PyPI version](https://img.shields.io/pypi/v/lyriq.svg)](https://pypi.org/project/lyriq/)\n[![Python Versions](https://img.shields.io/pypi/pyversions/lyriq.svg)](https://pypi.org/project/lyriq/)\n[![License](https://img.shields.io/github/license/tn3w/lyriq.svg)](https://github.com/tn3w/lyriq/blob/main/LICENSE)\n\n## Features\n\n- Fetch lyrics from the LRCLib API\n- Support for both plain and synchronized lyrics\n- Built-in caching to reduce API calls\n- CLI tool with synchronized lyrics playback\n- Support for saving/loading lyrics in JSON and plain text formats\n- Colorful terminal output with synchronized highlighting\n- Database dump management with download functionality\n- Progress tracking for large file downloads\n\n## Installation\n\n### From PyPI\n\n```bash\npip install lyriq\n```\n\n### Development Installation\n\n```bash\ngit clone https://github.com/tn3w/lyriq.git\ncd lyriq\npip install -e \".[dev]\"\n```\n\n## Usage\n\n### Basic Usage\n\n```python\nimport sys\nfrom lyriq import get_lyrics\n\nlyrics = get_lyrics(\"Circles\", \"Post Malone\")\nif not lyrics:\n    print(\"No lyrics found for 'Circles' by Post Malone\")\n    sys.exit(0)\n\nprint(f\"ID: {lyrics.id}\")\nprint(f\"Name: {lyrics.name}\")\nprint(f\"Track: {lyrics.track_name}\")\nprint(f\"Artist: {lyrics.artist_name}\")\nprint(f\"Album: {lyrics.album_name}\")\nprint(f\"Duration: {lyrics.duration} seconds\")\nprint(f\"Instrumental: {'Yes' if lyrics.synced_lyrics else 'No'}\")\nprint(\"\\nPlain Lyrics:\")\nprint(\"-\" * 40)\nprint(lyrics.plain_lyrics)\nprint(\"\\nSynchronized Lyrics (timestamp: lyric):\")\nprint(\"-\" * 40)\n\nfor timestamp, line in sorted(lyrics.lyrics.items()):\n    print(f\"[{timestamp}] {line}\")\n\nprint(\"-\" * 40)\n```\n\n### Fetch by ID\n\n```python\nfrom lyriq import get_lyrics_by_id\n\n# Fetch lyrics by ID\nlyrics = get_lyrics_by_id(\"449\")  # ID for \"Circles\" by Post Malone\n\nif lyrics:\n    print(f\"Found: {lyrics.track_name} by {lyrics.artist_name}\")\n```\n\n### Convert to Plain Text\n\n```python\nfrom lyriq import to_plain_lyrics, get_lyrics\n\nlyrics = get_lyrics(\"Circles\", \"Post Malone\")\nplain_text = to_plain_lyrics(lyrics)\nprint(plain_text)\n```\n\n### Search for Lyrics\n\n```python\nfrom lyriq import search_lyrics\n\n# Search by general query\nresults = search_lyrics(q=\"Circles Post Malone\")\n\n# Or search by song and artist name\nresults = search_lyrics(song_name=\"Circles\", artist_name=\"Post Malone\")\n\nif results:\n    print(f\"Found {len(results)} results:\")\n    for i, lyrics in enumerate(results[:3]):  # Display first 3 results\n        print(f\"{i+1}. {lyrics.track_name} by {lyrics.artist_name} ({lyrics.album_name})\")\nelse:\n    print(\"No results found\")\n```\n\n### Save and Load Lyrics\n\n```python\nfrom lyriq import get_lyrics\n\n# Fetch and save lyrics\nlyrics = get_lyrics(\"Circles\", \"Post Malone\")\n\n# Save to plain text file\nlyrics.to_plain_file(\"circles.txt\")\n\n# Save to JSON file\nlyrics.to_json_file(\"circles.json\")\n\n# Load from JSON file\nloaded_lyrics = lyrics.from_json_file(\"circles.json\")\n```\n\n### Database Dumps\n\n```python\nfrom lyriq import get_database_dumps, get_latest_database_dump, download_database_dump\n\n# Get all available database dumps\ndumps = get_database_dumps()\nif dumps:\n    print(f\"Found {len(dumps)} database dumps:\")\n    for i, dump in enumerate(dumps, 1):\n        size_mb = dump.size / (1024 * 1024)\n        print(f\"{i}. {dump.filename} ({size_mb:.1f} MB)\")\n\n# Get the latest database dump\nlatest_dump = get_latest_database_dump()\nif latest_dump:\n    print(f\"Latest dump: {latest_dump.filename}\")\n    print(f\"Size: {latest_dump.size / (1024 * 1024):.1f} MB\")\n    print(f\"Uploaded: {latest_dump.uploaded}\")\n\n# Download a database dump with progress tracking\ndef progress_callback(downloaded, total):\n    if total > 0:\n        percent = (downloaded / total) * 100\n        print(f\"Progress: {percent:.1f}%\")\n\nif latest_dump:\n    file_path = download_database_dump(\n        latest_dump, \n        download_path=\"./my_dump.sqlite3.gz\",\n        progress_callback=progress_callback\n    )\n    if file_path:\n        print(f\"Downloaded to: {file_path}\")\n```\n\n## Command Line Interface\n\nThe library comes with a command-line interface for quick access to lyrics with synchronized lyrics playback:\n```\nusage: lyriq [-h] [-v] [--id ID] [--duration [DURATION]] [--search [SEARCH]]\n             [--search-index SEARCH_INDEX] [--none-char NONE_CHAR] [--no-info]\n             [--plain [{plain,lrc,json}]] [--file FILE] [--file-format {plain,lrc,json}]\n             [--load LOAD]\n             [song_name] [artist_name] [album_name]\n\nFetch and display song lyrics\n\npositional arguments:\n  song_name             Name of the song (optional)\n  artist_name           Name of the artist (optional)\n  album_name            Name of the album (optional)\n\noptions:\n  -h, --help            show this help message and exit\n  -v, --version         show version message and exit\n  --id ID               ID of the song\n  --duration [DURATION]\n                        Duration of the song (optional)\n  --search [SEARCH]     Search for lyrics by song name and artist name. Optionally provide a search query.\n  --search-index SEARCH_INDEX\n                        Select search result at specified index directly (1-based)\n  --none-char NONE_CHAR\n                        Character to use for empty lines\n  --no-info             Do not display track information\n  --plain [{plain,lrc,json}]\n                        Display only plain lyrics (default), or specify 'lrc' or 'json' for other formats\n  --file FILE           File to save lyrics to and exit\n  --file-format {plain,lrc,json}\n                        Format to save lyrics to\n  --load LOAD           Load lyrics from file\n  --dumps               List and download database dumps\n  --dumps-index DUMPS_INDEX\n                        Select database dump at specified index directly (1-based)\n```\n\n### Usage Examples\n\n```bash\n# Basic usage\nlyriq \"Circles\" \"Post Malone\"\n\n# With album name (optional)\nlyriq \"Circles\" \"Post Malone\" \"Hollywood's Bleeding\"\n\n# With duration (optional)\nlyriq \"Circles\" \"Post Malone\" --duration 210\n\n# Fetch lyrics by ID\nlyriq --id 449\n\n# Custom character for empty lines\nlyriq \"Circles\" \"Post Malone\" --none-char \"*\"\n\n# Display no track information\nlyriq \"Circles\" \"Post Malone\" --no-info\n\n# Display only plain lyrics\nlyriq \"Circles\" \"Post Malone\" --plain\n\n# Display plain lyrics in other formats\nlyriq \"Circles\" \"Post Malone\" --plain lrc\nlyriq \"Circles\" \"Post Malone\" --plain json\n\n# Save lyrics to file and exit\nlyriq \"Circles\" \"Post Malone\" --file Circles-Post-Malone.txt\n\n# Save lyrics to JSON file and exit\nlyriq \"Circles\" \"Post Malone\" --file Circles-Post-Malone.json --file-format json\n\n# Save lyrics to LRC file and exit\nlyriq \"Circles\" \"Post Malone\" --file Circles-Post-Malone.json --file-format lrc\n\n# Load lyrics from file\nlyriq --load Circles-Post-Malone.json\nlyriq --load Circles-Post-Malone.lrc\n\n# Search for lyrics using song name and artist name fields with interactive UI\nlyriq \"Circles\" \"Post Malone\" --search\n\n# Search with general query\nlyriq --search \"Circles Post Malone\"\n\n# Search and select result at specific index\nlyriq --search \"Circles Post Malone\" --search-index 1\n\n# List and download database dumps with interactive UI\nlyriq --dumps\n\n# Download database dump at specific index directly\nlyriq --dumps --dumps-index 1\n```\n\n### CLI Features\n\n- Display song metadata with colored highlighting of differences\n- Synchronized lyrics playback (if available)\n- Interactive controls:\n    - Press `SPACE` (or custom character): Play/Pause\n    - Press `\u2190` / `\u2192` arrows: Rewind/Fast-forward by 5 seconds\n    - Press `r`: Toggle repeat\n    - Press `q`: Quit\n- Interactive search UI:\n    - Navigate results with `\u2191` / `\u2193` arrow keys\n    - Select with `Enter` or number keys `1-9`\n    - Pagination with 4 results per page\n    - Shows synchronized lyrics availability with color indicators\n\n## API Reference\n\n### Main Functions\n\n#### `get_lyrics(song_name, artist_name, album_name=None, duration=None, none_char=\"\u266a\")`\n\nFetches lyrics for a song by artist name and song name.\n\n- **Parameters**:\n    - `song_name`: Name of the song\n    - `artist_name`: Name of the artist\n    - `album_name`: (Optional) Album name to improve search accuracy\n    - `duration`: (Optional) Duration of the song in seconds\n    - `none_char`: Character to use for empty lines in synchronized lyrics\n- **Returns**: A `Lyrics` object if found, `None` otherwise\n\n#### `get_lyrics_by_id(lyrics_id, none_char=\"\u266a\")`\n\nFetches lyrics for a song by its LRCLib ID.\n\n- **Parameters**:\n    - `lyrics_id`: The LRCLib ID of the song\n    - `none_char`: Character to use for empty lines in synchronized lyrics\n- **Returns**: A `Lyrics` object if found, `None` otherwise\n\n#### `search_lyrics(q=None, song_name=None, artist_name=None, album_name=None, none_char=\"\u266a\")`\n\nSearches for lyrics by query or song/artist information.\n\n- **Parameters**:\n    - `q`: General search query string\n    - `song_name`: Optional song name for searching\n    - `artist_name`: Optional artist name for searching\n    - `album_name`: Optional album name for better matching\n    - `none_char`: Character to use for empty lines in synchronized lyrics\n- **Returns**: A list of `Lyrics` objects if found, `None` otherwise\n\n#### `to_plain_lyrics(lyrics, none_char=\"\u266a\")`\n\nConverts a `Lyrics` object or lyrics dictionary to plain text.\n\n- **Parameters**:\n    - `lyrics`: A `Lyrics` object or dictionary containing lyrics data\n    - `none_char`: Character to use for empty lines\n- **Returns**: Plain text lyrics as a string\n\n#### `request_challenge()`\n\nRequests a cryptographic challenge from the API for generating a publish token.\n\n- **Returns**: A tuple containing `(prefix, target)` for the proof-of-work challenge\n- **Raises**: `LyriqError` if the API returns an error\n\n#### `verify_nonce(result_bytes, target_bytes)`\n\nVerifies if a nonce satisfies the target requirement for the proof-of-work challenge.\n\n- **Parameters**:\n    - `result_bytes`: The hashed result as bytes\n    - `target_bytes`: The target as bytes\n- **Returns**: `True` if the nonce satisfies the target, `False` otherwise\n\n#### `generate_publish_token(prefix, target)`\n\nGenerates a valid publish token by solving a proof-of-work challenge.\n\n- **Parameters**:\n    - `prefix`: The prefix string provided by the challenge\n    - `target`: The target string in hexadecimal format provided by the challenge\n- **Returns**: A valid publish token in the format `{prefix}:{nonce}`\n- **Raises**: `LyriqError` if there is an error with the token generation\n\n#### `publish_lyrics(track_name, artist_name, album_name, duration, plain_lyrics=\"\", synced_lyrics=\"\")`\n\nPublishes lyrics to the LRCLIB API.\n\n- **Parameters**:\n    - `track_name`: Name of the track\n    - `artist_name`: Name of the artist\n    - `album_name`: Name of the album\n    - `duration`: Duration of the track in seconds\n    - `plain_lyrics`: Plain text lyrics (optional)\n    - `synced_lyrics`: Synchronized lyrics (optional)\n- **Returns**: `True` if the publish was successful, `False` otherwise\n- **Raises**: `LyriqError` if there is an error publishing the lyrics\n\n#### `get_database_dumps()`\n\nFetches the list of available database dumps from the LRCLib database dumps API.\n\n- **Returns**: A list of `DatabaseDump` objects if successful, `None` otherwise\n- **Raises**: `LyriqError` if there is an error fetching the database dumps\n\n#### `get_latest_database_dump()`\n\nGets the latest database dump from the LRCLib database dumps API.\n\n- **Returns**: The latest `DatabaseDump` object if found, `None` otherwise\n\n#### `download_database_dump(dump, download_path=None, progress_callback=None)`\n\nDownloads a database dump file.\n\n- **Parameters**:\n    - `dump`: The `DatabaseDump` object to download\n    - `download_path`: Optional path to save the file. If not provided, saves to cache directory\n    - `progress_callback`: Optional callback function to track download progress. Called with `(bytes_downloaded, total_bytes)`\n- **Returns**: The path to the downloaded file if successful, `None` otherwise\n- **Raises**: `LyriqError` if there is an error downloading the file\n\n### Lyrics Class\n\n#### Properties\n\n- `lyrics`: Dictionary mapping timestamps to lyric lines\n- `synced_lyrics`: Raw synchronized lyrics string\n- `plain_lyrics`: Plain lyrics string\n- `id`: LRCLib ID of the song\n- `name`: Name of the song\n- `track_name`: Name of the track\n- `artist_name`: Name of the artist\n- `album_name`: Name of the album\n- `duration`: Duration of the song in seconds\n- `instrumental`: Whether the song is instrumental (True/False)\n\n#### Methods\n\n##### `from_dict(data, none_char=\"\u266a\")`\n\nCreate a `Lyrics` instance from a dictionary.\n\n- **Parameters**:\n    - `data`: Raw lyrics data dictionary from the API\n    - `none_char`: Character to use for empty lines\n- **Returns**: A new `Lyrics` instance\n\n##### `to_dict()`\n\nConvert the `Lyrics` instance to a dictionary.\n\n- **Returns**: Dictionary representation of the lyrics\n\n##### `to_plain_string(none_char=None)`\n\nConvert the lyrics to a plain string representation.\n\n- **Parameters**:\n    - `none_char`: Character to use for empty lines (optional)\n- **Returns**: Plain string representation of lyrics or None if empty\n- **Raises**: `EmptyLyricsError` if lyrics are empty\n\n##### `to_plain_file(file_path, none_char=None)`\n\nWrite the lyrics to a plain text file.\n\n- **Parameters**:\n    - `file_path`: Path to the output file\n    - `none_char`: Character to use for empty lines (optional)\n- **Raises**: `EmptyLyricsError` if lyrics are empty\n\n##### `to_lrc_string()`\n\nConvert the lyrics to LRC format string.\n\n- **Returns**: LRC format string with metadata and timestamps\n\n##### `to_lrc_file(file_path)`\n\nWrite the lyrics to a LRC file.\n\n- **Parameters**:\n    - `file_path`: Path to the output file\n\n##### `from_lrc_string(lrc_string, none_char=\"\u266a\")`\n\nCreate a `Lyrics` instance from a LRC format string.\n\n- **Parameters**:\n    - `lrc_string`: LRC format string with metadata and timestamps\n    - `none_char`: Character to use for empty lines\n- **Returns**: A new `Lyrics` instance\n\n##### `from_lrc_file(file_path, none_char=\"\u266a\")`\n\nRead lyrics from a LRC file.\n\n- **Parameters**:\n    - `file_path`: Path to the LRC file\n    - `none_char`: Character to use for empty lines\n- **Returns**: A new `Lyrics` instance\n\n##### `to_json_file(file_path)`\n\nWrite the lyrics to a JSON file.\n\n- **Parameters**:\n    - `file_path`: Path to the output file\n\n##### `from_json_file(file_path, none_char=\"\u266a\")`\n\nRead lyrics from a JSON file.\n\n- **Parameters**:\n    - `file_path`: Path to the JSON file\n    - `none_char`: Character to use for empty lines\n- **Returns**: A new `Lyrics` instance\n\n### DatabaseDump Class\n\n#### Properties\n\n- `storage_class`: Storage class of the dump file (e.g., \"Standard\")\n- `uploaded`: Upload datetime as a `datetime` object\n- `checksums`: Dictionary of checksums for the file\n- `http_etag`: HTTP ETag header value\n- `etag`: ETag value\n- `size`: Size of the dump file in bytes\n- `version`: Version identifier of the dump\n- `key`: Key/filename of the dump file\n- `filename`: Extracted filename from the key (property)\n- `download_url`: Full download URL for the dump (property)\n\n#### Methods\n\n##### `from_dict(data)`\n\nCreate a `DatabaseDump` instance from a dictionary.\n\n- **Parameters**:\n    - `data`: Raw database dump data dictionary from the API\n- **Returns**: A new `DatabaseDump` instance\n\n## Examples\n\nSee the examples directory for practical usage examples:\n\n- `examples/basic_usage.py` - Basic usage of the library\n- `examples/fetch_by_id.py` - Fetching lyrics by ID\n- `examples/search_lyrics.py` - Searching for lyrics\n- `examples/synced_lyrics.py` - Working with synchronized lyrics\n- `examples/format_conversion.py` - Converting between formats\n- `examples/publishing_lyrics.py` - Publishing lyrics to the API\n- `examples/database_dumps.py` - Working with database dumps\n\n## Development\n\n### Project Structure\n\n```\nlyriq/\n\u251c\u2500\u2500 .github/\n\u2502   \u251c\u2500\u2500 workflows/\n\u2502   \u2502   \u251c\u2500\u2500 python-tests.yml\n\u2502   \u2502   \u2514\u2500\u2500 python-publish.yml\n\u251c\u2500\u2500 environment.yml      # Environment file\n\u251c\u2500\u2500 examples/            # Example files demonstrating usage\n\u2502   \u251c\u2500\u2500 basic_usage.py\n\u2502   \u251c\u2500\u2500 fetch_by_id.py\n\u2502   \u251c\u2500\u2500 search_lyrics.py\n\u2502   \u251c\u2500\u2500 synced_lyrics.py\n\u2502   \u251c\u2500\u2500 format_conversion.py\n\u2502   \u2514\u2500\u2500 publishing_lyrics.py\n\u251c\u2500\u2500 lyriq/\n\u2502   \u251c\u2500\u2500 __init__.py      # Package exports\n\u2502   \u251c\u2500\u2500 __main__.py      # CLI entry point\n\u2502   \u251c\u2500\u2500 lyriq.py         # Core functionality\n\u2502   \u251c\u2500\u2500 cli.py           # Command line interface\n\u2502   \u2514\u2500\u2500 cache/           # Auto-generated cache directory\n\u2502       \u251c\u2500\u2500 lyrics.json  # Lyrics cache\n\u2502       \u2514\u2500\u2500 search.json  # Search cache\n\u251c\u2500\u2500 tests/\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u2514\u2500\u2500 test_lyriq.py    # Test suite\n\u251c\u2500\u2500 pyproject.toml       # Project configuration\n\u251c\u2500\u2500 setup.py             # Setup script\n\u251c\u2500\u2500 main.py              # Example usage\n\u251c\u2500\u2500 README.md            # This file\n\u251c\u2500\u2500 LICENSE              # License file\n\u2514\u2500\u2500 .gitignore           # Git ignore file\n```\n\n### Running Tests\n\n```bash\n# Install development dependencies\npip install -e \".[dev]\"\n\n# Run the test suite\npytest\n\n# Run with coverage report\npytest --cov=lyriq --cov-report=term-missing\n```\n\n### Adding Features\n\n1. Make changes to the core functionality in `lyriq.py`\n2. Add appropriate tests in `test_lyriq.py`\n3. Update documentation in docstrings and README.md\n4. Run tests to ensure everything works\n\n## Technical Details\n\n### API\n\nLyriq uses the LRCLib API (https://lrclib.net/api) to fetch lyrics data. The API provides both synchronized and plain lyrics.\n\n### Caching\n\nTo reduce API calls and improve performance, Lyriq caches all retrieved lyrics in JSON files located in the cache directory:\n\n```\n<package_location>/cache/\n```\n\nThe cache is thread-safe and automatically writes to disk when new lyrics are added.\n\n### Synchronized Lyrics Format\n\nSynchronized lyrics are stored in LRC format with timestamps in the format `[MM:SS.ms]`. The CLI tool interprets these timestamps to display the lyrics at the right moment during playback.\n\n## License\n\nCopyright 2025 TN3W\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A lightweight Python library designed to effortlessly fetch song lyrics.",
    "version": "1.4.0",
    "project_urls": {
        "Documentation": "https://github.com/tn3w/lyriq/blob/master/README.md",
        "Homepage": "https://github.com/tn3w/lyriq",
        "Issues": "https://github.com/tn3w/lyriq/issues",
        "Repository": "https://github.com/tn3w/lyriq.git"
    },
    "split_keywords": [
        "lyrics",
        " music",
        " synchronization"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "4502fcb44d89f1ea677b30d6fb9f57421861dbb47cce369351b0e26ab17ad8ab",
                "md5": "bcb5d9e84acba5926a3ca096b5423c72",
                "sha256": "0e93608531d1a421abad5a167939fe6dcc553b26d483dc967bebbe0aa751fa7b"
            },
            "downloads": -1,
            "filename": "lyriq-1.4.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "bcb5d9e84acba5926a3ca096b5423c72",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 25941,
            "upload_time": "2025-07-24T13:50:45",
            "upload_time_iso_8601": "2025-07-24T13:50:45.489381Z",
            "url": "https://files.pythonhosted.org/packages/45/02/fcb44d89f1ea677b30d6fb9f57421861dbb47cce369351b0e26ab17ad8ab/lyriq-1.4.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "777bd1d436bda78d6ee8e5bf0f13f1375fbc79de930d2d875eafd867dab9ae8d",
                "md5": "39a7d881174ffc7194aba04124c4cdbd",
                "sha256": "d3f453f664b0b4425dd5f6e13ca35b601ffd75702bee2eeb81470198a8bc6d38"
            },
            "downloads": -1,
            "filename": "lyriq-1.4.0.tar.gz",
            "has_sig": false,
            "md5_digest": "39a7d881174ffc7194aba04124c4cdbd",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 37613,
            "upload_time": "2025-07-24T13:50:46",
            "upload_time_iso_8601": "2025-07-24T13:50:46.852962Z",
            "url": "https://files.pythonhosted.org/packages/77/7b/d1d436bda78d6ee8e5bf0f13f1375fbc79de930d2d875eafd867dab9ae8d/lyriq-1.4.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-24 13:50:46",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "tn3w",
    "github_project": "lyriq",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "lyriq"
}
        
Elapsed time: 0.43328s