taevas.xyz/api/infos/gaming/speedruncom.ts
2025-04-03 15:16:59 +02:00

96 lines
3 KiB
TypeScript

import {type SpeedruncomInfo} from "#Infos/Gaming/Speedruncom.tsx";
import type { Handler } from "../..";
const user_id = "j03v45mj";
interface Runs {
data: {
place: number;
run: {
weblink: string;
game: string;
level?: string;
category?: string;
videos: {
links: {
uri: string
}[]
}
date: string;
times: {
primary_t: number
}
};
}[]
}
interface Game {
data: {
names: {
international: string;
};
assets: {
"cover-tiny": {
uri: string;
};
};
};
}
interface Level {
data: {
name: string;
};
}
export const speedruncom: Handler = async () => {
// using the API's embedding would be stupid here, as that'd create lag due to irrelevant runs
/** https://github.com/speedruncomorg/api/blob/master/version1/users.md#get-usersidpersonal-bests */
const speedruncom = await (await fetch(`https://www.speedrun.com/api/v1/users/${user_id}/personal-bests`)).json() as Runs;
const data = speedruncom.data.at(0);
if (!data) {
return new Response("Not Found", {status: 404});
}
/** https://github.com/speedruncomorg/api/blob/master/version1/games.md#get-gamesid */
const urlsToRequest = [`https://www.speedrun.com/api/v1/games/${data.run.game}`];
/** https://github.com/speedruncomorg/api/blob/master/version1/levels.md#get-levelsid */
if (data.run.level) {urlsToRequest.push(`https://www.speedrun.com/api/v1/levels/${data.run.level}`);}
/** https://github.com/speedruncomorg/api/blob/master/version1/categories.md */
if (data.run.category) {urlsToRequest.push(`https://www.speedrun.com/api/v1/categories/${data.run.category}`);}
const toRequest = urlsToRequest.map((url) => new Promise(async (resolve) => resolve(await (await fetch(url)).json())));
const responses = await Promise.all(toRequest) as [Game, Level?, Level?];
const game = responses[0];
const details = [responses[1]].concat(responses[2]).filter((d) => d !== undefined);
const run: SpeedruncomInfo = {
place: data.place,
link: data.run.weblink,
date: data.run.date,
thumbnail: game.data.assets["cover-tiny"].uri,
game: game.data.names.international,
details: details.map((d) => d.data.name),
time: sec2time(data.run.times.primary_t),
video: data.run.videos.links.at(0)?.uri,
};
while (run.time.startsWith("0") || run.time.startsWith(":")) {
run.time = run.time.substring(1);
}
return Response.json(run, {status: 200});
};
// https://gist.github.com/vankasteelj/74ab7793133f4b257ea3
function sec2time(timeInSeconds: number) {
const pad = (num: number, size: number) => ("000" + num).slice(size * -1);
const time = Number(parseFloat(timeInSeconds.toString()).toFixed(3));
const hours = Math.floor(time / 60 / 60);
const minutes = Math.floor(time / 60) % 60;
const seconds = Math.floor(time - minutes * 60);
const milliseconds = Number(time.toString().slice(-3));
return pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + "." + pad(milliseconds, 3);
};