1
1

More than 3 years have passed since last update.

Youtube字幕の自動翻訳を日本語に固定する方法 PC版

Posted at

この記事はオリジナルではなく単なる紹介記事になります。

Youtubeの長年の悩みが一つ解消されたので・・・
普通に検索していたら確かに見つけにくいと思います。

このスクリプトでYoutubeの外国語動画を開くだけで日本語の自動字幕が表示されます。
最初に設定しておくだけで、動画を開く段階では何も操作する必要はありません。

日本語字幕がついている場合の動作は不明です

TampermonkeyはChromeブラウザで独自のスクリプトを実行してくれます。

Tampermonkey
https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo

紹介記事
【YouTube】字幕の自動翻訳を日本語に固定する方法【PC】 - 音又空間’
https://www.otomata01.com/entry/youtube-translate-japanese

スクリプト
YouTubeで自動翻訳字幕(日本語)を常にオン
https://greasyfork.org/ja/scripts/391647-youtube%E3%81%A7%E8%87%AA%E5%8B%95%E7%BF%BB%E8%A8%B3%E5%AD%97%E5%B9%95-%E6%97%A5%E6%9C%AC%E8%AA%9E-%E3%82%92%E5%B8%B8%E3%81%AB%E3%82%AA%E3%83%B3

// ==UserScript==
// @name YouTubeで自動翻訳字幕(日本語)を常にオン
// @description B:再度試みる N,Enter:次の動画 P,]:前の動画 A:今の位置から再生するURLをコピー(Shift+A:タブのURLにも反映) [:全画面化
// @version      0.5.15
// @run-at document-idle
// @match *://www.youtube.com/*
// @grant GM.setClipboard
// @namespace https://greasyfork.org/users/181558
// ==/UserScript==

