Multi-OCH Helper

nopremium.pl and premiumize.me. Inserts a direct download link on several one-click-hosters and some container/folder providers.

当前为 2023-04-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Multi-OCH Helper
  3. // @namespace cuzi
  4. // @license MIT
  5. // @description nopremium.pl and premiumize.me. Inserts a direct download link on several one-click-hosters and some container/folder providers.
  6. // @homepageURL https://openuserjs.org/scripts/cuzi/Multi-OCH_Helper
  7. // @contributionURL https://buymeacoff.ee/cuzi
  8. // @contributionURL https://ko-fi.com/cuzicvzi
  9. // @icon https://raw.githubusercontent.com/cvzi/Userscripts/master/Multi-OCH/icons/helper.png
  10. // @version 17.1.2
  11.  
  12. // @match https://cvzi.github.io/Userscripts/index.html?link=*
  13. // @match https://www.nopremium.pl/files*
  14. // @match https://www.premiumize.me/hosters/*
  15. // @match https://www.premiumize.me/services/*
  16. // @match https://www.premiumize.me/downloader*
  17.  
  18. // @match https://*.filecrypt.cc/Container/*
  19. // @match https://*.filecrypt.cc/helper.html*
  20. // @match https://protected.to/*
  21. // @match https://rapidgator.net/folder/*
  22. // @match https://safelinking.net/p/*
  23. // @match https://multiup.org/*
  24.  
  25. // @match https://1fichier.com/*
  26. // @match https://*.1fichier.com/*
  27. // @match https://www.4shared.com/*
  28. // @match https://alfafile.net/*
  29. // @match https://*.alfafile.net/*
  30. // @match https://anonfiles.com/*
  31. // @match https://bayfiles.com/*
  32. // @match https://*.bayfiles.com/*
  33. // @match http://clicknupload.link/*
  34. // @match https://clicknupload.to/*
  35. // @match https://clicknupload.org/*
  36. // @match https://clicknupload.co/*
  37. // @match https://clicknupload.cc/*
  38. // @match https://clicknupload.to/*
  39. // @match https://clicknupload.club/*
  40. // @match https://clicknupload.click/*
  41. // @match https://dailyuploads.net/*
  42. // @match https://ddl.to/*
  43. // @match https://ddownload.com/*
  44. // @match https://*.dropapk.com/*
  45. // @match https://dropapk.com/*
  46. // @match https://*.drop.download.com/*
  47. // @match https://drop.download.com/*
  48. // @match https://fastclick.to/*
  49. // @match https://fastshare.cz/*
  50. // @match https://file.al/*
  51. // @match https://www.file.al/*
  52. // @match https://filefactory.com/*
  53. // @match https://www.filefactory.com/*
  54. // @match https://filenext.com/*
  55. // @match https://www.filenext.com/*
  56. // @match https://filer.net/*
  57. // @match https://filerice.com/*
  58. // @match https://filespace.com/*
  59. // @match https://filestore.to/*
  60. // @match http://fireget.com/*
  61. // @match https://fireget.com/*
  62. // @match https://hitfile.net/*
  63. // @match https://hil.to/*
  64. // @match https://isra.cloud/*
  65. // @match https://katfile.com/*
  66. // @match https://www.mediafire.com/*
  67. // @match https://mediafire.com/*
  68. // @match https://mega.nz/*
  69. // @match https://megaup.net/*
  70. // @match https://mixdrop.co/*
  71. // @match https://modsbase.com/*
  72. // @match https://nitroflare.com/*
  73. // @match https://rapidgator.net/file/*
  74. // @match https://rg.to/file/*
  75. // @match https://spicyfile.com/*
  76. // @match https://www.spicyfile.com/*
  77. // @match https://turbobit.net/*
  78. // @match https://turb.to/*
  79. // @match https://tusfiles.net/*
  80. // @match https://ubiqfile.com/*
  81. // @match https://uploadboy.com/*
  82. // @match https://uploadgig.com/*
  83. // @match https://uptobox.com/*
  84. // @match https://userscloud.com/*
  85. // @match https://vidoza.org/*
  86. // @match https://worldbytez.com/*
  87. // @match https://wrzucajpliki.pl/*
  88. // @match https://xubster.com/*
  89. // @match https://*.zippyshare.com/*
  90.  
  91. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js
  92. // @require https://greasyfork.org/scripts/13883-aes-js/code/aesjs.js
  93. // @grant GM.registerMenuCommand
  94. // @grant unsafeWindow
  95. // @grant GM_setClipboard
  96. // @grant GM.xmlHttpRequest
  97. // @grant GM.openInTab
  98. // @grant GM.setClipboard
  99. // @grant GM.setValue
  100. // @grant GM.getValue
  101. // @grant GM.deleteValue
  102. // @grant GM.listValues
  103. // ==/UserScript==
  104.  
  105. /* globals confirm, alert, GM, GM_setClipboard, unsafeWindow, $, atob, slowAES, cloneInto */
  106. /* eslint n/no-callback-literal: 0 */
  107.  
  108. (async function () {
  109. 'use strict'
  110.  
  111. // And to keep for myself whatever I may find? - Certainly. For yourself, and any friends you want to share with you.
  112.  
  113. // This program inserts a download link on One-Click-Hosters and a few folder services.
  114. // If you click on the button, the current website address (or the links on the relink website) will be sent to nopremium.pl and you'll receive a nopremium.pl download link.
  115. //
  116. // Standard actions for the button are
  117. // * left mouse click: copy the link to the clipboard
  118. // * middle/wheel click: start download of the link
  119. // * right mouse click: open the nopremium.pl website and insert the link in the text box
  120. // * hovering the mouse over the button: open a menu with all the above option
  121. //
  122.  
  123. const scriptName = 'Multi-OCH Helper'
  124. const scriptReferer = 'multiochhelper'
  125. const scriptHightligherName = 'Multi-OCH Helper Highlight links'
  126.  
  127. const chrome = ~navigator.userAgent.indexOf('Chrome')
  128. const greasemonkey = 'info' in GM && 'scriptHandler' in GM.info && GM.info.scriptHandler === 'Greasemonkey'
  129.  
  130. const config = {
  131. position: [['bottom', 'top'], ['left', 'right']],
  132. position_desc: ['vertical', 'horizontal'],
  133. position_quest: 'Position of the Button. If you use "' + scriptHightligherName + '" this has to be set to bottom left',
  134. leftClick: ['clipboard', 'download', 'showLinks', 'openWebsite', 'menu', 'sendToJD', 'none'],
  135. leftClick_desc: ['Copy link to clipboard', 'Direct download', 'Show links like on website', 'Open the multihoster website', 'Show the extended menu', 'Send links to JDownloader', 'Do nothing'],
  136. leftClick_quest: 'Action on left mouse click on button',
  137. middleClick: ['download', 'clipboard', 'showLinks', 'openWebsite', 'menu', 'sendToJD', 'none'],
  138. middleClick_desc: ['Direct download', 'Copy link to clipboard', 'Show links like on website', 'Open the multihoster website', 'Show the extended menu', 'Send links to JDownloader', 'Do nothing'],
  139. middleClick_quest: 'Action on middle mouse/wheel click on button',
  140. rightClick: ['openWebsite', 'clipboard', 'showLinks', 'download', 'menu', 'sendToJD', 'none'],
  141. rightClick_desc: ['Show links like on website', 'Copy link to clipboard', 'Direct download', 'Open the multihoster website', 'Show the extended menu', 'Send links to JDownloader', 'Do nothing'],
  142. rightClick_quest: 'Action on right mouse click on button',
  143. mouseOver: ['menu', 'clipboard', 'download', 'showLinks', 'openWebsite', 'sendToJD', 'none'],
  144. mouseOver_desc: ['Show the extended menu', 'Copy link to clipboard', 'Direct download', 'Show links like on website', 'Open the multihoster website', 'Send links to JDownloader', 'Do nothing'],
  145. mouseOver_quest: 'Action on mouse hover over button',
  146. mouseOverDelay: 'int',
  147. mouseOverDelay_range: [0, 700, 3000],
  148. mouseOverDelay_quest: 'Mouse hover time before action is executed.',
  149. mouseOverDelay_suffix: 'milliseconds',
  150. newTab: 'bool',
  151. newTab_desc: ['Open in a new tab', 'Open in the same window'],
  152. newTab_quest: 'Should websites be opened in a new tab?',
  153. updateHosterStatusInterval: 'int',
  154. updateHosterStatusInterval_range: [1, 168, 9999],
  155. updateHosterStatusInterval_quest: 'How often should the status of the hosters be updated?',
  156. updateHosterStatusInterval_prefix: 'Every',
  157. updateHosterStatusInterval_suffix: 'hours',
  158. jDownloaderSupport: 'bool',
  159. jDownloaderSupport_desc: ['Show JDownloader button if JDownloader is runnning', 'Never show JDownloader button'],
  160. jDownloaderSupport_quest: ['Show a JDownloader button in the menu?']
  161.  
  162. }
  163. const settings = {}
  164. // Load settings
  165. const savedsettings = JSON.parse(await GM.getValue('settings', '{}')) // e.g. { position : [0,1], newTab : 1 }
  166. for (const key in config) {
  167. if (key in savedsettings) { // Saved
  168. if (config[key] === 'int') { // Int
  169. settings[key] = parseInt(savedsettings[key], 10)
  170. } else if (config[key] === 'string') { // String
  171. settings[key] = savedsettings[key].toString()
  172. } else if (config[key] === 'bool') { // Bool
  173. settings[key] = (savedsettings[key] === 'true' || savedsettings[key] === true)
  174. } else if (Array.isArray(config[key][0])) { // Nested array
  175. if (!Array.isArray(savedsettings[key])) {
  176. try {
  177. const tmp = JSON.parse(savedsettings[key])
  178. if (Array.isArray(tmp)) {
  179. savedsettings[key] = tmp
  180. }
  181. } catch (e) {}
  182. }
  183. settings[key] = []
  184. for (let i = 0; i < savedsettings[key].length; i++) {
  185. settings[key].push(savedsettings[key][i])
  186. }
  187. } else { // Array
  188. settings[key] = savedsettings[key]
  189. }
  190. } else { // Default
  191. if (config[key] === 'int') { // Int
  192. settings[key] = config[key + '_range'][1]
  193. } else if (config[key] === 'string') { // String
  194. settings[key] = '' // String defaults to empty string
  195. } else if (config[key] === 'bool') { // Bool
  196. settings[key] = true
  197. } else if (Array.isArray(config[key][0])) { // Nested array defaults to first value for each array
  198. settings[key] = []
  199. for (let i = 0; i < config[key].length; i++) {
  200. settings[key].push(config[key][i][0])
  201. }
  202. } else {
  203. settings[key] = config[key][0] // Array defaults to first value
  204. }
  205. }
  206. }
  207.  
  208. const JDOWNLOADER = 'http://127.0.0.1:9666/'
  209. const SPINNERCSS = `/* http://www.designcouch.com/home/why/2013/05/23/dead-simple-pure-css-loading-spinner/ */
  210. .ochspinner {
  211. height:16px;
  212. width:16px;
  213. margin:0px auto;
  214. position:relative;
  215. animation: rotation .6s infinite linear;
  216. border-left:6px solid rgba(0,174,239,.15);
  217. border-right:6px solid rgba(0,174,239,.15);
  218. border-bottom:6px solid rgba(0,174,239,.15);
  219. border-top:6px solid rgba(0,174,239,.8);
  220. border-radius:100%;
  221. }
  222. @keyframes rotation {
  223. from {transform: rotate(0deg)}
  224. to {transform: rotate(359deg)}
  225. }
  226. `
  227. // const LOADINGBARBG = 'background: #b4e391;background: linear-gradient(to bottom, #b4e391 0%,#61c419 50%,#b4e391 100%);'
  228.  
  229. let showOneclickButton = false
  230. let showOneclickLink = ''
  231. let showOneclickFromHighlighScriptAllLinks = document.location.host === 'cvzi.github.io'
  232. let showOneclickFromHighlighScriptAllLinksLoc = false
  233. let showOneclickFromHighlighScriptAllLinksLinks = ''
  234. let showOneclickFromHighlighScriptSelectedLinks = false
  235. let showOneclickFromHighlighScriptSelectedLinksLoc = false
  236. let showOneclickFromHighlighScriptSelectedLinksLinks = ''
  237.  
  238. let linksBeforeSelection = false
  239.  
  240. const multi = {
  241. 'premiumize.me': new function () {
  242. const self = this
  243. this.config = {
  244. apikey: 'string',
  245. apikey_hidden: true,
  246. apikey_quest: 'Enter your premiumize.me API key',
  247. apikey_prefix: 'API key: ',
  248. apikey_suffix: ' find it under <a target="_blank" href="https://www.premiumize.me/account">https://www.premiumize.me/account</a>'
  249. }
  250. this.key = 'premiumize.me'
  251. this.name = 'premiumize'
  252. this.homepage = 'https://www.premiumize.me/'
  253. // this.updateStatusURL = 'https://www.premiumize.me/services';
  254. this.updateStatusURLpattern = /https:\/\/www\.premiumize\.me\/services\/?/
  255. this.updateDownloadProgressInterval = 5000
  256. this.updateDownloadProgressInterfaceInterval = 500
  257.  
  258. this.status = {}
  259.  
  260. this.init = async function () {
  261. self.status = JSON.parse(await GM.getValue(self.key + '_status', '{}'))
  262. self.lastUpdate = new Date(await GM.getValue(self.key + '_status_time', 0))
  263. }
  264.  
  265. this.settings = {}
  266. this.loadSettings = async function (silent) {
  267. // Load settings, use first value as default
  268. const savedsettings = JSON.parse(await GM.getValue(self.key + '_settings', '{}'))
  269.  
  270. for (const key in self.config) {
  271. if (key.endsWith('desc') || key.endsWith('range') || key.endsWith('quest') || key.endsWith('prefix') || key.endsWith('suffix')) {
  272. continue
  273. }
  274. if (key in savedsettings) { // Saved
  275. if (self.config[key] === 'int') { // Int
  276. self.settings[key] = parseInt(savedsettings[key], 10)
  277. } else if (self.config[key] === 'string') { // String
  278. self.settings[key] = savedsettings[key].toString()
  279. } else if (config[key] === 'bool') { // Bool
  280. self.settings[key] = savedsettings[key] === 'true' || savedsettings[key] === true
  281. } else if (Array.isArray(savedsettings[key])) { // Nested array
  282. self.settings[key] = []
  283. for (let i = 0; i < savedsettings[key].length; i++) {
  284. self.settings[key].push(savedsettings[key][i])
  285. }
  286. } else { // Array
  287. self.settings[key] = savedsettings[key]
  288. }
  289. } else { // Default
  290. if (self.config[key] === 'int') { // Int
  291. self.settings[key] = self.config[key + '_range'][1]
  292. } else if (self.config[key] === 'string') { // String
  293. self.settings[key] = '' // String defaults to empty string
  294. } else if (config[key] === 'bool') { // Bool
  295. self.settings[key] = true
  296. } else if (Array.isArray(self.config[key][0])) { // Nested array defaults to first value for each array
  297. self.settings[key] = []
  298. for (let i = 0; i < self.config[key].length; i++) {
  299. self.settings[key].push(self.config[key][i][0])
  300. }
  301. } else {
  302. self.settings[key] = self.config[key][0] // Array defaults to first value
  303. }
  304. }
  305. }
  306.  
  307. if (!self.settings.apikey && !silent) {
  308. // Try to get the apikey from the website
  309. GM.xmlHttpRequest({
  310. method: 'GET',
  311. url: self.homepage + 'account',
  312. onerror: function (response) {
  313. console.log(scriptName + ': premiumize.me API Key could not be loaded')
  314. setStatus('You have not set you premiumize.me Api key ')
  315. },
  316. onload: function (response) {
  317. let s = ''
  318. try {
  319. s = response.responseText.split('class="apipass"')[1].split('</')[0].split('>')[1]
  320. } catch (e) {
  321. }
  322. if (s) {
  323. self.settings.apikey = s
  324. GM.setValue(self.key + '_settings', JSON.stringify(self.settings))
  325.  
  326. console.log(scriptName + ': premiumize.me API Key was loaded from account and saved!')
  327. } else {
  328. setStatus('You need to set you premiumize.me Api key')
  329. }
  330. }
  331. })
  332. }
  333. }
  334.  
  335. this.updateStatus = async function () { // Update list of online hosters
  336. await self.loadSettings()
  337. if (document.location.href.match(self.updateStatusURL)) {
  338. // Read and save current status of all hosters
  339. if ($('table.table tr>td:first-child').length) {
  340. self.status = {}
  341. await GM.setValue(self.key + '_status_time', '' + (new Date()))
  342. $('table.table tr>td:first-child').each(function () {
  343. const text = $(this).text()
  344. if (text.match(/^\s*[0-9a-z-]+\.\w{0,6}\s*$/i)) {
  345. const name = text.match(/^\s*([0-9a-z-]+)\.\w{0,6}\s*$/i)[1]
  346. self.status[name.toLowerCase()] = true
  347. }
  348. })
  349. await GM.setValue(self.key + '_status', JSON.stringify(self.status))
  350. console.log(scriptName + ': ' + self.name + ': Hosters (' + Object.keys(self.status).length + ') updated')
  351. } else if (self.settings.apikey) {
  352. GM.xmlHttpRequest({
  353. method: 'GET',
  354. url: self.homepage + 'api/services/list?apikey=' + encodeURIComponent(self.settings.apikey),
  355. onerror: function (response) {
  356. console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/services/list')
  357. console.log(response)
  358. },
  359. onload: async function (response) {
  360. const result = JSON.parse(response.responseText)
  361. /*
  362. { "cache": [ "uploaded.to", "filefactory.com", ... ], "directdl": [ "uploaded.to", "filefactory.com", ... ] }
  363. */
  364. if ('cache' in result && 'directdl' in result) {
  365. self.status = {}
  366. await GM.setValue(self.key + '_status_time', '' + (new Date()))
  367. result.cache.forEach(function (host) {
  368. const name = host.match(/^\s*([0-9a-z-]+)\.\w{0,6}\s*$/i)[1]
  369. self.status[name.toLowerCase()] = result.directdl.indexOf(host) !== -1
  370. })
  371.  
  372. await GM.setValue(self.key + '_status', JSON.stringify(self.status))
  373. console.log(scriptName + ': ' + self.name + ': Hosters (' + Object.keys(self.status).length + ') updated')
  374. } else {
  375. console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/services/list')
  376. console.log(response)
  377. }
  378. }
  379. })
  380. } else {
  381. console.log(scriptName + ': Cannot update hosters, no html and no api key found')
  382. }
  383. } else {
  384. alert(scriptName + '\n\nError: wrong update URL')
  385. }
  386. }
  387. this.isOnline = hostername => hostername in self.status && self.status[hostername]
  388.  
  389. this.getOpenWebsiteURL = function (urls) {
  390. // Return a link to the premiumize.me website that will insert the links
  391. const url = this.homepage + 'downloader?link:' + encodeURIComponent(urls.join('\n'))
  392. return url
  393. }
  394.  
  395. this.checkLink = function (url, cb) { // check whether the link is supported and online
  396. const host = url.match(/https?:\/\/(.+?)\//)[1]
  397. let hoster = host.split('.')
  398. hoster.pop()
  399. hoster = hoster.pop().replace('-', '')
  400. cb(this.isOnline(hoster))
  401. }
  402.  
  403. this.getResults = function (urls, cb) {
  404. // cb($node,linkNumber) -- $node contains the result, linkNumber is the number of links that should be online i.e. number of hashes
  405. alert('This function does not work for ' + this.name)
  406. }
  407.  
  408. this._notLoggedIn = false
  409.  
  410. this.getLinks = async function (urls, cb) {
  411. await showConfirm('fairPointsWarning', 'You will be charged premiumize fair points for generating ' + (urls.length > 1 ? ('<b>' + urls.length + '</b> files') : ('<b>one</b> file')) + '!<br><br>Generate links?', function () { self._getLinks(urls, cb) }, function () { setStatus('Operation canceled!', 0); cb([], -1) }, self)
  412. }
  413.  
  414. this._getLinks = function (urls, cb) {
  415. setTitle('✈️' + urls.length + '🔗 ')
  416. const N = urls.length
  417. const downloadLinks = []
  418. const errors = []
  419. for (let i = 0; i < urls.length; i++) {
  420. this._addSingleTransfer(urls[i], function (downloadlink, originallink, message) {
  421. if (downloadlink) {
  422. downloadLinks.push(downloadlink)
  423. } else {
  424. errors.push([originallink, message])
  425. }
  426. })
  427. }
  428.  
  429. const checkprogress = function () {
  430. if (self._notLoggedIn) {
  431. // Stop checking and open premiumize homepage
  432. setTitle('🔑 ')
  433. setStatus(self.name + ' error: Not logged in!\nMaybe update your API key?', 0)
  434. GM.openInTab(self.homepage)
  435. cb([], -2)
  436. return
  437. }
  438.  
  439. if (N === errors.length) { // All errors
  440. setTitle('❌ ')
  441. cb(false, -1)
  442. if (errors.length === 1 && errors[0][1]) {
  443. setStatus(errors[0][1], 0)
  444. } else {
  445. alert('Errors occured\n' + errors.length + ' links failed:\n\n' + errors.join('\n'))
  446. }
  447. } else if (N === downloadLinks.length + errors.length) { // All finished
  448. setTitle(downloadLinks.length + '/' + errors.length + '✅ ')
  449. cb(downloadLinks)
  450. if (errors.length > 0) { // Errors occured
  451. alert('Errors occured\n' + errors.length + ' links failed:\n\n' + errors.join('\n'))
  452. }
  453. } else { // not finished yet
  454. setTitle(downloadLinks.length + '/' + N + '⏳ ')
  455. window.setTimeout(checkprogress, self.updateDownloadProgressInterfaceInterval)
  456. }
  457. }
  458. window.setTimeout(checkprogress, self.updateDownloadProgressInterfaceInterval * Math.max(5, N))
  459. }
  460.  
  461. this._addSingleTransfer = function (url, cb) {
  462. GM.xmlHttpRequest({
  463. method: 'POST',
  464. url: self.homepage + 'api/transfer/create',
  465. data: 'apikey=' + encodeURIComponent(self.settings.apikey) + '&src=' + encodeURIComponent(url),
  466. headers: {
  467. 'Content-Type': 'application/x-www-form-urlencoded',
  468. 'Cache-Control': 'no-cache'
  469. },
  470. onerror: function (response) {
  471. console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/transfer/create')
  472. console.log(response)
  473. cb(false, url, 'GM.xmlHttpRequest error: api/transfer/create')
  474. },
  475. onload: function (response) {
  476. const result = JSON.parse(response.responseText)
  477. /*
  478. {"status":"success","type":"savetocloud","id":"gfwRtdgd5fgdfgfhgfhf","name":"test.zip"}
  479. {"status":"error","error":"duplicate","id":"gfdgd5fgFddfgfhgfhf","message":"You already have this job added."}
  480. {"status":"error","message":"This link is not available on the file hoster website"}
  481. */
  482. if ('id' in result && result.id) {
  483. window.setTimeout(function () {
  484. self._getFileFromTransfer(url, result.id, cb)
  485. }, 1000)
  486. if ('message' in result) {
  487. addStatus(result.message, -1)
  488. }
  489. } else {
  490. if ('message' in result && !self._notLoggedIn) {
  491. addStatus(result.message, -1)
  492. if (~result.message.indexOf('log')) {
  493. self._notLoggedIn = true
  494. }
  495. }
  496. cb(false, url, 'message' in result ? result.message : response.responseText)
  497. }
  498. }
  499. })
  500. }
  501.  
  502. this._getFileFromTransfer = function (url, transferId, cb) {
  503. GM.xmlHttpRequest({
  504. method: 'GET',
  505. url: self.homepage + 'api/transfer/list?apikey=' + encodeURIComponent(self.settings.apikey),
  506. headers: {
  507. 'Content-Type': 'application/x-www-form-urlencoded',
  508. 'Cache-Control': 'no-cache'
  509. },
  510. onerror: function (response) {
  511. console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/transfer/list')
  512. console.log(response)
  513. cb(false, url, 'GM.xmlHttpRequest error: /api/transfer/list')
  514. },
  515. onload: function (response) {
  516. const result = JSON.parse(response.responseText)
  517. /*
  518. {
  519. "status": "success",
  520. "transfers": [
  521. {
  522. "id": "xXFDSXXFDSGD",
  523. "name": "test.zip",
  524. "message": null,
  525. "status": "finished",
  526. "progress": 0,
  527. "folder_id": "gfjdfsuigjfdoikfsadf",
  528. "file_id": "trhgf982u30fjklfsdag"
  529. }
  530. ]
  531. }
  532. {
  533. "status": "success",
  534. "transfers": [
  535. {
  536. "id":"xXFDSXXFDSGD",
  537. "name":"test.zip",
  538. "message":"Initializing Download...",
  539. "status":"running",
  540. "progress":0,
  541. "folder_id":"gfjdfsuigjfdoikfsadf",
  542. "file_id":null
  543. }
  544. ]
  545. }
  546. */
  547. if (result.status === 'success' && 'transfers' in result) {
  548. for (let i = 0; i < result.transfers.length; i++) {
  549. if (result.transfers[i].id === transferId) {
  550. if (result.transfers[i].file_id) {
  551. // Finished
  552. window.setTimeout(function () {
  553. self._getSingleLink(url, result.transfers[i].file_id, cb)
  554. }, result.transfers[i].status === 'finished' ? 10 : self.updateDownloadProgressInterval)
  555. } else {
  556. // Downloading
  557. if ('message' in result.transfers[i] && result.transfers[i].message) {
  558. setStatus(result.transfers[i].message, -1)
  559. }
  560. window.setTimeout(function () {
  561. self._getFileFromTransfer(url, transferId, cb)
  562. }, self.updateDownloadProgressInterval)
  563. }
  564.  
  565. return
  566. }
  567. }
  568. }
  569. if ('message' in result && result.message) {
  570. alert(scriptName + '\n\nCould not get /api/transfer/list\nError:\n' + result.message)
  571. }
  572. cb(false, url, 'Could not find url in transfer list')
  573. }
  574. })
  575. }
  576.  
  577. this._getSingleLink = function (url, fileId, cb) {
  578. GM.xmlHttpRequest({
  579. method: 'POST',
  580. url: self.homepage + 'api/item/details',
  581. data: 'apikey=' + encodeURIComponent(self.settings.apikey) + '&id=' + encodeURIComponent(fileId),
  582. headers: {
  583. 'Content-Type': 'application/x-www-form-urlencoded',
  584. 'Cache-Control': 'no-cache'
  585. },
  586. onerror: function (response) {
  587. console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/item/details')
  588. console.log(response)
  589. cb(false, url, 'GM.xmlHttpRequest error: /api/item/details')
  590. },
  591. onload: function (response) {
  592. const result = JSON.parse(response.responseText)
  593. /*
  594. {
  595. "id": "xxXxXxxxXxxx",
  596. "name": "test.zip",
  597. "size": 156,
  598. "created_at": 1572458477,
  599. "transcode_status": "not_applicable",
  600. "folder_id": "XxXXXxxxxxx",
  601. "ip": "1.1.1.1",
  602. "acodec": "",
  603. "vcodec": "",
  604. "mime_type": "application/zip",
  605. "opensubtitles_hash": "",
  606. "resx": "",
  607. "resy": "",
  608. "duration": "",
  609. "virus_scan": "ok",
  610. "type": "file",
  611. "link": "https://down.host.example.com/dl/abcdefg/test.zip",
  612. "stream_link": null
  613. }
  614. */
  615. if ('link' in result && result.link) {
  616. cb(result.link, url)
  617. } else {
  618. window.setTimeout(function () {
  619. self._getSingleLink(url, fileId, cb)
  620. }, self.updateDownloadProgressInterval)
  621. }
  622. }
  623. })
  624. }
  625. }(),
  626.  
  627. 'nopremium.pl': new function () {
  628. const self = this
  629. this.config = {
  630. mode: ['transfer', 'premium', 'none'],
  631. mode_desc: ['Transfer User (Pakiety Transferowe)', 'Premium User (Konta Premium)', 'No account'],
  632. mode_quest: 'What kind of account do you have at nopremium.pl',
  633. downloadmode: ['direct', 'server'],
  634. downloadmode_desc: ['Direct download (TRYB SZYBKIEGO POBIERANIA)', 'Downloading via NoPremium.pl server (TRYB POBIERANIA NA SERWERY)'],
  635. downloadmode_quest: ['Which download mode do you want to use?']
  636. }
  637. this.key = 'nopremium.pl'
  638. this.name = 'NoPremium.pl'
  639. this.homepage = 'https://www.nopremium.pl/'
  640. this.updateStatusURL = 'https://www.nopremium.pl/files'
  641. this.updateStatusURLpattern = /https?:\/\/www\.nopremium\.pl\/files\/?/
  642. this.updateDownloadProgressInterval = 5000
  643. const mapHosterName = name => name.replace('-', '')
  644. this.status = {}
  645.  
  646. this.init = async function () {
  647. self.status = JSON.parse(await GM.getValue(self.key + '_status', '{}'))
  648.  
  649. self.lastUpdate = new Date(await GM.getValue(self.key + '_status_time', 0))
  650. }
  651.  
  652. this.settings = {}
  653. this.loadSettings = async function (silent) {
  654. // Load settings, use first value as default
  655. const savedsettings = JSON.parse(await GM.getValue(self.key + '_settings', '{}'))
  656.  
  657. for (const key in self.config) {
  658. if (key.endsWith('desc') || key.endsWith('range') || key.endsWith('quest') || key.endsWith('prefix') || key.endsWith('suffix')) {
  659. continue
  660. }
  661. if (key in savedsettings) { // Saved
  662. if (self.config[key] === 'int') { // Int
  663. self.settings[key] = parseInt(savedsettings[key], 10)
  664. } else if (self.config[key] === 'string') { // String
  665. self.settings[key] = savedsettings[key].toString()
  666. } else if (config[key] === 'bool') { // Bool
  667. self.settings[key] = savedsettings[key] === 'true' || savedsettings[key] === true
  668. } else if (Array.isArray(savedsettings[key])) { // Nested array
  669. self.settings[key] = []
  670. for (let i = 0; i < savedsettings[key].length; i++) {
  671. self.settings[key].push(savedsettings[key][i])
  672. }
  673. } else { // Array
  674. self.settings[key] = savedsettings[key]
  675. }
  676. } else { // Default
  677. if (self.config[key] === 'int') { // Int
  678. self.settings[key] = self.config[key + '_range'][1]
  679. } else if (self.config[key] === 'string') { // String
  680. self.settings[key] = '' // String defaults to empty string
  681. } else if (config[key] === 'bool') { // Bool
  682. self.settings[key] = true
  683. } else if (Array.isArray(self.config[key][0])) { // Nested array defaults to first value for each array
  684. self.settings[key] = []
  685. for (let i = 0; i < self.config[key].length; i++) {
  686. self.settings[key].push(self.config[key][i][0])
  687. }
  688. } else {
  689. self.settings[key] = self.config[key][0] // Array defaults to first value
  690. }
  691. }
  692. }
  693. }
  694.  
  695. this.updateStatus = async function () { // Update list of online hosters
  696. if (document.location.href.match(self.updateStatusURL)) {
  697. // Read and save current status of all hosters
  698. await GM.setValue(self.key + '_status_time', '' + (new Date()))
  699. self.status = {}
  700. $('#servers a[title]').each(function () {
  701. const name = mapHosterName(this.title)
  702. self.status[name] = true
  703. })
  704. await GM.setValue(self.key + '_status', JSON.stringify(self.status))
  705. console.log(scriptName + ': ' + self.name + ': Hosters (' + Object.keys(self.status).length + ') updated')
  706. } else {
  707. alert(scriptName + '\n\nError: wrong update URL')
  708. }
  709. }
  710. this.isOnline = hostername => hostername in self.status && self.status[hostername]
  711.  
  712. this.getOpenWebsiteURL = function (urls) {
  713. // Return a link to the nopremium.pl website that will insert the links
  714. const url = this.homepage + 'files?link:' + encodeURIComponent(urls.join('\n'))
  715. return url
  716. }
  717.  
  718. const getHashs = function (urls, cb, silent) {
  719. // cb(hashes,sizestring)
  720. setTitle('✈️ ')
  721. setStatus('Sending ' + (urls.length === 1 ? 'one link' : (urls.length + ' links')), -1)
  722. GM.xmlHttpRequest({
  723. method: 'POST',
  724. url: self.homepage + 'files',
  725. data: 'watchonline=&session=' + (Math.round(Math.random() * 1234567)) + '&links=' + encodeURIComponent(urls.join('\n')),
  726. headers: {
  727. 'Content-Type': 'application/x-www-form-urlencoded',
  728. 'Cache-Control': 'no-cache'
  729. // "Referer" : "https://www.nopremium.pl/files" // FIREFOX57
  730. },
  731. onload: function (response) {
  732. if (response.responseText.indexOf('<input type="text" name="login" placeholder="Login"/>') !== -1) {
  733. setTitle('🔑 ')
  734. setStatus(self.name + ' error: Not logged in!', 0)
  735. GM.openInTab(self.homepage)
  736. return cb([], -1)
  737. }
  738.  
  739. const hashes = []
  740. // Find hashes
  741. const re = /name="hash(\d+)" value="(\w+)"/g // <input type="checkbox" id="hash0" name="hash0" value="fab3c41988" onclick="UpdateCounter();" c
  742. let ma = re.exec(response.responseText)
  743. while (ma) {
  744. hashes.push(ma[2])
  745. ma = re.exec(response.responseText)
  746. }
  747. // Find errors
  748. ma = response.responseText.match(/Pliki nieprzetworzone \((\d+)\)/)
  749. if (ma && !silent) {
  750. addStatus('Error: ' + (parseInt(ma[1], 10) === 1 ? ('One file is offline or unsupported') : (ma[1] + ' files are offline or unsupported')), 0)
  751. }
  752.  
  753. // Find size
  754. let size = '0 Byte'
  755. if (response.responseText.indexOf('id="countSize"') !== -1) {
  756. ma = response.responseText.split('id="countSize"')[1].match(/value="(\d+.?\d*) (\w+)"/) // <input type="text" name="countSize" id="countSize" style="width:80px;" readonly="readonly" value="1.38 GB">
  757. size = ma[1] + ' ' + ma[2]
  758. }
  759.  
  760. setStatus(self.name + ' identified ' + (hashes.length === 1 ? 'one online file' : (hashes.length + ' online files')), -1)
  761. setTitle(hashes.length + '🔗 ')
  762. cb(hashes, size)
  763. }
  764. })
  765. }
  766.  
  767. this.checkLink = function (url, cb) { // check whether the link is supported and online
  768. // cb(boolresult)
  769. return getHashs([url], function (hashes, size) {
  770. cb(hashes.length === 1)
  771. }, true)
  772. }
  773.  
  774. this.getResults = function (urls, cb, hashes) {
  775. // cb($node,linkNumber) -- $node contains the result, linkNumber is the number of links that should be online i.e. number of hashes
  776. // Get download links from nopremium.pl and show the usual info about the file, that is normally shown on nopremium.pl
  777. if (typeof hashes === 'undefined') {
  778. // 1. Get hashes and show transfer warning
  779. getHashs(urls, async function (hashes, size) {
  780. if (settings.mode === 'transfer') {
  781. await showConfirm('transferWarning', 'You will be charged <b>' + size + "</b> 'Transfer' for generating " + (hashes.length > 1 ? ('<b>' + hashes.length + '</b> files') : ('<b>one</b> file')) + '!<br><br>Generate links?', function () { this.getResults(urls, cb, hashes) }, null, self)
  782. } else if (hashes.length > 0) {
  783. self.getResults(urls, cb, hashes)
  784. } else if (size === -1) { // Error was already handled (probably not logged in)
  785. console.log('getHashs->cb: Error was already handled (probably not logged in)')
  786. cb(false, -2)
  787. } else { // No files found
  788. setStatus('No online/available files', 0)
  789. cb(false, 0)
  790. }
  791. })
  792. return
  793. }
  794.  
  795. // 2. Work with hashes
  796. const $resultContainer = $('<div></div>').attr('id', 'generated-links')
  797. const mode = self.settings.downloadmode === 'direct' ? 0 : 1 // 0 -> direct , 1 -> via server
  798. GM.xmlHttpRequest({
  799. method: 'POST',
  800. url: self.homepage + 'files',
  801. data: 'insert=1&mode=' + mode + '&hh=0&hash[]=' + hashes.join('&hash[]=') + '&',
  802. headers: {
  803. 'Content-Type': 'application/x-www-form-urlencoded',
  804. 'Cache-Control': 'no-cache'
  805. // "Referer" : "https://www.nopremium.pl/files" // FIREFOX57
  806. },
  807. onload: function (response) {
  808. GM.xmlHttpRequest({
  809. method: 'POST',
  810. url: self.homepage + 'files',
  811. data: 'loadfiles=1',
  812. headers: {
  813. 'Content-Type': 'application/x-www-form-urlencoded',
  814. 'Cache-Control': 'no-cache'
  815. // "Referer" : "https://www.nopremium.pl/files" // FIREFOX57
  816. },
  817. onload: function (response) {
  818. if (mode === 0) {
  819. $resultContainer.append($('<div></div>').append(response.responseText).find('#fastFilesArea'))
  820. } else {
  821. $resultContainer.append($('<div></div>').append(response.responseText).find('#downloadFilesArea'))
  822. }
  823. $resultContainer.find('input[type=checkbox]').remove()
  824. cb($resultContainer, hashes.length)
  825. }
  826. })
  827. }
  828. })
  829. }
  830. this.getLinks = function (urls, cb) {
  831. // cb(downloadlinks)
  832.  
  833. if (this.settings.downloadmode === 'direct') {
  834. return this._getDirectLinks(urls, cb)
  835. } else {
  836. return this._getServerLinks(urls, cb)
  837. }
  838. }
  839.  
  840. this._getDirectLinks = function (urls, cb) {
  841. // Get Direct download links
  842.  
  843. this.getResults(urls, async function ($node, N) {
  844. if (!$node || N < 1) {
  845. cb(false)
  846. return
  847. }
  848.  
  849. const text = $node.html()
  850.  
  851. /*
  852. <td>16-08-2014 20:22</td>
  853. <td class="dlBox"><a href="http://direct.nopremium.pl/9091456/7895ca02bfcb2c2e43806f1079b7ff069129e/result.file"><img src="https://www.nopremium.pl/images/download_ico.png" alt="Sciagnij" title="Sciagnij"></a></td>
  854. */
  855. const files = []
  856. const re = /<td>(\d+)-(\d+)-(\d+) (\d+):(\d+)<\/td>(\s|\n)+<td class="dlBox"><a href="(.*?)"/gm
  857. let m = re.exec(text) // wholeString, 16,08,2014,20,37,#newline#,http://direct.nopremium.pl/9091456/7895ca02bfcb2c2e43806f1079b7ff069129e/result.file
  858.  
  859. while (m) {
  860. if (m[7].indexOf('//direct.nopremium.pl') === -1) {
  861. continue // Skip files via server, only use direct download links
  862. }
  863. const d = new Date(m[3], m[2], m[1], m[4], m[5], 0, 0)
  864. files.push([d, m[7]])
  865. m = re.exec(text)
  866. }
  867.  
  868. if (files.length === 0) {
  869. alert(scriptName + '\n\nAn error occured.\nCould not find download links in response.')
  870. cb(false)
  871. return
  872. }
  873. // Find youngest files by comparing their ids
  874. const pattern = /\.pl\/(\d+)\//
  875. files.sort(function (a, b) {
  876. const x = a[1].match(pattern)[1]
  877. const y = a[1].match(pattern)[1]
  878. return x > y ? -1 : x < y ? 1 : 0
  879. })
  880.  
  881. const result = []
  882. for (let i = 0; i < N; i++) {
  883. result.push(files[i][1])
  884. await cacheLink([urls[i]], files[i][0], [files[i][1]], self.key) // CACHE single URLs
  885. }
  886.  
  887. await cacheLink(urls, new Date(), result, self.key) // CACHE all URLs
  888.  
  889. cb(result)
  890. })
  891. }
  892.  
  893. this._getServerLinks = function (urls, cb) {
  894. this.getResults(urls, function ($node, N) {
  895. if (N === 0) {
  896. cb(false)
  897. } else {
  898. self._getProgress(cb, $node, N)
  899. }
  900. })
  901. }
  902.  
  903. this._getProgress = function (cb, $node, N, ids) {
  904. GM.xmlHttpRequest({
  905. method: 'POST',
  906. url: self.homepage + 'files',
  907. data: 'downloadprogress=1',
  908. headers: {
  909. 'Content-Type': 'application/x-www-form-urlencoded',
  910. 'Cache-Control': 'no-cache'
  911. // "Referer" : "https://www.nopremium.pl/files" // FIREFOX57
  912. },
  913. onerror: function () {
  914. self._getProgressBlocked = false
  915.  
  916. window.setTimeout(function () {
  917. self._getProgress(cb, $node, N, ids)
  918. }, self.updateDownloadProgressInterval)
  919. },
  920. onload: function (response) {
  921. self._getProgressBlocked = false
  922.  
  923. let data
  924. try {
  925. data = JSON.parse(response.responseText)
  926. } catch (e) {
  927. console.log(scriptName + ': ' + e)
  928. console.log(response.responseText)
  929.  
  930. if (response.responseText.indexOf('<input type="text" name="login" placeholder="Login"/>') !== -1) {
  931. setTitle('🔑 ')
  932. setStatus(self.name + ' error: Not logged in!', 0)
  933. GM.openInTab(self.homepage)
  934. cb(false, -2)
  935. } else {
  936. window.setTimeout(function () {
  937. self._getProgress(cb, $node, N, ids)
  938. }, self.updateDownloadProgressInterval)
  939. }
  940. return
  941. }
  942.  
  943. data.StandardFiles.sort(function (a, b) {
  944. const x = new Date(a.insert_date.split('-').join('/'))
  945. const y = new Date(b.insert_date.split('-').join('/'))
  946. return x > y ? -1 : x < y ? 1 : 0
  947. })
  948.  
  949. const result = []
  950. const runnning = []
  951. let percent = 0
  952. const progess = []
  953.  
  954. if (!ids) { // First run: Find the correct files: just use the first N files
  955. ids = []
  956. for (let i = 0; i < data.StandardFiles.length && i < N; i++) {
  957. ids.push(data.StandardFiles[i].id)
  958. if (data.StandardFiles[i].status === 'finish') {
  959. result.push(data.StandardFiles[i].download_url)
  960. progess.push(100)
  961. percent += 100
  962. } else {
  963. runnning.push(data.StandardFiles[i])
  964. if (parseInt(data.StandardFiles[i].status, 10) > 0) {
  965. progess.push(parseInt(data.StandardFiles[i].status, 10))
  966. percent += parseInt(data.StandardFiles[i].status, 10)
  967. }
  968. }
  969. }
  970. } else { // Consecutive runs: Use the ids from first run
  971. for (let i = 0; i < data.StandardFiles.length; i++) {
  972. if (ids.indexOf(data.StandardFiles[i].id) === -1) continue
  973. if (data.StandardFiles[i].status === 'finish') {
  974. result.push(data.StandardFiles[i].download_url)
  975. progess.push(100)
  976. percent += 100
  977. } else {
  978. runnning.push(data.StandardFiles[i])
  979. if (parseInt(data.StandardFiles[i].status, 10) > 0) {
  980. progess.push(parseInt(data.StandardFiles[i].status, 10))
  981. percent += parseInt(data.StandardFiles[i].status, 10)
  982. }
  983. }
  984. }
  985. }
  986.  
  987. /*
  988. Regarding caching in server mode:
  989. If you add a file, that is already on the server (or currently downloading), you will not be charged additional bandwith - therefore caching is not necessary at the moment.
  990. */
  991.  
  992. if (result.length === N) {
  993. setStatus((result.length === 1 ? 'One file' : (result.length + ' files')) + ' downloaded to server', 1)
  994. setTitle(result.length + '✅ ')
  995. cb(result)
  996. } else {
  997. // Waiting
  998. percent = percent / N
  999. // setStatus('Download '+result.length+'/'+N+' ('+Math.floor(percent)+'%)\n<span title="'+round(percent,2)+'%" style="display:block; width:120px; height:18px; background:white; border:1px solid black; border-radius:5px;"><span style="display:block; border-radius:5px; height:18px; width:'+Math.ceil(percent*1.2)+'px; '+LOADINGBARBG+'"> </span></span>',-1);
  1000. const dotheight = N > 2 ? 2 : 4
  1001. let h = 'Download ' + result.length + '/' + N + ' (' + Math.floor(percent) + '%)\n<div style="display:block; width:130px; height:auto; background:white; border:1px solid black; border-radius:5px; padding:2px; ">'
  1002. for (let i = 0; i < N; i++) {
  1003. if (progess[i]) {
  1004. h += '<span style="display:block; width:' + Math.ceil(progess[i] * 1.2) + 'px; height:1px; background:white; border-top:' + dotheight + 'px ' + (progess[i] > 99.9 ? 'solid' : 'dotted') + ' green; margin-bottom:1px;"></span>'
  1005. } else {
  1006. h += '<span style="display:block; width:0x; height:1px; background:white; border-top:' + dotheight + 'px dotted silver; margin-bottom:1px;"></span>'
  1007. }
  1008. }
  1009. h += '</div>'
  1010.  
  1011. setTitle(Math.floor(percent) + '%⏳ ')
  1012.  
  1013. setStatus(h)
  1014. showOnlyStatus()
  1015.  
  1016. window.setTimeout(function () {
  1017. self._getProgress(cb, $node, N, ids)
  1018. }, self.updateDownloadProgressInterval)
  1019. }
  1020. }
  1021. })
  1022. }
  1023. }()
  1024.  
  1025. }
  1026.  
  1027. const debridprovider = Object.keys(multi)
  1028. let currentdebrid = await GM.getValue('currentdebrid', debridprovider[0])
  1029.  
  1030. for (const key in multi) {
  1031. await multi[key].init()
  1032. if (key === currentdebrid) {
  1033. await multi[key].loadSettings()
  1034. continue
  1035. }
  1036. if (!greasemonkey) {
  1037. GM.registerMenuCommand(scriptName + ' - Switch to ' + multi[key].name, (function (key) {
  1038. return async function () {
  1039. if (!confirm(scriptName + '\n\nSet multi-download provider:\n' + multi[key].name)) return
  1040.  
  1041. await GM.setValue('currentdebrid', key)
  1042. currentdebrid = key
  1043. document.location.reload()
  1044. }
  1045. })(key)
  1046. )
  1047. }
  1048. }
  1049.  
  1050. if (!greasemonkey) {
  1051. GM.registerMenuCommand(scriptName + ' - Delete cached links', async function () {
  1052. if (!confirm(scriptName + '\n\nReally delete cached links?')) return
  1053.  
  1054. await GM.setValue('cachedDownloadLinks', '{}')
  1055.  
  1056. alert(scriptName + '\n\nCache is empty!')
  1057. })
  1058. GM.registerMenuCommand(scriptName + ' - Restore dialogs and warnings', async function () {
  1059. if (!confirm(scriptName + '\n\nReally restore all dialogs and warnings?')) return
  1060.  
  1061. await GM.setValue('dialogs', '[]')
  1062.  
  1063. alert(scriptName + '\n\nDialogs and warnings restored')
  1064. })
  1065. }
  1066.  
  1067. /*
  1068. function round (f, p) {
  1069. // Round f to p places after the comma
  1070. return parseFloat(parseFloat(f).toFixed(p))
  1071. }
  1072. */
  1073.  
  1074. const orgDocumentTitle = document.title
  1075. function setTitle (message) {
  1076. if (window.parent.parent !== window) {
  1077. window.parent.parent.postMessage({ iAm: 'Unrestrict.li', type: 'title', str: message }, '*')
  1078. }
  1079. if (message) {
  1080. document.title = message + orgDocumentTitle
  1081. } else {
  1082. document.title = orgDocumentTitle
  1083. }
  1084. }
  1085.  
  1086. function popUp (id, onClose, thisArg, doNotCloseOnOutsideClick) {
  1087. // Remove window scrolling
  1088. $(document.body).css('overflow', 'hidden')
  1089. let zi = getNextZIndex()
  1090. id = id || ('popup' + (new Date()).getTime())
  1091. const $par = $('<div style="position:absolute; top:0px;"></div>').attr('id', id).appendTo(document.body)
  1092. const $background = $('<div style="position:fixed; top:0px; left:0px; right:0px; bottom:0px; background:black; opacity:0.5; z-index:' + (zi++) + '"></div>').appendTo($par)
  1093. const $div = $('<div style="position:fixed; top:50px; left:100px; overflow:auto; z-index:' + (zi++) + '; background:#E6E6E6; color:Black; border:#B555C5 2px solid;border-radius:5px; padding:10px; font-family: "Ubuntu",Arial,Sans-Serif"></div>').css('maxHeight', window.innerHeight - 100).css('maxWidth', window.innerWidth - 200).appendTo($par)
  1094.  
  1095. const close = function () {
  1096. $par.remove()
  1097. if (onClose) onClose.call(thisArg)
  1098. // Restore scrolling
  1099. $(document.body).css('overflow', 'initial')
  1100. }
  1101.  
  1102. if (!doNotCloseOnOutsideClick) {
  1103. $background.click(close)
  1104. }
  1105.  
  1106. return { node: $div, close }
  1107. }
  1108.  
  1109. function configForm ($form, c, s, formid) {
  1110. for (const key in c) {
  1111. if (key.endsWith('desc') || key.endsWith('range') || key.endsWith('quest') || key.endsWith('prefix') || key.endsWith('suffix') || key.endsWith('hidden')) {
  1112. continue
  1113. }
  1114.  
  1115. const $p = $('<p>').appendTo($form)
  1116.  
  1117. if (c[key + '_quest']) {
  1118. $p.append(c[key + '_quest'])
  1119. } else {
  1120. $p.append(key)
  1121. }
  1122.  
  1123. $p.append('<br>')
  1124.  
  1125. if (c[key + '_prefix']) {
  1126. $p.append(c[key + '_prefix'] + ' ')
  1127. }
  1128.  
  1129. const hidden = (key + '_hidden') in c && c[key + '_hidden']
  1130. if (c[key] === 'int') { // Int
  1131. const $input = $('<input type="number">').addClass('form_' + formid).data('key', key).data('parse', 'int').val(s[key]).appendTo($p)
  1132. if (c[key + '_range']) {
  1133. $input.prop('min', c[key + '_range'][0])
  1134. $input.prop('max', c[key + '_range'][2])
  1135. $input.prop('title', c[key + '_range'][0] + ' - ' + c[key + '_range'][2])
  1136. }
  1137. } else if (c[key] === 'string') { // String
  1138. const $inputText = $('<input type="text">').addClass('form_' + formid).data('key', key).data('parse', 'string').appendTo($p)
  1139. if (hidden && s[key]) {
  1140. $inputText.val('## HIDDEN ##')
  1141. $inputText.data('hidden', '1')
  1142. } else {
  1143. $inputText.val(s[key])
  1144. }
  1145. } else if (c[key] === 'bool') { // Bool
  1146. const $select = $('<select></select>').addClass('form_' + formid).data('key', key).data('parse', 'bool').appendTo($p)
  1147.  
  1148. const $optionYes = $('<option></option>').val('true').appendTo($select)
  1149. if (c[key + '_desc']) {
  1150. $optionYes.html(c[key + '_desc'][0])
  1151. } else {
  1152. $optionYes.html('Yes')
  1153. }
  1154. if (s[key]) {
  1155. $optionYes[0].selected = true
  1156. }
  1157.  
  1158. const $optionNo = $('<option></option>').val('false').appendTo($select)
  1159. if (c[key + '_desc']) {
  1160. $optionNo.html(c[key + '_desc'][1])
  1161. } else {
  1162. $optionNo.html('No')
  1163. }
  1164. if (!s[key]) {
  1165. $optionNo[0].selected = true
  1166. }
  1167. } else if (Array.isArray(c[key][0])) { // Nested array
  1168. for (let j = 0; j < c[key].length; j++) {
  1169. if (c[key + '_desc'] && !Array.isArray(c[key + '_desc'][j])) {
  1170. $p.append(c[key + '_desc'][j] + ': ')
  1171. }
  1172.  
  1173. const $select = $('<select></select>').addClass('form_' + formid).data('key', key).data('index', j).appendTo($p)
  1174. for (let i = 0; i < c[key][j].length; i++) {
  1175. const $option = $('<option></option>').val(c[key][j][i]).appendTo($select)
  1176. if (c[key + '_desc'] && Array.isArray(c[key + '_desc'][0])) {
  1177. $option.html(c[key + '_desc'][j][i])
  1178. } else {
  1179. $option.html(c[key][j][i])
  1180. }
  1181. if (s[key][j] === c[key][j][i]) { $option[0].selected = true }
  1182. }
  1183. $p.append('<br>')
  1184. }
  1185. } else { // Array
  1186. const $select = $('<select></select>').addClass('form_' + formid).data('key', key).appendTo($p)
  1187. for (let i = 0; i < c[key].length; i++) {
  1188. const $option = $('<option></option>').val(c[key][i]).appendTo($select)
  1189. if (c[key + '_desc']) {
  1190. $option.html(c[key + '_desc'][i])
  1191. } else {
  1192. $option.html(c[key][i])
  1193. }
  1194. if (s[key] === c[key][i]) { $option[0].selected = true }
  1195. }
  1196. }
  1197.  
  1198. if (c[key + '_suffix']) {
  1199. $p.append(' ' + c[key + '_suffix'])
  1200. }
  1201. }
  1202. }
  1203.  
  1204. async function saveSettings (ev) {
  1205. const $body = ev.data
  1206. const $form = $body.find('.form')
  1207.  
  1208. // Save preferred hoster:
  1209. currentdebrid = $form.find('.debridhoster').val()
  1210.  
  1211. // Save options:
  1212. const newsettings = { general: {} }
  1213. for (const key in multi) {
  1214. newsettings[key] = {}
  1215. }
  1216.  
  1217. $form.find('*[class^=form_]').each(function () {
  1218. const $this = $(this)
  1219. const namespace = $this.prop('class').split('_', 2)[1]
  1220. const key = $this.data('key')
  1221. const index = $this.data('index')
  1222. let value = $this.val()
  1223. const parse = $this.data('parse')
  1224. const hiddenAndUnchanged = $this.data('hidden') && value === '## HIDDEN ##'
  1225. if (typeof index !== 'undefined') { // Nested Array
  1226. if (!(key in newsettings[namespace]) || !Array.isArray(newsettings[namespace][key])) {
  1227. newsettings[namespace][key] = []
  1228. }
  1229. newsettings[namespace][key][index] = value
  1230. } else { // Normal
  1231. if (hiddenAndUnchanged) {
  1232. value = multi[namespace].settings[key]
  1233. } else if (parse === 'int') {
  1234. value = parseInt(value, 10)
  1235. } else if (parse === 'bool') {
  1236. value = (value === 'true')
  1237. }
  1238. newsettings[namespace][key] = value
  1239. }
  1240. })
  1241.  
  1242. await GM.setValue('setup', true)
  1243. await GM.setValue('currentdebrid', currentdebrid)
  1244. await GM.setValue('settings', JSON.stringify(newsettings.general))
  1245. for (const key in multi) {
  1246. await GM.setValue(key + '_settings', JSON.stringify(newsettings[key]))
  1247. }
  1248.  
  1249. alert(scriptName + '\n\nSettings were successfully saved!')
  1250. document.location.reload()
  1251. }
  1252.  
  1253. async function aboutMe () {
  1254. const popup = popUp('multiochhelper_about', null, null, true)
  1255. const $popup = popup.node
  1256. const $frame = $('<iframe width="' + (window.innerWidth - 250) + '" height="' + (window.innerHeight - 150) + '" style="border:0">').appendTo($popup)
  1257. $frame.bind('load', async function (e) {
  1258. // Load settings for all
  1259. for (const key in multi) {
  1260. await multi[key].loadSettings(true)
  1261. }
  1262.  
  1263. const $body = $($frame[0].contentDocument.getElementsByTagName('body')[0])
  1264.  
  1265. $body.css('fontFamily', 'Ubuntu,Arial,Sans-Serif')
  1266.  
  1267. $('<div style="position:fixed; top:0px; right:5px; cursor:pointer; color:White; background:#b555c5; border: 1px solid White; border-radius:3px; padding:0px; font-weight:bold ; " title="Close menu">X</span>').click(function () { if (confirm('Settings will NOT be saved!')) popup.close() }).appendTo($body)
  1268.  
  1269. $body.append('<h2>' + scriptName + '</h2>')
  1270. $('<a>').appendTo($body).attr('target', '_blank').css('fontSize', 'small').html('https://openuserjs.org/scripts/cuzi/Multi-OCH_Helper').attr('href', 'https://openuserjs.org/scripts/cuzi/Multi-OCH_Helper')
  1271.  
  1272. const $form = $('<div class="form">').appendTo($body)
  1273.  
  1274. // General options
  1275. $form.append('<h3>Settings</h3>')
  1276. configForm($form, config, settings, 'general')
  1277.  
  1278. // Preferred multihoster
  1279. const $p = $('<p>').appendTo($form)
  1280. $p.append('Preferred multihoster:<br>')
  1281. const $select = $('<select></select>').addClass('debridhoster').appendTo($p)
  1282. for (const key in multi) {
  1283. const $option = $('<option></option>').val(key).appendTo($select)
  1284. $option.html(multi[key].name)
  1285. $option[0].selected = key === currentdebrid
  1286. }
  1287.  
  1288. // Options for multihosters
  1289. for (const key in multi) {
  1290. $('<h3>').appendTo($form).html(multi[key].name)
  1291. $('<a>').appendTo($form).css('fontSize', 'small').attr('target', '_blank').html(multi[key].homepage).attr('href', multi[key].homepage)
  1292. if (multi[key].config) {
  1293. configForm($form, multi[key].config, multi[key].settings, key)
  1294. } else {
  1295. $('<p>').appendTo($form).text('No settings available for this service.')
  1296. }
  1297. }
  1298.  
  1299. $form.append('<br>')
  1300.  
  1301. $('<input type="button">').val('Cancel').click(function () {
  1302. if (confirm('Settings will NOT be saved!')) {
  1303. popup.close()
  1304. }
  1305. }).appendTo($form)
  1306. $('<input type="button">').val('Save').click($body, saveSettings).appendTo($form)
  1307.  
  1308. $form.append('<h3>Other options</h3>')
  1309.  
  1310. $('<input type="button">').val('Clear cache (' + humanBytes((await GM.getValue('cachedDownloadLinks', '{}')).length - 2) + ')').appendTo($form).click(async function () {
  1311. if (!confirm(scriptName + '\n\nReally delete cached links?')) {
  1312. return
  1313. }
  1314.  
  1315. await GM.setValue('cachedDownloadLinks', '{}')
  1316.  
  1317. this.value = 'Clear cache (' + humanBytes((await GM.getValue('cachedDownloadLinks', '{}')).length - 2) + ')'
  1318. alert(scriptName + '\n\nCache is empty!')
  1319. })
  1320.  
  1321. $form.append('<br>')
  1322. $form.append('<br>')
  1323.  
  1324. $('<input type="button">').val('Restore dialogs and warnings').appendTo($form).click(async function () {
  1325. if (!confirm(scriptName + '\n\nReally restore all dialogs and warnings?')) {
  1326. return
  1327. }
  1328.  
  1329. await GM.setValue('dialogs', '[]')
  1330.  
  1331. alert(scriptName + '\n\nDialogs and warnings restored')
  1332. })
  1333.  
  1334. let greasemonkeyIssue = ''
  1335. if (greasemonkey) {
  1336. greasemonkeyIssue = `<li>In Greasymonkey it is not possible to select multiple links with the mouse and send them at once.<br>
  1337. The reason is this bug: <a href="https://github.com/greasemonkey/greasemonkey/issues/2574">https://github.com/greasemonkey/greasemonkey/issues/2574</a><br>
  1338. If you need this functionality, you can use Tampermonkey instead of Greasemonkey</li>`
  1339. }
  1340.  
  1341. $(`<div>
  1342. <br>
  1343. <br>
  1344. <h3>Known issues:</h3>
  1345. <ul>
  1346. <li>nopremium.pl sometimes omits a few links in folders</li>
  1347. <li>In Firefox the script sometimes does not work if the "Accept thid-parts cookies" policy is set to "Never".<br>
  1348. To resolve this problem open the Firefox options and go to the tab "Privacy". Set the "Accept thid-parts cookies" to "From visited" or "Always"<br>
  1349. Close and re-open Firefox. Log out and then log in your nopremium.pl account. Everything should work fine now.</li>
  1350. ${greasemonkeyIssue}
  1351. </ul>
  1352. </div><br><br><br>`).appendTo($body)
  1353.  
  1354. $('<input type="button">').val('Debug info').appendTo($body).click(inspectGMvalues)
  1355. })
  1356. if (chrome) {
  1357. $frame.attr('src', 'about:blank')
  1358. }
  1359. }
  1360.  
  1361. function inspectGMvalues () {
  1362. let iv
  1363. const popup = popUp('multiochhelper_inspectGM', function () {
  1364. clearInterval(iv)
  1365. })
  1366. const $popup = popup.node
  1367. const $frame = $('<iframe width="' + (window.innerWidth - 250) + '" height="' + (window.innerHeight - 150) + '" style="border:0">').appendTo($popup)
  1368. $frame.bind('load', async function (e) {
  1369. $($frame[0].contentDocument.getElementsByTagName('head')[0]).append('<style type="text/css">' + SPINNERCSS + '</style>')
  1370.  
  1371. const $body = $($frame[0].contentDocument.getElementsByTagName('body')[0])
  1372. $body.append('<h2>' + scriptName + '</h2>')
  1373.  
  1374. let keys = await GM.listValues()
  1375. if (keys.length && typeof keys[0] === 'undefined') { // Firefox 35+ workaround
  1376. keys = cloneInto(await GM.listValues(), window)
  1377. }
  1378.  
  1379. const $table = $('<table>').appendTo($body)
  1380. let $tr
  1381. $tr = $('<tr>').appendTo($table)
  1382. $('<th>').html('Key').appendTo($tr)
  1383. $('<th>').html('Value').appendTo($tr)
  1384. $('<th>').html('Type').appendTo($tr)
  1385. $('<th>').html('').appendTo($tr)
  1386.  
  1387. const deleteValue = async function (ev) {
  1388. const key = $(this).data('key')
  1389. await GM.deleteValue(key)
  1390. $(this).parent().parent().remove()
  1391. }
  1392.  
  1393. let total = 0
  1394. for (let i = 0; i < keys.length; i++) {
  1395. const value = await GM.getValue(keys[i])
  1396. let svalue = '' + value
  1397. let len = 1
  1398. if (typeof value === 'undefined') {
  1399. svalue = 'undefined'
  1400. } else if (typeof value === 'string') {
  1401. len = value.length
  1402. }
  1403. total += len
  1404. $tr = $('<tr>').appendTo($table)
  1405. $('<td>').html(keys[i]).appendTo($tr)
  1406. $('<td>').append($('<input type="text" style="width:600px">').val(svalue)).appendTo($tr)
  1407. $('<td>').append('' + (typeof value) + (typeof value === 'string' ? ('(' + len + ')') : '')).appendTo($tr)
  1408. $('<td>').append($('<input type="button">').val('Delete').data('key', keys[i]).click(deleteValue)).appendTo($tr)
  1409. }
  1410.  
  1411. $tr = $('<tr>').appendTo($table)
  1412. $('<th>').html('Total').appendTo($tr)
  1413. $('<th>').html(keys.length).appendTo($tr)
  1414. $('<th>').html('approx. ' + humanBytes(total)).appendTo($tr)
  1415.  
  1416. const $reload = $('<div>').appendTo($body)
  1417. $('<div style="display:inline-block;width:20px; height:20px;" class="ochspinner"></div>').appendTo($reload)
  1418. $reload.append(' Reload in ')
  1419. const $timer = $('<span style="pointer:cursor;" title="Click to reload now"></span>').html('20 seconds').click(function () { this.innerHTML = 0 }).appendTo($reload)
  1420. iv = window.setInterval(function () {
  1421. let s = parseInt($timer.html(), 10)
  1422. if (s === 0) {
  1423. clearInterval(iv)
  1424. popup.close()
  1425. inspectGMvalues()
  1426. } else {
  1427. s--
  1428. $timer.html(s + ' seconds')
  1429. }
  1430. }, 1000)
  1431. })
  1432. if (chrome) {
  1433. $frame.attr('src', 'about:blank')
  1434. }
  1435. }
  1436.  
  1437. function hexToBytes (s) {
  1438. return s.match(/([0-9a-fA-F]{2})/g).map(v => parseInt(v, 16))
  1439. }
  1440.  
  1441. function stringToBytes (s) {
  1442. return s.split('').map(v => v.charCodeAt(0))
  1443. }
  1444.  
  1445. function bytesToString (a) {
  1446. return String.fromCharCode.apply(String, a)
  1447. }
  1448.  
  1449. function addCSSHead (body) {
  1450. const style = document.createElement('style')
  1451. style.type = 'text/css'
  1452. style.innerHTML = body
  1453. document.head.appendChild(style)
  1454. }
  1455.  
  1456. function humanBytes (bytes, precision) {
  1457. // http://stackoverflow.com/a/18650828
  1458. bytes = parseInt(bytes, 10)
  1459. if (bytes === 0) return '0 Byte'
  1460. const k = 1024
  1461. const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
  1462. const i = Math.floor(Math.log(bytes) / Math.log(k))
  1463. return parseFloat((bytes / Math.pow(k, i)).toPrecision(2)) + ' ' + sizes[i]
  1464. }
  1465.  
  1466. function getNextZIndex () {
  1467. // Calculate: max(zIndex) + 1
  1468. let zIndexMax = 0
  1469. try {
  1470. $('div').each(function () {
  1471. const z = parseInt($(this).css('z-index'), 10)
  1472. if (z > zIndexMax) {
  1473. zIndexMax = z
  1474. }
  1475. })
  1476. } catch (e) {} finally {
  1477. if (zIndexMax < 20000) {
  1478. zIndexMax = 20006
  1479. }
  1480. }
  1481. return zIndexMax + 1
  1482. }
  1483.  
  1484. async function showConfirm (id, text, onConfirm, onNotConfirm, thisArg) {
  1485. // Skip
  1486. const dialogs = JSON.parse(await GM.getValue('dialogs', '[]'))
  1487. if (dialogs.indexOf(id) !== -1) {
  1488. onConfirm.call(thisArg)
  1489. return
  1490. }
  1491.  
  1492. const popup = popUp('confirm' + id, function () {}, thisArg)
  1493. const $div = popup.node
  1494. $div.append(text)
  1495. $div.append('<br>')
  1496. $('<input type="button" value="Yes">').click(function () {
  1497. popup.close()
  1498. onConfirm.call(thisArg)
  1499. }).appendTo($div)
  1500.  
  1501. $('<input type="button" value="No">').click(function () {
  1502. popup.close()
  1503. if (onNotConfirm) {
  1504. onNotConfirm.call(thisArg)
  1505. }
  1506. }).appendTo($div)
  1507.  
  1508. $div.append('<br>')
  1509.  
  1510. $('<input type="checkbox" value="remember">').click(async function () {
  1511. const dialogs = JSON.parse(await GM.getValue('dialogs', '[]'))
  1512. if (this.checked) {
  1513. if (dialogs.indexOf(id) === -1) {
  1514. dialogs.push(id)
  1515. await GM.setValue('dialogs', JSON.stringify(dialogs))
  1516. }
  1517. } else {
  1518. if (dialogs.indexOf(id) !== -1) {
  1519. dialogs.splice(dialogs.indexOf(id), 1)
  1520. await GM.setValue('dialogs', JSON.stringify(dialogs))
  1521. }
  1522. }
  1523. }).appendTo($div)
  1524. $div.append(' Always "Yes". Do not show this message again!')
  1525. }
  1526.  
  1527. function setStatus (text, success) {
  1528. addStatus(text, success, true)
  1529. }
  1530.  
  1531. function addStatus (text, success, clear) {
  1532. if (!document.getElementById('multiochhelper')) {
  1533. alert(`${scriptName}\n\n${text}`)
  1534. return
  1535. }
  1536. let $status = $('#multiochhelper_status')
  1537. if (!document.getElementById('multiochhelper_status_text')) {
  1538. if (!document.getElementById('multiochhelper_status')) {
  1539. const $div = $('#multiochhelper')
  1540. $status = $('<div>').prependTo($div)
  1541. $status.attr('id', 'multiochhelper_status')
  1542. } else {
  1543. $status.empty()
  1544. }
  1545. const $loader = $('<div>').appendTo($status)
  1546. $loader.attr('id', 'multiochhelper_status_loader')
  1547. const $statustext = $('<div>').appendTo($status)
  1548. $statustext.attr('id', 'multiochhelper_status_text')
  1549. const $statusclear = $('<div>').appendTo($status)
  1550. $statusclear.attr('id', 'multiochhelper_status_clear')
  1551. }
  1552.  
  1553. const $statustext = $('#multiochhelper_status_text')
  1554. if (clear) {
  1555. $statustext.empty()
  1556. } else if ($statustext.html().trim() !== '') {
  1557. $statustext.append(document.createElement('br'))
  1558. }
  1559. $status.show()
  1560.  
  1561. $statustext.append(text)
  1562. if (success === 1) {
  1563. $statustext.css('color', '#33FF99')
  1564. } else if (success === 0) {
  1565. $statustext.css('color', 'orange')
  1566. } else if (success === -1) {
  1567. $statustext.css('color', 'cyan')
  1568. } else {
  1569. $statustext.css('color', 'white')
  1570. }
  1571. }
  1572.  
  1573. function showOnlyStatus () {
  1574. const $status = $('#multiochhelper_status')
  1575. $status.siblings().not('#multiochhelper_status').remove()
  1576. }
  1577.  
  1578. function getMultiOCHWebsiteURL (links) {
  1579. return multi[currentdebrid].getOpenWebsiteURL(links)
  1580. }
  1581.  
  1582. function openWebsite (links, cb) {
  1583. // Call cb() and navigate to the website
  1584. if (!links) {
  1585. cb(false)
  1586. return
  1587. }
  1588.  
  1589. if (cb) {
  1590. cb()
  1591. }
  1592. const url = getMultiOCHWebsiteURL(links)
  1593.  
  1594. if (settings.newTab) {
  1595. if (typeof GM.openInTab === 'undefined') {
  1596. window.open(url)
  1597. } else {
  1598. GM.openInTab(url)
  1599. }
  1600. } else {
  1601. document.location.href = url
  1602. }
  1603. }
  1604.  
  1605. async function useCache (urls, cb) {
  1606. urls = '' + urls
  1607. const cachedDownloadLinks = JSON.parse(await GM.getValue('cachedDownloadLinks', '{}')) // [datestring,downloadlink,multihoster]
  1608. if (urls in cachedDownloadLinks) {
  1609. if (confirm(scriptName + '\n\nLink was found in cache.\nUse cached link?\n\nFrom: ' + (new Date(cachedDownloadLinks[urls][0])) + '\nWith: ' + cachedDownloadLinks[urls][2] + '\n' + cachedDownloadLinks[urls][1].join('\n'))) {
  1610. cb(cachedDownloadLinks[urls][1])
  1611. return true
  1612. }
  1613. }
  1614. return false
  1615. }
  1616. async function cacheLink (urls, datetime, downloadLinks, multihoster) {
  1617. if (!Array.isArray(downloadLinks)) {
  1618. const parts = downloadLinks.split('\n')
  1619. downloadLinks = []
  1620. for (let i = 0; i < parts.length; i++) {
  1621. if ($.trim(parts[i])) {
  1622. downloadLinks.push($.trim(parts[i]))
  1623. }
  1624. }
  1625. }
  1626. if (downloadLinks.length === 0) return
  1627.  
  1628. urls = '' + urls
  1629. const cachedDownloadLinks = JSON.parse(await GM.getValue('cachedDownloadLinks', '{}'))
  1630. cachedDownloadLinks[urls] = [datetime, downloadLinks, multihoster]
  1631. await GM.setValue('cachedDownloadLinks', JSON.stringify(cachedDownloadLinks))
  1632. }
  1633.  
  1634. function showExtractedLinks (links) {
  1635. if (document.querySelector('.alertlinkscont')) {
  1636. alert(links.join('\n'))
  1637. $('.alertlinkscont').remove()
  1638. return
  1639. }
  1640. $('<style type="text/css">.alertlinkscont{transition: left 500ms;}.alertlinkscont a{font-size:12px;user-select:all; font-family: monospace;} .alertlinkscont a:link,.alertlinkscont a:hover{color:black; text-decoration:none;}.alertlinkscont a:visited{color:rgb(70,0,120); text-decoration:none;}</style>').appendTo('head')
  1641. const $div = $('<div class="alertlinkscont"></div>')
  1642. $div.appendTo(document.body)
  1643. $div.css({
  1644. zIndex: 10000,
  1645. position: 'fixed',
  1646. top: '20px',
  1647. left: '20px',
  1648. minWidth: '300px',
  1649. minHeight: '300px',
  1650. background: 'white',
  1651. color: 'black',
  1652. border: '2px solid black',
  1653. borderRadius: '5px',
  1654. padding: '20px 25px 10px',
  1655. fontFamily: 'monospace',
  1656. fontSize: '12px',
  1657. overflow: 'auto'
  1658. })
  1659. for (let i = 0; i < links.length; i++) {
  1660. $div[0].innerHTML += '<a target="_blank" href="' + links[i] + '">' + links[i] + '</a><br>\n'
  1661. }
  1662. $div[0].innerHTML += '<br><br>\n'
  1663. window.setTimeout(function moveMenuIntoView () {
  1664. $div.css('maxHeight', (document.documentElement.clientHeight - 100) + 'px')
  1665. $div.css('maxWidth', (document.documentElement.clientWidth - 40) + 'px')
  1666. $div.css('left', Math.max(20, 0.5 * (document.body.clientWidth - $div[0].clientWidth)) + 'px')
  1667. }, 0)
  1668. }
  1669.  
  1670. async function generateLinks (urls, cb) {
  1671. // Check cache
  1672. if (await useCache(urls, cb)) {
  1673. return
  1674. }
  1675.  
  1676. await multi[currentdebrid].getLinks(urls, cb)
  1677. }
  1678.  
  1679. async function download (urls, cb) {
  1680. // Get one/first download link and open it immediately/start download
  1681. if (urls.length > 1) {
  1682. alert(scriptName + '\n\nOnly the first link will be opened!')
  1683. }
  1684.  
  1685. await generateLinks(urls, function (result, code) {
  1686. if (cb) {
  1687. cb()
  1688. }
  1689. if (result && result[0]) {
  1690. addStatus('Opening download...', -1)
  1691. if (window.top === window) {
  1692. document.location.href = result[0]
  1693. } else {
  1694. // Changing location may be blocked by sandboxed iframe
  1695. window.top.location.href = result[0]
  1696. }
  1697. } else if (code === -2) {
  1698. // Error was already handled
  1699. console.log('download() in generateLinks(): error already handled')
  1700. } else if (!code) {
  1701. addStatus('An error occured: No downloadlink to open', 0)
  1702. }
  1703. })
  1704. }
  1705.  
  1706. async function clipboard (urls, cb) {
  1707. // Get download links and copy them into clipboard
  1708. generateLinks(urls, function (result, code) {
  1709. if (result) {
  1710. let succeeded = false
  1711. setStatus('Trying to set clipboard', -1)
  1712. window.setTimeout(function () {
  1713. if (succeeded) {
  1714. return
  1715. }
  1716. setStatus('Trying GM_setClipboard()', -1)
  1717. try {
  1718. GM_setClipboard(result.join('\r\n'))
  1719. setStatus('Copied to clipboard', 1)
  1720. } catch (e) {
  1721. setStatus('Failed to access clipboard 02', 0)
  1722. alert('Failed to access clipboard.\n\nLinks will appear in next dialog window')
  1723. alert(result.join('\r\n'))
  1724. }
  1725. }, 3000)
  1726. try {
  1727. GM.setClipboard(result.join('\r\n')).then(function () {
  1728. setStatus('Copied to clipboard', 1)
  1729. succeeded = true
  1730. }, function () {
  1731. setStatus('Failed to access clipboard 01', 0)
  1732. })
  1733. } catch (e) {
  1734. setStatus('Clipboard not supported by this browser', 0)
  1735. alert(result.join('\n'))
  1736. };
  1737. } else if (code === -2) {
  1738. // Error was already handled
  1739. console.log('clipboard() in generateLinks(): error already handled')
  1740. } else {
  1741. setStatus('An error occured: No downloadlinks found', 0)
  1742. }
  1743. if (cb) {
  1744. cb()
  1745. }
  1746. })
  1747. }
  1748.  
  1749. async function sendToJD (urls, cb) {
  1750. // Get download links and send them to JDownloader
  1751. generateLinks(urls, function (result, code) {
  1752. if (result) {
  1753. setStatus('Waiting for JDownloader', -1)
  1754.  
  1755. // Comment should be the original page in case of multiple links
  1756. let comment = urls[0]
  1757. if (urls.length > 1) {
  1758. if (showOneclickFromHighlighScriptAllLinksLoc) {
  1759. comment = showOneclickFromHighlighScriptAllLinksLoc
  1760. } else if (showOneclickFromHighlighScriptSelectedLinksLoc) {
  1761. comment = showOneclickFromHighlighScriptAllLinksLoc
  1762. } else {
  1763. comment = document.location.href
  1764. }
  1765. }
  1766.  
  1767. GM.xmlHttpRequest({
  1768. method: 'POST',
  1769. url: JDOWNLOADER + 'flash/add',
  1770. headers: {
  1771. 'Content-Type': 'application/x-www-form-urlencoded',
  1772. Referer: scriptReferer,
  1773. 'User-Agent': scriptReferer
  1774. },
  1775. // data: "source="+encodeURIComponent(scriptReferer)+"&urls="+encodeURIComponent(result.join("\r\n")), // Moved "source" to Referer
  1776. // data: "comment="+encodeURIComponent(comment)+"&urls="+encodeURIComponent(result.join("\r\n")), // See ExternInterfaceImpl.java
  1777. data: 'source=' + encodeURIComponent(scriptReferer) + '&comment=' + encodeURIComponent(comment) + '&urls=' + encodeURIComponent(result.join('\r\n')), // See ExternInterfaceImpl.java
  1778. onload: function (resp) {
  1779. if (cb) {
  1780. cb()
  1781. }
  1782. if (resp.status === 204 || resp.responseText.startsWith('success')) {
  1783. setStatus('Sent to JDownloader', 1)
  1784. } else {
  1785. setStatus('JDownloader rejected the request', 0)
  1786. }
  1787. },
  1788. onerror: function (resp) {
  1789. if (cb) {
  1790. cb()
  1791. }
  1792. setStatus('JDownloader is not running', 0)
  1793. }
  1794.  
  1795. })
  1796. } else if (code === -2) {
  1797. // Error was already handled
  1798. console.log('sendToJD() in generateLinks(): error already handled')
  1799. if (cb) {
  1800. cb()
  1801. }
  1802. } else {
  1803. if (cb) {
  1804. cb()
  1805. }
  1806. addStatus('No links to send', 0)
  1807. }
  1808. })
  1809. }
  1810.  
  1811. function showLinks (urls, cb, append, n) {
  1812. const popup = popUp('showLinks')
  1813. const $div = popup.node
  1814. const $loader = $('<div style="width:20px; height:20px;" class="ochspinner"></div>').appendTo($div)
  1815.  
  1816. const $frame = $('<iframe width="900" height="500" style="border:0">').appendTo($div)
  1817. $frame.bind('load', function (e) {
  1818. $($frame[0].contentDocument.getElementsByTagName('head')[0]).append('<link rel="stylesheet" href="https://www.nopremium.pl/css/style.css" type="text/css" />')
  1819. const $body = $($frame[0].contentDocument.getElementsByTagName('body')[0])
  1820. multi[currentdebrid].getResults(urls, function ($node) {
  1821. $loader.remove()
  1822. $body.append($node)
  1823. $body.find('a').each(function () {
  1824. // Open links in new window
  1825. this.setAttribute('target', '_blank')
  1826. })
  1827. if (cb) {
  1828. cb()
  1829. }
  1830. })
  1831. })
  1832. if (chrome) {
  1833. $frame.attr('src', 'about:blank')
  1834. }
  1835. }
  1836.  
  1837. function decryptClickNLoad (cb, jk, cryptedBase64) {
  1838. // Get all the links by decrypting the Click'n'Load form
  1839. // return False for any error
  1840. // return True, run cb() and open the menu if Click'n'Load was successfully decoded
  1841.  
  1842. if (!cryptedBase64 && !(document.getElementsByName('crypted').length && document.getElementsByName('jk').length)) {
  1843. return false // Click'n'Load not avaiblabe
  1844. }
  1845.  
  1846. setStatus("Trying to decrypt Click'n'Load", -1)
  1847.  
  1848. try {
  1849. // Key/IV
  1850. if (!jk) {
  1851. jk = document.getElementsByName('jk')[0].value
  1852. }
  1853. if (jk.indexOf('return') !== -1) {
  1854. jk = eval(jk + '; f();') // eslint-disable-line no-eval
  1855. }
  1856.  
  1857. const key = hexToBytes(jk)
  1858. const iv = key.slice(0)
  1859.  
  1860. // Text
  1861. if (!cryptedBase64) {
  1862. cryptedBase64 = document.getElementsByName('crypted')[0].value
  1863. }
  1864. const cryptedString = atob(cryptedBase64)
  1865. const cryptedBytes = stringToBytes(cryptedString)
  1866.  
  1867. // Decrypt
  1868. const textBytes = slowAES.decrypt(cryptedBytes, slowAES.modeOfOperation.CBC, key, iv)
  1869. let text = bytesToString(textBytes)
  1870.  
  1871. text = text.replace('\r', '')
  1872.  
  1873. const splitted = text.split('\n')
  1874.  
  1875. const links = []
  1876. for (let i = 0; i < splitted.length; i++) {
  1877. // Remove any line that is not a http link
  1878. const t = $.trim(splitted[i])
  1879. if (t && t.substring(0, 4) === 'http') {
  1880. links.push(t)
  1881. }
  1882. }
  1883.  
  1884. const N = links.length
  1885.  
  1886. if (N === 0) {
  1887. return false // Click'n'Load probably failed, try another method...
  1888. }
  1889.  
  1890. if (cb) {
  1891. cb()
  1892. }
  1893. menu(links)
  1894. setStatus('Found ' + (N === 1 ? 'one link' : (N + ' links')), 1)
  1895. return true
  1896. } catch (e) {
  1897. alert("Click'N'Load failed:\n" + e)
  1898. return false // Click'n'Load probably failed, try another method...
  1899. }
  1900.  
  1901. /*
  1902. // Get all the links by decrypting the Click'n'Load form
  1903. if(!document.getElementsByName('crypted').length || !document.getElementsByName('jk').length) {
  1904. if(cb) {
  1905. cb();
  1906. }
  1907. return;
  1908. }
  1909.  
  1910. setStatus("Trying linkdecrypter.com",-1);
  1911. const crypted = document.getElementsByName('crypted')[0].value;
  1912. const jk = document.getElementsByName('jk')[0].value;
  1913.  
  1914. GM.xmlHttpRequest( {
  1915. method: "POST",
  1916. url: "http://linkdecrypter.com/api/?t=cnl2",
  1917. data: 'crypted=' + encodeURIComponent(crypted) + '&jk=' + encodeURIComponent(btoa(jk)),
  1918. headers: {
  1919. "User-agent": "Mozilla/5.0 (X11;U;Linux i686;es-ES;rv:1.9.2.8) Gecko/20100723 Ubuntu/10.04 (lucid) Firefox/3.6.8",
  1920. "Accept": "application/atom+xml,application/xml,text/xml",
  1921. "Content-type" : "application/x-www-form-urlencoded"
  1922. },
  1923. onload: function(response) {
  1924. if(cb) {
  1925. cb();
  1926. }
  1927. const N = response.responseText.split("\n").length;
  1928. if(!response.responseText || response.responseText.indexOf("ERROR(CNL2)") !== -1 || N === 0) {
  1929. setStatus("An error occurred while handling the response of linkdecrypter.com",0);
  1930. } else {
  1931. menu(response.responseText);
  1932. setStatus("Found "+(N===1?"one link":(N+" links")),1);
  1933. }
  1934. }
  1935. });
  1936. */
  1937. }
  1938.  
  1939. function getAllSerienjunkiesLinks (cb) {
  1940. // Get all download links from a serienjunkies.org download page (i.e. the page right after the captcha)
  1941. const urls = [] // [ [partnumber0,link0] , [partnumber1,link1] , .... ]
  1942. let total = 0
  1943.  
  1944. const rap = document.getElementById('rap')
  1945. const table = rap.getElementsByTagName('table')[0]
  1946. const forms = table.getElementsByTagName('form')
  1947.  
  1948. let j = 1 // part number, in order to make sure that sorting of the links is the same as on the page.
  1949. // This is only a fallback in case there is no visible part number in the actual downloadlink/filename.
  1950. for (let i = 0; i < forms.length; i++) {
  1951. const url = forms[i].action
  1952. if (url.indexOf('mirror') !== -1 || url.indexOf('firstload') !== -1) {
  1953. continue
  1954. }
  1955. GM.xmlHttpRequest({
  1956. method: 'GET',
  1957. url,
  1958. onload: (function (j) {
  1959. return function (response) {
  1960. const loc = response.finalUrl // Actual link after posible redirections
  1961. if (response.finalUrl.match(/part*(\d+)\./)) { // Try to guess part number
  1962. const part = response.finalUrl.match(/part*(\d+)\./)[1]
  1963. urls.push([parseInt(part, 10), loc])
  1964. } else { // fallback part number
  1965. urls.push([j, loc])
  1966. }
  1967. setStatus('Decrypting: ' + urls.length + '/' + total, total === urls.length ? 1 : -1)
  1968. if (total === urls.length) {
  1969. // Got all links
  1970. cb(urls)
  1971. }
  1972. }
  1973. }(j))
  1974. })
  1975. j++
  1976. }
  1977. total = j - 1
  1978. };
  1979.  
  1980. function getSerienjunkiesLinks (cb) {
  1981. // Get all the links from the page
  1982. getAllSerienjunkiesLinks(function (urls) {
  1983. if (cb) {
  1984. cb()
  1985. }
  1986.  
  1987. urls = urls.sort(function (a, b) {
  1988. if (a[0] > b[0]) return 1
  1989. else if (a[0] < b[0]) return -1
  1990. return 0
  1991. })
  1992.  
  1993. let alllinks = ''
  1994. for (let i = 0; i < urls.length; ++i) {
  1995. alllinks += urls[i][1] + '\n'
  1996. }
  1997. menu(alllinks)
  1998. })
  1999. }
  2000.  
  2001. function getFilecryptcc (jddata, cb) {
  2002. // Get all the links by decrypting the Click'n'Load form
  2003. const fieldJk = jddata[0]
  2004. const fieldCrypted = jddata[1]
  2005.  
  2006. const r = decryptClickNLoad(cb, fieldJk, fieldCrypted)
  2007. if (!r) {
  2008. setStatus("Could not find Click'n'Load", -1)
  2009. if (cb) {
  2010. cb()
  2011. }
  2012. }
  2013. }
  2014.  
  2015. function getSafeLinkingNetLinks (cb) {
  2016. // Get all the links by following each link
  2017.  
  2018. const crypticUrls = []
  2019. $('div.links-container.result-form a.result-a').each(function () {
  2020. if (this.getAttribute('href') && this.getAttribute('href').indexOf('/d/') !== -1) { crypticUrls.push(this.getAttribute('href')) }
  2021. })
  2022.  
  2023. const urls = []
  2024. let total = 0
  2025.  
  2026. let j = 1
  2027. for (let i = 0; i < crypticUrls.length; i++) {
  2028. GM.xmlHttpRequest({
  2029. method: 'GET',
  2030. url: crypticUrls[i],
  2031. onload: (function (j) {
  2032. return function (response) {
  2033. const loc = response.finalUrl // Actual link after posible redirections
  2034. urls.push(loc)
  2035. setStatus('Decrypting: ' + urls.length + '/' + total, -1)
  2036. if (total === urls.length) {
  2037. // Got all links
  2038. cb()
  2039. menu(urls)
  2040. setStatus('Found ' + (total === 1 ? 'one link' : (total + ' links')), 1)
  2041. }
  2042. }
  2043. }(j))
  2044. })
  2045. j++
  2046. }
  2047. total = j - 1
  2048. };
  2049.  
  2050. const linkSelectorFilter = {
  2051. _filter: function (key) {
  2052. const a = Array.prototype.slice.call(arguments, 1)
  2053. return function () {
  2054. linkSelectorFilter[key].apply(linkSelectorFilter, a)
  2055. }
  2056. },
  2057.  
  2058. all: function (trs) {
  2059. for (let i = 0; i < trs.length; i++) {
  2060. trs[i].$check.prop('checked', true)
  2061. }
  2062. },
  2063. none: function (trs) {
  2064. for (let i = 0; i < trs.length; i++) {
  2065. trs[i].$check.prop('checked', false)
  2066. }
  2067. },
  2068. flip: function (trs) {
  2069. for (let i = 0; i < trs.length; i++) {
  2070. trs[i].$check.prop('checked', !trs[i].$check.prop('checked'))
  2071. }
  2072. },
  2073.  
  2074. has: function (trs, inpFilter) {
  2075. const s = inpFilter.val()
  2076. for (let i = 0; i < trs.length; i++) {
  2077. if (trs[i].link.indexOf(s) !== -1) {
  2078. trs[i].$check.prop('checked', !trs[i].$check.prop('checked'))
  2079. }
  2080. }
  2081. },
  2082.  
  2083. host: function (trs, $selHost) {
  2084. const h = $selHost.val()
  2085.  
  2086. for (let i = 0; i < trs.length; i++) {
  2087. if (trs[i].host === h) {
  2088. trs[i].$check.prop('checked', !trs[i].$check.prop('checked'))
  2089. }
  2090. }
  2091. },
  2092.  
  2093. fromto: function (trs, $table, $thead, $th) {
  2094. const _self = this
  2095.  
  2096. for (let i = 0; i < trs.length; i++) {
  2097. trs[i].$check.prop('disabled', true)
  2098. }
  2099. $table.find('td').hover(function () {
  2100. $(this).parent().find('td').each(function (i, e) {
  2101. $(e).css('background', 'PaleGreen')
  2102. })
  2103. }, function () {
  2104. $(this).parent().find('td').each(function (i, e) {
  2105. $(e).css('background', '')
  2106. })
  2107. })
  2108. $thead.find('th').css('display', 'none')
  2109. $th.css('display', '')
  2110. $th.html('Select from where to start')
  2111.  
  2112. $table.find('td').click(function () {
  2113. const from = $(this.parentNode).data('index')
  2114. $(this).parent().find('td').css('background', 'PaleGreen')
  2115. $table.find('td').unbind('click mouseenter mouseleave')
  2116. $th.html('Select where to stop')
  2117.  
  2118. $table.find('td').hover(function () {
  2119. const to = $(this.parentNode).data('index')
  2120. $table.find('td').each(function (i, e) {
  2121. const $e = $(e)
  2122. const j = $e.parent().data('index')
  2123. if (j > from && j <= to) {
  2124. $e.css('background', 'DarkSeaGreen')
  2125. } else if (j > from && j > to) {
  2126. $e.css('background', '')
  2127. }
  2128. })
  2129. if ($(this).parent().data('index') > from) $(this).parent().find('td').css('background', 'PaleGreen')
  2130. })
  2131. $table.find('td').filter(function (i, e) { return $(e.parentNode).data('index') > from }).click(function () {
  2132. const to = $(this.parentNode).data('index') + 1
  2133.  
  2134. $table.find('td').unbind('click mouseenter mouseleave')
  2135. $(this).parent().find('td').css('background', 'PaleGreen')
  2136. $table.find('td').each(function (i, e) {
  2137. const $e = $(e)
  2138. const j = $e.parent().data('index')
  2139. if (j < from || j >= to) {
  2140. $e.css('display', 'none')
  2141. }
  2142. })
  2143.  
  2144. const ntrs = trs.slice(from, to)
  2145. for (let i = 0; i < ntrs.length; i++) {
  2146. ntrs[i].$check.prop('disabled', false)
  2147. }
  2148.  
  2149. $th.html('Select ')
  2150. $('<button>').appendTo($th).text('all').click(_self._filter('all', ntrs))
  2151. $('<button>').appendTo($th).text('none').click(_self._filter('none', ntrs))
  2152. $('<button>').appendTo($th).text('flip').click(_self._filter('flip', ntrs))
  2153. $('<button>').appendTo($th).text('return to all links').click(function () {
  2154. $table.find('td').each(function (i, e) {
  2155. const $e = $(e)
  2156. $e.css('display', '')
  2157. $e.css('background', '')
  2158. })
  2159. $thead.find('th').css('display', '')
  2160. $th.css('display', 'none')
  2161. $th.html('')
  2162. for (let i = 0; i < trs.length; i++) {
  2163. trs[i].$check.prop('disabled', false)
  2164. }
  2165. })
  2166. $th[0].scrollIntoView()
  2167.  
  2168. return false
  2169. })
  2170. })
  2171. },
  2172.  
  2173. every: function (trs, $table, $thead, $th) {
  2174. const _self = this
  2175.  
  2176. for (let i = 0; i < trs.length; i++) {
  2177. trs[i].$check.prop('disabled', true)
  2178. }
  2179. $table.find('td').hover(function () {
  2180. $(this).parent().find('td').each(function (i, e) {
  2181. $(e).css('background', 'PaleGreen')
  2182. })
  2183. }, function () {
  2184. $(this).parent().find('td').each(function (i, e) {
  2185. $(e).css('background', '')
  2186. })
  2187. })
  2188. $thead.find('th').css('display', 'none')
  2189. $th.css('display', '')
  2190. $th.html('Select from where to start')
  2191.  
  2192. $table.find('td').click(function () {
  2193. const from = $(this.parentNode).data('index')
  2194. $(this).parent().find('td').css('background', 'PaleGreen')
  2195. $table.find('td').unbind('click mouseenter mouseleave')
  2196. $th.html('Select next')
  2197.  
  2198. $table.find('td').hover(function () {
  2199. const to = $(this.parentNode).data('index')
  2200. const diff = to - from
  2201. if (to < from + 2) {
  2202. $table.find('td').filter(function (i, e) { return $(e.parentNode).data('index') > from + 1 }).css('background', '')
  2203. } else {
  2204. $table.find('td').filter(function (i, e) { return $(e.parentNode).data('index') > from + 1 }).each(function (i, e) {
  2205. const j = $(this.parentNode).data('index')
  2206. if ((j - from) % diff === 0 && j > from + 1) {
  2207. $(this).css('background', 'PaleGreen')
  2208. } else {
  2209. $(this).css('background', '')
  2210. }
  2211. })
  2212. $(this).parent().find('td').css('background', 'DarkSeaGreen')
  2213. }
  2214. }).click(function () {
  2215. const to = $(this.parentNode).data('index')
  2216.  
  2217. if (to < from + 2) return false
  2218.  
  2219. $(this).parent().find('td').css('background', 'PaleGreen')
  2220.  
  2221. const diff = to - from
  2222.  
  2223. $table.find('td').unbind('click mouseenter mouseleave')
  2224.  
  2225. $table.find('td').each(function (i, e) {
  2226. const $e = $(e)
  2227. const j = $e.parent().data('index')
  2228. if ((j - from) % diff !== 0 || j < from) {
  2229. $e.css('display', 'none')
  2230. }
  2231. })
  2232.  
  2233. const ntrs = []
  2234. for (let i = 0; i < trs.length; i++) {
  2235. if ((i - from) % diff === 0 && i >= from) {
  2236. trs[i].$check.prop('disabled', false)
  2237. ntrs.push(trs[i])
  2238. }
  2239. }
  2240.  
  2241. $th.html('Select ')
  2242. $('<button>').appendTo($th).text('all').click(_self._filter('all', ntrs))
  2243. $('<button>').appendTo($th).text('none').click(_self._filter('none', ntrs))
  2244. $('<button>').appendTo($th).text('flip').click(_self._filter('flip', ntrs))
  2245. $('<button>').appendTo($th).text('return to all links').click(function () {
  2246. $table.find('td').each(function (i, e) {
  2247. const $e = $(e)
  2248. $e.css('display', '')
  2249. $e.css('background', '')
  2250. })
  2251. $thead.find('th').css('display', '')
  2252. $th.css('display', 'none')
  2253. $th.html('')
  2254. for (let i = 0; i < trs.length; i++) {
  2255. trs[i].$check.prop('disabled', false)
  2256. }
  2257. })
  2258. $th[0].scrollIntoView()
  2259.  
  2260. return false
  2261. })
  2262. })
  2263. }
  2264.  
  2265. }
  2266.  
  2267. function linkSelector (links) {
  2268. const filter = function (key) {
  2269. const a = Array.prototype.slice.call(arguments, 1)
  2270. return function () {
  2271. linkSelectorFilter[key].apply(linkSelectorFilter, a)
  2272. }
  2273. }
  2274.  
  2275. const trs = []
  2276.  
  2277. const selectedLinks = []
  2278. // Coyp array and remove empty elements
  2279. for (let i = 0; i < links.length; i++) {
  2280. const t = $.trim(links[i])
  2281. if (t) {
  2282. selectedLinks.push(t)
  2283. }
  2284. }
  2285.  
  2286. if (linksBeforeSelection === false) {
  2287. linksBeforeSelection = links.slice(0) // Save all links for later selections
  2288. }
  2289. const allLinks = linksBeforeSelection.slice(0)
  2290.  
  2291. const popup = popUp('linkSelector')
  2292. const $div = popup.node
  2293. const $loader = $('<div style="width:20px; height:20px;" class="ochspinner"></div>').appendTo($div)
  2294. $div.css('overflow', 'none')
  2295. const $frame = $('<iframe style="border:0">').appendTo($div)
  2296. $frame.attr('width', window.innerWidth - 190)
  2297. $frame.attr('height', window.innerHeight - 120)
  2298. $frame.bind('load', function (e) {
  2299. const $body = $($frame[0].contentDocument.getElementsByTagName('body')[0])
  2300.  
  2301. const $main = $('<div>').appendTo($body)
  2302.  
  2303. const $table = $('<table>').appendTo($main)
  2304. const $thead = $('<thead>').appendTo($table)
  2305.  
  2306. const $tr0 = $('<tr>').appendTo($thead)
  2307. const $th0 = $('<th>').appendTo($tr0).attr('colspan', 2)
  2308. const $tr1 = $('<tr>').appendTo($thead)
  2309. const $th1 = $('<th>').appendTo($tr1).attr('colspan', 2)
  2310. const $tr2 = $('<tr>').appendTo($thead)
  2311. const $th2 = $('<th>').appendTo($tr2).attr('colspan', 2)
  2312. const $tr3 = $('<tr>').appendTo($thead)
  2313. const $th3 = $('<th>').appendTo($tr3).attr('colspan', 2)
  2314. const $tr4 = $('<tr>').appendTo($thead)
  2315. const $th4 = $('<th>').appendTo($tr4).attr('colspan', 2)
  2316.  
  2317. $('<span>Select: <span>').appendTo($th0)
  2318. $('<button>').appendTo($th0).text('all').click(filter('all', trs))
  2319. $('<button>').appendTo($th0).text('none').click(filter('none', trs))
  2320. $('<button>').appendTo($th0).text('flip').click(filter('flip', trs))
  2321.  
  2322. $('<button>').appendTo($th1).text('Select from ... to ...').click(filter('fromto', trs, $table, $thead, $th4))
  2323. $('<button>').appendTo($th1).text('Select every ...').click(filter('every', trs, $table, $thead, $th4))
  2324.  
  2325. $('<span> Filter:<span>').appendTo($th2)
  2326. const inpFilter = $('<input>').appendTo($th2).attr('type', 'text')
  2327. $('<button>').appendTo($th2).text('Flip with filter').click(filter('has', trs, inpFilter))
  2328.  
  2329. $('<span> Host filter:<span>').appendTo($th3)
  2330. const $selHost = $('<select>').appendTo($th3)
  2331. $('<button>').appendTo($th3).text('Flip with host filter').click(filter('has', trs, $selHost))
  2332.  
  2333. const allhosts = []
  2334. for (let i = 0; i < allLinks.length; i++) {
  2335. const $tr = $('<tr>').data('index', i).appendTo($table)
  2336. const $td0 = $('<td>').appendTo($tr)
  2337. const $check = $('<input>').appendTo($td0).attr('type', 'checkbox').attr('id', 'link_checkbox_' + i).prop('checked', selectedLinks.indexOf(allLinks[i]) !== -1)
  2338. const $td1 = $('<td>').appendTo($tr)
  2339. $('<label>').attr('for', 'link_checkbox_' + i).text(allLinks[i]).css('font-family', 'monospace').appendTo($td1)
  2340.  
  2341. const host = allLinks[i].split('/')[2].replace(/^www\./, '')
  2342. if (allhosts.indexOf(host) === -1) {
  2343. allhosts.push(host)
  2344. }
  2345.  
  2346. trs.push({ $tr, $check, link: allLinks[i], host })
  2347. }
  2348.  
  2349. for (let i = 0; i < allhosts.length; i++) {
  2350. $('<option>').val(allhosts[i]).text(allhosts[i]).appendTo($selHost)
  2351. }
  2352.  
  2353. $('<button>').appendTo($main).text('Apply').click(function () {
  2354. const nlinks = []
  2355. for (let i = 0; i < trs.length; i++) {
  2356. if (trs[i].$check.prop('checked')) {
  2357. nlinks.push(trs[i].link)
  2358. }
  2359. }
  2360. if (nlinks.length === 0) {
  2361. alert('No links selected?!')
  2362. return
  2363. }
  2364. menu(nlinks)
  2365. setStatus((nlinks.length === 1 ? 'One link' : (nlinks.length + ' links')) + ' selected', 1)
  2366. popup.close()
  2367. })
  2368.  
  2369. $loader.remove()
  2370. })
  2371. if (chrome) {
  2372. $frame.attr('src', 'about:blank')
  2373. }
  2374. }
  2375.  
  2376. function menu (links) {
  2377. // normalize links:
  2378. if (!Array.isArray(links)) {
  2379. const parts = links.split('\n')
  2380. links = []
  2381. for (let i = 0; i < parts.length; i++) {
  2382. if ($.trim(parts[i])) {
  2383. links.push($.trim(parts[i]))
  2384. }
  2385. }
  2386. }
  2387.  
  2388. const $c = $('#multiochhelper ul')
  2389. $c.html('')
  2390.  
  2391. const $select = $('<select>')
  2392. const m = links[0].match(/https?:\/\/(.+?)\//)
  2393. if (!m) {
  2394. console.log(scriptName + ": Not a valid link: '" + links[0] + "'")
  2395. return
  2396. }
  2397. const host = m[1]
  2398. let hoster = host.split('.')
  2399. hoster.pop()
  2400. hoster = hoster.pop().replace('-', '')
  2401. $.each(multi, function (key, val) {
  2402. const $option = $('<option></option>').val(key).html(val.name).appendTo($select)
  2403. if (key === currentdebrid) {
  2404. $option[0].selected = true
  2405. }
  2406. if (multi[key].isOnline(hoster)) {
  2407. $option.css('color', 'green')
  2408. } else {
  2409. $option.css('color', '#F00')
  2410. }
  2411. })
  2412. let $entry = menuentry($select)
  2413. $select.bind('change', function (ev) {
  2414. const $this = $(this)
  2415. // Change hoster
  2416. currentdebrid = $this.val()
  2417.  
  2418. // Check general support
  2419. if (multi[currentdebrid].isOnline(hoster)) {
  2420. // Check first link for support on this multi hoster
  2421. multi[currentdebrid].checkLink(links[0], function (result) {
  2422. if (!result) {
  2423. alert(scriptName + '\n\n' + host + ' is not supported by this hoster or the file is offline.\n\nChecked: ' + links[0])
  2424. }
  2425. })
  2426. } else {
  2427. alert(scriptName + '\n\n' + host + ' is not supported by ' + multi[currentdebrid].name)
  2428. }
  2429.  
  2430. // Add "Remember" checkbox
  2431. if (!$this.parent().find('#remember').length) {
  2432. const $div = $('<div>')
  2433. const $check = $('<input id="remember" type="checkbox" value="remember" title="Remember selection">').click(async function () {
  2434. if (this.checked) {
  2435. currentdebrid = $select.val()
  2436. await GM.setValue('currentdebrid', currentdebrid)
  2437. setStatus('Switched to ' + multi[currentdebrid].name, 1)
  2438. $div.remove()
  2439. }
  2440. })
  2441. $div.append($check).append('Remember')
  2442. $this.parent().append($div)
  2443. }
  2444. })
  2445.  
  2446. $entry = menuentry('Direct download')
  2447. $entry.click(function () { mouse('download', links) })
  2448.  
  2449. $entry = menuentry('Copy to clipboard')
  2450. $entry.click(function () { mouse('clipboard', links) })
  2451.  
  2452. if (settings.jDownloaderSupport) {
  2453. $entry = menuentry('Send to JDownloader')
  2454. $entry.attr('id', 'multiochhelperjdbutton')
  2455. $entry.hide()
  2456. $entry.click(function () { mouse('sendToJD', links) })
  2457. GM.xmlHttpRequest({
  2458. method: 'GET',
  2459. url: JDOWNLOADER + 'flash/',
  2460. onerror: function () {
  2461. },
  2462. onload: function (resp) {
  2463. if (resp && resp.responseText && resp.responseText.startsWith('JDownloader')) {
  2464. $('#multiochhelperjdbutton').show()
  2465. }
  2466. }
  2467. })
  2468. }
  2469.  
  2470. if (!showOneclickFromHighlighScriptAllLinks) {
  2471. $entry = menuentry('Show generated links')
  2472. $entry.click(function () { mouse('showLinks', links) })
  2473. }
  2474.  
  2475. $entry = menuentry('Show extracted links')
  2476. $entry.click(function () {
  2477. if (window.parent.parent !== window) {
  2478. window.parent.parent.postMessage({ iAm: 'Unrestrict.li', type: 'alert', str: links.join('\n') }, '*')
  2479. alert(links.join('\n'))
  2480. } else {
  2481. showExtractedLinks(links)
  2482. }
  2483. })
  2484.  
  2485. if (!showOneclickFromHighlighScriptAllLinks && (links.length > 1 || linksBeforeSelection !== false)) {
  2486. $entry = menuentry('Select links')
  2487. $entry.click(function () { linkSelector(links) })
  2488. }
  2489.  
  2490. if (!showOneclickFromHighlighScriptAllLinks) {
  2491. $entry = menuentry()
  2492. $('<a style="color:white !important;">Open Website</a>').attr('href', getMultiOCHWebsiteURL(links)).appendTo($entry)
  2493. }
  2494.  
  2495. if (showOneclickFromHighlighScriptAllLinks && showOneclickFromHighlighScriptAllLinksLinks) {
  2496. $entry = $(menuentry('Use all links on page...'))
  2497. $entry.click(function () {
  2498. // Switch to all links instead of one
  2499. const links = showOneclickFromHighlighScriptAllLinksLinks
  2500. showOneclickFromHighlighScriptAllLinksLinks = ''
  2501. menu(links)
  2502. $('#multiochhelper div:empty:not(:first)').remove()
  2503. setStatus('All links!', 1)
  2504. })
  2505. }
  2506.  
  2507. if (showOneclickFromHighlighScriptSelectedLinks && showOneclickFromHighlighScriptSelectedLinksLinks) {
  2508. $entry = $(menuentry('Use selected links...'))
  2509. $entry.click(function () {
  2510. // Switch to selected links instead of one
  2511. const links = showOneclickFromHighlighScriptSelectedLinksLinks
  2512. showOneclickFromHighlighScriptSelectedLinksLinks = ''
  2513. menu(links)
  2514. $('#multiochhelper div:empty:not(:first)').remove()
  2515. setStatus('Using selected links!', 1)
  2516. })
  2517. }
  2518.  
  2519. if (!showOneclickFromHighlighScriptAllLinks) {
  2520. $entry = menuentry($('<span style="cursor:default; color:silver">Userscript menu</span>').click(function (ev) { ev.stopPropagation(); aboutMe() }))
  2521. $entry.css('cursor', 'default')
  2522. $('<span style="cursor:pointer; color:White; border: 1px solid White; border-radius:3px; padding:0px; margin-left:20px; font-weight:bold ; " title="Close menu">X</span>').click(function () { $('#multiochhelper').remove() }).appendTo($entry)
  2523. }
  2524. }
  2525.  
  2526. function loader () {
  2527. // Show an animation, return function to remove the loader
  2528. $('#multiochhelper_status_loader').parent().show()
  2529. const $div = $('<div class="ochspinner"></div>').appendTo($('#multiochhelper_status_loader'))
  2530. return function () {
  2531. $div.remove()
  2532. }
  2533. }
  2534.  
  2535. async function mouse (action, linkText) {
  2536. // decide what to do after a mouse click
  2537. const removeImg = loader()
  2538. if (action === 'download') {
  2539. await download(linkText, removeImg)
  2540. } else if (action === 'showLinks') {
  2541. showLinks(linkText, removeImg)
  2542. } else if (action === 'openWebsite') {
  2543. openWebsite(linkText)
  2544. } else if (action === 'clipboard') {
  2545. await clipboard(linkText, removeImg)
  2546. } else if (action === 'menu') {
  2547. removeImg()
  2548. menu(linkText)
  2549. } else if (action === 'sendToJD') {
  2550. await sendToJD(linkText, removeImg)
  2551. }
  2552. }
  2553.  
  2554. function menuentry (html) {
  2555. const $li = $('<li>')
  2556. if (html) {
  2557. $li.append(html)
  2558. }
  2559. $li.appendTo('#multiochhelper ul')
  2560. return $li
  2561. }
  2562.  
  2563. function button (label) {
  2564. addCSSHead(`
  2565. #multiochhelper,#multiochhelper * {
  2566. font-family:Sans-Serif !important;
  2567. padding:0px; margin:0px;
  2568. }
  2569. #multiochhelper a, #multiochhelper a:link,#multiochhelper a:visited {
  2570. text-decoration:underline !important;
  2571. color:#3788e8 !important;
  2572. font-style:none !important;
  2573. }
  2574. #multiochhelper a:hover {
  2575. text-decoration:none !important;
  2576. color:#3788e8 !important;
  2577. font-style:none !important;
  2578. }
  2579. #multiochhelper ul li,#multiochhelper_status {
  2580. margin:1px 1px;
  2581. padding:1px 5px;
  2582. font-size:13px;
  2583. text-shadow:0 -1px 0 #333333;
  2584. color:White;
  2585. border:1px solid #8B3D92;
  2586. background-color:#B555C5;
  2587. background:radial-gradient(ellipse at center, #B555C5, #8B3D92);
  2588. list-style:none outside;
  2589. }
  2590. #multiochhelper div#multiochhelper_status_loader {
  2591. float:left;
  2592. }
  2593. #multiochhelper div#multiochhelper_status_text {
  2594. float:left;
  2595. }
  2596. #multiochhelper div#multiochhelper_status_clear {
  2597. clear:left;
  2598. }
  2599. #multiochhelper ul li {
  2600. cursor:pointer;
  2601. }
  2602. #multiochhelper ul li:hover {
  2603. background-color:#CC6BDD;
  2604. background:radial-gradient(ellipse at center, #CC6BDD, #8B3D92);
  2605. }
  2606. #multiochhelper select,#multiochhelper input {
  2607. border-radius:0;
  2608. box-shadow:none;
  2609. text-shadow:none;
  2610. border:none;
  2611. background:white;
  2612. color:black;
  2613. }
  2614.  
  2615. ${SPINNERCSS}
  2616. `)
  2617.  
  2618. // div container
  2619. const zi = getNextZIndex()
  2620. const $div = $('<div>').appendTo(document.body)
  2621. $div.attr('id', 'multiochhelper')
  2622. $div.attr('style', 'z-index:' + zi + '; position:fixed; background:#E6E6E6; color:Black; border:#B555C5 2px solid;border-radius:5px; padding:3px;')
  2623. if (settings.position[0] === 'top') {
  2624. $div.css('top', '0%')
  2625. } else {
  2626. $div.css('bottom', '0%')
  2627. }
  2628. if (settings.position[1] === 'left') {
  2629. $div.css('left', '0%')
  2630. } else {
  2631. $div.css('right', '0%')
  2632. }
  2633. // Status
  2634. const $status = $('<div>').appendTo($div).hide()
  2635. $status.attr('id', 'multiochhelper_status')
  2636. const $loader = $('<div>').appendTo($status)
  2637. $loader.attr('id', 'multiochhelper_status_loader')
  2638. const $statustext = $('<div>').appendTo($status)
  2639. $statustext.attr('id', 'multiochhelper_status_text')
  2640. const $statusclear = $('<div>').appendTo($status)
  2641. $statusclear.attr('id', 'multiochhelper_status_clear')
  2642.  
  2643. const $ul = $('<ul>').appendTo($div)
  2644.  
  2645. // Button
  2646. const $entry = menuentry(label || (multi[currentdebrid].name.charAt(0).toUpperCase() + multi[currentdebrid].name.slice(1)))
  2647.  
  2648. $ul.append($entry)
  2649.  
  2650. return $entry
  2651. }
  2652.  
  2653. const isSetup = await GM.getValue('setup', false)
  2654.  
  2655. // Update hoster status
  2656. let updatinghosters = false
  2657. if (isSetup) {
  2658. for (const key in multi) {
  2659. if (multi[key].updateStatusURLpattern.test(document.location.href)) { // usually in this is true in the iframe which is defined below
  2660. multi[key].updateStatus()
  2661. updatinghosters = true
  2662. break
  2663. }
  2664. }
  2665. }
  2666.  
  2667. // Create iframes to check hoster status:
  2668. if (!updatinghosters && isSetup) {
  2669. const now = new Date()
  2670. for (const key in multi) {
  2671. if ('updateStatusURL' in multi[key] && (now - multi[key].lastUpdate) > (settings.updateHosterStatusInterval * 60 * 60 * 1000)) {
  2672. const $iframe = $('<embed>').appendTo(document.body)
  2673. $iframe.bind('load', function () {
  2674. const frame = this
  2675. window.setTimeout(function () { $(frame).remove() }, 3000)
  2676. })
  2677. $iframe.attr('src', multi[key].updateStatusURL)
  2678. }
  2679. }
  2680. }
  2681.  
  2682. // Setup
  2683. if (!updatinghosters) {
  2684. if (!isSetup) {
  2685. await aboutMe()
  2686. if (!confirm(scriptName + ' Setup\n\nPlease take some time to configure ' + scriptName + ' and then save the settings!\n\nPress cancel to continue with the default configuration!')) {
  2687. await GM.setValue('setup', true)
  2688. alert(scriptName + '\n\nDefault settings will be used.')
  2689. document.location.reload()
  2690. }
  2691. }
  2692. }
  2693.  
  2694. if (document.location.href.indexOf('nopremium.pl') !== -1) {
  2695. // nopremium.pl Website
  2696. if (document.location.search.substring(0, 6) === '?link:') {
  2697. // Insert link on nopremium.pl
  2698. $('#filesList').val(decodeURIComponent(document.location.search.substring(6)))
  2699. }
  2700. } else if (document.location.href.indexOf('www.premiumize.me') !== -1) {
  2701. // premiumize.me Website
  2702. if (document.location.search.substring(0, 6) === '?link:') {
  2703. // Insert link on nopremium.pl
  2704. $('textarea').val(decodeURIComponent(document.location.search.substring(6)))
  2705. }
  2706. } else if (document.location.href.indexOf('download.serienjunkies.org') !== -1) {
  2707. // Serienjunkies
  2708. if (!document.querySelector('.g-recaptcha')) { // if not on captcha page
  2709. const $b = button('Decrypt links')
  2710. $b.click(function (ev) {
  2711. const removeImg = loader()
  2712. getSerienjunkiesLinks(removeImg)
  2713. })
  2714. }
  2715. } else if (document.location.href === 'http://filecloud.io/download.html') {
  2716. // filecloud.io
  2717. if (unsafeWindow.__currentUrl) {
  2718. showOneclickButton = true
  2719. showOneclickLink = decodeURIComponent(unsafeWindow.__currentUrl)
  2720. }
  2721. } else if (document.location.href.indexOf('filecrypt.cc') !== -1) {
  2722. // filecrypt.cc folder
  2723. if (document.location.href.indexOf('helper.html') !== -1) { // if not on captcha page
  2724. window.addEventListener('message', function filecryptmessage (event) {
  2725. if (event.data && typeof (event.data) === 'object') {
  2726. window.opener.postMessage({ filecryptData: JSON.stringify(event.data) }, '*') // Send message back to the opening window
  2727. window.removeEventListener('message', filecryptmessage) // Prevent further messages from creating several buttons
  2728. }
  2729. }, false)
  2730. } else if (document.location.href.indexOf('Container') !== -1) { // if not on captcha page
  2731. const $b = button("Please open the Click'n'Load Popup (several times)")
  2732. $b.click(function () {
  2733. $('#cnl_btn').click()
  2734. })
  2735. window.addEventListener('message', function filecryptmessage2 (event) { // Receive messages from the popup
  2736. if (event.data && typeof (event.data) === 'object' && 'filecryptData' in event.data) {
  2737. window.removeEventListener('message', filecryptmessage2) // Prevent further messages from creating several buttons
  2738. setStatus('Decrypting', -1)
  2739. const removeImg = loader()
  2740. getFilecryptcc(JSON.parse(event.data.filecryptData), removeImg)
  2741. }
  2742. }, false)
  2743. }
  2744. } else if (document.location.href.substring(7, 22) === 'protected.to/f-') {
  2745. // http://protected.to folder
  2746. if (document.querySelectorAll('.links a').length > 0) { // If not on captcha page
  2747. showOneclickButton = true
  2748. showOneclickLink = ''
  2749. $('.links a').each(function () {
  2750. showOneclickLink += decodeURIComponent(this.href) + '\n'
  2751. })
  2752. }
  2753. } else if (document.location.href.substring(8, 23) === 'safelinking.net') {
  2754. // safelinking.net folder
  2755. if (!document.getElementById('captcha-wrapper')) {
  2756. const $b = button('Decrypt links')
  2757. $b.click(function (ev) {
  2758. const removeImg = loader()
  2759. getSafeLinkingNetLinks(removeImg)
  2760. })
  2761. }
  2762. } else if (document.location.href.indexOf('.firedrive.com/share/') !== -1) {
  2763. // firedrive.com folder
  2764. showOneclickButton = true
  2765. showOneclickLink = ''
  2766. $('a.pf_item_link:visible').each(function () {
  2767. showOneclickLink += decodeURIComponent(this.href) + '\n'
  2768. })
  2769. } else if (document.location.href.indexOf('rapidgator.net/folder/') !== -1) {
  2770. // Rapidgator folder
  2771. showOneclickButton = true
  2772. showOneclickLink = ''
  2773. $('#grid tbody a').each(function () {
  2774. showOneclickLink += decodeURIComponent(this.href) + '\n'
  2775. })
  2776. } else if (document.location.hostname === "dailyuploads.net" && currentdebrid === 'premiumize.me') {
  2777. // Dailyuploads.net: submit direct download link (after captcha was solved) to premiumize.me instead of link
  2778. if (document.querySelector('div.banner div.inner a>img[src*="redbutton.png"]')) {
  2779. showOneclickButton = true
  2780. showOneclickLink = document.querySelector('div.banner div.inner a>img[src*="redbutton.png"]').parentNode.href
  2781. } else {
  2782. showOneclickButton = false
  2783. button("Please solve the captcha first")
  2784. }
  2785. } else if (document.location.hostname === 'multiup.org') {
  2786. // Multiup.org mirrors
  2787. showOneclickButton = document.querySelectorAll('button[link]').length > 0
  2788. showOneclickLink = Array.from(document.querySelectorAll('button[link]')).map(b => b.getAttribute('link')).join('\n')
  2789. } else if (document.location.href.substring(0, 55) === 'https://cvzi.github.io/Userscripts/index.html?link=sync') {
  2790. // Window opened from Helper script to sync hoster status (see postMessage events below)
  2791. showOneclickButton = false
  2792. const message = 'Updating hoster status...'
  2793. const h1 = document.body.appendChild(document.createElement('h1'))
  2794. h1.appendChild(document.createTextNode(scriptHightligherName + ': ' + message))
  2795. setTitle('')
  2796. window.setTimeout(function () {
  2797. const h2 = document.body.appendChild(document.createElement('h2'))
  2798. h2.appendChild(document.createTextNode('You may close this tab now'))
  2799. }, 4000)
  2800. } else if (document.location.href.substring(0, 51) === 'https://cvzi.github.io/Userscripts/index.html?link=') {
  2801. // Iframe for a X-Frame-Options website
  2802. showOneclickButton = true
  2803. showOneclickLink = decodeURIComponent(document.location.search.match(/link=(.+)/)[1])
  2804. } else {
  2805. // One click hoster website
  2806. showOneclickButton = true
  2807. showOneclickLink = decodeURIComponent(document.location.href)
  2808. }
  2809.  
  2810. if (showOneclickButton) {
  2811. let mouseOverAvailable = true
  2812.  
  2813. // Split links into array
  2814. const splitted = showOneclickLink.split('\n')
  2815. showOneclickLink = []
  2816. for (let i = 0; i < splitted.length; i++) {
  2817. if ($.trim(splitted[i])) {
  2818. showOneclickLink.push($.trim(splitted[i]))
  2819. }
  2820. }
  2821.  
  2822. const $b = button()
  2823.  
  2824. $b.bind('mousedown',
  2825. function (ev) {
  2826. mouseOverAvailable = false
  2827. if (ev.which === 3) { // Right click
  2828. mouse(settings.rightClick, showOneclickLink)
  2829. } else if (ev.which === 2) { // Middle click
  2830. mouse(settings.middleClick, showOneclickLink)
  2831. } else if (ev.which === 1) { // Left click {
  2832. mouse(settings.leftClick, showOneclickLink)
  2833. }
  2834. })
  2835. if (settings.mouseOver !== 'none') {
  2836. let ti = false
  2837. $b.on({
  2838. mouseover: function () {
  2839. if (!mouseOverAvailable) { return }
  2840. ti = setTimeout(function () {
  2841. if (!mouseOverAvailable) { return }
  2842. mouseOverAvailable = false
  2843. mouse(settings.mouseOver, showOneclickLink)
  2844. }, settings.mouseOverDelay)
  2845. },
  2846. mouseout: function () {
  2847. if (ti !== false) clearTimeout(ti)
  2848. }
  2849. })
  2850. }
  2851. // Prevent context menu on right click
  2852. if (settings.rightClick !== 'none') {
  2853. $b[0].addEventListener('contextmenu', e => e.preventDefault(), false)
  2854. }
  2855. }
  2856.  
  2857. // Handle messages from the highlight script
  2858. window.addEventListener('message', function (e) {
  2859. if (typeof e.data !== 'object' || !('iAm' in e.data) || e.data.iAm !== 'Unrestrict.li') {
  2860. return
  2861. }
  2862.  
  2863. switch (e.data.type) {
  2864. case 'alllinks':
  2865. if (showOneclickFromHighlighScriptAllLinks) {
  2866. return
  2867. }
  2868. showOneclickFromHighlighScriptAllLinks = true
  2869. showOneclickFromHighlighScriptAllLinksLoc = e.data.loc
  2870. showOneclickFromHighlighScriptAllLinksLinks = e.data.links.join('\n')
  2871. if ($('#multiochhelper ul li').length > 1) { // Menu already opened
  2872. menu(showOneclickLink)
  2873. }
  2874. break
  2875. case 'selectedlinks':
  2876. if (showOneclickFromHighlighScriptSelectedLinks) {
  2877. return
  2878. }
  2879. showOneclickFromHighlighScriptSelectedLinks = true
  2880. showOneclickFromHighlighScriptSelectedLinksLoc = e.data.loc
  2881. showOneclickFromHighlighScriptSelectedLinksLinks = e.data.links.join('\n')
  2882. if ($('#multiochhelper ul li').length > 1) { // Menu already opened
  2883. menu(showOneclickLink)
  2884. }
  2885. break
  2886. case 'requesthosterstatus': {
  2887. window.setTimeout(function () {
  2888. const h3 = document.body.appendChild(document.createElement('h3'))
  2889. h3.appendChild(document.createTextNode('This will only take a few seconds'))
  2890. }, 0)
  2891. const o = {}
  2892. for (const key in multi) {
  2893. o[key] = multi[key].status
  2894. }
  2895. e.source.postMessage({ iAm: 'Unrestrict.li', type: 'hosterstatus', str: JSON.stringify(o) }, '*')
  2896. break
  2897. }
  2898. }
  2899. }, true)
  2900. })()