From 90708b9dac27bbf0521e104f71013a4ce3dae3c9 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 23 Mar 2022 12:07:01 +1300 Subject: [PATCH 1/2] refactor: changed API calls to be properly using innertube --- NUXT/plugins/constants.js | 2 - NUXT/plugins/innertube.js | 115 ++++++++++++++++++-------------------- 2 files changed, 53 insertions(+), 64 deletions(-) diff --git a/NUXT/plugins/constants.js b/NUXT/plugins/constants.js index 7ba2a13..0ad519b 100644 --- a/NUXT/plugins/constants.js +++ b/NUXT/plugins/constants.js @@ -39,8 +39,6 @@ module.exports = { "x-goog-visitor-id": info.visitorData || "", "x-youtube-client-name": ytApiVal.CLIENTNAME, "x-youtube-client-version": ytApiVal.VERSION, - "x-origin": info.originalUrl, - origin: info.originalUrl, }; return headers; }, diff --git a/NUXT/plugins/innertube.js b/NUXT/plugins/innertube.js index 18954b0..2d80637 100644 --- a/NUXT/plugins/innertube.js +++ b/NUXT/plugins/innertube.js @@ -109,33 +109,35 @@ class Innertube { async getVidAsync(id) { let data = { context: this.context, videoId: id }; - const response = await Http.get({ - url: `https://m.youtube.com/watch?v=${id}&pbj=1`, - params: {}, - headers: Object.assign(this.header, { - referer: `https://m.youtube.com/watch?v=${id}`, - "x-youtube-client-name": constants.YT_API_VALUES.CLIENT_WEB, - "x-youtube-client-version": constants.YT_API_VALUES.VERSION_WEB, - }), + const responseNext = await Http.post({ + url: `${constants.URLS.YT_BASE_API}/next?v=${id}`, + data: data, + headers: constants.INNERTUBE_HEADER(this.context.client), }).catch((error) => error); - const responseMobile = await Http.post({ + const response = await Http.post({ url: `${constants.URLS.YT_BASE_API}/player?key=${this.key}`, data: data, - headers: constants.INNERTUBE_HEADER(this.context), + headers: constants.INNERTUBE_HEADER(this.context.client), }).catch((error) => error); - if (response instanceof Error) + if (response.error) return { success: false, - status_code: response.response.status, + status_code: response.status, message: response.message, - }; - + } + else if (responseNext.error) + return { + success: false, + status_code: responseNext.status, + message: responseNext.message, + } + return { success: true, status_code: response.status, - data: { webOutput: response.data, appOutput: responseMobile.data }, + data: { output: response.data, outputNext: responseNext.data }, }; } @@ -150,68 +152,57 @@ class Innertube { let response = await this.getVidAsync(id); if ( - response.success && - response.data.webOutput[2].playerResponse?.playabilityStatus?.status == + response.success == false || + response.data.output?.playabilityStatus?.status == ("ERROR" || undefined) ) throw new Error( - `Could not get information for video: ${response[2].playerResponse?.playabilityStatus?.status} - ${response[2].playerResponse?.playabilityStatus?.reason}` + `Could not get information for video: ${response.status_code || response.data.output?.playabilityStatus?.status} - ${response.message || response.data.output?.playabilityStatus?.reason}` ); - const responseWeb = response.data.webOutput; - const responseApp = response.data.appOutput; - const details = responseWeb[2].playerResponse?.videoDetails; - const microformat = - responseWeb[2].playerResponse?.microformat?.playerMicroformatRenderer; - const renderedPanels = responseWeb[3].response?.engagementPanels; - const columnUI = - responseWeb[3].response?.contents.singleColumnWatchNextResults?.results - ?.results; - const resolutions = responseApp.streamingData; + const responseInfo = response.data.output; + const responseNext = response.data.outputNext; + const details = responseInfo.videoDetails; + // const columnUI = + // responseInfo[3].response?.contents.singleColumnWatchNextResults?.results + // ?.results; + const resolutions = responseInfo.streamingData; + const columnUI = responseNext.contents.singleColumnWatchNextResults.results.results - console.log(columnUI.contents.length); - - return { + const vidData = { id: details.videoId, - title: details.title || microformat.title?.runs[0].text, - isLive: - details.isLiveContent || - microformat.liveBroadcastDetails?.isLiveNow || - false, - channelName: details.author || microformat.ownerChannelName, - channelUrl: microformat.ownerProfileUrl, + title: details.title, + isLive: details.isLiveContent, + channelName: details.author, + channelUrl: columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(contents => contents.elementRenderer)?.newElement?.type?.componentType?.model?.channelBarModel?.videoChannelBarData + ?.onTap?.innertubeCommand?.browseEndpoint?.canonicalBaseUrl, + channelImg: columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(contents => contents.elementRenderer)?.newElement?.type?.componentType?.model?.channelBarModel?.videoChannelBarData + ?.avatar?.image?.sources[0].url, availableResolutions: resolutions?.formats, availableResolutionsAdaptive: resolutions?.adaptiveFormats, metadata: { - description: microformat.description?.runs[0].text, - descriptionShort: details.shortDescription, - thumbnails: - details.thumbnails?.thumbnails || microformat.thumbnails?.thumbnails, - isFamilySafe: microformat.isFamilySafe, - availableCountries: microformat.availableCountries, - liveBroadcastDetails: microformat.liveBroadcastDetails, - uploadDate: microformat.uploadDate, - publishDate: microformat.publishDate, + description: details.shortDescription, + thumbnails: details.thumbnails?.thumbnails, + uploadDate: columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(contents => contents.slimVideoDescriptionRenderer)?.slimVideoDescriptionRenderer.publishDate.runs[0].text, isPrivate: details.isPrivate, - viewCount: details.viewCount || microformat.viewCount, - lengthSeconds: details.lengthSeconds || microformat.lengthSeconds, - likes: parseInt( - columnUI?.contents[1].slimVideoMetadataSectionRenderer?.contents[1].slimVideoActionBarRenderer?.buttons[0].slimMetadataToggleButtonRenderer?.button?.toggleButtonRenderer?.defaultText?.accessibility?.accessibilityData?.label?.replace( - /\D/g, - "" - ) - ), // Yes. I know. + viewCount: details.viewCount, + lengthSeconds: details.lengthSeconds, + likes: parseInt(columnUI?.contents[0].slimVideoMetadataSectionRenderer?.contents.find(contents => contents.slimVideoScrollableActionBarRenderer)?.slimVideoScrollableActionBarRenderer + .buttons.find(button => button.slimMetadataToggleButtonRenderer.isLike == true)?.slimMetadataToggleButtonRenderer?.button + .toggleButtonRenderer?.defaultText?.accessibility?.accessibilityData?.label?.replace(/\D/g,"")), // Yes. I know. }, renderedData: { - description: - renderedPanels[0].engagementPanelSectionListRenderer?.content - .structuredDescriptionContentRenderer?.items[1] - .expandableVideoDescriptionBodyRenderer?.descriptionBodyText.runs, - recommendations: - columnUI?.contents[columnUI.contents.length - 1].itemSectionRenderer - ?.contents, + description: columnUI?.contents.find(contents => contents.slimVideoMetadataSectionRenderer).slimVideoMetadataSectionRenderer?.contents.find(contents => contents.slimVideoDescriptionRenderer)?.slimVideoDescriptionRenderer.description.runs, + recommendations: columnUI?.contents.find(contents => contents.shelfRenderer).shelfRenderer?.content?.horizontalListRenderer?.items, + recommendationsContinuation: columnUI?.continuations[0].reloadContinuationData?.continuation }, }; + + console.log(vidData) + + return vidData } + + } export default Innertube; From 979691a9781ce97f5dbfb2888869c9c0db6cb265 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 24 Mar 2022 00:21:06 +1300 Subject: [PATCH 2/2] feat: new render method that directly utilizes the youtube API --- .../VideoRenderers/compactVideoRenderer.vue | 46 +++++++ .../VideoRenderers/gridVideoRenderer.vue | 113 ++++++++++++++++++ NUXT/components/recommended.vue | 41 ++----- NUXT/pages/watch.vue | 50 +++++--- NUXT/plugins/renderers.js | 56 +++++---- NUXT/plugins/youtube.js | 49 ++++---- 6 files changed, 256 insertions(+), 99 deletions(-) create mode 100644 NUXT/components/VideoRenderers/compactVideoRenderer.vue create mode 100644 NUXT/components/VideoRenderers/gridVideoRenderer.vue diff --git a/NUXT/components/VideoRenderers/compactVideoRenderer.vue b/NUXT/components/VideoRenderers/compactVideoRenderer.vue new file mode 100644 index 0000000..dd32c02 --- /dev/null +++ b/NUXT/components/VideoRenderers/compactVideoRenderer.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/NUXT/components/VideoRenderers/gridVideoRenderer.vue b/NUXT/components/VideoRenderers/gridVideoRenderer.vue new file mode 100644 index 0000000..1091e91 --- /dev/null +++ b/NUXT/components/VideoRenderers/gridVideoRenderer.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/NUXT/components/recommended.vue b/NUXT/components/recommended.vue index 335666a..30e0744 100644 --- a/NUXT/components/recommended.vue +++ b/NUXT/components/recommended.vue @@ -6,42 +6,23 @@ - - - -
- -
-
-
-
- - + +
- - diff --git a/NUXT/plugins/renderers.js b/NUXT/plugins/renderers.js index c15e033..c927f1f 100644 --- a/NUXT/plugins/renderers.js +++ b/NUXT/plugins/renderers.js @@ -10,6 +10,8 @@ function useRender(video, renderer) { return gridVideoRenderer(video); case "compactAutoplayRenderer": return compactAutoplayRenderer(video); + case "compactVideoRenderer": + return compactVideoRenderer(video); default: return undefined; } @@ -49,35 +51,6 @@ function gridVideoRenderer(video) { }; } -function videoWithContextRenderer(video) { - return { - id: video.videoId, - title: video.headline?.runs[0].text, - thumbnail: Innertube.getThumbnail(video.videoId, "max"), - channel: video.shortBylineText?.runs[0].text, - channelURL: - video.channelThumbnail?.channelThumbnailWithLinkRenderer - ?.navigationEndpoint?.browseEndpoint?.canonicalBaseUrl, - channelId: - video.channelThumbnail?.channelThumbnailWithLinkRenderer - ?.navigationEndpoint?.browseEndpoint?.browseId, - channelThumbnail: - video.channelThumbnail?.channelThumbnailWithLinkRenderer?.thumbnail - .thumbnails[0].url, - metadata: { - views: video.shortViewCountText?.runs[0].text, - length: video.lengthText?.runs[0].text, - overlayStyle: video.thumbnailOverlays?.map( - (overlay) => overlay.thumbnailOverlayTimeStatusRenderer?.style - ), - overlay: video.thumbnailOverlays?.map( - (overlay) => - overlay.thumbnailOverlayTimeStatusRenderer?.text.runs[0].text - ), - isWatched: video.isWatched, - }, - }; -} function compactAutoplayRenderer(video) { video = video.contents; let item; @@ -86,4 +59,29 @@ function compactAutoplayRenderer(video) { else return undefined; } +function compactVideoRenderer(video) { + return { + id: video.videoId, + title: video.title?.runs[0].text, + thumbnail: Innertube.getThumbnail(video.videoId, "max"), + channel: video.shortBylineText?.runs[0].text, + channelURL: + video.shortBylineText?.runs[0].navigationEndpoint?.browseEndpoint + ?.canonicalBaseUrl, + channelThumbnail: video.channelThumbnail?.thumbnails[0].url, + metadata: { + views: video.viewCountText?.runs[0].text, + length: video.lengthText?.runs[0].text, + publishedTimeText: video.publishedTimeText.runs[0].text, + overlayStyle: video.thumbnailOverlays?.map( + (overlay) => overlay.thumbnailOverlayTimeStatusRenderer?.style + ), + overlay: video.thumbnailOverlays?.map( + (overlay) => + overlay.thumbnailOverlayTimeStatusRenderer?.text.runs[0].text + ), + }, + }; +} + export default useRender; diff --git a/NUXT/plugins/youtube.js b/NUXT/plugins/youtube.js index ef35fe5..7944400 100644 --- a/NUXT/plugins/youtube.js +++ b/NUXT/plugins/youtube.js @@ -212,6 +212,18 @@ const innertubeModule = { } }, + getThumbnail(id, resolution) { + if (resolution == "max") { + const url = `https://img.youtube.com/vi/${id}/maxresdefault.jpg`; + let img = new Image(); + img.src = url; + img.onload = function () { + if (img.height !== 120) return url; + }; + } + return `https://img.youtube.com/vi/${id}/mqdefault.jpg`; + }, + // It just works™ // Front page recommendation async recommend() { @@ -226,35 +238,26 @@ const innertubeModule = { const video = shelves.shelfRenderer?.content?.horizontalListRenderer?.items; - if (video) - return video.map((item) => { - if (item) { - const renderedItem = useRender( - item[Object.keys(item)[0]], - Object.keys(item)[0] - ); - console.log(renderedItem); - return renderedItem; - } else { - return undefined; - } - }); + if (video) return video; + // if (video) + // return video.map((item) => { + // if (item) { + // const renderedItem = useRender( + // item[Object.keys(item)[0]], + // Object.keys(item)[0] + // ); + // console.log(renderedItem); + // return renderedItem; + // } else { + // return undefined; + // } + // }); }); console.log(final); return final; }, // This is the recommendations that exist under videos - viewRecommends(recommendList) { - if (recommendList) - return recommendList.map((item) => { - if (item) { - return useRender(item[Object.keys(item)[0]], Object.keys(item)[0]); - } else { - return undefined; - } - }); - }, }; //--- Start ---//