lyriq


Namelyriq JSON
Version 1.2.0 PyPI version JSON
download
home_pageNone
SummaryA lightweight Python library designed to effortlessly fetch song lyrics.
upload_time2025-07-15 12:37:24
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

## 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")
```

## 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] [--file FILE] [--file-format {plain,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               Display only plain lyrics
  --file FILE           File to save lyrics to and exit
  --file-format {plain,json}
                        Format to save lyrics to
  --load LOAD           Load lyrics from file
```

### 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

# 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

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

# 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
```

### 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

### 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_file(file_path)`

Write the lyrics to a plain text file.

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

##### `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

## 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

## 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

Apache 2.0 License - See LICENSE file for details.

## 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/3d/41/7793aeda2a31115b30f8543f6901e043367a7675dc90d32ae68bc52db681/lyriq-1.2.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\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## 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]] [--search-index SEARCH_INDEX]\n             [--none-char NONE_CHAR] [--no-info] [--plain] [--file FILE] [--file-format {plain,json}] [--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               Display only plain lyrics\n  --file FILE           File to save lyrics to and exit\n  --file-format {plain,json}\n                        Format to save lyrics to\n  --load LOAD           Load lyrics from file\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# 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# Load lyrics from file\nlyriq --load Circles-Post-Malone.json\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\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### 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_file(file_path)`\n\nWrite the lyrics to a plain text file.\n\n- **Parameters**:\n    - `file_path`: Path to the output file\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## 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\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\nApache 2.0 License - See LICENSE file for details.\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.2.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": "a2e4dfc980a037341a578531231744cb707133e49bd0b97edc7d5fd2b05b1baf",
                "md5": "0f420d82dc71914cff4af84d02b471d6",
                "sha256": "d96207204bce964fb66c46b6c7173ca228a9a7ec43dcc5ca50fecf711663b4c1"
            },
            "downloads": -1,
            "filename": "lyriq-1.2.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "0f420d82dc71914cff4af84d02b471d6",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 20792,
            "upload_time": "2025-07-15T12:37:23",
            "upload_time_iso_8601": "2025-07-15T12:37:23.350851Z",
            "url": "https://files.pythonhosted.org/packages/a2/e4/dfc980a037341a578531231744cb707133e49bd0b97edc7d5fd2b05b1baf/lyriq-1.2.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "3d417793aeda2a31115b30f8543f6901e043367a7675dc90d32ae68bc52db681",
                "md5": "257c705e8337f34df5f881754dc1da07",
                "sha256": "8d4407d1a6bd5de2330ab7f50185c2fc1736ef5b61d43e2bb3331bcadcf6a71e"
            },
            "downloads": -1,
            "filename": "lyriq-1.2.0.tar.gz",
            "has_sig": false,
            "md5_digest": "257c705e8337f34df5f881754dc1da07",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 28349,
            "upload_time": "2025-07-15T12:37:24",
            "upload_time_iso_8601": "2025-07-15T12:37:24.263182Z",
            "url": "https://files.pythonhosted.org/packages/3d/41/7793aeda2a31115b30f8543f6901e043367a7675dc90d32ae68bc52db681/lyriq-1.2.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-15 12:37:24",
    "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: 1.45965s