(function() {

  const WAIT_PAGEOPEN_OFFSET = 0; // ミリ秒 不安定なら大きくする
  const ENABLE_EVENJAPANESE = 1; // 1なら日本語タイトルの動画でも実行
  const ENABLE_EVENEMBED = 1; // 1なら埋め込み動画でも実行
  const DISABLE_AUTO_GENERATED_JAPANESE = 1; // 1なら自動生成の日本語しかない時は字幕をオフ
  const ENABLE_WHEN_MUTED = 1; // 1なら音声がミュートされている時は字幕をオン
  const DO_NOT_SELECT_LANGUAGE = 0; // 1なら日本語を選択しない(字幕をオンにするだけ)
  const WORDS_TO_FORCE_ENABLE = /ラジオ/; // 動画タイトルに含むと字幕をオンにする正規表現 対象なしは/$^/
  const WORDS_TO_FORCE_DISABLE = /$^/; // 動画タイトルに含むと字幕をオフにする正規表現 対象なしは/$^/

  const WAIT_EACHACTION = 300; // ミリ秒 不安定なら大きくする
  const verbose = 0; // 開発用
  const VERBOSE_NOTIFY = 0; // 開発用 1:vn()
  const CHECK_ROAJ = 0; // 開発用 1:日本語にしなかった理由をalert

  //const WAIT_VIDEO_START = 0.2; // 秒 ビデオ再生開始後この秒数まで待つ 0~5ぐらい 不安定なら大きくする

  var checkROAJAlready = 0;

  var kaisuuA = 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;
  vn("wait_pageOpen : " + Math.floor(wait_pageOpen) + " ms");

  document.addEventListener('keydown', function(e) {
    if (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA' || e.target.getAttribute('contenteditable') == 'true') return;
    var key = (e.shiftKey ? "Shift+" : "") + (e.altKey ? "Alt+" : "") + (e.ctrlKey ? "Ctrl+" : "") + e.key;

    if (e.key === "[") { // [ 全画面化
      toggleFullScreen();
      return false;

      function toggleFullScreen() {
        if (!document.fullscreenElement) {
          document.documentElement.requestFullscreen();
        } else {
          if (document.exitFullscreen) {
            document.exitFullscreen();
          }
        }
      }
    }
    if (key === "b") {
      toJP("forced");
      e.preventDefault();
      return false;
    }
    if (key === "n" || key === "Enter") {
      elecli("forced", '//a[@class="ytp-next-button ytp-button"]');
      e.preventDefault();
      return false;
    }
    if (key === "p" || key === "]") {
      elecli("forced", '//a[@class="ytp-prev-button ytp-button"]');
      e.preventDefault();
      return false;
    }
    if ((key === "a" || key === "Shift+A") && location.href.match(/\/\/www\.youtube\.com\/watch\?.*v\=([^?&]*)/)) {
      var ctimeEle = eleget0('//video');
      if (ctimeEle) {
        var ctime = Math.floor(ctimeEle.currentTime);
        //ctime = ctime < 1 ? "" : "&t=" + ((kaisuuA % 2) == 0 ? (ctime) : ((ctime / 60 / 60 >= 1 ? Math.floor(ctime / 60 / 60) + "h" : "") + (ctime / 60 >= 1 ? Math.floor(ctime / 60 % 60) + "m" : "") + ctime % 60 + "s"));
        var ret = (navigator.platform.indexOf("Win") != -1) ? "\r\n" : "\n";
        let newurl = key === "a" ? "https://www.youtube.com/watch?v=" + location.href.match(/v\=([^?&]*)/)[1] + (ctime > 0 ? ("&t=" + ctime) : "") : location.href.replace(/&t=\d*s?/, "") + (ctime > 0 ? ("&t=" + ctime) : "");
        //let domain = (kaisuuA % 3) < 2 ? 'https://www.youtube.com/watch?v=' : 'https://youtu.be/';
        //let newurl = key === "a" ? domain + location.href.match(/v\=([^?&]*)/)[1] + ctime : location.href.replace(/&t=[0-9hms]*s?/, "") + ctime;
        let te = eleget0('//h1[@class="title style-scope ytd-video-primary-info-renderer"]/yt-formatted-string[@force-default-style="" and @class="style-scope ytd-video-primary-info-renderer"]');
        let titleInPage = te ? te.innerText + " - YouTube" : null;
        //var cb = (document.title + ret + newurl + ret);
        var cb = ((titleInPage || document.title) + ret + newurl + ret);
        if (key === "Shift+A") history.pushState(null, null, newurl);
        GM.setClipboard(cb);
        elecli("forced", '//div[@class="ytp-right-controls"]/button[@aria-label="設定"]|//button[@class="ytp-button ytp-settings-button"]', 0, "close");
        elecli("forced", '//div[@class="ytp-right-controls"]/button[@aria-label="設定"]|//button[@class="ytp-button ytp-settings-button"]', 0.33, "blur"); // 設定ボタン
        kaisuuA = ++kaisuuA % 3;
      }
      e.preventDefault();
      return false;
    }
  }, false);

  var latestVideo = elegeta('//video').map(e => e.src).toString();

  setInterval(() => {
    if (elegeta('//video').map(e => e.src).toString() != latestVideo) { // video要素の.srcたちの変化を監視
      latestVideo = elegeta('//video').map(e => e.src).toString();
      checkROAJAlready = 0;
      if (VERBOSE_NOTIFY) vn(latestVideo, "//video変化 : " + elegeta('//video').length + "要素 ");
      //      run(wait_pageOpen * 3);
      if (latestVideo.length > 3) run(wait_pageOpen * 3); // src属性の中身があるvideo要素が1つ以上あれば
    }
  }, 1000);

  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) {
    verb(elegeta('//video[@class="video-stream html5-main-video"]'))
    elegeta('//video').forEach(e => { if (!(e.src)) e.remove() })
    let video = elegeta('//video[@class="video-stream html5-main-video"]').slice(-1)[0]; // video要素が複数ある場合があるので最後の1つを取得
    if (!video) { verb("まだビデオ要素がありません"); return; }
    //verb(video.paused)
    //    if (video.paused == true || (video.currentTime < WAIT_VIDEO_START)) {
    if (!video.src) {
      verb("まだビデオ再生が始まっていません");
      setTimeout(() => run(delay), 100);
      return;
    }
    setTimeout(function() {
      elegeta('//video').forEach(e => { if (!(e.src)) e.remove() })
      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); // 字幕ボタン
      if (!DO_NOT_SELECT_LANGUAGE && !eleget0('//div[text()="デフォルトの字幕"]')) {
        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(() => {
      var title = eleget0('//h1/yt-formatted-string');
      if (!f && (((title.innerText) || "").match(WORDS_TO_FORCE_DISABLE))) { elecli(1, '//button[@class="ytp-subtitles-button ytp-button" and @aria-pressed="true"]'); return }
      let muted = eleget0('//button[@class="ytp-mute-button ytp-button" and @title="ミュート解除(m)"]');
      verb(!f, !(ENABLE_WHEN_MUTED && muted), DISABLE_AUTO_GENERATED_JAPANESE, (((title.innerText) || "").match(WORDS_TO_FORCE_ENABLE) == null))
      if ((!f && !(ENABLE_WHEN_MUTED && muted) && DISABLE_AUTO_GENERATED_JAPANESE && (((title.innerText) || "").match(WORDS_TO_FORCE_ENABLE) == null))) { // 自動翻訳の日本語しかなければ字幕をオフにして終わる
        var str = "";
        for (let am of elegeta('//div[@class="ytp-menuitem-label"]')) { str += am.innerText };
        verb("available lang check:" + delay + ', //div[@class="ytp-menuitem-label"] .innerText: ' + str);
        if (str === 'アノテーション再生速度字幕 (1)画質オフ日本語 (自動生成)自動翻訳' || str === '自動再生アノテーション再生速度字幕 (1)画質オフ日本語 (自動生成)自動翻訳' || str === 'オフ日本語 (自動生成)自動翻訳' || str === '自動再生アノテーション再生速度字幕 (1)画質字幕を追加オフ日本語 (自動生成)自動翻訳' || str === '自動再生再生速度字幕 (1)画質字幕を追加オフ日本語 (自動生成)自動翻訳' || str.match(/^(自動再生)?再生速度字幕 \(1\)画質オフ日本語 \(自動生成\)自動翻訳$|字幕を追加オフ日本語 \(自動生成\)自動翻訳$/)) {
          elecli(1, '//button[@class="ytp-subtitles-button ytp-button" and @aria-pressed="true"]');
          //          roaj(str + "だったのでやめました");
          vn(str + "だったのでやめました");
          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"]'); // 歯車がオープンの状態の歯車
          //          roaj(xpath + "がなかったのでやめました");
          vn(xpath + "がなかったのでやめました");
        }
      }
      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);
  }

  function roaj(str) {
    if (CHECK_ROAJ && !checkROAJAlready) {
      alert(str);
      checkROAJAlready = 1;
    }
  }

  function vn(body, title = "") { // verbose notify
    if (VERBOSE_NOTIFY) notify(body, title);
  }

  function notify(body, title = "") {
    if (!("Notification" in window)) return;
    else if (Notification.permission == "granted") new Notification(title, { body: body });
    else if (Notification.permission !== "denied") Notification.requestPermission().then(function(permission) {
      if (permission === "granted") new Notification(title, { body: body });
    });
  }
})();


1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1