Skip to content
This repository was archived by the owner on Jan 9, 2024. It is now read-only.

Added channelPlaylists concept #106

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ Returns a playlist ID from a YouTube URL. Can be called with the playlist ID dir

* returns a promise resolving into a string containing the id

### ytpl.channelPlaylists(channelID)

Returns the playlists created by a specific user or channel. Watch out: many returned fields are set to `null` as they are
not provided by Youtube. You will have to fetch those manually after this call if you need to.


# Related / Works well with

Expand Down
7 changes: 7 additions & 0 deletions example/update_examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ const main = async() => {
FS.writeFileSync('./example_output.txt', saveString);
};
main();


const main2 = async () => {
const cn = 'https://www.youtube.com/c/NoCopyrightSounds'
console.log(await YTPL.channelPlaylists(cn))
}
// main2();
63 changes: 63 additions & 0 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,66 @@ const toChannelList = async ref => {
if (channelMatch) return `UU${channelMatch[1]}`;
throw new Error(`unable to resolve the ref: ${ref}`);
};


main.channelPlaylists = async(linkOrId, options, rt = 3) => {
if (rt === 0) throw new Error('Unable to find JSON!');

const searchres = await main(linkOrId); // also validates the link
const cnurl = searchres.author.url; // You can't really fuck with channel aliases
const ref = cnurl + '/playlists';
const opts = UTIL.checkArgs(cnurl, options);
const body = await MINIGET(ref, opts.requestOptions).text();
const parsed = UTIL.parseBody(body, options);

// Retry if unable to find json => most likely old response
if (!parsed.json) return main.channelPlaylists(linkOrId, opts, rt - 1);

// Pass Errors from the API
if (parsed.json.alerts && !parsed.json.contents) {
let error = parsed.json.alerts.find(a => a.alertRenderer && a.alertRenderer.type === 'ERROR');
if (error) throw new Error(`API-Error: ${UTIL.parseText(error.alertRenderer.text)}`);
}

const tabs = parsed.json.contents.twoColumnBrowseResultsRenderer.tabs;
const tab = tabs.find(t => t.tabRenderer.title === 'Playlists');
const slrc = tab.tabRenderer.content.sectionListRenderer.contents;
const isrc = slrc.find(x => Object.keys(x)[0] === 'itemSectionRenderer').itemSectionRenderer.contents;
const gr = isrc.find(x => Object.keys(x)[0] === 'gridRenderer'); // users only?
const sr = isrc.find(x => Object.keys(x)[0] === 'shelfRenderer'); // channels only?

if (!gr && !sr) {
// Channel has no playlists?
return [];
}

let playlists;
if (gr)
playlists = gr.gridRenderer.items.map(i => i.gridPlaylistRenderer);
else
playlists = sr.shelfRenderer.content.horizontalListRenderer.items.map(i => i.gridPlaylistRenderer);


return playlists.map(p => {
const thumbnails = (
p.thumbnailRenderer.playlistVideoThumbnailRenderer ||
p.thumbnailRenderer.playlistCustomThumbnailRenderer
).thumbnail.thumbnails.sort((a, b) => b.width - a.width)

return {
id: p.playlistId,
url: `${BASE_PLIST_URL}list=${p.playlistId}`,
title: UTIL.parseText(p.title),
estimatedItemCount: UTIL.parseIntegerFromText(p.videoCountShortText),
views: null, // not available
thumbnails,
bestThumbnail: thumbnails[0],
lastUpdated: null, // not available
description: null, // not available
visibility: 'everyone', // otherwise you wouldn't have it at all
author: null, // playlistSidebarSecondaryInfoRenderer is not available
items: null, // Let's not preload since it could be massive
continuation: null,
}
});
};
Loading