您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Toggle for Searching Torrents via Search aggegrator
当前为
// ==UserScript== // @name Torrent Quick Search // @namespace https://github.com/TMD20/torrent-quick-search // @supportURL https://github.com/TMD20/torrent-quick-search // @version 1.58 // @description Toggle for Searching Torrents via Search aggegrator // @icon https://cdn2.iconfinder.com/data/icons/flat-icons-19/512/Eye.png // @author tmd // @noframes // @run-at document-end // @require https://openuserjs.org/src/libs/sizzle/GM_config.min.js // @grant GM.getValue // @grant GM.setValue // @grant GM.xmlHttpRequest // @grant GM.registerMenuCommand // @grant GM_config // @grant GM.notification // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_notification // @require https://cdn.jsdelivr.net/npm/[email protected]/lib/semaphore.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/lib/semaphore.min.js // @match https://animebytes.tv/requests.php?action=viewrequest&id=* // @match https://animebytes.tv/series.php?id=* // @match https://animebytes.tv/torrents.php?id=* // @match https://blutopia.xyz/requests/* // @match https://blutopia.xyz/torrents/* // @match https://beyond-hd.me/requests/* // @match https://beyond-hd.me/torrents/* // @match https://beyond-hd.me/library/title/* // @match https://imdb.com/title/* // @match https://www.imdb.com/title/* // @match https://www.themoviedb.org/movie/* // @match https://www.themoviedb.org/tv/* // @license MIT // ==/UserScript== ` General Functions Functions that don't fit in any other catergory `; function recreateController() { controller = new AbortController(); } function semaphoreLeave() { if (sem && sem.current > 0) { sem.leave(); } } let searchObj = { ready: true, search() { if (controller.signal.aborted) { return Promise.reject(AbortError); } return new Promise(async (resolve, reject) => { controller.signal.addEventListener("abort", () => { reject(AbortError); }); document.querySelector("#torrent-quicksearch-msgnode").textContent = "Loading"; let indexers = await getIndexers(); document.querySelector("#torrent-quicksearch-msgnode").textContent = "Fetching Results From Indexers"; let imdb = await setIMDBNode(); setTitleNode(); //reset count let count = []; let length = indexers.length; let data = []; let x = Number.MAX_VALUE; while (indexers.length) { // x at a time let newData = await Promise.allSettled( indexers .splice(0, Math.min(indexers.length, x)) .map((e) => searchIndexer(e, imdb, length, count)) ); data = [...data, ...newData]; } console.log(data); let errorMsgs = data .filter((e) => e["status"] == "rejected") .map((e) => e["reason"].message); errorMsgs = [...new Set(errorMsgs)]; if (errorMsgs.length > 0) { reject(errorMsgs.join("\n")); } resolve(); }); }, cancel() { controller.abort(); }, async setup() { this.searchPromise = new Promise((resolve, reject) => { this.timeout = setTimeout(async () => { try { resolve(await this.search()); } catch (e) { reject(e); } }, 1000); }); }, async doSearch() { showDisplay(); recreateController(); await this.setup(); setTimeout(() => { resetResultList(); resetSearchDOM(); getTableHead(); }, 0); setTimeout(async () => { //reset sem = semaphore(10); try { await this.searchPromise; this.finalize(); } catch (error) { if (error.message.match(/aborted!/i) === null) { GM.notification(error.message, program, searchIcon); } console.log(error); } }, 100); }, finalize() { if ( Array.from(document.querySelectorAll(".torrent-quicksearch-resultitem")) .length == 0 ) { this.nomatchID = setTimeout( () => (document.querySelector( "#torrent-quicksearch-resultlist" ).textContent = "No Matches"), 1000 ); } this.finalmsgID = setTimeout( () => (document.querySelector("#torrent-quicksearch-msgnode").textContent = "Finished"), 1000 ); this.removemsgnodeID = setTimeout(() => { (document.querySelector("#torrent-quicksearch-msgnode").style.display = "none"), 3000; document.querySelector("#torrent-quicksearch-msgnode").textContent = ""; }); }, async toggleSearch() { let content = document.querySelector("#torrent-quicksearch-box"); if (content.style.display === "inline-block") { hideDisplay(); searchObj.cancel(); } else if ( content.style.display === "none" || content.style.display === "" ) { let customSearch = false; await this.doSearch(); } }, }; function searchIndexer(indexerObj, imdb, total, count) { if (controller.signal.aborted) { return Promise.reject(AbortError); } return new Promise(async (resolve, reject) => { let msg = null; controller.signal.addEventListener("abort", () => { reject(AbortError); }); let searchprogram = GM_config.get("searchprogram"); let data = null; if (searchprogram == "Prowlarr") { data = await searchProwlarrIndexer(indexerObj, controller); } else if (searchprogram == "Jackett") { data = await searchJackettIndexer(indexerObj); } else if (searchprogram == "NZBHydra2") { data = await searchHydra2Indexer(indexerObj); } msg = `Results fetched fom ${indexerObj["Name"]}:${ count.length + 1 }/${total} Indexers completed`; data = data.filter((e) => imdbFilter(e, imdbCleanup(imdb))); data.forEach((e) => { if (e["ImdbId"] == 0 || e["ImdbId"] == null) { e["ImdbId"] = imdbParserFail; } }); data = data.filter((e) => currSiteFilter(e["InfoUrl"])); addResultsTable(data); count.push(indexerObj["ID"]); document.querySelector("#torrent-quicksearch-msgnode").textContent = msg; console.log(msg); resolve(data); }); } async function searchProwlarrIndexer(indexer) { console.log(getSearchURLProwlarr(indexer["ID"])); let req = await fetch(getSearchURLProwlarr(indexer["ID"]), { timeout: indexerSearchTimeout, }); let data = JSON.parse(req.responseText) || []; let dataCopy = [...data]; let promiseArray = []; let x = Number.MAX_VALUE; while (dataCopy.length) { let newData = await Promise.allSettled( dataCopy.splice(0, Math.min(dataCopy.length, x)).map(async (e) => { return { Title: e["title"], Indexer: e["indexer"], Grabs: e["grabs"], PublishDate: e["publishDate"], Size: e["size"], Leechers: e["leechers"], Seeders: e["seeders"], InfoUrl: e["infoUrl"], DownloadUrl: e["downloadUrl"], ImdbId: e["imdbId"], Cost: e["indexerFlags"].includes("freeleech") == "100% Freeleech" ? "100% Freeleech" : "Cost Unknown With Prowlarr", Protocol: e["protocol"], }; }) ); promiseArray = [...promiseArray, ...newData]; } return promiseArray.map((e) => e["value"]).filter((e) => e != null); } async function searchJackettIndexer(indexer) { let req = await fetch(getSearchURLJackett(indexer["ID"]), { timeout: indexerSearchTimeout, }); let data = JSON.parse(req.responseText)["Results"] || []; let dataCopy = [...data]; let promiseArray = []; let x = Number.MAX_VALUE; while (dataCopy.length) { let newData = await Promise.allSettled( dataCopy.splice(0, Math.min(dataCopy.length, x)).map(async (e) => { return { Title: e["Title"], Indexer: e["Tracker"], Grabs: e["Grabs"], PublishDate: e["PublishDate"], Size: e["Size"], Leechers: e["Peers"], Seeders: e["Seeders"], InfoUrl: e["Details"], DownloadUrl: e["Link"], ImdbId: e["Imdb"], Cost: `${(1 - e["DownloadVolumeFactor"]) * 100}% Freeleech`, Protocol: "torrent", }; }) ); promiseArray = [...promiseArray, ...newData]; } return promiseArray.map((e) => e["value"]).filter((e) => e != null); } async function searchHydra2Indexer(indexer) { let req = await fetch(getSearchURLHydraTor(indexer["ID"]), { timeout: indexerSearchTimeout, }); let req2 = await fetch(getSearchURLHydraNZB(indexer["ID"]), { timeout: indexerSearchTimeout, }); let parser = new DOMParser(); let data = [ ...Array.from( parser .parseFromString(req.responseText, "text/xml") .querySelectorAll("channel>item") ), ...Array.from( parser .parseFromString(req2.responseText, "text/xml") .querySelectorAll("channel>item") ), ]; let dataCopy = [...data]; let promiseArray = []; let x = Number.MAX_VALUE; while (dataCopy.length) { let newData = await Promise.allSettled( dataCopy.splice(0, Math.min(dataCopy.length, x)).map(async (e) => //array is final dictkey,queryselector,attribute { let t = [ ["Title", "title", "textContent"], ["Indexer", "[name=hydraIndexerName]", "null"], ["Leechers", "[name=peers]", "null"], ["Seeders", "[name=seeders]", "null"], ["Cost", "[name=downloadvolumefactor]", "null"], ["PublishDate", "pubDate", "textContent"], ["Size", "size", "textContent"], ["InfoUrl", "comments", "textContent"], ["DownloadUrl", "link", "textContent"], ["ImdbId", "[name=imdb]", "null"], ]; let out = {}; out["Grabs"] = "Hydra Does not Report"; for (let i in t) { let key = t[i][0]; let node = e.querySelector(t[i][1]); let textContent = t[i][2] == "textContent"; if (!node) { continue; } if (textContent) { out[key] = node.textContent; } else if (key == "cost") { out[key] = `${(1 - node.getAttribute("value")) * 100}% Freeleech`; } else { out[key] = node.getAttribute("value"); } } out["Protocol"] = data[0].querySelector("enclosure").getAttribute("type") == "application/x-bittorrent" ? "torrent" : "usenet"; return out; } ) ); promiseArray = [...promiseArray, ...newData]; } return promiseArray.map((e) => e["value"]).filter((e) => e != null); } function fetch( url, { method = "GET", data = null, headers = {}, timeout = 90000, semaphore = true, } = {} ) { async function semforeFetch() { return new Promise((resolve, reject) => { sem.take(async () => { controller.signal.addEventListener("abort", () => { reject(AbortError); }); setTimeout(() => reject(AbortError), timeout); GM.xmlHttpRequest({ method: method, url: url, data: data, headers: headers, onload: (response) => { semaphoreLeave(); resolve(response); }, onerror: (response) => { semaphoreLeave(); reject(response.responseText); }, }); }); }); } async function normalFetch() { return new Promise((resolve, reject) => { controller.signal.addEventListener("abort", () => { reject(AbortError); }); setTimeout(() => reject(AbortError), timeout); GM.xmlHttpRequest({ method: method, url: url, data: data, headers: headers, onload: (response) => { resolve(response); }, onerror: (response) => { reject(response.responseText); }, }); }); } if (semaphore) { return semforeFetch(); } else { return normalFetch(); } } function getParser() { let siteName = standardNames[window.location.host] || window.location.host; let data = infoParser[siteName]; if (data === undefined) { let msg = "Could not get Parser"; GM.notification(msg, program, searchIcon); throw new Error(msg); } return data; } function verifyConfig() { if ( GM_config.get("searchapi", "null") == "null" || GM_config.get("searchurl", "null") == "null" ) { return false; } if ( GM_config.get("searchapi", "null") == "" || GM_config.get("searchurl", "null") == "" ) { return false; } return true; } ` DOM Manipulators These Functions are used to manipulate the DOM `; function setTitleNode() { if (customSearch == false) { document.querySelector("#torrent-quicksearch-customsearch").value = getTitle(); } } async function setIMDBNode() { let imdb = null; //Get Old IMDB if ( document.querySelector("#torrent-quicksearch-imdbinfo").textContent != imdbParserFail && document.querySelector("#torrent-quicksearch-imdbinfo").textContent .length != 0 && document.querySelector("#torrent-quicksearch-imdbinfo").textContent != "None" ) { imdb = document.querySelector("#torrent-quicksearch-imdbinfo").textContent; } //Else get New IMDB else { imdb = await getIMDB(); document.querySelector("#torrent-quicksearch-imdbinfo").textContent = imdb || imdbParserFail; } return imdb; } function resetSearchDOM() { document.querySelector("#torrent-quicksearch-imdbinfo").textContent = "None"; document.querySelector("#torrent-quicksearch-msgnode").textContent = "Waiting"; } function hideDisplay() { document .querySelector("#torrent-quicksearch-overlay") .style.setProperty("--icon-size", `${iconSmall}%`); document.querySelector("#torrent-quicksearch-customsearch").value = ""; document.querySelector("#torrent-quicksearch-box").style.display = "none"; } function showDisplay() { document.querySelector("#torrent-quicksearch-msgnode").textContent = ""; document.querySelector("#torrent-quicksearch-msgnode").style.display = "block"; document .querySelector("#torrent-quicksearch-overlay") .style.setProperty("--icon-size", `${iconLarge}%`); document.querySelector("#torrent-quicksearch-box").style.display = "inline-block"; } function getTableHead() { let node = document.querySelector("#torrent-quicksearch-resultheader"); node.innerHTML = ` <span class="torrent-quicksearch-resultcell" >Links</span> <span class="torrent-quicksearch-resultcell" >Clients</span> <span class="torrent-quicksearch-resultcell" >Title</span> <span class="torrent-quicksearch-resultcell" >Indexer</span> <span class="torrent-quicksearch-resultcell" >Grabs</span> <span class="torrent-quicksearch-resultcell" >Seeders</span> <span class="torrent-quicksearch-resultcell" >Leechers</span> <span class="torrent-quicksearch-resultcell" >DLCost</span> <span class="torrent-quicksearch-resultcell" >Date</span> <span class="torrent-quicksearch-resultcell">Size</span> <span class="torrent-quicksearch-resultcell">IMDB</span> `; Array.from(node.children).forEach((e, i) => { e.style.gridColumnStart = i + 1; e.style.fontSize = `${GM_config.get("fontsize", 12)}px`; }); } function addResultsTable(data) { if (data.length == 0) { return; } let resultList = document.querySelector("#torrent-quicksearch-resultlist"); let tempFrag = new DocumentFragment(); data.forEach((e, i) => { let node = document.createElement("span"); node.setAttribute("class", "torrent-quicksearch-resultitem"); node.innerHTML = ` <span class="torrent-quicksearch-resultcell torrent-quicksearch-links" style='grid-column-start:1' > <a href=${e["DownloadUrl"]}>Download</a> <br> <br> <a href=${e["InfoUrl"]}>Details</a> </span> <span style='grid-column-start:2'> <form> <span> <select class=torrent-quicksearch-clientSelect> </select> </span> <span> <span class="tooltip"> <button class=torrent-quicksearch-clientSubmit>Send</button> <span class="tooltiptext">Arr Clients imdbID sent from entry if null then page</span> </span> </span> </form> </span> <span class="torrent-quicksearch-resultcell" style='grid-column-start:3' >${ e["Title"] }</span> <span class="torrent-quicksearch-resultcell" style='grid-column-start:4' >${ e["Indexer"] }</span> <span class="torrent-quicksearch-resultcell" style='grid-column-start:5'>${ e["Grabs"] || "No Data" } </span> <span class="torrent-quicksearch-resultcell" style='grid-column-start:6'>${ e["Seeders"] || "No Data" } </span> <span class="torrent-quicksearch-resultcell" style='grid-column-start:7' >${ e["Leechers"] || "No Data" } </span> <span class="torrent-quicksearch-resultcell" style='grid-column-start:8'>${ e["Cost"] } </span> <span class="torrent-quicksearch-resultcell" style='grid-column-start:9' >${new Date( e["PublishDate"] ).toLocaleString("en-CA")}</span> <span class="torrent-quicksearch-resultcell" style='grid-column-start:10' >${( parseInt(e["Size"]) / 1073741824 ).toFixed(2)} GB</span> <span class="torrent-quicksearch-resultcell" style='grid-column-start:11' >${ e["ImdbId"] }</span>`; let selNode = node.querySelector("select"); JSON.parse(GM_config.getValue("downloadClients", "[]")).forEach((e) => { let optnode = document.createElement("option"); optnode.setAttribute("id", e.clientID); optnode.setAttribute("value", e.clientID); optnode.textContent = e.clientName; selNode.appendChild(optnode); }); node.querySelector("form").addEventListener("submit", clientFactory(e)); tempFrag.append(node); }); resultList.appendChild(tempFrag); } function resetResultList() { document.querySelector("#torrent-quicksearch-resultheader").textContent = ""; document.querySelector("#torrent-quicksearch-resultlist").textContent = ""; } function createMainDOM() { const box = document.createElement("div"); box.setAttribute("id", "torrent-quicksearch-overlay"); let rowSplit = 12; let contentWidth = 70; let boxMinHeight = 5; let boxMaxHeight = 100; let boxHeight = 40; let boxWidth = 70; let boxMaxWidth = 150; box.innerHTML = ` <div> <img id="torrent-quicksearch-toggle" src="${searchIcon}"></img> <div id="torrent-quicksearch-box"> <div id="torrent-quicksearch-content"> <div> <div id="torrent-quicksearch-msgnode"></div> <div id="torrent-quicksearch-custombox"> <div> <label>Title:</label> <input type="text" id="torrent-quicksearch-customsearch"> <label>Page IMDB:</label> <div id="torrent-quicksearch-imdbinfo">None</div> <button id="torrent-quicksearch-customsearchbutton">Custom Search</button> </div> </div> <div id="torrent-quicksearch-resultheader"></div> </div> <div id="torrent-quicksearch-resultlist"> </div> </div> </div> <style> /* Variables */ #torrent-quicksearch-overlay { --grid-size: max(calc(50vw/${rowSplit}),calc(100%/${rowSplit})); --icon-size:${iconSmall}%; --icon-padding:${paddingSmall}%; } #torrent-quicksearch-overlay { position: sticky; display: flex; flex-direction: column; gap: 10px; top: 40vh; pointer-events: none; z-index: 900000; } #torrent-quicksearch-overlay> div:first-of-type { position: absolute; left:80vw; } * { font-size:${GM_config.get("fontsize", 12)}px; } #torrent-quicksearch-toggle { margin-left: auto; display:block; cursor: pointer; pointer-events:all; width: var(--icon-size); height: var(--icon-size); padding-top: var(--icon-padding); padding-bottom:var(--icon-padding); margin-bottom:calc(${paddingLarge}vh - var(--icon-padding)); margin-top:calc(${paddingLarge}vh - var(--icon-padding)); } #torrent-quicksearch-box{ resize:both; direction:rtl; right:5vw; margin-right:auto; position:absolute; display:none; min-height: ${boxMinHeight}vh; max-height:${boxMaxHeight}vh; height: ${boxHeight}vh; width: ${boxWidth}vw; max-width: ${boxMaxWidth}vw; overflow:hidden; border:solid black 5px; } #torrent-quicksearch-msgnode{ background-color:#FFFFFF; width:calc(var(--grid-size)*${rowSplit}); display:none; height:calc(((${GM_config.get("fontsize", 12)}em) + 2em)/16); } #torrent-quicksearch-custombox { background-color:#FFFFFF; width:calc(var(--grid-size)*${rowSplit}); pointer-events:all; height:calc(((${GM_config.get("fontsize", 12)}em) + 2em) * (2/16)); } #torrent-quicksearch-custombox>div { display: flex; background-color:#FFFFFF; flex-direction:row; justify-content: center; width: 100% } #torrent-quicksearch-custombox>div >label { margin-left:2.5%; margin-right:2.5%; } #torrent-quicksearch-custombox>div >button { margin-left:2.5%; } #torrent-quicksearch-customsearch{ background-color:#FFFFFF; border:solid black 2px; flex-grow:1; } #torrent-quicksearch-custombox > label{ margin-left:2% margin-right:2% } #torrent-quicksearch-customsearchbutton { background-color: #4CAF50; border: none; color: white; text-align: center; text-decoration: none; font-size: ${GM_config.get("fontsize", 12) + 2}px; border-radius: 5px; } #torrent-quicksearch-content { pointer-events:all; background-color: #D7C49EFF; direction:ltr; height:100%; width:100%; } #torrent-quicksearch-content>div:nth-child(2) { scrollbar-color: white; overflow:scroll; width:100%; height:calc(100% - ((${GM_config.get("fontsize", 12)}em) + 2em)*(4/16)); } #torrent-quicksearch-content>div:nth-child(1) { width:100%; background-color: #B1D79E; } #torrent-quicksearch-resultlist{ border:solid white 5px; width:calc(var(--grid-size)*${rowSplit}); } .torrent-quicksearch-resultitem,#torrent-quicksearch-resultheader{ display: grid; grid-template-columns: repeat(${rowSplit},var(--grid-size)); width:calc((var(--grid-size)*${rowSplit})-10); } .torrent-quicksearch-resultitem{ font-size:${GM_config.get("fontsize", 12)}px; } #torrent-quicksearch-resultlist>.torrent-quicksearch-resultitem:nth-child(even) { background-color: #D7C49EFF; } #torrent-quicksearch-resultlist>.torrent-quicksearch-resultitem:nth-child(odd) { background-color: #d5cbcb; } #torrent-quicksearch-resultheader{ background-color: #B1D79E; font-size:${GM_config.get("fontsize", 12) + 2}px; height:calc(((${GM_config.get("fontsize", 12)}em) + 2em)*(2/16)); } .torrent-quicksearch-resultcell{ font-weight: bold; margin-left: 10%; overflow-wrap:break-word; } .torrent-quicksearch-clientSubmit { background-color: white; border: none; text-align: center; text-decoration: none; font-size:${GM_config.get("fontsize", 12) + 2}px; font-weight: bold; overflow: hidden; white-space: nowrap; width:100%'' } .torrent-quicksearch-clientSelect { font-size:${GM_config.get("fontsize", 12) + 2}px; font-weight: bold; width:100%; } .torrent-quicksearch-links *{ color:blue; cursor:pointer; text-decoration: none; } .torrent-quicksearch-links *:hover{ color:white; } .torrent-quicksearch-links *:active,focus{ animation: pulse 2s infinite; } @keyframes pulse { 0% ,100%{ color: blue; } 50% { color: white; } } ::-webkit-scrollbar-thumb{ background-color:white; } /* Tooltip container */ .tooltip { position: relative; display: inline-block; border-bottom: 1px dotted black; /* If you want dots under the hoverable text */ } /* Tooltip text */ .tooltip .tooltiptext { visibility: hidden; width: 120px; background-color: black; color: #fff; text-align: center; padding: 5px 0; border-radius: 6px; /* Position the tooltip text - see examples below! */ position: absolute; z-index: 1; } /* Show the tooltip text when you mouse over the tooltip container */ .tooltip:hover .tooltiptext { visibility: visible; } <style/>`; box .querySelector("#torrent-quicksearch-toggle") .addEventListener("mousedown", leftClickProcess); box .querySelector("#torrent-quicksearch-toggle") .addEventListener("mouseup", mouseUpProcess); document.addEventListener("mouseup", resetMouse); box .querySelector("#torrent-quicksearch-customsearchbutton") .addEventListener("click", () => { searchObj.cancel(); setTimeout(() => { if (Date.now() - lastClick < clickLimit) { return; } lastClick = Date.now(); let customSearch = true; searchObj.doSearch(); }, 0); }); document.body.insertBefore(box, document.body.children[0]); } ` Matching Function These help with finding a Match `; function getTitle() { let titleNode = document.querySelector(siteParser["title"]); if (titleNode == null) { throw new Error("Title Node Not Found"); } let title = titleNode[siteParser["titleAttrib"]]; title = titleCleanup(title); return title; } async function getIMDB() { let imdb = null; if (standardNames[window.location.host] == "imdb.com") { imdb = window.location.href; } else if (standardNames[window.location.host] == "themoviedb.org") { imdb = await tmdbPageIMDBParser(); } else { let imdbNode = document.querySelector(siteParser["imdb"]); if (imdbNode == null) { return null; } imdb = imdbNode[siteParser["imdbAttrib"]]; } imdb = imdbCleanup(imdb); return imdb; } function titleCleanup(title) { title = title.trim().replaceAll(/\n/g, ""); title = title.replaceAll(/ +/g, " "); return title; } function imdbCleanup(imdb) { if (imdb === null || imdb === undefined || imdb === 0 || imdb === "0") { return imdb; } imdb = String(imdb); imdb = imdb.match(/[0-9]+/).toString(); imdb = imdb.trim().replaceAll(/\n/g, ""); imdb = imdb.replace(/imdb/i, ""); imdb = imdb.replace(/tt/, ""); imdb = imdb.replace(/[:|&|*|\(|\)|!|@|#|$|%|^|\*|\\|\/]/, ""); imdb = imdb.replaceAll(/ +/g, ""); imdb = imdb.replace(/^0+/, ""); imdb = parseInt(imdb); return imdb; } function imdbFilter(entry, imdb) { if (imdb === null || imdb === "IMDB Not Provided") { return true; } else if (entry["ImdbId"] == 0) { return true; } else if (entry["ImdbId"] == imdb) { return true; } return false; } async function tmdbExternalMedia(type, id) { let key = GM_config.get("tmdbapi", "null"); if (key == "null") { return null; } let baseURL = new URL( `/3/movie/${id}/external_ids`, `https://api.themoviedb.org` ).toString(); let params = new URLSearchParams(); params.append("api_key", key); if (type == "tv") { baseURL = new URL( `/3/tv/${id}/external_ids`, `https://api.themoviedb.org` ).toString(); } let searchURL = `${baseURL}?${params.toString()}`; let req = await fetch(searchURL); return JSON.parse(req.responseText); } async function imdbTMDBConvertor(imdb) { let key = GM_config.get("tmdbapi", "null"); imdb = String(imdb); if (key == "null") { return null; } if (imdb.match(/tt/i) == null) { imdb = `tt${imdb}`; } let baseURL = new URL( `/3/find/${imdb}`, `https://api.themoviedb.org` ).toString(); let params = new URLSearchParams(); params.append("api_key", key); params.append("external_source", "imdb_id"); let searchURL = `${baseURL}?${params.toString()}`; let req = await fetch(searchURL); let data = JSON.parse(req.responseText); let output = data["tv_results"]; if (data["movie_results"].length > output.length) output = data["movie_results"]; return output[0]; } // First call to tmdbapi should be removed once we parse Movies vs TV async function tmdbTVDBConvertor(imdb) { let key = GM_config.get("tmdbapi", "null"); if (key == "null") { return null; } let helperData = await imdbTMDBConvertor(imdb); if (helperData == null) { return; } else if (helperData["media_type"] == "tv") { return (await tmdbExternalMedia("tv", helperData.id))["tvdb_id"]; } else if (helperData["media_type"] == "movie") { return (await tmdbExternalMedia("movie", helperData.id))["tvdb_id"]; } } async function tmdbPageIMDBParser() { let id = window.location.href .match(/\/[0-9]+/) .toString() .substring(1); if (window.location.href.match(/\/tv\//)) { return (await tmdbExternalMedia("tv", id))["imdb_id"]; } else { return (await tmdbExternalMedia("movie", id))["imdb_id"]; } } function currSiteFilter(entryURL) { if (GM_config.get("sitefilter") == "false") { return true; } if ( new URL(entryURL).hostname.replace(/\..*$/, "") == window.location.host.replace(/\..*$/, "") ) { return false; } return true; } ` URL Processing Indexer Functions Used to Get Indexer Info `; async function getIndexers() { document.querySelector("#torrent-quicksearch-msgnode").textContent = "Getting Indexers"; let searchprogram = GM_config.get("searchprogram"); let indexers = null; if (searchprogram == "Prowlarr") { indexers = await getIndexersProwlarr(); } else if (searchprogram == "Jackett") { indexers = await getIndexersJackett(); } else if (searchprogram == "NZBHydra2") { indexers = await getIndexersHydra(); } await indexerCacheHelper(indexers); return await listFilter(indexers); } async function getIndexersJackett() { let key = "jackettIndexers"; let cachedIndexers = await GM.getValue(key, "none"); if (cachedIndexers == "none") { null; } else if (Date.now() - (cachedIndexers?.date || 0) < day) { return cachedIndexers["indexers"]; } let params = new URLSearchParams(); params.append("apikey", GM_config.get("searchapi")); //Invalid Category Search params.append("Category[]", 20130); params.append("Query", "''"); let baseURL = new URL( `/api/v2.0/indexers/all/results`, `${GM_config.get("searchurl")}` ).toString(); let indexerURL = `${baseURL}?${params.toString()}`; let req = await fetch(indexerURL); let data = JSON.parse(req.responseText)["Indexers"]; let output = data.map((e) => { let dict = {}; dict["Name"] = e["Name"]; dict["ID"] = e["ID"]; return dict; }); await GM.setValue(key, { date: Date.now(), indexers: output, }); return output; } async function getIndexersProwlarr() { let key = "prowlarrIndexers", cachedIndexers = await GM.getValue(key, "none"); if (cachedIndexers == "none") { null; } else if (Date.now() - (cachedIndexers?.date || 0) < day) { return cachedIndexers["indexers"]; } let params = new URLSearchParams(); params.append("apikey", GM_config.get("searchapi")); let baseURL = new URL( `/api/v1/indexer`, `${GM_config.get("searchurl")}` ).toString(); let indexerURL = `${baseURL}?${params.toString()}`; let req = await fetch(indexerURL); let data = JSON.parse(req.responseText); data = data.sort(prowlarIndexSortHelper); let output = data.map((e) => { let dict = {}; dict["Name"] = e["name"]; dict["ID"] = e["id"]; return dict; }); await GM.setValue(key, { date: Date.now(), indexers: output, }); return output; } function prowlarIndexSortHelper(a, b) { if (a["priority"] > b["priority"]) { return -1; } if (a["priority"] < b["priority"]) { return 1; } return 0; } async function getIndexersHydra() { let key = "hydraIndexers"; let cachedIndexers = await GM.getValue(key, "none"); if (cachedIndexers == "none") { null; } else if (Date.now() - (cachedIndexers?.date || 0) < day) { return cachedIndexers["indexers"]; } let params = new URLSearchParams(); params.append("apikey", GM_config.get("searchapi")); let baseURL = new URL( `/api/stats/indexers/`, `${GM_config.get("searchurl")}` ).toString(); let indexerURL = `${baseURL}?${params.toString()}`; let req = await fetch(indexerURL); let data = JSON.parse(req.responseText); let output = data.map((e) => { let dict = {}; dict["Name"] = e["indexer"]; dict["ID"] = e["indexer"]; return dict; }); await GM.setValue(key, { date: Date.now(), indexers: output, }); return output; } async function listFilter(allIndexers) { let selectedIndexers = null; if (GM_config.get("listType") == "black") { selectedIndexers = await blackListHelper(allIndexers); } else { selectedIndexers = await whiteListHelper(allIndexers); } let output = []; for (let i in allIndexers) { if (selectedIndexers.has(allIndexers[i]["ID"])) { output.push(allIndexers[i]); } } return output; } async function indexerCacheHelper(allIndexers) { if (GM_config.get("indexers") == "") { return; } let searchprogram = GM_config.get("searchprogram"); let indexerNames = GM_config.get("indexers") .split(",") .map((e) => e.trim().toLowerCase()); for (let j in indexerNames) { let key = `${searchprogram}_${indexerNames[j]}`; let cached = await GM.getValue(key, "none"); if (cached != "none") { continue; } for (let i in allIndexers) { if (allIndexers[i]["Name"].match(new RegExp(indexerNames[j], "i"))) { await GM.setValue(key, allIndexers[i]["ID"]); } } } } async function blackListHelper(allIndexers) { let indexerID = new Set(allIndexers.map((e) => e["ID"])); let indexerNames = GM_config.get("indexers") .split(",") .map((e) => e.trim()); let searchprogram = GM_config.get("searchprogram"); for (let j in indexerNames) { let key = `${searchprogram}_${indexerNames[j]}`; let cached = await GM.getValue(key, "none"); if (cached != "none") { indexerID.delete(cached); } } return indexerID; } async function whiteListHelper(allIndexers) { let indexerID = new Set(); let indexerNames = GM_config.get("indexers") .split(",") .map((e) => e.trim()); let searchprogram = GM_config.get("searchprogram"); for (let j in indexerNames) { let key = `${searchprogram}_${indexerNames[j]}`; let cached = await GM.getValue(key, "none"); if (cached != "none") { indexerID.add(cached); } } return indexerID; } ` URL Processing Search Functions Used to Produce URL for Search `; function getSearchURLProwlarr(indexer) { let params = new URLSearchParams(); params.append("apikey", GM_config.get("searchapi")); params.append( "query", `${document.querySelector("#torrent-quicksearch-customsearch").value}` ); params.append("IndexerIds", indexer); let baseURL = new URL( "/api/v1/search", GM_config.get("searchurl") ).toString(); return `${baseURL}?${params.toString()}`; } function getSearchURLJackett(indexer) { let params = new URLSearchParams(); params.append("apikey", GM_config.get("searchapi")); params.append( "Query", `${document.querySelector("#torrent-quicksearch-customsearch").value}` ); let baseURL = new URL( `/api/v2.0/indexers/${indexer}/results`, GM_config.get("searchurl") ).toString(); params.append("cachetime", "20"); return `${baseURL}?${params.toString()}`; } function getSearchURLHydraNZB(indexer) { let params = new URLSearchParams(); params.append("apikey", GM_config.get("searchapi")); params.append( "q", `${document.querySelector("#torrent-quicksearch-customsearch").value}` ); params.append("indexers", indexer); params.append("t", "search"); params.append("o", "xml"); params.append("cachetime", "20"); let baseURL = new URL("/api", GM_config.get("searchurl")).toString(); return `${baseURL}?${params.toString()}`; } function getSearchURLHydraTor(indexer) { let params = new URLSearchParams(); params.append("apikey", GM_config.get("searchapi")); params.append( "q", `${document.querySelector("#torrent-quicksearch-customsearch").value}` ); params.append("indexers", indexer); params.append("t", "search"); params.append("o", "xml"); //hydra likes to send no data params.append("cachetime", "20"); let baseURL = new URL("/torznab/api", GM_config.get("searchurl")).toString(); return `${baseURL}?${params.toString()}`; } ` client Functions Functions to support Clients `; function getSonarrURL(clientURL, clientAPI) { let params = new URLSearchParams(); params.append("apikey", clientAPI); let baseURL = new URL("/api/v3/release/push", clientURL).toString(); return `${baseURL}?${params.toString()}`; } function getRadarrURL(clientURL, clientAPI) { let params = new URLSearchParams(); params.append("apikey", clientAPI); let baseURL = new URL("/api/v3/release/push", clientURL).toString(); return `${baseURL}?${params.toString()}`; } ` Events These Functions Create Events to be used by script `; function leftClickProcess(e) { e.preventDefault(); e.stopPropagation(); if (e.button != 0) { return; } mouseState = "down"; document.addEventListener("mousemove", mouseDragProcess); document .querySelector("#torrent-quicksearch-overlay") .style.setProperty("--icon-padding", `${paddingLarge}vh`); } function mouseUpProcess(e) { e.preventDefault(); e.stopPropagation(); mouseClicksProcess(); resetMouse(); } async function mouseClicksProcess() { if (mouseState == "dragged") { return; } else if (Date.now() - lastClick < clickLimit) { return; } else if (verifyConfig() == false) { GM.notification( "At Minimum You Need to Set\nSearch URl\nSearch API\nSearch Program", program, searchIcon ); GM_config.open(); return; } lastClick = Date.now(); await searchObj.toggleSearch(); } //Reset Mouse Events function resetMouse() { mouseState = "up"; document .querySelector("#torrent-quicksearch-overlay") .style.setProperty("--icon-padding", `${paddingSmall}vh`); document.removeEventListener("mousemove", mouseDragProcess); } function mouseDragProcess(e) { mouseState = "dragged"; //check mouse state on enter if (mouseState == "up" || e.buttons == 0) { return; } //poll mouse state setInterval( () => { if (mouseState == "up" || e.buttons == 0) { return; } }, 1000 ); let dragState = true; let toggleHeight = parseInt( getComputedStyle( document.querySelector("#torrent-quicksearch-toggle") ).height.replaceAll(/[^0-9.]/g, "") ); let startMousePosition = parseInt(e.clientY); let offsetMousePosition = startMousePosition - toggleHeight / 2; let viewport = (offsetMousePosition / window.innerHeight) * 100; viewport = Math.max(viewport, -20); viewport = Math.min(viewport, 89); document.querySelector( "#torrent-quicksearch-overlay" ).style.top = `${viewport}vh`; } ` Client Functions Functions For Sending Downloads to Clients `; function arrIMDBHelper(releaseData) { let pageIMDB = document.querySelector( "#torrent-quicksearch-imdbinfo" ).textContent; if (releaseData["ImdbId"] != imdbParserFail) { return parseInt(releaseData["ImdbId"]); } else if (pageIMDB != imdbParserFail && pageIMDB != null) { return parseInt(pageIMDB); } else { return; } } async function sendSonarrClient(releaseData, clientData) { releaseData["ImdbId"] = arrIMDBHelper(releaseData); (releaseData["TvdbId"] = await tmdbTVDBConvertor(releaseData["ImdbId"])), (releaseData["tmdbId"] = ( await imdbTMDBConvertor(releaseData["ImdbId"]) )?.id); let res = await fetch( getSonarrURL(clientData.clientURL, clientData.clientAPI), { method: "post", data: JSON.stringify(releaseData), semaphore: false, } ); if (res.status != 200) { GM.notification(res.responseText, program, searchIcon); return; } let data = JSON.parse(res.responseText); let initValue = ""; let finalMsg = data.reduce((prev, curr, index) => { if (curr["rejections"].length > 0) { let epNums = curr["mappedEpisodeNumbers"].length > 0 ? curr["mappedEpisodeNumbers"].join(",") : "No Episodes"; let errorMsg = [ `${curr["seriesTitle"]} Season ${curr["seasonNumber"]} Episodes ${epNums}`, `Status Rejected: ${curr["rejections"].join(",")}`, ]; return `${prev}\n\[${errorMsg.join("\n")}\]`; } else if (curr["approved"] == true) { let acceptMsg = `Added ${curr["title"]} to client`; return `${prev}\n${acceptMsg}`; } }, initValue); GM.notification(finalMsg, program, searchIcon); } async function sendRadarrClient(releaseData, clientData) { releaseData["ImdbId"] = arrIMDBHelper(releaseData); (releaseData["TvdbId"] = await tmdbTVDBConvertor(releaseData["ImdbId"])), (releaseData["tmdbId"] = ( await imdbTMDBConvertor(releaseData["ImdbId"]) )?.id); let res = await fetch( getRadarrURL(clientData.clientURL, clientData.clientAPI), { method: "post", data: JSON.stringify(releaseData), headers: { "content-type": "application/json" }, semaphore: false, } ); if (res.status != 200) { GM.notification(res.responseText, program, searchIcon); return; } let data = JSON.parse(res.responseText); let initValue = ""; let finalMsg = data.reduce((prev, curr, index) => { if (curr["rejected"] == true) { let errorMsg = [ `${curr["movieTitles"].join(",")}`, `Status Rejected: ${curr["rejections"].join(",")}`, ]; return `${prev}\n\[${errorMsg.join("\n")}\]`; } if (curr["approved"] == true) { let acceptMsg = `Added ${curr["title"]} to client`; return `${prev}\n${acceptMsg}`; } }, initValue); GM.notification(finalMsg, program, searchIcon); } function clientFactory(releaseData) { let clientEvent = async function (e) { e.preventDefault(); e.stopPropagation(); let clientData = JSON.parse( GM_config.getValue("downloadClients", "[]") ).filter( (ele) => ele.clientID == e.target.querySelector("select").value )[0]; if (clientData.clientType == "Sonarr") { sendSonarrClient(releaseData, clientData); } else if (clientData.clientType == "Radarr") { sendRadarrClient(releaseData, clientData); } }; return clientEvent; } ` GM_config Functions `; function addNewClient(e) { e.preventDefault() e.stopPropagation() saveDownloadClient(); recreateDownloadClientNode(); } function saveDownloadClient() { let wrapper = GM_config.fields["downloadclients"].wrapper; function verify(obj) { let missingName = "Could Not add new Client\nClientName is missing"; let missingValue = `Could Not add new Client\nClientType ${obj["clientType"]} is missing one of it's required values`; if (Object.values(obj).filter((e) => e != "").length <= 3) { return false; } if (obj["clientName"] == "") { GM.notification(missingName, program, searchIcon); return false; } if (obj["clientType"] == "Sonarr" || obj["clientType"] == "Radarr") { if (obj["clientURL"] != "" && obj["clientAPI"] != "") { return true; } GM.notification(missingValue, program, searchIcon); return false; } //Add More conditionals for other clients else { if (obj["clientURL"] != "" && obj["clientAPI"] != "") { return true; } GM.notification(missingValue, program, searchIcon); return false; } } if (wrapper) { let inputs = wrapper.querySelectorAll("input,select"); let val = JSON.parse(GM_config.getValue("downloadClients", "[]")); let outdict = {}; outdict["clientID"] = Array(10) .fill() .map(() => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".charAt( Math.random() * 62 ) ) .join(""); for (let i in inputs) { let ele = inputs[i]; outdict[ele.id] = ele.value; } if (verify(outdict)) { val.push(outdict); GM_config.setValue("downloadClients", JSON.stringify(val)); } } } function getCurrentDownloadClientsNode() { let parent = document.createElement("div"); parent.setAttribute("id", "torrent-quicksearch-downloadclientsParent"); let titleNode = document.createElement("h1"); titleNode.textContent = "Current Clients"; parent.append(titleNode); let clients = JSON.parse(GM_config.getValue("downloadClients", "[]")); for (let i in clients) { //delete button let button = document.createElement("button"); button.setAttribute("class", "torrent-quicksearch-downloadclientsDelete"); button.textContent = "Delete Client"; button.addEventListener("click", deleteClient); parent.append(button); //client info box let client = clients[i]; let keys = Object.keys(client); let section = document.createElement("div"); section.setAttribute("class", "torrent-quicksearch-downloadclients"); section.append(); for (let j in keys) { let key = keys[j]; let value = client[key]; let node = document.createElement("div"); let keyNode = document.createElement("span"); keyNode.textContent = `${key}: `; keyNode.style.display = "inline-block"; keyNode.style.fontWeight = "bold"; keyNode.style.marginRight = "5px"; let valNode = document.createElement("span"); valNode.textContent = `${value}`; valNode.style.display = "inline-block"; node.append(keyNode); node.append(valNode); section.append(node); } parent.append(section); } return parent; } function deleteClient(e) { let clientNode = e.target.nextElementSibling; let clientID = Array.from(clientNode.childNodes).filter((e) => e.textContent.match(/clientID/) )[0].childNodes[1].textContent; let clients = JSON.parse(GM_config.getValue("downloadClients", "[]")); GM_config.setValue( "downloadClients", JSON.stringify(clients.filter((e) => e.clientID != clientID)) ); recreateDownloadClientNode(); } function recreateDownloadClientNode() { let wrapper = GM_config.fields["downloadclients"].wrapper; let oldParent = wrapper.querySelector( "#torrent-quicksearch-downloadclientsParent" ); let newParent = getCurrentDownloadClientsNode(); oldParent.parentElement.replaceChild(newParent, oldParent); } function downloadClientNode(configId) { var field = this.settings, id = this.id, create = this.create, retNode = create("div", { className: "config_var", id: configId + "_" + id + "_var", title: field.title || "", }); let currentClients = getCurrentDownloadClientsNode(); let newSubmissionForm = document.createElement("div"); newSubmissionForm.innerHTML = ` <h1>Add New Client</h1> <form> <div> <label for="client-select">Client Type:</label> <select id="clientType"> <option value="Sonarr">Sonarr</option> <option value="Radarr"option>Radarr</option> </select> </div> <br> <div> <label for="Name">Client Name:</label> <input type="text" placeholder="Name" id="clientName"> </div> <br> <div> <label for="clientURL">Client URL:</label> <input type="text" placeholder="URL" id="clientURL"> </div> <br> <div> <label for="clientAPI">Client API:</label> <input type="text" placeholder="API" id="clientAPI"> </div> <br> <div> <label for="clientUserName">Client Username:</label> <input type="text" placeholder="Username" id="clientUserName"> </div> <br> <div> <label for="clientPassword">Client Password:</label> <input type="" placeholder="Password" id="clientPassword"> </div> <button id=torrent-quicksearch-downloadclientsAdd>Add Client</button> </form> `; newSubmissionForm.querySelector("button").addEventListener("click", addNewClient); retNode.appendChild(currentClients); retNode.appendChild(newSubmissionForm); return retNode; } function openMenu() { hideDisplay(); resetResultList(); resetSearchDOM(); document .querySelector("#torrent-quicksearch-overlay") .style.setProperty("--icon-size", `${iconLarge}%`); document .querySelector("#torrent-quicksearch-toggle") .removeEventListener("mousedown", leftClickProcess); document .querySelector("#torrent-quicksearch-toggle") .removeEventListener("mouseup", mouseUpProcess); searchObj.cancel(); } function closeMenu() { /* url = GM_config.get('searchurl') if (url.match(/htt(p|ps):\/\//) == null) { url = `http://${url}` } GM_config.set('searchurl', url) GM_config.save()*/ hideDisplay(); document .querySelector("#torrent-quicksearch-overlay") .style.setProperty("--icon-size", `${iconSmall}%`); document .querySelector("#torrent-quicksearch-toggle") .addEventListener("mousedown", leftClickProcess); document .querySelector("#torrent-quicksearch-toggle") .addEventListener("mouseup", mouseUpProcess); } function initConfig() { GM_config.init({ id: "torrent-quick-search", title: "Torrent Quick Search Settings", // Panel Title fields: { tmdbapi: { label: "TMDB API Key", type: "text", title: "TMDB Key For TMDB/IMDB conversion\nAlso better media matching for arr clients", }, searchurl: { label: "Search URL", section: ["Search"], type: "text", title: "Base URl for search program", }, searchapi: { label: "API Key", type: "text", title: "API key for search program", }, searchprogram: { label: "Search Program", type: "select", options: ["Prowlarr", "Jackett", "NZBHydra2"], title: "Which search program", }, indexers: { label: "Indexers", section: ["Indexers"], type: "text", title: "Comma Seperated List of Indexers Names\nYou can just use part of the indexer name\nIt just needs to be a substring of name in search program", }, listType: { type: "radio", options: ["black", "white"], label: "Indexers ListType", title: "Use White list if you want to manually approve indexers\nUse Black list if you want to use all Indexers, and just have few or none to disable", default: "black", }, sitefilter: { label: "Filter Current Site", type: "radio", options: ["true", "false"], title: "Should Results From Current Site be Filtered Out", default: "false", }, fontsize: { label: "Font Size", section: ["GUI"], type: "int", title: "fontsize", default: 12, }, downloadclients: { section: ["Download Clients"], type: "downloadclient", }, }, types: { downloadclient: { default: null, toNode: downloadClientNode, toValue: function () { return; }, reset: function () { GM_config.setValue("downloadClients", "[]"); }, }, }, events: { open: openMenu, close: closeMenu, }, css: ` .torrent-quicksearch-downloadclients{ border:solid black 5px; margin-bottom:5px; } .torrent-quicksearch-downloadclientsDelete,#torrent-quicksearch-downloadclientsAdd{ margin-bottom:5px; background-color: gray; border: none; color: white; text-align: center; text-decoration: none; font-size: 20px; } `, }); GM.registerMenuCommand("Torrent Quick Search Settings", function () { GM_config.open(); }); } ` Globals + Main Function `; let searchIcon = "data:image/png;base64,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"; let iconLarge = 50; let iconSmall = 25; let paddingSmall = 0; let paddingLarge = 4; let mouseState = "up"; let imdbParserFail = "Could Not Parse"; let AbortError = new DOMException("aborted!", "AbortError"); var controller = new AbortController(); let program = "Torrent Quick Search"; let clickLimit = 500; let lastClick = Date.now() - clickLimit; let indexerSearchTimeout = 30000; let day = 86400000; let customSearch = false; let sem = null; //keep track of whether program was recently dragged //Normalize Site Name so we don't have repeat keys in larger infoparser dict let standardNames = { "www.imdb.com": "imdb.com", "www.themoviedb.org": "themoviedb.org", }; let infoParser = { "animebytes.tv": { title: "h2>a[href*=series]", titleAttrib: "textContent", }, "blutopia.xyz": { title: "h1>a[href*=torrents\\/similar]", titleAttrib: "textContent", imdb: "div[class*=movie-details]>span>a[title=IMDB]", imdbAttrib: "textContent", }, "beyond-hd.me": { title: "h1[class=movie-heading]", titleAttrib: "textContent", imdb: "ul[class*=movie-details]>li>span>a[href*=imdb]", imdbAttrib: "href", }, "imdb.com": { title: "h1", titleAttrib: "textContent", }, "themoviedb.org": { title: "h2", titleAttrib: "textContent", }, }; let siteParser = getParser(); function overideBuiltins() { URL = class extends URL { constructor(url, base) { if (url == undefined && base == undefined) { null; } else if (base != undefined && base.match(/(http|https)/) == null) { base = `http://${base}`; } else if (base == undefined && url.match(/(http|https)/) == null) { url = `http://${url}`; } super(url, base); } }; } function main() { overideBuiltins(); initConfig(); createMainDOM(); } main();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址