YT: not interested in one click

Hover a thumbnail on youtube.com and click an icon at the right: "Not interested" and "Don't recommend channel"

Från och med 2023-06-13. Se den senaste versionen.

// ==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.1
//
// @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;

addEventListener('mouseover', onHover);

function onHover(e, isDelayed) {
  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 ||
    isDelayed ||
    !getProp(el.closest('ytd-rich-grid-media'), 'isInlinePreviewEligible') ||
    setTimeout(getInlineState, 500, e) && false
  )) {
    addButtons(el,
      inline ? $(PREVIEW_PARENT, el) : thumb,
      inline ? getProp(el, 'opts.mediaRenderer') : thumb);
  }
}

async function onClick(e) {
  e.stopPropagation();
  let index;
  try {
    index = STYLE.sheet.insertRule('ytd-menu-popup-renderer { display: none !important }');
    for (let more, el = this[ME]; el && el.localName !== 'ytd-app'; el = el.parentElement) {
      if ((more = $('.dropdown-trigger', el))) {
        await 0;
        more.dispatchEvent(new Event('click'));
        await new Promise(setTimeout);
        break;
      }
    }
    for (const el of $('ytd-menu-popup-renderer [role="listbox"]').children) {
      if (this.dataset.block === (COMMANDS[getProp(el, '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(renderer, parent, thumb) {
  const elems = [];
  for (const item of getProp(thumb, '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;
    el.addEventListener('click', 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) {
  return obj && path.split('.').reduce((res, name) => res && res[name],
    (obj.wrappedJSObject || obj).__data || obj);
}

function $(sel, base = document) {
  return base.querySelector(sel);
}

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-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);
}