saveapi.org

Как сделать Telegram-бота для скачивания видео

Бот, в который приходит ссылка, а выходит видео, укладывается примерно в сорок строк — если разбор ссылки не ваша забота. Это руководство проходит весь путь: от ссылки к адресу медиа, от адреса медиа к сообщению в Telegram, и три вещи, которые ломаются в проде — закрытые посты, файлы, которые Telegram не может забрать сам, и повторная отправка одного и того же видео сто раз.

Обновлено

Установите две библиотеки

aiogram общается с Telegram, httpx делает вызов API. Больше на вашей стороне ничего не нужно: ни стека для скрапинга, ни headless-браузера, ни cookie, которые надо поддерживать живыми.

Terminal
pip install aiogram httpx

Разберите ссылку

Один GET с ключом в заголовке Authorization. Эндпоинт определяет платформу по самой ссылке, поэтому бот не ветвится по домену — и reel из Instagram, и видео TikTok, и клип Likee проходят через одну и ту же функцию.

Python
import httpx

API = "https://api.saveapi.org/v1/download"
KEY = "sk_live_xxxx"

client = httpx.AsyncClient(timeout=30)

async def resolve(link: str) -> dict:
    r = await client.get(
        API,
        params={"url": link},
        headers={"Authorization": f"Bearer {KEY}"},
    )
    return r.json()

Отправьте медиа обратно

Ответ сам говорит, что пришло. Проверяйте medias[0].type, а не предполагайте видео: слайд-шоу TikTok и карусель Instagram приходят картинками, а для них у бота другой метод. meta.title вполне годится как подпись.

Python
from aiogram import Bot, Dispatcher, F
from aiogram.types import Message

dp = Dispatcher()

@dp.message(F.text.startswith("http"))
async def on_link(message: Message):
    data = await resolve(message.text.strip())

    if not data["success"]:
        await message.answer(EXPLAIN.get(data["error"]["code"], "Try another link."))
        return

    media = data["medias"][0]
    if media["type"] == "video":
        await message.answer_video(media["url"], caption=data["meta"]["title"])
    else:
        await message.answer_photo(media["url"])

Напишите тексты ошибок человеческим языком

Пользователи целыми днями присылают закрытые посты и мёртвые ссылки. Свяжите стабильные коды ошибок с понятными фразами: бот, который говорит «этот аккаунт закрыт», получит другую ссылку, а бот, который говорит «Error 403», — гневное сообщение. Закрытый пост и пост без медиа стоят 0.1 кредита вместо полных 1.5.

Python
EXPLAIN = {
    "PRIVATE_CONTENT": "That account is private — I can only fetch public posts.",
    "MEDIA_NOT_FOUND": "That post has no media, or it was deleted.",
    "UNSUPPORTED_PLATFORM": "I do not know that site yet.",
    "QUOTA_EXCEEDED": "Out of credits for today.",
    "UPSTREAM_TIMEOUT": "The source was slow. Send the link again.",
}

Кэшируйте file_id, а не адрес медиа

Как только Telegram получил файл, он выдаёт file_id, и повторная отправка по нему не стоит ничего и ничего не загружает. Хранить адрес медиа — ошибка: CDN источников подписывают ссылки и гасят их за часы, так что кэш адресов к утру превращается в кэш из 403.

Python
sent = await message.answer_video(media["url"])
file_id = sent.video.file_id      # BU saqlanadi, media["url"] emas

# Keyingi safar o'sha post so'ralsa:
await message.answer_video(file_id)

Запасной путь — загрузить байты самому

Telegram пытается забрать ссылку сам, и иногда CDN источника ему отказывает — чаще всего на Instagram и всегда в медиагруппе. Поймайте этот случай, скачайте байты и отправьте файлом. Лимит на загрузку ботом в 50 МБ при этом никуда не девается, поэтому это запасной путь, а не основной.

Python
from aiogram.types import BufferedInputFile

async def send_bytes(message: Message, url: str):
    r = await client.get(url, follow_redirects=True)
    file = BufferedInputFile(r.content, filename="video.mp4")
    await message.answer_video(file)

Вопросы перед началом

Может ли бот отправлять файлы больше 50 МБ?
Загрузкой — нет: 50 МБ это потолок Telegram для загрузки ботом. Обычный обход — отправить адрес медиа и дать Telegram забрать его самому либо держать большие файлы в приватном канале и копировать сообщение по file_id.
Нужен ли отдельный обработчик для каждой платформы?
Нет. /v1/download определяет платформу по ссылке, поэтому один обработчик закрывает все поддерживаемые сайты. Отдельные эндпоинты нужны тогда, когда вы намеренно принимаете только одну платформу.
Что будет, если пользователь пришлёт закрытый пост?
Придёт 403 с кодом PRIVATE_CONTENT, и спишется 0.1 кредита вместо полных 1.5. Превратите это во фразу: закрытые ссылки присылают постоянно, и человек в этом не виноват.

Платформы, использованные здесь

Другие руководства

Запустите это руководство со своим ключом

Ключ за минуту. 1 000 бесплатных кредитов, без карты и без срока.

Получить ключ и попробоватьПолный справочник API