There are 11 previous versions of this script.
// ==UserScript==
// @name YouTube Center Only Download
// @version 1.2
// @author Jeppe Rune Mortensen (YePpHa)
// @description This adds the download button from YouTube Center.
// @match http://*.youtube.com/watch?*
// @match https://*.youtube.com/watch?*
// @match http://*.youtube.com/verify_age?next_url=/watch*
// @match https://*.youtube.com/verify_age?next_url=/watch*
// @match http://userscripts.org/scripts/source/124921.meta.js
// @updateVersion 8
// ==/UserScript==
(function(){
if (!location.href.match(/^(http|https)\:\/\/(.*?)\.youtube\.com\/watch\?/)) {
return;
}
var updateVersion = 8;
var updateId = "124921";
var YouTubeType = {
'video/x-flv': 'FLV',
'video/mp4': 'MP4',
'video/webm': 'WebM',
'video/3gpp': '3GP'
};
var YouTubeQuality = {
'small': 'Low Definition',
'medium': 'Standard Definition',
'large': 'High Definition',
'hd720': 'HD Definition',
'hd1080': 'Full HD Definition',
'highres': 'Original Definition'
};
var YouTubeQualityResolution = {
'small': '240p',
'medium': '360p',
'large': '480p',
'hd720': '720p',
'hd1080': '1080p',
'highres': 'Original'
};
var YouTubeVariables = {};
var YouTubeStreamInformation = [];
var YouTubeStreamSortedByFormat = {};
var DownloadIFrame = null;
var YouTubeVideoTitle = null;
var YouTubeUpdateElement = null;
function xhr(details) {
if (typeof GM_xmlhttpRequest != "undefined") {
GM_xmlhttpRequest(details);
return true;
} else {
var xmlhttp;
if (typeof XMLHttpRequest != "undefined") {
xmlhttp = new XMLHttpRequest();
} else if (typeof opera != "undefined" && typeof opera.XMLHttpRequest != "undefined") {
xmlhttp = new opera.XMLHttpRequest();
} else if (typeof uw != "undefined" && typeof uw.XMLHttpRequest != "undefined") {
xmlhttp = new uw.XMLHttpRequest();
} else {
if (details["onerror"]) {
details["onerror"]();
}
return false;
}
xmlhttp.onreadystatechange = function(){
var responseState = {
responseXML:(xmlhttp.readyState == 4 ? xmlhttp.responseXML : ''),
responseText:(xmlhttp.readyState == 4 ? xmlhttp.responseText : ''),
readyState:xmlhttp.readyState,
responseHeaders:(xmlhttp.readyState == 4 ? xmlhttp.getAllResponseHeaders() : ''),
status:(xmlhttp.readyState == 4 ? xmlhttp.status : 0),
statusText:(xmlhttp.readyState == 4 ? xmlhttp.statusText : '')
};
if (details["onreadystatechange"]) {
details["onreadystatechange"](responseState);
}
if (xmlhttp.readyState == 4) {
if (details["onload"] && xmlhttp.status >= 200 && xmlhttp.status < 300) {
details["onload"](responseState);
}
if (details["onerror"] && (xmlhttp.status < 200 || xmlhttp.status >= 300)) {
details["onerror"](responseState);
}
}
};
try {
xmlhttp.open(details.method, details.url);
} catch(e) {
if(details["onerror"]) {
details["onerror"]({responseXML:'',responseText:'',readyState:4,responseHeaders:'',status:403,statusText:'Forbidden'});
}
return false;
}
if (details.headers) {
for (var prop in details.headers) {
xmlhttp.setRequestHeader(prop, details.headers[prop]);
}
}
xmlhttp.send((typeof(details.data) != 'undefined') ? details.data : null);
return true;
}
return false;
}
function getTitle() {
var meta = document.getElementsByTagName("meta");
for (var i = 0; i < meta.length; i++) {
if (meta[i].getAttribute("name") == "title") {
return meta[i].getAttribute("content");
}
}
}
function getFormatType(format) {
if (YouTubeType[format]) {
return YouTubeType[format];
}
return "Unknown";
}
function getQuality(quality) {
if (YouTubeQuality[quality]) {
return YouTubeQuality[quality];
}
return "Unknown";
}
function getQualityResolution(quality) {
if (YouTubeQualityResolution[quality]) {
return YouTubeQualityResolution[quality];
}
return "Unknown";
}
function getSignature(stream) {
if (stream.sig) return stream.sig;
if (stream.s) return decodeSignature(stream.s);
}
function decodeSignature(sig){
function swap(arr, num) {
var a = arr[0];
var b = arr[num%arr.length];
arr[0] = b;
arr[num] = a;
return arr;
}
function clone(arr, num) {
return arr.slice(num);
}
function reverse(arr) {
return arr.reverse();
}
var __sig = sig.split("");
__sig = clone(__sig, 2);
__sig = swap(__sig, 34);
__sig = clone(__sig, 1);
__sig = swap(__sig, 23);
__sig = reverse(__sig);
return __sig.join("");
}
function downloadFile(itag) {
if (!DownloadIFrame) {
initDownloadFrame();
}
for (var i = 0; i < YouTubeStreamInformation.length; i++) {
if (YouTubeStreamInformation[i].itag == itag) {
DownloadIFrame.setAttribute("src", YouTubeStreamInformation[i].url + "&title=" + encodeURIComponent(YouTubeVideoTitle) + "&signature=" + encodeURIComponent(getSignature(YouTubeStreamInformation[i])));
break;
}
}
}
function createYouTubeButton(title, html, menu, classNames, onclick) {
var button = document.createElement("button");
button.innerHTML = html;
if (menu) {
button.appendChild(menu);
}
button.className = "yt-uix-button yt-uix-button-text yt-uix-tooltip" + (classNames ? " " + classNames : "");
button.style.marginTop = "1px";
button.setAttribute("title", title);
button.setAttribute("type", "button");
button.setAttribute("aria-pressed", "false");
button.setAttribute("onclick", ";return false;");
button.setAttribute("role", "button");
button.setAttribute("data-tooltip-text", title);
if (onclick) {
button.addEventListener("click", onclick, false);
}
return button;
}
function createYouTubeMenu(build) {
var menu = document.createElement("div");
menu.className = "yt-uix-button-menu hid";
for (var i = 0; i < build.length; i++) {
var item = document.createElement("span");
item.className = (build[i].className || build[i].className == "" ? build[i].className : "yt-uix-button-menu-item");
if (build[i].style) {
item.setAttribute("style", build[i].style);
}
item.setAttribute("onclick", ";return false;");
item.innerHTML = build[i].text;
if (build[i].onclick) {
item.addEventListener("click", build[i].onclick, false);
}
if (build[i].args) {
for (var key in build[i].args) {
item.setAttribute(key, build[i].args[key]);
}
}
if (build[i].register) {
build[i].register(item);
}
menu.appendChild(item);
}
return menu;
}
function splitYouTubeInformationByFormat(informations) {
var collection = {};
for (var i = 0; i < informations.length; i++) {
if (!collection[informations[i].type.format]) {
collection[informations[i].type.format] = [];
}
var l = collection[informations[i].type.format].length;
collection[informations[i].type.format][l] = informations[i];
}
return collection;
}
function getFormatTitle(f) {
return getQuality(f.quality) + ", " + getQualityResolution(f.quality) + " (" + f.dimension + ")" + (f.stereo3d && f.stereo3d == 1 ? "<span style=\"float:right\"> 3D</span>" : "");
}
function is3D(f) {
return (f.stereo3d && f.stereo3d == 1 ? true : false);
}
function getYouTubePlayerConfigVariables() {
var uw = (function(){
var a;
try {
a = unsafeWindow === window ? false : unsafeWindow;
} finally {
return a || (function(){
var e = document.createElement('p');
e.setAttribute('onclick', 'return window;');
return e.onclick();
}());
}
})();
if (uw && uw.ytplayer && uw.ytplayer.config) return uw.ytplayer.config;
if (document.body.innerHTML.indexOf("<script>var ytplayer = ytplayer || {};ytplayer.config = ") !== -1) {
return JSON.parse(document.body.innerHTML.split("<script>var ytplayer = ytplayer || {};ytplayer.config = ")[1].split(";</script>")[0]);
}
}
function parseYouTubeFmtList(list) {
var parts = list.split(",");
var collection = [];
for (var i = 0; i < parts.length; i++) {
var a = parts[i].split("/");
collection[collection.length] = {
itag: a[0],
dimension: a[1],
unknown1: a[2],
unknown2: a[3],
unknown3: a[4]
};
}
return collection;
}
function parseYouTubeStreamList(list) {
var parts = list.split(",");
var collection = [];
for (var i = 0; i < parts.length; i++) {
var coll = {};
var args = parts[i].split("&");
for (var j = 0; j < args.length; j++) {
var keys = args[j].split("=");
coll[keys[0]] = unescape(keys[1]);
}
if (coll.type.match(/[0-9a-zA-Z]+\/[0-9a-zA-Z]+;\+(.*)/)) {
var tmp = coll.type.split(";");
var ff = tmp[0];
var fe = "";
if (tmp.length > 2) {
var t = "";
for (var j = 1; j < tmp.length; j++) {
t += tmp[j];
}
fe = t;
} else {
fe = tmp[1];
}
coll.type = {
format: ff,
extra: fe
};
} else {
coll.type = {
format: coll.type,
extra: ""
};
}
collection[collection.length] = coll;
}
return collection;
}
function checkForUpdates() {
xhr({
method: "GET",
url: "http://userscripts.org/scripts/source/" + updateId + ".meta.js",
headers: {
"Content-Type": "text/plain"
},
onload: function (response) {
var nv;
if (response.readyState === 4 && response.status === 200) {
nv = parseInt(/^\/\/ @updateVersion\s+([0-9]+)$/m.exec(response.responseText)[1], 10);
if (nv && nv > updateVersion) {
if (YouTubeUpdateElement) {
YouTubeUpdateElement.style.display = "block";
}
}
}
}
});
}
function parseYouTubeFormats(ytplayer_config) {
var fmtList = parseYouTubeFmtList(ytplayer_config.args.fmt_list);
var streamList = parseYouTubeStreamList(ytplayer_config.args.url_encoded_fmt_stream_map);
var collection = [];
for (var i = 0; i < streamList.length; i++) {
var fl = null;
for (var j = 0; j < fmtList.length; j++) {
if (streamList[i].itag != fmtList[j].itag) continue;
fl = fmtList[j];
break;
}
if (fl == null) {
collection[collection.length] = streamList[i];
} else {
var coll = streamList[i];
coll.dimension = fl.dimension;
coll.unknown1 = fl.unknown1;
coll.unknown2 = fl.unknown2;
coll.unknown3 = fl.unknown3;
collection[collection.length] = coll;
}
}
return collection;
}
function initUI() {
var b = [];
for (var key in YouTubeStreamSortedByFormat) {
b[b.length] = {
text: "<b>"+getFormatType(key)+"</b>",
className: "",
style: "color:#666;font-size:0.9166em;padding-left:9px;"
};
for (var i = 0; i < YouTubeStreamSortedByFormat[key].length; i++) {
b[b.length] = {
text: getFormatTitle(YouTubeStreamSortedByFormat[key][i]),
onclick: function() {
downloadFile(this.getAttribute("itag"));
},
args: {
itag: YouTubeStreamSortedByFormat[key][i].itag
}
};
}
}
b[b.length] = {
text: "New update available, <a href=\"http://userscripts.org/scripts/show/" + updateId + "\">Install</a>.",
className: "",
style: "padding-top:7px;display:none;border-top:1px solid #666;text-align:center;",
register: function(elm){
YouTubeUpdateElement = elm;
}
};
var downloadMenu = createYouTubeMenu(b);
var downloadButton = downloadButton = createYouTubeButton("Download List", "<span class=\"yt-uix-button-content\">Download</span><img class=\"yt-uix-button-arrow\" src=\"//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif\" alt=\"\">", downloadMenu, null, null);
document.getElementById("watch7-sentiment-actions").appendChild(downloadButton);
}
function initDownloadFrame() {
DownloadIFrame = document.createElement("iframe");
DownloadIFrame.style.position = "absolute";
DownloadIFrame.style.top = "-100px";
DownloadIFrame.style.left = "-100px";
DownloadIFrame.style.width = "1px";
DownloadIFrame.style.height = "1px";
DownloadIFrame.style.border = "0";
DownloadIFrame.style.margin = "0";
DownloadIFrame.style.padding = "0";
document.body.appendChild(DownloadIFrame);
}
function init() {
YouTubeVideoTitle = getTitle();
YouTubePlayerConfigVariables = getYouTubePlayerConfigVariables();
YouTubeStreamInformation = parseYouTubeFormats(YouTubePlayerConfigVariables);
YouTubeStreamSortedByFormat = splitYouTubeInformationByFormat(YouTubeStreamInformation);
initUI();
checkForUpdates();
}
init();
})();