Greasy Fork

GGn No-Intro Helper

A GGn user script to help with No-Intro uploads/trumps

目前为 2022-09-05 提交的版本。查看 最新版本

// ==UserScript==
// @name          GGn No-Intro Helper
// @description   A GGn user script to help with No-Intro uploads/trumps
// @namespace     http://tampermonkey.net/
// @version       2.3.0
// @author        BestGrapeLeaves
// @license       MIT
// @match         *://gazellegames.net/upload.php?groupid=*
// @match         *://gazellegames.net/torrents.php?id=*
// @grant         unsafeWindow
// @grant         GM_xmlhttpRequest
// @grant         GM_listValues
// @grant         GM_deleteValue
// @grant         GM_setValue
// @grant         GM_getValue
// @connect       datomatic.no-intro.org
// @icon          https://i.imgur.com/UFOk0Iu.png
// ==/UserScript==


/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
var __webpack_exports__ = {};

;// CONCATENATED MODULE: ./src/inserts/checkForTrumpsButton.ts
function checkForTrumpsButton() {
    const existing = $("#check-for-no-intro-trumps-button");
    const button = existing.length > 0 ? existing : $(`<input id="check-for-no-intro-trumps-button" type="button" value="Check for No-Intro Trumps" style="background: hotpink; color: black; font-weight: bold; margin-left: 10px;"/>`);
    const progress = (text)=>{
        button.val(text);
    };
    const disable = ()=>{
        button.prop("disabled", true);
        button.css("background-color", "pink");
        button.css("color", "darkslategray");
        button.css("box-shadow", "none");
    };
    const insert = ()=>{
        button.detach();
        $(".torrent_table > tbody > tr:first-child > td:first-child").first().append(button);
    };
    return {
        disable,
        progress,
        insert,
        button
    };
}

;// CONCATENATED MODULE: ./src/utils/dom/extractNoIntroLinkFromDescription.ts
function extractNoIntroLinkFromDescription(torrentId) {
    const links = $(`#torrent_${torrentId} #description a`);
    return links.map(function() {
        return $(this).attr("href");
    }).get().map((link)=>{
        const url = new URL(link);
        url.protocol = "https:"; // Rarely descriptions have the http protocol
        return url.toString();
    }).find((link)=>link.startsWith("https://datomatic.no-intro.org/"));
}

;// CONCATENATED MODULE: ./src/utils/dom/getNoIntroTorrentsOnPage.ts

function notFalse(x) {
    return x !== false;
}
function getNoIntroTorrentsOnPage() {
    return $('a[title="Permalink"]').map(function() {
        const torrentId = new URLSearchParams($(this).attr("href").replace("torrents.php", "")).get("torrentid");
        const noIntroLink = extractNoIntroLinkFromDescription(torrentId);
        if (!noIntroLink) {
            return false;
        }
        const reported = $(this).parent().parent().find(".reported_label").text() === "Reported";
        return {
            torrentId,
            a: $(this),
            noIntroLink,
            reported,
            permalink: window.location.origin + "/" + $(this).attr("href")
        };
    }).get().filter(notFalse);
}

;// CONCATENATED MODULE: ./src/inserts/insertAddCopyHelpers.ts

function insertAddCopyHelpers() {
    getNoIntroTorrentsOnPage().forEach((param)=>{
        let { torrentId , a , noIntroLink  } = param;
        // Extract edition information
        const editionInfo = a.parents(".group_torrent").parent().prev().find(".group_torrent > td > strong").text();
        const [editionYear, ...rest] = editionInfo.split(" - ");
        const editionName = rest.join(" - ");
        const formatedEditionInfo = `${editionName} (${editionYear})`;
        // GroupId
        const groupId = new URLSearchParams(window.location.search).get("id");
        // Create params
        const params = new URLSearchParams();
        params.set("groupid", groupId);
        params.set("edition", formatedEditionInfo);
        params.set("no-intro", noIntroLink);
        // Insert button
        const addCopyButton = $(`<a href="upload.php?${params.toString()}" title="Add Copy" id="ac_${torrentId}">AC</a>`);
        $([
            " | ",
            addCopyButton
        ]).insertAfter(a);
    });
}

