plkocev

script

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