Last active
December 10, 2025 02:29
-
-
Save weskerty/ce6a4ea2c4b0a73889cae8431911734d to your computer and use it in GitHub Desktop.
Download Everything, YT-DLP and CURL - DownLoadAll. Need lyfe00011 Levanter Bot
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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 }; |
Author
It showing can't read properties of mp4
…On Tue, Dec 31, 2024, 2:06 AM laWiskaPY ***@***.***> wrote:
***@***.**** commented on this gist.
------------------------------
I can't download YouTube shorts
What error does it show?
Because it works perfectly for me.
image.png (view on web)
<https://gist.github.com/user-attachments/assets/738a2883-00bc-4085-a26c-ceac932e2d7e>
—
Reply to this email directly, view it on GitHub
<https://gist.github.com/weskerty/ce6a4ea2c4b0a73889cae8431911734d#gistcomment-5369375>
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BIQPSKCTROCFWYPK3VM3LB32IHU2DBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTGNBSGQ4TMOJVU52HE2LHM5SXFJTDOJSWC5DF>
.
You are receiving this email because you commented on the thread.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>
.
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?
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.
bro how to put leventer public what is cmd
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

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