mr-chamuwa-downloader
Version:
š„ MrChamuwa's Ultimate Facebook Video Downloader - The most powerful FB video downloading tool in Sri Lanka!
357 lines (308 loc) ⢠12.2 kB
JavaScript
const { Command } = require('commander');
const inquirer = require('inquirer');
const chalk = require('chalk');
const ora = require('ora');
const boxen = require('boxen');
const figlet = require('figlet');
const gradient = require('gradient-string');
const updateNotifier = require('update-notifier');
const pkg = require('../package.json');
const MrChamuwaFBDownloader = require('../src/index.js');
// Check for updates
const notifier = updateNotifier({ pkg });
notifier.notify();
// MrChamuwa's signature banner
const createBanner = () => {
const title = figlet.textSync('MrChamuwa', {
font: 'Slant',
horizontalLayout: 'fitted'
});
const subtitle = 'FB DOWNLOADER š„';
return boxen(
gradient.pastel.multiline(title) + '\n' +
chalk.yellow.bold(subtitle) + '\n' +
chalk.cyan('The Ultimate Facebook Video Downloader š±š°') + '\n' +
chalk.gray('Made with ā¤ļø by MrChamuwa'),
{
padding: 1,
margin: 1,
borderStyle: 'double',
borderColor: 'cyan',
backgroundColor: '#000040'
}
);
};
const program = new Command();
program
.name('chamuwa-fb')
.description(createBanner() + '\n\nš Download Facebook videos like a pro!')
.version(pkg.version);
// Interactive download command
program
.command('download')
.alias('dl')
.description('šÆ Download Facebook video (Interactive Mode)')
.action(async () => {
console.log(createBanner());
try {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'url',
message: chalk.yellow('š Facebook Video URL ą¶ą¶ paste ą¶ą¶»ą¶±ą·ą¶±:'),
validate: (input) => {
if (!input.trim()) return 'URL ą¶ą¶ą¶ą· enter ą¶ą¶»ą¶±ą·ą¶±!';
if (!input.includes('facebook.com') && !input.includes('fb.watch')) {
return 'Valid Facebook URL ą¶ą¶ą¶ą· enter ą¶ą¶»ą¶±ą·ą¶±!';
}
return true;
}
},
{
type: 'list',
name: 'quality',
message: chalk.yellow('š„ Video Quality:'),
choices: [
{ name: 'š„ HD Quality (High)', value: 'hd' },
{ name: 'š± SD Quality (Standard)', value: 'sd' },
{ name: 'š¤ Auto Select', value: 'auto' }
],
default: 'hd'
},
{
type: 'input',
name: 'filename',
message: chalk.yellow('š Custom filename (optional):'),
default: ''
},
{
type: 'list',
name: 'language',
message: chalk.yellow('š Language / ą¶·ą·ą·ą·:'),
choices: [
{ name: 'š±š° Sinhala (ą·ą·ą¶ą·ą¶½)', value: 'si' },
{ name: 'šŗšø English', value: 'en' }
],
default: 'si'
}
]);
// Initialize downloader
const downloader = new MrChamuwaFBDownloader({
quality: answers.quality,
language: answers.language,
saveMetadata: true,
createSubfolders: true
});
const spinner = ora({
text: answers.language === 'si' ? 'š Video ą¶ą¶ analyze ą¶ą¶»ą¶±ą·ą·...' : 'š Analyzing video...',
spinner: 'dots12'
}).start();
// Set up progress tracking
let progressSpinner = null;
downloader.on('progress', (data) => {
if (!progressSpinner) {
spinner.stop();
progressSpinner = ora().start();
}
const percent = data.progress.toFixed(1);
const speed = (data.speed / 1024 / 1024).toFixed(2);
const downloaded = (data.downloaded / 1024 / 1024).toFixed(2);
const total = data.total > 0 ? (data.total / 1024 / 1024).toFixed(2) : '??';
progressSpinner.text = `ā¬ļø ${percent}% | ${downloaded}/${total} MB | ${speed} MB/s`;
});
// Download the video
const result = await downloader.downloadVideo(
answers.url,
answers.filename || null
);
if (progressSpinner) progressSpinner.stop();
// Success message
console.log('\n' + boxen(
chalk.green.bold('ā
DOWNLOAD COMPLETED! š\n\n') +
chalk.cyan(`š File: ${result.fileName}\n`) +
chalk.cyan(`š Location: ${result.filePath}\n`) +
chalk.cyan(`š Size: ${(result.size / 1024 / 1024).toFixed(2)} MB\n`) +
chalk.cyan(`ā±ļø Time: ${(result.downloadTime / 1000).toFixed(1)}s\n`) +
chalk.yellow('š Thank you for using MrChamuwa FB Downloader!'),
{
padding: 1,
borderStyle: 'round',
borderColor: 'green'
}
));
// Show video info if available
if (result.videoInfo.title) {
console.log(chalk.blue(`š¬ Title: ${result.videoInfo.title}`));
}
} catch (error) {
if (spinner && spinner.isSpinning) spinner.stop();
console.log('\n' + boxen(
chalk.red.bold('ā DOWNLOAD FAILED!\n\n') +
chalk.yellow(`Error: ${error.message}\n\n`) +
chalk.cyan('š” Tips:\n') +
chalk.white('⢠Make sure the Facebook video is public\n') +
chalk.white('⢠Check your internet connection\n') +
chalk.white('⢠Try with a different video URL'),
{
padding: 1,
borderStyle: 'round',
borderColor: 'red'
}
));
}
});
// Quick download command
program
.command('quick <url>')
.alias('q')
.description('ā” Quick download (no prompts)')
.option('-q, --quality <quality>', 'Video quality (hd/sd)', 'hd')
.option('-o, --output <filename>', 'Output filename')
.option('-l, --lang <language>', 'Language (si/en)', 'si')
.action(async (url, options) => {
console.log(createBanner());
const spinner = ora('š Starting quick download...').start();
try {
const downloader = new MrChamuwaFBDownloader({
quality: options.quality,
language: options.lang
});
const result = await downloader.downloadVideo(url, options.output);
spinner.succeed(chalk.green('ā
Quick download completed!'));
console.log(chalk.cyan(`š Saved: ${result.filePath}`));
console.log(chalk.yellow(`š Size: ${(result.size / 1024 / 1024).toFixed(2)} MB`));
} catch (error) {
spinner.fail(chalk.red(`ā Quick download failed: ${error.message}`));
}
});
// Batch download command
program
.command('batch <file>')
.alias('b')
.description('š Batch download from URL list file')
.option('-c, --concurrent <number>', 'Concurrent downloads', '2')
.action(async (file, options) => {
console.log(createBanner());
const spinner = ora('š Loading URL list...').start();
try {
const fs = require('fs-extra');
const urls = (await fs.readFile(file, 'utf8'))
.split('\n')
.filter(url => url.trim())
.map(url => url.trim());
if (urls.length === 0) {
throw new Error('No URLs found in file');
}
spinner.text = `š Starting batch download of ${urls.length} videos...`;
const downloader = new MrChamuwaFBDownloader({
createSubfolders: true,
saveMetadata: true
});
let completed = 0;
let failed = 0;
// Process URLs with concurrency control
const concurrency = parseInt(options.concurrent) || 2;
for (let i = 0; i < urls.length; i += concurrency) {
const batch = urls.slice(i, i + concurrency);
const promises = batch.map(async (url, index) => {
try {
const result = await downloader.downloadVideo(url);
completed++;
spinner.text = `ā
${completed}/${urls.length} completed | Current: ${result.fileName}`;
return { success: true, url, result };
} catch (error) {
failed++;
spinner.text = `ā ${failed} failed, ${completed}/${urls.length} completed`;
return { success: false, url, error: error.message };
}
});
await Promise.all(promises);
}
spinner.succeed(`š Batch download completed! ${completed} success, ${failed} failed`);
} catch (error) {
spinner.fail(chalk.red(`ā Batch download failed: ${error.message}`));
}
});
// Info command
program
.command('info <url>')
.alias('i')
.description('ā¹ļø Get video information without downloading')
.action(async (url) => {
console.log(createBanner());
const spinner = ora('š Getting video information...').start();
try {
const downloader = new MrChamuwaFBDownloader();
const videoInfo = await downloader.extractVideoInfo(url);
spinner.succeed('ā¹ļø Video information extracted!');
console.log('\n' + boxen(
chalk.blue.bold('š VIDEO INFORMATION\n\n') +
chalk.cyan(`š¬ Title: ${videoInfo.title || 'N/A'}\n`) +
chalk.cyan(`š¤ Uploader: ${videoInfo.uploader || 'N/A'}\n`) +
chalk.cyan(`š„ Quality: ${videoInfo.quality || 'N/A'}\n`) +
chalk.cyan(`ā±ļø Duration: ${videoInfo.duration ? `${videoInfo.duration}s` : 'N/A'}\n`) +
chalk.cyan(`š Description: ${videoInfo.description ? videoInfo.description.substring(0, 100) + '...' : 'N/A'}\n`) +
chalk.cyan(`š Thumbnail: ${videoInfo.thumbnailUrl ? 'Available' : 'N/A'}\n`) +
chalk.cyan(`š
Method: ${videoInfo.extractionMethod || 'N/A'}`),
{
padding: 1,
borderStyle: 'round',
borderColor: 'blue'
}
));
} catch (error) {
spinner.fail(chalk.red(`ā Failed to get video info: ${error.message}`));
}
});
// Stats command
program
.command('stats')
.alias('s')
.description('š Show download statistics')
.action(() => {
console.log(createBanner());
// This would typically read from a stats file
console.log(boxen(
chalk.green.bold('š DOWNLOAD STATISTICS\n\n') +
chalk.cyan('šÆ Total Downloads: 0\n') +
chalk.cyan('ā Failed Downloads: 0\n') +
chalk.cyan('š Success Rate: 0%\n') +
chalk.cyan('š¾ Total Size: 0 MB\n') +
chalk.cyan('ā±ļø Total Time: 0s\n\n') +
chalk.yellow('Start downloading to see stats!'),
{
padding: 1,
borderStyle: 'round',
borderColor: 'green'
}
));
});
// Help command with MrChamuwa style
program
.command('help-si')
.description('š±š° Help in Sinhala (ą·ą·ą¶ą·ą¶½ ą¶ą¶Æą·ą·)')
.action(() => {
console.log(createBanner());
console.log(boxen(
chalk.yellow.bold('š±š° MrChamuwa FB Downloader - ą·ą·ą¶ą·ą¶½ ą¶ą¶Æą·ą·\n\n') +
chalk.cyan('š Commands:\n\n') +
chalk.white('chamuwa-fb download ') + chalk.gray('- Interactive download\n') +
chalk.white('chamuwa-fb quick <url> ') + chalk.gray('- Quick download\n') +
chalk.white('chamuwa-fb batch <file> ') + chalk.gray('- Batch download\n') +
chalk.white('chamuwa-fb info <url> ') + chalk.gray('- Video info\n') +
chalk.white('chamuwa-fb stats ') + chalk.gray('- Statistics\n\n') +
chalk.green('šÆ Usage Example:\n') +
chalk.white('chamuwa-fb download\n') +
chalk.white('chamuwa-fb quick "https://facebook.com/video-url"\n\n') +
chalk.red('ā ļø Important:\n') +
chalk.yellow('⢠Video public ą·ą·ą¶ŗ ą¶ŗą·ą¶ą·ą¶ŗą·\n') +
chalk.yellow('⢠Internet connection ą·ą·ą¶³ ą·ą·ą¶ŗ ą¶ŗą·ą¶ą·ą¶ŗą·\n') +
chalk.yellow('⢠Facebook URL ą¶ą¶ valid ą·ą·ą¶ŗ ą¶ŗą·ą¶ą·ą¶ŗą·'),
{
padding: 1,
borderStyle: 'round',
borderColor: 'yellow'
}
));
});
program.parse();