duck-duck-scrape
Version:
Search from DuckDuckGo and use it's spice APIs.
126 lines (125 loc) • 5.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.searchVideos = exports.VideoLicense = exports.VideoDuration = exports.VideoDefinition = void 0;
const html_entities_1 = require("html-entities");
const needle_1 = __importDefault(require("needle"));
const util_1 = require("../util");
/** The types of video definition. */
var VideoDefinition;
(function (VideoDefinition) {
/** Any definition. */
VideoDefinition["ANY"] = "";
/** High definition. */
VideoDefinition["HIGH"] = "high";
/** Standard definition. */
VideoDefinition["STANDARD"] = "standard";
})(VideoDefinition = exports.VideoDefinition || (exports.VideoDefinition = {}));
/** The types of video duration. */
var VideoDuration;
(function (VideoDuration) {
/** Any video duration. */
VideoDuration["ANY"] = "";
/** Short videos, shorter than ~5 minutes. */
VideoDuration["SHORT"] = "short";
/** Medium length videos, between 5 and 20 minutes. */
VideoDuration["MEDIUM"] = "medium";
/** Long videos, longer than 20 minutes. */
VideoDuration["LONG"] = "long";
})(VideoDuration = exports.VideoDuration || (exports.VideoDuration = {}));
/** The types of video licenses. */
var VideoLicense;
(function (VideoLicense) {
/** Any video license. */
VideoLicense["ANY"] = "";
/** Creative Commons license. */
VideoLicense["CREATIVE_COMMONS"] = "creativeCommon";
/** YouTube Standard license. */
VideoLicense["YOUTUBE"] = "youtube";
})(VideoLicense = exports.VideoLicense || (exports.VideoLicense = {}));
const defaultOptions = {
safeSearch: util_1.SafeSearchType.OFF,
locale: 'en-us',
offset: 0
};
/**
* Search videos.
* @category Search
* @param query The query to search with
* @param options The options of the search
* @param needleOptions The options of the HTTP request
* @returns Search results
*/
async function searchVideos(query, options, needleOptions) {
if (!query)
throw new Error('Query cannot be empty!');
if (!options)
options = defaultOptions;
else
options = sanityCheck(options);
let vqd = options.vqd;
if (!vqd)
vqd = await (0, util_1.getVQD)(query, 'web', needleOptions);
const filters = [
options.time && options.time !== 'a' ? `publishedAfter:${options.time}` : '',
options.definition ? `videoDefinition:${options.definition}` : '',
options.duration ? `videoDuration:${options.duration}` : '',
options.license ? `videoLicense:${options.license}` : ''
];
const queryObject = {
l: options.locale,
o: 'json',
q: query,
vqd,
p: options.safeSearch === 0 ? '1' : String(options.safeSearch),
f: filters.toString(),
s: String(options.offset || 0)
};
const response = await (0, needle_1.default)('get', `https://duckduckgo.com/v.js?${(0, util_1.queryString)(queryObject)}`, needleOptions || { headers: util_1.COMMON_HEADERS });
if (response.statusCode === 403)
throw new Error('A server error occurred!');
const videosResult = (0, util_1.ensureJSON)(response.body);
return {
noResults: !videosResult.results.length,
vqd,
results: videosResult.results.map((video) => ({
url: video.content,
title: (0, html_entities_1.decode)(video.title),
description: (0, html_entities_1.decode)(video.description),
image: video.images.large || video.images.medium || video.images.small || video.images.motion,
duration: video.duration,
publishedOn: video.publisher,
published: video.published,
publisher: video.uploader,
viewCount: video.statistics.viewCount || undefined
}))
};
}
exports.searchVideos = searchVideos;
function sanityCheck(options) {
options = Object.assign({}, defaultOptions, options);
if (!(options.safeSearch in util_1.SafeSearchType))
throw new TypeError(`${options.safeSearch} is an invalid safe search type!`);
if (typeof options.safeSearch === 'string')
// @ts-ignore
options.safeSearch = util_1.SafeSearchType[options.safeSearch];
if (typeof options.offset !== 'number')
throw new TypeError(`Search offset is not a number!`);
if (options.offset < 0)
throw new RangeError('Search offset cannot be below zero!');
if (!options.locale || typeof options.locale !== 'string')
throw new TypeError('Search locale must be a string!');
if (options.time && !Object.values(util_1.SearchTimeType).includes(options.time))
throw new TypeError(`${options.time} is an invalid time filter!`);
if (options.definition && !Object.values(VideoDefinition).includes(options.definition))
throw new TypeError(`${options.definition} is an invalid video definition!`);
if (options.duration && !Object.values(VideoDuration).includes(options.duration))
throw new TypeError(`${options.duration} is an invalid video duration!`);
if (options.license && !Object.values(VideoLicense).includes(options.license))
throw new TypeError(`${options.license} is an invalid video license!`);
if (options.vqd && !/\d-\d+-\d+/.test(options.vqd))
throw new Error(`${options.vqd} is an invalid VQD!`);
return options;
}