// ==UserScript== // @name SimDevices Osu Beatmap Downloader Plugin // @name:zh-CN SimDevices Osu 谱面下载器插件 // @include http*://osu.ppy.sh/* // @copyright 2020, Handle // @version 0.5.2 // @description Add extra download buttons on beatmap page for SimDevices Beatmap Downloader on osu.ppy.sh // @description:zh-CN 在 osu! 谱面下载页面上添加额外的按钮,可以唤醒下载器自动下载并导入谱面。 // @author Handle // @namespace https://github.com/SimDevices-Project // @supportURL https://github.com/SimDevices-Project/beatmap-downloader-user-script/issues // @grant none // @downloadURL https://update.greasyfork.cloud/scripts/401946/SimDevices%20Osu%20Beatmap%20Downloader%20Plugin.user.js // @updateURL https://update.greasyfork.cloud/scripts/401946/SimDevices%20Osu%20Beatmap%20Downloader%20Plugin.meta.js // ==/UserScript== ;(function () { 'use strict' const styleClassName = '_beatmap_downloader_style_' const beapmapPageDownloadBtnClassName = '_beatmap_downloader_btn_' const searchPageBtnClassName = `_beatmap_downloader_quick_btn_` const searchPageBtnText = 'Launch Downloader' const searchPageDownloadTipClassName = `_beatmap_downloader_qtip_` const insertStyle = () => { const styleDOM = document.createElement('style') styleDOM.classList.add(styleClassName) styleDOM.innerHTML = ` .${searchPageBtnClassName}:hover { text-decoration: none!important; } .${searchPageDownloadTipClassName}:after{ display:block; content:''; border-width:8px 5px 8px 5px; border-style:solid; border-color:hsl(var(--base-hue),10%,10%) transparent transparent transparent; position:absolute; left:calc(50% - 2px); top:100%; } ` if (!document.querySelector(`.${styleClassName}`)) { document.head.append(styleDOM) } } const formatURL = () => { const URL = document.URL const [domain, argument] = URL.substr(URL.indexOf('//') + 2).split('#') const pathname = window.location.pathname.substr(1) const [type, sid] = pathname.split('/') switch (type) { case 'beatmapsets': if (sid && sid.length) { const [mode, bid] = argument || [] return { id: sid, type: 'beatmapset', } } else { return { id: 0, type: 'beatmapsearch', } } break default: return { id: 0, type: type } } } const insertBeatmapPageDownloadBtn = (id = 1011011, type = 's') => { const htmlText = ` 启动 Beatmap Downloader ` const htmlDOM = document.createRange().createContextualFragment(htmlText) const btnContainer = document.querySelector('.beatmapset-header__buttons') const downloaderBtnQueryWith = `.${beapmapPageDownloadBtnClassName}` const downloaderBtnDOM = document.querySelector(downloaderBtnQueryWith) if (!downloaderBtnDOM) { btnContainer.insertBefore(htmlDOM, btnContainer.lastElementChild) } } const getSearchPageDownloadBtn = (id = 1011011, type = 's') => { const htmlText = ` ` /** * @type {HTMLAnchorElement} */ const htmlDOM = document.createRange().createContextualFragment(htmlText) return htmlDOM } const insertSearchPageDownloadTip = () => { const insertHTML = `
` /** * @type {HTMLDivElement} */ const htmlDOM = document.createRange().createContextualFragment(insertHTML) const qTipQueryWith = `.${searchPageDownloadTipClassName}` const qTipDOM = document.querySelector(qTipQueryWith) if (!qTipDOM) { document.body.appendChild(htmlDOM) } } /** * 获取DOM元素绝对坐标 * @param {HTMLElement} element 要获取坐标的元素 */ const getAbsolutePostion = (element) => { const rect = element.getBoundingClientRect() const X = rect.left + document.documentElement.scrollLeft const Y = rect.top + document.documentElement.scrollTop const width = rect.width const height = rect.height return { x: X, y: Y, width, height, } } const setSearchPageDownloadTipPosition = ({ x = 0, y = 0, show = true } = {}) => { const qTipQueryWith = `.${searchPageDownloadTipClassName}` /** * @type {HTMLDivElement} */ const qTipDOM = document.querySelector(qTipQueryWith) if (!qTipDOM) { return } if (!show) { qTipDOM.style.display = 'none' qTipDOM.style.opacity = 0 } else { if (qTipDOM.style.display === 'block') { } else { qTipDOM.style.display = 'block' let opacitySet = 0 if (qTipDOM.dataset.timer) { clearInterval(qTipDOM.dataset.timer) } qTipDOM.style.opacity = opacitySet.toString(10) const easeIn = setInterval(() => { opacitySet += 0.2 if (opacitySet >= 1) { opacitySet = 1 clearInterval(easeIn) } qTipDOM.style.opacity = opacitySet.toString(10) }, 16.67) qTipDOM.dataset.timer = easeIn } } qTipDOM.style.left = `${x}px` qTipDOM.style.top = `${y}px` } const insertSearchPageDownloadBtns = (target = document) => { /** * @type {NodeListOf