Formatted code
This commit is contained in:
parent
5b1c408fb9
commit
7c713a99ab
@ -1,49 +1,55 @@
|
|||||||
// 1. import `NextUIProvider` component
|
// 1. import `NextUIProvider` component
|
||||||
import { NextUIProvider } from '@nextui-org/react';
|
import { NextUIProvider } from "@nextui-org/react";
|
||||||
import { NextSeo } from 'next-seo';
|
import { NextSeo } from "next-seo";
|
||||||
import Head from 'next/head'
|
import Head from "next/head";
|
||||||
|
|
||||||
import Config from '../config.json';
|
import Config from "../config.json";
|
||||||
|
|
||||||
import '../styles/globals.css';
|
import "../styles/globals.css";
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }) {
|
function MyApp({ Component, pageProps }) {
|
||||||
return (
|
return (
|
||||||
// 2. Use at the root of your app
|
// 2. Use at the root of your app
|
||||||
<NextUIProvider>
|
<NextUIProvider>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
title={Config.name}
|
title={Config.name}
|
||||||
description={Config.description}
|
description={Config.description}
|
||||||
openGraph={{
|
openGraph={{
|
||||||
url: Config.url,
|
url: Config.url,
|
||||||
title: Config.name,
|
title: Config.name,
|
||||||
description: Config.description,
|
description: Config.description,
|
||||||
site_name: Config.name,
|
site_name: Config.name,
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: "https://cdn.fascinated.cc/YrATaLjUOP.png?raw=true",
|
url: "https://cdn.fascinated.cc/YrATaLjUOP.png?raw=true",
|
||||||
alt: "Site Example"
|
alt: "Site Example",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}}
|
}}
|
||||||
twitter={{
|
twitter={{
|
||||||
cardType: "summary_large_image",
|
cardType: "summary_large_image",
|
||||||
site: "@BeatSaber Overlay",
|
site: "@BeatSaber Overlay",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Head>
|
<Head>
|
||||||
<meta name="theme-color" content= {Config.color} />
|
<meta name="theme-color" content={Config.color} />
|
||||||
<meta property="og:keywords" content="BeatSaber,Overlay,OBS,Twitch,YouTube,BeatSaber Overlay,Github," />
|
<meta
|
||||||
|
property="og:keywords"
|
||||||
<noscript>
|
content="BeatSaber,Overlay,OBS,Twitch,YouTube,BeatSaber Overlay,Github,"
|
||||||
<img src="https://analytics.fascinated.cc/ingress/4bc413fa-a126-4860-9a6a-22d10d5cf2fb/pixel.gif" />
|
/>
|
||||||
</noscript>
|
|
||||||
<script defer src="https://analytics.fascinated.cc/ingress/4bc413fa-a126-4860-9a6a-22d10d5cf2fb/script.js"></script>
|
<noscript>
|
||||||
</Head>
|
<img src="https://analytics.fascinated.cc/ingress/4bc413fa-a126-4860-9a6a-22d10d5cf2fb/pixel.gif" />
|
||||||
<Component {...pageProps} />
|
</noscript>
|
||||||
</NextUIProvider>
|
<script
|
||||||
);
|
defer
|
||||||
|
src="https://analytics.fascinated.cc/ingress/4bc413fa-a126-4860-9a6a-22d10d5cf2fb/script.js"
|
||||||
|
></script>
|
||||||
|
</Head>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</NextUIProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MyApp;
|
export default MyApp;
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
import React from 'react';
|
import { CssBaseline } from "@nextui-org/react";
|
||||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
import Document, { Head, Html, Main, NextScript } from "next/document";
|
||||||
import { CssBaseline } from '@nextui-org/react';
|
import React from "react";
|
||||||
|
|
||||||
class MyDocument extends Document {
|
class MyDocument extends Document {
|
||||||
static async getInitialProps(ctx) {
|
static async getInitialProps(ctx) {
|
||||||
const initialProps = await Document.getInitialProps(ctx);
|
const initialProps = await Document.getInitialProps(ctx);
|
||||||
return {
|
return {
|
||||||
...initialProps,
|
...initialProps,
|
||||||
styles: React.Children.toArray([initialProps.styles])
|
styles: React.Children.toArray([initialProps.styles]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Html lang="en">
|
<Html lang="en">
|
||||||
<Head>
|
<Head>
|
||||||
{CssBaseline.flush()}
|
{CssBaseline.flush()}
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css2?family=Teko:wght@300&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Teko:wght@300&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<body>
|
<body>
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
</body>
|
</body>
|
||||||
</Html>
|
</Html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MyDocument;
|
export default MyDocument;
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
import path from 'path';
|
import fetch from "node-fetch";
|
||||||
import sharp from 'sharp';
|
import path from "path";
|
||||||
import fetch from 'node-fetch';
|
import sharp from "sharp";
|
||||||
|
|
||||||
const cacheDir = process.cwd() + path.sep + "cache";
|
const cacheDir = process.cwd() + path.sep + "cache";
|
||||||
if (!fs.existsSync(cacheDir)) {
|
if (!fs.existsSync(cacheDir)) {
|
||||||
fs.mkdirSync(cacheDir);
|
fs.mkdirSync(cacheDir);
|
||||||
console.log("Created cache directory");
|
console.log("Created cache directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const mapHash = req.query.hash.replace("custom_level_", "").toLowerCase();
|
const mapHash = req.query.hash.replace("custom_level_", "").toLowerCase();
|
||||||
const ext = req.query.ext;
|
const ext = req.query.ext;
|
||||||
|
|
||||||
const imagePath = cacheDir + path.sep + mapHash + "." + ext;
|
const imagePath = cacheDir + path.sep + mapHash + "." + ext;
|
||||||
const exists = fs.existsSync(imagePath);
|
const exists = fs.existsSync(imagePath);
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
const data = await fetch(`https://eu.cdn.beatsaver.com/${mapHash}.${ext}`);
|
const data = await fetch(`https://eu.cdn.beatsaver.com/${mapHash}.${ext}`);
|
||||||
let buffer = await data.buffer();
|
let buffer = await data.buffer();
|
||||||
buffer = await sharp(buffer).resize(150, 150).toBuffer();
|
buffer = await sharp(buffer).resize(150, 150).toBuffer();
|
||||||
fs.writeFileSync(imagePath, buffer);
|
fs.writeFileSync(imagePath, buffer);
|
||||||
res.setHeader('Content-Type', 'image/' + ext);
|
res.setHeader("Content-Type", "image/" + ext);
|
||||||
res.send(buffer);
|
res.send(buffer);
|
||||||
console.log("Song Art Cache - Added song \"" + mapHash + "\"");
|
console.log('Song Art Cache - Added song "' + mapHash + '"');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const buffer = fs.readFileSync(imagePath);
|
const buffer = fs.readFileSync(imagePath);
|
||||||
res.setHeader('Content-Type', 'image/jpg' + ext);
|
res.setHeader("Content-Type", "image/jpg" + ext);
|
||||||
res.send(buffer);
|
res.send(buffer);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
import Utils from '../../../utils/utils';
|
import Utils from "../../../utils/utils";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const mapHash = req.query.hash;
|
const mapHash = req.query.hash;
|
||||||
|
|
||||||
const mapData = await Utils.getMapData(mapHash.replace("custom_level_", ""));
|
const mapData = await Utils.getMapData(mapHash.replace("custom_level_", ""));
|
||||||
if (mapData === undefined) { // Check if a map hash was provided
|
if (mapData === undefined) {
|
||||||
return res.json({ error: true, message: "Unknown map" });
|
// Check if a map hash was provided
|
||||||
}
|
return res.json({ error: true, message: "Unknown map" });
|
||||||
const data = { // The maps data from the provided map hash
|
}
|
||||||
bsr: mapData.id,
|
const data = {
|
||||||
songArt: "http://" + req.headers.host + "/api/beatsaver/art/" + mapHash + "?ext=" + mapData.versions[0].coverURL
|
// The maps data from the provided map hash
|
||||||
.split("/")[3].split(".")[1]
|
bsr: mapData.id,
|
||||||
};
|
songArt:
|
||||||
res.json({ error: false, data: data });
|
"http://" +
|
||||||
|
req.headers.host +
|
||||||
|
"/api/beatsaver/art/" +
|
||||||
|
mapHash +
|
||||||
|
"?ext=" +
|
||||||
|
mapData.versions[0].coverURL.split("/")[3].split(".")[1],
|
||||||
|
};
|
||||||
|
res.json({ error: false, data: data });
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
res.json({
|
res.json({
|
||||||
"avatar": "https://avatars.akamai.steamstatic.com/4322d8d20cb6dbdd1d891b4efa9952a9679c9a76_full.jpg",
|
avatar:
|
||||||
"country": "GB",
|
"https://avatars.akamai.steamstatic.com/4322d8d20cb6dbdd1d891b4efa9952a9679c9a76_full.jpg",
|
||||||
"pp": 0,
|
country: "GB",
|
||||||
"rank": 0,
|
pp: 0,
|
||||||
"countryRank": 0,
|
rank: 0,
|
||||||
});
|
countryRank: 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
329
pages/index.js
329
pages/index.js
@ -1,14 +1,24 @@
|
|||||||
import { Button, Card, Container, Grid, Input, Link, Modal, Spacer, Switch, Text, textTransforms } from '@nextui-org/react';
|
import {
|
||||||
import { Component } from 'react';
|
Button,
|
||||||
import NavBar from '../src/components/Navbar';
|
Card,
|
||||||
|
Container,
|
||||||
|
Grid,
|
||||||
|
Input,
|
||||||
|
Link,
|
||||||
|
Modal,
|
||||||
|
Spacer,
|
||||||
|
Switch,
|
||||||
|
Text,
|
||||||
|
} from "@nextui-org/react";
|
||||||
|
import { Component } from "react";
|
||||||
|
import NavBar from "../src/components/Navbar";
|
||||||
|
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
import { toast, ToastContainer } from "react-toastify";
|
||||||
import 'react-toastify/dist/ReactToastify.css';
|
import "react-toastify/dist/ReactToastify.css";
|
||||||
|
|
||||||
import styles from '../styles/main.module.css';
|
import styles from "../styles/main.module.css";
|
||||||
|
|
||||||
export default class Home extends Component {
|
export default class Home extends Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@ -26,7 +36,7 @@ export default class Home extends Component {
|
|||||||
showScoreInfo: false,
|
showScoreInfo: false,
|
||||||
showSongInfo: false,
|
showSongInfo: false,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@ -34,29 +44,35 @@ export default class Home extends Component {
|
|||||||
const params = Object.fromEntries(urlSearchParams.entries());
|
const params = Object.fromEntries(urlSearchParams.entries());
|
||||||
|
|
||||||
if (params.id) {
|
if (params.id) {
|
||||||
document.location.href = "/overlay/"+ window.location.search
|
document.location.href = "/overlay/" + window.location.search;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localStorage.getItem('values') == undefined) {
|
if (localStorage.getItem("values") == undefined) {
|
||||||
localStorage.setItem('values', JSON.stringify({
|
localStorage.setItem(
|
||||||
steamId: this.state.steamId,
|
"values",
|
||||||
values: this.state.values
|
JSON.stringify({
|
||||||
}));
|
steamId: this.state.steamId,
|
||||||
|
values: this.state.values,
|
||||||
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
const json = JSON.parse(localStorage.getItem('values'))
|
const json = JSON.parse(localStorage.getItem("values"));
|
||||||
this.setState({ steamId: json.steamId, values: json.values });
|
this.setState({ steamId: json.steamId, values: json.values });
|
||||||
}
|
}
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPreview() {
|
loadPreview() {
|
||||||
this.setState({ isPreviewVisible: true, previewUrl: this.generateUrl(true) });
|
this.setState({
|
||||||
|
isPreviewVisible: true,
|
||||||
|
previewUrl: this.generateUrl(true),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generateUrl(withTc = false) {
|
generateUrl(withTc = false) {
|
||||||
let values = "";
|
let values = "";
|
||||||
Object.entries(this.state.values).forEach(value => {
|
Object.entries(this.state.values).forEach((value) => {
|
||||||
if (value[1] === undefined) {
|
if (value[1] === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -67,7 +83,13 @@ export default class Home extends Component {
|
|||||||
values += `&${value[0]}=${value[1]}`;
|
values += `&${value[0]}=${value[1]}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
return window.location.origin + "/overlay?id=" + this.state.steamId + values + (withTc ? "&textColor=black" : "");
|
return (
|
||||||
|
window.location.origin +
|
||||||
|
"/overlay?id=" +
|
||||||
|
this.state.steamId +
|
||||||
|
values +
|
||||||
|
(withTc ? "&textColor=black" : "")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateValue(key, value) {
|
updateValue(key, value) {
|
||||||
@ -79,122 +101,185 @@ export default class Home extends Component {
|
|||||||
|
|
||||||
updateStorage() {
|
updateStorage() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
localStorage.setItem('values', JSON.stringify({
|
localStorage.setItem(
|
||||||
steamId: this.state.steamId,
|
"values",
|
||||||
values: this.state.values
|
JSON.stringify({
|
||||||
}));
|
steamId: this.state.steamId,
|
||||||
|
values: this.state.values,
|
||||||
|
})
|
||||||
|
);
|
||||||
}, 5);
|
}, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return this.state.loading ? <h1>Loading...</h1> :
|
return this.state.loading ? (
|
||||||
<div className={styles.main}>
|
<h1>Loading...</h1>
|
||||||
<NavBar></NavBar>
|
) : (
|
||||||
|
<div className={styles.main}>
|
||||||
|
<NavBar></NavBar>
|
||||||
|
|
||||||
<Container css={{
|
<Container
|
||||||
marginTop: '$8'
|
css={{
|
||||||
}}>
|
marginTop: "$8",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Preview */}
|
||||||
|
{this.state.isPreviewVisible ? (
|
||||||
|
<Modal
|
||||||
|
closeButton
|
||||||
|
open={this.state.isPreviewVisible}
|
||||||
|
width={"100%"}
|
||||||
|
blur
|
||||||
|
onClose={() => this.setState({ isPreviewVisible: false })}
|
||||||
|
>
|
||||||
|
<Modal.Header>
|
||||||
|
<Text size={18}>Overlay Preview</Text>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<iframe height={600} src={this.state.previewUrl}></iframe>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Preview */}
|
<Grid.Container gap={2} justify="center">
|
||||||
{
|
<Grid
|
||||||
this.state.isPreviewVisible ? <Modal
|
xs={12}
|
||||||
closeButton
|
css={{
|
||||||
open={this.state.isPreviewVisible}
|
color: "black",
|
||||||
width={"100%"}
|
}}
|
||||||
blur
|
justify="center"
|
||||||
onClose={() => this.setState({ isPreviewVisible: false })}
|
>
|
||||||
>
|
<div
|
||||||
<Modal.Header>
|
style={{
|
||||||
<Text size={18}>
|
textAlign: "center",
|
||||||
Overlay Preview
|
}}
|
||||||
</Text>
|
>
|
||||||
</Modal.Header>
|
<Text h1>BeatSaber Overlay</Text>
|
||||||
<Modal.Body>
|
<Text h4>Welcome to the Setup panel</Text>
|
||||||
<iframe height={600} src={this.state.previewUrl}></iframe>
|
</div>
|
||||||
</Modal.Body>
|
</Grid>
|
||||||
</Modal> : <></>
|
|
||||||
}
|
|
||||||
|
|
||||||
<Grid.Container gap={2} justify='center'>
|
<Grid xs={12}>
|
||||||
<Grid xs={12} css={{
|
<Card>
|
||||||
color: 'black'
|
<Card.Body>
|
||||||
}} justify='center'>
|
<Spacer y={1} />
|
||||||
<div style={{
|
|
||||||
textAlign: 'center',
|
|
||||||
}}>
|
|
||||||
<Text h1>BeatSaber Overlay</Text>
|
|
||||||
<Text h4>Welcome to the Setup panel</Text>
|
|
||||||
</div>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid xs={12}>
|
<Input
|
||||||
<Card>
|
underlined
|
||||||
<Card.Body>
|
labelPlaceholder="Ip Address (Only set if you stream on multiple devices)"
|
||||||
<Spacer y={1} />
|
initialValue="localhost"
|
||||||
|
value={this.state.values.socketAddr}
|
||||||
<Input
|
onChange={(event) =>
|
||||||
underlined
|
this.updateValue("socketAddr", event.target.value)
|
||||||
labelPlaceholder="Ip Address (Only set if you stream on multiple devices)"
|
|
||||||
initialValue="localhost"
|
|
||||||
value={this.state.values.socketAddr}
|
|
||||||
onChange={event => this.updateValue("socketAddr", event.target.value)} checked={true}
|
|
||||||
/>
|
|
||||||
<Spacer y={2} />
|
|
||||||
<Input
|
|
||||||
underlined
|
|
||||||
labelPlaceholder="Steam Id"
|
|
||||||
initialValue=""
|
|
||||||
value={this.state.steamId}
|
|
||||||
onChange={event => {
|
|
||||||
this.setState({ steamId: event.target.value });
|
|
||||||
this.updateStorage();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Spacer y={1} />
|
|
||||||
<Text>Do you want to use BeatLeader rather than ScoreSaber?</Text>
|
|
||||||
<Switch onChange={event => this.updateValue("useBeatLeader", event.target.checked)} checked={this.state.values.useBeatLeader} size="md" />
|
|
||||||
<Text>Do you want to show Player Stats (Current PP, global pos, etc)</Text>
|
|
||||||
<Switch onChange={event => this.updateValue("showPlayerStats", event.target.checked)} checked={this.state.values.showPlayerStats} size="md" />
|
|
||||||
<Text>Do you want to show Score Info (Current swing values, total score, etc)</Text>
|
|
||||||
<Switch onChange={event => this.updateValue("showScoreInfo", event.target.checked)} checked={this.state.values.showScoreInfo} size="md" />
|
|
||||||
<Text>Do you want to show Song Info (Song name, bsr, song art, etc)</Text>
|
|
||||||
<Switch onChange={event => this.updateValue("showSongInfo", event.target.checked)} checked={this.state.values.showSongInfo} size="md" />
|
|
||||||
<Spacer y={1} />
|
|
||||||
|
|
||||||
<Button.Group>
|
|
||||||
<Button flat auto onClick={() => {
|
|
||||||
if (!this.state.steamId) {
|
|
||||||
toast.error("Please provide a Steam ID");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
window.open(this.generateUrl(), "_blank");
|
checked={true}
|
||||||
}}>Open Overlay</Button>
|
/>
|
||||||
<Button flat auto onClick={() => {
|
<Spacer y={2} />
|
||||||
if (!this.state.steamId) {
|
<Input
|
||||||
toast.error("Please provide a Steam ID");
|
underlined
|
||||||
return;
|
labelPlaceholder="Steam Id"
|
||||||
|
initialValue=""
|
||||||
|
value={this.state.steamId}
|
||||||
|
onChange={(event) => {
|
||||||
|
this.setState({ steamId: event.target.value });
|
||||||
|
this.updateStorage();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Spacer y={1} />
|
||||||
|
<Text>
|
||||||
|
Do you want to use BeatLeader rather than ScoreSaber?
|
||||||
|
</Text>
|
||||||
|
<Switch
|
||||||
|
onChange={(event) =>
|
||||||
|
this.updateValue("useBeatLeader", event.target.checked)
|
||||||
}
|
}
|
||||||
this.loadPreview();
|
checked={this.state.values.useBeatLeader}
|
||||||
}}>Preview</Button>
|
size="md"
|
||||||
</Button.Group>
|
/>
|
||||||
|
<Text>
|
||||||
|
Do you want to show Player Stats (Current PP, global pos,
|
||||||
|
etc)
|
||||||
|
</Text>
|
||||||
|
<Switch
|
||||||
|
onChange={(event) =>
|
||||||
|
this.updateValue("showPlayerStats", event.target.checked)
|
||||||
|
}
|
||||||
|
checked={this.state.values.showPlayerStats}
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
<Text>
|
||||||
|
Do you want to show Score Info (Current swing values, total
|
||||||
|
score, etc)
|
||||||
|
</Text>
|
||||||
|
<Switch
|
||||||
|
onChange={(event) =>
|
||||||
|
this.updateValue("showScoreInfo", event.target.checked)
|
||||||
|
}
|
||||||
|
checked={this.state.values.showScoreInfo}
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
<Text>
|
||||||
|
Do you want to show Song Info (Song name, bsr, song art,
|
||||||
|
etc)
|
||||||
|
</Text>
|
||||||
|
<Switch
|
||||||
|
onChange={(event) =>
|
||||||
|
this.updateValue("showSongInfo", event.target.checked)
|
||||||
|
}
|
||||||
|
checked={this.state.values.showSongInfo}
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
<Spacer y={1} />
|
||||||
|
|
||||||
{
|
<Button.Group>
|
||||||
this.state.overlayUrl !== undefined ?
|
<Button
|
||||||
|
flat
|
||||||
|
auto
|
||||||
|
onClick={() => {
|
||||||
|
if (!this.state.steamId) {
|
||||||
|
toast.error("Please provide a Steam ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.open(this.generateUrl(), "_blank");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Open Overlay
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
flat
|
||||||
|
auto
|
||||||
|
onClick={() => {
|
||||||
|
if (!this.state.steamId) {
|
||||||
|
toast.error("Please provide a Steam ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loadPreview();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Preview
|
||||||
|
</Button>
|
||||||
|
</Button.Group>
|
||||||
|
|
||||||
|
{this.state.overlayUrl !== undefined ? (
|
||||||
<>
|
<>
|
||||||
<Text b>
|
<Text b>Url</Text>
|
||||||
Url
|
<Link href={this.state.overlayUrl}>
|
||||||
</Text>
|
{this.state.overlayUrl}
|
||||||
<Link href={this.state.overlayUrl}>{this.state.overlayUrl}</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
: <></>
|
) : (
|
||||||
}
|
<></>
|
||||||
</Card.Body>
|
)}
|
||||||
</Card>
|
</Card.Body>
|
||||||
</Grid>
|
</Card>
|
||||||
</Grid.Container>
|
</Grid>
|
||||||
</Container>
|
</Grid.Container>
|
||||||
|
</Container>
|
||||||
|
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
289
pages/overlay.js
289
pages/overlay.js
@ -1,14 +1,13 @@
|
|||||||
import { Link } from '@nextui-org/react';
|
import { Link } from "@nextui-org/react";
|
||||||
import {Component} from 'react'
|
import { Component } from "react";
|
||||||
import PlayerStats from '../src/components/PlayerStats';
|
import PlayerStats from "../src/components/PlayerStats";
|
||||||
import ScoreStats from '../src/components/ScoreStats';
|
import ScoreStats from "../src/components/ScoreStats";
|
||||||
import SongInfo from "../src/components/SongInfo";
|
import SongInfo from "../src/components/SongInfo";
|
||||||
|
|
||||||
import Utils from '../src/utils/utils';
|
import Utils from "../src/utils/utils";
|
||||||
import styles from '../styles/overlay.module.css';
|
import styles from "../styles/overlay.module.css";
|
||||||
|
|
||||||
export default class Overlay extends Component {
|
export default class Overlay extends Component {
|
||||||
|
|
||||||
#_beatSaverURL = "";
|
#_beatSaverURL = "";
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -36,16 +35,16 @@ export default class Overlay extends Component {
|
|||||||
percentage: "100.00%",
|
percentage: "100.00%",
|
||||||
failed: false,
|
failed: false,
|
||||||
leftHand: {
|
leftHand: {
|
||||||
averageCut: [15.00],
|
averageCut: [15.0],
|
||||||
averagePreSwing: [70.00],
|
averagePreSwing: [70.0],
|
||||||
averagePostSwing: [30.00],
|
averagePostSwing: [30.0],
|
||||||
},
|
},
|
||||||
rightHand: {
|
rightHand: {
|
||||||
averageCut: [15.00],
|
averageCut: [15.0],
|
||||||
averagePreSwing: [70.00],
|
averagePreSwing: [70.0],
|
||||||
averagePostSwing: [30.00],
|
averagePostSwing: [30.0],
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
this.setupTimer();
|
this.setupTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,25 +59,25 @@ export default class Overlay extends Component {
|
|||||||
setupTimer() {
|
setupTimer() {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (this.isCurrentSongTimeProvided) {
|
if (this.isCurrentSongTimeProvided) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if (!this.state.paused && this.state.beatSaverData !== undefined) {
|
if (!this.state.paused && this.state.beatSaverData !== undefined) {
|
||||||
this.setState({ currentSongTime: this.state.currentSongTime + 1 })
|
this.setState({ currentSongTime: this.state.currentSongTime + 1 });
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current song time
|
* Update the current song time
|
||||||
*
|
*
|
||||||
* @param {[]} data The song data
|
* @param {[]} data The song data
|
||||||
*/
|
*/
|
||||||
handleCurrentSongTime(data) {
|
handleCurrentSongTime(data) {
|
||||||
try {
|
try {
|
||||||
const time = data.status.performance.currentSongTime
|
const time = data.status.performance.currentSongTime;
|
||||||
if (time !== undefined && time != null) {
|
if (time !== undefined && time != null) {
|
||||||
this.isCurrentSongTimeProvided = true
|
this.isCurrentSongTimeProvided = true;
|
||||||
this.setState({ currentSongTime: time })
|
this.setState({ currentSongTime: time });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// do nothing
|
// do nothing
|
||||||
@ -86,24 +85,26 @@ export default class Overlay extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
console.log("Initializing...");
|
console.log("Initializing...");
|
||||||
this.#_beatSaverURL = document.location.origin + "/api/beatsaver/map?hash=%s";
|
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());
|
||||||
|
|
||||||
// Check what website the player wants to use
|
// Check what website the player wants to use
|
||||||
if (params.beatleader === 'true') {
|
if (params.beatleader === "true") {
|
||||||
this.setState({ websiteType: "BeatLeader" });
|
this.setState({ websiteType: "BeatLeader" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = params.id;
|
const id = params.id;
|
||||||
if (!id) { // Check if the id param is valid
|
if (!id) {
|
||||||
|
// Check if the id param is valid
|
||||||
this.setState({ loading: false, isValidSteamId: false });
|
this.setState({ loading: false, isValidSteamId: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the player wants to disable their stats (pp, global pos, etc)
|
// Check if the player wants to disable their stats (pp, global pos, etc)
|
||||||
if (params.showPlayerStats === 'false' || params.playerstats === 'false') {
|
if (params.showPlayerStats === "false" || params.playerstats === "false") {
|
||||||
this.setState({ showPlayerStats: false });
|
this.setState({ showPlayerStats: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,18 +115,18 @@ export default class Overlay extends Component {
|
|||||||
let shouldConnectSocket = false;
|
let shouldConnectSocket = false;
|
||||||
|
|
||||||
// Check if the player wants to show their current score information
|
// Check if the player wants to show their current score information
|
||||||
if (params.showScoreInfo === 'true' || params.scoreinfo === 'true') {
|
if (params.showScoreInfo === "true" || params.scoreinfo === "true") {
|
||||||
this.setState({ showScore: true });
|
this.setState({ showScore: true });
|
||||||
shouldConnectSocket = true;
|
shouldConnectSocket = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the player wants to show the current song
|
// Check if the player wants to show the current song
|
||||||
if (params.showSongInfo === 'true' || params.songinfo === 'true') {
|
if (params.showSongInfo === "true" || params.songinfo === "true") {
|
||||||
this.setState({ showSongInfo: true });
|
this.setState({ showSongInfo: true });
|
||||||
shouldConnectSocket = true;
|
shouldConnectSocket = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mainly used for the preview
|
// Mainly used for the preview
|
||||||
if (params.textColor) {
|
if (params.textColor) {
|
||||||
this.setState({ textColor: params.textColor });
|
this.setState({ textColor: params.textColor });
|
||||||
}
|
}
|
||||||
@ -138,16 +139,22 @@ export default class Overlay extends Component {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch and update the data from the respective platform
|
* Fetch and update the data from the respective platform
|
||||||
*
|
*
|
||||||
* @param {string} id The steam id of the player
|
* @param {string} id The steam id of the player
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
async updateData(id) {
|
async updateData(id) {
|
||||||
const data = await fetch(new Utils().getWebsiteApi(id == "test" ? "Test" : this.state.websiteType).ApiUrl.replace("%s", id), {
|
const data = await fetch(
|
||||||
mode: 'cors'
|
new Utils()
|
||||||
});
|
.getWebsiteApi(id == "test" ? "Test" : this.state.websiteType)
|
||||||
|
.ApiUrl.replace("%s", id),
|
||||||
|
{
|
||||||
|
mode: "cors",
|
||||||
|
}
|
||||||
|
);
|
||||||
const json = await data.json();
|
const json = await data.json();
|
||||||
if (json.errorMessage) { // Invalid account
|
if (json.errorMessage) {
|
||||||
|
// Invalid account
|
||||||
this.setState({ loading: false, isValidSteamId: false });
|
this.setState({ loading: false, isValidSteamId: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -158,7 +165,10 @@ export default class Overlay extends Component {
|
|||||||
* Setup the HTTP Status connection
|
* Setup the HTTP Status connection
|
||||||
*/
|
*/
|
||||||
connectSocket(socketAddress) {
|
connectSocket(socketAddress) {
|
||||||
socketAddress = (socketAddress === undefined ? 'ws://localhost' : `ws://${socketAddress}`) + ":6557/socket";
|
socketAddress =
|
||||||
|
(socketAddress === undefined
|
||||||
|
? "ws://localhost"
|
||||||
|
: `ws://${socketAddress}`) + ":6557/socket";
|
||||||
if (this.state.isConnectedToSocket) return;
|
if (this.state.isConnectedToSocket) return;
|
||||||
|
|
||||||
if (this.state.isVisible) {
|
if (this.state.isVisible) {
|
||||||
@ -167,42 +177,46 @@ export default class Overlay extends Component {
|
|||||||
|
|
||||||
console.log(`Connecting to ${socketAddress}`);
|
console.log(`Connecting to ${socketAddress}`);
|
||||||
const socket = new WebSocket(socketAddress);
|
const socket = new WebSocket(socketAddress);
|
||||||
socket.addEventListener('open', () => {
|
socket.addEventListener("open", () => {
|
||||||
console.log(`Connected to ${socketAddress}`);
|
console.log(`Connected to ${socketAddress}`);
|
||||||
this.setState({ isConnectedToSocket: true });
|
this.setState({ isConnectedToSocket: true });
|
||||||
});
|
});
|
||||||
socket.addEventListener('close', () => {
|
socket.addEventListener("close", () => {
|
||||||
console.log("Attempting to re-connect to the HTTP Status socket in 10 seconds.");
|
console.log(
|
||||||
|
"Attempting to re-connect to the HTTP Status socket in 10 seconds."
|
||||||
|
);
|
||||||
this.setState({ isConnectedToSocket: false });
|
this.setState({ isConnectedToSocket: false });
|
||||||
setTimeout(() => this.connectSocket(), 10_000);
|
setTimeout(() => this.connectSocket(), 10_000);
|
||||||
});
|
});
|
||||||
socket.addEventListener('message', (message) => {
|
socket.addEventListener("message", (message) => {
|
||||||
const json = JSON.parse(message.data);
|
const json = JSON.parse(message.data);
|
||||||
this.handleCurrentSongTime(json)
|
this.handleCurrentSongTime(json);
|
||||||
if (!this.handlers[json.event]) {
|
if (!this.handlers[json.event]) {
|
||||||
console.log("Unhandled message from HTTP Status. (" + json.event + ")");
|
console.log("Unhandled message from HTTP Status. (" + json.event + ")");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.handlers[json.event](json || []);
|
this.handlers[json.event](json || []);
|
||||||
})
|
});
|
||||||
this.setState({ socket: socket });
|
this.setState({ socket: socket });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current songs beat saver url in {@link #_beatSaverURL}
|
* Set the current songs beat saver url in {@link #_beatSaverURL}
|
||||||
*
|
*
|
||||||
* @param {[]} songData
|
* @param {[]} songData
|
||||||
*/
|
*/
|
||||||
async setBeatSaver(songData) {
|
async setBeatSaver(songData) {
|
||||||
console.log("Updating BeatSaver info")
|
console.log("Updating BeatSaver info");
|
||||||
const data = await fetch(this.#_beatSaverURL.replace("%s", songData.levelId));
|
const data = await fetch(
|
||||||
|
this.#_beatSaverURL.replace("%s", songData.levelId)
|
||||||
|
);
|
||||||
const json = await data.json();
|
const json = await data.json();
|
||||||
this.setState({ beatSaverData: json })
|
this.setState({ beatSaverData: json });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup the data and get ready for the next song
|
* Cleanup the data and get ready for the next song
|
||||||
*
|
*
|
||||||
* @param {boolean} visible Whether to show info other than the player stats
|
* @param {boolean} visible Whether to show info other than the player stats
|
||||||
*/
|
*/
|
||||||
async resetData(visible) {
|
async resetData(visible) {
|
||||||
@ -211,59 +225,68 @@ export default class Overlay extends Component {
|
|||||||
}, 250);
|
}, 250);
|
||||||
this.setState({
|
this.setState({
|
||||||
leftHand: {
|
leftHand: {
|
||||||
averageCut: [15.00],
|
averageCut: [15.0],
|
||||||
averagePreSwing: [70.00],
|
averagePreSwing: [70.0],
|
||||||
averagePostSwing: [30.00],
|
averagePostSwing: [30.0],
|
||||||
},
|
},
|
||||||
rightHand: {
|
rightHand: {
|
||||||
averageCut: [15.00],
|
averageCut: [15.0],
|
||||||
averagePreSwing: [70.00],
|
averagePreSwing: [70.0],
|
||||||
averagePostSwing: [30.00],
|
averagePostSwing: [30.0],
|
||||||
},
|
},
|
||||||
songInfo: undefined,
|
songInfo: undefined,
|
||||||
beatSaverData: undefined,
|
beatSaverData: undefined,
|
||||||
currentSongTime: 0,
|
currentSongTime: 0,
|
||||||
currentScore: 0,
|
currentScore: 0,
|
||||||
percentage: "100.00%",
|
percentage: "100.00%",
|
||||||
isVisible: visible
|
isVisible: visible,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// The HTTP Status handlers
|
// The HTTP Status handlers
|
||||||
handlers = {
|
handlers = {
|
||||||
"hello": (data) => {
|
hello: (data) => {
|
||||||
console.log("Hello from HTTP Status!");
|
console.log("Hello from HTTP Status!");
|
||||||
if (data.status) {
|
if (data.status) {
|
||||||
this.setState({songData: data});
|
this.setState({ songData: data });
|
||||||
if (data.status.beatmap) {
|
if (data.status.beatmap) {
|
||||||
this.setBeatSaver(data.status.beatmap);
|
this.setBeatSaver(data.status.beatmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scoreChanged": (data) => {
|
scoreChanged: (data) => {
|
||||||
const { status } = data;
|
const { status } = data;
|
||||||
const { score, currentMaxScore } = status.performance;
|
const { score, currentMaxScore } = status.performance;
|
||||||
const percent = currentMaxScore > 0 ? ((score / currentMaxScore) * 1000 / 10).toFixed(2) : 0.00;
|
const percent =
|
||||||
|
currentMaxScore > 0
|
||||||
|
? (((score / currentMaxScore) * 1000) / 10).toFixed(2)
|
||||||
|
: 0.0;
|
||||||
this.setState({
|
this.setState({
|
||||||
currentScore: score,
|
currentScore: score,
|
||||||
percentage: this.state.failed ? percent * 2 : percent + "%"
|
percentage: this.state.failed ? percent * 2 : percent + "%",
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
"noteFullyCut": (data) => {
|
noteFullyCut: (data) => {
|
||||||
const { noteCut } = data;
|
const { noteCut } = data;
|
||||||
|
|
||||||
console.log(noteCut)
|
console.log(noteCut);
|
||||||
|
|
||||||
// Left Saber
|
// Left Saber
|
||||||
if (noteCut.saberType === 'SaberA') {
|
if (noteCut.saberType === "SaberA") {
|
||||||
const data = this.state.leftHand;
|
const data = this.state.leftHand;
|
||||||
if (data.averageCut.includes(15) && data.averageCut.length === 1) {
|
if (data.averageCut.includes(15) && data.averageCut.length === 1) {
|
||||||
data.averageCut = [];
|
data.averageCut = [];
|
||||||
}
|
}
|
||||||
if (data.averagePreSwing.includes(70) && data.averagePreSwing.length === 1) {
|
if (
|
||||||
|
data.averagePreSwing.includes(70) &&
|
||||||
|
data.averagePreSwing.length === 1
|
||||||
|
) {
|
||||||
data.averagePreSwing = [];
|
data.averagePreSwing = [];
|
||||||
}
|
}
|
||||||
if (data.averagePostSwing.includes(30) && data.averagePostSwing.length === 1) {
|
if (
|
||||||
|
data.averagePostSwing.includes(30) &&
|
||||||
|
data.averagePostSwing.length === 1
|
||||||
|
) {
|
||||||
data.averagePostSwing = [];
|
data.averagePostSwing = [];
|
||||||
}
|
}
|
||||||
data.averagePreSwing.push(noteCut.beforeSwingRating * 70);
|
data.averagePreSwing.push(noteCut.beforeSwingRating * 70);
|
||||||
@ -273,15 +296,21 @@ export default class Overlay extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Left Saber
|
// Left Saber
|
||||||
if (noteCut.saberType === 'SaberB') {
|
if (noteCut.saberType === "SaberB") {
|
||||||
const data = this.state.rightHand;
|
const data = this.state.rightHand;
|
||||||
if (data.averageCut.includes(15) && data.averageCut.length === 1) {
|
if (data.averageCut.includes(15) && data.averageCut.length === 1) {
|
||||||
data.averageCut = [];
|
data.averageCut = [];
|
||||||
}
|
}
|
||||||
if (data.averagePreSwing.includes(70) && data.averagePreSwing.length === 1) {
|
if (
|
||||||
|
data.averagePreSwing.includes(70) &&
|
||||||
|
data.averagePreSwing.length === 1
|
||||||
|
) {
|
||||||
data.averagePreSwing = [];
|
data.averagePreSwing = [];
|
||||||
}
|
}
|
||||||
if (data.averagePostSwing.includes(30) && data.averagePostSwing.length === 1) {
|
if (
|
||||||
|
data.averagePostSwing.includes(30) &&
|
||||||
|
data.averagePostSwing.length === 1
|
||||||
|
) {
|
||||||
data.averagePostSwing = [];
|
data.averagePostSwing = [];
|
||||||
}
|
}
|
||||||
data.averagePreSwing.push(noteCut.beforeSwingRating * 70);
|
data.averagePreSwing.push(noteCut.beforeSwingRating * 70);
|
||||||
@ -290,77 +319,85 @@ export default class Overlay extends Component {
|
|||||||
this.setState({ rightHand: data });
|
this.setState({ rightHand: data });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"songStart": (data) => {
|
songStart: (data) => {
|
||||||
console.log("Going into level, resetting data.")
|
console.log("Going into level, resetting data.");
|
||||||
this.resetData(true);
|
this.resetData(true);
|
||||||
this.setState({ songData: data, paused: false })
|
this.setState({ songData: data, paused: false });
|
||||||
this.setBeatSaver(data.status.beatmap);
|
this.setBeatSaver(data.status.beatmap);
|
||||||
},
|
},
|
||||||
"finished": () => {
|
finished: () => {
|
||||||
this.resetData(false);
|
this.resetData(false);
|
||||||
},
|
},
|
||||||
"softFail": () => {
|
softFail: () => {
|
||||||
this.setState({ failed: true });
|
this.setState({ failed: true });
|
||||||
},
|
},
|
||||||
"pause": () => {
|
pause: () => {
|
||||||
this.setState({ paused: true });
|
this.setState({ paused: true });
|
||||||
},
|
},
|
||||||
"resume": () => {
|
resume: () => {
|
||||||
this.setState({ paused: false });
|
this.setState({ paused: false });
|
||||||
},
|
},
|
||||||
"menu": () => {
|
menu: () => {
|
||||||
this.resetData(false);
|
this.resetData(false);
|
||||||
},
|
},
|
||||||
"noteCut": () => {},
|
noteCut: () => {},
|
||||||
"noteMissed": () => {},
|
noteMissed: () => {},
|
||||||
"noteSpawned": () => {},
|
noteSpawned: () => {},
|
||||||
"bombMissed": () => {},
|
bombMissed: () => {},
|
||||||
"beatmapEvent": () => {},
|
beatmapEvent: () => {},
|
||||||
"energyChanged": () => {},
|
energyChanged: () => {},
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loading, isValidSteamId, data, websiteType } = this.state;
|
const { loading, isValidSteamId, data, websiteType } = this.state;
|
||||||
|
|
||||||
if (this.state.textColor !== undefined) {
|
if (this.state.textColor !== undefined) {
|
||||||
const element = document.querySelector("." + styles.main);
|
const element = document.querySelector("." + styles.main);
|
||||||
element.style.color = this.state.textColor
|
element.style.color = this.state.textColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={styles.main}>
|
return (
|
||||||
{ loading ?
|
<div className={styles.main}>
|
||||||
<div className={styles.loading}>
|
{loading ? (
|
||||||
<h2>Loading...</h2>
|
<div className={styles.loading}>
|
||||||
|
<h2>Loading...</h2>
|
||||||
|
</div>
|
||||||
|
) : !isValidSteamId ? (
|
||||||
|
<div className={styles.invalidPlayer}>
|
||||||
|
<h1>Invalid player, please visit the main page.</h1>
|
||||||
|
<Link href="/">
|
||||||
|
<a>Go Home</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={styles.overlay}>
|
||||||
|
{this.state.showPlayerStats ? (
|
||||||
|
<PlayerStats
|
||||||
|
pp={data.pp.toLocaleString()}
|
||||||
|
globalPos={data.rank.toLocaleString()}
|
||||||
|
country={data.country}
|
||||||
|
countryRank={data.countryRank.toLocaleString()}
|
||||||
|
websiteType={websiteType}
|
||||||
|
avatar={data.profilePicture || data.avatar}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
{this.state.showScore && this.state.isVisible ? (
|
||||||
|
<ScoreStats data={this.state} />
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
{this.state.showSongInfo &&
|
||||||
|
this.state.beatSaverData !== undefined &&
|
||||||
|
this.state.isVisible ? (
|
||||||
|
<SongInfo data={this.state} />
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
: !isValidSteamId ?
|
);
|
||||||
<div className={styles.invalidPlayer}>
|
|
||||||
<h1>Invalid player, please visit the main page.</h1>
|
|
||||||
<Link href="/">
|
|
||||||
<a>Go Home</a>
|
|
||||||
</Link>
|
|
||||||
</div> :
|
|
||||||
<div className={styles.overlay}>
|
|
||||||
{
|
|
||||||
this.state.showPlayerStats ? <PlayerStats
|
|
||||||
pp={data.pp.toLocaleString()}
|
|
||||||
globalPos={data.rank.toLocaleString()}
|
|
||||||
country={data.country}
|
|
||||||
countryRank={data.countryRank.toLocaleString()}
|
|
||||||
websiteType={websiteType}
|
|
||||||
avatar={data.profilePicture || data.avatar}
|
|
||||||
/>
|
|
||||||
: <></>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
this.state.showScore && this.state.isVisible ?
|
|
||||||
<ScoreStats data={this.state} /> : <></>
|
|
||||||
}
|
|
||||||
{
|
|
||||||
this.state.showSongInfo && this.state.beatSaverData !== undefined && this.state.isVisible ?
|
|
||||||
<SongInfo data={this.state}/> : <></>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
import styles from '../../styles/avatar.module.css';
|
import styles from "../../styles/avatar.module.css";
|
||||||
|
|
||||||
const Avatar = (props) => {
|
const Avatar = (props) => {
|
||||||
return <>
|
return (
|
||||||
<Image
|
<>
|
||||||
className={styles.playerAvatar}
|
<Image
|
||||||
src={props.url}
|
className={styles.playerAvatar}
|
||||||
width={180}
|
src={props.url}
|
||||||
height={180}
|
width={180}
|
||||||
alt={'Avatar image'}
|
height={180}
|
||||||
loading='lazy'
|
alt={"Avatar image"}
|
||||||
placeholder="blur"
|
loading="lazy"
|
||||||
blurDataURL="https://cdn.fascinated.cc/MhCUeHZLsh.webp?raw=true"
|
placeholder="blur"
|
||||||
unoptimized={true}
|
blurDataURL="https://cdn.fascinated.cc/MhCUeHZLsh.webp?raw=true"
|
||||||
/>
|
unoptimized={true}
|
||||||
</>
|
/>
|
||||||
}
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Avatar;
|
export default Avatar;
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { Navbar, Text } from "@nextui-org/react";
|
import { Navbar, Text } from "@nextui-org/react";
|
||||||
|
|
||||||
const NavBar = () => {
|
const NavBar = () => {
|
||||||
return <Navbar isBordered variant={"sticky"}>
|
return (
|
||||||
<Navbar.Brand>
|
<Navbar isBordered variant={"sticky"}>
|
||||||
<Text b color="inherit">
|
<Navbar.Brand>
|
||||||
BeatSaber Overlay
|
<Text b color="inherit">
|
||||||
</Text>
|
BeatSaber Overlay
|
||||||
</Navbar.Brand>
|
</Text>
|
||||||
</Navbar>
|
</Navbar.Brand>
|
||||||
}
|
</Navbar>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default NavBar;
|
export default NavBar;
|
||||||
|
@ -1,24 +1,37 @@
|
|||||||
import ReactCountryFlag from "react-country-flag";
|
import ReactCountryFlag from "react-country-flag";
|
||||||
|
|
||||||
import styles from '../../styles/playerStats.module.css';
|
import styles from "../../styles/playerStats.module.css";
|
||||||
import Avatar from "./Avatar";
|
import Avatar from "./Avatar";
|
||||||
|
|
||||||
const PlayerStats = (props) => {
|
const PlayerStats = (props) => {
|
||||||
return <div className={styles.playerStatsContainer}>
|
return (
|
||||||
<div>
|
<div className={styles.playerStatsContainer}>
|
||||||
<Avatar url={props.avatar} />
|
<div>
|
||||||
</div>
|
<Avatar url={props.avatar} />
|
||||||
<div className={styles.playerStats}>
|
</div>
|
||||||
<p>{props.pp}pp <span style={{
|
<div className={styles.playerStats}>
|
||||||
fontSize: '20px',
|
<p>
|
||||||
}}>({props.websiteType})</span></p>
|
{props.pp}pp{" "}
|
||||||
<p>#{props.globalPos}</p>
|
<span
|
||||||
<div className={styles.playerCountry}>
|
style={{
|
||||||
<p>#{props.countryRank}</p>
|
fontSize: "20px",
|
||||||
<ReactCountryFlag className={styles.playerCountryIcon} svg countryCode={props.country} />
|
}}
|
||||||
</div>
|
>
|
||||||
</div>
|
({props.websiteType})
|
||||||
</div>
|
</span>
|
||||||
}
|
</p>
|
||||||
|
<p>#{props.globalPos}</p>
|
||||||
|
<div className={styles.playerCountry}>
|
||||||
|
<p>#{props.countryRank}</p>
|
||||||
|
<ReactCountryFlag
|
||||||
|
className={styles.playerCountryIcon}
|
||||||
|
svg
|
||||||
|
countryCode={props.country}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default PlayerStats;
|
export default PlayerStats;
|
||||||
|
@ -1,44 +1,45 @@
|
|||||||
import {Component} from "react";
|
import { Component } from "react";
|
||||||
|
|
||||||
import styles from '../../styles/scoreStats.module.css';
|
import styles from "../../styles/scoreStats.module.css";
|
||||||
|
|
||||||
export default class ScoreStats extends Component {
|
export default class ScoreStats extends Component {
|
||||||
|
constructor(params) {
|
||||||
|
super(params);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(params) {
|
/**
|
||||||
super(params);
|
* Returns the average of the provided numbers list
|
||||||
}
|
*
|
||||||
|
* @param {List<Number>} hitValues
|
||||||
|
* @returns The average value
|
||||||
|
*/
|
||||||
|
getAverage(hitValues) {
|
||||||
|
return hitValues.reduce((p, c) => p + c, 0) / hitValues.length;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
render() {
|
||||||
* Returns the average of the provided numbers list
|
const data = this.props.data;
|
||||||
*
|
|
||||||
* @param {List<Number>} hitValues
|
|
||||||
* @returns The average value
|
|
||||||
*/
|
|
||||||
getAverage(hitValues) {
|
|
||||||
return hitValues.reduce((p, c) => p + c, 0) / hitValues.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const data = this.props.data;
|
<div className={styles.scoreStats}>
|
||||||
|
<div className={styles.scoreStatsInfo}>
|
||||||
return <div className={styles.scoreStats}>
|
<p>{data.percentage}</p>
|
||||||
<div className={styles.scoreStatsInfo}>
|
<p>{data.currentScore.toLocaleString()}</p>
|
||||||
<p>{data.percentage}</p>
|
</div>
|
||||||
<p>{data.currentScore.toLocaleString()}</p>
|
<p className={styles.scoreStatsAverageCut}>Average Cut</p>
|
||||||
</div>
|
<div className={styles.scoreStatsHands}>
|
||||||
<p className={styles.scoreStatsAverageCut}>Average Cut</p>
|
<div className={styles.scoreStatsLeft}>
|
||||||
<div className={styles.scoreStatsHands}>
|
<p>{this.getAverage(data.leftHand.averagePreSwing).toFixed(2)}</p>
|
||||||
<div className={styles.scoreStatsLeft}>
|
<p>{this.getAverage(data.leftHand.averagePostSwing).toFixed(2)}</p>
|
||||||
<p>{this.getAverage(data.leftHand.averagePreSwing).toFixed(2)}</p>
|
<p>{this.getAverage(data.leftHand.averageCut).toFixed(2)}</p>
|
||||||
<p>{this.getAverage(data.leftHand.averagePostSwing).toFixed(2)}</p>
|
</div>
|
||||||
<p>{this.getAverage(data.leftHand.averageCut).toFixed(2)}</p>
|
<div className={styles.scoreStatsRight}>
|
||||||
</div>
|
<p>{this.getAverage(data.rightHand.averagePreSwing).toFixed(2)}</p>
|
||||||
<div className={styles.scoreStatsRight}>
|
<p>{this.getAverage(data.rightHand.averagePostSwing).toFixed(2)}</p>
|
||||||
<p>{this.getAverage(data.rightHand.averagePreSwing).toFixed(2)}</p>
|
<p>{this.getAverage(data.rightHand.averageCut).toFixed(2)}</p>
|
||||||
<p>{this.getAverage(data.rightHand.averagePostSwing).toFixed(2)}</p>
|
</div>
|
||||||
<p>{this.getAverage(data.rightHand.averageCut).toFixed(2)}</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
</div>
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -1,84 +1,99 @@
|
|||||||
import {Component} from "react";
|
import { Component } from "react";
|
||||||
|
|
||||||
import styles from '../../styles/songInfo.module.css';
|
import styles from "../../styles/songInfo.module.css";
|
||||||
|
|
||||||
export default class SongInfo extends Component {
|
export default class SongInfo extends Component {
|
||||||
|
constructor(params) {
|
||||||
constructor(params) {
|
super(params);
|
||||||
super(params);
|
this.state = {
|
||||||
this.state = {
|
diffColor: undefined,
|
||||||
diffColor: undefined
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const data = this.props.data.songData.status.beatmap;
|
const data = this.props.data.songData.status.beatmap;
|
||||||
this.formatDiff(data.difficulty);
|
this.formatDiff(data.difficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the difficulity color from the given difficulity
|
* Update the difficulity color from the given difficulity
|
||||||
*
|
*
|
||||||
* @param {string} diff
|
* @param {string} diff
|
||||||
*/
|
*/
|
||||||
formatDiff(diff) {
|
formatDiff(diff) {
|
||||||
if (diff === "Expert+") {
|
if (diff === "Expert+") {
|
||||||
this.setState({ diffColor: "#8f48db" });
|
this.setState({ diffColor: "#8f48db" });
|
||||||
}
|
}
|
||||||
if (diff === "Expert") {
|
if (diff === "Expert") {
|
||||||
this.setState({ diffColor: "#bf2a42" });
|
this.setState({ diffColor: "#bf2a42" });
|
||||||
}
|
}
|
||||||
if (diff === "Hard") {
|
if (diff === "Hard") {
|
||||||
this.setState({ diffColor: "tomato" });
|
this.setState({ diffColor: "tomato" });
|
||||||
}
|
}
|
||||||
if (diff === "Normal") {
|
if (diff === "Normal") {
|
||||||
this.setState({ diffColor: "#59b0f4" });
|
this.setState({ diffColor: "#59b0f4" });
|
||||||
}
|
}
|
||||||
if (diff === "Easy") {
|
if (diff === "Easy") {
|
||||||
this.setState({ diffColor: "MediumSeaGreen" });
|
this.setState({ diffColor: "MediumSeaGreen" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format the given ms
|
* Format the given ms
|
||||||
*
|
*
|
||||||
* @param {Number} millis
|
* @param {Number} millis
|
||||||
* @returns The formatted time
|
* @returns The formatted time
|
||||||
*/
|
*/
|
||||||
msToMinSeconds(millis) {
|
msToMinSeconds(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;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const data = this.props.data.songData.status.beatmap;
|
const data = this.props.data.songData.status.beatmap;
|
||||||
const beatSaverData = this.props.data.beatSaverData.data;
|
const beatSaverData = this.props.data.beatSaverData.data;
|
||||||
const songArt = beatSaverData.songArt;
|
const songArt = beatSaverData.songArt;
|
||||||
const bsr = beatSaverData.bsr;
|
const bsr = beatSaverData.bsr;
|
||||||
const {
|
const { songName, songAuthorName, difficulty } = data;
|
||||||
songName,
|
// what in the fuck is this?? LMFAO
|
||||||
songAuthorName,
|
const songTimerPercentage =
|
||||||
difficulty
|
(this.props.data.currentSongTime / 1000 / (data.length / 1000)) * 100000;
|
||||||
} = data
|
|
||||||
// what in the fuck is this?? LMFAO
|
|
||||||
const songTimerPercentage = ((this.props.data.currentSongTime / 1000) / (data.length / 1000)) * 100000;
|
|
||||||
|
|
||||||
return <div className={styles.songInfoContainer}>
|
return (
|
||||||
<img src={songArt}/>
|
<div className={styles.songInfoContainer}>
|
||||||
<div className={styles.songInfo}>
|
<img src={songArt} />
|
||||||
<p className={styles.songInfoSongName}>{songName.length > 35 ? songName.substring(0, 35) + "..." : songName}</p>
|
<div className={styles.songInfo}>
|
||||||
<p className={styles.songInfoSongAuthor}>{songAuthorName}</p>
|
<p className={styles.songInfoSongName}>
|
||||||
<div className={styles.songInfoSongOtherContainer}>
|
{songName.length > 35
|
||||||
<p className={styles.songInfoDiff} style={{ backgroundColor: this.state.diffColor }}>{difficulty}</p>
|
? songName.substring(0, 35) + "..."
|
||||||
<p className={styles.songInfoBsr}>!bsr {bsr}</p>
|
: songName}
|
||||||
</div>
|
</p>
|
||||||
<p className={styles.songTimeText}>{this.msToMinSeconds(this.props.data.currentSongTime * 1000)}/{this.msToMinSeconds(data.length)}</p>
|
<p className={styles.songInfoSongAuthor}>{songAuthorName}</p>
|
||||||
<div className={styles.songTimeContainer}>
|
<div className={styles.songInfoSongOtherContainer}>
|
||||||
<div className={styles.songTimeBackground}/>
|
<p
|
||||||
<div className={styles.songTime} style={{ width: songTimerPercentage + '%' }}/>
|
className={styles.songInfoDiff}
|
||||||
</div>
|
style={{ backgroundColor: this.state.diffColor }}
|
||||||
</div>
|
>
|
||||||
</div>
|
{difficulty}
|
||||||
}
|
</p>
|
||||||
}
|
<p className={styles.songInfoBsr}>!bsr {bsr}</p>
|
||||||
|
</div>
|
||||||
|
<p className={styles.songTimeText}>
|
||||||
|
{this.msToMinSeconds(this.props.data.currentSongTime * 1000)}/
|
||||||
|
{this.msToMinSeconds(data.length)}
|
||||||
|
</p>
|
||||||
|
<div className={styles.songTimeContainer}>
|
||||||
|
<div className={styles.songTimeBackground} />
|
||||||
|
<div
|
||||||
|
className={styles.songTime}
|
||||||
|
style={{ width: songTimerPercentage + "%" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import Config from '../../config.json';
|
import Config from "../../config.json";
|
||||||
|
|
||||||
const WebsiteTypes = {
|
const WebsiteTypes = {
|
||||||
ScoreSaber: {
|
ScoreSaber: {
|
||||||
ApiUrl: Config.proxy_url + "/https://scoresaber.com/api/player/%s/full"
|
ApiUrl: Config.proxy_url + "/https://scoresaber.com/api/player/%s/full",
|
||||||
},
|
},
|
||||||
BeatLeader: {
|
BeatLeader: {
|
||||||
ApiUrl: Config.proxy_url + "/https://api.beatleader.xyz/player/%s"
|
ApiUrl: Config.proxy_url + "/https://api.beatleader.xyz/player/%s",
|
||||||
},
|
},
|
||||||
Test: {
|
Test: {
|
||||||
ApiUrl: "/api/mockdata"
|
ApiUrl: "/api/mockdata",
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export default WebsiteTypes
|
export default WebsiteTypes;
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import WebsiteTypes from "../consts/WebsiteType";
|
import WebsiteTypes from "../consts/WebsiteType";
|
||||||
|
|
||||||
export default class Utils {
|
export default class Utils {
|
||||||
constructor() {};
|
constructor() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the information for the given website type.
|
* Returns the information for the given website type.
|
||||||
*
|
*
|
||||||
* @param {WebsiteTypes} website
|
* @param {WebsiteTypes} website
|
||||||
* @returns The website type's information.
|
* @returns The website type's information.
|
||||||
*/
|
*/
|
||||||
getWebsiteApi(website) {
|
getWebsiteApi(website) {
|
||||||
return WebsiteTypes[website]
|
return WebsiteTypes[website];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
.playerAvatar {
|
.playerAvatar {
|
||||||
border-radius: 5%;
|
border-radius: 5%;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
*, html, body {
|
*,
|
||||||
|
html,
|
||||||
|
body {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.main {
|
.main {
|
||||||
font-family: 'Roboto', sans-serif !important;
|
font-family: "Roboto", sans-serif !important;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: xx-large;
|
font-size: xx-large;
|
||||||
line-height: 1.4em !important;
|
line-height: 1.4em !important;
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
.main {
|
.main {
|
||||||
font-family: 'Teko', sans-serif !important;
|
font-family: "Teko", sans-serif !important;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: xx-large;
|
font-size: xx-large;
|
||||||
line-height: 1.4em !important;
|
line-height: 1.4em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main p {
|
.main p {
|
||||||
font-family: 'Teko', sans-serif !important;
|
font-family: "Teko", sans-serif !important;
|
||||||
color: white;
|
color: white;
|
||||||
letter-spacing: normal;
|
letter-spacing: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
@ -38,4 +38,4 @@
|
|||||||
|
|
||||||
.playerStats p {
|
.playerStats p {
|
||||||
font-size: 37px;
|
font-size: 37px;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
.scoreStats {
|
.scoreStats {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position:absolute;
|
position: absolute;
|
||||||
top:0;
|
top: 0;
|
||||||
right:0;
|
right: 0;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
min-width: 135px;
|
min-width: 135px;
|
||||||
}
|
}
|
||||||
@ -42,4 +42,4 @@
|
|||||||
.scoreStatsInfo {
|
.scoreStatsInfo {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
.songInfoContainer {
|
.songInfoContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom:0;
|
bottom: 0;
|
||||||
left:0;
|
left: 0;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
@ -76,4 +76,4 @@
|
|||||||
.songTimeText {
|
.songTimeText {
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,37 @@
|
|||||||
import Config from '../config.json';
|
import Config from "../config.json";
|
||||||
|
|
||||||
const mapCache = new Map();
|
const mapCache = new Map();
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
BEATSAVER_MAP_API: Config.proxy_url + "/https://api.beatsaver.com/maps/hash/%s",
|
BEATSAVER_MAP_API:
|
||||||
|
Config.proxy_url + "/https://api.beatsaver.com/maps/hash/%s",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a specified maps data from BeatSaver
|
* Gets a specified maps data from BeatSaver
|
||||||
*
|
*
|
||||||
* @param {string} hash
|
* @param {string} hash
|
||||||
* @returns The map data
|
* @returns The map data
|
||||||
*/
|
*/
|
||||||
async getMapData(hash) {
|
async getMapData(hash) {
|
||||||
hash = this.BEATSAVER_MAP_API.replace("%s", hash);
|
hash = this.BEATSAVER_MAP_API.replace("%s", hash);
|
||||||
if (mapCache.has(hash)) { // Return from cache
|
if (mapCache.has(hash)) {
|
||||||
return mapCache.get(hash);
|
// Return from cache
|
||||||
}
|
return mapCache.get(hash);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await fetch(hash, {
|
const data = await fetch(hash, {
|
||||||
headers: {
|
headers: {
|
||||||
"origin": "Fascinated Overlay"
|
origin: "Fascinated Overlay",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
if (data.status === 404) {
|
if (data.status === 404) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const json = await data.json();
|
const json = await data.json();
|
||||||
mapCache.set(hash, json);
|
mapCache.set(hash, json);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
mapCache.delete(hash);
|
mapCache.delete(hash);
|
||||||
}, 60 * 60 * 1000); // 1h
|
}, 60 * 60 * 1000); // 1h
|
||||||
return json;
|
return json;
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
Reference in New Issue
Block a user