osu! my download

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

  1. // ==UserScript==
  2. // @name osu! my download
  3. // @description osu beatmap download from mirror. osu beatmap镜像站下载。支持的镜像站点:inso.link、osu.sayobot.cn、osu.direct、nerinyan.moe。也可以自行添加。
  4. // @author dazzulay
  5. // @copyright 2024, dazzulay
  6. // @version 2.4.8
  7. // @license GPLv3
  8. // @icon http://osu.ppy.sh/favicon.ico
  9. // @match http*://osu.ppy.sh/*
  10. // @match http*://old.ppy.sh/*
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // @namespace https://greasyfork.org/scripts/3916
  15. // @homepageURL https://greasyfork.org/scripts/3916
  16. // ==/UserScript==
  17.  
  18. (function () {
  19. function init() {
  20. const has_init = GM_getValue("has_init", null);
  21. if (has_init === null) {
  22. GM_setValue("mirros", {
  23. sayobot: {
  24. url: 'https://osu.sayobot.cn/home?search={bmid}',
  25. text: 'DOWNLOAD SAYOBOT',
  26. class: 'my_green',
  27. style: '',
  28. target: '_blank'
  29. },
  30. insolink: {
  31. url: 'https://inso.link/?source=osu_my_download&m={bmid}',
  32. text: 'DOWNLOAD INSO.LINK',
  33. class: 'my_pink',
  34. style: '',
  35. target: '_blank'
  36. },
  37. osu_direct: {
  38. url: 'https://osu.direct/api/d/{bmid}',
  39. text: 'DOWNLOAD OSU.DIRECT',
  40. class: 'my_purpule',
  41. style: '',
  42. target: '_blank'
  43. },
  44. nerinyan: {
  45. url: 'https://api.nerinyan.moe/d/{bmid}',
  46. text: 'DOWNLOAD NERINYAN',
  47. class: 'my_orange',
  48. style: '',
  49. target: '_blank'
  50. }
  51. });
  52. GM_setValue("has_init", 1);
  53. }
  54. }
  55. init()
  56.  
  57. function settingBox() {
  58.  
  59. // css样式 设置
  60. $('head').append(`
  61. <style>
  62. #my_setting_box{
  63. max-width: 800px;
  64. position: fixed;
  65. top: 100px;
  66. bottom: 100px;
  67. left: 0;
  68. right: 0;
  69. margin: auto;
  70. padding: 20px;
  71. display: flex;
  72. gap: 20px;
  73. flex-direction: column;
  74. background: #fff;
  75. color: #000;
  76. }
  77. #my_setting_mirros{
  78. flex: 1;
  79. }
  80. .my_setting_buttons{
  81. display: flex;
  82. gap: 20px;
  83. height: 50px;
  84. }
  85. .my_setting_buttons > *{
  86. display: block;
  87. }
  88. #my_setting_save{
  89. flex: 2;
  90. }
  91. #my_setting_reset{
  92. flex: 1;
  93. }
  94. #my_setting_cancel{
  95. flex: 1;
  96. }
  97. </style>
  98. `);
  99. GM_registerMenuCommand("Setting", function () {
  100. if ($('#my_setting_box').length == 0) {
  101. var mirros = GM_getValue("mirros")
  102. mirros = JSON.stringify(mirros, null, "\t")
  103. var box = `
  104. <div id="my_setting_box">
  105. <div>osu! my download Setting</div>
  106. <textarea id="my_setting_mirros">${mirros}</textarea>
  107. <div class="my_setting_buttons">
  108. <button id="my_setting_save">保存 Save</button>
  109. <button id="my_setting_reset">重置 Reset</button>
  110. <button id="my_setting_cancel">取消 Cancel</button>
  111. </div>
  112. </div>
  113. `;
  114. $('body').append(box);
  115. $('#my_setting_cancel').on('click', function () {
  116. $('#my_setting_box').remove()
  117. });
  118. $('#my_setting_reset').on('click', function () {
  119. GM_setValue("has_init", null);
  120. window.location.reload();
  121. });
  122. $('#my_setting_save').on('click', function () {
  123. try {
  124. var mirros = JSON.parse($('#my_setting_mirros').val());
  125. GM_setValue("mirros", mirros);
  126. window.location.reload();
  127. } catch (e) {
  128. alert("Error:" + e)
  129. }
  130. });
  131. }
  132. });
  133. }
  134. settingBox()
  135.  
  136. var domain = document.domain;
  137. var drive = domain.replace(/\./g, '_');
  138.  
  139. function myJQueryCode() {
  140. function osu_my_downoad() {
  141. var self = this;
  142. this.mirros = GM_getValue("mirros")
  143. /*
  144. 渲染mirro模板,返回渲染的字符串。暂时只渲染url的bmid
  145. */
  146. this.mirros_parse = function (bmid) {
  147. var return_mirros = $.extend(true, {}, self.mirros);
  148. var _param = 'url';
  149. $.each(return_mirros, function (k, v) {
  150. return_mirros[k][_param] = str_render(v[_param], { bmid: bmid });
  151. });
  152. return return_mirros;
  153. };
  154. this.drives = {
  155. osu_ppy_sh: function () {
  156. var is_new = $('.osu-layout').length;
  157.  
  158. if (is_new) {
  159. // css样式 设置
  160. $('head').append(`
  161. <style>
  162. .my_container .btn-osu-big__text-top {
  163. white-space: normal;
  164. }
  165. .my_container .btn-osu-big{
  166. position: relative;
  167. }
  168. .my_container .btn-osu-big__content{
  169. position: relative;
  170. }
  171. .my_container a:before {
  172. content: " ";
  173. position: absolute;
  174. left: 0;
  175. right: 0;
  176. top: 0;
  177. bottom: 0;
  178. border-radius: 4px;
  179. }
  180.  
  181. .my_orange:before {
  182. background-color: rgba(255,141,0,.5)
  183. }
  184.  
  185. .my_green:before {
  186. background-color: rgba(0,101,0,.5)
  187. }
  188.  
  189. .my_pink:before {
  190. background-color: rgba(255, 102, 170,.5)
  191. }
  192.  
  193. .my_purpule:before {
  194. background-color: rgba(169, 10, 165,0.5);
  195. }
  196. </style>
  197. `);
  198.  
  199. var beatmapset_page = '.js-react--beatmapset-page';
  200.  
  201.  
  202. var ready = function (event) {
  203. var _timer = setInterval(function () {
  204. var $bp = $(beatmapset_page);
  205. if ($bp.length == 0) {
  206. clearInterval(_timer);
  207. return;
  208. }
  209. if ($('.beatmapset-header__box--main').length > 0) {
  210. loaded();
  211. clearInterval(_timer);
  212. }
  213. }, 200);
  214.  
  215. function loaded() {
  216. if ($('.my_container').length > 0) {
  217. return;
  218. }
  219. // 获取beatmapid
  220. var bmsrc = $('.js-audio--play').attr('data-audio-url');
  221. if (!bmsrc) {
  222. return false;
  223. }
  224. var bmid = bmsrc.substring(bmsrc.lastIndexOf("/") + 1, bmsrc.lastIndexOf("."));
  225.  
  226. // 设置url
  227. var parsed_mirros = self.mirros_parse(bmid);
  228.  
  229. // 添加按钮
  230. var $container = $('<div class="my_container"></div>');
  231. 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>';
  232. $.each(parsed_mirros, function (k, v) {
  233. $container.append(str_render(btn_tpl, parsed_mirros[k]));
  234. });
  235. $('.beatmapset-header__buttons').append($container);
  236. }
  237. };
  238.  
  239.  
  240. document.addEventListener('turbolinks:load', ready);
  241.  
  242. ready();
  243.  
  244. } else {
  245. // 获取beatmapid
  246. var bmsrc = $('.bmt').attr('src');
  247. if (!bmsrc) {
  248. return false;
  249. }
  250. var bmid = bmsrc.substring(bmsrc.indexOf("thumb/") + 6, bmsrc.lastIndexOf("l"));
  251.  
  252. // css样式 设置
  253. $('head').append(`
  254. <style>
  255. .my_container {
  256. position: fixed;
  257. top: 20px;
  258. right: 0px;
  259. }
  260.  
  261. .my_btn {
  262. text-align: center;
  263. width: 150px;
  264. height: 111px;
  265. display: table-cell;
  266. vertical-align: middle;
  267. margin: 0 0 10px 0;
  268. padding: 10px;
  269. font-family: Haettenschweiler,Impact,"Arial Grande",Tahoma,Helvetica,Arial,sans-serif;
  270. font-size: 32px;
  271. font-weight: normal;
  272. color: #fff;
  273. border: 4px solid #fff;
  274. border-radius: 6px;
  275. }
  276.  
  277. .my_btn:hover {
  278. text-shadow: 0 0 20px floralwhite;
  279. color: #fff;
  280. }
  281.  
  282. .my_btn span {
  283. display: inline-block;
  284. vertical-align: middle;
  285. text-align: center
  286. }
  287.  
  288. .my_orange {
  289. background: linear-gradient(to bottom,darkorange,wheat,darkorange);
  290. }
  291.  
  292. .my_blue {
  293. background: linear-gradient(to bottom,darkblue,lightblue,darkblue);
  294. }
  295.  
  296. .my_green {
  297. background: linear-gradient(to bottom,darkgreen,lightgreen,darkgreen);
  298. }
  299.  
  300. .my_pink {
  301. background: linear-gradient(to bottom,HotPink,pink,HotPink);
  302. }
  303. .my_purpule {
  304. background: linear-gradient(to bottom,#261326,#E064E0,#261326);
  305. }
  306. </style>
  307. `);
  308.  
  309.  
  310. // 设置url
  311. var parsed_mirros = self.mirros_parse(bmid);
  312.  
  313. // 添加按钮
  314. var $container = $('<div class="my_container"></div>');
  315. var btn_tpl = '<a class="my_btn {class}" style="{style}" href="{url}" target="{target}"><span>{text}</span></a><br/>';
  316. $.each(parsed_mirros, function (k, v) {
  317. $container.append(str_render(btn_tpl, parsed_mirros[k]));
  318. });
  319. $('body').append($container);
  320. }
  321. }
  322. };
  323. this.init = function () {
  324. // var domain = document.domain;
  325. // var drive = domain.replace(/\./g, '_');
  326. // self.drives[drive]();
  327. self.drives.osu_ppy_sh();
  328. };
  329. self.init();
  330. }
  331.  
  332. osu_my_downoad();
  333. }
  334.  
  335. if (typeof jQuery == 'undefined') {
  336. var headTag = document.getElementsByTagName("head")[0];
  337. var jqTag = document.createElement('script');
  338. jqTag.type = 'text/javascript';
  339. jqTag.src = '//code.jquery.com/jquery-1.8.3.min.js';
  340. jqTag.onload = myJQueryCode;
  341. headTag.appendChild(jqTag);
  342. } else {
  343. myJQueryCode();
  344. }
  345.  
  346. function str_render(template, context) {
  347.  
  348. var tokenReg = /(\\)?\{([^\{\}\\]+)(\\)?\}/g;
  349.  
  350. return template.replace(tokenReg, function (word, slash1, token, slash2) {
  351. if (slash1 || slash2) {
  352. return word.replace('\\', '');
  353. }
  354.  
  355. var variables = token.replace(/\s/g, '').split('.');
  356. var currentObject = context;
  357. var i, length, variable;
  358.  
  359. for (i = 0, length = variables.length; i < length; ++i) {
  360. variable = variables[i];
  361. currentObject = currentObject[variable];
  362. if (currentObject === undefined || currentObject === null) return '';
  363. }
  364. return currentObject;
  365. });
  366. }
  367.  
  368. function getUrlParameter(sParam) {
  369. var sPageURL = decodeURIComponent(window.location.search.substring(1)),
  370. sURLVariables = sPageURL.split('&'),
  371. sParameterName,
  372. i;
  373.  
  374. for (i = 0; i < sURLVariables.length; i++) {
  375. sParameterName = sURLVariables[i].split('=');
  376.  
  377. if (sParameterName[0] === sParam) {
  378. return sParameterName[1] === undefined ? true : sParameterName[1];
  379. }
  380. }
  381. }
  382. })();