您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Filter beatmap by favorites (osu! website only)
- // ==UserScript==
- // @name osu beatmap filter
- // @name:zh-TW osu 圖譜過濾器
- // @namespace https://gf.qytechs.cn/zh-TW/users/891293
- // @version 1.1.1
- // @description Filter beatmap by favorites (osu! website only)
- // @description:zh-TW 依照收藏數過濾 beatmap (僅限 osu! 網站)
- // @author Archer_Wn
- // @match https://osu.ppy.sh/*
- // @grant none
- // @license MIT
- // ==/UserScript==
- // Options (選項)
- const options = {
- // global options (全域選項)
- global: {
- // enable animation (啟用動畫)
- animation: {
- // enable (啟用)
- enable: true,
- // duration [ms] (持續時間 [毫秒])
- duration: 700,
- },
- // filter method [threshold, percentile] (過濾方法 [門檻, 百分位數])
- filterMethod: "percentile",
- // compare method [less, lessEqual, greater, greaterEqual] (比較方法 [小於, 小於等於, 大於, 大於等於])
- compareMethod: "lessEqual",
- // opacity of filtered beatmap [0~1] (過濾後的 beatmap 透明度 [0~1])
- opacity: 0.15,
- },
- // favorites filter (收藏數過濾)
- favorites: {
- // threshold of favorite count (收藏數門檻)
- threshold: 100,
- // percentile of favorite count (收藏數百分位數)
- percentile: 75,
- },
- };
- class BeatmapFilter {
- constructor() {
- this.beatmapItems = [];
- }
- addBeatmapItem(beatmapItem) {
- const beatmapInfo = this._parseBeatmapItem(beatmapItem);
- this.beatmapItems.push({ beatmapItem, beatmapInfo });
- this._filter();
- }
- removeBeatmapItem(beatmapItem) {
- const index = this.beatmapItems.findIndex(
- (item) => item.beatmapItem === beatmapItem
- );
- if (index === -1) return;
- this.beatmapItems.splice(index, 1);
- this._filter();
- }
- _filter() {
- if (options.global.animation.enable) {
- for (const { beatmapItem } of this.beatmapItems) {
- beatmapItem.style.transition = `opacity ${options.global.animation.duration}ms`;
- }
- }
- switch (options.global.filterMethod) {
- case "threshold":
- this._filterByThreshold();
- break;
- case "percentile":
- this._filterByPercentile();
- break;
- }
- }
- _filterByThreshold() {
- for (const { beatmapItem, beatmapInfo } of this.beatmapItems) {
- switch (options.global.compareMethod) {
- case "less":
- beatmapItem.style.opacity =
- beatmapInfo.favoriteCount < options.favorites.threshold
- ? options.global.opacity
- : 1;
- break;
- case "lessEqual":
- beatmapItem.style.opacity =
- beatmapInfo.favoriteCount <= options.favorites.threshold
- ? options.global.opacity
- : 1;
- break;
- case "greater":
- beatmapItem.style.opacity =
- beatmapInfo.favoriteCount > options.favorites.threshold
- ? options.global.opacity
- : 1;
- break;
- case "greaterEqual":
- beatmapItem.style.opacity =
- beatmapInfo.favoriteCount >= options.favorites.threshold
- ? options.global.opacity
- : 1;
- break;
- }
- }
- }
- _filterByPercentile() {
- const favoriteCounts = this.beatmapItems
- .map((item) => item.beatmapInfo.favoriteCount)
- .sort((a, b) => a - b);
- const threshold =
- favoriteCounts[
- Math.floor(
- (options.favorites.percentile / 100) * (favoriteCounts.length - 1)
- )
- ];
- for (const { beatmapItem, beatmapInfo } of this.beatmapItems) {
- switch (options.global.compareMethod) {
- case "less":
- beatmapItem.style.opacity =
- beatmapInfo.favoriteCount < threshold ? options.global.opacity : 1;
- break;
- case "lessEqual":
- beatmapItem.style.opacity =
- beatmapInfo.favoriteCount <= threshold ? options.global.opacity : 1;
- break;
- case "greater":
- beatmapItem.style.opacity =
- beatmapInfo.favoriteCount > threshold ? options.global.opacity : 1;
- break;
- case "greaterEqual":
- beatmapItem.style.opacity =
- beatmapInfo.favoriteCount >= threshold ? options.global.opacity : 1;
- break;
- }
- }
- }
- _convertToNumber(str) {
- const unitMap = {
- K: 1000,
- M: 1000000,
- 萬: 10000,
- 億: 100000000,
- };
- const regex = new RegExp(`([0-9.]+)(${Object.keys(unitMap).join("|")})?`);
- const result = regex.exec(str);
- if (!result) return 0;
- const number = parseFloat(result[1]);
- const unit = result[2];
- return number * (unit ? unitMap[unit] : 1);
- }
- _parseBeatmapItem(beatmapItem) {
- const beatmapInfo = {};
- // Extract necessary information
- beatmapInfo.favoriteCount = this._convertToNumber(
- beatmapItem.querySelectorAll(
- ".beatmapset-panel__stats-item--favourite-count span"
- )[1].textContent
- );
- return beatmapInfo;
- }
- }
- (function () {
- "use strict";
- // define BeatmapFilter
- let beatmapFilter;
- // if beatmapList loaded, start main function
- new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- if (mutation.addedNodes.length < 1) return;
- for (const node of mutation.addedNodes) {
- if (!node.querySelectorAll) return;
- const divs = node.querySelectorAll("div");
- for (const div of divs) {
- if (div.classList.contains("beatmapsets__items")) {
- // create beatmap filter
- beatmapFilter = new BeatmapFilter();
- // start main function
- main();
- return;
- }
- }
- }
- });
- }).observe(document, {
- childList: true,
- subtree: true,
- });
- // main function
- function main() {
- // get beatmap list
- const beatmapList = document.querySelector(".beatmapsets__items");
- if (!beatmapList) {
- console.error("beatmapList not found");
- return;
- }
- // handle beatmap items that already loaded
- const beatmapItems = beatmapList.querySelectorAll(".beatmapsets__item");
- for (const beatmapItem of beatmapItems) {
- beatmapFilter.addBeatmapItem(beatmapItem);
- }
- // observe beatmap list
- new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- if (mutation.type !== "childList") return;
- // node added
- if (mutation.addedNodes.length >= 1) {
- for (const beatmapItem of mutation.addedNodes[0].querySelectorAll(
- ".beatmapsets__item"
- )) {
- beatmapFilter.addBeatmapItem(beatmapItem);
- }
- }
- // node removed
- if (mutation.removedNodes.length >= 1) {
- for (const beatmapItem of mutation.removedNodes[0].querySelectorAll(
- ".beatmapsets__item"
- )) {
- beatmapFilter.removeBeatmapItem(beatmapItem);
- }
- }
- });
- }).observe(beatmapList, {
- attributes: true,
- childList: true,
- subtree: true,
- });
- }
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址