├── Chrome-Extension ├── content-script.js └── manifest.json ├── License ├── README.md └── bookmarklet.js /Chrome-Extension/content-script.js: -------------------------------------------------------------------------------- 1 | function getURLMap(bodyHTML) { 2 | var urlMap = null; 3 | var urlMapStartIndex = bodyHTML.indexOf('"url_encoded_fmt_stream_map"'); 4 | if (urlMapStartIndex != -1) { 5 | urlMap = bodyHTML.substring(urlMapStartIndex); 6 | var urlMapEndIndex = urlMap.indexOf('", '); 7 | if (urlMapEndIndex != -1) { 8 | urlMap = urlMap.substring(30, urlMapEndIndex); 9 | } 10 | } 11 | 12 | if (urlMap == null) throw 'Error: Couldn\'t find url map.'; 13 | urlMap = urlMap.replace(/\\u0026/g, '&'); 14 | return urlMap; 15 | } 16 | 17 | function getLinksAndFormats(urlMap) { 18 | var allLinks = urlMap.split(','); 19 | 20 | var linksAndFormats = new Array(); 21 | var numOfLinks = 0; 22 | for (var i = 0; i < allLinks.length; i++) { 23 | var link = getCleanURL(allLinks[i]); 24 | var itagIndex = link.lastIndexOf('itag='); 25 | if (itagIndex != -1) { 26 | var fmt = parseInt(link.substring(itagIndex + 5)); 27 | if (!isNaN(fmt)) { 28 | linksAndFormats[fmt.toString()] = link; 29 | numOfLinks++; 30 | } 31 | // else { 32 | // alert('Ignoring link:\n' + link + '\nbecause tag at ' + itagIndex + ' is Nan.'); 33 | // } 34 | } 35 | // else { 36 | // alert('Ignoring link:\n' + link + '\nbecause itag index coud not be found'); 37 | // } 38 | } 39 | 40 | if (numOfLinks == 0) throw 'Failed to find download links.'; 41 | linksAndFormats[0] = numOfLinks; 42 | return linksAndFormats; 43 | } 44 | 45 | function getKeyAndVal(str) { 46 | var m = str.match(/^([^=]*)=(.*)$/); 47 | if (m) 48 | return [m[1], m[2]]; 49 | return null; 50 | } 51 | 52 | function getCleanURL(url) { 53 | var mainParams = {}; 54 | var splits = url.split("&"); 55 | for (var i = 0; i < splits.length; i++) { 56 | var keyVal = getKeyAndVal(splits[i]); 57 | if (keyVal) mainParams[keyVal[0]] = keyVal[1]; 58 | } 59 | 60 | var cleanURL = null; 61 | if ('url' in mainParams) 62 | cleanURL = unescape(mainParams['url']); 63 | 64 | else { 65 | // use regex 66 | var regexes = ['url=(http.+?videoplayback.+?id=.+?)(\u0026|&)quality=', '(http.+?videoplayback.+?id=.+?)(\u0026|&)']; 67 | for (var i = 0; i < regexes.length; i++) { 68 | var match = new RegExp(regexes[i]).exec(url); 69 | if (match != null) { 70 | cleanURL = unescape(match[1]); 71 | break; 72 | } 73 | } 74 | } 75 | 76 | if (cleanURL) { 77 | // check for the signature 78 | if (cleanURL.indexOf('signature=') == -1 && cleanURL.indexOf('sig=') == -1) { 79 | var sig = null; 80 | if ('signature' in mainParams) 81 | sig = mainParams['signature']; 82 | else 83 | sig = mainParams['sig']; 84 | 85 | if (sig) cleanURL = cleanURL + '&signature=' + sig; 86 | } 87 | else if (cleanURL.indexOf('sig=') != -1) { 88 | cleanURL.replace(/sig=/g, 'signature='); 89 | } 90 | 91 | return cleanURL + '&title=' + document.title.match(/^(.*) - YouTube$/)[1]; 92 | } 93 | 94 | return null; 95 | } 96 | 97 | function getHTMLForLinks(linksAndFormats) { 98 | var numOfLinks = linksAndFormats[0]; 99 | var standardLinksHTML = ''; 100 | var hdLinksHTML = ''; 101 | var thereIsHD = false; 102 | var addDash = false; 103 | /** FMT mapping from youtube's videos' source: 104 | * 37 = 1920X1080 MP4 105 | * 46 = 1920X1080 WebM 106 | * 22 = 1280X720 MP4 107 | * 45 = 1280X720 WebM 108 | * 35 = Large FLV 109 | * 44 = Large WebM 110 | * 34 = Medium FLV 111 | * 18 = Medium MP4 112 | * 43 = Medium WebM 113 | * 5 = Small FLV 114 | */ 115 | 116 | var addedSmall = false; 117 | if (linksAndFormats['5']) { 118 | standardLinksHTML += 'Small (FLV)%20%20%20%20%20'; 119 | addDash = true; 120 | } 121 | 122 | var openedBracket = false, 123 | closedBracket = false; 124 | if (linksAndFormats['18']) { 125 | if (addDash) standardLinksHTML += ' - '; 126 | standardLinksHTML += 'Medium (MP4'; 127 | addDash = false; 128 | openedBracket = true; 129 | } 130 | 131 | if (linksAndFormats['34']) { 132 | if (!openedBracket) standardLinksHTML += '- Medium ('; 133 | else standardLinksHTML += ', '; 134 | standardLinksHTML += 'FLV'; 135 | openedBracket = true; 136 | } 137 | 138 | if (linksAndFormats['43']) { 139 | if (!openedBracket) standardLinksHTML += '- Medium ('; 140 | else standardLinksHTML += ', '; 141 | standardLinksHTML += 'WebM)'; 142 | addDash = true; 143 | openedBracket = true; 144 | closedBracket = true; 145 | } 146 | 147 | if (openedBracket && !closedBracket) standardLinksHTML += ')'; 148 | if (!addDash && (linksAndFormats['18'] || linksAndFormats['34'])) addDash = true; 149 | openedBracket = false, closedBracket = false; 150 | 151 | if (linksAndFormats['35']) { 152 | if (addDash) standardLinksHTML += ' - '; 153 | standardLinksHTML += 'Large (FLV'; 154 | addDash = false; 155 | openedBracket = true; 156 | } 157 | 158 | if (linksAndFormats['44']) { 159 | if (!openedBracket) standardLinksHTML += '- Large ('; 160 | else standardLinksHTML += ', '; 161 | standardLinksHTML += 'WebM)'; 162 | addDash = true; 163 | openedBracket = true; 164 | closedBracket = true; 165 | } 166 | if (openedBracket && !closedBracket) standardLinksHTML += ')'; 167 | 168 | addDash = false, openedBracket = false, closedBracket = false; 169 | if (linksAndFormats['22']) { 170 | hdLinksHTML += '720p (MP4'; 171 | thereIsHD = true; 172 | addDash = true; 173 | openedBracket = true; 174 | } 175 | 176 | if (linksAndFormats['45']) { 177 | if (!openedBracket) hdLinksHTML += '- 720p ('; 178 | else hdLinksHTML += ', '; 179 | hdLinksHTML += 'WebM)'; 180 | thereIsHD = true; 181 | addDash = true; 182 | openedBracket = true; 183 | closedBracket = true; 184 | } 185 | if (openedBracket && !closedBracket) hdLinksHTML += ')'; 186 | 187 | if (!addDash && (linksAndFormats['22'] || linksAndFormats['45'])) addDash = true; 188 | openedBracket = false, closedBracket = false; 189 | 190 | if (linksAndFormats['37']) { 191 | if (addDash) hdLinksHTML += ' - '; 192 | hdLinksHTML += '1080p (MP4'; 193 | thereIsHD = true; 194 | addDash = true; 195 | openedBracket = true; 196 | } 197 | if (linksAndFormats['46']) { 198 | if (!openedBracket) hdLinksHTML += '- 1080p ('; 199 | else hdLinksHTML += ', '; 200 | hdLinksHTML += 'WebM)'; 201 | thereIsHD = true; 202 | addDash = true; 203 | openedBracket = true; 204 | closedBracket = true; 205 | } 206 | if (openedBracket && !closedBracket) hdLinksHTML += ')'; 207 | 208 | var title = '

Download (' + numOfLinks + ' links found)

'; 209 | if (thereIsHD) return unescape(title + standardLinksHTML + '
' + hdLinksHTML + '

'); 210 | else return unescape(title + standardLinksHTML + '

'); 211 | } 212 | 213 | function start() { 214 | if (document.URL.indexOf('.youtube.com/watch?v=') == -1) return; 215 | 216 | var error = null; 217 | var urlMap = null; 218 | var linksAndFormats = null; 219 | try { 220 | urlMap = getURLMap(document.body.innerHTML); 221 | linksAndFormats = getLinksAndFormats(urlMap); 222 | } catch (err) { 223 | error = err; 224 | } 225 | 226 | var download_div = document.createElement('span'); 227 | download_div.id = 'youtube-download-span'; 228 | var style = download_div.style; 229 | style.padding = '5px'; 230 | style.borderRadius = '1em'; 231 | style.lineHeight = '1.6'; 232 | style.display = 'inline-block'; 233 | style.margin = '5px auto'; 234 | style.boxShadow = '4px 4px 3px #999'; 235 | style.border = '1px #999 solid'; 236 | style.backgroundColor = '#ffe'; 237 | 238 | if (error == null) { 239 | // Append the links to the div 240 | download_div.innerHTML = getHTMLForLinks(linksAndFormats); 241 | } else { 242 | // Display the error 243 | download_div.innerHTML = unescape('

' + error + '

'); 244 | } 245 | var container_div = document.createElement('div'); 246 | container_div.style.textAlign = 'center'; 247 | container_div.appendChild(download_div); 248 | document.body.insertBefore(container_div, document.body.firstChild); 249 | } 250 | 251 | start(); 252 | -------------------------------------------------------------------------------- /Chrome-Extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "YouTube Download", 4 | "description": "Shows the download links for youtube videos.", 5 | "version": "2.1.1", 6 | "permissions": ["http://www.youtube.com/*"], 7 | "content_scripts" : [ 8 | { 9 | "matches" : [ 10 | "http://www.youtube.com/*" 11 | ], 12 | "js" : ["content-script.js"], 13 | "run_at" : "document_end", 14 | "all_frames" : false 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Motasim Abdulaziz Alsayed 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | DISCLAIMED. IN NO EVENT SHALL MOTASIM ABDULAZIZ ALSAYED BE LIABLE FOR ANY 16 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Screenshot 1](https://dl.dropbox.com/u/1693311/gh/YT-downloads.png) 2 | 3 | There are two ways to use this script: 4 | 5 | 1. Using the bookmarklet: simply copy the code in the bookmarklet.js file and add it as a new bookmark. Then, whenever you're on a YT video page, click this bookmark to see the download links. 6 | 2. Using the Google Chrome extension: go to the extensions page in Chrome, and click on Load unpacked extension..., then choose the folder "Chrome-Extension". Then, whenever you visit a video page on youtube's domain (e.g. http://www.youtube.com/watch?v=_0-SJNnnHFM) the extension will load automatically. 7 | 8 | **Please, do not use this extension for any thing that would violate any copyrights.** -------------------------------------------------------------------------------- /bookmarklet.js: -------------------------------------------------------------------------------- 1 | javascript:function%20getURLMap(bodyHTML){var%20urlMap=null;var%20urlMapStartIndex=bodyHTML.indexOf('"url_encoded_fmt_stream_map"');if(urlMapStartIndex!=-1){urlMap=bodyHTML.substring(urlMapStartIndex);var%20urlMapEndIndex=urlMap.indexOf('",%20');if(urlMapEndIndex!=-1){urlMap=urlMap.substring(0,urlMapEndIndex);}}if(urlMap==null)throw%20'Error:%20Couldn\'t%20find%20url%20map.';urlMap=urlMap.replace(/\\u0026/g,'&');return%20urlMap;}function%20getCleanURL(url){var%20mainParams={};var%20splits=url.split("&");for(var%20i=0;iFLV)%20%20%20%20%20';addDash=true;}var%20openedBracket=false,closedBracket=false;if(linksAndFormats['18']){if(addDash)standardLinksHTML+='%20-%20';standardLinksHTML+='Medium(MP4';addDash=false;openedBracket=true;}if(linksAndFormats['34']){if(!openedBracket)standardLinksHTML+='-%20Medium(';else%20standardLinksHTML+=',';standardLinksHTML+='FLV';openedBracket=true;}if(linksAndFormats['43']){if(!openedBracket)standardLinksHTML+='-%20Medium(';else%20standardLinksHTML+=',';standardLinksHTML+='WebM)';addDash=true;openedBracket=true;closedBracket=true;}if(openedBracket&&!closedBracket)standardLinksHTML+=')';if(!addDash&&(linksAndFormats['18']||linksAndFormats['34']))addDash=true;openedBracket=false,closedBracket=false;if(linksAndFormats['35']){if(addDash)standardLinksHTML+='%20-%20';standardLinksHTML+='Large(FLV';addDash=false;openedBracket=true;}if(linksAndFormats['44']){if(!openedBracket)standardLinksHTML+='-%20Large(';else%20standardLinksHTML+=',';standardLinksHTML+='WebM)';addDash=true;openedBracket=true;closedBracket=true;}if(openedBracket&&!closedBracket)standardLinksHTML+=')';addDash=false,openedBracket=false,closedBracket=false;if(linksAndFormats['22']){hdLinksHTML+='720p(MP4';thereIsHD=true;addDash=true;openedBracket=true;}if(linksAndFormats['45']){if(!openedBracket)hdLinksHTML+='-%20720p(';else%20hdLinksHTML+=',';hdLinksHTML+='WebM)';thereIsHD=true;addDash=true;openedBracket=true;closedBracket=true;}if(openedBracket&&!closedBracket)hdLinksHTML+=')';if(!addDash&&(linksAndFormats['22']||linksAndFormats['45']))addDash=true;openedBracket=false,closedBracket=false;if(linksAndFormats['37']){if(addDash)hdLinksHTML+='%20-%20';hdLinksHTML+='1080p(MP4';thereIsHD=true;addDash=true;openedBracket=true;}if(linksAndFormats['46']){if(!openedBracket)hdLinksHTML+='-%201080p(';else%20hdLinksHTML+=',';hdLinksHTML+='WebM)';thereIsHD=true;addDash=true;openedBracket=true;closedBracket=true;}if(openedBracket&&!closedBracket)hdLinksHTML+=')';var%20title='Download('+numOfLinks+'%20links%20found)';if(thereIsHD)return%20unescape(title+standardLinksHTML+''+hdLinksHTML+'

');else%20return%20unescape(title+standardLinksHTML+'

');}function%20start(){if(document.URL.indexOf('http://www.youtube.com/watch?v=')==-1)return;var%20error=null;var%20urlMap=null;var%20linksAndFormats=null;try{urlMap=getURLMap(document.body.innerHTML);linksAndFormats=getLinksAndFormats(urlMap);}catch(err){error=err;}var%20download_div=document.createElement('span');download_div.id='youtube-download-span';var%20style=download_div.style;style.padding='5px';style.borderRadius='1em';style.lineHeight='1.6';style.display='inline-block';style.margin='5px%20auto';style.boxShadow='4px%204px%203px%20#999';style.border='1px%20#999%20solid';style.backgroundColor='#ffe';if(error==null){download_div.innerHTML=getHTMLForLinks(linksAndFormats);}else{download_div.innerHTML=unescape(''+error+'');}var%20container_div=document.createElement('div');container_div.style.textAlign='center';container_div.appendChild(download_div);document.body.insertBefore(container_div,document.body.firstChild);}start(); --------------------------------------------------------------------------------