Greasy Fork

Greasy Fork is available in English.

YouTubeで自動翻訳字幕(日本語)を常にオン

B:再度試みる N.:次の動画 P,:前の動画 A:今の位置から再生するURLをコピー

目前为 2020-02-22 提交的版本,查看 最新版本

// ==UserScript==
// @name YouTubeで自動翻訳字幕(日本語)を常にオン
// @description B:再度試みる N.:次の動画 P,:前の動画 A:今の位置から再生するURLをコピー
// @version      0.5.5
// @run-at document-idle
// @match *://www.youtube.com/*
// @grant GM.setClipboard
// @namespace http://greasyfork.icu/users/181558
// ==/UserScript==

(function() {

  const WAIT_PAGEOPEN_OFFSET = 0; // ミリ秒 不安定なら大きくする
  const ENABLE_EVENJAPANESE = 1; // 日本語タイトルの動画でも実行
  const ENABLE_EVENEMBED = 1; // 埋め込み動画でも実行
  const DISABLE_AUTO_GENERATED_JAPANESE = 1; // 自動生成の日本語しかなければ字幕をオフ

  const WAIT_EACHACTION = 300; // ミリ秒 不安定なら大きくする
  const verbose = 0;

  var per = (performance.now()) / 3;
  verb("performance.now:" + performance.now(), "wait_base:" + per + "ms");
  var wait_pageOpen = (per > 1500 ? 1500 : per < 300 ? 300 : per) + WAIT_PAGEOPEN_OFFSET;
  verb("wait_pageOpen:" + wait_pageOpen + "ms");

  document.addEventListener('keydown', function(e) {
    if (/input|textarea/i.test(e.target.tagName) == false) {
      var key = (e.shiftKey ? "Shift+" : "") + (e.altKey ? "Alt+" : "") + (e.ctrlKey ? "Ctrl+" : "") + e.key;
      if (key == "b") {
        toJP("forced");
        e.preventDefault();
        return false;
      }
      if (key == "n" || key == ".") {
        elecli(null, '//a[@class="ytp-next-button ytp-button" and @aria-label="次へ(SHIFT+n)"]|//a[@class="ytp-next-button ytp-button cleaned" and @role="button" and @aria-label="次へ(SHIFT+n)"]');
        e.preventDefault();
        return false;
      }
      if (key == "p" || key == ",") {
        elecli(null, '//a[@class="ytp-prev-button ytp-button" and @aria-label="前へ(SHIFT+p)"]|//a[@class="ytp-prev-button ytp-button cleaned" and @role="button" and  @aria-label="前へ(SHIFT+p)"]');
        e.preventDefault();
        return false;
      }
      if (key == "a" && location.href.match(/\/\/www\.youtube\.com\/watch\?.*v\=([^?&]*)/)) {
        elecli(null, '//div[@class="ytp-right-controls"]/button[@aria-label="設定"]|//button[@class="ytp-button ytp-settings-button"]', 0, "close");
        elecli(null, '//div[@class="ytp-right-controls"]/button[@aria-label="設定"]|//button[@class="ytp-button ytp-settings-button"]', 1, "blur"); // 設定ボタン
        setTimeout(() => {
          var ctimeEle = elegeta('//span[@class="ytp-time-current"]');
          if (ctimeEle) {
            ctimeEle = ctimeEle.slice(-1)[0]; // 動画要素を複数持つ場合があるので最後の1つを取得
            var ctimeText = ctimeEle.innerText.trim().split(":");
            var ctime = ctimeText.length == 2 ? Number(ctimeText[0]) * 60 + Number(ctimeText[1]) : Number(ctimeText[0]) * 60 * 60 + Number(ctimeText[1]) * 60 + Number(ctimeText[2]);
            var ret = (navigator.platform.indexOf("Win") != -1) ? "\r\n" : "\n";
            var cb = (document.title + ret + "https://www.youtube.com/watch?v=" + location.href.match(/v\=([^?&]*)/)[1] + (ctime > 0 ? ("&t=" + ctime) : "") + ret);
          }
          GM.setClipboard(cb); //alert(cb);
          e.preventDefault();
        }, 100);
        return false;
      }
    }
  }, false);

  var href = location.href;
  var observer = new MutationObserver(function(mutations) {
    if (href !== location.href) {
      href = location.href;
      run(wait_pageOpen * 3);
    }
  });
  observer.observe(document, { childList: true, subtree: true });

  if (window === parent && location.href.match(/^https:\/\/www\.youtube\.com\/watch/)) run(wait_pageOpen * 2);

  for (let ele of elegeta('//div[@id="player"]/div/div/button[@aria-label="再生"]|//div[@id="player"]/div/div/button[@aria-label="Play"]')) {
    ele.addEventListener('click', () => { setTimeout(run, wait_pageOpen * 2) });
  }

  return;

  function run(delay = 500) {
    setTimeout(function() {
      verb("run");
      var title = eleget0('//h1/yt-formatted-string');
      if (!(location.href.match(/\/embed/)) && (!title || !eleget0('//div[contains(@class,"ytp-right-controls")]/button[@aria-haspopup="true"]|//button[@class="ytp-button ytp-settings-button" and @data-tooltip-target-id="ytp-settings-button"]'))) {
        setTimeout(run, 100);
        verb("title does not exist");
        return;
      }
      if (!ENABLE_EVENJAPANESE && title && title.innerText.match(/[\u30a0-\u30ff\u3040-\u309f\u3005-\u3006\u30e0-\u9fcf]+/)) { verb("title has Japanese character(s)"); return; } // タイトルに日本語あるならやめる
      setTimeout(() => { toJP(), wait_pageOpen });
    }, delay + ((window.navigator.userAgent.toLowerCase().indexOf('chrome') > -1) ? 200 : 0));
  }

  function toJP(f = null) {
    if (!(location.href.match(ENABLE_EVENEMBED ? /:\/\/www\.youtube\.com\/watch\?|:\/\/www\.youtube\.com\/embed/ : /:\/\/www\.youtube\.com\/watch\?/))) return;
    if (eleget0('//button[@class="ytp-subtitles-button ytp-button" and @aria-pressed="true"]|//button[@class="ytp-subtitles-button ytp-button" and @aria-pressed="false"]')) { // 字幕ボタン
      elecli(f, '//button[@class="ytp-subtitles-button ytp-button" and @aria-label="字幕(c)" and @aria-pressed="false"]|//button[@class="ytp-subtitles-button ytp-button" and @aria-pressed="false"]', 1); // 字幕ボタン
      elecli(f, '//div[@class="ytp-right-controls"]/button[@aria-label="設定"]|//button[@class="ytp-button ytp-settings-button"]', 2); // 設定ボタン
      elecli(f, '//div[@class="ytp-menuitem-label"]/div/span[text()="字幕"]|//div[@class="ytp-menuitem-label"]/div/span[contains(text(),"Subtitles/CC")]', 3, "close", "smooth");
      elecli(f, '//div[@class="ytp-menuitem-label" and text()="自動翻訳"]|//div[2]/div/div[@class="ytp-menuitem-label" and contains(text(),"Auto-translate")]', 4, "close", "instant");
      elecli(f, '//div[@class="ytp-menuitem-label" and text()="日本語"]|//div[@class="ytp-menuitem-label" and text()="Japanese"]', 5, "close");
      elecli(f, '//div[contains(@class,"ytp-right-controls")]/button[@aria-haspopup="true" and @aria-expanded="true"]|//button[@class="ytp-button ytp-settings-button" and @data-tooltip-target-id="ytp-settings-button" and @aria-expanded="true"]', 7, "blur");
    }
  }

  function elecli(f, xpath, delay = 0, command = false, beha = null) {
    setTimeout(() => {
      if (!f && DISABLE_AUTO_GENERATED_JAPANESE) {
        var str = "";
        for (let am of elegeta('//div[@class="ytp-menuitem-label"]')) { str += am.innerText };
        verb(delay + ', //div[@class="ytp-menuitem-label"] .innerText: ' + str);
        if (str === '自動再生アノテーション再生速度字幕 (1)画質オフ日本語 (自動生成)自動翻訳' || str === 'オフ日本語 (自動生成)自動翻訳' || str === '自動再生アノテーション再生速度字幕 (1)画質字幕を追加オフ日本語 (自動生成)自動翻訳' || str === '自動再生再生速度字幕 (1)画質字幕を追加オフ日本語 (自動生成)自動翻訳' || str.match(/^(自動再生)?再生速度字幕 \(1\)画質オフ日本語 \(自動生成\)自動翻訳$|字幕を追加オフ日本語 \(自動生成\)自動翻訳$/)) { elecli(1, '//button[@class="ytp-subtitles-button ytp-button" and @aria-pressed="true"]'); return }
      }
      var ele = eleget0(xpath);
      verb(xpath, "...found : " + (elegeta(xpath).length));
      if (ele) {
        for (let ele of elegeta(xpath)) {
          if (command == "focus") {
            ele.focus();
            verb("...focused")
          } else {
            ele.click();
            verb("...clicked")
            if (window != parent && beha) {
              let foc = eleget0('//div[@id="movie_player"]|//div[@id="player"]');
              if (foc) foc.scrollIntoView({ behavior: beha, block: "center", inline: "center" });
            }
          }
        }
      } else {
        verb("...not found");
        if (command == "close") {
          elecli(f, '//div[contains(@class,"ytp-right-controls")]/button[@aria-haspopup="true" and @aria-expanded="true"]|//button[@class="ytp-button ytp-settings-button" and @data-tooltip-target-id="ytp-settings-button" and @aria-expanded="true"]');
        }
      }
      if (command == "blur") elecli(f, '//div[@id="movie_player"]|//div[@id="player"]/div/div/video', 0, "focus");
    }, delay * WAIT_EACHACTION);
  }

  function eleget0(xpath, node = document) {
    if (!xpath) return null;
    try {
      var ele = document.evaluate(xpath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
      return ele.snapshotLength > 0 ? ele.snapshotItem(0) : "";
    } catch (e) { return null; }
  }

  function elegeta(xpath, node = document) {
    var ele = document.evaluate("." + xpath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
    var array = [];
    for (var i = 0; i < ele.snapshotLength; i++) array[i] = ele.snapshotItem(i);
    return array;
  }

  function verb() {
    if (verbose)
      for (let str of arguments) console.log(str);
  }
})()