Telegram-бот на Python + AIOGram для скачивания видео из YouTube

Telegram-бот на Python + AIOGram для скачивания видео из YouTube

Сегодня мы разберем как сделать телеграм-бота для скачивания видео из YouTube. Часто бывает, что мы куда-то едем и нужно скачать пару аудио версий видео, но платить возможности нет. Чтобы избежать трат, мы прибегнем к библиотеке pyTube (для скачивания видео) и AIOGram (для взаимодействия с API Telegram). 

Я дам удобный шаблон для работы с AIOGram, где уже будет структурированный проект. Хотя для такого маленького проекта можно все собрать и в один файл, мы всё же оставим место для расширения нашего помощника.

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

Подготовка проекта в PyCharm

Создаем проект в PyCharm и устанавливаем библиотеки. Скачиваем шаблон телеграм-бота с GutHub.

Устанавливаем модули, с которыми будем работать:

pip install aiogram pytube

Вариант для macOS:

pip3 install aiogram pytube

Мы будем делить проект на пакеты для удобства его расширения. Теперь о самой структуре:

+---pyTube
|  +---handlers
|  |   +---Users
|  |   |   +---__init__.py
|  |   |   +---help.py
|  |   |   +---audio.py
|  |   |   \---start.py
|  |   \---__init__.py
|   +---states
|   |  +---__init__.py
|   |  \---dowload.py
|   +---utils
|   |  +---__init__.py
|   |   \---set_bot_commands.py
|  +---app.py
|  \---loader.py

В директории handlers будут храниться все команды, которые можно использовать.

В директории states будут храниться все FSM.

В директории utils все прочие команды и настройки бота.

App.py – это «сердце» бота. В этом файле будет инициализироваться handlers и запускаться обработка сообщений.

Loader.py – сюда вынесутся все ключевые переменные бота, чтобы можно было к ним обращаться из любого пакета (на таком принципе строится часть программ на python). 

__init__.py – файл пакета, который позволяет вынести на уровень выше все функции внутри него (на этом принципе строится, не побоюсь сказать, все объёмные библиотеки python).

Разбор кода telegram-бота в AIOGram на Python

Ниже я буду называть все файлы по именам. Чтобы найти их, смотрите на дерево директорий.

Файл loader.py будет двигателем бота, к которому мы будем обращаться из модулей: 

from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage


token = 'Ваш токен вставлять сюда'

bot = Bot(token=token, parse_mode=types.ParseMode.HTML)

storage = MemoryStorage()

dp = Dispatcher(bot, storage=storage)

Импортируем Класс бота, диспетчера и типы, а также класс для хранения информации в оперативной памяти.

Вводим наш токен (как его получить смотрите в предыдущей статье).

Дальше создаем объект бота - указываем токен и тип выделения текста, как в html. Создаем объект для хранения в оперативной памяти. Дальше все передаем в диспетчер (подробнее для чего он нужен можно посмотреть в инструкции к AIOGram).

Запуск бота app.py

from aiogram import executor

from loader import dp
import handlers
from utils.set_bot_commands import set_default_commands


async def on_startup(dispatcher):
    await set_default_commands(dispatcher)

if __name__ == '__main__':
    executor.start_polling(dp, on_startup=on_startup)

Импортируем executor - он будет принимать ответы от API телеграма (если интересно узнать, что это такое, есть краткий ответ на stackoverflow, а также на qna.habr).

Дальше нужно импортировать пакеты бота в основной файл. Подробнее о пакетах в python: ссылка на статью, ссылка на видео. Для представления о пакетах этих ссылок хватит.

Функция on_startup, как ясно из названия, срабатывает при запуске бота, в неё мы «зашиваем» установку меню с командами, которые мы раньше настраивали через botFather.

Дальше идёт стандартная конструкция для Python: if __name__ == ‘__main__’. Эта проверка будет действительна, если запускаемый файл будет app.py. После проверки мы начинаем принимать все сообщения от телеграма. Подробнее ознакомиться с конструкцией if __name__ = ‘__main__’ можно тут.

Пакеты utils и states

Пакет utils является вспомогательным - туда мы будем сохранять всё, что не связанно с обработкой сообщений. В нашем случае мы положили туда файл с присваиванием команд в меню: 

from aiogram import types


async def set_default_commands(dp):
    await dp.bot.set_my_commands(
        [
            types.BotCommand("start", "Запустить бота"),
            types.BotCommand("help", "Вывести справку"),
            types.BotCommand("audio", "Скачать видео из ютуба и переести в аудио")
        ]
    )

Внутрь функции мы передаем диспетчер и дальше обращаемся к нему, чтобы прописать команды в меню. Как можно заметить, это массив с типами. Первым идет название команды, вторым - описание. 

В пакете states будут храниться все машинные состояния.  Поскольку в проекте их может быть много, выделим их в отдельную директорию.

Файл dowload.py

from aiogram.dispatcher.filters.state import StatesGroup, State

class Dowload(StatesGroup):
    dowload = State()

Мы наследуем класс от StatesGroup и передаём внутрь полей класса объекты класса State.

Пакет handlers 

Этот пакет становится основным для обработки сообщений, будь то группа или переписка.

__init__.py в основной директории 

from . import users

Директория users 

__init__.py

from . import help
from . import start
from . import video

Если будете добавлять новые python файлы, не забывайте их импортировать.

Отработка стандартных команд 

help.py

from aiogram import types
from aiogram.dispatcher.filters.builtin import CommandHelp

from loader import dp