;// CONCATENATED MODULE: ./src/constants.ts
// REGEXES
const PARENS_TAGS_REGEX = /\(.*?\)/g;
const NO_INTRO_TAGS_REGEX = /\((Unl|Proto|Sample|Aftermarket|Homebrew)\)|\(Rev \d+\)|\(v[\d\.]+\)|\(Beta(?: \d+)?\)/;
// LISTS
const GGN_REGIONS = [
    "USA",
    "Europe",
    "Japan",
    "Asia",
    "Australia",
    "France",
    "Germany",
    "Spain",
    "Italy",
    "UK",
    "Netherlands",
    "Sweden",
    "Russia",
    "China",
    "Korea",
    "Hong Kong",
    "Taiwan",
    "Brazil",
    "Canada",
    "Japan, USA",
    "Japan, Europe",
    "USA, Europe",
    "Europe, Australia",
    "Japan, Asia",
    "UK, Australia",
    "World",
    "Region-Free",
    "Other", 
];
// TABLES
const REGION_TO_LANGUAGE = {
    USA: "English",
    Europe: "English",
    Japan: "Japanese",
    World: "English",
    "USA, Europe": "English",
    Other: "English",
    Korea: "Korean",
    Taiwan: "Chinese"
};
const TWO_LETTER_REGION_CODE_TO_NAME = {
    en: "English",
    de: "German",
    fr: "French",
    cz: "Czech",
    zh: "Chinese",
    it: "Italian",
    ja: "Japanese",
    ko: "Korean",
    pl: "Polish",
    pt: "Portuguese",
    ru: "Russian",
    es: "Spanish"
};

;// CONCATENATED MODULE: ./src/utils/GMCache.ts
class GMCache {
    getKeyName(key) {
        return `cache${this.name}.${key}`;
    }
    get(key) {
        const res = GM_getValue(this.getKeyName(key));
        if (res === undefined) {
            return undefined;
        }
        const { value , expires  } = res;
        if (expires && expires < Date.now()) {
            this.delete(key);
            return undefined;
        }
        return value;
    }
    set(key, value, ttl) {
        const expires = Date.now() + ttl;
        GM_setValue(this.getKeyName(key), {
            value,
            expires
        });
    }
    delete(key) {
        GM_deleteValue(this.getKeyName(key));
    }
    cleanUp() {
        const keys = GM_listValues();
        keys.forEach((key)=>{
            if (key.startsWith(this.getKeyName(""))) {
                const { expires  } = GM_getValue(key);
                if (expires < Date.now()) {
                    GM_deleteValue(key);
                }
            }
        });
    }
    clear() {
        const keys = GM_listValues();
        keys.forEach((key)=>{
            if (key.startsWith(this.getKeyName(""))) {
                GM_deleteValue(key);
            }
        });
    }
    constructor(name){
        this.name = name;
    }
}

;// CONCATENATED MODULE: ./src/utils/noIntroToGGnLanguage.ts

function noIntroToGGnLanguage(region, possiblyLanguages) {
    if (possiblyLanguages === undefined) {
        // @ts-expect-error
        return REGION_TO_LANGUAGE[region] || "Other";
    }
    const twoLetterCodes = possiblyLanguages.split(",").map((l)=>l.trim().toLowerCase());
    const isLanguages = twoLetterCodes.every((l)=>l.length === 2);
    if (!isLanguages || twoLetterCodes.length === 0) {
        // @ts-expect-error
        return REGION_TO_LANGUAGE[region] || "Other";
    }
    if (twoLetterCodes.length > 1) {
        return "Multi-Language";
    }
    return TWO_LETTER_REGION_CODE_TO_NAME[twoLetterCodes[0]] || "Other";
}

;// CONCATENATED MODULE: ./src/utils/fetchNoIntro.ts



