// ==UserScript== // @name YouTube検索結果「全てキューに入れて再生」ボタンを追加 // @description musictonicの代わり 右クリックだとシャッフル再生 // @version 0.1.12 // @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 https://greasyfork.org/users/181558 // @downloadURL none // ==/UserScript== (function() { const CLOSE_MINI_PLAYER_ALWAYS = 1; // 1:Escでミニプレイヤーを常に閉じる const AGREE_TO_CONTINUE_ALWAYS = 1; // 1:無操作一時停止を常に解除 const HIDE_SUGGEST = 1000; // 1-:検索結果に割り込む「あなたへのおすすめ」「他の人はこちらも視聴しています」「家にいながら学ぶ」を隠す const CHROME = (window.navigator.userAgent.toLowerCase().indexOf('chrome') != -1); const WAIT_FIRST = CHROME ? 700 : 100; // 取りこぼす時は大きく const WAIT_MIN = CHROME ? 150 : 100; // 取りこぼす時は大きく 50- const WAIT_MAX = 300; // 取りこぼす時は大きく 250- const waitLast = performance.now() * 1; // 係数 const wait = Math.round(Math.min(WAIT_MAX, Math.max(WAIT_MIN, waitLast / 20))); const DEBUG = 0; // Math.random() > 0.5 ? 1 : 0; // 0; // 1:wait値を表示 var videoDisplayedLast = 0; var lastLength = 0; var playAllCount; //URLの変化を監視 var href = location.href; var observer = new MutationObserver(function(mutations) { if (href !== location.href) { href = location.href; $('#playAllButton').remove(); setTimeout(() => { lastLength = 0; run() }, 1500); } }); observer.observe(document, { childList: true, subtree: true }); setTimeout(() => { run(); }, 1009); setInterval(() => { hideSuggest() }, 1511); setInterval(() => { if (AGREE_TO_CONTINUE_ALWAYS) if (eleget0('//yt-formatted-string[@split-lines="" and text()="動画が一時停止されました。続きを視聴しますか?"]')) { let e = eleget0('//yt-formatted-string[@id="text" and @class="style-scope yt-button-renderer style-blue-text size-default" and text()="はい"]'); if (e) e.click(); } }, 3001); if (CLOSE_MINI_PLAYER_ALWAYS) setInterval(() => { // ミニプレイヤーを常に閉じる let e = eleget0('//yt-formatted-string[@id="text" and @class="style-scope yt-button-renderer style-blue-text size-default" and text()="プレーヤーを閉じる"]'); if (e) e.click(); }, 701); 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/" || location.href.match(/https:\/\/www\.youtube\.com\/results\?.*(q=|search_query=)/) || location.href.match("//www.youtube.com/channel/.*/search|//www.youtube.com/user/.*/search") || (location.href.match("//www.youtube.com/channel/|//www.youtube.com/c/|//www.youtube.com/user/") && !(location.href.match("/community|/channels|/about|/playlists"))) || location.href.match("//www.youtube.com/playlist") || location.href.match("//www.youtube.com/watch")) { var place = eleget0('//div[@id="center" and @class="style-scope ytd-masthead"]'); } else return; if (place) { $('#playAllButton').remove(); var playAllButton = $('Play All') playAllButton.insertAfter(place); playAllButton.on("contextmenu", () => { playAll("shuffle"); return false; }); playAllButton.on("click", () => { playAll(); return false; }); if (!playAllCount) { playAllCount = setInterval(() => { let currentLength = elegeta('//yt-icon[@class="style-scope ytd-menu-renderer"]').length; if (lastLength != currentLength) $('#playAllButton').html("Play All (" + currentLength + ")" + (DEBUG ? "
wait:" + wait : "")); lastLength = currentLength; }, 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 += WAIT_FIRST; // ? 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; if (!(location.href.match(/\/watch\?v=/))) cli('//div[@class="ytp-miniplayer-scrim"]/button[@aria-label="拡大(i)"]|//div[@class="ytp-miniplayer-scrim"]/button[@aria-label="Expand (i)"]', d, "infinity"); 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, mode = "") { // mode: infinity:押せるまで繰り返す setTimeout(() => { let ele = eleget0(xpath); if (ele) { ele.click(); } else if (mode === "infinity") { cli(xpath, 200, mode) } }, 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; } } })();