LRC — караоке в MP3
LRC — караоке в MP3

Случайно узнал, что программы-плееры и носимые устройства, чуть дороже базового уровня, отображают текст в mp3 файлах.

И, если его разметить временными блоками в начале каждой строки, выделяют эту строку, как в караоке.

Такой формат называется LRC (видимо, от слова lyric) и понимается достаточно большим количеством клиентов.

Хотите, расскажу где взять эту разметку?

Есть сайты с большими базами готовых текстов, поиск по которым даёт впечатляющие результаты:

  • lrclib.net — бесплатная база данных с внятным поиском и собственным клиентом
  • megalobiz.com — большая база с неустойчивым соединением (может это только мне так повезло)
  • netease — азиатский поисковик, тоже временами отваливается
  • musixmatch.com — эти ещё и денег по подписке хотят

В общем, идея интересная, но для большой аудио коллекции хлопотная в реализации. А что, если доверить поиск программе…

Можно автоматизировать поиск LRC?

Разумеется, можно. Я пробовал на Python 3 (в Windows). И вот, что получилось:

Для начала установим библиотеки обработки mp3 и поиска по lrc базам. В cmd введём:

pip install mutagen syncedlyrics

и подтвердим — Enter. А когда процесс завершится, напишем простенький скрипт пакетной обработки (кликните на название файла):

main.py
import configparser
import os
import sys
import time
from mutagen.id3 import ID3, USLT, ID3NoHeaderError
import syncedlyrics


def get_base_path():
    """Получает путь к папке, где лежит exe или py файл."""
    if getattr(sys, "frozen", False):
        return os.path.dirname(sys.executable)
    return os.path.dirname(os.path.abspath(__file__))


def load_config():
    base_path = get_base_path()
    config_path = os.path.join(base_path, "config.ini")

    if not os.path.exists(config_path):
        print(f"[Ошибка] Файл конфигурации не найден: {config_path}")
        input("\nНажмите Enter для выхода...")
        sys.exit(1)

    config = configparser.ConfigParser()
    config.read(config_path, encoding="utf-8")

    try:
        settings = config["Settings"]
        music_dir = settings.get("music_dir", "").strip()
        overwrite_existing = settings.getboolean(
            "overwrite_existing", fallback=False
        )
        delay_seconds = settings.getfloat("delay_seconds", fallback=1.5)
        providers_raw = settings.get("providers", "lrclib, netease, megalobiz")
        providers = [p.strip() for p in providers_raw.split(",") if p.strip()]

        return music_dir, overwrite_existing, delay_seconds, providers
    except Exception as e:
        print(f"[Ошибка] Сбой при чтении config.ini: {e}")
        input("\nНажмите Enter для выхода...")
        sys.exit(1)


def process_folder(music_dir, overwrite_existing, delay_seconds, providers):
    if not os.path.exists(music_dir):
        print(f"[Ошибка] Указанная папка с музыкой не найдена: {music_dir}")
        return

    print("=" * 60)
    print(f" Папка: {music_dir}")
    print(
        f" Перезапись существующего USLT: {'ВКЛ' if overwrite_existing else 'ВЫКЛ (пропуск)'}"
    )
    print(f" Провайдеры: {', '.join(providers)}")
    print(f" Пауза между запросами: {delay_seconds} сек.")
    print("=" * 60)

    for root, _, files in os.walk(music_dir):
        for file in files:
            if not file.lower().endswith(".mp3"):
                continue

            mp3_path = os.path.join(root, file)

            try:
                tags = ID3(mp3_path)
                artist = tags.get("TPE1", [""])[0]
                title = tags.get("TIT2", [""])[0]
                uslt_tags = tags.getall("USLT")
            except ID3NoHeaderError:
                tags = ID3()
                artist, title = "", ""
                uslt_tags = []

            # Пропуск, если тег уже есть и перезапись отключена
            if uslt_tags and not overwrite_existing:
                print(f"[Пропуск - USLT есть]: {file}")
                continue

            search_query = (
                f"{artist} - {title}"
                if artist and title
                else os.path.splitext(file)[0]
            )

            print(f"\n[Ищем]: {search_query}")

            try:
                lrc_text = syncedlyrics.search(
                    search_query,
                    providers=providers,
                    synced_only=True,
                )

                if lrc_text:
                    tags.setall(
                        "USLT",
                        [
                            USLT(
                                encoding=3,  # UTF-8
                                lang="eng",
                                desc="",
                                text=lrc_text,
                            )
                        ],
                    )
                    tags.save(mp3_path, v2_version=3)
                    print(f"  [OK] Вшито в тег USLT: {file}")
                else:
                    print(f"  [Пропуск] LRC с таймкодами не найден.")

            except Exception as e:
                print(f"  [Ошибка]: {e}")

            time.sleep(delay_seconds)


if __name__ == "__main__":
    music_dir, overwrite, delay, providers = load_config()
    process_folder(music_dir, overwrite, delay, providers)
    print("\n" + "=" * 60)
    print("Обработка завершена!")
    input("Нажмите Enter для закрытия программы...")

В отдельный файл конфигурации вынесем

  • путь к обрабатываемой папке с медиатекой,
  • перечень провайдеров, у которых будем искать lrc-тексты
  • режим обработки существующих данных (перезаписать или пропустить), и 
  • задержку между запросами для параноидальных защит.
config.ini
[Settings]
# Путь к папке с Вашей музыкой
music_dir = E:\Music

# Режим перезаписи: true — перезаписывать все, false — только где нет USLT
overwrite_existing = false

# Задержка между запросами к API (в секундах)
delay_seconds = 1.5

# Провайдеры через запятую (lrclib, netease, megalobiz, musixmatch)
providers = lrclib, netease, megalobiz, musixmatch

Сложим это «счастье» рядышком, и запустим компиллятор.

Программа просканирует папку; для каждого файла mp3 по названию трека и артисту сделает запрос на серверы и, при получении данных, запишет их в поле USLT исходного mp3 файла.

Откуда плеер их потом благополучно и считает.

У меня нет Python-а. Без него LRC пакетом не найти?

Почему же — и без пайтона можно:

Скачайте архив с файлообменника, распакуйте, отредактируйте config.ini под свои нужды и запустите lyricator.exe

Как только выскочит сообщение про то, что программа справилась — нажмите Enter для закрытия окна.

zip
Скачать: Автопоиск LRC
Пакетный поиск синхронизированных текстов для Вашей mp3 коллекции.
Скачано: 26, размер: 13,74Мб, дата: 19 Авг. 2026

В отличие от клиента lrcget, эта программа пакетно обработает папку с музыкой, и во вложенные папки заглянет, ищет сразу по нескольким провайдерам и не переспрашивает Вас, что ей делать.Пусть она и выглядит неказисто, но работает шустрее ручного поиска в разы.

Делал для себя, делюсь безвозмездно, претензии по порче имущества не принимаю. Если опасаетесь сразу всю медиатеку пакетом сканировать — правильно делаете.

Для начала скопируйте несколько файлов во временную папку, «натравите» на неё скрипт и посмотрите, что получится. Так и нервы убережёте, и конфигурацию настраивать научитесь.

В общем, пробуйте, дерзайте и… пойте на здоровье.

P. S. Да, и напишите в комментариях, стоит ли поделиться с Вами редактором, который из простого текста синхронизированный в несколько кликов делает?