Skip to content

Instantly share code, notes, and snippets.

@sidneys
Last active July 2, 2022 08:30
Show Gist options
  • Save sidneys/6756166a781bd76b97eeeda9fb0bc0c1 to your computer and use it in GitHub Desktop.
Save sidneys/6756166a781bd76b97eeeda9fb0bc0c1 to your computer and use it in GitHub Desktop.
Greasemonkey | YouTube (New Design) | Expand All Video Comments
// ==UserScript==
// @name YouTube: Expand All Video Comments
// @namespace org.sidneys.userscripts
// @homepage https://gist.githubusercontent.com/sidneys/6756166a781bd76b97eeeda9fb0bc0c1/raw/
// @version 4.7.8
// @description Adds a "Expand all" button to video comments which expands every comment and replies - no more clicking "Read more".
// @author sidneys
// @icon https://www.youtube.com/favicon.ico
// @noframes
// @match http*://www.youtube.com/*
// @require https://greasyfork.org/scripts/38888-greasemonkey-color-log/code/Greasemonkey%20%7C%20Color%20Log.js
// @require https://greasyfork.org/scripts/374849-library-onelementready-es7/code/Library%20%7C%20onElementReady%20ES7.js
// @run-at document-end
// @grant GM_addStyle
// ==/UserScript==
/* global Debug, onElementReady */
/**
* ESLint
* @global
*/
Debug = false
/**
* Applicable URL paths
* @default
* @constant
*/
const urlPathList = [
'/watch'
]
/**
* Inject Stylesheet
*/
let injectStylesheet = () => {
console.debug('injectStylesheet')
GM_addStyle(`
/* =======================================
ELEMENTS
======================================= */
/* Button: Expand all Comments
--------------------------------------- */
.expand-all-comments-button
{
padding: 0;
align-self: start;
margin-left: var(--ytd-margin-4x, 8px);
margin-top: 0;
}
.expand-all-comments-button #checkboxLabel
{
margin-left: var(--ytd-margin-2x, 8px);
padding-left: 0;
display: inline-flex;
}
.busy .expand-all-comments-button #checkboxContainer,
.busy .expand-all-comments-button #checkboxLabel
{
animation: var(--animation-busy-on);
}
/* Button: Expand all Comments
Spinner
--------------------------------------- */
.expand-all-comments-button #checkboxLabel::after
{
background-image: url("https://i.imgur.com/z4V4Os8.png");
content: "";
background-size: 100%;
background-repeat: no-repeat;
margin-left: var(--ytd-margin-2x, 8px);
height: var(--ytd-margin-4x, 16px);
width: var(--ytd-margin-4x, 16px);
}
.expand-all-comments-button #checkboxLabel,
.expand-all-comments-button #checkboxLabel::after
{
transition: filter 1000ms ease-in-out;
}
:not(.busy) .expand-all-comments-button #checkboxLabel::after
{
filter: opacity(0);
}
.busy .expand-all-comments-button #checkboxLabel::after
{
filter: opacity(1);
}
/* =======================================
ANIMATIONS
======================================= */
:root
{
--animation-busy-on: 'busy-on' 500ms ease-in-out 1000ms 1 normal forwards running;
}
@keyframes busy-on {
from {
pointer-events: none;
cursor: default;
}
to {
filter: saturate(0.1);
color: hsla(0deg, 0%, 100%, 0.5);
}
}
`)
}
/**
* Set global busy mode
* @param {Boolean} isBusy - Yes/No
* @param {String=} selector - Contextual element selector
*/
let setBusy = (isBusy, selector = 'ytd-comments') => {
// console.debug('setBusy', 'isBusy:', isBusy)
let element = document.querySelector(selector)
if (isBusy === true) {
element.classList.add('busy')
return
} else {
element.classList.remove('busy')
}
}
/**
* Get Button element
* @returns {Boolean} - On/Off
*/
let getButtonElement = () => document.querySelector('.expand-all-comments-button')
/**
* Get Toggle state
* @returns {Boolean} - On/Off
*/
let getToggleState = () => Boolean(getButtonElement() && getButtonElement().checked)
/**
* Expand all comments
*/
let expandAllComments = () => {
console.debug('expandAllComments')
// Look for "View X replies" buttons in comment section
onElementReady('ytd-comment-replies-renderer #more-replies.ytd-comment-replies-renderer', false, (buttonElement) => {
// Abort if toggle disabled
if (!getToggleState()) { return }
/** @listens buttonElement:Event#click */
// buttonElement.addEventListener('click', () => setBusy(false), { once: true, passive: true })
// Busy = yes
// setBusy(true)
// Click button
buttonElement.click()
})
// Look for "Read More" buttons in comment section
onElementReady('ytd-comments tp-yt-paper-button.ytd-expander#more:not([hidden])', false, (buttonElement) => {
// Abort if toggle disabled
if (!getToggleState()) { return }
/** @listens buttonElement:Event#click */
// buttonElement.addEventListener('click', () => setBusy(false), { once: true, passive: true })
// Busy = yes
// setBusy(true)
// Click button
buttonElement.click()
})
}
/**
* Check if the toggle is enabled, if yes, start expanding
*/
let tryExpandAllComments = () => {
console.debug('tryExpandAllComments')
const toggleState = getToggleState()
console.debug('toggle state:', toggleState)
// Abort if toggle disabled
if (!toggleState) { return }
expandAllComments()
}
/**
* Render button: 'Expand all Comments'
* @param {Element} element - Container element
*/
let renderButton = (element) => {
console.debug('renderButton')
const buttonElement = document.createElement('tp-yt-paper-checkbox')
buttonElement.className = 'expand-all-comments-button'
buttonElement.innerHTML = `
<div id="icon-label" class="yt-dropdown-menu">
Expand all Comments
</div>
`
// Add button
element.appendChild(buttonElement)
// Handle button toggle
buttonElement.onchange = tryExpandAllComments
// Status
console.debug('rendered button')
}
/**
* Init
*/
let init = () => {
console.info('init')
// Verify URL path
if (!urlPathList.some(urlPath => window.location.pathname.startsWith(urlPath))) { return }
// Add Stylesheet
injectStylesheet()
// Wait for menu container
onElementReady('ytd-comments ytd-comments-header-renderer > #title', false, (element) => {
console.debug('onElementReady', 'ytd-comments ytd-comments-header-renderer > #title')
// Render button
renderButton(element)
})
// // Wait for variable section container
// onElementReady('ytd-item-section-renderer#sections.style-scope.ytd-comments > #contents', false, (element) => {
// console.debug('onElementReady', 'element:', '#contents')
//
// /**
// * YouTube: Detect "Load More" stuff
// * @listens ytd-item-section-rendere:Event#yt-load-next-continuation
// */
// element.parentElement.addEventListener('yt-load-next-continuation', (event) => {
// console.debug('ytd-item-section-renderer#yt-load-next-continuation')
//
// const currentTarget = event.currentTarget
// const shownItems = currentTarget && currentTarget.__data && currentTarget.__data.shownItems || []
// const shownItemsCount = shownItems.length
//
// // DEBUG
// // console.debug('currentTarget.__data:')
// // console.dir(currentTarget.__data)
//
// // Probe whether this is still the initial item batch, if yes, skip
// if (shownItemsCount === 0) { return }
//
// tryExpandAllComments()
// })
// })
}
/**
* YouTube: Detect in-page navigation
* @listens window:Event#yt-navigate-finish
*/
window.addEventListener('yt-navigate-finish', () => {
console.debug('window#yt-navigate-finish')
init()
})
@krem
Copy link

krem commented Oct 23, 2021

sadly, does not always work on Chromium 94.0.4606

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment