Only cache what we need from BeatSaver
This commit is contained in:
parent
efbbd6b6f1
commit
7665cbbae4
@ -1,55 +0,0 @@
|
||||
import fetch from "node-fetch";
|
||||
import sharp from "sharp";
|
||||
import {
|
||||
getValue,
|
||||
setValue,
|
||||
valueExists,
|
||||
} from "../../../../src/utils/redisUtils";
|
||||
|
||||
const KEY = "BS_MAP_ART_";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @returns
|
||||
*/
|
||||
export default async function handler(req, res) {
|
||||
const mapHash = req.query.hash.replace("custom_level_", "").toLowerCase();
|
||||
const ext = req.query.ext || "jpg";
|
||||
|
||||
const exists = await valueExists(`${KEY}${mapHash}`.replace(" ", ""));
|
||||
if (exists) {
|
||||
const data = await getValue(`${KEY}${mapHash}`);
|
||||
const buffer = Buffer.from(data, "base64");
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "image/" + ext,
|
||||
"Content-Length": buffer.length,
|
||||
"Cache-Status": "hit",
|
||||
});
|
||||
return res.end(buffer);
|
||||
}
|
||||
|
||||
const before = Date.now();
|
||||
const data = await fetch(`https://eu.cdn.beatsaver.com/${mapHash}.${ext}`);
|
||||
if (data.status === 404) {
|
||||
return res.status(404).json({
|
||||
status: 404,
|
||||
message: "Unknown Map Hash",
|
||||
});
|
||||
}
|
||||
|
||||
let buffer = await data.buffer(); // Change to arrayBuffer at some point to make it shush
|
||||
buffer = await sharp(buffer).resize(400, 400).toBuffer();
|
||||
const bytes = buffer.toString("base64");
|
||||
|
||||
await setValue(`${KEY}${mapHash}`.replace(" ", ""), bytes);
|
||||
console.log(
|
||||
`[Cache]: Cached BS Song Art for hash ${mapHash} in ${
|
||||
Date.now() - before
|
||||
}ms`
|
||||
);
|
||||
res.setHeader("Cache-Status", "miss");
|
||||
res.setHeader("Content-Type", "image/" + ext);
|
||||
res.status(200).send(buffer);
|
||||
}
|
@ -11,12 +11,5 @@ export default async function handler(req, res) {
|
||||
message: "Unknown Map Hash",
|
||||
});
|
||||
}
|
||||
const data = {
|
||||
// The maps data from the provided map hash
|
||||
bsr: mapData.id,
|
||||
songArt: `http://${req.headers.host}/api/beatsaver/art/${mapHash}?ext=${
|
||||
mapData.versions[0].coverURL.split("/")[3].split(".")[1]
|
||||
}`,
|
||||
};
|
||||
res.status(200).json({ error: false, data: data });
|
||||
res.status(200).json({ status: "OK", data: mapData });
|
||||
}
|
||||
|
@ -56,12 +56,11 @@ export default class SongInfo extends Component {
|
||||
render() {
|
||||
const data = this.props.data.songData.status.beatmap;
|
||||
const beatSaverData = this.props.data.beatSaverData.data;
|
||||
const songArt = beatSaverData.songArt;
|
||||
const mapArt = beatSaverData.mapArt;
|
||||
const bsr = beatSaverData.bsr;
|
||||
const { songName, songAuthorName, difficulty } = data;
|
||||
// what in the fuck is this?? LMFAO
|
||||
const songTimerPercentage =
|
||||
(this.props.data.currentSongTime / 1000 / (data.length / 1000)) * 100000;
|
||||
(this.props.data.currentSongTime / data.length) * 100;
|
||||
|
||||
return (
|
||||
<div className={styles.songInfoContainer}>
|
||||
@ -69,7 +68,7 @@ export default class SongInfo extends Component {
|
||||
width={150}
|
||||
height={150}
|
||||
alt="Song artwork"
|
||||
src={songArt}
|
||||
src={mapArt}
|
||||
loading="lazy"
|
||||
placeholder="blur"
|
||||
blurDataURL="https://cdn.fascinated.cc/IkQFyodbZv.jpg?raw=true"
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { BeatSaverMapData } from "../types/BeatSaverMapData";
|
||||
import { getValue, setValue, valueExists } from "../utils/redisUtils";
|
||||
|
||||
const BEATSAVER_MAP_API =
|
||||
@ -6,13 +7,28 @@ const BEATSAVER_MAP_API =
|
||||
|
||||
const KEY = "BS_MAP_DATA_";
|
||||
|
||||
function getLatestMapArt(data: BeatSaverMapData) {
|
||||
console.log(data);
|
||||
let url: string | undefined = undefined;
|
||||
for (const version of data.versions) {
|
||||
url = version.coverURL;
|
||||
}
|
||||
console.log(url);
|
||||
return url;
|
||||
}
|
||||
|
||||
type MapData = {
|
||||
bsr: string;
|
||||
mapArt: string | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a specified maps data from BeatSaver
|
||||
*
|
||||
* @param {string} hash
|
||||
* @returns The map data
|
||||
*/
|
||||
export async function getMapData(hash) {
|
||||
export async function getMapData(hash): Promise<MapData | undefined> {
|
||||
const mapHash = hash.replace("custom_level_", "").toLowerCase();
|
||||
|
||||
const key = `${KEY}${mapHash}`;
|
||||
@ -31,7 +47,11 @@ export async function getMapData(hash) {
|
||||
if (data.status === 404) {
|
||||
return undefined;
|
||||
}
|
||||
const json = await data.json();
|
||||
const jsonResponse = await data.json();
|
||||
const json = {
|
||||
bsr: jsonResponse.id,
|
||||
mapArt: getLatestMapArt(jsonResponse),
|
||||
};
|
||||
await setValue(key, JSON.stringify(json));
|
||||
console.log(
|
||||
`[Cache]: Cached BS Map Data for hash ${mapHash} in ${
|
91
src/types/BeatSaverMapData.ts
Normal file
91
src/types/BeatSaverMapData.ts
Normal file
@ -0,0 +1,91 @@
|
||||
export type BeatSaverMapData = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
uploader: {
|
||||
id: string;
|
||||
name: string;
|
||||
hash: string;
|
||||
avatar: string;
|
||||
type: string;
|
||||
admin: boolean;
|
||||
curator: boolean;
|
||||
verifiedMapper: boolean;
|
||||
};
|
||||
metadata: {
|
||||
bpm: number;
|
||||
duration: number;
|
||||
songName: string;
|
||||
songSubName: string;
|
||||
songAuthorName: string;
|
||||
levelAuthorName: string;
|
||||
};
|
||||
stats: {
|
||||
plays: number;
|
||||
downloads: number;
|
||||
upvotes: number;
|
||||
downvotes: number;
|
||||
score: number;
|
||||
};
|
||||
uploaded: string;
|
||||
automapper: boolean;
|
||||
ranked: boolean;
|
||||
qualified: boolean;
|
||||
versions: Array<Version>;
|
||||
curator: Curator;
|
||||
curatedAt: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
lastPublishedAt: string;
|
||||
tags: Array<string>;
|
||||
};
|
||||
|
||||
export type Version = {
|
||||
hash: string;
|
||||
state: string;
|
||||
createdAt: string;
|
||||
sageScore: number;
|
||||
diffs: Array<Difficulty>;
|
||||
downloadURL: string;
|
||||
coverURL: string;
|
||||
previewURL: string;
|
||||
};
|
||||
|
||||
export type Difficulty = {
|
||||
njs: number;
|
||||
offset: number;
|
||||
notes: number;
|
||||
bombs: number;
|
||||
obstacles: number;
|
||||
nps: number;
|
||||
length: number;
|
||||
characteristic: string;
|
||||
difficulty: DifficultyType;
|
||||
events: number;
|
||||
chroma: boolean;
|
||||
me: boolean;
|
||||
ne: boolean;
|
||||
cinema: boolean;
|
||||
seconds: number;
|
||||
paritySummary: ParitySummary;
|
||||
stars: number;
|
||||
maxScore: number;
|
||||
};
|
||||
|
||||
export type DifficultyType = ["Easy", "Normal", "Hard", "Expert", "ExpertPlus"];
|
||||
|
||||
export type ParitySummary = {
|
||||
errors: number;
|
||||
warns: number;
|
||||
resets: number;
|
||||
};
|
||||
|
||||
export type Curator = {
|
||||
id: number;
|
||||
name: string;
|
||||
hash: string;
|
||||
avatar: string;
|
||||
type: string;
|
||||
admin: boolean;
|
||||
curator: boolean;
|
||||
};
|
Reference in New Issue
Block a user