mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-11 16:58:25 +03:00
Merge pull request #670 from hewoicvewse/sgl-tables
This commit is contained in:
@@ -253,3 +253,13 @@ export function FormatSieglindePMS(sgl: number): string {
|
||||
|
||||
return `●${(sgl - 12).toFixed(2)}`;
|
||||
}
|
||||
|
||||
export function CreateSongMap<G extends Game = Game>(songs: Array<SongDocument<G>>) {
|
||||
const songMap = new Map<integer, SongDocument<G>>();
|
||||
|
||||
for (const song of songs) {
|
||||
songMap.set(song.id, song);
|
||||
}
|
||||
|
||||
return songMap;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import t from "tap";
|
||||
import mockApi from "test-utils/mock-api";
|
||||
|
||||
t.test("GET /api/v1/games/bms/7K/content", (t) => {
|
||||
t.test("Should exist.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/games/bms/7K/content");
|
||||
|
||||
t.equal(res.body.success, true);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("GET /api/v1/games/bms/7K/content/sieglindeEC", (t) => {
|
||||
t.test("Should point to the right file.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/games/bms/7K/content/sieglindeEC/header.json");
|
||||
|
||||
t.equal(res.body.data_url, "./body.json");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("`body.json` should return an array.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/games/bms/7K/content/sieglindeEC/body.json");
|
||||
|
||||
t.equal(Array.isArray(res.body), true);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("GET /api/v1/games/bms/7K/content/sieglindeHC", (t) => {
|
||||
t.test("Should point to the right file.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/games/bms/7K/content/sieglindeHC/header.json");
|
||||
|
||||
t.equal(res.body.data_url, "./body.json");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("`body.json` should return an array.", async (t) => {
|
||||
const res = await mockApi.get("/api/v1/games/bms/7K/content/sieglindeHC/body.json");
|
||||
|
||||
t.equal(Array.isArray(res.body), true);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
@@ -0,0 +1,178 @@
|
||||
import { Router } from "express";
|
||||
import db from "external/mongo/db";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { CreateSongMap } from "tachi-common";
|
||||
import { integer, SongDocument } from "tachi-common/types";
|
||||
import { GetFolderCharts, GetFoldersFromTable } from "utils/folder";
|
||||
import { GetTachiData } from "utils/req-tachi-data";
|
||||
import type { Request, Response } from "express";
|
||||
import type { ChartDocument } from "tachi-common/types";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const router: Router = Router({ mergeParams: true });
|
||||
|
||||
/**
|
||||
* Returns "Endpoint exists"
|
||||
*
|
||||
* @name GET /api/v1/games/bms/7K/content
|
||||
*/
|
||||
router.get("/", (req, res) => {
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
description: "Endpoint exists",
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Utility function for mounting a BMS-style Table. This defines
|
||||
* a header.json endpoint, and a body.json endpoint.
|
||||
*
|
||||
* It also defines a HTML-emitting endpoint that contains a pointer
|
||||
* to said header.json file.
|
||||
*/
|
||||
function CreateAndMountTable(
|
||||
router: Router,
|
||||
tableName: string,
|
||||
headerHandler: (req: Request, res: Response) => any,
|
||||
bodyHandler: (req: Request, res: Response) => any
|
||||
) {
|
||||
router.get(`/${tableName}`, (req, res) => {
|
||||
return res.status(200).send(`<html>
|
||||
<head>
|
||||
<meta name="bmstable" value="./${tableName}/header.json">
|
||||
</head>
|
||||
<body>This is a stub page for beatoraja compatibility. No useful information here!</body>
|
||||
</html>`);
|
||||
});
|
||||
|
||||
router.get(`/${tableName}/header.json`, headerHandler);
|
||||
|
||||
router.get(`/${tableName}/body.json`, bodyHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a difficulty table of all charts rated according to their Sieglinde values for easy clears
|
||||
*
|
||||
* @name GET /api/v1/games/bms/7k/content/sieglindeEC
|
||||
*/
|
||||
CreateAndMountTable(
|
||||
router,
|
||||
"sieglindeEC",
|
||||
/* header.json */ (_req, res) => {
|
||||
return res.status(200).send({
|
||||
name: "Sieglinde EC",
|
||||
symbol: "sgl-",
|
||||
data_url: "./body.json",
|
||||
});
|
||||
},
|
||||
/* body.json */ async (req, res) => {
|
||||
const game = "bms";
|
||||
|
||||
const table = await db.tables.findOne({
|
||||
tableID: "bms-7K-sgl-EC",
|
||||
game,
|
||||
playtype: "7K",
|
||||
});
|
||||
|
||||
if (!table) {
|
||||
logger.error(
|
||||
"Could not find table bms-7K-sgl-EC. Cannot convert sieglinde tables into BMS form!"
|
||||
);
|
||||
return res.status(500).json([]);
|
||||
}
|
||||
|
||||
const folders = await GetFoldersFromTable(table);
|
||||
const response = [];
|
||||
|
||||
for (const folder of folders) {
|
||||
const data = await GetFolderCharts(folder, {}, true);
|
||||
const charts = data.charts as Array<ChartDocument<"bms:7K">>;
|
||||
const songMap = CreateSongMap(data.songs);
|
||||
|
||||
for (const chart of charts) {
|
||||
const song = songMap.get(chart.songID);
|
||||
|
||||
if (!song) {
|
||||
response.push({
|
||||
level: folder.title,
|
||||
md5: chart.data.hashMD5,
|
||||
});
|
||||
} else {
|
||||
response.push({
|
||||
level: folder.title,
|
||||
title: song.title,
|
||||
artist: song.artist,
|
||||
md5: chart.data.hashMD5,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res.status(200).json(response);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns a difficulty table of all charts rated according to their Sieglinde values for hard clears
|
||||
*
|
||||
* @name GET /api/v1/games/bms/7k/content/sieglindeHC
|
||||
*/
|
||||
CreateAndMountTable(
|
||||
router,
|
||||
"sieglindeHC",
|
||||
/* header.json */ (_req, res) => {
|
||||
return res.status(200).send({
|
||||
name: "Sieglinde HC",
|
||||
symbol: "sgl-",
|
||||
data_url: "./body.json",
|
||||
});
|
||||
},
|
||||
/* body.json */ async (req, res) => {
|
||||
const game = "bms";
|
||||
|
||||
const table = await db.tables.findOne({
|
||||
tableID: "bms-7K-sgl-HC",
|
||||
game,
|
||||
playtype: "7K",
|
||||
});
|
||||
|
||||
if (!table) {
|
||||
logger.error(
|
||||
"Could not find table bms-7K-sgl-HC. Cannot convert sieglinde tables into BMS form!"
|
||||
);
|
||||
return res.status(500).json([]);
|
||||
}
|
||||
|
||||
const folders = await GetFoldersFromTable(table);
|
||||
const response = [];
|
||||
|
||||
for (const folder of folders) {
|
||||
const data = await GetFolderCharts(folder, {}, true);
|
||||
const charts = data.charts as Array<ChartDocument<"bms:7K">>;
|
||||
const songMap = CreateSongMap(data.songs);
|
||||
|
||||
for (const chart of charts) {
|
||||
const song = songMap.get(chart.songID);
|
||||
|
||||
if (!song) {
|
||||
response.push({
|
||||
level: folder.title,
|
||||
md5: chart.data.hashMD5,
|
||||
});
|
||||
} else {
|
||||
response.push({
|
||||
level: folder.title,
|
||||
title: song.title,
|
||||
artist: song.artist,
|
||||
md5: chart.data.hashMD5,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res.status(200).json(response);
|
||||
}
|
||||
);
|
||||
|
||||
export default router;
|
||||
@@ -1,3 +1,4 @@
|
||||
import bmsContentRouter from "./_game/_playtype/content/bms-router";
|
||||
import gameRouter from "./_game/router";
|
||||
import { Router } from "express";
|
||||
import { TachiConfig } from "lib/setup/config";
|
||||
@@ -25,6 +26,14 @@ router.get("/", (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
// "Content" endpoints are game-playtype specific.
|
||||
// as such, we want to mount them *before* any generic endpoints
|
||||
// so that they'll guaranteeably get hit.
|
||||
|
||||
// This is a bit unorthodox, but arguably necessary, as all /content
|
||||
// endpoints really are *entirely* GPT specific.
|
||||
router.use("/bms/7K/content", bmsContentRouter);
|
||||
|
||||
router.use("/:game", gameRouter);
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user