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) { export async function GET(request: Request) {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const mapHashes = searchParams.get("hashes")?.split(",") ?? undefined; const mapHashes = searchParams.get("hashes")?.split(",") ?? undefined;
if (!mapHashes) { if (!mapHashes) {
return new Response("mapHashes parameter is required", { status: 400 }); return new Response("mapHashes parameter is required", { status: 400 });
} }
const idOnly = searchParams.get("idonly") === "true"; const idOnly = searchParams.get("idonly") === "true";
let totalInCache = 0; let totalInCache = 0;
const maps: Record<string, BeatsaverMap | { id: string }> = {}; const maps: Record<string, BeatsaverMap | { id: string }> = {};
for (const mapHash of mapHashes) {
const fetchMapFromCache = async (mapHash: string) => {
const cachedMap = await ( const cachedMap = await (
await Redis.client await Redis.client
).get(`beatsaver:map:${mapHash}`); ).get(`beatsaver:map:${mapHash}`);
if (cachedMap) { return cachedMap;
maps[mapHash] = JSON.parse(cachedMap); };
totalInCache++;
} else { const fetchAndCacheMap = async (mapHash: string) => {
const map = await BeatsaverAPI.fetchMapByHash(mapHash); const beatSaverMap = await BeatsaverAPI.fetchMapByHash(mapHash);
if (!map) { if (beatSaverMap) {
continue; maps[mapHash] = idOnly ? { id: beatSaverMap.id } : beatSaverMap;
}
maps[mapHash] = map;
await ( await (
await Redis.client await Redis.client
).set("beatsaver:map:" + mapHash, JSON.stringify(map), { ).set(
EX: 60 * 60 * 24 * 7, // 7 days `beatsaver:map:${mapHash}`,
}); JSON.stringify(idOnly ? { id: beatSaverMap.id } : beatSaverMap),
"EX",
60 * 60 * 24 * 7,
);
} }
};
if (idOnly) { for (const mapHash of mapHashes) {
maps[mapHash] = { id: (maps[mapHash] as BeatsaverMap).id }; 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"; const BS_API_URL = ssrSettings.proxy + "/https://api.beatsaver.com";
export const BS_GET_MAP_BY_HASH_URL = BS_API_URL + "/maps/hash/{}"; 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 * Returns the map info for the provided hash
* *
* @param hash the hash of the map * @param hash the hash of the map
* @returns the map info * @returns the map info
*/ */
async function fetchMapByHash( async function fetchMapByHash(hash: string): Promise<BeatsaverMap | undefined> {
hash: string,
): Promise<BeatsaverMap | undefined | null> {
const response = await BeatsaverFetchQueue.fetch( const response = await BeatsaverFetchQueue.fetch(
formatString(BS_GET_MAP_BY_HASH_URL, true, hash), formatString(BS_GET_MAP_BY_HASH_URL, true, hash),
); );
@ -29,9 +54,10 @@ async function fetchMapByHash(
return undefined; return undefined;
} }
return json as BeatsaverMap; return json;
} }
export const BeatsaverAPI = { export const BeatsaverAPI = {
fetchMapsByHash,
fetchMapByHash, fetchMapByHash,
}; };