Skip to content

Instantly share code, notes, and snippets.

@iptq
Created January 7, 2022 11:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iptq/db2a3c123711ff2a49a5e3acbdc49621 to your computer and use it in GitHub Desktop.
Save iptq/db2a3c123711ff2a49a5e3acbdc49621 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name OsuTweaks
// @version 0.1.0
// @author IOException
// @run-at document-start
// @include http://osu.ppy.sh*
// @include https://osu.ppy.sh*
// @require https://code.jquery.com/jquery-3.6.0.min.js
// ==/UserScript==
// UTILS ======================================================================
// HSL / RGB conversion functions
// minified, original is https://gist.github.com/mjackson/5311256
function rgbToHsl(r,a,e){r/=255,a/=255,e/=255;var n,t=Math.max(r,a,e),c=Math.min(r,a,e),s=(t+c)/2;if(t==c)n=u=0;else{var i=t-c,u=.5<s?i/(2-t-c):i/(t+c);switch(t){case r:n=(a-e)/i+(a<e?6:0);break;case a:n=(e-r)/i+2;break;case e:n=(r-a)/i+4}n/=6}return[n,u,s]}function hslToRgb(r,a,e){var n,t,c;function s(r,a,e){return e<0&&(e+=1),1<e&&--e,e<1/6?r+6*(a-r)*e:e<.5?a:e<2/3?r+(a-r)*(2/3-e)*6:r}return 0==a?n=t=c=e:(n=s(a=2*e-(e=e<.5?e*(1+a):e+a-e*a),e,r+1/3),t=s(a,e,r),c=s(a,e,r-1/3)),[255*n,255*t,255*c]}
// Color parsing function
// minified, original is from https://stackoverflow.com/a/68580275
function parseCssColor(e){const o=document.createElement("div");document.body.appendChild(o),o.style.color=e;e=getComputedStyle(o).color.match(/[\.\d]+/g).map(Number);return o.remove(),e}
// START ======================================================================
console.log("OsuTweaks");
let currentUrl = null;
let listenersToPop = [];
function wait(func, pred) {
return new Promise((resolve) => {
var interval;
let test = function() {
let res = func();
if (b = pred(res)) {
if (interval) clearInterval(interval);
resolve(res);
}
return b;
};
if (!test()) {
interval = setInterval(test, 1000);
}
});
}
function waitForElementToExist(selector) {
return new Promise((resolve) => {
var interval;
let findEl = function() {
let el = document.querySelector(selector);
console.log(`Finding ${selector}`, el);
if (el !== null) {
console.log("Found.", el);
if (interval) clearInterval(interval);
resolve(el);
}
return el != null;
};
if (!findEl()) {
interval = setInterval(findEl, 1000);
}
});
}
let pathHandlers = [
[/\/users\/(\d+)(\/(osu|taiko|fruits|mania).+)?/, async (m) => {
console.log("User page");
let currentUser = await wait(() => unsafeWindow.currentUser, c => !!c);
console.log(currentUser);
}],
[/\/beatmapsets\/(\d+).?/, async (m) => {
console.log("Beatmap page");
// get the beatmapset data
// TODO: error checking?
let beatmapsetData = JSON.parse($("#json-beatmapset").text());
// console.log(beatmapsetData);
// remove existing difficulty name indicator
let diffNameIndicator = await waitForElementToExist(".beatmapset-header__diff-name");
diffNameIndicator.parentNode.removeChild(diffNameIndicator);
let starRatingIndicator = await waitForElementToExist(".beatmapset-header__star-difficulty");
starRatingIndicator.parentNode.removeChild(starRatingIndicator);
// put the names into the bubbles
let beatmapMap = new Map();
for (let beatmap of beatmapsetData.beatmaps) {
beatmapMap.set(beatmap.id, beatmap);
}
let beatmapPicker = await waitForElementToExist(".beatmapset-beatmap-picker");
for (let child of beatmapPicker.children) {
if (!child.href) continue;
let targetBeatmapId = parseInt(child.href.split("/").at(-1));
let correspondingBeatmap = beatmapMap.get(targetBeatmapId);
// create the elements and add it to screen
let icon = child.children[0];
let container = document.createElement("div");
container.style.display = "flex";
container.style.flexDirection = "column";
container.style.gap = "0";
container.style.transform = "translateZ(1px)";
let diffNameContainer = document.createElement("div");
let diffNameNode = document.createTextNode(correspondingBeatmap.version);
diffNameContainer.appendChild(diffNameNode);
let colorString = icon.style.getPropertyValue("--diff");
let color = parseCssColor(colorString);
let hsl = rgbToHsl(...color);
// make sure the colors aren't too dim
if (hsl[2] < .5) hsl[2] = .5;
hsl[2] *= 1.5;
if (hsl[2] > 1) hsl[2] = 1;
let [r, g, b] = hslToRgb(...hsl);
diffNameContainer.style.lineHeight = "0.75";
child.style.color = `rgb(${r}, ${g}, ${b})`;
diffNameContainer.style.textShadow = "0 1px 3px rgba(0,0,0,.75)";
container.appendChild(diffNameContainer);
let starRating = correspondingBeatmap.difficulty_rating.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: false
});
let infoContainer = document.createElement("small");
let infoNode = document.createTextNode(`${starRating}★`);
infoContainer.style.color = `rgb(${r}, ${g}, ${b})`;
infoContainer.style.textShadow = "0 1px 3px rgba(0,0,0,.75)";
infoContainer.style.display = "block";
infoContainer.style.fontSize = "75%";
infoContainer.appendChild(infoNode);
container.appendChild(infoContainer);
child.style.opacity = "1";
child.style.width = "auto";
child.style.height = "auto";
child.style.display = "flex";
child.style.alignItems = "center";
child.style.paddingLeft = "8px";
child.style.paddingRight = "12px";
child.style.gap = "6px";
child.appendChild(container);
}
}],
];
// this function is run whenever a new "page" is visited
// unfortunately the osu website is basically an SPA, so
// clicking links doesn't actually trigger a full refresh
let handleActualPageVisit = (path) => {
console.log(`New page visit to ${path}`);
let pathName = window.location.pathname;
for (let [regex, callback] of pathHandlers) {
let m = pathName.match(regex);
if (m !== null) {
// a match was found
callback(m);
break;
}
}
};
// wrap in a function to prevent global namespace pollution
document.addEventListener("DOMContentLoaded", () => {
"use strict";
// main initialization function
// detects which page we're on
$(document).ready(() => {
// detect page change, ripped from osuplus
// this is really ugly but whatever
// spent an hour trying to jack some addEventListener calls with no success
var currentBody = null;
setInterval(function() {
if (currentBody !== null && currentBody !== document.body) {
currentBody = document.body;
handleActualPageVisit(window.location.pathname);
} else if (currentBody === null) {
currentBody = document.body;
}
}, 1000);
// the very first time the browser is opened, handle the page visit
handleActualPageVisit(window.location.pathname);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment