mono/packages/media/ref/yt-dlp/dist/cli.js

234 lines
9.5 KiB
JavaScript

#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { YtDlp } from './ytdlp.js';
import { logger } from './logger.js';
import { FormatOptionsSchema, VideoInfoOptionsSchema, DownloadOptionsSchema } from './types.js';
import { z } from 'zod';
import { exec } from 'node:child_process';
import { promisify } from 'node:util';
const execAsync = promisify(exec);
const ytdlp = new YtDlp();
// Function to generate user-friendly help message
function printHelp() {
console.log("\n======= yt-dlp TypeScript Wrapper =======");
console.log("A TypeScript wrapper for the yt-dlp video downloader\n");
console.log("USAGE:");
console.log(" ytdlp-ts <command> [options] <url>\n");
console.log("COMMANDS:");
console.log(" download [url] Download a video from the specified URL");
console.log(" info [url] Get information about a video");
console.log(" formats [url] List available formats for a video");
console.log(" tiktok:meta [url] Scrape metadata from a TikTok video and save as JSON\n");
console.log("DOWNLOAD OPTIONS:");
console.log(" --format, -f Specify video format code");
console.log(" --output, -o Specify output filename template");
console.log(" --quiet, -q Activate quiet mode");
console.log(" --verbose, -v Print various debugging information");
console.log(" --mp3 Download only the audio in MP3 format\n");
console.log("INFO OPTIONS:");
console.log(" --dump-json Output JSON information");
console.log(" --flat-playlist Flat playlist output\n");
console.log("FORMATS OPTIONS:");
console.log(" --all Show all available formats\n");
console.log("EXAMPLES:");
console.log(" ytdlp-ts download https://www.tiktok.com/@woman.power.quote/video/7476910372121970");
console.log(" ytdlp-ts download https://www.youtube.com/watch?v=_oVI0GW-Xd4 -f \"bestvideo[height<=1080]+bestaudio/best[height<=1080]\"");
console.log(" ytdlp-ts download https://www.youtube.com/watch?v=_oVI0GW-Xd4 --mp3");
console.log(" ytdlp-ts info https://www.tiktok.com/@woman.power.quote/video/7476910372121970 --dump-json");
console.log(" ytdlp-ts formats https://www.youtube.com/watch?v=_oVI0GW-Xd4 --all\n");
console.log("For more information, visit https://github.com/yt-dlp/yt-dlp");
}
/**
* Checks if ffmpeg is installed on the system
* @returns {Promise<boolean>} True if ffmpeg is installed, false otherwise
*/
async function isFFmpegInstalled() {
try {
// Try to execute ffmpeg -version command
await execAsync('ffmpeg -version');
return true;
}
catch (error) {
return false;
}
}
// Check for help flags directly in process.argv
if (process.argv.includes('--help') || process.argv.includes('-h')) {
printHelp();
process.exit(0);
}
// Create a simple yargs CLI with clear command structure
yargs(hideBin(process.argv))
.scriptName('yt-dlp-wrapper')
.usage('$0 <cmd> [args]')
.command('download [url]', 'Download a video', (yargs) => {
return yargs
.positional('url', {
type: 'string',
describe: 'URL of the video to download',
demandOption: true,
})
.option('format', {
type: 'string',
describe: 'Video format code',
alias: 'f',
})
.option('output', {
type: 'string',
describe: 'Output filename template',
alias: 'o',
})
.option('quiet', {
type: 'boolean',
describe: 'Activate quiet mode',
alias: 'q',
default: false,
})
.option('verbose', {
type: 'boolean',
describe: 'Print various debugging information',
alias: 'v',
default: false,
})
.option('mp3', {
type: 'boolean',
describe: 'Download only the audio in MP3 format',
default: false,
});
}, async (argv) => {
try {
logger.info(`Starting download process for: ${argv.url}`);
logger.debug(`Download options: mp3=${argv.mp3}, format=${argv.format}, output=${argv.output}`);
// Check if ffmpeg is installed when MP3 option is specified
if (argv.mp3) {
logger.info('MP3 option detected. Checking for ffmpeg installation...');
const ffmpegInstalled = await isFFmpegInstalled();
if (!ffmpegInstalled) {
logger.error('\x1b[31mError: ffmpeg is not installed or not found in PATH\x1b[0m');
logger.error('\nTo download videos as MP3, ffmpeg is required. Please install ffmpeg:');
logger.error('\n • Windows: https://ffmpeg.org/download.html or install via Chocolatey/Scoop');
logger.error(' • macOS: brew install ffmpeg');
logger.error(' • Linux: apt install ffmpeg / yum install ffmpeg / etc. (depending on your distribution)');
logger.error('\nAfter installing, make sure ffmpeg is in your PATH and try again.');
process.exit(1);
}
logger.info('ffmpeg is installed. Proceeding with MP3 download...');
}
// Parse and validate options using Zod
const options = DownloadOptionsSchema.parse({
format: argv.format,
output: argv.output,
quiet: argv.quiet,
verbose: argv.verbose,
audioOnly: argv.mp3 ? true : undefined,
audioFormat: argv.mp3 ? 'mp3' : undefined,
});
logger.debug(`Parsed download options: ${JSON.stringify(options)}`);
logger.info(`Starting download with options: ${JSON.stringify({
url: argv.url,
mp3: argv.mp3,
format: argv.format,
output: argv.output
})}`);
const downloadedFile = await ytdlp.downloadVideo(argv.url, options);
logger.info(`Download completed successfully: ${downloadedFile}`);
}
catch (error) {
if (error instanceof z.ZodError) {
logger.error('Invalid options:', error.errors);
}
else {
logger.error('Failed to download video:', error);
}
process.exit(1);
}
})
.command('info [url]', 'Get video information', (yargs) => {
return yargs
.positional('url', {
type: 'string',
describe: 'URL of the video',
demandOption: true,
})
.option('dump-json', {
type: 'boolean',
describe: 'Output JSON information',
default: false,
})
.option('flat-playlist', {
type: 'boolean',
describe: 'Flat playlist output',
default: false,
});
}, async (argv) => {
try {
logger.info(`Starting info retrieval for: ${argv.url}`);
// Parse and validate options using Zod
const options = VideoInfoOptionsSchema.parse({
dumpJson: argv.dumpJson,
flatPlaylist: argv.flatPlaylist,
});
const info = await ytdlp.getVideoInfo(argv.url, options);
logger.info(`Info retrieval completed successfully`);
console.log(JSON.stringify(info, null, 2));
}
catch (error) {
if (error instanceof z.ZodError) {
logger.error('Invalid options:', error.errors);
}
else {
logger.error('Failed to get video info:', error);
}
process.exit(1);
}
})
.command('formats [url]', 'List available formats of a video', (yargs) => {
return yargs
.positional('url', {
type: 'string',
describe: 'URL of the video',
demandOption: true,
})
.option('all', {
type: 'boolean',
describe: 'Show all available formats',
default: false,
});
}, async (argv) => {
try {
logger.info(`Getting available formats for video: ${argv.url}`);
// Parse and validate options using Zod
const options = FormatOptionsSchema.parse({
all: argv.all,
});
const formats = await ytdlp.listFormats(argv.url, options);
logger.info(`Format listing completed successfully`);
console.log(formats);
}
catch (error) {
if (error instanceof z.ZodError) {
logger.error('Invalid options:', error.errors);
}
else {
logger.error('Failed to list formats:', error);
}
process.exit(1);
}
})
.example('$0 download https://www.tiktok.com/@woman.power.quote/video/7476910372121971970', 'Download a TikTok video with default settings')
.example('$0 download https://www.youtube.com/watch?v=_oVI0GW-Xd4 -f "bestvideo[height<=1080]+bestaudio/best[height<=1080]"', 'Download a YouTube video in 1080p or lower quality')
.example('$0 download https://www.youtube.com/watch?v=_oVI0GW-Xd4 --mp3', 'Extract and download only the audio in MP3 format')
.example('$0 info https://www.tiktok.com/@woman.power.quote/video/7476910372121971970 --dump-json', 'Retrieve and display detailed video metadata in JSON format')
.example('$0 formats https://www.youtube.com/watch?v=_oVI0GW-Xd4 --all', 'List all available video and audio formats for a YouTube video')
.example('$0 tiktok:meta https://www.tiktok.com/@username/video/1234567890 -o metadata.json', 'Scrape metadata from a TikTok video and save it to metadata.json')
.demandCommand(1, 'You need to specify a command')
.strict()
.help()
.alias('h', 'help')
.version()
.alias('V', 'version')
.wrap(800) // Fixed width value instead of yargs.terminalWidth() which isn't compatible with ESM
.showHelpOnFail(true)
.parse();
//# sourceMappingURL=cli.js.map