├── LICENSE
├── README.md
├── banhammer.user.js
├── blockmod.user.js
├── cfqol.user.js
├── images
├── allfiles.png
├── defaultusertab.png
├── fortcraft.png
├── modpackdownload.png
├── modstab.png
├── moduleundisabled.png
├── nocountdown.png
├── nocountdown2.png
├── pagination.png
└── searchbar.png
├── oss.user.js
└── redir.user.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 comp500
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Curseforge Userscripts
2 | Some useful userscripts for curseforge.com. Feel free to PR or create issues for features you want added. They work well with [Curse Dark by its_meow](https://itsmeow.dev/cursedark/). If you want your own userscript to be listed here, submit an issue!
3 |
4 | ## Install
5 | To install these userscripts, you will need a supported userscript manager for your browser. I highly recommend Tampermonkey, which is available for all popular browsers:
6 |
7 | - [Chrome](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo)
8 | - [Firefox](https://addons.mozilla.org/firefox/addon/tampermonkey/)
9 | - [Safari](http://tampermonkey.net/?browser=safari)
10 | - [Microsoft Edge](https://www.microsoft.com/store/p/tampermonkey/9nblggh5162s)
11 | - [Opera](https://addons.opera.com/extensions/details/tampermonkey-beta/)
12 |
13 | Once you have installed a userscript manager, click the following links to install userscripts from this repository:
14 |
15 | - [Curseforge Quality Of Life Fixes](https://github.com/comp500/Curseforge-Userscripts/raw/master/cfqol.user.js)
16 | - [Redirect Old Links](https://github.com/comp500/Curseforge-Userscripts/raw/master/redir.user.js)
17 |
18 | They can also be installed from [Greasy Fork](https://greasyfork.org/en/users/331451-comp500).
19 |
20 | ## Curseforge Quality Of Life Fixes
21 | This userscript fixes a few annoying problems with Curseforge. If you have any suggestions, please submit them as issues or [ping me on Discord](https://discord.mcmoddev.com/)!
22 |
23 | ### Features
24 | - Adds a search bar to the top navigation bar that searches mods.
25 |
26 | 
27 | - Readds modpack download buttons, because they removed them. Useful for people using alternative launchers.
28 |
29 | 
30 | - Adds a tab to mod and modpack pages that allows all the files to be viewed, rather than just the recent files - normally hidden behind a "View All" button
31 |
32 | 
33 | - Adds pagination for dependency lists to the bottom of the page, so you don't have to scroll up!
34 |
35 | 
36 | - Changes the default action of the Minecraft logo on the homepage and the games list to the Mods tab of Minecraft
37 |
38 | 
39 | - Automatically follows the 5 second download redirect for you, so you don't have to wait. Works best with version-specific download buttons.
40 |
41 | 
42 | - Automatically follows the "You are about to leave Curseforge" message for you, so you don't have to wait.
43 |
44 | 
45 | - Changes the default user tab to "Projects", because most of the time that's what you actually want to see.
46 |
47 | 
48 | - Sorts the "Related Projects" list on the file details page.
49 | - Changes the default Dependency Type in the Relations tab to "Required Dependency"
50 |
51 | ## Redirect Old Links
52 | When Curseforge redesigned their site, they moved mods from minecraft.curseforge.com to www.curseforge.com/minecraft. It doesn't seem to redirect the old links to the new ones any more, so this userscript will do it for you. For some reason the new site also doesn't support viewing mods by Addon ID anymore, so this script will resolve the IDs if they exist in the old links.
53 |
54 | 
55 |
--------------------------------------------------------------------------------
/banhammer.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name No More MCreator Mods
3 | // @version 0.10
4 | // @description you weren't gonna use them anyway
5 | // @author comp500
6 | // @namespace https://infra.link/
7 | // @match https://www.curseforge.com/minecraft/*
8 | // @match https://www.curseforge.com/Minecraft/*
9 | // @match https://legacy.curseforge.com/minecraft/*
10 | // @match https://legacy.curseforge.com/Minecraft/*
11 | // @updateURL https://github.com/comp500/Curseforge-Userscripts/raw/master/banhammer.user.js
12 | // @downloadURL https://github.com/comp500/Curseforge-Userscripts/raw/master/banhammer.user.js
13 | // @homepageURL https://github.com/comp500/Curseforge-Userscripts/
14 | // @supportURL https://github.com/comp500/Curseforge-Userscripts/issues/
15 | // @source https://github.com/comp500/Curseforge-Userscripts/
16 | // @run-at document-end
17 | // @grant GM_xmlhttpRequest
18 | // @connect addons-ecs.forgesvc.net
19 | // @connect edge.forgecdn.net
20 | // @connect media.forgecdn.net
21 | // @require https://unpkg.com/jszip@3.2.2/dist/jszip.min.js
22 | // ==/UserScript==
23 |
24 | (function() {
25 | "use strict";
26 |
27 | if (!JSZip) {
28 | console.log("NoMoreMCreatorMods: JSZip did not load properly!");
29 | return;
30 | }
31 |
32 | let storage = {};
33 | if (localStorage.NoMoreMCreatorMods != null) {
34 | try {
35 | storage = JSON.parse(localStorage.NoMoreMCreatorMods);
36 | } catch (e) {
37 | console.error(e);
38 | storage = {};
39 | }
40 | }
41 |
42 | let modRegex = /\/minecraft\/mc-mods\/([a-z][\da-z\-_]{0,127})$/;
43 | let mCreatorDescRegex = /
[\w\W]*MCreator[\w\W]*?<\/div>/i;
44 | let openSourceRegex = /Source
<\/svg>/;
45 | // haha nice try
46 | let hahaSourceRegex = /\s*\s*Source/i;
47 | let projectIDRegex = /Project ID<\/span>\s*(\d+)<\/span>/;
48 |
49 | let greaseMonkeyXHR = details => {
50 | details.method = details.method ? details.method : "GET";
51 | details.anonymous = true;
52 | details.responseType = details.responseType ? details.responseType : "arraybuffer";
53 | return new Promise((resolve, reject) => {
54 | details.onload = resolve;
55 | details.onerror = reject;
56 | GM_xmlhttpRequest(details);
57 | });
58 | };
59 |
60 | Promise.all(
61 | Array.from(document.querySelectorAll(".project-listing-row")).map(async row => {
62 | let link = Array.from(row.getElementsByTagName("a")).find(a => modRegex.test(a.href));
63 | if (link != undefined) {
64 | let stored = (link.href
65 | .replace("https://legacy.curseforge.com/minecraft/mc-mods/", "")
66 | .replace("https://legacy.curseforge.com/Minecraft/mc-mods/", "")
67 | .replace("https://www.curseforge.com/minecraft/mc-mods/", "")
68 | .replace("https://www.curseforge.com/Minecraft/mc-mods/", ""));
69 | if (storage[stored] == "mcreator") {
70 | row.parentNode.removeChild(row);
71 | return null;
72 | } else if (storage[stored] == "normal" || stored == "thanos-skin") {
73 | return null;
74 | }
75 | let res = await fetch(link.href);
76 | if (res.status !== 200) {
77 | console.error("NoMoreMCreatorMods: failed to get mod page " + link.href);
78 | console.log(res);
79 | return null;
80 | }
81 |
82 | let text = await res.text();
83 | // Exclude mods with MCreator in the description
84 | if (mCreatorDescRegex.test(text)) {
85 | storage[stored] = "mcreator";
86 | row.parentNode.removeChild(row);
87 | return null;
88 | }
89 | // Allow OSS mods
90 | if (openSourceRegex.test(text) && !hahaSourceRegex.test(text)) {
91 | storage[stored] = "normal";
92 | return null;
93 | }
94 |
95 | let projectIDMatches = projectIDRegex.exec(text);
96 | if (projectIDMatches != null && projectIDMatches.length > 0) {
97 | return [parseInt(projectIDMatches[1], 10), row, stored];
98 | }
99 | }
100 | })
101 | )
102 | .then(async ids => {
103 | let validIDs = ids.filter(id => id != null);
104 | if (validIDs.length > 0) {
105 | // Request the mod data from CurseForge's API
106 | let res = await greaseMonkeyXHR({
107 | url: "https://addons-ecs.forgesvc.net/api/v2/addon/",
108 | method: "POST",
109 | data: JSON.stringify(validIDs.map(i => i[0])),
110 | responseType: "json",
111 | headers: {
112 | Accept: "application/json",
113 | "Content-Type": "application/json"
114 | }
115 | });
116 | if (res.status != 200) {
117 | console.error("NoMoreMCreatorMods: failed to get addon info: " + res.status + " " + res.statusText);
118 | return;
119 | }
120 | if (res.response == null) {
121 | console.error("NoMoreMCreatorMods: failed to get addon info: null response");
122 | return;
123 | }
124 | await Promise.all(
125 | res.response.map(async mod => {
126 | if (mod.latestFiles == null) {
127 | console.error("NoMoreMCreatorMods: failed to get addon info: null latestFiles");
128 | return;
129 | }
130 | if (mod.latestFiles.length > 0) {
131 | // Download the mod
132 | let url = mod.latestFiles[0].downloadUrl;
133 | if (url == null) {
134 | console.error("NoMoreMCreatorMods: failed to get addon info: null download url");
135 | return;
136 | }
137 | let downloadRes = await greaseMonkeyXHR({ url: url });
138 | if (downloadRes.status != 200) {
139 | console.error("NoMoreMCreatorMods: failed to get mod download: " + res.status);
140 | return;
141 | }
142 | let zip = await JSZip.loadAsync(downloadRes.response);
143 | let row = validIDs.find(i => i[0] == mod.id)[1];
144 | let stored = validIDs.find(i => i[0] == mod.id)[2];
145 | // Check for a folder called net/mcreator
146 | if (zip.files["net/mcreator/"] != null || zip.files["mod/mcreator/"] != null) {
147 | storage[stored] = "mcreator";
148 | row.parentNode.removeChild(row);
149 | return;
150 | } else {
151 | storage[stored] = "normal";
152 | return;
153 | }
154 | }
155 | })
156 | );
157 | }
158 | })
159 | .then(a => {
160 | localStorage.NoMoreMCreatorMods = JSON.stringify(storage);
161 | })
162 | .catch(e => {
163 | console.error(e);
164 | });
165 | })();
166 |
--------------------------------------------------------------------------------
/blockmod.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Block mod button
3 | // @version 0.2
4 | // @description Hides selected mods from Curseforge
5 | // @author comp500
6 | // @namespace https://infra.link/
7 | // @match https://www.curseforge.com/minecraft/*
8 | // @match https://www.curseforge.com/Minecraft/*
9 | // @match https://legacy.curseforge.com/minecraft/*
10 | // @match https://legacy.curseforge.com/Minecraft/*
11 | // @updateURL https://github.com/comp500/Curseforge-Userscripts/raw/master/blockmod.user.js
12 | // @downloadURL https://github.com/comp500/Curseforge-Userscripts/raw/master/blockmod.user.js
13 | // @homepageURL https://github.com/comp500/Curseforge-Userscripts/
14 | // @supportURL https://github.com/comp500/Curseforge-Userscripts/issues/
15 | // @source https://github.com/comp500/Curseforge-Userscripts/
16 | // @grant GM_registerMenuCommand
17 | // ==/UserScript==
18 |
19 | (function() {
20 | "use strict";
21 |
22 | let storage = {};
23 | if (localStorage.BlockMod != null) {
24 | try {
25 | storage = JSON.parse(localStorage.BlockMod);
26 | } catch (e) {
27 | console.error(e);
28 | storage = {};
29 | }
30 | }
31 |
32 | let modRegex = /\/minecraft\/mc-mods\/([a-z][\da-z\-_]{0,127})$/;
33 |
34 | GM_registerMenuCommand("List blocked mods", () => {
35 | alert(Object.keys(storage).join(", "));
36 | });
37 |
38 | GM_registerMenuCommand("Clear blocked mods", () => {
39 | storage = {};
40 | localStorage.BlockMod = JSON.stringify(storage);
41 | location.reload();
42 | });
43 |
44 | Array.from(document.querySelectorAll(".project-listing-row")).map(row => {
45 | for (let link of Array.from(row.getElementsByTagName("a")).filter(a => modRegex.test(a.href))) {
46 | let stored = (link.href
47 | .replace("https://legacy.curseforge.com/minecraft/mc-mods/", "")
48 | .replace("https://legacy.curseforge.com/Minecraft/mc-mods/", "")
49 | .replace("https://www.curseforge.com/minecraft/mc-mods/", "")
50 | .replace("https://www.curseforge.com/Minecraft/mc-mods/", ""));
51 | if (storage[stored] == "block") {
52 | if (row.parentNode != null) {
53 | row.parentNode.removeChild(row);
54 | break;
55 | }
56 | } else {
57 | if (link.querySelector(".text-lg") == null && link.querySelector(".font-bold") == null) {
58 | continue;
59 | }
60 | let blockLink = document.createElement("a");
61 | blockLink.href = "#";
62 | blockLink.innerText = "Block";
63 | blockLink.style.paddingLeft = "5px";
64 | blockLink.addEventListener("click", e => {
65 | storage[stored] = "block";
66 | localStorage.BlockMod = JSON.stringify(storage);
67 | e.preventDefault();
68 | row.parentNode.removeChild(row);
69 | return false;
70 | }, false);
71 | link.addEventListener("mouseenter", () => {
72 | link.parentNode.insertBefore(blockLink, link.nextSibling);
73 | }, false);
74 | let timeout = -1;
75 | link.addEventListener("mouseleave", () => {
76 | if (timeout > -1) {
77 | window.clearTimeout(timeout);
78 | }
79 | timeout = window.setTimeout(() => {
80 | link.parentNode.removeChild(blockLink);
81 | }, 1000);
82 | }, false);
83 | }
84 | }
85 | });
86 | })();
87 |
--------------------------------------------------------------------------------
/cfqol.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Curseforge QOL Fixes
3 | // @version 0.27
4 | // @description Various Quality of Life improvements to the Curseforge website
5 | // @author comp500
6 | // @namespace https://infra.link/
7 | // @match https://www.curseforge.com/*
8 | // @match https://legacy.curseforge.com/*
9 | // @updateURL https://github.com/comp500/Curseforge-Userscripts/raw/master/cfqol.user.js
10 | // @downloadURL https://github.com/comp500/Curseforge-Userscripts/raw/master/cfqol.user.js
11 | // @homepageURL https://github.com/comp500/Curseforge-Userscripts/
12 | // @supportURL https://github.com/comp500/Curseforge-Userscripts/issues/
13 | // @source https://github.com/comp500/Curseforge-Userscripts/
14 | // @run-at document-end
15 | // @grant none
16 | // ==/UserScript==
17 |
18 | (function () {
19 | "use strict";
20 |
21 | // Add a search box
22 | let searchBoxContainer = document.createElement("div");
23 | searchBoxContainer.className = "flex mr-4 items-center";
24 | // Get the current assets path
25 | let styleSheet = Array.from(document.styleSheets).find((s) => /\/Content\/([\d\-]+)\//.test(s.href));
26 | let assetsPath = styleSheet == null ? "2-0-7179-35052" : /\/Content\/([\d\-]+)\//.exec(styleSheet.href)[1];
27 | searchBoxContainer.innerHTML = ``;
36 | let insertLocation = document.querySelector(".curseforge-header .ml-auto > div");
37 | if (insertLocation != null && insertLocation.firstChild != null) {
38 | // @Inject(method = "the navbar", at = @At("HEAD"))
39 | insertLocation.insertBefore(searchBoxContainer, insertLocation.firstChild);
40 |
41 | // Make the search box magically grow
42 | let searchBox = searchBoxContainer.querySelector("#cfqolTopbarSearch");
43 | if (searchBox != null) {
44 | // Fix stupid flexboxes - set to flex-grow 1 flex-shrink 0
45 | searchBoxContainer.style.flex = "1 0";
46 | searchBoxContainer.parentNode.parentNode.style.flex = "1 0";
47 |
48 | let navLinksContainer = document.querySelector(".top-nav__nav-link").parentNode;
49 | navLinksContainer.style.transition = "opacity 0.4s, max-width 0.3s";
50 | searchBox.addEventListener("focus", (e) => {
51 | navLinksContainer.style.opacity = 0;
52 | navLinksContainer.style.maxWidth = "0";
53 | });
54 | searchBox.addEventListener("blur", (e) => {
55 | navLinksContainer.style.opacity = 1;
56 | navLinksContainer.style.maxWidth = "2000px";
57 | });
58 | // Make the search icon focus the search box
59 | let searchIcon = searchBoxContainer.querySelector(".search");
60 | if (searchIcon != null) {
61 | searchIcon.addEventListener("click", (e) => {
62 | searchBox.focus();
63 | });
64 | }
65 | }
66 | }
67 |
68 | // Add an "All Files" tab for all curseforge projects
69 | let projectPathMatches = /^\/([\w-]+)\/([\w-]+)\/([a-z][\da-z-_]{0,127})/.exec(document.location.pathname);
70 | let filesTab = document.getElementById("nav-files");
71 | if (projectPathMatches != null && projectPathMatches.length == 4 && filesTab != null) {
72 | let gameName = projectPathMatches[1];
73 | let projectCategory = projectPathMatches[2];
74 | let projectSlug = projectPathMatches[3];
75 |
76 | let projectAllFiles = document.createElement("li");
77 | let isAllFilesPage = /\/files\/all$/.test(document.location.pathname);
78 | if (isAllFilesPage) {
79 | projectAllFiles.className =
80 | "border-b-2 border-primary-500 b-list-item p-nav-item px-2 pb-1/10 -mb-1/10 text-gray-500";
81 | filesTab.className = "b-list-item p-nav-item px-2 pb-1/10 -mb-1/10 text-gray-500";
82 | filesTab.getElementsByTagName("a")[0].className = "text-gray-500 hover:no-underline";
83 | } else {
84 | projectAllFiles.className = "b-list-item p-nav-item px-2 pb-1/10 -mb-1/10 text-gray-500";
85 | }
86 | projectAllFiles.innerHTML = `
89 |
90 | All Files
91 |
92 | `;
93 | filesTab.parentNode.insertBefore(projectAllFiles, filesTab.nextSibling);
94 | }
95 |
96 | // Add pagination to the bottom of the page in dependency lists
97 | let dependenciesPage = document.querySelector(".project-dependencies-page > div");
98 | if (dependenciesPage != null) {
99 | let paginationTop = document.querySelector(".project-dependencies-page > div .pagination-top");
100 | if (paginationTop != null) {
101 | dependenciesPage.appendChild(paginationTop.parentNode.cloneNode(true)).classList.remove("mb-4");
102 | }
103 | }
104 |
105 | // Skip download countdowns
106 | let downloadScript = Array.from(document.scripts).find(
107 | (s) => s.innerText != null && s.innerText.includes("PublicProjectDownload.countdown")
108 | );
109 | if (downloadScript != null && downloadScript.innerText != null) {
110 | let matches = downloadScript.innerText.match(/countdown\("(.+)"\)/);
111 | if (matches != null && matches[1] != null) {
112 | // Break the existing script
113 | let countdownEl = document.querySelector("span[data-countdown-seconds]");
114 | if (countdownEl != null) {
115 | // UNSAFE if grant != none! For some reason jQuery stores data in itself rather than attrs?!
116 | jQuery.removeData(countdownEl, "countdown-seconds");
117 | }
118 |
119 | let downloadText = document.querySelector("p[data-countdown-timer]");
120 | if (downloadText != null) {
121 | downloadText.innerText = "Downloading now...";
122 | }
123 |
124 | window.location.href = matches[1];
125 | }
126 | }
127 |
128 | /**
129 | * Link redirections
130 | */
131 |
132 | const linkList = Array.from(document.getElementsByTagName("a"));
133 | const regexDownloadLink = /^https:\/\/(www|legacy).curseforge.com\/.*\/download\/\d+$/;
134 |
135 | const redirections = [
136 | // Better method for skipping, if links contain file ID already
137 | [regexDownloadLink, a => {
138 | a.href = a.href + "/file";
139 | }],
140 | // Change the default Minecraft tab (from other links) to /minecraft/mc-mods
141 | [/^http:\/\/bit.ly\/2Lzpfsl|https:\/\/(www|legacy).curseforge.com\/minecraft\/?$/, a => {
142 | a.path = "/minecraft/mc-mods";
143 | }],
144 | // Change the default member tab to projects
145 | [/^https:\/\/(www|legacy).curseforge.com\/members\/[^\/]+\/?$/, a => {
146 | a.href = a.href + (a.href.endsWith("/") ? "" : "/") + "projects";
147 | }],
148 | // Redirect linkout URLs
149 | [/^https:\/\/(www|legacy).curseforge.com\/linkout/, a => {
150 | let url = new URL(a.href);
151 | a.href = decodeURIComponent(url.searchParams.get("remoteUrl"));
152 | }],
153 | // Change the default dependency type to "Include" for Minecraft modpacks
154 | [/^https:\/\/(www|legacy).curseforge.com\/minecraft\/modpacks\/([a-z][\da-z-_]{0,127})\/relations\/dependencies\/?$/, a => {
155 | a.href = a.href + "?filter-related-dependencies=6";
156 | }],
157 | // Change the default dependency type to "Required Dependency"
158 | [/^https:\/\/(www|legacy).curseforge.com\/([\w-]+)\/([\w-]+)\/([a-z][\da-z-_]{0,127})\/relations\/dependencies\/?$/, a => {
159 | a.href = a.href + "?filter-related-dependencies=3";
160 | }],
161 | // Change the default dependent type to "Required Dependency"
162 | [/^https:\/\/(www|legacy).curseforge.com\/([\w-]+)\/([\w-]+)\/([a-z][\da-z-_]{0,127})\/relations\/dependents\/?$/, a => {
163 | a.href = a.href + "?filter-related-dependents=3";
164 | }]
165 | ];
166 |
167 | for (let link of linkList) {
168 | for (let redir of redirections) {
169 | if (redir[0].test(link.href)) {
170 | redir[1](link);
171 | break;
172 | }
173 | }
174 | }
175 |
176 | /**
177 | * Readd download buttons for modpacks
178 | */
179 |
180 | // All Files list
181 | Array.from(document.querySelectorAll("table.listing a.button"))
182 | .filter(l => l.pathname.startsWith("/minecraft/modpacks") && l.href.endsWith("?client=y"))
183 | .map(link => {
184 | let newHref = link.href.slice(0, -9);
185 | if (regexDownloadLink.test(newHref)) {
186 | newHref = newHref + "/file";
187 | }
188 |
189 | let newLink = link.cloneNode(true);
190 | newLink.href = newHref;
191 | newLink.classList.add("button--icon-only");
192 | newLink.classList.add("mr-2");
193 |
194 | newLink.innerHTML = `
195 |
196 |
197 |
198 | `;
199 |
200 | if (link.parentNode != null) {
201 | link.parentNode.insertBefore(newLink, link);
202 | }
203 | });
204 |
205 | // Main File button, Page header button
206 | Array.from(document.querySelectorAll("article a.button, header a.button"))
207 | .filter((l) => l.pathname.startsWith("/minecraft/modpacks") && l.href.endsWith("?client=y"))
208 | .map((link) => {
209 | let newHref = link.href.slice(0, -9);
210 | if (regexDownloadLink.test(newHref)) {
211 | newHref = newHref + "/file";
212 | }
213 |
214 | if (link.parentNode.parentNode.childElementCount >= 2) {
215 | // For some reason, direct file pages now have it, but not the main files page?
216 | return;
217 | }
218 |
219 | let newButton = link.parentNode.cloneNode(true);
220 | newButton.classList.remove("ml-2");
221 | let newLink = newButton.querySelector("a.button");
222 | newLink.classList.add("button--hollow");
223 | newLink.href = newHref;
224 |
225 | newLink.innerHTML = `
226 |
227 |
228 | Download
229 | `;
230 | link.parentNode.parentNode.insertBefore(newButton, link.parentNode);
231 | });
232 |
233 | // Minecraft version-specific files list
234 | Array.from(document.querySelectorAll(".cf-recentfiles-credits-wrapper"))
235 | .filter((w) => w.firstChild == null || (w.childNodes.length == 1 && w.firstChild.nodeType != Node.ELEMENT_NODE))
236 | .forEach((wrapper) => {
237 | let link = wrapper.parentNode.querySelector("a");
238 |
239 | if (link != null) {
240 | let newHref = link.href.replace("files", "download");
241 | if (regexDownloadLink.test(newHref)) {
242 | newHref = newHref + "/file";
243 | }
244 | wrapper.innerHTML = ``;
249 | }
250 | });
251 |
252 | // Sort file dependency lists
253 | let sortNodeList = (list, sortBy) => {
254 | let pairs = Array.from(list).map(el => [el, el.parentNode]);
255 | pairs.sort((a, b) => sortBy(a[0], b[0]));
256 | pairs.forEach(pair => pair[1].removeChild(pair[0]));
257 | pairs.forEach(pair => pair[1].appendChild(pair[0]));
258 | };
259 |
260 | let relatedProjectsHeading = Array.from(document.querySelectorAll("section > h3")).find(heading => heading.innerText == "Related Projects");
261 | if (relatedProjectsHeading != undefined) {
262 | sortNodeList(relatedProjectsHeading.parentNode.querySelectorAll("section > div.flex > div"), (a, b) =>
263 | a.querySelector("p.font-bold > a").innerText.localeCompare(b.querySelector("p.font-bold > a").innerText));
264 | }
265 | })();
266 |
--------------------------------------------------------------------------------
/images/allfiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/allfiles.png
--------------------------------------------------------------------------------
/images/defaultusertab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/defaultusertab.png
--------------------------------------------------------------------------------
/images/fortcraft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/fortcraft.png
--------------------------------------------------------------------------------
/images/modpackdownload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/modpackdownload.png
--------------------------------------------------------------------------------
/images/modstab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/modstab.png
--------------------------------------------------------------------------------
/images/moduleundisabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/moduleundisabled.png
--------------------------------------------------------------------------------
/images/nocountdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/nocountdown.png
--------------------------------------------------------------------------------
/images/nocountdown2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/nocountdown2.png
--------------------------------------------------------------------------------
/images/pagination.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/pagination.png
--------------------------------------------------------------------------------
/images/searchbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/comp500/Curseforge-Userscripts/389fd8017b9fe15c59e9d414e065995ca7431f1c/images/searchbar.png
--------------------------------------------------------------------------------
/oss.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name No More ARR Mods
3 | // @version 0.3
4 | // @description you weren't gonna use them anyway
5 | // @author comp500
6 | // @namespace https://infra.link/
7 | // @match https://www.curseforge.com/minecraft/*
8 | // @match https://www.curseforge.com/Minecraft/*
9 | // @match https://legacy.curseforge.com/minecraft/*
10 | // @match https://legacy.curseforge.com/Minecraft/*
11 | // @updateURL https://github.com/comp500/Curseforge-Userscripts/raw/master/oss.user.js
12 | // @downloadURL https://github.com/comp500/Curseforge-Userscripts/raw/master/oss.user.js
13 | // @homepageURL https://github.com/comp500/Curseforge-Userscripts/
14 | // @supportURL https://github.com/comp500/Curseforge-Userscripts/issues/
15 | // @source https://github.com/comp500/Curseforge-Userscripts/
16 | // @run-at document-end
17 | // @grant GM_xmlhttpRequest
18 | // @connect addons-ecs.forgesvc.net
19 | // @connect edge.forgecdn.net
20 | // @connect media.forgecdn.net
21 | // ==/UserScript==
22 |
23 | (function() {
24 | "use strict";
25 |
26 | let storage = {};
27 | if (localStorage.NoMoreARRMods != null) {
28 | try {
29 | storage = JSON.parse(localStorage.NoMoreARRMods);
30 | } catch (e) {
31 | console.error(e);
32 | storage = {};
33 | }
34 | }
35 |
36 | let modRegex = /\/minecraft\/mc-mods\/([a-z][\da-z\-_]{0,127})$/;
37 | let mCreatorDescRegex = /