levelKro / YouTube Direct Downloader

// ==UserScript==
// @name         YouTube Direct Downloader
// @namespace    https://levelkro.com
// @version      0.6.0
// @description  Download direct files of Youtube video (no recode).
// @author       levelKro (https://levelkro.com)
// @match        https://*.youtube.com/*
// @match        https://*.youtube.fr/*
// @match        https://*.youtube.ca/*
// @match        https://*.youtube.us/*
// @match        https://*.youtube.it/*
// @match        https://*.youtu.be/*
// @match        https://youtube.com/*
// @match        https://youtube.fr/*
// @match        https://youtube.ca/*
// @match        https://youtube.us/*
// @match        https://youtube.it/*
// @match        https://youtu.be/*
// @grant        none
// @noframes
// @run-at document-idle
// @license     MIT
// @copyright 2019, levelKro (https://levelkro.com) (https://openuserjs.org/users/levelKro)
// ==/UserScript==

// FileSaver Script (MIN) from https://github.com/eligrey/FileSaver.js
(function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(b,c,d){var e=new XMLHttpRequest;e.open("GET",b),e.responseType="blob",e.onload=function(){a(e.response,c,d)},e.onerror=function(){console.error("could not download file")},e.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(a,b,d,e){if(e=e||open("","_blank"),e&&(e.document.title=e.document.body.innerText="downloading..."),"string"==typeof a)return c(a,b,d);var g="application/octet-stream"===a.type,h=/constructor/i.test(f.HTMLElement)||f.safari,i=/CriOS\/[\d]+/.test(navigator.userAgent);if((i||g&&h)&&"object"==typeof FileReader){var j=new FileReader;j.onloadend=function(){var a=j.result;a=i?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),e?e.location.href=a:location=a,e=null},j.readAsDataURL(a)}else{var k=f.URL||f.webkitURL,l=k.createObjectURL(a);e?e.location=l:location.href=l,e=null,setTimeout(function(){k.revokeObjectURL(l)},4E4)}});f.saveAs=a.saveAs=a,"undefined"!=typeof module&&(module.exports=a)});
//# sourceMappingURL=FileSaver.min.js.map

var cUrl="";
var cVideo="";
var cOldVideo="";
var cOldUrl="";
var i=0;
var put=!1;
var output="";
var v_url="";
var v_type=""
var url="";
var out=!1