const cache = new GMCache("no-intro");
// @ts-expect-error
unsafeWindow.GGN_NO_INTRO_HELPER_CACHE = cache;
function fetchNoIntro(url) {
    return new Promise((resolve, reject)=>{
        if (url.endsWith("n=")) {
            return reject(new Error("Blacklist no-intro url. Fetch was aborted to prevent IP ban."));
        }
        const cached = cache.get(url);
        if (cached) {
            resolve({
                ...cached,
                cached: true
            });
            return;
        }
        GM_xmlhttpRequest({
            method: "GET",
            url,
            timeout: 5000,
            onload: (param)=>{
                let { responseText  } = param;
                try {
                    const parser = new DOMParser();
                    const scraped = parser.parseFromString(responseText, "text/html");
                    // HTML is great
                    const dumpsTitle = [
                        ...scraped.querySelectorAll("td.TableTitle"), 
                    ].find((td)=>td.innerText.trim() === "Dump(s)");
                    if (!dumpsTitle) {
                        // @ts-expect-error
                        unsafeWindow.GMPARSER = scraped;
                        console.error("GGn No-Intro Helper: dumps title not found, set parser as global: GMPARSER", responseText);
                        throw new Error("No dump's title found");
                    }
                    const filename = dumpsTitle.parentElement.parentElement.parentElement.nextElementSibling.querySelector("table > tbody > tr:nth-child(2) > td:last-child").innerText.trim();
                    const title = scraped.querySelector("tr.romname_section > td").innerText.trim();
                    // Region/Lang
                    const [region, possiblyLanguages] = title.match(/\(.+?\)/g).map((p)=>p.slice(1, -1));
                    const matchedGGnRegion = GGN_REGIONS.find((r)=>r === region) || "Other";
                    const matchedGGnLanguage = noIntroToGGnLanguage(matchedGGnRegion, possiblyLanguages);
                    // One hour seems appropriate
                    const extension = filename.split(".").pop() || "";
                    const info = {
                        // We stopped shipping entire filenames
                        // when zibzab reported that it varies from dump to dump
                        // like some can have a "bad" filename
                        // title is 100% accurate, and filename shouldn't vary
                        extension,
                        title,
                        filename: title + "." + extension,
                        language: matchedGGnLanguage,
                        region: matchedGGnRegion,
                        cached: false
                    };
                    cache.set(url, info, 1000 * 60 * 60);
                    resolve(info);
                } catch (err) {
                    console.error("zibzab helper failed to parse no-intro:", err);
                    reject(new Error("Failed to parse no-intro :/\nPlease report to BestGrapeLeaves,\nincluding the error that was logged to the browser console"));
                }
            },
            ontimeout: ()=>{
                reject(new Error("Request to No-Intro timed out after 5 seconds"));
            }
        });
    });
}

;// CONCATENATED MODULE: ./src/utils/dom/fetchTorrentFilelist.ts
// We are fetching files for checking,
// might as well reduce load on servers and save to dom (like the button does)
function fetchTorrentFilelist(torrentId) {
    const parseFromDom = ()=>$(`#files_${torrentId} > table > tbody > tr:not(.colhead_dark) > td:first-child`).map(function() {
            return $(this).text();
        }).get();
    return new Promise((resolve)=>{
        // @ts-expect-error
        if ($("#files_" + torrentId).raw().innerHTML === "") {
            // $('#files_' + torrentId).gshow().raw().innerHTML = '<h4>Loading...</h4>';
            ajax.get("torrents.php?action=torrentfilelist&torrentid=" + torrentId, function(response) {
                // @ts-expect-error
                $("#files_" + torrentId).ghide();
                // @ts-expect-error
                $("#files_" + torrentId).raw().innerHTML = response;
                resolve(parseFromDom());
            });
        } else {
            resolve(parseFromDom());
        }
    });
}

;// CONCATENATED MODULE: ./src/utils/dom/checkIfTrumpable.ts


async function checkIfTrumpable(torrent) {
    try {
        const { title , cached  } = await fetchNoIntro(torrent.noIntroLink);
        const desiredFilename = title + ".zip";
        const files = await fetchTorrentFilelist(torrent.torrentId);
        if (files.length !== 1) {
            return {
                trumpable: true,
                desiredFilename,
                cached,
                inditermint: "Couldn't determine if the torrent is trumpable -\nMultiple/No zip files found in torrent"
            };
        }
        const actualFilename = files[0];
        return {
            trumpable: desiredFilename !== actualFilename,
            desiredFilename,
            actualFilename,
            cached
        };
    } catch (err) {
        console.error("GGn No-Intro Helper: Error checking trumpability", err);
        return {
            trumpable: true,
            cached: false,
            inditermint: "Couldn't determine if the torrent is trumpable -\nFailed fetching No-Intro:\n" + err.message
        };
    }
}

;// CONCATENATED MODULE: ./src/inserts/smallPre.ts
function smallPre(text, bgColor) {
    return `<pre style="
    padding: 0px;
    margin: 0;
    background-color: ${bgColor};
    color: black;
    font-weight: bold;
    font-size: 12px;
    padding-left: 3px;
    padding-right: 3px;
    width: fit-content;
  ">${text}</pre>`;
}

;// CONCATENATED MODULE: ./src/inserts/insertTrumpNotice.ts

function inditermintNoticeInfo(param) {
    let { inditermint  } = param;
    return {
        title: "Couldn't determine if the torrent is trumpable:",
        details: inditermint,
        color: "pink"
    };
}
function reportedNoticeInfo(param) {
    let { inditermint  } = param;
    return {
        title: "Torrent was trumped and reported!",
        details: "",
        color: "var(--darkRed)"
    };
}
function trumpableNoticeInfo(param) {
    let { actualFilename , desiredFilename  } = param;
    return {
        title: "This torrent is trumpable!",
        details: `The filename in the torrent is: ${smallPre(actualFilename, "lightcoral")} but the desired filename, based on <i>No-Intro</i> is: ${smallPre(desiredFilename, "lightgreen")}`,
        color: "hotpink"
    };
}
function reportableNoticeInfo(param) {
    let { fixedVersion , torrentId , actualFilename , desiredFilename , noIntroLink  } = param;
    const form = $("<div/></div>");
    const trumpingTorrentInput = $(`<input type="text" value="${fixedVersion.permalink}" placeholder="https://gazellegames.net/torrents.php?id=xxxxx&torrentid=yyyyyy" style="width: 75%; background: #ffb952; color: black;"/>`);
    const commentTextarea = $(`<textarea placeholder="Previous upload didn't like hummus" style="height: 100px; width: 90%; background: #ffb952; color: black; margin: 0;"/>`);
    commentTextarea.text(`ROM name changed on No-Intro.
Reported filename        : ${actualFilename}
Trumping filename       : ${desiredFilename}
No-Intro for reference : ${noIntroLink}`);
    const submitInputButton = $(`<input id="no-intro-helper-submit-trump-report-${torrentId}" type="button" value="REPORT" style="width: 70px; margin: 0; background: #ffb952; color: black; font-weight: bolder;"/>`);
    const errorMessage = $("<div style='color: red; font-weight: bold; font-size: 13px; white-space: pre-line;'></div>").hide();
    form.append(`<p style="font-size: 12px;">Trumping Torrent Permalink:</p>`, trumpingTorrentInput, `<p style="font-size: 12px;">Report Comment:</p>`, commentTextarea, `<p style="font-size: 12px;">Submit Report:</p>`, submitInputButton, errorMessage);
    submitInputButton.click(async ()=>{
        errorMessage.hide();
        const data = new FormData();
        data.append("submit", "true");
        data.append("torrentid", torrentId);
        data.append("categoryid", "1");
        data.append("type", "trump");
        data.append("sitelink", fixedVersion.permalink);
        data.append("extra", commentTextarea.val());
        data.append("id_token", new Date().getTime().toString());
        try {
            await fetch("/reportsv2.php?action=takereport", {
                method: "POST",
                body: data
            });
            location.reload();
        } catch (err) {
            console.error("Error submitting trump report", err);
            console.error("Form data sent:", Object.fromEntries([
                ...data.entries()
            ]));
            errorMessage.text(`An error occurred while submitting the trump report. If you believe this is a problem with the script, please report to BestGrapeLeaves (including console logs if you can).
Error Message:
${err.message}`);
            errorMessage.show();
        }
    });
    return {
        title: "Torrent needs to be reported for trump!",
        details: form,
        color: "#ff7600"
    };
}
function insertTrumpNotice(torrent) {
    const { inditermint , fixedVersion , torrentId , reported  } = torrent;
    // Settings
    let info;
    let type;
    if (inditermint) {
        type = "inditermint";
        info = inditermintNoticeInfo(torrent);
    } else if (fixedVersion) {
        if (reported) {
            type = "reported";
            info = reportedNoticeInfo(torrent);
        } else {
            type = "reportable";
            info = reportableNoticeInfo(torrent);
        }
    } else {
        type = "trumpable";
        info = trumpableNoticeInfo(torrent);
    }
    const { color , details , title  } = info;
    // Elements
    const detailsDiv = $(`<div style="font-weight: normal; color: white;"></div>`).hide();
    detailsDiv.append(details);
    const titleSpan = $(`
    <span style="color: ${color}; font-size: 14px; font-weight: bold;">${title}</span>`);
    const actionsDiv = $(`<div id="trump-notice-links-${torrentId}" style="font-weight: normal; font-size: 11px; display: inline; margin: 5px; user-select: none;"></div>`);
    // Toggle Details
    if (type !== "reported") {
        const toggleDetailsActionSpan = $(`<span style="cursor: pointer; margin-right: 5px;">[Expand]</span>`);
        toggleDetailsActionSpan.click(()=>{
            const collapsed = toggleDetailsActionSpan.text() === "[Expand]";
            if (collapsed) {
                toggleDetailsActionSpan.text("[Collapse]");
                detailsDiv.show();
            } else {
                toggleDetailsActionSpan.text("[Expand]");
                detailsDiv.hide();
            }
        });
        actionsDiv.append(toggleDetailsActionSpan);
    }
    // Send Report
    if (type === "reportable") {
        const sendReportActionSpan = $(`<span style="cursor: pointer; margin-right: 5px;">[Send Report]</span>`);
        sendReportActionSpan.click(()=>{
            $(`#no-intro-helper-submit-trump-report-${torrentId}`).click();
        });
        actionsDiv.append(sendReportActionSpan);
    }
    // Cheer
    if (type === "reported") {
        const cheerActionSpan = $(`<span style="cursor: pointer; margin-right: 5px; position: absolute;">[Cheer]</span>`);
        cheerActionSpan.click(()=>{
            cheerActionSpan.text("HOORAY!");
            cheerActionSpan.animate({
                opacity: 0
            }, // @ts-expect-error
            {
                duration: 2000,
                step: function(now) {
                    cheerActionSpan.css({
                        transform: "rotate(" + now * 360 * 5 + "deg)"
                    });
                }
            }, "swing");
        });
        actionsDiv.append(cheerActionSpan);
    }
    // Tree
    const wrapper = $(`<div></div>`);
    titleSpan.append(actionsDiv);
    wrapper.append(titleSpan);
    wrapper.append(detailsDiv);
    // Place
    let currentlyAdaptedToSmallScreen;
    function placeTrumpNotice() {
        console.log("adapting", window.innerWidth);
        if (window.innerWidth <= 800) {
            if (currentlyAdaptedToSmallScreen) {
                return;
            }
            currentlyAdaptedToSmallScreen = true;
            $(`#torrent${torrentId}`).css("border-bottom", "none");
            wrapper.css("margin-left", "25px");
            wrapper.detach();
            wrapper.insertAfter(`#torrent${torrentId}`);
        } else {
            if (currentlyAdaptedToSmallScreen === false) {
                return;
            }
            currentlyAdaptedToSmallScreen = false;
            $(`#torrent${torrentId}`).css("border-bottom", "");
            wrapper.css("margin-left", "0px");
            wrapper.detach();
            wrapper.appendTo(`#torrent${torrentId} > td:first-child`);
        }
    }
    placeTrumpNotice();
    $(window).resize(placeTrumpNotice);
    // Call global hook (for other scripts)
    // @ts-expect-error
    if (typeof unsafeWindow.GM_GGN_NOINTRO_HELPER_ADDED_LINKS === "function") {
        // @ts-expect-error
        unsafeWindow.GM_GGN_NOINTRO_HELPER_ADDED_LINKS({
            ...torrent,
            links: actionsDiv
        });
    }
}

;// CONCATENATED MODULE: ./src/inserts/insertTrumpSuggestions.ts




async function checkForImproperlyNamedTorrents(torrents) {
    const { disable , progress  } = checkForTrumpsButton();
    disable();
    let prevCached = false;
    const results = [];
    for(let i = 0; i < torrents.length; i++){
        const torrent = torrents[i];
        progress(`Checking For Trumps ${i + 1}/${torrents.length}...`);
        // timeout to avoid rate limiting
        if (!prevCached) {
            await new Promise((resolve)=>setTimeout(resolve, 500));
        }
        // Check trump
        const TrumpCheckResult = await checkIfTrumpable(torrent);
        const { cached  } = TrumpCheckResult;
        prevCached = cached;
        results.push({
            ...TrumpCheckResult,
            ...torrent
        });
    }
    return results;
}
// Filter the torrents that have a trump uploaded
function attachFixedVersionsToTorrents(torrents) {
    const trumpCanidates = [];
    const validTorrents = [];
    for (const torrent of torrents){
        if (torrent.trumpable) {
            trumpCanidates.push(torrent);
            continue;
        }
        validTorrents.push(torrent);
    }
    // Efficiency is not my greatest of concerns,
    // if you want implement a graph theory solution in O(1) or something
    const processed = trumpCanidates.map((c)=>({
            ...c,
            fixedVersion: validTorrents.find((v)=>!v.inditermint && v.noIntroLink === c.noIntroLink)
        }));
    return [
        ...validTorrents,
        ...processed
    ];
}
async function insertTrumpSuggestions(results) {
    const { progress  } = checkForTrumpsButton();
    let trumps = 0;
    results.forEach((torrent)=>{
        if (!torrent.trumpable) {
            return;
        }
        if (!torrent.inditermint && !torrent.fixedVersion) {
            trumps++;
        }
        insertTrumpNotice(torrent);
    });
    if (trumps === 0) {
        progress("No Trumps Found");
    } else if (trumps === 1) {
        progress("1 Trump Found");
    } else {
        progress(`${trumps} Trumps Found`);
    }
}
async function findAndDisplayTrumps() {
    const torrents = getNoIntroTorrentsOnPage();
    const results = await checkForImproperlyNamedTorrents(torrents);
    const processed = attachFixedVersionsToTorrents(results);
    console.log("GGn No-Intro Helper: Trumps", processed);
    insertTrumpSuggestions(processed);
}

