From 60130f4d0f03e32b8df2459e2807c4bfe73b1bb4 Mon Sep 17 00:00:00 2001 From: "luan.lrt4@gmail.com" Date: Mon, 4 Apr 2022 13:51:27 -0300 Subject: [PATCH] refactor: add utility to access deep object properties --- lib/Utils.js | 70 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/lib/Utils.js b/lib/Utils.js index e90cfa5e..d8c55153 100644 --- a/lib/Utils.js +++ b/lib/Utils.js @@ -3,6 +3,54 @@ const Fs = require('fs'); const Crypto = require('crypto'); const UserAgent = require('user-agents'); +const Flatten = require('flat'); + +function InnertubeError(message, info) { + this.info = info; + this.stack = Error(message).stack; +} + +InnertubeError.prototype = Object.create(Error.prototype); +InnertubeError.prototype.constructor = InnertubeError; + +class ParsingError extends InnertubeError {}; +class DownloadError extends InnertubeError {}; +class MissingParamError extends InnertubeError {}; +class UnavailableContentError extends InnertubeError {}; +class NoStreamingDataError extends InnertubeError {}; + +/** + * Utility to help access deep properties of an object. + * + * @param {object} obj - The object. + * @param {string} key - Key of the property being accessed. + * @param {string} target - Anything that might be inside of the property. + * @param {number} depth - Maximum number of nested objects to flatten. + * @param {boolean} safe - If set to true arrays will be preserved. + */ +function findNode (obj, key, target, depth, safe = true) { + const flat_obj = Flatten(obj, { safe, maxDepth: depth || 2 }); + const result = Object.keys(flat_obj).find((entry) => entry.includes(key) && JSON.stringify(flat_obj[entry] || '{}').includes(target)); + if (!result) throw new ParsingError(`Expected to find "${key}" with content "${target}" but got ${result}`, { key, target, data_snippet: `${JSON.stringify(flat_obj).slice(0, 300)}..` }); + return flat_obj[result]; +} + +/** + * Gets a string between two delimiters. + * + * @param {string} data - The data. + * @param {string} start_string - Start string. + * @param {string} end_string - End string. + */ +function getStringBetweenStrings(data, start_string, end_string) { + const regex = new RegExp(`${escapeStringRegexp(start_string)}(.*?)${escapeStringRegexp(end_string)}`, 's'); + const match = data.match(regex); + return match ? match[1] : undefined; +} + +function escapeStringRegexp(string) { + return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d"); +} /** * Returns a random user agent. @@ -38,23 +86,6 @@ function generateSidAuth(sid) { return ['SAPISIDHASH', [timestamp, gen_hash].join('_')].join(' '); } -/** - * Gets a string between two delimiters. - * - * @param {string} data - The data. - * @param {string} start_string - Start string. - * @param {string} end_string - End string. - */ -function getStringBetweenStrings(data, start_string, end_string) { - const regex = new RegExp(`${escapeStringRegexp(start_string)}(.*?)${escapeStringRegexp(end_string)}`, "s"); - const match = data.match(regex); - return match ? match[1] : undefined; -} - -function escapeStringRegexp(string) { - return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d"); -} - /** * Converts time (h:m:s) to seconds. * @@ -96,4 +127,7 @@ function refineNTokenData(data) { .replace(/""/g, '').replace(/length]\)}"/g, 'length])}'); } -module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, camelToSnake, timeToSeconds, refineNTokenData }; \ No newline at end of file +const errors = { UnavailableContentError, ParsingError, DownloadError, InnertubeError, MissingParamError, NoStreamingDataError }; +const functions = { findNode, getRandomUserAgent, generateSidAuth, getStringBetweenStrings, camelToSnake, timeToSeconds, refineNTokenData }; + +module.exports = { ...functions, ...errors }; \ No newline at end of file