// ==UserScript== // @name YouTube All Videos Playlists (YAVP) // @namespace c0d3r // @license MIT // @version 0.1 // @description Adds buttons for various channel playlists: All Videos, Shorts, Streams, Members-Only // @author c0d3r // @match https://www.youtube.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @run-at document-idle // @grant none // @downloadURL none // ==/UserScript== // custom buttons use native classes and tags so both Light and Dark theme is supported function btnHtml(text, title, href) { return `
${text}
`; } function btnBlock(data, chanId) { const root = 'https://www.youtube.com/playlist?list='; let html = '
'; // only add relevant buttons based on available channel tabs // for IDs see https://github.com/RobertWesner/YouTube-Play-All/blob/main/documentation/available-lists.md data.response.contents.twoColumnBrowseResultsRenderer.tabs.forEach(function (tab) { if (tab.hasOwnProperty('tabRenderer')) { // check for URLs instead of tab titles because languages might be different const url = tab.tabRenderer.endpoint.commandMetadata.webCommandMetadata.url; if (url.endsWith('/videos')) { html += btnHtml('VAll', 'All Videos', root + 'UULF' + chanId); html += btnHtml('VPop', 'Popular Videos', root + 'UULP' + chanId); } else if (url.endsWith('/shorts')) { html += btnHtml('SAll', 'All Shorts', root + 'UUSH' + chanId); html += btnHtml('SPop', 'Popular Shorts', root + 'UUPS' + chanId); } else if (url.endsWith('/streams')) { html += btnHtml('LAll', 'All Streams', root + 'UULV' + chanId); html += btnHtml('LPop', 'Popular Streams', root + 'UUPV' + chanId); } } }); // add Members-Only button only if Join button exists let addMemberBtn = false; const head = data.response.header; // when not logged in if (head.hasOwnProperty('c4TabbedHeaderRenderer') && head.c4TabbedHeaderRenderer.hasOwnProperty('sponsorButton')) { addMemberBtn = true; } // when logged in if (head.hasOwnProperty('pageHeaderRenderer')) { const actionRows = head.pageHeaderRenderer.content.pageHeaderViewModel.actions.flexibleActionsViewModel.actionsRows; actionRows.forEach(function (row) { row.actions.forEach(function (action) { if (action.hasOwnProperty('buttonViewModel') && action.buttonViewModel.targetId === 'sponsorships-button') { addMemberBtn = true; } }); }); } if (addMemberBtn) { html += btnHtml('Mem', 'Members-Only Videos', root + 'UUMO' + chanId); } html += '
'; return html; } (function () { 'use strict'; let oldChanId; // native event that fires on each page or tab change window.addEventListener('yt-navigate-finish', function () { const path = window.location.pathname; // run only on channel pages, see https://support.google.com/youtube/answer/6180214 if (path.startsWith('/channel/') || path.startsWith('/@') || path.startsWith('/c/') || path.startsWith('/user/')) { const pageMan = document.querySelector('#page-manager'); // native function that returns useful data const pageData = pageMan.getCurrentData(); // get channel ID, but remove UC from the start const chanId = pageData.response.metadata.channelMetadataRenderer.externalId.substring(2); // proceed only if a new channel is opened, don't react to tab changes if (oldChanId !== chanId) { // remove the wrapper if it exists const wrapper = document.querySelector('#yavp-wrap'); if (wrapper) { wrapper.remove(); } const tabBlock = document.querySelector('#tabsContainer'); if (tabBlock) { // tabs container might already exist in DOM tabBlock.insertAdjacentHTML('afterEnd', btnBlock(pageData, chanId)); } else { // Chrome needs to wait for this block to be added at first let stopObserve = false; const observer = new MutationObserver(function (mutList) { for (let i = 0; i < mutList.length; i++) { for (let j = 0; j < mutList[i].addedNodes.length; j++) { if (mutList[i].addedNodes[j].tagName === 'TP-YT-APP-HEADER-LAYOUT') { const tabs = mutList[i].addedNodes[j].querySelector('#tabsContainer'); tabs.insertAdjacentHTML('afterEnd', btnBlock(pageData, chanId)); observer.disconnect(); stopObserve = true; break; } } if (stopObserve) { break; } } }); observer.observe(pageMan, { subtree: true, childList: true }); } // save channel ID for future checks oldChanId = chanId; } } }); })();