// ==UserScript==
// @name GGn VNDB uploady new
// @namespace none
// @version 7.002
// @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/1658586/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()
}
/** @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'
Platform()
}
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", '<a href="javascript:" id="fill_vndb">vndb</a>')
const fill_vndb = document.getElementById('fill_vndb')
fill_vndb.onclick = () => {
if (!gameTitleInput.value) return
const idMatch = idRegExp.exec(gameTitleInput.value)?.[0]
if (!idMatch) return
document.getElementById('vndburi').value = `https://vndb.org/${idMatch}`
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,
})
}
/** @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]],
"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(', ')
}