Greasy Fork

Greasy Fork is available in English.

Download MP3 & Video for YouTube

Add a download button for YouTube!

当前为 2017-10-29 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name Download MP3 & Video for YouTube
// @description Add a download button for YouTube!
// @homepageURL http://greasyfork.icu/scripts/34613
// @author Punisher
// @version 1.1
// @date 2017-10-28
// @namespace http://greasyfork.icu/
// @include http://www.youtube.com/*
// @include https://www.youtube.com/*
// @exclude http://www.youtube.com/embed/*
// @exclude https://www.youtube.com/embed/*
// @match http://www.youtube.com/*
// @match https://www.youtube.com/*
// @match http://s.ytimg.com/yts/jsbin/html5player*
// @match https://s.ytimg.com/yts/jsbin/html5player*
// @match http://manifest.googlevideo.com/*
// @match https://manifest.googlevideo.com/*
// @match http://*.googlevideo.com/videoplayback*
// @match https://*.googlevideo.com/videoplayback*
// @match http://*.youtube.com/videoplayback*
// @match https://*.youtube.com/videoplayback*
// ==/UserScript==


(function () {
    var FORMAT_TYPE={'mp3':'mp3'};
    var FORMAT_ORDER=[];
    var FORMAT_RULE=[];
    var BUTTON_TEXT={'cs':'Stáhnout','de':'Herunterladen','en':'Download','es':'Descargar','fr':'Télécharger','it':'Scarica','ja':'ダウンロード','pt':'Baixar','ru':'Загрузить'};
    var BUTTON_TOOLTIP={'cs':'Stáhnout','de':'Herunterladen','en':'Download','es':'Descargar','fr':'Télécharger','it':'Scarica','ja':'ダウンロード','pt':'Baixar','ru':'Загрузить'};
    var DECODE_RULE=[];
    var RANDOM=7489235179;
    var CONTAINER_ID='download-youtube-video'+RANDOM;
    var LISTITEM_ID='download-youtube-video-fmt'+RANDOM;
    var BUTTON_ID='download-youtube-video-button'+RANDOM;
    var DEBUG_ID='download-youtube-video-debug-info';
    var STORAGE_URL='download-youtube-script-url';
    var STORAGE_CODE='download-youtube-signature-code';
    var STORAGE_DASH='download-youtube-dash-enabled';
    var isDecodeRuleUpdated=false;

    start();

    function start() {
        var pagecontainer=document.getElementById('page-container');
        if (!pagecontainer) return;
        if (/^https?:\/\/www\.youtube.com\/watch\?/.test(window.location.href)) run();

        var isAjax=/class[\w\s"'-=]+spf\-link/.test(pagecontainer.innerHTML);
        var logocontainer=document.getElementById('logo-container');
        if (logocontainer && !isAjax) {
            isAjax=(' '+logocontainer.className+' ').indexOf(' spf-link ')>=0;
        }

        var content=document.getElementById('content');
        if (isAjax && content) {
            var mo=window.MutationObserver||window.WebKitMutationObserver;
            if(typeof mo!=='undefined') {
                var observer=new mo(function(mutations) {
                    mutations.forEach(function(mutation) {
                        if(mutation.addedNodes!==null) {
                            for (var i=0; i<mutation.addedNodes.length; i++) {
                                if (mutation.addedNodes[i].id=='watch7-container' ||
                                    mutation.addedNodes[i].id=='watch7-main-container') {
                                    run();
                                    break;
                                }
                            }
                        }
                    });
                });
                observer.observe(content, {childList: true, subtree: true});
            } else {
                pagecontainer.addEventListener('DOMNodeInserted', onNodeInserted, false);
            }
        }
    }

    function onNodeInserted(e) {
        if (e && e.target && (e.target.id=='watch7-container' ||
            e.target.id=='watch7-main-container')) {
            run();
        }
    }

    function run() {
        if (document.getElementById(CONTAINER_ID)) return;
        if (document.getElementById('p') && document.getElementById('vo')) return;

        var videoID, videoFormats, videoAdaptFormats, videoManifestURL, scriptURL=null;
        var isSignatureUpdatingStarted=false;
        var operaTable=new Array();
        var language=document.documentElement.getAttribute('lang');
        var textDirection='left';
        if (document.body.getAttribute('dir')=='rtl') {
            textDirection='right';
        }
        if (document.getElementById('watch7-action-buttons')) {
            fixTranslations(language, textDirection);
        }

        var args=null;
        var usw=(typeof this.unsafeWindow !== 'undefined')?this.unsafeWindow:window;
        if (usw.ytplayer && usw.ytplayer.config && usw.ytplayer.config.args) {
            args=usw.ytplayer.config.args;
        }
        if (usw.ytplayer && usw.ytplayer.config && usw.ytplayer.config.assets) {
            scriptURL=usw.ytplayer.config.assets.js;
        }

        if (videoID==null) {
            var buffer=document.getElementById(DEBUG_ID+'2');
            if (buffer) {
                while (buffer.firstChild) {
                    buffer.removeChild(buffer.firstChild);
                }
            } else {
                buffer=createHiddenElem('pre', DEBUG_ID+'2');
            }
            injectScript ('if(ytplayer&&ytplayer.config&&ytplayer.config.args){document.getElementById("'+DEBUG_ID+'2").appendChild(document.createTextNode(\'"video_id":"\'+ytplayer.config.args.video_id+\'", "js":"\'+ytplayer.config.assets.js+\'", "dashmpd":"\'+ytplayer.config.args.dashmpd+\'", "url_encoded_fmt_stream_map":"\'+ytplayer.config.args.url_encoded_fmt_stream_map+\'", "adaptive_fmts":"\'+ytplayer.config.args.adaptive_fmts+\'"\'));}');
            var code=buffer.innerHTML;
        }

        if (videoID==null) {
            var bodyContent=document.body.innerHTML;
            if (bodyContent!=null) {
                videoID=findMatch(bodyContent, /\"video_id\":\s*\"([^\"]+)\"/);
                videoFormats=findMatch(bodyContent, /\"url_encoded_fmt_stream_map\":\s*\"([^\"]+)\"/);
            }
        }

        if (typeof window.opera !== 'undefined' && window.opera && typeof opera.extension !== 'undefined') {
            opera.extension.onmessage = function(event) {
                var index=findMatch(event.data.action, /xhr\-([0-9]+)\-response/);
                if (index && operaTable[parseInt(index,10)]) {
                    index=parseInt(index,10);
                    var trigger=(operaTable[index])['onload'];
                    if (typeof trigger === 'function' && event.data.readyState == 4) {
                        if (trigger) {
                            trigger(event.data);
                        }
                    }
                }
            }
        }

        var videoTitle=document.title || 'video';
        videoTitle=videoTitle.replace(/\s*\-\s*YouTube$/i,'').replace(/[#"\?:\*]/g,'').replace(/[&\|\\\/]/g,'_').replace(/'/g,'\'').replace(/^\s+|\s+$/g,'').replace(/\.+$/g,'');

        var sep1='%2C', sep2='%26', sep3='%3D';
        if (videoFormats.indexOf(',')>-1) {
            sep1=',';
            sep2=(videoFormats.indexOf('&')>-1)?'&':'\\u0026';
            sep3='=';
        }

        var videoURL=new Array();
        var videoSignature=new Array();
        if (videoAdaptFormats) {
            videoFormats=videoFormats+sep1+videoAdaptFormats;
        }

        var downloadCodeList=[];
        for (var i=0;i<FORMAT_ORDER.length;i++) {
            var format=FORMAT_ORDER[i];
            if (!SHOW_DASH_FORMATS && format.length>2) continue;
            if (videoURL[format]!=undefined && FORMAT_LABEL[format]!=undefined && showFormat[format]) {
                downloadCodeList.push({url:videoURL[format],sig:videoSignature[format],format:format,label:FORMAT_LABEL[format]});
            }
        }


        downloadCodeList.push({
            'url': 'http://api.yt-mp3.com/watch?v=' + encodeURIComponent(location.href),
            'format': 'mp3',
            'label': 'MP3 or VIDEO',
            'popup': true
        });


        var newWatchPage=false;
        var parentElement=document.getElementById('watch7-action-buttons');
        if (parentElement==null) {
            parentElement=document.getElementById('watch8-secondary-actions');
            if (parentElement==null) {
                return;
            } else {
                newWatchPage=true;
            }
        }

        var buttonText=(BUTTON_TEXT[language])?BUTTON_TEXT[language]:BUTTON_TEXT['en'];
        var buttonLabel=(BUTTON_TOOLTIP[language])?BUTTON_TOOLTIP[language]:BUTTON_TOOLTIP['en'];
        var mainSpan=document.createElement('span');

        if (newWatchPage) {
            var spanIcon=document.createElement('span');
            spanIcon.setAttribute('class', 'yt-uix-button-icon-wrapper');
            var imageIcon=document.createElement('img');
            imageIcon.setAttribute('src', '//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif');
            imageIcon.setAttribute('class', 'yt-uix-button-icon');
            imageIcon.setAttribute('style', 'width:20px;height:20px;background-size:20px 20px;background-repeat:no-repeat;background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABG0lEQVRYR+2W0Q3CMAxE2wkYAdiEEWADmIxuACMwCmzABpCTEmRSO7YTQX+ChECV43t2nF7GYeHPuLD+0AKwC/DnWMAp/N5qimkBuAfBdRTF/+2/AV6ZYFUxVYuicAfoHegd6B3oHfhZB+ByF+JyV8FkrAB74pqH3DU5L3iGoBURhdVODIQF4EjEkWLmmhYALOQgNIBcHHke4buhxXAAaFnaAhqbQ5QAOHHkwhZ8balkx1ICCiEBWNZ+CivdB7REHIC2ZjZK2oWklDDdB1NSdCd/Js2PqQMpSIKYVcM8kE6QCwDBNRCqOBJrW0CL8kCYxL0A1k6YxWsANAiXeC2ABOEWbwHAWrwxpzgkmA/JtIqnxTOElmPnjlkc4A3FykAhA42AxwAAAABJRU5ErkJggg==);');
            spanIcon.appendChild(imageIcon);
            mainSpan.appendChild(spanIcon);
        }

        var spanButton=document.createElement('span');
        spanButton.setAttribute('class', 'yt-uix-button-content');
        spanButton.appendChild(document.createTextNode(buttonText+' '));
        mainSpan.appendChild(spanButton);

        if (!newWatchPage) {
            var imgButton=document.createElement('img');
            imgButton.setAttribute('class', 'yt-uix-button-arrow');
            imgButton.setAttribute('src', '//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif');
            mainSpan.appendChild(imgButton);
        }

        var listItems=document.createElement('ol');
        listItems.setAttribute('style', 'display:none;');
        listItems.setAttribute('class', 'yt-uix-button-menu');
        for (var i=0;i<downloadCodeList.length;i++) {
            var listItem=document.createElement('li');
            var listLink=document.createElement('a');
            listLink.setAttribute('style', 'text-decoration:none;');
            listLink.setAttribute('href', downloadCodeList[i].url);

            if (downloadCodeList[i].popup) {
                listLink.onclick = (function (href, e) {
                    e.preventDefault();
                    window.open(href,href,'height=327,width=954,location=yes,menubar=no,resizable=no,scrollbars=no,status=no,toolbar=no');
                }).bind(null, downloadCodeList[i].url);
            } else {
                listLink.setAttribute('download', videoTitle+'.'+FORMAT_TYPE[downloadCodeList[i].format]);
            }

            var listButton=document.createElement('span');
            listButton.setAttribute('class', 'yt-uix-button-menu-item');
            listButton.setAttribute('loop', i+'');
            listButton.setAttribute('id', LISTITEM_ID+downloadCodeList[i].format);
            listButton.appendChild(document.createTextNode(downloadCodeList[i].label));
            listLink.appendChild(listButton);
            listItem.appendChild(listLink);
            listItems.appendChild(listItem);
        }
        mainSpan.appendChild(listItems);

        var buttonElement=document.createElement('button');
        buttonElement.setAttribute('id', BUTTON_ID);
        if (newWatchPage) {
            buttonElement.setAttribute('class', 'yt-uix-button  yt-uix-button-size-default yt-uix-button-opacity yt-uix-tooltip');
        } else {
            buttonElement.setAttribute('class', 'yt-uix-button yt-uix-tooltip yt-uix-button-empty yt-uix-button-text');
            buttonElement.setAttribute('style', 'margin-top:4px; margin-left:'+((textDirection=='left')?5:10)+'px;');
        }

        buttonElement.setAttribute('data-tooltip-text', buttonLabel);
        buttonElement.setAttribute('type', 'button');
        buttonElement.setAttribute('role', 'button');
        buttonElement.addEventListener('click', function(){return false;}, false);
        buttonElement.appendChild(mainSpan);

        var containerSpan=document.createElement('span');
        containerSpan.setAttribute('id', CONTAINER_ID);
        containerSpan.appendChild(document.createTextNode(' '));
        containerSpan.appendChild(buttonElement);

        if (!newWatchPage) {
            parentElement.appendChild(containerSpan);
        } else {
            parentElement.insertBefore(containerSpan, parentElement.firstChild);
        }

        function injectScript(code) {
            var script=document.createElement('script');
            script.type='application/javascript';
            script.textContent=code;
            document.body.appendChild(script);
            document.body.removeChild(script);
        }

        function createHiddenElem(tag, id) {
            var elem=document.createElement(tag);
            elem.setAttribute('id', id);
            elem.setAttribute('style', 'display:none;');
            document.body.appendChild(elem);
            return elem;
        }

        function fixTranslations(language, textDirection) {
            if (/^af|bg|bn|ca|cs|de|el|es|et|eu|fa|fi|fil|fr|gl|hi|hr|hu|id|it|iw|kn|lv|lt|ml|mr|ms|nl|pl|ro|ru|sl|sk|sr|sw|ta|te|th|uk|ur|vi|zu$/.test(language)) {
                var likeButton=document.getElementById('watch-like');
                if (likeButton) {
                    var spanElements=likeButton.getElementsByClassName('yt-uix-button-content');
                    if (spanElements) {
                        spanElements[0].style.display='none';
                    }
                }

                var marginPixels=10;
                if (/^bg|ca|cs|el|eu|hr|it|ml|ms|pl|sl|sw|te$/.test(language)) {
                    marginPixels=1;
                }
                injectStyle('#watch7-secondary-actions .yt-uix-button{margin-'+textDirection+':'+marginPixels+'px!important}');
            }
        }

        function findMatch(text, regexp) {
            var matches=text.match(regexp);
            return (matches)?matches[1]:null;
        }

        function crossXmlHttpRequest(details) {
            if (typeof GM_xmlhttpRequest === 'function') {
                GM_xmlhttpRequest(details);
            } else if (typeof window.opera !== 'undefined' && window.opera && typeof opera.extension !== 'undefined' &&
                typeof opera.extension.postMessage !== 'undefined') {
                var index=operaTable.length;
                opera.extension.postMessage({'action':'xhr-'+index, 'url':details.url, 'method':details.method});
                operaTable[index]=details;
            } else if (typeof window.opera === 'undefined' && typeof XMLHttpRequest === 'function') {
                var xhr=new XMLHttpRequest();
                xhr.onreadystatechange = function() {
                    if (xhr.readyState == 4) {
                        if (details['onload']) {
                            details['onload'](xhr);
                        }
                    }
                }
                xhr.open(details.method, details.url, true);
                xhr.send();
            }
        }
    }
})();