SaveTube

Download videos from video sharing web sites.

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