Only Hanzi YT Captions

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

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