您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically downloads from Nexus Mods without wait times. Supports Manual/Vortex/MO2/NMM downloads, auto-clicks slow download, and handles archived files.
// ==UserScript== // @name Nexus Mods Enhanced Auto Download // @namespace http://tampermonkey.net/ // @version 1.2 // @description Automatically downloads from Nexus Mods without wait times. Supports Manual/Vortex/MO2/NMM downloads, auto-clicks slow download, and handles archived files. // @author Kristijan1001 // @match https://www.nexusmods.com/* // @match https://nexusmods.com/* // @icon https://www.nexusmods.com/favicon.ico // @run-at document-idle // @grant GM.xmlHttpRequest // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== /* jshint esversion: 6 */ (function() { 'use strict'; // ========== AJAX REQUEST HANDLER ========== let ajaxRequestRaw; if (typeof(GM_xmlhttpRequest) !== "undefined") { ajaxRequestRaw = GM_xmlhttpRequest; } else if (typeof(GM) !== "undefined" && typeof(GM.xmlHttpRequest) !== "undefined") { ajaxRequestRaw = GM.xmlHttpRequest; } function ajaxRequest(obj) { if (!ajaxRequestRaw) { console.log("[Nexus Enhanced] Unable to request", obj); return; } const requestObj = { url: obj.url, method: obj.type, data: obj.data, headers: obj.headers }; let loadCb = function(result) { if (result.readyState !== 4) { return; } if (result.status !== 200) { return obj.error(result); } return obj.success(result.responseText); }; requestObj.onload = loadCb; requestObj.onerror = loadCb; ajaxRequestRaw(requestObj); } // ========== BUTTON STATE HELPERS ========== function btnError(button) { button.style.color = "red"; button.innerText = "ERROR"; } function btnSuccess(button) { button.style.color = "green"; button.innerText = "LOADING"; } function btnWait(button) { button.style.color = "yellow"; button.innerText = "WAIT"; } // ========== NO WAIT DOWNLOAD HANDLER ========== function clickListener(event) { const href = this.href || window.location.href; const params = new URL(href).searchParams; if (params.get("file_id")) { let button = event; if (this.href) { button = this; event.preventDefault(); } btnWait(button); const section = document.getElementById("section"); const gameId = section ? section.dataset.gameId : this.current_game_id; let fileId = params.get("file_id"); if (!fileId) { fileId = params.get("id"); } if (!params.get("nmm")) { ajaxRequest({ type: "POST", url: "/Core/Libs/Common/Managers/Downloads?GenerateDownloadUrl", data: "fid=" + fileId + "&game_id=" + gameId, headers: { Origin: "https://www.nexusmods.com", Referer: href, "Sec-Fetch-Site": "same-origin", "X-Requested-With": "XMLHttpRequest", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }, success(data) { if (data) { try { data = JSON.parse(data); if (data.url) { btnSuccess(button); document.location.href = data.url; } } catch (e) { console.error('[Nexus Enhanced] Error parsing response:', e); } } }, error() { btnError(button); } }); } else { ajaxRequest({ type: "GET", url: href, headers: { Origin: "https://www.nexusmods.com", Referer: document.location.href, "Sec-Fetch-Site": "same-origin", "X-Requested-With": "XMLHttpRequest" }, success(data) { if (data) { const text = String(data); let downloadUrlMatch = text.match(/const downloadUrl = '([^']+)'/); if (!downloadUrlMatch) { downloadUrlMatch = text.match(/id="slowDownloadButton".*?data-download-url="([^"]+)"/); } const downloadUrl = downloadUrlMatch ? downloadUrlMatch[1].replaceAll('&', '&') : null; if (downloadUrl) { btnSuccess(button); document.location.href = downloadUrl; } else { btnError(button); } } }, error(ajaxContext) { console.error('[Nexus Enhanced] Ajax error:', ajaxContext.responseText); btnError(button); } }); } const popup = this.parentNode; if (popup && popup.classList.contains("popup")) { popup.getElementsByTagName("button")[0].click(); const popupButton = document.getElementById("popup" + fileId); if (popupButton) { btnSuccess(popupButton); } } } else if (/ModRequirementsPopUp/.test(href)) { const fileId = params.get("id"); if (fileId) { this.setAttribute("id", "popup" + fileId); } } } function addClickListener(el) { el.addEventListener("click", clickListener, true); } function addClickListeners(els) { for (let i = 0; i < els.length; i++) { addClickListener(els[i]); } } // ========== AUTO START FILE LINK ========== function getDonwloadButton(doc) { let button = doc.getElementById("slowDownloadButton"); if (button) { return button; } button = doc.getElementById('upsell-cards'); if (button) { const newBtn = button.lastChild?.getElementsByTagName('button'); return newBtn ? newBtn[0] : button.lastChild; } button = doc.getElementById("startDownloadButton"); if (button) { return button; } return null; } function autoStartFileLink() { if (/file_id=/.test(window.location.href)) { setTimeout(() => { let button = getDonwloadButton(document); if (!button) { const sr = document.getElementsByTagName("mod-file-download"); if (sr && sr[0]?.shadowRoot) { button = getDonwloadButton(sr[0].shadowRoot); } } if (button) { console.log('[Nexus Enhanced] Auto-starting file link download'); clickListener.call(button, button); } }, 1000); } } // ========== AUTO SLOW DOWNLOAD CLICKER ========== let autoClickAttempts = 0; const maxAutoClickAttempts = 60; function clickSlowDownload() { autoClickAttempts++; // Method 1: Look for slow download button by ID const slowButton = document.getElementById('slowDownloadButton'); if (slowButton) { console.log('[Nexus Enhanced] Found slowDownloadButton by ID, clicking...'); slowButton.click(); return true; } // Method 2: Look for the mod-file-download component const modComponent = document.querySelector('mod-file-download'); if (modComponent) { // Try to access shadow DOM if available try { if (modComponent.shadowRoot) { const slowButtons = modComponent.shadowRoot.querySelectorAll('button, [role="button"], a'); for (const btn of slowButtons) { const text = btn.textContent?.toLowerCase() || ''; if (text.includes('slow') && text.includes('download')) { console.log('[Nexus Enhanced] Found slow download button in shadow DOM, clicking...'); btn.click(); return true; } } } } catch (e) { console.log('[Nexus Enhanced] Shadow DOM access failed:', e); } } // Method 3: Look for any button with "slow download" text const allButtons = document.querySelectorAll('button, a, [role="button"]'); for (const btn of allButtons) { const text = btn.textContent?.toLowerCase() || ''; if (text.includes('slow') && text.includes('download')) { console.log('[Nexus Enhanced] Found slow download button via text search, clicking...'); btn.click(); return true; } } // Method 4: Look specifically for the span structure const slowDownloadSpans = document.querySelectorAll('span'); for (const span of slowDownloadSpans) { const trimmedText = span.textContent?.trim().toLowerCase() || ''; if (trimmedText === 'slow download') { const button = span.closest('button, a, [role="button"]'); if (button) { console.log('[Nexus Enhanced] Found slow download via span structure, clicking...'); button.click(); return true; } } } return false; } function tryAutoClick() { if (autoClickAttempts >= maxAutoClickAttempts) { console.log('[Nexus Enhanced] Max auto-click attempts reached'); return; } if (clickSlowDownload()) { console.log('[Nexus Enhanced] Successfully auto-clicked slow download button!'); return; } setTimeout(tryAutoClick, 1000); } // ========== ARCHIVED FILE HANDLER ========== function archivedFile() { if (/[?&]category=archived/.test(window.location.href)) { console.log('[Nexus Enhanced] Processing archived files'); const fileIds = document.getElementsByClassName("file-expander-header"); const elements = document.getElementsByClassName("accordion-downloads"); const path = `${location.protocol}//${location.host}${location.pathname}`; for (let i = 0; i < elements.length; i++) { elements[i].innerHTML = '' + `<li><a class="btn inline-flex" href="${path}?tab=files&file_id=${fileIds[i].getAttribute("data-id")}&nmm=1" tabindex="0">` + '<svg title="" class="icon icon-nmm"><use xlink:href="https://www.nexusmods.com/assets/images/icons/icons.svg#icon-nmm"></use></svg> <span class="flex-label">Mod manager download</span>' + "</a></li><li></li><li>" + `<li><a class="btn inline-flex" href="${path}?tab=files&file_id=${fileIds[i].getAttribute("data-id")}" tabindex="0">` + '<svg title="" class="icon icon-manual"><use xlink:href="https://www.nexusmods.com/assets/images/icons/icons.svg#icon-manual"></use></svg> <span class="flex-label">Manual download</span>' + "</a></li>"; } } } // ========== INITIALIZATION ========== function initializeAutoClick() { if (window.location.href.includes('?tab=files') || window.location.href.includes('/files/') || window.location.href.includes('file_id=') || document.querySelector('mod-file-download')) { console.log('[Nexus Enhanced] Starting auto-click functionality...'); setTimeout(tryAutoClick, 1000); const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.addedNodes.length > 0) { for (const node of mutation.addedNodes) { if (node.tagName === 'MOD-FILE-DOWNLOAD' || (node.querySelector && node.querySelector('mod-file-download'))) { console.log('[Nexus Enhanced] Detected mod-file-download component added to DOM'); setTimeout(tryAutoClick, 500); break; } } } } }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => observer.disconnect(), maxAutoClickAttempts * 1000); } } function initialize() { console.log('[Nexus Enhanced] Script initialized v1.1'); // Handle archived files archivedFile(); // Add click listeners to existing buttons addClickListeners(document.querySelectorAll("a.btn")); // Auto-start file link if on download page autoStartFileLink(); // Initialize auto-click for slow download initializeAutoClick(); // Observe for dynamically added buttons let observer = new MutationObserver((mutations) => { for (let i = 0; i < mutations.length; i++) { if (mutations[i].addedNodes) { for (let x = 0; x < mutations[i].addedNodes.length; x++) { const node = mutations[i].addedNodes[x]; if (node.tagName === "A" && node.classList.contains("btn")) { addClickListener(node); } else if (node.children && node.children.length > 0) { addClickListeners(node.querySelectorAll("a.btn")); } } } } }); observer.observe(document, { childList: true, subtree: true }); } // Start when page is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { initialize(); } })();