HJ-OTMOP / Adopt a HANDJOB

// ==UserScript==
// @name         Adopt a HANDJOB
// @namespace    http://tampermonkey.net/
// @version      0.1.2
// @description  Download at-risk torrents released by HANDJOB
// @author       OTMOP
// @license MIT
// @copyright 2020, OTMOP and HANDJOB
// @icon         https://ptpimg.me/6uob9q.png
// @match        https://passthepopcorn.me/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        unsafeWindow
// @run-at       document-end
// ==/UserScript==

/* CHANGELOG
0.1.2
- Added option to include or ignore trumpable torrents
- Added option to search only for DVDRip encodes

0.1.1
- Minor style tweaks
- Refined some option names
- Added option to include previously downloaded (but not snatched or leeching) torrents in the search
- Users can now repeat the search without reloading the page
- Added help icon tooltips to explain each option
*/

(() => {
    const PRIMARY_COLOUR = "#3498db";
    // eslint-disable-next-line no-undef
    const savedOptions = GM_getValue("adopt-options") || {};
    let TORRENT_CAP = savedOptions.torrentCap || 20;
    const IGNORE_ZERO_SEEDERS = true;
    let INCLUDE_TRUMPABLE = savedOptions.includeTrumpable || false;
    let INCLUDE_DOWNLOADED = savedOptions.includeDownloaded || false;
    let MAX_SEEDERS = savedOptions.maxSeeders || 2;
    let SEARCH_DELAY = savedOptions.searchDelay || 1000;
    let SEARCH_PAGE_OFFSET = savedOptions.searchOffset || 10;
    let DOWNLOAD_DELAY = savedOptions.downloadDelay || 1000;
    let RESOLUTION = savedOptions.resolutions || "anysd"; // null, anysd, anyhd, 480p, 576p, 720p, 1080p
    let AuthKey, TorrentPass;

    const modal = injectElements();

    class Stats {
        constructor() {
            this.form = document.querySelector("#handjob__adopt-modal fieldset");
            this.form.querySelector("#handjob__adopt-input--resolution").value = RESOLUTION;
            if (INCLUDE_DOWNLOADED === true)
                this.form.querySelector("#handjob__adopt-input--include-downloaded").checked = true;
            if (INCLUDE_TRUMPABLE === true)
                this.form.querySelector("#handjob__adopt-input--include-trumpable").checked = true;
            this.button = document.querySelector("#handjob__adopt-submit-button");
            this.closeButton = document.querySelector("#handjob__adopt-close-button");
            this.closeButton.addEventListener("click", () => modal.classList.add("hidden"));
            this.searchDisplay = document.querySelector("#handjob__adopt-download-status-pages span");
            this.foundDisplay = document.querySelector("#handjob__adopt-download-status-found span");
            this.torrentsDisplay = document.querySelector("#handjob__adopt-download-status-torrents span");
            this.activeTaskDisplay = document.querySelector("#handjob__adopt-download-active-task span");
            this.activeTaskIcon = document.querySelector("#handjob__adopt-download-active-task .handjob__adopt-icon");
            this.percentDisplay = document.querySelector(".handjob__adopt-loader-percent");
            this.loader = document.querySelector(".handjob__adopt-loader");
            this.tasks = [];
            this.Reset();
            this.Refresh();
        }

        Reset() {
            this.tasks = [];
            this.searching = true;
            this.pagesSearched = 0;
            this.found = 0;
            this.downloaded = 0;
            this.percent = 0;
            this.button.disabled = "";
            this.closeButton.disabled = "";
            this.loader.style.borderTop = "";
        }

        loadOptions() {
            const dl = !!this.form.querySelector("#handjob__adopt-input--include-downloaded").checked;
            INCLUDE_DOWNLOADED = savedOptions.includeDownloaded = dl;
            const trumpable = !!this.form.querySelector("#handjob__adopt-input--include-trumpable").checked;
            INCLUDE_TRUMPABLE = savedOptions.includeTrumpable = trumpable;
            const cap = parseInt(this.form.querySelector("#handjob__adopt-input--torrent-cap").value, 10);
            TORRENT_CAP = savedOptions.torrentCap = cap ? cap : TORRENT_CAP;
            const res = this.form.querySelector("#handjob__adopt-input--resolution").value;
            RESOLUTION = savedOptions.resolutions = res == "any" ? "" : !res ? RESOLUTION : res;
            if (savedOptions.resolutions === "") savedOptions.resolutions = "any";
            const offset = parseInt(this.form.querySelector("#handjob__adopt-input--search-offset").value, 10);
            SEARCH_PAGE_OFFSET = savedOptions.searchOffset = offset ? offset : SEARCH_PAGE_OFFSET;
            const maxSeeders = parseInt(this.form.querySelector("#handjob__adopt-input--max-seeders").value, 10);
            MAX_SEEDERS = savedOptions.maxSeeders = maxSeeders ? maxSeeders : MAX_SEEDERS;
            const searchDelay = parseInt(this.form.querySelector("#handjob__adopt-input--search-delay").value, 10);
            SEARCH_DELAY = savedOptions.searchDelay = searchDelay ? searchDelay : SEARCH_DELAY;
            const downloadDelay = parseInt(this.form.querySelector("#handjob__adopt-input--download-delay").value, 10);
            DOWNLOAD_DELAY = savedOptions.downloadDelay = downloadDelay ? downloadDelay : DOWNLOAD_DELAY;
            // eslint-disable-next-line no-undef
            GM_setValue("adopt-options", savedOptions);
        }

        AddPageSearched() {
            this.pagesSearched++;
            this.Refresh();
        }

        AddFound(num) {
            this.found += num;
            this.Refresh();
        }

        AddDownloaded() {
            this.downloaded++;
            this.Refresh();
        }

        AddTask(task) {
            switch (task.type) {
                case "error":
                    this.activeTaskIcon.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M9 1.03c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zM10 13H8v-2h2v2zm0-3H8V5h2v5z"/></svg>`;
                    this.activeTaskIcon.className = "handjob__adopt-icon icon-error";
                    break;
                case "success":
                    this.activeTaskIcon.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>`;
                    this.activeTaskIcon.className = "handjob__adopt-icon icon-success";
            }
            this.tasks.push(task);
            const changeTask = () => {
                if (task) this.activeTaskDisplay.innerHTML = task.text;
                this.activeTaskDisplay.removeEventListener("transitionend", changeTask);
                this.activeTaskDisplay.classList.remove("hide-task");
            };

            this.activeTaskDisplay.addEventListener("transitionend", changeTask);
            this.activeTaskDisplay.classList.add("hide-task");
        }

        Start() {
            this.Reset();
            this.loadOptions();
            this.loader.classList.remove("paused");
            this.button.disabled = true;
            this.closeButton.disabled = true;
        }

        End() {
            this.loader.classList.add("paused");
            this.closeButton.disabled = "";
            this.button.disabled = "";
        }

        Refresh() {
            if (this.downloaded >= 1) {
                this.searching = false;
                this.loader.style.borderTop = "16px solid #ef7017";
            }

            if (this.searching) {
                this.percent = Math.round((100 / TORRENT_CAP) * this.found);
                if (this.percent > 100) this.percent = 100;
            } else {
                this.percent = Math.round((100 / TORRENT_CAP) * this.downloaded);
            }

            this.searchDisplay.innerHTML = this.pagesSearched;
            this.torrentsDisplay.innerHTML = this.downloaded;
            this.foundDisplay.innerHTML = this.found;
            if (this.percent !== null) this.percentDisplay.innerHTML = this.percent + "%";
        }
    }

    class Torrent {
        constructor(json) {
            this.torrentid = json.TorrentId;
            this.filename = json.ReleaseName;
        }

        Download() {
            return new Promise((resolve, reject) => {
                fetch(this.GenerateDL())
                    .then(res => res.blob())
                    .then(blob => {
                        const url = window.URL.createObjectURL(blob);
                        const a = document.createElement("a");
                        a.style.display = "none";
                        a.href = url;
                        a.download = this.filename + ".torrent";
                        document.body.appendChild(a);
                        a.click();
                        window.URL.revokeObjectURL(url);
                        setTimeout(() => {
                            resolve(this.filename + " downloaded");
                        }, DOWNLOAD_DELAY);
                    })
                    .catch(err => {
                        console.log(err);
                    });
            });
        }

        GenerateDL() {
            const root = `https://passthepopcorn.me/torrents.php?`;
            return root + `action=download&id=${this.torrentid}&authkey=${AuthKey}&torrent_pass=${TorrentPass}`;
        }
    }

    const stats = new Stats();

    let torrents = [];

    function getSearchURL() {
        let search = "https://passthepopcorn.me/torrents.php?action=advanced&encoding=MKV&format=x264";
        if (RESOLUTION === "dvd") search += "&media=DVD&resolution=anysd";
        else if (RESOLUTION) search += "&resolution=" + RESOLUTION;
        search += "&filelist=handjob&order_by=seeders&order_way=asc&grouping=0";
        return search;
    }

    function getTorrentResults(resultsPage = SEARCH_PAGE_OFFSET) {
        return new Promise((resolve, reject) => {
            stats.AddTask({ type: "info", text: "Downloading results from page " + resultsPage });
            const url = getSearchURL() + "&page=" + resultsPage;

            fetch(url)
                .then(res => res.text())
                .then(raw => {
                    stats.AddPageSearched();
                    const parse = new DOMParser();
                    const dom = parse.parseFromString(raw, "text/html");
                    const script = Array.from(dom.querySelectorAll("#content script")).find(
                        x => !x.src && /var PageData/i.test(x.innerHTML)
                    );
                    if (!script) return reject(torrents);
                    const jsonString = script.textContent.match(/(?:var PageData\s*=\s*)([\w\W]*)(?:;\s*)$/);
                    const json = JSON.parse(jsonString[1]);
                    if (json.Movies.length === 0) return reject(torrents);
                    if (!AuthKey) AuthKey = json.AuthKey;
                    if (!TorrentPass) TorrentPass = json.TorrentPass;

                    const results = json.Movies.map(result => {
                        const torrentResult = result.GroupingQualities[0].Torrents[0];
                        if (INCLUDE_TRUMPABLE === false)
                            if (/torrent-info__trumpable/i.test(torrentResult.Title)) return null;
                        if (INCLUDE_DOWNLOADED === true) {
                            if (torrentResult.ColorType && torrentResult.ColorType !== "downloaded") return null;
                        } else if (torrentResult.ColorType) return null;
                        if (IGNORE_ZERO_SEEDERS === true && torrentResult.Seeders == 0) return null;
                        if (torrentResult.Seeders > MAX_SEEDERS) return null;

                        return new Torrent(torrentResult);
                    }).filter(x => x);

                    stats.AddFound(results.length);
                    if (results.length === 0) resultsPage += 5;
                    else resultsPage++;

                    torrents.push(...results);

                    if (torrents.length < TORRENT_CAP) {
                        setTimeout(() => {
                            resolve(getTorrentResults(resultsPage));
                        }, SEARCH_DELAY);
                    } else resolve(torrents);
                });
        });
    }

    function runDownloader() {
        modal.classList.remove("hidden");
        stats.AddTask({ type: "info", text: "Waiting to begin..." });

        const cb = () =>
            getTorrentResults()
                .catch(torrents => {
                    if (torrents.length > 1) {
                        stats.AddTask({ type: "error", text: "No results in search page" });
                        return torrents;
                    } else {
                        stats.Reset();
                        throw "No torrents found. Try adjusting search page offset!";
                    }
                })
                .then(res => {
                    res = res.slice(0, TORRENT_CAP);
                    let i = 0;

                    const downloadTorrent = (torrent = res[i]) =>
                        new Promise((resolve, reject) => {
                            stats.AddTask({ type: "info", text: "Downloading torrent file for " + torrent.filename });
                            torrent.Download().then(() => {
                                stats.AddDownloaded();
                                i++;
                                if (res[i]) return resolve(downloadTorrent(res[i]));
                                else return resolve();
                            });
                        });

                    downloadTorrent().then(() => {
                        stats.End();
                        stats.AddTask({ type: "success", text: "All downloads complete" });
                        torrents = [];
                    });
                })
                .catch(err => {
                    stats.AddTask({ type: "error", text: err });
                    stats.End();
                });

        stats.button.addEventListener("click", () => {
            stats.Start();
            cb();
        });
    }

    function injectElements() {
        const css = `
        #handjob__adopt-modal.hidden {
            opacity: 0;
            pointer-events: none;
        }

        .handjob__adopt-toolbar {
            width: 100%;
            background-color: ${PRIMARY_COLOUR};
            color: white;
            font-family: Roboto,Verdana, sans-serif;
            font-size: 24px;
            text-align: center;
            padding: 8px;
            border-radius: 4px;
            margin-top: -44px;
            margin-bottom: 20px;
            box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
            text-transform: uppercase;
            font-weight: bold;
        }

        #handjob__adopt-download-active-task span {
            transition: opacity 100ms ease-in-out;
        }

        #handjob__adopt-download-active-task span.hide-task {
            opacity: 0;
        }
        
        #handjob__adopt-modal {
            display: flex;
            position: fixed;
            z-index: 9999;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0,0,0,0.3);
            justify-content: center;
            align-items: center;
            opacity: 1;
            transition: opacity 500ms ease-in-out;
        }

        .handjob__adopt-loader-container {
            position: relative;
            width: 120px;
            height: 120px;
        }

        .handjob__adopt-loader {
                width: 100%;
                height: 100%;
                border: 16px solid #030303;
                border-radius: 50%;
                border-top: 16px solid ${PRIMARY_COLOUR};
                transition: border-top 500ms ease-in-out;
                -webkit-animation: spin 2s linear infinite; /* Safari */
                animation: spin 2s linear infinite;
        }

        .handjob__adopt-loader.paused{
            -webkit-animation-play-state:paused;
            -moz-animation-play-state:paused;
            -o-animation-play-state:paused; 
            animation-play-state:paused;
        }

        .handjob__adopt-loader-percent {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%,-50%);
            font-size: 24px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
          }

        #handjob__adopt-modal-inner {
            width: 800px;
            min-height: 200px;
            background-color: #EFEFEF;
            border-radius: 10px;
            display: flex;
            align-items: center;
            flex-flow: column;
            padding: 20px;
        }

        #handjob__adopt-download-status {
            font-family: Inconsolata, monospace;
            margin-top: 24px;
            color: #3c3c3c;
            font-size: 14px;
            width: 100%;
            display: flex;
            flex-flow: column;
            align-items: flex-end;
        }

        #handjob__adopt-download-status-top {
            width: 100%;
            display: flex;
            justify-content: space-between;
        }

        #handjob__adopt-download-status-pages {

        }

        #handjob__adopt-download-status-torrents {

        }

        #handjob__adopt-download-active-task {
            width: 100%;
            display: flex;
            align-items: center;
            border-radius: 4px;
            padding: 16px;
            margin-top: 16px;
            max-width: 100%;
            outline: none;
            text-decoration: none;
            transition-property: box-shadow,opacity;
            overflow-wrap: break-word;
            position: relative;
            white-space: normal;
            transition: box-shadow .28s cubic-bezier(.4,0,.2,1);
            will-change: box-shadow;
            box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
        }

        #handjob__adopt-download-active-task svg {
            margin-right: 16px;
        }

        .handjob__adopt-btn {
            font-family: Roboto, Arial, sans-serif;
            text-transform: uppercase;
            display: inline-block;
            margin-bottom: 0;
            font-weight: 400;
            text-align: center;
            white-space: nowrap;
            vertical-align: middle;
            -ms-touch-action: manipulation;
            touch-action: manipulation;
            cursor: pointer;
            background-image: none;
            border: 1px solid transparent;
                border-top-color: transparent;
                border-right-color: transparent;
                border-bottom-color: transparent;
                border-left-color: transparent;
            padding: 6px 12px;
            font-size: 14px;
            line-height: 1.42857143;
            border-radius: 4px;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }

        .handjob__adopt-btn:focus {
            outline: none;
        }

        .handjob__adopt-btn.disabled, .handjob__adopt-btn[disabled] {
            cursor: not-allowed;
            pointer-events: none;
            filter: alpha(opacity=65);
            opacity: .65;
            -webkit-box-shadow: none;
            box-shadow: none;
        }

        .handjob__adopt-btn-primary {
            color: #fff;
            background-color: #337ab7;
            border-color: #2e6da4;
        }

        .handjob__adopt-btn-error {
            color: #fff;
            background-color: #d9534f;
            border-color: #d43f3a;
        }

        .handjob__adopt-btn-error:hover {
            color: #fff;
            background-color: #c9302c;
            background-image: none;
            border-color: #ac2925;
        }

        .handjob__adopt-btn-error:active {
            color: #fff;
            background-color: #c9302c;
            background-image: none;
            border-color: #ac2925;
        }

        .handjob__adopt-btn-primary:hover {
            color: #fff;
            background-color: #286090;
            border-color: #204d74;
        }

        .handjob__adopt-btn-primary:active {
            color: #fff;
            background-color: #286090;
            background-image: none;
            border-color: #204d74;
        }

        #handjob__adopt-actions {
            margin-top: 24px;
        }

        #handjob__adopt-actions button:not(:last-child) {
            margin-right: 16px;
        }

        #handjob__adopt-modal fieldset {
            width: 100%;
            margin-bottom: 16px;
            border-radius: 4px;
        }

        #handjob__adopt-input--torrent-cap,
        #handjob__adopt-input--search-offset,
        #handjob__adopt-input--max-seeders {
            max-width: 50px;
            text-align: center;
        }

        #handjob__adopt-input--search-delay,
        #handjob__adopt-input--download-delay {
            max-width: 75px;
            text-align: center;
        }

        #handjob__adopt-input--resolution {
            max-width: 200px;
        }

        #handjob__adopt-modal table {
            color: #000;
        }

        #handjob__adopt-modal table td {
            padding: 8px 16px;
        }

        .handjob__adopt-icon {
            min-width: 18px;
            min-height: 18px;
        }

        .handjob__adopt-icon.icon-error svg {
            fill: red;
        }

        .handjob__adopt-icon.icon-success svg {
            fill: #009688;
        }

        .handjob__adopt-switch {
            position: relative;
            display: inline-block;
            width: 30px;
            height: 17px;
          }
          
          .handjob__adopt-switch input {
            opacity: 0;
            width: 0;
            height: 0;
          }

          .handjob__adopt-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            -webkit-transition: .4s;
            transition: .4s;
          }
          
          .handjob__adopt-slider:before {
            position: absolute;
            content: "";
            height: 13px;
            width: 13px;
            left: 2px;
            bottom: 2px;
            background-color: white;
            -webkit-transition: .4s;
            transition: .4s;
          }
          
          input:checked + .handjob__adopt-slider {
            background-color: #2196F3;
          }
          
          input:focus + .handjob__adopt-slider {
            box-shadow: 0 0 1px #2196F3;
          }
          
          input:checked + .handjob__adopt-slider:before {
            -webkit-transform: translateX(13px);
            -ms-transform: translateX(13px);
            transform: translateX(13px);
          }
          
          /* Rounded sliders */
          .handjob__adopt-slider.handjob__adopt-round {
            border-radius: 17px;
          }
          
          .handjob__adopt-slider.handjob__adopt-round:before {
            border-radius: 50%;
          }

          .handjob__adopt-input-container {
              display: inline-flex;
              align-items: center;
          }

            .handjob__adopt-help-icon {
                position: relative;
                margin-left: 16px;
                cursor: pointer;
            }

            .handjob__adopt-help-icon:before {
                pointer-events: none;
                content: "";
                position: absolute;
                right: -16px;
                transition: opacity 150ms ease-in-out;
                opacity: 0;
                width: 0;
                height: 0;
                border-top: 5px solid transparent;
                border-bottom: 5px solid transparent;
                border-right: 5px solid ${PRIMARY_COLOUR};
                top: 6px;
            }

            .handjob__adopt-help-icon:hover:before {
                opacity: 1;
            }

          .handjob__adopt-help-icon:after {
              content: attr(data-help-message);
              position: absolute;
              background-color: ${PRIMARY_COLOUR};
              opacity: 0;
              transition: opacity 150ms ease-in-out;
              white-space: nowrap;
              padding: 4px 8px;
              color: white;
              border-radius: 4px;
              margin-left: 16px;
              pointer-events: none;
          }

          .handjob__adopt-help-icon:hover:after {
            opacity: 1;
          }

          .handjob__adopt-help-icon path {
              fill: ${PRIMARY_COLOUR};
          }
    `;

        const styleInsert = document.createElement("style");
        styleInsert.innerHTML = css;
        document.head.appendChild(styleInsert);

        const insertNode = document.createElement("li");
        insertNode.className = "user-info-bar__item";
        const activate = insertNode.appendChild(document.createElement("a"));
        activate.style.cursor = "pointer";
        activate.textContent = "Adopt a HANDJOB";
        const insertionPoint = document.querySelector("#userinfo_minor");
        insertionPoint.appendChild(insertNode);
        activate.addEventListener("click", runDownloader);

        const modal = document.createElement("div");
        const helpIcon = message => {
            return `<div class="handjob__adopt-help-icon" data-help-message="${message}"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/></svg></div>`;
        };
        modal.id = "handjob__adopt-modal";
        modal.className = "hidden";
        const modalInner = modal.appendChild(document.createElement("div"));
        modalInner.innerHTML = `
        <div class="handjob__adopt-toolbar">Adopt A HANDJOB</div>
        <fieldset>
            <table>
                <tr>
                    <td>Starting Search Page: </td>
                    <td>
                        <div class="handjob__adopt-input-container">
                            <input type="number" min="1" id="handjob__adopt-input--search-offset" value="${SEARCH_PAGE_OFFSET}">
                            ${helpIcon(
                                "Starting search results page. Increase this if it takes a long while for a first match"
                            )}
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>Max Seeders: </td>
                    <td>
                        <div class="handjob__adopt-input-container">
                            <input type="number" id="handjob__adopt-input--max-seeders" value="${MAX_SEEDERS}">
                            ${helpIcon("Max no. of seeders a downloaded torrent can have")}
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>Torrents Cap: </td>
                    <td>
                        <div class="handjob__adopt-input-container">
                            <input type="number" id="handjob__adopt-input--torrent-cap" value="${TORRENT_CAP}">
                            ${helpIcon("Keep searching until this many torrents have been found")}
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>Resolutions: </td>
                    <td>
                        <div class="handjob__adopt-input-container">
                            <select id="handjob__adopt-input--resolution">
                                <option value="any">Any</option>
                                <option value="dvd">Only DVDRip</option>
                                <option value="anysd">Any Standard Definition</option>
                                <option value="anyhd">Any High Definition</option>
                                <option value="480p">480p</option>
                                <option value="576p">576p</option>
                                <option value="720p">720p</option>
                                <option value="1080p">1080p</option>
                            </select>
                            ${helpIcon("Resolutions of torrents to download")}
                        <div>
                    </td>
                </tr>
                <tr>
                    <td>Include Downloaded Torrents?</td>
                    <td>
                        <div class="handjob__adopt-input-container">
                            <label class="handjob__adopt-switch">
                                <input type="checkbox" id="handjob__adopt-input--include-downloaded">
                                <span class="handjob__adopt-slider handjob__adopt-round"></span>
                            </label>
                            ${helpIcon(
                                "Include torrents that you've downloaded before (but are not seeding or leeching)"
                            )}
                        </div>   
                    </td>
                </tr>
                <tr>
                    <td>Include Trumpable Torrents?</td>
                    <td>
                        <div class="handjob__adopt-input-container">
                            <label class="handjob__adopt-switch">
                                <input type="checkbox" id="handjob__adopt-input--include-trumpable">
                                <span class="handjob__adopt-slider handjob__adopt-round"></span>
                            </label>
                            ${helpIcon("Include torrents that are marked as trumpable")}
                        </div>   
                    </td>
                </tr>
                <tr>
                    <td>Search Request Delay (ms): </td>
                    <td>
                        <div class="handjob__adopt-input-container">
                            <input type="number" step="100" id="handjob__adopt-input--search-delay" value="${SEARCH_DELAY}">
                            ${helpIcon("Number of milliseconds to wait between requests for search result pages")}
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>Download Request Delay (ms): </td>
                    <td>
                        <div class="handjob__adopt-input-container">
                            <input type="number" step="100" id="handjob__adopt-input--download-delay" value="${DOWNLOAD_DELAY}">
                            ${helpIcon("Number of milliseconds to wait between requests for torrent downloads")}
                        </div>
                    </td>
                </tr>
            </table>
        </fieldset>
        <div class="handjob__adopt-loader-container">
            <div class="handjob__adopt-loader paused"></div>
            <div class="handjob__adopt-loader-percent"></div>
        </div>
        <div id="handjob__adopt-download-status">
            <div id="handjob__adopt-download-status-top">
                <div style="display: flex; flex-flow: column;">
                    <div id="handjob__adopt-download-status-pages">Search pages scanned: <span></span></div>
                    <div id="handjob__adopt-download-status-found">Eligible torrents found: <span></span></div>
                </div>
                <div id="handjob__adopt-download-status-torrents">Torrents downloaded: <span></span></div>
            </div>
            <div id="handjob__adopt-download-active-task">
                <div class="handjob__adopt-icon"></div>
                <span></span>
            </div>
            <div id="handjob__adopt-actions">
                <button id="handjob__adopt-close-button" class="handjob__adopt-btn handjob__adopt-btn-error">Close</button>
                <button id="handjob__adopt-submit-button" class="handjob__adopt-btn handjob__adopt-btn-primary">Find Torrents</button>
            <div>
        </div>
    `;
        modalInner.id = "handjob__adopt-modal-inner";

        document.querySelector("#extra2").appendChild(modal);
        return modal;
    }
})();