GGn VNDB uploady new

input game title or vndb id (anything with v(digits)) and click vndb to fill

// ==UserScript==
// @name         GGn VNDB uploady new
// @namespace    none
// @version      9
// @description  input game title or vndb id (anything with v(digits)) and click vndb to fill
// @author       ingts
// @match        https://gazellegames.net/upload.php*
// @match        https://gazellegames.net/torrents.php?action=editgroup*
// @connect      api.vndb.org
// @grant        GM.xmlHttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @require      https://update.greasyfork.org/scripts/548332/1671511/GGn%20Uploady.js
// ==/UserScript==
if (typeof GM_getValue('auto_search_trailer') === 'undefined')
    GM_setValue('auto_search_trailer', false)

const tagsDictionary = {
    'romance': ["Love", "Polyamory", "Polygamy", "Swinging", "Romance"],
    'horror': ["Horror", "Graphic Violence"],
    'science.fiction': ["Science Fiction", "AI"],
    'drama': ["Drama", "Suicide", "Suicidal", "Desperation"],
    'crime': ["Crime", "Slave"],
    'mystery': ["Mystery", "Amnesia", "Disappearance", "Secret Identity"],
    'comedy': ["Comedy", "Slapstick", "Comedic"],
    'fantasy': ["Fantasy", "Magic", "Mahou", "Superpowers"]
}

function removeLastBracket(str) {
    if (!str) return ''
    if (!str.endsWith(']')) return str

    let i = str.length - 1
    let bracketCounter = 0
    for (; i >= 0; i--) {
        if (str[i] === ']') {
            bracketCounter++
        } else if (str[i] === '[') {
            bracketCounter--
            if (bracketCounter === 0) {
                break
            }
        }
    }
    return str.substring(0, i).trim()
}

let platform = ''

/** @type {HTMLInputElement} */
const gameTitleInput = document.getElementById('title')

/**
 * @typedef Result
 * @property {VisualNovel} vn
 * @property {Release[]} releases
 */

/**
 * @param {Result} result
 */
function fillUpload(result) {
    Platform()
    const vn = result.vn

    const englishTitle = vn.titles.find(a => a.lang === 'en')
    gameTitleInput.value = getTitle(result, englishTitle)

    if (GM_getValue('auto_search_trailer'))
        window.open(`https://www.youtube.com/results?search_query=${gameTitleInput.value} trailer`, '_blank').focus()

    const aliasInput = document.getElementById('aliases')
    aliasInput.value = getAliases(result, englishTitle)

    const foundTags = new Set()

    const noRomance = vn.tags.some(tag => tag.name === "No Romance Plot")
    for (const [ggnTag, vndbTagsArr] of Object.entries(tagsDictionary)) {
        if (ggnTag === 'romance' && noRomance) continue
        for (const resultTag of vn.tags) {
            if (vndbTagsArr.some(word => resultTag.name.includes(word)))
                foundTags.add(ggnTag)
        }
    }

    document.querySelector('input[name="tags"]').value =
        `visual.novel${foundTags.size > 0 ? ', ' + Array.from(foundTags).join(', ') : ''}`

    document.getElementById('year').value = getYear(result)

    document.getElementById('image').value = vn.image.url

    document.getElementById('album_desc').value = getDescription(result)

    insertScreenshots(vn.screenshots.map(s => s.url), true)
}

const idRegExp = /v(\d+)/

if (location.href.includes('upload.php')) {
    gameTitleInput.insertAdjacentHTML("afterend", '<a href="javascript:" id="fill_vndb">vndb</a>')
    const fill_vndb = document.getElementById('fill_vndb')
    fill_vndb.onclick = () => {
        if (!gameTitleInput.value) return

        platform = document.getElementById('platform').value
        if (!platform) {
            alert('Platform must be selected')
            return
        }

        const idMatch = idRegExp.exec(gameTitleInput.value)?.[0]
        if (!idMatch) return
        document.getElementById('vndburi').value = `https://vndb.org/${idMatch}`
        req(idMatch).then(fillUpload)
    }
} else {
    platform = document.getElementById('user_script_data').dataset.platform

    createFiller('vndburi', idRegExp, req, {
        getAliases: r => getAliases(r),
        getCover: r => r.vn.image.url,
        getAgeRating: getAgeRating,
        getDescription: getDescription,
        getScreenshots: result => result.vn.screenshots.map(s => s.url),
        getYear: getYear,
        getTitle: getTitle,
    })
}

