您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Some small improvements to osu!web, featuring beatmapset filter and profile page improvement.
当前为
- // ==UserScript==
- // @name osu!web enhancement
- // @namespace http://tampermonkey.net/
- // @version 0.4.2
- // @description Some small improvements to osu!web, featuring beatmapset filter and profile page improvement.
- // @author VoltaXTY
- // @match https://osu.ppy.sh/*
- // @icon http://ppy.sh/favicon.ico
- // @grant none
- // @run-at document-end
- // ==/UserScript==
- console.log("osu!web enhancement loaded");
- const inj_style =
- `#osu-db-input{
- display: none;
- }
- .osu-db-button{
- align-items: center;
- padding: 10px;
- }
- .osu-db-button:hover{
- cursor: pointer;
- }
- .beatmapsets__item.owned-beatmapset{
- opacity: 1.0;
- }
- .beatmapsets__item.owned-beatmapset .beatmapset-panel__menu-container{
- background-color: #87dda8;
- }
- .beatmapsets__item.owned-beatmapset .fas, .beatmapsets__item.owned-beatmapset .far{
- color: #5c9170;
- }
- .play-detail__accuracy{
- margin: 0px 12px;
- }
- .play-detail__accuracy.ppAcc{
- color: #8ef9f1;
- padding: 0;
- }
- .play-detail__weighted-pp{
- margin: 0px;
- }
- .play-detail__pp{
- flex-direction: column;
- }
- .lost-pp{
- font-size: 10px;
- position: relative;
- right: 7px;
- font-weight: 600;
- /* color: #81ff81; */
- }
- .score-detail{
- display: inline-block;
- }
- /*
- .score-detail-mania-max-300{
- width: 6em;
- }
- .score-detail-mania-200, .score-detail-mania-100, .score-detail-mania-miss{
- width: 4em;
- }
- .score-detail-mania-50{
- width: 3.5em;
- }
- */
- .score-detail-data-text{
- margin-left: 5px;
- margin-right: 10px;
- width: auto;
- display: inline-block;
- }
- @keyframes rainbow{
- 0%{
- color: #be19ff;
- }
- 25%{
- color: #0075ff;
- }
- 50%{
- color: #4ddf86;
- }
- 75%{
- color: #e9ea00;
- }
- 100%{
- color: #ff7800;
- }
- }
- .play-detail__accuracy-and-weighted-pp{
- display: flex;
- flex-direction: row-reverse;
- }
- .mania-max{
- animation: 0.16s infinite alternate rainbow;
- }
- .mania-300{
- color: #fbff00;
- }
- .osu-100{
- color: #67ff5b;
- }
- .mania-200{
- color: #6cd800;
- }
- .osu-300{
- color: #7dfbff;
- }
- .mania-100{
- color: #257aea;
- }
- .mania-50{
- color: #d2d2d2;
- }
- .osu-50{
- color: #ffbf00;
- }
- .mania-miss{
- color: #cc2626;
- }
- .mania-max, .mania-300, .mania-200, .mania-100, .mania-50, .mania-miss, .osu-300, .osu-100, .osu-50, .osu-miss{
- font-weight: 600;
- }
- .score-detail-data-text{
- font-weight: 500;
- }
- .osu-miss{
- display: inline-block;
- }
- .osu-miss > svg{
- width: 14px;
- height: 14px;
- top: 3px;
- position: relative;
- }
- div.bar__exp-info{
- position: relative;
- bottom: 100%;
- }
- .play-detail__group--background{
- position: absolute;
- width: 90%;
- height: 100%;
- left: 0px;
- margin: 0px;
- pointer-events: none;
- z-index: 1;
- border-radius: 10px 0px 0px 10px;
- mask-image: linear-gradient(to right, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0));
- -webkit-mask-image: linear-gradient(to right, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0));
- }
- .play-detail__group.play-detail__group--top *{
- z-index: 3;
- }
- time.js-timeago{
- color: #ccc;
- }
- button.show-more-link{
- z-index: 4;
- }
- `;
- let scriptContent =
- String.raw`console.log("page script injected from osu!web enhancement");
- let oldXHROpen = window.XMLHttpRequest.prototype.open;
- window.XMLHttpRequest.prototype.open = function() {
- this.addEventListener("load", function() {
- const url = this.responseURL;
- const trreg = /https:\/\/osu\.ppy\.sh\/users\/([0-9]+)\/extra-pages\/(top_ranks|historical)\?mode=(osu|taiko|fruits|mania)/.exec(url);
- const adreg = /https:\/\/osu\.ppy\.sh\/users\/([0-9]+)\/scores\/(firsts|best|recent|pinned)\?mode=(osu|taiko|fruits|mania)&limit=[0-9]*&offset=[0-9]*/.exec(url);
- let info;
- if(trreg) info = {
- type: trreg[2],
- userId: Number(trreg[1]),
- mode: trreg[3],
- }
- else{
- if(adreg) info = {
- type: adreg[2],
- userId: Number(adreg[1]),
- mode: adreg[3],
- }
- else return;
- }
- const responseBody = this.responseText;
- info.data = JSON.parse(responseBody);
- info.id = "osu!web enhancement";
- window.postMessage(info, "*");
- });
- return oldXHROpen.apply(this, arguments);
- };`;
- const scriptId = "osu-web-enhancement-XHR-script";
- if(!document.querySelector(`script#${scriptId}`)){
- const script = document.createElement("script");
- script.textContent = scriptContent;
- document.body.appendChild(script);
- }
- const HTML = (tagname, attrs, ...children) => {
- if(attrs === undefined) return document.createTextNode(tagname);
- const ele = document.createElement(tagname);
- if(attrs) for(let [key, value] of Object.entries(attrs)){
- if(key === "eventListener"){
- for(let listener of value){
- ele.addEventListener(listener.type, listener.listener, listener.options);
- }
- }
- else ele.setAttribute(key, value);
- }
- for(let child of children) ele.append(child);
- return ele;
- };
- const html = (html) => {
- const t = document.createElement("template");
- t.innerHTML = html;
- return t.content.firstElementChild;
- };
- const PostMessage = (msg) => { console.error(msg); };
- const OsuMod = {
- NoFail: 1 << 0,
- Easy: 1 << 1,
- TouchDevice: 1 << 2,
- NoVideo: 1 << 2,
- Hidden: 1 << 3,
- HardRock: 1 << 4,
- SuddenDeath: 1 << 5,
- DoubleTime: 1 << 6,
- Relax: 1 << 7,
- HalfTime: 1 << 8,
- Nightcore: 1 << 9, // always with DT
- Flashlight: 1 << 10,
- Autoplay: 1 << 11,
- SpunOut: 1 << 12,
- Autopilot: 1 << 13,
- Perfect: 1 << 14,
- Key4: 1 << 15,
- Key5: 1 << 16,
- Key6: 1 << 17,
- Key7: 1 << 18,
- Key8: 1 << 19,
- KeyMod: 1 << 19 | 1 << 18 | 1 << 17 | 1 << 16 | 1 << 15,
- FadeIn: 1 << 20,
- Random: 1 << 21,
- Cinema: 1 << 22,
- TargetPractice: 1 << 23,
- Key9: 1 << 24,
- Coop: 1 << 25,
- Key1: 1 << 26,
- Key3: 1 << 27,
- Key2: 1 << 28,
- ScoreV2: 1 << 29,
- Mirror: 1 << 30,
- };
- class Byte{ value = 0; constructor(arr, iter){ this.value = arr[iter.nxtpos++]; } };
- class RankedStatus extends Byte{
- constructor(arr, iter){
- super(arr, iter);
- switch(this.value){
- case 1: this.description = "unsubmitted"; break;
- case 2: this.description = "pending/wip/graveyard"; break;
- case 3: this.description = "unused"; break;
- case 4: this.description = "ranked"; break;
- case 5: this.description = "approved"; break;
- case 6: this.description = "qualified"; break;
- case 7: this.description = "loved"; break;
- default: this.description = "unknown"; this.value = 0;
- }
- }
- };
- class OsuMode extends Byte{
- constructor(arr, iter){
- super(arr, iter);
- switch(this.value){
- case 1: this.description = "taiko"; break;
- case 2: this.description = "catch"; break;
- case 3: this.description = "mania"; break;
- default: this.value = 0; this.description = "osu";
- }
- }
- };
- class Grade extends Byte{
- constructor(arr, iter){
- super(arr, iter);
- switch(this.value){
- case 0: this.description = "SSH"; break;
- case 1: this.description = "SH"; break;
- case 2: this.description = "SS"; break;
- case 3: this.description = "S"; break;
- case 4: this.description = "A"; break;
- case 5: this.description = "B"; break;
- case 6: this.description = "C"; break;
- case 7: this.description = "D"; break;
- default: this.description = "not played";
- }
- }
- };
- class Short{ value = 0; constructor(arr, iter){ this.value = arr[iter.nxtpos++] | arr[iter.nxtpos++] << 8; } };
- class Int{ value = 0; constructor(arr, iter){ this.value = arr[iter.nxtpos++] | arr[iter.nxtpos++] << 8 | arr[iter.nxtpos++] << 16 | arr[iter.nxtpos++] << 24; } };
- class Long{ value = 0n; constructor(arr, iter){ this.value = new DataView(arr.buffer, iter.nxtpos, 8).getBigUint64(0, true); iter.nxtpos += 8; } };
- class ULEB128{
- value = 0n;
- constructor(arr, iter){
- let shift = 0n;
- while(true){
- let peek = BigInt(arr[iter.nxtpos++]);
- this.value |= (peek & 0x7Fn) << shift;
- if((peek & 0x80n) === 0n) break;
- shift += 7n;
- }
- }
- };
- class Single{ value = 0; constructor(arr, iter){ this.value = new DataView(arr.buffer, iter.nxtpos, 4).getFloat32(0, true); iter.nxtpos += 4; } };
- class Double{ value = 0; constructor(arr, iter){ this.value = new DataView(arr.buffer, iter.nxtpos, 8).getFloat64(0, true); iter.nxtpos += 8; } };
- class Boolean{ value = false; constructor(arr, iter){ this.value = arr[iter.nxtpos++] !== 0x00; } };
- class OString{
- value = "";
- constructor(arr, iter){
- switch(arr[iter.nxtpos++]){
- case 0: break;
- case 0x0b:
- const l = new ULEB128(arr, iter).value;
- const bv = new Uint8Array(arr.buffer, iter.nxtpos, Number(l));
- this.value = new TextDecoder().decode(bv);
- iter.nxtpos += Number(l);
- break;
- default: console.assert(false, `error occurred while parsing osu string with the first byte.`);
- }
- }
- };
- class IntDouble{
- int = 0;
- double = 0;
- constructor(arr, iter){
- const m1 = arr[iter.nxtpos++];
- console.assert(m1 === 0x08, `error occurred while parsing Int-Double pair at ${iter.nxtpos - 1} with value 0x${m1.toString(16)}: should be 0x8.`);
- this.int = new Int(arr, iter).value;
- const m2 = arr[iter.nxtpos++];
- console.assert(m2 === 0x0d, `error occurred while parsing Int-Double pair at ${iter.nxtpos - 1} with value 0x${m1.toString(16)}: should be 0x8.`);
- this.double = new Double(arr, iter).value;
- }
- };
- class IntDoubleArray extends Array{
- constructor(arr, iter){
- super(new Int(arr, iter).value);
- for(let i = 0; i < this.length; i++) this[i] = new IntDouble(arr, iter);
- }
- };
- class TimingPoint{
- BPM = 0;
- offset = 0;
- notInherited = false;
- constructor(arr, iter){
- this.BPM = new Double(arr, iter).value;
- this.offset = new Double(arr, iter).value;
- this.notInherited = new Boolean(arr, iter).value;
- }
- };
- class TimingPointArray extends Array{
- constructor(arr, iter){
- super(new Int(arr, iter).value);
- for(let i = 0; i < this.length; i++) this[i] = new TimingPoint(arr, iter);
- }
- };
- class DateTime extends Long{};
- class Beatmap{
- constructor(arr, iter){
- if(iter.osuVersion < 20191106) this.bytes = new Int(arr, iter);
- this.artistName = new OString(arr, iter);
- this.artistNameUnicode = new OString(arr, iter);
- this.songTitle = new OString(arr, iter);
- this.songTitleUnicode = new OString(arr, iter);
- this.creatorName = new OString(arr, iter);
- this.difficultyName = new OString(arr, iter);
- this.audioFilename = new OString(arr, iter);
- this.MD5Hash = new OString(arr, iter);
- this.beatmapFilename = new OString(arr, iter);
- this.rankedStatus = new RankedStatus(arr, iter);
- this.hitcircleCount = new Short(arr, iter);
- this.sliderCount = new Short(arr, iter);
- this.spinnerCount = new Short(arr, iter);
- this.lastModified = new Long(arr, iter);
- this.AR = iter.osuVersion < 20140609 ? new Byte(arr, iter) : new Single(arr, iter);
- this.CS = iter.osuVersion < 20140609 ? new Byte(arr, iter) : new Single(arr, iter);
- this.HP = iter.osuVersion < 20140609 ? new Byte(arr, iter) : new Single(arr, iter);
- this.OD = iter.osuVersion < 20140609 ? new Byte(arr, iter) : new Single(arr, iter);
- this.sliderVelocity = new Double(arr, iter);
- if(iter.osuVersion >= 20140609) this.osuSRInfoArr = new IntDoubleArray(arr, iter);
- if(iter.osuVersion >= 20140609) this.taikoSRInfoArr = new IntDoubleArray(arr, iter);
- if(iter.osuVersion >= 20140609) this.catchSRInfoArr = new IntDoubleArray(arr, iter);
- if(iter.osuVersion >= 20140609) this.maniaSRInfoArr = new IntDoubleArray(arr, iter);
- this.drainTime = new Int(arr, iter);
- this.totalTime = new Int(arr, iter);
- this.audioPreviewTime = new Int(arr, iter);
- this.timingPointArr = new TimingPointArray(arr, iter);
- this.difficultyID = new Int(arr, iter);
- this.beatmapID = new Int(arr, iter);
- this.threadID = new Int(arr, iter);
- this.osuGrade = new Grade(arr, iter);
- this.taikoGrade = new Grade(arr, iter);
- this.catchGrade = new Grade(arr, iter);
- this.maniaGrade = new Grade(arr, iter);
- this.offsetLocal = new Short(arr, iter);
- this.stackLeniency = new Single(arr, iter);
- this.mode = new OsuMode(arr, iter);
- this.sourceStr = new OString(arr, iter);
- this.tagStr = new OString(arr, iter);
- this.offsetOnline = new Short(arr, iter);
- this.titleFont = new OString(arr, iter);
- this.unplayed = new Boolean(arr, iter);
- this.lastTimePlayed = new Long(arr, iter);
- this.isOsz2 = new Boolean(arr, iter);
- this.folderName = new OString(arr, iter);
- this.lastTimeChecked = new Long(arr, iter);
- this.ignoreBeatmapSound = new Boolean(arr, iter);
- this.ignoreBeatmapSkin = new Boolean(arr, iter);
- this.disableStoryboard = new Boolean(arr, iter);
- this.disableVideo = new Boolean(arr, iter);
- this.visualOverride = new Boolean(arr, iter);
- if(iter.osuVersion < 20140609) this.uselessShort = new Short(arr, iter);
- this.lastModified = new Int(arr, iter);
- this.scrollSpeedMania = new Byte(arr, iter);
- }
- };
- class BeatmapArray extends Array{
- constructor(arr, iter){
- super(new Int(arr, iter).value);
- for(let i = 0; i < this.length; i++) this[i] = new Beatmap(arr, iter);
- }
- };
- class OsuDb{
- constructor(arr, iter){
- this.version = new Int(arr, iter);
- iter.osuVersion = this.version.value;
- this.folderCount = new Int(arr, iter);
- this.accountUnlocked = new Boolean(arr, iter);
- this.timeTillUnlock = new DateTime(arr, iter);
- this.playerName = new OString(arr, iter);
- this.beatmapArray = new BeatmapArray(arr, iter);
- this.permission = new Int(arr, iter);
- }
- };
- const beatmapsets = new Set();
- const beatmaps = new Set();
- const bmsReg = /https:\/\/osu\.ppy\.sh\/beatmapsets\/([0-9]+)/;
- const bmReg = /https:\/\/osu\.ppy\.sh\/beatmapsets\/(?:[0-9]+)#(?:mania|osu|fruits|taiko)\/([0-9]+)/;
- const BeatmapsetRefresh = () => {
- for(const bm of window.osudb.beatmapArray){
- beatmaps.add(bm.difficultyID.value);
- beatmapsets.add(bm.beatmapID.value);
- }
- OnMutation();
- };
- const GetBestScores = async (id, mode) => {
- const querystr = new URLSearchParams({
- k: apiK,
- u: id,
- m: mode,
- limit: 100,
- type: "id",
- }).toString();
- const r = await fetch(`${guburl}?${querystr}`);
- return await r.json();
- };
- const NewOsuDb = (r) => {
- return new Promise((resolve, reject) => {
- const start = performance.now();
- const result = new Uint8Array(r.result);
- const length = result.length;
- console.log(`start reading osu!.db(${length} Bytes).`);
- const iter = {
- nxtpos: 0,
- };
- window.osudb = new OsuDb(result, iter);
- console.assert(iter.nxtpos === length, "there are still remaining unread bytes, something may be wrong. iter: %o", iter);
- console.log(`finished reading osu!.db in ${performance.now() - start} ms.`);
- resolve();
- })
- };
- const ReadOsuDb = (file) => {
- if(file.name !== "osu!.db"){ console.assert( false, "filename should be 'osu!.db'."); return; }
- const r = new FileReader();
- r.onload = () => {
- NewOsuDb(r);
- BeatmapsetRefresh();
- };
- r.onerror = () => console.assert(false, "error occurred while reading file.");
- r.readAsArrayBuffer(file);
- };
- const SelectOsuDb = (event) => {
- const t = event.target;
- const l = t.files;
- console.assert(l && l.length === 1, "No file or multiple files are selected.");
- ReadOsuDb(l[0]);
- };
- const PlaceSelectOsuDbButton = () => {
- if(document.querySelector(".osu-db-button")) return;
- const i = HTML("input", {type: "file", id: "osu-db-input", accept: ".db", eventListener: [{
- type: "change",
- listener: SelectOsuDb,
- options: false,
- }]});
- const d = HTML("div", {class: "osu-db-button nav2__col nav2__col--menu", eventListener: [{
- type: "click",
- listener: () => {if(i) i.click();},
- options: false,
- }]}, HTML("osu!.db"));
- document.body.appendChild(i);
- const a = document.querySelector("div.nav2__col.nav2__col--menu");
- a.parentElement.insertBefore(d, a);
- };
- const FilterBeatmapSet = () => {
- document.querySelectorAll(".beatmapsets__item").forEach((item) => {
- const bmsID = Number(bmsReg.exec(item.innerHTML)?.[1]);
- if(bmsID && beatmapsets.has(bmsID)){
- item.classList.add("owned-beatmapset");
- }
- });
- ShowBeatmapsSetInfo();
- };
- const ShowBeatmapsSetInfo = () => {
- const p = document.querySelector(".beatmaps-popup__group");
- if(!p) return;
- };
- const AdjustStyle = (modeId, sectionName) => {
- const styleSheetId = `userscript-generated-stylesheet-${sectionName}`;
- let e = document.getElementById(styleSheetId);
- if(!e){
- e = document.createElement("style");
- e.id = styleSheetId;
- document.head.appendChild(e);
- }
- const s = e.sheet;
- while(s.cssRules.length) s.deleteRule(0);
- const sectionSelector = `div.js-sortable--page[data-page-id="${sectionName}"]`;
- let ll;
- switch(modeId){
- case 3: ll = [".mania-300", ".mania-200", ".mania-100", ".mania-50", ".mania-miss"]; break;
- case 0: ll = [".osu-300", ".osu-100", ".osu-50", ".osu-miss"]; break;
- }
- ll.forEach((str) =>
- s.insertRule(
- `${sectionSelector} ${str} + .score-detail-data-text {
- width: ${[...document.querySelectorAll(`${sectionSelector} ${str} + .score-detail-data-text`)].reduce((max, ele) => ele.clientWidth > max ? ele.clientWidth : max, 0) + 2}px;
- }` ,0
- )
- );
- s.insertRule(
- `${sectionSelector} .play-detail__pp{
- width: ${[...document.querySelectorAll(`${sectionSelector} .play-detail__pp`)].reduce((max, ele) => ele.clientWidth > max ? ele.clientWidth : max, 0) + 1}px;
- }`
- ,0
- );
- };
- const TopRanksWorker = (items, tabId, sectionName = "top_ranks") => {
- if(!items.length) return true;
- const tabEle = document.querySelector(`div.js-sortable--page[data-page-id="${sectionName}"]`);
- if(!tabEle) return false;
- const listEle = tabEle.querySelectorAll(`.title.title--page-extra-small + div.play-detail-list.u-relative`)?.[tabId];
- if(!listEle) return false;
- let completed = true;
- for(const item of [...listEle.querySelectorAll(".play-detail.play-detail--highlightable")]){
- const a = item.querySelector("a.play-detail__title")
- const bm = bmReg.exec(a.href)[1];
- const data = items.find(item => item.beatmap_id === Number(bm));
- if(!data) completed = false;
- else ListItemWorker(item, data);
- }
- if(!completed) return false;
- AdjustStyle(items[0].ruleset_id, sectionName, tabId);
- return true;
- };
- const ListItemWorker = (ele, data) => {
- if(ele.classList.contains("improved")) return;
- ele.classList.add("improved");
- if(data.pp){
- const pptext = ele.querySelector(".play-detail__pp > span").childNodes[0];
- pptext.nodeValue = Number(data.pp).toPrecision(5);
- }
- const left = ele.querySelector("div.play-detail__group.play-detail__group--top");
- const leftc = HTML("div", {class: "play-detail__group--background", style: `background-image: url(https://assets.ppy.sh/beatmaps/${data.beatmap.beatmapset_id}/covers/card@2x.jpg);`});
- left.parentElement.insertBefore(leftc, left);
- const detail= ele.querySelector("div.play-detail__score-detail-top-right");
- const du = detail.children[0];
- if(!detail.children[1]) detail.append(HTML("div", {classList: "play-detail__pp-weight"}));
- const db = detail.children[1];
- data.statistics.perfect ??= 0, data.statistics.great ??= 0, data.statistics.good ??= 0, data.statistics.ok ??= 0, data.statistics.meh ??= 0, data.statistics.miss ??= 0;
- switch(data.ruleset_id){
- case 0:{
- du.replaceChildren(
- HTML("span", {class: "play-detail__accuracy"}, HTML(`V1Acc: ${(data.accuracy * 100).toFixed(2)}%`)),
- );
- const m_300 = HTML("span", {class: "score-detail score-detail-osu-300"},
- HTML("span", {class: "osu-300"},
- HTML("300")
- ),
- HTML("span", {class: "score-detail-data-text"},
- HTML(`${data.statistics.great + data.statistics.perfect}`)
- )
- );
- const s100 = HTML("span", {class: "score-detail score-detail-osu-100"},
- HTML("span", {class: "osu-100"},
- HTML("100")
- ),
- HTML("span", {class: "score-detail-data-text"},
- HTML(`${data.statistics.ok + data.statistics.good}`)
- )
- );
- const s50 = HTML("span", {class: "score-detail score-detail-osu-50"},
- HTML("span", {class: "osu-50"},
- HTML("50")
- ),
- HTML("span", {class: "score-detail-data-text"},
- HTML(`${data.statistics.meh}`)
- )
- );
- const s0 = HTML("span", {class: "score-detail score-detail-osu-miss"},
- HTML("span", {class: "osu-miss"},
- html(`<svg viewBox="0 0 128 128" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
- <filter id="blur">
- <feFlood flood-color="red" flood-opacity="0.5" in="SourceGraphic" />
- <feComposite operator="in" in2="SourceGraphic" />
- <feGaussianBlur stdDeviation="6" />
- <feComponentTransfer result="glow1">
- <feFuncA type="linear" slope="10" intercept="0" />
- </feComponentTransfer>
- <feGaussianBlur in="glow1" stdDeviation="1" result="glow2" />
- <feMerge>
- <feMergeNode in="SourceGraphic" />
- <feMergeNode in="glow2" />
- </feMerge>
- </filter>
- <filter id="blur2"> <feGaussianBlur stdDeviation="0.2"/> </filter>
- <path id="cross" d="M 26 16 l -10 10 l 38 38 l -38 38 l 10 10 l 38 -38 l 38 38 l 10 -10 l -38 -38 l 38 -38 l -10 -10 l -38 38 Z" />
- <use href="#cross" stroke="red" stroke-width="2" fill="transparent" filter="url(#blur)"/>
- <use href="#cross" fill="white" stroke="transparent" filter="url(#blur2)"/>
- </svg>`)
- ),
- HTML("span", {class: "score-detail-data-text"},
- HTML(`${data.statistics.miss}`)
- )
- );
- db.replaceChildren(m_300, s100, s50, s0);
- break;
- }
- case 1:{
- break;
- }
- case 2:{
- break;
- }
- case 3:{
- const v2acc = (320*data.statistics.perfect+300*data.statistics.great+200*data.statistics.good+100*data.statistics.ok+50*data.statistics.meh)/(320*(data.statistics.perfect+data.statistics.great+data.statistics.good+data.statistics.ok+data.statistics.meh+data.statistics.miss));
- du.replaceChildren(
- HTML("span", {class: "play-detail__accuracy"}, HTML(`V1Acc: ${(data.accuracy * 100).toFixed(2)}%`)),
- HTML("span", {class: "play-detail__accuracy ppAcc"}, HTML(`PPAcc: ${(v2acc * 100).toFixed(2)}%`)),
- );
- if(data.pp){
- const lostpp = data.pp * (0.2 / (Math.min(Math.max(v2acc, 0.8), 1) - 0.8) - 1);
- ele.querySelector(".play-detail__pp").appendChild(HTML("span", {class: "lost-pp"}, HTML(`-${lostpp.toPrecision(4)}`)));
- }
- const M_300 = Number(data.statistics.perfect) / Math.max(Number(data.statistics.great), 1);
- db.replaceChildren(
- HTML("span", {class: "score-detail score-detail-mania-max-300"},
- HTML("span", {class: "mania-max"}, HTML("M")),
- HTML("/"),
- HTML("span", {class: "mania-300"}, HTML("300")),
- HTML("span", {class: "score-detail-data-text"}, HTML(`${M_300.toFixed(2)}`))
- ),
- HTML("span", {class: "score-detail score-detail-mania-max-200"},
- HTML("span", {class: "mania-200"}, HTML("200")),
- HTML("span", {class: "score-detail-data-text"}, HTML(data.statistics.good))
- ),
- HTML("span", {class: "score-detail score-detail-mania-max-100"},
- HTML("span", {class: "mania-100"}, HTML("100")),
- HTML("span", {class: "score-detail-data-text"}, HTML(data.statistics.ok))
- ),
- HTML("span", {class: "score-detail score-detail-mania-max-50"},
- HTML("span", {class: "mania-50"}, HTML("50")),
- HTML("span", {class: "score-detail-data-text"}, HTML(data.statistics.meh))
- ),
- HTML("span", {class: "score-detail score-detail-mania-max-0"},
- HTML("span", {class: "mania-miss"}, HTML("miss")),
- HTML("span", {class: "score-detail-data-text"}, HTML(data.statistics.miss))
- )
- );
- break;
- }
- }
- }
- let lastLocationStr = "";
- let lastInitData;
- const OsuLevelToExp = (n) => {
- if(n <= 100) return 5000 / 3 * (4 * n ** 3 - 3 * n ** 2 - n) + 1.25 * 1.8 ** (n - 60);
- else return 26_931_190_827 + 99_999_999_999 * (n - 100);
- }
- const OsuExpValToStr = (num) => {
- let suffix = "";
- let exp = Math.log10(num);
- if(exp >= 12){
- return `${(num / 10 ** 12).toPrecision(4)}T`;
- }
- else if(exp >= 9){
- return `${(num / 10 ** 9).toPrecision(4)}B`;
- }
- else if(exp >= 6){
- return `${(num / 10 ** 6).toPrecision(4)}M`;
- }
- else if(exp >= 4){
- return `${(num / 10 ** 3).toPrecision(4)}K`;
- }
- else return `${num.toPrecision(4)}`;
- }
- const ImproveProfile = (message) => {
- let initData;
- if(window.location.toString() === lastLocationStr){
- initData = lastInitData;
- }
- else{
- initData = JSON.parse(document.querySelector(".js-react--profile-page.osu-layout.osu-layout--full").dataset.initialData);
- lastLocationStr = window.location.toString();
- lastInitData = initData;
- }
- const userId = initData.user.id;
- const modestr = initData.current_mode;
- if(!(userId === message.userId && modestr === message.mode)) return;
- const ttscore = initData.user.statistics.total_score;
- const lvl = initData.user.statistics.level.current;
- const upgradescore = Math.round(OsuLevelToExp(lvl + 1) - OsuLevelToExp(lvl));
- const lvlscore = ttscore - Math.round(OsuLevelToExp(lvl));
- document.querySelector("div.bar__text").textContent = `${OsuExpValToStr(lvlscore)}/${OsuExpValToStr(upgradescore)} (${(lvlscore/upgradescore * 100).toPrecision(3)}%)`;
- let ppDiv;
- document.querySelectorAll("div.value-display.value-display--plain").forEach((ele) => {
- if(ele.querySelector("div.value-display__label").textContent === "pp") ppDiv = ele;
- });
- ppDiv.children[1].children[0].childNodes[0].nodeValue = initData.user.statistics.pp;
- const obcb = () => {
- ob.disconnect();
- let result = true;
- switch(message.type){
- case "top_ranks":
- result &&= TopRanksWorker(message.data.pinned.items, 0);
- result &&= TopRanksWorker(message.data.best.items, 1);
- result &&= TopRanksWorker(message.data.firsts.items, 2);
- break;
- case "firsts":
- result &&= TopRanksWorker(message.data, 2);
- break;
- case "pinned":
- result &&= TopRanksWorker(message.data, 0);
- break;
- case "best":
- result &&= TopRanksWorker(message.data, 1);
- break;
- case "historical":
- result &&= TopRanksWorker(message.data.recent.items, 0, "historical");
- break;
- case "recent":
- result &&= TopRanksWorker(message.data, 0, "historical");
- break;
- }
- if(!result) ob.observe(document, {subtree: true, childList: true});
- };
- const ob = new MutationObserver(obcb);
- ob.observe(document, {subtree: true, childList: true});
- obcb();
- }
- let wloc = "";
- const WindowLocationChanged = () => {
- if(window.location !== wloc){
- wloc = window.location;
- return true;
- }
- else return false;
- }
- const InsertStyleSheet = () => {
- //const sheetId = "osu-web-enhancement-general-stylesheet";
- const s = new CSSStyleSheet();
- s.replaceSync(inj_style);
- document.adoptedStyleSheets = [...document.adoptedStyleSheets, s];
- }
- const OnBeatmapsetDownload = (message) => {
- beatmapsets.add(message.beatmapsetId);
- }
- const OnMutation = (mulist) => {
- mut.disconnect();
- PlaceSelectOsuDbButton();
- FilterBeatmapSet();
- mut.observe(document, {childList: true, subtree: true});
- };
- const MessageFilter = (message) => {
- switch(message.type){
- case "beatmapset_download_complete": OnBeatmapsetDownload(message); break;
- }
- }
- const WindowMessageFilter = (event) => {
- if(event.source === window && event?.data?.id === "osu!web enhancement"){
- ImproveProfile(event.data);
- }
- }
- window.addEventListener("message", WindowMessageFilter);
- const mut = new MutationObserver(OnMutation);
- mut.observe(document, {childList: true, subtree: true});
- InsertStyleSheet();