Last active
April 5, 2022 00:11
-
-
Save neonfuz/ea14fe2ad32c4caa860f36bb521b9a60 to your computer and use it in GitHub Desktop.
youtube quick playlist button userscript
This file contains 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
// ==UserScript== | |
// @name youtube quick playlist button | |
// @namespace http://neonfuz.xyz/ | |
// @author neonfuz | |
// @version 0.9 | |
// @match https://www.youtube.com/* | |
// @icon https://www.google.com/s2/favicons?domain=youtube.com | |
// @updateURL https://gist.github.com/neonfuz/ea14fe2ad32c4caa860f36bb521b9a60/raw/youtube-quick-playlist.user.js | |
// @downloadURL https://gist.github.com/neonfuz/ea14fe2ad32c4caa860f36bb521b9a60/raw/youtube-quick-playlist.user.js | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
const buttonProps = { | |
className: 'quick-playlist-button', | |
innerHTML: '+', | |
onclick: function () { | |
const popupContainer = document.querySelector('ytd-popup-container'); | |
popupContainer.style.opacity = 0; | |
setTimeout(() => { | |
// Close existing dialog | |
const dialog = document.querySelector('tp-yt-paper-dialog'); | |
if (dialog !== null && dialog.style.display !== 'none') { | |
dialog.querySelector('#close-button').click(); | |
} | |
// Return opacity on next event cycle | |
setTimeout(() => {popupContainer.style.opacity = 1}); | |
// Click save to playlist button | |
Array.prototype.find.call( | |
document.querySelectorAll('ytd-menu-service-item-renderer'), | |
e => e.innerText === 'Save to playlist' // TODO multilingual | |
).click(); | |
}, 0); | |
}, | |
}; | |
function elem(tag, props) { return Object.assign(document.createElement(tag), props); } | |
function debounce(fn, time) { | |
let timeout = null; | |
return (...args) => { | |
clearTimeout(timeout); | |
timeout = setTimeout(() => fn(...args), time); | |
} | |
} | |
class LogUnique { | |
constructor(time) { | |
this.seen = new Map(); | |
this.log = debounce(console.log, time); | |
} | |
get(item) { return this.seen.get(item); } | |
add(key, val) { | |
this.log(this.seen); | |
return this.seen.set( | |
key, | |
[ ...(this.seen.get(key) ?? []), val ] | |
); | |
} | |
} | |
const seenUnknownMenuClasses = new LogUnique(3000); | |
function appendButton(menu) { | |
if (menu.querySelector('.' + buttonProps.className)) return; | |
switch (menu.className.split(' ')[1]) { | |
// Useless: | |
case 'ytd-shelf-renderer': | |
case 'ytd-rich-shelf-renderer': | |
case 'ytd-playlist-sidebar-primary-info-renderer': | |
case 'ytd-comment-renderer': | |
return; //skip | |
// TODO fix these: | |
case 'ytd-video-renderer': // in youtube history | |
case 'ytd-video-primary-info-renderer': // under each playing video | |
case 'ytd-engagement-panel-title-header-renderer': // what is this? | |
case 'ytd-notification-renderer': // notification videos | |
return; //skip | |
case 'ytd-rich-grid-media': | |
case 'ytd-grid-video-renderer': | |
case 'ytd-playlist-video-renderer': | |
case 'ytd-compact-video-renderer': | |
case 'ytd-video-preview': | |
case 'ytd-playlist-panel-video-renderer': | |
menu = menu.querySelector('yt-icon-button'); | |
Object.assign(menu.style, {display: 'flex', 'flex-direction': 'column', 'align-items': 'center'}); | |
menu.appendChild(elem('button', buttonProps)); | |
break; | |
default: | |
seenUnknownMenuClasses.add(menu.className, menu); | |
} | |
} | |
document.body.appendChild(elem('style', {innerHTML: ` | |
.${buttonProps.className} { | |
color: var(--yt-spec-text-primary); | |
font-size: calc(var(--yt-icon-width)/2); | |
cursor: pointer; | |
background: none; | |
border: none; | |
} | |
`})); | |
document.body.querySelectorAll('YTD-MENU-RENDERER').forEach(appendButton); | |
new MutationObserver(function(mutationList, observer) { | |
for (const mutation of mutationList) { | |
if (mutation.target.tagName === 'YTD-MENU-RENDERER') { | |
appendButton(mutation.target); | |
} | |
} | |
}).observe(document.body, {childList: true, subtree: true}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment