diff --git a/lib/Innertube.js b/lib/Innertube.js index 38f4c77d..51ece615 100644 --- a/lib/Innertube.js +++ b/lib/Innertube.js @@ -12,7 +12,6 @@ const Livechat = require('./Livechat'); const Constants = require('./Constants'); const SigDecipher = require('./Sig'); const EventEmitter = require('events'); -const TimeToSeconds = require('time-to-seconds'); const CancelToken = Axios.CancelToken; class Innertube { @@ -108,7 +107,7 @@ class Innertube { const response = await Actions.search(this, options.client, { query, options }); if (!response.success) throw new Error(`Could not search on YouTube: ${response.message}`); - const refined_data = new Parser(response.data, { + const refined_data = new Parser(this, response.data, { client: options.client, data_type: 'SEARCH', query @@ -126,12 +125,15 @@ class Innertube { if (!id) throw new Error('You must provide a video id'); const data = await Actions.getVideoInfo(this, { id, is_desktop: false }); - const refined_data = new Parser(data, { client: 'YOUTUBE', data_type: 'VIDEO_INFO', desktop_v: false }).parse(); + const refined_data = new Parser(this, data, { client: 'YOUTUBE', data_type: 'VIDEO_INFO', desktop_v: false }).parse(); if (refined_data.metadata.is_live_content) { const data_continuation = await Actions.getContinuation(this, { video_id: id }); - if (!data_continuation.data.contents.twoColumnWatchNextResults.conversationBar) return; - refined_data.getLivechat = () => new Livechat(this, data_continuation.data.contents.twoColumnWatchNextResults.conversationBar.liveChatRenderer.continuations[0].reloadContinuationData.continuation, refined_data.metadata.channel_id, id); + if (data_continuation.data.contents.twoColumnWatchNextResults.conversationBar) { + refined_data.getLivechat = () => new Livechat(this, data_continuation.data.contents.twoColumnWatchNextResults.conversationBar.liveChatRenderer.continuations[0].reloadContinuationData.continuation, refined_data.metadata.channel_id, id); + } else { + refined_data.getLivechat = () => { }; + } } else { refined_data.getLivechat = () => { }; } @@ -392,7 +394,7 @@ class Innertube { if (!selected_format) { return stream.emit('error', { message: 'Could not find any suitable format.', type: 'FORMAT_UNAVAILABLE' }); } else { - const refined_data = new Parser(video_data, { client: 'YOUTUBE', data_type: 'VIDEO_INFO', desktop_v: true }).parse(); + const refined_data = new Parser(this, video_data, { client: 'YOUTUBE', data_type: 'VIDEO_INFO', desktop_v: true }).parse(); stream.emit('info', { video_details: refined_data, selected_format, formats }); } diff --git a/lib/Parser.js b/lib/Parser.js index 38c27b58..977caa37 100644 --- a/lib/Parser.js +++ b/lib/Parser.js @@ -1,15 +1,16 @@ 'use strict'; -const Utils = require('./Utils') +const Utils = require('./Utils'); +const Actions = require('./Actions'); const Constants = require('./Constants'); -const TimeToSeconds = require('time-to-seconds'); /** * Takes raw data from the Innertube API and refines it. * Mainly used for video data and search results, as those are more complex to parse. */ class Parser { - constructor(data, args = {}) { + constructor(session, data, args = {}) { + this.session = session; this.data = data; this.args = args; } @@ -58,7 +59,7 @@ class Parser { }, thumbnails: video.thumbnail.thumbnails, duration: { - seconds: TimeToSeconds(video.lengthText && video.lengthText.simpleText || '0'), + seconds: Utils.timeToSeconds(video.lengthText && video.lengthText.simpleText || '0'), simple_text: video.lengthText && video.lengthText.simpleText || 'N/A', accessibility_label: video.lengthText && video.lengthText.accessibility.accessibilityData.label || 'N/A' }, @@ -76,6 +77,14 @@ class Parser { const tabs = this.data.contents.tabbedSearchResultsRenderer.tabs; const contents = tabs[0].tabRenderer.content.sectionListRenderer.contents; + /** + * WIP + **/ + const getLyrics = async (id) => { + // const data_continuation = await Actions.getContinuation(this.session, { video_id: id, ytmusic: true }); + return undefined; + } + const songs_ms = contents.find((content) => content.musicShelfRenderer.title.runs[0].text == 'Songs'); const songs = songs_ms.musicShelfRenderer.contents.map((item) => { const list_item = item.musicResponsiveListItemRenderer; @@ -85,7 +94,8 @@ class Parser { artist: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[2].text, album: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[4].text, duration: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[6].text, - thumbnail: list_item.thumbnail.musicThumbnailRenderer.thumbnail + thumbnail: list_item.thumbnail.musicThumbnailRenderer.thumbnail, + getLyrics: () => getLyrics(list_item.playlistItemData.videoId) }; }); @@ -98,7 +108,8 @@ class Parser { author: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[2].text, views: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[4].text, duration: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[6].text, - thumbnail: list_item.thumbnail.musicThumbnailRenderer.thumbnail + thumbnail: list_item.thumbnail.musicThumbnailRenderer.thumbnail, + getLyrics: () => getLyrics(list_item.playlistItemData.videoId) }; }); @@ -109,7 +120,7 @@ class Parser { title: list_item.flexColumns[0].musicResponsiveListItemFlexColumnRenderer.text.runs[0].text, author: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs[2].text, year: list_item.flexColumns[1].musicResponsiveListItemFlexColumnRenderer.text.runs.find((run) => /^[12][0-9]{3}$/.test(run.text)).text, - thumbnail: list_item.thumbnail.musicThumbnailRenderer.thumbnail + thumbnail: list_item.thumbnail.musicThumbnailRenderer.thumbnail, }; }); @@ -119,6 +130,12 @@ class Parser { #parseVideoInfo() { const desktop_v = this.args.desktop_v; + const playability_status = desktop_v && this.data.playabilityStatus || + this.data[2].playerResponse.playabilityStatus; + + if (playability_status.status == 'ERROR') + throw new Error(`Could not retrieve details for this video: ${playability_status.status} - ${playability_status.reason}`); + const details = desktop_v && this.data.videoDetails || this.data[2].playerResponse.videoDetails; @@ -135,7 +152,7 @@ class Parser { thumbnail: [], metadata: {} }; - + const mf_raw_data = Object.entries(microformat); const dt_raw_data = Object.entries(details); diff --git a/lib/Utils.js b/lib/Utils.js index ba283d35..706fe20c 100644 --- a/lib/Utils.js +++ b/lib/Utils.js @@ -55,6 +55,21 @@ function escapeStringRegexp(string) { return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d"); } +/** + * Converts time (h:m:s) to seconds. + * + * @param {string} time + * @returns {string} seconds + */ +function timeToSeconds(time) { + let params = time.split(':'); + return parseInt(({ + 3: +params[0] * 3600 + +params[1] * 60 + +params[2], + 2: +params[0] * 60 + +params[1], + 1: +params[0] + })[params.length]); +} + /** * Converts strings in camelCase to snake_case. * @@ -156,4 +171,4 @@ function encodeFilter(period, duration, order) { return encodeURIComponent(Buffer.from(search_filter_buff).toString('base64')); } -module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, camelToSnake, encodeMessageParams, encodeCommentParams, encodeNotificationPref, encodeFilter }; \ No newline at end of file +module.exports = { getRandomUserAgent, generateSidAuth, getStringBetweenStrings, camelToSnake, timeToSeconds, encodeMessageParams, encodeCommentParams, encodeNotificationPref, encodeFilter }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2dca15a5..7064b173 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "axios": "^0.21.4", "protons": "^2.0.3", - "time-to-seconds": "^1.1.5", "user-agents": "^1.0.778", "uuid": "^8.3.2" } @@ -54,9 +53,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", - "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==", + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", "funding": [ { "type": "individual", @@ -78,9 +77,9 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, "node_modules/multiformats": { - "version": "9.5.4", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.5.4.tgz", - "integrity": "sha512-MFT8e8BOLX7OZKfSBGm13FwYvJVI6MEcZ7hujUCpyJwvYyrC1anul50A0Ee74GdeJ77aYTO6YU1vO+oF8NqSIw==" + "version": "9.5.8", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.5.8.tgz", + "integrity": "sha512-GY154q1yPPdHX4ArXHE8Z1Mm9BxZcJetzEqfwQg/ongo91qIJDHJEio3zboHIKGEvBLrhVqKwlRuDqwa7+xECQ==" }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", @@ -106,15 +105,6 @@ "varint": "~5.0.0" } }, - "node_modules/time-to-seconds": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/time-to-seconds/-/time-to-seconds-1.1.5.tgz", - "integrity": "sha512-mpzJDHGF4VdhiahyusCUSy+BWJdN3q8Cluzfy0n7GMU9IIj+HJDX9bbbr7wVSUiqmRn1vqhhfECgdfj+SByu2A==", - "engines": { - "node": ">=15.0.1", - "vscode": "^1.22.0" - } - }, "node_modules/uint8arrays": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", @@ -137,9 +127,9 @@ } }, "node_modules/user-agents": { - "version": "1.0.869", - "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.869.tgz", - "integrity": "sha512-fjHZWYMi0CBF8ni/TIcCUFB7GiTTT+UcCAxT9/kNbPd/d5PR9VZ0wOeWZ5McbdJ42PRdFWc1ZZUMJIvpNSsewg==", + "version": "1.0.900", + "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.900.tgz", + "integrity": "sha512-uE1xBRjfHuyMbue/c/ZZTU58jzwLNeCzT9UB2g27dm/jvnkH9e/89z/FcJ0Jf1w45KDtFeXAjQUUbtAYyF70eg==", "dependencies": { "dot-json": "^1.2.2", "lodash.clonedeep": "^4.5.0" @@ -189,9 +179,9 @@ } }, "follow-redirects": { - "version": "1.14.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", - "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==" + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" }, "lodash.clonedeep": { "version": "4.5.0", @@ -199,9 +189,9 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, "multiformats": { - "version": "9.5.4", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.5.4.tgz", - "integrity": "sha512-MFT8e8BOLX7OZKfSBGm13FwYvJVI6MEcZ7hujUCpyJwvYyrC1anul50A0Ee74GdeJ77aYTO6YU1vO+oF8NqSIw==" + "version": "9.5.8", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.5.8.tgz", + "integrity": "sha512-GY154q1yPPdHX4ArXHE8Z1Mm9BxZcJetzEqfwQg/ongo91qIJDHJEio3zboHIKGEvBLrhVqKwlRuDqwa7+xECQ==" }, "protocol-buffers-schema": { "version": "3.6.0", @@ -227,11 +217,6 @@ "varint": "~5.0.0" } }, - "time-to-seconds": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/time-to-seconds/-/time-to-seconds-1.1.5.tgz", - "integrity": "sha512-mpzJDHGF4VdhiahyusCUSy+BWJdN3q8Cluzfy0n7GMU9IIj+HJDX9bbbr7wVSUiqmRn1vqhhfECgdfj+SByu2A==" - }, "uint8arrays": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", @@ -254,9 +239,9 @@ } }, "user-agents": { - "version": "1.0.869", - "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.869.tgz", - "integrity": "sha512-fjHZWYMi0CBF8ni/TIcCUFB7GiTTT+UcCAxT9/kNbPd/d5PR9VZ0wOeWZ5McbdJ42PRdFWc1ZZUMJIvpNSsewg==", + "version": "1.0.900", + "resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.0.900.tgz", + "integrity": "sha512-uE1xBRjfHuyMbue/c/ZZTU58jzwLNeCzT9UB2g27dm/jvnkH9e/89z/FcJ0Jf1w45KDtFeXAjQUUbtAYyF70eg==", "requires": { "dot-json": "^1.2.2", "lodash.clonedeep": "^4.5.0" @@ -273,4 +258,4 @@ "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index bb1b312e..a4679a5d 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "dependencies": { "axios": "^0.21.4", "protons": "^2.0.3", - "time-to-seconds": "^1.1.5", "user-agents": "^1.0.778", "uuid": "^8.3.2" },