// ==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
Get on Modrinth
'; const new_design_donation = ``; 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 });