This repository has been archived on 2025-09-04. You can view files and clone it, but cannot push or open issues or pull requests.
animevibe-cli/index.js
2020-10-24 17:20:11 +02:00

114 lines
3.6 KiB
JavaScript
Executable file

#!/usr/bin/env node
const path = require('path');
const fs = require('fs');
const yargs = require('yargs');
const { spawnSync } = require('child_process');
const {
scrapeAnimevibeDownloadPage,
scrapeVidCDNDownloadPage,
scrapeAnimevibeSeriesPage,
} = require('./scrape.js');
const argv = yargs
.command('download <urls...>', 'Download one or more episodes', yargs =>
yargs.positional('urls', {
describe: 'List of urls to download from',
})
.option('start-episode', {
alias: 's',
type: 'number',
describe: 'First episode to download. Default is current episode',
})
.option('end-episode', {
alias: 'e',
type: 'number',
describe: 'Last episode to download. Default is current episode',
})
.option('all-episodes', {
alias: 'a',
type: 'boolean',
describe: 'Download all available episodes',
})
.option('sleep', {
alias: 'S',
type: 'number',
describe: 'Seconds to sleep after each download',
}),
download
)
.help().alias('help', 'h')
.demandCommand()
.strict()
.argv;
async function download(options) {
for (const url of options.urls) {
const info = await scrapeAnimevibeSeriesPage(url);
let startEpisode;
let endEpisode;
if (options.allEpisodes) {
startEpisode = 1;
endEpisode = info.episodeCount;
} else {
startEpisode = options.startEpisode || info.currentEpisodeNumber || 1;
endEpisode = options.endEpisode || info.currentEpisodeNumber || 1;
}
for (let i = startEpisode; i <= endEpisode; i++) {
console.log(`Downloading '${info.titles.english}' episode ${i}`);
const downloadInfo = await getDownloadOptions(info.downloadId, i);
const download = getBestDownload(downloadInfo.downloads);
const filename = getOutputFilename(info, download, pad(i, 4));
console.log(`Saving to ${filename}`);
fs.mkdirSync(path.dirname(filename), { recursive: true });
spawnSync('wget', ['--output-document', filename, download.url], { stdio: 'inherit' });
if (i < endEpisode && options.sleep) {
console.log(`Sleeping for ${options.sleep} seconds`);
await sleep(options.sleep*1000);
}
}
}
}
function getOutputFilename(seriesInfo, download, episode) {
const basename = `${seriesInfo.titles.english}-${seriesInfo.titles.japanese}`;
const filename = seriesInfo.type.startsWith('Movie')
? `${basename}.${download.format}`
: `${basename}/${episode}.${download.format}`;
return filename
.replace(/ /g, '_')
.replace(/[^\/\w\d-_\.]/g, '');
}
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
function getBestDownload(downloads) {
downloads.sort(flip(comparing(d => d.quality)));
return downloads[0];
}
async function getDownloadOptions(downloadId, episode) {
const url = `https://animevibe.tv/anime-downloader/?id=${downloadId}&episode=${episode}`;
const mirrors = await scrapeAnimevibeDownloadPage(url);
return await scrapeVidCDNDownloadPage(mirrors.vidcdn);
}
function sleep(millis) {
return new Promise(resolve => setTimeout(resolve, millis));
}
function flip(f) {
return (a, b) => f(b, a);
}
function comparing(key) {
return (a, b) => key(a) > key(b) ? 1 : -1;
}