osu-play 1.0.7 → 1.2.0
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.
- package/README.md +93 -39
- package/dist/cli.cjs +1579 -0
- package/dist/cli.cjs.map +25 -0
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1569 -0
- package/dist/cli.js.map +25 -0
- package/dist/core/cli/main.d.ts +20 -0
- package/dist/core/lazer/compat.d.ts +24 -0
- package/dist/core/lazer/mod.d.ts +10 -0
- package/dist/core/lazer/schema/beatmap.d.ts +35 -0
- package/dist/core/lazer/schema/beatmapMetadata.d.ts +16 -0
- package/dist/core/lazer/schema/beatmapSet.d.ts +18 -0
- package/{src/realm/schema/mod.ts → dist/core/lazer/schema/mod.d.ts} +0 -1
- package/dist/core/lazer/schema/realmFile.d.ts +6 -0
- package/dist/core/lazer/schema/realmNamedFileUsage.d.ts +9 -0
- package/dist/core/lazer/schema/realmUser.d.ts +9 -0
- package/dist/core/player/mod.d.ts +3 -0
- package/dist/core/player/mpv.d.ts +37 -0
- package/dist/core/player/session.d.ts +45 -0
- package/dist/core/player/types.d.ts +46 -0
- package/dist/core/playlist/mod.d.ts +28 -0
- package/dist/core/tui/player-screen.d.ts +26 -0
- package/dist/core/utils/mod.d.ts +16 -0
- package/dist/index.cjs +529 -0
- package/dist/index.cjs.map +21 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +512 -0
- package/dist/index.js.map +21 -0
- package/package.json +54 -25
- package/bin/osu-play.js +0 -7
- package/index.ts +0 -1
- package/src/cli/main.ts +0 -156
- package/src/mod.ts +0 -2
- package/src/realm/mod.ts +0 -54
- package/src/realm/schema/beatmap.ts +0 -85
- package/src/realm/schema/beatmapMetadata.ts +0 -33
- package/src/realm/schema/beatmapSet.ts +0 -36
- package/src/realm/schema/realmFile.ts +0 -15
- package/src/realm/schema/realmNamedFileUsage.ts +0 -19
- package/src/realm/schema/realmUser.ts +0 -21
- package/src/utils/mod.ts +0 -77
- package/tsconfig.json +0 -109
package/dist/index.js
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __export = (target, all) => {
|
|
19
|
+
for (var name in all)
|
|
20
|
+
__defProp(target, name, {
|
|
21
|
+
get: all[name],
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
set: (newValue) => all[name] = () => newValue
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
28
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
29
|
+
|
|
30
|
+
// src/core/lazer/compat.ts
|
|
31
|
+
function describeRequirement(requirement) {
|
|
32
|
+
if (requirement.type === "object" || requirement.type === "list") {
|
|
33
|
+
return `${requirement.type}:${requirement.objectType ?? "unknown"}`;
|
|
34
|
+
}
|
|
35
|
+
return requirement.type ?? "unknown";
|
|
36
|
+
}
|
|
37
|
+
function describeProperty(property) {
|
|
38
|
+
if (!property?.type) {
|
|
39
|
+
return "missing";
|
|
40
|
+
}
|
|
41
|
+
if (property.type === "object" || property.type === "list") {
|
|
42
|
+
return `${property.type}:${property.objectType ?? "unknown"}`;
|
|
43
|
+
}
|
|
44
|
+
return property.type;
|
|
45
|
+
}
|
|
46
|
+
function inspectLazerSchemaEntries(schemaEntries, version) {
|
|
47
|
+
const schemaByName = new Map(schemaEntries.map((entry) => [entry.name, entry]));
|
|
48
|
+
const missingObjects = [];
|
|
49
|
+
const issues = [];
|
|
50
|
+
for (const [objectName, requirements] of Object.entries(REQUIRED_PLAYLIST_SCHEMA)) {
|
|
51
|
+
const schemaEntry = schemaByName.get(objectName);
|
|
52
|
+
if (!schemaEntry) {
|
|
53
|
+
missingObjects.push(objectName);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
for (const [propertyName, requirement] of Object.entries(requirements)) {
|
|
57
|
+
const property = schemaEntry.properties[propertyName];
|
|
58
|
+
const typeMatches = !requirement.type || property?.type === requirement.type;
|
|
59
|
+
const objectTypeMatches = !requirement.objectType || property?.objectType === requirement.objectType;
|
|
60
|
+
if (!typeMatches || !objectTypeMatches) {
|
|
61
|
+
issues.push({
|
|
62
|
+
objectName,
|
|
63
|
+
propertyName,
|
|
64
|
+
expected: describeRequirement(requirement),
|
|
65
|
+
actual: describeProperty(property)
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
compatible: missingObjects.length === 0 && issues.length === 0,
|
|
72
|
+
version,
|
|
73
|
+
missingObjects,
|
|
74
|
+
issues
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function formatLazerSchemaCompatibilityError(report) {
|
|
78
|
+
const details = [
|
|
79
|
+
`Detected osu!lazer schema version ${report.version}, but the playlist reader is missing fields it needs.`
|
|
80
|
+
];
|
|
81
|
+
if (report.missingObjects.length > 0) {
|
|
82
|
+
details.push(`Missing objects: ${report.missingObjects.join(", ")}`);
|
|
83
|
+
}
|
|
84
|
+
if (report.issues.length > 0) {
|
|
85
|
+
details.push(`Mismatched properties: ${report.issues.map((issue) => `${issue.objectName}.${issue.propertyName} expected ${issue.expected}, got ${issue.actual}`).join("; ")}`);
|
|
86
|
+
}
|
|
87
|
+
details.push("This app now reflects the live lazer schema and only depends on a small playlist-safe subset, so version bumps are fine until those required fields change.");
|
|
88
|
+
return details.join(`
|
|
89
|
+
`);
|
|
90
|
+
}
|
|
91
|
+
var LAST_STATIC_LAZER_SCHEMA_VERSION = 46, REQUIRED_PLAYLIST_SCHEMA;
|
|
92
|
+
var init_compat = __esm(() => {
|
|
93
|
+
REQUIRED_PLAYLIST_SCHEMA = {
|
|
94
|
+
BeatmapSet: {
|
|
95
|
+
Beatmaps: { type: "list", objectType: "Beatmap" },
|
|
96
|
+
Files: { type: "list", objectType: "RealmNamedFileUsage" }
|
|
97
|
+
},
|
|
98
|
+
Beatmap: {
|
|
99
|
+
Metadata: { type: "object", objectType: "BeatmapMetadata" }
|
|
100
|
+
},
|
|
101
|
+
BeatmapMetadata: {
|
|
102
|
+
AudioFile: { type: "string" },
|
|
103
|
+
Title: { type: "string" },
|
|
104
|
+
Artist: { type: "string" },
|
|
105
|
+
TitleUnicode: { type: "string" },
|
|
106
|
+
ArtistUnicode: { type: "string" }
|
|
107
|
+
},
|
|
108
|
+
RealmNamedFileUsage: {
|
|
109
|
+
File: { type: "object", objectType: "File" },
|
|
110
|
+
Filename: { type: "string" }
|
|
111
|
+
},
|
|
112
|
+
File: {
|
|
113
|
+
Hash: { type: "string" }
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// src/core/lazer/schema/beatmap.ts
|
|
119
|
+
import Realm from "realm";
|
|
120
|
+
var Beatmap;
|
|
121
|
+
var init_beatmap = __esm(() => {
|
|
122
|
+
Beatmap = class Beatmap extends Realm.Object {
|
|
123
|
+
ID;
|
|
124
|
+
DifficultyName;
|
|
125
|
+
Metadata;
|
|
126
|
+
BeatmapSet;
|
|
127
|
+
Status;
|
|
128
|
+
OnlineID;
|
|
129
|
+
Length;
|
|
130
|
+
BPM;
|
|
131
|
+
Hash;
|
|
132
|
+
StarRating;
|
|
133
|
+
MD5Hash;
|
|
134
|
+
OnlineMD5Hash;
|
|
135
|
+
LastLocalUpdate;
|
|
136
|
+
LastOnlineUpdate;
|
|
137
|
+
Hidden;
|
|
138
|
+
AudioLeadIn;
|
|
139
|
+
StackLeniency;
|
|
140
|
+
SpecialStyle;
|
|
141
|
+
LetterboxInBreaks;
|
|
142
|
+
WidescreenStoryboard;
|
|
143
|
+
EpilepsyWarning;
|
|
144
|
+
SamplesMatchPlaybackRate;
|
|
145
|
+
LastPlayed;
|
|
146
|
+
DistanceSpacing;
|
|
147
|
+
BeatDivisor;
|
|
148
|
+
GridSize;
|
|
149
|
+
TimelineZoom;
|
|
150
|
+
EditorTimestamp;
|
|
151
|
+
CountdownOffset;
|
|
152
|
+
static schema = {
|
|
153
|
+
name: "Beatmap",
|
|
154
|
+
primaryKey: "ID",
|
|
155
|
+
properties: {
|
|
156
|
+
ID: { type: "uuid", default: "" },
|
|
157
|
+
DifficultyName: { type: "string", default: "", optional: true },
|
|
158
|
+
Metadata: {
|
|
159
|
+
type: "object",
|
|
160
|
+
objectType: "BeatmapMetadata",
|
|
161
|
+
default: null
|
|
162
|
+
},
|
|
163
|
+
BeatmapSet: { type: "object", objectType: "BeatmapSet", default: null },
|
|
164
|
+
Status: { type: "int", default: -3 },
|
|
165
|
+
OnlineID: { type: "int", default: -1 },
|
|
166
|
+
Length: { type: "double", default: 0 },
|
|
167
|
+
BPM: { type: "double", default: 0 },
|
|
168
|
+
Hash: { type: "string", default: "", optional: true },
|
|
169
|
+
StarRating: { type: "double", default: -1 },
|
|
170
|
+
MD5Hash: { type: "string", default: "", optional: true },
|
|
171
|
+
OnlineMD5Hash: { type: "string", default: "", optional: true },
|
|
172
|
+
LastLocalUpdate: { type: "date", optional: true },
|
|
173
|
+
LastOnlineUpdate: { type: "date", optional: true },
|
|
174
|
+
Hidden: { type: "bool", default: false },
|
|
175
|
+
AudioLeadIn: { type: "double", default: 0 },
|
|
176
|
+
StackLeniency: { type: "float", default: 0.7 },
|
|
177
|
+
SpecialStyle: { type: "bool", default: false },
|
|
178
|
+
LetterboxInBreaks: { type: "bool", default: false },
|
|
179
|
+
WidescreenStoryboard: { type: "bool", default: false },
|
|
180
|
+
EpilepsyWarning: { type: "bool", default: false },
|
|
181
|
+
SamplesMatchPlaybackRate: { type: "bool", default: false },
|
|
182
|
+
LastPlayed: { type: "date", optional: true },
|
|
183
|
+
DistanceSpacing: { type: "double", default: 0 },
|
|
184
|
+
BeatDivisor: { type: "int", default: 0 },
|
|
185
|
+
GridSize: { type: "int", default: 0 },
|
|
186
|
+
TimelineZoom: { type: "double", default: 0 },
|
|
187
|
+
EditorTimestamp: { type: "double", optional: true },
|
|
188
|
+
CountdownOffset: { type: "int", default: 0 }
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// src/core/lazer/schema/beatmapMetadata.ts
|
|
195
|
+
import Realm2 from "realm";
|
|
196
|
+
var BeatmapMetadata;
|
|
197
|
+
var init_beatmapMetadata = __esm(() => {
|
|
198
|
+
BeatmapMetadata = class BeatmapMetadata extends Realm2.Object {
|
|
199
|
+
Title;
|
|
200
|
+
TitleUnicode;
|
|
201
|
+
Artist;
|
|
202
|
+
ArtistUnicode;
|
|
203
|
+
Author;
|
|
204
|
+
Source;
|
|
205
|
+
Tags;
|
|
206
|
+
PreviewTime;
|
|
207
|
+
AudioFile;
|
|
208
|
+
BackgroundFile;
|
|
209
|
+
static schema = {
|
|
210
|
+
name: "BeatmapMetadata",
|
|
211
|
+
properties: {
|
|
212
|
+
Title: { type: "string", default: "", optional: true },
|
|
213
|
+
TitleUnicode: { type: "string", default: "", optional: true },
|
|
214
|
+
Artist: { type: "string", default: "", optional: true },
|
|
215
|
+
ArtistUnicode: { type: "string", default: "", optional: true },
|
|
216
|
+
Author: { type: "object", objectType: "RealmUser", default: null },
|
|
217
|
+
Source: { type: "string", default: "", optional: true },
|
|
218
|
+
Tags: { type: "string", default: "", optional: true },
|
|
219
|
+
PreviewTime: { type: "int", default: 0 },
|
|
220
|
+
AudioFile: { type: "string", default: "", optional: true },
|
|
221
|
+
BackgroundFile: { type: "string", default: "", optional: true }
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// src/core/lazer/schema/beatmapSet.ts
|
|
228
|
+
import Realm3 from "realm";
|
|
229
|
+
var BeatmapSet;
|
|
230
|
+
var init_beatmapSet = __esm(() => {
|
|
231
|
+
BeatmapSet = class BeatmapSet extends Realm3.Object {
|
|
232
|
+
ID;
|
|
233
|
+
OnlineID;
|
|
234
|
+
DateAdded;
|
|
235
|
+
DateSubmitted;
|
|
236
|
+
DateRanked;
|
|
237
|
+
Beatmaps;
|
|
238
|
+
Files;
|
|
239
|
+
Status;
|
|
240
|
+
DeletePending;
|
|
241
|
+
Hash;
|
|
242
|
+
Protected;
|
|
243
|
+
static schema = {
|
|
244
|
+
name: "BeatmapSet",
|
|
245
|
+
primaryKey: "ID",
|
|
246
|
+
properties: {
|
|
247
|
+
ID: { type: "uuid", default: "" },
|
|
248
|
+
OnlineID: { type: "int", default: -1 },
|
|
249
|
+
DateAdded: { type: "date" },
|
|
250
|
+
DateSubmitted: { type: "date", optional: true },
|
|
251
|
+
DateRanked: { type: "date", optional: true },
|
|
252
|
+
Beatmaps: { type: "list", objectType: "Beatmap", default: [] },
|
|
253
|
+
Files: { type: "list", objectType: "RealmNamedFileUsage", default: [] },
|
|
254
|
+
Status: { type: "int", default: -3 },
|
|
255
|
+
DeletePending: { type: "bool", default: false },
|
|
256
|
+
Hash: { type: "string", default: "", optional: true },
|
|
257
|
+
Protected: { type: "bool", default: false }
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// src/core/lazer/schema/realmFile.ts
|
|
264
|
+
import Realm4 from "realm";
|
|
265
|
+
var RealmFile;
|
|
266
|
+
var init_realmFile = __esm(() => {
|
|
267
|
+
RealmFile = class RealmFile extends Realm4.Object {
|
|
268
|
+
Hash;
|
|
269
|
+
static schema = {
|
|
270
|
+
name: "File",
|
|
271
|
+
primaryKey: "Hash",
|
|
272
|
+
properties: {
|
|
273
|
+
Hash: { type: "string", default: "", optional: true }
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// src/core/lazer/schema/realmUser.ts
|
|
280
|
+
import Realm5 from "realm";
|
|
281
|
+
var RealmUser;
|
|
282
|
+
var init_realmUser = __esm(() => {
|
|
283
|
+
RealmUser = class RealmUser extends Realm5.Object {
|
|
284
|
+
OnlineID;
|
|
285
|
+
Username;
|
|
286
|
+
CountryCode;
|
|
287
|
+
static embedded = true;
|
|
288
|
+
static schema = {
|
|
289
|
+
name: "RealmUser",
|
|
290
|
+
embedded: true,
|
|
291
|
+
properties: {
|
|
292
|
+
OnlineID: { type: "int", default: 1 },
|
|
293
|
+
Username: { type: "string", default: "", optional: true },
|
|
294
|
+
CountryCode: { type: "string", optional: true }
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// src/core/lazer/schema/realmNamedFileUsage.ts
|
|
301
|
+
import Realm6 from "realm";
|
|
302
|
+
var RealmNamedFileUsage;
|
|
303
|
+
var init_realmNamedFileUsage = __esm(() => {
|
|
304
|
+
RealmNamedFileUsage = class RealmNamedFileUsage extends Realm6.Object {
|
|
305
|
+
File;
|
|
306
|
+
Filename;
|
|
307
|
+
static embedded = true;
|
|
308
|
+
static schema = {
|
|
309
|
+
name: "RealmNamedFileUsage",
|
|
310
|
+
embedded: true,
|
|
311
|
+
properties: {
|
|
312
|
+
File: "File",
|
|
313
|
+
Filename: { type: "string", optional: true }
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// src/core/lazer/schema/mod.ts
|
|
320
|
+
var init_mod = __esm(() => {
|
|
321
|
+
init_beatmap();
|
|
322
|
+
init_beatmapMetadata();
|
|
323
|
+
init_beatmapSet();
|
|
324
|
+
init_realmFile();
|
|
325
|
+
init_realmUser();
|
|
326
|
+
init_realmNamedFileUsage();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// src/core/utils/mod.ts
|
|
330
|
+
import path from "node:path";
|
|
331
|
+
import { existsSync } from "node:fs";
|
|
332
|
+
function getDataDir() {
|
|
333
|
+
switch (process.platform) {
|
|
334
|
+
case "linux":
|
|
335
|
+
case "openbsd":
|
|
336
|
+
case "freebsd": {
|
|
337
|
+
const xdg = process.env.XDG_DATA_HOME;
|
|
338
|
+
if (xdg)
|
|
339
|
+
return xdg;
|
|
340
|
+
const home = process.env.HOME;
|
|
341
|
+
if (home)
|
|
342
|
+
return path.join(home, ".local", "share");
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
case "darwin": {
|
|
346
|
+
const home = process.env.HOME;
|
|
347
|
+
if (home)
|
|
348
|
+
return path.join(home, "Library", "Application Support");
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
case "win32":
|
|
352
|
+
return process.env.LOCALAPPDATA ?? undefined;
|
|
353
|
+
}
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
function getConfigDir() {
|
|
357
|
+
switch (process.platform) {
|
|
358
|
+
case "openbsd":
|
|
359
|
+
case "freebsd":
|
|
360
|
+
case "linux": {
|
|
361
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
362
|
+
if (xdg)
|
|
363
|
+
return xdg;
|
|
364
|
+
const home = process.env.HOME;
|
|
365
|
+
if (home)
|
|
366
|
+
return path.join(home, ".config");
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
case "darwin": {
|
|
370
|
+
const home = process.env.HOME;
|
|
371
|
+
if (home)
|
|
372
|
+
return path.join(home, "Library", "Preferences");
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
case "win32":
|
|
376
|
+
return process.env.APPDATA ?? undefined;
|
|
377
|
+
}
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
function getDefaultOsuDataDir() {
|
|
381
|
+
return path.join(getDataDir() ?? ".", "osu");
|
|
382
|
+
}
|
|
383
|
+
function getDefaultConfigPath(appName = "osu-play") {
|
|
384
|
+
return path.join(getConfigDir() ?? ".", appName);
|
|
385
|
+
}
|
|
386
|
+
function hashedFilePath(hash, osuDataDir = getDefaultOsuDataDir()) {
|
|
387
|
+
return path.join(osuDataDir, "files", hash.slice(0, 1), hash.slice(0, 2), hash);
|
|
388
|
+
}
|
|
389
|
+
function getRealmDBPath(legacyAppConfigDirOrOptions = {}, maybeOptions = {}) {
|
|
390
|
+
const options = typeof legacyAppConfigDirOrOptions === "string" ? maybeOptions : legacyAppConfigDirOrOptions;
|
|
391
|
+
const osuDataDir = options.osuDataDir ?? getDefaultOsuDataDir();
|
|
392
|
+
const osuDBPath = path.join(osuDataDir, "client.realm");
|
|
393
|
+
if (!existsSync(osuDBPath)) {
|
|
394
|
+
console.log(`[getRealmDBPath]: ${osuDBPath} not found`);
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
return osuDBPath;
|
|
398
|
+
}
|
|
399
|
+
var init_mod2 = () => {};
|
|
400
|
+
|
|
401
|
+
// src/core/lazer/mod.ts
|
|
402
|
+
var exports_mod = {};
|
|
403
|
+
__export(exports_mod, {
|
|
404
|
+
inspectLazerSchemaEntries: () => inspectLazerSchemaEntries,
|
|
405
|
+
inspectLazerSchema: () => inspectLazerSchema,
|
|
406
|
+
hashedFilePath: () => hashedFilePath,
|
|
407
|
+
getNamedFileHash: () => getNamedFileHash,
|
|
408
|
+
getLazerDB: () => getLazerDB,
|
|
409
|
+
getBeatmapSets: () => getBeatmapSets,
|
|
410
|
+
formatLazerSchemaCompatibilityError: () => formatLazerSchemaCompatibilityError,
|
|
411
|
+
RealmUser: () => RealmUser,
|
|
412
|
+
RealmNamedFileUsage: () => RealmNamedFileUsage,
|
|
413
|
+
RealmFile: () => RealmFile,
|
|
414
|
+
LAST_STATIC_LAZER_SCHEMA_VERSION: () => LAST_STATIC_LAZER_SCHEMA_VERSION,
|
|
415
|
+
BeatmapSet: () => BeatmapSet,
|
|
416
|
+
BeatmapMetadata: () => BeatmapMetadata,
|
|
417
|
+
Beatmap: () => Beatmap
|
|
418
|
+
});
|
|
419
|
+
import Realm7 from "realm";
|
|
420
|
+
function inspectLazerSchema(realm) {
|
|
421
|
+
return inspectLazerSchemaEntries(realm.schema, Realm7.schemaVersion(realm.path));
|
|
422
|
+
}
|
|
423
|
+
function getBeatmapSets(realm) {
|
|
424
|
+
return realm.objects("BeatmapSet");
|
|
425
|
+
}
|
|
426
|
+
var getLazerDB = async (realmDBPath) => {
|
|
427
|
+
return Realm7.open({
|
|
428
|
+
path: realmDBPath,
|
|
429
|
+
readOnly: true
|
|
430
|
+
});
|
|
431
|
+
}, getNamedFileHash = (fileName, beatmapSet) => {
|
|
432
|
+
const files = beatmapSet.Files;
|
|
433
|
+
for (const file of files) {
|
|
434
|
+
if (file.Filename == fileName)
|
|
435
|
+
return file.File.Hash;
|
|
436
|
+
}
|
|
437
|
+
return;
|
|
438
|
+
};
|
|
439
|
+
var init_mod3 = __esm(() => {
|
|
440
|
+
init_compat();
|
|
441
|
+
init_mod();
|
|
442
|
+
init_compat();
|
|
443
|
+
init_mod2();
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// src/index.ts
|
|
447
|
+
init_mod3();
|
|
448
|
+
init_mod();
|
|
449
|
+
init_mod2();
|
|
450
|
+
|
|
451
|
+
// src/core/playlist/mod.ts
|
|
452
|
+
init_mod2();
|
|
453
|
+
function uniqueParts(parts) {
|
|
454
|
+
return [...new Set(parts.map((part) => part?.trim()).filter(Boolean))];
|
|
455
|
+
}
|
|
456
|
+
function formatTrackTitle(metadata) {
|
|
457
|
+
const title = uniqueParts([metadata.Title, metadata.TitleUnicode]).join(" / ");
|
|
458
|
+
const artist = uniqueParts([metadata.Artist, metadata.ArtistUnicode]).join(" / ");
|
|
459
|
+
return `${title || "Unknown Title"} - ${artist || "Unknown Artist"}`;
|
|
460
|
+
}
|
|
461
|
+
function getNamedFileHash2(fileName, beatmapSet) {
|
|
462
|
+
for (const file of beatmapSet.Files) {
|
|
463
|
+
if (file.Filename === fileName && file.File?.Hash) {
|
|
464
|
+
return file.File.Hash;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
function buildPlaylist(beatmapSets, osuDataDir) {
|
|
470
|
+
const seenHashes = new Set;
|
|
471
|
+
const playlist = [];
|
|
472
|
+
for (const beatmapSet of beatmapSets) {
|
|
473
|
+
for (const beatmap of beatmapSet.Beatmaps) {
|
|
474
|
+
const hash = getNamedFileHash2(beatmap.Metadata.AudioFile ?? "", beatmapSet);
|
|
475
|
+
if (!hash || seenHashes.has(hash)) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
seenHashes.add(hash);
|
|
479
|
+
playlist.push({
|
|
480
|
+
hash,
|
|
481
|
+
path: hashedFilePath(hash, osuDataDir),
|
|
482
|
+
title: formatTrackTitle(beatmap.Metadata)
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return playlist;
|
|
487
|
+
}
|
|
488
|
+
export {
|
|
489
|
+
inspectLazerSchemaEntries,
|
|
490
|
+
inspectLazerSchema,
|
|
491
|
+
hashedFilePath,
|
|
492
|
+
getRealmDBPath,
|
|
493
|
+
getNamedFileHash,
|
|
494
|
+
getLazerDB,
|
|
495
|
+
getDefaultOsuDataDir,
|
|
496
|
+
getDefaultConfigPath,
|
|
497
|
+
getDataDir,
|
|
498
|
+
getConfigDir,
|
|
499
|
+
getBeatmapSets,
|
|
500
|
+
formatTrackTitle,
|
|
501
|
+
formatLazerSchemaCompatibilityError,
|
|
502
|
+
buildPlaylist,
|
|
503
|
+
RealmUser,
|
|
504
|
+
RealmNamedFileUsage,
|
|
505
|
+
RealmFile,
|
|
506
|
+
LAST_STATIC_LAZER_SCHEMA_VERSION,
|
|
507
|
+
BeatmapSet,
|
|
508
|
+
BeatmapMetadata,
|
|
509
|
+
Beatmap
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
//# debugId=BF8AA078DFC49D3564756E2164756E21
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/lazer/compat.ts", "../src/core/lazer/schema/beatmap.ts", "../src/core/lazer/schema/beatmapMetadata.ts", "../src/core/lazer/schema/beatmapSet.ts", "../src/core/lazer/schema/realmFile.ts", "../src/core/lazer/schema/realmUser.ts", "../src/core/lazer/schema/realmNamedFileUsage.ts", "../src/core/lazer/schema/mod.ts", "../src/core/utils/mod.ts", "../src/core/lazer/mod.ts", "../src/index.ts", "../src/core/playlist/mod.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"type SchemaPropertyLike = {\n type?: string;\n objectType?: string;\n};\n\ntype SchemaEntryLike = {\n name: string;\n properties: Record<string, SchemaPropertyLike>;\n};\n\ntype RequiredSchemaShape = Record<\n string,\n Record<string, { type?: string; objectType?: string }>\n>;\n\nexport type LazerSchemaCompatibilityIssue = {\n objectName: string;\n propertyName: string;\n expected: string;\n actual: string;\n};\n\nexport type LazerSchemaCompatibilityReport = {\n compatible: boolean;\n version: number;\n missingObjects: string[];\n issues: LazerSchemaCompatibilityIssue[];\n};\n\nexport const LAST_STATIC_LAZER_SCHEMA_VERSION = 46;\n\nconst REQUIRED_PLAYLIST_SCHEMA: RequiredSchemaShape = {\n BeatmapSet: {\n Beatmaps: { type: \"list\", objectType: \"Beatmap\" },\n Files: { type: \"list\", objectType: \"RealmNamedFileUsage\" },\n },\n Beatmap: {\n Metadata: { type: \"object\", objectType: \"BeatmapMetadata\" },\n },\n BeatmapMetadata: {\n AudioFile: { type: \"string\" },\n Title: { type: \"string\" },\n Artist: { type: \"string\" },\n TitleUnicode: { type: \"string\" },\n ArtistUnicode: { type: \"string\" },\n },\n RealmNamedFileUsage: {\n File: { type: \"object\", objectType: \"File\" },\n Filename: { type: \"string\" },\n },\n File: {\n Hash: { type: \"string\" },\n },\n};\n\nfunction describeRequirement(requirement: { type?: string; objectType?: string }) {\n if (requirement.type === \"object\" || requirement.type === \"list\") {\n return `${requirement.type}:${requirement.objectType ?? \"unknown\"}`;\n }\n\n return requirement.type ?? \"unknown\";\n}\n\nfunction describeProperty(property: SchemaPropertyLike | undefined) {\n if (!property?.type) {\n return \"missing\";\n }\n\n if (property.type === \"object\" || property.type === \"list\") {\n return `${property.type}:${property.objectType ?? \"unknown\"}`;\n }\n\n return property.type;\n}\n\nexport function inspectLazerSchemaEntries(\n schemaEntries: SchemaEntryLike[],\n version: number,\n): LazerSchemaCompatibilityReport {\n const schemaByName = new Map(schemaEntries.map((entry) => [entry.name, entry]));\n const missingObjects: string[] = [];\n const issues: LazerSchemaCompatibilityIssue[] = [];\n\n for (const [objectName, requirements] of Object.entries(REQUIRED_PLAYLIST_SCHEMA)) {\n const schemaEntry = schemaByName.get(objectName);\n if (!schemaEntry) {\n missingObjects.push(objectName);\n continue;\n }\n\n for (const [propertyName, requirement] of Object.entries(requirements)) {\n const property = schemaEntry.properties[propertyName];\n const typeMatches = !requirement.type || property?.type === requirement.type;\n const objectTypeMatches =\n !requirement.objectType || property?.objectType === requirement.objectType;\n\n if (!typeMatches || !objectTypeMatches) {\n issues.push({\n objectName,\n propertyName,\n expected: describeRequirement(requirement),\n actual: describeProperty(property),\n });\n }\n }\n }\n\n return {\n compatible: missingObjects.length === 0 && issues.length === 0,\n version,\n missingObjects,\n issues,\n };\n}\n\nexport function formatLazerSchemaCompatibilityError(\n report: LazerSchemaCompatibilityReport,\n) {\n const details = [\n `Detected osu!lazer schema version ${report.version}, but the playlist reader is missing fields it needs.`,\n ];\n\n if (report.missingObjects.length > 0) {\n details.push(`Missing objects: ${report.missingObjects.join(\", \")}`);\n }\n\n if (report.issues.length > 0) {\n details.push(\n `Mismatched properties: ${report.issues\n .map(\n (issue) =>\n `${issue.objectName}.${issue.propertyName} expected ${issue.expected}, got ${issue.actual}`,\n )\n .join(\"; \")}`,\n );\n }\n\n details.push(\n \"This app now reflects the live lazer schema and only depends on a small playlist-safe subset, so version bumps are fine until those required fields change.\",\n );\n\n return details.join(\"\\n\");\n}\n",
|
|
6
|
+
"import Realm from \"realm\";\nimport type { ObjectSchema } from \"realm\";\nimport { BeatmapMetadata } from \"./beatmapMetadata.js\";\n\nexport class Beatmap extends Realm.Object<Beatmap> {\n ID!: string;\n DifficultyName?: string;\n Metadata!: BeatmapMetadata;\n BeatmapSet!: any;\n Status!: number;\n OnlineID!: number;\n Length!: number;\n BPM!: number;\n Hash?: string;\n StarRating!: number;\n MD5Hash?: string;\n OnlineMD5Hash?: string;\n LastLocalUpdate?: Date;\n LastOnlineUpdate?: Date;\n Hidden!: boolean;\n AudioLeadIn!: number;\n StackLeniency!: number;\n SpecialStyle!: boolean;\n LetterboxInBreaks!: boolean;\n WidescreenStoryboard!: boolean;\n EpilepsyWarning!: boolean;\n SamplesMatchPlaybackRate!: boolean;\n LastPlayed?: Date;\n DistanceSpacing!: number;\n BeatDivisor!: number;\n GridSize!: number;\n TimelineZoom!: number;\n EditorTimestamp?: number;\n CountdownOffset!: number;\n\n static schema: ObjectSchema = {\n name: \"Beatmap\",\n primaryKey: \"ID\",\n properties: {\n ID: { type: \"uuid\", default: \"\" },\n DifficultyName: { type: \"string\", default: \"\", optional: true },\n Metadata: {\n type: \"object\",\n objectType: \"BeatmapMetadata\",\n default: null,\n },\n BeatmapSet: { type: \"object\", objectType: \"BeatmapSet\", default: null },\n Status: { type: \"int\", default: -3 },\n OnlineID: { type: \"int\", default: -1 },\n Length: { type: \"double\", default: 0 },\n BPM: { type: \"double\", default: 0 },\n Hash: { type: \"string\", default: \"\", optional: true },\n StarRating: { type: \"double\", default: -1 },\n MD5Hash: { type: \"string\", default: \"\", optional: true },\n OnlineMD5Hash: { type: \"string\", default: \"\", optional: true },\n LastLocalUpdate: { type: \"date\", optional: true },\n LastOnlineUpdate: { type: \"date\", optional: true },\n Hidden: { type: \"bool\", default: false },\n AudioLeadIn: { type: \"double\", default: 0 },\n StackLeniency: { type: \"float\", default: 0.7 },\n SpecialStyle: { type: \"bool\", default: false },\n LetterboxInBreaks: { type: \"bool\", default: false },\n WidescreenStoryboard: { type: \"bool\", default: false },\n EpilepsyWarning: { type: \"bool\", default: false },\n SamplesMatchPlaybackRate: { type: \"bool\", default: false },\n LastPlayed: { type: \"date\", optional: true },\n DistanceSpacing: { type: \"double\", default: 0 },\n BeatDivisor: { type: \"int\", default: 0 },\n GridSize: { type: \"int\", default: 0 },\n TimelineZoom: { type: \"double\", default: 0 },\n EditorTimestamp: { type: \"double\", optional: true },\n CountdownOffset: { type: \"int\", default: 0 },\n },\n };\n}\n",
|
|
7
|
+
"import Realm from \"realm\";\nimport type { ObjectSchema } from \"realm\";\nimport { RealmUser } from \"./realmUser.js\";\n\nexport class BeatmapMetadata extends Realm.Object<BeatmapMetadata> {\n Title?: string;\n TitleUnicode?: string;\n Artist?: string;\n ArtistUnicode?: string;\n Author?: RealmUser;\n Source?: string;\n Tags?: string;\n PreviewTime?: number;\n AudioFile?: string;\n BackgroundFile?: string;\n\n static schema: ObjectSchema = {\n name: \"BeatmapMetadata\",\n properties: {\n Title: { type: \"string\", default: \"\", optional: true },\n TitleUnicode: { type: \"string\", default: \"\", optional: true },\n Artist: { type: \"string\", default: \"\", optional: true },\n ArtistUnicode: { type: \"string\", default: \"\", optional: true },\n Author: { type: \"object\", objectType: \"RealmUser\", default: null },\n Source: { type: \"string\", default: \"\", optional: true },\n Tags: { type: \"string\", default: \"\", optional: true },\n PreviewTime: { type: \"int\", default: 0 },\n AudioFile: { type: \"string\", default: \"\", optional: true },\n BackgroundFile: { type: \"string\", default: \"\", optional: true },\n },\n };\n}\n",
|
|
8
|
+
"import Realm from \"realm\";\nimport type { ObjectSchema } from \"realm\";\nimport { Beatmap } from \"./beatmap.js\";\nimport { RealmNamedFileUsage } from \"./realmNamedFileUsage.js\";\n\nexport class BeatmapSet extends Realm.Object<BeatmapSet> {\n ID!: string;\n OnlineID!: number;\n DateAdded!: Date;\n DateSubmitted?: Date;\n DateRanked?: Date;\n Beatmaps!: Realm.List<Beatmap>;\n Files!: Realm.List<RealmNamedFileUsage>;\n Status!: number;\n DeletePending!: boolean;\n Hash?: string;\n Protected!: boolean;\n\n static schema: ObjectSchema = {\n name: \"BeatmapSet\",\n primaryKey: \"ID\",\n properties: {\n ID: { type: \"uuid\", default: \"\" },\n OnlineID: { type: \"int\", default: -1 },\n DateAdded: { type: \"date\" },\n DateSubmitted: { type: \"date\", optional: true },\n DateRanked: { type: \"date\", optional: true },\n Beatmaps: { type: \"list\", objectType: \"Beatmap\", default: [] },\n Files: { type: \"list\", objectType: \"RealmNamedFileUsage\", default: [] },\n Status: { type: \"int\", default: -3 },\n DeletePending: { type: \"bool\", default: false },\n Hash: { type: \"string\", default: \"\", optional: true },\n Protected: { type: \"bool\", default: false },\n },\n };\n}\n",
|
|
9
|
+
"import Realm from \"realm\";\nimport type { ObjectSchema } from \"realm\";\n\nexport class RealmFile extends Realm.Object<RealmFile> {\n Hash?: string;\n\n static schema: ObjectSchema = {\n name: \"File\",\n primaryKey: \"Hash\",\n properties: {\n Hash: { type: \"string\", default: \"\", optional: true },\n },\n };\n}\n\n",
|
|
10
|
+
"import Realm from \"realm\";\nimport type { ObjectSchema } from \"realm\";\n\nexport class RealmUser extends Realm.Object<RealmUser> {\n OnlineID!: number;\n Username?: string;\n CountryCode?: string;\n\n static embedded = true;\n\n static schema: ObjectSchema = {\n name: \"RealmUser\",\n embedded: true,\n properties: {\n OnlineID: { type: \"int\", default: 1 },\n Username: { type: \"string\", default: \"\", optional: true },\n CountryCode: { type: \"string\", optional: true },\n },\n };\n}\n\n",
|
|
11
|
+
"import Realm from \"realm\";\nimport type { ObjectSchema } from \"realm\";\nimport { RealmFile } from \"./realmFile.js\";\n\nexport class RealmNamedFileUsage extends Realm.Object<RealmNamedFileUsage> {\n File!: RealmFile;\n Filename?: string;\n\n static embedded = true;\n\n static schema: ObjectSchema = {\n name: \"RealmNamedFileUsage\",\n embedded: true,\n properties: {\n File: \"File\",\n Filename: { type: \"string\", optional: true },\n },\n };\n}\n",
|
|
12
|
+
"export { Beatmap } from \"./beatmap.js\";\nexport { BeatmapMetadata } from \"./beatmapMetadata.js\";\nexport { BeatmapSet } from \"./beatmapSet.js\";\nexport { RealmFile } from \"./realmFile.js\";\nexport { RealmUser } from \"./realmUser.js\";\nexport { RealmNamedFileUsage } from \"./realmNamedFileUsage.js\";\n",
|
|
13
|
+
"import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\nexport function getDataDir(): string | undefined {\n switch (process.platform) {\n case \"linux\":\n case \"openbsd\":\n case \"freebsd\": {\n const xdg = process.env.XDG_DATA_HOME;\n if (xdg) return xdg;\n\n const home = process.env.HOME;\n if (home) return path.join(home, \".local\", \"share\");\n break;\n }\n\n case \"darwin\": {\n const home = process.env.HOME;\n if (home) return path.join(home, \"Library\", \"Application Support\");\n break;\n }\n\n case \"win32\":\n return process.env.LOCALAPPDATA ?? undefined;\n }\n\n return undefined;\n}\n\nexport function getConfigDir(): string | undefined {\n switch (process.platform) {\n case \"openbsd\":\n case \"freebsd\":\n case \"linux\": {\n const xdg = process.env.XDG_CONFIG_HOME;\n if (xdg) return xdg;\n\n const home = process.env.HOME;\n if (home) return path.join(home, \".config\");\n break;\n }\n\n case \"darwin\": {\n const home = process.env.HOME;\n if (home) return path.join(home, \"Library\", \"Preferences\");\n break;\n }\n\n case \"win32\":\n return process.env.APPDATA ?? undefined;\n }\n\n return undefined;\n}\n\nexport function getDefaultOsuDataDir() {\n return path.join(getDataDir() ?? \".\", \"osu\");\n}\n\nexport function getDefaultConfigPath(appName: string = \"osu-play\") {\n return path.join(getConfigDir() ?? \".\", appName);\n}\n\nexport function hashedFilePath(\n hash: string,\n osuDataDir: string = getDefaultOsuDataDir(),\n) {\n return path.join(\n osuDataDir,\n \"files\",\n hash.slice(0, 1),\n hash.slice(0, 2),\n hash,\n );\n}\n\ntype RealmPathOptions = {\n osuDataDir?: string;\n};\n\n/**\n * Returns osu!lazer's live Realm DB path directly.\n * The legacy `appConfigDir` positional argument is accepted for backwards\n * compatibility but no longer used.\n */\nexport function getRealmDBPath(options?: RealmPathOptions): string | null;\nexport function getRealmDBPath(\n legacyAppConfigDir: string,\n options?: RealmPathOptions,\n): string | null;\nexport function getRealmDBPath(\n legacyAppConfigDirOrOptions: string | RealmPathOptions = {},\n maybeOptions: RealmPathOptions = {},\n) {\n const options =\n typeof legacyAppConfigDirOrOptions === \"string\"\n ? maybeOptions\n : legacyAppConfigDirOrOptions;\n const osuDataDir = options.osuDataDir ?? getDefaultOsuDataDir();\n const osuDBPath = path.join(osuDataDir, \"client.realm\");\n\n if (!existsSync(osuDBPath)) {\n console.log(`[getRealmDBPath]: ${osuDBPath} not found`);\n return null;\n }\n\n return osuDBPath;\n}\n",
|
|
14
|
+
"import Realm from \"realm\";\nimport type { PlaylistBeatmapSet } from \"../playlist/mod.js\";\nimport type { BeatmapSet } from \"./schema/beatmapSet.js\";\nimport { inspectLazerSchemaEntries } from \"./compat.js\";\n\nexport {\n Beatmap,\n BeatmapMetadata,\n BeatmapSet,\n RealmFile,\n RealmNamedFileUsage,\n RealmUser,\n} from \"./schema/mod.js\";\nexport {\n formatLazerSchemaCompatibilityError,\n inspectLazerSchemaEntries,\n LAST_STATIC_LAZER_SCHEMA_VERSION,\n} from \"./compat.js\";\nexport { hashedFilePath } from \"../utils/mod.js\";\n\ntype SchemaPropertyLike = {\n type?: string;\n objectType?: string;\n};\n\ntype SchemaEntryLike = {\n name: string;\n properties: Record<string, SchemaPropertyLike>;\n};\n\nexport function inspectLazerSchema(realm: Realm) {\n return inspectLazerSchemaEntries(\n realm.schema as unknown as SchemaEntryLike[],\n Realm.schemaVersion(realm.path),\n );\n}\n\nexport const getLazerDB = async (realmDBPath: string) => {\n return Realm.open({\n path: realmDBPath,\n readOnly: true,\n });\n};\n\nexport function getBeatmapSets(realm: Realm) {\n return realm.objects(\"BeatmapSet\") as unknown as Iterable<PlaylistBeatmapSet>;\n}\n\nexport const getNamedFileHash = (fileName: string, beatmapSet: BeatmapSet) => {\n const files = beatmapSet.Files;\n for (const file of files) {\n if (file.Filename == fileName) return file.File.Hash;\n }\n return undefined;\n};\n",
|
|
15
|
+
"export * from \"./core/lazer/mod.js\";\nexport * from \"./core/lazer/schema/mod.js\";\nexport * from \"./core/utils/mod.js\";\nexport { buildPlaylist, formatTrackTitle } from \"./core/playlist/mod.js\";\n",
|
|
16
|
+
"import { hashedFilePath } from \"../utils/mod.js\";\n\nexport type PlaylistBeatmapMetadata = {\n AudioFile?: string | null;\n Title?: string | null;\n Artist?: string | null;\n TitleUnicode?: string | null;\n ArtistUnicode?: string | null;\n};\n\nexport type PlaylistBeatmap = {\n Metadata: PlaylistBeatmapMetadata;\n};\n\nexport type PlaylistFile = {\n Filename?: string | null;\n File?: {\n Hash?: string | null;\n } | null;\n};\n\nexport type PlaylistBeatmapSet = {\n Beatmaps: Iterable<PlaylistBeatmap>;\n Files: Iterable<PlaylistFile>;\n};\n\nexport type PlaylistTrack = {\n hash: string;\n path: string;\n title: string;\n};\n\nfunction uniqueParts(parts: Array<string | null | undefined>) {\n return [...new Set(parts.map((part) => part?.trim()).filter(Boolean))];\n}\n\nexport function formatTrackTitle(metadata: PlaylistBeatmapMetadata) {\n const title = uniqueParts([metadata.Title, metadata.TitleUnicode]).join(\" / \");\n const artist = uniqueParts([metadata.Artist, metadata.ArtistUnicode]).join(\" / \");\n\n return `${title || \"Unknown Title\"} - ${artist || \"Unknown Artist\"}`;\n}\n\nexport function getNamedFileHash(fileName: string, beatmapSet: PlaylistBeatmapSet) {\n for (const file of beatmapSet.Files) {\n if (file.Filename === fileName && file.File?.Hash) {\n return file.File.Hash;\n }\n }\n\n return undefined;\n}\n\nexport function buildPlaylist(\n beatmapSets: Iterable<PlaylistBeatmapSet>,\n osuDataDir: string,\n) {\n const seenHashes = new Set<string>();\n const playlist: PlaylistTrack[] = [];\n\n for (const beatmapSet of beatmapSets) {\n for (const beatmap of beatmapSet.Beatmaps) {\n const hash = getNamedFileHash(beatmap.Metadata.AudioFile ?? \"\", beatmapSet);\n if (!hash || seenHashes.has(hash)) {\n continue;\n }\n\n seenHashes.add(hash);\n playlist.push({\n hash,\n path: hashedFilePath(hash, osuDataDir),\n title: formatTrackTitle(beatmap.Metadata),\n });\n }\n }\n\n return playlist;\n}\n"
|
|
17
|
+
],
|
|
18
|
+
"mappings": "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",
|
|
19
|
+
"debugId": "BF8AA078DFC49D3564756E2164756E21",
|
|
20
|
+
"names": []
|
|
21
|
+
}
|