rhythia-api 149.0.0 → 151.0.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.
@@ -101,6 +101,15 @@ export async function handler({
101
101
  .toBuffer();
102
102
  } catch (error) {}
103
103
 
104
+ let largeBuffer = Buffer.from([]);
105
+ try {
106
+ largeBuffer = await require("sharp")(parsedData.cover)
107
+ .resize(850)
108
+ .jpeg({ mozjpeg: true })
109
+ .toBuffer();
110
+ } catch (error) {}
111
+
112
+ // Images
104
113
  const command = new PutObjectCommand({
105
114
  Bucket: "rhthia-avatars",
106
115
  Key: imgkey,
@@ -109,6 +118,17 @@ export async function handler({
109
118
  });
110
119
 
111
120
  await s3Client.send(command);
121
+
122
+ const command2 = new PutObjectCommand({
123
+ Bucket: "rhthia-avatars",
124
+ Key: imgkey + "large",
125
+ Body: largeBuffer,
126
+ ContentType: "image/jpeg",
127
+ });
128
+
129
+ await s3Client.send(command2);
130
+ // Images End
131
+
112
132
  const markers = parsedData.markers.sort((a, b) => a.position - b.position);
113
133
 
114
134
  const upserted = await supabase.from("beatmaps").upsert({
@@ -120,6 +140,7 @@ export async function handler({
120
140
  length: markers[markers.length - 1].position,
121
141
  beatmapFile: url,
122
142
  image: `https://static.rhythia.com/${imgkey}`,
143
+ imageLarge: `https://static.rhythia.com/${imgkey}large`,
123
144
  starRating: rateMap(parsedData),
124
145
  });
125
146
 
@@ -23,6 +23,7 @@ export const Schema = {
23
23
  ranked: z.boolean().nullable().optional(),
24
24
  beatmapFile: z.string().nullable().optional(),
25
25
  image: z.string().nullable().optional(),
26
+ imageLarge: z.string().nullable().optional(),
26
27
  starRating: z.number().nullable().optional(),
27
28
  owner: z.number().nullable().optional(),
28
29
  ownerUsername: z.string().nullable().optional(),
@@ -60,6 +61,7 @@ export async function handler(
60
61
  ranked,
61
62
  beatmapFile,
62
63
  image,
64
+ imageLarge,
63
65
  starRating,
64
66
  difficulty,
65
67
  noteCount,
@@ -87,6 +89,7 @@ export async function handler(
87
89
  ranked: beatmapPage.beatmaps?.ranked,
88
90
  beatmapFile: beatmapPage.beatmaps?.beatmapFile,
89
91
  image: beatmapPage.beatmaps?.image,
92
+ imageLarge: beatmapPage.beatmaps?.imageLarge,
90
93
  starRating: beatmapPage.beatmaps?.starRating,
91
94
  owner: beatmapPage.owner,
92
95
  ownerUsername: beatmapPage.profiles?.username,
@@ -7,9 +7,9 @@ export const Schema = {
7
7
  input: z.strictObject({
8
8
  session: z.string(),
9
9
  id: z.number(),
10
- beatmapHash: z.string(),
11
- tags: z.string(),
12
- description: z.string(),
10
+ beatmapHash: z.string().optional(),
11
+ tags: z.string().optional(),
12
+ description: z.string().optional(),
13
13
  }),
14
14
  output: z.strictObject({
15
15
  error: z.string().optional(),
@@ -47,14 +47,17 @@ export async function handler({
47
47
  .eq("id", id)
48
48
  .single();
49
49
 
50
+ if (!userData) return NextResponse.json({ error: "No user." });
51
+
50
52
  let { data: beatmapData, error: bmPageError } = await supabase
51
53
  .from("beatmaps")
52
54
  .select("*")
53
- .eq("beatmapHash", beatmapHash)
55
+ .eq("beatmapHash", beatmapHash || "")
54
56
  .single();
55
57
 
56
- if (!userData) return NextResponse.json({ error: "No user." });
57
- if (!beatmapData) return NextResponse.json({ error: "No beatmap." });
58
+ if (!beatmapData && beatmapHash) {
59
+ return NextResponse.json({ error: "No beatmap." });
60
+ }
58
61
 
59
62
  if (userData.id !== pageData?.owner)
60
63
  return NextResponse.json({ error: "Non-authz user." });
@@ -62,19 +65,24 @@ export async function handler({
62
65
  if (pageData?.status !== "UNRANKED")
63
66
  return NextResponse.json({ error: "Only unranked maps can be updated" });
64
67
 
68
+ const upsertPayload = {
69
+ id,
70
+ genre: "",
71
+ status: "UNRANKED",
72
+ owner: userData.id,
73
+ description: description ? description : pageData.description,
74
+ tags: tags ? tags : pageData.tags,
75
+ };
76
+
77
+ if (beatmapHash && beatmapData) {
78
+ upsertPayload["title"] = beatmapData.title;
79
+ upsertPayload["latestBeatmapHash"] = beatmapHash;
80
+ upsertPayload["nominations"] = [];
81
+ }
82
+
65
83
  const upserted = await supabase
66
84
  .from("beatmapPages")
67
- .upsert({
68
- id,
69
- latestBeatmapHash: beatmapHash,
70
- genre: "",
71
- title: beatmapData.title,
72
- status: "UNRANKED",
73
- owner: userData.id,
74
- description,
75
- tags,
76
- nominations: [],
77
- })
85
+ .upsert(upsertPayload)
78
86
  .select("*")
79
87
  .single();
80
88
 
package/index.ts CHANGED
@@ -198,6 +198,7 @@ export const Schema = {
198
198
  ranked: z.boolean().nullable().optional(),
199
199
  beatmapFile: z.string().nullable().optional(),
200
200
  image: z.string().nullable().optional(),
201
+ imageLarge: z.string().nullable().optional(),
201
202
  starRating: z.number().nullable().optional(),
202
203
  owner: z.number().nullable().optional(),
203
204
  ownerUsername: z.string().nullable().optional(),
@@ -593,9 +594,9 @@ export const Schema = {
593
594
  input: z.strictObject({
594
595
  session: z.string(),
595
596
  id: z.number(),
596
- beatmapHash: z.string(),
597
- tags: z.string(),
598
- description: z.string(),
597
+ beatmapHash: z.string().optional(),
598
+ tags: z.string().optional(),
599
+ description: z.string().optional(),
599
600
  }),
600
601
  output: z.strictObject({
601
602
  error: z.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhythia-api",
3
- "version": "149.0.0",
3
+ "version": "151.0.0",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "update": "bun ./scripts/update.ts",
@@ -38,7 +38,7 @@
38
38
  "osu-standard-stable": "^5.0.0",
39
39
  "sharp": "^0.33.5",
40
40
  "simple-git": "^3.25.0",
41
- "supabase": "^1.204.3",
41
+ "supabase": "^1.207.9",
42
42
  "tsx": "^4.17.0",
43
43
  "utf-8-validate": "^6.0.4",
44
44
  "zod": "^3.23.8"
package/types/database.ts CHANGED
@@ -109,6 +109,7 @@ export type Database = {
109
109
  created_at: string
110
110
  difficulty: number | null
111
111
  image: string | null
112
+ imageLarge: string | null
112
113
  length: number | null
113
114
  noteCount: number | null
114
115
  playcount: number | null
@@ -122,6 +123,7 @@ export type Database = {
122
123
  created_at?: string
123
124
  difficulty?: number | null
124
125
  image?: string | null
126
+ imageLarge?: string | null
125
127
  length?: number | null
126
128
  noteCount?: number | null
127
129
  playcount?: number | null
@@ -135,6 +137,7 @@ export type Database = {
135
137
  created_at?: string
136
138
  difficulty?: number | null
137
139
  image?: string | null
140
+ imageLarge?: string | null
138
141
  length?: number | null
139
142
  noteCount?: number | null
140
143
  playcount?: number | null
@@ -202,15 +205,7 @@ export type Database = {
202
205
  username?: string | null
203
206
  verified?: boolean | null
204
207
  }
205
- Relationships: [
206
- {
207
- foreignKeyName: "profiles_uid_fkey"
208
- columns: ["uid"]
209
- isOneToOne: true
210
- referencedRelation: "users"
211
- referencedColumns: ["id"]
212
- },
213
- ]
208
+ Relationships: []
214
209
  }
215
210
  scores: {
216
211
  Row: {
@@ -363,3 +358,18 @@ export type Enums<
363
358
  : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"]
364
359
  ? PublicSchema["Enums"][PublicEnumNameOrOptions]
365
360
  : never
361
+
362
+ export type CompositeTypes<
363
+ PublicCompositeTypeNameOrOptions extends
364
+ | keyof PublicSchema["CompositeTypes"]
365
+ | { schema: keyof Database },
366
+ CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
367
+ schema: keyof Database
368
+ }
369
+ ? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
370
+ : never = never,
371
+ > = PublicCompositeTypeNameOrOptions extends { schema: keyof Database }
372
+ ? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
373
+ : PublicCompositeTypeNameOrOptions extends keyof PublicSchema["CompositeTypes"]
374
+ ? PublicSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
375
+ : never
@@ -1,4 +1,4 @@
1
- import { BeatmapDecoder } from "osu-parsers";
1
+ import { BeatmapDecoder, BeatmapEncoder } from "osu-parsers";
2
2
  import { StandardRuleset } from "osu-standard-stable";
3
3
  import { sampleMap } from "./osuUtils";
4
4
  import { SSPMParsedMap } from "./sspmParser";
@@ -18,9 +18,6 @@ export function calculatePerformancePoints(
18
18
  }
19
19
 
20
20
  export function rateMap(map: SSPMParsedMap) {
21
- const decoder = new BeatmapDecoder();
22
- const beatmap1 = decoder.decodeFromString(sampleMap);
23
-
24
21
  let notes = map.markers
25
22
  .filter((marker) => marker.type === 0)
26
23
  .map((marker) => ({
@@ -29,44 +26,52 @@ export function rateMap(map: SSPMParsedMap) {
29
26
  y: marker.data["field0"].y,
30
27
  }));
31
28
 
32
- notes = notes.sort((a, b) => a.time - b.time);
33
-
34
- for (const note of notes) {
35
- const hittable = beatmap1.hitObjects[0].clone();
36
- hittable.startX = Math.round((note.x / 2) * 100);
37
- hittable.startY = Math.round((note.y / 2) * 100);
38
- hittable.startTime = note.time;
39
- beatmap1.hitObjects.push(hittable);
40
- }
41
- const ruleset = new StandardRuleset();
42
- const mods = ruleset.createModCombination("RX");
43
- const difficultyCalculator = ruleset.createDifficultyCalculator(beatmap1);
44
- const difficultyAttributes = difficultyCalculator.calculateWithMods(mods);
45
- return difficultyAttributes.starRating;
29
+ return rate(notes);
46
30
  }
47
31
 
48
32
  export function rateMapOld(map: SSPMMap) {
49
- const decoder = new BeatmapDecoder();
50
- const beatmap1 = decoder.decodeFromString(sampleMap);
51
-
52
33
  let notes = map.notes.map((marker) => ({
53
34
  time: marker.position,
54
35
  x: marker.x,
55
36
  y: marker.y,
56
37
  }));
57
38
 
39
+ return rate(notes);
40
+ }
41
+
42
+ export function rate(
43
+ notes: {
44
+ time: number;
45
+ x: number;
46
+ y: number;
47
+ }[]
48
+ ) {
58
49
  notes = notes.sort((a, b) => a.time - b.time);
50
+ const decoder = new BeatmapDecoder();
51
+ const encoder = new BeatmapEncoder();
52
+ const beatmap1 = decoder.decodeFromString(sampleMap);
53
+ let i = 0;
54
+ while (i < notes.length - 1) {
55
+ const note = notes[i];
56
+ const nextNote = notes[i + 1];
57
+ if (distance(note.x, note.y, nextNote.x, nextNote.y) < 1.25) {
58
+ notes.splice(i + 1, 1);
59
+ continue;
60
+ }
59
61
 
60
- for (const note of notes) {
61
62
  const hittable = beatmap1.hitObjects[0].clone();
62
63
  hittable.startX = Math.round((note.x / 2) * 100);
63
64
  hittable.startY = Math.round((note.y / 2) * 100);
64
65
  hittable.startTime = note.time;
65
66
  beatmap1.hitObjects.push(hittable);
67
+ i++;
66
68
  }
67
69
  const ruleset = new StandardRuleset();
68
70
  const mods = ruleset.createModCombination("RX");
69
71
  const difficultyCalculator = ruleset.createDifficultyCalculator(beatmap1);
70
72
  const difficultyAttributes = difficultyCalculator.calculateWithMods(mods);
73
+ encoder.encodeToPath("./map.osu", beatmap1);
71
74
  return difficultyAttributes.starRating;
72
75
  }
76
+
77
+ const distance = (x1, y1, x2, y2) => Math.hypot(x2 - x1, y2 - y1);