// ==UserScript==
// @name Modrinthify
// @namespace Violentmonkey Scripts
// @match *://*.curseforge.com/minecraft/*
// @grant none
// @version 1.6.3
// @author devBoi76
// @license MIT
// @description Redirect curseforge.com mod pages to modrinth.com when possible
// @downloadURL https://update.greasyfork.icu/scripts/445993/Modrinthify.user.js
// @updateURL https://update.greasyfork.icu/scripts/445993/Modrinthify.meta.js
// ==/UserScript==
/* jshint esversion: 6 */
function htmlToElements(html) {
var t = document.createElement("template");
t.innerHTML = html;
return t.content;
}
function similarity(s1, s2) {
var longer = s1;
var shorter = s2;
if (s1.length < s2.length) {
longer = s2;
shorter = s1;
}
var longerLength = longer.length;
if (longerLength == 0) {
return 1.0;
}
return (
(longerLength - editDistance(longer, shorter)) /
parseFloat(longerLength)
);
}
function editDistance(s1, s2) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
var costs = new Array();
for (var i = 0; i <= s1.length; i++) {
var lastValue = i;
for (var j = 0; j <= s2.length; j++) {
if (i == 0) costs[j] = j;
else {
if (j > 0) {
var newValue = costs[j - 1];
if (s1.charAt(i - 1) != s2.charAt(j - 1))
newValue =
Math.min(Math.min(newValue, lastValue), costs[j]) +
1;
costs[j - 1] = lastValue;
lastValue = newValue;
}
}
}
if (i > 0) costs[s2.length] = lastValue;
}
return costs[s2.length];
}
const new_design_button =
'
MOD_NAME
';
const new_design_donation = `
Support the Author `;
const svg =
'';
const HTML = ` \
\
\
${svg}
\
Get on Modrinth\
\
`;
const DONATE_HTML = `\
\
\
\
\
Support the Author
\
`;
const REGEX =
/[\(\[](forge|fabric|forge\/fabric|fabric\/forge|unused|deprecated)[\)\]]/gim;
const MOD_PAGE_HTML = `\

\
MOD_NAME
\
BUTTON_HTML
`;
const SEARCH_PAGE_HTML = `\

\
MOD_NAME
\
BUTTON_HTML
`;
let query = "head title";
const tab_title = document.querySelector(query).innerText;
let mod_name = undefined;
let mod_name_noloader = undefined;
let page = undefined;
function main() {
const url = document.URL.split("/");
page = url[4];
const is_new_design = !location.hostname.startsWith("old.curseforge.com");
const is_search = is_new_design
? url[4].split("?")[0] == "search"
: url[5].startsWith("search") && url[5].split("?").length >= 2;
if (is_search) {
if (is_new_design) {
search_query = document.querySelector(".search-input-field").value;
} else {
search_query = document
.querySelector(".mt-6 > h2:nth-child(1)")
.textContent.match(/Search results for '(.*)'/)[1];
}
} else {
if (is_new_design) {
// search_query = document.querySelector(".project-header > h1:nth-child(2)").innerText
search_query = document.title.split(" - Minecraft ")[0];
} else {
search_query = document
.querySelector("head meta[property='og:title']")
.getAttribute("content");
}
}
mod_name = search_query;
mod_name_noloader = mod_name.replace(REGEX, "");
if (is_search && is_new_design) {
page_re = /.*&class=(.*?)&.*/;
page = (page.match(page_re) || ["", "all"])[1];
}
api_facets = "";
switch (page) {
//=Mods===============
case "mc-mods":
api_facets = `facets=[["categories:'forge'","categories:'fabric'","categories:'quilt'","categories:'liteloader'","categories:'modloader'","categories:'rift'"],["project_type:mod"]]`;
break;
//=Server=Plugins=====
case "mc-addons":
return;
case "customization":
api_facets = `facets=[["project_type:shader"]]`;
break;
case "bukkit-plugins":
api_facets = `facets=[["categories:'bukkit'","categories:'spigot'","categories:'paper'","categories:'purpur'","categories:'sponge'","categories:'bungeecord'","categories:'waterfall'","categories:'velocity'"],["project_type:mod"]]`;
break;
//=Resource=Packs=====
case "texture-packs":
api_facets = `facets=[["project_type:resourcepack"]]`;
break;
//=Modpacks===========
case "modpacks":
api_facets = `facets=[["project_type:modpack"]]`;
break;
case "all":
api_facets = ``;
break;
}
fetch(
`https://api.modrinth.com/v2/search?limit=3&query=${mod_name_noloader}&${api_facets}`,
{ method: "GET", mode: "cors" },
)
.then((response) => response.json())
.then((resp) => {
let bd = document.querySelector("#modrinth-body");
if (bd) {
bd.remove();
}
if (page == undefined) {
return;
}
if (resp.hits.length == 0) {
return;
}
let max_sim = 0;
let max_hit = undefined;
for (const hit of resp.hits) {
if (similarity(hit.title.trim(), mod_name) > max_sim) {
max_sim = similarity(hit.title.trim(), mod_name.trim());
max_hit = hit;
}
if (similarity(hit.title.trim(), mod_name_noloader) > max_sim) {
max_sim = similarity(
hit.title.trim(),
mod_name_noloader.trim(),
);
max_hit = hit;
}
}
if (max_sim <= 0.7) {
return;
}
// Add the buttons
if (is_search) {
if (is_new_design) {
// query = ".results-count"
query = ".search-tags";
let s = document.querySelector(query);
let buttonElement = htmlToElements(
new_design_button
.replace("ICON_SOURCE", max_hit.icon_url)
.replace("MOD_NAME", max_hit.title.trim())
.replace(
"REDIRECT",
`https://modrinth.com/${max_hit.project_type}/${max_hit.slug}`,
)
.replace("BUTTON_HTML", HTML),
);
buttonElement.childNodes[0].style.marginLeft = "auto";
s.appendChild(buttonElement);
} else {
query = ".mt-6 > div:nth-child(3)";
let s = document.querySelector(query);
let buttonElement = htmlToElements(
SEARCH_PAGE_HTML.replace(
"ICON_SOURCE",
max_hit.icon_url,
)
.replace("MOD_NAME", max_hit.title.trim())
.replace(
"REDIRECT",
`https://modrinth.com/${max_hit.project_type}/${max_hit.slug}`,
)
.replace("BUTTON_HTML", HTML),
);
s.appendChild(buttonElement);
}
} else {
if (is_new_design) {
let buttonElement = htmlToElements(
new_design_button
.replace("ICON_SOURCE", max_hit.icon_url)
.replace("MOD_NAME", max_hit.title.trim())
.replace(
"REDIRECT",
`https://modrinth.com/${max_hit.project_type}/${max_hit.slug}`,
),
);
const injectButton = () => {
query = ".breadcrumbs";
if (document.querySelector("#modrinth-body")) return;
let s = document.querySelector(query);
if (!s) return;
console.log("Inject", s);
s.appendChild(buttonElement);
};
injectButton();
const observer = new MutationObserver(() => injectButton());
observer.observe(document.body, {
childList: true,
subtree: true,
});
} else {
query = "div.-mx-1:nth-child(1)";
let s = document.querySelector(query);
let buttonElement = htmlToElements(
MOD_PAGE_HTML.replace("ICON_SOURCE", max_hit.icon_url)
.replace("MOD_NAME", max_hit.title.trim())
.replace(
"REDIRECT",
`https://modrinth.com/${max_hit.project_type}/${max_hit.slug}`,
)
.replace("BUTTON_HTML", HTML),
);
s.appendChild(buttonElement);
}
}
// Add donation button if present
fetch(`https://api.modrinth.com/v2/project/${max_hit.slug}`, {
method: "GET",
mode: "cors",
})
.then((response_p) => response_p.json())
.then((resp_p) => {
if (document.querySelector("#donate-button")) {
return;
}
if (resp_p.donation_urls.length > 0) {
let redir = document.getElementById("modrinth-body");
if (is_new_design) {
redir.innerHTML += new_design_donation.replace(
"REDIRECT",
resp_p.donation_urls[0].url,
);
if (is_search) {
redir.style.marginRight = "-195.5px";
} else {
redir.style.marginRight = "-195.5px";
}
} else {
let donations = resp_p.donation_urls;
let dbutton = document.createElement("div");
dbutton.innerHTML = DONATE_HTML.replace(
"REDIRECT",
donations[0].url,
);
dbutton.style.display = "inline-block";
let redir = document.getElementById(
"modrinthify-redirect",
);
redir.after(dbutton);
if (!is_search) {
redir.parentNode.parentNode.parentNode.style.marginRight =
"-150px";
}
}
}
});
});
}
main();
// document.querySelector(".classes-list").childNodes.forEach( (el) => {
// el.childNodes[0].addEventListener("click", main)
// })
let lastURL = document.URL;
new MutationObserver(() => {
let url = document.URL;
if (url != lastURL) {
lastURL = url;
main();
}
}).observe(document, { subtree: true, childList: true });