setInterval(function () {
        cVideo=document.getElementsByTagName("title")[0].innerText;
        cVideo=cVideo.replace(" - YouTube","").replace(":","-");
        //console.log("Debug video name: "+cVideo);
    var goout=false;
    var inew=false;
    console.log("YTD Ping! Pong?");
    if ("-1" != window.location.href.indexOf("/watch")) {
        var chk = document.getElementById("messages");
        if (chk) {
            put = document.getElementById("ytdownload");
            if (!put) {
                inew=true;
                //console.log("YTDD : Installing code for rendering");
                chk.innerHTML = '<div id="ytdownload" style="background:#040404; color:#fefefe; padding:10px; border-radius:5px; border:1px solid #242424; font-size:1.2em;">Please wait, loading the meta data of the video...</div>';
                output="<h2>Downloads for <b>"+cVideo+"</b></h2>Click on a link in the list above for downloading this Youtube video.<br/><small><b>Please note</b><br/>Some videos have link but can't download, this is a protection from YouTube. Sorry!</small><hr/><ul style=\"margin-left:30px;\">";
            }
            else if(cVideo!=cOldVideo) {
                inew=true;
                //console.log("YTDD : New video detected");
                output="<h2>Downloads for <b>"+cVideo+"</b></h2>Click on a link in the list above for downloading this Youtube video.<br/><small><b>Please note</b><br/>Some videos have link but can't download, this is a protection from YouTube. Sorry!</small><hr/><ul style=\"margin-left:30px;\">";
            }

            if (inew) {
                url = window.location.href;
                //console.log("YTDD : New page detected, Watching now; " + url);
                var v="";
                ("YTDD&" + url.split("?")[1]).split("&").forEach(function (entry) {
                    if ("-1" != entry.indexOf("v=")) {
                        v = entry.split("=")[1];
                        if (v) {
                            console.log("YTDD : Found video ID; " + v);
                        }
                    }
                });
                var url_api = "//www.youtube.com/get_video_info?video_id=" + v + "&el=embedded&ps=default&eurl=&gl=US&hl=en";
                if (window.XMLHttpRequest) {
                    // code for IE7+, Firefox, Chrome, Opera, Safari
                    var xmlhttp = new XMLHttpRequest;
                }
                else {
                    // code for IE6, IE5
                    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
                }
                xmlhttp.onreadystatechange = function () {
                    if (4 == xmlhttp.readyState && 200 == xmlhttp.status) {
                        //console.log("YTDD : Youtube API answer");
                        var response = xmlhttp.responseText;
                        var request = [];
                        out = !1;
                        var pairs = response.split('&');
                        for (var x = 0; x < pairs.length; x++) {
                            if (pairs[x]) {
                                var pair = pairs[x].split('=');
                                request[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1])
                                if(decodeURIComponent(pair[0]) == "url_encoded_fmt_stream_map") {
                                    var streams_array = decodeURIComponent(pair[1]).split(",");
                                    for (var z = 0; z < streams_array.length; z++) {
                                        v_url = "";
                                        v_type = "";
                                        var entry=streams_array[z].split("&");
                                        for (var y = 0; y < entry.length; y++) {
                                            var s = entry[y].split("=");
                                            if ("type" == s[0]) {
                                                v_type = decodeURIComponent(s[1]);
                                            }
                                            else if("url" == s[0]){
                                                (v_url = decodeURIComponent(s[1]));
                                            }
                                        }
                                        if("" != v_type) {
                                            v_type = v_type.replace(';+codecs="', " (").replace('")', ")").replace(',+', " / ").replace('"', ")");
                                            var v_format="unknown";
                                            if(v_type.search("%2Fwebm")!="-1") v_format="webm";
                                            if(v_type.search("%2Fmp4")!="-1") v_format="mp4";
                                            if(v_type.search("%2Fm4a")!="-1") v_format="m4a";
                                            if(v_type.search("%2Fm4v")!="-1") v_format="m4v";
                                            output+="<li><a style=\"cursor:pointer;color:lightblue;\" onclick=\"console.log('Downloading "+type+" file, please wait'); alert('Downloading file in background, you see the video file ("+v_type+") in the download folder when is done.'); saveAs('"+v_url+"','"+cVideo.replace("'", "\\'")+"."+v_format+"');\">Download</a> from API in "+v_type+"</li>";
                                            out = !0;
                                        }
                                    }
                                }
                            }
                            inew=false;
                        }
                        if(0 == out) {
                            output += '<li>Sorry, no link found with YouTube API.</li>';
                            //console.log("YTDD : Sorry, no video link found. This video is private, region locked or protected.");
                        }
                        else {
                            //console.log("YTDD : Display links for the video.");
                        }
                        goout=true;
                    }
                }
                //console.log("YTDD : Call Youtube API for video details"+url_api);
                xmlhttp.open("GET", url_api, !1);
                xmlhttp.send();
            }
        }
    }
    if(put){
        var res = performance.getEntriesByType("resource");
        for (i=i;i < res.length; i++) {
            var isMe=res[i];
            if (isMe.initiatorType == "audio" || isMe.initiatorType == "video" || isMe.name.search(".mp4")!="-1" || isMe.name.search(".m4a")!="-1" ) {
                cUrl=isMe.name;
                //console.log("Debug video file: "+cUrl);
            }
            else if (isMe.initiatorType == "xmlhttprequest" && isMe.name.search("videoplayback")!="-1") {
                cUrl=isMe.name;
                cUrl=cUrl.split("&range");
                //console.log("Debug video file CDN: "+cUrl[0]);
            }
            var type="unknown";
            var format="unknown";
            if(isMe.name.search("audio%2F")!="-1") type="audio";
            if(isMe.name.search("video%2F")!="-1") type="video";
            if(isMe.name.search("%2Fwebm")!="-1") format="webm";
            if(isMe.name.search("%2Fmp4")!="-1") format="mp4";
            if(isMe.name.search("%2Fm4a")!="-1") format="m4a";
            if(isMe.name.search("%2Fm4v")!="-1") format="m4v";
            if(cVideo!=cOldVideo && cUrl!=cOldUrl && cUrl!="") {
                cOldVideo=cVideo;
                cOldUrl=cUrl;
                output+="<li><a style=\"cursor:pointer;color:lightblue;\" onclick=\"console.log('Downloading "+type+" file, please wait'); alert('Downloading file in background, you see the "+type+" file ("+format+") in the download folder when is done.'); saveAs('"+cUrl+"','"+cVideo.replace("'", "\\'")+"."+format+"');\">Download</a> "+type+" in "+format+"</li>";
                //console.log("YTD New video link; "+type+"/"+format+" : "+cUrl);
            }

            goout=true;
            //console.log("YTD Debug for "+i+" - '"+isMe.name+"' is a type; "+isMe.initiatorType);
        }
        if(goout) document.getElementById("ytdownload").innerHTML = output;
    }
}, 2000);
console.log("YouTube Direct Downloader is ready!");