UNPKG

yt-dlx

Version:

Effortless Audio-Video Downloader And Streamer!

207 lines 8.59 kB
import * as fs from "fs"; import colors from "colors"; import * as path from "path"; import web from "../../../web"; import { z, ZodError } from "zod"; import ffmpeg from "fluent-ffmpeg"; import { encore } from "yt-dlx-encore"; import ytdlx from "../../../base/Agent"; import YouTubeID from "../../../web/YouTubeId"; import formatTime from "../../../base/formatTime"; import calculateETA from "../../../base/calculateETA"; var ZodSchema = z.object({ output: z.string().optional(), useTor: z.boolean().optional(), verbose: z.boolean().optional(), query: z.array(z.string().min(2)), resolution: z.enum([ "144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p", "3072p", "4320p", "6480p", "8640p", "12000p", ]), filter: z .enum([ "invert", "rotate90", "rotate270", "grayscale", "rotate180", "flipVertical", "flipHorizontal", ]) .optional(), }); /** * Downloads videos from YouTube based on a list of video URLs with the lowest available resolution. * * @param query - An array of YouTube video URLs to process. * @param resolution - The desired resolution of the downloaded videos. * @param verbose - (optional) Whether to log verbose output or not. * @param useTor - (optional) Whether to use Tor for the download or not. * @param output - (optional) The output directory for the processed files. * @param filter - (optional) The video filter to apply. Available options: "invert", "rotate90", "rotate270", "grayscale", "rotate180", "flipVertical", "flipHorizontal". * @returns A Promise that resolves when all videos have been processed. */ export default async function ListVideoCustom({ query, resolution, verbose, output, useTor, filter, }) { try { ZodSchema.parse({ query, resolution, verbose, output, useTor, filter, }); var startTime; var unique = new Set(); for (var purl of query) { try { var playlistId = await YouTubeID(purl); if (!playlistId) { console.log(colors.red("@error: "), "@error: invalid playlist", purl); continue; } else { var punique = await web.playlistVideos({ playlistId, }); if (punique === undefined) { console.log(colors.red("@error:"), "unable to get response for", purl); continue; } for (var video of punique.result) unique.add(video); } } catch (error) { console.log(colors.red("@error:"), error.message); continue; } } console.log(colors.blue("@info:"), "total number of uncommon videos:", colors.blue(unique.size.toString())); for (const video of unique) { try { var engineData = await ytdlx({ query: video.videoLink, verbose, useTor, }); if (engineData === undefined) { console.log(colors.red("@error:"), "unable to get response!"); continue; } var title = engineData.metaData.title.replace(/[^a-zA-Z0-9_]+/g, "_"); var folder = output ? path.join(__dirname, output) : __dirname; if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true }); var filename = `yt-dlx_(VideoCustom_${resolution}_`; var ff = ffmpeg() .setFfmpegPath((await encore().then(fp => fp.ffmpeg)).toString()) .setFfprobePath((await encore().then(fp => fp.ffprobe)).toString()); var vdata = engineData.ManifestHigh.find(i => i.format.includes(resolution.replace("p", "").toString())); ff.addInput(engineData.AudioHighF.url); if (vdata) ff.addInput(vdata.url.toString()); else throw new Error(colors.red("@error: ") + "no video data found. use list_formats() maybe?"); ff.videoCodec("copy"); ff.withOutputFormat("matroska"); ff.addOption("-headers", "X-Forwarded-For: " + engineData.ipAddress); switch (filter) { case "grayscale": ff.withVideoFilter("colorchannelmixer=.3:.4:.3:0:.3:.4:.3:0:.3:.4:.3"); filename += `grayscale)_${title}.mkv`; break; case "invert": ff.withVideoFilter("negate"); filename += `invert)_${title}.mkv`; break; case "rotate90": ff.withVideoFilter("rotate=PI/2"); filename += `rotate90)_${title}.mkv`; break; case "rotate180": ff.withVideoFilter("rotate=PI"); filename += `rotate180)_${title}.mkv`; break; case "rotate270": ff.withVideoFilter("rotate=3*PI/2"); filename += `rotate270)_${title}.mkv`; break; case "flipHorizontal": ff.withVideoFilter("hflip"); filename += `flipHorizontal)_${title}.mkv`; break; case "flipVertical": ff.withVideoFilter("vflip"); filename += `flipVertical)_${title}.mkv`; break; default: filename += `)_${title}.mkv`; break; } ff.on("error", error => { throw new Error(error.message); }); ff.on("start", comd => { startTime = new Date(); if (verbose) console.info(colors.green("@comd:"), comd); }); ff.on("end", () => process.stdout.write("\n")); ff.on("progress", ({ percent, timemark }) => { var color = colors.green; if (isNaN(percent)) percent = 0; if (percent > 98) percent = 100; if (percent < 25) color = colors.red; else if (percent < 50) color = colors.yellow; var width = Math.floor(process.stdout.columns / 4); var scomp = Math.round((width * percent) / 100); var progb = color("━").repeat(scomp) + color(" ").repeat(width - scomp); process.stdout.write(`\r${color("@prog:")} ${progb}` + ` ${color("| @percent:")} ${percent.toFixed(2)}%` + ` ${color("| @timemark:")} ${timemark}` + ` ${color("| @eta:")} ${formatTime(calculateETA(startTime, percent))}`); }); await new Promise((resolve, _reject) => { ff.output(path.join(folder, filename.replace("_)_", ")_"))); ff.on("end", () => resolve()); ff.on("error", error => { throw new Error(colors.red("@error: ") + error.message); }); ff.run(); }); } catch (error) { console.log(colors.red("@error:"), error); continue; } } } catch (error) { switch (true) { case error instanceof ZodError: throw new Error(colors.red("@zod-error:") + error.errors); default: throw new Error(colors.red("@error:") + error.message); } } finally { console.log(colors.green("@info:"), "❣️ Thank you for using yt-dlx. Consider 🌟starring the GitHub repo https://github.com/yt-dlx."); } } //# sourceMappingURL=ListVideoCustom.js.map