您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
download all the ranked/gd/pending/graveyard beatmaps by a user
// ==UserScript== // @name trolloscript osu! profile beatmaps downloader // @namespace https://osu.ppy.sh/users/9228032 // @version 0.3.1.3 // @description download all the ranked/gd/pending/graveyard beatmaps by a user // @author trollocat // @match http://osu.ppy.sh/users/* // @match https://osu.ppy.sh/users/* // @icon https://www.google.com/s2/favicons?sz=64&domain=ppy.sh // @grant none // @license MIT // ==/UserScript== (() => { "use strict"; ///////////// // constantes const beatmapTitulos = [ "Favourite", "Ranked", "Loved", "Guest", "Pending", "Graveyard", "Nominated", ]; // fin constantes ///////////////// // helper function const removeDuplicates = (arr) => { return arr.filter((item, index) => arr.indexOf(item) === index); }; const getBeatmapsPanel = () => { // agarrar el panel de Beatmaps const paneles = document.getElementsByClassName( "js-switchable-mode-page--scrollspy" ); for (let i = 0; i < paneles.length; i++) { if (paneles[i].dataset.pageId === "beatmaps") { return paneles[i]; } } }; const buscarLinks = (index) => { // funcion que busca genericamente todos los links en el arrayTitulo[index] que le digas (ranked, pending, gd, etc) // y devuelve un array con los link mirrors listos para descargar const beatmapsPanel = getBeatmapsPanel(); const arrayTitulos = beatmapsPanel.getElementsByClassName( "title--page-extra-small" ); let beatmapsTitle = arrayTitulos[index]; let cantidadDeMapas = arrayTitulos[index].innerText.split("Beatmaps")[1]; // el numerito de cantidad de mapas de X Beatmaps if (cantidadDeMapas == 0) return []; // lo siguiente al titulo X Beatmaps, o sea toda la lista de mapas, y me guardo todos los tag "a" (links) let arrayDeAs = Array.from( beatmapsTitle.nextElementSibling.getElementsByTagName("a") ); // solo quiero la propiedad href guardada en el array let arrayDeHrefs = arrayDeAs.map((a) => a.href); // filtro los elementos para que Sí tengan formato de link de beatmap y No terminen en /download let arrayDeHrefsSeleccionados = arrayDeHrefs.filter( (href) => href.startsWith("https://osu.ppy.sh/beatmapsets/") && !href.endsWith("/download") ); // lo filtro de vuelta para remover duplicados let arrayDeHrefsUnicos = removeDuplicates(arrayDeHrefsSeleccionados); // ahora sí creo links con los beatmapsetID en los mirrors :3 let arraySayoNeriChimu = arrayDeHrefsUnicos.map((link, i) => { // uso i para crear un patrón alternado entre los 3 mirror servers switch (i%3) { case 0: // nerinyan no video return `https://api.nerinyan.moe/d/${ link.split("https://osu.ppy.sh/beatmapsets/")[1] }?nv=1`; case 1: // sayobot no video return `https://dl.sayobot.cn/beatmaps/download/novideo/${ link.split("https://osu.ppy.sh/beatmapsets/")[1] }`; case 2: // chimu.moe return `https://api.chimu.moe/v1/download/${ link.split("https://osu.ppy.sh/beatmapsets/")[1] }?n=1`; } }); console.log(arraySayoNeriChimu); return arraySayoNeriChimu; }; // tengo ver los checkboxes marcados del 0 al 6, cada uno tiene un array vacio por defecto y en el arrayTotal // los sumo tipo [...arrayFavourites, ...arrayRanked, etc] // al final habia una funcion para eso XD .flat() // tendria que hacer una funcion generica que retorne un array de los links y usarla en cada uno de los 7 casos // y despues los sumo en el array final const buscarAllLinksCheckeados = () => { const checkboxes = []; const arrDeArraysSayoNeriChimu = []; // for para almacenar los checkbox como htmlelements for (let i = 0; i <= 6; i++) { checkboxes.push( document.getElementById("checkbox-" + beatmapTitulos[i].toLowerCase()) ); } // voy a tener los checkbox en un array checkboxes y les voy a hacer if(checkboxes[i].checked) // si es true llamo a buscarLinks(i) y lo pusheo en arrDeArraysSayoNeriChimu for (let i = 0; i <= 6; i++) { if (checkboxes[i].checked) arrDeArraysSayoNeriChimu.push(buscarLinks(i)); } // al final flateo todos los arrays dentro del array en uno solo gigante concatenado return arrDeArraysSayoNeriChimu.flat(); }; const crearTrollodescargador = () => { // me agarro el espacio donde aparecen los amigos y suscriptores y le agrego ahi un boton mio duplicado // boton trollodescargador const [profileBar] = document.getElementsByClassName("profile-detail-bar"); let divBtn = document.createElement("div"); let buttonBtn = document.createElement("button"); let spanBtn = document.createElement("span"); divBtn.title = "trollocat"; buttonBtn.className = "user-action-button user-action-button--profile-page"; buttonBtn.id = "trolloBtn"; spanBtn.className = "user-action-button__counter"; spanBtn.innerHTML = "trollodescargador"; spanBtn.style.padding = "0 5px 0 5px"; profileBar .insertBefore(divBtn, profileBar.children[2]) .appendChild(buttonBtn) .appendChild(spanBtn); // hacer funcion de click trollodescargador buttonBtn.addEventListener("click", trollodescargadorHandler); }; const crearCheckboxes = () => { // checkboxes // fav, rank, lov, gd, pd, grave, nom // 0 1 2 3 4 5 6 const [profileBar] = document.getElementsByClassName("profile-detail-bar"); let divChBx = document.createElement("div"); divChBx.className = "div-checkboxes"; divChBx.style.display = "flex"; divChBx.style.paddingLeft = ".85rem"; profileBar.appendChild(divChBx); for (let i = 0; i < beatmapTitulos.length; i++) { let divElemento = document.createElement("div"); divElemento.id = "elemento-" + i; divElemento.style.display = "flex"; divElemento.style.alignItems = "center"; divChBx.appendChild(divElemento); let checkbox = document.createElement("input"); checkbox.id = "checkbox-" + beatmapTitulos[i].toLowerCase(); checkbox.type = "checkbox"; checkbox.style.margin = "0"; divElemento.appendChild(checkbox); let label = document.createElement("label"); label.id = "label-" + beatmapTitulos[i].toLowerCase(); label.innerHTML = beatmapTitulos[i]; label.style.paddingLeft = "2px"; label.style.paddingRight = "8px"; label.style.textAlign = "left"; label.style.margin = "0"; label.style.userSelect = "none"; // hacer que tocar el label funcione igual que tocar al checkbox (que esta justo atras, por eso previous) label.addEventListener( "click", (e) => (e.target.previousElementSibling.checked = !e.target.previousElementSibling.checked) ); divElemento.appendChild(label); } }; const crearShowMoreStatus = () => { const [profileBar] = document.getElementsByClassName("profile-detail-bar"); const loadStatus = document.createElement("label"); loadStatus.id = "loadStatus"; loadStatus.innerHTML = "Cargar mapas"; loadStatus.style.color = "#999999"; loadStatus.style.display = "flex"; loadStatus.style.borderRadius = "5rem"; loadStatus.style.padding = "0 1rem 0 1rem"; loadStatus.style.margin = "0 0 0 22.3rem"; loadStatus.style.alignItems = "center"; loadStatus.style.justifyContent = "center"; loadStatus.style.width = "14.3rem"; // loadStatus.style.whiteSpace = "nowrap"; loadStatus.style.backgroundColor = "#1C1719"; profileBar.appendChild(loadStatus); loadStatus.addEventListener("click", cargarMapas); }; const cargarMapas = () => { // elemento html let loadStatus = document.getElementById("loadStatus"); loadStatus.innerHTML = "X Cargando mapas..."; loadStatus.style.color = "#bf2e2e"; const beatmapsPanel = getBeatmapsPanel(); const tocarShowMores = (panel) => { // funcion que toca todos los "Show More" dentro de un panel let showMores = panel.getElementsByClassName("show-more-link"); for (let i = 0; i < showMores.length; i++) showMores[i].click(); if (showMores.length === 0) { clearInterval(interval); loadStatus.innerHTML = "✔️ Mapas cargados"; loadStatus.style.color = "#4dbf2e"; // alert("Terminaron de cargarse en pantalla los mapas!"); } }; let interval = setInterval(tocarShowMores, 500, beatmapsPanel); }; const trollodescargadorHandler = () => { // links de mirrors let urls = buscarAllLinksCheckeados(); if (!urls.length) { return alert("No hay mapas en las categorías seleccionadas."); } const download = (urls) => { let url = urls.pop(); let a = document.createElement("a"); a.setAttribute("href", url); a.setAttribute("download", ""); a.setAttribute("target", "_blank"); a.click(); if (urls.length == 0) { clearInterval(interval); } }; let interval = setInterval(download, 2500, urls); }; //////////////////////////////////////////////////////// // constructor de elementos HTML cuando cargue la pagina setTimeout(() => { crearTrollodescargador(); crearShowMoreStatus(); crearCheckboxes(); }, 1500); // fin de constructor de elementos HTML cuando cargue la pagina /////////////////////////////////////////////////////////////// })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址