您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Don't like the youtube autoplay suggestion? This script can create a queue with videos you want to play after your current video has finished!
当前为
// ==UserScript== // @name Youtube Play Next Queue // @version 1.0.0 // @description Don't like the youtube autoplay suggestion? This script can create a queue with videos you want to play after your current video has finished! // @author Cpt_mathix // @include https://www.youtube.com* // @license GPL version 2 or any later version; http://www.gnu.org/licenses/gpl-2.0.txt // @require https://cdnjs.cloudflare.com/ajax/libs/JavaScript-autoComplete/1.0.4/auto-complete.min.js // @namespace https://gf.qytechs.cn/users/16080 // @grant none // @noframes // ==/UserScript== (function() { var script = { ytplayer: getVideoPlayer(), playnext: true, queue: new Queue(), version: '1.0.0', search_timeout: null, suggestions: [], debug: false }; // callback function for search results window.search_callback = search_callback; // reload script on page change using youtube spf events (http://youtube.github.io/js/documentation/events/) window.addEventListener("spfdone", function(e) { if (script.debug) console.log("new page loaded"); script.ytplayer = getVideoPlayer(); if (script.debug) console.log(script.ytplayer); if (isPlayerAvailable()) { startScript(); } }); addAutoCompleteCSS(); function addAutoCompleteCSS() { var css = ` .autocomplete-suggestions { text-align: left; cursor: default; border: 1px solid #ccc; border-top: 0; background: #fff; box-shadow: -1px 1px 3px rgba(0,0,0,.1); position: absolute; display: none; z-index: 9999; max-height: 254px; overflow: hidden; overflow-y: auto; box-sizing: border-box; } .autocomplete-suggestion { position: relative; padding: 0 .6em; line-height: 23px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1.02em; color: #333; } .autocomplete-suggestion b { font-weight: normal; color: #b31217; } .autocomplete-suggestion.selected { background: #f0f0f0; } `; var style = document.createElement('style'); style.type = 'text/css'; if (style.styleSheet){ style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } document.documentElement.appendChild(style); } //deleteCache('queue'); // for testing purposes if (isPlayerAvailable()) { startScript(); } function startScript() { if (script.ytplayer) { if (getVideoInfoFromUrl(document.location.href, "t") == "0s") script.ytplayer.seekTo(0); startQueue(); } try { globalScrollListener(); findVideoThumbs(); } catch(error) { console.error("Couldn't initialize add to queue buttons \n" + error.message); } } function startQueue() { if (script.debug) console.log("initialising queue"); initQueue(); if (script.debug) console.log("initialising search"); initSearch(); if (script.debug) console.log("initialising video state listener"); initStateListener(); if (!script.queue.isEmpty()) { if (script.debug) console.log("showing queue"); if (script.debug) console.log(script.queue.get()); displayQueue(); } } function initQueue() { var cachedQueue = getCache('queue'); if (cachedQueue) { script.queue.set(cachedQueue); } else { setCache('queue', script.queue.get()); } // prepare html for queue var queue = document.getElementsByClassName("autoplay-bar")[0]; queue.classList.add("video-list"); queue.id = "watch-queue"; queue.setAttribute("style", "list-style:none"); // add class to suggestion video so it doesn't get queue related buttons var suggestion = queue.getElementsByClassName("related-list-item")[0]; suggestion.classList.add("suggestion"); } function initStateListener() { // play next video in queue if current video is finished playing (state equal to 0) script.ytplayer.addEventListener("onStateChange", function(e) { if (script.debug) console.log("state changed", e); if (e === 0 && script.playnext && !script.queue.isEmpty()) { script.playnext = false; var next = script.queue.dequeue(); playNextVideo(next.id); } else if (e !== 0) { script.playnext = true; } }); } // Did new content load? Triggered everytime you scroll function globalScrollListener() { document.addEventListener("scroll", function scroll(e) { try { if (isPlayerAvailable()) { findVideoThumbs(); } } catch(error) { console.error("Couldn't initialize add to queue buttons \n" + error.message); } e.currentTarget.removeEventListener(e.type, scroll); if (script.debug) console.log("scroll"); setTimeout( function() { globalScrollListener(); }, 1000); }); } // *** Search *** // // initialize search function initSearch() { var anchor = document.querySelector("#watch7-sidebar-modules > div:nth-child(2)"); var html = '<input id="masthead-queueSearch" class="search-term yt-uix-form-input-bidi" type="text" placeholder="Search" style="outline: none; width:95%; padding: 5px 5px; margin: 0 4px">'; anchor.insertAdjacentHTML('afterbegin', html); var input = document.getElementById("masthead-queueSearch"); // suggestion dropdown init new autoComplete({ selector: '#masthead-queueSearch', minChars: 1, delay: 250, source: function(term, suggest) { suggest(script.suggestions); }, onSelect: function(event, term, item) { sendSearchRequest(term); } }); input.addEventListener('keydown', function(e) { if (script.debug) console.log(e); if (this.value !== "" && e.keyCode === 13) { sendSearchRequest(this.value); } else if (this.value !== "" && e.keyCode === 8) { searchSuggestions(this.value); } else { searchSuggestions(this.value + e.key); } }); input.addEventListener('click', function(event) { this.select(); }); } // callback from search suggestions attached to window function search_callback(data) { var raw = data[1]; script.suggestions = raw.map(function(array) { return array[0]; }); if (script.debug) console.log(script.suggestions); } // get search suggestions function searchSuggestions(value) { if (script.search_timeout !== null) clearTimeout(script.search_timeout); script.search_timeout = setTimeout( function() { if (script.debug) console.log("search request send"); var s = document.createElement('script'); s.type = 'text/javascript'; s.src = 'https://clients1.google.com/complete/search?client=youtube&hl=en&gl=be&gs_ri=youtube&ds=yt&q=' + encodeURIComponent(value) + '&callback=search_callback'; var h = document.getElementsByTagName('script')[0]; h.parentNode.insertBefore(s, h); }.bind(value), 100); } // send search request function sendSearchRequest(value) { if (script.debug) console.log("searching for " + value); var nextPage = document.getElementById("watch-more-related-button"); if (nextPage !== null) nextPage.parentNode.removeChild(nextPage); script.suggestions = []; var xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { var container = document.implementation.createHTMLDocument().documentElement; container.innerHTML = xmlHttp.responseText; processSearch(container); } }; xmlHttp.open('GET', 'https://www.youtube.com/results?q=' + encodeURIComponent(value), true); // true for asynchronous xmlHttp.send(null); } // process search request function processSearch(value) { var videoList = value.getElementsByClassName("item-section")[0]; var ul = document.getElementById("watch-related"); var li = ul.querySelectorAll("li.video-list-item"); if (li) { for (var i = li.length - 1; i >= 0; i--) { li[i].remove(); } } var videos = videoList.querySelectorAll('.yt-lockup-video'); for (var j = videos.length - 1; j >= 0; j--) { var video = videos[j]; var videoId = video.dataset.contextItemId; var videoTitle = video.querySelector('.yt-lockup-title > a').title; var videoStats = video.querySelector('.yt-lockup-meta-info').innerHTML; var videoTime = video.querySelector('.video-time').textContent; var videoChannelHTML = video.querySelector('.yt-lockup-byline'); var videoThumb = video.querySelector('div.yt-lockup-thumbnail > a > div > span > img'); if (videoThumb && videoThumb.hasAttribute("data-thumb")) { videoThumb = videoThumb.dataset.thumb; } else if (videoThumb) { videoThumb = videoThumb.src; } if (videoChannelHTML) { videoChannelHTML = videoChannelHTML.textContent; } else if (video.querySelector('.yt-lockup-description')) { videoChannelHTML = "<a href=\"" + window.location.href + "\" class=\"spf-link\">" + video.querySelector('.yt-lockup-description').firstChild.textContent + "</a>"; } var videoObject = new extendedYtVideo(videoTitle, videoId, null, null, videoChannelHTML, videoTime, videoStats, videoThumb); if (script.debug) console.log(videoObject); ul.insertAdjacentHTML("afterbegin", videoQueueHTML(videoObject).html); } // next page results var disabledButton = value.querySelector("button.yt-uix-button-default[disabled=true]"); var nextPageButton = disabledButton.nextElementSibling; findVideoThumbs(); } // *** Objects *** // // video object function ytVideo(name, id, html, anchor) { this.name = name; this.id = id; this.html = html; this.buttonAnchor = anchor; } // extended video object function extendedYtVideo(name, id, html, anchor, channelHTML, time, stats, thumb) { this.name = name; this.id = id; this.html = html; this.channelHTML = channelHTML; this.time = time; this.stats = stats; this.buttonAnchor = anchor; this.thumb = thumb; } // Queue object function Queue() { var queue = []; this.get = function() { return queue; }; this.set = function(newQueue) { queue = newQueue; setCache("queue", this.get()); }; this.isEmpty = function() { return 0 === queue.length; }; this.reset = function() { queue = []; this.update(0); }; this.enqueue = function(item) { queue.push(item); this.update(500); }; this.dequeue = function() { var item = queue.shift(); this.update(0); return item; }; this.remove = function(index) { queue.splice(index, 1); this.update(250); }; this.playNext = function(index) { var video = queue.splice(index, 1); queue.unshift(video[0]); this.update(0); }; this.playNow = function(index) { var video = queue.splice(index, 1); this.update(0); playNextVideo(video[0].id); }; this.showQueue = function() { var html = ""; queue.forEach( function(item) { html += item.html; }); return html; }; this.update = function(time) { setCache("queue", this.get()); if (script.debug) console.log(this.get().slice()); setTimeout(function() {displayQueue();}, time); }; } // *** Video *** // // play next video behavior depending on if you're watching fullscreen function playNextVideo(nextVidId) { if (script.debug) console.log("playing next song"); if (isPlayerFullscreen()) { script.ytplayer.loadVideoById(nextVidId, 0); } else { window.spf.navigate("https://www.youtube.com/watch?v=" + nextVidId + "&t=0s"); } } // finding video's that you can add to the queue function findVideoThumbs() { var videos = document.querySelectorAll(".related-list-item:not(.processed-buttons)"); for (var j = 0; j < videos.length; j++) { var video = findVideoInformation(videos[j], "#watch-related"); videos[j].classList.add("processed-buttons"); if (video) { addButton(video); } } } // extracting video information and creating a video object (that can be added to the queue) function findVideoInformation(video, query) { var anchor = video.querySelector(query + " .yt-uix-sessionlink:not(.related-playlist)"); if (anchor) { var videoTitle = video.querySelector("span.title").textContent.trim(); var id = getVideoInfoFromUrl(video.querySelector("a.yt-uix-sessionlink").href, "v"); var newVidObject = new ytVideo(videoTitle, id, video.outerHTML, anchor); return newVidObject; } return null; } // *** QUEUE *** // function displayQueue() { var html = script.queue.showQueue(); var queue = document.querySelector(".autoplay-bar"); var anchor = document.querySelector(".watch-sidebar-head"); // cleanup current queue var li = document.querySelectorAll(".autoplay-bar > li.video-list-item"); if (li) { for (var i = li.length - 1; i >= 0; i--) { li[i].remove(); } } // display new queue if (html !== null) { anchor.insertAdjacentHTML("afterend", html); // add remove buttons var items = queue.querySelectorAll(".related-list-item:not(.suggestion)"); for (var z = 0; z < items.length; z++) { var video = findVideoInformation(items[z], "#watch-queue"); // remove addbutton if there is one var addedButton = items[z].querySelector(".youtubequeue-add"); if (addedButton) addedButton.parentNode.parentNode.removeChild(addedButton.parentNode); if (video) { if (z > 0) { playNextButton(video, z); } else { playNowButton(video, z); } removeButton(video, z); } } // replace autoplay options with remove queue button var autoplay = queue.querySelector(".checkbox-on-off"); if (autoplay && !script.queue.isEmpty()) { removeQueueButton(autoplay); } // add queue button to suggestion video var suggestion = queue.querySelector(".suggestion:not(.processed)"); if (suggestion && !script.queue.isEmpty()) { var suggestionVideo = findVideoInformation(suggestion, "#watch-queue"); suggestion.classList.add("processed"); suggestionAddButton(suggestionVideo, suggestion); } // triggering lazyload window.scrollTo(window.scrollX, window.scrollY + 1); window.scrollTo(window.scrollX, window.scrollY - 1); } // remove not interested menu var menu = queue.getElementsByClassName("yt-uix-menu-trigger"); for (var j = menu.length - 1; j >= 0; j--) { menu[j].remove(); } } // *** Buttons *** // // The "add to queue" button function addButton(video) { var anchor = video.buttonAnchor; var html = '<div class="yt-uix-button yt-uix-button-default yt-uix-button-size-default" style="height:initial; padding:3px"><button class="yt-uix-button-content youtubequeue-add">Add to queue</button></div>'; anchor.insertAdjacentHTML('beforeend', html); anchor.querySelector(".youtubequeue-add").addEventListener("click", function handler(e) { e.preventDefault(); this.textContent = "Added!"; script.queue.enqueue(video); e.currentTarget.removeEventListener(e.type, handler); this.addEventListener("click", function (e) { e.preventDefault(); }); }); } // The "add to queue" button for the suggestion video function suggestionAddButton(video, suggestion) { var anchor = video.buttonAnchor; var html = '<div class="yt-uix-button yt-uix-button-default yt-uix-button-size-default" style="height:initial; padding:3px"><button class="yt-uix-button-content youtubequeue-add">Add to queue</button></div>'; anchor.insertAdjacentHTML('beforeend', html); anchor.querySelector(".youtubequeue-add").addEventListener("click", function handler(e) { e.preventDefault(); this.textContent = "Added!"; suggestion.classList.remove("suggestion"); video.html = suggestion.outerHTML; script.queue.enqueue(video); e.currentTarget.removeEventListener(e.type, handler); suggestion.parentNode.removeChild(suggestion); }); } // The "remove from queue" button function removeButton(video, nb) { var anchor = video.buttonAnchor; var html = '<div class="yt-uix-button yt-uix-button-default yt-uix-button-size-default" style="height:initial; padding:3px; margin-left:3px"><button class="yt-uix-button-content youtubequeue-remove">Remove</button></div>'; anchor.insertAdjacentHTML("beforeend", html); anchor.querySelector(".youtubequeue-remove").addEventListener('click', function handler(e) { e.preventDefault(); this.textContent = "Removed!"; script.queue.remove(nb); e.currentTarget.removeEventListener(e.type, handler); this.addEventListener("click", function (e) { e.preventDefault(); }); }); } // The "play next" button function playNextButton(video, nb) { var anchor = video.buttonAnchor; var html = '<div class="yt-uix-button yt-uix-button-default yt-uix-button-size-default" style="height:initial; padding:3px"><button class="yt-uix-button-content youtubequeue-next">Play Next</button></div>'; anchor.insertAdjacentHTML("beforeend", html); anchor.querySelector(".youtubequeue-next").addEventListener('click', function handler(e) { e.preventDefault(); this.textContent = "To the top!"; script.queue.playNext(nb); e.currentTarget.removeEventListener(e.type, handler); this.addEventListener("click", function (e) { e.preventDefault(); }); }); } // The "play now" button function playNowButton(video, nb) { var anchor = video.buttonAnchor; var html = '<div class="yt-uix-button yt-uix-button-default yt-uix-button-size-default" style="height:initial; padding:3px"><button class="yt-uix-button-content youtubequeue-now">Play Now</button></div>'; anchor.insertAdjacentHTML("beforeend", html); anchor.querySelector(".youtubequeue-now").addEventListener("click", function handler(e) { e.preventDefault(); this.textContent = "Playing!"; script.queue.playNow(nb); e.currentTarget.removeEventListener(e.type, handler); this.addEventListener("click", function (e) { e.preventDefault(); }); }); } // The "remove queue and all its videos" button function removeQueueButton(anchor) { var html = '<div class="yt-uix-button yt-uix-button-default yt-uix-button-size-default" style="height:initial; padding:3px"><button class="yt-uix-button-content youtubequeue-remove-list">Remove Queue</button></div>'; anchor.innerHTML = html; anchor.querySelector(".youtubequeue-remove-list").addEventListener("click", function handler(e) { e.preventDefault(); this.textContent = "Removed!"; script.queue.reset(); e.currentTarget.removeEventListener(e.type, handler); this.addEventListener("click", function (e) { e.preventDefault(); }); }); } // *** GETTERS *** // function getVideoPlayer() { return document.getElementById("movie_player"); } function isPlayerAvailable() { return /https:\/\/www\.youtube\.com\/watch\?v=.*/.test(document.location.href) && !getVideoInfoFromUrl(document.location.href, "list"); } function isPlayerFullscreen() { return (script.ytplayer.classList.contains("ytp-fullscreen")); } function getVideoInfoFromUrl(url, info) { if (url.indexOf('?') === -1) return null; var urlVariables = url.split('?')[1].split('&'), varName; for (var i = 0; i < urlVariables.length; i++) { varName = urlVariables[i].split('='); if (varName[0] === info) { return varName[1] === undefined ? null : varName[1]; } } } // *** LOCALSTORAGE *** // function getCache(key) { return JSON.parse(localStorage.getItem("YTQUEUE#" + script.version + '#' + key)); } function deleteCache(key) { localStorage.removeItem("YTQUEUE#" + script.version + '#' + key); } function setCache(key, value) { localStorage.setItem("YTQUEUE#" + script.version + '#' + key, JSON.stringify(value)); } // *** HTML *** // function videoQueueHTML(video) { var strVar=""; strVar += "<li class=\"video-list-item related-list-item show-video-time related-list-item-compact-video\">"; strVar += " <div class=\"related-item-dismissable\">"; strVar += " <div class=\"content-wrapper\">"; strVar += " <a href=\"\/watch?v=" + video.id + "\" class=\"yt-uix-sessionlink content-link spf-link spf-link\" rel=\"spf-prefetch\" title=\"" + video.name + "\">"; strVar += " <span dir=\"ltr\" class=\"title\">" + video.name + "<\/span>"; strVar += " <span class=\"stat\">" + video.channelHTML + "<\/span>"; strVar += " <ul class=\"yt-lockup-meta-info stat\">" + video.stats + "<\/ul>"; strVar += " <\/a>"; strVar += " <\/div>"; strVar += " <div class=\"thumb-wrapper\">"; strVar += " <a href=\"\/watch?v=" + video.id + "\" class=\"yt-uix-sessionlink thumb-link spf-link spf-link\" rel=\"spf-prefetch\" tabindex=\"-1\" aria-hidden=\"true\">"; strVar += " <span class=\"yt-uix-simple-thumb-wrap yt-uix-simple-thumb-related\" tabindex=\"0\" data-vid=\"" + video.id + "\"><img aria-hidden=\"true\" style=\"top: 0px\" width=\"168\" height=\"94\" alt=\"\" src=\"" + video.thumb + "\"><\/span>"; strVar += " <\/a>"; strVar += " <span class=\"video-time\">"+ video.time +"<\/span>"; strVar += " <button class=\"yt-uix-button yt-uix-button-size-small yt-uix-button-default yt-uix-button-empty yt-uix-button-has-icon no-icon-markup addto-button video-actions spf-nolink hide-until-delayloaded addto-watch-later-button yt-uix-tooltip\" type=\"button\" onclick=\";return false;\" title=\"Watch Later\" role=\"button\" data-video-ids=\"" + video.id + "\" data-tooltip-text=\"Watch Later\"><\/button>"; strVar += " <\/div>"; strVar += " <\/div>"; strVar += "<\/li>"; video.html = strVar; return video; } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址