SaveTube

Download videos from video sharing web sites.

  1. // ==UserScript==
  2. // @name SaveTube
  3. // @version 2025.04.23
  4. // @description Download videos from video sharing web sites.
  5. // @author sebaro
  6. // @namespace http://sebaro.pro/savetube
  7. // @icon https://gitlab.com/sebaro/savetube/raw/master/savetube.png
  8. // @include http://youtube.com*
  9. // @include http://www.youtube.com*
  10. // @include https://youtube.com*
  11. // @include https://www.youtube.com*
  12. // @include http://m.youtube.com*
  13. // @include https://m.youtube.com*
  14. // @exclude http://youtube.com/shorts*
  15. // @exclude http://www.youtube.com/shorts*
  16. // @exclude https://youtube.com/shorts*
  17. // @exclude https://www.youtube.com/shorts*
  18. // @include http://dailymotion.com*
  19. // @include http://www.dailymotion.com*
  20. // @include https://dailymotion.com*
  21. // @include https://www.dailymotion.com*
  22. // @include http://vimeo.com*
  23. // @include http://www.vimeo.com*
  24. // @include https://vimeo.com*
  25. // @include https://www.vimeo.com*
  26. // @include http://imdb.com*
  27. // @include http://www.imdb.com*
  28. // @include https://imdb.com*
  29. // @include https://www.imdb.com*
  30. // @noframes
  31. // @grant none
  32. // @run-at document-end
  33. // ==/UserScript==
  34.  
  35.  
  36. /*
  37.  
  38. Copyright (C) 2010 - 2025 Sebastian Luncan
  39.  
  40. This program is free software: you can redistribute it and/or modify
  41. it under the terms of the GNU General Public License as published by
  42. the Free Software Foundation, either version 3 of the License, or
  43. (at your option) any later version.
  44.  
  45. This program is distributed in the hope that it will be useful,
  46. but WITHOUT ANY WARRANTY; without even the implied warranty of
  47. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  48. GNU General Public License for more details.
  49.  
  50. You should have received a copy of the GNU General Public License
  51. along with this program. If not, see <http://www.gnu.org/licenses/>.
  52.  
  53. Website: http://sebaro.pro/savetube
  54. Contact: http://sebaro.pro/contact
  55.  
  56. */
  57.  
  58.  
  59. (function() {
  60.  
  61.  
  62. // Don't run on frames or iframes
  63. if (window.top != window.self) return;
  64.  
  65.  
  66. // ==========Variables========== //
  67.  
  68. // Userscript
  69. var userscript = 'SaveTube';
  70. var website = 'http://sebaro.pro/savetube';
  71. var contact = 'http://sebaro.pro/contact';
  72.  
  73. // Page
  74. var page = {win: window, doc: window.document, body: window.document.body, url: window.location.href, site: window.location.hostname.match(/([^.]+)\.[^.]+$/)[1]};
  75.  
  76. // Saver
  77. var saver = {};
  78. var panelHeight = 30;
  79.  
  80. // Features/Options
  81. var feature = {'definition': true, 'container': true, 'openpagelink': true, 'autosave': true, 'savedash': false, 'showsavelink': true};
  82. var option = {'definition': 'High Definition', 'container': 'MP4', 'openpagelink': false, 'autosave': false, 'savedash': false, 'showsavelink': false, 'hidden': false};
  83.  
  84. // Media
  85. var mediatypes = {'MP4': 'video/mp4', 'WebM': 'video/webm', 'M3U8': 'application/x-mpegURL', 'WebVTT': 'text/vtt'};
  86.  
  87. // Sources
  88. var sources = {};
  89.  
  90.  
  91. // ==========Functions========== //
  92.  
  93. function createMyElement(type, properties, event, listener) {
  94. var obj = page.doc.createElement(type);
  95. for (var propertykey in properties) {
  96. if (propertykey == 'target') obj.setAttribute('target', properties[propertykey]);
  97. else if (propertykey == 'innerHTML') {
  98. try {
  99. obj[propertykey] = properties[propertykey];
  100. }
  101. catch(e) {
  102. if (window.trustedTypes) {
  103. if (!window.trustedTypes.defaultPolicy) {
  104. if (window.trustedTypes.createPolicy) {
  105. window.trustedTypes.createPolicy('default', {
  106. createHTML: (string, sink) => string
  107. });
  108. }
  109. }
  110. if (window.trustedTypes.defaultPolicy) {
  111. obj[propertykey] = window.trustedTypes.defaultPolicy.createHTML(properties[propertykey]);
  112. }
  113. }
  114. }
  115. }
  116. else obj[propertykey] = properties[propertykey];
  117. }
  118. if (event && listener) {
  119. obj.addEventListener(event, listener, false);
  120. }
  121. return obj;
  122. }
  123.  
  124. function modifyMyElement(obj, properties, event, listener) {
  125. for (var propertykey in properties) {
  126. if (propertykey == 'target') obj.setAttribute('target', properties[propertykey]);
  127. else if (propertykey == 'innerHTML') {
  128. try {
  129. obj[propertykey] = properties[propertykey];
  130. }
  131. catch(e) {
  132. if (window.trustedTypes) {
  133. if (!window.trustedTypes.defaultPolicy) {
  134. if (window.trustedTypes.createPolicy) {
  135. window.trustedTypes.createPolicy('default', {
  136. createHTML: (string, sink) => string
  137. });
  138. }
  139. }
  140. if (window.trustedTypes.defaultPolicy) {
  141. obj[propertykey] = window.trustedTypes.defaultPolicy.createHTML(properties[propertykey]);
  142. }
  143. }
  144. }
  145. }
  146. else obj[propertykey] = properties[propertykey];
  147. }
  148. if (event && listener) {
  149. obj.addEventListener(event, listener, false);
  150. }
  151. }
  152.  
  153. function styleMyElement(obj, styles) {
  154. for (var stylekey in styles) {
  155. obj.style[stylekey] = styles[stylekey];
  156. }
  157. }
  158.  
  159. function cleanMyElement(obj, hide) {
  160. if (hide) {
  161. for (var i = 0; i < obj.children.length; i++) {
  162. styleMyElement(obj.children[i], {display: 'none'});
  163. }
  164. }
  165. else {
  166. if (obj.hasChildNodes()) {
  167. while (obj.childNodes.length >= 1) {
  168. obj.removeChild(obj.firstChild);
  169. }
  170. }
  171. }
  172. }
  173.  
  174. function getMyElement(obj, type, from, value, child, content) {
  175. var getObj, chObj, coObj;
  176. var pObj = (!obj) ? page.doc : obj;
  177. if (type == 'body') {
  178. getObj = pObj.body;
  179. }
  180. else {
  181. if (from == 'id') getObj = pObj.getElementById(value);
  182. else if (from == 'class') getObj = pObj.getElementsByClassName(value);
  183. else if (from == 'tag') getObj = pObj.getElementsByTagName(type);
  184. else if (from == 'ns') {
  185. if (pObj.getElementsByTagNameNS) getObj = pObj.getElementsByTagNameNS(value, type);
  186. }
  187. else if (from == 'query') {
  188. if (child > 0) {
  189. if (pObj.querySelectorAll) getObj = pObj.querySelectorAll(value);
  190. }
  191. else {
  192. if (pObj.querySelector) getObj = pObj.querySelector(value);
  193. }
  194. }
  195. }
  196. chObj = (getObj && child >= 0) ? getObj[child] : getObj;
  197. if (content && chObj) {
  198. if (type == 'html' || type == 'body' || type == 'div' || type == 'option') coObj = chObj.innerHTML;
  199. else if (type == 'object') coObj = chObj.data;
  200. else if (type == 'img' || type == 'video' || type == 'embed') coObj = chObj.src;
  201. else coObj = chObj.textContent;
  202. return coObj;
  203. }
  204. else {
  205. return chObj;
  206. }
  207. }
  208.  
  209. function appendMyElement(parent, child) {
  210. parent.appendChild(child);
  211. }
  212.  
  213. function removeMyElement(parent, child) {
  214. parent.removeChild(child);
  215. }
  216.  
  217. function replaceMyElement(parent, orphan, child) {
  218. parent.replaceChild(orphan, child);
  219. }
  220.  
  221. function cleanMyContent(content, unesc, extra) {
  222. if (unesc) content = unescape(content);
  223. content = content.replace(/\\u0025/g, '%');
  224. content = content.replace(/\\u0026/g, '&');
  225. content = content.replace(/\\u002F/g, '/');
  226. content = content.replace(/\\/g, '');
  227. content = content.replace(/\n/g, '');
  228. if (extra) {
  229. content = content.replace(/&quot;/g, '\'').replace(/&#34;/g, '\'').replace(/&#034;/g, '\'').replace(/["“”„‘’]/g, '\'');
  230. content = content.replace(/&#39;/g, '\'').replace(/&#039;/g, '\'').replace(/'/g, '\'');
  231. content = content.replace(/&amp;/g, 'and').replace(/&/g, 'and');
  232. //content = content.replace(/[^\x20-\xFF]/g, '');
  233. content = content.replace(/[\/\|]/g, '-');
  234. content = content.replace(/[<>#:\*\?]/g, '');
  235. content = content.replace(/^\s+|\s+$/, '').replace(/\s+/g, ' ').replace(/\.+$/g, '');
  236. }
  237. return content;
  238. }
  239.  
  240. function parseMyContent(content, pattern) {
  241. var parse, response;
  242. content = content.replace(/(\r\n|\n|\r)/gm, '');
  243. parse = content.match(pattern);
  244. if (parse) {
  245. response = (/g$/.test(pattern)) ? parse : parse[1];
  246. }
  247. return response;
  248. }
  249.  
  250. function getMyContent(url, pattern, data, headers) {
  251. var urle, xhr, response;
  252. if (data) {
  253. if (typeof data === 'object') data = JSON.stringify(data);
  254. urle = btoa(url + data + JSON.stringify(headers));
  255. if (!sources[urle]) {
  256. //console.log('SaveTube: POST\nURL: ' + url + '\nData: ' + data + '\nHeaders: ' + JSON.stringify(headers));
  257. xhr = new XMLHttpRequest();
  258. xhr.open('POST', url, false);
  259. if (data.indexOf('{') != -1) {
  260. xhr.setRequestHeader('Content-Type', 'application/json');
  261. }
  262. else {
  263. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  264. }
  265. if (headers) {
  266. if (headers['withCredentials']) {
  267. xhr.withCredentials = true;
  268. delete headers['withCredentials']
  269. }
  270. for (var header in headers) {
  271. xhr.setRequestHeader(header, headers[header]);
  272. }
  273. }
  274. try {
  275. xhr.send(data);
  276. }
  277. catch(e) {
  278. }
  279. sources[urle] = (xhr.responseText) ? xhr.responseText : xhr.responseXML;
  280. }
  281. }
  282. else {
  283. urle = btoa(url + JSON.stringify(headers));
  284. if (!sources[urle]) {
  285. //console.log('SaveTube: GET\nURL: ' + url + '\nHeaders: ' + JSON.stringify(headers));
  286. xhr = new XMLHttpRequest();
  287. xhr.open('GET', url, false);
  288. if (headers) {
  289. if (headers['withCredentials']) {
  290. xhr.withCredentials = true;
  291. delete headers['withCredentials']
  292. }
  293. for (var header in headers) {
  294. xhr.setRequestHeader(header, headers[header]);
  295. }
  296. }
  297. try {
  298. xhr.send();
  299. }
  300. catch(e) {
  301. }
  302. sources[urle] = (xhr.responseText) ? xhr.responseText : xhr.responseXML;
  303. }
  304. }
  305. if (sources[urle]) {
  306. response = sources[urle];
  307. if (pattern) {
  308. response = parseMyContent(response, pattern);
  309. }
  310. }
  311. return response;
  312. }
  313.  
  314. function createMySaver() {
  315. /* The Panel */
  316. saver['saverPanel'] = createMyElement('div');
  317. styleMyElement(saver['saverPanel'], {position: 'fixed', fontFamily: 'sans-serif', fontSize: '10px', minHeight: panelHeight + 'px', lineHeight: panelHeight + 'px', backgroundColor: '#FFFFFF', padding: '0px 10px 5px 10px', bottom: '0px', right: '25px', zIndex: '2000000000', borderTop: '1px solid #CCCCCC', borderLeft: '1px solid #CCCCCC', borderRight: '1px solid #CCCCCC', borderRadius: '5px 5px 0px 0px', textAlign: 'center', boxSizing: 'content-box'});
  318. appendMyElement(page.body, saver['saverPanel']);
  319.  
  320. /* Panel Hide/Show Toggle Button */
  321. saver['buttonHide'] = createMyElement('div', {title: '{Hide/Show: click to hide/show this panel}'}, 'click', function() {
  322. toggleMySaver();
  323. });
  324. styleMyElement(saver['buttonHide'], {width: '0px', height: '0px', display: 'inline-block', borderTop: '8px solid transparent', borderBottom: '8px solid transparent', borderLeft: '15px solid #777777', borderRight: '0px solid #777777', lineHeight: 'normal', verticalAlign: 'middle', marginLeft: '5px', marginRight: '10px', cursor: 'pointer', boxSizing: 'content-box'});
  325. appendMyElement(saver['saverPanel'], saver['buttonHide']);
  326.  
  327. /* Panel Logo */
  328. saver['panelLogo'] = createMyElement('div', {title: '{SaveTube: click to visit the script wesite}', textContent: userscript}, 'click', function() {
  329. page.win.location.href = website;
  330. });
  331. styleMyElement(saver['panelLogo'], {display: 'inline-block', color: '#32D132', fontSize: '14px', fontWeight: 'bold', border: '1px solid #32D132', borderRadius: '3px', padding: '0px 4px', marginRight: '10px', lineHeight: 'normal', verticalAlign: 'middle', cursor: 'pointer', boxSizing: 'content-box'});
  332. appendMyElement(saver['saverPanel'], saver['panelLogo']);
  333.  
  334. /* Warnings */
  335. if (saver['warnMess']) {
  336. saver['saverMessage'] = createMyElement('div');
  337. styleMyElement(saver['saverMessage'], {display: 'inline-block', fontSize: '12px', color: '#AD0000'});
  338. appendMyElement(saver['saverPanel'], saver['saverMessage']);
  339. if (saver['warnContent']) showMyMessage(saver['warnMess'], saver['warnContent']);
  340. else showMyMessage(saver['warnMess']);
  341. return;
  342. }
  343.  
  344. /* Panel Video Menu */
  345. saver['videoMenu'] = createMyElement('select', {title: '{Videos: select the video format for download}'}, 'change', function() {
  346. saver['videoSave'] = this.value;
  347. if (saver['isShowingLink']) {
  348. cleanMyElement(saver['buttonSaveLink'], false);
  349. saver['isShowingLink'] = false;
  350. }
  351. if (option['autosave']) {
  352. saveMyVideo();
  353. }
  354. });
  355. styleMyElement(saver['videoMenu'], {display: 'inline-block', width: 'auto', height: '20px', fontFamily: 'inherit', fontSize: '14px', fontWeight: 'bold', padding: '0px 3px', overflow: 'hidden', border: '1px solid #CCCCCC', color: '#777777', backgroundColor: '#FFFFFF', lineHeight: 'normal', verticalAlign: 'middle', cursor: 'pointer', boxSizing: 'content-box'});
  356. appendMyElement(saver['saverPanel'], saver['videoMenu']);
  357. if (feature['openpagelink']) {
  358. saver['videoList']['Page Link'] = page.url;
  359. }
  360. var videosProgressive = [];
  361. var videosAdaptiveHLS = [];
  362. var videosAdaptiveDASHVideo = [];
  363. var videosAdaptiveDASHAudio = [];
  364. var videosAdaptiveDASHMuxed = [];
  365. var videosExtra = [];
  366. for (var videoCode in saver['videoList']) {
  367. if (videoCode.indexOf('Video') != -1) videosAdaptiveDASHVideo.push(videoCode);
  368. else if (videoCode.indexOf('Audio') != -1) videosAdaptiveDASHAudio.push(videoCode);
  369. else if (saver['videoList'][videoCode] == 'DASH') videosAdaptiveDASHMuxed.push(videoCode);
  370. else if (videoCode.indexOf('M3U8') != -1) videosAdaptiveHLS.push(videoCode);
  371. else if (videoCode.indexOf('MP4') != -1 || videoCode.indexOf('WebM') != -1) videosProgressive.push(videoCode);
  372. else videosExtra.push(videoCode);
  373. }
  374. if (videosProgressive.length > 0) {
  375. for (var i = 0; i < videosProgressive.length; i++) {
  376. saver['videoItem'] = createMyElement('option', {value: videosProgressive[i], textContent: videosProgressive[i]});
  377. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', cursor: 'pointer'});
  378. appendMyElement(saver['videoMenu'], saver['videoItem']);
  379. }
  380. }
  381. if (videosAdaptiveHLS.length > 0) {
  382. saver['videoItem'] = createMyElement('option', {value: 'HLS', textContent: 'HLS'});
  383. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', color: '#FF0000'});
  384. saver['videoItem'].disabled = 'disabled';
  385. appendMyElement(saver['videoMenu'], saver['videoItem']);
  386. for (var i = 0; i < videosAdaptiveHLS.length; i++) {
  387. saver['videoItem'] = createMyElement('option', {value: videosAdaptiveHLS[i], textContent: videosAdaptiveHLS[i]});
  388. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', cursor: 'pointer'});
  389. appendMyElement(saver['videoMenu'], saver['videoItem']);
  390. }
  391. }
  392. if (videosAdaptiveDASHVideo.length > 0) {
  393. saver['videoItem'] = createMyElement('option', {value: 'DASH (Video Only)', textContent: 'DASH (Video Only)'});
  394. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', color: '#FF0000'});
  395. saver['videoItem'].disabled = 'disabled';
  396. appendMyElement(saver['videoMenu'], saver['videoItem']);
  397. for (var i = 0; i < videosAdaptiveDASHVideo.length; i++) {
  398. saver['videoItem'] = createMyElement('option', {value: videosAdaptiveDASHVideo[i], textContent: videosAdaptiveDASHVideo[i]});
  399. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', cursor: 'pointer'});
  400. appendMyElement(saver['videoMenu'], saver['videoItem']);
  401. }
  402. }
  403. if (videosAdaptiveDASHAudio.length > 0) {
  404. saver['videoItem'] = createMyElement('option', {value: 'DASH (Audio Only)', textContent: 'DASH (Audio Only)'});
  405. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', color: '#FF0000'});
  406. saver['videoItem'].disabled = 'disabled';
  407. appendMyElement(saver['videoMenu'], saver['videoItem']);
  408. for (var i = 0; i < videosAdaptiveDASHAudio.length; i++) {
  409. saver['videoItem'] = createMyElement('option', {value: videosAdaptiveDASHAudio[i], textContent: videosAdaptiveDASHAudio[i]});
  410. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', cursor: 'pointer'});
  411. appendMyElement(saver['videoMenu'], saver['videoItem']);
  412. }
  413. }
  414. if (videosAdaptiveDASHMuxed.length > 0) {
  415. feature['savedash'] = true;
  416. if (option['savedash']) {
  417. saver['videoItem'] = createMyElement('option', {value: 'DASH (Video With Audio)', textContent: 'DASH (Video With Audio)'});
  418. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', color: '#FF0000'});
  419. saver['videoItem'].disabled = 'disabled';
  420. appendMyElement(saver['videoMenu'], saver['videoItem']);
  421. for (var i = 0; i < videosAdaptiveDASHMuxed.length; i++) {
  422. saver['videoItem'] = createMyElement('option', {value: videosAdaptiveDASHMuxed[i], textContent: videosAdaptiveDASHMuxed[i]});
  423. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', cursor: 'pointer'});
  424. appendMyElement(saver['videoMenu'], saver['videoItem']);
  425. }
  426. }
  427. else {
  428. for (var videoCode in saver['videoList']) {
  429. if (saver['videoList'][videoCode] == 'DASH') delete saver['videoList'][videoCode];
  430. }
  431. }
  432. }
  433. if (videosExtra.length > 0) {
  434. saver['videoItem'] = createMyElement('option', {value: 'Extra', textContent: 'Extra'});
  435. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', color: '#FF0000'});
  436. saver['videoItem'].disabled = 'disabled';
  437. appendMyElement(saver['videoMenu'], saver['videoItem']);
  438. for (var i = 0; i < videosExtra.length; i++) {
  439. saver['videoItem'] = createMyElement('option', {value: videosExtra[i], textContent: videosExtra[i]});
  440. styleMyElement(saver['videoItem'], {fontSize: '14px', fontWeight: 'bold', cursor: 'pointer'});
  441. appendMyElement(saver['videoMenu'], saver['videoItem']);
  442. }
  443. }
  444.  
  445. /* Panel Options Button */
  446. saver['buttonOptions'] = createMyElement('div', {title: '{Options: click to show the available options}'}, 'click', function() {
  447. if (saver['showsOptions']) {
  448. saver['showsOptions'] = false;
  449. styleMyElement(saver['optionsContent'], {display: 'none'})
  450. }
  451. else {
  452. saver['showsOptions'] = true;
  453. styleMyElement(saver['optionsContent'], {display: 'block'})
  454. }
  455. });
  456. styleMyElement(saver['buttonOptions'], {width: '1px', height: '8px', borderTop: '4px dotted #777777', borderBottom: '4px dotted #777777', borderLeft: '4px dotted #777777', display: 'inline-block', lineHeight: 'normal', verticalAlign: 'middle', marginLeft: '20px', cursor: 'pointer', boxSizing: 'content-box'});
  457. appendMyElement(saver['saverPanel'], saver['buttonOptions']);
  458.  
  459. /* Panel Save Button */
  460. saver['buttonSave'] = createMyElement('div', {title: '{Save: click to download the selected video format}'}, 'click', function() {
  461. saveMyVideo();
  462. });
  463. styleMyElement(saver['buttonSave'], {width: '0px', height: '0px', display: 'inline-block', borderLeft: '8px solid transparent', borderRight: '8px solid transparent', borderTop: '15px solid #777777', borderBottom: '0px solid #777777', lineHeight: 'normal', verticalAlign: 'middle', marginTop: '1px', marginLeft: '20px', cursor: 'pointer', boxSizing: 'content-box'});
  464. appendMyElement(saver['saverPanel'], saver['buttonSave']);
  465.  
  466. /* Panel Save Button Link */
  467. saver['buttonSaveLink'] = createMyElement('div', {title: '{Save: right click & save as to download the selected video format}'});
  468. styleMyElement(saver['buttonSaveLink'], {display: 'inline-block', color: '#777777', fontSize: '14px', fontWeight: 'bold', lineHeight: 'normal', verticalAlign: 'middle', marginLeft: '5px', marginBottom: '2px', boxSizing: 'content-box'});
  469. appendMyElement(saver['saverPanel'], saver['buttonSaveLink']);
  470.  
  471. /* Disable Features */
  472. if (saver['videoDefinitions'].length < 2) feature['definition'] = false;
  473. if (saver['videoContainers'].length < 2) feature['container'] = false;
  474.  
  475. /* Select The Video */
  476. if (feature['definition'] || feature['container'] || feature['openpagelink']) {
  477. if (!option['definition'] || saver['videoDefinitions'].indexOf(option['definition']) == -1) option['definition'] = saver['videoSave'].replace(/Definition.*/, 'Definition');
  478. if (!option['container'] || saver['videoContainers'].indexOf(option['container']) == -1) option['container'] = saver['videoSave'].replace(/.*\s/, '');
  479. selectMyVideo();
  480. }
  481.  
  482. /* Save The Video On Autosave */
  483. if (option['autosave']) saveMyVideo();
  484.  
  485. /* Panel Options */
  486. saver['optionsContent'] = createMyElement('div');
  487. styleMyElement(saver['optionsContent'], {display: 'none', fontSize: '14px', fontWeight: 'bold', padding: '10px', textAlign: 'center', boxSizing: 'content-box'});
  488. appendMyElement(saver['saverPanel'], saver['optionsContent']);
  489.  
  490. /* Options Object => option: [label, options, new line, change video] */
  491. var options = {
  492. 'definition': ['Definition', saver['videoDefinitions'], true, true],
  493. 'container': ['Container', saver['videoContainers'], false, true],
  494. 'openpagelink': ['Open Page Link', ['On', 'Off'], true, true],
  495. 'autosave': ['Autosave', ['On', 'Off'], true, true],
  496. 'showsavelink': ['Show Save Link', ['On', 'Off'], false, true],
  497. 'savedash': ['Save DASH (Video With Audio)', ['On', 'Off'], true, false]
  498. };
  499.  
  500. /* Options */
  501. var optionsBox, optionBox, optionLabel, optionMenu, optionMenuItem;
  502. for (var o in options) {
  503. if (feature[o] === false) continue;
  504. if (options[o][2]) {
  505. optionsBox = createMyElement('div');
  506. styleMyElement(optionsBox, {display: 'block', padding: '5px 0px 5px 0px'});
  507. appendMyElement(saver['optionsContent'], optionsBox);
  508. }
  509. optionBox = createMyElement('div');
  510. styleMyElement(optionBox, {display: 'inline-block'});
  511. optionLabel = createMyElement('div', {textContent: options[o][0]});
  512. styleMyElement(optionLabel, {display: 'inline-block', color: '#777777', marginRight: '10px', verticalAlign: 'middle'});
  513. optionMenu = createMyElement('select', {id: 'savetube-option-' + o}, 'change', function() {
  514. var id = this.id.replace('savetube-option-', '');
  515. if (this.value == 'On' || this.value == 'Off') {
  516. option[id] = (this.value == 'On') ? true : false;
  517. }
  518. else {
  519. option[id] = this.value;
  520. }
  521. setMyOptions(id, option[id]);
  522. if (options[id][3]) {
  523. if (saver['isShowingLink']) {
  524. cleanMyElement(saver['buttonSaveLink'], false);
  525. saver['isShowingLink'] = false;
  526. }
  527. selectMyVideo();
  528. if (option['autosave']) {
  529. saveMyVideo();
  530. }
  531. }
  532. });
  533. styleMyElement(optionMenu, {display: 'inline-block', width: 'auto', height: '20px', color: '#777777', backgroundColor: '#FFFFFF', border: '1px solid #CCCCCC', fontFamily: 'inherit', fontSize: '14px', fontWeight: 'bold', marginRight: '10px', verticalAlign: 'middle'});
  534. appendMyElement(optionBox, optionLabel);
  535. appendMyElement(optionBox, optionMenu);
  536. appendMyElement(optionsBox, optionBox);
  537. for (var i = 0; i < options[o][1].length; i++) {
  538. optionMenuItem = createMyElement('option', {value: options[o][1][i], textContent: options[o][1][i]});
  539. styleMyElement(optionMenuItem, {fontSize: '14px', fontWeight: 'bold', cursor: 'pointer'});
  540. appendMyElement(optionMenu, optionMenuItem);
  541. }
  542. if (optionMenu.value == 'On' || optionMenu.value == 'Off') {
  543. if (option[o]) optionMenu.value = 'On';
  544. else optionMenu.value = 'Off';
  545. }
  546. else {
  547. optionMenu.value = option[o];
  548. }
  549. }
  550.  
  551. /* Hide */
  552. if (option['hidden']) {
  553. toggleMySaver('hide');
  554. }
  555. }
  556.  
  557. function toggleMySaver(toggle) {
  558. if (toggle == 'hide') {
  559. styleMyElement(saver['saverPanel'], {right: '-' + (saver['saverPanel'].offsetWidth - 40) + 'px', backgroundColor: 'transparent', borderColor: '#777777'});
  560. styleMyElement(saver['buttonHide'], {borderTop: '8px solid transparent', borderBottom: '8px solid transparent', borderLeft: '0px solid #777777', borderRight: '15px solid #777777'});
  561. }
  562. else {
  563. if (option['hidden']) {
  564. styleMyElement(saver['saverPanel'], {right: '25px', backgroundColor: '#FFFFFF', borderColor: '#CCCCCC', transition: 'right 2s, background-color 5s, border-color 5s'});
  565. styleMyElement(saver['buttonHide'], {borderTop: '8px solid transparent', borderBottom: '8px solid transparent', borderLeft: '15px solid #777777', borderRight: '0px solid #777777'});
  566. option['hidden'] = false;
  567. }
  568. else {
  569. styleMyElement(saver['saverPanel'], {right: '-' + (saver['saverPanel'].offsetWidth - 40) + 'px', backgroundColor: 'transparent', borderColor: '#777777', transition: 'right 2s, background-color 5s, border-color 5s'});
  570. styleMyElement(saver['buttonHide'], {borderTop: '8px solid transparent', borderBottom: '8px solid transparent', borderLeft: '0px solid #777777', borderRight: '15px solid #777777'});
  571. option['hidden'] = true;
  572. }
  573. setMyOptions('hidden', option['hidden']);
  574. }
  575. }
  576.  
  577. function setMyOptions(key, value) {
  578. key = page.site + '_' + userscript.toLowerCase() + '_' + key;
  579. try {
  580. localStorage.setItem(key, value);
  581. if (localStorage.getItem(key) == value) return;
  582. else throw false;
  583. }
  584. catch(e) {
  585. var date = new Date();
  586. date.setTime(date.getTime() + (356*24*60*60*1000));
  587. var expires = '; expires=' + date.toGMTString();
  588. page.doc.cookie = key + '=' + value + expires + '; path=/';
  589. }
  590. }
  591.  
  592. function getMyOptions() {
  593. for (var opt in option) {
  594. var key = page.site + '_' + userscript.toLowerCase() + '_' + opt;
  595. try {
  596. if (localStorage.getItem(key)) {
  597. option[opt] = localStorage.getItem(key);
  598. continue;
  599. }
  600. else throw false;
  601. }
  602. catch(e) {
  603. var cookies = page.doc.cookie.split(';');
  604. for (var i=0; i < cookies.length; i++) {
  605. var cookie = cookies[i];
  606. while (cookie.charAt(0) == ' ') cookie = cookie.substring(1, cookie.length);
  607. option[opt] = (cookie.indexOf(key) == 0) ? cookie.substring(key.length + 1, cookie.length) : option[opt];
  608. }
  609. }
  610. }
  611. var boolOptions = ['openpagelink', 'autosave', 'showsavelink', 'savedash', 'hidden'];
  612. for (var i = 0; i < boolOptions.length; i++) {
  613. option[boolOptions[i]] = (option[boolOptions[i]] === true || option[boolOptions[i]] == 'true') ? true : false;
  614. }
  615. }
  616.  
  617. function selectMyVideo() {
  618. if (option['openpagelink']) {
  619. saver['videoSave'] = 'Page Link';
  620. }
  621. else {
  622. var vdoCont = (option['container'] != 'Any') ? [option['container']] : saver['videoContainers'];
  623. var vdoDef = saver['videoDefinitions'];
  624. var vdoList = {};
  625. for (var vC = 0; vC < vdoCont.length; vC++) {
  626. if (vdoCont[vC] != 'Any') {
  627. for (var vD = 0; vD < vdoDef.length; vD++) {
  628. var format = vdoDef[vD] + ' ' + vdoCont[vC];
  629. if (!vdoList[vdoDef[vD]]) {
  630. for (var vL in saver['videoList']) {
  631. if (vL == format) {
  632. vdoList[vdoDef[vD]] = vL;
  633. break;
  634. }
  635. }
  636. }
  637. }
  638. }
  639. }
  640. var vdoDef2 = [];
  641. var keepDef = false;
  642. for (var vD = 0; vD < vdoDef.length; vD++) {
  643. if (vdoDef[vD] == option['definition'] && keepDef == false) keepDef = true;
  644. if (keepDef == true) vdoDef2.push(vdoDef[vD])
  645. }
  646. for (var vD = 0; vD < vdoDef2.length; vD++) {
  647. if (vdoList[vdoDef2[vD]]) {
  648. saver['videoSave'] = vdoList[vdoDef2[vD]];
  649. break;
  650. }
  651. }
  652. }
  653. saver['videoMenu'].value = saver['videoSave'];
  654. }
  655.  
  656. function saveMyVideo() {
  657. var vdoURL = saver['videoList'][saver['videoSave']];
  658. var vdoDef = ' (' + saver['videoSave'].split(' ').slice(0, -1).join('').match(/[A-Z]/g).join('') + ')';
  659. var vdoExt = saver['videoSave'].split(' ').slice(-1).join('');
  660. var vdoTle = (saver['videoTitle']) ? saver['videoTitle'] + vdoDef : page.url.replace(/https?:\/\//, '').replace(/[^0-9a-zA-Z]/g, '-') + vdoDef;
  661. if (saver['videoSave'] == 'Page Link' || vdoURL == 'DASH' || (vdoExt == 'M3U8' && !option['showsavelink'])) {
  662. var vdoVdo, vdoAdo;
  663. if (saver['videoSave'] == 'Page Link' || vdoExt == 'M3U8') {
  664. vdoVdo = vdoURL;
  665. vdoAdo = '';
  666. vdoDef = '';
  667. }
  668. else {
  669. if (saver['videoSave'].indexOf('MP4') != -1) {
  670. vdoVdo = saver['videoList'][saver['videoSave'].replace('MP4', 'Video MP4')];
  671. vdoAdo = saver['videoList']['Medium Bitrate Audio MP4'] || saver['videoList'][saver['videoSave'].replace('MP4', 'Audio MP4')];
  672. }
  673. else {
  674. vdoVdo = saver['videoList'][saver['videoSave'].replace('WebM', 'Video WebM')];
  675. vdoAdo = saver['videoList']['High Bitrate Audio WebM'] || saver['videoList']['Medium Bitrate Audio WebM'] || saver['videoList']['Medium Bitrate Audio MP4'];
  676. }
  677. }
  678. page.win.location.href = 'savetube:' + vdoTle + 'SEPARATOR' + vdoVdo + 'SEPARATOR' + vdoAdo;
  679. }
  680. else {
  681. if (page.site == 'youtube' && saver['videoSave'] == 'High Definition MP4') {
  682. vdoURL = vdoURL + '&title=' + vdoTle;
  683. }
  684. var vdoLnk = createMyElement('a', {href: vdoURL, target: '_blank', textContent: '[Link]'});
  685. styleMyElement(vdoLnk, {color: '#777777', textDecoration: 'underline'});
  686. if (option['showsavelink'] || vdoExt == 'M3U8') {
  687. if (!saver['isShowingLink']) {
  688. appendMyElement(saver['buttonSaveLink'], vdoLnk);
  689. saver['isShowingLink'] = true;
  690. if (page.site == 'youtube' && saver['videoSave'] == 'High Definition MP4') {
  691. page.win.location.href = vdoURL;
  692. }
  693. }
  694. }
  695. else {
  696. if (!saver['isSaving']) {
  697. if (page.site == 'youtube' && saver['videoSave'] == 'High Definition MP4') {
  698. page.win.location.href = vdoURL;
  699. }
  700. else {
  701. if (page.win.URL && page.win.URL.createObjectURL) {
  702. saver['isSaving'] = true;
  703. styleMyElement(saver['buttonSave'], {borderBottomWidth: '1px', cursor: 'none'});
  704. var vdoLnkBlob, vdoBlob, vdoBlobLnk;
  705. vdoLnkBlob = createMyElement('a');
  706. styleMyElement(vdoLnkBlob, {display: 'none'});
  707. appendMyElement(page.body, vdoLnkBlob);
  708. var XHRequest = new XMLHttpRequest();
  709. XHRequest.open('GET', vdoURL);
  710. XHRequest.responseType = 'arraybuffer';
  711. XHRequest.onload = function() {
  712. if (this.status === 200 && this.response) {
  713. vdoBlob = new Blob([this.response], {type: mediatypes[vdoExt]});
  714. vdoBlobLnk = page.win.URL.createObjectURL(vdoBlob);
  715. modifyMyElement(vdoLnkBlob, {href: vdoBlobLnk, target: '_blank', download: vdoTle + '.' + vdoExt.toLowerCase()});
  716. vdoLnkBlob.click();
  717. page.win.URL.revokeObjectURL(vdoBlobLnk);
  718. removeMyElement(page.body, vdoLnkBlob);
  719. saver['isSaving'] = false;
  720. styleMyElement(saver['buttonSave'], {borderBottomWidth: '0px', cursor: 'pointer'});
  721. }
  722. else {
  723. saver['isSaving'] = false;
  724. styleMyElement(saver['buttonSave'], {borderBottomWidth: '0px', cursor: 'pointer'});
  725. if (!saver['isShowingLink']) {
  726. appendMyElement(saver['buttonSaveLink'], vdoLnk);
  727. saver['isShowingLink'] = true;
  728. }
  729. }
  730. }
  731. XHRequest.onerror = function() {
  732. saver['isSaving'] = false;
  733. styleMyElement(saver['buttonSave'], {borderBottomWidth: '0px', cursor: 'pointer'});
  734. if (!saver['isShowingLink']) {
  735. appendMyElement(saver['buttonSaveLink'], vdoLnk);
  736. saver['isShowingLink'] = true;
  737. }
  738. }
  739. XHRequest.send();
  740. }
  741. else {
  742. appendMyElement(saver['buttonSaveLink'], vdoLnk);
  743. saver['isShowingLink'] = true;
  744. }
  745. }
  746. }
  747. }
  748. }
  749. }
  750.  
  751. function showMyMessage(cause, content) {
  752. if (cause == '!content') {
  753. modifyMyElement(saver['saverMessage'], {innerHTML: 'Couldn\'t get the videos content. Please report it <a href="' + contact + '" style="color:#00892C">here</a>.'});
  754. }
  755. else if (cause == '!videos') {
  756. modifyMyElement(saver['saverMessage'], {innerHTML: 'Couldn\'t get any video. Please report it <a href="' + contact + '" style="color:#00892C">here</a>.'});
  757. }
  758. else if (cause == '!support') {
  759. modifyMyElement(saver['saverMessage'], {innerHTML: 'This video uses the RTMP protocol which is not supported.'});
  760. }
  761. else if (cause == 'embed') {
  762. modifyMyElement(saver['saverMessage'], {innerHTML: 'This is an embedded video. You can get it <a href="' + content + '" style="color:#00892C">here</a>.'});
  763. }
  764. else if (cause == 'other') {
  765. modifyMyElement(saver['saverMessage'], {innerHTML: content});
  766. }
  767. }
  768.  
  769.  
  770. // ==========Websites========== //
  771.  
  772. function SaveTube() {
  773.  
  774. // =====YouTube===== //
  775.  
  776. if (page.url.indexOf('youtube.com/watch') != -1) {
  777.  
  778. /* Video Availability */
  779. if (getMyContent(page.url, /"playabilityStatus":\{"status":"(ERROR|UNPLAYABLE|LIVE_STREAM_OFFLINE|LOGIN_REQUIRED)"/)) return;
  780.  
  781. /* Get Video ID */
  782. var ytVideoId = parseMyContent(page.url, /(?:\?|&)v=(.*?)(&|$)/);
  783.  
  784. /* Create Saver */
  785. var ytVideoTitle, ytVideoAuthor;
  786. var ytVideoList = {};
  787. var ytDefaultVideo = 'Low Definition MP4';
  788. function ytCreateSaver(data) {
  789. if (data) {
  790. saver = data;
  791. }
  792. else {
  793. saver = {
  794. 'videoList': ytVideoList,
  795. 'videoDefinitions': ['Ultra High Definition', 'Quad High Definition', 'Full High Definition', 'High Definition', 'Standard Definition', 'Low Definition', 'Very Low Definition'],
  796. 'videoContainers': ['MP4', 'WebM', 'M3U8', 'Any'],
  797. 'videoSave': ytDefaultVideo,
  798. 'videoTitle': ytVideoTitle
  799. };
  800. }
  801. createMySaver();
  802. }
  803.  
  804. /* Script */
  805. var ytScriptUrl;
  806. function ytGetScriptUrl() {
  807. if (!ytScriptUrl) {
  808. ytScriptUrl = getMyContent(page.url, /"js(?:Url)?":\s*"(.*?)"/);
  809. if (!ytScriptUrl) {
  810. ytScriptUrl = getMyContent(page.url.replace(/watch.*?v=/, 'embed/').replace(/&.*$/, ''), /"js(?:Url)?":\s*"(.*?)"/);
  811. }
  812. if (ytScriptUrl && ytScriptUrl.indexOf('//') == -1) {
  813. ytScriptUrl = page.win.location.protocol + '//' + page.win.location.hostname + ytScriptUrl;
  814. }
  815. if (!ytScriptUrl) {
  816. showMyMessage('other', 'Couldn\'t get the script link. Please report it <a href="' + contact + '" style="color:#00892C">here</a>.');
  817. }
  818. }
  819. }
  820.  
  821. /* Parameter Unscrambler */
  822. var ytUnscrambleParam = {};
  823. function ytGetUnscrambleParamFunc() {
  824. ytGetScriptUrl();
  825. var ytMainFuncName, ytMainFuncBody, ytExtraFuncName, ytExtraFuncBody;
  826. /* s */
  827. ytMainFuncName = getMyContent(ytScriptUrl, /[\w$]+&&\([\w$]+=([\w$]+)\(decodeURIComponent/);
  828. if (ytMainFuncName) {
  829. ytMainFuncBody = getMyContent(ytScriptUrl, new RegExp('(?:^|;)' + ytMainFuncName.replace(/\$/, '\\$') + '\\s*=\\s*function\\s*' + '\\s*\\(\\w+\\)\\s*\\{(.*?\\))\\};'));
  830. if (ytMainFuncBody) {
  831. ytExtraFuncName = parseMyContent(ytMainFuncBody, /;([\w$]+)[\.|\[]/);
  832. if (ytExtraFuncName) {
  833. ytExtraFuncBody = getMyContent(ytScriptUrl, new RegExp('var\\s+' + ytExtraFuncName.replace(/\$/, '\\$') + '=\\s*\\{(.*?)\\};'));
  834. if (ytExtraFuncBody) {
  835. ytMainFuncBody = 'var ' + ytExtraFuncName + '={' + ytExtraFuncBody + '};' + ytMainFuncBody;
  836. ytExtraFuncBody = getMyContent(ytScriptUrl, /use strict';(var.*?[\)|\]]),/);
  837. if (ytExtraFuncBody) {
  838. ytMainFuncBody = 'try {' + ytExtraFuncBody + ';' + ytMainFuncBody + '} catch(e) {return null}';
  839. ytUnscrambleParam['s'] = new Function(ytMainFuncBody.replace(/.*;return\s+(\w).*/, '$1'), ytMainFuncBody);
  840. }
  841. }
  842. }
  843. }
  844. }
  845. /* n */
  846. ytMainFuncName = getMyContent(ytScriptUrl, /(?:^|;)([\w$]+)=function\([\w$]+\)\s*\{var\s+\w=\w\.split\(\w\.slice/);
  847. if (!ytMainFuncName) {
  848. ytMainFuncName = getMyContent(ytScriptUrl, /(?:^|;)([\w$]+)=function\([\w$]+\)\s*\{var\s+\w=\w\[\w\[\d+\]\]\(\w\[\d+\]\)/);
  849. }
  850. if (ytMainFuncName) {
  851. ytMainFuncBody = getMyContent(ytScriptUrl, new RegExp('(?:^|;)' + ytMainFuncName.replace(/\$/, '\\$') + '\\s*=\\s*function\\s*' + '\\s*\\(\\w+\\)\\s*\\{(.*?\\))\\};'));
  852. if (ytMainFuncBody) {
  853. ytMainFuncBody = ytMainFuncBody.replace(/(\d+)?--(\d+)/, '$1- -$2').replace(/if\(typeof.*?;/, '');
  854. ytExtraFuncBody = getMyContent(ytScriptUrl, /use strict';(var.*?[\)|\]]),/);
  855. if (ytExtraFuncBody) {
  856. ytMainFuncBody = 'try {' + ytExtraFuncBody + ';' + ytMainFuncBody + '} catch(e) {return null}';
  857. ytUnscrambleParam['n'] = new Function(ytMainFuncBody.replace(/.*\+(\w)\}return.*/, '$1'), ytMainFuncBody);
  858. }
  859. }
  860. }
  861. }
  862.  
  863. /* Get Videos Content */
  864. var ytVideosContent = {};
  865. var ytVideosContentHLS;
  866. var ytVideoInfoUrl = page.win.location.protocol + '//' + page.win.location.hostname + '/youtubei/v1/player?prettyPrint=false';
  867. var ytVideoInfoClients = {
  868. 'MWEB': {
  869. 'clientName': 'MWEB',
  870. 'clientVersion': '2.20250311.03.00',
  871. 'userAgent': 'Mozilla/5.0 (iPad; CPU OS 16_7_10 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1,gzip(gfe)'
  872. },
  873. 'WEB_SAFARI': {
  874. 'clientName': 'WEB',
  875. 'clientVersion': '2.20250312.04.00',
  876. 'userAgent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15,gzip(gfe)'
  877. },
  878. 'IOS': {
  879. 'clientName': 'IOS',
  880. 'clientVersion': '20.10.4',
  881. 'userAgent': 'com.google.ios.youtube/20.10.4 (iPhone16,2; U; CPU iOS 18_3_2 like Mac OS X;)'
  882. },
  883. 'TV': {
  884. 'clientName': 'TVHTML5',
  885. 'clientVersion': '7.20250312.16.00',
  886. 'userAgent': 'Mozilla/5.0 (ChromiumStylePlatform) Cobalt/Version'
  887. },
  888. 'TV_EMBEDDED': {
  889. 'clientName': 'TVHTML5_SIMPLY_EMBEDDED_PLAYER',
  890. 'clientVersion': '2.0'
  891. }
  892. };
  893. var ytVideoInfoData = {'videoId': ytVideoId, 'context': {'client': null}, 'playbackContext': {'contentPlaybackContext': {'html5Preference': 'HTML5_PREF_WANTS'}}};
  894. var ytVideoInfoExtra = {'signatureTimestamp': 20068, 'visitorData': ''};
  895. var ytVideoInfoHeaders;
  896. function ytGetVideos(api, client, embed) {
  897. if (api) {
  898. ytVideoInfoData['context']['client'] = ytVideoInfoClients[client];
  899. if (embed) {
  900. ytVideoInfoData['context']['client']['clientScreen'] = 'EMBED';
  901. ytVideoInfoData['context']['thirdParty'] = {};
  902. ytVideoInfoData['context']['thirdParty']['embedUrl'] = 'https://www.youtube.com';
  903. }
  904. ytGetScriptUrl();
  905. ytVideoInfoExtra['signatureTimestamp'] = getMyContent(ytScriptUrl, /signatureTimestamp:(\d+)/);
  906. if (ytVideoInfoExtra['signatureTimestamp']) {
  907. ytVideoInfoData['playbackContext']['contentPlaybackContext']['signatureTimestamp'] = ytVideoInfoExtra['signatureTimestamp'];
  908. }
  909. ytVideoInfoExtra['visitorData'] = getMyContent(page.url, /"visitorData":\s*"(.*?)"/);
  910. if (ytVideoInfoClients[client]['userAgent']) {
  911. if (!ytVideoInfoHeaders) ytVideoInfoHeaders = {};
  912. ytVideoInfoHeaders['User-Agent'] = ytVideoInfoClients[client]['userAgent'];
  913. }
  914. if (ytVideoInfoExtra['visitorData']) {
  915. if (!ytVideoInfoHeaders) ytVideoInfoHeaders = {};
  916. ytVideoInfoHeaders['X-Goog-Visitor-Id'] = ytVideoInfoExtra['visitorData'];
  917. }
  918. ytVideosContent = getMyContent(ytVideoInfoUrl, null, ytVideoInfoData, ytVideoInfoHeaders && ytVideoInfoHeaders);
  919. }
  920. else {
  921. ytVideosContent = getMyContent(page.url, /ytInitialPlayerResponse\s*=\s*({.*?});/);
  922. }
  923. try {
  924. ytVideosContent = JSON.parse(ytVideosContent);
  925. if (!ytVideosContentHLS) ytVideosContentHLS = ytVideosContent['streamingData']['hlsManifestUrl'];
  926. if (ytVideosContent['videoDetails'] && ytVideosContent['videoDetails']['videoId'] != ytVideoId) {
  927. ytVideosContent = {};
  928. }
  929. if (ytVideosContent['videoDetails']) {
  930. ytVideoTitle = ytVideosContent['videoDetails']['title'];
  931. ytVideoAuthor = ytVideosContent['videoDetails']['author'];
  932. ytVideoTitle = ytVideoTitle + ' by ' + ytVideoAuthor;
  933. ytVideoTitle = cleanMyContent(ytVideoTitle, false, true);
  934. if (ytVideosContent['captions'] && ytVideosContent['captions']['playerCaptionsTracklistRenderer']) {
  935. ytSubtitlesContent = ytVideosContent['captions']['playerCaptionsTracklistRenderer']['captionTracks'];
  936. }
  937. }
  938. }
  939. catch(e) {
  940. ytVideosContent = {};
  941. }
  942. ytVideosContent = (ytVideosContent['streamingData']) ? ytVideosContent['streamingData'] : {};
  943. }
  944.  
  945. /* Get Videos */
  946. ytGetVideos(true, 'TV', false);
  947. if (!ytVideosContent['formats']) {
  948. ytGetVideos(true, 'MWEB', false);
  949. }
  950. if (!ytVideosContent['formats']) {
  951. ytGetVideos(true, 'TV_EMBEDDED', true);
  952. }
  953. if (ytVideosContent['formats']) {
  954. var ytVideoFormats = {
  955. '18': 'Low Definition MP4',
  956. '22': 'High Definition MP4',
  957. '43': 'Low Definition WebM',
  958. '133': 'Very Low Definition Video MP4',
  959. '134': 'Low Definition Video MP4',
  960. '135': 'Standard Definition Video MP4',
  961. '136': 'High Definition Video MP4',
  962. '137': 'Full High Definition Video MP4',
  963. '140': 'Medium Bitrate Audio MP4',
  964. '242': 'Very Low Definition Video WebM',
  965. '243': 'Low Definition Video WebM',
  966. '244': 'Standard Definition Video WebM',
  967. '247': 'High Definition Video WebM',
  968. '248': 'Full High Definition Video WebM',
  969. '249': 'Low Bitrate Audio WebM',
  970. '250': 'Medium Bitrate Audio WebM',
  971. '251': 'High Bitrate Audio WebM',
  972. '264': 'Quad High Definition Video MP4',
  973. '271': 'Quad High Definition Video WebM',
  974. '272': 'Ultra High Definition Video WebM',
  975. '298': 'High Definition Video MP4',
  976. '299': 'Full High Definition Video MP4',
  977. '302': 'High Definition Video WebM',
  978. '303': 'Full High Definition Video WebM',
  979. '308': 'Quad High Definition Video WebM',
  980. '313': 'Ultra High Definition Video WebM',
  981. '315': 'Ultra High Definition Video WebM',
  982. '333': 'Standard Definition Video WebM',
  983. '334': 'High Definition Video WebM',
  984. '335': 'Full High Definition Video WebM',
  985. '337': 'Ultra High Definition Video WebM'
  986. };
  987. var ytVideoFound = false;
  988. var ytVideos = (ytVideosContent['adaptiveFormats']) ? ytVideosContent['formats'].concat(ytVideosContent['adaptiveFormats']) : ytVideosContent['formats']
  989. var ytVideoParse, ytVideoCodeParse, ytVideoCode, myVideoCode, ytVideo, ytSParam, ytSParamName, ytNParam;
  990. if (ytVideos[0]['signatureCipher'] || ytVideos[0]['cipher'] || (ytVideos[0]['url'] && parseMyContent(ytVideos[0]['url'], /(?:&|&amp;)n=(.*?)(&|&amp;|$)/))) {
  991. ytGetUnscrambleParamFunc();
  992. }
  993. for (var i = 0; i < ytVideos.length; i++) {
  994. if (ytVideos[i]['signatureCipher'] || ytVideos[i]['cipher']) {
  995. ytVideo = ytVideos[i]['signatureCipher'] || ytVideos[i]['cipher'];
  996. ytVideo = cleanMyContent(ytVideo, true);
  997. ytVideoParse = ytVideo.match(/(.*)(url=.*$)/);
  998. if (ytVideoParse) {
  999. ytVideo = ytVideoParse[2] + '&' + ytVideoParse[1];
  1000. ytVideo = ytVideo.replace(/url=/, '').replace(/&$/, '');
  1001. }
  1002. ytSParam = parseMyContent(ytVideo, /&s=(.*?)(&|$)/);
  1003. if (ytSParam && ytUnscrambleParam['s']) {
  1004. ytSParam = ytUnscrambleParam['s'](ytSParam);
  1005. if (ytSParam) {
  1006. ytSParamName = parseMyContent(ytVideo, /&sp=(.*?)(&|$)/);
  1007. ytSParamName = (ytSParamName) ? ytSParamName : ((/&lsig=/.test(ytVideo)) ? 'sig' : 'signature');
  1008. ytVideo = ytVideo.replace(/&s=.*?(&|$)/, '&' + ytSParamName + '=' + ytSParam + '$1');
  1009. }
  1010. else ytVideo = '';
  1011. }
  1012. else ytVideo = '';
  1013. }
  1014. else {
  1015. ytVideo = ytVideos[i]['url'];
  1016. ytVideo = cleanMyContent(ytVideo, true);
  1017. if (/&sig=/.test(ytVideo) && !/&lsig=/.test(ytVideo)) {
  1018. ytVideo = ytVideo.replace(/&sig=/, '&signature=');
  1019. }
  1020. }
  1021. ytVideoCode = ytVideos[i]['itag'];
  1022. if (!ytVideoCode) continue;
  1023. myVideoCode = ytVideoFormats[ytVideoCode];
  1024. if (!myVideoCode) continue;
  1025. if (myVideoCode.indexOf('Video') != -1) {
  1026. if (ytVideo.indexOf('source=yt_otf') != -1) continue;
  1027. }
  1028. ytVideo = cleanMyContent(ytVideo, true);
  1029. ytNParam = parseMyContent(ytVideo, /&n=(.*?)(&|$)/);
  1030. if (ytNParam && ytUnscrambleParam['n']) {
  1031. ytNParam = ytUnscrambleParam['n'](ytNParam);
  1032. if (ytNParam) {
  1033. ytVideo = ytVideo.replace(/&n=.*?(&|$)/, '&n=' + ytNParam + '$1');
  1034. }
  1035. }
  1036. if (ytVideo.indexOf('ratebypass') == -1) ytVideo += '&ratebypass=yes';
  1037. if (ytVideo && ytVideo.indexOf('http') == 0) {
  1038. if (!ytVideoFound) ytVideoFound = true;
  1039. ytVideoList[myVideoCode] = ytVideo;
  1040. }
  1041. }
  1042. if (ytVideoFound) {
  1043. /* DASH */
  1044. if (ytVideoList['Medium Bitrate Audio MP4'] || ytVideoList['Medium Bitrate Audio WebM']) {
  1045. for (var myVideoCode in ytVideoList) {
  1046. if (myVideoCode.indexOf('Video') != -1) {
  1047. if (!ytVideoList[myVideoCode.replace(' Video', '')]) {
  1048. ytVideoList[myVideoCode.replace(' Video', '')] = 'DASH';
  1049. }
  1050. }
  1051. }
  1052. }
  1053. /* HLS */
  1054. if (!ytVideosContentHLS) {
  1055. ytGetVideos(true, 'IOS', false);
  1056. }
  1057. if (ytVideosContentHLS) {
  1058. ytVideoList["Multi Definition M3U8"] = ytVideosContentHLS;
  1059. }
  1060. ytVideosContentHLS = '';
  1061. ytGetVideos(true, 'WEB_SAFARI', false);
  1062. if (ytVideosContentHLS) {
  1063. var ytHLSFormats = {
  1064. '92': 'Very Low Definition M3U8',
  1065. '93': 'Low Definition M3U8',
  1066. '94': 'Standard Definition M3U8',
  1067. '95': 'High Definition M3U8',
  1068. '96': 'Full High Definition M3U8'
  1069. };
  1070. var ytHLSVideos, ytHLSVideo, ytVideoCode, myVideoCode;
  1071. ytHLSVideos = getMyContent(ytVideosContentHLS, /(http.*?m3u8)/g);
  1072. if (ytHLSVideos) {
  1073. for (var i = 0; i < ytHLSVideos.length; i++) {
  1074. ytHLSVideo = ytHLSVideos[i];
  1075. ytVideoCode = parseMyContent(ytHLSVideo, /\/itag\/(\d{1,3})\//);
  1076. if (ytVideoCode) {
  1077. myVideoCode = ytHLSFormats[ytVideoCode];
  1078. if (myVideoCode) {
  1079. ytVideoList[myVideoCode] = ytHLSVideo;
  1080. }
  1081. }
  1082. }
  1083. }
  1084. }
  1085. ytCreateSaver();
  1086. }
  1087. else {
  1088. ytCreateSaver({'warnMess': '!videos'});
  1089. }
  1090. }
  1091. else {
  1092. /* HLS */
  1093. if (!ytVideosContentHLS) {
  1094. ytGetVideos(true, 'IOS', false);
  1095. }
  1096. if (ytVideosContentHLS) {
  1097. ytVideoList["Multi Definition M3U8"] = ytVideosContentHLS;
  1098. ytDefaultVideo = 'Multi Definition M3U8';
  1099. ytVideosContentHLS = '';
  1100. ytGetVideos(true, 'WEB_SAFARI', false);
  1101. if (ytVideosContentHLS) {
  1102. var ytHLSFormats = {
  1103. '92': 'Very Low Definition M3U8',
  1104. '93': 'Low Definition M3U8',
  1105. '94': 'Standard Definition M3U8',
  1106. '95': 'High Definition M3U8',
  1107. '96': 'Full High Definition M3U8'
  1108. };
  1109. var ytHLSVideos, ytHLSVideo, ytVideoCode, myVideoCode;
  1110. ytHLSVideos = getMyContent(ytVideosContentHLS, /(http.*?m3u8)/g);
  1111. if (ytHLSVideos) {
  1112. for (var i = 0; i < ytHLSVideos.length; i++) {
  1113. ytHLSVideo = ytHLSVideos[i];
  1114. ytVideoCode = parseMyContent(ytHLSVideo, /\/itag\/(\d{1,3})\//);
  1115. if (ytVideoCode) {
  1116. myVideoCode = ytHLSFormats[ytVideoCode];
  1117. if (myVideoCode) {
  1118. ytVideoList[myVideoCode] = ytHLSVideo;
  1119. }
  1120. }
  1121. }
  1122. }
  1123. }
  1124. ytCreateSaver();
  1125. }
  1126. else {
  1127. ytCreateSaver({'warnMess': '!content'});
  1128. }
  1129. }
  1130.  
  1131. }
  1132.  
  1133. // =====DailyMotion===== //
  1134.  
  1135. else if (page.url.indexOf('dailymotion.com/video') != -1) {
  1136.  
  1137. /* Video Source */
  1138. var dmMetadataUrl = page.url.replace(/\/video\//, "/player/metadata/video/");
  1139.  
  1140. /* Video Availability */
  1141. if (getMyContent(dmMetadataUrl, /"error":\{"title":"(.*?)"/)) return;
  1142. if (getMyContent(dmMetadataUrl, /"error_title":"(.*?)"/)) return;
  1143.  
  1144. /* Get Video Title */
  1145. var dmVideoTitle = getMyContent(dmMetadataUrl, /"title":"((\\"|[^"])*?)"/);
  1146. if (dmVideoTitle) {
  1147. var dmVideoAuthor = getMyContent(dmMetadataUrl, /"screenname":"((\\"|[^"])*?)"/);
  1148. if (dmVideoAuthor) dmVideoTitle = dmVideoTitle + ' by ' + dmVideoAuthor;
  1149. dmVideoTitle = cleanMyContent(dmVideoTitle, false, true);
  1150. }
  1151.  
  1152. /* Get Videos Content */
  1153. var dmVideosContent = getMyContent(dmMetadataUrl, /"qualities":\{(.*?)\]\},/);
  1154.  
  1155. /* Get Videos */
  1156. if (dmVideosContent) {
  1157. var dmVideoFormats = {'auto': 'Low Definition MP4', '240': 'Very Low Definition MP4', '380': 'Low Definition MP4',
  1158. '480': 'Standard Definition MP4', '720': 'High Definition MP4', '1080': 'Full High Definition MP4'};
  1159. var dmVideoList = {};
  1160. var dmVideoFound = false;
  1161. var myVideoCode, dmVideo;
  1162. for (var dmVideoCode in dmVideoFormats) {
  1163. dmVideo = parseMyContent(dmVideosContent, new RegExp('"' + dmVideoCode + '".*?"type":"video.*?mp4","url":"(.*?)"'));
  1164. if (dmVideo) {
  1165. if (!dmVideoFound) dmVideoFound = true;
  1166. dmVideo = cleanMyContent(dmVideo, true);
  1167. myVideoCode = dmVideoFormats[dmVideoCode];
  1168. if (!dmVideoList[myVideoCode]) dmVideoList[myVideoCode] = dmVideo;
  1169. }
  1170. }
  1171. if (!dmVideoFound) {
  1172. var dmHLSManifest = parseMyContent(dmVideosContent, /"type":"application.*?mpegURL","url":"(.*?)"/);
  1173. if (dmHLSManifest) {
  1174. dmVideoFound = true;
  1175. dmHLSManifest = cleanMyContent(dmHLSManifest, true);
  1176. dmVideoList["Multi Definition M3U8"] = dmHLSManifest;
  1177. var dmHLSFormats = {
  1178. 'h264_aac_2': 'Low Definition M3U8',
  1179. 'h264_aac_hq_2': 'Standard Definition M3U8',
  1180. 'h264_aac_hd_2': 'High Definition M3U8',
  1181. };
  1182. var dmHLSVideos = getMyContent(dmHLSManifest, /(http.*?m3u8)/g);
  1183. var dmHLSVideo;
  1184. if (dmHLSVideos) {
  1185. for (var i = 0; i < dmHLSVideos.length; i++) {
  1186. dmHLSVideo = dmHLSVideos[i];
  1187. dmVideoCode = parseMyContent(dmHLSVideo, /mp4_(h264_aac_.*).m3u8/);
  1188. if (dmVideoCode) {
  1189. myVideoCode = dmHLSFormats[dmVideoCode];
  1190. if (myVideoCode) {
  1191. dmVideoList[myVideoCode] = dmHLSVideo;
  1192. }
  1193. }
  1194. }
  1195. }
  1196. }
  1197. }
  1198.  
  1199. if (dmVideoFound) {
  1200. /* Create Saver */
  1201. var dmDefaultVideo = 'Low Definition MP4';
  1202. if (!dmVideoList[dmDefaultVideo]) dmDefaultVideo = 'Low Definition M3U8';
  1203. saver = {
  1204. 'videoList': dmVideoList,
  1205. 'videoDefinitions': ['Full High Definition', 'High Definition', 'Standard Definition', 'Low Definition', 'Very Low Definition'],
  1206. 'videoContainers': ['MP4'],
  1207. 'videoSave': dmDefaultVideo,
  1208. 'videoTitle': dmVideoTitle
  1209. };
  1210. createMySaver();
  1211. }
  1212. else {
  1213. saver = {'warnMess': '!videos'};
  1214. createMySaver();
  1215. }
  1216. }
  1217. else {
  1218. saver = {'warnMess': '!content'};
  1219. createMySaver();
  1220. }
  1221.  
  1222. }
  1223.  
  1224. // =====Vimeo===== //
  1225.  
  1226. else if (page.url.indexOf('vimeo.com/') != -1) {
  1227.  
  1228. /* Page Type */
  1229. var viPageType = getMyContent(page.url, /meta\s+property="og:type"\s+content="(.*?)"/);
  1230. if (!viPageType || viPageType.indexOf('video') == -1) return;
  1231.  
  1232. /* Get Video Title */
  1233. var viVideoTitle;
  1234. if (viPageType.indexOf('video') != -1) {
  1235. viVideoTitle = getMyContent(page.url, /meta\s+property="og:title"\s+content="(.*?)"/);
  1236. }
  1237. else {
  1238. viVideoTitle = getMyContent(page.url, /"title":"((\\"|[^"])*?)"/);
  1239. }
  1240. if (viVideoTitle) {
  1241. viVideoTitle = viVideoTitle.replace(/\s*on\s*Vimeo$/, '');
  1242. var viVideoAuthor = getMyContent(page.url, /"display_name":"((\\"|[^"])*?)"/);
  1243. if (viVideoAuthor) viVideoTitle = viVideoTitle + ' by ' + viVideoAuthor;
  1244. viVideoTitle = cleanMyContent(viVideoTitle, false, true);
  1245. }
  1246.  
  1247. /* Get Content Source */
  1248. var viVideoSource = getMyContent(page.url, /config_url":"(.*?)"/);
  1249. if (viVideoSource) viVideoSource = cleanMyContent(viVideoSource, false);
  1250. else {
  1251. viVideoSource = getMyContent(page.url, /data-config-url="(.*?)"/);
  1252. if (viVideoSource) viVideoSource = viVideoSource.replace(/&amp;/g, '&');
  1253. else viVideoSource = getMyContent(page.url, /embedUrl":"(.*?)"/);
  1254. }
  1255.  
  1256. /* Get Videos Content */
  1257. var viVideosContent;
  1258. if (viVideoSource) {
  1259. viVideosContent = getMyContent(viVideoSource);
  1260. try {
  1261. viVideosContent = JSON.parse(viVideosContent);
  1262. if (viVideosContent['request'] && viVideosContent['request']['files']) {
  1263. viVideosContent = viVideosContent['request']['files'];
  1264. }
  1265. else {
  1266. viVideosContent = '';
  1267. }
  1268. }
  1269. catch(e) {
  1270. viVideosContent = '';
  1271. }
  1272. if (!viVideosContent) {
  1273. viVideosContent = getMyContent(viVideoSource, null, null, {'withCredentials':true});
  1274. try {
  1275. viVideosContent = JSON.parse(viVideosContent);
  1276. if (viVideosContent['request'] && viVideosContent['request']['files']) {
  1277. viVideosContent = viVideosContent['request']['files'];
  1278. }
  1279. else {
  1280. viVideosContent = '';
  1281. }
  1282. }
  1283. catch(e) {
  1284. viVideosContent = '';
  1285. }
  1286. }
  1287. if (!viVideosContent) {
  1288. var viAPIToken = getMyContent('https://vimeo.com/_next/jwt', /"token":"((\\"|[^"])*?)"/, null, {'X-Requested-With':'XMLHttpRequest'});
  1289. var viAPIUrl = '';
  1290. if (page.url.replace(/\/$/, '').split('/').length > 4) {
  1291. viAPIUrl = 'https://api.vimeo.com/videos/' + page.url.split('/')[3] + ':' + page.url.split('/')[4] + '?fields=config_url';
  1292. }
  1293. else {
  1294. viAPIUrl = 'https://api.vimeo.com/videos/' + page.url.split('/')[3] + '?fields=config_url';
  1295. }
  1296. if (viAPIToken) {
  1297. viVideoSource = getMyContent(viAPIUrl, /config_url":\s*"(.*?)"/, null, {'Authorization':'jwt ' + viAPIToken});
  1298. if (viVideoSource) {
  1299. viVideoSource = cleanMyContent(viVideoSource, false);
  1300. viVideosContent = getMyContent(viVideoSource);
  1301. try {
  1302. viVideosContent = JSON.parse(viVideosContent);
  1303. if (viVideosContent['request'] && viVideosContent['request']['files']) {
  1304. viVideosContent = viVideosContent['request']['files'];
  1305. }
  1306. else {
  1307. viVideosContent = '';
  1308. }
  1309. }
  1310. catch(e) {
  1311. viVideosContent = '';
  1312. }
  1313. if (!viVideosContent) {
  1314. viVideosContent = getMyContent(viVideoSource, null, null, {'withCredentials':true});
  1315. try {
  1316. viVideosContent = JSON.parse(viVideosContent);
  1317. if (viVideosContent['request'] && viVideosContent['request']['files']) {
  1318. viVideosContent = viVideosContent['request']['files'];
  1319. }
  1320. else {
  1321. viVideosContent = '';
  1322. }
  1323. }
  1324. catch(e) {
  1325. viVideosContent = '';
  1326. }
  1327. }
  1328. }
  1329. }
  1330. }
  1331. }
  1332.  
  1333. /* Get Videos */
  1334. if (viVideosContent) {
  1335. var viVideoFormats = {'1440p': 'Quad High Definition MP4', '1080p': 'Full High Definition MP4', '720p': 'High Definition MP4', '540p': 'Standard Definition MP4',
  1336. '414': 'Standard Definition MP4', '480p': 'Standard Definition MP4', '360p': 'Low Definition MP4', '270p': 'Very Low Definition MP4', '240p': 'Very Low Definition MP4'};
  1337. var viVideoList = {};
  1338. var viVideoFound = false;
  1339. var viVideo, myVideoCode;
  1340. if (viVideosContent['progressive']) {
  1341. var viVideos = viVideosContent['progressive'];
  1342. for (var i = 0; i < viVideos.length; i++) {
  1343. for (var viVideoCode in viVideoFormats) {
  1344. if (viVideos[i]['quality'] == viVideoCode || viVideos[i]['height'] == viVideoCode) {
  1345. viVideo = viVideos[i]['url'];
  1346. if (viVideo) {
  1347. if (!viVideoFound) viVideoFound = true;
  1348. myVideoCode = viVideoFormats[viVideoCode];
  1349. viVideoList[myVideoCode] = viVideo;
  1350. }
  1351. }
  1352. }
  1353. }
  1354. }
  1355. if (viVideosContent['hls']) {
  1356. if (viVideosContent['hls']['cdns']) {
  1357. if (viVideosContent['hls']['cdns']['akfire_interconnect_quic']) {
  1358. viVideoList["Multi Definition M3U8"] = viVideosContent['hls']['cdns']['akfire_interconnect_quic']['url'];
  1359. if (!viVideoFound) viVideoFound = true;
  1360. }
  1361. else if (viVideosContent['hls']['cdns']['fastly_skyfire']) {
  1362. viVideoList["Multi Definition M3U8"] = viVideosContent['hls']['cdns']['fastly_skyfire']['url'];
  1363. if (!viVideoFound) viVideoFound = true;
  1364. }
  1365. }
  1366. }
  1367.  
  1368. if (viVideoFound) {
  1369. /* Create Saver */
  1370. var viDefaultVideo = 'Low Definition MP4';
  1371. if (!viVideoList[viDefaultVideo]) {
  1372. viDefaultVideo = 'Multi Definition M3U8';
  1373. }
  1374. saver = {
  1375. 'videoList': viVideoList,
  1376. 'videoDefinitions': ['Quad High Definition', 'Full High Definition', 'High Definition', 'Standard Definition', 'Low Definition', 'Very Low Definition'],
  1377. 'videoContainers': ['MP4'],
  1378. 'videoSave': viDefaultVideo,
  1379. 'videoTitle': viVideoTitle
  1380. };
  1381. createMySaver();
  1382. }
  1383. else {
  1384. saver = {'warnMess': '!videos'};
  1385. createMySaver();
  1386. }
  1387. }
  1388. else {
  1389. saver = {'warnMess': '!content'};
  1390. createMySaver();
  1391. }
  1392.  
  1393. }
  1394.  
  1395. // =====IMDB===== //
  1396.  
  1397. else if (page.url.indexOf('imdb.com') != -1) {
  1398.  
  1399. /* Redirect To Video Page */
  1400. if (page.url.indexOf('/video/') == -1 && page.url.indexOf('/videoplayer/') == -1) {
  1401. page.doc.addEventListener('click', function(e) {
  1402. var p = e.target.parentNode;
  1403. while (p) {
  1404. if (p.tagName === 'A' && p.href.indexOf('/video/imdb') != -1) {
  1405. page.win.location.href = p.href.replace(/imdb\/inline.*/, '');
  1406. }
  1407. p = p.parentNode;
  1408. }
  1409. }, false);
  1410. return;
  1411. }
  1412.  
  1413. /* Get Video Title */
  1414. var imdbVideoTitle = getMyContent(page.url, /meta\s+property="og:title"\s+content="(.*?)"/);
  1415. if (imdbVideoTitle) imdbVideoTitle = cleanMyContent(imdbVideoTitle, false, true);
  1416.  
  1417. /* Get Videos Content */
  1418. var imdbVideosContent = getMyContent(page.url, /"playbackURLs":(\[.*?\])/);
  1419. try {
  1420. imdbVideosContent = JSON.parse(imdbVideosContent);
  1421. }
  1422. catch(e) {
  1423. imdbVideosContent = {};
  1424. }
  1425.  
  1426. /* Get Videos */
  1427. var imdbVideoList = {};
  1428. if (imdbVideosContent) {
  1429. var imdbVideoFormats = {'1080p': 'Full High Definition MP4', '720p': 'High Definition MP4', '480p': 'Standard Definition MP4',
  1430. '360p': 'Low Definition MP4', 'SD': 'Low Definition MP4', '240p': 'Very Low Definition MP4', 'AUTO': 'Multi Definition M3U8'};
  1431. var imdbVideoFound = false;
  1432. var myVideoCode, imdbVideo;
  1433. for (var imdbVideoCode in imdbVideoFormats) {
  1434. for (var i = 0; i < imdbVideosContent.length; i++) {
  1435. if (imdbVideosContent[i]["displayName"]["value"] == imdbVideoCode) {
  1436. imdbVideo = imdbVideosContent[i]["url"];
  1437. }
  1438. if (imdbVideo) {
  1439. imdbVideo = cleanMyContent(imdbVideo, false);
  1440. if (!imdbVideoFound) imdbVideoFound = true;
  1441. myVideoCode = imdbVideoFormats[imdbVideoCode];
  1442. if (!imdbVideoList[myVideoCode]) imdbVideoList[myVideoCode] = imdbVideo;
  1443. }
  1444. }
  1445. }
  1446.  
  1447. if (imdbVideoFound) {
  1448. /* Create Saver */
  1449. var imdbDefaultVideo = 'Low Definition MP4';
  1450. saver = {
  1451. 'videoList': imdbVideoList,
  1452. 'videoDefinitions': ['Full High Definition', 'High Definition', 'Standard Definition', 'Low Definition', 'Very Low Definition'],
  1453. 'videoContainers': ['MP4', 'M3U8', 'Any'],
  1454. 'videoSave': imdbDefaultVideo,
  1455. 'videoTitle': imdbVideoTitle
  1456. };
  1457. createMySaver();
  1458. }
  1459. else {
  1460. saver = {'warnMess': '!videos'};
  1461. createMySaver();
  1462. }
  1463. }
  1464. else {
  1465. saver = {'warnMess': '!content'};
  1466. createMySaver();
  1467. }
  1468.  
  1469. }
  1470.  
  1471. }
  1472.  
  1473.  
  1474. // ==========Run========== //
  1475.  
  1476. getMyOptions();
  1477. SaveTube();
  1478.  
  1479. page.win.setInterval(function() {
  1480. if (page.url != page.win.location.href.replace(page.win.location.hash, '')) {
  1481. if (saver['saverPanel'] && saver['saverPanel'].parentNode) {
  1482. removeMyElement(saver['saverPanel'].parentNode, saver['saverPanel']);
  1483. }
  1484. page.doc = page.win.document;
  1485. page.body = page.doc.body;
  1486. page.url = page.win.location.href.replace(page.win.location.hash, '');
  1487. SaveTube();
  1488. }
  1489. }, 500);
  1490.  
  1491. })();