Last active
January 28, 2025 23:26
-
-
Save Dorifor/c63bde5341aa64f18879680f183faa33 to your computer and use it in GitHub Desktop.
Get all videos from a Youtube playlist page
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// if you also need the data from each video element of this playlist | |
/** | |
* @typedef VideoElement | |
* @property { string } id a#video-title[href] > get ?v | |
* @property { string } title a#video-title | |
* @property { string } channel_name yt-formatted-string.ytd-channel-name | |
* @property { string } channel_url yt-formatted-string.ytd-channel-name > a[href] | |
* @property { string } thumbnail_url yt-image.ytd-thumbnail>img[src] | |
* @property { string } duration span.ytd-thumbnail-overlay-time-status-renderer | |
* @property { string } watch_url a#video-title[href] | |
* @property { string } playlist_id a#video-title[href] > get ?list | |
*/ | |
/** | |
* extract needed data from each Node | |
* @param { HTMLElement[] } videoNodes | |
*/ | |
function getVideosData(videoNodes) { | |
/** @type { VideoElement[] } */ | |
const videos = [] | |
videoNodes.forEach(videoNode => { | |
const channelElement = videoNode.querySelector('yt-formatted-string.ytd-channel-name') | |
const videoUrl = new URL(videoNode.querySelector('a#video-title').href) | |
/** @type { VideoElement } */ | |
const newVideo = { | |
id: videoUrl.searchParams.get('v'), | |
title: videoNode.querySelector('a#video-title').textContent, | |
channel_name: channelElement.textContent, | |
channel_url: channelElement.querySelector('a').href, | |
thumbnail_url: videoNode.querySelector('yt-image.ytd-thumbnail>img').getAttribute('src'), | |
duration: videoNode.querySelector('span.ytd-thumbnail-overlay-time-status-renderer').textContent, | |
watch_url: videoUrl.href, | |
playlist_id: videoUrl.searchParams.get('list') | |
} | |
videos.push(newVideo); | |
}) | |
console.table(videos); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// if you need to fetch every video of a playlist (from its page) when you don't have access to the API | |
// how it works : | |
// 1. scroll to the end if there's a need ( videos > 100) | |
// 2. it'll load the next videos in the DOM (and if there's still more repeat step 1) | |
// 3. it stops when there's no more to load | |
// 4. you get all video DOM elements (line 34) | |
function isContinuationInsideContents() { | |
const continuation = document.querySelector('ytd-continuation-item-renderer'); | |
if (continuation == null) | |
return false; | |
return continuation.parentNode === document.querySelector('ytd-playlist-video-list-renderer > #contents'); | |
} | |
function scrollToContinuation() { | |
const continuation = document.querySelector('ytd-continuation-item-renderer'); | |
continuation.scrollIntoView() | |
} | |
function addContinuationCallback() { | |
const contents = document.querySelector('ytd-playlist-video-list-renderer > #contents'); | |
console.log(`number of videos loaded: ${contents.childElementCount - 1}`) | |
var obs = new MutationObserver((mutationRecords, observer) => { | |
if (mutationRecords[0].removedNodes.length <= 0) return; | |
console.log(`number of videos loaded: ${contents.childElementCount}`) | |
if (isContinuationInsideContents()) { | |
scrollToContinuation(); | |
} else { | |
console.log('FINISHED GATHERING !') | |
// YOUR VIDEOS ARE HERE | |
const videoNodes = document.querySelectorAll('ytd-playlist-video-renderer'); | |
observer.disconnect(); | |
} | |
}); | |
obs.observe(contents, { childList: true, subtree: false }); | |
} | |
addContinuationCallback(); | |
scrollToContinuation(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment