ToshaMusic: выгрузка музыки в Hi-Res с проверкой качества
собственный Telegram-бот: FLAC 24 бит / 192 кГц из Qobuz, Deezer и Tidal
Telegram-бот, который скачивает музыку в Hi-Res по ссылке Qobuz, Deezer или Tidal, до FLAC 24 бит / 192 кГц, и проверяет, что FLAC настоящий: ffprobe, mutagen и спектральный анализ вместо обещаний 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?
Что такое Hi-Res и почему стримингу нельзя верить на слово?
Какие сервисы поддерживаются?
Как бот отдаёт файлы больше 50 МБ?
Что будет, если нужного качества у релиза нет?
Можно ли встроить загрузчик в другого бота?
Ещё по этому направлению
Финансовый ассистент: учёт денег из PDF-выписок в Telegram
собственный продукт: бот и веб-дашборд, выписки четырёх банков, Gemini
Тикет-система для IT-отдела в Telegram
для музейного комплекса в Москве: заявки, учёт техники, база знаний и аналитика
Tech Poly VPN: биллинг VPN-подписок в Telegram
собственный продукт: оплата по СБП, выдача ключей в Marzban, резервный бот во ВКонтакте
Нужно похожее?
Расскажите о задаче — покажем, как решали такое, и прикинем объём работ.