ddg-bulk-image-downloader
Version:
Lazy way to download images from Duck Duck Go search results in bulk
94 lines (93 loc) • 4.02 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.downloadImages = void 0;
const node_fetch_1 = require("node-fetch");
const download_image_1 = require("./download-image");
const path = require("path");
const fs_extra_1 = require("fs-extra");
const cli_ux_1 = require("cli-ux");
const utils_1 = require("./utils");
const filterOptionsToParam_1 = require("./filterOptionsToParam");
const ROOT_URL = "https://duckduckgo.com";
async function getToken(query) {
try {
const response = await node_fetch_1.default(`${ROOT_URL}/?q=${encodeURIComponent(query)}`).then((t) => t.text());
const regex = /vqd='(.*?)'/gm;
const payload = regex.exec(response);
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
return payload[1];
}
catch (error) {
throw new Error("Failed to get token!" + error);
}
}
async function downloadImages({ query, limit, filter = () => true, imageOptions, outputPath = "", debug, }) {
cli_ux_1.default.action.start("Initializing download", "", { stdout: true });
const token = await getToken(query);
const tokenQueryParamObj = { vqd: token };
const queryParamString = utils_1.objToQueryString(Object.assign({ o: "json", l: "wt-wt", f: filterOptionsToParam_1.filterOptionsToParam(imageOptions), q: encodeURIComponent(query) }, tokenQueryParamObj));
const url = `${ROOT_URL}/i.js?${queryParamString}`;
let response;
// Ensure output folder is created
fs_extra_1.ensureDirSync(outputPath);
let count = 0;
let page = 1;
const failed = [];
while (count - failed.length < limit) {
// console.log("Next:", response.next);
let nextUrl = url;
if (response === null || response === void 0 ? void 0 : response.next) {
++page;
if (debug) {
console.log("Going to page", page);
}
nextUrl = `${ROOT_URL}/${response.next}&${utils_1.objToQueryString(tokenQueryParamObj)}`;
}
console.log("Fetching", nextUrl);
response = (await node_fetch_1.default(nextUrl)
.then((t) => {
console.log("Status", t.status);
return t.json();
})
.catch((e) => {
console.error(e);
return undefined;
}));
const effectiveLimit = limit - (count - failed.length);
const filteredImage = response.results.filter(filter);
const toBeDownloadedImages = filteredImage.slice(0, Math.min(effectiveLimit, filteredImage.length));
cli_ux_1.default.action.start(`Downloading ${toBeDownloadedImages.length} images from`, "page :" + page.toString(), { stdout: true });
// Method 1 : Doesn't uses parallel downloadImage capabilities, but ensures all files present.
// for (let i = 0; i < filteredImage.length; i++) {
// try {
// await downloadImage(filteredImage[i].image, `${outputPath + query}_${count++}`)
// } catch (error) {
// count--
// console.error('FFFF', count)
// }
// }
// count += filteredImage.length
// Method 2 : Could leave holes, when URl returns non success. But fast.
await Promise.all(toBeDownloadedImages.map(async (item) => {
try {
const savePath = path.join(outputPath, `${query}_${count++}`);
// console.log(savePath)
await download_image_1.default(item.image, savePath);
// console.log("Success", count)
}
catch (error) {
if (debug) {
console.error(error.message);
}
failed.push(error);
}
finally {
// eslint-disable-next-line no-unsafe-finally
return Promise.resolve();
}
}));
}
cli_ux_1.default.action.stop("done");
}
exports.downloadImages = downloadImages;