Nexus No Wait Sn0rkl Tweaks

Download from Nexusmods.com without wait and redirect (Manual/Vortex/MO2/NMM), Tweaked with extra features.

目前为 2024-11-27 提交的版本,查看 最新版本

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