This commit is contained in:
vchikalkin 2026-01-14 11:54:52 +03:00
commit 05852eff40
4 changed files with 26 additions and 56 deletions

View File

@ -40,7 +40,6 @@
"tough-cookie": "^6.0.0", "tough-cookie": "^6.0.0",
"tsup": "^8.5.0", "tsup": "^8.5.0",
"typescript": "catalog:", "typescript": "catalog:",
"ytdlp-nodejs": "^2.3.5",
"zod": "catalog:" "zod": "catalog:"
}, },
"devDependencies": { "devDependencies": {

View File

@ -9,8 +9,6 @@ import { validateTikTokUrl, validateInstagramUrl, validateYoutubeUrl } from '@/u
import { Composer, InputFile } from 'grammy'; import { Composer, InputFile } from 'grammy';
import { cluster } from 'radashi'; import { cluster } from 'radashi';
import { getYoutubeDownloadUrl } from '@/utils/youtube'; import { getYoutubeDownloadUrl } from '@/utils/youtube';
import { getDownloadUrl } from '@/utils/yt-dlp';
import { logger } from '@/utils/logger';
const composer = new Composer<Context>(); const composer = new Composer<Context>();
const feature = composer.chatType('private'); const feature = composer.chatType('private');
@ -24,9 +22,7 @@ feature.on('message:text', logHandle('download-message'), async (context) => {
const isInstagram = validateInstagramUrl(url); const isInstagram = validateInstagramUrl(url);
const isYoutube = validateYoutubeUrl(url); const isYoutube = validateYoutubeUrl(url);
const isServiceSupported = isTikTok || isInstagram || isYoutube; if (!isTikTok && !isInstagram && !isYoutube) {
if (!isServiceSupported) {
return context.reply(context.t('err-invalid-url')); return context.reply(context.t('err-invalid-url'));
} }
@ -36,31 +32,28 @@ feature.on('message:text', logHandle('download-message'), async (context) => {
} }
let imagesUrls: string[] | undefined; let imagesUrls: string[] | undefined;
let videoUrl = await getDownloadUrl(url); let videoUrl: string | undefined;
if (!videoUrl) { try {
logger.info(`Failed to get download URL for ${url}, using fallback`); 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'));
} }
} 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) {
@ -74,7 +67,11 @@ feature.on('message:text', logHandle('download-message'), async (context) => {
chunk.map((imageUrl) => ({ media: imageUrl, type: 'photo' })), chunk.map((imageUrl) => ({ media: imageUrl, type: 'photo' })),
); );
} }
} else {
return;
}
if (videoUrl) {
const { video } = await context.replyWithVideo(new InputFile({ url: videoUrl })); const { video } = await context.replyWithVideo(new InputFile({ url: videoUrl }));
await redis.set(url, video.file_id, 'EX', TTL_URLS); await redis.set(url, video.file_id, 'EX', TTL_URLS);
} }

View File

@ -1,16 +0,0 @@
import { logger } from './logger';
import { YtDlp } from 'ytdlp-nodejs';
export const getDownloadUrl = async (url: string): Promise<string | undefined> => {
try {
const ytdlp = new YtDlp();
const [downloadUrl] = await ytdlp.getUrlsAsync(url, { format: 'b' });
return downloadUrl;
} catch (error: any) {
if (error.code === 'ENOENT') {
throw new Error('yt-dlp not found. Please install yt-dlp.');
}
logger.error(`Failed to get download URL for ${url}: ${error.message}`);
}
};

10
pnpm-lock.yaml generated
View File

@ -117,9 +117,6 @@ importers:
typescript: typescript:
specifier: 'catalog:' specifier: 'catalog:'
version: 5.9.2 version: 5.9.2
ytdlp-nodejs:
specifier: ^2.3.5
version: 2.3.5
zod: zod:
specifier: 'catalog:' specifier: 'catalog:'
version: 3.25.76 version: 3.25.76
@ -3603,11 +3600,6 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'} engines: {node: '>=10'}
ytdlp-nodejs@2.3.5:
resolution: {integrity: sha512-7V08DRv8C1K0HxJFvRoaoLYFS/reJ9VJBlaMVhEvdi2IsYK/9Hae1Mah65Y+bhk3RAmx7G9eTfpOhkj3bp0Zbw==}
engines: {node: '>=16.0.0'}
hasBin: true
zod@3.25.76: zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
@ -7449,6 +7441,4 @@ snapshots:
yocto-queue@0.1.0: {} yocto-queue@0.1.0: {}
ytdlp-nodejs@2.3.5: {}
zod@3.25.76: {} zod@3.25.76: {}