Greasy Fork is available in English.

Nexus No Wait

Download from Nexusmods.com without wait and redirect (support Manual/Vortex/MO2/NMM)

  1. // ==UserScript==
  2. // @name Nexus No Wait
  3. // @description Download from Nexusmods.com without wait and redirect (support Manual/Vortex/MO2/NMM)
  4. // @namespace NexusNoWait
  5. // @include https://www.nexusmods.com/*/mods/*
  6. // @run-at document-idle
  7. // @grant GM.xmlHttpRequest
  8. // @grant GM_xmlhttpRequest
  9. // @version 2.01
  10. // ==/UserScript==
  11.  
  12. /* jshint esversion: 6 */
  13.  
  14. (function () {
  15. let ajaxRequestRaw;
  16.  
  17. if (typeof(GM_xmlhttpRequest) !== "undefined") {
  18. ajaxRequestRaw = GM_xmlhttpRequest;
  19. } else if (typeof(GM) !== "undefined" && typeof(GM.xmlHttpRequest) !== "undefined") {
  20. ajaxRequestRaw = GM.xmlHttpRequest;
  21. }
  22.  
  23. function ajaxRequest(obj) {
  24. if (!ajaxRequestRaw) {
  25. console.log("Unable to request", obj);
  26.  
  27. return;
  28. }
  29.  
  30. const requestObj = {
  31. url: obj.url,
  32. method: obj.type,
  33. data: obj.data,
  34. headers: obj.headers
  35. };
  36.  
  37. let loadCb = function (result) {
  38. if (result.readyState !== 4) {
  39. return;
  40. }
  41.  
  42. if (result.status !== 200) {
  43. return obj.error(result);
  44. }
  45.  
  46. return obj.success(result.responseText);
  47. };
  48.  
  49. requestObj.onload = loadCb;
  50. requestObj.onerror = loadCb;
  51.  
  52. ajaxRequestRaw(requestObj);
  53. }
  54.  
  55. function btnError(button) {
  56. button.style.color = "red";
  57. button.innerText = "ERROR";
  58. }
  59.  
  60. function btnSuccess(button) {
  61. button.style.color = "green";
  62. button.innerText = "LOADING";
  63. }
  64.  
  65. function btnWait(button) {
  66. button.style.color = "yellow";
  67. button.innerText = "WAIT";
  68. }
  69.  
  70. function clickListener(event) {
  71. const href = this.href || window.location.href;
  72. const params = new URL(href).searchParams;
  73.  
  74. if (params.get("file_id")) {
  75. let button = event;
  76. if (this.href) {
  77. button = this;
  78. event.preventDefault();
  79. }
  80. btnWait(button);
  81.  
  82. const section = document.getElementById("section");
  83. const gameId = section ? section.dataset.gameId : this.current_game_id;
  84.  
  85. let fileId = params.get("file_id");
  86. if (!fileId) {
  87. fileId = params.get("id");
  88. }
  89.  
  90. if (!params.get("nmm")) {
  91. ajaxRequest({
  92. type: "POST",
  93. url: "/Core/Libs/Common/Managers/Downloads?GenerateDownloadUrl",
  94. data: "fid=" + fileId + "&game_id=" + gameId,
  95. headers: {
  96. Origin: "https://www.nexusmods.com",
  97. Referer: href,
  98. "Sec-Fetch-Site": "same-origin",
  99. "X-Requested-With": "XMLHttpRequest",
  100. "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
  101. },
  102. success(data) {
  103. if (data) {
  104. try {
  105. data = JSON.parse(data);
  106.  
  107. if (data.url) {
  108. btnSuccess(button);
  109. document.location.href = data.url;
  110. }
  111. } catch (e) {
  112. console.error(e);
  113. }
  114. }
  115. },
  116. error() {
  117. btnError(button);
  118. }
  119. });
  120. } else {
  121. ajaxRequest({
  122. type: "GET",
  123. url: href,
  124. headers: {
  125. Origin: "https://www.nexusmods.com",
  126. Referer: document.location.href,
  127. "Sec-Fetch-Site": "same-origin",
  128. "X-Requested-With": "XMLHttpRequest"
  129. },
  130. success(data) {
  131. if (data) {
  132. const xml = new DOMParser().parseFromString(data, "text/html");
  133. const slow = xml.getElementById("slowDownloadButton");
  134. const downloadUrl = slow.getAttribute("data-download-url");
  135. btnSuccess(button);
  136. document.location.href = downloadUrl;
  137. }
  138. },
  139. error(ajaxContext) {
  140. console.error(ajaxContext.responseText);
  141. btnError(button);
  142. }
  143. });
  144. }
  145.  
  146. const popup = this.parentNode;
  147. if (popup && popup.classList.contains("popup")) {
  148. popup.getElementsByTagName("button")[0].click();
  149. const popupButton = document.getElementById("popup" + fileId);
  150. if (popupButton) {
  151. btnSuccess(popupButton);
  152. }
  153. }
  154. } else if (/ModRequirementsPopUp/.test(href)) {
  155. const fileId = params.get("id");
  156.  
  157. if (fileId) {
  158. this.setAttribute("id", "popup" + fileId);
  159. }
  160. }
  161. }
  162.  
  163. function addClickListener(el) {
  164. el.addEventListener("click", clickListener, true);
  165. }
  166.  
  167. function addClickListeners(els) {
  168. for (let i = 0; i < els.length; i++) {
  169. addClickListener(els[i]);
  170. }
  171. }
  172.  
  173. function autoStartFileLink() {
  174. if (/file_id=/.test(window.location.href)) {
  175. clickListener(document.getElementById("slowDownloadButton"));
  176. }
  177. }
  178.  
  179. function archivedFile() {
  180. if (/[?&]category=archived/.test(window.location.href)) {
  181. const fileIds = document.getElementsByClassName("file-expander-header");
  182. const elements = document.getElementsByClassName("accordion-downloads");
  183. const path = `${location.protocol}//${location.host}${location.pathname}`;
  184. for (let i = 0; i < elements.length; i++) {
  185. elements[i].innerHTML = ''
  186. + `<li><a class="btn inline-flex" href="${path}?tab=files&amp;file_id=${fileIds[i].getAttribute("data-id")}&amp;nmm=1" tabindex="0">`
  187. + "<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>"
  188. + "</a></li><li></li><li>"
  189. + `<li><a class="btn inline-flex" href="${path}?tab=files&amp;file_id=${fileIds[i].getAttribute("data-id")}" tabindex="0">`
  190. + "<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>"
  191. + "</a></li>";
  192. }
  193. }
  194. }
  195.  
  196. archivedFile();
  197. addClickListeners(document.querySelectorAll("a.btn"));
  198. autoStartFileLink();
  199.  
  200. let observer = new MutationObserver(((mutations, observer) => {
  201. for (let i = 0; i < mutations.length; i++) {
  202. if (mutations[i].addedNodes) {
  203. for (let x = 0; x < mutations[i].addedNodes.length; x++) {
  204. const node = mutations[i].addedNodes[x];
  205.  
  206. if (node.tagName === "A" && node.classList.contains("btn")) {
  207. addClickListener(node);
  208. } else if (node.children && node.children.length > 0) {
  209. addClickListeners(node.querySelectorAll("a.btn"));
  210. }
  211. }
  212. }
  213. }
  214. }));
  215. observer.observe(document, {childList: true, subtree: true});
  216. })();