您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Overrides the "Manual" button on NexusMods mod pages to download the file without extra steps.
// ==UserScript== // @name Nexus Mods Fast Download // @namespace https://greasyfork.org/en/users/814191-ctosango // @version 1.1 // @description Overrides the "Manual" button on NexusMods mod pages to download the file without extra steps. // @author Your Name // @match https://www.nexusmods.com/*/mods/* // @icon https://www.google.com/s2/favicons?sz=64&domain=nexusmods.com // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; const spinnerCSS = ` @keyframes spinner { to { transform: rotate(360deg); } } .nm-download-spinner { border: 2px solid rgba(0,0,0,0.2); border-left-color: #000; border-radius: 50%; width: 15px; height: 15px; margin: 0 5px 0 0; animation: spinner 0.6s linear infinite; display: inline-block; vertical-align: middle; } .nm-download-error { color: red; font-size: 0.9em; position: absolute; top: -20px; text-wrap: nowrap; right: 0; } @media (max-width: 1280px) { .nm-download-error { left: 0; right: unset; } } .modactions.clearfix { position: relative; margin-top: 15px; } `; const style = document.createElement('style'); style.textContent = spinnerCSS; document.head.appendChild(style); function showError(message, manualBtn) { console.error(message); let errorEl = manualBtn.parentNode.querySelector('.nm-download-error'); if (!errorEl) { errorEl = document.createElement('div'); errorEl.className = 'nm-download-error'; manualBtn.parentNode.appendChild(errorEl); } errorEl.textContent = message; } function clearError(manualBtn) { const errorEl = manualBtn.parentNode.querySelector('.nm-download-error'); if (errorEl) { errorEl.remove(); } } function setupManualButton() { const manualLi = document.getElementById('action-manual'); if (!manualLi) return; const manualBtn = manualLi.querySelector('a.btn.inline-flex'); if (!manualBtn) return; if (manualBtn.dataset.downloadOverrideSet === "true") return; observer.disconnect(); if (!manualBtn.dataset.originalHref) { manualBtn.dataset.originalHref = manualBtn.href; } manualBtn.removeAttribute('href'); manualBtn.dataset.downloadOverrideSet = "true"; const svgIcon = manualBtn.querySelector('svg.icon'); if (svgIcon && !manualBtn._originalSvg) { manualBtn._originalSvg = svgIcon.cloneNode(true); } manualBtn.addEventListener('click', async function(e) { e.preventDefault(); e.stopImmediatePropagation(); clearError(manualBtn); let spinner; const currentSvg = manualBtn.querySelector('svg.icon'); if (currentSvg) { spinner = document.createElement('span'); spinner.className = 'nm-download-spinner'; currentSvg.parentNode.replaceChild(spinner, currentSvg); } else { spinner = document.createElement('span'); spinner.className = 'nm-download-spinner'; manualBtn.appendChild(spinner); } const originalUrl = manualBtn.dataset.originalHref; if (!originalUrl) { showError("Error: Original URL not available.", manualBtn); spinner.remove(); return; } let urlObj; try { urlObj = new URL(originalUrl, location.href); } catch (err) { showError("Error parsing the original URL.", manualBtn); spinner.remove(); return; } const fileId = urlObj.searchParams.get('file_id'); if (!fileId) { showError("Error: No file_id found in the URL.", manualBtn); spinner.remove(); window.location.href = originalUrl; return; } const gameId = window.current_game_id; if (!gameId) { showError("Error: Variable current_game_id is unavailable.", manualBtn); spinner.remove(); if (manualBtn._originalSvg) { spinner.parentNode.replaceChild(manualBtn._originalSvg, spinner); } return; } const params = new URLSearchParams(); params.append('fid', fileId); params.append('game_id', gameId); const postUrl = 'https://www.nexusmods.com/Core/Libs/Common/Managers/Downloads?GenerateDownloadUrl='; try { const response = await fetch(postUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }, body: params.toString() }); if (!response.ok) { if (response.status === 403) { showError("Error: Not logged in or access denied.", manualBtn); } else { showError("Error: Failed to get download URL (" + response.statusText + ").", manualBtn); } if (spinner && spinner.parentNode && manualBtn._originalSvg) { spinner.parentNode.replaceChild(manualBtn._originalSvg, spinner); } return; } const data = await response.json(); if (data && data.url) { if (spinner && spinner.parentNode && manualBtn._originalSvg) { spinner.parentNode.replaceChild(manualBtn._originalSvg, spinner); } setTimeout(() => { window.location.href = data.url; }, 100); } else if (data && data.length === 0) { showError("Error: Please login to download mods.", manualBtn); if (spinner && spinner.parentNode && manualBtn._originalSvg) { spinner.parentNode.replaceChild(manualBtn._originalSvg, spinner); } } else { console.log(data); showError("Error: Unexpected response format.", manualBtn); if (spinner && spinner.parentNode && manualBtn._originalSvg) { spinner.parentNode.replaceChild(manualBtn._originalSvg, spinner); } } } catch (err) { showError("Error during POST request: " + err.message, manualBtn); if (spinner && spinner.parentNode && manualBtn._originalSvg) { spinner.parentNode.replaceChild(manualBtn._originalSvg, spinner); } } }, true); observer.observe(document.body, { childList: true, subtree: true }); } const observer = new MutationObserver(() => { setupManualButton(); }); observer.observe(document.body, { childList: true, subtree: true }); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', setupManualButton); } else { setupManualButton(); } })();