Я написал бота на Python, используя библиотеки kurigramyt‑dlp и ffmpeg.Данный бот умеет скачивать что‑то (в зависимости от площадки) из следующих платформ: YouTube, TikTok, Instagram, Pinterest, Rutube, VK, Spotify, Shazam, Yandex Music, SoundCloud, VK Music, Deezer, Apple Music, Tenor, JioSaavn, Twitch (только клипы), Snapchat, Reddit, Kinopoisk.

Функции:

  • Загрузка видео, аудио и миниатюр со всех поддерживаемых платформ через yt‑dlp.

  • Полный пользовательский интерфейс Telegram‑бота: встроенные клавиатуры для выбора формата/качества, кнопки отмены и история загрузок для каждого пользователя.

  • Inline режим — запуск загрузок из любого чата через @your_bot <ссылка или поисковый запрос>.

  • Резервный вариант поиска музыки: ссылки Deezer/Apple Music обрабатывают метаданные, а затем находят соответствующий трек через поиск YouTube.

  • Поддержка Кинопоиска для просмотра сезонов, эпизодов и вариантов озвучки/перевода.

  • Кэширование результатов (Postgres + Redis), поэтому повторные запросы обрабатываются мгновенно, а не загружаются повторно.

  • Настраиваемые одновременные обработчики загрузок и очередь загрузок с поддержкой отмены.

  • Встраивание аудиотегов/обложки (ID3, FLAC, MP4) через mutagen.

  • Встроенный HTTP‑сервер проверки работоспособности для контейнеров.

  • Готовность к Docker для простого развертывания.

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

Однако код открыл я только сейчас. Обусловлено это тем, что я хотел допилить его до более‑менее адекватного состояния.

В нем имеется поддержка двух языков (RU, EN).

Красивый, но простой дизайн. Загрузка видео до 4K, а также загрузка аудио от MP3 (192 kbps) до FLAC.

Сообщения не отправляются подряд. Сделано тока для красивой фотки.
Сообщения не отправляются подряд. Сделано тока для красивой фотки.

История запросов.

Очередь.

Конечно, без трудностей не обошлось. Пришлось реверсить некоторые платформы, чтобы использовать не веб‑скрейпинг, а конкретно их API. Это сделано для «Яндекс Музыки»«VK Музыки»«Кинопоиска» и Spotify. У последних двух это сделано частично, так как пришлось использовать Playwright.

Сообщения не отправляются подряд. Сделано тока для красивой фотки.
Сообщения не отправляются подряд. Сделано тока для красивой фотки.

Специально для Habr

Мне сказали рассказать больше технических подробностей. Чтож... А я и не против;)

1. Общая архитектура системы

Схема движения запроса от сообщения пользователя до выдачи готового медиафайла:

картинка сделана в claude. сори я не умею в красивые картинки... :P
картинка сделана в claude. сори я не умею в красивые картинки...:P

2. Реверс‑инжиниринг API: Spotify и Кинопоиск

Spotify Partner GraphQL без API‑ключей и платных подписок

Стандартный API Spotify требует OAuth‑авторизацию пользователя и имеет жесткие лимиты. Для бота реализован обход через внутренний Partner GraphQL API Spotify (api-partner.spotify.com):

  1. Бот делает асинхронный HTTP‑запрос к публичной странице эмбеда трека/альбома (open.spotify.com/embed/track/...).

  2. Из тега <script id="__NEXT_DATA__"> парсится JSON‑конфиг страницы, откуда вытаскивается анонимный accessToken.

  3. С этим токеном бот напрямую дергает эндпоинт pathfinder/v2/query с SHA256-хешами объявленных операций (getTrack, getAlbum, fetchPlaylistContents).

  4. После получения чистых метаданных (название, артисты, обложка HD) трек находится в сопряженных аудиоисточниках через движок поиска.

# Извлечение анонимного accessToken из Spotify Embed
m = re.search(r'<script id="__NEXT_DATA__" type="application/json">([^<]+)</script>', resp.text)
data = json.loads(m.group(1))
access_token = data["props"]["pageProps"]["state"]["settings"]["session"]["accessToken"]

# Запрос к внутреннему GraphQL Spotify
payload = {
    "variables": {"uri": f"spotify:track:{track_id}"},
    "operationName": "getTrack",
    "extensions": {
        "persistedQuery": {
            "version": 1,
            "sha256Hash": "612585ae06ba435ad26369870deaae23b5c8800a256cd8a57e08eddc25a37294",
        }
    }
}

Кинопоиск: деобфускация ссылок и сигнатур

Для Кинопоиска пришлось разбирать клиентские скрипты плееров и восстанавливать алгоритмы расшифровки потоков. В коде бота реализованы функции маппинга и математических перестановок символов по простым числам и битовым маскам:

