nonebot-plugin-osubot 6.23.1__py3-none-any.whl → 6.24.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nonebot-plugin-osubot might be problematic. Click here for more details.

Files changed (81) hide show
  1. nonebot_plugin_osubot/api.py +7 -5
  2. nonebot_plugin_osubot/draw/bmap.py +19 -21
  3. nonebot_plugin_osubot/draw/bp.py +1 -1
  4. nonebot_plugin_osubot/draw/echarts.py +8 -2
  5. nonebot_plugin_osubot/draw/info.py +2 -0
  6. nonebot_plugin_osubot/draw/map.py +5 -2
  7. nonebot_plugin_osubot/draw/osu_preview.py +64 -0
  8. nonebot_plugin_osubot/draw/osu_preview_templates/css/style.css +258 -0
  9. nonebot_plugin_osubot/draw/osu_preview_templates/gif.js/README.md +109 -0
  10. nonebot_plugin_osubot/draw/osu_preview_templates/gif.js/gif.js +3 -0
  11. nonebot_plugin_osubot/draw/osu_preview_templates/gif.js/gif.js.map +1 -0
  12. nonebot_plugin_osubot/draw/osu_preview_templates/gif.js/gif.worker.js +3 -0
  13. nonebot_plugin_osubot/draw/osu_preview_templates/gif.js/gif.worker.js.map +1 -0
  14. nonebot_plugin_osubot/draw/osu_preview_templates/index.html +437 -0
  15. nonebot_plugin_osubot/draw/osu_preview_templates/js/beatmap/beatmap.js +211 -0
  16. nonebot_plugin_osubot/draw/osu_preview_templates/js/beatmap/hitobject.js +29 -0
  17. nonebot_plugin_osubot/draw/osu_preview_templates/js/beatmap/point.js +55 -0
  18. nonebot_plugin_osubot/draw/osu_preview_templates/js/beatmap/scroll.js +45 -0
  19. nonebot_plugin_osubot/draw/osu_preview_templates/js/beatmap/timingpoint.js +35 -0
  20. nonebot_plugin_osubot/draw/osu_preview_templates/js/catch/LegacyRandom.js +81 -0
  21. nonebot_plugin_osubot/draw/osu_preview_templates/js/catch/PalpableCatchHitObject.js +53 -0
  22. nonebot_plugin_osubot/draw/osu_preview_templates/js/catch/bananashower.js +33 -0
  23. nonebot_plugin_osubot/draw/osu_preview_templates/js/catch/catch.js +211 -0
  24. nonebot_plugin_osubot/draw/osu_preview_templates/js/catch/fruit.js +21 -0
  25. nonebot_plugin_osubot/draw/osu_preview_templates/js/catch/juicestream.js +176 -0
  26. nonebot_plugin_osubot/draw/osu_preview_templates/js/mania/hitnote.js +21 -0
  27. nonebot_plugin_osubot/draw/osu_preview_templates/js/mania/holdnote.js +37 -0
  28. nonebot_plugin_osubot/draw/osu_preview_templates/js/mania/mania.js +164 -0
  29. nonebot_plugin_osubot/draw/osu_preview_templates/js/preview.js +61 -0
  30. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/curve/bezier2.js +33 -0
  31. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/curve/catmullcurve.js +34 -0
  32. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/curve/centripetalcatmullrom.js +30 -0
  33. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/curve/circumstancedcircle.js +47 -0
  34. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/curve/curve.js +25 -0
  35. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/curve/curvetype.js +17 -0
  36. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/curve/equaldistancemulticurve.js +70 -0
  37. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/curve/linearbezier.js +40 -0
  38. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/hitcircle.js +85 -0
  39. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/slider.js +120 -0
  40. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/spinner.js +56 -0
  41. nonebot_plugin_osubot/draw/osu_preview_templates/js/standard/standard.js +170 -0
  42. nonebot_plugin_osubot/draw/osu_preview_templates/js/taiko/donkat.js +40 -0
  43. nonebot_plugin_osubot/draw/osu_preview_templates/js/taiko/drumroll.js +34 -0
  44. nonebot_plugin_osubot/draw/osu_preview_templates/js/taiko/shaker.js +58 -0
  45. nonebot_plugin_osubot/draw/osu_preview_templates/js/taiko/taiko.js +120 -0
  46. nonebot_plugin_osubot/draw/osu_preview_templates/js/util.js +61 -0
  47. nonebot_plugin_osubot/draw/osu_preview_templates/pic.html +115 -0
  48. nonebot_plugin_osubot/draw/score.py +4 -4
  49. nonebot_plugin_osubot/file.py +1 -12
  50. nonebot_plugin_osubot/mania/__init__.py +9 -10
  51. nonebot_plugin_osubot/matcher/bp_analyze.py +9 -9
  52. nonebot_plugin_osubot/matcher/guess.py +3 -3
  53. nonebot_plugin_osubot/matcher/map_convert.py +12 -7
  54. nonebot_plugin_osubot/matcher/preview.py +10 -3
  55. nonebot_plugin_osubot/matcher/recommend.py +7 -12
  56. nonebot_plugin_osubot/osufile/mods/AP.png +0 -0
  57. nonebot_plugin_osubot/osufile/mods/CL.png +0 -0
  58. nonebot_plugin_osubot/osufile/mods/DT.png +0 -0
  59. nonebot_plugin_osubot/osufile/mods/EZ.png +0 -0
  60. nonebot_plugin_osubot/osufile/mods/FI.png +0 -0
  61. nonebot_plugin_osubot/osufile/mods/FL.png +0 -0
  62. nonebot_plugin_osubot/osufile/mods/HD.png +0 -0
  63. nonebot_plugin_osubot/osufile/mods/HR.png +0 -0
  64. nonebot_plugin_osubot/osufile/mods/HT.png +0 -0
  65. nonebot_plugin_osubot/osufile/mods/MR.png +0 -0
  66. nonebot_plugin_osubot/osufile/mods/NC.png +0 -0
  67. nonebot_plugin_osubot/osufile/mods/NF.png +0 -0
  68. nonebot_plugin_osubot/osufile/mods/PF.png +0 -0
  69. nonebot_plugin_osubot/osufile/mods/RX.png +0 -0
  70. nonebot_plugin_osubot/osufile/mods/SD.png +0 -0
  71. nonebot_plugin_osubot/osufile/mods/SO.png +0 -0
  72. nonebot_plugin_osubot/osufile/mods/TD.png +0 -0
  73. nonebot_plugin_osubot/osufile/mods/V2.png +0 -0
  74. nonebot_plugin_osubot/pp.py +7 -0
  75. nonebot_plugin_osubot/schema/__init__.py +0 -2
  76. nonebot_plugin_osubot/schema/beatmapsets.py +42 -0
  77. nonebot_plugin_osubot/schema/score.py +1 -0
  78. {nonebot_plugin_osubot-6.23.1.dist-info → nonebot_plugin_osubot-6.24.1.dist-info}/METADATA +2 -2
  79. {nonebot_plugin_osubot-6.23.1.dist-info → nonebot_plugin_osubot-6.24.1.dist-info}/RECORD +80 -39
  80. nonebot_plugin_osubot/schema/sayo_beatmap.py +0 -59
  81. {nonebot_plugin_osubot-6.23.1.dist-info → nonebot_plugin_osubot-6.24.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,437 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7
+ <meta name="robots" content="nofollow, noindex, noarchive">
8
+ <title>osu! GIF 预览生成器</title>
9
+ <style>
10
+ body {
11
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
12
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
13
+ color: #fff;
14
+ margin: 0;
15
+ padding: 20px;
16
+ min-height: 100vh;
17
+ }
18
+
19
+ .container {
20
+ max-width: 1200px;
21
+ margin: 0 auto;
22
+ display: flex;
23
+ flex-direction: column;
24
+ align-items: center;
25
+ }
26
+
27
+ .header {
28
+ text-align: center;
29
+ margin-bottom: 30px;
30
+ width: 100%;
31
+ }
32
+
33
+ h1 {
34
+ color: #ff66aa;
35
+ text-shadow: 0 0 10px rgba(255, 102, 170, 0.5);
36
+ margin-bottom: 10px;
37
+ }
38
+
39
+ .instructions {
40
+ background: rgba(0, 0, 0, 0.3);
41
+ padding: 15px;
42
+ border-radius: 10px;
43
+ margin-bottom: 20px;
44
+ width: 100%;
45
+ box-sizing: border-box;
46
+ }
47
+
48
+ .input-area {
49
+ display: flex;
50
+ flex-wrap: wrap;
51
+ gap: 10px;
52
+ margin-bottom: 20px;
53
+ width: 100%;
54
+ justify-content: center;
55
+ }
56
+
57
+ .input-group {
58
+ display: flex;
59
+ align-items: center;
60
+ gap: 5px;
61
+ }
62
+
63
+ .input-group label {
64
+ white-space: nowrap;
65
+ }
66
+
67
+ input[type="text"] {
68
+ padding: 12px;
69
+ border: none;
70
+ border-radius: 5px;
71
+ background: rgba(255, 255, 255, 0.1);
72
+ color: white;
73
+ width: 200px;
74
+ }
75
+
76
+ input[type="checkbox"] {
77
+ width: 16px;
78
+ height: 16px;
79
+ }
80
+
81
+ .checkbox-group {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 5px;
85
+ margin-left: 10px;
86
+ }
87
+
88
+ button {
89
+ padding: 12px 20px;
90
+ border: none;
91
+ border-radius: 5px;
92
+ background: #ff66aa;
93
+ color: white;
94
+ cursor: pointer;
95
+ transition: background 0.3s;
96
+ }
97
+
98
+ button:hover {
99
+ background: #ff3388;
100
+ }
101
+
102
+ .status {
103
+ margin: 20px 0;
104
+ padding: 15px;
105
+ border-radius: 5px;
106
+ background: rgba(0, 0, 0, 0.3);
107
+ width: 100%;
108
+ text-align: center;
109
+ box-sizing: border-box;
110
+ }
111
+
112
+ .progress-container {
113
+ width: 100%;
114
+ background: rgba(255, 255, 255, 0.1);
115
+ border-radius: 5px;
116
+ margin: 20px 0;
117
+ overflow: hidden;
118
+ display: none;
119
+ }
120
+
121
+ .progress-bar {
122
+ width: 0%;
123
+ height: 20px;
124
+ background: linear-gradient(90deg, #ff66aa 0%, #ff3388 100%);
125
+ transition: width 0.3s;
126
+ }
127
+
128
+ #result {
129
+ margin-top: 30px;
130
+ text-align: center;
131
+ width: 100%;
132
+ }
133
+
134
+ #img {
135
+ max-width: 100%;
136
+ border-radius: 10px;
137
+ box-shadow: 0 0 20px rgba(255, 102, 170, 0.5);
138
+ }
139
+
140
+ .footer {
141
+ margin-top: 30px;
142
+ text-align: center;
143
+ font-size: 0.9em;
144
+ color: #aaa;
145
+ }
146
+ </style>
147
+ </head>
148
+
149
+ <body>
150
+ <div class="container">
151
+ <div class="header">
152
+ <h1>osu! GIF 预览生成器</h1>
153
+ <div class="instructions">
154
+ <p>输入osu!谱面ID,生成游戏预览GIF</p>
155
+ </div>
156
+ </div>
157
+
158
+ <div class="input-area">
159
+ <div class="input-group">
160
+ <label>谱面ID:</label>
161
+ <input type="text" value="172662" id="beatmapInput" placeholder="例如: 172662" />
162
+ </div>
163
+
164
+ <div class="input-group">
165
+ <label>缩放比例:</label>
166
+ <input type="text" value="0.2" id="scaleInput" placeholder="建议0.2,0.5以上生成速度显著降低" />
167
+ </div>
168
+
169
+ <div class="input-group">
170
+ <label>帧间隔(ms):</label>
171
+ <input type="text" value="50" id="timeSpanInput" placeholder="建议50,最低15" />
172
+ </div>
173
+
174
+ <div class="input-group">
175
+ <label>开始时间(ms):</label>
176
+ <input type="text" value="-1" id="startTimeInput" placeholder="-1表示第一个note前" />
177
+ <div class="checkbox-group">
178
+ <input type="checkbox" id="usePreviewTimeCheckbox" checked />
179
+ <label for="usePreviewTimeCheckbox">优先使用谱面预览时间</label>
180
+ </div>
181
+ </div>
182
+
183
+ <div class="input-group">
184
+ <label>持续时间(ms):</label>
185
+ <input type="text" value="-1" id="durationInput" placeholder="-1表示到结尾" />
186
+ </div>
187
+
188
+ <div class="input-group">
189
+ <label>GIF质量:</label>
190
+ <input type="text" value="10" id="qualityInput" placeholder="不建议修改,值越小质量越好,但是经测试区别非常小" />
191
+ </div>
192
+
193
+ <button id="generateBtn">生成GIF</button>
194
+ </div>
195
+
196
+ <div class="status" id="status">准备就绪,请输入谱面ID</div>
197
+
198
+ <div class="progress-container" id="progressContainer">
199
+ <div class="progress-bar" id="progressBar"></div>
200
+ </div>
201
+
202
+ <div id="result"></div>
203
+
204
+ <div class="footer">
205
+ <p>使用 osu.direct API 获取谱面数据 | 基于 gif.js 生成GIF</p>
206
+ </div>
207
+ </div>
208
+
209
+ <script src="js/util.js"></script>
210
+
211
+ <script src="js/beatmap/beatmap.js"></script>
212
+ <script src="js/beatmap/timingpoint.js"></script>
213
+ <script src="js/beatmap/hitobject.js"></script>
214
+ <script src="js/beatmap/point.js"></script>
215
+ <script src="js/beatmap/scroll.js"></script>
216
+
217
+ <script src="js/standard/standard.js"></script>
218
+ <script src="js/standard/hitcircle.js"></script>
219
+ <script src="js/standard/slider.js"></script>
220
+ <script src="js/standard/curve/curve.js"></script>
221
+ <script src="js/standard/curve/equaldistancemulticurve.js"></script>
222
+ <script src="js/standard/curve/linearbezier.js"></script>
223
+ <script src="js/standard/curve/catmullcurve.js"></script>
224
+ <script src="js/standard/curve/curvetype.js"></script>
225
+ <script src="js/standard/curve/bezier2.js"></script>
226
+ <script src="js/standard/curve/centripetalcatmullrom.js"></script>
227
+ <script src="js/standard/curve/circumstancedcircle.js"></script>
228
+ <script src="js/standard/spinner.js"></script>
229
+
230
+ <script src="js/taiko/taiko.js"></script>
231
+ <script src="js/taiko/donkat.js"></script>
232
+ <script src="js/taiko/drumroll.js"></script>
233
+ <script src="js/taiko/shaker.js"></script>
234
+
235
+ <script src="js/catch/LegacyRandom.js"></script>
236
+ <script src="js/catch/catch.js"></script>
237
+ <script src="js/catch/fruit.js"></script>
238
+ <script src="js/catch/bananashower.js"></script>
239
+ <script src="js/catch/juicestream.js"></script>
240
+ <script src="js/catch/PalpableCatchHitObject.js"></script>
241
+
242
+ <script src="js/mania/mania.js"></script>
243
+ <script src="js/mania/hitnote.js"></script>
244
+ <script src="js/mania/holdnote.js"></script>
245
+
246
+ <script src="js/preview.js"></script>
247
+ <script src="gif.js/gif.js"></script>
248
+
249
+ <script>
250
+ document.addEventListener('DOMContentLoaded', function () {
251
+ const beatmapInput = document.getElementById('beatmapInput');
252
+ const scaleInput = document.getElementById('scaleInput');
253
+ const timeSpanInput = document.getElementById('timeSpanInput');
254
+ const startTimeInput = document.getElementById('startTimeInput');
255
+ const durationInput = document.getElementById('durationInput');
256
+ const qualityInput = document.getElementById('qualityInput');
257
+ const usePreviewTimeCheckbox = document.getElementById('usePreviewTimeCheckbox');
258
+ const generateBtn = document.getElementById('generateBtn');
259
+ const status = document.getElementById('status');
260
+ const progressContainer = document.getElementById('progressContainer');
261
+ const progressBar = document.getElementById('progressBar');
262
+ const result = document.getElementById('result');
263
+
264
+ generateBtn.addEventListener('click', function () {
265
+ const beatmapID = beatmapInput.value.trim();
266
+ const scaleValue = parseFloat(scaleInput.value.trim());
267
+ const timeSpanValue = parseInt(timeSpanInput.value.trim());
268
+ const startTimeValue = parseInt(startTimeInput.value.trim());
269
+ const durationValue = parseInt(durationInput.value.trim());
270
+ const qualityValue = parseInt(qualityInput.value.trim());
271
+ const usePreviewTime = usePreviewTimeCheckbox.checked;
272
+
273
+ // 验证输入
274
+ if (isNaN(scaleValue) || scaleValue <= 0 || scaleValue > 1) {
275
+ status.textContent = '请输入有效的缩放比例 (0 < 比例 ≤ 1)';
276
+ status.style.color = '#ff6666';
277
+ return;
278
+ }
279
+ if (!beatmapID) {
280
+ status.textContent = '请输入有效的谱面ID';
281
+ status.style.color = '#ff6666';
282
+ return;
283
+ }
284
+ if (isNaN(timeSpanValue) || timeSpanValue < 15) {
285
+ status.textContent = '请输入有效的帧间隔时间';
286
+ status.style.color = '#ff6666';
287
+ return;
288
+ }
289
+ if (isNaN(startTimeValue) || (startTimeValue < 0 && startTimeValue !== -1)) {
290
+ status.textContent = '请输入有效的开始时间';
291
+ status.style.color = '#ff6666';
292
+ return;
293
+ }
294
+ if (isNaN(durationValue) || (durationValue < 0 && durationValue !== -1)) {
295
+ status.textContent = '请输入有效的持续时间';
296
+ status.style.color = '#ff6666';
297
+ return;
298
+ }
299
+ if (isNaN(qualityValue) || qualityValue < 1 || qualityValue > 30) {
300
+ status.textContent = '请输入有效的GIF质量 (1-30)';
301
+ status.style.color = '#ff6666';
302
+ return;
303
+ }
304
+
305
+ generateGIF(beatmapID, scaleValue, timeSpanValue, startTimeValue, durationValue, qualityValue, usePreviewTime);
306
+ });
307
+
308
+ function generateGIF(beatmapID, scaleValue, timeSpanValue, startTimeValue, durationValue, qualityValue, usePreviewTime) {
309
+ status.textContent = '正在获取谱面数据...';
310
+ status.style.color = '#fff';
311
+ progressContainer.style.display = 'block';
312
+ progressBar.style.width = '10%';
313
+
314
+ // 清除之前的结果
315
+ result.innerHTML = '';
316
+
317
+ var osuFileUrl = "https://osu.direct/api/osu/" + beatmapID;
318
+ !async function () {
319
+ var self = this;
320
+ let osufile;
321
+ try {
322
+ const response = await fetch(osuFileUrl);
323
+ osufile = await response.text();
324
+ }
325
+ catch (e) {
326
+ console.log(e);
327
+ document.title = "从osu.direct获取谱面文件失败";
328
+ }
329
+ if (!osufile) {
330
+ return;
331
+ }
332
+
333
+ var preview = new Preview(scaleValue);
334
+ preview.load(osufile, function () {
335
+ status.textContent = '加载帧...';
336
+ progressBar.style.width = '30%';
337
+
338
+ // 计算实际开始时间
339
+ let actualStartTime;
340
+
341
+ if (usePreviewTime && preview.previewTime >= 0) {
342
+ // 检查预览时间是否有效
343
+ const previewEndTime = durationValue === -1 ? preview.endTime : preview.previewTime + durationValue;
344
+
345
+ if (previewEndTime <= preview.endTime) {
346
+ // 使用谱面预览时间
347
+ actualStartTime = preview.previewTime;
348
+ status.textContent += ' (使用谱面预览时间)';
349
+ } else {
350
+ // 预览时间加上持续时间超过谱面结束时间,使用用户指定的开始时间
351
+ actualStartTime = startTimeValue === -1 ? preview.startTime : Math.max(startTimeValue, 0);
352
+ }
353
+ } else {
354
+ // 使用用户指定的开始时间
355
+ actualStartTime = startTimeValue === -1 ? preview.startTime : Math.max(startTimeValue, 0);
356
+ }
357
+
358
+ const actualEndTime = durationValue === -1 ? preview.endTime : Math.min(actualStartTime + durationValue, preview.endTime);
359
+
360
+ var gif = new GIF({
361
+ workers: 4,
362
+ workerScript: './gif.js/gif.worker.js',
363
+ quality: qualityValue,
364
+ width: preview.screen.width * scaleValue,
365
+ height: preview.screen.height * scaleValue,
366
+ //transparent: "0x000000",
367
+ });
368
+
369
+ const totalFrames = Math.ceil((actualEndTime - actualStartTime) / timeSpanValue);
370
+ let currentFrame = 0;
371
+
372
+ // 添加帧
373
+ while (currentFrame <= totalFrames) {
374
+ const t = actualStartTime + currentFrame * timeSpanValue;
375
+ preview.at(t);
376
+
377
+ gif.addFrame(preview.ctx, {
378
+ copy: true,
379
+ delay: timeSpanValue
380
+ });
381
+
382
+ currentFrame++;
383
+ progressBar.style.width = (30 + (currentFrame / totalFrames) * 60) + '%';
384
+ }
385
+ // 所有帧已添加
386
+ progressBar.style.width = '95%';
387
+ status.textContent = '最终处理中...';
388
+
389
+ gif.on('finished', function (blob) {
390
+ const url = URL.createObjectURL(blob);
391
+
392
+ const img = document.createElement('img');
393
+ img.id = 'img';
394
+ img.src = url;
395
+ img.alt = '生成的GIF预览';
396
+
397
+ const downloadLink = document.createElement('a');
398
+ downloadLink.href = url;
399
+ downloadLink.download = `osu-preview-${beatmapID}.gif`;
400
+ downloadLink.textContent = '下载GIF';
401
+ downloadLink.className = 'download-btn';
402
+ downloadLink.style.display = 'inline-block';
403
+ downloadLink.style.marginTop = '15px';
404
+ downloadLink.style.padding = '10px 15px';
405
+ downloadLink.style.backgroundColor = '#ff66aa';
406
+ downloadLink.style.color = 'white';
407
+ downloadLink.style.borderRadius = '5px';
408
+ downloadLink.style.textDecoration = 'none';
409
+
410
+ result.appendChild(img);
411
+ result.appendChild(document.createElement('br'));
412
+ result.appendChild(downloadLink);
413
+
414
+ progressBar.style.width = '100%';
415
+ status.textContent = 'GIF生成完成!';
416
+
417
+ //progressContainer.style.display = 'none';
418
+ });
419
+
420
+ gif.render();
421
+
422
+ }, function (error) {
423
+ status.textContent = '错误: ' + error;
424
+ status.style.color = '#ff6666';
425
+ progressContainer.style.display = 'none';
426
+ });
427
+
428
+
429
+ }();
430
+
431
+
432
+ }
433
+ });
434
+ </script>
435
+ </body>
436
+
437
+ </html>
@@ -0,0 +1,211 @@
1
+ function Beatmap(osu)
2
+ {
3
+ // for temporary vars that need for drawing
4
+ this.tmp = {};
5
+
6
+ // [General]
7
+ this.StackLeniency = 0.7;
8
+ this.PreviewTime = -1;
9
+
10
+ // [Metadata]
11
+ this.Title = '';
12
+ this.TitleUnicode = undefined;
13
+ this.Artist = '';
14
+ this.ArtistUnicode = undefined;
15
+ this.Creator = '';
16
+ this.Version = undefined;
17
+
18
+ // [Difficulty]
19
+ this.CircleSize = 5;
20
+ this.OverallDifficulty = 5;
21
+ this.ApproachRate = undefined;
22
+ this.SliderMultiplier = 1.4;
23
+ this.SliderTickRate = 1;
24
+
25
+ // [TimingPoints]
26
+ this.TimingPoints = [];
27
+
28
+ // [Colours]
29
+ this.Colors = [];
30
+
31
+ // [HitObjects]
32
+ this.HitObjects = [];
33
+
34
+
35
+ var stream = osu.replace(/\r\n?/g, '\n').split('\n').reverse(),
36
+ currentSection, line;
37
+ while (typeof (line = stream.pop()) != 'undefined')
38
+ {
39
+ // skip comments
40
+ if (/^\/\//.test(line))
41
+ {
42
+ continue;
43
+ }
44
+
45
+ if (/^\[/.test(line))
46
+ {
47
+ currentSection = line.slice(1, line.indexOf(']'));
48
+ continue;
49
+ }
50
+
51
+ switch (currentSection)
52
+ {
53
+ case 'General':
54
+ case 'Metadata':
55
+ case 'Difficulty':
56
+ {
57
+ // let [key, ...value] = line.split(':');
58
+ var data = line.split(':'),
59
+ key = data.shift(),
60
+ value = data.join(':');
61
+ if (key in this)
62
+ {
63
+ this[key] = parseFloat(value) == value ? +value : value;
64
+ }
65
+ break;
66
+ }
67
+ case 'TimingPoints':
68
+ {
69
+ try
70
+ {
71
+ this.TimingPoints.push(new TimingPoint(line));
72
+ }
73
+ catch (e) {}
74
+ break;
75
+ }
76
+ case 'Colours':
77
+ {
78
+ // let [key, value] = line.split(':');
79
+ var data = line.split(':');
80
+ if (/^Combo\d+/.test(data[0]))
81
+ {
82
+ this.Colors.push('rgb(' + data[1] + ')');
83
+ }
84
+ break;
85
+ }
86
+ case 'HitObjects':
87
+ {
88
+ try
89
+ {
90
+ this.HitObjects.push(HitObject.parse(line, this));
91
+ }
92
+ catch (e) { console.log(e); }
93
+ break;
94
+ }
95
+ }
96
+ }
97
+ this.HitObjects.sort((a, b) => a.time - b.time);
98
+ }
99
+ Object.defineProperties(Beatmap.prototype, {
100
+ Version: {
101
+ get: function()
102
+ {
103
+ return typeof this._Version == 'undefined' || /^$/.test(this._Version)
104
+ ? 'Normal'
105
+ : this._Version;
106
+ },
107
+ set: function(value)
108
+ {
109
+ this._Version = value;
110
+ }
111
+ },
112
+ ApproachRate: {
113
+ get: function()
114
+ {
115
+ return typeof this._ApproachRate == 'undefined'
116
+ ? this.OverallDifficulty
117
+ : this._ApproachRate;
118
+ },
119
+ set: function(value)
120
+ {
121
+ this._ApproachRate = value;
122
+ }
123
+ },
124
+ hitObjectTypeMask: {
125
+ get: function()
126
+ {
127
+ if (typeof this._hitObjectTypeMask == 'undefined')
128
+ {
129
+ this._hitObjectTypeMask = Object.keys(this.hitObjectTypes).reduce(function(a, b)
130
+ {
131
+ return a | b;
132
+ });
133
+ }
134
+ return this._hitObjectTypeMask;
135
+ }
136
+ }
137
+ });
138
+ Beatmap.prototype.hitObjectTypes = undefined;
139
+ Beatmap.prototype.update = undefined;
140
+ Beatmap.prototype.draw = undefined;
141
+ Beatmap.prototype.processBG = undefined;
142
+ Beatmap.WIDTH = 640;
143
+ Beatmap.HEIGHT = 480;
144
+ Beatmap.MAX_X = 512;
145
+ Beatmap.MAX_Y = 384;
146
+ Beatmap.modes = {};
147
+ Beatmap.parse = function(osu)
148
+ {
149
+ if (!/^osu/.test(osu))
150
+ {
151
+ throw 'target is not a beatmap file';
152
+ }
153
+
154
+ // default mode is standard(id: 0)
155
+ var mode = +((osu.match(/[\r\n]Mode.*?:(.*?)[\r\n]/) || [])[1]) || 0;
156
+ if (!(mode in Beatmap.modes))
157
+ {
158
+ throw 'we do not support this beatmap mode';
159
+ }
160
+
161
+ return new Beatmap.modes[mode](osu);
162
+ };
163
+ Beatmap.prototype.timingPointIndexAt = function(time)
164
+ {
165
+ var begin = 0,
166
+ end = this.TimingPoints.length - 1;
167
+ while (begin <= end)
168
+ {
169
+ var mid = (begin + end) / 2 | 0;
170
+ if (time >= this.TimingPoints[mid].time)
171
+ {
172
+ if (mid + 1 == this.TimingPoints.length ||
173
+ time < this.TimingPoints[mid + 1].time)
174
+ {
175
+ return mid;
176
+ }
177
+ begin = mid + 1;
178
+ }
179
+ else
180
+ {
181
+ end = mid - 1;
182
+ }
183
+ }
184
+ return 0;
185
+ };
186
+ Beatmap.prototype.timingPointAt = function(time)
187
+ {
188
+ return this.TimingPoints[this.timingPointIndexAt(time)];
189
+ };
190
+ Beatmap.prototype.refresh = function()
191
+ {
192
+ this.tmp = {};
193
+ };
194
+ Beatmap.prototype.toString = function()
195
+ {
196
+ var unicode = JSON.parse(localStorage['osu_tool'] || '{"unicode":false}')['unicode'];
197
+ return [
198
+ (unicode ?
199
+ [
200
+ this.ArtistUnicode || this.Artist,
201
+ this.TitleUnicode || this.Title
202
+ ] :
203
+ [
204
+ this.Artist,
205
+ this.Title
206
+ ]
207
+ ).join(' - '),
208
+ ' (', this.Creator, ')',
209
+ ' [', this.Version || 'Normal' , ']'
210
+ ].join('');
211
+ };
@@ -0,0 +1,29 @@
1
+ function HitObject(data, beatmap)
2
+ {
3
+ this.beatmap = beatmap;
4
+
5
+ this.position = new Point(data);
6
+ this.endPosition = this.position.clone();
7
+ this.time = data[2] | 0;
8
+ this.endTime = this.time;
9
+ this.flag = data[3] | 0;
10
+ this.hitSound = data[4] | 0;
11
+ }
12
+ HitObject.prototype.draw = undefined;
13
+ HitObject.parse = function(line, beatmap)
14
+ {
15
+ var data = line.split(',');
16
+ if (data.length < 5)
17
+ {
18
+ throw 'invalid data: ' + line;
19
+ }
20
+
21
+ var type = data[3] & beatmap.hitObjectTypeMask;
22
+ if (!(type in beatmap.hitObjectTypes))
23
+ {
24
+ // throw 'we do not support this hitobject type';
25
+ return new HitObject(data, beatmap);
26
+ }
27
+
28
+ return new beatmap.hitObjectTypes[type](data, beatmap);
29
+ };