Skip to content

Instantly share code, notes, and snippets.

@weskerty
Last active December 10, 2025 02:29
Show Gist options
  • Select an option

  • Save weskerty/ce6a4ea2c4b0a73889cae8431911734d to your computer and use it in GitHub Desktop.

Select an option

Save weskerty/ce6a4ea2c4b0a73889cae8431911734d to your computer and use it in GitHub Desktop.
Download Everything, YT-DLP and CURL - DownLoadAll. Need lyfe00011 Levanter Bot
// dla.js is a complement to LevanterBot: https://github.com/lyfe00011/levanter
// Copyright (C) 2025 Weskerty
//
// Este programa se distribuye bajo los términos de la Licencia Pública General Affero de GNU (AGPLv3).
// Usted puede usarlo, modificarlo y redistribuirlo bajo esa licencia.
// Este software se proporciona SIN GARANTÍA alguna.
// Licencia completa: https://www.gnu.org/licenses/agpl-3.0.html
// DownLoadAll. Descargar con Aria y corregir el formato con File.
const fs = require('fs').promises;
const path = require('path');
const os = require('os');
const { promisify } = require('util');
const { execFile } = require('child_process');
const { bot, isUrl } = require('../lib');
const execFileAsync = promisify(execFile);
const FILE_TYPES = {
video: {
extensions: new Set(['mp4', 'mkv', 'avi', 'webm', 'mov', 'flv', 'm4v']),
mimetype: 'video/mp4',
},
image: {
extensions: new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'tiff', 'svg']),
mimetype: 'image/jpeg',
},
document: {
extensions: new Set(['pdf', 'epub', 'docx', 'txt', 'apk', 'apks', 'zip', 'rar', 'iso', 'ini', 'cbr', 'cbz', 'torrent', 'json', 'xml', 'html', 'css', 'js', 'csv', 'xls', 'xlsx', 'ppt', 'pptx']),
mimetypes: new Map([
['pdf', 'application/pdf'],
['epub', 'application/epub+zip'],
['docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
['txt', 'text/plain'],
['apk', 'application/vnd.android.package-archive'],
['apks', 'application/vnd.android.package-archive'],
['zip', 'application/zip'],
['rar', 'application/x-rar-compressed'],
['iso', 'application/x-iso9660-image'],
['ini', 'text/plain'],
['cbr', 'application/x-cbr'],
['cbz', 'application/x-cbz'],
['torrent', 'application/x-bittorrent'],
['json', 'application/json'],
['xml', 'application/xml'],
['html', 'text/html'],
['css', 'text/css'],
['js', 'application/javascript'],
['csv', 'text/csv'],
['xls', 'application/vnd.ms-excel'],
['xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
['ppt', 'application/vnd.ms-powerpoint'],
['pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'],
]),
defaultMimetype: 'application/octet-stream',
},
audio: {
extensions: new Set(['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac', 'wma']),
mimetype: 'audio/mpeg',
},
};
const TIMEOUT_MS = 300000;
function withTimeout(promise, ms = TIMEOUT_MS, errorMsg = 'Operation timeout') {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(errorMsg)), ms)
)
]);
}
function getFileDetails(filePath) {
const ext = path.extname(filePath).slice(1).toLowerCase();
for (const [category, typeInfo] of Object.entries(FILE_TYPES)) {
if (typeInfo.extensions.has(ext)) {
return {
category,
mimetype: category === 'document'
? typeInfo.mimetypes.get(ext) || typeInfo.defaultMimetype
: typeInfo.mimetype,
};
}
}
return {
category: 'document',
mimetype: FILE_TYPES.document.defaultMimetype,
};
}
class DownloadQueue {
constructor(maxConcurrent = 2) {
this.queue = [];
this.activeDownloads = 0;
this.maxConcurrent = maxConcurrent;
this.isProcessing = false;
}
async add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.processNext();
});
}
async processNext() {
if (this.isProcessing) {
return;
}
this.isProcessing = true;
try {
while (this.activeDownloads < this.maxConcurrent && this.queue.length > 0) {
const { task, resolve, reject } = this.queue.shift();
this.activeDownloads++;
task()
.then(resolve)
.catch(reject)
.finally(() => {
this.activeDownloads--;
setImmediate(() => this.processNext());
});
}
} finally {
this.isProcessing = false;
}
}
}
class MediaDownloader {
constructor() {
this.ctx = null;
this.ytDlpBinaryPath = null;
this.ytDlpBinaries = new Map([
['win32-x64', 'yt-dlp.exe'],
['win32-ia32', 'yt-dlp_x86.exe'],
['darwin', 'yt-dlp_macos'],
['linux-x64', 'yt-dlp_linux'],
['linux-arm64', 'yt-dlp_linux_aarch64'],
['linux-arm', 'yt-dlp_linux_armv7l'],
['default', 'yt-dlp'],
]);
this.presetFormats = {
video: ['-f', 'sd/18/bestvideo[height<=720][vcodec*=h264]+bestaudio[acodec*=aac]/bestvideo[height<=720][vcodec*=h264]+bestaudio[acodec*=mp4a]/bestvideo[height<=720][vcodec*=h264]+bestaudio/bestvideo[height<=720]+bestaudio/bestvideo[vcodec*=h264]+bestaudio[acodec*=aac]/bestvideo[vcodec*=h264]+bestaudio[acodec*=mp4a]/bestvideo[vcodec*=h264]+bestaudio/bestvideo+bestaudio/best', '--sponsorblock-remove', 'all', '--embed-chapters', '--embed-metadata'],
audio: ['-f', 'ba/best', '-x', '--audio-format', 'mp3', '--audio-quality', '0', '--sponsorblock-remove', 'all'],
playlist: ['--yes-playlist'],
noPlaylist: ['--no-playlist']
};
this.commonArgs = [
'--restrict-filenames',
'--extractor-retries', '3',
'--fragment-retries', '3',
'--compat-options', 'no-youtube-unavailable-videos',
'--ignore-errors',
'--no-abort-on-error',
'--remote-components', 'ejs:github',
'--js-runtimes', 'node'
];
this.aria2Args = [
'--external-downloader', 'aria2c',
'--external-downloader-args', 'aria2c:-x 16 -k 1M -j 16 --file-allocation=none --async-dns=false --optimize-concurrent-downloads=true --max-tries=5 --retry-wait=3'
];
}
setContext(ctx) {
this.ctx = ctx;
this.config = {
tempDir: ctx.TEMP_DOWNLOAD_DIR || path.join(process.cwd(), 'tmp'),
maxFileSize: (parseInt(ctx.MAX_UPLOAD, 10) * 1048576) || 1500000000,
ytDlpPath: path.join(process.cwd(), 'media', 'bin'),
maxConcurrent: parseInt(ctx.MAXSOLICITUD, 10) || 2,
playlistLimit: parseInt(ctx.PLAYLIST_LIMIT, 10) || 20,
cookies: ctx.COOKIES || null,
enableMp3Zip: ctx.DLAMP3ZIP === undefined || ctx.DLAMP3ZIP === 'TRUE' || ctx.DLAMP3ZIP === 'true'
};
this.downloadQueue = new DownloadQueue(this.config.maxConcurrent);
}
normalizeTimeSegment(segment) {
const parts = segment.split(':');
if (parts.length === 2) {
const [min, sec] = parts;
return `00:${min.padStart(2, '0')}:${sec.padStart(2, '0')}`;
} else if (parts.length === 3) {
const [h, m, s] = parts;
return `${h.padStart(2, '0')}:${m.padStart(2, '0')}:${s.padStart(2, '0')}`;
}
return segment;
}
parseTimeRanges(timeString) {
if (!timeString || !timeString.trim()) {
return null;
}
const ranges = timeString.split(/\s+|,/).filter(r => r.trim());
const normalizedRanges = [];
for (const range of ranges) {
if (range.includes('-')) {
const [start, end] = range.split('-');
const normalizedStart = this.normalizeTimeSegment(start.trim());
const normalizedEnd = this.normalizeTimeSegment(end.trim());
normalizedRanges.push(`*${normalizedStart}-${normalizedEnd}`);
}
}
return normalizedRanges.length > 0 ? normalizedRanges.join(',') : null;
}
buildCookiesArgs() {
const cookiesPath = path.join(this.config.ytDlpPath, 'yt-dlp.cookies.txt');
try {
require('fs').accessSync(cookiesPath, require('fs').constants.F_OK);
return ['--cookies', cookiesPath];
} catch {
return this.config.cookies ? ['--cookies', this.config.cookies] : [];
}
}
async checkZipAvailable() {
try {
await withTimeout(execFileAsync('which', ['zip']), TIMEOUT_MS, 'Timeout checking zip availability');
return true;
} catch {
return false;
}
}
async compressWithZip(outputDir) {
const outputZip = path.join(this.config.tempDir, `playlist_${Date.now()}.zip`);
const files = await withTimeout(fs.readdir(outputDir), TIMEOUT_MS, 'Timeout reading directory for zip');
await withTimeout(
execFileAsync('zip', ['-r', outputZip, ...files], {
cwd: outputDir,
maxBuffer: 1024 * 1024 * 100
}),
TIMEOUT_MS,
'Timeout creating zip file'
);
return outputZip;
}
async safeExecuteYtDlp(args) {
return await execFileAsync(this.ytDlpBinaryPath, args, {
maxBuffer: 1024 * 1024 * 100,
timeout: TIMEOUT_MS
});
}
async isYtDlpAvailable() {
try {
await withTimeout(execFileAsync('yt-dlp', ['--version']), TIMEOUT_MS, 'Timeout checking yt-dlp');
return true;
} catch {
return false;
}
}
detectYtDlpBinaryName() {
const platform = os.platform();
const arch = os.arch();
const key = `${platform}-${arch}`;
return this.ytDlpBinaries.get(key) || this.ytDlpBinaries.get('default');
}
async ensureDirectories() {
await Promise.all([
withTimeout(fs.mkdir(this.config.tempDir, { recursive: true }), TIMEOUT_MS, 'Timeout creating temp directory'),
withTimeout(fs.mkdir(this.config.ytDlpPath, { recursive: true }), TIMEOUT_MS, 'Timeout creating yt-dlp directory'),
]);
}
async detectYtDlpBinary(message) {
if (this.ytDlpBinaryPath) {
return this.ytDlpBinaryPath;
}
if (await this.isYtDlpAvailable()) {
this.ytDlpBinaryPath = 'yt-dlp';
return this.ytDlpBinaryPath;
}
const fileName = this.detectYtDlpBinaryName();
const filePath = path.join(this.config.ytDlpPath, fileName);
try {
await withTimeout(fs.access(filePath), TIMEOUT_MS, 'Timeout accessing yt-dlp binary');
this.ytDlpBinaryPath = filePath;
return this.ytDlpBinaryPath;
} catch {
if (message) {
this.ytDlpBinaryPath = await this.downloadYtDlp(message);
return this.ytDlpBinaryPath;
}
return null;
}
}
async downloadYtDlp(message) {
await this.ensureDirectories();
const fileName = this.detectYtDlpBinaryName();
const downloadUrl = `https://github.com/yt-dlp/yt-dlp/releases/latest/download/${fileName}`;
const filePath = path.join(this.config.ytDlpPath, fileName);
const fetch = (await import('node-fetch')).default;
const response = await withTimeout(fetch(downloadUrl), TIMEOUT_MS, 'Timeout downloading yt-dlp');
if (!response.ok) {
throw new Error(`Download failed: ${response.statusText}`);
}
const buffer = Buffer.from(await withTimeout(response.arrayBuffer(), TIMEOUT_MS, 'Timeout reading download response'));
await withTimeout(fs.writeFile(filePath, buffer), TIMEOUT_MS, 'Timeout writing yt-dlp binary');
if (os.platform() !== 'win32') {
await withTimeout(fs.chmod(filePath, '755'), TIMEOUT_MS, 'Timeout setting permissions');
}
return filePath;
}
async loadInfoJson(outputDir) {
const files = await withTimeout(fs.readdir(outputDir), TIMEOUT_MS, 'Timeout reading directory for info.json');
const jsonFiles = files.filter(f => f.endsWith('.info.json'));
const infoMap = new Map();
let genericInfo = null;
for (const jsonFile of jsonFiles) {
try {
const jsonPath = path.join(outputDir, jsonFile);
const content = await withTimeout(fs.readFile(jsonPath, 'utf8'), TIMEOUT_MS, 'Timeout reading info.json');
const data = JSON.parse(content);
const baseName = jsonFile.replace('.info.json', '');
const mediaFiles = files.filter(f => {
const fileBase = f.substring(0, f.lastIndexOf('.'));
return fileBase === baseName && !f.endsWith('.info.json');
});
if (mediaFiles.length > 0) {
for (const mediaFile of mediaFiles) {
infoMap.set(mediaFile, {
title: data.title || '',
description: data.description || '',
uploader: data.uploader || ''
});
}
} else {
if (!genericInfo) {
genericInfo = {
title: data.title || '',
description: data.description || '',
uploader: data.uploader || ''
};
}
}
} catch (error) {
continue;
}
}
return { infoMap, genericInfo };
}
formatCaption(info) {
if (!info) return null;
const titleLine = info.uploader ? `> ${info.title} - ${info.uploader}` : info.title;
const descLine = info.description ? `${info.description}` : '';
return descLine ? `${titleLine}\n${descLine}` : titleLine;
}
async fixExtension(filePath) {
const ext = path.extname(filePath).slice(1).toLowerCase();
for (const typeInfo of Object.values(FILE_TYPES)) {
if (typeInfo.extensions && typeInfo.extensions.has(ext)) {
return filePath;
}
}
try {
const { stdout } = await execFileAsync('file', ['-b', '--extension', filePath]);
const detectedExt = stdout.trim().split('/')[0];
if (detectedExt && detectedExt !== '???') {
const newPath = ext
? filePath.replace(/\.[^.]*$/, `.${detectedExt}`)
: `${filePath}.${detectedExt}`;
await fs.rename(filePath, newPath);
return newPath;
}
} catch {}
return filePath;
}
async processDownloadedFile(message, filePath, originalFileName, quotedMessage, caption = null) {
filePath = await this.fixExtension(filePath);
const { mimetype, category } = getFileDetails(filePath);
const fileBuffer = await withTimeout(fs.readFile(filePath), TIMEOUT_MS, 'Timeout reading file for upload');
const options = { fileName: path.basename(filePath), mimetype, quoted: quotedMessage };
if (caption) {
options.caption = caption;
}
await message.send(fileBuffer, options, category);
await this.safeCleanup(filePath);
}
async safeCleanup(target, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const stats = await fs.stat(target);
if (stats.isDirectory()) {
await withTimeout(fs.rm(target, { recursive: true, force: true }), TIMEOUT_MS, 'Timeout cleaning directory');
} else {
await withTimeout(fs.unlink(target), TIMEOUT_MS, 'Timeout cleaning file');
}
return;
} catch (err) {
if (err.code === 'ENOENT') {
return;
}
if (i === retries - 1) {
console.error(`Failed to cleanup ${target} after ${retries} attempts:`, err.message);
return;
}
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
}
async updateYtDlp(message) {
const ytDlpPath = await this.detectYtDlpBinary(message);
try {
const result = await this.safeExecuteYtDlp(['--update-to', 'master']);
const updateOutput = result.stdout || result.stderr || 'yt-dlp actualizado exitosamente';
return `> 🔄UpdateBin \n${updateOutput}`;
} catch (updateError) {
return null;
}
}
async uploadCookies(message, cookieText = null) {
const quotedMessage = message.reply_message;
let cookieContent = null;
if (cookieText) {
cookieContent = cookieText;
} else if (quotedMessage) {
const mediaBuffer = await quotedMessage.downloadMediaMessage();
if (!mediaBuffer) {
throw new Error('Error al descargar el archivo de cookies');
}
cookieContent = mediaBuffer.toString();
} else {
throw new Error('Las cookies deben ser texto o citar un archivo de cookies');
}
await this.ensureDirectories();
const cookiesPath = path.join(this.config.ytDlpPath, 'yt-dlp.cookies.txt');
await withTimeout(fs.writeFile(cookiesPath, cookieContent), TIMEOUT_MS, 'Timeout writing cookies file');
await message.send('Cookies subida', { quoted: message.quoted });
}
async downloadWithYtDlp(message, urls, formatType = 'video', enablePlaylist = false, timeRanges = null) {
return this.downloadQueue.add(async () => {
const ytDlpPath = await this.detectYtDlpBinary(message);
const sessionId = `yt-dlp_${Date.now()}`;
const outputDir = path.join(this.config.tempDir, sessionId);
const cookiesArgs = this.buildCookiesArgs();
await this.ensureDirectories();
await withTimeout(fs.mkdir(outputDir, { recursive: true }), TIMEOUT_MS, 'Timeout creating session directory');
const isMp3Playlist = formatType === 'audio' && enablePlaylist;
const isDirectLink = formatType === 'video' && !enablePlaylist;
let lastError = null;
try {
for (const url of urls) {
const outputTemplate = path.join(outputDir, '%(title).70s.%(ext)s');
const playlistArgs = enablePlaylist ? this.presetFormats.playlist : this.presetFormats.noPlaylist;
const playlistItemsArgs = enablePlaylist ? ['--playlist-items', `1:${this.config.playlistLimit}`] : [];
const timeRangeArgs = timeRanges && !enablePlaylist ? ['--download-sections', timeRanges] : [];
const infoJsonArgs = isDirectLink ? ['--write-info-json'] : [];
const args = [
'--max-filesize', this.config.maxFileSize.toString(),
...this.commonArgs,
...playlistArgs,
...playlistItemsArgs,
...cookiesArgs,
...timeRangeArgs,
...infoJsonArgs,
...this.presetFormats[formatType],
'-o', outputTemplate,
url
];
try {
await this.safeExecuteYtDlp(args);
} catch (error) {
lastError = error;
}
}
const allFiles = await withTimeout(fs.readdir(outputDir), TIMEOUT_MS, 'Timeout reading output directory');
const files = allFiles.filter(f => !f.endsWith('.info.json'));
if (files.length === 0 && isDirectLink) {
for (const url of urls) {
const outputTemplate = path.join(outputDir, '%(title).70s.%(ext)s');
const timeRangeArgs = timeRanges ? ['--download-sections', timeRanges] : [];
const ariaArgs = [
'--max-filesize', this.config.maxFileSize.toString(),
...this.commonArgs,
...this.presetFormats.noPlaylist,
...cookiesArgs,
...timeRangeArgs,
...this.aria2Args,
...this.presetFormats[formatType],
'-o', outputTemplate,
url
];
try {
await this.safeExecuteYtDlp(ariaArgs);
} catch (error) {
lastError = error;
}
}
const ariaFiles = (await withTimeout(fs.readdir(outputDir), TIMEOUT_MS, 'Timeout reading output directory')).filter(f => !f.endsWith('.info.json'));
if (ariaFiles.length === 0) {
await this.safeCleanup(outputDir);
if (lastError) {
const errorMessage = lastError.stderr || lastError.message || 'Error desconocido';
const updateMessage = await this.updateYtDlp(message);
const fullError = updateMessage
? `${errorMessage}\n\n${updateMessage}`
: errorMessage;
throw new Error(fullError);
}
throw new Error('No se descargaron archivos');
}
for (const file of ariaFiles) {
await this.processDownloadedFile(
message,
path.join(outputDir, file),
file,
message.quoted
);
}
await this.safeCleanup(outputDir);
return;
}
if (files.length === 0) {
await this.safeCleanup(outputDir);
if (lastError) {
const errorMessage = lastError.stderr || lastError.message || 'Error desconocido';
const updateMessage = await this.updateYtDlp(message);
const fullError = updateMessage
? `${errorMessage}\n\n${updateMessage}`
: errorMessage;
throw new Error(fullError);
}
throw new Error('No se descargaron archivos');
}
let infoMap = new Map();
let genericInfo = null;
if (isDirectLink) {
const infoData = await this.loadInfoJson(outputDir);
infoMap = infoData.infoMap;
genericInfo = infoData.genericInfo;
}
if (isMp3Playlist && this.config.enableMp3Zip && files.length > 1) {
const zipAvailable = await this.checkZipAvailable();
if (zipAvailable) {
try {
const zipPath = await this.compressWithZip(outputDir);
const { mimetype } = getFileDetails(zipPath);
await message.send(
await withTimeout(fs.readFile(zipPath), TIMEOUT_MS, 'Timeout reading zip file'),
{
fileName: path.basename(zipPath),
mimetype,
caption: 'Usa 7zip para descomprimir',
quoted: message.quoted
},
'document'
);
await this.safeCleanup(zipPath);
} catch (zipError) {
for (const file of files) {
await this.processDownloadedFile(
message,
path.join(outputDir, file),
file,
message.quoted
);
}
}
} else {
for (const file of files) {
await this.processDownloadedFile(
message,
path.join(outputDir, file),
file,
message.quoted
);
}
}
} else {
let firstFileSent = false;
for (const file of files) {
let caption = null;
if (isDirectLink) {
if (infoMap.has(file)) {
caption = this.formatCaption(infoMap.get(file));
} else if (!firstFileSent && genericInfo) {
caption = this.formatCaption(genericInfo);
}
}
await this.processDownloadedFile(
message,
path.join(outputDir, file),
file,
message.quoted,
caption
);
firstFileSent = true;
}
}
if (lastError) {
const errorMessage = lastError.stderr || lastError.message || 'Error desconocido';
const updateMessage = await this.updateYtDlp(message);
const fullError = updateMessage
? `${errorMessage}\n\n${updateMessage}`
: errorMessage;
throw new Error(fullError);
}
} finally {
await this.safeCleanup(outputDir);
}
});
}
async searchAndDownload(message, searchQuery, isVideo = false) {
return this.downloadQueue.add(async () => {
const sessionId = `yt-dlp_${Date.now()}`;
const outputDir = path.join(this.config.tempDir, sessionId);
const cookiesArgs = this.buildCookiesArgs();
await this.ensureDirectories();
await withTimeout(fs.mkdir(outputDir, { recursive: true }), TIMEOUT_MS, 'Timeout creating session directory');
const outputTemplate = path.join(outputDir, '%(title).70s.%(ext)s');
const ytDlpPath = await this.detectYtDlpBinary(message);
const formatArgs = isVideo ? this.presetFormats.video : this.presetFormats.audio;
const searchSources = [
{ prefix: '', useDefaultSearch: true },
...(isVideo ? [
{ prefix: 'ytsearch10:' },
{ prefix: 'gvsearch10:' },
{ prefix: 'yvsearch10:' },
{ prefix: 'dailymotionsearch:' }
] : [
{ prefix: 'ytsearch10:' },
{ prefix: 'scsearch10:' },
{ prefix: 'mailrusearch10:' },
{ prefix: 'nicosearch10:' }
])
];
let lastError = null;
try {
for (const { prefix, useDefaultSearch } of searchSources) {
const searchUrl = useDefaultSearch ? searchQuery : `${prefix}${searchQuery}`;
const searchArgs = useDefaultSearch ? ['--default-search', 'auto'] : [];
const args = [
...searchArgs,
'--max-filesize', this.config.maxFileSize.toString(),
...this.commonArgs,
'--playlist-items', '1',
...formatArgs,
...cookiesArgs,
'-o', outputTemplate,
searchUrl
];
try {
await this.safeExecuteYtDlp(args);
const files = await withTimeout(fs.readdir(outputDir), TIMEOUT_MS, 'Timeout reading output directory');
if (files.length > 0) {
await Promise.all(
files.map(file => this.processDownloadedFile(
message,
path.join(outputDir, file),
file,
message.quoted
))
);
await this.safeCleanup(outputDir);
return;
}
} catch (error) {
lastError = error;
const currentFiles = await withTimeout(fs.readdir(outputDir), TIMEOUT_MS, 'Timeout checking files');
if (currentFiles.length > 0) {
await Promise.all(
currentFiles.map(file => this.processDownloadedFile(
message,
path.join(outputDir, file),
file,
message.quoted
))
);
await this.safeCleanup(outputDir);
return;
}
}
}
await this.safeCleanup(outputDir);
const errorMessage = lastError?.stderr || lastError?.message || 'Error desconocido';
const updateMessage = await this.updateYtDlp(message);
const fullError = updateMessage ? `${errorMessage}\n\n${updateMessage}` : errorMessage;
throw new Error(fullError || `No se pudo descargar: ${searchQuery}`);
} catch (error) {
await this.safeCleanup(outputDir);
throw error;
}
});
}
}
const mediaDownloader = new MediaDownloader();
bot(
{
pattern: 'dla ?(.*)',
fromMe: true,
desc: 'Download All Media Web Site.',
type: 'download',
},
async (message, match, ctx) => {
mediaDownloader.setContext(ctx);
const input = match.trim() || message.reply_message?.text || '';
if (!input) {
await message.send(
'> 🎶Search and Download Song:\n`dla` <query>\n' +
'> 🎥Search and Download Video:\n`dla vd` <query>\n' +
'> ⬇️Download Media: \n`dla` <url>\n' +
'> ✂️Download Segment: \n`dla` <url> `--t` <time>\n' +
'> 🎵Download Audio from Playlist: \n`dla mp3` <url>\n' +
'> 🍪Cookies: `dla cookies` <cookie_text>\n' +
'> 📖Help: github.com/yt-dlp/yt-dlp',
{ quoted: message.quoted }
);
return;
}
if (input.toLowerCase().startsWith('cookies')) {
const cookieText = input.substring('cookies'.length).trim();
await mediaDownloader.uploadCookies(message, cookieText || null);
return;
}
const urlRegex = /(https?:\/\/[^\s]+)/g;
const urls = (input.match(urlRegex) || []).filter(url => isUrl(url));
if (urls.length > 0) {
let commandPart = input;
urls.forEach(url => {
commandPart = commandPart.replace(url, '').trim();
});
const parts = commandPart.split(/\s+/).filter(p => p);
const firstPart = parts[0] || '';
if (firstPart === 'mp3') {
await mediaDownloader.downloadWithYtDlp(message, urls, 'audio', true, null);
} else {
let timeRanges = null;
const timeIndex = parts.indexOf('--t');
if (timeIndex !== -1 && parts[timeIndex + 1]) {
const timeString = parts.slice(timeIndex + 1).join(' ');
timeRanges = mediaDownloader.parseTimeRanges(timeString);
}
await mediaDownloader.downloadWithYtDlp(message, urls, 'video', false, timeRanges);
}
return;
}
const args = input.trim().split(/\s+/);
const command = args[0];
const remainingArgs = args.slice(1);
if (command === 'vd') {
await mediaDownloader.searchAndDownload(message, remainingArgs.join(' '), true);
} else {
await mediaDownloader.searchAndDownload(message, input, false);
}
}
);
module.exports = { mediaDownloader };
@weskerty
Copy link
Author

weskerty commented Dec 31, 2024

YT-DLP-DLA Plugin is a complement to Bots:
lyfelevanter - BSMystic - AcopleBot

image

@Jbcreates
Copy link

Jbcreates commented Dec 31, 2024 via email

@weskerty
Copy link
Author

It showing can't read properties of mp4

Does this happen with all shorts?
What are you running it on? What server? What Linux version?

@Ayomide661
Copy link

I can't download YouTube shorts

What error does it show? Because it works perfectly for me. image

Well, that's because you are using termux right?

@weskerty
Copy link
Author

Well, that's because you are using termux right?

Yes, I use Termux.
If you use an external server that doesn't support ffmpeg, there's nothing you can do, this is not a problem with the bot, the script or yt-dlp, it's a problem with your server.

@xbladev
Copy link

xbladev commented Oct 25, 2025

bro how to put leventer public what is cmd

@weskerty
Copy link
Author

zushi dla

Permite dla en grupo o persona en donde usaste ese comando

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment