NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Multi-OCH Helper // @namespace cuzi // @license MIT // @description nopremium.pl and premiumize.me. Inserts a direct download link on several one-click-hosters and some container/folder providers. // @homepageURL https://openuserjs.org/scripts/cuzi/Multi-OCH_Helper // @updateURL https://openuserjs.org/meta/cuzi/Multi-OCH_Helper.meta.js // @contributionURL https://buymeacoff.ee/cuzi // @contributionURL https://ko-fi.com/cuzicvzi // @icon https://raw.githubusercontent.com/cvzi/Userscripts/master/Multi-OCH/icons/helper.png // @version 17.1.4 // @match https://cvzi.github.io/Userscripts/index.html?link=* // @match https://www.nopremium.pl/files* // @match https://www.premiumize.me/hosters/* // @match https://www.premiumize.me/services/* // @match https://www.premiumize.me/downloader* // @match https://*.filecrypt.cc/Container/* // @match https://*.filecrypt.cc/helper.html* // @match https://protected.to/* // @match https://rapidgator.net/folder/* // @match https://safelinking.net/p/* // @match https://multiup.org/* // @match https://1fichier.com/* // @match https://*.1fichier.com/* // @match https://www.4shared.com/* // @match https://alfafile.net/* // @match https://*.alfafile.net/* // @match https://anonfiles.com/* // @match https://bayfiles.com/* // @match https://*.bayfiles.com/* // @match http://clicknupload.link/* // @match https://clicknupload.to/* // @match https://clicknupload.org/* // @match https://clicknupload.co/* // @match https://clicknupload.cc/* // @match https://clicknupload.to/* // @match https://clicknupload.club/* // @match https://clicknupload.click/* // @match https://clicknupload.space/* // @match https://dailyuploads.net/* // @match https://ddl.to/* // @match https://ddownload.com/* // @match https://*.dropapk.com/* // @match https://dropapk.com/* // @match https://*.drop.download.com/* // @match https://drop.download.com/* // @match https://fastclick.to/* // @match https://fastshare.cz/* // @match https://fikper.com/* // @match https://file.al/* // @match https://www.file.al/* // @match https://filefactory.com/* // @match https://www.filefactory.com/* // @match https://filenext.com/* // @match https://www.filenext.com/* // @match https://filer.net/* // @match https://filerice.com/* // @match https://filespace.com/* // @match https://filestore.to/* // @match http://fireget.com/* // @match https://fireget.com/* // @match https://hitfile.net/* // @match https://hil.to/* // @match https://isra.cloud/* // @match https://katfile.com/* // @match https://www.mediafire.com/* // @match https://mediafire.com/* // @match https://mega.nz/* // @match https://megaup.net/* // @match https://mixdrop.co/* // @match https://modsbase.com/* // @match https://nitroflare.com/* // @match https://rapidgator.net/file/* // @match https://rg.to/file/* // @match https://spicyfile.com/* // @match https://www.spicyfile.com/* // @match https://turbobit.net/* // @match https://turb.to/* // @match https://tusfiles.net/* // @match https://ubiqfile.com/* // @match https://uploadboy.com/* // @match https://uploadgig.com/* // @match https://uptobox.com/* // @match https://userscloud.com/* // @match https://usersdrive.com/* // @match https://vidoza.org/* // @match https://worldbytez.com/* // @match https://wrzucajpliki.pl/* // @match https://xubster.com/* // @match https://*.zippyshare.com/* // @require https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js // @require https://greasyfork.org/scripts/13883-aes-js/code/aesjs.js // @grant GM.registerMenuCommand // @grant unsafeWindow // @grant GM_setClipboard // @grant GM.xmlHttpRequest // @grant GM.openInTab // @grant GM.setClipboard // @grant GM.setValue // @grant GM.getValue // @grant GM.deleteValue // @grant GM.listValues // ==/UserScript== /* globals confirm, alert, GM, GM_setClipboard, unsafeWindow, $, atob, slowAES, cloneInto */ /* eslint n/no-callback-literal: 0 */ /* jshint asi: true, esversion: 8 */ (async function () { 'use strict' // And to keep for myself whatever I may find? - Certainly. For yourself, and any friends you want to share with you. // This program inserts a download link on One-Click-Hosters and a few folder services. // 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. // // Standard actions for the button are // * left mouse click: copy the link to the clipboard // * middle/wheel click: start download of the link // * right mouse click: open the nopremium.pl website and insert the link in the text box // * hovering the mouse over the button: open a menu with all the above option // const scriptName = 'Multi-OCH Helper' const scriptReferer = 'multiochhelper' const scriptHightligherName = 'Multi-OCH Helper Highlight links' const chrome = ~navigator.userAgent.indexOf('Chrome') const greasemonkey = 'info' in GM && 'scriptHandler' in GM.info && GM.info.scriptHandler === 'Greasemonkey' const config = { position: [['bottom', 'top'], ['left', 'right']], position_desc: ['vertical', 'horizontal'], position_quest: 'Position of the Button. If you use "' + scriptHightligherName + '" this has to be set to bottom left', leftClick: ['clipboard', 'download', 'showLinks', 'openWebsite', 'menu', 'sendToJD', 'none'], 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'], leftClick_quest: 'Action on left mouse click on button', middleClick: ['download', 'clipboard', 'showLinks', 'openWebsite', 'menu', 'sendToJD', 'none'], 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'], middleClick_quest: 'Action on middle mouse/wheel click on button', rightClick: ['openWebsite', 'clipboard', 'showLinks', 'download', 'menu', 'sendToJD', 'none'], 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'], rightClick_quest: 'Action on right mouse click on button', mouseOver: ['menu', 'clipboard', 'download', 'showLinks', 'openWebsite', 'sendToJD', 'none'], 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'], mouseOver_quest: 'Action on mouse hover over button', mouseOverDelay: 'int', mouseOverDelay_range: [0, 700, 3000], mouseOverDelay_quest: 'Mouse hover time before action is executed.', mouseOverDelay_suffix: 'milliseconds', newTab: 'bool', newTab_desc: ['Open in a new tab', 'Open in the same window'], newTab_quest: 'Should websites be opened in a new tab?', updateHosterStatusInterval: 'int', updateHosterStatusInterval_range: [1, 168, 9999], updateHosterStatusInterval_quest: 'How often should the status of the hosters be updated?', updateHosterStatusInterval_prefix: 'Every', updateHosterStatusInterval_suffix: 'hours', jDownloaderSupport: 'bool', jDownloaderSupport_desc: ['Show JDownloader button if JDownloader is runnning', 'Never show JDownloader button'], jDownloaderSupport_quest: ['Show a JDownloader button in the menu?'] } const settings = {} // Load settings const savedsettings = JSON.parse(await GM.getValue('settings', '{}')) // e.g. { position : [0,1], newTab : 1 } for (const key in config) { if (key in savedsettings) { // Saved if (config[key] === 'int') { // Int settings[key] = parseInt(savedsettings[key], 10) } else if (config[key] === 'string') { // String settings[key] = savedsettings[key].toString() } else if (config[key] === 'bool') { // Bool settings[key] = (savedsettings[key] === 'true' || savedsettings[key] === true) } else if (Array.isArray(config[key][0])) { // Nested array if (!Array.isArray(savedsettings[key])) { try { const tmp = JSON.parse(savedsettings[key]) if (Array.isArray(tmp)) { savedsettings[key] = tmp } } catch (e) {} } settings[key] = [] for (let i = 0; i < savedsettings[key].length; i++) { settings[key].push(savedsettings[key][i]) } } else { // Array settings[key] = savedsettings[key] } } else { // Default if (config[key] === 'int') { // Int settings[key] = config[key + '_range'][1] } else if (config[key] === 'string') { // String settings[key] = '' // String defaults to empty string } else if (config[key] === 'bool') { // Bool settings[key] = true } else if (Array.isArray(config[key][0])) { // Nested array defaults to first value for each array settings[key] = [] for (let i = 0; i < config[key].length; i++) { settings[key].push(config[key][i][0]) } } else { settings[key] = config[key][0] // Array defaults to first value } } } const JDOWNLOADER = 'http://127.0.0.1:9666/' const SPINNERCSS = `/* http://www.designcouch.com/home/why/2013/05/23/dead-simple-pure-css-loading-spinner/ */ .ochspinner { height:16px; width:16px; margin:0px auto; position:relative; animation: rotation .6s infinite linear; border-left:6px solid rgba(0,174,239,.15); border-right:6px solid rgba(0,174,239,.15); border-bottom:6px solid rgba(0,174,239,.15); border-top:6px solid rgba(0,174,239,.8); border-radius:100%; } @keyframes rotation { from {transform: rotate(0deg)} to {transform: rotate(359deg)} } ` // const LOADINGBARBG = 'background: #b4e391;background: linear-gradient(to bottom, #b4e391 0%,#61c419 50%,#b4e391 100%);' let showOneclickButton = false let showOneclickLink = '' let showOneclickFromHighlighScriptAllLinks = document.location.host === 'cvzi.github.io' let showOneclickFromHighlighScriptAllLinksLoc = false let showOneclickFromHighlighScriptAllLinksLinks = '' let showOneclickFromHighlighScriptSelectedLinks = false let showOneclickFromHighlighScriptSelectedLinksLoc = false let showOneclickFromHighlighScriptSelectedLinksLinks = '' let linksBeforeSelection = false const multi = { 'premiumize.me': new function () { const self = this this.config = { apikey: 'string', apikey_hidden: true, apikey_quest: 'Enter your premiumize.me API key', apikey_prefix: 'API key: ', apikey_suffix: ' find it under <a target="_blank" href="https://www.premiumize.me/account">https://www.premiumize.me/account</a>' } this.key = 'premiumize.me' this.name = 'premiumize' this.homepage = 'https://www.premiumize.me/' // this.updateStatusURL = 'https://www.premiumize.me/services'; this.updateStatusURLpattern = /https:\/\/www\.premiumize\.me\/services\/?/ this.updateDownloadProgressInterval = 5000 this.updateDownloadProgressInterfaceInterval = 500 this.status = {} this.init = async function () { self.status = JSON.parse(await GM.getValue(self.key + '_status', '{}')) self.lastUpdate = new Date(await GM.getValue(self.key + '_status_time', 0)) } this.settings = {} this.loadSettings = async function (silent) { // Load settings, use first value as default const savedsettings = JSON.parse(await GM.getValue(self.key + '_settings', '{}')) for (const key in self.config) { if (key.endsWith('desc') || key.endsWith('range') || key.endsWith('quest') || key.endsWith('prefix') || key.endsWith('suffix')) { continue } if (key in savedsettings) { // Saved if (self.config[key] === 'int') { // Int self.settings[key] = parseInt(savedsettings[key], 10) } else if (self.config[key] === 'string') { // String self.settings[key] = savedsettings[key].toString() } else if (config[key] === 'bool') { // Bool self.settings[key] = savedsettings[key] === 'true' || savedsettings[key] === true } else if (Array.isArray(savedsettings[key])) { // Nested array self.settings[key] = [] for (let i = 0; i < savedsettings[key].length; i++) { self.settings[key].push(savedsettings[key][i]) } } else { // Array self.settings[key] = savedsettings[key] } } else { // Default if (self.config[key] === 'int') { // Int self.settings[key] = self.config[key + '_range'][1] } else if (self.config[key] === 'string') { // String self.settings[key] = '' // String defaults to empty string } else if (config[key] === 'bool') { // Bool self.settings[key] = true } else if (Array.isArray(self.config[key][0])) { // Nested array defaults to first value for each array self.settings[key] = [] for (let i = 0; i < self.config[key].length; i++) { self.settings[key].push(self.config[key][i][0]) } } else { self.settings[key] = self.config[key][0] // Array defaults to first value } } } if (!self.settings.apikey && !silent) { // Try to get the apikey from the website GM.xmlHttpRequest({ method: 'GET', url: self.homepage + 'account', onerror: function (response) { console.log(scriptName + ': premiumize.me API Key could not be loaded') setStatus('You have not set you premiumize.me Api key ') }, onload: function (response) { let s = '' try { s = response.responseText.split('class="apipass"')[1].split('</')[0].split('>')[1] } catch (e) { } if (s) { self.settings.apikey = s GM.setValue(self.key + '_settings', JSON.stringify(self.settings)) console.log(scriptName + ': premiumize.me API Key was loaded from account and saved!') } else { setStatus('You need to set you premiumize.me Api key') } } }) } } this.updateStatus = async function () { // Update list of online hosters await self.loadSettings() if (document.location.href.match(self.updateStatusURL)) { // Read and save current status of all hosters if ($('table.table tr>td:first-child').length) { self.status = {} await GM.setValue(self.key + '_status_time', '' + (new Date())) $('table.table tr>td:first-child').each(function () { const text = $(this).text() if (text.match(/^\s*[0-9a-z-]+\.\w{0,6}\s*$/i)) { const name = text.match(/^\s*([0-9a-z-]+)\.\w{0,6}\s*$/i)[1] self.status[name.toLowerCase()] = true } }) await GM.setValue(self.key + '_status', JSON.stringify(self.status)) console.log(scriptName + ': ' + self.name + ': Hosters (' + Object.keys(self.status).length + ') updated') } else if (self.settings.apikey) { GM.xmlHttpRequest({ method: 'GET', url: self.homepage + 'api/services/list?apikey=' + encodeURIComponent(self.settings.apikey), onerror: function (response) { console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/services/list') console.log(response) }, onload: async function (response) { const result = JSON.parse(response.responseText) /* { "cache": [ "uploaded.to", "filefactory.com", ... ], "directdl": [ "uploaded.to", "filefactory.com", ... ] } */ if ('cache' in result && 'directdl' in result) { self.status = {} await GM.setValue(self.key + '_status_time', '' + (new Date())) result.cache.forEach(function (host) { const name = host.match(/^\s*([0-9a-z-]+)\.\w{0,6}\s*$/i)[1] self.status[name.toLowerCase()] = result.directdl.indexOf(host) !== -1 }) await GM.setValue(self.key + '_status', JSON.stringify(self.status)) console.log(scriptName + ': ' + self.name + ': Hosters (' + Object.keys(self.status).length + ') updated') } else { console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/services/list') console.log(response) } } }) } else { console.log(scriptName + ': Cannot update hosters, no html and no api key found') } } else { alert(scriptName + '\n\nError: wrong update URL') } } this.isOnline = hostername => hostername in self.status && self.status[hostername] this.getOpenWebsiteURL = function (urls) { // Return a link to the premiumize.me website that will insert the links const url = this.homepage + 'downloader?link:' + encodeURIComponent(urls.join('\n')) return url } this.checkLink = function (url, cb) { // check whether the link is supported and online const host = url.match(/https?:\/\/(.+?)\//)[1] let hoster = host.split('.') hoster.pop() hoster = hoster.pop().replace('-', '') cb(this.isOnline(hoster)) } this.getResults = function (urls, cb) { // cb($node,linkNumber) -- $node contains the result, linkNumber is the number of links that should be online i.e. number of hashes alert('This function does not work for ' + this.name) } this._notLoggedIn = false this.getLinks = async function (urls, cb) { 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) } this._getLinks = function (urls, cb) { setTitle('✈️' + urls.length + '🔗 ') const N = urls.length const downloadLinks = [] const errors = [] for (let i = 0; i < urls.length; i++) { this._addSingleTransfer(urls[i], function (downloadlink, originallink, message) { if (downloadlink) { downloadLinks.push(downloadlink) } else { errors.push([originallink, message]) } }) } const checkprogress = function () { if (self._notLoggedIn) { // Stop checking and open premiumize homepage setTitle('🔑 ') setStatus(self.name + ' error: Not logged in!\nMaybe update your API key?', 0) GM.openInTab(self.homepage) cb([], -2) return } if (N === errors.length) { // All errors setTitle('❌ ') cb(false, -1) if (errors.length === 1 && errors[0][1]) { setStatus(errors[0][1], 0) } else { alert('Errors occured\n' + errors.length + ' links failed:\n\n' + errors.join('\n')) } } else if (N === downloadLinks.length + errors.length) { // All finished setTitle(downloadLinks.length + '/' + errors.length + '✅ ') cb(downloadLinks) if (errors.length > 0) { // Errors occured alert('Errors occured\n' + errors.length + ' links failed:\n\n' + errors.join('\n')) } } else { // not finished yet setTitle(downloadLinks.length + '/' + N + '⏳ ') window.setTimeout(checkprogress, self.updateDownloadProgressInterfaceInterval) } } window.setTimeout(checkprogress, self.updateDownloadProgressInterfaceInterval * Math.max(5, N)) } this._addSingleTransfer = function (url, cb) { GM.xmlHttpRequest({ method: 'POST', url: self.homepage + 'api/transfer/create', data: 'apikey=' + encodeURIComponent(self.settings.apikey) + '&src=' + encodeURIComponent(url), headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache' }, onerror: function (response) { console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/transfer/create') console.log(response) cb(false, url, 'GM.xmlHttpRequest error: api/transfer/create') }, onload: function (response) { const result = JSON.parse(response.responseText) /* {"status":"success","type":"savetocloud","id":"gfwRtdgd5fgdfgfhgfhf","name":"test.zip"} {"status":"error","error":"duplicate","id":"gfdgd5fgFddfgfhgfhf","message":"You already have this job added."} {"status":"error","message":"This link is not available on the file hoster website"} */ if ('id' in result && result.id) { window.setTimeout(function () { self._getFileFromTransfer(url, result.id, cb) }, 1000) if ('message' in result) { addStatus(result.message, -1) } } else { if ('message' in result && !self._notLoggedIn) { addStatus(result.message, -1) if (~result.message.indexOf('log')) { self._notLoggedIn = true } } cb(false, url, 'message' in result ? result.message : response.responseText) } } }) } this._getFileFromTransfer = function (url, transferId, cb) { GM.xmlHttpRequest({ method: 'GET', url: self.homepage + 'api/transfer/list?apikey=' + encodeURIComponent(self.settings.apikey), headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache' }, onerror: function (response) { console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/transfer/list') console.log(response) cb(false, url, 'GM.xmlHttpRequest error: /api/transfer/list') }, onload: function (response) { const result = JSON.parse(response.responseText) /* { "status": "success", "transfers": [ { "id": "xXFDSXXFDSGD", "name": "test.zip", "message": null, "status": "finished", "progress": 0, "folder_id": "gfjdfsuigjfdoikfsadf", "file_id": "trhgf982u30fjklfsdag" } ] } { "status": "success", "transfers": [ { "id":"xXFDSXXFDSGD", "name":"test.zip", "message":"Initializing Download...", "status":"running", "progress":0, "folder_id":"gfjdfsuigjfdoikfsadf", "file_id":null } ] } */ if (result.status === 'success' && 'transfers' in result) { for (let i = 0; i < result.transfers.length; i++) { if (result.transfers[i].id === transferId) { if (result.transfers[i].file_id) { // Finished window.setTimeout(function () { self._getSingleLink(url, result.transfers[i].file_id, cb) }, result.transfers[i].status === 'finished' ? 10 : self.updateDownloadProgressInterval) } else { // Downloading if ('message' in result.transfers[i] && result.transfers[i].message) { setStatus(result.transfers[i].message, -1) } window.setTimeout(function () { self._getFileFromTransfer(url, transferId, cb) }, self.updateDownloadProgressInterval) } return } } } if ('message' in result && result.message) { alert(scriptName + '\n\nCould not get /api/transfer/list\nError:\n' + result.message) } cb(false, url, 'Could not find url in transfer list') } }) } this._getSingleLink = function (url, fileId, cb) { GM.xmlHttpRequest({ method: 'POST', url: self.homepage + 'api/item/details', data: 'apikey=' + encodeURIComponent(self.settings.apikey) + '&id=' + encodeURIComponent(fileId), headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache' }, onerror: function (response) { console.log(scriptName + ': GM.xmlHttpRequest error: ' + self.homepage + 'api/item/details') console.log(response) cb(false, url, 'GM.xmlHttpRequest error: /api/item/details') }, onload: function (response) { const result = JSON.parse(response.responseText) /* { "id": "xxXxXxxxXxxx", "name": "test.zip", "size": 156, "created_at": 1572458477, "transcode_status": "not_applicable", "folder_id": "XxXXXxxxxxx", "ip": "1.1.1.1", "acodec": "", "vcodec": "", "mime_type": "application/zip", "opensubtitles_hash": "", "resx": "", "resy": "", "duration": "", "virus_scan": "ok", "type": "file", "link": "https://down.host.example.com/dl/abcdefg/test.zip", "stream_link": null } */ if ('link' in result && result.link) { cb(result.link, url) } else { window.setTimeout(function () { self._getSingleLink(url, fileId, cb) }, self.updateDownloadProgressInterval) } } }) } }(), 'nopremium.pl': new function () { const self = this this.config = { mode: ['transfer', 'premium', 'none'], mode_desc: ['Transfer User (Pakiety Transferowe)', 'Premium User (Konta Premium)', 'No account'], mode_quest: 'What kind of account do you have at nopremium.pl', downloadmode: ['direct', 'server'], downloadmode_desc: ['Direct download (TRYB SZYBKIEGO POBIERANIA)', 'Downloading via NoPremium.pl server (TRYB POBIERANIA NA SERWERY)'], downloadmode_quest: ['Which download mode do you want to use?'] } this.key = 'nopremium.pl' this.name = 'NoPremium.pl' this.homepage = 'https://www.nopremium.pl/' this.updateStatusURL = 'https://www.nopremium.pl/files' this.updateStatusURLpattern = /https?:\/\/www\.nopremium\.pl\/files\/?/ this.updateDownloadProgressInterval = 5000 const mapHosterName = name => name.replace('-', '') this.status = {} this.init = async function () { self.status = JSON.parse(await GM.getValue(self.key + '_status', '{}')) self.lastUpdate = new Date(await GM.getValue(self.key + '_status_time', 0)) } this.settings = {} this.loadSettings = async function (silent) { // Load settings, use first value as default const savedsettings = JSON.parse(await GM.getValue(self.key + '_settings', '{}')) for (const key in self.config) { if (key.endsWith('desc') || key.endsWith('range') || key.endsWith('quest') || key.endsWith('prefix') || key.endsWith('suffix')) { continue } if (key in savedsettings) { // Saved if (self.config[key] === 'int') { // Int self.settings[key] = parseInt(savedsettings[key], 10) } else if (self.config[key] === 'string') { // String self.settings[key] = savedsettings[key].toString() } else if (config[key] === 'bool') { // Bool self.settings[key] = savedsettings[key] === 'true' || savedsettings[key] === true } else if (Array.isArray(savedsettings[key])) { // Nested array self.settings[key] = [] for (let i = 0; i < savedsettings[key].length; i++) { self.settings[key].push(savedsettings[key][i]) } } else { // Array self.settings[key] = savedsettings[key] } } else { // Default if (self.config[key] === 'int') { // Int self.settings[key] = self.config[key + '_range'][1] } else if (self.config[key] === 'string') { // String self.settings[key] = '' // String defaults to empty string } else if (config[key] === 'bool') { // Bool self.settings[key] = true } else if (Array.isArray(self.config[key][0])) { // Nested array defaults to first value for each array self.settings[key] = [] for (let i = 0; i < self.config[key].length; i++) { self.settings[key].push(self.config[key][i][0]) } } else { self.settings[key] = self.config[key][0] // Array defaults to first value } } } } this.updateStatus = async function () { // Update list of online hosters if (document.location.href.match(self.updateStatusURL)) { // Read and save current status of all hosters await GM.setValue(self.key + '_status_time', '' + (new Date())) self.status = {} $('#servers a[title]').each(function () { const name = mapHosterName(this.title) self.status[name] = true }) await GM.setValue(self.key + '_status', JSON.stringify(self.status)) console.log(scriptName + ': ' + self.name + ': Hosters (' + Object.keys(self.status).length + ') updated') } else { alert(scriptName + '\n\nError: wrong update URL') } } this.isOnline = hostername => hostername in self.status && self.status[hostername] this.getOpenWebsiteURL = function (urls) { // Return a link to the nopremium.pl website that will insert the links const url = this.homepage + 'files?link:' + encodeURIComponent(urls.join('\n')) return url } const getHashs = function (urls, cb, silent) { // cb(hashes,sizestring) setTitle('✈️ ') setStatus('Sending ' + (urls.length === 1 ? 'one link' : (urls.length + ' links')), -1) GM.xmlHttpRequest({ method: 'POST', url: self.homepage + 'files', data: 'watchonline=&session=' + (Math.round(Math.random() * 1234567)) + '&links=' + encodeURIComponent(urls.join('\n')), headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache' // "Referer" : "https://www.nopremium.pl/files" // FIREFOX57 }, onload: function (response) { if (response.responseText.indexOf('<input type="text" name="login" placeholder="Login"/>') !== -1) { setTitle('🔑 ') setStatus(self.name + ' error: Not logged in!', 0) GM.openInTab(self.homepage) return cb([], -1) } const hashes = [] // Find hashes const re = /name="hash(\d+)" value="(\w+)"/g // <input type="checkbox" id="hash0" name="hash0" value="fab3c41988" onclick="UpdateCounter();" c let ma = re.exec(response.responseText) while (ma) { hashes.push(ma[2]) ma = re.exec(response.responseText) } // Find errors ma = response.responseText.match(/Pliki nieprzetworzone \((\d+)\)/) if (ma && !silent) { addStatus('Error: ' + (parseInt(ma[1], 10) === 1 ? ('One file is offline or unsupported') : (ma[1] + ' files are offline or unsupported')), 0) } // Find size let size = '0 Byte' if (response.responseText.indexOf('id="countSize"') !== -1) { 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"> size = ma[1] + ' ' + ma[2] } setStatus(self.name + ' identified ' + (hashes.length === 1 ? 'one online file' : (hashes.length + ' online files')), -1) setTitle(hashes.length + '🔗 ') cb(hashes, size) } }) } this.checkLink = function (url, cb) { // check whether the link is supported and online // cb(boolresult) return getHashs([url], function (hashes, size) { cb(hashes.length === 1) }, true) } this.getResults = function (urls, cb, hashes) { // cb($node,linkNumber) -- $node contains the result, linkNumber is the number of links that should be online i.e. number of hashes // Get download links from nopremium.pl and show the usual info about the file, that is normally shown on nopremium.pl if (typeof hashes === 'undefined') { // 1. Get hashes and show transfer warning getHashs(urls, async function (hashes, size) { if (settings.mode === 'transfer') { 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) } else if (hashes.length > 0) { self.getResults(urls, cb, hashes) } else if (size === -1) { // Error was already handled (probably not logged in) console.log('getHashs->cb: Error was already handled (probably not logged in)') cb(false, -2) } else { // No files found setStatus('No online/available files', 0) cb(false, 0) } }) return } // 2. Work with hashes const $resultContainer = $('<div></div>').attr('id', 'generated-links') const mode = self.settings.downloadmode === 'direct' ? 0 : 1 // 0 -> direct , 1 -> via server GM.xmlHttpRequest({ method: 'POST', url: self.homepage + 'files', data: 'insert=1&mode=' + mode + '&hh=0&hash[]=' + hashes.join('&hash[]=') + '&', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache' // "Referer" : "https://www.nopremium.pl/files" // FIREFOX57 }, onload: function (response) { GM.xmlHttpRequest({ method: 'POST', url: self.homepage + 'files', data: 'loadfiles=1', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache' // "Referer" : "https://www.nopremium.pl/files" // FIREFOX57 }, onload: function (response) { if (mode === 0) { $resultContainer.append($('<div></div>').append(response.responseText).find('#fastFilesArea')) } else { $resultContainer.append($('<div></div>').append(response.responseText).find('#downloadFilesArea')) } $resultContainer.find('input[type=checkbox]').remove() cb($resultContainer, hashes.length) } }) } }) } this.getLinks = function (urls, cb) { // cb(downloadlinks) if (this.settings.downloadmode === 'direct') { return this._getDirectLinks(urls, cb) } else { return this._getServerLinks(urls, cb) } } this._getDirectLinks = function (urls, cb) { // Get Direct download links this.getResults(urls, async function ($node, N) { if (!$node || N < 1) { cb(false) return } const text = $node.html() /* <td>16-08-2014 20:22</td> <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> */ const files = [] const re = /<td>(\d+)-(\d+)-(\d+) (\d+):(\d+)<\/td>(\s|\n)+<td class="dlBox"><a href="(.*?)"/gm let m = re.exec(text) // wholeString, 16,08,2014,20,37,#newline#,http://direct.nopremium.pl/9091456/7895ca02bfcb2c2e43806f1079b7ff069129e/result.file while (m) { if (m[7].indexOf('//direct.nopremium.pl') === -1) { continue // Skip files via server, only use direct download links } const d = new Date(m[3], m[2], m[1], m[4], m[5], 0, 0) files.push([d, m[7]]) m = re.exec(text) } if (files.length === 0) { alert(scriptName + '\n\nAn error occured.\nCould not find download links in response.') cb(false) return } // Find youngest files by comparing their ids const pattern = /\.pl\/(\d+)\// files.sort(function (a, b) { const x = a[1].match(pattern)[1] const y = a[1].match(pattern)[1] return x > y ? -1 : x < y ? 1 : 0 }) const result = [] for (let i = 0; i < N; i++) { result.push(files[i][1]) await cacheLink([urls[i]], files[i][0], [files[i][1]], self.key) // CACHE single URLs } await cacheLink(urls, new Date(), result, self.key) // CACHE all URLs cb(result) }) } this._getServerLinks = function (urls, cb) { this.getResults(urls, function ($node, N) { if (N === 0) { cb(false) } else { self._getProgress(cb, $node, N) } }) } this._getProgress = function (cb, $node, N, ids) { GM.xmlHttpRequest({ method: 'POST', url: self.homepage + 'files', data: 'downloadprogress=1', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'no-cache' // "Referer" : "https://www.nopremium.pl/files" // FIREFOX57 }, onerror: function () { self._getProgressBlocked = false window.setTimeout(function () { self._getProgress(cb, $node, N, ids) }, self.updateDownloadProgressInterval) }, onload: function (response) { self._getProgressBlocked = false let data try { data = JSON.parse(response.responseText) } catch (e) { console.log(scriptName + ': ' + e) console.log(response.responseText) if (response.responseText.indexOf('<input type="text" name="login" placeholder="Login"/>') !== -1) { setTitle('🔑 ') setStatus(self.name + ' error: Not logged in!', 0) GM.openInTab(self.homepage) cb(false, -2) } else { window.setTimeout(function () { self._getProgress(cb, $node, N, ids) }, self.updateDownloadProgressInterval) } return } data.StandardFiles.sort(function (a, b) { const x = new Date(a.insert_date.split('-').join('/')) const y = new Date(b.insert_date.split('-').join('/')) return x > y ? -1 : x < y ? 1 : 0 }) const result = [] const runnning = [] let percent = 0 const progess = [] if (!ids) { // First run: Find the correct files: just use the first N files ids = [] for (let i = 0; i < data.StandardFiles.length && i < N; i++) { ids.push(data.StandardFiles[i].id) if (data.StandardFiles[i].status === 'finish') { result.push(data.StandardFiles[i].download_url) progess.push(100) percent += 100 } else { runnning.push(data.StandardFiles[i]) if (parseInt(data.StandardFiles[i].status, 10) > 0) { progess.push(parseInt(data.StandardFiles[i].status, 10)) percent += parseInt(data.StandardFiles[i].status, 10) } } } } else { // Consecutive runs: Use the ids from first run for (let i = 0; i < data.StandardFiles.length; i++) { if (ids.indexOf(data.StandardFiles[i].id) === -1) continue if (data.StandardFiles[i].status === 'finish') { result.push(data.StandardFiles[i].download_url) progess.push(100) percent += 100 } else { runnning.push(data.StandardFiles[i]) if (parseInt(data.StandardFiles[i].status, 10) > 0) { progess.push(parseInt(data.StandardFiles[i].status, 10)) percent += parseInt(data.StandardFiles[i].status, 10) } } } } /* Regarding caching in server mode: 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. */ if (result.length === N) { setStatus((result.length === 1 ? 'One file' : (result.length + ' files')) + ' downloaded to server', 1) setTitle(result.length + '✅ ') cb(result) } else { // Waiting percent = percent / N // 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); const dotheight = N > 2 ? 2 : 4 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; ">' for (let i = 0; i < N; i++) { if (progess[i]) { 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>' } else { h += '<span style="display:block; width:0x; height:1px; background:white; border-top:' + dotheight + 'px dotted silver; margin-bottom:1px;"></span>' } } h += '</div>' setTitle(Math.floor(percent) + '%⏳ ') setStatus(h) showOnlyStatus() window.setTimeout(function () { self._getProgress(cb, $node, N, ids) }, self.updateDownloadProgressInterval) } } }) } }() } const debridprovider = Object.keys(multi) let currentdebrid = await GM.getValue('currentdebrid', debridprovider[0]) for (const key in multi) { await multi[key].init() if (key === currentdebrid) { await multi[key].loadSettings() continue } if (!greasemonkey) { GM.registerMenuCommand(scriptName + ' - Switch to ' + multi[key].name, (function (key) { return async function () { if (!confirm(scriptName + '\n\nSet multi-download provider:\n' + multi[key].name)) return await GM.setValue('currentdebrid', key) currentdebrid = key document.location.reload() } })(key) ) } } if (!greasemonkey) { GM.registerMenuCommand(scriptName + ' - Delete cached links', async function () { if (!confirm(scriptName + '\n\nReally delete cached links?')) return await GM.setValue('cachedDownloadLinks', '{}') alert(scriptName + '\n\nCache is empty!') }) GM.registerMenuCommand(scriptName + ' - Restore dialogs and warnings', async function () { if (!confirm(scriptName + '\n\nReally restore all dialogs and warnings?')) return await GM.setValue('dialogs', '[]') alert(scriptName + '\n\nDialogs and warnings restored') }) } /* function round (f, p) { // Round f to p places after the comma return parseFloat(parseFloat(f).toFixed(p)) } */ const orgDocumentTitle = document.title function setTitle (message) { if (window.parent.parent !== window) { window.parent.parent.postMessage({ iAm: 'Unrestrict.li', type: 'title', str: message }, '*') } if (message) { document.title = message + orgDocumentTitle } else { document.title = orgDocumentTitle } } function popUp (id, onClose, thisArg, doNotCloseOnOutsideClick) { // Remove window scrolling $(document.body).css('overflow', 'hidden') let zi = getNextZIndex() id = id || ('popup' + (new Date()).getTime()) const $par = $('<div style="position:absolute; top:0px;"></div>').attr('id', id).appendTo(document.body) 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) 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) const close = function () { $par.remove() if (onClose) onClose.call(thisArg) // Restore scrolling $(document.body).css('overflow', 'initial') } if (!doNotCloseOnOutsideClick) { $background.click(close) } return { node: $div, close } } function configForm ($form, c, s, formid) { for (const key in c) { if (key.endsWith('desc') || key.endsWith('range') || key.endsWith('quest') || key.endsWith('prefix') || key.endsWith('suffix') || key.endsWith('hidden')) { continue } const $p = $('<p>').appendTo($form) if (c[key + '_quest']) { $p.append(c[key + '_quest']) } else { $p.append(key) } $p.append('<br>') if (c[key + '_prefix']) { $p.append(c[key + '_prefix'] + ' ') } const hidden = (key + '_hidden') in c && c[key + '_hidden'] if (c[key] === 'int') { // Int const $input = $('<input type="number">').addClass('form_' + formid).data('key', key).data('parse', 'int').val(s[key]).appendTo($p) if (c[key + '_range']) { $input.prop('min', c[key + '_range'][0]) $input.prop('max', c[key + '_range'][2]) $input.prop('title', c[key + '_range'][0] + ' - ' + c[key + '_range'][2]) } } else if (c[key] === 'string') { // String const $inputText = $('<input type="text">').addClass('form_' + formid).data('key', key).data('parse', 'string').appendTo($p) if (hidden && s[key]) { $inputText.val('## HIDDEN ##') $inputText.data('hidden', '1') } else { $inputText.val(s[key]) } } else if (c[key] === 'bool') { // Bool const $select = $('<select></select>').addClass('form_' + formid).data('key', key).data('parse', 'bool').appendTo($p) const $optionYes = $('<option></option>').val('true').appendTo($select) if (c[key + '_desc']) { $optionYes.html(c[key + '_desc'][0]) } else { $optionYes.html('Yes') } if (s[key]) { $optionYes[0].selected = true } const $optionNo = $('<option></option>').val('false').appendTo($select) if (c[key + '_desc']) { $optionNo.html(c[key + '_desc'][1]) } else { $optionNo.html('No') } if (!s[key]) { $optionNo[0].selected = true } } else if (Array.isArray(c[key][0])) { // Nested array for (let j = 0; j < c[key].length; j++) { if (c[key + '_desc'] && !Array.isArray(c[key + '_desc'][j])) { $p.append(c[key + '_desc'][j] + ': ') } const $select = $('<select></select>').addClass('form_' + formid).data('key', key).data('index', j).appendTo($p) for (let i = 0; i < c[key][j].length; i++) { const $option = $('<option></option>').val(c[key][j][i]).appendTo($select) if (c[key + '_desc'] && Array.isArray(c[key + '_desc'][0])) { $option.html(c[key + '_desc'][j][i]) } else { $option.html(c[key][j][i]) } if (s[key][j] === c[key][j][i]) { $option[0].selected = true } } $p.append('<br>') } } else { // Array const $select = $('<select></select>').addClass('form_' + formid).data('key', key).appendTo($p) for (let i = 0; i < c[key].length; i++) { const $option = $('<option></option>').val(c[key][i]).appendTo($select) if (c[key + '_desc']) { $option.html(c[key + '_desc'][i]) } else { $option.html(c[key][i]) } if (s[key] === c[key][i]) { $option[0].selected = true } } } if (c[key + '_suffix']) { $p.append(' ' + c[key + '_suffix']) } } } async function saveSettings (ev) { const $body = ev.data const $form = $body.find('.form') // Save preferred hoster: currentdebrid = $form.find('.debridhoster').val() // Save options: const newsettings = { general: {} } for (const key in multi) { newsettings[key] = {} } $form.find('*[class^=form_]').each(function () { const $this = $(this) const namespace = $this.prop('class').split('_', 2)[1] const key = $this.data('key') const index = $this.data('index') let value = $this.val() const parse = $this.data('parse') const hiddenAndUnchanged = $this.data('hidden') && value === '## HIDDEN ##' if (typeof index !== 'undefined') { // Nested Array if (!(key in newsettings[namespace]) || !Array.isArray(newsettings[namespace][key])) { newsettings[namespace][key] = [] } newsettings[namespace][key][index] = value } else { // Normal if (hiddenAndUnchanged) { value = multi[namespace].settings[key] } else if (parse === 'int') { value = parseInt(value, 10) } else if (parse === 'bool') { value = (value === 'true') } newsettings[namespace][key] = value } }) await GM.setValue('setup', true) await GM.setValue('currentdebrid', currentdebrid) await GM.setValue('settings', JSON.stringify(newsettings.general)) for (const key in multi) { await GM.setValue(key + '_settings', JSON.stringify(newsettings[key])) } alert(scriptName + '\n\nSettings were successfully saved!') document.location.reload() } async function aboutMe () { const popup = popUp('multiochhelper_about', null, null, true) const $popup = popup.node const $frame = $('<iframe width="' + (window.innerWidth - 250) + '" height="' + (window.innerHeight - 150) + '" style="border:0">').appendTo($popup) $frame.bind('load', async function (e) { // Load settings for all for (const key in multi) { await multi[key].loadSettings(true) } const $body = $($frame[0].contentDocument.getElementsByTagName('body')[0]) $body.css('fontFamily', 'Ubuntu,Arial,Sans-Serif') $('<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) $body.append('<h2>' + scriptName + '</h2>') $('<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') const $form = $('<div class="form">').appendTo($body) // General options $form.append('<h3>Settings</h3>') configForm($form, config, settings, 'general') // Preferred multihoster const $p = $('<p>').appendTo($form) $p.append('Preferred multihoster:<br>') const $select = $('<select></select>').addClass('debridhoster').appendTo($p) for (const key in multi) { const $option = $('<option></option>').val(key).appendTo($select) $option.html(multi[key].name) $option[0].selected = key === currentdebrid } // Options for multihosters for (const key in multi) { $('<h3>').appendTo($form).html(multi[key].name) $('<a>').appendTo($form).css('fontSize', 'small').attr('target', '_blank').html(multi[key].homepage).attr('href', multi[key].homepage) if (multi[key].config) { configForm($form, multi[key].config, multi[key].settings, key) } else { $('<p>').appendTo($form).text('No settings available for this service.') } } $form.append('<br>') $('<input type="button">').val('Cancel').click(function () { if (confirm('Settings will NOT be saved!')) { popup.close() } }).appendTo($form) $('<input type="button">').val('Save').click($body, saveSettings).appendTo($form) $form.append('<h3>Other options</h3>') $('<input type="button">').val('Clear cache (' + humanBytes((await GM.getValue('cachedDownloadLinks', '{}')).length - 2) + ')').appendTo($form).click(async function () { if (!confirm(scriptName + '\n\nReally delete cached links?')) { return } await GM.setValue('cachedDownloadLinks', '{}') this.value = 'Clear cache (' + humanBytes((await GM.getValue('cachedDownloadLinks', '{}')).length - 2) + ')' alert(scriptName + '\n\nCache is empty!') }) $form.append('<br>') $form.append('<br>') $('<input type="button">').val('Restore dialogs and warnings').appendTo($form).click(async function () { if (!confirm(scriptName + '\n\nReally restore all dialogs and warnings?')) { return } await GM.setValue('dialogs', '[]') alert(scriptName + '\n\nDialogs and warnings restored') }) let greasemonkeyIssue = '' if (greasemonkey) { greasemonkeyIssue = `<li>In Greasymonkey it is not possible to select multiple links with the mouse and send them at once.<br> The reason is this bug: <a href="https://github.com/greasemonkey/greasemonkey/issues/2574">https://github.com/greasemonkey/greasemonkey/issues/2574</a><br> If you need this functionality, you can use Tampermonkey instead of Greasemonkey</li>` } $(`<div> <br> <br> <h3>Known issues:</h3> <ul> <li>nopremium.pl sometimes omits a few links in folders</li> <li>In Firefox the script sometimes does not work if the "Accept thid-parts cookies" policy is set to "Never".<br> 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> Close and re-open Firefox. Log out and then log in your nopremium.pl account. Everything should work fine now.</li> ${greasemonkeyIssue} </ul> </div><br><br><br>`).appendTo($body) $('<input type="button">').val('Debug info').appendTo($body).click(inspectGMvalues) }) if (chrome) { $frame.attr('src', 'about:blank') } } function inspectGMvalues () { let iv const popup = popUp('multiochhelper_inspectGM', function () { clearInterval(iv) }) const $popup = popup.node const $frame = $('<iframe width="' + (window.innerWidth - 250) + '" height="' + (window.innerHeight - 150) + '" style="border:0">').appendTo($popup) $frame.bind('load', async function (e) { $($frame[0].contentDocument.getElementsByTagName('head')[0]).append('<style type="text/css">' + SPINNERCSS + '</style>') const $body = $($frame[0].contentDocument.getElementsByTagName('body')[0]) $body.append('<h2>' + scriptName + '</h2>') let keys = await GM.listValues() if (keys.length && typeof keys[0] === 'undefined') { // Firefox 35+ workaround keys = cloneInto(await GM.listValues(), window) } const $table = $('<table>').appendTo($body) let $tr $tr = $('<tr>').appendTo($table) $('<th>').html('Key').appendTo($tr) $('<th>').html('Value').appendTo($tr) $('<th>').html('Type').appendTo($tr) $('<th>').html('').appendTo($tr) const deleteValue = async function (ev) { const key = $(this).data('key') await GM.deleteValue(key) $(this).parent().parent().remove() } let total = 0 for (let i = 0; i < keys.length; i++) { const value = await GM.getValue(keys[i]) let svalue = '' + value let len = 1 if (typeof value === 'undefined') { svalue = 'undefined' } else if (typeof value === 'string') { len = value.length } total += len $tr = $('<tr>').appendTo($table) $('<td>').html(keys[i]).appendTo($tr) $('<td>').append($('<input type="text" style="width:600px">').val(svalue)).appendTo($tr) $('<td>').append('' + (typeof value) + (typeof value === 'string' ? ('(' + len + ')') : '')).appendTo($tr) $('<td>').append($('<input type="button">').val('Delete').data('key', keys[i]).click(deleteValue)).appendTo($tr) } $tr = $('<tr>').appendTo($table) $('<th>').html('Total').appendTo($tr) $('<th>').html(keys.length).appendTo($tr) $('<th>').html('approx. ' + humanBytes(total)).appendTo($tr) const $reload = $('<div>').appendTo($body) $('<div style="display:inline-block;width:20px; height:20px;" class="ochspinner"></div>').appendTo($reload) $reload.append(' Reload in ') const $timer = $('<span style="pointer:cursor;" title="Click to reload now"></span>').html('20 seconds').click(function () { this.innerHTML = 0 }).appendTo($reload) iv = window.setInterval(function () { let s = parseInt($timer.html(), 10) if (s === 0) { clearInterval(iv) popup.close() inspectGMvalues() } else { s-- $timer.html(s + ' seconds') } }, 1000) }) if (chrome) { $frame.attr('src', 'about:blank') } } function hexToBytes (s) { return s.match(/([0-9a-fA-F]{2})/g).map(v => parseInt(v, 16)) } function stringToBytes (s) { return s.split('').map(v => v.charCodeAt(0)) } function bytesToString (a) { return String.fromCharCode.apply(String, a) } function addCSSHead (body) { const style = document.createElement('style') style.type = 'text/css' style.innerHTML = body document.head.appendChild(style) } function humanBytes (bytes, precision) { // http://stackoverflow.com/a/18650828 bytes = parseInt(bytes, 10) if (bytes === 0) return '0 Byte' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toPrecision(2)) + ' ' + sizes[i] } function getNextZIndex () { // Calculate: max(zIndex) + 1 let zIndexMax = 0 try { $('div').each(function () { const z = parseInt($(this).css('z-index'), 10) if (z > zIndexMax) { zIndexMax = z } }) } catch (e) {} finally { if (zIndexMax < 20000) { zIndexMax = 20006 } } return zIndexMax + 1 } async function showConfirm (id, text, onConfirm, onNotConfirm, thisArg) { // Skip const dialogs = JSON.parse(await GM.getValue('dialogs', '[]')) if (dialogs.indexOf(id) !== -1) { onConfirm.call(thisArg) return } const popup = popUp('confirm' + id, function () {}, thisArg) const $div = popup.node $div.append(text) $div.append('<br>') $('<input type="button" value="Yes">').click(function () { popup.close() onConfirm.call(thisArg) }).appendTo($div) $('<input type="button" value="No">').click(function () { popup.close() if (onNotConfirm) { onNotConfirm.call(thisArg) } }).appendTo($div) $div.append('<br>') $('<input type="checkbox" value="remember">').click(async function () { const dialogs = JSON.parse(await GM.getValue('dialogs', '[]')) if (this.checked) { if (dialogs.indexOf(id) === -1) { dialogs.push(id) await GM.setValue('dialogs', JSON.stringify(dialogs)) } } else { if (dialogs.indexOf(id) !== -1) { dialogs.splice(dialogs.indexOf(id), 1) await GM.setValue('dialogs', JSON.stringify(dialogs)) } } }).appendTo($div) $div.append(' Always "Yes". Do not show this message again!') } function setStatus (text, success) { addStatus(text, success, true) } function addStatus (text, success, clear) { if (!document.getElementById('multiochhelper')) { alert(`${scriptName}\n\n${text}`) return } let $status = $('#multiochhelper_status') if (!document.getElementById('multiochhelper_status_text')) { if (!document.getElementById('multiochhelper_status')) { const $div = $('#multiochhelper') $status = $('<div>').prependTo($div) $status.attr('id', 'multiochhelper_status') } else { $status.empty() } const $loader = $('<div>').appendTo($status) $loader.attr('id', 'multiochhelper_status_loader') const $statustext = $('<div>').appendTo($status) $statustext.attr('id', 'multiochhelper_status_text') const $statusclear = $('<div>').appendTo($status) $statusclear.attr('id', 'multiochhelper_status_clear') } const $statustext = $('#multiochhelper_status_text') if (clear) { $statustext.empty() } else if ($statustext.html().trim() !== '') { $statustext.append(document.createElement('br')) } $status.show() $statustext.append(text) if (success === 1) { $statustext.css('color', '#33FF99') } else if (success === 0) { $statustext.css('color', 'orange') } else if (success === -1) { $statustext.css('color', 'cyan') } else { $statustext.css('color', 'white') } } function showOnlyStatus () { const $status = $('#multiochhelper_status') $status.siblings().not('#multiochhelper_status').remove() } function getMultiOCHWebsiteURL (links) { return multi[currentdebrid].getOpenWebsiteURL(links) } function openWebsite (links, cb) { // Call cb() and navigate to the website if (!links) { cb(false) return } if (cb) { cb() } const url = getMultiOCHWebsiteURL(links) if (settings.newTab) { if (typeof GM.openInTab === 'undefined') { window.open(url) } else { GM.openInTab(url) } } else { document.location.href = url } } async function useCache (urls, cb) { urls = '' + urls const cachedDownloadLinks = JSON.parse(await GM.getValue('cachedDownloadLinks', '{}')) // [datestring,downloadlink,multihoster] if (urls in cachedDownloadLinks) { 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'))) { cb(cachedDownloadLinks[urls][1]) return true } } return false } async function cacheLink (urls, datetime, downloadLinks, multihoster) { if (!Array.isArray(downloadLinks)) { const parts = downloadLinks.split('\n') downloadLinks = [] for (let i = 0; i < parts.length; i++) { if ($.trim(parts[i])) { downloadLinks.push($.trim(parts[i])) } } } if (downloadLinks.length === 0) return urls = '' + urls const cachedDownloadLinks = JSON.parse(await GM.getValue('cachedDownloadLinks', '{}')) cachedDownloadLinks[urls] = [datetime, downloadLinks, multihoster] await GM.setValue('cachedDownloadLinks', JSON.stringify(cachedDownloadLinks)) } function showExtractedLinks (links) { if (document.querySelector('.alertlinkscont')) { alert(links.join('\n')) $('.alertlinkscont').remove() return } $('<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') const $div = $('<div class="alertlinkscont"></div>') $div.appendTo(document.body) $div.css({ zIndex: 10000, position: 'fixed', top: '20px', left: '20px', minWidth: '300px', minHeight: '300px', background: 'white', color: 'black', border: '2px solid black', borderRadius: '5px', padding: '20px 25px 10px', fontFamily: 'monospace', fontSize: '12px', overflow: 'auto' }) for (let i = 0; i < links.length; i++) { $div[0].innerHTML += '<a target="_blank" href="' + links[i] + '">' + links[i] + '</a><br>\n' } $div[0].innerHTML += '<br><br>\n' window.setTimeout(function moveMenuIntoView () { $div.css('maxHeight', (document.documentElement.clientHeight - 100) + 'px') $div.css('maxWidth', (document.documentElement.clientWidth - 40) + 'px') $div.css('left', Math.max(20, 0.5 * (document.body.clientWidth - $div[0].clientWidth)) + 'px') }, 0) } async function generateLinks (urls, cb) { // Check cache if (await useCache(urls, cb)) { return } await multi[currentdebrid].getLinks(urls, cb) } async function download (urls, cb) { // Get one/first download link and open it immediately/start download if (urls.length > 1) { alert(scriptName + '\n\nOnly the first link will be opened!') } await generateLinks(urls, function (result, code) { if (cb) { cb() } if (result && result[0]) { addStatus('Opening download...', -1) if (window.top === window) { document.location.href = result[0] } else { // Changing location may be blocked by sandboxed iframe window.top.location.href = result[0] } } else if (code === -2) { // Error was already handled console.log('download() in generateLinks(): error already handled') } else if (!code) { addStatus('An error occured: No downloadlink to open', 0) } }) } async function clipboard (urls, cb) { // Get download links and copy them into clipboard generateLinks(urls, function (result, code) { if (result) { let succeeded = false setStatus('Trying to set clipboard', -1) window.setTimeout(function () { if (succeeded) { return } setStatus('Trying GM_setClipboard()', -1) try { GM_setClipboard(result.join('\r\n')) setStatus('Copied to clipboard', 1) } catch (e) { setStatus('Failed to access clipboard 02', 0) alert('Failed to access clipboard.\n\nLinks will appear in next dialog window') alert(result.join('\r\n')) } }, 3000) try { GM.setClipboard(result.join('\r\n')).then(function () { setStatus('Copied to clipboard', 1) succeeded = true }, function () { setStatus('Failed to access clipboard 01', 0) }) } catch (e) { setStatus('Clipboard not supported by this browser', 0) alert(result.join('\n')) }; } else if (code === -2) { // Error was already handled console.log('clipboard() in generateLinks(): error already handled') } else { setStatus('An error occured: No downloadlinks found', 0) } if (cb) { cb() } }) } async function sendToJD (urls, cb) { // Get download links and send them to JDownloader generateLinks(urls, function (result, code) { if (result) { setStatus('Waiting for JDownloader', -1) // Comment should be the original page in case of multiple links let comment = urls[0] if (urls.length > 1) { if (showOneclickFromHighlighScriptAllLinksLoc) { comment = showOneclickFromHighlighScriptAllLinksLoc } else if (showOneclickFromHighlighScriptSelectedLinksLoc) { comment = showOneclickFromHighlighScriptAllLinksLoc } else { comment = document.location.href } } GM.xmlHttpRequest({ method: 'POST', url: JDOWNLOADER + 'flash/add', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Referer: scriptReferer, 'User-Agent': scriptReferer }, // data: "source="+encodeURIComponent(scriptReferer)+"&urls="+encodeURIComponent(result.join("\r\n")), // Moved "source" to Referer // data: "comment="+encodeURIComponent(comment)+"&urls="+encodeURIComponent(result.join("\r\n")), // See ExternInterfaceImpl.java data: 'source=' + encodeURIComponent(scriptReferer) + '&comment=' + encodeURIComponent(comment) + '&urls=' + encodeURIComponent(result.join('\r\n')), // See ExternInterfaceImpl.java onload: function (resp) { if (cb) { cb() } if (resp.status === 204 || resp.responseText.startsWith('success')) { setStatus('Sent to JDownloader', 1) } else { setStatus('JDownloader rejected the request', 0) } }, onerror: function (resp) { if (cb) { cb() } setStatus('JDownloader is not running', 0) } }) } else if (code === -2) { // Error was already handled console.log('sendToJD() in generateLinks(): error already handled') if (cb) { cb() } } else { if (cb) { cb() } addStatus('No links to send', 0) } }) } function showLinks (urls, cb, append, n) { const popup = popUp('showLinks') const $div = popup.node const $loader = $('<div style="width:20px; height:20px;" class="ochspinner"></div>').appendTo($div) const $frame = $('<iframe width="900" height="500" style="border:0">').appendTo($div) $frame.bind('load', function (e) { $($frame[0].contentDocument.getElementsByTagName('head')[0]).append('<link rel="stylesheet" href="https://www.nopremium.pl/css/style.css" type="text/css" />') const $body = $($frame[0].contentDocument.getElementsByTagName('body')[0]) multi[currentdebrid].getResults(urls, function ($node) { $loader.remove() $body.append($node) $body.find('a').each(function () { // Open links in new window this.setAttribute('target', '_blank') }) if (cb) { cb() } }) }) if (chrome) { $frame.attr('src', 'about:blank') } } function decryptClickNLoad (cb, jk, cryptedBase64) { // Get all the links by decrypting the Click'n'Load form // return False for any error // return True, run cb() and open the menu if Click'n'Load was successfully decoded if (!cryptedBase64 && !(document.getElementsByName('crypted').length && document.getElementsByName('jk').length)) { return false // Click'n'Load not avaiblabe } setStatus("Trying to decrypt Click'n'Load", -1) try { // Key/IV if (!jk) { jk = document.getElementsByName('jk')[0].value } if (jk.indexOf('return') !== -1) { jk = eval(jk + '; f();') // eslint-disable-line no-eval } const key = hexToBytes(jk) const iv = key.slice(0) // Text if (!cryptedBase64) { cryptedBase64 = document.getElementsByName('crypted')[0].value } const cryptedString = atob(cryptedBase64) const cryptedBytes = stringToBytes(cryptedString) // Decrypt const textBytes = slowAES.decrypt(cryptedBytes, slowAES.modeOfOperation.CBC, key, iv) let text = bytesToString(textBytes) text = text.replace('\r', '') const splitted = text.split('\n') const links = [] for (let i = 0; i < splitted.length; i++) { // Remove any line that is not a http link const t = $.trim(splitted[i]) if (t && t.substring(0, 4) === 'http') { links.push(t) } } const N = links.length if (N === 0) { return false // Click'n'Load probably failed, try another method... } if (cb) { cb() } menu(links) setStatus('Found ' + (N === 1 ? 'one link' : (N + ' links')), 1) return true } catch (e) { alert("Click'N'Load failed:\n" + e) return false // Click'n'Load probably failed, try another method... } /* // Get all the links by decrypting the Click'n'Load form if(!document.getElementsByName('crypted').length || !document.getElementsByName('jk').length) { if(cb) { cb(); } return; } setStatus("Trying linkdecrypter.com",-1); const crypted = document.getElementsByName('crypted')[0].value; const jk = document.getElementsByName('jk')[0].value; GM.xmlHttpRequest( { method: "POST", url: "http://linkdecrypter.com/api/?t=cnl2", data: 'crypted=' + encodeURIComponent(crypted) + '&jk=' + encodeURIComponent(btoa(jk)), headers: { "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", "Accept": "application/atom+xml,application/xml,text/xml", "Content-type" : "application/x-www-form-urlencoded" }, onload: function(response) { if(cb) { cb(); } const N = response.responseText.split("\n").length; if(!response.responseText || response.responseText.indexOf("ERROR(CNL2)") !== -1 || N === 0) { setStatus("An error occurred while handling the response of linkdecrypter.com",0); } else { menu(response.responseText); setStatus("Found "+(N===1?"one link":(N+" links")),1); } } }); */ } function getAllSerienjunkiesLinks (cb) { // Get all download links from a serienjunkies.org download page (i.e. the page right after the captcha) const urls = [] // [ [partnumber0,link0] , [partnumber1,link1] , .... ] let total = 0 const rap = document.getElementById('rap') const table = rap.getElementsByTagName('table')[0] const forms = table.getElementsByTagName('form') let j = 1 // part number, in order to make sure that sorting of the links is the same as on the page. // This is only a fallback in case there is no visible part number in the actual downloadlink/filename. for (let i = 0; i < forms.length; i++) { const url = forms[i].action if (url.indexOf('mirror') !== -1 || url.indexOf('firstload') !== -1) { continue } GM.xmlHttpRequest({ method: 'GET', url, onload: (function (j) { return function (response) { const loc = response.finalUrl // Actual link after posible redirections if (response.finalUrl.match(/part*(\d+)\./)) { // Try to guess part number const part = response.finalUrl.match(/part*(\d+)\./)[1] urls.push([parseInt(part, 10), loc]) } else { // fallback part number urls.push([j, loc]) } setStatus('Decrypting: ' + urls.length + '/' + total, total === urls.length ? 1 : -1) if (total === urls.length) { // Got all links cb(urls) } } }(j)) }) j++ } total = j - 1 }; function getSerienjunkiesLinks (cb) { // Get all the links from the page getAllSerienjunkiesLinks(function (urls) { if (cb) { cb() } urls = urls.sort(function (a, b) { if (a[0] > b[0]) return 1 else if (a[0] < b[0]) return -1 return 0 }) let alllinks = '' for (let i = 0; i < urls.length; ++i) { alllinks += urls[i][1] + '\n' } menu(alllinks) }) } function getFilecryptcc (jddata, cb) { // Get all the links by decrypting the Click'n'Load form const fieldJk = jddata[0] const fieldCrypted = jddata[1] const r = decryptClickNLoad(cb, fieldJk, fieldCrypted) if (!r) { setStatus("Could not find Click'n'Load", -1) if (cb) { cb() } } } function getSafeLinkingNetLinks (cb) { // Get all the links by following each link const crypticUrls = [] $('div.links-container.result-form a.result-a').each(function () { if (this.getAttribute('href') && this.getAttribute('href').indexOf('/d/') !== -1) { crypticUrls.push(this.getAttribute('href')) } }) const urls = [] let total = 0 let j = 1 for (let i = 0; i < crypticUrls.length; i++) { GM.xmlHttpRequest({ method: 'GET', url: crypticUrls[i], onload: (function (j) { return function (response) { const loc = response.finalUrl // Actual link after posible redirections urls.push(loc) setStatus('Decrypting: ' + urls.length + '/' + total, -1) if (total === urls.length) { // Got all links cb() menu(urls) setStatus('Found ' + (total === 1 ? 'one link' : (total + ' links')), 1) } } }(j)) }) j++ } total = j - 1 }; const linkSelectorFilter = { _filter: function (key) { const a = Array.prototype.slice.call(arguments, 1) return function () { linkSelectorFilter[key].apply(linkSelectorFilter, a) } }, all: function (trs) { for (let i = 0; i < trs.length; i++) { trs[i].$check.prop('checked', true) } }, none: function (trs) { for (let i = 0; i < trs.length; i++) { trs[i].$check.prop('checked', false) } }, flip: function (trs) { for (let i = 0; i < trs.length; i++) { trs[i].$check.prop('checked', !trs[i].$check.prop('checked')) } }, has: function (trs, inpFilter) { const s = inpFilter.val() for (let i = 0; i < trs.length; i++) { if (trs[i].link.indexOf(s) !== -1) { trs[i].$check.prop('checked', !trs[i].$check.prop('checked')) } } }, host: function (trs, $selHost) { const h = $selHost.val() for (let i = 0; i < trs.length; i++) { if (trs[i].host === h) { trs[i].$check.prop('checked', !trs[i].$check.prop('checked')) } } }, fromto: function (trs, $table, $thead, $th) { const _self = this for (let i = 0; i < trs.length; i++) { trs[i].$check.prop('disabled', true) } $table.find('td').hover(function () { $(this).parent().find('td').each(function (i, e) { $(e).css('background', 'PaleGreen') }) }, function () { $(this).parent().find('td').each(function (i, e) { $(e).css('background', '') }) }) $thead.find('th').css('display', 'none') $th.css('display', '') $th.html('Select from where to start') $table.find('td').click(function () { const from = $(this.parentNode).data('index') $(this).parent().find('td').css('background', 'PaleGreen') $table.find('td').unbind('click mouseenter mouseleave') $th.html('Select where to stop') $table.find('td').hover(function () { const to = $(this.parentNode).data('index') $table.find('td').each(function (i, e) { const $e = $(e) const j = $e.parent().data('index') if (j > from && j <= to) { $e.css('background', 'DarkSeaGreen') } else if (j > from && j > to) { $e.css('background', '') } }) if ($(this).parent().data('index') > from) $(this).parent().find('td').css('background', 'PaleGreen') }) $table.find('td').filter(function (i, e) { return $(e.parentNode).data('index') > from }).click(function () { const to = $(this.parentNode).data('index') + 1 $table.find('td').unbind('click mouseenter mouseleave') $(this).parent().find('td').css('background', 'PaleGreen') $table.find('td').each(function (i, e) { const $e = $(e) const j = $e.parent().data('index') if (j < from || j >= to) { $e.css('display', 'none') } }) const ntrs = trs.slice(from, to) for (let i = 0; i < ntrs.length; i++) { ntrs[i].$check.prop('disabled', false) } $th.html('Select ') $('<button>').appendTo($th).text('all').click(_self._filter('all', ntrs)) $('<button>').appendTo($th).text('none').click(_self._filter('none', ntrs)) $('<button>').appendTo($th).text('flip').click(_self._filter('flip', ntrs)) $('<button>').appendTo($th).text('return to all links').click(function () { $table.find('td').each(function (i, e) { const $e = $(e) $e.css('display', '') $e.css('background', '') }) $thead.find('th').css('display', '') $th.css('display', 'none') $th.html('') for (let i = 0; i < trs.length; i++) { trs[i].$check.prop('disabled', false) } }) $th[0].scrollIntoView() return false }) }) }, every: function (trs, $table, $thead, $th) { const _self = this for (let i = 0; i < trs.length; i++) { trs[i].$check.prop('disabled', true) } $table.find('td').hover(function () { $(this).parent().find('td').each(function (i, e) { $(e).css('background', 'PaleGreen') }) }, function () { $(this).parent().find('td').each(function (i, e) { $(e).css('background', '') }) }) $thead.find('th').css('display', 'none') $th.css('display', '') $th.html('Select from where to start') $table.find('td').click(function () { const from = $(this.parentNode).data('index') $(this).parent().find('td').css('background', 'PaleGreen') $table.find('td').unbind('click mouseenter mouseleave') $th.html('Select next') $table.find('td').hover(function () { const to = $(this.parentNode).data('index') const diff = to - from if (to < from + 2) { $table.find('td').filter(function (i, e) { return $(e.parentNode).data('index') > from + 1 }).css('background', '') } else { $table.find('td').filter(function (i, e) { return $(e.parentNode).data('index') > from + 1 }).each(function (i, e) { const j = $(this.parentNode).data('index') if ((j - from) % diff === 0 && j > from + 1) { $(this).css('background', 'PaleGreen') } else { $(this).css('background', '') } }) $(this).parent().find('td').css('background', 'DarkSeaGreen') } }).click(function () { const to = $(this.parentNode).data('index') if (to < from + 2) return false $(this).parent().find('td').css('background', 'PaleGreen') const diff = to - from $table.find('td').unbind('click mouseenter mouseleave') $table.find('td').each(function (i, e) { const $e = $(e) const j = $e.parent().data('index') if ((j - from) % diff !== 0 || j < from) { $e.css('display', 'none') } }) const ntrs = [] for (let i = 0; i < trs.length; i++) { if ((i - from) % diff === 0 && i >= from) { trs[i].$check.prop('disabled', false) ntrs.push(trs[i]) } } $th.html('Select ') $('<button>').appendTo($th).text('all').click(_self._filter('all', ntrs)) $('<button>').appendTo($th).text('none').click(_self._filter('none', ntrs)) $('<button>').appendTo($th).text('flip').click(_self._filter('flip', ntrs)) $('<button>').appendTo($th).text('return to all links').click(function () { $table.find('td').each(function (i, e) { const $e = $(e) $e.css('display', '') $e.css('background', '') }) $thead.find('th').css('display', '') $th.css('display', 'none') $th.html('') for (let i = 0; i < trs.length; i++) { trs[i].$check.prop('disabled', false) } }) $th[0].scrollIntoView() return false }) }) } } function linkSelector (links) { const filter = function (key) { const a = Array.prototype.slice.call(arguments, 1) return function () { linkSelectorFilter[key].apply(linkSelectorFilter, a) } } const trs = [] const selectedLinks = [] // Coyp array and remove empty elements for (let i = 0; i < links.length; i++) { const t = $.trim(links[i]) if (t) { selectedLinks.push(t) } } if (linksBeforeSelection === false) { linksBeforeSelection = links.slice(0) // Save all links for later selections } const allLinks = linksBeforeSelection.slice(0) const popup = popUp('linkSelector') const $div = popup.node const $loader = $('<div style="width:20px; height:20px;" class="ochspinner"></div>').appendTo($div) $div.css('overflow', 'none') const $frame = $('<iframe style="border:0">').appendTo($div) $frame.attr('width', window.innerWidth - 190) $frame.attr('height', window.innerHeight - 120) $frame.bind('load', function (e) { const $body = $($frame[0].contentDocument.getElementsByTagName('body')[0]) const $main = $('<div>').appendTo($body) const $table = $('<table>').appendTo($main) const $thead = $('<thead>').appendTo($table) const $tr0 = $('<tr>').appendTo($thead) const $th0 = $('<th>').appendTo($tr0).attr('colspan', 2) const $tr1 = $('<tr>').appendTo($thead) const $th1 = $('<th>').appendTo($tr1).attr('colspan', 2) const $tr2 = $('<tr>').appendTo($thead) const $th2 = $('<th>').appendTo($tr2).attr('colspan', 2) const $tr3 = $('<tr>').appendTo($thead) const $th3 = $('<th>').appendTo($tr3).attr('colspan', 2) const $tr4 = $('<tr>').appendTo($thead) const $th4 = $('<th>').appendTo($tr4).attr('colspan', 2) $('<span>Select: <span>').appendTo($th0) $('<button>').appendTo($th0).text('all').click(filter('all', trs)) $('<button>').appendTo($th0).text('none').click(filter('none', trs)) $('<button>').appendTo($th0).text('flip').click(filter('flip', trs)) $('<button>').appendTo($th1).text('Select from ... to ...').click(filter('fromto', trs, $table, $thead, $th4)) $('<button>').appendTo($th1).text('Select every ...').click(filter('every', trs, $table, $thead, $th4)) $('<span> Filter:<span>').appendTo($th2) const inpFilter = $('<input>').appendTo($th2).attr('type', 'text') $('<button>').appendTo($th2).text('Flip with filter').click(filter('has', trs, inpFilter)) $('<span> Host filter:<span>').appendTo($th3) const $selHost = $('<select>').appendTo($th3) $('<button>').appendTo($th3).text('Flip with host filter').click(filter('has', trs, $selHost)) const allhosts = [] for (let i = 0; i < allLinks.length; i++) { const $tr = $('<tr>').data('index', i).appendTo($table) const $td0 = $('<td>').appendTo($tr) const $check = $('<input>').appendTo($td0).attr('type', 'checkbox').attr('id', 'link_checkbox_' + i).prop('checked', selectedLinks.indexOf(allLinks[i]) !== -1) const $td1 = $('<td>').appendTo($tr) $('<label>').attr('for', 'link_checkbox_' + i).text(allLinks[i]).css('font-family', 'monospace').appendTo($td1) const host = allLinks[i].split('/')[2].replace(/^www\./, '') if (allhosts.indexOf(host) === -1) { allhosts.push(host) } trs.push({ $tr, $check, link: allLinks[i], host }) } for (let i = 0; i < allhosts.length; i++) { $('<option>').val(allhosts[i]).text(allhosts[i]).appendTo($selHost) } $('<button>').appendTo($main).text('Apply').click(function () { const nlinks = [] for (let i = 0; i < trs.length; i++) { if (trs[i].$check.prop('checked')) { nlinks.push(trs[i].link) } } if (nlinks.length === 0) { alert('No links selected?!') return } menu(nlinks) setStatus((nlinks.length === 1 ? 'One link' : (nlinks.length + ' links')) + ' selected', 1) popup.close() }) $loader.remove() }) if (chrome) { $frame.attr('src', 'about:blank') } } function menu (links) { // normalize links: if (!Array.isArray(links)) { const parts = links.split('\n') links = [] for (let i = 0; i < parts.length; i++) { if ($.trim(parts[i])) { links.push($.trim(parts[i])) } } } const $c = $('#multiochhelper ul') $c.html('') const $select = $('<select>') const m = links[0].match(/https?:\/\/(.+?)\//) if (!m) { console.log(scriptName + ": Not a valid link: '" + links[0] + "'") return } const host = m[1] let hoster = host.split('.') hoster.pop() hoster = hoster.pop().replace('-', '') $.each(multi, function (key, val) { const $option = $('<option></option>').val(key).html(val.name).appendTo($select) if (key === currentdebrid) { $option[0].selected = true } if (multi[key].isOnline(hoster)) { $option.css('color', 'green') } else { $option.css('color', '#F00') } }) let $entry = menuentry($select) $select.bind('change', function (ev) { const $this = $(this) // Change hoster currentdebrid = $this.val() // Check general support if (multi[currentdebrid].isOnline(hoster)) { // Check first link for support on this multi hoster multi[currentdebrid].checkLink(links[0], function (result) { if (!result) { alert(scriptName + '\n\n' + host + ' is not supported by this hoster or the file is offline.\n\nChecked: ' + links[0]) } }) } else { alert(scriptName + '\n\n' + host + ' is not supported by ' + multi[currentdebrid].name) } // Add "Remember" checkbox if (!$this.parent().find('#remember').length) { const $div = $('<div>') const $check = $('<input id="remember" type="checkbox" value="remember" title="Remember selection">').click(async function () { if (this.checked) { currentdebrid = $select.val() await GM.setValue('currentdebrid', currentdebrid) setStatus('Switched to ' + multi[currentdebrid].name, 1) $div.remove() } }) $div.append($check).append('Remember') $this.parent().append($div) } }) $entry = menuentry('Direct download') $entry.click(function () { mouse('download', links) }) $entry = menuentry('Copy to clipboard') $entry.click(function () { mouse('clipboard', links) }) if (settings.jDownloaderSupport) { $entry = menuentry('Send to JDownloader') $entry.attr('id', 'multiochhelperjdbutton') $entry.hide() $entry.click(function () { mouse('sendToJD', links) }) GM.xmlHttpRequest({ method: 'GET', url: JDOWNLOADER + 'flash/', onerror: function () { }, onload: function (resp) { if (resp && resp.responseText && resp.responseText.startsWith('JDownloader')) { $('#multiochhelperjdbutton').show() } } }) } if (!showOneclickFromHighlighScriptAllLinks) { $entry = menuentry('Show generated links') $entry.click(function () { mouse('showLinks', links) }) } $entry = menuentry('Show extracted links') $entry.click(function () { if (window.parent.parent !== window) { window.parent.parent.postMessage({ iAm: 'Unrestrict.li', type: 'alert', str: links.join('\n') }, '*') alert(links.join('\n')) } else { showExtractedLinks(links) } }) if (!showOneclickFromHighlighScriptAllLinks && (links.length > 1 || linksBeforeSelection !== false)) { $entry = menuentry('Select links') $entry.click(function () { linkSelector(links) }) } if (!showOneclickFromHighlighScriptAllLinks) { $entry = menuentry() $('<a style="color:white !important;">Open Website</a>').attr('href', getMultiOCHWebsiteURL(links)).appendTo($entry) } if (showOneclickFromHighlighScriptAllLinks && showOneclickFromHighlighScriptAllLinksLinks) { $entry = $(menuentry('Use all links on page...')) $entry.click(function () { // Switch to all links instead of one const links = showOneclickFromHighlighScriptAllLinksLinks showOneclickFromHighlighScriptAllLinksLinks = '' menu(links) $('#multiochhelper div:empty:not(:first)').remove() setStatus('All links!', 1) }) } if (showOneclickFromHighlighScriptSelectedLinks && showOneclickFromHighlighScriptSelectedLinksLinks) { $entry = $(menuentry('Use selected links...')) $entry.click(function () { // Switch to selected links instead of one const links = showOneclickFromHighlighScriptSelectedLinksLinks showOneclickFromHighlighScriptSelectedLinksLinks = '' menu(links) $('#multiochhelper div:empty:not(:first)').remove() setStatus('Using selected links!', 1) }) } if (!showOneclickFromHighlighScriptAllLinks) { $entry = menuentry($('<span style="cursor:default; color:silver">Userscript menu</span>').click(function (ev) { ev.stopPropagation(); aboutMe() })) $entry.css('cursor', 'default') $('<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) } } function loader () { // Show an animation, return function to remove the loader $('#multiochhelper_status_loader').parent().show() const $div = $('<div class="ochspinner"></div>').appendTo($('#multiochhelper_status_loader')) return function () { $div.remove() } } async function mouse (action, linkText) { // decide what to do after a mouse click const removeImg = loader() if (action === 'download') { await download(linkText, removeImg) } else if (action === 'showLinks') { showLinks(linkText, removeImg) } else if (action === 'openWebsite') { openWebsite(linkText) } else if (action === 'clipboard') { await clipboard(linkText, removeImg) } else if (action === 'menu') { removeImg() menu(linkText) } else if (action === 'sendToJD') { await sendToJD(linkText, removeImg) } } function menuentry (html) { const $li = $('<li>') if (html) { $li.append(html) } $li.appendTo('#multiochhelper ul') return $li } function button (label) { addCSSHead(` #multiochhelper,#multiochhelper * { font-family:Sans-Serif !important; padding:0px; margin:0px; } #multiochhelper a, #multiochhelper a:link,#multiochhelper a:visited { text-decoration:underline !important; color:#3788e8 !important; font-style:none !important; } #multiochhelper a:hover { text-decoration:none !important; color:#3788e8 !important; font-style:none !important; } #multiochhelper ul li,#multiochhelper_status { margin:1px 1px; padding:1px 5px; font-size:13px; text-shadow:0 -1px 0 #333333; color:White; border:1px solid #8B3D92; background-color:#B555C5; background:radial-gradient(ellipse at center, #B555C5, #8B3D92); list-style:none outside; } #multiochhelper div#multiochhelper_status_loader { float:left; } #multiochhelper div#multiochhelper_status_text { float:left; } #multiochhelper div#multiochhelper_status_clear { clear:left; } #multiochhelper ul li { cursor:pointer; } #multiochhelper ul li:hover { background-color:#CC6BDD; background:radial-gradient(ellipse at center, #CC6BDD, #8B3D92); } #multiochhelper select,#multiochhelper input { border-radius:0; box-shadow:none; text-shadow:none; border:none; background:white; color:black; } ${SPINNERCSS} `) // div container const zi = getNextZIndex() const $div = $('<div>').appendTo(document.body) $div.attr('id', 'multiochhelper') $div.attr('style', 'z-index:' + zi + '; position:fixed; background:#E6E6E6; color:Black; border:#B555C5 2px solid;border-radius:5px; padding:3px;') if (settings.position[0] === 'top') { $div.css('top', '0%') } else { $div.css('bottom', '0%') } if (settings.position[1] === 'left') { $div.css('left', '0%') } else { $div.css('right', '0%') } // Status const $status = $('<div>').appendTo($div).hide() $status.attr('id', 'multiochhelper_status') const $loader = $('<div>').appendTo($status) $loader.attr('id', 'multiochhelper_status_loader') const $statustext = $('<div>').appendTo($status) $statustext.attr('id', 'multiochhelper_status_text') const $statusclear = $('<div>').appendTo($status) $statusclear.attr('id', 'multiochhelper_status_clear') const $ul = $('<ul>').appendTo($div) // Button const $entry = menuentry(label || (multi[currentdebrid].name.charAt(0).toUpperCase() + multi[currentdebrid].name.slice(1))) $ul.append($entry) return $entry } const isSetup = await GM.getValue('setup', false) // Update hoster status let updatinghosters = false if (isSetup) { for (const key in multi) { if (multi[key].updateStatusURLpattern.test(document.location.href)) { // usually in this is true in the iframe which is defined below multi[key].updateStatus() updatinghosters = true break } } } // Create iframes to check hoster status: if (!updatinghosters && isSetup) { const now = new Date() for (const key in multi) { if ('updateStatusURL' in multi[key] && (now - multi[key].lastUpdate) > (settings.updateHosterStatusInterval * 60 * 60 * 1000)) { const $iframe = $('<embed>').appendTo(document.body) $iframe.bind('load', function () { const frame = this window.setTimeout(function () { $(frame).remove() }, 3000) }) $iframe.attr('src', multi[key].updateStatusURL) } } } // Setup if (!updatinghosters) { if (!isSetup) { await aboutMe() 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!')) { await GM.setValue('setup', true) alert(scriptName + '\n\nDefault settings will be used.') document.location.reload() } } } if (document.location.href.indexOf('nopremium.pl') !== -1) { // nopremium.pl Website if (document.location.search.substring(0, 6) === '?link:') { // Insert link on nopremium.pl $('#filesList').val(decodeURIComponent(document.location.search.substring(6))) } } else if (document.location.href.indexOf('www.premiumize.me') !== -1) { // premiumize.me Website if (document.location.search.substring(0, 6) === '?link:') { // Insert link on nopremium.pl $('textarea').val(decodeURIComponent(document.location.search.substring(6))) } } else if (document.location.href.indexOf('download.serienjunkies.org') !== -1) { // Serienjunkies if (!document.querySelector('.g-recaptcha')) { // if not on captcha page const $b = button('Decrypt links') $b.click(function (ev) { const removeImg = loader() getSerienjunkiesLinks(removeImg) }) } } else if (document.location.href === 'http://filecloud.io/download.html') { // filecloud.io if (unsafeWindow.__currentUrl) { showOneclickButton = true showOneclickLink = decodeURIComponent(unsafeWindow.__currentUrl) } } else if (document.location.href.indexOf('filecrypt.cc') !== -1) { // filecrypt.cc folder if (document.location.href.indexOf('helper.html') !== -1) { // if not on captcha page window.addEventListener('message', function filecryptmessage (event) { if (event.data && typeof (event.data) === 'object') { window.opener.postMessage({ filecryptData: JSON.stringify(event.data) }, '*') // Send message back to the opening window window.removeEventListener('message', filecryptmessage) // Prevent further messages from creating several buttons } }, false) } else if (document.location.href.indexOf('Container') !== -1) { // if not on captcha page const $b = button("Please open the Click'n'Load Popup (several times)") $b.click(function () { $('#cnl_btn').click() }) window.addEventListener('message', function filecryptmessage2 (event) { // Receive messages from the popup if (event.data && typeof (event.data) === 'object' && 'filecryptData' in event.data) { window.removeEventListener('message', filecryptmessage2) // Prevent further messages from creating several buttons setStatus('Decrypting', -1) const removeImg = loader() getFilecryptcc(JSON.parse(event.data.filecryptData), removeImg) } }, false) } } else if (document.location.href.substring(7, 22) === 'protected.to/f-') { // http://protected.to folder if (document.querySelectorAll('.links a').length > 0) { // If not on captcha page showOneclickButton = true showOneclickLink = '' $('.links a').each(function () { showOneclickLink += decodeURIComponent(this.href) + '\n' }) } } else if (document.location.href.substring(8, 23) === 'safelinking.net') { // safelinking.net folder if (!document.getElementById('captcha-wrapper')) { const $b = button('Decrypt links') $b.click(function (ev) { const removeImg = loader() getSafeLinkingNetLinks(removeImg) }) } } else if (document.location.href.indexOf('.firedrive.com/share/') !== -1) { // firedrive.com folder showOneclickButton = true showOneclickLink = '' $('a.pf_item_link:visible').each(function () { showOneclickLink += decodeURIComponent(this.href) + '\n' }) } else if (document.location.href.indexOf('rapidgator.net/folder/') !== -1) { // Rapidgator folder showOneclickButton = true showOneclickLink = '' $('#grid tbody a').each(function () { showOneclickLink += decodeURIComponent(this.href) + '\n' }) } else if (document.location.hostname === "dailyuploads.net" && currentdebrid === 'premiumize.me') { // Dailyuploads.net: submit direct download link (after captcha was solved) to premiumize.me instead of link if (document.querySelector('div.banner div.inner a>img[src*="redbutton.png"]')) { showOneclickButton = true showOneclickLink = document.querySelector('div.banner div.inner a>img[src*="redbutton.png"]').parentNode.href } else { showOneclickButton = false button("Please solve the captcha first") } } else if (document.location.hostname === 'multiup.org') { // Multiup.org mirrors showOneclickButton = document.querySelectorAll('button[link]').length > 0 showOneclickLink = Array.from(document.querySelectorAll('button[link]')).map(b => b.getAttribute('link')).join('\n') } else if (document.location.href.substring(0, 55) === 'https://cvzi.github.io/Userscripts/index.html?link=sync') { // Window opened from Helper script to sync hoster status (see postMessage events below) showOneclickButton = false const message = 'Updating hoster status...' const h1 = document.body.appendChild(document.createElement('h1')) h1.appendChild(document.createTextNode(scriptHightligherName + ': ' + message)) setTitle('') window.setTimeout(function () { const h2 = document.body.appendChild(document.createElement('h2')) h2.appendChild(document.createTextNode('You may close this tab now')) }, 4000) } else if (document.location.href.substring(0, 51) === 'https://cvzi.github.io/Userscripts/index.html?link=') { // Iframe for a X-Frame-Options website showOneclickButton = true showOneclickLink = decodeURIComponent(document.location.search.match(/link=(.+)/)[1]) } else { // One click hoster website showOneclickButton = true showOneclickLink = decodeURIComponent(document.location.href) } if (showOneclickButton) { let mouseOverAvailable = true // Split links into array const splitted = showOneclickLink.split('\n') showOneclickLink = [] for (let i = 0; i < splitted.length; i++) { if ($.trim(splitted[i])) { showOneclickLink.push($.trim(splitted[i])) } } const $b = button() $b.bind('mousedown', function (ev) { mouseOverAvailable = false if (ev.which === 3) { // Right click mouse(settings.rightClick, showOneclickLink) } else if (ev.which === 2) { // Middle click mouse(settings.middleClick, showOneclickLink) } else if (ev.which === 1) { // Left click { mouse(settings.leftClick, showOneclickLink) } }) if (settings.mouseOver !== 'none') { let ti = false $b.on({ mouseover: function () { if (!mouseOverAvailable) { return } ti = setTimeout(function () { if (!mouseOverAvailable) { return } mouseOverAvailable = false mouse(settings.mouseOver, showOneclickLink) }, settings.mouseOverDelay) }, mouseout: function () { if (ti !== false) clearTimeout(ti) } }) } // Prevent context menu on right click if (settings.rightClick !== 'none') { $b[0].addEventListener('contextmenu', e => e.preventDefault(), false) } } // Handle messages from the highlight script window.addEventListener('message', function (e) { if (typeof e.data !== 'object' || !('iAm' in e.data) || e.data.iAm !== 'Unrestrict.li') { return } switch (e.data.type) { case 'alllinks': if (showOneclickFromHighlighScriptAllLinks) { return } showOneclickFromHighlighScriptAllLinks = true showOneclickFromHighlighScriptAllLinksLoc = e.data.loc showOneclickFromHighlighScriptAllLinksLinks = e.data.links.join('\n') if ($('#multiochhelper ul li').length > 1) { // Menu already opened menu(showOneclickLink) } break case 'selectedlinks': if (showOneclickFromHighlighScriptSelectedLinks) { return } showOneclickFromHighlighScriptSelectedLinks = true showOneclickFromHighlighScriptSelectedLinksLoc = e.data.loc showOneclickFromHighlighScriptSelectedLinksLinks = e.data.links.join('\n') if ($('#multiochhelper ul li').length > 1) { // Menu already opened menu(showOneclickLink) } break case 'requesthosterstatus': { window.setTimeout(function () { const h3 = document.body.appendChild(document.createElement('h3')) h3.appendChild(document.createTextNode('This will only take a few seconds')) }, 0) const o = {} for (const key in multi) { o[key] = multi[key].status } e.source.postMessage({ iAm: 'Unrestrict.li', type: 'hosterstatus', str: JSON.stringify(o) }, '*') break } } }, true) })()