gdd/cli.js
2022-08-07 04:22:44 -04:00

238 lines
6.7 KiB
JavaScript
Executable file

#!/usr/bin/node
const axios = require("axios");
const fs = require("fs");
const progress = require("cli-progress");
const lib = require("./lib");
let args;
if (process.argv[0].includes("node")) {
args = require("minimist")(process.argv.slice(2));
} else {
args = require("minimist")(process.argv.slice(1));
}
if (args._.length == 0) {
console.log(`gdd - google drive downloader`);
console.log(`source code: https://git.gay/a/gdd`);
console.log(`licensed under the unlicense.`);
return;
}
switch(args._[0]) {
case "captcha":
if (args._[1] && args._[2]) {
let solver = args._[1];
let key = args._[2];
console.log("Setting captcha solver to", solver);
console.log("Setting captcha solver key to", key);
lib.setConfig("captcha", {
solver: solver,
key: key
});
console.log("- Completed!");
} else {
console.log("ERR! Usage: gdd captcha <solver name> <solver key>");
}
return;
case "config":
switch(args._[1]) {
case "location":
console.log(`${__dirname}/config.json`);
return;
case "print":
console.log(JSON.stringify(lib.getConfig(), null, 2));
return;
default:
console.log("ERR! Usage: gdd <print|location>");
return;
}
return;
}
args._.forEach(function(e) {
if (!lib.isDriveLink(e)) {
console.log(`ERR! "${e}" is not a valid Google Drive link.`)
return;
}
});
let downloads = args._;
delete args._;
begin(downloads, args);
async function begin(array, args) {
if (!args) args = {};
try {
for (let a in array) {
console.log(`Fetching metadata for "${array[a]}"...`);
let meta = await lib.fetchMetadata(array[a], "", args);
if (meta.type == "file") {
let dl;
try {
dl = await lib.fetchDownloadLink(meta.id, args);
} catch(err) {
if (err.code == "LT100") {
if (args["dry-run"] == true) {
console.log(`Got download URL for "${meta.name}": `, err.url);
continue;
} else {
await download(err.url, meta.name, (args.dest || args.destination));
console.log(`- Download is complete!`);
continue;
}
} else throw err;
}
if (args["dry-run"] == true) {
console.log(`Got download URL for "${dl.name}": `, dl.url);
continue;
} else {
await download(dl, meta.name, (args.dest || args.destination));
console.log(`- Download is complete!`);
continue;
}
} else {
if (lib.isDebug(args)) console.log(`[DEBUG] Got data`, meta);
meta = await lib.flattenFolder(meta, "", args);
console.log("Finished retrieving metadata, initiating download...");
await downloadFolder(meta.files, 0, args);
console.log(`- Downloads from "${array[a]}" are complete!`);
continue;
}
}
process.exit();
} catch(err) {
console.log(`ERR!`, (err.stack || err.message || err));
process.exit(1);
}
}
async function downloadFolder(array, index, args) {
if (!index) index = 0;
if (array[index]) {
console.log(`\nStarting download ${(index + 1)} of ${array.length} ("${array[index].name}")`);
if (lib.isDebug(args) == true) console.log(`[DEBUG] Fetching download link...`);
let dl;
try {
dl = await lib.fetchDownloadLink(array[index].id, args);
} catch(err) {
if (err.code == "LT100") {
console.log(`Got download URL for "${array[index].name}": `, err.url);
await download(err.url, array[index].name, args.destination);
return (await downloadFolder(array, (index + 1), args));
} else {
console.log(`ERR! Skipped download due to error:`, (err.message || err.code || err));
console.log(`Waiting 10 seconds to retry...`);
await new Promise(function(resolve, reject) {setTimeout(function() {resolve()}, 10000)});
return (await downloadFolder(array, index, args));
}
}
if (lib.isDebug(args) == true) console.log(`[DEBUG] Got download link, sending to download function....`);
let downloadPath = ``;
if (args.dest || args.destination) {
let dest = (args.dest || args.destination);
if (!dest.endsWith("/")) dest = `${dest}/`;
if (array[index].path) downloadPath = `${dest}${array[index].path}`;
}
try {
await download(dl, array[index].name, downloadPath);
return (await downloadFolder(array, (index + 1), args));
} catch(err) {
console.log(`ERR! Skipped download due to error:`, (err.message || err.code || err));
console.log(`Waiting 10 seconds to retry...`);
await new Promise(function(resolve, reject) {setTimeout(function() {resolve()}, 10000)});
return (await downloadFolder(array, (index + 1), args));
}
} else return true;
}
async function download(url, name, destination) {
if (!destination || destination == "" || destination == "./") destination = `./${name}`;
else if (destination) {
let e = destination.split("/");
for (let a in e) {
if (a == 0) continue;
let f = e.slice(0, a).join("/");
if (!fs.existsSync(f)) fs.mkdirSync(f);
}
if (!e[e.length - 1].includes(".")) {
if (destination.endsWith("/")) destination = `${destination}${name}`;
else destination = `${destination}/${name}`;
} else {
name = e[e.length - 1];
}
}
let speed;
let header = lib.defaultHeaders;
let string = ``;
if (args["cookies"]) {
let cookie = await getCookies(args["cookies"], "drive.google.com");
for (let a in cookie) {
string = `${string} ${cookie[a].name}=${cookie[a].value};`;
}
header.cookie = string.substring(1, (string.length - 1));
}
let {data, headers} = await axios({
url: url,
responseType: "stream",
headers: header
});
let size = parseInt(headers["content-length"]);
if (name.length >= 30) name = `${name.substring(0, 27)}...`;
let pb = new progress.SingleBar({
format: `${name} {bar} {speed}`,
barCompleteChar: `\u2588`,
barIncompleteChar: `\u2591`
});
pb.start(size, 0);
data.on("data", function(chunk) {
if (!speed) speed = chunk.length;
else speed = speed + chunk.length;
let s;
if (speed !== 0) s = bytesToSize(speed);
if (!s) pb.increment(chunk.length)
else pb.increment(chunk.length, {speed: s});
});
setInterval(function() {
speed = 0;
}, 1000);
data.pipe(fs.createWriteStream(destination));
return (await lib.returnWhenDone(data, pb));
}
function bytesToSize(bytes) {
var sizes = ['B/s', 'KB/s', 'MB/s', 'GB/s', 'TB/s'];
if (bytes == 0) return '0 Byte';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
}