Skip to content

Commit

Permalink
refactor: migrate to undici
Browse files Browse the repository at this point in the history
  • Loading branch information
skick1234 committed Sep 22, 2023
1 parent 77cdd1b commit f0a310c
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 91 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Searches for the given string
* safeSearch[Boolean] -> pull items in youtube restriction mode.
* limit[integer] -> limits the pulled items, defaults to 100, set to Infinity to get the whole list of search results - numbers <1 result in the default being used
* type[String] -> filter for a specific type of item, defaults to `video` - possible values: `video`, `playlist`
* requestOptions[Object] -> Additional parameters to passed to [miniget](https://github.com/fent/node-miniget), which is used to do the https requests
* requestOptions[Object] -> Additional parameters to passed to undici's [request options](https://github.com/nodejs/undici#undicirequesturl-options-promise), which is used to do the https requests

* returns a Promise
* [Example response](#example-response)
Expand Down
4 changes: 2 additions & 2 deletions lib/main.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const PARSE_ITEM = require('./parseItem.js');
const MINIGET = require('miniget');
const { request } = require('undici');
const UTIL = require('./util.js');
const QS = require('querystring');

Expand Down Expand Up @@ -29,7 +29,7 @@ const main = module.exports = async (searchString, options, rt = 3) => {
const ref = BASE_SEARCH_URL + QS.encode(opts.query);
let parsed = {};
if (!opts.safeSearch || !CACHE.has('apiKey') || !CACHE.has('clientVersion') || !CACHE.has('playlistParams')) {
const body = await MINIGET(ref, opts.requestOptions).text();
const body = await request(ref, opts.requestOptions).then(r => r.body.text());
parsed = UTIL.parseBody(body, opts);
let plParams = UTIL.betweenFromRight(body, `"params":"`, '"}},"tooltip":"Search for Playlist"');
if (plParams) CACHE.set('playlistParams', plParams);
Expand Down
109 changes: 22 additions & 87 deletions lib/util.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const MINIGET = require('miniget');
const { request } = require('undici');

const BASE_URL = 'https://www.youtube.com/';
const DEFAULT_OPTIONS = { limit: 100, safeSearch: false };
const DEFAULT_OPTIONS = { limit: 10, safeSearch: false };
const DEFAULT_QUERY = { gl: 'US', hl: 'en' };
const DEFAULT_CONTEXT = {
client: {
Expand All @@ -14,10 +14,25 @@ const DEFAULT_CONTEXT = {
user: {},
request: {},
};
const CONSENT_COOKIE = 'SOCS=CAE';
const CONSENT_COOKIE = 'SOCS=CAI';

const tryParseBetween = (body, left, right, addEndCurly = false) => {
try {
let data = between(body, left, right);
if (!data) return null;
if (addEndCurly) data += '}';
return JSON.parse(data);
} catch (e) {
return null;
}
};

exports.parseBody = (body, options = {}) => {
let json = jsonAfter(body, 'var ytInitialData = ') || jsonAfter(body, 'window["ytInitialData"] = ') || null;
const json =
tryParseBetween(body, 'var ytInitialData = ', '};', true) ||
tryParseBetween(body, 'window["ytInitialData"] = ', '};', true) ||
tryParseBetween(body, 'var ytInitialData = ', ';</script>') ||
tryParseBetween(body, 'window["ytInitialData"] = ', ';</script>');
const apiKey = between(body, 'INNERTUBE_API_KEY":"', '"') || between(body, 'innertubeApiKey":"', '"');
const clientVersion =
between(body, 'INNERTUBE_CONTEXT_CLIENT_VERSION":"', '"') ||
Expand Down Expand Up @@ -46,15 +61,10 @@ const parseText = exports.parseText = txt =>
exports.parseIntegerFromText = x => typeof x === 'string' ? Number(x) : Number(parseText(x).replace(/\D+/g, ''));

// Request Utility
exports.doPost = async (url, opts, payload) => {
// Enforce POST-Request
exports.doPost = (url, opts, payload) => {
if (!opts) opts = {};
const reqOpts = Object.assign({}, opts, { method: 'POST' });
const req = MINIGET(url, reqOpts);
// Write request body
if (payload) req.once('request', r => r.write(JSON.stringify(payload)));
// Await response-text and parse json
return JSON.parse(await req.text());
const reqOpts = Object.assign({}, opts, { method: 'POST', body: JSON.stringify(payload) });
return request(url, reqOpts).then(r => r.body.json());
};

// Guarantee that all arguments are valid
Expand Down Expand Up @@ -165,81 +175,6 @@ exports.betweenFromRight = (haystack, left, right) => {
return haystack;
};

/**
* Extract json after given string.
* loosely based on utils#between
*
* @param {string} haystack haystack
* @param {string} left left
* @returns {Object|null} the parsed json or null
*/
const jsonAfter = (haystack, left) => {
try {
const pos = haystack.indexOf(left);
if (pos === -1) {
return null;
}
haystack = haystack.slice(pos + left.length);
return JSON.parse(cutAfterJSON(haystack));
} catch (e) {
return null;
}
};

/**
* Match begin and end braces of input JSON, return only json
* Property of https://github.com/fent/node-ytdl-core/blob/master/lib/utils.js
*
* @param {string} mixedJson mixedJson
* @returns {string}
* @throws {Error} no json or invalid json
*/
const cutAfterJSON = exports.cutAfterJSON = mixedJson => {
let open, close;
if (mixedJson[0] === '[') {
open = '[';
close = ']';
} else if (mixedJson[0] === '{') {
open = '{';
close = '}';
}

if (!open) {
throw new Error(`Can't cut unsupported JSON (need to begin with [ or { ) but got: ${mixedJson[0]}`);
}

// States if the loop is currently in a string
let isString = false;

// Current open brackets to be closed
let counter = 0;

let i;
for (i = 0; i < mixedJson.length; i++) {
// Toggle the isString boolean when leaving/entering string
if (mixedJson[i] === '"' && mixedJson[i - 1] !== '\\') {
isString = !isString;
continue;
}
if (isString) continue;

if (mixedJson[i] === open) {
counter++;
} else if (mixedJson[i] === close) {
counter--;
}

// All brackets have been closed, thus end of JSON is reached
if (counter === 0) {
// Return the cut JSON
return mixedJson.substring(0, i + 1);
}
}

// We ran through the whole string and ended up with an unclosed bracket
throw Error("Can't cut unsupported JSON (no matching closing bracket found)");
};

// Sorts Images in descending order & normalizes the url's
exports.prepImg = img => {
// Resolve url
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"lint:fix": "eslint --fix ./"
},
"dependencies": {
"miniget": "^4.2.3"
"undici": "^5.25.2"
},
"devDependencies": {
"eslint": "^8.49.0"
Expand Down

0 comments on commit f0a310c

Please sign in to comment.