// ==UserScript== // @name GGn VNDB uploady new // @namespace none // @version 6 // @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 // @require https://update.greasyfork.icu/scripts/548332/1654460/GGn%20Uploady.js // @downloadURL none // ==/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() } /** @type {HTMLInputElement} */ const gameTitleInput = document.getElementById('title') /** * @typedef Result * @property {VisualNovel} vn * @property {Release[]} releases */ /** * @param {Result} result */ function fillUpload(result) { const pcPlatforms = ['win', 'lin', 'mac'] const vn = result.vn const consoleOnly = vn.platforms.every(platform => !pcPlatforms.includes(platform)) if (!consoleOnly) document.getElementById('platform').value = 'Windows' 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) } } const tagsInput = document.getElementById('tags') tagsInput.value = foundTags.size === 0 ? 'visual.novel' : `visual.novel, ${Array.from(foundTags).join(', ')}` document.getElementById('year').value = getYear(result) document.getElementById('image').value = vn.image.url const systemRequirements = ` [quote][align=center][b][u]System Requirements[/u][/b][/align] [*][b]OS[/b]: [*][b]Processor[/b]: [*][b]Memory[/b]: [*][b]Graphics[/b]: [*][b]DirectX[/b]: [*][b]Storage[/b]: [/quote]` const descInput = document.getElementById('album_desc') descInput.value = `${getDescription(result)} ${consoleOnly ? '' : systemRequirements}` insertScreenshots(vn.screenshots.map(s => s.url), true) } const idRegExp = /v(\d+)/ if (location.href.includes('upload.php')) { gameTitleInput.insertAdjacentHTML("afterend", 'vndb') const fill_vndb = document.getElementById('fill_vndb') fill_vndb.onclick = () => { if (!gameTitleInput.value) { return } const idMatch = idRegExp.exec(gameTitleInput.value)?.[0] req(idMatch).then(fillUpload) } } else { 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, }) } async function fakeReq(id) { return { "vn": { "aliases": [ "Pascha3" ], "alttitle": "パステルチャイム3 バインドシーカー", "description": `One day, during a mission with a 0% danger probability, an A-class adventurer Kaitos comes across a mysterious witch and accidentally disrupts her ritual. As a result, many evil spirits called Binds are released into the world and Kaitos, being affected by the ritual too, doesn't have much choice but to start helping the witch in sealing the Binds that have been set free. Enlisting the support from his friends both new and old, our hero sets out on a quest which ends up being much more complicated than a simple Bind hunt... [quote][align=center][b][u]System Requirements[/u][/b][/align] [b]Minimum[/b] [*][b]OS[/b]: Windows 7 [*][b]Processor[/b]: 2.0 GHz [*][b]Memory[/b]: 2 GB RAM [*][b]Graphics[/b]: Integrated Graphics [*][b]Storage[/b]: 200 MB available space[/quote] []`, "id": "v11292", "image": { "url": "https://t.vndb.org/cv/92/95792.jpg" }, "platforms": [ "win" ], "released": "2013-02-15", "screenshots": [ { "url": "https://t.vndb.org/sf/42/43242.jpg" }, { "url": "https://t.vndb.org/sf/43/43243.jpg" }, { "url": "https://t.vndb.org/sf/44/43244.jpg" }, { "url": "https://t.vndb.org/sf/45/43245.jpg" }, { "url": "https://t.vndb.org/sf/46/43246.jpg" }, { "url": "https://t.vndb.org/sf/47/43247.jpg" }, { "url": "https://t.vndb.org/sf/48/43248.jpg" }, { "url": "https://t.vndb.org/sf/49/43249.jpg" }, { "url": "https://t.vndb.org/sf/50/43250.jpg" }, { "url": "https://t.vndb.org/sf/17/44017.jpg" } ], "tags": [ { "id": "g2", "name": "Fantasy" }, { "id": "g3171", "name": "High School Student Heroine" } ], "title": "Pastel Chime 3 Bind Seeker", "titles": [ { "lang": "ja", "title": "パステルチャイム3 バインドシーカー" }, { "lang": "zh-Hans", "title": "粉彩铃音3" } ] }, "releases": [ { "extlinks": [ { "id": "http://www.alicesoft.com/pastelchime3/" }, { "id": "14859" }, { "id": "699486" } ], "has_ero": true, "id": "r23266", "minage": 18 }, { "extlinks": [ { "id": "http://www.alicesoft.com/pastelchime3/download/index.html" } ], "has_ero": true, "id": "r25263", "minage": 18 }, { "extlinks": [ { "id": "http://www.alicesoft.com/pastelchime3/" }, { "id": "VJ007518" }, { "id": "102512" }, { "id": "80992" }, { "id": "1004276" }, { "id": "dlsoft.dmm.co.jp/detail/alice_0021/" } ], "has_ero": true, "id": "r28316", "minage": 18 }, { "extlinks": [ { "id": "https://mangagamer.org/pastelchime3/" } ], "has_ero": true, "id": "r138108", "minage": 18 } ] } } /** @returns {Promise} */ 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]], "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) { return `[align=center][b][u]About the game[/u][/b][/align] ${removeLastBracket(result.vn.description)}` } /** @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 [...new Set(aliases)].join(', ') }