NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Intercept YouTube Links
// @namespace http://github.com/panzi
// @description Inserts a hop to all watch links on YouTube in order to disable auto-play. The link won't be inserted by a normal click, only middle-click/context menu/D'n'D.
// @include *
// @run-at document-end
// @version 1.0
// ==/UserScript==
var linkSelector;
var linkRegex;
var videoTitle;
if (/^https?:\/\/(www\.)?youtube(-nocookie)?\.com\//i.test(location.href)) {
linkSelector = 'a[href^="/watch?"], a[href^="https://www.youtube.com/watch?"], a[href^="http://www.youtube.com/watch?"], '+
'a[href^="https://www.youtube-nocookie.com/watch?"], a[href^="http://www.youtube-nocookie.com/watch?"]';
linkRegex = /^(https?:\/\/(www\.)?youtube(-nocookie)?\.com)?\/watch\?/i;
videoTitle = function (link) {
var title = link.title;
if (!title) {
title = link.querySelector('.title');
if (title) {
title = title.title||title.textContent||link.href;
}
else {
title = link.href;
var elem = link.parentNode.parentNode;
if (elem && elem.classList && elem.classList.contains('yt-lockup-video')) {
elem = elem.querySelector('a[title]');
if (elem) title = elem.title;
}
}
}
return title;
};
}
else {
linkSelector = 'a[href^="https://www.youtube.com/watch?"], a[href^="http://www.youtube.com/watch?"], '+
'a[href^="https://www.youtube-nocookie.com/watch?"], a[href^="http://www.youtube-nocookie.com/watch?"]';
linkRegex = /^https?:\/\/(www\.)?youtube(-nocookie)?\.com\/watch\?/i;
videoTitle = function (link) {
return link.title||link.textContent||link.href;
};
}
function isVideoLink (elem) {
return elem.nodeType === 1 && elem.nodeName === "A" && linkRegex.test(elem.href);
}
function insertHops (ctx) {
var links = ctx.querySelectorAll(linkSelector);
if (isVideoLink(ctx)) {
links = Array.prototype.slice.call(links);
links.push(ctx);
}
for (var i = 0; i < links.length; ++ i) {
insertHop(links[i]);
}
}
function parseParams (search) {
var params = {};
if (search) {
search = search.split("&");
for (var i = 0; i < search.length; ++ i) {
var param = search[i].split("=");
params[decodeURIComponent(param[0])] = decodeURIComponent(param.slice(1).join("="));
}
}
return params;
}
function insertHop (link) {
var url = link.href;
var video_id = parseParams(/^[^\?]*\?([^#]*)/i.exec(url)[1]).v;
var thumb = 'https://i1.ytimg.com/vi/'+video_id+'/hqdefault.jpg';
var title = videoTitle(link);
var reader = new FileReader();
reader.onload = function () {
link.href = this.result;
link.addEventListener('click',function (event) {
if ('buttons' in event ? event.buttons & 1 : event.button === 0) {
location.href = url;
event.preventDefault();
event.stopPropagation();
}
}, false);
};
reader.readAsDataURL(new Blob(['<!DOCTYPE html>',
'<html>',
'<head>',
'<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>',
'<title>',escapeHtml(title),'</title>',
'<style type="text/css">html, body {margin:0;padding:0;}\n',
'.main {width:100%;height:100%;position:absolute;left:0;right:0;top:0;bottom:0;display:table;}\n',
'.title {display:block;margin-top:10px;font-size:26px;font-weight:bold;}\n',
'a {display:table-cell;text-align:center;vertical-align:middle;margin:auto;text-decoration:none;}\n',
'img {border:none;}</style>',
'</head>',
'<body>',
'<div class="main">',
'<a href="',escapeHtml(url),'">',
'<img src="',escapeHtml(thumb),'"/>',
'<span class="title">',escapeHtml(title),'</a></span>',
'</a>',
'</div>',
'</body>',
'</html>'],{encoding:"UTF-8",type:"text/html;charset=UTF-8"}));
}
var HTML_CHAR_MAP = {
'<': '<',
'>': '>',
'&': '&',
'"': '"',
"'": '''
};
function escapeHtml (s) {
return s.replace(/[<>&"']/g, function (ch) {
return HTML_CHAR_MAP[ch];
});
}
insertHops(document);
var observer = new MutationObserver(function (mutations) {
for (var i = 0; i < mutations.length; ++ i) {
var mutation = mutations[i];
if (mutation.type === "childList") {
for (var j = 0; j < mutation.addedNodes.length; ++ j) {
var node = mutation.addedNodes[j];
if (node.nodeType === 1) {
insertHops(node);
}
}
}
else if (mutation.type === "attributes") {
if (mutation.attributeName === "href" && isVideoLink(mutation.target)) {
insertHop(mutation.target);
}
}
}
});
observer.observe(document.body, {attributes: true, childList: true, subtree: true});