yt-dlx
Version:
Effortless Audio-Video Downloader And Streamer!
261 lines • 12.5 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = AudioVideoHighest;
const fs = __importStar(require("fs"));
const colors_1 = __importDefault(require("colors"));
const path = __importStar(require("path"));
const zod_1 = require("zod");
const fluent_ffmpeg_1 = __importDefault(require("fluent-ffmpeg"));
const Agent_1 = __importDefault(require("../../utils/Agent"));
const locator_1 = require("../../utils/locator");
const stream_1 = require("stream");
function formatTime(seconds) {
if (!isFinite(seconds) || isNaN(seconds))
return "00h 00m 00s";
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
return `${hours.toString().padStart(2, "0")}h ${minutes.toString().padStart(2, "0")}m ${secs.toString().padStart(2, "0")}s`;
}
function calculateETA(startTime, percent) {
const currentTime = new Date();
const elapsedTime = (currentTime.getTime() - startTime.getTime()) / 1000;
if (percent <= 0)
return NaN;
const totalTimeEstimate = (elapsedTime / percent) * 100;
const remainingTime = totalTimeEstimate - elapsedTime;
return remainingTime;
}
function progbar({ percent, timemark, startTime }) {
let displayPercent = isNaN(percent || 0) ? 0 : percent || 0;
displayPercent = Math.min(Math.max(displayPercent, 0), 100);
const colorFn = displayPercent < 25 ? colors_1.default.red : displayPercent < 50 ? colors_1.default.yellow : colors_1.default.green;
const width = Math.floor((process.stdout.columns || 80) / 4);
const scomp = Math.round((width * displayPercent) / 100);
const progb = colorFn("━").repeat(scomp) + colorFn(" ").repeat(width - scomp);
const etaSeconds = calculateETA(startTime, displayPercent);
const etaFormatted = formatTime(etaSeconds);
process.stdout.write(`\r${colorFn("@prog:")} ${progb} ${colorFn("| @percent:")} ${displayPercent.toFixed(2)}% ${colorFn("| @timemark:")} ${timemark} ${colorFn("| @eta:")} ${etaFormatted}`);
}
const ZodSchema = zod_1.z.object({
query: zod_1.z.string().min(2),
output: zod_1.z.string().optional(),
useTor: zod_1.z.boolean().optional(),
stream: zod_1.z.boolean().optional(),
verbose: zod_1.z.boolean().optional(),
metadata: zod_1.z.boolean().optional(),
showProgress: zod_1.z.boolean().optional(),
filter: zod_1.z.enum(["invert", "rotate90", "rotate270", "grayscale", "rotate180", "flipVertical", "flipHorizontal"]).optional(),
});
async function AudioVideoHighest({ query, output, useTor, stream, filter, metadata, verbose, showProgress, }) {
try {
ZodSchema.parse({ query, output, useTor, stream, filter, metadata, verbose, showProgress });
if (metadata && (stream || output || filter || showProgress)) {
throw new Error(`${colors_1.default.red("@error:")} The 'metadata' parameter cannot be used with 'stream', 'output', 'filter', or 'showProgress'.`);
}
if (stream && output) {
throw new Error(`${colors_1.default.red("@error:")} The 'stream' parameter cannot be used with 'output'.`);
}
const engineData = await (0, Agent_1.default)({ query, verbose, useTor });
if (!engineData) {
throw new Error(`${colors_1.default.red("@error:")} Unable to retrieve a response from the engine.`);
}
if (!engineData.metaData) {
throw new Error(`${colors_1.default.red("@error:")} Metadata not found in the engine response.`);
}
if (metadata) {
return {
metadata: {
metaData: engineData.metaData,
AudioHighF: engineData.AudioHighF,
AudioHighDRC: engineData.AudioHighDRC,
VideoHighF: engineData.VideoHighF,
VideoHighHDR: engineData.VideoHighHDR,
ManifestHigh: engineData.ManifestHigh,
filename: `yt-dlx_AudioVideoHighest_${filter ? filter + "_" : ""}${engineData.metaData.title?.replace(/[^a-zA-Z0-9_]+/g, "_") || "audio"}.avi`,
},
};
}
const title = engineData.metaData.title?.replace(/[^a-zA-Z0-9_]+/g, "_") || "audio";
const folder = output ? output : process.cwd();
if (!stream && !fs.existsSync(folder)) {
try {
fs.mkdirSync(folder, { recursive: true });
}
catch (mkdirError) {
throw new Error(`${colors_1.default.red("@error:")} Failed to create output directory: ${mkdirError.message}`);
}
}
const instance = (0, fluent_ffmpeg_1.default)();
try {
const paths = await (0, locator_1.locator)();
if (!paths.ffmpeg) {
throw new Error(`${colors_1.default.red("@error:")} ffmpeg executable not found.`);
}
if (!paths.ffprobe) {
throw new Error(`${colors_1.default.red("@error:")} ffprobe executable not found.`);
}
instance.setFfmpegPath(paths.ffmpeg);
instance.setFfprobePath(paths.ffprobe);
}
catch (locatorError) {
throw new Error(`${colors_1.default.red("@error:")} Failed to locate ffmpeg or ffprobe: ${locatorError.message}`);
}
if (!engineData.AudioHighF?.url) {
throw new Error(`${colors_1.default.red("@error:")} Highest quality audio URL not found.`);
}
instance.addInput(engineData.AudioHighF.url);
if (!engineData.metaData.thumbnail) {
throw new Error(`${colors_1.default.red("@error:")} Thumbnail URL not found.`);
}
instance.addInput(engineData.metaData.thumbnail);
instance.withOutputFormat("avi");
const filterMap = {
invert: ["negate"],
rotate90: ["transpose=1"],
rotate180: ["transpose=2,transpose=2"],
rotate270: ["transpose=2"],
grayscale: ["hue=s=0"],
flipVertical: ["vflip"],
flipHorizontal: ["hflip"],
};
if (filter && filterMap[filter]) {
instance.withVideoFilter(filterMap[filter]);
}
else {
instance.outputOptions("-c copy");
}
let processStartTime;
if (showProgress) {
instance.on("start", () => {
processStartTime = new Date();
});
instance.on("progress", progress => {
if (processStartTime) {
progbar({ ...progress, percent: progress.percent !== undefined ? progress.percent : 0, startTime: processStartTime });
}
});
}
if (stream) {
const passthroughStream = new stream_1.PassThrough();
const filenameBase = `yt-dlx_AudioVideoHighest_`;
let filename = `${filenameBase}${filter ? filter + "_" : ""}${title}.avi`;
passthroughStream.filename = filename;
instance.on("start", command => {
if (verbose)
console.log(colors_1.default.green("@info:"), "FFmpeg stream started:", command);
});
instance.pipe(passthroughStream, { end: true });
instance.on("end", () => {
if (verbose)
console.log(colors_1.default.green("@info:"), "FFmpeg streaming finished.");
if (showProgress)
process.stdout.write("\n");
});
instance.on("error", (error, stdout, stderr) => {
const errorMessage = `${colors_1.default.red("@error:")} FFmpeg stream error: ${error?.message}`;
console.error(errorMessage, "\nstdout:", stdout, "\nstderr:", stderr);
passthroughStream.emit("error", new Error(errorMessage));
passthroughStream.destroy(new Error(errorMessage));
if (showProgress)
process.stdout.write("\n");
});
instance.run();
if (verbose)
console.log(colors_1.default.green("@info:"), "❣️ Thank you for using yt-dlx. Consider 🌟starring the GitHub repo https://github.com/yt-dlx.");
return { stream: passthroughStream };
}
else {
const filenameBase = `yt-dlx_AudioVideoHighest_`;
let filename = `${filenameBase}${filter ? filter + "_" : ""}${title}.avi`;
const outputPath = path.join(folder, filename);
instance.output(outputPath);
await new Promise((resolve, reject) => {
instance.on("start", command => {
if (verbose)
console.log(colors_1.default.green("@info:"), "FFmpeg download started:", command);
if (showProgress)
processStartTime = new Date();
});
instance.on("progress", progress => {
if (showProgress && processStartTime) {
progbar({ ...progress, percent: progress.percent !== undefined ? progress.percent : 0, startTime: processStartTime });
}
});
instance.on("end", () => {
if (verbose)
console.log(colors_1.default.green("@info:"), "FFmpeg download finished.");
if (showProgress)
process.stdout.write("\n");
resolve();
});
instance.on("error", (error, stdout, stderr) => {
const errorMessage = `${colors_1.default.red("@error:")} FFmpeg download error: ${error?.message}`;
console.error(errorMessage, "\nstdout:", stdout, "\nstderr:", stderr);
if (showProgress)
process.stdout.write("\n");
reject(new Error(errorMessage));
});
instance.run();
});
if (verbose)
console.log(colors_1.default.green("@info:"), "❣️ Thank you for using yt-dlx. Consider 🌟starring the GitHub repo https://github.com/yt-dlx.");
return { outputPath };
}
}
catch (error) {
if (error instanceof zod_1.ZodError) {
const errorMessage = `${colors_1.default.red("@error:")} Argument validation failed: ${error.errors.map(e => `${e.path.join(".")}: ${e.message}`).join(", ")}`;
console.error(errorMessage);
throw new Error(errorMessage);
}
else if (error instanceof Error) {
console.error(error.message);
throw error;
}
else {
const unexpectedError = `${colors_1.default.red("@error:")} An unexpected error occurred: ${String(error)}`;
console.error(unexpectedError);
throw new Error(unexpectedError);
}
}
finally {
}
}
//# sourceMappingURL=Highest.js.map