From 4f171a68dcc5a20b5caafb59e1d5a0d3ddd34626 Mon Sep 17 00:00:00 2001
From: Kelvin <kelvin@futo.org>
Date: Mon, 27 May 2024 17:45:22 +0200
Subject: [PATCH] Working getChannelPlaylists

---
 YoutubeConfig.json |   2 +-
 YoutubeScript.js   | 110 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 108 insertions(+), 4 deletions(-)

diff --git a/YoutubeConfig.json b/YoutubeConfig.json
index 10f5fe1..37c8daf 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": 178,
+	"version": 179,
 	"iconUrl": "./youtube.png",
 	"id": "35ae969a-a7db-11ed-afa1-0242ac120002",
 
diff --git a/YoutubeScript.js b/YoutubeScript.js
index 654afc0..2044085 100644
--- a/YoutubeScript.js
+++ b/YoutubeScript.js
@@ -7,6 +7,7 @@ const URL_CONTEXT_M = "https://m.youtube.com";
 
 const URL_CHANNEL_VIDEOS = "/videos";
 const URL_CHANNEL_STREAMS = "/streams";
+const URL_CHANNEL_PLAYLISTS = "/playlists";
 const URL_SEARCH_SUGGESTIONS = "https://suggestqueries-clients6.youtube.com/complete/search?client=youtube&gs_ri=youtube&ds=yt&q=";
 const URL_SEARCH = "https://www.youtube.com/youtubei/v1/search";
 const URL_BROWSE = "https://www.youtube.com/youtubei/v1/browse";
@@ -1016,6 +1017,29 @@ source.getChannelContents = (url, type, order, filters) => {
 	return new RichGridPager(tab, contextData, useAuth, useAuth);
 };
 
+source.getChannelPlaylists = (url) => {
+
+	const targetTab = "Playlists";
+	const useAuth = bridge.isLoggedIn() && !!_settings?.authChannels;
+	if(useAuth)
+		log("USING AUTH FOR CHANNEL");
+
+	const initialData = requestInitialData(url + URL_CHANNEL_PLAYLISTS, useAuth, useAuth);
+	if(!initialData)
+	    throw new ScriptException("No channel data found for: " + url);
+	const channel = extractChannel_PlatformChannel(initialData, url);
+	const contextData = {
+		authorLink: new PlatformAuthorLink(new PlatformID(PLATFORM, channel.id.value, config.id, PLATFORM_CLAIMTYPE), channel.name, channel.url, channel.thumbnail)
+	};
+	const tabs = extractPage_Tabs(initialData, contextData);
+	
+	const tab = tabs.find(x=>x.title == targetTab);
+	if(!tab) 
+		return new PlaylistPager([], false);
+
+	return new RichGridPlaylistPager(tab, contextData, useAuth, useAuth);
+}
+
 source.getPeekChannelTypes = () => {
 	return [Type.Feed.Videos, Type.Feed.Mixed];
 }
@@ -1745,6 +1769,48 @@ class RichGridPager extends VideoPager {
 		return this;
 	}
 }
