Greasy Fork is available in English.

osu! my download

osu beatmap download from mirror. osu beatmap镜像站下载。支持的镜像站点:inso.link、osu.sayobot.cn、osu.direct、nerinyan.moe。也可以自行添加。

// ==UserScript==
// @name           osu! my download
// @description    osu beatmap download from mirror. osu beatmap镜像站下载。支持的镜像站点:inso.link、osu.sayobot.cn、osu.direct、nerinyan.moe。也可以自行添加。
// @author         dazzulay
// @copyright      2024, dazzulay
// @version        2.4.7
// @license        GPLv3
// @icon           http://osu.ppy.sh/favicon.ico
// @match          http*://osu.ppy.sh/*
// @match          http*://old.ppy.sh/*
// @grant          GM_registerMenuCommand
// @grant          GM_setValue
// @grant          GM_getValue
// @namespace      https://greasyfork.org/scripts/3916
// @homepageURL    https://greasyfork.org/scripts/3916
// ==/UserScript==

(function () {
    function init() {
        const has_init = GM_getValue("has_init", null);
        if (has_init === null) {
            GM_setValue("mirros", {
                sayobot: {
                    url: 'https://osu.sayobot.cn/home?search={bmid}',
                    text: 'DOWNLOAD SAYOBOT',
                    class: 'my_green',
                    style: '',
                    target: '_blank'
                },
                insolink: {
                    url: 'https://inso.link/?source=osu_my_download&m={bmid}',
                    text: 'DOWNLOAD INSO.LINK',
                    class: 'my_pink',
                    style: '',
                    target: '_blank'
                },
                osu_direct: {
                    url: 'https://osu.direct/api/d/{bmid}',
                    text: 'DOWNLOAD OSU.DIRECT',
                    class: 'my_purpule',
                    style: '',
                    target: '_blank'
                },
                nerinyan: {
                    url: 'https://api.nerinyan.moe/d/{bmid}',
                    text: 'DOWNLOAD NERINYAN',
                    class: 'my_orange',
                    style: '',
                    target: '_blank'
                }
            });
            GM_setValue("has_init", 1);
        }
    }
    init()

    function settingBox() {

        // css样式 设置
        $('head').append(`
<style>
#my_setting_box{
    max-width: 800px;
    position: fixed;
    top: 100px;
    bottom: 100px;
    left: 0;
    right: 0;
    margin: auto;
    padding: 20px;
    display: flex;
    gap: 20px;
    flex-direction: column;
    background: #fff;
    color: #000;
}
#my_setting_mirros{
    flex: 1;
}
.my_setting_buttons{
    display: flex;
    gap: 20px;
    height: 50px;
}
.my_setting_buttons > *{
    display: block;
}
#my_setting_save{
    flex: 2;
}
#my_setting_reset{
    flex: 1;
}
#my_setting_cancel{
    flex: 1;
}
</style>
`);
        GM_registerMenuCommand("Setting", function () {
            if ($('#my_setting_box').length == 0) {
                var mirros = GM_getValue("mirros")
                mirros = JSON.stringify(mirros, null, "\t")
                var box = `
<div id="my_setting_box">
    <div>osu! my download  Setting</div>
    <textarea id="my_setting_mirros">${mirros}</textarea>
    <div class="my_setting_buttons">
        <button id="my_setting_save">保存 Save</button>
        <button id="my_setting_reset">重置 Reset</button>
        <button id="my_setting_cancel">取消 Cancel</button>
    </div>
</div>
`;
                $('body').append(box);
                $('#my_setting_cancel').on('click', function () {
                    $('#my_setting_box').remove()
                });
                $('#my_setting_reset').on('click', function () {
                    GM_setValue("has_init", null);
                    window.location.reload();
                });
                $('#my_setting_save').on('click', function () {
                    try {
                        var mirros = JSON.parse($('#my_setting_mirros').val());
                        GM_setValue("mirros", mirros);
                        window.location.reload();
                    } catch (e) {
                        alert("Error:" + e)
                    }
                });
            }
        });
    }
    settingBox()

    var domain = document.domain;
    var drive = domain.replace(/\./g, '_');

    function myJQueryCode() {
        function osu_my_downoad() {
            var self = this;
            this.mirros = GM_getValue("mirros")
            /*
            渲染mirro模板,返回渲染的字符串。暂时只渲染url的bmid
             */
            this.mirros_parse = function (bmid) {
                var return_mirros = $.extend(true, {}, self.mirros);
                var _param = 'url';
                $.each(return_mirros, function (k, v) {
                    return_mirros[k][_param] = str_render(v[_param], { bmid: bmid });
                });
                return return_mirros;
            };
            this.drives = {
                osu_ppy_sh: function () {
                    var is_new = $('.osu-layout').length;

                    if (is_new) {
                        // css样式 设置
                        $('head').append(`
<style>
.my_container .btn-osu-big__text-top {
white-space: normal;
}
.my_container .btn-osu-big{
position: relative;
}
.my_container .btn-osu-big__content{
position: relative;
}
.my_container a:before {
content: " ";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
border-radius: 4px;
}

.my_orange:before {
background-color: rgba(255,141,0,.5)
}

.my_green:before {
background-color: rgba(0,101,0,.5)
}

.my_pink:before {
background-color: rgba(255, 102, 170,.5)
}

.my_purpule:before {
background-color: rgba(169, 10, 165,0.5);
}
</style>
`);

                        var beatmapset_page = '.js-react--beatmapset-page';


                        var ready = function (event) {
                            var _timer = setInterval(function () {
                                var $bp = $(beatmapset_page);
                                if ($bp.length == 0) {
                                    clearInterval(_timer);
                                    return;
                                }
                                if ($('.beatmapset-header__box--main').length > 0) {
                                    loaded();
                                    clearInterval(_timer);
                                }
                            }, 200);

                            function loaded() {
                                if ($('.my_container').length > 0) {
                                    return;
                                }
                                // 获取beatmapid
                                var bmsrc = $('.js-audio--play').attr('data-audio-url');
                                if (!bmsrc) {
                                    return false;
                                }
                                var bmid = bmsrc.substring(bmsrc.lastIndexOf("/") + 1, bmsrc.lastIndexOf("."));

                                // 设置url
                                var parsed_mirros = self.mirros_parse(bmid);

                                // 添加按钮
                                var $container = $('<div class="my_container"></div>');
                                var btn_tpl = '<a href="{url}" class="btn-osu-big btn-osu-big--beatmapset-header {class}" style="{style}"  target="{target}"><div class="btn-osu-big__content"><div class="btn-osu-big__left"><span class="btn-osu-big__text-top">{text}</span></div><div class="btn-osu-big__icon"><span class="fa fa-download"></span></div></div></a>';
                                $.each(parsed_mirros, function (k, v) {
                                    $container.append(str_render(btn_tpl, parsed_mirros[k]));
                                });
                                $('.beatmapset-header__box--main').append($container);
                            }
                        };


                        document.addEventListener('turbolinks:load', ready);

                        ready();

                    } else {
                        // 获取beatmapid
                        var bmsrc = $('.bmt').attr('src');
                        if (!bmsrc) {
                            return false;
                        }
                        var bmid = bmsrc.substring(bmsrc.indexOf("thumb/") + 6, bmsrc.lastIndexOf("l"));

                        // css样式 设置
                        $('head').append(`
<style>
.my_container {
position: fixed;
top: 20px;
right: 0px;
}

.my_btn {
text-align: center;
width: 150px;
height: 111px;
display: table-cell;
vertical-align: middle;
margin: 0 0 10px 0;
padding: 10px;
font-family: Haettenschweiler,Impact,"Arial Grande",Tahoma,Helvetica,Arial,sans-serif;
font-size: 32px;
font-weight: normal;
color: #fff;
border: 4px solid #fff;
border-radius: 6px;
}

.my_btn:hover {
text-shadow: 0 0 20px floralwhite;
color: #fff;
}

.my_btn span {
display: inline-block;
vertical-align: middle;
text-align: center
}

.my_orange {
background: linear-gradient(to bottom,darkorange,wheat,darkorange);
}

.my_blue {
background: linear-gradient(to bottom,darkblue,lightblue,darkblue);
}

.my_green {
background: linear-gradient(to bottom,darkgreen,lightgreen,darkgreen);
}

.my_pink {
background: linear-gradient(to bottom,HotPink,pink,HotPink);
}
.my_purpule {
background: linear-gradient(to bottom,#261326,#E064E0,#261326);
}
</style>
`);


                        // 设置url
                        var parsed_mirros = self.mirros_parse(bmid);

                        // 添加按钮
                        var $container = $('<div class="my_container"></div>');
                        var btn_tpl = '<a class="my_btn {class}" style="{style}" href="{url}" target="{target}"><span>{text}</span></a><br/>';
                        $.each(parsed_mirros, function (k, v) {
                            $container.append(str_render(btn_tpl, parsed_mirros[k]));
                        });
                        $('body').append($container);
                    }
                }
            };
            this.init = function () {
                // var domain = document.domain;
                // var drive = domain.replace(/\./g, '_');
                // self.drives[drive]();
                self.drives.osu_ppy_sh();
            };
            self.init();
        }

        osu_my_downoad();
    }

    if (typeof jQuery == 'undefined') {
        var headTag = document.getElementsByTagName("head")[0];
        var jqTag = document.createElement('script');
        jqTag.type = 'text/javascript';
        jqTag.src = '//code.jquery.com/jquery-1.8.3.min.js';
        jqTag.onload = myJQueryCode;
        headTag.appendChild(jqTag);
    } else {
        myJQueryCode();
    }

    function str_render(template, context) {

        var tokenReg = /(\\)?\{([^\{\}\\]+)(\\)?\}/g;

        return template.replace(tokenReg, function (word, slash1, token, slash2) {
            if (slash1 || slash2) {
                return word.replace('\\', '');
            }

            var variables = token.replace(/\s/g, '').split('.');
            var currentObject = context;
            var i, length, variable;

            for (i = 0, length = variables.length; i < length; ++i) {
                variable = variables[i];
                currentObject = currentObject[variable];
                if (currentObject === undefined || currentObject === null) return '';
            }
            return currentObject;
        });
    }

    function getUrlParameter(sParam) {
        var sPageURL = decodeURIComponent(window.location.search.substring(1)),
            sURLVariables = sPageURL.split('&'),
            sParameterName,
            i;

        for (i = 0; i < sURLVariables.length; i++) {
            sParameterName = sURLVariables[i].split('=');

            if (sParameterName[0] === sParam) {
                return sParameterName[1] === undefined ? true : sParameterName[1];
            }
        }
    }
})();