;// CONCATENATED MODULE: ./src/pages/torrents.ts




function trumpSuggestions() {
    const torrents = getNoIntroTorrentsOnPage();
    if (torrents.length === 0) {
        return;
    }
    const { button , insert  } = checkForTrumpsButton();
    insert();
    if (torrents.length <= 4) {
        findAndDisplayTrumps();
    }
    button.click((e)=>{
        e.stopImmediatePropagation();
        findAndDisplayTrumps();
    });
}
function torrentsPageMain() {
    insertAddCopyHelpers();
    trumpSuggestions();
}

;// CONCATENATED MODULE: ./src/inserts/uploadLinkParserUI.ts
function uploadNoIntroLinkParserUI() {
    // elements
    const container = $(`<tr id="no-intro-url" name="no-intro-url">
      <td class="label">No-Intro Link</td>
    </tr>`);
    const input = $('<input type="text" id="no-intro-url-input" name="no-intro-url-input" size="70%" class="input_tog" value="">');
    const error = $('<p id="no-intro-url-error" name="no-intro-url-error" style="color: red; white-space:pre-line;"></p>').hide();
    const loading = $('<p id="no-intro-url-loading" name="no-intro-url-loading" style="color: green;">Loading...</p>').hide();
    // structure
    const td = $("<td></td>");
    td.append(input);
    td.append(error);
    td.append(loading);
    container.append(td);
    // utils
    const setError = (msg)=>{
        error.text(msg);
        error.show();
    };
    const setLoading = (isLoading)=>{
        if (isLoading) {
            loading.show();
        } else {
            loading.hide();
        }
    };
    return {
        loading,
        error,
        container,
        input,
        setError,
        setLoading
    };
}

;// CONCATENATED MODULE: ./src/utils/dom/setUploadEdition.ts
function setUploadEdition(edition) {
    try {
        $("#groupremasters").val(edition).change();
        GroupRemaster();
    } catch  {
    // group remaster always throws (regardless of the userscript)
    }
}

