make loading beatsaver map hashes way faster
All checks were successful
deploy / deploy (push) Successful in 2m9s

This commit is contained in:
Lee 2023-11-21 12:27:47 +00:00
parent 14f67af125
commit b3c3be4b7c
2 changed files with 71 additions and 19 deletions

@ -5,35 +5,61 @@ import { BeatsaverAPI } from "@/utils/beatsaver/api";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const mapHashes = searchParams.get("hashes")?.split(",") ?? undefined;
if (!mapHashes) {
return new Response("mapHashes parameter is required", { status: 400 });
}
const idOnly = searchParams.get("idonly") === "true";
let totalInCache = 0;
const maps: Record<string, BeatsaverMap | { id: string }> = {};
for (const mapHash of mapHashes) {
const fetchMapFromCache = async (mapHash: string) => {
const cachedMap = await (
await Redis.client
).get(`beatsaver:map:${mapHash}`);
if (cachedMap) {
maps[mapHash] = JSON.parse(cachedMap);
totalInCache++;
} else {
const map = await BeatsaverAPI.fetchMapByHash(mapHash);
if (!map) {
continue;
}
maps[mapHash] = map;
return cachedMap;
};
const fetchAndCacheMap = async (mapHash: string) => {
const beatSaverMap = await BeatsaverAPI.fetchMapByHash(mapHash);
if (beatSaverMap) {
maps[mapHash] = idOnly ? { id: beatSaverMap.id } : beatSaverMap;
await (
await Redis.client
).set("beatsaver:map:" + mapHash, JSON.stringify(map), {
EX: 60 * 60 * 24 * 7, // 7 days
});
).set(
`beatsaver:map:${mapHash}`,
JSON.stringify(idOnly ? { id: beatSaverMap.id } : beatSaverMap),
"EX",
60 * 60 * 24 * 7,
);
}
};
if (idOnly) {
maps[mapHash] = { id: (maps[mapHash] as BeatsaverMap).id };
for (const mapHash of mapHashes) {
const map = await fetchMapFromCache(mapHash);
if (map) {
maps[mapHash] = JSON.parse(map);
totalInCache++;
}
}
if (totalInCache === 0) {
const beatSaverMaps = await BeatsaverAPI.fetchMapsByHash(...mapHashes);
if (beatSaverMaps) {
for (const mapHash of mapHashes) {
const beatSaverMap = beatSaverMaps[mapHash.toLowerCase()];
if (beatSaverMap) {
await fetchAndCacheMap(mapHash);
}
}
}
} else {
for (const mapHash of mapHashes) {
if (!maps[mapHash]) {
await fetchAndCacheMap(mapHash);
}
}
}

@ -10,15 +10,40 @@ export const BeatsaverFetchQueue = new FetchQueue();
const BS_API_URL = ssrSettings.proxy + "/https://api.beatsaver.com";
export const BS_GET_MAP_BY_HASH_URL = BS_API_URL + "/maps/hash/{}";
/**
* Returns the map info for the provided hashes
*
* @param hash the hashes for the maps
* @returns the map info
*/
async function fetchMapsByHash(
...hash: string[]
): Promise<{ [key: string]: BeatsaverMap } | undefined> {
const hashes = hash.join(",");
const response = await BeatsaverFetchQueue.fetch(
formatString(
BS_GET_MAP_BY_HASH_URL,
true,
hashes.substring(0, hashes.length - 1),
),
);
const json = await response.json();
// Check if there was an error fetching the user data
if (json.error) {
return undefined;
}
return json;
}
/**
* Returns the map info for the provided hash
*
* @param hash the hash of the map
* @returns the map info
*/
async function fetchMapByHash(
hash: string,
): Promise<BeatsaverMap | undefined | null> {
async function fetchMapByHash(hash: string): Promise<BeatsaverMap | undefined> {
const response = await BeatsaverFetchQueue.fetch(
formatString(BS_GET_MAP_BY_HASH_URL, true, hash),
);
@ -29,9 +54,10 @@ async function fetchMapByHash(
return undefined;
}
return json as BeatsaverMap;
return json;
}
export const BeatsaverAPI = {
fetchMapsByHash,
fetchMapByHash,
};