/** @returns {Promise<Result>} */
async function req(id) {
    /** @type {Result} */
    const result = {}
    await GM.xmlHttpRequest({
        url: 'https://api.vndb.org/kana/vn',
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        data: JSON.stringify({
            "filters": id ? ["id", "=", id] : ["search", "=", `${gameTitleInput.value}`],
            "fields": "alttitle, titles.title, title, aliases, description, image.url, screenshots.url, released, titles.lang, tags.name, platforms",
            "results": 1
        }),
        responseType: "json",
        onload: response => result.vn = response.response.results[0]
    })

    await GM.xmlHttpRequest({
        url: 'https://api.vndb.org/kana/release',
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        data: JSON.stringify({
            "filters": ["and", ["vn", "=", ["id", "=", result.vn.id]], ["official", "=", 1], ["platform", "=", `${ggnToVndbPlatform.get(platform)}`]],
            "fields": "minage, has_ero, extlinks.id",
            "results": 100
        }),
        responseType: "json",
        onload: response => result.releases = response.response.results
    })

    return result
}

/** @param {Result} result
 * @param {VndbTitle?} englishTitle
 */
function getTitle(result, englishTitle) {
    englishTitle ??= result.vn.titles.find(a => a.lang === 'en')
    return englishTitle ? englishTitle.title : result.vn.title
}

/** @param {Result} result */
function getYear(result) {
    return result.vn.released === 'TBA' ? new Date().getFullYear() + 1 : result.vn.released.split('-')[0]
}

/** @param {Result} result */
function getDescription(result) {
    const isConsole = !["Windows", "Mac", "Linux"].some(p => platform === p)
    return createDescription(removeLastBracket(result.vn.description),
        isConsole ? '' : `[*][b]OS[/b]: 
[*][b]Processor[/b]: 
[*][b]Memory[/b]: 
[*][b]Graphics[/b]: 
[*][b]DirectX[/b]: 
[*][b]Storage[/b]: `)
}

/** @param {Result} result */
function getAgeRating(result) {
    let rating
    const highestMinAge = Math.max(...result.releases.map(result => result.minage))
    if (highestMinAge === 12 || highestMinAge === 13) rating = 5
    else if (highestMinAge === 16 || highestMinAge === 17) rating = 7
    else if (highestMinAge >= 18) rating = 9
    else rating = 13
    return rating
}

/**
 * @param {Result} result
 * @param {VndbTitle?} englishTitle
 * @returns {string}
 */
function getAliases(result, englishTitle) {
    const vn = result.vn
    englishTitle ??= vn.titles.find(a => a.lang === 'en')
    const aliases = [vn.alttitle, vn.aliases.join(", "), englishTitle ? vn.title : null].filter(Boolean)

    for (const externalLink of result.releases.flatMap(release => release.extlinks)) {
        if (/[A-Z]{2}\d{4,}/.test(externalLink.id))
            aliases.push(externalLink.id)
    }

    return joinAliases([...new Set(aliases)])
}

// noinspection DuplicatedCode
const ggnToVndbPlatform = new Map([
    ["Windows", "win"],
    ["Linux", "lin"],
    ["Mac", "mac"],
    ["3DO", "tdo"],
    ["iOS", "ios"],
    ["Android", "and"],
    ["DOS", "dos"],
    ["Dreamcast", "drc"],
    ["NES", "nes"],
    ["SNES", "sfc"],
    ["Game Boy Advance", "gba"],
    ["Game Boy Color", "gbc"],
    ["MSX", "msx"],
    ["Nintendo DS", "nds"],
    ["Switch", "swi"],
    ["Wii", "wii"],
    ["Wii U", "wiu"],
    ["Nintendo 3DS", "n3d"],
    ["NEC PC-98", "p98"],
    ["NEC TurboGrafx-16", "pce"],
    ["NEC PC-FX", "pcf"],
    ["PlayStation Portable", "psp"],
    ["PlayStation 1", "ps1"],
    ["PlayStation 2", "ps2"],
    ["PlayStation 3", "ps3"],
    ["PlayStation 4", "ps4"],
    ["PlayStation 5", "ps5"],
    ["PlayStation Vita", "psv"],
    ["Mega Drive", "smd"],
    ["Saturn", "sat"],
    ["Sharp X1", "x1s"],
    ["Sharp X68000", "x68"],
    ["Xbox", "xb1"],
    ["Xbox 360", "xb3"],
])