Елесей
Автор
78
Просмотров
34.239.151.124
Ваш IP
14 января 2024
Дата создания

Пишем Telegram-бота для скачивания видео из VK на Spring Boot и деплоим в Kubernetes

Пишем Telegram-бота для скачивания видео из VK на Spring Boot и деплоим в Kubernetes

Сложный
10 мин
7.4K
Программирование*Java*Kotlin*Kubernetes*

В последнее время на Хабре уже несколько раз всплывала тема сложности загрузки видео из VK для дальнейшего просмотра оффлайн. Я решил подойти к этому вопросу с помощью создания телеграм-бота.

У нового бота сразу несколько преимуществ над другими решениями:

  • Не нужно ставить никаких дополнительный приложений или плагинов: скорее всего Telegram и так уже установлен на телефоне и/или на компе.

  • Возможна автоматическая синхронизация между устройствами: можно поставить на закачку на компе, а тот же файл появился в телефоне.

  • Можно просматривать / прослушивать видео / аудио с выключенным экраном телефона (привет, премиум подписки vk.com/youtube.com).

  • Можно выбрать необходимый формат аудио или видео различного качества.

Что мы хотим получить в результате?

Задача бота заключается в том чтобы трансворфмировать ссылку, полученную от пользователя, в аудио / видео выбранного формата. То есть сначала пользователь присылает ссылку на видео из VK, потом бот предлагает формат для скачивания.

А после выбора формата в ответ бот присылает соответствующее видео.

Выбор способа скачивания

Только вот как получить из ссылки вида https://vk.com/video-201886811_456239019нужный файл для скачивания? Можно воспользоваться VK API, которое предоставит временную ссылку на файл. А можно попытаться "выковырить" данные из страницы с помощью javascript, как это делается в соседних статьях.

Но всё это требует не только глубого погружения в особенности VK, что довольно трудоёмко. А ведь мы хотим делать бота расширяемым, чтобы для добавления нового источника (youtube, rutube и т.д.) не пришлось бы погружаться в новое API.

Поэтому я решил воспользоваться уже готовым решением, а именно проектом youtube-dl, а точнее его форком - yt-dlp (о разнице между этими двумя проектами можно почитать здесь).

Это консольная утилита, написанная на python, в которой уже реализована поддержка более 1000 видео-хостингов. Таким образом добавление нового сайта (к примеру, youtube-а) дело небольшого допиливания конфига. По крайней мере, так казалось в теории, но на практике у каждого хостинга свои особенности, с которыми нужно разбираться.

Собственно, на этом этапе я принял решение не ограничиваться одним vk - пусть бот скачивает и с youtube.com тоже.

Разбираемся с yt-dlp cli

Пользоваться проектом довольно удобно и даже не обязательно устанавливать ни python, ни даже yt-dlp себе на компьютер, все можно запустить его через докер.

К примеру, для получения списка форматов запускаем:

docker run --rm -it jauderho/yt-dlp:latest https://vk.com/video-6246566_163356305 -F

Ответ будет примерно таким:

[vk] Extracting URL: https://vk.com/video-6246566_163356305
[vk] -6246566_163356305: Downloading JSON metadata
[vk] -6246566_163356305: Downloading m3u8 information
[info] Available formats for -6246566_163356305:
ID       EXT           RESOLUTION FPS │  FILESIZE   TBR PROTO │ VCODEC    VBR ACODEC  ABR
─────────────────────────────────────────────────────────────────────────────────────────
hls-146  mp4           256x144     25 │ ~ 4.23MiB  146k m3u8  │ unknown  146k unknown  0k
hls-365  mp4           426x238     25 │ ~10.57MiB  365k m3u8  │ unknown  365k unknown  0k
url240   unknown_video 240p           │                 https │ unknown       unknown
url360   unknown_video 360p           │                 https │ unknown       unknown
hls-738  mp4           640x360     25 │ ~21.36MiB  738k m3u8  │ unknown  738k unknown  0k
hls-1067 mp4           852x478     25 │ ~30.87MiB 1067k m3u8  │ unknown 1067k unknown  0k
url480   unknown_video 480p           │                 https │ unknown       unknown

А для скачивания видео в нужном формате запускаем (здесь -f hls-1067- это формат из таблицы выше):

docker run --rm -it -v $(pwd)/downloads:/tmp jauderho/yt-dlp:latest https://vk.com/video-6246566_163356305 -o "/tmp/%(extractor)s/%(format_id)s/%(extractor)s%(id)s.%(ext)s" -f hls-1067 -N 10 --print after_move:filepath

А раз уж мы умеем оборачивать скачивалку в docker image, то тут один шаг и до kubernetes job. Зачем усложнять и делать job? А это сразу дает нам и retry, и удобный api для мониторинга статуса, и автоматическую очистку ресурсов после завершения задачи.

Итак, с "движком" определились, дело за малым - "склеить" запросы из телеги с запросами в yt-dlp.

Немного system design-а

Если порисовать картинки в стиле system-design собеседований, то верхнеуровнево схема, к которой я пришел, выглядит следующим образом.

Интерактивная схема на whimsical
Интерактивная схема на whimsical

Обработка запроса на загрузку конкретного видео, выполняются в следующей последовательности:

  1. На первом шаге запрос из telegram-a попадает в Local Bot API Server (о том что это, и зачем он нужен - ниже).

  2. Local Bot API передает запрос нашему приложению с помощью http webhook (почему именно webhook, а не long-polling, также - ниже).

  3. Spring Boot приложение принимает запрос, создаёт и запускает kubernetes job-у, для загрузки видео, после чего переходит в режим ожидания завершения работы job-ы.

  4. Kubernetes job-а начинает скачивать видео из источника (vk.com/youtube.com) в выбранном формате.

  5. Источник отдает файл нашей job-е, плюс, в случае необходимости, применяются пост-обработки.

  6. Как только файл скачен, job-а заканчивает работу, передавая ссылку на файл в Spring Boot приложение.

  7. Приложение сохраняет полученные метаданые в PostgreSQL базу (в дальнейшем, повторная загрузка данного видео не потребуется).

  8. Приложение отправляет файл в Local Bot API - сервер.

  9. Local Bot API сервер отправляет видео в Telegram.

Telegram Local Bot API server и зачем он нужен?

Большинство ботов для telegram взаимодействуют с дефолтным endpoint-ом https://api.telegram.org для отправки запросов и это работает. Но у этого способа есть ограничение на пересылку файлов более 50 МБ. А так как подавляющее большинство видео не вписываются в этот лимит, то нам этот способ не подойдет.

Благо, Telegram сам предоставляет решение для этой проблемы: нужно настроить Local Bot API Server. Помимо того, что он поднимает лимит на размер загружаемого файла в 40 раз (до 2000 МБ), он также позволяет при загрузке видео ссылаться на него с помощью file URI scheme, а не http-scheme.

Плюс, так как Local Bot API Server находится внутри нашего kubernetes-кластера и держит соединение с Telegram Cloud API, нам больше нет необходимости выставлять наше приложение наружу кластера (настраивать dns, конфигурировать cert-manager, настравивать ingress и т.д.). Плюс, взаимодействие telegram local bot api <-> spring boot app внутри кластера будет осуществляться по протоколу http, а не https, и все вопросы шифрования и безопасности Local Bot API Server берёт на себя.

Более того, всё уже украдено сделано до нас, и для Local Bot API Server-а существует docker-образ, завернуть который в kubernetes service не составляет особых проблем. Главное, не забыть к deployment-у "привязать" тот же PersistentVolumeClaim, который привязывается кyt-dlp job-ам.

Конфиг deployment-а telegram local bot api