114 lines
3.6 KiB
JavaScript
Executable file
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;
|
|
}
|