Greasy Fork镜像 还支持 简体中文。

GGn Uploady

Uploady library

目前為 2025-09-04 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/548332/1654723/GGn%20Uploady.js

// ==UserScript==
// @name         GGn Uploady
// @version      2
// @description  Uploady library
// @author       ingts
// @match        https://gazellegames.net/
// ==/UserScript==
/**
 * @template T
 * @typedef {{
 * getAliases?(result: T): string,
 * getCover?(result: T): string,
 * getDescription?(result: T): string,
 * getScreenshots?(result: T): string[],
 * getAgeRating?(result: T): number
 * getYear?(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

    /** @type {{[name: string]: {selector?: string, value?: string|string[]}}} */
    const inputMap = {
        aliases: {
            selector: 'aliases',
        },
        cover: {
            selector: 'image',
        },
        description: {
            selector: 'body',
        },
        screenshots: {},
        ageRating: {
            selector: 'rating',
        },
        year: {
            selector: 'year'
        },
        trailer: {
            selector: 'trailer',
        },
        title: {
            selector: 'name',
        },
    }

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

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

        let result = JSON.parse(sessionStorage.getItem(param))
        if (!result) {
            result = await fetchFn(param)
            sessionStorage.setItem(param, JSON.stringify(result))
        }

        showButton.textContent = 'Fill'
        showButton.disabled = false

        for (const [name, fn] of Object.entries(valueGetters)) {
            const value = 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
        }

        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: 820px;
         `
        mainContainer.id = `${linkInputId}_filler`

        const yearInput = document.querySelector('input[name=year]')
        const yearInputLabelRect = yearInput.closest('tr').firstElementChild.getBoundingClientRect()
        mainContainer.style.top = yearInputLabelRect.top + window.scrollY + 'px'
        mainContainer.style.left = yearInputLabelRect.right + window.scrollX - yearInputLabelRect.width + 'px'

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

        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='fillerSr'", 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: 5px;margin-bottom: 10px;align-items:center;">
                            <label>
                                <input type="checkbox" checked class="ss-replace-existing">
                                Remove existing
                            </label>
                            <button type="button" style="display:none;">Check highest resolutions</button>
                            <button type="button" style="display:none;">Check majority resolutions</button>
                            <button type="button" style="display:none;">Check all</button>
                            <span>0/${value.length} selected</span>
                        </div>
                        <div style="display:flex;gap: 5px;flex-wrap:wrap;max-height: 450px;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.onload = () => {
                    checkbox.insertAdjacentText('afterend', ` ${img.naturalWidth}x${img.naturalHeight}`)
                    label.style.display = 'flex'
                }
                imgEls.push(img)
            }

            const ssTopDiv = innerContainer.lastElementChild.children[0]
            const countSpan = ssTopDiv.querySelector('span')

            imgDiv.addEventListener('change', e => {
                if (e.target instanceof HTMLInputElement) {
                    const checkedCount = Array.from(imgDiv.querySelectorAll('input:checked')).length
                    countSpan.textContent = countSpan.textContent.replace(/\d+/, checkedCount.toString())
                }
            })

            const resolutions = Array.from(imgEls).map(img => `${img.naturalWidth}x${img.naturalHeight}`)
            const [highestResBtn,
                majorityResBtn,
                checkAllBtn] = ssTopDiv.querySelectorAll('button')

            if (resolutions.every(r => r === resolutions[0])) {
                checkAllBtn.style.display = 'block'
                checkAllBtn.onclick = () => {
                    for (const imgEl of imgEls) {
                        checkImg(imgEl)
                    }
                }
            } else {
                highestResBtn.style.display = 'block'
                highestResBtn.onclick = () => {
                    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(resolutions, highestRes)
                }

                majorityResBtn.style.display = 'block'
                majorityResBtn.onclick = () => {
                    const resolutions = Array.from(imgEls).map(img => `${img.naturalWidth}x${img.naturalHeight}`)
                    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(resolutions, majorityRes)
                }
            }

            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 (document.querySelector('.fillerSr')?.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} removeExisting
 */
function insertScreenshots(urls, removeExisting) {
    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()
        }
    }
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址