Added an api

Fixed image loading with said api
This commit is contained in:
Liam 2022-02-20 23:52:57 +00:00
parent 78b92e9ec2
commit 18098707d1
10 changed files with 1173 additions and 43 deletions

@ -14,6 +14,10 @@ export default class ScoreStats extends Component {
const data = this.props.data; const data = this.props.data;
return <div className={'score-stats'}> return <div className={'score-stats'}>
<div className={'score-stats-info'}>
<p>{data.percentage}</p>
<p>{data.currentScore.toLocaleString()}</p>
</div>
<p className={'score-stats-average-cut'}>Average Cut</p> <p className={'score-stats-average-cut'}>Average Cut</p>
<div className={'score-stats-hands'}> <div className={'score-stats-hands'}>
<div className={'score-stats-left'}> <div className={'score-stats-left'}>
@ -27,11 +31,6 @@ export default class ScoreStats extends Component {
<p>{this.getAverage(data.rightHand.averageCut).toFixed(2)}</p> <p>{this.getAverage(data.rightHand.averageCut).toFixed(2)}</p>
</div> </div>
</div> </div>
<div className={'score-stats-info'}>
<p>{data.percentage}</p>
<p>{data.currentScore.toLocaleString()}</p>
</div>
</div> </div>
} }
} }