+class RichGridPlaylistPager extends PlaylistPager {
+	constructor(tab, context, useMobile = false, useAuth = false) {
+		super(tab.playlists, tab.videos.length > 0 && !!tab.continuation, context);
+		this.continuation = tab.continuation;
+		this.useMobile = useMobile;
+		this.useAuth = useAuth;
+	}
+	
+	nextPage() {
+		this.context.page = this.context.page + 1;
+		if(this.continuation) {
+			const newData = validateContinuation(()=>requestBrowse({
+				continuation: this.continuation.token
+			}, !!this.useMobile, !!this.useAuth));
+			if(newData && newData.length > 0) {
+				const fakeRichGrid = {
+					contents: newData
+				};
+				const newItemSection = extractRichGridRenderer_Shelves(fakeRichGrid, this.context);
+
+				if(newItemSection.playlists && newItemSection.playlists.length == 0 && newItemSection.shelves && newItemSection.shelves.length > 0) {
+				    if(IS_TESTING)
+				        console.log("No playlists in root found, checking shelves", newItemSection);
+				    let vids = [];
+				    for(let i = 0; i < newItemSection.shelves.length; i++) {
+				        const shelf = newItemSection.shelves[i];
+                        vids = vids.concat(shelf.playlists);
+				    }
+				    newItemSection.playlists = vids;
+				}
+
+				if(newItemSection.playlists)
+					return new RichGridPager(newItemSection, this.context, this.useMobile, this.useAuth);
+			}
+			else
+				log("Call [RichGridPager.nextPage] continuation gave no appended items, setting empty page with hasMore to false");
+		}
+		this.hasMore = false;
+		this.results = [];
+		return this;
+	}
+}
 class SearchItemSectionVideoPager extends VideoPager {
 	constructor(itemSection) {
 		super(itemSection.videos, itemSection.videos.length > 0 && !!itemSection.continuation);
@@ -3037,7 +3103,7 @@ function extractRichGridRenderer_Shelves(richGridRenderer, contextData) {
 				continuation = extractContinuationItemRenderer_Continuation(renderer, contextData);
 			},
 			itemSectionRenderer(renderer) {
-		        const items = extractItemSectionRenderer_Shelves(renderer);
+		        const items = extractItemSectionRenderer_Shelves(renderer, contextData);
 
 		        if(items.shelves)
 		            shelves = shelves.concat(items.shelves);
@@ -3069,7 +3135,7 @@ function extractSectionListRenderer_Sections(sectionListRenderer, contextData) {
 		const item = contents[i];
 		switchKey(item, {
 			itemSectionRenderer(renderer) {
-				const items = extractItemSectionRenderer_Shelves(renderer);
+				const items = extractItemSectionRenderer_Shelves(renderer, contextData);
 				if(items.videos.length > 0)
 					videos.push(...items.videos);
 				if(items.channels.length > 0)
@@ -3128,6 +3194,11 @@ function extractItemSectionRenderer_Shelves(itemSectionRenderer, contextData) {
 			    if(shelf)
 				    shelves.push(shelf);
 			},
+			gridRenderer(renderer) {
+				const shelf = extractGridRenderer_Shelf(renderer, contextData);
+				if(shelf.playlists.length > 0)
+					playlists.push(...shelf.playlists);
+			},
 			default() {
 				const video = switchKeyVideo(item, contextData);
 				if(video)
@@ -3144,6 +3215,35 @@ function extractItemSectionRenderer_Shelves(itemSectionRenderer, contextData) {
 		playlists: playlists.filter(x=>x != null)
 	};
 }
+function extractGridRenderer_Shelf(gridRenderer, contextData) {
+	const contents = gridRenderer.items;
+	let shelves = [];
+	let videos = [];
+	let channels = [];
+	let playlists = [];
+
+	contents.forEach((item)=>{
+		switchKey(item, {
+			gridPlaylistRenderer(renderer) {
+			    const playlist = extractPlaylistRenderer_Playlist(renderer, contextData);
+			    if(playlist)
+			        playlists.push(playlist);
+			},
+			default() {
+				const video = switchKeyVideo(item, contextData);
+				if(video)
+					videos.push(video);
+			}
+		});
+		
+	});
+
+	return {
+		videos: videos.filter(x=>x != null),
+		channels: channels.filter(x=>x != null),
+		playlists: playlists.filter(x=>x != null)
+	};
+}
 function switchKeyVideos(contents, contextData) {
 	let videos = [];
 	for(let content of contents) {
@@ -3396,11 +3496,15 @@ function extractPlaylistRenderer_Playlist(playlistRenderer, contextData) {
 	const author = (contextData && contextData.authorLink) ?
 		contextData.authorLink : extractRuns_AuthorLink(playlistRenderer.shortBylineText?.runs);
 
+	let thumbnail = (playlistRenderer.thumbnails && playlistRenderer.thumbnails.length > 0) ? extractThumbnail_BestUrl(playlistRenderer.thumbnails[0]) : null;
+	if(!thumbnail && playlistRenderer.thumbnail)
+		thumbnail = extractThumbnail_BestUrl(playlistRenderer.thumbnail);
+
     return new PlatformPlaylist({
 		id: new PlatformID(PLATFORM, playlistRenderer.playlistId, config.id),
 		author: author,
         name: extractText_String(playlistRenderer.title),
-        thumbnail: (playlistRenderer.thumbnails && playlistRenderer.thumbnails.length > 0) ? extractThumbnail_BestUrl(playlistRenderer.thumbnails[0]) : null,
+        thumbnail: thumbnail,
         url: URL_PLAYLIST + playlistRenderer.playlistId,
         videoCount: extractFirstNumber_Integer(extractText_String(playlistRenderer.videoCountText)),
     });
-- 
GitLab