diff --git a/lib/Actions.js b/lib/Actions.js index cc3867c8..b9693c6c 100644 --- a/lib/Actions.js +++ b/lib/Actions.js @@ -72,8 +72,8 @@ async function likeVideo(session, video_id) { } } -async function removeLike(session, video_id) { - if (!session.logged_in) throw new Error('You must be logged in to remove a like/dislike.'); +async function dislikeVideo(session, video_id) { + if (!session.logged_in) throw new Error('You must be logged in to like a video'); let data = { context: session.context, target: { @@ -81,7 +81,7 @@ async function removeLike(session, video_id) { } }; - const response = await axios.post(Constants.urls.YT_BASE_URL + '/youtubei/v1/like/removelike?key=' + session.key, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error); + const response = await axios.post(Constants.urls.YT_BASE_URL + '/youtubei/v1/like/dislike?key=' + session.key, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error); if (response instanceof Error) { return { success: false, @@ -96,8 +96,8 @@ async function removeLike(session, video_id) { } } -async function dislikeVideo(session, video_id) { - if (!session.logged_in) throw new Error('You must be logged in to like a video'); +async function removeLike(session, video_id) { + if (!session.logged_in) throw new Error('You must be logged in to remove a like/dislike.'); let data = { context: session.context, target: { @@ -105,7 +105,7 @@ async function dislikeVideo(session, video_id) { } }; - const response = await axios.post(Constants.urls.YT_BASE_URL + '/youtubei/v1/like/dislike?key=' + session.key, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error); + const response = await axios.post(Constants.urls.YT_BASE_URL + '/youtubei/v1/like/removelike?key=' + session.key, JSON.stringify(data), Constants.innertube_request_opts({ session, id: video_id, data })).catch((error) => error); if (response instanceof Error) { return { success: false, diff --git a/lib/Constants.js b/lib/Constants.js index 455fcea9..ad3ea4c2 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -56,6 +56,21 @@ const innertube_request_opts = (info) => { return req_opts; }; +const stream_headers = (range) => { + let headers = { + 'Accept': '*/*', + 'User-Agent': Utils.getRandomUserAgent('desktop').userAgent, + 'Connection': 'keep-alive', + 'Origin': urls.YT_BASE_URL, + 'Referer': urls.YT_BASE_URL, + 'DNT': '?1' + }; + if (range) { + headers.Range = range; + } + return headers; +}; + const video_details_req_body = (id, sts, context) => { return { playbackContext: { @@ -76,25 +91,12 @@ const video_details_req_body = (id, sts, context) => { }; }; -const stream_headers = (range) => { - let headers = { - 'Accept': '*/*', - 'User-Agent': Utils.getRandomUserAgent('desktop').userAgent, - 'Connection': 'keep-alive', - 'Origin': urls.YT_BASE_URL, - 'Referer': urls.YT_BASE_URL, - 'DNT': '?1' - }; - if (range) { - headers.Range = range; - } - return headers; -}; const formatVideoData = (data, context, desktop) => { - if (desktop) { - let metadata = {}; + let video_details = {}; + let metadata = {}; + if (desktop) { metadata.embed = data.microformat.playerMicroformatRenderer.embed; metadata.view_count = parseInt(data.videoDetails.viewCount); metadata.average_rating = data.videoDetails.averageRating; @@ -113,15 +115,11 @@ const formatVideoData = (data, context, desktop) => { metadata.upload_date = data.microformat.playerMicroformatRenderer.uploadDate || 'N/A'; metadata.keywords = data.videoDetails.keywords || []; - let video_details = {}; video_details.title = data.videoDetails.title; video_details.description = data.videoDetails.shortDescription; video_details.thumbnail = data.videoDetails.thumbnail.thumbnails.slice(-1)[0]; video_details.metadata = metadata; - return video_details; } else { - let metadata = {}; - metadata.embed = data[2].playerResponse.microformat.playerMicroformatRenderer.embed; metadata.likes = parseInt(data[3].response.contents.singleColumnWatchNextResults.results.results.contents[1].slimVideoMetadataSectionRenderer.contents[1].slimVideoActionBarRenderer.buttons[0].slimMetadataToggleButtonRenderer.button.toggleButtonRenderer.defaultText.accessibility.accessibilityData.label.replace(/\D/g, '')); metadata.dislikes = parseInt(data[3].response.contents.singleColumnWatchNextResults.results.results.contents[1].slimVideoMetadataSectionRenderer.contents[1].slimVideoActionBarRenderer.buttons[1].slimMetadataToggleButtonRenderer.button.toggleButtonRenderer.defaultText.accessibility.accessibilityData.label.replace(/\D/g, '')); @@ -142,13 +140,10 @@ const formatVideoData = (data, context, desktop) => { metadata.upload_date = data[2].playerResponse.microformat.playerMicroformatRenderer.uploadDate; metadata.keywords = data[2].playerResponse.videoDetails.keywords; - const id = data[2].playerResponse.videoDetails.videoId; - - let video_details = {}; video_details.title = data[2].playerResponse.videoDetails.title; video_details.description = data[2].playerResponse.videoDetails.shortDescription; video_details.thumbnail = data[2].playerResponse.videoDetails.thumbnail.thumbnails.slice(-1)[0]; - + // actions video_details.like = like => {}; video_details.dislike = dislike => {}; @@ -156,11 +151,11 @@ const formatVideoData = (data, context, desktop) => { video_details.subscribe = subscribe => {}; video_details.unsubscribe = unsubscribe => {}; video_details.comment = comment => {}; - + // additional metadata video_details.metadata = metadata; - return video_details; } + return video_details; }; const filters = (order) => { diff --git a/lib/Innertube.js b/lib/Innertube.js index 8b045228..def25073 100644 --- a/lib/Innertube.js +++ b/lib/Innertube.js @@ -103,7 +103,18 @@ class Innertube { return video_data; } - + + async requestVideoInfo(id, desktop) { + let response; + if (!desktop) { + response = await axios.get(Constants.urls.YT_WATCH_PAGE + '?v=' + id + 't=8s&pbj=1&bpctr=9999999999&has_verified=1&', Constants.innertube_request_opts({ session: this, id, desktop: false })).catch((error) => error); + } else { + response = await axios.post(Constants.urls.YT_BASE_URL + '/youtubei/v1/player?key=' + this.key, JSON.stringify(Constants.video_details_req_body(id, this.sts, this.context)), Constants.innertube_request_opts({ session: this, id, desktop: true })).catch((error) => error); + } + if (response instanceof Error) throw new Error('Could not retrieve watch page info: ' + response.message); + return response.data; + } + download(id, options = {}) { options.quality = options.quality || '360p'; options.type = options.type || 'both'; @@ -260,17 +271,6 @@ class Innertube { }; return stream; } - - async requestVideoInfo(id, desktop) { - let response; - if (!desktop) { - response = await axios.get(Constants.urls.YT_WATCH_PAGE + '?v=' + id + 't=8s&pbj=1&bpctr=9999999999&has_verified=1&', Constants.innertube_request_opts({ session: this, id, desktop: false })).catch((error) => error); - } else { - response = await axios.post(Constants.urls.YT_BASE_URL + '/youtubei/v1/player?key=' + this.key, JSON.stringify(Constants.video_details_req_body(id, this.sts, this.context)), Constants.innertube_request_opts({ session: this, id, desktop: true })).catch((error) => error); - } - if (response instanceof Error) throw new Error('Could not retrieve watch page info: ' + response.message); - return response.data; - } } module.exports = Innertube; \ No newline at end of file diff --git a/lib/Player.js b/lib/Player.js index 3d6df923..3277e303 100644 --- a/lib/Player.js +++ b/lib/Player.js @@ -20,8 +20,10 @@ class Player { } else { const response = await axios.get(Constants.urls.YT_BASE_URL + this.session.player_url, { path: this.session.playerUrl, headers: { 'content-type': 'text/javascript', 'user-agent': Utils.getRandomUserAgent('desktop').userAgent } }).catch((error) => error); if (response instanceof Error) throw new Error('Could not get player data: ' + response.message); + fs.mkdirSync(this.tmp_cache_dir, { recursive: true }); fs.writeFileSync(this.tmp_cache_dir + '/' + this.player_name + '.js', response.data); + this.getSigDecipherCode(response.data); this.getNEncoder(response.data); } diff --git a/lib/SigDecipher.js b/lib/SigDecipher.js index ddadff5f..8ef2f254 100644 --- a/lib/SigDecipher.js +++ b/lib/SigDecipher.js @@ -26,8 +26,8 @@ class SigDecipher { arr[position % arr.length] = origArrI; } - function reverse(options) { - options.reverse(); + function reverse(arr) { + arr.reverse(); } let actions; diff --git a/lib/Utils.js b/lib/Utils.js index 41a17756..c36260a2 100644 --- a/lib/Utils.js +++ b/lib/Utils.js @@ -3,8 +3,14 @@ const Crypto = require('crypto'); const UserAgent = require('user-agents'); -function encodeId(id) { - return encodeURI(new Buffer.from(` ` + id + `*`).toString('base64').replace('==', '') + 'BQBw=='); +function getRandomUserAgent(type) { + switch (type) { + case 'mobile': + return new UserAgent(/Android/).data; + case 'desktop': + return new UserAgent({ deviceCategory: 'desktop' }).data; + default: + } } function generateSidAuth(sid) { @@ -19,16 +25,6 @@ function generateSidAuth(sid) { return ['SAPISIDHASH', [timestamp, gen_hash].join('_')].join(' '); } -function getRandomUserAgent(type) { - switch (type) { - case 'mobile': - return new UserAgent(/Android/).data; - case 'desktop': - return new UserAgent({ deviceCategory: 'desktop' }).data; - default: - } -} - function getStringBetweenStrings(data, start_string, end_string) { const regex = new RegExp(`${escapeStringRegexp(start_string)}(.*?)${escapeStringRegexp(end_string)}`, "s"); const match = data.match(regex); @@ -43,4 +39,8 @@ function createFunction(input, raw_code) { // I hate this return new Function(input, raw_code); } -module.exports = { generateSidAuth, encodeId, getRandomUserAgent, getStringBetweenStrings, createFunction }; \ No newline at end of file +function encodeId(id) { + return encodeURI(new Buffer.from(` ` + id + `*`).toString('base64').replace('==', '') + 'BQBw=='); +} + +module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, createFunction, encodeId }; \ No newline at end of file