def a5(s: str) -> str:
    """Перестановка символов строки s с использованием модульной арифметики по простым числам."""
    ah = len(s)
    if ah <= 0:
        return s
    ax = _next_prime_above(ah)
    ac, seen, pos = [], [False] * ah, 0
    while len(ac) < ah:
        pos = (pos + 2) % ax
        if pos < ah and not seen[pos]:
            ac.append(pos)
            seen[pos] = True
    result = [''] * ah
    for i in range(ah):
        result[ac[i]] = s[i]
    return ''.join(result)

И важно понимать! Я не разбирал сам сервис Кинопоиск, а лишь сторонние пиратские сервисы. Кинопоиск лишь выступает посредником для получения названия фильма или сериала. На удивление, да, у пиратских сервисов есть защита плеера.:P

3. Двухуровневое кэширование (PostgreSQL + Redis)

Чтобы не качать один и тот же популярный трек или ролик повторно (что расходует трафик сервера и время пользователя), используется система кэширования Telegram file_id:

  1. Для любого входящего урла и выбранного качества/формата формируется уникальный медиа‑ключ: media_key = sha256(f"{url}:{media_type}:{fmt}").

  2. Перед запуском yt-dlp происходит быстрый чек в PostgreSQL/Redis.

  3. Если хэш найден в БД, бот мгновенно отправляет медиафайл пользователю через вызов send_audio(chat_id, file_id) без повторного скачивания на диск и без повторной загрузки в Telegram. Время выдачи меньше 0.2 секунды.

class MediaCache(Base):
    __tablename__ = "media_cache"

    media_key: Mapped[str] = mapped_column(String(64), primary_key=True)
    file_id: Mapped[str] = mapped_column(Text, nullable=False)
    media_type: Mapped[str] = mapped_column(String(10), nullable=False)
    media_format: Mapped[str | None] = mapped_column(String(32))
    title: Mapped[str | None] = mapped_column(Text)
    performer: Mapped[str | None] = mapped_column(Text)

4. Неблокирующий асинхронный пайплайн и защиту от FloodWait

Библиотека yt-dlp по своей природе синхронная и блокирующая. Вызывать её прямо в Telegram‑хэндлере нельзя. Это заблокирует весь event loop бота.

  • Вынос в ThreadPoolExecutor: Вызовы YoutubeDL().download() выполняются в пуле потоков через asyncio.to_thread().

  • Троттлинг прогресс‑бара: При скачивании больших файлов yt-dlp вызывает hook прогресса сотни раз в секунду. Если на каждый вызов обновлять сообщение в Telegram (edit_message_text), бот моментально поймает ошибку 420 FLOOD_WAIT_X. Для решения сделан троттлер: статус в Telegram редактируется не чаще 1 раза в 2–3 секунды с проверкой флага отмены от пользователя (CancelFlag).

async def _download_in_executor(ydl_opts, url):
    loop = asyncio.get_running_loop()
    def _sync_download():
        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            return ydl.extract_info(url, download=True)
    return await loop.run_in_executor(None, _sync_download)

5. Обработка аудио, FFmpeg и Mutagen

При скачивании музыки важно не просто получить файл, но и сделать так, чтобы в нативном плеере Telegram отображались правильная обложка, имя исполнителя и название альбома:

  1. Транскодинг через FFmpeg: В зависимости от выбора пользователя (FLAC, MP3 320kbps, MP3 192kbps, M4A) запускается оптимизированный пайплайн конвертации.

  2. Вшивание метаданных через Mutagen:

    • Для MP3: создаются теги ID3v2.3/v2.4 с фреймами TIT2 (Title), TPE1 (Artist), TALB (Album) и APIC (Cover Image).

    • Для FLAC: используется Picture() с Mime‑type image/jpeg.

    • Для M4A / MP4: метаданные пишутся в атомы ©nam, ©ART, covr.

# Пример вшивания обложки и тегов в MP3 через mutagen
audio = MP3(filepath, ID3=ID3)
try:
    audio.add_tags()
except Exception:
    pass

if thumbnail_data:
    audio.tags.add(
        APIC(
            encoding=3,
            mime='image/jpeg',
            type=3, # Front cover
            desc='Cover',
            data=thumbnail_data
        )
    )
audio.save()

Однако мне нравится, что оно так вышло. Да, конечно, появляются вопросы: «А кому это надо?». Но, судя по всему, кому‑то да надо, раз такие боты существуют в огромном количестве. Лично я использую своего бота для скачивания треков.

Вы тоже можете принять участие в разработке. Я не говорю, что проект завершён, в нём определённо есть что допиливать (много чего...). Так что те, кому понравился бот и кто хочет добавить либо что‑то новое и интересное, либо исправить баги, могут отправлять PR‑запросы, и я их с радостью буду проверять и одобрять.

Тем же, кому всё это чуждо и кто просто хочет поскачивать что‑то, вы, конечно, можете зайти в Telegram и просто использовать бота;)

Github | Telegram Bot | мой тгк

Ну и спасибо вам за прочтение. ❤
Приятно оставить хоть и маленькую, но какую‑то историю.