osuMapFilter

filter osu maps

  1. // ==UserScript==
  2. // @name osuMapFilter
  3. // @namespace https://greasyfork.org/users/110545
  4. // @version 0.5
  5. // @description filter osu maps
  6. // @author x94fujo6
  7. // @match https://osu.ppy.sh/*
  8. // @grant none
  9. // ==/UserScript==
  10. /* jshint esversion: 9 */
  11. /*
  12. you need map list to make this work!!!
  13. https://github.com/x94fujo6rpg/osuMapFilter
  14. */
  15.  
  16. (function () {
  17. 'use strict';
  18. const LS = window.localStorage;
  19. let map_list = [],
  20. stop = false,
  21. debug_msg = false,
  22. mode = 3, // don't touch this
  23. // 1:array 2:hash 3:set, https://jsbench.me/zfknghmteu/2
  24. tester;
  25.  
  26. function getTester() {
  27. switch (mode) {
  28. case 1:
  29. tester = (id) => { return map_list.includes(id); };
  30. break;
  31. case 2:
  32. tester = (id) => { return map_list[id]; };
  33. break;
  34. case 3:
  35. tester = (id) => { return map_list.has(id); };
  36. break;
  37. default:
  38. tester = false;
  39. break;
  40. }
  41. }
  42.  
  43. getTester();
  44. if (!tester) return console.log("tester not set");
  45. window.onload = main();
  46.  
  47. async function main() {
  48. let link = window.location.href;
  49. await wait_tab();
  50. if (link.includes("/beatmapsets")) {
  51. if (link.match(/beatmapsets\/\d+/)) {
  52. waitHTML(".beatmapset-header__buttons [href$='download']", mapPage);
  53. } else {
  54. mapListPage();
  55. }
  56. } else {
  57. console.log("no match, abort");
  58. }
  59. }
  60.  
  61. function mapPage() {
  62. let dlb = document.querySelector(".beatmapset-header__buttons [href$='download']");
  63. let map_id = dlb.href.match(/beatmapsets\/(\d+)\/download/)[1];
  64. mapListPage(false);
  65.  
  66. dlb.onclick = () => {
  67. console.log(`download ${map_id}, add to list`);
  68. addItem(map_id);
  69. saveData({
  70. mode,
  71. map_list,
  72. });
  73. };
  74. }
  75.  
  76. function mapListPage(run = true) {
  77. if (run) {
  78. creatbox();
  79. document.getElementById("read_osu_map_list")
  80. .addEventListener("change", readfile, false);
  81. }
  82.  
  83. if (LS.getItem("mode")) {
  84. console.log("found old data, load it");
  85. mode = Number(LS.getItem("mode"));
  86. if (mode == 3) {
  87. map_list = new Set(JSON.parse(LS.getItem("map_list")));
  88. } else {
  89. map_list = JSON.parse(LS.getItem("map_list"));
  90. }
  91. getTester();
  92. console.log({
  93. mode,
  94. tester,
  95. });
  96. stop = false;
  97. if (run) {
  98. setTimeout(runfilter, 1000);
  99. }
  100. }
  101. }
  102.  
  103. function wait_tab() {
  104. return new Promise(resolve => {
  105. if (document.visibilityState === "visible") return resolve();
  106. console.log("tab in background, script paused");
  107. document.addEventListener("visibilitychange", () => {
  108. if (document.visibilityState === "visible") {
  109. console.log("script unpaused");
  110. return resolve();
  111. }
  112. });
  113. });
  114. }
  115.  
  116. function waitHTML(css_selector, run) {
  117. let id = setInterval(() => {
  118. if (document.querySelectorAll(css_selector).length) {
  119. clearInterval(id);
  120. run();
  121. console.log(`found [${css_selector}]`);
  122. } else {
  123. console.log(`[${css_selector}] not found`);
  124. }
  125. }, 1000);
  126. }
  127.  
  128. function updatestatus(text = "") {
  129. document.getElementById("current_filter_status").textContent = text;
  130. }
  131.  
  132. function runfilter() {
  133. let length = mode == 3 ? map_list.size : map_list.length;
  134. if (stop || length == 0) return updatestatus("Filter stopped");
  135. let all_map = document.querySelectorAll(".beatmapsets__item");
  136. let count = all_map.length;
  137. updatestatus(`Filter is running\n${length} maps in list`);
  138. all_map.forEach(item => {
  139. setTimeout(() => {
  140. if (stop || length === 0) return updatestatus("Filter stopped");
  141. let map_id = item.querySelector(`[data-audio-url]`).getAttribute("data-audio-url").match(/\/(\d+)\.mp3/);
  142. let link = item.querySelectorAll("a");
  143. let dlb = item.querySelector("[href$='/download']");
  144. if (!map_id) return;
  145. map_id = map_id[1];
  146. if (tester(map_id)) {
  147. item.style.opacity = "10%";
  148. if (debug_msg) console.log(`${count} hide ${map_id}`);
  149. } else {
  150. item.style.opacity = "100%";
  151. }
  152. link.forEach(a => {
  153. if (!a.href.includes("/download")) {
  154. a.setAttribute("target", "_blank");
  155. }
  156. });
  157. dlb.onclick = () => {
  158. console.log(`download ${map_id}, add to list`);
  159. addItem(map_id);
  160. saveData({
  161. mode,
  162. map_list,
  163. });
  164. };
  165. count--;
  166. if (count == 0) {
  167. setTimeout(runfilter, 200);
  168. }
  169. }, 0);
  170. });
  171. }
  172.  
  173. function addItem(id = "") {
  174. switch (mode) {
  175. case 1:
  176. map_list.push(id);
  177. break;
  178. case 2:
  179. map_list[id] = true;
  180. break;
  181. case 3:
  182. map_list.add(id);
  183. break;
  184. default:
  185. throw Error("unknown mode");
  186. }
  187. }
  188.  
  189. function creatbox() {
  190. let newbox = document.createElement("div");
  191. Object.assign(newbox.style, {
  192. position: "fixed",
  193. top: "20%",
  194. right: "5%",
  195. width: "200px",
  196. "z-index": "100",
  197. border: "2px",
  198. "border-color": "rgba(255, 255, 255, 0.7)",
  199. "border-style": "ridge",
  200. "background-color": "rgba(255, 255, 255, 0.3)",
  201. });
  202. let readfile = document.createElement("input");
  203. Object.assign(readfile, {
  204. type: "file",
  205. id: "read_osu_map_list"
  206. });
  207. let status = document.createElement("span");
  208. Object.assign(status, {
  209. id: "current_filter_status",
  210. style: "word-wrap:break-word;white-space:pre-line;",
  211. textContent: "Load map_list.txt to start"
  212. });
  213. let button = document.createElement("button");
  214. Object.assign(button, {
  215. textContent: "Stop Script",
  216. style: "color: black",
  217. onclick: function () {
  218. if (!stop) {
  219. stop = true;
  220. updatestatus("Filter stopped");
  221. button.textContent = "Resume Script";
  222. } else {
  223. stop = false;
  224. button.textContent = "Stop Script";
  225. runfilter();
  226. }
  227. }
  228. });
  229. newbox.appendChild(readfile);
  230. newbox.appendChild(status);
  231. newbox.appendChild(button);
  232. document.body.appendChild(newbox);
  233. }
  234.  
  235. function readfile(myfile) {
  236. let file = myfile.target.files[0];
  237. if (!file) {
  238. stop = true;
  239. updatestatus("Filter stopped");
  240. return;
  241. }
  242. let reader = new FileReader();
  243. reader.onload = function (myfile) {
  244. let contents = myfile.target.result;
  245. map_list = JSON.parse(contents);
  246. stop = 0;
  247. if (map_list.length > 0) {
  248. switch (mode) {
  249. case 1:
  250. console.log("mode: array");
  251. break;
  252. case 2:
  253. console.log("mode: hash");
  254. let new_obj = {};
  255. map_list.forEach(id => new_obj[id] = true);
  256. new_obj.length = map_list.length;
  257. map_list = new_obj;
  258. break;
  259. case 3:
  260. console.log("mode: set");
  261. map_list = new Set(map_list);
  262. break;
  263. }
  264. runfilter();
  265. saveData({
  266. mode,
  267. map_list,
  268. });
  269. }
  270. };
  271. reader.readAsText(file);
  272. }
  273.  
  274. function removeData(data = {}) {
  275. return new Promise((resolve, reject) => {
  276. try {
  277. for (let key in data) {
  278. LS.removeItem(key);
  279. }
  280. } catch (e) {
  281. console.log(e);
  282. reject();
  283. }
  284. resolve();
  285. });
  286. }
  287.  
  288. async function saveData(data = {}) {
  289. //await removeData(data);
  290. for (let key in data) {
  291. if (key == "map_list") {
  292. let json;
  293. if (data[key] instanceof Set) {
  294. json = JSON.stringify([...data[key]]);
  295. } else {
  296. json = JSON.stringify(data[key]);
  297. }
  298. LS.setItem(key, json);
  299. continue;
  300. }
  301.  
  302. LS.setItem(key, data[key]);
  303. }
  304. return true;
  305. }
  306. })();
  307.