Last active
June 7, 2025 10:44
-
-
Save respektive/7795fecf6dd559cd0570dd29516b4eab to your computer and use it in GitHub Desktop.
Ranked Score Rank on osu! user profiles.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Ranked Score Rank | |
| // @namespace https://gist.github.com/respektive/7795fecf6dd559cd0570dd29516b4eab/ | |
| // @version 0.7 | |
| // @description Adds the Ranked Score Rank on user profiles. | |
| // @author respektive | |
| // @match http://osu.ppy.sh/* | |
| // @match https://osu.ppy.sh/* | |
| // @grant none | |
| // @updateURL https://gist.githubusercontent.com/respektive/7795fecf6dd559cd0570dd29516b4eab/raw/ranked-score-rank.user.js | |
| // @downloadURL https://gist.githubusercontent.com/respektive/7795fecf6dd559cd0570dd29516b4eab/raw/ranked-score-rank.user.js | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| let scoreRankVisible = false; | |
| async function addScoreRank() { | |
| const ranksElement = document.querySelector(".profile-detail__values"); | |
| const modesElement = document.querySelector(".game-mode-link--active"); | |
| if (!modesElement) { | |
| return; | |
| } | |
| if (ranksElement) { | |
| const path = window.location.pathname.split("/"); | |
| const userId = path[2]; | |
| const mode = modesElement.dataset.mode; | |
| const scoreRankInfo = await (await fetch(`https://score.respektive.pw/u/${userId}?mode=${mode}`)).json(); | |
| const scoreRank = scoreRankInfo[0].rank; | |
| const scoreRankHighest = scoreRankInfo[0].rank_highest.rank; | |
| const scoreRankHighestDate = new Date(Date.parse(scoreRankInfo[0].rank_highest.updated_at)); | |
| // Add element to page | |
| let scoreRankElement = document.createElement("div"); | |
| scoreRankElement.classList.add("value-display", "value-display--rank"); | |
| let scoreRankLabel = document.createElement("div"); | |
| scoreRankLabel.classList.add("value-display__label"); | |
| scoreRankLabel.innerHTML = "Score Ranking"; | |
| scoreRankElement.append(scoreRankLabel); | |
| let scoreRankValue = document.createElement("div"); | |
| scoreRankValue.classList.add("value-display__value"); | |
| scoreRankElement.append(scoreRankValue); | |
| let rank = document.createElement("div"); | |
| // Show a dash even if user has no score rank, so it can still show the peak rank if available | |
| if (scoreRank === 0) { | |
| rank.innerHTML = "-"; | |
| } else { | |
| rank.innerHTML = `#${scoreRank.toLocaleString()}`; | |
| } | |
| // Add peak score rank if available | |
| if (scoreRankHighest) { | |
| const scoreRankTooltip = `Highest Rank: #${scoreRankHighest.toLocaleString()} on ${new Intl.DateTimeFormat('en-GB', { dateStyle: 'medium' }).format(scoreRankHighestDate)}`; | |
| rank.setAttribute("data-html-title", scoreRankTooltip); | |
| rank.setAttribute("title", ""); | |
| } | |
| scoreRankValue.append(rank); | |
| if (!scoreRankVisible) { | |
| ranksElement.append(scoreRankElement); | |
| scoreRankVisible = true; | |
| } | |
| } | |
| } | |
| let lastUrl = location.href; | |
| new MutationObserver(() => { | |
| const url = location.href; | |
| if (url !== lastUrl) { | |
| lastUrl = url; | |
| scoreRankVisible = false; | |
| setTimeout(onUrlChange, 1500); | |
| } | |
| }).observe(document, { subtree: true, childList: true }); | |
| function onUrlChange() { | |
| observer.observe(document, { childList: true, subtree: true }); | |
| } | |
| const observer = new MutationObserver(check) | |
| observer.observe(document, { childList: true, subtree: true }); | |
| function check(changes, observer) { | |
| if (document.querySelector(".profile-detail")) { | |
| observer.disconnect(); | |
| addScoreRank(); | |
| } | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment