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 Highlight links // @namespace cuzi // @license MIT // @copyright 2014, cuzi (https://openuserjs.org/users/cuzi) // @description nopremium.pl and premiumize.me. Highlight one-click-hoster links and include Multi-OCH Helper button // @homepageURL https://openuserjs.org/scripts/cuzi/Multi-OCH_Helper_Highlight_links // @updateURL https://openuserjs.org/meta/cuzi/Multi-OCH_Helper_Highlight_links.meta.js // @icon https://greasyfork.org/system/screenshots/screenshots/000/003/478/original/icon_ampel.png // @include * // @exclude *.yahoo.* // @exclude *.google.* // @exclude *.youtube.* // @exclude *.bing.com* // @exclude *duckduckgo.com* // @exclude *bandcamp.com* // @exclude *.tumblr.com* // @version 10.6 // @grant GM_setValue // @grant GM_getValue // @grant GM.setValue // @grant GM.getValue // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js // @require https://openuserjs.org/src/libs/cuzi/OCH_List.js // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // ==/UserScript== (async function () { "use strict"; const MAXTEXTNODES = 10000; const s_myname = "Multi-OCH Helper Highlight links"; const s_topname = "Multi-OCH Helper"; const syncHostersHost = "https://cvzi.github.io/" const syncHostersUrl = syncHostersHost + "Userscripts/index.html?link=sync" const chrome = ~navigator.userAgent.indexOf("Chrome") var $J = $.noConflict(true); var config = { mouseOverDelay: 700, frameWidth: "170px", frameHeight: "200px", colorHosterAvailableBG: 'green', colorHosterAvailableFG: 'white', colorHosterUnavailableBG: 'rgba(255,0,0,0.5)', colorHosterUnavailableFG: 'white', colorLinkOfflineBG: 'rgba(255,0,0,0.5)', colorLinkOfflineFG: 'silver', maxRequestsPerPage: 2, updateHosterStatusInterval: 24 * 7 // weekly update }; // These hosters are supported by but have a X-Frame-Options enabled or simply do not work without javascript. var noframeHosters = [ '1fichier', 'clicknupload', 'filecloud', 'filefactory', 'firedrive', 'mega', 'rapidgator', 'uploaded', 'uploadrocket', 'uptobox' ]; var multi = { 'nopremium.pl': new(function () { var self = this; this.key = 'nopremium.pl'; this.name = 'NoPremium.pl'; this.homepage = 'https://www.nopremium.pl/'; var mapHosterName = function (name) { return name.replace("-", "") }; this.updateStatusURL = 'https://www.nopremium.pl/'; this.updateStatusURLpattern = /https?:\/\/www\.nopremium\.pl.*/; this.status = {}; this.init = async function () { self.lastUpdate = new Date(await GM.getValue(self.key + "_status_time", 0)); self.status = JSON.parse(await GM.getValue(self.key + "_status", "{}")); }; this.updateStatus = async function () { // Update list of online hosters if (document.location.href.match(self.updateStatusURL)) { // Read and save current status of all hosters self.status = {}; $J("#servers a[title]").each(function () { var name = mapHosterName(this.title); self.status[name] = true; }); await GM.setValue(self.key + "_status", JSON.stringify(self.status)); await GM.setValue(self.key + "_status_time", "" + (new Date())); console.log(s_myname + ": " + self.name + ": Hosters (" + Object.keys(self.status).length + ") updated"); } else { alert(s_myname + "\n\nError: wrong update URL"); } }; this.isOnline = function (hostername) { return hostername in self.status && self.status[hostername]; }; })(), 'premiumize.me': new(function () { var self = this; this.key = 'premiumize.me'; this.name = 'premiumize'; this.homepage = 'https://www.premiumize.me/'; var mapHosterName = function (name) { return name.replace("-", "") }; this.updateStatusURL = 'https://www.premiumize.me/hosters'; this.updateStatusURLpattern = /https:\/\/www\.premiumize\.me\/hosters\/?/; this.status = {}; this.init = async function () { self.lastUpdate = new Date(await GM.getValue(self.key + "_status_time", 0)); self.status = JSON.parse(await GM.getValue(self.key + "_status", "{}")); }; this.updateStatus = function () { // This works only with api key, which only the main script has }; this.isOnline = function (hostername) { return hostername in self.status && self.status[hostername]; }; })() }; function matchHoster(str) { // Return name of first hoster that matches, otherwise return false for (var name in OCH) { for (var i = 0; i < OCH[name].pattern.length; i++) { if (OCH[name].pattern[i].test(str)) { return name } } } return false; } // All suitable urls are saved in this array: var alllinks = []; function frameSrc(src) { // Prevent websites from busting the iframe by using a second "sandboxed" iframe // It's a kind of magic. var framesrc = 'data:text/html,'; framesrc += encodeURIComponent('<!DOCTYPE html>\ <html lang="en">\ <head>\ <meta charset="utf-8">\ <title>HTML5</title>\ <style>* { margin:0px; padding:0px; }</style>\ <script>\ function addlistener() {\ window.addEventListener("message", function(e){\ if(! "iAm" in e.data || e.data.iAm != "Unrestrict.li") return; \ document.getElementById("mysandyframe").contentWindow.postMessage(e.data,\'*\');\ }, true);\ }\ </script>\ </head>\ <body onload="addlistener();">\ <iframe\ id="mysandyframe"\ sandbox\ scrolling="no"\ frameborder="0"\ seamless="seamless"\ src="' + src + '"\ style="border: 0; width:' + config.frameWidth + '; height:' + config.frameHeight + ';">\ </body>\ </html>'); return framesrc; } function showMenu(jlink, textContent) { // Show the button var link; if (textContent) { link = jlink.text(); } else { link = jlink.attr("href"); } /* if(noframeHosters.indexOf(jlink.data('hoster')) != -1) { link = 'https://cvzi.github.io/Userscripts/index.html?link='+encodeURIComponent(link); } */ // Create iframe // var frame = $J("<iframe></iframe>"); // GREASEMONKEY4 var frame = $J("<embed></embed>"); //if(noframeHosters.indexOf(jlink.data('hoster')) != -1) { // GREASEMONKEY4 if (true) { frame.attr("src", 'https://cvzi.github.io/Userscripts/index.html?link=' + encodeURIComponent(link)); } else { frame.attr("src", frameSrc(link)); } frame.attr("scrolling", "no"); frame.attr("frameborder", "no"); frame.attr("seamless", "seamless"); var p = jlink.offset(); frame.css({ "position": "absolute", "background": "white", "width": config.frameWidth, "height": config.frameHeight, "top": p.top + 15, "left": p.left, "padding": "1px", "boxShadow": "3px 3px 5px #444", "border": "4px solid #9055c5", "borderRadius": "0 5px 5px 5px" }); frame.appendTo(document.body); // Send all links on this page to the "Multi-OCH Helper" setInterval(function () { if (frame[0].contentWindow) frame[0].contentWindow.postMessage({ "iAm": "Unrestrict.li", "type": "alllinks", "links": alllinks, "loc": document.location.href }, '*'); }, 500); // Check whether more links are selected var sel = window.getSelection(); var selelected_links = []; if (!sel.isCollapsed) { for (var j = 0; j < sel.rangeCount; j++) { var frag = sel.getRangeAt(j).cloneContents(); var span = document.createElement("span"); span.appendChild(frag); var a = span.getElementsByTagName("a"); for (var i = 0; i < a.length; i++) { var url = a[i].href; var m = matchHoster(url); if (url && m !== false) { selelected_links.push(url); } } } } if (selelected_links.length > 0) { setInterval(function () { if (frame[0].contentWindow) frame[0].contentWindow.postMessage({ "iAm": "Unrestrict.li", "type": "selectedlinks", "links": selelected_links, "loc": document.location.href }, '*'); }, 500); } // Close frame on first click and prevent the <a>-element from opening a new window jlink.data('onclick', jlink[0].onclick); jlink[0].onclick = null; jlink.one("click", function (event) { event.stopImmediatePropagation(); event.preventDefault(); let jthis = $J(this); // Close frame frame.remove(); // Restore onclick event this.onclick = jthis.data('onclick'); // Restore mouseover event jthis.data('mouseOverAvailable', true); jthis.data('mouseOverTimeout', false); return false; }); } var firstAttach = true; var attachEvents = function () { let links = []; // Normalize hoster object: Replace single patterns with arrays [RegExp] if (firstAttach) { for (let name in OCH) { if (!Array.isArray(OCH[name].pattern)) { OCH[name].pattern = [OCH[name].pattern]; } } firstAttach = false; } // Find all text nodes that contain "http://" let nodes = []; let walk = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, { acceptNode: function (node) { if (node.parentNode.href || node.parentNode.parentNode.href || node.parentNode.parentNode.parentNode.href) return NodeFilter.FILTER_REJECT; if (node.parentNode.tagName == "TEXTAREA" || node.parentNode.parentNode.tagName == "TEXTAREA") return NodeFilter.FILTER_REJECT; if (node.data.match(/(\s|^)https?:\/\/\w+/)) return NodeFilter.FILTER_ACCEPT; } }, false); let node = walk.nextNode(); while (node) { nodes.push(node); node = walk.nextNode(); } // For each found text nodes check whether the URL is a valid OCH URL for (var i = 0; i < nodes.length && i < MAXTEXTNODES; i++) { if ("" === nodes[i].data) { continue; } var httpPosition = nodes[i].data.indexOf("http"); if (httpPosition == -1) { continue; } var urlnode; if (httpPosition > 0) { urlnode = nodes[i].splitText(httpPosition); // Split leading text } else { urlnode = nodes[i]; } var stop = urlnode.data.match(/$|\s/)[0]; // Find end of URL if ("" !== stop) { // If empty string, we found $ end of string var nextnode = urlnode.splitText(urlnode.data.indexOf(stop)); // Split trailing text if ("" !== nextnode.data && nextnode.data.indexOf("http") != -1) { // The trailing text might contain another URL nodes.push(nextnode); } } // Check whether the URL is a OCH. If so, create an <a> element var url = urlnode.data; var m = matchHoster(url); if (url && url && m !== false) { // Create <a> element var a = document.createElement("a"); a.href = url; a.appendChild(urlnode.parentNode.replaceChild(a, urlnode)); var li = $J(a); links.push({ 'hoster': m, 'url': url, 'element': li }); alllinks.push(url); } } // Find actual <a> links let al = document.getElementsByTagName('a'); for (var i = 0; i < al.length; i++) { if (al[i].dataset && al[i].dataset.linkValidatedAs) { continue; // Link was already checked } var url = al[i].href; var mH = matchHoster(url); if (mH !== false) { let li = $J(al[i]); links.push({ 'hoster': mH, 'url': url, 'element': li }); alllinks.push(url); } } // Attach mouseover/out events to all the links for (var i = 0; i < links.length; i++) { var a = links[i].element; var hoster = links[i].hoster; if ("attached" in links[i] || a.data('hoster')) // Already attached continue; if (OCH[hoster].multi.length == 0) // Not supported by nopremium.pl according to hardcoded rules continue; var notsupported = true; for (var debrid in multi) { if (multi[debrid].isOnline(hoster)) { notsupported = false; break; } } if (notsupported) { continue; // Not supported by nopremium.pl according to status } links[i].attached = true; //if(links[i].data('hosterAvailable')) { // links[i].attr("style","background:"+config.colorHosterAvailableBG+"; color:"+config.colorHosterAvailableFG+";"); //} else { // links[i].attr("style","background:"+config.colorHosterUnavailableBG+"; color:"+config.colorHosterUnavailableFG+";"); //} a.attr("style", "background:" + config.colorHosterAvailableBG + "; color:" + config.colorHosterAvailableFG + ";"); a.data('hoster', hoster); a.data('mouseOverAvailable', true); a.data('mouseOverTimeout', false); a.on({ 'mouseover': function () { var link = $J(this); if (!link.data('mouseOverAvailable')) return; link.data('mouseOverTimeout', setTimeout(function () { if (!link.data('mouseOverAvailable')) return; link.data('mouseOverAvailable', false) showMenu(link); }, config.mouseOverDelay)); }, 'mouseout': function () { var link = $J(this); if (link.data('mouseOverTimeout') !== false) { clearTimeout(link.data('mouseOverTimeout')); link.data('mouseOverTimeout', false); } } }); } return links.length; } // Get OCH list const OCH = getOCH(); // Init hosters for (let key in multi) { await multi[key].init(); } // This is the start of everything var numberFoundLinks = 0; window.setTimeout(function () { numberFoundLinks = attachEvents(); }, 0); window.setTimeout(function () { if (numberFoundLinks == 0) numberFoundLinks = attachEvents(); }, 1500); // Let's try again. // Update hoster status for (let key in multi) { if (multi[key].updateStatusURLpattern.test(document.location.href)) { multi[key].updateStatus(); break; } } // Create iframes to update hoster status: let now = new Date(); for (let key in multi) { if ((now - multi[key].lastUpdate) > (7 * 24 * 60 * 60 * 1000)) { if (document.getElementById('multiochhelper')) { // Button is visible on this page window.setTimeout(function () { window.postMessage({ "iAm": "Unrestrict.li", "type": "requesthosterstatus" }, '*'); }, 1000) } else if (chrome) { // Chrome: we can use iframe to load Multi-OCH_Helper script in the frame let $iframe = $J("<iframe>").appendTo(document.body); $iframe.bind("load", function () { let frame = this; window.setTimeout(function () { $J(frame).remove(); }, 4000); if ($iframe[0].contentWindow) { $iframe[0].contentWindow.postMessage({ "iAm": "Unrestrict.li", "type": "requesthosterstatus" }, '*'); } }); $iframe.attr("src", syncHostersUrl); } else { // Firefox: we need to open a new tab to communicate with the Multi-OCH_Helper script if (document.location.href.startsWith(syncHostersHost)) { window.setTimeout(function () { window.postMessage({ "iAm": "Unrestrict.li", "type": "requesthosterstatus" }, '*'); }, 1000) } else { const w = window.open(syncHostersUrl) window.setTimeout(function () { if (w) { w.postMessage({ "iAm": "Unrestrict.li", "type": "requesthosterstatus" }, '*'); } window.setTimeout(function () { if (w) w.close() }, 3000) }, 1000) } } break } } // Handle messages from the button script window.addEventListener("message", async function (e) { if (typeof e.data != "object" || !("iAm" in e.data) || e.data.iAm != "Unrestrict.li") { return; } switch (e.data.type) { case "alert": // Alert on page, not in frame alert(e.data.str); break; case "findLinks": // Research links window.setTimeout(function () { numberFoundLinks = attachEvents(); }, 0); break; case "hosterstatus": { // Update hoster status, this script has no API access on premiumize, so it cannot update the hosters itself const data = JSON.parse(e.data.str) const result = {} for (let key in multi) { if (data && key in data) { await GM.setValue(key + "_status", JSON.stringify(data[key])); result[key] = Object.keys(data[key]).length multi[key].status = data[key] } await GM.setValue(key + "_status_time", "" + (new Date())); } console.log(s_myname + ": Received hoster status from " + s_topname + ": " + JSON.stringify(result)) break } } }, true); })();