lrclib Documentation

repository·main·Indexed 23 days ago

https://github.com/tranxuanthang/lrclib

A high-performance, free service for finding and contributing synchronized lyrics. Written in Rust using the Axum framework and SQLite3, lrclib provides an API for track lookup, FTS-backed search, and a proof-of-work based publishing system. Documentation covers server architecture, deployment via Docker/Podman, the YAML-based lyricsfile specification for synced and plain lyrics, and API endpoint references.

Tokens
6.9K
Snippets
10
Records
42
Agent score
82%

What's inside lrclib

  1. Overview of the LRCLIB API Server Architecture

    main

    LRCLIB is a Rust-based workspace for an API server. The architecture is organized as follows:

    • Root crate: A thin CLI wrapper (src/main.rs) that parses arguments like --port, --database, and --workers-count.
    • Main app (server/ crate): Contains the core logic, including app bootstrap, AppState, Axum router, middleware, and background tasks.
    • Storage: Uses SQLite via rusqlite and r2d2 for connection pooling.
    • HTTP Framework: Built on Axum.

    Workspace Structure

    • server/src/routes/: HTTP handlers.
    • server/src/repositories/: SQL access layer.
    • server/src/entities/: Database and domain structs.
    • server/src/utils.ts: Input normalization and verification helpers.
    • server/migrations/: Embedded SQLite schema and migrations applied at startup.
  2. How track matching and searching works

    main

    Matching Rules

    Track matching is designed to be fuzzy but narrow:

    • Normalization: Names are processed via utils::prepare_input (punctuation is removed/collapsed, comparisons are lowercase).
    • Duration: Metadata duration matching uses a tolerance of approximately +/-2 seconds.
    • Preference: /api/get requests prefer tracks where the current lyrics include synced lyrics.

    Search Mechanism

    • Engine: Uses SQLite FTS5 (tracks_fts virtual table).
    • Caching: Results are stored in search_cache (versioned as search:v2) with a 24h TTL and 4h idle timeout. Cached results may be refreshed in the background.
  3. How the LRCLIB Runtime Flow works

    main

    When the server starts via server::serve_with_queue, it follows this lifecycle:

    1. Initialization: Sets up tracing (via LRCLIB_LOG), opens the SQLite connection pool, and applies necessary PRAGMAs and migrations.
    2. State Setup: Builds the shared AppState container.
    3. Background Tasks: Spawns tasks for:
      • Request metrics reporter: Logs requests per minute.
      • Recent lyrics counter: Refreshes the count of lyrics published in the last 10 minutes once per minute.
      • Queue supervisor: Monitors the desired worker count and manages the queue backend.
    4. Routing: Builds the Axum router and starts serving on 0.0.0.0:<port> with support for graceful shutdown.
  4. Understand Lyricsfile precedence and backward compatibility

    main

    LRCLIB uses a precedence model to transition from legacy lyric formats to the new lyricsfile format while ensuring older clients do not break.

    Precedence Logic:

    1. Lyricsfile Wins: If a POST /api/publish request contains a non-empty lyricsfile, the server treats it as the primary source of truth. Any plainLyrics or syncedLyrics sent in the same request are ignored.
    2. Legacy Derivation: To support older clients that only understand plainLyrics and syncedLyrics, the server performs a best-effort extraction from the lyricsfile YAML to populate these legacy fields in the database.
    3. Fallback: If lyricsfile is empty or absent, the server falls back to the standard legacy publishing behavior.

    Client Impact:

    • New Clients: Should consume the lyricsfile field for the most complete lyric representation.
    • Old Clients: Will continue to receive data via plainLyrics and syncedLyrics because the server derives them from the lyricsfile during the publish process.
  5. Understand the AppState shared container

    main

    AppState is the central shared runtime container used throughout the application. It holds:

    • Database: pool (SQLite connection pool).
    • Caches:
      • challenge_cache: Stores proof-of-work publish challenges (TTL 5 min).
      • get_cache: Deduplicates missing-track enqueue requests (TTL 7 days).
      • get_metadata_cache: Stores /api/get responses.
      • get_metadata_index: Maps track_id to cache keys for invalidation during publishing.
      • search_cache: Stores /api/search responses (24h TTL, 4h idle timeout).
    • Queue: A bounded in-memory ArrayQueue<MissingTrack>.
    • Metrics: request_counter and recent_lyrics_count.
    • Queue Config: workers_count and workers_tx for runtime configuration.
  6. The LRCLIB Lyrics File Specification

    main

    The LRCLIB lyrics format is a YAML-based specification used to store song metadata, synced lyrics (line and word level), and plain text lyrics. It supports versioning, metadata for song identification, and different modes such as unsynced text or instrumental tracks.

    version: "1.0"
    
    metadata:
      title: "Song Title"
      artist: "Artist Name"
      album: "Album Name"
      duration_ms: 245000
      offset_ms: 0
      language: "en"
      instrumental: false
    
    lines:
      - text: "Synced line here"
        start_ms: 12000
        end_ms: 15500
    
    plain: |
      Song Title
    
      [Verse 1]
      Synced line here
      Another synced line
    
      [Chorus]
      Hook line here
  7. Cache behavior for Lyricsfile updates

    main

    Caching behavior differs between the get and search APIs when new lyrics are published:

    1. /api/get (Metadata Cache):

      • This cache is actively invalidated on publish.
      • When you publish a new lyricsfile for a track, the cache for that specific track_id is cleared, ensuring immediate consistency for direct lookups.
      • Note: The cache key version has been bumped (e.g., to get:v3) to accommodate the new response shape.
    2. /api/search (Search Cache):

      • This cache is time-based (typically 24h TTL with a 4h idle timeout).
      • It is not invalidated when a track is published.
      • Implication: Newly published lyricsfile content may not appear in search results immediately. It will appear once the existing cache entry expires or is refreshed in the background.
  8. Build and run LRCLIB from source

    main
    To run the LRCLIB server locally using Cargo, build the project in release mode and then use the serve command, specifying a SQLite database file. The server defaults to listening on http://0.0.0.0:3300.
  9. Run LRCLIB as a systemd service using Quadlet

    main

    For production-like environments using Podman, you can use Quadlet to manage the LRCLIB container as a systemd service. This ensures the container starts automatically on boot.

    1. Create a lrclib.container file in ~/.config/containers/systemd/.
    2. Reload the systemd daemon.
    3. Start the service.

    Example lrclib.container configuration:

    [Container]
    Image=lrclib-rs:latest
    PublishPort=3300:3300
    Volume=lrclib-data:/data
    ContainerName=lrclib-rs
    Environment=LRCLIB_LOG=info
    
    [Service]
    Restart=always
    
    [Install]
    WantedBy=multi-user.target default.target

    Service Management Commands:

    # Reload daemon after creating/editing the file
    systemctl --user daemon-reload
    
    # Start the service
    systemctl --user start lrclib.service
    
    # Check status
    systemctl --user status lrclib.service
    
    # Restart after image updates
    systemctl --user restart lrclib.service
  10. How to publish lyrics via the API

    main

    Publishing lyrics involves a proof-of-work challenge to prevent spam.

    1. Request Challenge: Call POST /api/request-challenge to receive a random prefix and target difficulty.
    2. Solve Challenge: Compute the solution.
    3. Publish: Call POST /api/publish providing the X-Publish-Token in the format prefix:nonce. The server verifies this using SHA-256 threshold comparison.

    Data Formats

    • Lyricsfile: If a lyricsfile is supplied, it is stored as raw YAML. The server performs a best-effort derivation of legacy plainLyrics and syncedLyrics columns from the YAML payload.
    • Synced Lyrics: If only synced lyrics are provided, plainLyrics is derived by stripping timestamps.
    • Instrumental: Providing the marker [au: instrumental] creates a lyrics row with no text and sets instrumental = true. For lyricsfile uploads, this is populated via the metadata.instrumental field in the YAML.
  11. Implement Lyricsfile support in LRCLIB

    main

    To support the Lyricsfile format, LRCLIB implements it as a strictly additive third representation stored within the existing lyrics row.

    For the initial rollout, the following behavior is defined:

    • Publishing: Accept raw YAML Lyricsfile in the publish endpoint under the lyricsfile field.
    • Retrieval: Return raw YAML Lyricsfile in get and search endpoints under the lyricsfile field.
    • Precedence: When a lyricsfile is present, legacy lyric fields should be ignored.
    • Caching:
      • Keep the /search cache strategy unchanged.
      • Version the /api/get cache keys to prevent mixing different response shapes (e.g., when a response contains legacy lyrics vs. when it contains a lyricsfile).

    This design avoids schema redesigns, backfills, and high migration risks while allowing Lyricsfile to eventually become the primary format.