Convert M4A and TTML lyric files to Enhanced LRC (word-by-word karaoke format)
Project description
lrc-maker
Convert M4A and TTML lyric files into Enhanced LRC — the word-by-word karaoke format supported by players like Poweramp, Plexamp, foobar2000, and others.
Lyrics are sourced from LyricsPlus (Apple Music, Spotify, Musixmatch) with Musixmatch as a fallback. No Apple Music or Spotify credentials required.
Features
- 🎵 Word-by-word Enhanced LRC from Apple Music TTML data
- 🔄 Automatic fallback chain: Apple Music → Spotify → Musixmatch (word) → Musixmatch (line)
- 🏷️ Full metadata in LRC headers: artist, album, year, genre, ISRC, composer, etc.
- 📁 Batch processing — point at a folder and convert everything recursively
- 🔍 Smart matching — handles accented characters, smart quotes, featured artists
- 📄 TTML support — convert raw Apple Music
.ttmlfiles directly
Installation
From PyPI (once published)
pip install lrc-maker
From source
git clone https://github.com/YOUR_USERNAME/lrc-maker.git
cd lrc-maker
pip install -e .
Requirements: Python 3.10+, mutagen (installed automatically)
Usage
M4A → Enhanced LRC
Convert a single file or an entire music folder:
# Single file
m4a-to-lrc "path/to/song.m4a" --musixmatch-token-file data.json
# Entire folder (recursive)
m4a-to-lrc "G:\Music\Apple Music" --musixmatch-token-file data.json
# Overwrite existing .lrc files
m4a-to-lrc "G:\Music" --musixmatch-token-file data.json --overwrite
# Preview without writing files
m4a-to-lrc "G:\Music" --musixmatch-token-file data.json --dry-run
Example output:
Converted [Apple (word)]: Cigarettes After Sex - Apocalypse -> ...lrc
Converted [Apple (line)]: Cigarettes After Sex - Affection -> ...lrc
Converted [Spotify (word)]: The Marías - Blur -> ...lrc
Converted [Musixmatch (word)]: Some Track -> ...lrc
Converted [Musixmatch (line)]: Some Track -> ...lrc
Done. 27 file(s) processed, 0 failed.
The label in brackets tells you the source and timing type:
| Label | Meaning |
|---|---|
Apple (word) |
Apple Music TTML, word-by-word ✨ best quality |
Apple (line) |
Apple Music, line-by-line |
Spotify (word) |
Spotify synced, word-by-word |
Spotify (line) |
Spotify synced, line-by-line |
Musixmatch (word) |
Musixmatch RichSync, word-by-word |
Musixmatch (line) |
Musixmatch Subtitle, line-by-line |
TTML → Enhanced LRC
Convert raw Apple Music .ttml files:
# Single file
ttml-to-lrc "song.ttml"
# Folder
ttml-to-lrc "path/to/ttml/folder" --overwrite
# With Musixmatch RichSync as primary source
ttml-to-lrc "path/to/ttml/folder" --musixmatch-token-file data.json --overwrite
Getting a Musixmatch Token
The Musixmatch token is used as a fallback when LyricsPlus can't find a track. To get one:
- Open musixmatch.com in your browser
- Open DevTools → Network tab
- Search for any song and filter requests by
apic-desktop - Copy the
usertokenvalue from any request URL - Save it to
data.json:
{
"tokens": {
"web-desktop-app-v1.0": "YOUR_TOKEN_HERE"
}
}
How It Works
m4a-to-lrc
│
▼
Read M4A tags (artist, title, album, year, genre, ISRC, ...)
│
▼
LyricsPlus API ──► Apple Music TTML (word-by-word) ✨
(no auth) ──► Spotify (word or line)
──► Musixmatch (word or line)
──► own cache
│
│ if nothing found
▼
Musixmatch API ──► RichSync (word-by-word)
(token needed) ──► Subtitle (line-by-line)
│
▼
Write .lrc file next to the .m4a
Artist and title are read from the M4A tags. If tags are missing, the filename is parsed as Artist - Title.m4a. Multiple candidate spellings are tried (smart quotes → straight, accented → ASCII, strip featured artist credits) to maximise match rate.
LRC Format
Enhanced LRC files look like this:
[ar:Cigarettes After Sex]
[ti:Apocalypse]
[al:Cigarettes After Sex]
[yr:2017]
[re:lyricsplus-apple]
[00:13.15]<00:13.15>Oh <00:13.55>it <00:13.80>was <00:14.20>the <00:14.50>night
[00:16.80]<00:16.80>Things <00:17.10>were <00:17.40>different
Each [timestamp] is the line start; each <timestamp>word is when that individual word is sung.
Project Structure
lrc-maker/
├── src/lrc_maker/
│ ├── __init__.py
│ ├── m4a_to_elrc.py # M4A → LRC (LyricsPlus + Musixmatch)
│ └── ttml_to_elrc.py # TTML → LRC (direct parse + optional Musixmatch)
├── pyproject.toml
├── README.md
└── LICENSE
Contributing
Pull requests welcome. Please open an issue first for significant changes.
License
MIT — see LICENSE.
Credits
- LyricsPlus / KPOE — the serverless lyrics aggregation API
- mutagen — M4A/MP4 tag reading
- Musixmatch — lyrics fallback source
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lrc_maker-0.2.0.tar.gz.
File metadata
- Download URL: lrc_maker-0.2.0.tar.gz
- Upload date:
- Size: 18.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54c27463f56a594c2da9f3bd4995b0049842df869d8fdcc48493be7b1fa1978c
|
|
| MD5 |
28142aceff1d83a170a4f0e3ce92ee76
|
|
| BLAKE2b-256 |
1ddeb3da07848caa58caf8907d12f967c40be9cf8c709ab1d6f0a6bf156c1c25
|
File details
Details for the file lrc_maker-0.2.0-py3-none-any.whl.
File metadata
- Download URL: lrc_maker-0.2.0-py3-none-any.whl
- Upload date:
- Size: 17.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ff53314d16b34a4c1c1e7a983fd166614e2d7b57ccb3ab7f5888fb6b57147a1
|
|
| MD5 |
977ae01659f6af55d3e36cfc762091c9
|
|
| BLAKE2b-256 |
5476d76b074e8efbb76d8726a038f7c4ddbc088c5d7bb8c56f1756c0df6dac59
|