SkyMods downloader for steam

Download mod via skymods.ru directly from steam workshop

  1. // ==UserScript==
  2. // @name SkyMods downloader for steam
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.6
  5. // @description Download mod via skymods.ru directly from steam workshop
  6. // @author Namkazt ( nam.kazt.91@gmail.com )
  7. // @match https://steamcommunity.com/sharedfiles/filedetails/*
  8. // @match https://steamcommunity.com/workshop/filedetails/*
  9. // @match https://steamcommunity.com/workshop/browse/*
  10. // @match https://steamcommunity.com/workshop/browse/*
  11. // @connect smods.ru
  12. // @connect modsbase.com
  13. // @connect modsbasedl.com
  14. // @connect uploadfiles.eu
  15. // @run-at document-end
  16. // @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js
  17. // @grant GM_xmlhttpRequest
  18. // @grant GM_addStyle
  19. // @grant GM_notification
  20. // @grant GM_download
  21. // @grant GM_setValue
  22. // @grant GM_getValue
  23. // @grant GM_deleteValue
  24. // ==/UserScript==
  25. var DOWNLOAD_BTN =
  26. '<a id="DownloadBtn" class="btn_green_white_innerfade btn_border_2px btn_medium " style="line-height: 20px;transition: all 0.5s ease;height: 20px;background: linear-gradient( to bottom, #ffafbd 35%, #ffc3a0 65%);color: #345f59 !important;position: fixed;left: 10px;bottom: 50vh;display: block;padding: 6px;"> <span id="DownloadTxt" class="subscribeText" style="height: 20px;font-size: 13px;background: linear-gradient( to bottom, #ffafbd 35%, #ffc3a0 65%);color: #345f59 !important;line-height: 20px;text-align: center;padding: 0;font-weight: 700;"> Download </span></a>';
  27. var DOWNLOAD_BTN_MINI =
  28. '<a id="DownloadBtn" class="btn_green_white_innerfade btn_border_2px btn_medium " style="z-index: 1000;line-height: 12px;transition: all 0.5s ease;height: 12px;background: linear-gradient( to bottom, #ffafbd 35%, #ffc3a0 65%);color: #345f59 !important;padding: 6px;position: absolute;margin-top: 5px;margin-left: 5px;"> <span id="DownloadTxt" class="subscribeText" style="height: 12px;font-size: 9px;background: linear-gradient( to bottom, #ffafbd 35%, #ffc3a0 65%);color: #345f59 !important;line-height: 12px;text-align: center;padding: 0;font-weight: 700;"> Download </span></a>';
  29. var LOADING_CSS =
  30. ".lds-ripple { display: inline-block; position: relative; width: 64px; height: 64px; top: -40px; z-index: 999} .lds-ripple div { position: absolute; border: 4px solid #fff; opacity: 1; border-radius: 50%; animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; } .lds-ripple div:nth-child(2) { animation-delay: -0.5s; } @keyframes lds-ripple { 0% { top: 28px; left: 28px; width: 0; height: 0; opacity: 1; } 100% { top: -1px; left: -1px; width: 58px; height: 58px; opacity: 0; } }";
  31. var LOADING_BLOCK = '<div class="lds-ripple"><div></div><div></div></div>';
  32.  
  33. function Download(url) {
  34. document.getElementById("downloadHelper").src = url;
  35. }
  36.  
  37. function init() {
  38. if ($ === undefined) {
  39. $ = JQuery;
  40. console.log("----------- Add $ for JQuery");
  41. }
  42. var downloadHelper = createElementFromHTML(
  43. '<iframe id="downloadHelper" style="display:none;"></iframe>'
  44. );
  45. document.body.appendChild(downloadHelper);
  46.  
  47. if (window.location.href.indexOf("appid=") >= 0) {
  48. console.log("----------- Workshop browser page");
  49. var itemList = document.querySelectorAll(".workshopItemPreviewHolder");
  50.  
  51. for (var item of itemList) {
  52. var itemDownloadId = item.id.replace("sharedfile_", "");
  53. var btnNode = createElementFromHTML(DOWNLOAD_BTN_MINI);
  54.  
  55. searchForMod(itemDownloadId,
  56. (function() {
  57. var workshopId = itemDownloadId;
  58. var btn = btnNode;
  59. var textNode = btn.querySelector("#DownloadTxt");
  60. textNode.innerText = "Checking for mod";
  61. return function(found, downloadId, downloadUrl, updated) {
  62. if (found) {
  63. changeButtonGradient(btn, "abecd6", "fbed96");
  64. textNode.innerText = "Download - " + updated;
  65. btn.addEventListener("click", function() {
  66. searchForDownloadLink(btn, downloadId, downloadUrl);
  67. });
  68. } else {
  69. textNode.innerText = "Not Available (REQUEST)";
  70. changeButtonGradient(btn, "e6e9f0", "eef1f5");
  71. btn.addEventListener("click", function() {
  72. gotoRequestPage(workshopId);
  73. });
  74. }
  75. };
  76. })()
  77. );
  78.  
  79. item.parentNode.parentNode.insertBefore(
  80. btnNode,
  81. item.parentNode.parentNode.firstChild
  82. );
  83. }
  84. } else if (isCollectionPage()) {
  85. console.log("----------- Collection page");
  86. var itemList = document.querySelectorAll(".collectionItem");
  87. for (var item of itemList) {
  88. var itemDownloadId = item.id.replace("sharedfile_", "");
  89. var btnNode = createElementFromHTML(DOWNLOAD_BTN_MINI);
  90. searchForMod(itemDownloadId,
  91. (function() {
  92. var workshopId = itemDownloadId;
  93. var btn = btnNode;
  94. var textNode = btn.querySelector("#DownloadTxt");
  95. textNode.innerText = "Checking for mod";
  96. return function(found, downloadId, downloadUrl, updated) {
  97. if (found) {
  98. changeButtonGradient(btn, "abecd6", "fbed96");
  99. textNode.innerText = "Download - " + updated;
  100. btn.addEventListener("click", function() {
  101. searchForDownloadLink(btn, downloadId, downloadUrl);
  102. });
  103. } else {
  104. textNode.innerText = "Not Available (REQUEST)";
  105. changeButtonGradient(btn, "e6e9f0", "eef1f5");
  106. btn.addEventListener("click", function() {
  107. gotoRequestPage(workshopId);
  108. });
  109. }
  110. };
  111. })()
  112. );
  113.  
  114. item.insertBefore(btnNode, item.firstChild);
  115. }
  116. } else {
  117. console.log("----------- Single item page");
  118. // init style
  119.  
  120. // add download button on steam page
  121. var btnNode = createElementFromHTML(DOWNLOAD_BTN);
  122. var textNode = btnNode.querySelector("#DownloadTxt");
  123. textNode.innerText = "Checking for mod";
  124. searchForMod(publishedfileid, function(
  125. found,
  126. downloadId,
  127. downloadUrl,
  128. updated
  129. ) {
  130. if (found) {
  131. changeButtonGradient(btnNode, "abecd6", "fbed96");
  132. textNode.innerText = "Download - " + updated;
  133. btnNode.addEventListener("click", function() {
  134. searchForDownloadLink(btnNode, downloadId, downloadUrl);
  135. });
  136. } else {
  137. textNode.innerText = "Not Available (REQUEST)";
  138. changeButtonGradient(btnNode, "e6e9f0", "eef1f5");
  139. btnNode.addEventListener("click", function() {
  140. gotoRequestPage(publishedfileid);
  141. });
  142. }
  143. });
  144.  
  145. document.body.appendChild(btnNode);
  146. }
  147. console.log("----------- Init successfully");
  148. }
  149.  
  150. function createElementFromHTML(htmlString) {
  151. var div = document.createElement("div");
  152. div.innerHTML = htmlString.trim();
  153. return div.firstChild;
  154. }
  155.  
  156. function getAppId() {
  157. return document.querySelector(".apphub_OtherSiteInfo a").getAttribute('data-appid');
  158. }
  159.  
  160. function isCitiesSkylines() {
  161. return (
  162. document.querySelector(".apphub_HeaderTop .apphub_AppName").innerText ===
  163. "Cities: Skylines"
  164. );
  165. }
  166.  
  167. function isCV6() {
  168. return (
  169. document.querySelector(".apphub_HeaderTop .apphub_AppName").innerText ===
  170. "Sid Meier's Civilization VI"
  171. );
  172. }
  173.  
  174. function isCollectionPage() {
  175. return $("mainContentsCollection") != null;
  176. }
  177.  
  178. function getDownloadId(downloadUrl) {
  179. console.log("----------- parsing download url: " + downloadUrl);
  180. var regex = /\/[^\/]*\//gm;
  181. var m;
  182. var downloadId = "";
  183. while ((m = regex.exec(downloadUrl)) !== null) {
  184. if (m.index === regex.lastIndex) {
  185. regex.lastIndex++;
  186. }
  187. if (m.index > 6) {
  188. downloadId = m[0].substr(1, m[0].length - 2);
  189. }
  190. }
  191. return downloadId;
  192. }
  193.  
  194. function downloadModBase(downloadId, referer, callback) {
  195. var formData = new FormData();
  196. formData.append("op", "download2");
  197. formData.append("id", downloadId);
  198. formData.append("rand", "");
  199. formData.append("referer", "");
  200. formData.append("method_free", "");
  201. formData.append("method_premium", "");
  202. var parameters = [];
  203. for (var pair of formData.entries()) {
  204. parameters.push(
  205. encodeURIComponent(pair[0]) + "=" + encodeURIComponent(pair[1])
  206. );
  207. }
  208. var postData = parameters.join("&");
  209. var request = GM_xmlhttpRequest({
  210. anonymous: true,
  211. method: "POST",
  212. url: "https://modsbase.com/",
  213. headers: {
  214. "content-type": "application/x-www-form-urlencoded",
  215. "Referer": referer
  216. },
  217. data: postData,
  218. onreadystatechange: function(e) {
  219. if (this.readyState !== 4) {
  220. return;
  221. }
  222.  
  223. var parser = new DOMParser();
  224. var temp = parser.parseFromString(e.response, "text/html").documentElement;
  225. var urlHolder = temp.querySelector('.download-details a');
  226. if (urlHolder === null || urlHolder == undefined) {
  227. window.open(referer, '_blank').focus();
  228. }else{
  229. const downloadUrl = urlHolder.href;
  230. console.log("----------- redirect download url: " +downloadUrl);
  231. Download(downloadUrl);
  232. callback(downloadUrl);
  233. request.abort();
  234. }
  235. }
  236. });
  237. }
  238.  
  239. function searchForMod(id, callback) {
  240. var appId = getAppId();
  241. var url = "http://catalogue.smods.ru/?s=" + id + "&app=" + appId;
  242.  
  243. console.log("----------- URL: " + url);
  244.  
  245. GM_xmlhttpRequest({
  246. anonymous: true,
  247. method: "GET",
  248. url: url,
  249. headers: {
  250. "Referer": "http://catalogue.smods.ru"
  251. },
  252. onload: function(e) {
  253. doc = new DOMParser().parseFromString(e.responseText, "text/html");
  254. if (doc.getElementsByClassName("post-inner").length > 0) {
  255. var downloadUrl = doc.querySelector(".post-inner .skymods-excerpt-btn").href;
  256. var downloadId = getDownloadId(downloadUrl);
  257. if (downloadId != undefined || downloadId != null || downloadId != "") {
  258. console.log("----------- download id: " + downloadId);
  259. var rDateStr = doc.querySelector(".post-inner .skymods-item-date").innerText;
  260. var updated = moment(rDateStr, "DD MMM at HH:mm YYYY").format(
  261. "DD MMM, YYYY"
  262. );
  263. callback(true, downloadId, downloadUrl, updated);
  264. } else {
  265. callback(false, downloadId, downloadUrl, "");
  266. }
  267. } else {
  268. callback(false, downloadId, downloadUrl, "");
  269. }
  270. }
  271. });
  272. }
  273.  
  274. function gotoRequestPage(id) {
  275. var url = "https://steamcommunity.com/sharedfiles/filedetails/?id=" + id;
  276. if (isCitiesSkylines()) {
  277. window.open('https://docs.google.com/forms/d/e/1FAIpQLSdXlq9OAWVwX5lRLNvpkMSmpKbEDY50Bl-UU3f6P7OBI2Ny3Q/viewform?c=0&w=1&entry.417177883=' + url, '_blank');
  278. } else {
  279. window.open('https://docs.google.com/forms/d/e/1FAIpQLSe7MisYbKNUlTXBcSR2clHxpwaoo0HiZ3zWto0osemubdDP1g/viewform?entry.417177883=' + url, '_blank');
  280. }
  281. }
  282.  
  283. function changeButtonGradient(btn, color1, color2) {
  284. var gradient =
  285. "linear-gradient(42deg, #" + color1 + " 35%, #" + color2 + " 65%)";
  286. btn.style.background = gradient;
  287. btn.querySelector("#DownloadTxt").style.background = gradient;
  288. }
  289.  
  290. function searchForDownloadLink(e, id, downloadUrl) {
  291. var textNode = e.querySelector("#DownloadTxt");
  292. textNode.innerText = "Search for Link";
  293. downloadModBase(id, downloadUrl, function(downloadUrl) {
  294. textNode.innerHTML = "Completed [<a href='" + downloadUrl +"'>Link</a>]";
  295. changeButtonGradient(e, "209cff", "68e0cf");
  296. });
  297. }
  298.  
  299. (function() {
  300. "use strict";
  301.  
  302. init();
  303. })();

QingJ © 2025

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