Nexus One Click Download

Скачивание с нексуса в 1 клик

  1. // ==UserScript==
  2. // @name Nexus One Click Download
  3. // @description:ru-RU Скачивание с нексуса в 1 клик
  4. // @description:en-EN Nexus One Click Download
  5. // @namespace Violentmonkey Scripts
  6. // @match https://www.nexusmods.com/*/mods/*
  7. // @grant GM.xmlHttpRequest
  8. // @version 1.1
  9. // @license MIT
  10. // @description Скачивание с нексуса в 1 клик
  11. // ==/UserScript==
  12.  
  13. (async function () {
  14. const lang = navigator.language.startsWith('ru') ? 'ru' : 'en';
  15. const messages = {
  16. ru: { error: 'ОШИБКА', loading: 'ЗАГРУЗКА...', wait: 'ОЖИДАНИЕ...', success: 'УСПЕШНО', alert: 'Ошибка при загрузке:\n' },
  17. en: { error: 'ERROR', loading: 'LOADING...', wait: 'WAITING...', success: 'SUCCESS', alert: 'Error occurred while downloading:\n' }
  18. };
  19.  
  20. const ajax = async ({ url, method = 'GET', data = null, headers = {} }) => {
  21. try {
  22. const res = await fetch(url, { method, body: data, headers: { ...headers, 'X-Requested-With': 'XMLHttpRequest' } });
  23. if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
  24. return await res.text();
  25. } catch (e) {
  26. console.error(e);
  27. throw e;
  28. }
  29. };
  30.  
  31. const setButtonState = (btn, state) => {
  32. btn.style.backgroundColor = { error: 'red', loading: 'black', wait: 'yellow', success: 'green' }[state];
  33. btn.innerText = messages[lang][state];
  34. };
  35.  
  36. const handleClick = async (e) => {
  37. const href = e.currentTarget.href || window.location.href;
  38. const params = new URL(href).searchParams;
  39.  
  40. if (!params.get('file_id')) return;
  41.  
  42. e.preventDefault();
  43. const btn = e.currentTarget;
  44. setButtonState(btn, 'wait');
  45.  
  46. try {
  47. const gameId = document.getElementById('section')?.dataset.gameId || window.current_game_id;
  48. const fileId = params.get('file_id') || params.get('id');
  49. const nmmParam = params.get('nmm');
  50. if (!fileId || !gameId) throw new Error('Missing parameters');
  51.  
  52. let downloadUrl;
  53. if (!nmmParam) {
  54. const resText = await ajax({
  55. url: '/Core/Libs/Common/Managers/Downloads?GenerateDownloadUrl',
  56. method: 'POST',
  57. data: `fid=${fileId}&game_id=${gameId}`,
  58. headers: { Origin: href, Referer: href, 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }
  59. });
  60. downloadUrl = JSON.parse(resText)?.url;
  61. } else {
  62. const resText = await ajax({ url: href, headers: { Origin: href, Referer: document.location.href } });
  63. downloadUrl = new DOMParser().parseFromString(resText, 'text/html').querySelector('#slowDownloadButton')?.dataset.downloadUrl;
  64. }
  65.  
  66. if (downloadUrl) {
  67. setButtonState(btn, 'loading');
  68. setTimeout(() => (window.location.href = downloadUrl), 1000);
  69. setButtonState(btn, 'success');
  70. } else throw new Error('Download URL not found');
  71. } catch (err) {
  72. setButtonState(btn, 'error');
  73. alert(messages[lang].alert + err.message);
  74. }
  75. };
  76.  
  77. const addListenersToButtons = () => {
  78. document.querySelectorAll('a.btn:not([data-listener])').forEach((btn) => {
  79. btn.addEventListener('click', handleClick);
  80. btn.setAttribute('data-listener', true);
  81. });
  82. };
  83.  
  84. new MutationObserver(() => addListenersToButtons()).observe(document.body, { childList: true, subtree: true });
  85. addListenersToButtons();
  86. })();

QingJ © 2025

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