// ==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 // @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 // @downloadURL https://update.greasyfork.icu/scripts/13884/Multi-OCH%20Helper.user.js // @updateURL https://update.greasyfork.icu/scripts/13884/Multi-OCH%20Helper.meta.js // ==/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 https://www.premiumize.me/account' } 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('')[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 ? ('' + urls.length + ' files') : ('one file')) + '!

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('') !== -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 // 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 ' + size + " 'Transfer' for generating " + (hashes.length > 1 ? ('' + hashes.length + ' files') : ('one file')) + '!

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 = $('
').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($('
').append(response.responseText).find('#fastFilesArea')) } else { $resultContainer.append($('
').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() /* 16-08-2014 20:22 Sciagnij */ const files = [] const re = /(\d+)-(\d+)-(\d+) (\d+):(\d+)<\/td>(\s|\n)+ 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('') !== -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 ',-1); const dotheight = N > 2 ? 2 : 4 let h = 'Download ' + result.length + '/' + N + ' (' + Math.floor(percent) + '%)\n
' for (let i = 0; i < N; i++) { if (progess[i]) { h += '' } else { h += '' } } h += '
' 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 = $('
').attr('id', id).appendTo(document.body) const $background = $('
').appendTo($par) const $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 = $('

').appendTo($form) if (c[key + '_quest']) { $p.append(c[key + '_quest']) } else { $p.append(key) } $p.append('
') 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 = $('').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 = $('').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 = $('').addClass('form_' + formid).data('key', key).data('parse', 'bool').appendTo($p) const $optionYes = $('').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 = $('').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 = $('').addClass('form_' + formid).data('key', key).data('index', j).appendTo($p) for (let i = 0; i < c[key][j].length; i++) { const $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('
') } } else { // Array const $select = $('').addClass('form_' + formid).data('key', key).appendTo($p) for (let i = 0; i < c[key].length; i++) { const $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 = $('