234 lines
9.5 KiB
JavaScript
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
|