@ -1,5 +1,6 @@
import {Component} from "react"; import {Component} from "react";
import Image from 'next/image' import Image from 'next/image'
import Config from "../config.json";
export default class SongInfo extends Component { export default class SongInfo extends Component {
@ -34,7 +35,6 @@ export default class SongInfo extends Component {
} }
msToMinSeconds(millis) { msToMinSeconds(millis) {
console.log(millis)
const minutes = Math.floor(millis / 60000); const minutes = Math.floor(millis / 60000);
const seconds = Number(((millis % 60000) / 1000).toFixed(0)); const seconds = Number(((millis % 60000) / 1000).toFixed(0));
return seconds === 60 ? minutes + 1 + ":00" : minutes + ":" + (seconds < 10 ? "0" : "") + seconds; return seconds === 60 ? minutes + 1 + ":00" : minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
@ -42,9 +42,9 @@ export default class SongInfo extends Component {
render() { render() {
const data = this.props.data.songData.status.beatmap; const data = this.props.data.songData.status.beatmap;
const beatSaverData = this.props.data.beatSaverData; const beatSaverData = this.props.data.beatSaverData.data;
const { id: bsr } = beatSaverData; const songArt = beatSaverData.songArt;
const songArt = beatSaverData.versions[0].coverURL; const bsr = beatSaverData.bsr;
const { const {
songName, songName,
songAuthorName, songAuthorName,
@ -53,7 +53,7 @@ export default class SongInfo extends Component {
const songTimerPercentage = ((this.props.data.currentSongTime / 1000) / (data.length / 1000)) * 100000; const songTimerPercentage = ((this.props.data.currentSongTime / 1000) / (data.length / 1000)) * 100000;
return <div className={'song-info-container'}> return <div className={'song-info-container'}>
<Image src={songArt} width={150} height={150}/> <img src={songArt}/>
<div className={'song-info'}> <div className={'song-info'}>
<p className={'song-info-song-name'}>{songName}</p> <p className={'song-info-song-name'}>{songName}</p>
<p className={'song-info-song-author'}>{songAuthorName}</p> <p className={'song-info-song-author'}>{songAuthorName}</p>

@ -3,8 +3,9 @@ module.exports = {
images: { images: {
domains: [ domains: [
'cdn.scoresaber.com', 'cdn.scoresaber.com',
'na.cdn.beatsaver.com', 'bs-overlay.fascinated.cc',
'eu.cdn.beatsaver.com' 'localhost:3000',
''
], ],
} }
} }

1111
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -14,7 +14,8 @@
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react": "17.0.2", "react": "17.0.2",
"react-country-flag": "^3.0.2", "react-country-flag": "^3.0.2",
"react-dom": "17.0.2" "react-dom": "17.0.2",
"sharp": "^0.30.1"
}, },
"devDependencies": { "devDependencies": {
"eslint": "8.9.0", "eslint": "8.9.0",

@ -0,0 +1,31 @@
import fs from 'fs';
import path from 'path';
import sharp from 'sharp';
const cacheDir = process.cwd() + path.sep + "cache";
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir);
console.log("Created cache directory")
}
export default async function handler(req, res) {
const mapHash = req.query.hash.replace("custom_level_", "").toLowerCase();
const ext = req.query.ext;
const imagePath = cacheDir + path.sep + mapHash + "." + ext;
const exists = fs.existsSync(imagePath);
console.log(`https://na.cdn.beatsaver.com/${mapHash}.${ext}`)
if (!exists) {
const data = await fetch(`https://na.cdn.beatsaver.com/${mapHash}.${ext}` );
let buffer = await data.buffer();
buffer = await sharp(buffer).resize(150, 150).toBuffer()
fs.writeFileSync(imagePath, buffer);
res.setHeader('Content-Type', 'image/' + ext)
res.send(buffer);
console.log("Song Cache - Added song \"" + mapHash + "\"")
return;
}
const buffer = fs.readFileSync(imagePath);
res.setHeader('Content-Type', 'image/jpg' + ext)
res.send(buffer);
}

@ -0,0 +1,15 @@
import Utils from '../../../utils/utils'
export default async function handler(req, res) {
const mapHash = req.query.hash;
const mapData = await Utils.getMapData(mapHash.replace("custom_level_", ""));
if (mapData === undefined) {
return res.json({ error: true, message: "Unknown map" })
}
const data = {
bsr: mapData.id,
songArt: "http://" + req.headers.host + "/api/beatsaver/art/" + mapHash + "?ext=" + mapData.versions[0].coverURL.split("/")[3].split(".")[1]
};
res.json({ error: false, data: data })
}

@ -8,11 +8,12 @@ import SongInfo from "../components/SongInfo";
// Why do u have to proxy requests... it's so dumb LOL // Why do u have to proxy requests... it's so dumb LOL
const SCORESABER_API_URL = Config.proxy_url + "/https://scoresaber.com/api/player/%s/full"; const SCORESABER_API_URL = Config.proxy_url + "/https://scoresaber.com/api/player/%s/full";
const BEATSAVER_API_URL = Config.proxy_url + "/https://api.beatsaver.com/maps/hash/%s";
const GITHUB_URL = "https://github.com/RealFascinated/beatsaber-overlay"; const GITHUB_URL = "https://github.com/RealFascinated/beatsaber-overlay";
export default class Home extends Component { export default class Home extends Component {
#_beatSaverURL = "";
constructor(props) { constructor(props) {
super(props); super(props);
@ -58,6 +59,7 @@ export default class Home extends Component {
} }
async componentDidMount() { async componentDidMount() {
this.#_beatSaverURL = document.location.origin + "/api/beatsaver/map?hash=%s";
const urlSearchParams = new URLSearchParams(window.location.search); const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries()); const params = Object.fromEntries(urlSearchParams.entries());
@ -110,7 +112,7 @@ export default class Home extends Component {
connectSocket() { connectSocket() {
const socket = new WebSocket('ws://localhost:6557/socket'); const socket = new WebSocket('ws://localhost:6557/socket');
socket.addEventListener('error' || 'close', () => { socket.addEventListener('close', () => {
console.log("Attempting to re-connect to the HTTP Status socket in 30 seconds."); console.log("Attempting to re-connect to the HTTP Status socket in 30 seconds.");
setTimeout(() => this.connectSocket(), 30_000); setTimeout(() => this.connectSocket(), 30_000);
}); });
@ -127,9 +129,8 @@ export default class Home extends Component {
async setBeatSaver(songData) { async setBeatSaver(songData) {
console.log("Updating BeatSaver info") console.log("Updating BeatSaver info")
const data = await fetch(BEATSAVER_API_URL.replace("%s", songData.levelId.replace("custom_level_", ""))); const data = await fetch(this.#_beatSaverURL.replace("%s", songData.levelId));
const json = await data.json(); const json = await data.json();
console.log(json)
this.setState({ beatSaverData: json }) this.setState({ beatSaverData: json })
} }

@ -56,7 +56,7 @@
} }
.player-country-icon { .player-country-icon {
margin-top: -10px; margin-top: -11px;
border-radius: 30%; border-radius: 30%;
} }
@ -98,11 +98,7 @@
} }
.score-stats-info { .score-stats-info {
position: fixed;
bottom:0;
right:0;
margin-right: 10px; margin-right: 10px;
margin-bottom: -10px;
text-align: right; text-align: right;
} }

17
utils/utils.js Normal file

@ -0,0 +1,17 @@
import Config from '../config.json';
module.exports = {
BEATSAVER_MAP_API: Config.proxy_url + "/https://api.beatsaver.com/maps/hash/%s",
// todo: cache map data for 12 hrs
async getMapData(hash) {
const data = await fetch(this.BEATSAVER_MAP_API.replace("%s", hash), {
headers: {
"origin": "Fascinated Overlay"
}
});
if (data.status === 404) {
return undefined;
}
return await data.json();
}
}