// ==UserScript==
// @name YT: not interested in one click
// @description Hover a thumbnail on youtube.com and click an icon at the right: "Not interested" and "Don't recommend channel"
// @version 1.2.11
//
// @match https://www.youtube.com/*
//
// @noframes
// @grant none
//
// @author wOxxOm
// @namespace wOxxOm.scripts
// @license MIT License
// ==/UserScript==
'use strict';
const ME = 'yt-one-click-dismiss';
const THUMB_TAG = 'ytd-thumbnail';
const PREVIEW_TAG = 'ytd-video-preview';
const PREVIEW_PARENT = '#media-container'; // parent for the added buttons; must be visible
const CLOSEST = [THUMB_TAG, PREVIEW_TAG].join(',');
const COMMANDS = {
NOT_INTERESTED: {block: 'video', text: 'Not interested'},
REMOVE: {block: 'channel', text: "Don't recommend channel"},
DELETE: {block: 'unwatch', text: "Remove from 'Watch later'"},
};
let STYLE;
let inlinable;
addEventListener('mouseover', onHover);
addEventListener('yt-action', ({detail: d}) => {
if (d.actionName === 'yt-set-cookie-command') {
inlinable = !d.args[0].setCookieCommand.value;
}
});
function onHover(e, delayed) {
const el = e.target.closest(CLOSEST); if (!el) return;
const inline = el.localName === PREVIEW_TAG;
const thumb = inline ? $(THUMB_TAG, el) : !el.getElementsByClassName(ME)[0] && el;
if (thumb && (
inline ||
delayed ||
!(inlinable != null ? inlinable : getProp($(PREVIEW_TAG), '__data.inlinePreviewIsEnabled')) ||
setTimeout(getInlineState, 250, e) && false
)) {
if (inline) {
addButtons($(PREVIEW_PARENT, el),
getProp(el, 'mediaRenderer', '') ||
getProp(el, 'opts.mediaRenderer', ''));
} else {
addButtons(thumb, thumb);
}
}
}
async function onClick(e) {
const me = this.el; if (e.target !== me) return;
e.stopPropagation();
let index, menu;
for (let more, el = me[ME]; el && el.localName !== 'ytd-app'; el = el.parentElement) {
if ((more = $('.dropdown-trigger', el))) {
await 0;
index = STYLE.sheet.insertRule('ytd-menu-popup-renderer:not(#\\0) { opacity: 0 !important }');
more.dispatchEvent(new Event('click'));
menu = await waitFor('ytd-popup-container tp-yt-iron-dropdown');
break;
}
}
if (!menu) {
console.warn('No "..." menu button!');
return;
}
if (!isMenuReady(menu)) {
await new Promise(resolve => new MutationObserver((_, mo) => {
if (isMenuReady(menu)) {
mo.disconnect();
resolve();
}
}).observe(menu, {attributes: true, attributeFilter: ['style']}));
}
await new Promise(setTimeout);
try {
for (const el of $('[role="listbox"]', menu).children) {
if (me.dataset.block === (COMMANDS[getProp(el, '__data.data.icon.iconType')] || {}).block) {
el.click();
break;
}
}
} catch (e) {}
await new Promise(setTimeout);
document.body.click();
await new Promise(setTimeout);
STYLE.sheet.deleteRule(index);
}
function addButtons(parent, thumb) {
const elems = [];
for (const item of getProp(thumb, '__data.data.menu.menuRenderer.items') || []) {
const type = getProp(item, 'menuServiceItemRenderer.icon.iconType');
const data = COMMANDS[type]; if (!data) continue;
const old = $(`.${ME}[data-block="${data.block}"]`); if (old && old[ME] === thumb) continue;
const el = document.createElement('div');
el[ME] = thumb;
el.className = ME;
el.dataset.block = data.block;
el.title = getProp(item, 'menuServiceItemRenderer.text.runs').map(r => r.text).join('')
|| data.text;
parent.addEventListener('mousedown', {el, handleEvent: onClick}, true);
elems.push(el);
if (old) old.remove();
}
parent.append(...elems);
if (!STYLE) initStyle();
}
function getInlineState(e) {
if (e.target.matches(':hover') && !$(PREVIEW_TAG).getBoundingClientRect().width) {
onHover(e, true);
}
}
function getProp(obj, path) {
if (obj)
return path.split('.').reduce((res, name) => res && res[name],
(obj = obj.wrappedJSObject || obj).inst || obj);
}
function isMenuReady(menu) {
return menu.style.cssText.includes('px;');
}
function $(sel, base = document) {
return base.querySelector(sel);
}
async function waitFor(sel, base = document) {
return $(sel, base) || new Promise(resolve => {
new MutationObserver((_, o) => {
const el = $(sel, base); if (!el) return;
o.disconnect();
resolve(el);
}).observe(base, {childList: true, subtree: true});
});
}
function initStyle() {
STYLE = document.createElement('style');
STYLE.textContent = /*CSS*/ `
${PREVIEW_PARENT} .${ME} {
opacity: .5;
}
${PREVIEW_PARENT} .${ME},
${THUMB_TAG}:hover .${ME} {
display: block;
}
.${ME} {
display: none;
position: absolute;
width: 16px;
height: 16px;
border-radius: 100%;
border: 2px solid #fff;
right: 8px;
background: #0006;
box-shadow: .5px .5px 7px #000;
cursor: pointer;
opacity: .75;
z-index: 11000;
}
${PREVIEW_PARENT} .${ME}:hover,
.${ME}:hover {
opacity: 1;
}
.${ME}:active {
color: yellow;
}
.${ME}[data-block] { top: 70px; }
.${ME}[data-block="channel"] { top: 100px; }
${PREVIEW_TAG} .${ME}[data-block] { top: 50px; }
${PREVIEW_TAG} .${ME}[data-block="channel"] { top: 80px; }
.ytd-playlist-panel-video-renderer .${ME}[data-block="unwatch"],
.ytd-playlist-video-renderer .${ME}[data-block="unwatch"] {
top: 15px;
}
ytd-compact-video-renderer .${ME}[data-block] {
top: 70px;
right: 7px;
box-shadow: .5px .5px 4px 6px #000;
background: #000;
}
ytd-compact-video-renderer .${ME}[data-block="channel"] {
right: 37px;
}
.${ME}::before {
position: absolute;
content: '';
top: -8px;
left: -6px;
width: 32px;
height: 30px;
}
.${ME}::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 0;
margin: auto;
border: none;
border-bottom: 2px solid #fff;
}
.${ME}[data-block="video"]::after {
transform: rotate(45deg);
}
.${ME}[data-block="channel"]::after {
margin: auto 3px;
}
`.replace(/;/g, '!important;');
document.head.appendChild(STYLE);
}