Greasy Fork

Greasy Fork is available in English.

YouTube検索結果「全てキューに入れて再生」ボタンを追加

musictonicの代わり 右クリックだとシャッフル再生

目前为 2021-02-28 提交的版本,查看 最新版本

// ==UserScript==
// @name YouTube検索結果「全てキューに入れて再生」ボタンを追加
// @description musictonicの代わり 右クリックだとシャッフル再生
// @version      0.1.9
// @run-at document-idle
// @match *://www.youtube.com/*
// @match *://www.youtube.com/
// @grant none
// @require https://code.jquery.com/jquery-3.4.1.min.js
// @namespace http://greasyfork.icu/users/181558
// ==/UserScript==

(function() {
  const CLOSE_MINI_PLAYER_ALWAYS = 1; // 1:ミニプレイヤーを常に閉じる
  const HIDE_SUGGEST = 1000; // 1-:検索結果に割り込む「あなたへのおすすめ」「他の人はこちらも視聴しています」「家にいながら学ぶ」を隠す
  const WAIT_MIN = 100; // 取りこぼす時は大きく 50-
  const WAIT_MAX = 300; // 取りこぼす時は大きく 250-
  const DEBUG = 0; // 1:wait値を表示

  const waitLast = performance.now() * 1; // 係数
  const wait = Math.round((window.navigator.userAgent.toLowerCase().indexOf('chrome') != -1) ? 250 : Math.min(WAIT_MAX, Math.max(WAIT_MIN, waitLast / 20)));

  var videoDisplayedLast = 0;

  var playAllCount;

  //URLの変化を監視
  var href = location.href;
  var observer = new MutationObserver(function(mutations) {
    if (href !== location.href) {
      href = location.href;
      $('#playAllButton').remove();
      setTimeout(() => { run() }, 1500);
    }
  });
  observer.observe(document, { childList: true, subtree: true });
  setTimeout(() => { run(); }, 1000);
  setInterval(() => { hideSuggest() }, 1500);

  if (CLOSE_MINI_PLAYER_ALWAYS) setInterval(() => { // ミニプレイヤーを常に閉じる
    let e = eleget0('//paper-button[@role="button" and @elevation="0" and @aria-disabled="false"]/yt-formatted-string[@id="text" and @class="style-scope yt-button-renderer style-blue-text size-default" and text()="プレーヤーを閉じる"]');
    if (e) e.click();
  }, 700);

  return;

  function hideSuggest() {
    if (HIDE_SUGGEST && location.href.indexOf('www.youtube.com/results?') !== -1) {
      ['//div/div/span[@id="title"  and (text()="Learn while you\'re at home" or text()="For you" or text()="People also watched" or text()="家にいながら学ぶ" or text()="あなたへのおすすめ" or text()="他の人はこちらも視聴しています")]/../../../../..', // 縦横
        '//div[@class="style-scope ytd-shelf-renderer"]/h2[@class="style-scope ytd-shelf-renderer"]/span[@id="title" and contains(@class,"style-scope ytd-shelf-renderer") and (text()="Learn while you\'re at home" or text()="For you" or text()="People also watched" or text()="家にいながら学ぶ" or text()="あなたへのおすすめ" or text()="他の人はこちらも視聴しています")]/../../../..', // 縦1列
      ].forEach(xp => {
        $(elegeta(xp)).hide(HIDE_SUGGEST, function() { $(this).remove() }); // 検索結果に割り込むサジェストを隠す
      });
    }
  }

  function run(node = document) {
    if (location.href == "https://www.youtube.com/") {
      var place = eleget0('//div[@id="center" and @class="style-scope ytd-masthead"]');
    } else if (location.href.match(/https:\/\/www\.youtube\.com\/results\?.*(q=|search_query=)/)) {
      var place = eleget0('//div[@id="center" and @class="style-scope ytd-masthead"]'); ////div[@id="filter-menu"]');
    } else if (location.href.match("//www.youtube.com/channel/.*/search|//www.youtube.com/user/.*/search")) {
      var place = eleget0('//div[@id="center" and @class="style-scope ytd-masthead"]'); //'//div[@id="tabsContainer"]');
    } else if (location.href.match("//www.youtube.com/channel/|//www.youtube.com/c/|//www.youtube.com/user/") && !(location.href.match("/community|/channels|/about|/playlists"))) {
      var place = eleget0('//div[@id="center" and @class="style-scope ytd-masthead"]'); //'//div[@id="primary-items"]');
    } else if (location.href.match("//www.youtube.com/watch")) {
      var place = eleget0('//div[@id="center" and @class="style-scope ytd-masthead"]'); //'//div[@id="upnext" and @class="style-scope ytd-compact-autoplay-renderer"]');
    } else if (location.href.match("//www.youtube.com/playlist")) {
      var place = eleget0('//div[@id="center" and @class="style-scope ytd-masthead"]'); //'//div[@id="items"]/ytd-playlist-sidebar-secondary-info-renderer');
    } else return;

    if (place) {
      $('#playAllButton').remove();
      var playAllButton = $('<span style="cursor:pointer;color: #000; text-shadow: 1px 0px 0 #fff, -1px 0px 0 #fff, 0px -1px 0 #fff, 0px 1px 0 #fff;" title="クリックで画面に出ている動画を全てキューに入れて再生(右クリックだとシャッフル)\nEnqueue all displayed videos and start playing (right-click to shuffle)" id="playAllButton">Play All</span>')
      playAllButton.insertAfter(place);
      playAllButton.on("contextmenu", () => { playAll("shuffle"); return false; });
      playAllButton.on("click", () => { playAll(); return false; });
      if (!playAllCount) {
        playAllCount = setInterval(() => { $('#playAllButton').html("Play All (" + elegeta('//yt-icon[@class="style-scope ytd-menu-renderer"]').length + ")" + (DEBUG ? "<br>wait:" + wait : ""));
        }, 1000);
      }
    }

    function pauseVideo() {
      let e = eleget0('//video');
      if (e) { e.pause(); } else { setTimeout(pauseVideo, 17) }
    }

    function playAll(option = false) {
      setTimeout(pauseVideo, 17);
      let d = 0;
      let videoEle = elegeta('//yt-icon[@class="style-scope ytd-menu-renderer"]');
      for (let e of (option == "shuffle" ? shuffle(videoEle) : videoEle)) {
        setTimeout(() => { e.click() }, d);
        setTimeout(() => {
          let queue = eleget0('//yt-formatted-string[text()="キューに追加"]|//yt-formatted-string[text()="Add to queue"]');
          if (queue) queue.click();
        }, d + wait / 2);
        if (d == 0) d += 100; // ?
        d += wait;
      }
      d += wait * Math.min(7000, Math.max(2000, waitLast)) / 1000;
      cli('//div[contains(@class,\"ytp-miniplayer-play-button-container\")]/button[@aria-label=\"再生(k)\"]|//button[@class="ytp-play-button ytp-button" and @aria-label="Play (k)"]', d);
      d += wait * Math.min(7000, Math.max(2000, waitLast)) / 1000;
      cli('//div[@class="ytp-miniplayer-scrim"]/button[@aria-label="拡大(i)"]|//div[@class="ytp-miniplayer-scrim"]/button[@aria-label="Expand (i)"]', d);
      d += wait;
      setTimeout(() => { let e = eleget0('//video'); if (e) { e.play(); } }, d);
    }
  }

  function shuffle(array) {
    for (let i = array.length - 1; i >= 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
  }

  function cli(xpath, wait) {
    setTimeout(() => {
      let ele = eleget0(xpath);
      if (ele) ele.click();
    }, wait);
  }

  function elegeta(xpath, node = document) {
    if (!xpath) return [];
    try {
      var array = [];
      var ele = document.evaluate("." + xpath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
      let j = 0;
      for (var i = 0; i < ele.snapshotLength; i++) {
        let ei = ele.snapshotItem(i);
        if (ei.offsetHeight) array[j++] = ei;
      }
      return array;
    } catch (e) { return []; }
  }

  function eleget0(xpath, node = document) {
    if (!xpath) return null;
    try {
      var ele = document.evaluate(xpath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
      if (ele.snapshotLength < 1) return "";
      let ei = ele.snapshotItem(0);
      if (ei.offsetHeight) return ei;
      return "";
    } catch (e) { return null; }
  }

})();