Greasy Fork

Greasy Fork is available in English.

GGn Steam Uploady (edited)

Fill upload form with Steam info. Edited from "GGn New Uploady"

当前为 2025-08-28 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         GGn Steam Uploady (edited)
// @namespace    https://gazellegames.net/
// @version      33.003
// @description  Fill upload form with Steam info. Edited from "GGn New Uploady"
// @author       NeutronNoir, ZeDoCaixao, ingts
// @match        https://gazellegames.net/upload.php*
// @match        https://gazellegames.net/torrents.php?action=editgroup*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @connect      store.steampowered.com
// @connect      steamcdn-a.akamaihd.net
// @require      https://update.greasyfork.icu/scripts/540511/1650736/GGn%20Formatters.js
// ==/UserScript==

if (typeof GM_getValue('get_languages') === 'undefined')
    GM_setValue('get_languages', true)
if (typeof GM_getValue('get_image_res') === 'undefined')
    GM_setValue('get_image_res', false)

const allowedTags = new Set([
    // "Casual", allowed but too common
    // "Exploration", allowed but too common
    "Action",
    "Adventure",
    "Simulation",
    "Strategy",
    "RPG",
    "Puzzle",
    "Fantasy",
    "Shooter",
    "Platformer",
    "Horror",
    "Visual Novel",
    "Open World",
    "Survival",
    "Sports",
    "Comedy",
    "FPS",
    "Mystery",
    "Sandbox",
    "Fighting",
    "Racing",
    "Shoot 'Em Up",
    "Point & Click",
    "Building",
    "Management",
    "Turn-Based Strategy",
    "Drama",
    "Romance",
    "Interactive Fiction",
    "Hidden Object",
    "Hack and Slash",
    "Education",
    "Bullet Hell",
    "Dungeon Crawler",
    "Dating Sim",
    "Historical",
    "Walking Simulator",
    "Card Game",
    "Third-Person Shooter",
    "RTS",
    "Life Sim",
    "Clicker",
    "Board Game",
    "Driving",
    "Tower Defense",
    "Time Management",
    "City Builder",
    "Thriller",
    "Wargame",
    "Beat 'em up",
    "Runner",
    "Stealth",
    "Trivia",
    "Typing",
    "Minigames",
    "4X",
    "Cooking",
    "Match 3",
    "Rhythm",
    "Cricket",
    "Rugby",
    "Mahjong",
    "Snowboarding",
    "Hockey",
    "Bowling",
    "Skateboarding",
    "Tennis",
    "Cycling",
    "Wrestling",
    "Basketball",
    "Golf",
    "Chess",
    "Boxing",
    "Gambling",
    "Fishing",
    "Auto Battler",
    "Solitaire",
    "Hunting",
    "Grand Strategy",
    "Space Sim",
])

const tagMap = new Map([
    ["Sci-fi", "science.fiction"],
    ["NSFW", "adult"],
    ["Hentai", "adult"],
    ["Roguelike", "roguelike"],
    ["Roguelite", "roguelike"],
    ["Text-Based", "text.adventure"],
    ["Flight", "flight.simulation"],
    ["Party", "party"],
    ["Party Game", "party"],
    ["Football (American)", "american.football"],
    ["Football (Soccer)", "soccer"],
])

