-
-
Save kasuganosoras/9a5bfe88f84aba5135ac308a260fe0f2 to your computer and use it in GitHub Desktop.
GTA5-Mods convert to FiveM resource
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Gta5Mods to FiveM resource tool | |
| // @namespace https://gta5mods.hk416.org/ | |
| // @version 2.1 | |
| // @description A tool that can convert gta5-mods.com mods to FiveM resources. | |
| // @author Akkariin | |
| // @match *://*.gta5-mods.com/* | |
| // @require https://cdn.jsdelivr.net/npm/sweetalert2@11.22.0/dist/sweetalert2.all.min.js | |
| // @grant GM_xmlhttpRequest | |
| // @grant GM_setValue | |
| // @grant GM_getValue | |
| // @grant GM_openInTab | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| // --- Update Configuration --- | |
| const UPDATE_CONFIG = { | |
| checkUrl: "https://cdn.worldofcraft.cn/fivem-converter-update.json", | |
| downloadUrl: "https://gist.github.com/kasuganosoras/9a5bfe88f84aba5135ac308a260fe0f2", | |
| ignoreVersionKey: "g2f_ignored_version" | |
| }; | |
| // --- Configuration --- | |
| const CONFIG = { | |
| apiBaseUrl: "https://convert.cfx.rs", | |
| fileContainerSelector: "#file-container", | |
| downloadButtonHookSelector: ".btn-download", | |
| fivemButtonClass: "downloadFiveMBtn", | |
| localStorageKey: "convertUid_g2f", | |
| pollIntervalMs: 1500, | |
| successMessageResetDelayMs: 3000, | |
| currentVersion: "2.1" | |
| }; | |
| const SCRIPT_TEXTS = { | |
| en: { | |
| buttonText: "<i class='fa fa-download'></i> Convert to FiveM resource", | |
| buttonStatusSubmitting: '<i class="fa fa-spinner fa-spin"></i> Submitting...', | |
| buttonStatusSubmitted: "<i class='fa fa-check'></i> Task submitted, ID: ", | |
| buttonStatusConverting: '<i class="fa fa-circle-o-notch fa-spin"></i> ', | |
| buttonStatusSuccess: "<i class='fa fa-check'></i> Convert finished: ", | |
| buttonStatusRestoring: '<i class="fa fa-spinner fa-spin"></i> Restoring state...', | |
| popupErrorTitle: "Error", | |
| popupSubmitError: "Failed to submit the task to the server.", | |
| popupSubmitErrorWithDetails: "Failed to submit the task: ", | |
| popupStatusFetchError: "Failed to get task status from the server.", | |
| popupConvertError: "Conversion failed: ", | |
| popupParseError: "An error occurred while processing the server response. Please try again.", | |
| // Update UI | |
| updateTitle: "New Version Available", | |
| updateMsg: "New version <b>{v}</b> is available. Update now?", | |
| updateBtnYes: "Update Now", | |
| updateBtnNo: "Cancel", | |
| updateBtnIgnore: "Don't Remind" | |
| }, | |
| zh: { | |
| buttonText: "<i class='fa fa-download'></i> 转换为 FiveM 资源", | |
| buttonStatusSubmitting: '<i class="fa fa-spinner fa-spin"></i> 提交中...', | |
| buttonStatusSubmitted: "<i class='fa fa-check'></i> 任务已提交,ID: ", | |
| buttonStatusConverting: '<i class="fa fa-circle-o-notch fa-spin"></i> ', | |
| buttonStatusSuccess: "<i class='fa fa-check'></i> 转换完成: ", | |
| buttonStatusRestoring: '<i class="fa fa-spinner fa-spin"></i> 恢复状态中...', | |
| popupErrorTitle: "错误", | |
| popupSubmitError: "提交任务到服务器失败。", | |
| popupSubmitErrorWithDetails: "提交任务失败: ", | |
| popupStatusFetchError: "从服务器获取任务状态失败。", | |
| popupConvertError: "转换失败: ", | |
| popupParseError: "处理服务器响应时发生错误,请重试。", | |
| // Update UI | |
| updateTitle: "发现新版本", | |
| updateMsg: "检测到新版本 <b>{v}</b>,是否立即更新?", | |
| updateBtnYes: "立即更新", | |
| updateBtnNo: "取消", | |
| updateBtnIgnore: "不再提醒此版本" | |
| } | |
| }; | |
| // --- Initialization --- | |
| if (!document.querySelector(CONFIG.fileContainerSelector)) { | |
| console.log("Gta5Mods to FiveM tool: #file-container not found. Script will not run on this page."); | |
| return; | |
| } | |
| let currentLang = 'en'; | |
| if (window.location.hostname === "zh.gta5-mods.com") { | |
| currentLang = 'zh'; | |
| } | |
| const TEXTS = SCRIPT_TEXTS[currentLang]; | |
| const API_LANG_PARAM = currentLang === 'zh' ? 'zh-CN' : 'en'; | |
| const SUBMIT_ENDPOINT = CONFIG.apiBaseUrl + "/api/convert"; | |
| const QUERY_ENDPOINT = CONFIG.apiBaseUrl + "/api/query"; | |
| let $fivemButton; | |
| // --- Version Compare Helper --- | |
| function compareVersions(v1, v2) { | |
| const v1parts = v1.split('.').map(Number); | |
| const v2parts = v2.split('.').map(Number); | |
| for (let i = 0; i < Math.max(v1parts.length, v2parts.length); ++i) { | |
| const val1 = v1parts[i] || 0; | |
| const val2 = v2parts[i] || 0; | |
| if (val1 < val2) return -1; | |
| if (val1 > val2) return 1; | |
| } | |
| return 0; | |
| } | |
| // --- Update Check Logic --- | |
| function checkForUpdate() { | |
| if (UPDATE_CONFIG.checkUrl == "") return; | |
| GM_xmlhttpRequest({ | |
| method: 'GET', | |
| url: UPDATE_CONFIG.checkUrl + "?t=" + new Date().getTime(), // prevent cache | |
| onload: function(response) { | |
| if (response.status >= 200 && response.status < 300) { | |
| try { | |
| const json = JSON.parse(response.responseText); | |
| const latestVer = json.version; | |
| if (latestVer && compareVersions(latestVer, CONFIG.currentVersion) > 0) { | |
| // Check if ignored | |
| const ignoredVer = localStorage.getItem(UPDATE_CONFIG.ignoreVersionKey); | |
| if (ignoredVer === latestVer) { | |
| console.log("Gta5Mods to FiveM tool: User ignored update to " + latestVer); | |
| return; | |
| } | |
| // Show Update Popup | |
| Swal.fire({ | |
| title: TEXTS.updateTitle, | |
| html: TEXTS.updateMsg.replace('{v}', latestVer), | |
| icon: 'info', | |
| showCancelButton: true, | |
| showDenyButton: true, | |
| confirmButtonText: TEXTS.updateBtnYes, | |
| denyButtonText: TEXTS.updateBtnIgnore, | |
| cancelButtonText: TEXTS.updateBtnNo, | |
| reverseButtons: true | |
| }).then((result) => { | |
| if (result.isConfirmed) { | |
| // Open download link | |
| window.open(UPDATE_CONFIG.downloadUrl, '_blank'); | |
| } else if (result.isDenied) { | |
| // Ignore this version | |
| localStorage.setItem(UPDATE_CONFIG.ignoreVersionKey, latestVer); | |
| } | |
| }); | |
| } | |
| } catch (e) { | |
| console.warn("Gta5Mods to FiveM tool: Failed to parse update info.", e); | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // --- Helper Functions --- | |
| function resetButtonToDefaultState() { | |
| updateButtonUI(TEXTS.buttonText, false); | |
| if ($fivemButton) { | |
| $fivemButton.off('click').on('click', handleSubmitConversion); | |
| } | |
| localStorage.removeItem(CONFIG.localStorageKey); | |
| } | |
| function showErrorAlert(message) { | |
| Swal.fire({ | |
| icon: 'error', | |
| title: TEXTS.popupErrorTitle, | |
| text: message, | |
| }).then(() => { | |
| resetButtonToDefaultState(); | |
| }); | |
| } | |
| function updateButtonUI(htmlContent, disabled) { | |
| if ($fivemButton) { | |
| $fivemButton.html(htmlContent); | |
| if (disabled) { | |
| $fivemButton.attr('disabled', 'disabled'); | |
| } else { | |
| $fivemButton.removeAttr('disabled'); | |
| } | |
| } | |
| } | |
| function downloadFile(url) { | |
| window.location.href = url; | |
| } | |
| // --- Core Logic --- | |
| function pollConversionStatus(uuid) { | |
| GM_xmlhttpRequest({ | |
| method: 'POST', | |
| url: QUERY_ENDPOINT, | |
| data: `uuid=${encodeURIComponent(uuid)}&lang=${API_LANG_PARAM}`, | |
| headers: { | |
| "Content-Type": "application/x-www-form-urlencoded" | |
| }, | |
| onload: function(response) { | |
| if (response.status >= 200 && response.status < 300) { | |
| try { | |
| const json = JSON.parse(response.responseText); | |
| if (json.status === 200) { | |
| updateButtonUI(TEXTS.buttonStatusSuccess + json.name, true); | |
| localStorage.removeItem(CONFIG.localStorageKey); | |
| const downloadUrl = CONFIG.apiBaseUrl + "/" + json.file; | |
| downloadFile(downloadUrl); | |
| setTimeout(() => { | |
| resetButtonToDefaultState(); | |
| }, CONFIG.successMessageResetDelayMs); | |
| } else if (json.status === 101) { | |
| updateButtonUI(TEXTS.buttonStatusConverting + json.message, true); | |
| setTimeout(() => pollConversionStatus(uuid), CONFIG.pollIntervalMs); | |
| } else { | |
| showErrorAlert(TEXTS.popupConvertError + (json.message || 'Unknown API error')); | |
| } | |
| } catch (e) { | |
| console.error("Error parsing polling response:", e, response.responseText); | |
| showErrorAlert(TEXTS.popupParseError); | |
| } | |
| } else { | |
| console.error("Polling request failed with HTTP status:", response.status, response.responseText); | |
| if (response.status === 404) { | |
| showErrorAlert("Task not found or expired."); | |
| } else { | |
| showErrorAlert(TEXTS.popupStatusFetchError + ` (Status: ${response.status})`); | |
| } | |
| } | |
| }, | |
| onerror: function(response) { | |
| console.error("Polling request network error:", response); | |
| showErrorAlert(TEXTS.popupStatusFetchError); | |
| } | |
| }); | |
| } | |
| function handleSubmitConversion() { | |
| const pageUrl = window.location.href.split('#')[0]; | |
| if (pageUrl === "") return; | |
| updateButtonUI(TEXTS.buttonStatusSubmitting, true); | |
| GM_xmlhttpRequest({ | |
| method: 'POST', | |
| url: SUBMIT_ENDPOINT, | |
| data: `url=${encodeURIComponent(pageUrl)}&lang=${API_LANG_PARAM}`, | |
| headers: { | |
| "Content-Type": "application/x-www-form-urlencoded" | |
| }, | |
| onload: function(response) { | |
| if (response.status >= 200 && response.status < 300) { | |
| try { | |
| const json = JSON.parse(response.responseText); | |
| if (json.status === 200) { | |
| updateButtonUI(TEXTS.buttonStatusSubmitted + json.message, true); | |
| localStorage.setItem(CONFIG.localStorageKey, json.message); | |
| pollConversionStatus(json.message); | |
| } else { | |
| showErrorAlert(TEXTS.popupSubmitErrorWithDetails + (json.message || 'No details provided.')); | |
| } | |
| } catch (e) { | |
| console.error("Error parsing submission response:", e, response.responseText); | |
| showErrorAlert(TEXTS.popupParseError); | |
| } | |
| } else { | |
| console.error("Submission request failed with HTTP status:", response.status, response.responseText); | |
| let apiErrorMessage = `Server responded with status ${response.status}.`; | |
| try { | |
| const json = JSON.parse(response.responseText); | |
| if (json && json.message) { | |
| apiErrorMessage = json.message; | |
| } | |
| } catch (e) { /* Ignore */ } | |
| showErrorAlert(TEXTS.popupSubmitErrorWithDetails + apiErrorMessage); | |
| } | |
| }, | |
| onerror: function(response) { | |
| console.error("Submission request network error:", response); | |
| showErrorAlert(TEXTS.popupSubmitError); | |
| } | |
| }); | |
| } | |
| // --- UI Setup and Initialization --- | |
| function initialize() { | |
| const styleElement = document.createElement('style'); | |
| styleElement.textContent = ` | |
| .${CONFIG.fivemButtonClass} { | |
| width: 100%; | |
| margin-bottom: 10px; | |
| } | |
| .swal2-popup { | |
| font-size: 1.2em !important; | |
| } | |
| `; | |
| document.head.appendChild(styleElement); | |
| if (typeof $ === 'undefined') { | |
| console.error("Gta5Mods to FiveM tool: jQuery is not available. The script might not work correctly."); | |
| return; | |
| } | |
| $fivemButton = $("<button class='btn btn-default " + CONFIG.fivemButtonClass + "'></button>"); | |
| const $downloadButtonHook = $(CONFIG.downloadButtonHookSelector); | |
| if ($downloadButtonHook.length > 0) { | |
| if ($downloadButtonHook.css('display') === 'inline' || $downloadButtonHook.css('display') === 'inline-block') { | |
| $fivemButton.wrap('<p></p>').parent().insertAfter($downloadButtonHook); | |
| } else { | |
| $fivemButton.insertAfter($downloadButtonHook); | |
| } | |
| } else { | |
| console.warn("Gta5Mods to FiveM tool: Download button hook selector not found. Button not added."); | |
| return; | |
| } | |
| const existingUuid = localStorage.getItem(CONFIG.localStorageKey); | |
| if (existingUuid) { | |
| updateButtonUI(TEXTS.buttonStatusRestoring, true); | |
| pollConversionStatus(existingUuid); | |
| } else { | |
| resetButtonToDefaultState(); | |
| } | |
| // Check for updates after UI is ready | |
| setTimeout(checkForUpdate, 1000); | |
| } | |
| if (typeof $ === 'function') { | |
| $(document).ready(initialize); | |
| } else { | |
| window.addEventListener('DOMContentLoaded', initialize); | |
| } | |
| })(); |
Wie installiert man dieses script? Frage an alle da draußen!
how to install
Sick! Will really come in useful.
Wie installiert man dieses script? Frage an alle da draußen!
Du installierst im browser die erweiterung Tampermonkey und drückst dort auf Neues Userscript erstellen dort fügst du den Code ein und speicherst es.
Author
Refactored the script to adapt to the new converter website API and support multiple languages.
Refactored the script to adapt to the new converter website API and support multiple languages.
An automatic push on Tempermonkey would have been great :D I was wondering why it was broken until I checked on this GIT :D
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
.