diff --git a/src/app/api/beatsaver/mapdata/route.ts b/src/app/api/beatsaver/mapdata/route.ts index 278f3df..56fbd84 100644 --- a/src/app/api/beatsaver/mapdata/route.ts +++ b/src/app/api/beatsaver/mapdata/route.ts @@ -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 = {}; - 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); + } } } diff --git a/src/utils/beatsaver/api.ts b/src/utils/beatsaver/api.ts index 2addb1e..0edc5d7 100644 --- a/src/utils/beatsaver/api.ts +++ b/src/utils/beatsaver/api.ts @@ -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 { +async function fetchMapByHash(hash: string): Promise { 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, };