A fast, interactive TUI application for fetching and storing synchronized lyrics from lrclib.net with atomic session persistence and smart caching.
- ๐ต Multi-format Support - FLAC, Opus, MP3, AAC, OGG, APE, WAV, M4A
- ๐ Async Processing - Non-blocking worker with rate-limited API calls (10 req/s)
- ๐พ Smart Caching - SQLite-backed negative cache prevents redundant lookups
- ๐ฆ Atomic Session Persistence - Resume interrupted scans from exactly where you left off
- ๐จ Beautiful TUI - Real-time progress tracking with color-coded status
- ๐ XDG Compliant - Follows Linux standards (
~/.local/share/getlrc/)
- ๐ง Metadata Normalization - Cleans track numbers, punctuation, and extra whitespace
- ๐ฏ Fuzzy Matching - Uses Jaro-Winkler algorithm to match similar titles (>85% similarity)
- ๐ Fallback Search - Automatically retries with stripped metadata if first attempt fails
- โจ Featuring Artist Handling - Normalizes "feat.", "ft.", "featuring", "with" variations
- ๐ Potential Match Detection - Logs matches with 60-85% similarity for manual review
- ๐ผ Version Preservation - Keeps "Remix", "Live", "Acoustic" info (different lyrics/timing)
- ๐พ Auto-save on Pause - Session state saved atomically when paused
- ๐ Session Recovery - Automatically resumes from saved sessions
- ๐ Real-time Progress - Live progress bar with 100% completion guarantee
- ๐ Auto-scrolling Logs - Latest entries always visible
- โ๏ธ Atomic Saves - Crash-safe session persistence using temp files
- ๐ Integrity Checks - Validates session files before resuming
- ๐ก๏ธ Permission Verification - Checks write access before starting
- ๐ Comprehensive Logging - All operations logged to file for debugging
- ๐ฏ 100% Progress Accuracy - Progress bar always reaches completion
- โก Parallel Directory Scanning - Multi-threaded filesystem traversal with
jwalk - ๐ Concurrent Worker Pool - 5 async workers process files simultaneously
- ๐ฆ Token-Bucket Rate Limiting - Strict 10 req/s limit using
governorcrate - ๐ Thread-Safe State - Work-stealing queue with atomic session updates
- ๐ Optimized for Large Libraries - Handles 40,000+ files efficiently
# Clone the repository
git clone https://github.com/c0mpile/getlrc.git
cd getlrc
# Install locally
cargo install --path .The binary will be installed to ~/.cargo/bin/getlrc (or ~/.local/bin if configured).
# Check if installed
which getlrc
# Verify PATH (getlrc will warn if ~/.local/bin is missing)
echo $PATH | grep -o "$HOME/.local/bin"If ~/.cargo/bin or ~/.local/bin is not in your PATH:
Bash/Zsh (~/.bashrc or ~/.zshrc):
export PATH="$HOME/.cargo/bin:$PATH"
# or
export PATH="$HOME/.local/bin:$PATH"Fish (~/.config/fish/config.fish):
set -gx PATH $HOME/.cargo/bin $PATHThen reload your shell:
source ~/.bashrc # or ~/.zshrc# Scan a music directory
getlrc ~/Music
# Scan a specific album
getlrc ~/Music/Artist/Album
# Force retry: bypass negative cache and retry all files
getlrc --force-retry ~/Music
getlrc -f ~/Music
# Show help
getlrc --helpThe --force-retry (or -f) flag bypasses the negative cache and retries fetching lyrics for all files, even those previously marked as "not found":
getlrc --force-retry ~/MusicBehavior:
- โ Bypasses Cache: Ignores negative cache entries and queries the API for every file
- โ Removes on Success: If lyrics are found during a forced retry, the file is removed from the negative cache
- โ Updates on Failure: If still not found, updates the timestamp in the cache
- โ Session Persistence: The flag is preserved when resuming a paused session
- ๐ Logged: All cache bypasses are logged to
~/.local/share/getlrc/logs/getlrc.log
Use Cases:
- New lyrics were added to lrclib.net since your last scan
- You want to re-check files that were previously unavailable
- Testing or debugging cache behavior
While the TUI is running:
| Key | Action | Description |
|---|---|---|
q |
Quit | Exit application (saves session if paused) |
p |
Pause | Pause processing and save session |
r |
Resume | Resume processing from paused state |
Note: When you press p (Pause), the current session is automatically saved. You can safely quit with q and resume later by running getlrc again on the same directory.
Pause and Resume Workflow:
# Start scanning
$ getlrc ~/Music
# Processing: 45/200 files...
# Press 'p' to pause
# Session saved to ~/.local/share/getlrc/session.json
# Press 'q' to quit
# Application exits, state preserved
# Later, resume the scan
$ getlrc ~/Music
๐ Resuming previous session...
# Continues from file 46/200 with full log historySession Features:
- โ Atomic saves (crash-safe)
- โ Integrity checks (detects stale sessions)
- โ Log history restoration (visual continuity)
- โ Progress count preservation
- โ Automatic cleanup on completion
The TUI displays concise status updates for each file:
| Symbol | Meaning | Description |
|---|---|---|
[โ] |
Downloaded | Lyrics downloaded and saved successfully |
[~] |
Cached | Previously not found, skipped API call |
[โ] |
Existing | Already has .lrc file, skipped |
[โ] |
Not Found | Lyrics not available on lrclib.net |
[!] |
Error | Processing error (see logs for details) |
| Color | Meaning |
|---|---|
| ๐ข Green | Lyrics downloaded from API |
| ๐ก Yellow | Cached (previously not found) |
| ๐ต Blue | Already has .lrc file |
| โซ Dark Gray | Not yet processed |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ getlrc - Processing... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Progress โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ 80%
โ โ Downloaded: 45 โ Cached: 12 โ Existing: 8 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[โ] Downloaded | [~] Cached | [โ] Existing | [โ] Not Found | [!] Error
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Logs โ
โ [โ] song1.mp3 โ
โ [~] song2.flac โ
โ [โ] song3.m4a โ
โ ... (auto-scrolls to latest) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ q Quit | p Pause โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- Environment Verification - Checks directories and permissions
- Session Check - Looks for existing session to resume
- Parallel Directory Scan - Multi-threaded traversal finds all audio files using
jwalk - Skip Existing - Ignores files that already have
.lrcsidecars - Work Queue Population - Pending files added to thread-safe work-stealing queue
- Worker Pool Spawning - 5 concurrent async workers start processing
- Metadata Extraction - Reads artist, title, album, duration using
lofty - Cache Lookup - Checks SQLite database for previously unfound tracks
- Rate-Limited API Query - Fetches lyrics from lrclib.net (10 req/s via
governor) - Atomic Write - Saves synchronized lyrics as
.lrcfiles - Session Update - Thread-safe updates to session state for resume capability
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TUI (Ratatui) โ
โ โโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโ โ
โ โ Header โ โ Progress โ โ Logs โ โ Footer โ โ
โ โโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ mpsc channels (bidirectional)
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Worker Pool (Tokio + Arc) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Parallel Scanner (jwalk) โ โ
โ โ - Multi-threaded directory traversal โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Work Queue (Arc<Mutex<VecDeque>>) โ โ
โ โ - Work-stealing pattern โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโโโ โ
โ โWorker 1โ โWorker 2โ โWorker 3โ โWorker 4โ โW5โ โ
โ โโโโโโฌโโโโ โโโโโโฌโโโโ โโโโโโฌโโโโ โโโโโโฌโโโโ โโโฌโ โ
โ โ โ โ โ โ โ
โ โโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Governor Rate Limiter (10 req/s) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโ โ
โ โ Cache โโ โ LRCLIB โโ โ .lrc โ โSession โ โ
โ โ (SQLite) โ โ API โ โ Writer โ โ (Mutex)โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโ โ
โ โ
โ Thread-Safe Shared State (Arc<Mutex>): โ
โ - Session persistence (atomic saves) โ
โ - Progress counts (downloaded/cached/failed) โ
โ - Log history (500 entries) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
All persistent data follows XDG Base Directory specification:
| File | Path | Purpose |
|---|---|---|
| Cache Database | ~/.local/share/getlrc/negative_cache.db |
Stores tracks not found on lrclib.net |
| Session File | ~/.local/share/getlrc/session.json |
Saves progress when paused |
| Debug Logs | ~/.local/share/getlrc/logs/getlrc.log |
All operations and errors |
{
"root_path": "/home/user/Music",
"pending_files": ["file3.mp3", "file4.flac"],
"downloaded_count": 42,
"cached_count": 15,
"existing_count": 8,
"failed_count": 3,
"log_history": [
{ "filename": "song1.mp3", "status": "Downloaded" },
{ "filename": "song2.flac", "status": "Cached" }
]
}Features:
- โ๏ธ Atomic saves (temp file + rename)
- ๐ Integrity checks (validates on load)
- ๐ Log capping (max 500 entries)
- ๐งน Auto-cleanup (deleted on completion)
All debug output is written to the log file to keep the TUI clean:
Log Contents:
- Full file paths
- API request URLs
- Metadata extraction details
- Detailed error messages
- Timing information
- Session operations
View Logs:
# Real-time monitoring
tail -f ~/.local/share/getlrc/logs/getlrc.log
# Search for errors
grep ERROR ~/.local/share/getlrc/logs/getlrc.log
# View session operations
grep "Session" ~/.local/share/getlrc/logs/getlrc.log# Debug build
cargo build
# Release build (optimized)
cargo build --release
# Run tests
cargo test
# Lint (zero warnings required)
cargo clippy -- -D warnings
# Format code
cargo fmt
# Install locally for testing
cargo install --path . --forcegetlrc/
โโโ src/
โ โโโ main.rs # Entry point, CLI handling
โ โโโ lib.rs # Module exports
โ โโโ api.rs # lrclib.net API client
โ โโโ cache.rs # SQLite negative cache
โ โโโ env.rs # Environment verification
โ โโโ messages.rs # Worker โ TUI messages
โ โโโ paths.rs # XDG path utilities
โ โโโ scanner/ # Directory scanning, metadata
โ โโโ session.rs # Session persistence
โ โโโ tui/ # Terminal UI (Ratatui)
โ โโโ worker.rs # Async processing logic
โโโ Cargo.toml # Dependencies and metadata
โโโ README.md # This file
| Crate | Purpose |
|---|---|
tokio |
Async runtime |
ratatui |
Terminal UI framework |
crossterm |
Terminal control |
clap |
CLI argument parsing |
lofty |
Audio metadata extraction |
rusqlite |
SQLite database |
reqwest |
HTTP client (rustls) |
walkdir |
Sequential directory traversal (legacy) |
jwalk |
Parallel directory traversal |
governor |
Token-bucket rate limiting |
regex |
String normalization and cleaning |
strsim |
Fuzzy string matching (Jaro-Winkler) |
tracing |
Structured logging |
serde |
Serialization |
anyhow |
Error handling |
- Basic lyrics fetching and saving
- XDG Base Directory compliance
- SQLite negative cache
- Interactive TUI with real-time updates
- Pause/Resume controls
- Atomic session persistence
- Log history restoration
- Progress bar 100% completion
- Environment verification
- Integrity checks for stale sessions
- Comprehensive logging
- Parallel directory walking with
jwalk - Concurrent API worker pool (5 workers) with
governorrate limiting - Thread-safe session management with work-stealing queue
- Force retry mode with
--force-retryflag - Smart metadata normalization with regex cleaning
- Fuzzy matching using Jaro-Winkler algorithm (>85% auto-accept)
- Automatic fallback search with stripped metadata
- Exponential backoff for API retries
- Configurable retry limits
- Progress estimation and ETA
- Configurable API rate limits
- Multiple API source support
- Lyrics quality scoring
- Batch processing modes
- Configuration file support
Issue: Binary not found after installation
# Check if ~/.cargo/bin is in PATH
echo $PATH | grep cargo
# Add to PATH if missing (see Installation section)Issue: Permission denied errors
# Check data directory permissions
ls -la ~/.local/share/getlrc/
# Fix permissions if needed
chmod 755 ~/.local/share/getlrc/
chmod 644 ~/.local/share/getlrc/*.dbIssue: Stale session detected
# This happens if files were deleted while app was closed
# The app will automatically start a fresh scan
# To force a fresh scan, delete the session file:
rm ~/.local/share/getlrc/session.jsonIssue: Progress bar stuck before 100%
# This was fixed in v0.1.0
# Update to the latest version:
cargo install --path . --forceFor detailed debugging, check the log file:
# View full log
cat ~/.local/share/getlrc/logs/getlrc.log
# Monitor in real-time
tail -f ~/.local/share/getlrc/logs/getlrc.log
# Search for specific errors
grep -i "error\|warn" ~/.local/share/getlrc/logs/getlrc.logMIT
- Lyrics provided by lrclib.net
- Built with Rust ๐ฆ
- UI powered by Ratatui
Contributions are welcome! Please feel free to submit issues or pull requests.
- Follow Rust idioms and best practices
- Maintain zero
cargo clippywarnings - Add tests for new features
- Update documentation
- Preserve surgical precision (minimal diff noise)
Current Version: 0.4.0
Status: Production Ready (Multi-threaded + Fuzzy Matching)
Next Milestone: Error Resilience (Exponential Backoff)