Only Hanzi YT Captions

Blurs and shrinks non-Hanzi YouTube captions. They are revealed if hovered.

// ==UserScript==
// @name        Only Hanzi YT Captions
// @namespace   Violentmonkey Scripts
// @match       *://*.youtube.com/watch*
// @match       *://youtube.com/watch*
// @grant       GM_addStyle
// @grant       GM_registerMenuCommand
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_notification
// @version     0.2
// @author      KerfuffleV2
// @description Blurs and shrinks non-Hanzi YouTube captions. They are revealed if hovered.
// @license     MIT
// ==/UserScript==

// Set to true if you want the non-matching captions completely hidden. Note that you won't be able to hover.
const completelyHideNonHanzi = false;

// Font size for non-matching captions.
const nonHanziSize = "20pt";

// Regexp to match hanzi (or whatever).
const hanRe = /\p{Script=Han}/u;

// End user adjustable settings.


let enabled = GM_getValue('StartEnabled');

const sty = `
  .ohc-nonhanzi-caption {
    opacity: 0.6;
    filter: blur(.15em);
    font-size: ${nonHanziSize} !important;
    ${completelyHideNonHanzi ? 'display: none !important;' : ''}
  }

  .ohc-nonhanzi-caption:hover {
    opacity: inherit;
    filter: inherit;
    transition: opacity .5s ease-in, filter .5s ease-in;
  }
`;

GM_addStyle(sty);


function go(disable) {
  if (typeof window.ohc_observer !== 'undefined') {
    window.ohc_observer.disconnect();
    window.ohc_observer = undefined;
    if (disable) {
      return;
    }
  }

  const observer = new MutationObserver((mutations) => {
    if (!enabled) {
      return;
    }
    for (const mut of mutations) {
      if (mut.type !== 'childList') {
        continue;
      }
      mut.addedNodes.forEach((node) => {
        if (node.classList?.contains('ytp-caption-segment') &&
            !node.textContent?.match(hanRe)) {
          node.classList.add('ohc-nonhanzi-caption');
        }
      });
    }
  });

  const body = document.querySelector('body');
  if (!body) {
    console.log('OnlyHanziYTCaptions: No body. Bailing out!');
    return;
  }
  observer.observe(body, {childList: true, subtree: true});
  window.ohc_observer = observer;
}

function showNote(txt) {
  GM_notification(txt, 'Only Hanzi YT Captions');
}

GM_registerMenuCommand('Toggle enabled', () => {
  enabled = !enabled;
  showNote(enabled ? 'Now enabled' : 'Now disabled');
  go(!enabled);
});


GM_registerMenuCommand('Toggle initial state', () => {
  const startEnabled = !GM_getValue('StartEnabled');
  showNote(startEnabled ? 'Initially enabled' : 'Initially disabled');
  GM_setValue('StartEnabled', startEnabled);
});


if (enabled) {
  go(false);
}