[29984] | 1 | // ==UserScript==
|
---|
| 2 | // @name Download YouTube Videos as MP4
|
---|
| 3 | // @description Adds a button that lets you download YouTube videos.
|
---|
| 4 | // @homepageURL https://github.com/gantt/downloadyoutube
|
---|
| 5 | // @author Gantt
|
---|
| 6 | // @version 1.8.3
|
---|
| 7 | // @date 2015-05-17
|
---|
| 8 | // @namespace http://googlesystem.blogspot.com
|
---|
| 9 | // @include http://www.youtube.com/*
|
---|
| 10 | // @include https://www.youtube.com/*
|
---|
| 11 | // @exclude http://www.youtube.com/embed/*
|
---|
| 12 | // @exclude https://www.youtube.com/embed/*
|
---|
| 13 | // @match http://www.youtube.com/*
|
---|
| 14 | // @match https://www.youtube.com/*
|
---|
| 15 | // @match http://s.ytimg.com/yts/jsbin/html5player*
|
---|
| 16 | // @match https://s.ytimg.com/yts/jsbin/html5player*
|
---|
| 17 | // @match http://manifest.googlevideo.com/*
|
---|
| 18 | // @match https://manifest.googlevideo.com/*
|
---|
| 19 | // @match http://*.googlevideo.com/videoplayback*
|
---|
| 20 | // @match https://*.googlevideo.com/videoplayback*
|
---|
| 21 | // @match http://*.youtube.com/videoplayback*
|
---|
| 22 | // @match https://*.youtube.com/videoplayback*
|
---|
| 23 | // @grant GM_xmlhttpRequest
|
---|
| 24 | // @grant GM_getValue
|
---|
| 25 | // @grant GM_setValue
|
---|
| 26 | // @run-at document-end
|
---|
| 27 | // @license MIT License
|
---|
| 28 | // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAB3RJTUUH2wMOCgIoGUYEAQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAAGSUExURfi/JO/v797e3sbGxq2traWlpZSUlJycnNbW1oyEhIRaWow5OZQhIZwYGKUQEKUICK0ICJQxMYxKSoxzc4x7e4RCQpQYGKUAAK0AALUAAL0AAK0QEIxra5QpKa0YGIxSUsYAAKUhIZR7e87Ozr0ICJRSUr29vYxjY6U5OaUpKa0hIb21tZwAALUICO/Ozu/GxqUxMZSEhLUYGO/W1r0YGKVCQpQQEL0pKffe3vfW1pxra5Q5OcZCQvfn585CQr2trZx7e8ZSUs5SUq05Oc5jY9ZjY84AAKWMjM5zc957e60pKdaMjOelpbWcnLWUlLVCQsYYGMYICNbOzpQICMYhIbV7e5xaWt6cnPfv79bGxt6lpe+9vc5KSs6lpb0xMc6EhM69vbUxMbUhIb1aWs61tcZaWuecnMYxMb1KSsZjY96UlNa1td7W1r17e9a9vZwQEN6trb1jY8YQENZra+fOzr1zc85aWufe3t6MjMY5OdZaWt61tdZ7e+/n5+e9vc6MjMZra+/e3ue1tdalpd7GxrUpKalL4aAAAAABdFJOUwBA5thmAAACxklEQVR42uXX/1/SQBgH8NuAoEQ2ijgbBivJLznBAiUUKiyJSgOVAk0tKZKw75mRRt/7v4MBY8ezjW39Vs8v8rqHz/u1jbvbidC/XL8KmcpOqVT6nSjXjooGw8WfFd+QWGfE4oLbtbr++PdMOy0BDYLjEj/0xevfWIyVAI7b/aIj/9WHsRrA8Yf9bqSexVgD4Lic9kWE/LgPwPGfNfJHDO4P8Iuq+S2M9QD8oUp+nxEAcFCtfgIA/14x/9ElAKDQbNQAwN9VAiYEABy0OgsAWAnB/AcBAtVWawkAfJ4CD0BQADZavYcQgI9h3CCQjpD5PcEgwG+SwLRhIL0vz78SjAPEU3hrHODfyX4I6rUJIP0G3oExoNwFXpoB+HwXmDEFpF9IwKA5YK+Tp9fMAdUOsC6YA553gKcmgdTfAhV1oMQqADndQDmJ0AZLAsFnCIV3VYDHJLAjDkZKciAaFz/lCeBJB1glgXBrNLndBWLJ9uZGAI+keTBLANL8SnWAzWRniAC2pG+6lQF0hfjTqCIBrEvjDwiggFSLuIUoLY0vEwAbUcsnc/LlnO02HGvEz+hXEeJ5Yj+4L2vNkxOJDSnlQzliIq2synr3embiUBjmw0FyU83KX04Ob+9aAK/Ppd5deZloz4HFlCHzt3sX0x2a6LcvQb4ab8r7i+DVdqvnCq/D5ZzqdhfAcr5B9wD0PNwPEu0ZnLwK9oPgNfCQJ2fhhhITJ3E8BjeUOXA+QNQlBh5xLjemVCgKjzgzNIJFjWF4yJoKhafgIWt6VHGmjgR0HvMuTipPdWQJ6AImbBRSE8aY/sC4er5xFx5vHyB4YRRpFWUf0AL4c+dHkHZRFo9TDeB9Aa3Llwjr8FlFwB+wO/rHm0VbPae9mPini/O5h/XGxatw2I6fGHAOuhiGZVxO98lTdgutP94yaIvVdqxZdpvFYTT9X9UfqQQlTXlm8wkAAAAASUVORK5CYII=
|
---|
| 29 | // ==/UserScript==
|
---|
| 30 |
|
---|
| 31 |
|
---|
| 32 | (function () {
|
---|
| 33 | var FORMAT_LABEL={'5':'FLV 240p','18':'MP4 360p','22':'MP4 720p','34':'FLV 360p','35':'FLV 480p','37':'MP4 1080p','38':'MP4 2160p','43':'WebM 360p','44':'WebM 480p','45':'WebM 720p','46':'WebM 1080p','135':'MP4 480p - no audio','137':'MP4 1080p - no audio','138':'MP4 2160p - no audio','139':'M4A 48kbps - audio','140':'M4A 128kbps - audio','141':'M4A 256kbps - audio','264':'MP4 1440p - no audio','266':'MP4 2160p - no audio','298':'MP4 720p60 - no audio','299':'MP4 1080p60 - no audio'};
|
---|
| 34 | var FORMAT_TYPE={'5':'flv','18':'mp4','22':'mp4','34':'flv','35':'flv','37':'mp4','38':'mp4','43':'webm','44':'webm','45':'webm','46':'webm','135':'mp4','137':'mp4','138':'mp4','139':'m4a','140':'m4a','141':'m4a','264':'mp4','266':'mp4','298':'mp4','299':'mp4'};
|
---|
| 35 | var FORMAT_ORDER=['5','18','34','43','35','135','44','22','298','45','37','299','46','264','38','266','139','140','141'];
|
---|
| 36 | var FORMAT_RULE={'flv':'max','mp4':'all','webm':'none','m4a':'max'};
|
---|
| 37 | // all=display all versions, max=only highest quality version, none=no version
|
---|
| 38 | // the default settings show all MP4 videos, the highest quality FLV and no WebM
|
---|
| 39 | var SHOW_DASH_FORMATS=false;
|
---|
| 40 | var BUTTON_TEXT={'ar':'تنزيل','cs':'Stáhnout','de':'Herunterladen','en':'Download','es':'Descargar','fr':'Télécharger','hi':'डाउनलोड','hu':'Letöltés','id':'Unduh','it':'Scarica','ja':'ダウンロード','ko':'내려받기','pl':'Pobierz','pt':'Baixar','ro':'Descărcați','ru':'Скачать','tr':'İndir','zh':'下载','zh-TW':'下載'};
|
---|
| 41 | var BUTTON_TOOLTIP={'ar':'تنزيل هذا الفيديو','cs':'Stáhnout toto video','de':'Dieses Video herunterladen','en':'Download this video','es':'Descargar este vídeo','fr':'Télécharger cette vidéo','hi':'वीडियो डाउनलोड करें','hu':'Videó letöltése','id':'Unduh video ini','it':'Scarica questo video','ja':'このビデオをダウンロードする','ko':'이 비디오를 내려받기','pl':'Pobierz plik wideo','pt':'Baixar este vídeo','ro':'Descărcați acest videoclip','ru':'Скачать это видео','tr': 'Bu videoyu indir','zh':'下载此视频','zh-TW':'下載此影片'};
|
---|
| 42 | var DECODE_RULE=[];
|
---|
| 43 | var RANDOM=7489235179; // Math.floor(Math.random()*1234567890);
|
---|
| 44 | var CONTAINER_ID='download-youtube-video'+RANDOM;
|
---|
| 45 | var LISTITEM_ID='download-youtube-video-fmt'+RANDOM;
|
---|
| 46 | var BUTTON_ID='download-youtube-video-button'+RANDOM;
|
---|
| 47 | var DEBUG_ID='download-youtube-video-debug-info';
|
---|
| 48 | var STORAGE_URL='download-youtube-script-url';
|
---|
| 49 | var STORAGE_CODE='download-youtube-signature-code';
|
---|
| 50 | var STORAGE_DASH='download-youtube-dash-enabled';
|
---|
| 51 | var isDecodeRuleUpdated=false;
|
---|
| 52 |
|
---|
| 53 | start();
|
---|
| 54 |
|
---|
| 55 | function start() {
|
---|
| 56 | var pagecontainer=document.getElementById('page-container');
|
---|
| 57 | if (!pagecontainer) return;
|
---|
| 58 | if (/^https?:\/\/www\.youtube.com\/watch\?/.test(window.location.href)) run();
|
---|
| 59 | var isAjax=/class[\w\s"'-=]+spf\-link/.test(pagecontainer.innerHTML);
|
---|
| 60 | var logocontainer=document.getElementById('logo-container');
|
---|
| 61 | if (logocontainer && !isAjax) { // fix for blocked videos
|
---|
| 62 | isAjax=(' '+logocontainer.className+' ').indexOf(' spf-link ')>=0;
|
---|
| 63 | }
|
---|
| 64 | var content=document.getElementById('content');
|
---|
| 65 | if (isAjax && content) { // Ajax UI
|
---|
| 66 | var mo=window.MutationObserver||window.WebKitMutationObserver;
|
---|
| 67 | if(typeof mo!=='undefined') {
|
---|
| 68 | var observer=new mo(function(mutations) {
|
---|
| 69 | mutations.forEach(function(mutation) {
|
---|
| 70 | if(mutation.addedNodes!==null) {
|
---|
| 71 | for (var i=0; i<mutation.addedNodes.length; i++) {
|
---|
| 72 | if (mutation.addedNodes[i].id=='watch7-container' ||
|
---|
| 73 | mutation.addedNodes[i].id=='watch7-main-container') { // old value: movie_player
|
---|
| 74 | run();
|
---|
| 75 | break;
|
---|
| 76 | }
|
---|
| 77 | }
|
---|
| 78 | }
|
---|
| 79 | });
|
---|
| 80 | });
|
---|
| 81 | observer.observe(content, {childList: true, subtree: true}); // old value: pagecontainer
|
---|
| 82 | } else { // MutationObserver fallback for old browsers
|
---|
| 83 | pagecontainer.addEventListener('DOMNodeInserted', onNodeInserted, false);
|
---|
| 84 | }
|
---|
| 85 | }
|
---|
| 86 | }
|
---|
| 87 |
|
---|
| 88 | function onNodeInserted(e) {
|
---|
| 89 | if (e && e.target && (e.target.id=='watch7-container' ||
|
---|
| 90 | e.target.id=='watch7-main-container')) { // old value: movie_player
|
---|
| 91 | run();
|
---|
| 92 | }
|
---|
| 93 | }
|
---|
| 94 |
|
---|
| 95 | function run() {
|
---|
| 96 | if (document.getElementById(CONTAINER_ID)) return; // check download container
|
---|
| 97 | if (document.getElementById('p') && document.getElementById('vo')) return; // Feather not supported
|
---|
| 98 |
|
---|
| 99 | var videoID, videoFormats, videoAdaptFormats, videoManifestURL, scriptURL=null;
|
---|
| 100 | var isSignatureUpdatingStarted=false;
|
---|
| 101 | var operaTable=new Array();
|
---|
| 102 | var language=document.documentElement.getAttribute('lang');
|
---|
| 103 | var textDirection='left';
|
---|
| 104 | if (document.body.getAttribute('dir')=='rtl') {
|
---|
| 105 | textDirection='right';
|
---|
| 106 | }
|
---|
| 107 | if (document.getElementById('watch7-action-buttons')) { // old UI
|
---|
| 108 | fixTranslations(language, textDirection);
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 | // obtain video ID, formats map
|
---|
| 112 |
|
---|
| 113 | var args=null;
|
---|
| 114 | var usw=(typeof this.unsafeWindow !== 'undefined')?this.unsafeWindow:window; // Firefox, Opera<15
|
---|
| 115 | if (usw.ytplayer && usw.ytplayer.config && usw.ytplayer.config.args) {
|
---|
| 116 | args=usw.ytplayer.config.args;
|
---|
| 117 | }
|
---|
| 118 | if (args) {
|
---|
| 119 | videoID=args['video_id'];
|
---|
| 120 | videoFormats=args['url_encoded_fmt_stream_map'];
|
---|
| 121 | videoAdaptFormats=args['adaptive_fmts'];
|
---|
| 122 | videoManifestURL=args['dashmpd'];
|
---|
| 123 | debug('DYVAM - Info: Standard mode. videoID '+(videoID?videoID:'none')+'; ');
|
---|
| 124 | }
|
---|
| 125 | if (usw.ytplayer && usw.ytplayer.config && usw.ytplayer.config.assets) {
|
---|
| 126 | scriptURL=usw.ytplayer.config.assets.js;
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | if (videoID==null) { // unsafeWindow workaround (Chrome, Opera 15+)
|
---|
| 130 | var buffer=document.getElementById(DEBUG_ID+'2');
|
---|
| 131 | if (buffer) {
|
---|
| 132 | while (buffer.firstChild) {
|
---|
| 133 | buffer.removeChild(buffer.firstChild);
|
---|
| 134 | }
|
---|
| 135 | } else {
|
---|
| 136 | buffer=createHiddenElem('pre', DEBUG_ID+'2');
|
---|
| 137 | }
|
---|
| 138 | 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+\'"\'));}');
|
---|
| 139 | var code=buffer.innerHTML;
|
---|
| 140 | if (code) {
|
---|
| 141 | videoID=findMatch(code, /\"video_id\":\s*\"([^\"]+)\"/);
|
---|
| 142 | videoFormats=findMatch(code, /\"url_encoded_fmt_stream_map\":\s*\"([^\"]+)\"/);
|
---|
| 143 | videoFormats=videoFormats.replace(/&/g,'\\u0026');
|
---|
| 144 | videoAdaptFormats=findMatch(code, /\"adaptive_fmts\":\s*\"([^\"]+)\"/);
|
---|
| 145 | videoAdaptFormats=videoAdaptFormats.replace(/&/g,'\\u0026');
|
---|
| 146 | videoManifestURL=findMatch(code, /\"dashmpd\":\s*\"([^\"]+)\"/);
|
---|
| 147 | scriptURL=findMatch(code, /\"js\":\s*\"([^\"]+)\"/);
|
---|
| 148 | }
|
---|
| 149 | debug('DYVAM - Info: Injection mode. videoID '+(videoID?videoID:'none')+'; ');
|
---|
| 150 | }
|
---|
| 151 |
|
---|
| 152 | if (videoID==null) { // if all else fails
|
---|
| 153 | var bodyContent=document.body.innerHTML;
|
---|
| 154 | if (bodyContent!=null) {
|
---|
| 155 | videoID=findMatch(bodyContent, /\"video_id\":\s*\"([^\"]+)\"/);
|
---|
| 156 | videoFormats=findMatch(bodyContent, /\"url_encoded_fmt_stream_map\":\s*\"([^\"]+)\"/);
|
---|
| 157 | videoAdaptFormats=findMatch(bodyContent, /\"adaptive_fmts\":\s*\"([^\"]+)\"/);
|
---|
| 158 | videoManifestURL=findMatch(bodyContent, /\"dashmpd\":\s*\"([^\"]+)\"/);
|
---|
| 159 | if (scriptURL==null) {
|
---|
| 160 | scriptURL=findMatch(bodyContent, /\"js\":\s*\"([^\"]+)\"/);
|
---|
| 161 | if (scriptURL) {
|
---|
| 162 | scriptURL=scriptURL.replace(/\\/g,'');
|
---|
| 163 | }
|
---|
| 164 | }
|
---|
| 165 | }
|
---|
| 166 | debug('DYVAM - Info: Brute mode. videoID '+(videoID?videoID:'none')+'; ');
|
---|
| 167 | }
|
---|
| 168 |
|
---|
| 169 | debug('DYVAM - Info: url '+window.location.href+'; useragent '+window.navigator.userAgent);
|
---|
| 170 |
|
---|
| 171 | if (videoID==null || videoFormats==null || videoID.length==0 || videoFormats.length==0) {
|
---|
| 172 | debug('DYVAM - Error: No config information found. YouTube must have changed the code.');
|
---|
| 173 | return;
|
---|
| 174 | }
|
---|
| 175 |
|
---|
| 176 | // Opera 12 extension message handler
|
---|
| 177 | if (typeof window.opera !== 'undefined' && window.opera && typeof opera.extension !== 'undefined') {
|
---|
| 178 | opera.extension.onmessage = function(event) {
|
---|
| 179 | var index=findMatch(event.data.action, /xhr\-([0-9]+)\-response/);
|
---|
| 180 | if (index && operaTable[parseInt(index,10)]) {
|
---|
| 181 | index=parseInt(index,10);
|
---|
| 182 | var trigger=(operaTable[index])['onload'];
|
---|
| 183 | if (typeof trigger === 'function' && event.data.readyState == 4) {
|
---|
| 184 | if (trigger) {
|
---|
| 185 | trigger(event.data);
|
---|
| 186 | }
|
---|
| 187 | }
|
---|
| 188 | }
|
---|
| 189 | }
|
---|
| 190 | }
|
---|
| 191 |
|
---|
| 192 | if (!isDecodeRuleUpdated) {
|
---|
| 193 | DECODE_RULE=getDecodeRules(DECODE_RULE);
|
---|
| 194 | isDecodeRuleUpdated=true;
|
---|
| 195 | }
|
---|
| 196 | if (scriptURL) {
|
---|
| 197 | if (scriptURL.indexOf('//')==0) {
|
---|
| 198 | var protocol=(document.location.protocol=='http:')?'http:':'https:';
|
---|
| 199 | scriptURL=protocol+scriptURL;
|
---|
| 200 | }
|
---|
| 201 | fetchSignatureScript(scriptURL);
|
---|
| 202 | }
|
---|
| 203 |
|
---|
| 204 | // video title
|
---|
| 205 | var videoTitle=document.title || 'video';
|
---|
| 206 | videoTitle=videoTitle.replace(/\s*\-\s*YouTube$/i,'').replace(/[#"\?:\*]/g,'').replace(/[&\|\\\/]/g,'_').replace(/'/g,'\'').replace(/^\s+|\s+$/g,'').replace(/\.+$/g,'');
|
---|
| 207 |
|
---|
| 208 | // parse the formats map
|
---|
| 209 | var sep1='%2C', sep2='%26', sep3='%3D';
|
---|
| 210 | if (videoFormats.indexOf(',')>-1) {
|
---|
| 211 | sep1=',';
|
---|
| 212 | sep2=(videoFormats.indexOf('&')>-1)?'&':'\\u0026';
|
---|
| 213 | sep3='=';
|
---|
| 214 | }
|
---|
| 215 | var videoURL=new Array();
|
---|
| 216 | var videoSignature=new Array();
|
---|
| 217 | if (videoAdaptFormats) {
|
---|
| 218 | videoFormats=videoFormats+sep1+videoAdaptFormats;
|
---|
| 219 | }
|
---|
| 220 | var videoFormatsGroup=videoFormats.split(sep1);
|
---|
| 221 | for (var i=0;i<videoFormatsGroup.length;i++) {
|
---|
| 222 | var videoFormatsElem=videoFormatsGroup[i].split(sep2);
|
---|
| 223 | var videoFormatsPair=new Array();
|
---|
| 224 | for (var j=0;j<videoFormatsElem.length;j++) {
|
---|
| 225 | var pair=videoFormatsElem[j].split(sep3);
|
---|
| 226 | if (pair.length==2) {
|
---|
| 227 | videoFormatsPair[pair[0]]=pair[1];
|
---|
| 228 | }
|
---|
| 229 | }
|
---|
| 230 | if (videoFormatsPair['url']==null) continue;
|
---|
| 231 | var url=unescape(unescape(videoFormatsPair['url'])).replace(/\\\//g,'/').replace(/\\u0026/g,'&');
|
---|
| 232 | if (videoFormatsPair['itag']==null) continue;
|
---|
| 233 | var itag=videoFormatsPair['itag'];
|
---|
| 234 | var sig=videoFormatsPair['sig']||videoFormatsPair['signature'];
|
---|
| 235 | if (sig) {
|
---|
| 236 | url=url+'&signature='+sig;
|
---|
| 237 | videoSignature[itag]=null;
|
---|
| 238 | } else if (videoFormatsPair['s']) {
|
---|
| 239 | url=url+'&signature='+decryptSignature(videoFormatsPair['s']);
|
---|
| 240 | videoSignature[itag]=videoFormatsPair['s'];
|
---|
| 241 | }
|
---|
| 242 | if (url.toLowerCase().indexOf('ratebypass')==-1) { // speed up download for dash
|
---|
| 243 | url=url+'&ratebypass=yes';
|
---|
| 244 | }
|
---|
| 245 | if (url.toLowerCase().indexOf('http')==0) { // validate URL
|
---|
| 246 | videoURL[itag]=url+'&title='+videoTitle;
|
---|
| 247 | }
|
---|
| 248 | }
|
---|
| 249 |
|
---|
| 250 | var showFormat=new Array();
|
---|
| 251 | for (var category in FORMAT_RULE) {
|
---|
| 252 | var rule=FORMAT_RULE[category];
|
---|
| 253 | for (var index in FORMAT_TYPE){
|
---|
| 254 | if (FORMAT_TYPE[index]==category) {
|
---|
| 255 | showFormat[index]=(rule=='all');
|
---|
| 256 | }
|
---|
| 257 | }
|
---|
| 258 | if (rule=='max') {
|
---|
| 259 | for (var i=FORMAT_ORDER.length-1;i>=0;i--) {
|
---|
| 260 | var format=FORMAT_ORDER[i];
|
---|
| 261 | if (FORMAT_TYPE[format]==category && videoURL[format]!=undefined) {
|
---|
| 262 | showFormat[format]=true;
|
---|
| 263 | break;
|
---|
| 264 | }
|
---|
| 265 | }
|
---|
| 266 | }
|
---|
| 267 | }
|
---|
| 268 |
|
---|
| 269 | var dashPref=getPref(STORAGE_DASH);
|
---|
| 270 | if (dashPref=='1') {
|
---|
| 271 | SHOW_DASH_FORMATS=true;
|
---|
| 272 | } else if (dashPref!='0') {
|
---|
| 273 | setPref(STORAGE_DASH,'0');
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | var downloadCodeList=[];
|
---|
| 277 | for (var i=0;i<FORMAT_ORDER.length;i++) {
|
---|
| 278 | var format=FORMAT_ORDER[i];
|
---|
| 279 | if (format=='37' && videoURL[format]==undefined) { // hack for dash 1080p
|
---|
| 280 | if (videoURL['137']) {
|
---|
| 281 | format='137';
|
---|
| 282 | }
|
---|
| 283 | showFormat[format]=showFormat['37'];
|
---|
| 284 | } else if (format=='38' && videoURL[format]==undefined) { // hack for dash 4K
|
---|
| 285 | if (videoURL['138'] && !videoURL['266']) {
|
---|
| 286 | format='138';
|
---|
| 287 | }
|
---|
| 288 | showFormat[format]=showFormat['38'];
|
---|
| 289 | }
|
---|
| 290 | if (!SHOW_DASH_FORMATS && format.length>2) continue;
|
---|
| 291 | if (videoURL[format]!=undefined && FORMAT_LABEL[format]!=undefined && showFormat[format]) {
|
---|
| 292 | downloadCodeList.push({url:videoURL[format],sig:videoSignature[format],format:format,label:FORMAT_LABEL[format]});
|
---|
| 293 | debug('DYVAM - Info: itag'+format+' url:'+videoURL[format]);
|
---|
| 294 | }
|
---|
| 295 | }
|
---|
| 296 |
|
---|
| 297 | if (downloadCodeList.length==0) {
|
---|
| 298 | debug('DYVAM - Error: No download URL found. Probably YouTube uses encrypted streams.');
|
---|
| 299 | return; // no format
|
---|
| 300 | }
|
---|
| 301 |
|
---|
| 302 | // find parent container
|
---|
| 303 | var newWatchPage=false;
|
---|
| 304 | var parentElement=document.getElementById('watch7-action-buttons');
|
---|
| 305 | if (parentElement==null) {
|
---|
| 306 | parentElement=document.getElementById('watch8-secondary-actions');
|
---|
| 307 | if (parentElement==null) {
|
---|
| 308 | debug('DYVAM Error - No container for adding the download button. YouTube must have changed the code.');
|
---|
| 309 | return;
|
---|
| 310 | } else {
|
---|
| 311 | newWatchPage=true;
|
---|
| 312 | }
|
---|
| 313 | }
|
---|
| 314 |
|
---|
| 315 | // get button labels
|
---|
| 316 | var buttonText=(BUTTON_TEXT[language])?BUTTON_TEXT[language]:BUTTON_TEXT['en'];
|
---|
| 317 | var buttonLabel=(BUTTON_TOOLTIP[language])?BUTTON_TOOLTIP[language]:BUTTON_TOOLTIP['en'];
|
---|
| 318 |
|
---|
| 319 | // generate download code for regular interface
|
---|
| 320 | var mainSpan=document.createElement('span');
|
---|
| 321 |
|
---|
| 322 | if (newWatchPage) {
|
---|
| 323 | var spanIcon=document.createElement('span');
|
---|
| 324 | spanIcon.setAttribute('class', 'yt-uix-button-icon-wrapper');
|
---|
| 325 | var imageIcon=document.createElement('img');
|
---|
| 326 | imageIcon.setAttribute('src', '//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif');
|
---|
| 327 | imageIcon.setAttribute('class', 'yt-uix-button-icon');
|
---|
| 328 | 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==);');
|
---|
| 329 | spanIcon.appendChild(imageIcon);
|
---|
| 330 | mainSpan.appendChild(spanIcon);
|
---|
| 331 | }
|
---|
| 332 |
|
---|
| 333 | var spanButton=document.createElement('span');
|
---|
| 334 | spanButton.setAttribute('class', 'yt-uix-button-content');
|
---|
| 335 | spanButton.appendChild(document.createTextNode(buttonText+' '));
|
---|
| 336 | mainSpan.appendChild(spanButton);
|
---|
| 337 |
|
---|
| 338 | if (!newWatchPage) { // old UI
|
---|
| 339 | var imgButton=document.createElement('img');
|
---|
| 340 | imgButton.setAttribute('class', 'yt-uix-button-arrow');
|
---|
| 341 | imgButton.setAttribute('src', '//s.ytimg.com/yt/img/pixel-vfl3z5WfW.gif');
|
---|
| 342 | mainSpan.appendChild(imgButton);
|
---|
| 343 | }
|
---|
| 344 |
|
---|
| 345 | var listItems=document.createElement('ol');
|
---|
| 346 | listItems.setAttribute('style', 'display:none;');
|
---|
| 347 | listItems.setAttribute('class', 'yt-uix-button-menu');
|
---|
| 348 | for (var i=0;i<downloadCodeList.length;i++) {
|
---|
| 349 | var listItem=document.createElement('li');
|
---|
| 350 | var listLink=document.createElement('a');
|
---|
| 351 | listLink.setAttribute('style', 'text-decoration:none;');
|
---|
| 352 | listLink.setAttribute('href', downloadCodeList[i].url);
|
---|
| 353 | listLink.setAttribute('download', videoTitle+'.'+FORMAT_TYPE[downloadCodeList[i].format]);
|
---|
| 354 | var listButton=document.createElement('span');
|
---|
| 355 | listButton.setAttribute('class', 'yt-uix-button-menu-item');
|
---|
| 356 | listButton.setAttribute('loop', i+'');
|
---|
| 357 | listButton.setAttribute('id', LISTITEM_ID+downloadCodeList[i].format);
|
---|
| 358 | listButton.appendChild(document.createTextNode(downloadCodeList[i].label));
|
---|
| 359 | listLink.appendChild(listButton);
|
---|
| 360 | listItem.appendChild(listLink);
|
---|
| 361 | listItems.appendChild(listItem);
|
---|
| 362 | }
|
---|
| 363 | mainSpan.appendChild(listItems);
|
---|
| 364 | var buttonElement=document.createElement('button');
|
---|
| 365 | buttonElement.setAttribute('id', BUTTON_ID);
|
---|
| 366 | if (newWatchPage) {
|
---|
| 367 | buttonElement.setAttribute('class', 'yt-uix-button yt-uix-button-size-default yt-uix-button-opacity yt-uix-tooltip');
|
---|
| 368 | } else { // old UI
|
---|
| 369 | buttonElement.setAttribute('class', 'yt-uix-button yt-uix-tooltip yt-uix-button-empty yt-uix-button-text');
|
---|
| 370 | buttonElement.setAttribute('style', 'margin-top:4px; margin-left:'+((textDirection=='left')?5:10)+'px;');
|
---|
| 371 | }
|
---|
| 372 | buttonElement.setAttribute('data-tooltip-text', buttonLabel);
|
---|
| 373 | buttonElement.setAttribute('type', 'button');
|
---|
| 374 | buttonElement.setAttribute('role', 'button');
|
---|
| 375 | buttonElement.addEventListener('click', function(){return false;}, false);
|
---|
| 376 | buttonElement.appendChild(mainSpan);
|
---|
| 377 | var containerSpan=document.createElement('span');
|
---|
| 378 | containerSpan.setAttribute('id', CONTAINER_ID);
|
---|
| 379 | containerSpan.appendChild(document.createTextNode(' '));
|
---|
| 380 | containerSpan.appendChild(buttonElement);
|
---|
| 381 |
|
---|
| 382 | // add the button
|
---|
| 383 | if (!newWatchPage) { // watch7
|
---|
| 384 | parentElement.appendChild(containerSpan);
|
---|
| 385 | } else { // watch8
|
---|
| 386 | parentElement.insertBefore(containerSpan, parentElement.firstChild);
|
---|
| 387 | }
|
---|
| 388 |
|
---|
| 389 | // REPLACEWITH if (!isSignatureUpdatingStarted) {
|
---|
| 390 | for (var i=0;i<downloadCodeList.length;i++) {
|
---|
| 391 | addFileSize(downloadCodeList[i].url, downloadCodeList[i].format);
|
---|
| 392 | }
|
---|
| 393 | // }
|
---|
| 394 |
|
---|
| 395 | if (typeof GM_download !== 'undefined') {
|
---|
| 396 | for (var i=0;i<downloadCodeList.length;i++) {
|
---|
| 397 | var downloadFMT=document.getElementById(LISTITEM_ID+downloadCodeList[i].format);
|
---|
| 398 | var url=(downloadCodeList[i].url).toLowerCase();
|
---|
| 399 | if (url.indexOf('clen=')>0 && url.indexOf('dur=')>0 && url.indexOf('gir=')>0
|
---|
| 400 | && url.indexOf('lmt=')>0) {
|
---|
| 401 | downloadFMT.addEventListener('click', downloadVideoNatively, false);
|
---|
| 402 | }
|
---|
| 403 | }
|
---|
| 404 | }
|
---|
| 405 |
|
---|
| 406 | addFromManifest('140', '141'); // replace fmt140 with fmt141 if found in manifest
|
---|
| 407 |
|
---|
| 408 | function downloadVideoNatively(e) {
|
---|
| 409 | var elem=e.currentTarget;
|
---|
| 410 | e.returnValue=false;
|
---|
| 411 | if (e.preventDefault) {
|
---|
| 412 | e.preventDefault();
|
---|
| 413 | }
|
---|
| 414 | var loop=elem.getAttribute('loop');
|
---|
| 415 | if (loop) {
|
---|
| 416 | GM_download(downloadCodeList[loop].url, videoTitle+'.'+FORMAT_TYPE[downloadCodeList[loop].format]);
|
---|
| 417 | }
|
---|
| 418 | return false;
|
---|
| 419 | }
|
---|
| 420 |
|
---|
| 421 | function addFromManifest(oldFormat, newFormat) { // find newFormat URL in manifest
|
---|
| 422 | if (videoManifestURL && videoURL[newFormat]==undefined && SHOW_DASH_FORMATS && FORMAT_RULE['m4a']=='max') {
|
---|
| 423 | var matchSig=findMatch(videoManifestURL, /\/s\/([a-zA-Z0-9\.]+)\//i);
|
---|
| 424 | if (matchSig) {
|
---|
| 425 | var decryptedSig=decryptSignature(matchSig);
|
---|
| 426 | if (decryptedSig) {
|
---|
| 427 | videoManifestURL=videoManifestURL.replace('/s/'+matchSig+'/','/signature/'+decryptedSig+'/');
|
---|
| 428 | }
|
---|
| 429 | }
|
---|
| 430 | if (videoManifestURL.indexOf('//')==0) {
|
---|
| 431 | var protocol=(document.location.protocol=='http:')?'http:':'https:';
|
---|
| 432 | videoManifestURL=protocol+videoManifestURL;
|
---|
| 433 | }
|
---|
| 434 | debug('DYVAM - Info: manifestURL '+videoManifestURL);
|
---|
| 435 | crossXmlHttpRequest({
|
---|
| 436 | method:'GET',
|
---|
| 437 | url:videoManifestURL, // check if URL exists
|
---|
| 438 | onload:function(response) {
|
---|
| 439 | if (response.readyState === 4 && response.status === 200 && response.responseText) {
|
---|
| 440 | var regexp = new RegExp('<BaseURL.+>(http[^<]+itag='+newFormat+'[^<]+)<\\/BaseURL>','i');
|
---|
| 441 | var matchURL=findMatch(response.responseText, regexp);
|
---|
| 442 | debug('DYVAM - Info: matchURL '+matchURL);
|
---|
| 443 | if (!matchURL) return;
|
---|
| 444 | matchURL=matchURL.replace(/&\;/g,'&');
|
---|
| 445 | for (var i=0;i<downloadCodeList.length;i++) {
|
---|
| 446 | if (downloadCodeList[i].format==oldFormat) {
|
---|
| 447 | downloadCodeList[i].format==newFormat;
|
---|
| 448 | var downloadFMT=document.getElementById(LISTITEM_ID+oldFormat);
|
---|
| 449 | downloadFMT.setAttribute('id', LISTITEM_ID+newFormat);
|
---|
| 450 | downloadFMT.parentNode.setAttribute('href', matchURL);
|
---|
| 451 | downloadCodeList[i].url=matchURL;
|
---|
| 452 | downloadFMT.firstChild.nodeValue=FORMAT_LABEL[newFormat];
|
---|
| 453 | addFileSize(matchURL, newFormat);
|
---|
| 454 | }
|
---|
| 455 | }
|
---|
| 456 | }
|
---|
| 457 | }
|
---|
| 458 | });
|
---|
| 459 | }
|
---|
| 460 | }
|
---|
| 461 |
|
---|
| 462 | function injectStyle(code) {
|
---|
| 463 | var style=document.createElement('style');
|
---|
| 464 | style.type='text/css';
|
---|
| 465 | style.appendChild(document.createTextNode(code));
|
---|
| 466 | document.getElementsByTagName('head')[0].appendChild(style);
|
---|
| 467 | }
|
---|
| 468 |
|
---|
| 469 | function injectScript(code) {
|
---|
| 470 | var script=document.createElement('script');
|
---|
| 471 | script.type='application/javascript';
|
---|
| 472 | script.textContent=code;
|
---|
| 473 | document.body.appendChild(script);
|
---|
| 474 | document.body.removeChild(script);
|
---|
| 475 | }
|
---|
| 476 |
|
---|
| 477 | function debug(str) {
|
---|
| 478 | var debugElem=document.getElementById(DEBUG_ID);
|
---|
| 479 | if (!debugElem) {
|
---|
| 480 | debugElem=createHiddenElem('div', DEBUG_ID);
|
---|
| 481 | }
|
---|
| 482 | debugElem.appendChild(document.createTextNode(str+' '));
|
---|
| 483 | }
|
---|
| 484 |
|
---|
| 485 | function createHiddenElem(tag, id) {
|
---|
| 486 | var elem=document.createElement(tag);
|
---|
| 487 | elem.setAttribute('id', id);
|
---|
| 488 | elem.setAttribute('style', 'display:none;');
|
---|
| 489 | document.body.appendChild(elem);
|
---|
| 490 | return elem;
|
---|
| 491 | }
|
---|
| 492 |
|
---|
| 493 | function fixTranslations(language, textDirection) {
|
---|
| 494 | 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)) { // fix international UI
|
---|
| 495 | var likeButton=document.getElementById('watch-like');
|
---|
| 496 | if (likeButton) {
|
---|
| 497 | var spanElements=likeButton.getElementsByClassName('yt-uix-button-content');
|
---|
| 498 | if (spanElements) {
|
---|
| 499 | spanElements[0].style.display='none'; // hide like text
|
---|
| 500 | }
|
---|
| 501 | }
|
---|
| 502 | var marginPixels=10;
|
---|
| 503 | if (/^bg|ca|cs|el|eu|hr|it|ml|ms|pl|sl|sw|te$/.test(language)) {
|
---|
| 504 | marginPixels=1;
|
---|
| 505 | }
|
---|
| 506 | injectStyle('#watch7-secondary-actions .yt-uix-button{margin-'+textDirection+':'+marginPixels+'px!important}');
|
---|
| 507 | }
|
---|
| 508 | }
|
---|
| 509 |
|
---|
| 510 | function findMatch(text, regexp) {
|
---|
| 511 | var matches=text.match(regexp);
|
---|
| 512 | return (matches)?matches[1]:null;
|
---|
| 513 | }
|
---|
| 514 |
|
---|
| 515 | function isString(s) {
|
---|
| 516 | return (typeof s==='string' || s instanceof String);
|
---|
| 517 | }
|
---|
| 518 |
|
---|
| 519 | function isInteger(n) {
|
---|
| 520 | return (typeof n==='number' && n%1==0);
|
---|
| 521 | }
|
---|
| 522 |
|
---|
| 523 | function getPref(name) { // cross-browser GM_getValue
|
---|
| 524 | var a='', b='';
|
---|
| 525 | try {a=typeof GM_getValue.toString; b=GM_getValue.toString()} catch(e){}
|
---|
| 526 | if (typeof GM_getValue === 'function' &&
|
---|
| 527 | (a === 'undefined' || b.indexOf('not supported') === -1)) {
|
---|
| 528 | return GM_getValue(name, null); // Greasemonkey, Tampermonkey, Firefox extension
|
---|
| 529 | } else {
|
---|
| 530 | var ls=null;
|
---|
| 531 | try {ls=window.localStorage||null} catch(e){}
|
---|
| 532 | if (ls) {
|
---|
| 533 | return ls.getItem(name); // Chrome script, Opera extensions
|
---|
| 534 | }
|
---|
| 535 | }
|
---|
| 536 | return;
|
---|
| 537 | }
|
---|
| 538 |
|
---|
| 539 | function setPref(name, value) { // cross-browser GM_setValue
|
---|
| 540 | var a='', b='';
|
---|
| 541 | try {a=typeof GM_setValue.toString; b=GM_setValue.toString()} catch(e){}
|
---|
| 542 | if (typeof GM_setValue === 'function' &&
|
---|
| 543 | (a === 'undefined' || b.indexOf('not supported') === -1)) {
|
---|
| 544 | GM_setValue(name, value); // Greasemonkey, Tampermonkey, Firefox extension
|
---|
| 545 | } else {
|
---|
| 546 | var ls=null;
|
---|
| 547 | try {ls=window.localStorage||null} catch(e){}
|
---|
| 548 | if (ls) {
|
---|
| 549 | return ls.setItem(name, value); // Chrome script, Opera extensions
|
---|
| 550 | }
|
---|
| 551 | }
|
---|
| 552 | }
|
---|
| 553 |
|
---|
| 554 | function crossXmlHttpRequest(details) { // cross-browser GM_xmlhttpRequest
|
---|
| 555 | if (typeof GM_xmlhttpRequest === 'function') { // Greasemonkey, Tampermonkey, Firefox extension, Chrome script
|
---|
| 556 | GM_xmlhttpRequest(details);
|
---|
| 557 | } else if (typeof window.opera !== 'undefined' && window.opera && typeof opera.extension !== 'undefined' &&
|
---|
| 558 | typeof opera.extension.postMessage !== 'undefined') { // Opera 12 extension
|
---|
| 559 | var index=operaTable.length;
|
---|
| 560 | opera.extension.postMessage({'action':'xhr-'+index, 'url':details.url, 'method':details.method});
|
---|
| 561 | operaTable[index]=details;
|
---|
| 562 | } else if (typeof window.opera === 'undefined' && typeof XMLHttpRequest === 'function') { // Opera 15+ extension
|
---|
| 563 | var xhr=new XMLHttpRequest();
|
---|
| 564 | xhr.onreadystatechange = function() {
|
---|
| 565 | if (xhr.readyState == 4) {
|
---|
| 566 | if (details['onload']) {
|
---|
| 567 | details['onload'](xhr);
|
---|
| 568 | }
|
---|
| 569 | }
|
---|
| 570 | }
|
---|
| 571 | xhr.open(details.method, details.url, true);
|
---|
| 572 | xhr.send();
|
---|
| 573 | }
|
---|
| 574 | }
|
---|
| 575 |
|
---|
| 576 | function addFileSize(url, format) {
|
---|
| 577 |
|
---|
| 578 | function updateVideoLabel(size, format) {
|
---|
| 579 | var elem=document.getElementById(LISTITEM_ID+format);
|
---|
| 580 | if (elem) {
|
---|
| 581 | size=parseInt(size,10);
|
---|
| 582 | if (size>=1073741824) {
|
---|
| 583 | size=parseFloat((size/1073741824).toFixed(1))+' GB';
|
---|
| 584 | } else if (size>=1048576) {
|
---|
| 585 | size=parseFloat((size/1048576).toFixed(1))+' MB';
|
---|
| 586 | } else {
|
---|
| 587 | size=parseFloat((size/1024).toFixed(1))+' KB';
|
---|
| 588 | }
|
---|
| 589 | if (elem.childNodes.length>1) {
|
---|
| 590 | elem.lastChild.nodeValue=' ('+size+')';
|
---|
| 591 | } else if (elem.childNodes.length==1) {
|
---|
| 592 | elem.appendChild(document.createTextNode(' ('+size+')'));
|
---|
| 593 | }
|
---|
| 594 | }
|
---|
| 595 | }
|
---|
| 596 |
|
---|
| 597 | var matchSize=findMatch(url, /[&\?]clen=([0-9]+)&/i);
|
---|
| 598 | if (matchSize) {
|
---|
| 599 | updateVideoLabel(matchSize, format);
|
---|
| 600 | } else {
|
---|
| 601 | try {
|
---|
| 602 | crossXmlHttpRequest({
|
---|
| 603 | method:'HEAD',
|
---|
| 604 | url:url,
|
---|
| 605 | onload:function(response) {
|
---|
| 606 | if (response.readyState == 4 && response.status == 200) { // add size
|
---|
| 607 | var size=0;
|
---|
| 608 | if (typeof response.getResponseHeader === 'function') {
|
---|
| 609 | size=response.getResponseHeader('Content-length');
|
---|
| 610 | } else if (response.responseHeaders) {
|
---|
| 611 | var regexp = new RegExp('^Content\-length: (.*)$','im');
|
---|
| 612 | var match = regexp.exec(response.responseHeaders);
|
---|
| 613 | if (match) {
|
---|
| 614 | size=match[1];
|
---|
| 615 | }
|
---|
| 616 | }
|
---|
| 617 | if (size) {
|
---|
| 618 | updateVideoLabel(size, format);
|
---|
| 619 | }
|
---|
| 620 | }
|
---|
| 621 | }
|
---|
| 622 | });
|
---|
| 623 | } catch(e) { }
|
---|
| 624 | }
|
---|
| 625 | }
|
---|
| 626 |
|
---|
| 627 | function findSignatureCode(sourceCode) {
|
---|
| 628 | debug('DYVAM - Info: signature start '+getPref(STORAGE_CODE));
|
---|
| 629 | var signatureFunctionName =
|
---|
| 630 | findMatch(sourceCode,
|
---|
| 631 | /\.set\s*\("signature"\s*,\s*([a-zA-Z0-9_$][\w$]*)\(/)
|
---|
| 632 | || findMatch(sourceCode,
|
---|
| 633 | /\.sig\s*\|\|\s*([a-zA-Z0-9_$][\w$]*)\(/)
|
---|
| 634 | || findMatch(sourceCode,
|
---|
| 635 | /\.signature\s*=\s*([a-zA-Z_$][\w$]*)\([a-zA-Z_$][\w$]*\)/); //old
|
---|
| 636 | if (signatureFunctionName == null) return setPref(STORAGE_CODE, 'error');
|
---|
| 637 | signatureFunctionName=signatureFunctionName.replace('$','\\$');
|
---|
| 638 | var regCode = new RegExp('function \\s*' + signatureFunctionName +
|
---|
| 639 | '\\s*\\([\\w$]*\\)\\s*{[\\w$]*=[\\w$]*\\.split\\(""\\);(.+);return [\\w$]*\\.join');
|
---|
| 640 | var functionCode = findMatch(sourceCode, regCode);
|
---|
| 641 | debug('DYVAM - Info: signaturefunction ' + signatureFunctionName + ' -- ' + functionCode);
|
---|
| 642 | if (functionCode == null) return setPref(STORAGE_CODE, 'error');
|
---|
| 643 |
|
---|
| 644 | var reverseFunctionName = findMatch(sourceCode,
|
---|
| 645 | /([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.reverse\s*\(\s*\)\s*}/);
|
---|
| 646 | debug('DYVAM - Info: reversefunction ' + reverseFunctionName);
|
---|
| 647 | if (reverseFunctionName) reverseFunctionName=reverseFunctionName.replace('$','\\$');
|
---|
| 648 | var sliceFunctionName = findMatch(sourceCode,
|
---|
| 649 | /([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*,\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.(?:slice|splice)\(.+\)\s*}/);
|
---|
| 650 | debug('DYVAM - Info: slicefunction ' + sliceFunctionName);
|
---|
| 651 | if (sliceFunctionName) sliceFunctionName=sliceFunctionName.replace('$','\\$');
|
---|
| 652 |
|
---|
| 653 | var regSlice = new RegExp('\\.(?:'+'slice'+(sliceFunctionName?'|'+sliceFunctionName:'')+
|
---|
| 654 | ')\\s*\\(\\s*(?:[a-zA-Z_$][\\w$]*\\s*,)?\\s*([0-9]+)\\s*\\)'); // .slice(5) sau .Hf(a,5)
|
---|
| 655 | var regReverse = new RegExp('\\.(?:'+'reverse'+(reverseFunctionName?'|'+reverseFunctionName:'')+
|
---|
| 656 | ')\\s*\\([^\\)]*\\)'); // .reverse() sau .Gf(a,45)
|
---|
| 657 | var regSwap = new RegExp('[\\w$]+\\s*\\(\\s*[\\w$]+\\s*,\\s*([0-9]+)\\s*\\)');
|
---|
| 658 | var regInline = new RegExp('[\\w$]+\\[0\\]\\s*=\\s*[\\w$]+\\[([0-9]+)\\s*%\\s*[\\w$]+\\.length\\]');
|
---|
| 659 | var functionCodePieces=functionCode.split(';');
|
---|
| 660 | var decodeArray=[];
|
---|
| 661 | for (var i=0; i<functionCodePieces.length; i++) {
|
---|
| 662 | functionCodePieces[i]=functionCodePieces[i].trim();
|
---|
| 663 | var codeLine=functionCodePieces[i];
|
---|
| 664 | if (codeLine.length>0) {
|
---|
| 665 | var arrSlice=codeLine.match(regSlice);
|
---|
| 666 | var arrReverse=codeLine.match(regReverse);
|
---|
| 667 | debug(i+': '+codeLine+' --'+(arrSlice?' slice length '+arrSlice.length:'') +' '+(arrReverse?'reverse':''));
|
---|
| 668 | if (arrSlice && arrSlice.length >= 2) { // slice
|
---|
| 669 | var slice=parseInt(arrSlice[1], 10);
|
---|
| 670 | if (isInteger(slice)){
|
---|
| 671 | decodeArray.push(-slice);
|
---|
| 672 | } else return setPref(STORAGE_CODE, 'error');
|
---|
| 673 | } else if (arrReverse && arrReverse.length >= 1) { // reverse
|
---|
| 674 | decodeArray.push(0);
|
---|
| 675 | } else if (codeLine.indexOf('[0]') >= 0) { // inline swap
|
---|
| 676 | if (i+2<functionCodePieces.length &&
|
---|
| 677 | functionCodePieces[i+1].indexOf('.length') >= 0 &&
|
---|
| 678 | functionCodePieces[i+1].indexOf('[0]') >= 0) {
|
---|
| 679 | var inline=findMatch(functionCodePieces[i+1], regInline);
|
---|
| 680 | inline=parseInt(inline, 10);
|
---|
| 681 | decodeArray.push(inline);
|
---|
| 682 | i+=2;
|
---|
| 683 | } else return setPref(STORAGE_CODE, 'error');
|
---|
| 684 | } else if (codeLine.indexOf(',') >= 0) { // swap
|
---|
| 685 | var swap=findMatch(codeLine, regSwap);
|
---|
| 686 | swap=parseInt(swap, 10);
|
---|
| 687 | if (isInteger(swap) && swap>0){
|
---|
| 688 | decodeArray.push(swap);
|
---|
| 689 | } else return setPref(STORAGE_CODE, 'error');
|
---|
| 690 | } else return setPref(STORAGE_CODE, 'error');
|
---|
| 691 | }
|
---|
| 692 | }
|
---|
| 693 |
|
---|
| 694 | if (decodeArray) {
|
---|
| 695 | setPref(STORAGE_URL, scriptURL);
|
---|
| 696 | setPref(STORAGE_CODE, decodeArray.toString());
|
---|
| 697 | DECODE_RULE=decodeArray;
|
---|
| 698 | debug('DYVAM - Info: signature '+decodeArray.toString()+' '+scriptURL);
|
---|
| 699 | // update download links and add file sizes
|
---|
| 700 | for (var i=0;i<downloadCodeList.length;i++) {
|
---|
| 701 | var elem=document.getElementById(LISTITEM_ID+downloadCodeList[i].format);
|
---|
| 702 | var url=downloadCodeList[i].url;
|
---|
| 703 | var sig=downloadCodeList[i].sig;
|
---|
| 704 | if (elem && url && sig) {
|
---|
| 705 | url=url.replace(/\&signature=[\w\.]+/, '&signature='+decryptSignature(sig));
|
---|
| 706 | elem.parentNode.setAttribute('href', url);
|
---|
| 707 | addFileSize(url, downloadCodeList[i].format);
|
---|
| 708 | }
|
---|
| 709 | }
|
---|
| 710 | }
|
---|
| 711 | }
|
---|
| 712 |
|
---|
| 713 | function isValidSignatureCode(arr) { // valid values: '5,-3,0,2,5', 'error'
|
---|
| 714 | if (!arr) return false;
|
---|
| 715 | if (arr=='error') return true;
|
---|
| 716 | arr=arr.split(',');
|
---|
| 717 | for (var i=0;i<arr.length;i++) {
|
---|
| 718 | if (!isInteger(parseInt(arr[i],10))) return false;
|
---|
| 719 | }
|
---|
| 720 | return true;
|
---|
| 721 | }
|
---|
| 722 |
|
---|
| 723 | function fetchSignatureScript(scriptURL) {
|
---|
| 724 | var storageURL=getPref(STORAGE_URL);
|
---|
| 725 | var storageCode=getPref(STORAGE_CODE);
|
---|
| 726 | if (!(/,0,|^0,|,0$|\-/.test(storageCode))) storageCode=null; // hack for only positive items
|
---|
| 727 | if (storageCode && isValidSignatureCode(storageCode) && storageURL &&
|
---|
| 728 | scriptURL.replace(/^https?/i,'')==storageURL.replace(/^https?/i,'')) return;
|
---|
| 729 | try {
|
---|
| 730 | debug('DYVAM fetch '+scriptURL);
|
---|
| 731 | isSignatureUpdatingStarted=true;
|
---|
| 732 | crossXmlHttpRequest({
|
---|
| 733 | method:'GET',
|
---|
| 734 | url:scriptURL,
|
---|
| 735 | onload:function(response) {
|
---|
| 736 | debug('DYVAM fetch status '+response.status);
|
---|
| 737 | if (response.readyState === 4 && response.status === 200 && response.responseText) {
|
---|
| 738 | findSignatureCode(response.responseText);
|
---|
| 739 | }
|
---|
| 740 | }
|
---|
| 741 | });
|
---|
| 742 | } catch(e) { }
|
---|
| 743 | }
|
---|
| 744 |
|
---|
| 745 | function getDecodeRules(rules) {
|
---|
| 746 | var storageCode=getPref(STORAGE_CODE);
|
---|
| 747 | if (storageCode && storageCode!='error' && isValidSignatureCode(storageCode)) {
|
---|
| 748 | var arr=storageCode.split(',');
|
---|
| 749 | for (var i=0; i<arr.length; i++) {
|
---|
| 750 | arr[i]=parseInt(arr[i], 10);
|
---|
| 751 | }
|
---|
| 752 | rules=arr;
|
---|
| 753 | debug('DYVAM - Info: signature '+arr.toString()+' '+scriptURL);
|
---|
| 754 | }
|
---|
| 755 | return rules;
|
---|
| 756 | }
|
---|
| 757 |
|
---|
| 758 | function decryptSignature(sig) {
|
---|
| 759 | function swap(a,b){var c=a[0];a[0]=a[b%a.length];a[b]=c;return a};
|
---|
| 760 | function decode(sig, arr) { // encoded decryption
|
---|
| 761 | if (!isString(sig)) return null;
|
---|
| 762 | var sigA=sig.split('');
|
---|
| 763 | for (var i=0;i<arr.length;i++) {
|
---|
| 764 | var act=arr[i];
|
---|
| 765 | if (!isInteger(act)) return null;
|
---|
| 766 | sigA=(act>0)?swap(sigA, act):((act==0)?sigA.reverse():sigA.slice(-act));
|
---|
| 767 | }
|
---|
| 768 | var result=sigA.join('');
|
---|
| 769 | return result;
|
---|
| 770 | }
|
---|
| 771 |
|
---|
| 772 | if (sig==null) return '';
|
---|
| 773 | var arr=DECODE_RULE;
|
---|
| 774 | if (arr) {
|
---|
| 775 | var sig2=decode(sig, arr);
|
---|
| 776 | if (sig2) return sig2;
|
---|
| 777 | } else {
|
---|
| 778 | setPref(STORAGE_URL, '');
|
---|
| 779 | setPref(STORAGE_CODE, '');
|
---|
| 780 | }
|
---|
| 781 | return sig;
|
---|
| 782 | }
|
---|
| 783 |
|
---|
| 784 | }
|
---|
| 785 |
|
---|
| 786 | })(); |
---|