Greasy Fork

Greasy Fork is available in English.

Torrent Quick Search

Toggle for Searching Torrents via Search aggegrator

当前为 2022-10-25 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Torrent Quick Search
// @namespace  https://github.com/TMD20/torrent-quick-search
// @supportURL https://github.com/TMD20/torrent-quick-search
// @version     1.60
// @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
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.xmlHttpRequest
// @grant GM.registerMenuCommand
// @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/gh/sizzlemctwizzle/GM_config@43fd0fe4de1166f343883511e53546e87840aeaf/gm_config.js
// @require  https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.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() {
  if (GM.info.script.name == "Torrent Quick Search") {
    overideBuiltins();
    initConfig();
    createMainDOM();
  }
  

}

main();