@dp.message_handler(CommandHelp())
async def bot_help(message: types.Message):
    text = ("Список команд: ",
            "/start - Начать диалог",
            "/help - Получить справку",
            "/audio - это команда для скачивания видео и отправка вам аудио сообщением")

    await message.answer("\n".join(text))

Нам для работы потребуется типы сообщения. Чтобы грамотно отвечать на команду help мы импортируем ее из фильтров AIOGram. Для отслеживания сообщений импортируем диспетчера из загрузочного файла.

Отправка сообщения - асинхронная функция, поэтому мы должны приписывать await для ожидания ее окончания. В AIOGram у сообщения есть замечательный метод answer (ответ). Он сокращает путь для ответа на сообщение пользователя. В противном случаем нам бы пришлось импортировать объект бота и отправлять сообщение (пример: await bot.send_message (message.chat.id, text='')).

start.py

from aiogram import types
from aiogram.dispatcher.filters.builtin import CommandStart

from loader import dp


@dp.message_handler(CommandStart())
async def bot_start(message: types.Message):
    await message.answer(f"Привет, {message.from_user.full_name}!")

С командой start дела обстоят точно также, как и с help.

Основная часть telegram-бота

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

Файл video.py

В начале импортируем все компоненты AIOGram:

from aiogram import types
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.builtin import Command

from loader import dp, bot
from states import Dowload

FSMContext – это класс для работы с машинными состояниями  

Dp и bot - для оработы декораторов и отправки аудио.

Dowload – наши состояния.

Теперь импортируем библиотеки, которые понадобятся для основной логики скрипта:

from pytube import YouTube
import os
import uuid

pytube – библиотека для скачивания видео из YouTube. Она стримит нам видео, отправляя его по кусочкам. В связи с этим мы не можем скачать видео как цельный файл.

os – потребуется для работы с файлами системы - для их создания и удаления.

uuid – создаст нам уникальный ID для названия видео. Почему именно ID, а не название видео как на YouTube? Потому что при использованных кавычек или других знаков, в видео может выскочить ошибка, которую не так просто отследить. 

Скачивание видео

def dowload_video(url, type='audio'):
    yt = YouTube(url)
    audio_id = uuid.uuid4().fields[-1]
    if type == 'audio':
        yt.streams.filter(only_audio=True).first().download("audio", f"{audio_id}.mp3")
        return f"{audio_id}.mp3"
    elif type == 'video':
        return f"{audio_id}.mp4"

Функция download_video принимает в себя ссылку (формат строка) и тип, который мы получим на выходе - audio или video. 

Дальше создаем объект YouTube и передаем ссылку. После генерируем ID для файла.

Дальше идет простая проверка - какой файл нам нужен на выходе. Затем скачиваем видео (подробнее с функционалом библиотеки можно ознакомиться тут).

Обработка сообщений

@dp.message_handler(Command('audio'))
async def start_dow(message: types.Message):
    await message.answer(text=f"Привет, {message.from_user.full_name}, скинь ссылку на видео и я отправлю ее тебе ввиде аудио.")
    await Dowload.dowload.set()


@dp.message_handler(state=Dowload.dowload)
async def dowload(message: types.Message, state: FSMContext):
    title = dowload_video(message.text)
    audio = open(f'audio/{title}', 'rb')
    await message.answer(text="Все скачалось держи аудио")
    try:
        await bot.send_audio(message.chat.id, audio)
        await bot.send_message(message.chat.id, text='')
    except:
        await message.answer(text="Файл слишком большой")
    os.remove(f'audio/{title}')
    await state.finish()

Вся обработка сообщений в AIOGram строится на декораторах. Это функции, которые вызываются перед основным скриптом и могут передавать в него данные, полученные раньше (подробнее о декораторах тут).

start_dow – отрабатывает при команде audio, отправляя сообщение с указанием. Она устанавливает для пользователя состояние dowload. Это состояние можно установить разными методами - first() или set().

dowload – срабатывает, когда у пользователя состояние dowload. Скачивает видео и сохраняет название файла в title. После открывает файл в папке audio. Дальше оповещает пользователя о скачивании аудио и отправляет его. Важно уточнить, что отправлять типы audio, video или document можно только через обрушение к боту, а не к диспетчеру.

После отправки мы заканчиваем цепочку действий, завершая состояние пользователя и удаляем скачанный файл из папки.

Python + Telegram + AIOgram - что в итоге?

Итак, мы успешно собрали Telegram-бота, который скачивает аудиодорожку из YouTube и присылает её нам в Telegram. Это может быть очень удобным если мы не хотим или не можем оплатить YouTube Premium;)

Почему мы использовали именно AIOGram? Он имеет обширный функционал и может работать асинхронно со всеми пользователями, чего трудно добиться в pyTelegramBotAPI. 

Помимо этого, мы можем модерировать телеграм каналы и принимать команды, делить пользователей на администраторов, модераторов и других. Всё это даёт поистине огромные возможности для автоматизации различных задач с помощью Telegram.

Я люблю python за простоту использования - он как конструктор LEGO для взрослых. Вам не требуется все время следить за скобками как в C-подобных языках. Вам не надо разбираться в низкоуровневом программировании. Вы можете просто писать код и получать удовольствие от процесса и результата;)

Полезные ссылки:

Справка AIOGram - https://docs.aiogram.dev/en/latest/install.html

Библиотека PyTube - https://pytube.io/en/latest/

Библиотека Uuid - https://docs.python.org/3/library/uuid.html

Библиотека OS - https://docs.python.org/3/library/os.html

Проект из статьи - https://github.com/Redkomel56/PyTube_telegram

Комментарии