LyricFlow now includes a powerful Musixmatch integration that can automatically download synced/unsynced lyrics with three different interfaces:
- CLI - Fast, automatic best-match selection
- TUI (Textual) - Interactive preview and manual selection
- FastAPI - RESTful API for web integration
- ✅ Synced Lyrics (LRC format with timestamps)
- ✅ Unsynced Lyrics (Plain text)
- ✅ Translation Support (Musixmatch translations)
- ✅ Romanization (Using LyricFlow's local/AI romanizer)
- ✅ Smart Matching (Automatic best-match algorithm)
- ✅ Embedding (Direct embedding into audio files)
- ✅ Preview (Interactive TUI for selection)
# Install with fetch support (requests library)
pip install lyricflow[fetch]
# Install with TUI support (interactive mode)
pip install lyricflow[tui]
# Install with everything
pip install lyricflow[all]Basic search (shows preview):
lyricflow fetch -t "Bohemian Rhapsody" -a "Queen"Save to LRC file:
lyricflow fetch -t "Song Title" -a "Artist Name" -o output.lrcFetch and embed to audio file:
lyricflow fetch -t "Song Title" -a "Artist" --audio song.m4a --embedWith romanization:
lyricflow fetch -t "こんにちは" -a "日本アーティスト" --romanization -o output.lrcWith translation:
lyricflow fetch -t "Song Title" -a "Artist" --translation -o output.lrcFull example:
lyricflow fetch \
--title "Bohemian Rhapsody" \
--artist "Queen" \
--album "A Night at the Opera" \
--audio "song.m4a" \
--output "custom.lrc" \
--romanization \
--translation \
--embedLaunch interactive TUI:
lyricflow fetch -t "Song Title" -a "Artist" --interactiveTUI with audio file (auto-fills metadata):
lyricflow fetch --audio song.m4a --interactiveTUI Features:
- 📋 Search multiple results
- 👁️ Preview lyrics before selection
- ⚡ Real-time search
- 💾 Save to LRC file
- 📝 Embed directly to audio
- 🌍 Fetch translations
- 🔤 Generate romanization
TUI Controls:
Tab/Shift+Tab- Navigate fieldsEnter- Search / SelectCtrl+S- SearchCtrl+Q- QuitEscape- Clear- Mouse clicks work too!
TUI Screenshot Flow:
┌─────────────────────────────────────────────────┐
│ 🎵 LyricFlow - Musixmatch Lyrics Searcher │
├─────────────────────────────────────────────────┤
│ Title: [Bohemian Rhapsody ] │
│ Artist: [Queen ] │
│ Album: [A Night at the Opera ] │
│ │
│ [ ] Fetch Translation [ ] Fetch Romanization │
│ [Search] │
│ │
│ ✅ Found 3 results │
├─────────────────────────────────────────────────┤
│ Results Table: │
│ > Bohemian Rhapsody | Queen | A Night... | ... │
│ Bohemian Rhapsody | Queen | Greatest...| ... │
├─────────────────────────────────────────────────┤
│ Lyrics Preview: │
│ [00:00.50]Is this the real life? │
│ [00:04.25]Is this just fantasy? │
│ ... │
├─────────────────────────────────────────────────┤
│ [Save LRC] [Embed to Audio] [Cancel] │
└─────────────────────────────────────────────────┘
Start the server:
uvicorn lyricflow.api.server:app --reloadSearch for lyrics:
curl -X POST "http://localhost:8000/fetch/search" \
-H "Content-Type: application/json" \
-d '{
"title": "Bohemian Rhapsody",
"artist": "Queen",
"fetch_romanization": true
}'Response:
{
"query": {
"title": "Bohemian Rhapsody",
"artist": "Queen",
"album": ""
},
"results_count": 3,
"results": [
{
"track_id": 12345,
"title": "Bohemian Rhapsody",
"artist": "Queen",
"album": "A Night at the Opera",
"duration": 354,
"has_lyrics": true,
"has_subtitles": true,
"rating": 95,
"match_score": 0.98,
"synced_lyrics": "[00:00.50]Is this the real life?\\n[00:04.25]Is this just fantasy?...",
"romanization": "..."
}
]
}Get lyrics by track ID:
curl "http://localhost:8000/fetch/12345?synced=true&romanization=true"API Documentation:
Visit http://localhost:8000/docs for interactive Swagger UI
The CLI mode automatically selects the best match using:
-
Title Match (50% weight)
- Exact match: +50%
- Partial match: +30%
-
Artist Match (30% weight)
- Exact match: +30%
- Partial match: +15%
-
Synced Lyrics Bonus (10% weight)
- Has synced lyrics: +10%
-
Rating Bonus (10% weight)
- Based on Musixmatch rating (0-100)
Example:
$ lyricflow fetch -t "Bohemian" -a "Queen"
✅ Best match: Bohemian Rhapsody - Queen (score: 0.95)Synced Lyrics (LRC Format):
[ti:Bohemian Rhapsody]
[ar:Queen]
[al:A Night at the Opera]
[00:00.50]Is this the real life?
[00:04.25]Is this just fantasy?
[00:08.00]Caught in a landslide
Unsynced Lyrics:
Is this the real life?
Is this just fantasy?
Caught in a landslide
No escape from reality
Priority: CLI always prefers synced lyrics when available.
How it works:
- Fetches original lyrics from Musixmatch
- Uses LyricFlow's romanizer (local or AI)
- Saves separate
_romaji.lrcfile
Example:
lyricflow fetch -t "こんにちは" -a "アーティスト" --romanization -o song.lrcOutput:
song.lrc- Original lyrics with timestampssong_romaji.lrc- Romanized lyrics with timestamps
Note: Musixmatch provides translations for some songs.
Example:
lyricflow fetch -t "Song" -a "Artist" --translationOutput: Translation will be included in the API response or shown in TUI.
Supported formats:
- MP3 (.mp3)
- M4A (.m4a)
- FLAC (.flac)
- OGG (.ogg)
Example:
# Fetch and embed in one command
lyricflow fetch -t "Song" -a "Artist" --audio song.m4a --embed
# Or use TUI for interactive selection
lyricflow fetch --audio song.m4a --interactive
# Then click "Embed to Audio" buttonWhat gets embedded:
- Synced lyrics → SYLT tag (time-synced lyrics)
- Unsynced lyrics → USLT tag (unsynced lyrics text)
- Metadata preserved
Process multiple audio files:
#!/bin/bash
for file in *.m4a; do
echo "Processing: $file"
# Extract filename without extension
name="${file%.*}"
# Auto-fetch lyrics (will use audio metadata)
lyricflow fetch --audio "$file" --embed
donefrom pathlib import Path
from lyricflow.core.musixmatch import MusixmatchFetcher
# Initialize fetcher
fetcher = MusixmatchFetcher()
# Search for lyrics
results = fetcher.search(
title="Bohemian Rhapsody",
artist="Queen",
fetch_lyrics=True,
fetch_romanization=True
)
# Get best match
best = fetcher.get_best_match("Bohemian Rhapsody", "Queen")
if best:
print(f"Found: {best.title} by {best.artist}")
# Save to file
output = Path("output.lrc")
fetcher.save_lrc(best, output)
print(f"Saved to {output}")Webhook example:
import requests
# Your Flask/Django/FastAPI server
@app.post("/fetch-lyrics")
async def fetch_lyrics(song_data: dict):
# Call LyricFlow API
response = requests.post(
"http://localhost:8000/fetch/search",
json={
"title": song_data["title"],
"artist": song_data["artist"],
"fetch_romanization": True
}
)
results = response.json()
# Get best match
if results["results"]:
best = results["results"][0]
return best
return {"error": "No lyrics found"}Environment Variables:
# Optional: Configure romanization
export GEMINI_API_KEY="your-key"
export GEMINI_MODEL="gemini-2.5-pro"
# Or OpenAI
export OPENAI_API_KEY="your-key"
export OPENAI_MODEL="gpt-4-turbo"Config file (~/.lyricflow/config.yaml):
api:
default_provider: gemini # or openai, local
gemini:
api_key: "your-key"
model: "gemini-2.5-pro"
openai:
api_key: "your-key"
model: "gpt-4-turbo"
processing:
language: auto
skip_existing_lyrics: truepip install lyricflow[fetch]
# or
pip install requestspip install lyricflow[tui]
# or
pip install textualPossible reasons:
- Song not in Musixmatch database
- Typo in title/artist name
- Network connection issues
Solutions:
- Try different spelling
- Try artist-only search
- Use interactive TUI to see all results
- Check internet connection
Reason: Musixmatch API token acquisition failed
Solution:
- Check internet connection
- Try again (tokens are cached)
- Check if Musixmatch API is accessible in your region
Solutions:
- Update terminal (Windows Terminal, iTerm2, etc.)
- Update textual:
pip install -U textual - Check terminal size (minimum 80x24 recommended)
$ lyricflow fetch -t "Yesterday" -a "The Beatles"
🔍 Searching for: Yesterday by The Beatles
✅ Found match:
Title: Yesterday
Artist: The Beatles
Album: Help!
Duration: 2:05
Rating: 98
Type: Synced (LRC)
📄 Lyrics Preview:
[00:00.50]Yesterday
[00:03.25]All my troubles seemed so far away
...$ lyricflow fetch -t "Imagine" --interactive
# TUI opens with search results
# Click on preferred version
# Preview lyrics in real-time
# Click "Save LRC" or "Embed to Audio"$ lyricflow fetch --audio "song.m4a" --embed
🔍 Searching for: Song Title by Artist Name (from metadata)
✅ Found match: Song Title - Artist Name
📝 Embedding lyrics to audio file...
✅ Embedded lyrics to: song.m4a
✨ Done!$ lyricflow fetch \
-t "千本桜" \
-a "初音ミク" \
--romanization \
-o senbonzakura.lrc
✅ Saved lyrics to: senbonzakura.lrc
✅ Saved romanization to: senbonzakura_romaji.lrclyricflow fetch [OPTIONS]
Options:
-t, --title TEXT Song title [required]
-a, --artist TEXT Artist name
-l, --album TEXT Album name
--audio PATH Audio file to embed lyrics
-o, --output PATH Output LRC file path
--translation Fetch translation
--romanization Fetch romanization
--embed Embed lyrics to audio file
-i, --interactive Launch TUI for interactive selection
-v, --verbose Enable verbose logging
--help Show this message and exit
See lyricflow/core/musixmatch.py for full API documentation.
Key classes:
MusixmatchAPI- Low-level API clientMusixmatchFetcher- High-level fetcherLyricResult- Result container
Original Musixmatch Script: ohyeah & TT
Python Integration: LyricFlow Contributors
TUI Framework: Textual by Textualize
MIT License - See LICENSE file for details