Youtube Play Next Queue

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!

От 24.04.2017. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Youtube Play Next Queue
// @version      1.1.2
// @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://greasyfork.org/users/16080
// @grant        none
// @noframes
// ==/UserScript==

(function() {
	var script = {
		ytplayer: null,
		playnext: true,
		queue: new Queue(),
		version: '1.1.2',
		search_timeout: null,
		search_suggestions: [],
		debug: false
	};

	// callback function for search results
	window.search_callback = search_callback;

	// youtube search parameters
	const GeoLocation = window.yt.config_.INNERTUBE_CONTEXT_GL;
	const HostLanguage = window.yt.config_.INNERTUBE_CONTEXT_HL;

	// 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");
		clearSearchRequests();
		if (isPlayerAvailable()) {
			startScript(2);
		}
	});

	main();
	function main() {
		initGlobalScrollListener();
		addCSS();

		if (isPlayerAvailable()) {
			if (script.debug) console.log("player available");
			startScript(5);
		} else {
			if (script.debug) console.log("player unavailable");
		}
	}

	function startScript(retry) {
		script.ytplayer = getVideoPlayer();
		if (script.debug) console.log(script.ytplayer);
		if (script.ytplayer) {
			if (getVideoInfoFromUrl(document.location.href, "t") == "0s")
				script.ytplayer.seekTo(0);

			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");
			initVideoStateListener();
			if (script.debug) console.log("initialising queue buttons");
			initQueueButtons();
		} else if (retry > 0) { // fix conflict with Youtube+ script
			setTimeout( function() {
				startScript(retry--);
			}.bind(retry), 1000);
		}
	}

	// *** LISTENERS *** //

	function initVideoStateListener() {
		// play next video in queue if current video is finished playing (state equal to 0)
		script.ytplayer.addEventListener("onStateChange", function(videoState) {
			if (script.debug) console.log("state changed", videoState);
			const FINISHED_STATE = 0;
			if (videoState === FINISHED_STATE && script.playnext === true && !script.queue.isEmpty()) {
				script.playnext = false;
				var next = script.queue.dequeue();
				playNextVideo(next.id);
			} else if (videoState !== FINISHED_STATE) {
				script.playnext = true;
			}
		});
	}

	// Did new content load? Triggered everytime you scroll
	function initGlobalScrollListener() {
		document.addEventListener("scroll", function scroll(event) {
			try {
				if (isPlayerAvailable()) {
					if (script.ytplayer === null) {
						script.ytplayer = getVideoPlayer();
						startScript(0);
					} else {
						initQueueButtons();
					}
				}
			} catch(error) {
				console.error("Couldn't initialize add to queue buttons \n" + error.message);
			}

			event.currentTarget.removeEventListener(event.type, scroll);
			if (script.debug) console.log("scroll");

			setTimeout( function() {
				initGlobalScrollListener();
			}, 1000);
		});
	}

	// *** 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(delay) {
			setCache("queue", this.get());
			if (script.debug) console.log(this.get().slice());
			setTimeout(function() {displayQueue();}, delay);
		};
	}

	// *** VIDEO & PLAYER *** //

	// play next video behavior depending on if you're watching fullscreen
	function playNextVideo(nextVideoId) {
		if (script.debug) console.log("playing next song");
		if (isPlayerFullscreen()) {
			script.ytplayer.loadVideoById(nextVideoId, 0);
		} else {
			window.spf.navigate("https://www.youtube.com/watch?v=" + nextVideoId + "&t=0s");
		}
	}

	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") && document.getElementById("live-chat-iframe") === null;
	}

	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];
			}
		}
	}

	// extracting video information and creating a video object (that can be added to the queue)
	function findVideoInformation(video, selector) {
		var anchor = video.querySelector(selector + " .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 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");

		// show the queue if not empty
		if (!script.queue.isEmpty()) {
			if (script.debug) console.log("showing queue");
			if (script.debug) console.log(script.queue.get());
			displayQueue();
		}
	}

	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(".queue-add");
				if (addedButton)
					addedButton.parentNode.remove();

				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 *** //

	// finding video's and adding the queue buttons
	function initQueueButtons() {
		var videos = document.querySelectorAll(".related-list-item:not(.processed-buttons)");
		for (var j = 0; j < videos.length; j++) {
			try {
				var video = findVideoInformation(videos[j], "#watch-related");
				videos[j].classList.add("processed-buttons");
				if (video) {
					addButton(video);
				}
			} catch(error) {
			    console.error("Couldn't initialize \"Add to queue\" button on a video \n" + error.message);
		    }
	    }
	}

	// The "add to queue" button
	function addButton(video) {
		var anchor = video.buttonAnchor;
		var html = '<div class="queue-button yt-uix-button yt-uix-button-default yt-uix-button-size-default"><button class="yt-uix-button-content queue-add">Add to queue</button></div>';
		anchor.insertAdjacentHTML('beforeend', html);

		anchor.getElementsByClassName("queue-add")[0].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="queue-button yt-uix-button yt-uix-button-default yt-uix-button-size-default"><button class="yt-uix-button-content queue-add">Add to queue</button></div>';
		anchor.insertAdjacentHTML('beforeend', html);

		anchor.getElementsByClassName("queue-add")[0].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="queue-button yt-uix-button yt-uix-button-default yt-uix-button-size-default" style="margin-left:3px"><button class="yt-uix-button-content queue-remove">Remove</button></div>';
		anchor.insertAdjacentHTML("beforeend", html);

		anchor.getElementsByClassName("queue-remove")[0].addEventListener('click', function handler(e) {
			e.preventDefault();
			this.textContent = "Removed!";
			script.queue.remove(nb);
			restoreAddButton(video.id);
			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="queue-button yt-uix-button yt-uix-button-default yt-uix-button-size-default"><button class="yt-uix-button-content queue-next">Play Next</button></div>';
		anchor.insertAdjacentHTML("beforeend", html);

		anchor.getElementsByClassName("queue-next")[0].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="queue-button yt-uix-button yt-uix-button-default yt-uix-button-size-default"><button class="yt-uix-button-content queue-now">Play Now</button></div>';
		anchor.insertAdjacentHTML("beforeend", html);

		anchor.getElementsByClassName("queue-now")[0].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="queue-button yt-uix-button yt-uix-button-default yt-uix-button-size-default" style="margin:0px"><button class="yt-uix-button-content remove-queue">Remove Queue</button></div>';
		anchor.innerHTML = html;

		anchor.getElementsByClassName("remove-queue")[0].addEventListener("click", function handler(e) {
			e.preventDefault();
			this.textContent = "Removed!";
			script.queue.reset();
			restoreAddButton("*"); // restore all
			e.currentTarget.removeEventListener(e.type, handler);
			this.addEventListener("click", function (e) {
				e.preventDefault();
			});
		});
	}

	function restoreAddButton(id) {
		var videos = document.querySelectorAll(".related-list-item");
		for (var j = 0; j < videos.length; j++) {
			if (id === "*" || id === getVideoInfoFromUrl(videos[j].querySelector("a.yt-uix-sessionlink").href, "v")) {
				// remove current addbutton if there is one
				var addedButton = videos[j].querySelector(".queue-add");
				if (addedButton)
					addedButton.parentNode.remove();

				// make new addbutton
				var video = findVideoInformation(videos[j], "#watch-related");
				if (video) {
					addButton(video);
				}
			}
		}
	}

	// *** 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.search_suggestions);
			},
			onSelect: function(event, term, item) {
				sendSearchRequest(term);
			}
		});

		input.addEventListener('keydown', function(event) {
			if (script.debug) console.log(e);
			const ENTER = 13;
			const BACKSPACE = 8;
			if (this.value !== "" && event.keyCode === ENTER) {
				sendSearchRequest(this.value);
			} else if (this.value !== "" && event.keyCode === BACKSPACE) {
				searchSuggestions(this.value);
			} else {
				searchSuggestions(this.value + event.key);
			}
		});

		input.addEventListener('click', function(event) {
			this.select();
		});
	}

	// callback from search suggestions attached to window
	function search_callback(data) {
		var raw = data[1]; // extract relevant data from json
		script.search_suggestions = raw.map(function(array) {
			return array[0]; // change 2D array to 1D array with only suggestions
		});
		if (script.debug) console.log(script.search_suggestions);
	}

	// get search suggestions
	function searchSuggestions(value) {
		if (script.search_timeout !== null) clearTimeout(script.search_timeout);

		// only allow 1 search request every 100 milliseconds
		script.search_timeout = setTimeout( function() {
			if (script.debug) console.log("search request send");
			var scriptElement = document.createElement('script');
			scriptElement.type = 'text/javascript';
			scriptElement.className = 'search-request';
			scriptElement.src = 'https://clients1.google.com/complete/search?client=youtube&hl=' + HostLanguage + '&gl=' + GeoLocation + '&gs_ri=youtube&ds=yt&q=' + encodeURIComponent(value) + '&callback=search_callback';
			document.head.appendChild(scriptElement);
		}.bind(value), 100);
	}

	// send search request
	function sendSearchRequest(value) {
		if (script.debug) console.log("searching for " + value);

		document.getElementById("masthead-queueSearch").blur(); // close search suggestions dropdown

		var nextPage = document.getElementById("watch-more-related-button");
		if (nextPage !== null) nextPage.parentNode.removeChild(nextPage); // removing the "More Suggestions" link

		script.search_suggestions = []; // clearing the search 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);
	}

	function clearSearchRequests() {
		var requests = document.getElementsByClassName("search-request");
		if (requests) {
			for (var i = requests.length - 1; i >= 0; i--) {
				requests[i].remove();
			}
		}
	}

	// process search request
	function processSearch(value) {
		var videoList = value.getElementsByClassName("item-section")[0];

		// remove current videos (and replace with searched videos later)
		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();
			}
		}

		// insert searched videos
		var videos = videoList.querySelectorAll('.yt-lockup-video');
		for (var j = videos.length - 1; j >= 0; j--) {
			var video = videos[j];
			try {
				var videoId = video.dataset.contextItemId;
				var videoTitle = video.querySelector('.yt-lockup-title > a').title;
				var videoStats = video.querySelector('.yt-lockup-meta').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);
			} catch (error) {
				console.error("failed to process video", video);
			}
		}

		initQueueButtons();
	}

	// *** 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 & CSS *** //

	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 += "				   <div class=\"yt-lockup-meta stat\">" + video.stats + "<\/div>";
		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;
	}

	function addCSS() {
		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; }

           #watch-related .yt-uix-button-size-default { display: none; }
           #watch-related .processed-buttons:hover .yt-uix-button-size-default { display: inline-block; }
           .queue-button { height: 15px; padding: 0.1em 0.4em 0.3em 0.4em; margin: 2px 0; }

           .related-list-item span.title { max-height: 2.4em; }
            `;

		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);
	}
})();