feat: enhance error handling and add limits for video downloads

This commit is contained in:
vchikalkin 2025-12-26 16:44:12 +03:00
parent e40b8cd704
commit e79b0216b2
7 changed files with 39 additions and 16 deletions

View File

@ -24,5 +24,11 @@ err-invalid-download-urls = 🔍 Download links not found. The video might be de
err-generic = ⚠️ Something went wrong. Please try again in a few seconds err-generic = ⚠️ Something went wrong. Please try again in a few seconds
err-limit-exceeded = 🚫 Too many requests! Please wait err-limit-exceeded = 🚫 Too many requests! Please wait
err-invalid-tiktok-response = 🔍 Invalid TikTok response. The video might be deleted or unavailable
err-invalid-instagram-response = 🔍 Invalid Instagram response. The post might be deleted or unavailable
err-invalid-youtube-response = 🔍 Invalid YouTube response. The video might be deleted or unavailable
err-youtube-duration-exceeded = 🚫 Video duration exceeds limit
err-youtube-no-quality = 🔍 No suitable quality found for this video
msg-welcome = Welcome! I can download TikTok, Instagram or YouTube videos and images for you without watermark. Just send me the link (for example: https://vt.tiktok.com/ or https://www.instagram.com/p/ or https://www.youtube.com/) msg-welcome = Welcome! I can download TikTok, Instagram or YouTube videos and images for you without watermark. Just send me the link (for example: https://vt.tiktok.com/ or https://www.instagram.com/p/ or https://www.youtube.com/)

View File

@ -24,5 +24,11 @@ err-invalid-download-urls = 🔍 Не удалось найти ссылки д
err-generic = ⚠️ Что-то пошло не так. Попробуйте еще раз через несколько секунд err-generic = ⚠️ Что-то пошло не так. Попробуйте еще раз через несколько секунд
err-limit-exceeded = 🚫 Слишком много запросов! Подождите немного err-limit-exceeded = 🚫 Слишком много запросов! Подождите немного
err-invalid-tiktok-response = 🔍 Некорректный ответ от TikTok. Видео может быть удалено или недоступно
err-invalid-instagram-response = 🔍 Некорректный ответ от Instagram. Запись может быть удалена или недоступна
err-invalid-youtube-response = 🔍 Некорректный ответ от YouTube. Видео может быть удалено или недоступно
err-youtube-duration-exceeded = 🚫 Длительность видео превышает лимит
err-youtube-no-quality = 🔍 Не найдено подходящее качество для этого видео
msg-welcome = Добро пожаловать! Я могу скачать для вас видео и изображения из TikTok, Instagram или YouTube без водяного знака. Для этого просто отправьте мне ссылку (например: https://vt.tiktok.com/, https://www.instagram.com/p/ или https://www.youtube.com/watch?v=) msg-welcome = Добро пожаловать! Я могу скачать для вас видео и изображения из TikTok, Instagram или YouTube без водяного знака. Для этого просто отправьте мне ссылку (например: https://vt.tiktok.com/, https://www.instagram.com/p/ или https://www.youtube.com/watch?v=)

View File

@ -34,17 +34,26 @@ feature.on('message:text', logHandle('download-message'), async (context) => {
let imagesUrls: string[] | undefined; let imagesUrls: string[] | undefined;
let videoUrl: string | undefined; let videoUrl: string | undefined;
if (isTikTok) { try {
const result = await getTiktokDownloadUrl(url); if (isTikTok) {
imagesUrls = result.images; const result = await getTiktokDownloadUrl(url);
videoUrl = result.play; imagesUrls = result.images;
} else if (isInstagram) { videoUrl = result.play;
const result = await getInstagramDownloadUrl(url); } else if (isInstagram) {
imagesUrls = result.images; const result = await getInstagramDownloadUrl(url);
videoUrl = result.play; imagesUrls = result.images;
} else if (isYoutube) { videoUrl = result.play;
const result = await getYoutubeDownloadUrl(url); } else if (isYoutube) {
videoUrl = result.play; const result = await getYoutubeDownloadUrl(url);
videoUrl = result.play;
}
} catch (err: any) {
const message = err?.message ?? String(err);
if (typeof message === 'string' && message.startsWith('err-')) {
return context.reply(context.t(message));
}
return context.reply(context.t('err-generic'));
} }
if (!videoUrl && !imagesUrls?.length) { if (!videoUrl && !imagesUrls?.length) {

View File

@ -0,0 +1 @@
export const MAX_VIDEO_DURATION_SECONDS = 180; // 3 minutes

View File

@ -27,7 +27,7 @@ export async function getInstagramDownloadUrl(url: string) {
url, url,
}); });
if (!data) throw new Error('Invalid Instagram response'); if (!data) throw new Error('err-invalid-instagram-response');
const isVideo = data.type === 'video' || !data.carouselItems.length; const isVideo = data.type === 'video' || !data.carouselItems.length;

View File

@ -39,7 +39,7 @@ export async function getTiktokDownloadUrl(url: string) {
const res = await axios.get(`https://tikwm.com/api/?url=${encodeURIComponent(url)}`); const res = await axios.get(`https://tikwm.com/api/?url=${encodeURIComponent(url)}`);
const { data } = res.data as Root; const { data } = res.data as Root;
if (!data) throw new Error('Invalid TikTok response'); if (!data) throw new Error('err-invalid-tiktok-response');
return data; return data;
} }

View File

@ -1,3 +1,4 @@
import { MAX_VIDEO_DURATION_SECONDS } from '@/constants/limits';
import axios from 'axios'; import axios from 'axios';
import { wrapper } from 'axios-cookiejar-support'; import { wrapper } from 'axios-cookiejar-support';
import * as tough from 'tough-cookie'; import * as tough from 'tough-cookie';
@ -64,8 +65,8 @@ export async function getYoutubeDownloadUrl(url: string) {
}, },
); );
if (!infoData?.medias.length) throw new Error('Invalid YouTube response'); if (!infoData?.medias.length) throw new Error('err-invalid-youtube-response');
if (infoData.duration > 120) throw new Error('Video duration exceeds limit'); if (infoData.duration > MAX_VIDEO_DURATION_SECONDS) throw new Error('err-youtube-duration-exceeded');
let quality: string | undefined; let quality: string | undefined;
@ -80,7 +81,7 @@ export async function getYoutubeDownloadUrl(url: string) {
} }
} }
if (!quality) throw new Error('No suitable quality found'); if (!quality) throw new Error('err-youtube-no-quality');
// fetch download link // fetch download link
const { data: downloadData } = await client.post<DownloadRoot>( const { data: downloadData } = await client.post<DownloadRoot>(