GGn Uploady

Uploady library

Versione datata 07/09/2025. Vedi la nuova versione l'ultima versione.

Questo script non dovrebbe essere installato direttamente. È una libreria per altri script da includere con la chiave // @require https://update.greasyfork.org/scripts/548332/1656293/GGn%20Uploady.js

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         GGn Uploady
// @version      3
// @description  Uploady library
// @author       ingts
// @match        https://gazellegames.net/
// ==/UserScript==
/**
 * @template T
 * @typedef {{
 * getAliases?(result: T): string,
 * getCover?(result: T): Promise<string> | string,
 * getDescription?(result: T): string,
 * getScreenshots?(result: T): string[],
 * getAgeRating?(result: T): number
 * getYear?(result: T): string
 * getTrailer?(result: T): string
 * getTitle?(result: T): string
 * }} ValueGetters
 */

/**
 * @template T
 * @param {string} linkInputId
 * @param {RegExp} paramRegexp
 * @param {(param: string) => Promise<T>} fetchFn
 * @param {ValueGetters<T>} valueGetters
 */
function createFiller(linkInputId, paramRegexp, fetchFn, valueGetters) {
    const linkInput = document.getElementById(linkInputId)
    if (!linkInput) return
    const param = paramRegexp.exec(linkInput?.value)?.[0]
    if (!param) return


    const showButton = document.createElement('button')
    linkInput.insertAdjacentElement('afterend', showButton)
    showButton.textContent = 'Fill'
    showButton.type = 'button'
    showButton.onclick = async () => {
        const mainId = `${linkInputId}_filler`
        let mainContainer = document.getElementById(mainId)
        if (mainContainer) {
            mainContainer.style.display = 'block'
            return
        }

        const style = document.createElement('style')
        style.textContent = `
        [id*=filler] label {
        input[type=checkbox], input[type=radio] {
    margin: 0 3px 0 0;
}
        }
        `
        document.body.after(style)

        /** @type {{[name: string]: {selector?: string, value?: string|string[]}}} */
        let inputMap = JSON.parse(sessionStorage.getItem(param))

        if (!inputMap) {
            inputMap = {
                aliases: {
                    selector: 'aliases',
                },
                cover: {
                    selector: 'image',
                },
                description: {
                    selector: 'body',
                },
                screenshots: {},
                ageRating: {
                    selector: 'rating',
                },
                year: {
                    selector: 'year'
                },
                trailer: {
                    selector: 'trailer',
                },
                title: {
                    selector: 'name',
                },
            }

            showButton.textContent = 'Loading'
            showButton.disabled = true

            const result = await fetchFn(param)
            showButton.textContent = 'Fill'
            showButton.disabled = false

            for (const [name, fn] of Object.entries(valueGetters)) {
                const value = await fn(result)
                const inputName = name
                    .replace('get', '')
                    .replace(/\w/, s => s.toLowerCase())
                if (inputName === 'ageRating') {
                    const ratingMap = new Map([
                        [1, '3+'],
                        [3, '7+'],
                        [5, '12+'],
                        [7, '16+'],
                        [9, '18+'],
                    ])
                    inputMap[inputName].value = ratingMap.get(value) ?? 'N/A'
                } else inputMap[inputName].value = value
            }

            sessionStorage.setItem(param, JSON.stringify(inputMap))
        }

        mainContainer = document.createElement('div')
        document.body.append(mainContainer)
        mainContainer.style.cssText = `
        position: absolute;
        padding: 10px;
        background-color: rgb(29 22 48);
        border-radius: 2px;
        border: 2px solid #997979;
        z-index: 9;
        max-width: 71%;
        max-height: 830px;
         `
        mainContainer.id = mainId

        const tdRect = document.getElementById('non_wiki_editing').nextElementSibling.querySelector('td').getBoundingClientRect()
        mainContainer.style.top = tdRect.top + window.scrollY + 'px'
        mainContainer.style.left = tdRect.right + window.scrollX - tdRect.width + 'px'

        const innerContainer = document.createElement('div')
        mainContainer.appendChild(innerContainer)
        innerContainer.style.cssText = `
            display: flex;
            flex-direction: column;
            height: 100%;
            overflow: auto;
            max-height: 800px;
        `

        const bottom = document.createElement('div')
        mainContainer.appendChild(bottom)

        bottom.style.marginTop = '10px'
        bottom.insertAdjacentHTML('beforeend',
            `<button type="button">Close</button>
<button type="button">Check all</button>
    <button type="button" style="margin-left: 10px;">Fill</button>`)
        mainContainer.appendChild(bottom)


        const closeButton = bottom.children[0]
        closeButton.onclick = () => mainContainer.style.display = 'none'

        const checkallButton = bottom.children[1]
        checkallButton.onclick = () => {
            for (const checkbox of mainContainer.querySelectorAll('input[type=checkbox]')) {
                checkbox.checked = true
            }
        }

        let newSr = ''
        const srRegex = /\[quote]\[align=center]\[b]\[u]System Requirements\[\/u]\[\/b]\[\/align].*\[\/quote]/si
        const imgEls = []

        for (const [name, obj] of Object.entries(inputMap)) {
            const value = obj.value
            if (!value) continue
            const formattedName = name.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/\w/, s => s.toUpperCase())

            if (name !== 'screenshots') {
                if (name === 'cover') {
                    const label = createLabelWithCheckbox(`<strong>${formattedName}</strong>`, `data-name=${name}`)
                    const img = document.createElement('img')
                    label.after(img)
                    img.src = value
                    img.style.maxHeight = '180px'
                    img.style.maxWidth = '120px'
                    img.style.marginBottom = '10px'
                    img.onload = () =>
                        label.querySelector('strong').insertAdjacentText('afterend', `: ${img.naturalWidth}x${img.naturalHeight}`)
                    continue
                } else if (name === 'description') {
                    const appendTo = document.createElement('div')
                    appendTo.style.display = 'flex'
                    appendTo.style.gap = '10px'
                    innerContainer.appendChild(appendTo)

                    createLabelWithCheckbox('Description', `data-name=${name}`, appendTo)

                    newSr = srRegex.exec(value)?.[0]
                    if (newSr) createLabelWithCheckbox('Only system requirements', `class=newsr`, appendTo)

                    innerContainer.insertAdjacentHTML('beforeend',
                        `<textarea readonly cols="150" style="margin: 5px 0 10px 0;height: 12em;">${value}</textarea>`)
                    continue
                }

                createLabelWithCheckbox(`<strong>${formattedName}</strong><span>: ${value}</span>`, `data-name=${name}`,)
                continue
            }

            innerContainer.insertAdjacentHTML('beforeend',
                // language=HTML
                `
                    <strong>Screenshots</strong>
                    <div style="border:1px solid #c0a5a5;padding: 3px;margin-bottom: 20px;">
                        <div style="display:flex;gap: 15px;margin-bottom: 10px;align-items:center;">
                            <label>
                                <input type="checkbox" checked class="ss-replace-existing">
                                Remove existing
                            </label>
                            <div style="display:none;flex-direction:column;">
                                <div>
                                    <button type="button">Check</button>
                                    <span>resolutions</span>
                                </div>
                                <div style="display:flex;gap: 8px;">
                                    <label>
                                        <input type="radio" name="resCheck" value="highest" checked>
                                        Highest
                                    </label>
                                    <label>
                                        <input type="radio" name="resCheck" value="majority">
                                        Majority
                                    </label>
                                </div>
                            </div>
                            <button type="button"">Check all</button>
                            <span style="color: #8eddc0;">0/${value.length} selected</span>
                        </div>
                        <div style="display:flex;gap: 5px;flex-wrap:wrap;max-height: 280px;overflow-y: auto"></div>
                    </div>
                `
            )

            const imgDiv = innerContainer.lastElementChild.lastElementChild

            for (const url of value) {
                const div = document.createElement('div')
                div.style.display = 'flex'
                div.style.flexDirection = 'column'
                div.style.gap = '5px'
                imgDiv.append(div)
                const label = createLabelWithCheckbox('', undefined, div)
                label.style.display = 'none'

                const img = document.createElement('img')
                div.append(img)
                img.src = url
                img.style.maxWidth = '200px'
                const checkbox = label.querySelector('input')
                img.addEventListener('load', () => {
                    checkbox.insertAdjacentText('afterend', ` ${img.naturalWidth}x${img.naturalHeight}`)
                    label.style.display = 'flex'
                    label.style.fontSize = '0.9em'
                })
                imgEls.push(img)
            }

            const ssTopDiv = innerContainer.lastElementChild.children[0]
            const countSpan = ssTopDiv.querySelectorAll('span')[1]
            imgDiv.addEventListener('change', e => {
                if (e.target?.type === 'checkbox') {
                    debugger
                    const checkedCount = Array.from(imgDiv.querySelectorAll('input[type=checkbox]:checked')).length
                    countSpan.textContent = countSpan.textContent.replace(/\d+/, checkedCount.toString())
                }
            })

            Promise.all(imgEls.map(img => new Promise(resolve => img.addEventListener('load', resolve))))
                .then(() => {
                    const resolutions = Array.from(imgEls).map(img => `${img.naturalWidth}x${img.naturalHeight}`)
                    const [resCheckBtn,
                        checkAllBtn] = ssTopDiv.querySelectorAll('button')
                    debugger

                    if (!resolutions.every(r => r === resolutions[0])) {
                        const resOptions = ssTopDiv.querySelector('div')
                        resOptions.style.display = 'flex'

                        resCheckBtn.onclick = () => {
                            if (resOptions.querySelector('[name=resCheck]:checked').value === 'highest') {
                                const highestRes = resolutions.reduce((highest, current) => {
                                    const [width, height] = current.split('x').map(Number)
                                    const [highestWidth, highestHeight] = highest.split('x').map(Number)
                                    const highestPixels = highestWidth * highestHeight

                                    return width * height > highestPixels ? current : highest
                                })
                                resCheck(highestRes)
                                return
                            }

                            const freqMap = resolutions.reduce((acc, val) => {
                                acc[val] = (acc[val] || 0) + 1
                                return acc
                            }, {})
                            const majorityRes = Object.keys(freqMap)
                                .reduce((max, cur) => freqMap[max] > freqMap[cur] ? max : cur)

                            resCheck(majorityRes)
                        }
                    }

                    checkAllBtn.style.display = 'block'
                    checkAllBtn.onclick = () => {
                        for (const imgEl of imgEls) {
                            checkImg(imgEl)
                        }
                    }

                    function checkImg(imgEl) {
                        const checkbox = imgEl.parentElement.querySelector('input')
                        checkbox.checked = true
                        checkbox.dispatchEvent(new Event('change', {bubbles: true}))
                    }

                    function resCheck(target) {
                        for (let index = 0; index < imgEls.length; index++) {
                            const imgEl = imgEls[index]
                            if (resolutions[index] !== target) continue
                            checkImg(imgEl)
                        }
                    }
                })
        }

        const fillButton = bottom.children[2]
        fillButton.onclick = () => {
            const namedCheckboxes = innerContainer.querySelectorAll('input[type=checkbox][data-name]')
            for (const namedCheckbox of namedCheckboxes) {
                if (!namedCheckbox.checked) continue
                const name = namedCheckbox.dataset.name
                if (inputMap[name].value) {
                    const selector = inputMap[name].selector
                    document.querySelector(`[name=${selector}]`).value = inputMap[name].value
                }
            }

            if (innerContainer.querySelector('.newsr')?.checked) {
                const descInput = document.querySelector('textarea[name=body]')
                const existingSr = srRegex.exec(descInput.value)?.[0]
                descInput.value = existingSr
                    ? descInput.value.replace(existingSr, newSr)
                    : descInput.value + '\n\n' + newSr
            }

            if (imgEls.length > 0) {
                const checkedUrls = imgEls.filter(el => el.parentElement.querySelector('input:checked')).map(el => el.src)
                insertScreenshots(checkedUrls, document.querySelector(".ss-replace-existing").checked)
            }

            mainContainer.style.display = 'none'
        }

        function createLabelWithCheckbox(after, checkboxAttrs, appendTo) {
            const label = document.createElement('label')
            label.style.display = 'flex'
            label.style.alignItems = 'center';
            (appendTo || innerContainer).appendChild(label)
            label.innerHTML =
                `<input type="checkbox" ${checkboxAttrs ? checkboxAttrs : ''}>
${after}
                    `
            return label
        }
    }
}

/**
 * @param {string[]} urls
 * @param {boolean=true} removeExisting
 */
function insertScreenshots(urls, removeExisting = true) {
    const screenInputs = document.getElementsByName("screens[]")
    if (!removeExisting) urls = [...urls, ...Array.from(screenInputs).map(i => i.value)]

    for (let i = 0; i < urls.length; i++) {
        if (screenInputs.length === 20) break

        if (removeExisting) {
            if (!screenInputs[i]) AddScreenField(true)
            screenInputs[i].value = urls[i]
        } else {
            if (screenInputs[screenInputs.length - 1].value) AddScreenField(true)
            screenInputs[i].value = urls[i]
        }
    }

    if (removeExisting) {
        for (let i = 0; i < screenInputs.length - urls.length; i++) {
            RemoveScreenField()
        }
    } else {
        for (let i = screenInputs.length - 1; i >= 0; i--) {
            if (screenInputs[i].value) break
            RemoveScreenField()
        }
    }
}