From a1980eeac4e917c21fed3feb97b3dee24e536726 Mon Sep 17 00:00:00 2001 From: Kelvin <kelvin@futo.org> Date: Thu, 21 Dec 2023 19:23:57 +0100 Subject: [PATCH] New channel viewmodel support --- YoutubeConfig.json | 2 +- YoutubeScript.js | 118 +++++++++++++++++++++++++++++++++------------ 2 files changed, 87 insertions(+), 33 deletions(-) diff --git a/YoutubeConfig.json b/YoutubeConfig.json index ab86e81..7813508 100644 --- a/YoutubeConfig.json +++ b/YoutubeConfig.json @@ -7,7 +7,7 @@ "sourceUrl": "https://plugins.grayjay.app/Youtube/YoutubeConfig.json", "repositoryUrl": "https://futo.org", "scriptUrl": "./YoutubeScript.js", - "version": 159, + "version": 160, "iconUrl": "./youtube.png", "id": "35ae969a-a7db-11ed-afa1-0242ac120002", diff --git a/YoutubeScript.js b/YoutubeScript.js index cd1beee..5b8e213 100644 --- a/YoutubeScript.js +++ b/YoutubeScript.js @@ -2203,41 +2203,95 @@ function extractSearch_SearchResults(data, contextData) { * @returns {PlatformChannel} */ function extractChannel_PlatformChannel(initialData, sourceUrl = null) { - const headerRenderer = initialData?.header?.c4TabbedHeaderRenderer; - if(!headerRenderer) { - log("Missing header renderer in structure: (" + sourceUrl + ")\n" + JSON.stringify(initialData, null, " ")); - throw new ScriptException("No header renderer for " + sourceUrl); - } + if(initialData?.header?.c4TabbedHeaderRenderer) { + const headerRenderer = initialData?.header?.c4TabbedHeaderRenderer; + + if(IS_TESTING) + console.log("Initial Data", initialData); + + const thumbnailTargetWidth = 200; + const thumbnails = headerRenderer.avatar?.thumbnails; + const thumbnail = (thumbnails && thumbnails.length > 0) ? thumbnails.sort((a,b)=>Math.abs(a.width - thumbnailTargetWidth) - Math.abs(b.width - thumbnailTargetWidth))[0] : { url: "" }; + const banners = headerRenderer.banner?.thumbnails; + const bannerTargetWidth = 1080; + const banner = (banners && banners.length > 0) ? banners.sort((a,b)=>Math.abs(a.width - bannerTargetWidth) - Math.abs(b.width - bannerTargetWidth))[0] : { url: "" }; + + const idUrl = headerRenderer?.navigationEndpoint?.browseEndpoint?.browseId ? + URL_BASE + "/channel/" + headerRenderer.navigationEndpoint.browseEndpoint.browseId : + null; + const canonicalUrl = headerRenderer?.navigationEndpoint?.browseEndpoint?.canonicalBaseUrl ? + URL_BASE + headerRenderer.navigationEndpoint.browseEndpoint.canonicalBaseUrl : + null; + + return new PlatformChannel({ + id: new PlatformID(PLATFORM, headerRenderer.channelId, config.id, PLATFORM_CLAIMTYPE), + name: headerRenderer.title ?? "", + thumbnail: thumbnail.url, + banner: banner.url, + subscribers: Math.max(0, extractHumanNumber_Integer(extractText_String(headerRenderer.subscriberCountText))), + description: "", + url: idUrl, + urlAlternatives: [idUrl, canonicalUrl], + links: {} + }); + } + else if(initialData?.header?.pageHeaderRenderer) { + log("New channel model"); + const headerRenderer = initialData?.header?.pageHeaderRenderer; + + if(IS_TESTING) + console.log("Initial Data (New Model)", initialData); + + const thumbnailTargetWidth = 200; + const thumbnails = headerRenderer?.content?.pageHeaderViewModel?.image?.decoratedAvatarViewModel?.avatar?.avatarViewModel?.image?.sources; + const thumbnail = (thumbnails && thumbnails.length > 0) ? thumbnails.sort((a,b)=>Math.abs(a.width - thumbnailTargetWidth) - Math.abs(b.width - thumbnailTargetWidth))[0] : { url: "" }; + const banners = headerRenderer?.content?.pageHeaderViewModel?.banner?.imageBannerViewModel?.image?.sources; + const bannerTargetWidth = 1080; + const banner = (banners && banners.length > 0) ? banners.sort((a,b)=>Math.abs(a.width - bannerTargetWidth) - Math.abs(b.width - bannerTargetWidth))[0] : { url: "" }; + + const id = initialData?.metadata?.channelMetadataRenderer?.externalId; + if(!id) { + log("ID not found in new channel viewmodel:" + JSON.stringify(id, null, " ")); + throw new ScriptException("ID Not found in new channel view model"); + } - if(IS_TESTING) - console.log("Initial Data", initialData); + const idUrl = id ? + URL_BASE + "/channel/" + id: + null; + const canonicalUrl = initialData?.metadata?.channelMetadataRenderer?.vanityChannelUrl ? + initialData?.metadata?.channelMetadataRenderer?.vanityChannelUrl : + null; + + let subCount = 0; + const metadataRows = headerRenderer?.content?.pageHeaderViewModel?.metadata?.contentMetadataViewModel?.metadataRows; + for(let row of metadataRows) { + const subsStr = row.metadataParts.find(x=>x.text?.content?.indexOf("subscribers") > 0)?.text?.content; + if(!subsStr) + continue; + const subsNum = extractHumanNumber_Integer(extractText_String(subsStr)); + if(!isNaN(subsNum) && subsNum > 0) { + subCount = subsNum; + break; + } + } - const thumbnailTargetWidth = 200; - const thumbnails = headerRenderer.avatar?.thumbnails; - const thumbnail = (thumbnails && thumbnails.length > 0) ? thumbnails.sort((a,b)=>Math.abs(a.width - thumbnailTargetWidth) - Math.abs(b.width - thumbnailTargetWidth))[0] : { url: "" }; - const banners = headerRenderer.banner?.thumbnails; - const bannerTargetWidth = 1080; - const banner = (banners && banners.length > 0) ? banners.sort((a,b)=>Math.abs(a.width - bannerTargetWidth) - Math.abs(b.width - bannerTargetWidth))[0] : { url: "" }; - - const idUrl = headerRenderer?.navigationEndpoint?.browseEndpoint?.browseId ? - URL_BASE + "/channel/" + headerRenderer.navigationEndpoint.browseEndpoint.browseId : - null; - const canonicalUrl = headerRenderer?.navigationEndpoint?.browseEndpoint?.canonicalBaseUrl ? - URL_BASE + headerRenderer.navigationEndpoint.browseEndpoint.canonicalBaseUrl : - null; - - return new PlatformChannel({ - id: new PlatformID(PLATFORM, headerRenderer.channelId, config.id, PLATFORM_CLAIMTYPE), - name: headerRenderer.title ?? "", - thumbnail: thumbnail.url, - banner: banner.url, - subscribers: Math.max(0, extractHumanNumber_Integer(extractText_String(headerRenderer.subscriberCountText))), - description: "", - url: idUrl, - urlAlternatives: [idUrl, canonicalUrl], - links: {} - }); + return new PlatformChannel({ + id: new PlatformID(PLATFORM, id, config.id, PLATFORM_CLAIMTYPE), + name: initialData?.metadata?.channelMetadataRenderer?.title ?? "", + thumbnail: thumbnail.url, + banner: banner.url, + subscribers: Math.max(0, subCount), + description: initialData?.metadata?.channelMetadataRenderer?.description, + url: idUrl, + urlAlternatives: [idUrl, canonicalUrl].filter(x=>x != null), + links: {} + }); + } + else { + log("Missing header: (" + sourceUrl + ")\n" + JSON.stringify(initialData, null, " ")); + throw new ScriptException("No header for " + sourceUrl); + } } /** * Extracts multiple tabs from a page that contains a tab rendering -- GitLab