function html2bb(str) {
    if (!str) return ""
    str = str.replace(/<video.*?>.*?<\/video>/g, "")
    str = str.replace(/<picture>.*?<\/picture>/g, "")
    str = str.replace(/<(.*?) class="bb_img_ctn">.*?<\/\1>/g, "")
    str = str.replace(/<h2 class="bb_tag"><strong>(.*?)<\/strong><\/h2>/g, `<h2 class="bb_tag">$1</h2>`)
    str = str.replace(/<li><p class="bb_paragraph">/g, "\n[*]")
    str = str.replace(/<p class="bb_paragraph"><strong>/g, "\n[b]")
    str = str.replace(/<\/div>/g, "")
    str = str.replace(/<div .*?>/g, "")
    str = str.replace(/< *br *\/*>/g, "\n\n") //*/
    str = str.replace(/< *b *>/g, "[b]")
    str = str.replace(/< *\/ *b *>/g, "[/b]")
    str = str.replace(/< *u *>/g, "[u]")
    str = str.replace(/< *\/ *u *>/g, "[/u]")
    str = str.replace(/< *i *>/g, "[i]")
    str = str.replace(/< *\/ *i *>/g, "[/i]")
    str = str.replace(/<strong><\/strong>/g, " ")
    str = str.replace(/< *strong *>/g, "[b]")
    str = str.replace(/< *\/ *strong *>/g, "[/b]")
    str = str.replace(/< *em *>/g, "[i]")
    str = str.replace(/< *\/ *em *>/g, "[/i]")
    str = str.replace(/< *\/ *li *>/g, "")
    str = str.replace(/< *ul *class=\\*"bb_ul\\*" *>/g, "")
    str = str.replace(/< *\/ *ul *>/g, "")
    str = str.replace(/< *h2 *class="bb_tag" *>/g, "\n[align=center][b][u]")
    str = str.replace(/< *h[12] *>/g, "\n[align=center][b][u]")
    str = str.replace(/< *\/ *h[12] *>/g, "[/u][/b][/align]\n")
    str = str.replace(/&quot;/g, "\"")
    str = str.replace(/&amp;/g, "&")
    str = str.replace(/< *a [^>]*>/g, "")
    str = str.replace(/<p class="bb_paragraph"><\/p>/g, '\n\n')
    str = str.replace(/<p class="bb_paragraph" *>/g, '\n')
    str = str.replace(/< *li *>/g, "[*]")
    str = str.replace(/< *\/ *a *>/g, "")
    str = str.replace(/< *p *>/g, "\n\n")
    str = str.replace(/<\/p>/g, "")
    str = str.replace(/“/g, "\"")
    str = str.replace(/”/g, "\"")
    str = str.replace(/  +/g, " ")
    str = str.replace(/< *img.*?>/g, "\n")
    str = str.replace(/\n +/g, "\n")
    str = str.replace(/\[\/b]\[\/u]\[\/align]\n\n/g, "[/b][/u][/align]\n")
    str = str.replace(/\n\n\n+/gm, "\n\n")
    str = str.replace(/\n\n\[\*]/g, "\n[*]")
    str = str.replace(/.*]\[u]\s?\[\/u]\[.*/g, "") // remove empty tags
    return str.trim()
}

/**
 * @param {SteamAppDetails} data
 */
function fill_form(data) {
    let recfield = data.pc_requirements
    let platform = "Windows"
    platform = $("#platform").val()

    switch (platform) {
        case "Windows":
            recfield = data.pc_requirements
            break
        case "Linux":
            recfield = data.linux_requirements
            break
        case "Mac":
            recfield = data.mac_requirements
            break
    }

    let desc = html2bb(`${data.about_the_game}

[quote][align=center][b][u]System Requirements[/u][/b][/align]
${recfield.minimum + (recfield.recommended ? '\n' + recfield.recommended : '')}[/quote]`)

    const descField = isEditPage ? document.querySelector("textarea[name='body']") : document.getElementById('album_desc')
    createUnformattedArea(descField, desc)
    descField.value = formatAll(desc, data.name)

    const year = data.release_date.date.split(", ").pop()
    const groupScreenshots = document.getElementsByName("screens[]")
    const add_screen = $("#image_block a[href='#']").first()

    const screenshots = data.screenshots
    if (isEditPage) {
        while (groupScreenshots.length < screenshots.length) {
            add_screen.click()
        }
    }
    for (let i = 0; i < screenshots.length; i++) {
        if (i === 20) break
        if (!isEditPage && i >= 4) add_screen.click()
        groupScreenshots[i].value = screenshots[i].path_full.split("?")[0]
        if (GM_getValue('get_image_res')) {
            new Promise((resolve, reject) => {
                let img = new Image()
                img.src = screenshots[i].path_full.split("?")[0]
                img.onload = () => resolve(img)
                img.onerror = () => reject()
            }).then(img => {
                groupScreenshots[i].parentElement.style.position = 'relative'
                groupScreenshots[i].insertAdjacentHTML('afterend',
                    `<span style="position:absolute;top: -115%;right: 9.5%;">${img.naturalWidth}x${img.naturalHeight}</span>`)
            })
        }
    }

    const ratings = data.ratings
    const ratingInput = document.getElementById('Rating')

    if (ratings) {
        const ratingMap = new Map([
            ['pegi', new Map([
                ['3', 1],
                ['7', 3],
                ['12', 5],
                ['16', 7],
                ['18', 9],
            ])],
            ['esrb', new Map([
                ['e', 1],
                ['e10', 3],
                ['t', 5],
                ['m', 7],
                ['ao', 9],
            ])],
            ['nzoflc', new Map([
                ['g', 1],
                ['r13', 7],
                ['r16', 7],
                ['r18', 9],
            ])],
            ['cero', new Map([
                ['a', 1],
                ['b', 3],
                ['c', 5],
                ['d', 7],
                ['z', 9],
            ])],
            ['csrr', new Map([ // gsrr?
                ['g', 1],
                ['p', 3],
                ['pg12', 5],
                ['pg15', 7],
                ['r', 9],
            ])],
            ['usk', new Map([
                ['0', 1],
                ['6', 3],
                ['12', 5],
                ['16', 7],
                ['18', 9],
            ])],
            ['steam_germany', new Map([
                ['0', 1],
                ['6', 3],
                ['10', 5],
                ['12', 5],
                ['16', 7],
                ['18', 9],
            ])],
        ])

        for (const [board, map] of ratingMap) {
            if (Object.hasOwn(ratings, board)) {
                ratingInput.value = map.get(ratings[board].rating)
                ratingInput.closest('tr').firstElementChild
                    .insertAdjacentHTML('beforeend', `<span style="color: #d6c9b6;display: block;">Source: ${board}</span>`)
                break
            }
        }
    }

    if (!ratings || !ratingInput.value)
        ratingInput.value = 13

    if (isEditPage) {
        $("input[name='year']").val(year)
        $("input[name='name']").val(data.name)
        if ($("#trailer~a").attr("href").includes("Linux")) {
            platform = "Linux"
        } else if ($("#trailer~a").attr("href").includes("Mac")) {
            platform = "Mac"
        }
    } else {
        const parseSteamLanguage = unsafeWindow?.GetLanguagesFromSteam?.parseSteamLanguage // from Get Languages From Steam script
        if (parseSteamLanguage && GM_getValue('get_languages') && !document.getElementById('empty_group').checked) {
            parseSteamLanguage(data.supported_languages)
        }

        $("#title").val(data.name)
        $("#gameswebsiteuri").val(data.website)
        $("#year").val(year)
    }


    const coverField = $("input[name='image']")
    coverField.val(data.header_image.split("?")[0])
    const big_cover = "https://steamcdn-a.akamaihd.net/steam/apps/" + steamIdInput.value + "/library_600x900_2x.jpg"
    GM_xmlhttpRequest({
        method: "GET",
        url: big_cover,
        responseType: "json",
        onload: function (response) {
            if (response.status === 200) {
                coverField.val(big_cover)
                return
            }
            coverField.after(`<span style="color:yellow;">
Big cover could not be found. Until a way to retrieve it for newer games is found, take the <a href=https://steamdb.info/app/${steamIdInput.value}/info target="_blank">library_capsule_2x link</a>
</span>`)
        }
    })
    // $(desc_field).val(fix_emptylines($(desc_field).val()))
    if (data.metacritic) {
        $("#meta").val(data.metacritic.score)
        $("#metauri").val(data.metacritic.url.split("?")[0] + "/critic-reviews")
    }
    if (data.hasOwnProperty('movies')) {
        $("#trailer").val(data.movies[0].webm.max.split("?")[0].replace("http:", "https:"))
    }
}

let steamIdInput

if (isEditPage) {
    $("td.center").parent().after("<tr><td class='label'>Steam ID</td><td><input id='steamid' /></td></tr>")
    steamIdInput = document.getElementById('steamid')
} else {
    steamIdInput = document.getElementById('steamid')
    steamIdInput.type = 'text'
    steamIdInput.size = 20
    steamIdInput.removeAttribute('min')
    steamIdInput.insertAdjacentHTML('afterend',
        '<a href="javascript:;" id="fill_win">Win</a> <a href="javascript:;" id="fill_lin">Lin</a> <a href="javascript:;" id="fill_mac">Mac</a>')
    $('#fill_win').click(function () {
        $("#platform").val("Windows")
    })
    $('#fill_lin').click(function () {
        $("#platform").val("Linux")
    })
    $('#fill_mac').click(function () {
        $("#platform").val("Mac")
    })
}

steamIdInput.onblur = () => {
    if (steamIdInput.value.includes('store.steampowered')) {
        steamIdInput.value = /\d+/.exec(steamIdInput.value)[0]
    }
    GM_xmlhttpRequest({
        method: "GET",
        url: "https://store.steampowered.com/api/appdetails?l=en&appids=" + steamIdInput.value,
        responseType: "json",
        onload: response => fill_form(response.response[steamIdInput.value].data)
    })
    if (location.href.includes('upload.php')) {
        GM_xmlhttpRequest({
            url: `https://store.steampowered.com/app/${steamIdInput.value}`,
            onload: function (res) {
                const page = new DOMParser().parseFromString(res.responseText, "text/html")
                let uploadTags = new Set()

                for (const steamTag of page.querySelectorAll('.glance_tags a')) {
                    const text = steamTag.textContent.trim()
                    if (text === 'Survival Horror') {
                        uploadTags.add('survival')
                        uploadTags.add('horror')
                        continue
                    }
                    if (allowedTags.has(text)) {
                        uploadTags.add(text.toLowerCase()
                            .replace(/sim(?!\w)/, "simulation").replace("simulator", "simulation")
                            .replace(/'/g, '').replace(/&/g, 'and').replace(/-/g, ' ').replace(/ /g, '.'))
                        continue
                    }
                    const t = tagMap.get(text)
                    if (t) {
                        uploadTags.add(t)
                    }
                }
                document.getElementById('tags').value = Array.from(uploadTags).join(', ')
                if (uploadTags.has('adult'))
                    document.getElementById('Rating').value = 9
            }
        })
    }
}