Last active
September 7, 2018 04:48
-
-
Save Necroforger/acbd1a51e9b3ce0662322173bb4f41d7 to your computer and use it in GitHub Desktop.
Adds some extra buttons to youtube videos
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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