Skip to content

Commit 3236701

Browse files
authored
Merge pull request #14 from SunriseCommunity/feat/add-get-beatmap-by-filename
2 parents ef104f4 + 2472c9c commit 3236701

8 files changed

Lines changed: 305 additions & 25 deletions

File tree

server/src/controllers/api/index.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,48 @@ export default (app: App) => {
5252
tags: ["v2"],
5353
},
5454
)
55+
.get(
56+
"v2/filename/:filename",
57+
async ({
58+
BeatmapsManagerInstance,
59+
params: { filename },
60+
query: { full },
61+
set,
62+
}) => {
63+
const beatmap = await BeatmapsManagerInstance.getBeatmap({
64+
beatmapFilename: filename,
65+
allowMissingNonBeatmapValues: full,
66+
});
67+
68+
if (beatmap.source) {
69+
set.headers["X-Data-Source"] = beatmap.source;
70+
}
71+
72+
const { source: _, ...responseBeatmap } = beatmap;
73+
if (!full || !beatmap.data)
74+
return responseBeatmap?.data ?? responseBeatmap;
75+
76+
const beatmapset = await BeatmapsManagerInstance.getBeatmapSet({
77+
beatmapSetId: beatmap.data.beatmapset_id,
78+
});
79+
80+
if (beatmapset.source) {
81+
set.headers["X-Data-Source"] = beatmapset.source;
82+
}
83+
84+
const { source: __, ...responseBeatmapset } = beatmapset;
85+
return responseBeatmapset?.data ?? responseBeatmapset;
86+
},
87+
{
88+
params: t.Object({
89+
filename: t.String(),
90+
}),
91+
query: t.Object({
92+
full: t.Optional(t.BooleanString()),
93+
}),
94+
tags: ["v2"],
95+
},
96+
)
5597
.get(
5698
"v2/md5/:hash",
5799
async ({

server/src/core/abstracts/client/base-client.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export type DownloadOsuBeatmap = {
4141
export type GetBeatmapOptions = {
4242
beatmapId?: number;
4343
beatmapHash?: string;
44+
beatmapFilename?: string;
4445
allowMissingNonBeatmapValues?: boolean;
4546
};
4647

@@ -61,6 +62,7 @@ export enum ClientAbilities {
6162
GetBeatmapsetsByBeatmapIds = 1 << 11, // 2048
6263
GetBeatmapByIdWithSomeNonBeatmapValues = 1 << 12, // 4096
6364
GetBeatmapByHashWithSomeNonBeatmapValues = 1 << 13, // 8192
65+
GetBeatmapByFilename = 1 << 14, // 16384
6466
}
6567

6668
export type MirrorClient<T extends BaseClient = BaseClient> = {

server/src/core/domains/osu.ppy.sh/bancho.client.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export class BanchoClient extends BaseClient {
3232
abilities: [
3333
ClientAbilities.GetBeatmapById,
3434
ClientAbilities.GetBeatmapByHash,
35+
ClientAbilities.GetBeatmapByFilename,
3536
ClientAbilities.GetBeatmapSetById,
3637
ClientAbilities.GetBeatmaps,
3738
ClientAbilities.DownloadOsuBeatmap,
@@ -49,6 +50,7 @@ export class BanchoClient extends BaseClient {
4950
abilities: [
5051
ClientAbilities.GetBeatmapById,
5152
ClientAbilities.GetBeatmapByHash,
53+
ClientAbilities.GetBeatmapByFilename,
5254
ClientAbilities.GetBeatmapSetById,
5355
ClientAbilities.GetBeatmaps,
5456
ClientAbilities.DownloadOsuBeatmap,
@@ -85,6 +87,9 @@ export class BanchoClient extends BaseClient {
8587
else if (ctx.beatmapHash) {
8688
return await this.getBeatmapByHash(ctx.beatmapHash);
8789
}
90+
else if (ctx.beatmapFilename) {
91+
return await this.getBeatmapByFilename(ctx.beatmapFilename);
92+
}
8893

8994
throw new Error("Invalid arguments");
9095
}
@@ -293,6 +298,30 @@ export class BanchoClient extends BaseClient {
293298
};
294299
}
295300

301+
private async getBeatmapByFilename(
302+
beatmapHash: string,
303+
): Promise<ResultWithStatus<Beatmap>> {
304+
const result = await this.api.get<BanchoBeatmap>(`api/v2/beatmaps/lookup`, {
305+
config: {
306+
headers: {
307+
Authorization: `Bearer ${await this.osuApiKey}`,
308+
},
309+
params: {
310+
filename: beatmapHash,
311+
},
312+
},
313+
});
314+
315+
if (!result || result.status !== 200 || !result.data) {
316+
return { result: null, status: result?.status ?? 500 };
317+
}
318+
319+
return {
320+
result: this.convertService.convertBeatmap(result.data),
321+
status: result.status,
322+
};
323+
}
324+
296325
private get osuApiKey() {
297326
return this.banchoService.getBanchoClientToken();
298327
}

server/src/core/managers/mirrors/mirrors.manager.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ export class MirrorsManager {
125125
async getBeatmap(
126126
ctx: GetBeatmapOptions,
127127
): Promise<ResultWithStatus<Beatmap>> {
128-
if (!ctx.beatmapId && !ctx.beatmapHash) {
129-
throw new Error("Either beatmapId or beatmapHash is required");
128+
if (!ctx.beatmapId && !ctx.beatmapHash && !ctx.beatmapFilename) {
129+
throw new Error("Either beatmapId, beatmapHash or beatmapFilename is required");
130130
}
131131

132132
let criteria: ClientAbilities;
@@ -135,11 +135,14 @@ export class MirrorsManager {
135135
? ClientAbilities.GetBeatmapByIdWithSomeNonBeatmapValues
136136
: ClientAbilities.GetBeatmapById;
137137
}
138-
else {
138+
else if (ctx.beatmapHash) {
139139
criteria = ctx.allowMissingNonBeatmapValues
140140
? ClientAbilities.GetBeatmapByHashWithSomeNonBeatmapValues
141141
: ClientAbilities.GetBeatmapByHash;
142142
}
143+
else {
144+
criteria = ClientAbilities.GetBeatmapByFilename;
145+
}
143146

144147
return await this.useMirror<Beatmap>(ctx, criteria, "getBeatmap");
145148
}

server/src/core/managers/storage/storage-cache.service.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,28 @@ export class StorageCacheService {
2323
async getBeatmap(
2424
ctx: GetBeatmapOptions,
2525
): Promise<Beatmap | null | undefined> {
26+
if (!ctx.beatmapId && !ctx.beatmapHash && !ctx.beatmapFilename) {
27+
throw new Error("Either beatmapId, beatmapHash or beatmapFilename is required");
28+
}
29+
2630
let { beatmapId } = ctx;
2731

2832
if (ctx.beatmapHash) {
2933
const cachedId = await this.redis.get(
30-
`${RedisKeys.BEATMAP_ID_BY_HASH}${ctx.beatmapHash}`,
34+
`${RedisKeys.BEATMAP_ID_BY_HASH}${ctx.beatmapHash}`,
35+
);
36+
37+
if (!cachedId)
38+
return undefined;
39+
if (cachedId === "null")
40+
return null;
41+
42+
beatmapId = Number(cachedId);
43+
}
44+
45+
if (ctx.beatmapFilename) {
46+
const cachedId = await this.redis.get(
47+
`${RedisKeys.BEATMAP_ID_BY_FILENAME}${ctx.beatmapFilename}`,
3148
);
3249

3350
if (!cachedId)
@@ -46,9 +63,18 @@ export class StorageCacheService {
4663
}
4764

4865
async insertEmptyBeatmap(ctx: GetBeatmapOptions) {
49-
const key = ctx?.beatmapId
50-
? `${RedisKeys.BEATMAP_BY_ID}${ctx?.beatmapId}`
51-
: `${RedisKeys.BEATMAP_ID_BY_HASH}${ctx?.beatmapHash}`;
66+
let key = "";
67+
68+
if (ctx?.beatmapId) {
69+
key = `${RedisKeys.BEATMAP_BY_ID}${ctx?.beatmapId}`;
70+
}
71+
else if (ctx?.beatmapHash) {
72+
key = `${RedisKeys.BEATMAP_ID_BY_HASH}${ctx?.beatmapHash}`;
73+
}
74+
else if (ctx?.beatmapFilename) {
75+
key = `${RedisKeys.BEATMAP_ID_BY_FILENAME}${ctx?.beatmapFilename}`;
76+
}
77+
5278
await this.redis.set(
5379
key,
5480
"null",
@@ -73,6 +99,15 @@ export class StorageCacheService {
7399
);
74100
}
75101

102+
async insertBeatmapByFilename(beatmap: Beatmap, filename: string) {
103+
await this.redis.set(
104+
`${RedisKeys.BEATMAP_ID_BY_FILENAME}${filename}`,
105+
beatmap.id,
106+
"PX",
107+
this.getRedisTTLBasedOnStatus(beatmap.status),
108+
);
109+
}
110+
76111
async insertEmptyBeatmapset(ctx: GetBeatmapSetOptions) {
77112
const key = `${RedisKeys.BEATMAPSET_BY_ID}${ctx?.beatmapSetId}`;
78113
await this.redis.set(

server/src/core/managers/storage/storage.manager.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ export class StorageManager {
4949
async getBeatmap(
5050
ctx: GetBeatmapOptions,
5151
): Promise<Beatmap | null | undefined> {
52+
if (!ctx.beatmapId && !ctx.beatmapHash && !ctx.beatmapFilename) {
53+
throw new Error("Either beatmapId, beatmapHash or beatmapFilename is required");
54+
}
55+
5256
let entity = await this.cacheService.getBeatmap(ctx);
5357

5458
if (entity !== undefined) {
@@ -61,6 +65,9 @@ export class StorageManager {
6165
else if (ctx.beatmapHash) {
6266
entity = await getBeatmapByHash(ctx.beatmapHash);
6367
}
68+
else if (ctx.beatmapFilename) {
69+
// We don't store filenames in database, skip
70+
}
6471

6572
if (entity) {
6673
this.cacheService.insertBeatmap(entity);
@@ -145,6 +152,10 @@ export class StorageManager {
145152
if (beatmap) {
146153
await createBeatmap(beatmap);
147154
await this.cacheService.insertBeatmap(beatmap);
155+
156+
if (ctx.beatmapFilename) {
157+
await this.cacheService.insertBeatmapByFilename(beatmap, ctx.beatmapFilename);
158+
}
148159
}
149160
else {
150161
await this.cacheService.insertEmptyBeatmap(ctx);

server/src/types/redis.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export enum RedisKeys {
22
BEATMAP_BY_ID = "BEATMAP:ID:",
33
BEATMAP_ID_BY_HASH = "BEATMAP_ID:HASH:",
4+
BEATMAP_ID_BY_FILENAME = "BEATMAP_ID:FILENAME:",
45
BEATMAPSET_BY_ID = "BEATMAPSET:ID:",
56
BEATMAPSET_FILE_BY_ID = "BEATMAPSET_FILE:ID:",
67
BEATMAP_OSU_FILE = "BEATMAP_OSU_FILE:ID:",

0 commit comments

Comments
 (0)