// prettier-ignore var meta = { rawmdb: function () { // ==UserScript== // @name RARBG Enhancer // @namespace https://github.com/FarisHijazi // @version 1.6.35 // @description Auto-solve CAPTCHA, infinite scroll, add a magnet link shortcut and thumbnails of torrents, // @description adds a image search link in case you want to see more pics of the torrent, and more! // @author Faris Hijazi // with some code from https://greasyfork.org/en/users/2160-darkred // @grant unsafeWindow // @grant GM_setValue // @grant GM_getValue // @grant GM_download // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @icon https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://rarbg.to&size=16 // @run-at document-idle // @updateUrl https://github.com/FarisHijazi/Rarbg-Enhancer-UserScript/raw/master/Rarbg-Enhancer-UserScript.user.js // @downloadURL https://github.com/FarisHijazi/Rarbg-Enhancer-UserScript/raw/master/Rarbg-Enhancer-UserScript.user.js // @require https://code.jquery.com/jquery-3.4.0.min.js // @require https://unpkg.com/infinite-scroll@3.0.5/dist/infinite-scroll.pkgd.min.js // @require https://raw.githubusercontent.com/ccampbell/mousetrap/master/mousetrap.min.js // @require https://raw.githubusercontent.com/mitchellmebane/GM_fetch/master/GM_fetch.js // @require https://raw.githubusercontent.com/antimatter15/ocrad.js/master/ocrad.js // @require https://raw.githubusercontent.com/sizzlemctwizzle/GM_config/2207c5c1322ebb56e401f03c2e581719f909762a/gm_config.js // @include https://*rarbg.* // @include /https?:\/\/.{0,8}rarbg.*\.\/*/ // @include /https?:\/\/.{0,8}rargb.*\.\/*/ // @include /https?:\/\/.*u=MTcyLjIxLjAuMXw6Ly9yYXJiZy50by90b3JyZW50LzIyMDg3MjYwfE1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS83OS4wLjM5NDUuMTMwIFNhZmFyaS81MzcuMzZ8ODc4MDQz.*/ // @include https://www.rarbggo.to/ // @include https://www.rarbg.is // @include https://proxyrarbg.org // @include https://rarbg.com // @include https://rarbg.to // @include https://rarbg2018.org // @include https://rarbg2019.org // @include https://rarbg2020.org // @include https://rarbg2021.org // @include https://rarbgaccess.org // @include https://rarbgaccessed.org // @include https://rarbgcdn.org // @include https://rarbgcore.org // @include https://rarbgdata.org // @include https://rarbgenter.org // @include https://rarbgget.org // @include https://rarbggo.org // @include https://rarbgindex.org // @include https://rarbgmirror.com // @include https://rarbgmirror.org // @include https://rarbgmirrored.org // @include https://rarbgp2p.org // @include https://rarbgproxied.org // @include https://rarbgproxies.org // @include https://rarbgproxy.com // @include https://rarbgproxy.org // @include https://rarbgprx.org // @include https://rarbgto.org // @include https://rarbgtor.org // @include https://rarbgtorrents.org // @include https://rarbgunblock.com // @include https://rarbgunblock.org // @include https://rarbgunblocked.org // @include https://rarbgway.org // @include https://rarbgweb.org // @include https://unblockedrarbg.org // @include https://www.rarbg.is // @noframes // ==/UserScript== } }; if (meta.rawmdb && meta.rawmdb.toString && (meta.rawmdb = meta.rawmdb.toString())) { var kv, row = /\/\/\s+@(\S+)\s+(.+)/g; while ((kv = row.exec(meta.rawmdb)) !== null) { if (meta[kv[1]]) { if (typeof meta[kv[1]] == "string") meta[kv[1]] = [meta[kv[1]]]; meta[kv[1]].push(kv[2]); } else meta[kv[1]] = kv[2]; } } meta.window = this; if (typeof unsafeWindow === "undefined") { var unsafeWindow = window; } unsafeWindow.scriptMetas = unsafeWindow.scriptMetas || []; if (meta.hasOwnProperty("nodups")) { if (new Set(unsafeWindow.scriptMetas.map((meta) => meta.namespace + meta.name)).has(meta.namespace + meta.name)) { console.warn( "Another script is trying to execute but @nodups is set. Stopping execution.\n", meta.namespace + meta.name ); throw new Error( "Another script is trying to execute but @nodups is set. Stopping execution.\n" + meta.namespace + meta.name ); } } unsafeWindow.scriptMetas.push(meta); console.log("Script:", meta.name, "meta:", meta); // AddColumn() and add magnetLinks() code taken from: https://greasyfork.org/en/scripts/23493-rarbg-torrent-and-magnet-links/code /** * Sequence of function calls * * appendColumn() * -> appendColumnSingle() * -> addDlAndMl() * observeDocument(dealWithTorrents) * */ // pollyfill for Element.before() and Element.after(): (since some browsers like MS Edge don't already have them) if (Element.prototype.before === undefined) { Element.prototype.before = function (newNode) { if (this.parentElement) { return this.parentElement.insertBefore(newNode, this); } }; } if (Element.prototype.after === undefined) { Element.prototype.after = function (newNode) { if (this.parentElement) { return this.parentElement.insertBefore(newNode, this.nextSibling); } }; } // "Set" operations /** this - other * @param other * @returns {Set} containing what this has but other doesn't */ Set.prototype.difference = function (other) { if (!other.has) other = new Set(other); return new Set(Array.from(this).filter((x) => !other.has(x))); }; /** * {} */ let titleGroups = {}; const catCodeMap = { Movies: "48;17;44;45;47;50;51;52;42;46".split(";"), XXX: "4".split(";"), Music: "23;24;25;26".split(";"), "TV shows": "18;41;49".split(";"), Software: "33;34;43".split(";"), Games: "27;28;29;30;31;32;40;53".split(";"), "Non XXX": "2;14;15;16;17;21;22;42;18;19;41;27;28;29;30;31;32;40;23;24;25;26;33;34;43;44;45;46;47;48;49;50;51;52;54".split( ";" ), }; // categories map, given the category number (in the URL), returns the name of it const codeToCatMap = reverseMapping(catCodeMap); // converts key to a category code (number in URL) const catKeyMap = { v: "Movies", s: "TV shows", m: "Music", w: "Software", x: "XXX", g: "Games", n: "Non XXX", }; const ICON_DESCRIPTION_WIDE = "https://i.imgur.com/xreLTXq.gif"; const ICON_DESCRIPTION_TALL = "https://i.imgur.com/6gG2QGj.gif"; const ICON_THUMBNAILS_WIDE = "https://i.imgur.com/9xh3vuU.gif"; const ICON_THUMBNAILS_TALL = "https://i.imgur.com/XMV45fY.gif"; const ICON_THUMBNAILS = "https://i.imgur.com/nA2dRWu.gif"; const ICON_DESCRIPTION = "https://i.imgur.com/UxbSq2o.gif"; const ICON_MORE_BLUE = "https://i.imgur.com/FGwOuVT.gif"; const ICON_EXTRA_GREEN = "https://i.imgur.com/HU6J9kS.gif"; const BLACKLISTED_IMG_URLS = new Set([ "https://shotcan.com/images/finalec40346aca02aa34.png", "https://img.trafficimage.club/content/images/system/logo_1575804241329_3ec4e4.png", "https://pacific.picturedent.org/images/archive/galaxxxylogo.png", "https://worldmkv.com/wp-content/uploads/2017/10/Icon.jpg", "https://torrentgalaxy.to", // not an image ]); const SearchEngines = { google: { name: "Google", imageSearchUrl: (q) => `https://www.google.com/search?&hl=en&tbm=isch&q=${encodeURIComponent(q)}`, }, ddg: { name: "DuckDuckGo", imageSearchUrl: (q) => `https://duckduckgo.com/?q=${encodeURIComponent(q)}&atb=v73-5__&iar=images&iax=images&ia=images`, }, yandex: { name: "Yandex", imageSearchUrl: (q) => `https://yandex.com/images/search?text=${encodeURIComponent(q)}`, }, }; const debug = false; // debugmode (setting this to false will disable the console logging) // main (function () { "use strict"; const TORRENT_ICO = "https://dyncdn.me/static/20/img/16x16/download.png"; const MAGNET_ICO = "data:image/png;base64,R0lGODlhDAAMAPcAAAAAAGNjY97e3v8AAP8ICP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH5BAEAAAUALAAAAAAMAAwAAAhMAAEIHEhwoICDAgQiTAhgoUKEDw9GZDhgAAEAAQIAIMBRYMWLBQps7Aig4gCMGjleLGlSoMqVJj++xMhSZscABTSyJIkzZE6CPQsEBAA7"; const trackers = "http%3A%2F%2Ftracker.trackerfix.com%3A80%2Fannounce&tr=udp%3A%2F%2F9.rarbg.me%3A2710&tr=udp%3A%2F%2F9.rarbg.to%3A2710"; const isOnSingleTorrentPage = !!matchSite(/\/torrent\//); const isOnThreatDefencePage = /threat_defence/i.test(location.href); var title = createElement( '
Rarbg-Enhancer-UserScript Settings
You can get to this options page by pressing "ctrl+/" or by navigating to the menu as shown bellow
' ); GM_config.init({ id: "GM_config", title: title, css: "#GM_config_section_1 .config_var, #GM_config_section_2 .config_var, #GM_config_section_3 .config_var { margin: 5% !important;display: inline !important; }", fields: { thumbnailLink: { section: [ GM_config.create("Rarbg Settings"), "here you can configure settings for the rarbg-enhancer-userscript. Be aware that the page will reload after saving", ], label: "thumbnailLink", default: "torrentPageLink", title: "where should clicking the thumbnail take you", options: ["magnetLink", "torrentLink", "torrentPageLink", "img"], type: "radio", }, addThumbnails: { label: "addThumbnails", default: true, title: "if set to false, the content thumbnails will not be used, magnet or torrent thumbnails will be used isntead", type: "checkbox", }, showGeneratedSearchQuery: { label: "showGeneratedSearchQuery", default: false, title: "", type: "checkbox", }, addCategoryWithSearch: { label: "addCategoryWithSearch", default: true, title: 'when searching for a movie title like "X-men", will become "X-men movie"', type: "checkbox", }, showTorrentDownloadIcon: { label: "showTorrentDownloadIcon", default: false, title: "show the .torrent file download icon next to the magnet link (this doesn't really work anymore)", type: "checkbox", }, largeThumbnails: { label: "largeThumbnails", default: true, title: "", type: "checkbox", }, alwaysFetchExtraThumbnails: { label: "alwaysFetchExtraThumbnails", title: "automatically press fetch extra thumbnails", type: "checkbox", default: true, }, ImageSearchEngine: { label: "ImageSearchEngine", default: "google", title: "", options: Array.from(Object.keys(SearchEngines)), type: "radio", }, infiniteScrolling: { label: "infiniteScrolling", default: true, title: "", type: "checkbox", }, mirrors: { label: "mirrors", default: meta.include.join("\n"), title: "", type: "textarea", }, imgScale: { label: "imgScale", default: 50, title: "", type: "float", }, imgHoverPopupScale: { label: "imgHoverPopupScale", default: 3.0, title: "", type: "float", }, hideRecommendedTorrents: { label: "hideRecommendedTorrents", default: false, title: "", type: "checkbox", }, autoExitIndexPhp: { label: "autoExitIndexPhp", default: true, title: "", type: "checkbox", }, deduplicateTorrents: { label: "deduplicateTorrents", default: false, title: "attempt to remove duplicates when there are multiple torrents of the same item", type: "checkbox", }, staticSearchbar: { label: "staticSearchbar", default: false, title: "", type: "checkbox", }, includeDescriptionsButton: { label: "includeDescriptionsButton", default: true, title: "add a link for each row to search for getting the description thumbnails", type: "checkbox", }, includeSearchForImagesButton: { label: "includeSearchForImagesButton", default: true, title: "add a link for each row to search for images using a search engine (google, ddg, yandex..)", type: "checkbox", }, isFirstVisit: { type: "hidden", default: true, }, thumbnailHoverSound: { label: "thumbnailHoverSound", type: "checkbox", title: "play sound when hovering over thumbnail", default: true, }, // blocking "block Movies": { label: "block Movies", default: false, type: "checkbox", title: "Movies", section: "Block categories", }, "block TV shows": { label: "block TV shows", default: false, type: "checkbox", title: "TV shows" }, "block Music": { label: "block Music", default: false, type: "checkbox", title: "Music" }, "block Software": { label: "block Software", default: false, type: "checkbox", title: "Software" }, "block XXX": { label: "block XXX", default: true, type: "checkbox", title: "XXX" }, "block Games": { label: "block Games", default: false, type: "checkbox", title: "Games" }, hideEmptyTorrentLinks: { label: "hide torrents without download or magnet", default: true, type: "checkbox", title: "Removes the row of the torrent that has no magnet link or download link", }, }, events: { open: function (doc) {}, save: function (values) { // All the values that aren't saved are passed to this function // for (i in values) alert(values[i]); location.reload(); }, }, }); // override fetching description images // FIXME: maybe update this later // GM_config.set("alwaysFetchExtraThumbnails", true); if (!GM_config.get("mirrors")) { GM_config.save(); } if (typeof GM_registerMenuCommand !== "undefined") { GM_registerMenuCommand("Rarbg options", () => { GM_config.open(); }); } Math.clamp = function (a, min, max) { return a < min ? min : a > max ? max : a; }; let searchEngine = {}; initSearchEngine(); // click to verify browser document.querySelectorAll('a[href^="/threat_defence.php?defence=1"]').forEach((a) => a.click()); const searchBox = document.querySelector('#searchinput, input[id="keywords"]'); const isOnTop10page = location.pathname.startsWith("/top"); const isOnIndexPage = searchBox !== null || isOnTop10page; const isOnWrongTorrentLinkPage = getElementsByXPath( '(/html/body/table[3]/tbody/tr/td[2]/div/table/tbody/tr[2]/td/div[contains(., "The link you followed is wrong. Please try again!")])' ).length > 0; const getTorrentLinks = () => Array.from(document.querySelectorAll("table > tbody > tr.lista2 a[title]")); var row_others = getElementsByXPath('(//tr[contains(., "Others:")])[last()]').pop(); if (!!document.querySelector("thead.table1head")) { // therarbg: https://the.rarbg.club/get-posts/category:Movies:time:10D/ var tbodyEls = document.querySelectorAll("tbody"); } else { var tbodyEls = isOnSingleTorrentPage && row_others ? [row_others.parentElement.querySelector("tbody")] : Array.from(document.querySelectorAll("tbody")).filter((tbody) => { let tds = Array.from(tbody.querySelector("tr")?.querySelectorAll("td") || []); return tds.length >= 8 && tds[0].innerText.trim() === "Cat."; }); } if (!tbodyEls.length) console.warn("tbody element not found!"); var thumbnailsCssBlock = addCss(""); // language=CSS addCss( ` a.torrent-ml:not([href^=magnet]) { filter: grayscale(0.6); } /*this keeps the tableCells in the groups at equal heights*/ table.groupTable > tbody > tr > td { height: 10px !important; } /*the horsey suggestion thumbnails*/ .suggestion-thumbnail { max-width: 100px; max-height: 100px; } a.torrent-ml, a.torrent-dl { display: table-cell; padding: 5px; } a.torrent-ml > img, a.torrent-dl > img { max-width: 30px; } /*add margin*/ td.lista > * { margin: 10px; } a.search:link { color: red; } a.search:visited { color: green; } a.search:hover { color: hotpink; } a.search:active { color: blue; } .zoom { transition: transform .2s; /* Animation */ margin: 0 auto; } .zoom:hover { /* (150% zoom - Note: if the zoom is too large, it will go outside of the viewport) */ transform: scale(${GM_config.get("imgHoverPopupScale")}); } a.extra-tb { background: white; } .row{ display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; } ` ); updateCss(); /** Plays audio * @author https://stackoverflow.com/a/17762789/7771202 */ const PlaySound = (function () { const df = document.createDocumentFragment(); return function Sound(src) { var snd = new Audio(src); df.appendChild(snd); // keep in fragment until finished playing snd.addEventListener("ended", function () { df.removeChild(snd); }); snd.play(); return snd; }; })(); let snd = function () {}; snd.play = function () {}; if (GM_config.get("thumbnailHoverSound")) { // sound file from: http://freesound.org/data/previews/166/166186_3034894-lq.mp3 // noinspection JSUnusedAssignment snd = PlaySound( "data:audio/wav;base64," + "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" ); } // === end of variable declarations === function decrementImageSize(e) { GM_config.set("imgScale", Math.max(GM_config.get("imgScale") - 50, 0)); toggleThumbnailSize("update only"); } function incrementImageSize(e) { GM_config.set("imgScale", Math.min(GM_config.get("imgScale") + 50, 400)); toggleThumbnailSize("update only"); } $(document).ready(function main() { if (isOnThreatDefencePage) { // OnThreatDefencePage: check for captcha if (document.querySelector("#solve_string")) { console.log("Rarbg threat defence page"); try { // solveCaptcha(OCRAD); unsafeEval(solveCaptcha, OCRAD); } catch (e) { console.error("Error occurred while trying to solve captcha:\n", e); const container = document.querySelector("tbody > :nth-child(2)"); const img = container.querySelector("img"); const captcha = document.querySelector("#solve_string"); const submitBtn = document.querySelector("#button_submit"); const textEl = img.previousElementSibling.previousElementSibling.previousElementSibling; textEl.innerText += "Could not auto-solve capthca"; // prettier-ignore /* tesseract.js * https://tesseract.projectnaptha.com/ * https://cdn.jsdelivr.net/gh/naptha/tesseract.js@v1.0.14/dist/tesseract.min.js */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Tesseract=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o1){for(var i=1;i0&&arguments[0]!==undefined?arguments[0]:{};var worker=new TesseractWorker(Object.assign({},adapter.defaultOptions,workerOptions));worker.create=create;worker.version=version;return worker};var TesseractWorker=function(){function TesseractWorker(workerOptions){_classCallCheck(this,TesseractWorker);this.worker=null;this.workerOptions=workerOptions;this._currentJob=null;this._queue=[]}_createClass(TesseractWorker,[{key:"recognize",value:function recognize(image){var _this=this;var options=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};return this._delay(function(job){if(typeof options==="string")options={lang:options};options.lang=options.lang||"eng";job._send("recognize",{image:image,options:options,workerOptions:_this.workerOptions})})}},{key:"detect",value:function detect(image){var _this2=this;var options=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};return this._delay(function(job){job._send("detect",{image:image,options:options,workerOptions:_this2.workerOptions})})}},{key:"terminate",value:function terminate(){if(this.worker)adapter.terminateWorker(this);this.worker=null;this._currentJob=null;this._queue=[]}},{key:"_delay",value:function _delay(fn){var _this3=this;if(!this.worker)this.worker=adapter.spawnWorker(this,this.workerOptions);var job=new TesseractJob(this);this._queue.push(function(e){_this3._queue.shift();_this3._currentJob=job;fn(job)});if(!this._currentJob)this._dequeue();return job}},{key:"_dequeue",value:function _dequeue(){this._currentJob=null;if(this._queue.length){this._queue[0]()}}},{key:"_recv",value:function _recv(packet){if(packet.status==="resolve"&&packet.action==="recognize"){packet.data=circularize(packet.data)}if(this._currentJob.id===packet.jobId){this._currentJob._handle(packet)}else{console.warn("Job ID "+packet.jobId+" not known.")}}}]);return TesseractWorker}();module.exports=create()},{"../package.json":2,"./common/circularize.js":4,"./common/job":5,"./node/index.js":3}]},{},[6])(6)}); function solveCaptchaTesseract(Tesseract) { Tesseract.recognize(container.querySelector("img"), { tessedit_char_whitelist: "123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXY", }).then(function (result) { console.log("result", result, "\n", result.text); document.querySelector("#solve_string").value = result; return result; }); } unsafeEval(solveCaptchaTesseract, Tesseract); } } } else { // on torrent(s) page if (isOnSingleTorrentPage) { let mainTorrentLink = document.querySelector( "a[onmouseover], body > table:nth-child(6) > tbody > tr > td:nth-child(2) > div > table > tbody > tr:nth-child(2) > td > div > table > tbody > tr:nth-child(1) > td > a:nth-child(2)" ); addImageSearchAnchor(mainTorrentLink, mainTorrentLink.innerText); const relatedTorrent = document.querySelector(".lista_related"); if (relatedTorrent) { const tr_tableHeader = relatedTorrent.closest("table").querySelector("tbody > tr"); const thumbsHeader = tr_tableHeader.firstElementChild.cloneNode(); thumbsHeader.innerText = "thumbnail"; tr_tableHeader.firstElementChild.before(thumbsHeader); } document.querySelectorAll(".js-modal-url").forEach(async function (a) { if (!!a.querySelector("img")) return; if (a.pathname.length < 2) return; new Set(await getImagesFromUrl(a.href)).forEach((url) => { var img = document.createElement("img"); img.src = url; img.classList.add("descrimg"); var div = document.createElement("div"); div.appendChild(img); a.appendChild(div); }); replaceAllImageHosts(); }); // adding thumbnails for (const torrent of document.querySelectorAll('a[href^="/torrent/"]')) { //creating and adding thumbnails const cell = document.createElement("td"); const thumbnailLink = document.createElement("a"); const thumbnailImg = document.createElement("img"); thumbnailLink.href = torrent.href; cell.classList.add("thumbnail-cell"); cell.classList.add("preview-image"); cell.appendChild(thumbnailLink); // thumbnail thumbnailImg.classList.add("preview-image"); let thumb = extractThumbnailSrc(torrent); thumbnailImg.setAttribute("smallSrc", thumb); thumbnailImg.setAttribute("bigSrc", getLargeThumbnail(thumb)); setThumbnail(thumbnailImg); thumbnailLink.appendChild(thumbnailImg); torrent.closest("tr").firstElementChild.before(cell); thumbnailImg.style.width = "auto"; thumbnailImg.style["max-height"] = "500px"; thumbnailImg.style["max-width"] = "400px"; // thumbnailImg.style['margin-bottom'] = '20px'; } replaceAllImageHosts(); replaceAllImageHosts(document.querySelectorAll(".js-modal-url > img")); // putting the "Description:" row before the "Others:" row var poster = getElementsByXPath('(//tr[contains(., "Poster:")])[last()]')[0]; if (poster) poster.after(getElementsByXPath('(//tr[contains(., "Description:")])[last()]')[0]); // remove VPN row const vpnR = getElementsByXPath('(//tr[contains(., "VPN:")])[last()]'); if (vpnR && vpnR[0]) { vpnR[0].remove(); } void 0; } else if (isOnIndexPage) { if (isOnTop10page) { function removeTop100(catcodes, slash_category) { try { var a = document.querySelector( 'body > table:nth-child(6) > tbody > tr > td:nth-child(2) > div > table > tbody > tr:nth-child(2) > td > a[href$="/top100.php?category[]=' + catcodes.join("&category[]=") + '"]' ); // delete 3 elements before and 3 elements after and delete self a.previousElementSibling.previousElementSibling.previousElementSibling.remove(); a.previousElementSibling.previousElementSibling.remove(); a.previousElementSibling.remove(); a.nextElementSibling.nextElementSibling.nextElementSibling.remove(); a.nextElementSibling.nextElementSibling.remove(); a.nextElementSibling.remove(); a.remove(); var h1Rss = document.querySelector( 'body > table > tbody > tr > td:nth-child(2) > div > table > tbody > tr:nth-child(2) > td > h1 > a[href$="/rssdd.php?categories=' + catcodes.join(";") + '"]' ).parentElement; h1Rss.nextElementSibling.remove(); h1Rss.remove(); } catch (e) { console.warn(e); } } if (GM_config.get("block Movies")) removeTop100("14;15;16;17;21;22;42;44;45;46;47;48".split(";"), "movies"); if (GM_config.get("block XXX")) removeTop100("4".split(";"), "xxx"); if (GM_config.get("block TV shows")) removeTop100("18;19;41".split(";"), "tv"); if (GM_config.get("block Music")) removeTop100("23;24;25;26".split(";"), "music"); if (GM_config.get("block Games")) removeTop100("27;28;29;30;31;32;40".split(";"), "pc-games"); } // if on torrent page (index) if (searchBox) { searchBox.onkeyup = updateSearch; const searchContainer = searchBox.closest("form").closest("div"); // making checkbox (fixed searchbar) const moreBtn = searchContainer.querySelector( 'tr:nth-child(1) > td:nth-child(3), [id="filterBtn"]' ); moreBtn && moreBtn.after( $( '' )[0] ); const checkbox = document.querySelector("#static-checkbox"); checkbox.onchange = function (e) { const searchContainer = searchBox.closest("form").closest("div"); if (checkbox.checked) { searchContainer.style.position = "fixed"; searchContainer.style.top = "0"; searchContainer.style.left = "702px"; } else { searchContainer.style.position = ""; searchContainer.style.top = ""; searchContainer.style.left = ""; } GM_config.set("staticSearchbar", checkbox.checked); GM_config.write(); }; } if (GM_config.get("staticSearchbar")) { checkbox.click(); } if (GM_getValue("isFirstVisit", true)) { console.log("is first visit"); GM_config.open(); GM_setValue("isFirstVisit", false); } for (const tbodyEl of tbodyEls) { const mldlCol = appendColumn(tbodyEl, "ML DL
Download all", "File", addDlAndMl); mldlCol.header.addEventListener("click", downloadAllTorrents); } if (GM_config.get("infiniteScrolling") && !isOnTop10page) { // infiniteScrolling (function makeInfiniteScroll() { const tbody = [ "div.content-rounded table.lista-rounded tbody:nth-child(1) tr:nth-child(2) td:nth-child(1) > table.lista2t:nth-child(9) > tbody", ".lista2t > tbody", ]; const nav = [ "#pager_links", "td:nth-child(2) div.content-rounded table.lista-rounded tbody:nth-child(1) > tr:nth-child(3)", ]; var nextPageSelectors = ['a[title="next page"]']; const indexOfCurrentPage = [...document.querySelectorAll("#pager_links > *")] .map((x) => x.tagName) .indexOf("B"); if (indexOfCurrentPage >= 0) { nextPageSelectors = [ ...nextPageSelectors, `#pager_links > a:nth-child(${indexOfCurrentPage + 2})`, ]; } // finding the "next page" link const pageLinks = document.querySelectorAll("#pager_links > a"); const pageTextsIndex = [...pageLinks].map((a) => a.innerText).indexOf(">>"); if (pageTextsIndex >= 0) { nextPageSelectors = [ ...nextPageSelectors, `#pager_links > a:nth-child(${pageTextsIndex + 2})`, `#pager_links > a:nth-child(${pageTextsIndex + 1})`, ]; } nextPageSelectors = [...nextPageSelectors, "#pager_links > a:last-child"]; const container = tbodyEls[0].parentElement || getElementsByXPath("//table[@class='lista2t']")[0]; const infscrollOptions = { path: nextPageSelectors.join(", "), append: tbody.join(", "), // the table hideNav: nav.join(", "), scrollThreshold: 600, }; console.log("infinitescroll options:", infscrollOptions); const infScroll = new InfiniteScroll(container, infscrollOptions); // upon appending a new page infScroll.on("append", function (response, path, items) { if (items.length) { const lista2s = items[0].querySelectorAll(".lista2"); for (const lista2 of lista2s) { tbodyEls[0].appendChild(lista2); const columnCells = Array.from(lista2.childNodes).filter( (x) => x.nodeName !== "#text" ); appendColumnCell(columnCells[1]); } } try { // remove extra appended headers tbodyEls[0].nextElementSibling.remove(); } catch (error) {} // filter the new torrents that just arrived updateSearch(); }); })(); } } else if (isOnWrongTorrentLinkPage) { // this torrent link has failed, then we have to go to the torrent page and download it const torrentPageUrl = new URL(location.href).searchParams.get("tpageurl"); // get the previously injected page url here if (torrentPageUrl) { fetchDoc(torrentPageUrl).then((doc) => { const torrentDownloadLink = doc.querySelector("td.lista a[onmouseover]"); location.assign(torrentDownloadLink.href); document.addEventListener("DOMContentLoaded", () => document.close()); document.addEventListener("load", () => document.close()); setTimeout(() => window.close(), 1000); }); } else { console.warn('"tpageurl" is not in the location params!'); window.close(); } } else if (/\/index\d+\.php/.test(location.pathname)) { if (GM_config.get("autoExitIndexPhp")) { location.assign("/torrents.php"); } } (function onLoad() { console.log("loaded"); document.body.onclick = null; // remove annoying click listeners // remove annoying search description const searchDescription = document.querySelector("#SearchDescription"); if (searchDescription) searchDescription.remove(); // remove annoying signup form that doesn't work const signinForm = document.querySelector('form[action="/login"]'); if (signinForm) signinForm.remove(); const signinTab = document.querySelector( "body > table:nth-child(5) > tbody > tr > td > table > tbody > tr > td.header4" ); if (signinTab) signinTab.remove(); if (GM_config.get("hideRecommendedTorrents")) { // remove recommended torrents const recTor = document.querySelector('tr > [valign="top"] > [onmouseout="return nd();"]'); if (recTor) recTor.closest("div").remove(); } // remove "recommended torrents" title const recTitle = getElementsByXPath('(//text()[contains(., "Recommended torrents :")])/../../..')[0]; if (recTitle) recTitle.remove(); // scroll the table to view (top of screen will be the first torrent) const mainTable = document.querySelector( "body > table:nth-child(6) > tbody > tr > td:nth-child(2) > div > table > tbody > tr:nth-child(1) > td > table.lista2t" ); if (mainTable) mainTable.scrollIntoView(); // adding a dropdown list for mirrors (function addMirrorsDropdown() { const blankTab = document.querySelector("td.header:nth-child(1)"); if (!blankTab) return; const mirrorsTab = document.createElement("td"); mirrorsTab.className = "header3"; mirrorsTab.innerText = "Switch to mirror site:"; function openAllMirrors() { console.log("opening all mirrors"); for (const hostnames of mirrorsTab.querySelectorAll("option.mirrors-option")) { window.open(extractUrlFromMirrorHost(hostnames), "_blank"); } } function extractUrlFromMirrorHost(hostnameText) { const split = location.href.split("/").slice(2); split[0] = hostnameText; return split.join("/"); } const mirrorsSelect = document.createElement("select"); mirrorsSelect.onchange = function () { if (!this.value) return; console.log("this:", this); if (this.value === "Open ALL mirrors") { openAllMirrors(); } else { location.assign(extractUrlFromMirrorHost(this.value)); } }; mirrorsTab.appendChild(mirrorsSelect); for (const mirror of GM_config.get("mirrors").split("\n")) { const option = document.createElement("option"); option.className = "mirrors-option"; option.value = mirror; try { option.innerText = new URL(mirror).hostname; if (option.innerText !== location.hostname) { mirrorsSelect.appendChild(option); } } catch (e) {} } // adding another last one (which would be THIS hostsname's url) const option_thisHostname = document.createElement("option"); option_thisHostname.innerText = location.hostname; option_thisHostname.setAttribute("selected", ""); mirrorsSelect.appendChild(option_thisHostname); // another option to open ALL the hostnames in the new tab const option_openAllMirrors = document.createElement("option"); option_openAllMirrors.innerText = "Open ALL mirrors"; option_openAllMirrors.id = "open-all-mirrors"; option_openAllMirrors.setAttribute("selected", ""); option_openAllMirrors.onclick = openAllMirrors; mirrorsSelect.appendChild(option_openAllMirrors); mirrorsSelect.selectedIndex--; blankTab.after(mirrorsTab); })(); // create settings tab if (!document.querySelector("#settingsTab")) { var lastTab = document.querySelector( "tbody > tr > td > table > tbody > tr > td:last-child[class^='header3']" + ", body > div.topnav > div.postContUp > button:last-child" ); var settingsTab = lastTab.cloneNode(); settingsTab.id = "settingsTab"; settingsTab.onclick = function (e) { GM_config.open(); }; var a = document.createElement("a"); a.innerText = "⚙️ Script Settings"; settingsTab.appendChild(a); settingsTab.style["background-color"] = "green"; lastTab.after(settingsTab); } })(); observeDocument((target) => { if (isOnIndexPage) { for (const tbodyEl of tbodyEls) { const newCol = appendColumn(tbodyEl, "Thumbnails", "Cat.", addThumbnailColumn); if (!newCol.header.querySelector(".decrementImageSizeBtn")) { var decrementImageSizeBtn = createElement( '(-)' ); decrementImageSizeBtn.addEventListener("click", decrementImageSize); var incrementImageSizeBtn = createElement( '(+)' ); incrementImageSizeBtn.addEventListener("click", incrementImageSize); var nobr = document.createElement("nobr"); newCol.header.appendChild(nobr); nobr.appendChild(decrementImageSizeBtn); nobr.appendChild(incrementImageSizeBtn); } } } dealWithTorrents(target); // group torrents titleGroups = updateTorrentGroups(); for (const [torrentTitle, torrentAnchors] of Object.entries(titleGroups)) { if (torrentAnchors.length > 1) { if (GM_config.get("deduplicateTorrents")) { Array.from(torrentAnchors) .slice(1) .forEach((el) => el.parentElement.parentElement.remove()); } } } // remove links for adds that cover the screen for (const x of document.querySelectorAll( '[style*="2147483647"], a[href*="https://s4yxaqyq95.com/"]' )) { console.debug("removed redirect element:", x); x.remove(); } }); } }); (function bindKeys() { if (typeof Mousetrap === "undefined") return; Mousetrap.bind(["space"], (e) => { solveCaptcha(); // TODO: remove this, this is just for debugging }); Mousetrap.bind("ctrl+/", function (e) { GM_config.open(); }); Mousetrap.bind(["/"], (e) => { console.log("clicking search input"); e.preventDefault(); const searchBar = document.querySelector("#searchinput"); searchBar.click(); searchBar.scrollIntoView(); searchBar.select(); searchBar.setSelectionRange(searchBar.value.length, searchBar.value.length); }); Mousetrap.bind(["`"], (e) => toggleThumbnailSize()); // saves an html and json file for all torrents on page Mousetrap.bind(["ctrl+s"], (e) => { document .querySelectorAll("body > table > tbody > tr > td:nth-child(4) > a.torrent-ml") .forEach((a) => (a.protocol = "magnet:")); document.querySelectorAll("a").forEach( (a) => a.setAttribute("href", relativeToAbsoluteURL(a.getAttribute("href"), "https://rarbgprx.org/")) // TODO: remove rarbgprx.org and put something more general ); // converting image URLs to base64 (so they'd be saved in the page) Promise.all( Array.from(document.querySelectorAll("img")).map( (img) => new Promise((resolve, reject) => { fetchB64ImgUrl(img.src) .then((bin) => resolve((img.src = bin || img.src))) .catch(reject); setTimeout(resolve, 2000); }) ) ) .then((promises) => { console.log("SUCCESSFULLY converted image urls to base64", promises); }) .finally(function (promises) { const rows = document.querySelectorAll("table > tbody > tr.lista2"); const torrentJsons = Array.from(rows).map((row) => { try { const a = row.querySelector("a[onmouseover]"); const thumbnail = row.querySelector("td.thumbnail-cell > a > img.preview-image "); const ml = row.querySelector(".torrent-ml"); const torrentLink = row.querySelector(".torrent-dl"); return { title: a.title || a.innerText, page: a.href, torrentLink: torrentLink ? torrentLink.href : "", magnetLink: ml ? encodeURI(ml.href) : "", thumbnailSrc: thumbnail ? thumbnail.src : "", }; } catch (e) {} }); const torrentsObject = { documentTitle: document.title, date: Date.now(), torrents: torrentJsons, }; const tableOuterHTML = document.querySelector("table.lista2t, table.lista").outerHTML; const summaryHTML = `${document.head.outerHTML}${tableOuterHTML}`; anchorClick( makeTextFile(JSON.stringify(torrentsObject, null, 4)), document.title + " [" + rows.length + "]" + " info.json" ); anchorClick(makeTextFile(summaryHTML), document.title + " [" + rows.length + "]" + " summary.html"); }); }); Mousetrap.bind(["d a"], function (e) { document.querySelectorAll(`img[src="${ICON_DESCRIPTION}"]`).forEach((img) => img.parentElement.click()); }); // increase thumbnail size Mousetrap.bind("=", incrementImageSize); // decrease thumbnail size Mousetrap.bind("-", decrementImageSize); // binding each key to a category // then you can change categories by pressing that key (like "m" for (M)ovie) console.group("Binding keys to categories:"); for (const [key, catName] of Object.entries(catKeyMap)) { console.log(`"${key}": "${catName}"`); Mousetrap.bind(key, function (e) { const catCode = catCodeMap[catName].join(";"); if (typeof URL !== "undefined") { const url = new URL(location.href); url.searchParams.set("category", catCode); url.pathname = "/torrents.php"; location.assign(url.toString()); } else { location.assign("/torrents.php?category=" + catCode); } }); } console.groupEnd(); })(); function addThumbnailColumn(cell, torrent, row) { // = Creating and adding thumbnail cell // var cell = document.createElement('td'); const thumbnailLink = document.createElement("a"); const thumbnailImg = document.createElement("img"); cell.classList.add("thumbnail-cell"); cell.appendChild(thumbnailLink); thumbnailLink.appendChild(thumbnailImg); const dllink = row.querySelector("a.torrent-dl"); const ml = row.querySelector("a.torrent-ml"); const dlurl = dllink ? dllink.href : extractTorrentDL(torrent); // thumbnail link (function setLinkHref() { switch (GM_config.get("thumbnailLink")) { case "magnetLink": try { thumbnailLink.href = ml; if (!/^magnet:\?/.test(thumbnailLink.href)) // noinspection ExceptionCaughtLocallyJS throw new Error("Not a magnet link"); } catch (e) { thumbnailLink.href = dlurl; } break; case "torrentLink": try { thumbnailLink.href = dlurl; } catch (e) { thumbnailLink.href = ml; if (debug) console.debug("Using MagnetLink for torrent thumbnail since torrent failed:", ml); } break; case "torrentPageLink": thumbnailLink.href = torrent.href; break; } })(); if (thumbnailLink.href.indexOf("undefined") >= 0) console.warn("thumbnail Link:", thumbnailLink, "torrent:", torrent.innerText, "dl", dlurl); // thumbnail thumbnailImg.onmouseover = function playSound() { if (GM_config.get("thumbnailHoverSound")) { try { var snd = PlaySound( "data:audio/wav;base64," + "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" ); } catch (e) {} } }; thumbnailImg.classList.add("preview-image"); thumbnailImg.classList.add("zoom"); const src = extractThumbnailSrc(torrent); thumbnailImg.setAttribute("smallSrc", src); thumbnailImg.setAttribute("bigSrc", getLargeThumbnail(src)); setThumbnail(thumbnailImg); } function solveCaptcha(OCRAD) { console.log("solving captcha..."); const container = document.querySelector("tbody > :nth-child(2)"); const img = container.querySelector("img"); const captcha = document.querySelector("#solve_string"); const submitBtn = document.querySelector("#button_submit"); const url = new URL(location.href); function uriToImageData(uri) { return new Promise(function (resolve, reject) { if (uri == null) return reject(); var canvas = document.createElement("canvas"), context = canvas.getContext("2d"), image = new Image(); image.addEventListener( "load", function () { canvas.width = image.width; canvas.height = image.height; context.drawImage(image, 0, 0, canvas.width, canvas.height); resolve(context.getImageData(0, 0, canvas.width, canvas.height)); }, false ); image.src = uri; }); } function getBase64Image(img, excludeUrlProtocol = false) { // Create an empty canvas element var canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; // Copy the image contents to the canvas var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); // Get the data-URL formatted image // Firefox supports PNG and JPEG. You could check img.src to // guess the original format, but be aware the using "image/jpg" // will re-encode the image. var dataURL = canvas.toDataURL("image/png"); return (excludeUrlProtocol && dataURL.replace(/^data:image\/(png|jpg);base64,/, "")) || dataURL; } console.log("solveCAPTHA fetching image ..."); return new Promise((resolve) => { // wait for image to load if (img.complete) { return resolve(); } img.onload = resolve; }) .then(() => uriToImageData(getBase64Image(img)).then((imageData) => { if (img.naturalHeight === 0 && img.naturalWidth === 0) { console.warn("image hasn't loaded, refreshing to new captha page"); url.searchParams.set("defence", "1"); location.assign(url.toString()); return; } console.log("feeding image to OCR ..."); var imageText = OCRAD(imageData); console.log("OCRAD result:", imageText); if (!imageText) { throw Error("OCRAD result is empty"); } captcha.value = imageText; submitBtn.display = ""; submitBtn.click(); }) ) .catch((e) => { console.error(e); url.searchParams.set("defence", "1"); location.assign(url.toString()); }); } /** * hides all torrents that do not match the search query * TODO:maybe generalize this function to just return the resulting score for each item, * this way it can be portable and made as a library and use elsewhere * and then you can iterate and hide them later */ function updateSearch() { if (!searchBox) throw new Error("Search box not found"); const query = searchBox.value.trim(); const torrents = document.querySelectorAll("table > tbody > tr.lista2 a[title]"); const convertedQuery = query .replace(/[!$%^&*()_+|~=`{}\[\]:";'<>?,.\/]+/g, ".") .trim() .replace(/\s+/, "|"); const completelyNegativeQuery = (function () { for (const term of convertedQuery.split("|").filter((str) => str.length !== 0)) { // if a positive search term if (term.indexOf("-") === -1) return false; } return true; })(); const regex = new RegExp( convertedQuery.replace(/-/g, "|").replace(/\|\|/g, "|").replace(/^\|/, ""), // '|' that is at the beginning of a word "ig" ); console.debug( "regex:", regex, "\nconverted query:", convertedQuery, "\ncompletelyNegativeQuery:", completelyNegativeQuery ); for (const a of torrents) { var match = (a.title || a.innerText).match(regex); if (match) { const matches = !!match ? Array.from(match) : []; var negativeMatches = matches.filter( ( group // tests if it's a negative match (if there is a '-' before the search term) ) => group && new RegExp("-" + group, "ig").test(query) ); var positiveMatches = matches.filter( ( group // tests if it's a negative match (if there is a '-' before the search term) ) => group && !new RegExp("-" + group, "ig").test(query) ); const hideCondition = query && (completelyNegativeQuery ? negativeMatches.length : negativeMatches.length || !positiveMatches.length); a.closest(".lista2").style.display = hideCondition ? "none" : ""; } // skip if empty title // DONE: make it so that it doesn't just check if "query" starts with '-', rather, check each match and check if each word starts with '-' } } function updateCss() { thumbnailsCssBlock.innerText = `td.thumbnail-cell > a > img.preview-image, a.extra-tb > img { max-width: ${GM_config.get("imgScale")}px; max-height: ${GM_config.get("imgScale")}px; }`; } function observeDocument(callback) { callback(document.body); new MutationObserver(function (mutations, me) { me.disconnect(); for (var i = 0; i < mutations.length; i++) { if (mutations[i].addedNodes.length) { callback(mutations[i].target); } } me.observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false, }); }).observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false, }); } /** * grouping similar torrents together * this is useful when you have 3 torrents of the same movie but different resolutions, there's no need to see it 3 times * @returns {{string: HTMLAnchorElement[]}} */ function updateTorrentGroups() { const FILLER_WORDS = new Set([ "THE", "SD", "AND", "2160P", "MP4", "THE", "COM", "WEIRD", "IN", "YAPG", "A", "TRASHBIN", "FOR", "TO", "WITH", "HER", "MY", "ON", "PART", "X264", "OF", "720P", "SEX", "XXX", "MP4", "KTR", "1080P", "SD", "KLEENEX", ]); const extractCleanTitle = (a) => Array.from( new Set( (a.title || a.innerText) .toUpperCase() .replace(/\d{1,3}\.\d{1,3}\.\d{1,3}\.?/, "") // remove dates like 02.11.2019 .split(/[\s._\-\[\]]/g) // split on dots and dashes and spaces... .filter((word) => word) // remove empty words ).difference(FILLER_WORDS) // remove filler words ) .sort() // sort to maximize conflicts (we want the same title with different word orderings to be in the same group) .join(" "); const titleToLinkEntries = getTorrentLinks().map((a) => [ (a.cleanTitle = a.cleanTitle || extractCleanTitle(a)), a, ]); /* * looks like: * * 0: (2) ["HOPE ION10 S04 SAVING WEBRIP", a.modded] * 1: (2) ["ALASKA ION10 RAILROAD S01 WEBRIP", a.modded] * 2: (2) ["ION10 RULES S08E11 VANDERPUMP WEBRIP", a.modded] * 3: (2) ["ION10 RESIDENT S03E18 WEBRIP", a.modded] */ /** * clusters: titles to words * {key: value} {title: array of links that belong to that same title} * @typedef {string: Element[]} */ titleGroups = fromEntriesMultivalue( titleToLinkEntries //.concat(Object.entries(titleGroups)) // .map( ([k, v]) => [k, Array.from(new Set(v))] ) // remove duplicate links ); /* * looks something like: * * HOPE ION10 S04 SAVING WEBRIP: [a.modded] * ALASKA ION10 RAILROAD S01 WEBRIP: [a.modded] * ION10 RULES S08E11 VANDERPUMP WEBRIP: [a.modded] * ION10 RESIDENT S03E18 WEBRIP: [a.modded] * ION10 NAMASTE S04 WEBRIP YOGA: [a.modded] */ return titleGroups; } function dealWithTorrents(node) { for (const torrentLink of node.querySelectorAll( [ 'tbody tr.lista2 > td > a[title][href^="/torrent/"]:not(.modded)', 'tbody tr.table2ta > td > a[title][href^="/torrent/"]:not(.modded)', ].join(",") )) { const row = torrentLink.closest("tr"); // = adding relative time to columns for (const tbodyEl of tbodyEls) { if (!isOnTop10page) (function changeDateToRelativeTime() { var column_Added = row.querySelector( "td:nth-child(" + (getColumnIndex(tbodyEl, "Added") + 1) + ")" ); const diffInMinutes = (Date.now() - Date.parse(column_Added.innerHTML)) / (1000 * 60); var diffFinal = Math.round(diffInMinutes) + " minutes"; if (diffInMinutes / (60 * 24) >= 365 * 2) { // > 2years diffFinal = Math.round(diffInMinutes / (60 * 24 * 365)) + " years"; } else if (diffInMinutes / (60 * 24) >= 2) { // > 2days diffFinal = Math.round(diffInMinutes / (60 * 24)) + " days"; } else if (diffInMinutes / 60 >= 2) { diffFinal = Math.round(diffInMinutes / 60) + " hours"; } if (debug) console.log("column_Added:", column_Added); column_Added.innerHTML = column_Added.innerHTML + "
\n" + (diffFinal + " ago").replace(" ", " "); })(); } addImageSearchAnchor(torrentLink); torrentLink.classList.add("modded"); } var blockCatlist = []; if (GM_config.get("block Movies")) blockCatlist.push("/torrents.php?category=movies"); if (GM_config.get("block XXX")) blockCatlist.push("/torrents.php?category=2;4"); if (GM_config.get("block TV shows")) blockCatlist.push("/torrents.php?category=2;18;41;49"); if (GM_config.get("block Games")) blockCatlist.push("/torrents.php?category=2;27;28;29;30;31;53"); if (GM_config.get("block Music")) blockCatlist.push("/torrents.php?category=2;23;24;25;26"); if (GM_config.get("block Software")) blockCatlist.push("/torrents.php?category=2;33;34;43"); var selectorMenu = blockCatlist.map((c) => `tr > td > a.anal[href$="${c}"]`).join(", "); try { document.querySelectorAll(selectorMenu).forEach((a) => a.closest("tr").remove()); } catch (e) {} var blocklist = []; if (GM_config.get("block Movies")) blocklist = blocklist.concat(catCodeMap["Movies"]); if (GM_config.get("block XXX")) blocklist = blocklist.concat(catCodeMap["XXX"]); if (GM_config.get("block TV shows")) blocklist = blocklist.concat(catCodeMap["TV shows"]); if (GM_config.get("block Games")) blocklist = blocklist.concat(catCodeMap["Games"]); if (GM_config.get("block Music")) blocklist = blocklist.concat(catCodeMap["Music"]); if (GM_config.get("block Software")) blocklist = blocklist.concat(catCodeMap["Software"]); var selector = blocklist.map((c) => `img[src$="/static/20/images/categories/cat_new${c}.gif"]`).join(", "); try { document.querySelectorAll(selector).forEach((img) => img.closest("tr").remove()); } catch (e) {} } function setThumbnail(thumbnail) { if (!thumbnail.src) { thumbnail.src = thumbnail.getAttribute("smallSrc"); } // creating image objects to add load listeners to them var smallImage = new Image(); smallImage.src = thumbnail.getAttribute("smallSrc"); // set to small src when loading small image smallImage.onLoad = function () { thumbnail.setAttribute("small-loaded", ""); thumbnail.src = thumbnail.getAttribute("smallSrc"); if (GM_config.get("largeThumbnails") && thumbnail.getAttribute("big-loaded")) { thumbnail.src = thumbnail.getAttribute("bigSrc"); } }; // creating image objects to add load listeners to them var bigImage = new Image(); bigImage.src = thumbnail.getAttribute("bigSrc"); // set to small src when loading small image bigImage.onLoad = function () { thumbnail.setAttribute("big-loaded", ""); if (GM_config.get("largeThumbnails")) { thumbnail.src = thumbnail.getAttribute("bigSrc"); } }; // thumbnail.src = thumbnail.getAttribute((!GM_config.get('largeThumbnails') ? 'smallSrc' : 'bigSrc')); thumbnail.src = GM_config.get("largeThumbnails") ? thumbnail.getAttribute("bigSrc") : thumbnail.getAttribute("smallSrc"); if (!GM_config.get("addThumbnails")) { thumbnail.src = thumbnail.closest("a").href.indexOf("magnet:?") === 0 // if magnet link ? MAGNET_ICO // magnet icon : TORRENT_ICO; // else, just put torrent icon } } function toggleThumbnailSize(newSize = "toggle") { if (newSize === "large") { GM_config.set("largeThumbnails", true); } else if (newSize === "small") { GM_config.set("largeThumbnails", false); } else if (newSize === "toggle") { GM_config.set("largeThumbnails", !GM_config.get("largeThumbnails")); } GM_config.write(); console.log("toggleThumbnailSize(" + newSize + ")"); document.querySelectorAll(".preview-image").forEach(setThumbnail); updateCss(); if (debug) console.log( "toggling thumbnail sizes. GM_config.set(largeThumbnails,", GM_config.get("largeThumbnails"), ")" ); } // gets the large thumbnail from the small thumbnail (works for rarbg thumbnails) function getLargeThumbnail(smallThumbUrl) { // Movie example // Small pic: http://dyncdn.me/mimages/316661/over_opt.jpg // Big pic: http://dyncdn.me/mimages/316661/poster_opt.jpg return ( smallThumbUrl .replace("over_opt", "poster_opt") // movie thumbnail replacement // other thumbnail replacement .replace("static/over", "posters2/" + smallThumbUrl.replace(/(.*?)over\//, "").charAt(0)) //put "posters2" + the first character after the '/' ); } /** @Return returns the extracted source url from the 'onmouseover' attribute */ function extractThumbnailSrc(torrentAnchor) { if (!torrentAnchor) console.warn("null torrent anchor:", torrentAnchor); let thumbnailSrc = ""; try { // thumbnailSrc = thumbnailSrc.match(/(?<=(return overlib('\\'))/i)[0]; thumbnailSrc = torrentAnchor.getAttribute("onmouseover") || ""; thumbnailSrc = thumbnailSrc.substring( "return overlib(''".length - 2 ); } catch (r) { thumbnailSrc = MAGNET_ICO; console.error("extractThumbnailSrc error:", r); } if (debug) console.debug("extractThumbnailSrc(", torrentAnchor, ")->", thumbnailSrc); return thumbnailSrc; } function addImageSearchAnchor(torrentAnchor) { const searchTd = document.createElement("td"), searchLink = document.createElement("a"); searchTd.classList.add("search"); // searchTd.style['border-top-width'] = '10px'; // searchTd.style['padding-top'] = '10px'; //replacing common useless torrent terms let searchQuery = clearSymbolsFromString(torrentAnchor.title || torrentAnchor.innerText) .replace(/\s\s+/g, " ") // removes double spaces .trim(); /** * @return {string} the category of the torrent (Movies, XXX, TV Shows, Games, Music, Software, Non XXX) */ function getCategory(torrentAnchor) { const anchor = torrentAnchor.parentElement.parentElement.parentElement.querySelector( 'table.lista2t a[href^="/torrents.php?category="]' ); /* * extracting the code of the category from the url. * example: * TV shows: .../torrents.php?category=18 * code is: 18 */ const categoryCode = anchor.href.split("torrents.php?category=").pop(); // a map of the if (codeToCatMap.hasOwnProperty(categoryCode)) { return codeToCatMap[categoryCode]; } else { if (debug) console.debug("Unkown category:", categoryCode); } } searchLink.href = searchEngine.imageSearchUrl(searchQuery); try { if (GM_config.get("addCategoryWithSearch") && !new RegExp(getCategory(torrentAnchor)).test(searchLink.href)) searchLink.href += " " + getCategory(torrentAnchor); } catch (e) { if (debug) console.warn("unable to get category", searchLink); } if (debug) console.debug("search url:", searchLink.href); searchLink.classList.add("search"); searchLink.target = "_blank"; var searchEngineText = document.createElement("p5"); var qText = document.createElement("p6"); searchEngineText.innerHTML = `${searchEngine.name} Image Search`; qText.innerHTML = GM_config.get("showGeneratedSearchQuery") ? ": " + searchQuery : ""; let searchIcon = document.createElement("img"); // searchIcon.src = SEARCH_ICON_URL; searchIcon.src = "https://www.google.com/s2/favicons?domain=" + searchEngine.name.toLowerCase() + ".com"; searchIcon.style.height = "15px"; searchIcon.style.width = "15px"; // searchLink.style.padding = '20px'; searchLink.appendChild(searchIcon); // searchLink.appendChild(searchEngineText); searchLink.appendChild(qText); torrentAnchor.after(searchLink); if (!GM_config.get("includeSearchForImagesButton")) { searchLink.style.display = "none"; } var extraThumbnailsLink = document.createElement("a"); extraThumbnailsLink.style["cursor"] = "pointer"; const STR_FETCH_DESCRIPTION_THUMBNAILS = ""; const STR_FETCH_EXTRA_THUMBNAILS = ""; var moreIcon = createElement(''); moreIcon.alt = STR_FETCH_DESCRIPTION_THUMBNAILS; extraThumbnailsLink.textContent = STR_FETCH_DESCRIPTION_THUMBNAILS; if (isOnSingleTorrentPage) { extraThumbnailsLink.textContent = STR_FETCH_EXTRA_THUMBNAILS; moreIcon.src = ICON_MORE_BLUE; moreIcon.alt = STR_FETCH_EXTRA_THUMBNAILS; } // extraThumbnailsLink.firstElementChild.src = ''; searchLink.after(extraThumbnailsLink); extraThumbnailsLink.appendChild(moreIcon); if (!GM_config.get("includeDescriptionsButton")) { moreIcon.style.display = "none"; } var div = document.createElement("div"); div.classList.add("row"); torrentAnchor.parentNode.previousElementSibling.append(div); async function onclick(e) { if (moreIcon.src === ICON_DESCRIPTION) { try { function tryToGetParentAnchorHref(img) { try { return img.closest("a").href; } catch (e) { return ""; } } var descriptionSrcsDescriptionHrefs = await GM_fetch(torrentAnchor.href) .then((r) => r.text()) .then(async function (html) { let doc = new DOMParser().parseFromString(html, "text/html"); let imgs = doc.querySelectorAll("#description > a > img, a > img.descrimg, img.descrimg"); let imgUrls = Array.from(imgs).map((img) => [img.src, tryToGetParentAnchorHref(img)]); for (const a of doc.querySelectorAll(".js-modal-url")) { let urls = []; if (BLACKLISTED_IMG_URLS.has(a.href)) { console.warn("Skipping TGX link"); continue; } if (!a.querySelector("img")) { console.log("a.js-modal-url has no image", a); // If it's not an image URL, attempt to fetch images from the webpage urls = await GM_fetch(a.href) .then((response) => response.text()) .then((html) => { let parser = new DOMParser(); let doc = parser.parseFromString(html, "text/html"); const urls = [...doc.querySelectorAll("a > img")].map((img) => img.src); if (urls.length > 5) { console.warn("Too many images on page, skipping", a.href); return []; } return urls; }) .catch((err) => { console.error("Failed to fetch webpage: ", err); return []; }); } else { console.log("a.js-modal-url has an image", a); urls = [...a.querySelectorAll("img")].map((img) => img.src); } urls.forEach((url) => { imgUrls.push([url, url]); var img = doc.createElement("img"); img.src = url; img.classList.add("descrimg"); var div = doc.createElement("div"); div.appendChild(img); a.appendChild(div); }); } return imgUrls; }); descriptionSrcsDescriptionHrefs = descriptionSrcsDescriptionHrefs.filter( (x) => !BLACKLISTED_IMG_URLS.has(x[0]) ); // FIXME: .slice(0) add an option for this later instead of forcefully only using a single image for (var [descriptionSrc, descriptionHref] of descriptionSrcsDescriptionHrefs.slice(0, 1)) { var a = document.createElement("a"); a.href = descriptionHref; // a.style.maxHeight = '400px'; // a.innerText = "Description"; a.classList.add("description-tb"); a.style["font-size"] = "20px"; a.style["display"] = "grid"; a.target = "_blank"; var img = document.createElement("img"); img.src = descriptionSrc; img.alt = "thumbnail"; img.style.maxWidth = GM_config.get("imgScale") + "px"; img.classList.add("description"); img.classList.add("zoom"); a.append(img); // have elements side by side var subdiv = document.createElement("div"); subdiv.append(a); // stop if already exists if (div.querySelector("a.description-tb")) { return; } div.append(subdiv); div.style["display"] = "grid"; div.style["grid-template-columns"] = "1fr 1fr"; div.style["grid-gap"] = "10px"; replaceAllImageHosts([img]); // this section of the code should only run on a single image // add rotating gallery for image img.srcs = Object.entries(Object.fromEntries(descriptionSrcsDescriptionHrefs)); var lastImageIndex = -1; // Store the last displayed image index function calculateImageIndex(mouseX) { var rect = img.getBoundingClientRect(); var width = rect.right - rect.left; var numOfImages = img.srcs.length; var imageWidth = width / numOfImages; var index = Math.min( numOfImages - 1, Math.max(0, Math.floor((mouseX - rect.left) / imageWidth)) ); return index; } function updateImage(mouseX) { var index = calculateImageIndex(mouseX); if (index !== lastImageIndex) { lastImageIndex = index; var [src, href] = img.srcs[index]; img.src = src; img.closest("a").href = href; console.log("Updated image to", src); replaceAllImageHosts([img]); } } img.addEventListener("mouseover", function (event) { img.style.cursor = "url('https://cdn.icon-icons.com/icons2/1464/PNG/512/resizeleftright_100194.png'), auto"; // Replace with the path to your cursor image updateImage(event.clientX); // Initial update on mouseover }); img.addEventListener("mousemove", function (event) { img.style.cursor = "url('https://cdn.icon-icons.com/icons2/1464/PNG/512/resizeleftright_100194.png'), auto"; // Replace with the path to your cursor image updateImage(event.clientX); // Update on mouse move }); img.addEventListener("mouseout", function () { // Optionally reset the image to the first one or do nothing var [src, href] = img.srcs[0]; img.src = src; img.closest("a").href = href; img.style.cursor = ""; // Replace with the path to your cursor image replaceAllImageHosts([img]); }); window.addEventListener("keyup", function (event) { if (event.key === "Alt") { endImageCycle(); } }); } } catch (ee) { console.error(ee); // await onclick(e); } // extraThumbnailsLink.textContent = STR_FETCH_EXTRA_THUMBNAILS; moreIcon.src = ICON_MORE_BLUE; moreIcon.alt = STR_FETCH_EXTRA_THUMBNAILS; } else { var query = clearSymbolsFromString(torrentAnchor.innerText) .replace(/\s\s+/g, " ") // removes double spaces .trim(); getGoogleImages(query).then((metas) => { metas = Object.values(metas); for (const meta of metas) { if (/dyncdn/.test(meta.ou)) { continue; // skip rarbg thumbnails } var a = document.createElement("a"); a.href = meta.st; a.innerText = meta.pt; a.classList.add("extra-tb"); a.style["font-size"] = "5px"; a.style["display"] = "table-caption"; a.target = "_blank"; var img = document.createElement("img"); a.classList.add("zoom"); img.src = meta.ou; //https://stackoverflow.com/a/70725756/7771202 img.setAttribute( "onerror", "function incrementFallbackSrc(img, srcs) {if (typeof img.fallbackSrcIndex === 'undefined') img.fallbackSrcIndex = 0;img.src = srcs[img.fallbackSrcIndex++];}; incrementFallbackSrc(this, ['" + meta.tu + "'])" ); a.append(img); var subdiv = document.createElement("div"); subdiv.classList.add("column"); subdiv.append(a); div.append(subdiv); } }); extraThumbnailsLink.remove(); } e.preventDefault(); e.stopImmediatePropagation(); e.stopPropagation(); return false; } extraThumbnailsLink.addEventListener("click", onclick); if (GM_config.get("alwaysFetchExtraThumbnails") && !isOnSingleTorrentPage) { extraThumbnailsLink.click(); } } function initSearchEngine() { const searchEngineValue = GM_config.get("ImageSearchEngine"); if (SearchEngines.hasOwnProperty(searchEngineValue)) { searchEngine = SearchEngines[searchEngineValue]; console.log("search engine:", searchEngineValue, searchEngine); } else { searchEngine = SearchEngines.google; console.warn(`Search engine ${searchEngineValue} does not exist, falling back to ${searchEngine.name}`); } } function downloadAllTorrents() { console.log("downloadAllTorrents()"); const visibleTorrentAnchors = document.querySelectorAll( [ 'body > table:nth-child(6) > tbody > tr > td:nth-child(2) > div > table > tbody > tr:nth-child(1) > td > table.lista2t > tbody > tr.lista2 .torrent-dl:not([style*="display: none;"])', 'body > table:nth-child(6) > tbody > tr > td:nth-child(2) > div > table > tbody > tr:nth-child(1) > td > table.lista2t > tbody > tr.table2ta .torrent-dl:not([style*="display: none;"])', ".torrent-ml", ].join(",") ); if (confirm(`Would you like to download all the torrents on the page? (${visibleTorrentAnchors.length})`)) { // click all magnet links // document.querySelectorAll("a.torrent-dl").-forEach(a => window.open(a.click(), '_blank')); document.querySelectorAll("a.torrent-ml").forEach(async function (a) { // invoke the onmouseover event to get the magnet link a.fetchMagnetLink(null, true); await new Promise((resolve) => setTimeout(resolve, 100)); }); } } /** * @param {string} headerTitle - td.header6 element * @returns {number} the index of the column given the column header text */ function getColumnIndex(tbodyEl, headerTitle) { var headerTitles = Array.from(tbodyEl.querySelectorAll("tr:nth-child(1) > td")).map((el) => el.innerText.trim() ); if (!headerTitles) { console.warn("did not find header titles"); headerTitles = ["Cat.", "Thumbnails", "File", "ML DL", "Added", "Size", "S.", "L.", "", "Uploader"]; } var idx = headerTitles.indexOf(headerTitle); if (idx >= 0) { return idx; } else { console.warn( "The passed headerTitle:", headerTitle, "is not a valid headerTitle, choose from:", headerTitles ); const allHeaders = tbodyEl.querySelectorAll("tr > td.header6"); return [].map.call(allHeaders, (header) => header.innerText).indexOf(headerTitle); } } /** * Adds a column to the torrents table. Safe to call this function multiple times for the same column, it will not add duplicate cells to a row that already has this header. * @author https://greasyfork.org/en/scripts/23493-rarbg-torrent-and-magnet-links/code * @param {string} title - * @param {(number|string)=2} colIndex - A number specifying the column index to be inserted after (to the right of) (starts from zero). * or a string of one of the other headers (search will be performed automatically) * So for example, appendColumnGeneral("Between 0 and 1", 1) would come between the first and second columns * @param {Function} callback - paremeters: callback(newCell, anchor, row). will be called on the added cells, will not be called on cells that already exist. * @returns {HTMLTableDataCellElement[]} - returns the added elements (excluding the header, and excluding) */ function appendColumn(tbodyEl, title, colIndex = 2, callback = (cell, anchor, row) => true) { if (typeof colIndex === "string") { colIndex = getColumnIndex(tbodyEl, colIndex); } const sanitizedTitle = $.escapeSelector(title.replace(/\s/g, "")).replace(/\s/g, ""); /** * Make and insert column cell * @param {HTMLTableDataCellElement=} oldCell - if provided, new cell will be placed afterend * @returns {HTMLElement} */ function makeCell(oldCell) { const cell = document.createElement("td"); // cell.innerText = title; cell.classList.add("lista"); if (sanitizedTitle) cell.classList.add(sanitizedTitle); cell.setAttribute("width", "50px"); cell.setAttribute("align", "center"); if (oldCell) { oldCell.insertAdjacentElement("afterend", cell); } return cell; } // the initial column, after of which the extra column will be appended // let tbodySelector = ".lista2t > tbody"; const oldColumnEntries = tbodyEl.querySelectorAll("tr > td:nth-child(" + (colIndex + 1) + ")"); // header: the first cell (the header cell) of the new column var header; header = tbodyEl.querySelector("#" + sanitizedTitle + "-head"); // if this column has already been added if (!header) { header = document.createElement("td"); header.innerHTML = title; header.setAttribute("align", "center"); header.classList.add("header6"); header.id = sanitizedTitle + "-head"; // header.style.display = 'inline-flex'; oldColumnEntries[0].insertAdjacentElement("afterend", header); if (debug) console.log("header:", header); } // creation of the extra column const newColumn = [].slice .call(oldColumnEntries, 1) // exclude rows that already have this column .filter((oldCol) => { const row = oldCol.closest("tr.lista2, tr.table2ta"); const selector = "." + $.escapeSelector(sanitizedTitle); if (row) return !row.querySelector(selector); }) .map(makeCell); // fire callback for (let cell of newColumn) { let row = cell.closest("tr.lista2, tr.table2ta"); let anchor = row.querySelector("a[title]"); callback(cell, anchor, row); } newColumn.__defineGetter__("header", () => header); return newColumn; } /** * @param prevColCell * @return {*} */ function appendColumnCell(prevColCell) { if (prevColCell.closest("tr.lista2, tr.table2ta").querySelector(".has-torrent-DL-ML")) // check that the same row doesn't already have DL-ML return; // the initial column 'Files' after of which the extra column will be appended // creation of the extra column prevColCell.insertAdjacentHTML("afterend", ""); prevColCell.classList.add("has-torrent-DL-ML"); // the rest cells of the new column prevColCell.nextSibling.setAttribute("class", "lista"); prevColCell.nextSibling.setAttribute("width", "50px"); prevColCell.nextSibling.setAttribute("align", "center"); // populate the cells in the new column with DL and ML links return addDlAndMl(prevColCell.nextSibling, prevColCell); } /** * The actual function calls have `cell, fileTd` swapped * @param cell * @param fileTd * @returns {string} */ function addDlAndMl(cell, fileTd) { var row = fileTd.closest("tr.lista2, tr.table2ta"); let anchor = row.querySelector("a[title]"); if (GM_config.get("showTorrentDownloadIcon")) { // language=HTML let downloadLink = createElement( `Torrent` ); cell.appendChild(downloadLink); // torrent download } // real: // https://rarbgaccess.org/download.php?id=...&h=120&f=...-[rarbg.to].torrent // https://rarbgaccess.org/download.php?id=...& f=...-[rarbg.com].torrent // https://www.rarbgaccess.org/download.php?id=...&h=120&f=...-[rarbg.to].torrent // matches anything containing "over/*.jpg" *: anything const anchorOuterHTML = anchor.outerHTML; const hash = /over\/(.*)\.jpg\\/.test(anchorOuterHTML) ? anchorOuterHTML.match(/over\/(.*)\.jpg\\/)[1] : undefined; const title = anchor.innerText; const magnetUriStr = `magnet:?xt=urn:btih:${hash}&dn=${title}&tr=${trackers}`; const ml = createElement( `` ); if (hash === undefined) { ml.href = "javascript:void(0);"; addMouseoverListener(ml, "ml"); } cell.appendChild(ml); // magnet ink return magnetUriStr; } function extractTorrentDL(anchor) { return `${anchor.href.replace("torrent/", "download.php?id=")}&f=${encodeURI( anchor.innerText )}-[rarbg.to].torrent&tpageurl=${encodeURIComponent(anchor.href.trim())}`; } function addMouseoverListener(link, type) { function listener(event, openAfterFetching = false) { if (event) event.preventDefault(); if (link.magnetHref) { link.href = link.magnetHref; } if (!link.href.startsWith("magnet:")) { if (debug) console.log("actually addMouseoverListener()", link); let tUrl = link.getAttribute("data-href"); console.log("fetching ", tUrl); var xhr = new XMLHttpRequest(); xhr.open("GET", tUrl, true); // XMLHttpRequest.open(method, url, async) function onerror() { console.error("Error while fetching magnet link"); link.style.display = "none"; link.closest("tr").style.filter = "grayscale(1)"; if (GM_config.get("hideEmptyTorrentLinks")) { link.closest("tr").style.display = "none"; } } xhr.onload = function () { let container = document.implementation.createHTMLDocument().documentElement; container.innerHTML = xhr.responseText; // console.debug("xhr.responseText", xhr.responseText); let retrievedLink = type === "dl" ? container.querySelector('a[href^="/download.php"]') // download link : container.querySelector('a[href^="magnet:"]'); // magnet link if (retrievedLink) { link.href = retrievedLink.href; link.magnetHref = retrievedLink.href; console.debug("retrievedLink.href", retrievedLink.href); if (openAfterFetching) { window.open(link.href, "_blank"); } } else { onerror(); } }; xhr.onerror = onerror; xhr.send(); if (openAfterFetching) { window.open(link.href, "_blank"); } } else { if (openAfterFetching) { window.open(link.href, "_blank"); } return; } } link.fetchMagnetLink = listener; link.addEventListener("mouseover", listener, false); link.addEventListener( "mousedown", function (event) { // if left mousebutton if (event.button !== 0) return; listener(event, true); // trigger the mouseover event }, false ); link.fetchMagnetLink(); } // Cat. | File | Added | Size | S. | L. | comments | Uploader // this is one row /* * * * * * * * * The.Visitor.2007.720p.BluRay.H264.AAC-RARBG * *
* Drama IMDB: 7.7/10 * * 2018-07-25 16:55:06 * 1.25 GB * 1 * 5 * -- * Scene * */ })(); // == below are general helper functions, not specific to this script == // DuckDuckGo proxy class DDG { static get color() { return "#FFA500"; } static test(url) { return /^https:\/\/proxy\.duckduckgo\.com/.test(url); } static proxy(url) { return DDG.test(url) || /^(javascript)/i.test(url) ? url : `https://proxy.duckduckgo.com/iu/?u=${encodeURIComponent(url)}&f=1`; } static reverse(url) { // if (isZscalarUrl(url)) s = getOGZscalarUrl(url); // extra functionality: if (!DDG.test(url)) { return url; } return new URL(location.href).searchParams.get("u"); } } function getGoogleImages(query) { if (typeof getGoogleImages.cache === "undefined") getGoogleImages.cache = {}; if (Object.keys(getGoogleImages.cache).includes(query)) { console.log("getGoogleImages(): using cache for query:", query); return getGoogleImages[query]; } return GM_fetch("https://www.google.com/search?q=" + encodeURIComponent(query) + "&tbm=isch", { headers: { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "accept-language": "en-US,en;q=0.9,ar;q=0.8", }, referrer: "https://www.google.com/", referrerPolicy: "origin", body: null, method: "GET", mode: "cors", credentials: "include", }) .then((response) => response.text()) .then((text) => { let result; const doc = new DOMParser().parseFromString(text, "text/html"); try { result = parse_AF_initDataCallback(doc); getGoogleImages[query] = result; } catch (e) { console.warn("parse_AF_initDataCallback failed for query:", query, e); result = {}; } return result; }); } // this parses the google images search results function parse_AF_initDataCallback(doc) { var metasMap = {}; var data = Array.from(doc.querySelectorAll("script[nonce]")) .map((s) => s.innerText) .filter((t) => /^AF_initDataCallback/.test(t)) .map((t) => eval(t.replace(/^AF_initDataCallback/, "")).data) .filter((d) => d && d.length && d.reduce((acc, el) => acc || (el && el.length))); var entry = data.slice(-1)[0]; if (!entry) return metasMap; try { var imgMetas = entry[31][0][12][2].map((meta) => meta[1]); // confirmed } catch (error) { // var imgMetas = entry[56][1][0][0][1][0].map(x => x[0][0]['444383007'][1]); try { var imgMetas = entry[56][1][0][0][1][0].map((x) => Object.values(x[0][0])[0][1]); } catch (error2) { var imgMetas = entry[56][1][0].pop()[1][0].map((x) => Object.values(x[0][0])[0][1]); } } var metas = imgMetas .map((meta) => { try { const rg_meta = { id: "", // thumbnail tu: "", th: "", tw: "", // original ou: "", oh: "", ow: "", // site and name pt: "", st: "", // titles ity: "", rh: "IMAGE_HOST", ru: "IMAGE_SOURCE", }; rg_meta.id = meta[1]; [rg_meta.tu, rg_meta.th, rg_meta.tw] = meta[2]; [rg_meta.ou, rg_meta.oh, rg_meta.ow] = meta[3]; try { const siteAndNameInfo = meta[9] || meta[11] || meta[23]; if (siteAndNameInfo[2003]) { rg_meta.pt = siteAndNameInfo[2003][3]; } else { rg_meta.pt = siteAndNameInfo[2003][2]; } try { rg_meta.st = siteAndNameInfo[183836587][0]; // infolink TODO: doublecheck } catch (error) { try { rg_meta.st = siteAndNameInfo[2003][2]; // infolink TODO: doublecheck } catch (error) {} } } catch (error) { console.warn(error); } return rg_meta; } catch (e) { console.warn(e); } }) .filter((meta) => !!meta); metasMap = Object.fromEntries(metas.map((meta) => [meta.id, meta])); // same as metas, but is an object with the "id" as the key parse_AF_initDataCallback.metasMap = metasMap; return metasMap; } function getImagesFromUrl(url) { // List of possible image extensions const imageExtensions = ["jpg", "jpeg", "png", "gif", "webp", "tiff"]; // Get the extension of the URL const urlExtension = url.split(".").pop(); // Check if the URL is an image URL if (imageExtensions.includes(urlExtension)) { return Promise.resolve([url]); } else { // If it's not an image URL, attempt to fetch images from the webpage return GM_fetch(url) .then((response) => response.text()) .then((html) => { let parser = new DOMParser(); let doc = parser.parseFromString(html, "text/html"); let images = Array.from(doc.images).map((img) => img.src); return images; }) .catch((err) => { console.error("Failed to fetch webpage: ", err); return []; }); } } function proxifyDescriptionThumbnails() { document.querySelectorAll("#description > a > img, a.description-tb > img").forEach((img) => { img.src = DDG.proxy(img.src); }); } function replaceAllImageHosts(imgs = null) { if (!imgs || !imgs.length) { imgs = []; } const collectedImgs = []; // fullres for imgprime.com // link: https://imgprime.com/imga-u/b/2019/04/02/5ca35d660e76e.jpeg.html // img: https://imgprime.com/u/b/2019/04/02/5ca35d660e76e.jpeg collectedImgs.concat( replaceImageHostImageWithOriginal("https://imgprime.com/", { "imga-": "", ".html": "", "/small/": "/big/", "/u/s/": "/u/b/", imgs, }) ); // imagecurl.com collectedImgs.concat(replaceImageHostImageWithOriginal("https://imagecurl.com/images/", { _thumb: "", imgs })); // imagefruit.com collectedImgs.concat(replaceImageHostImageWithOriginal("/tn/t", { "/tn/t": "/tn/i", imgs })); // 22pixx.xyz collectedImgs.concat( replaceImageHostImageWithOriginal("https://22pixx.xyz/", { "22pixx.xyz/os/": "22pixx.xyz/o/", "22pixx.xyz/s/": "22pixx.xyz/i/", "22pixx.xyz/rs/": "22pixx.xyz/r/", "22pixx.xyz/as/": "22pixx.xyz/a/", imgs, }) ); // trueimg.xyz collectedImgs.concat( replaceImageHostImageWithOriginal("https://trueimg.xyz/s/", { "trueimg.xyz/s/": "trueimg.xyz/b/", imgs, }) ); // trueimg.xyz collectedImgs.concat( replaceImageHostImageWithOriginal("https://imgtaxi.com/images/small/", { "https://imgtaxi.com/images/small/": "https://imgtaxi.com/images/big/", imgs, }) ); collectedImgs.concat( replaceImageHostImageWithOriginal("http://pictureme.xyz/upload/big/", { "http://pictureme.xyz/upload/small/": "http://pictureme.xyz/upload/big/", imgs, }) ); collectedImgs.concat( replaceImageHostImageWithOriginal("/upload/small/", { "/upload/small/": "/upload/big/", imgs, }) ); collectedImgs.concat( replaceImageHostImageWithOriginal("/1s/", { "/1s/": "/1/", imgs, }) ); collectedImgs.concat( replaceImageHostImageWithOriginal(".th.jpg", { ".th.jpg": ".jpg", imgs, }) ); collectedImgs.concat( replaceImageHostImageWithOriginal(".md.jpg", { ".md.jpg": ".jpg", imgs, }) ); collectedImgs.concat( replaceImageHostImageWithOriginal("http", { "imga-": "", ".html": "", "/small/": "/big/", "/u/s/": "/u/b/", imgs, }) ); // // remove any image that is from the BLACKLISTED_IMG_URLS // [...document.querySelectorAll("img")].filter((img) => BLACKLISTED_IMG_URLS.has(img.src)) // .forEach((img) => img.remove()); proxifyDescriptionThumbnails(imgs); proxifyDescriptionThumbnails(collectedImgs); if (!!imgs && imgs.length) { return imgs; } else { return collectedImgs; } } function unsafeEval(func, ...arguments) { let body = "return (" + func + ").apply(this, arguments)"; unsafeWindow.Function(body).apply(unsafeWindow, arguments); } /** * * @param {Object} o Object to be reversed. * Note: that if there are multiple values in an entry, it will be stored as multiple keys each corresponding to the same key (duplication). */ function reverseMapping(o) { const r = {}; for (const [k, v] of Object.entries(o)) { var values = [v]; if (v instanceof Array) { values = v; } for (const val of values) { r[val] = k; } } return r; } /** * same as Object.fromEntries(), but support multiple values in case of key collision. * resolution: concat conflicts in an array * @param {*} entries * @returns */ function fromEntriesMultivalue(entries) { const o = {}; entries.forEach(([k, v]) => (o[k] = (o[k] || []).concat(v))); return o; } /** * replaces common thumbnails to originals from hosting sites like imagecurl.com... * * @param {string} imgCommonUrl - the segment of the URL that's unique to this replacement, example: "https://imagecurl.com/images/" * @param {object|Function} replaceMethod - replaceMethod(src)->newSrc a function that passes back the new string or a replacement map */ function replaceImageHostImageWithOriginal(imgCommonUrl, replaceMethod, imgs = null) { const collectedImgs = []; if (!imgs || !imgs.length) { imgs = Array.from(document.querySelectorAll('img[src*="' + imgCommonUrl + '"]')); } else { imgs = imgs.filter((img) => img.src.includes(imgCommonUrl)); } const callback = typeof replaceMethod === "function" ? replaceMethod : (src) => Object.entries(replaceMethod).reduce((acc, [k, v]) => acc.replace(k, v), src); // if object if (imgs.length === 0) { console.debug("no images found for", imgCommonUrl); return imgs; } for (const img of imgs) { if (img) { collectedImgs.push(img); const fullres = callback(img.src); if (debug) console.log("replacing thumbnail:", img.src, "->", fullres, "\n", img); img.src = fullres; a = img.closest("a"); if (a) a.href = fullres; } } if (!!imgs && imgs.length) { return imgs; } else { return collectedImgs; } } function matchSite(siteRegex) { let result = location.href.match(siteRegex); if (result) console.debug("Site matched regex: " + siteRegex); return result; } function anchorClick(href, downloadValue, target) { downloadValue = downloadValue || "_untitled"; var a = document.createElement("a"); a.setAttribute("href", href); a.setAttribute("download", downloadValue); a.target = target; document.body.appendChild(a); a.click(); a.remove(); } function removeDoubleSpaces(str) { return !!str ? str.replace(/(\s\s+)/g, " ") : str; } function clearSymbolsFromString(str) { function clearDatesFromString(str) { return !!str ? removeDoubleSpaces(str.replace(/\d+([.\-])(\d+)([.\-])\d*/g, " ")) : str; } return ( str && removeDoubleSpaces( clearDatesFromString(str) .replace(/[-!$%^&*()_+|~=`{}\[\]";'<>?,.\/]|(\s\s+)/gim, " ") .replace( /rarbg|\.com|#|x264|DVDRip|720p|1080p|2160p|MP4|IMAGESET|FuGLi|SD|KLEENEX|BRRip|XviD|MP3|XVID|BluRay|HAAC|WEBRip|DHD|rartv|KTR|YAPG|[^0-9a-zA-z]/gi, " " ) ).trim() ); } function makeTextFile(text) { const data = new Blob([text], { type: "text/plain" }); var textFile = null; // If we are replacing a previously generated file we need to manually revoke the object URL to avoid memory leaks. if (textFile !== null) window.URL.revokeObjectURL(textFile); return window.URL.createObjectURL(data); } /** Create an element by HTML. example: var myAnchor = createElement('Go to example.com');*/ function createElement(html) { return $(html)[0]; } function getElementsByXPath(xpath, parent) { let results = []; let query = document.evaluate(xpath, parent || document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for (let i = 0, length = query.snapshotLength; i < length; ++i) { results.push(query.snapshotItem(i)); } return results; } function addCss(cssStr, id = "") { // check if already exists const style = (id && document.getElementById(id)) || document.createElement("style"); if (style.styleSheet) { style.styleSheet.cssText = cssStr; } else { style.innerText = cssStr; } if (!!id) { style.id = id; } style.classList.add("addCss"); return document.getElementsByTagName("head")[0].appendChild(style); } function sanitizeHTML(html) { return new DOMParser().parseFromString(html, "text/html"); } function fetchDoc(url) { return fetch(url, { credentials: "include", headers: { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", "accept-language": "en-US,en;q=0.9", "cache-control": "no-cache", pragma: "no-cache", "sec-fetch-mode": "navigate", "sec-fetch-site": "same-origin", "sec-fetch-user": "?1", "upgrade-insecure-requests": "1", }, referrerPolicy: "no-referrer-when-downgrade", body: null, method: "GET", mode: "cors", }) .then((res) => res.text()) .then((html) => { const doc = new DOMParser().parseFromString(html, "text/html"); return doc; }); } function relativeToAbsoluteURL(url, base = null) { if (!base) base = document.baseURI; if ("string" !== typeof url || url == null) { return null; // wrong or empty url } else if (url.match(/^[a-z]+\:\/\//i)) { return url; // url is absolute already } else if (url.match(/^\/\//)) { return "http:" + url; // url is absolute already } else if (url.match(/^[a-z]+\:/i)) { return url; // data URI, mailto:, tel:, etc. } else if ("string" !== typeof base) { var a = document.createElement("a"); a.href = url; // try to resolve url without base if (!a.pathname) { return null; // url not valid } return "http://" + url; } else { base = relativeToAbsoluteURL(base); // check base if (base === null) { return null; // wrong base } } var a = document.createElement("a"); a.href = base; if (url[0] === "/") { base = []; // rooted path } else { base = a.pathname.split("/"); // relative path base.pop(); } url = url.split("/"); for (var i = 0; i < url.length; ++i) { if (url[i] === ".") { // current directory continue; } if (url[i] === "..") { // parent directory if ("undefined" === typeof base.pop() || base.length === 0) { return null; // wrong url accessing non-existing parent directories } } else { // child directory base.push(url[i]); } } return a.protocol + "//" + a.hostname + base.join("/"); } function fetchB64ImgUrl(url, opts) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url || "http://static.jsbin.com/images/dave.min.svg?4.1.4", onload: function (resp) { // resolve(resp); var binResp = customBase64Encode(resp.responseText); resolve("data:image/png;base64," + binResp); }, overrideMimeType: "text/plain; charset=x-user-defined", }); }); function customBase64Encode(inputStr) { var bbLen = 3, enCharLen = 4, inpLen = inputStr.length, inx = 0, jnx, keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789+/=", output = "", paddingBytes = 0; var bytebuffer = new Array(bbLen), encodedCharIndexes = new Array(enCharLen); while (inx < inpLen) { for (jnx = 0; jnx < bbLen; ++jnx) { /*--- Throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data */ if (inx < inpLen) { bytebuffer[jnx] = inputStr.charCodeAt(inx++) & 0xff; } else { bytebuffer[jnx] = 0; } } /*--- Get each encoded character, 6 bits at a time. index 0: first 6 bits index 1: second 6 bits (2 least significant bits from inputStr byte 1 + 4 most significant bits from byte 2) index 2: third 6 bits (4 least significant bits from inputStr byte 2 + 2 most significant bits from byte 3) index 3: forth 6 bits (6 least significant bits from inputStr byte 3) */ encodedCharIndexes[0] = bytebuffer[0] >> 2; encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4); encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6); encodedCharIndexes[3] = bytebuffer[2] & 0x3f; //--- Determine whether padding happened, and adjust accordingly. paddingBytes = inx - (inpLen - 1); switch (paddingBytes) { case 1: // Set last character to padding char encodedCharIndexes[3] = 64; break; case 2: // Set last 2 characters to padding char encodedCharIndexes[3] = 64; encodedCharIndexes[2] = 64; break; default: break; // No padding - proceed } /*--- Now grab each appropriate character out of our keystring, based on our index array and append it to the output string. */ for (jnx = 0; jnx < enCharLen; ++jnx) output += keyStr.charAt(encodedCharIndexes[jnx]); } return output; } } function isInViewport(element) { var rect = element.getBoundingClientRect(); var windowHeight = window.innerHeight || document.documentElement.clientHeight; var windowWidth = window.innerWidth || document.documentElement.clientWidth; return rect.top >= 0 && rect.left >= 0 && rect.bottom <= windowHeight && rect.right <= windowWidth; }