;// CONCATENATED MODULE: ./src/utils/generateTorrentDescription.ts
const generateTorrentDescription = function() {
    let url = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "xxx", filename = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "xxx";
    return `[align=center]${filename} matches [url=${url}]No-Intro checksum[/url]
Compressed with [url=https://sourceforge.net/projects/trrntzip/]torrentzip.[/url][/align]
`;
};

;// CONCATENATED MODULE: ./src/pages/upload.ts





function linkParser() {
    // UI
    const { error , container , input , setError , setLoading  } = uploadNoIntroLinkParserUI();
    // watch link input
    let justChecked = "";
    input.on("paste", (e)=>{
        e.preventDefault();
        const text = e.originalEvent.clipboardData.getData("text/plain");
        input.val(text);
        submit();
    });
    input.change(submit);
    // React to release type change, and insert input
    $("select#miscellaneous").change(function() {
        const selected = $("select#miscellaneous option:selected").text();
        if (selected === "ROM") {
            container.insertBefore("#regionrow");
            $("textarea#release_desc").val(generateTorrentDescription()); /// xxx temporary
        } else {
            container.detach();
        }
    });
    // handle submit
    async function submit() {
        // Prechecks
        error.hide();
        const url = input.val();
        if (justChecked === url) {
            return;
        }
        if (!url.startsWith("https://datomatic.no-intro.org/")) {
            setError("Invalid URL");
            return;
        }
        // Go
        justChecked = url;
        setLoading(true);
        try {
            const { filename , language , region  } = await fetchNoIntro(url);
            $("textarea#release_desc").val(generateTorrentDescription(url, filename));
            $("select#region").val(region);
            $("select#language").val(language);
        } catch (err) {
            setError(err.message || err || "An unexpected error has occurred");
        } finally{
            setLoading(false);
        }
    }
}
function magicNoIntroPress() {
    const filename = $("#file").val();
    const tags = filename ? filename.match(PARENS_TAGS_REGEX).filter((p)=>NO_INTRO_TAGS_REGEX.test(p)).join(" ") : "";
    // Release type = ROM
    $("select#miscellaneous").val("ROM").change();
    // It is a special edition
    if (!$("input#remaster").prop("checked")) {
        $("input#remaster").prop("checked", true);
        Remaster();
    }
    // Not a scene release
    $("#ripsrc_home").prop("checked", true);
    // @ts-expect-error Update title
    updateReleaseTitle($("#title").raw().value + " " + tags);
    // Get url params
    const params = new URLSearchParams(window.location.search);
    // Set correct edition (fallback to guessing)
    const editionInfo = params.get("edition");
    $("#groupremasters > option").each(function() {
        const title = $(this).text().toLowerCase();
        console.log("checking", title);
        if (editionInfo && title === editionInfo.toLowerCase()) {
            setUploadEdition($(this).val());
            return false; // This breaks out of the jquery loop
        } else {
            if (title.includes("no-intro") || title.includes("nointro")) {
                setUploadEdition($(this).val());
            }
        }
    });
    // Trigger no-intro link scraper
    const noIntroLink = params.get("no-intro");
    if (noIntroLink) {
        $("#no-intro-url-input").val(noIntroLink).change();
    }
}
function uploadPageMain() {
    // Insert No Intro magic button
    const noIntroMagicButton = $('<input type="button" value="No-Intro"></input>');
    noIntroMagicButton.click(()=>magicNoIntroPress());
    noIntroMagicButton.insertAfter("#file");
    linkParser();
}

;// CONCATENATED MODULE: ./src/index.ts


async function main() {
    console.log("GGn No-Intro Helper: Starting...");
    if (window.location.pathname === "/torrents.php") {
        torrentsPageMain();
    } else if (window.location.pathname === "/upload.php") {
        uploadPageMain();
    }
    // Blacklist no-intro URLs that lead to ban
    $("a").click(function() {
        if (/https?:\/\/datomatic\.no-intro\.org\/index\.php\?page=show_record&s=(.*?)&n=$/i.test($(this).attr("href"))) {
            $(this).text("BLACKLISTED");
            return false;
        }
    });
}
main().catch((e)=>{
    console.log(e);
});

/******/ })()
;