Skip to content

Instantly share code, notes, and snippets.

@Necroforger
Last active September 7, 2018 04:48
Show Gist options
  • Save Necroforger/acbd1a51e9b3ce0662322173bb4f41d7 to your computer and use it in GitHub Desktop.
Save Necroforger/acbd1a51e9b3ce0662322173bb4f41d7 to your computer and use it in GitHub Desktop.
Adds some extra buttons to youtube videos
// ==UserScript==
// @name Youtube Tools
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Adds some extra buttons to youtube
// @author necroforger
// @match https://www.youtube.com/watch*
// @grant none
// ==/UserScript==
(async function () {
'use strict';
/**
* Continually tries to get the #top-level-buttons element until it is found
* @returns {Promise<HTMLElement>}
*/
async function getContainer() {
return new Promise((resolve) => {
let id = window.setInterval(function () {
let container = document.querySelector("#top-level-buttons");
if (container) {
window.clearInterval(id);
resolve(container);
}
}, 300);
})
}
/**@type {CSSStyleDeclaration}*/
let btnStyle = {
backgroundColor: "#232323",
cursor: "pointer",
border: "none",
color: "white",
marginLeft: "10px",
}
/**@type {CSSStyleDeclaration} */
let btnStyleHover = {
backgroundColor: "white",
color: "black",
}
/**
* apply css to an element
* @param {HTMLElement} element
* @param {CSSStyleDeclaration} css
*/
function applyCSS(element, css) {
for (let key in css) {
element.style[key] = css[key];
}
}
/**
* Changes the CSS on hover
* @param {HTMLElement} element
* @param {CSSStyleDeclaration} oldCSS
* @param {CSSStyleDeclaration} css
*/
function hover(element, oldCSS, css) {
console.log(oldCSS);
element.addEventListener("mouseover", function () {
applyCSS(this, css);
});
element.addEventListener("mouseleave", function () {
applyCSS(this, oldCSS);
});
}
/**
* handler for the popout button.
* Opens a popout window linking to the corresponding embed
* page for the video
*/
function btnPopoutHandler() {
let v = document.querySelector(".video-stream");
if (!v) {
console.log("could not find video");
return;
}
v.pause();
let id = videoIDFromURL(window.location.toString());
let url = `https://www.youtube.com/embed/${id}?start=${Math.round(v.currentTime)}&autoplay=1&ytt_popout=1`
let win = window.open(url, url, "width=320,height=190,resizable=yes,scrollbars=no,titlebar=no");
console.log("navigating to: " + url);
if (window.focus()) {
win.focus();
}
}
/**
* Opens the thumbnail for this video
*/
function btnThumbnailHandler() {
window.open(`https://img.youtube.com/vi/${videoIDFromURL(window.location.toString())}/0.jpg`, "_blank");
}
/**
* gets a videoID from a given url
* @param {string} url
* @returns string
*/
function videoIDFromURL(url) {
return paramFromURL(url, "v");
}
/**
* returns a query parameter from a url given a url
* @param {string} url
* @param {string} key
* @returns {string}
*/
function paramFromURL(url, key) {
let index = url.indexOf("?");
if (index < 0 || index + 1 >= url.length) {
return "";
}
let query = url.substring(index + 1);
let fields = query.split("&");
for (let x of fields) {
let parts = x.split("=");
if (parts[0] == key) {
return parts[1] || "";
}
}
return "";
}
/**
* creates a button and attaches it to the #top-level-buttons element
* @param {string} name button text content
* @param {string} id id to assign element
* @param {string} handler handler called when button is clicked
* @param {CSSStyleDeclaration} style button styling
* @param {CSSStyleDeclaration} hoverStyle button styling when hovered
*/
async function createButton(name, id, handler, style, hoverStyle) {
let container = await getContainer();
let popoutButton = document.createElement("button");
popoutButton.id = id;
popoutButton.innerHTML = name;
popoutButton.addEventListener("click", handler);
applyCSS(popoutButton, btnStyle);
hover(popoutButton, style, hoverStyle);
container.insertBefore(popoutButton, container.children[0]);
}
/**
* creates all buttons
*/
async function createButtons() {
await Promise.all([
createButton("popout", "ytt_popout_button", btnPopoutHandler, btnStyle, btnStyleHover),
createButton("thumb", "ytt_thumbnail_button", btnThumbnailHandler, btnStyle, btnStyleHover),
]);
};
let btnCreating = false;
window.setInterval(async function () {
console.log("btnCreating");
if (!btnCreating && !document.getElementById("ytt_popout_button")) {
btnCreating = true;
await createButtons();
btnCreating = false;
}
}, 150);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment