🎵

ToshaMusic: выгрузка музыки в Hi-Res с проверкой качества

собственный Telegram-бот: FLAC 24 бит / 192 кГц из Qobuz, Deezer и Tidal

Telegram-бот, который скачивает музыку в Hi-Res по ссылке Qobuz, Deezer или Tidal, до FLAC 24 бит / 192 кГц, и проверяет, что FLAC настоящий: ffprobe, mutagen и спектральный анализ вместо обещаний API стриминга.

В продакшене · собственный продукт
24 бит / 192 кГц
максимальное качество FLAC, которое отдаёт бот; имя файла — по измеренному
2 ГБ
файлы через локальный Bot API вместо лимита в 50 МБ
2 режима
обычный Telegram-бот и headless-движок с HTTP API для встраивания

Задача

Стриминги обещают Hi-Res, но не всегда его отдают. Релиз помечен как «24 бит / 192 кГц», а внутри обычный CD-мастер, растянутый до нужных цифр. В каталогах встречается «псевдо-lossless»: FLAC, собранный из MP3, с обрезанным спектром. Обычные загрузчики верят тегам и ответу API и кладут в фонотеку то, что ни на слух, ни по спектру Hi-Res не является.

Нужен был загрузчик, который решает три задачи. Достать максимум: автоматически выбирать наивысший доступный формат источника. Не дать себя обмануть: измерять разрядность, частоту дискретизации и частоту среза спектра, сравнивать с заявленным и честно писать расхождение в чат. Выдержать нагрузку: очередь с ограничением параллелизма, чтобы аккаунт стриминга не забанили, и передача файлов до 2 ГБ.

Решение

ToshaMusic — собственный асинхронный Telegram-бот, версия 1.1.2. Реализовано:

  • Приём ссылок. Qobuz, Deezer и Tidal: треки, альбомы, плейлисты и исполнители; короткие ссылки разворачиваются через редирект.
  • Карточка релиза. Обложка, исполнитель, название, альбом, число треков, длительность, дата, лейбл, жанр, метка explicit, заявленное качество и список доступных форматов. Кнопка «Максимум» и кнопки конкретных уровней: FLAC 24/192, 24/96, 16/44.1, MP3 320.
  • Мягкая деградация. Если API источника не ответил, карточка всё равно показывается, а качество определится по факту.
  • Верификация. ffprobe, mutagen и спектральный анализ; имя файла, теги и строка проверки только из измеренного.
  • Очередь. Пул воркеров с лимитом параллельных загрузок, общим лимитом очереди и персональным лимитом на пользователя; отмена задачи в очереди и во время выполнения; понятные тексты для гео-блокировки, протухшего токена, лимита запросов, ненайденного релиза, таймаута.
  • Отправка. Локальный Bot API, файлы до 2000 МБ, обложка-превью, ZIP без сжатия для длинных релизов, повторы при flood control.
  • Админ-панель. Статистика с числом пойманных подделок и распределением по реальному качеству, пользователи с баном и историей, рассылка, токены стримингов с заменой из чата без перезапуска, настройки, /version и /sysinfo.
  • Headless-режим. Тот же движок как отдельный сервис с внутренним HTTP API для встраивания в другого бота.

Как это устроено

ffprobe и mutagen: проверка разрядности

После скачивания ffprobe (утилита из FFmpeg) читает кодек, частоту дискретизации, разрядность, битрейт, число каналов и длительность. Для FLAC ffprobe часто отдаёт контейнерные 32 бита вместо реальных 24, поэтому разрядность уточняет mutagen, библиотека чтения аудиотегов. Если запрошено 24/192, а пришло 16/44.1, в чат уходит предупреждение, что релиз доступен максимум в CD-качестве.

Спектр среза lossless: как ловится подделка

Фрагмент из середины трека декодируется в PCM (несжатые отсчёты звука), и по усреднённому БПФ (быстрое преобразование Фурье, разложение сигнала по частотам) ищется частота, выше которой спектр пуст. Обрыв в диапазоне 14–20,6 кГц помечает файл как перекодированный из lossy. Пустота выше 24 кГц при заявленных 96 или 192 кГц выдаёт растянутый CD-мастер. Срез 19–20,6 кГц бывает и у честного тёмного мастеринга, поэтому такое предупреждение сформулировано мягко, а однозначный вердикт выносится только при обрыве ниже 19,5 кГц. Анализ можно отключить в настройках.

Имя и теги только из измеренного

Имя файла собирается по фактическим параметрам: Исполнитель - Название [24bit-192kHz].flac. В теги пишется строка проверки с разрядностью, частотой, битрейтом, частотой среза и версией ToshaMusic, а также поле QUALITY_VERIFIED. Так в фонотеке не остаётся файлов, чьи цифры в имени расходятся с содержимым.

Очередь с ограничением параллелизма

Загрузки выполняет streamrip, запущенный как дочерний процесс, с конфигом, который бот генерирует сам. Пул воркеров поверх asyncio.Queue держит лимит одновременных загрузок (по умолчанию 2), общий лимит очереди и лимит задач на пользователя: это защита аккаунта стриминга от бана за слишком активное скачивание. Статус в чате обновляется по этапам: скачивание, проверка, теги, отправка. У каждой задачи изолированный рабочий каталог, брошенные каталоги убираются при старте, зависшие после падения задачи помечаются прерванными.

Local Bot API: файлы до 2 ГБ

