Last active
July 2, 2022 08:30
-
-
Save sidneys/6756166a781bd76b97eeeda9fb0bc0c1 to your computer and use it in GitHub Desktop.
Greasemonkey | YouTube (New Design) | Expand All Video Comments
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: 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() | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
sadly, does not always work on Chromium 94.0.4606