Облачный Bot API ограничивает отправку файлов 50 МБ. Рядом с ботом в Docker работает локальный telegram-bot-api в режиме local: файл передаётся ссылкой на путь на диске без копирования, лимит 2000 МБ. Каталог данных смонтирован по одному и тому же пути в оба контейнера, иначе сервер не увидит файл.

Headless-режим для встраивания

С версии 1.1.0 у проекта два входа. Обычный запускает aiogram-бота. Второй не создаёт бота вовсе: та же база, очередь и движок работают за внутренним HTTP API с маршрутами карточки, задач, отмены и подтверждения. Готовые файлы передаются через общий том, бизнес-ошибки возвращаются типизированно, чтобы потребитель не разбирал текст. Падение движка не задевает бота-потребителя: они в разных контейнерах. Так раздел Hi-Res музыки работает в Downloader Bot.

Результат

  • Максимально доступное качество: до FLAC 24 бит / 192 кГц.
  • Подделки под Hi-Res выявляются по спектру, расхождение с заявленным качеством видно в чате, имя файла и теги совпадают с содержимым.
  • Файлы до 2 ГБ уходят в Telegram напрямую, без ошибки лимита.
  • Сбои источника не ломают работу: карточка показывается, ошибки объясняются понятным текстом.
  • Токены стримингов меняются из админки без перезапуска контейнера.
  • Движок встраивается в другого бота по HTTP.

Технологии: что и зачем

  • Python 3.13 и aiogram 3 — асинхронный бот, FSM для сценариев админки, только инлайн-клавиатуры.
  • pydantic-settings — конфигурация с проверкой типов.
  • SQLAlchemy 2.0 async и Alembic — модели пользователей, задач, треков, настроек и рассылок; миграции с проверкой соответствия моделям.
  • streamrip — движок загрузки из Qobuz, Deezer и Tidal, запускается как дочерний процесс.
  • aiohttp — клиенты метаданных (Qobuz JSON API, публичный API Deezer, Tidal API v1), HTTP-сервер headless-режима, прокси.
  • FFmpeg, ffprobe, mutagen, numpy — измерение параметров, декодирование фрагмента, БПФ для поиска частоты среза.
  • loguru — логи с ротацией, отдельный файл ошибок.
  • Local Bot API — отправка файлов до 2000 МБ.
  • Docker Compose — бот и telegram-bot-api с общим каталогом данных, TZ=Europe/Moscow.

Статус

Собственный продукт, в продакшене. Текущая версия 1.1.2 от 8 сентября 2026 года: приведены в порядок пины зависимостей, streamrip ставится отдельно из-за несовместимых диапазонов aiofiles. Версия 1.1.0 (3 сентября 2026) добавила headless-режим, 1.0.0 (19 августа 2026) — первый рабочий выпуск.

Ограничения: бот работает от действующей подписки владельца и не обходит защиту сервисов; токены Tidal получаются внешним OAuth-сценарием, автообновление не реализовано; состояния админ-сценариев живут в памяти и сбрасываются при перезапуске. В планах: автообновление токена Tidal, хранилище FSM в Redis, экспорт статистики в CSV, поиск по каталогу прямо в боте, конвертация в ALAC/AAC, сохранение спектрограммы как доказательства, покрытие pytest.

Вопросы по проекту

Как проверить, что FLAC настоящий Hi-Res, а не MP3?
После скачивания ffprobe читает кодек, частоту дискретизации и разрядность, mutagen уточняет разрядность FLAC, а фрагмент из середины трека декодируется в PCM и по усреднённому БПФ ищется частота среза спектра. Обрыв в диапазоне 14–20,6 кГц выдаёт перекодировку из lossy, пустота выше 24 кГц при заявленных 96/192 кГц — растянутый CD-мастер.
Что такое Hi-Res и почему стримингу нельзя верить на слово?
Hi-Res — звук с разрядностью и частотой выше CD, например FLAC 24 бит / 192 кГц. Стриминги регулярно помечают релиз как 24/192, отдавая обычный CD-мастер, а в каталогах встречается «псевдо-lossless», собранный из MP3. Бот формирует имя файла, теги и подпись только по измеренным параметрам.
Какие сервисы поддерживаются?
Qobuz, Deezer и Tidal: треки, альбомы, плейлисты и исполнители, короткие ссылки разворачиваются через редирект. У Deezer потолок CD (16 бит / 44,1 кГц), запрос выше автоматически понижается; FLAC там требует подписки HiFi.
Как бот отдаёт файлы больше 50 МБ?
Через локальный telegram-bot-api в режиме local: файл передаётся путём на диске без копирования, лимит 2000 МБ вместо 50 МБ. Аудио уходит с обложкой, длительностью, исполнителем и названием; релизы длиннее порога упаковываются в ZIP без сжатия.
Что будет, если нужного качества у релиза нет?
Бот честно напишет в чат, например что релиз доступен максимум в 16 бит / 44,1 кГц FLAC. Файл называется по фактическому качеству, а в теги пишется строка проверки с измеренными параметрами.
Можно ли встроить загрузчик в другого бота?
Да. С версии 1.1.0 есть headless-режим: та же база, очередь и движок работают как отдельный сервис с внутренним HTTP API, а другой бот показывает свой интерфейс. Так ToshaMusic встроен в Downloader Bot.

Нужно похожее?

Расскажите о задаче — покажем, как решали такое, и прикинем объём работ.