diff --git a/pages/_app.js b/pages/_app.js index 9f0b947..dc9ff36 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,49 +1,55 @@ // 1. import `NextUIProvider` component -import { NextUIProvider } from '@nextui-org/react'; -import { NextSeo } from 'next-seo'; -import Head from 'next/head' +import { NextUIProvider } from "@nextui-org/react"; +import { NextSeo } from "next-seo"; +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 }) { - return ( - // 2. Use at the root of your app - - + - - - - - - - - - - ); + + + + + + + + + + ); } export default MyApp; diff --git a/pages/_document.js b/pages/_document.js index 9ff4766..e6c93ff 100644 --- a/pages/_document.js +++ b/pages/_document.js @@ -1,37 +1,37 @@ -import React from 'react'; -import Document, { Html, Head, Main, NextScript } from 'next/document'; -import { CssBaseline } from '@nextui-org/react'; +import { CssBaseline } from "@nextui-org/react"; +import Document, { Head, Html, Main, NextScript } from "next/document"; +import React from "react"; class MyDocument extends Document { - static async getInitialProps(ctx) { - const initialProps = await Document.getInitialProps(ctx); - return { - ...initialProps, - styles: React.Children.toArray([initialProps.styles]) - }; - } + static async getInitialProps(ctx) { + const initialProps = await Document.getInitialProps(ctx); + return { + ...initialProps, + styles: React.Children.toArray([initialProps.styles]), + }; + } - render() { - return ( - - - {CssBaseline.flush()} - - - - -
- - - - ); - } + render() { + return ( + + + {CssBaseline.flush()} + + + + +
+ + + + ); + } } -export default MyDocument; \ No newline at end of file +export default MyDocument; diff --git a/pages/api/beatsaver/art/[hash].js b/pages/api/beatsaver/art/[hash].js index 14236f7..5c7f436 100644 --- a/pages/api/beatsaver/art/[hash].js +++ b/pages/api/beatsaver/art/[hash].js @@ -1,31 +1,31 @@ -import fs from 'fs'; -import path from 'path'; -import sharp from 'sharp'; -import fetch from 'node-fetch'; +import fs from "fs"; +import fetch from "node-fetch"; +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"); + 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 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); - if (!exists) { - const data = await fetch(`https://eu.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 Art Cache - Added song \"" + mapHash + "\""); - return; - } - const buffer = fs.readFileSync(imagePath); - res.setHeader('Content-Type', 'image/jpg' + ext); - res.send(buffer); + const imagePath = cacheDir + path.sep + mapHash + "." + ext; + const exists = fs.existsSync(imagePath); + if (!exists) { + const data = await fetch(`https://eu.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 Art Cache - Added song "' + mapHash + '"'); + return; + } + const buffer = fs.readFileSync(imagePath); + res.setHeader("Content-Type", "image/jpg" + ext); + res.send(buffer); } diff --git a/pages/api/beatsaver/map.js b/pages/api/beatsaver/map.js index 5e2a6b7..1c3aa9e 100644 --- a/pages/api/beatsaver/map.js +++ b/pages/api/beatsaver/map.js @@ -1,16 +1,23 @@ -import Utils from '../../../utils/utils'; +import Utils from "../../../utils/utils"; 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_", "")); - if (mapData === undefined) { // 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, - songArt: "http://" + req.headers.host + "/api/beatsaver/art/" + mapHash + "?ext=" + mapData.versions[0].coverURL - .split("/")[3].split(".")[1] - }; - res.json({ error: false, data: data }); + const mapData = await Utils.getMapData(mapHash.replace("custom_level_", "")); + if (mapData === undefined) { + // 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, + songArt: + "http://" + + req.headers.host + + "/api/beatsaver/art/" + + mapHash + + "?ext=" + + mapData.versions[0].coverURL.split("/")[3].split(".")[1], + }; + res.json({ error: false, data: data }); } diff --git a/pages/api/mockdata.js b/pages/api/mockdata.js index f4a20e6..0443c75 100644 --- a/pages/api/mockdata.js +++ b/pages/api/mockdata.js @@ -1,9 +1,10 @@ export default async function handler(req, res) { - res.json({ - "avatar": "https://avatars.akamai.steamstatic.com/4322d8d20cb6dbdd1d891b4efa9952a9679c9a76_full.jpg", - "country": "GB", - "pp": 0, - "rank": 0, - "countryRank": 0, - }); + res.json({ + avatar: + "https://avatars.akamai.steamstatic.com/4322d8d20cb6dbdd1d891b4efa9952a9679c9a76_full.jpg", + country: "GB", + pp: 0, + rank: 0, + countryRank: 0, + }); } diff --git a/pages/index.js b/pages/index.js index 2aebb6c..c78e9ca 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,14 +1,24 @@ -import { Button, Card, Container, Grid, Input, Link, Modal, Spacer, Switch, Text, textTransforms } from '@nextui-org/react'; -import { Component } from 'react'; -import NavBar from '../src/components/Navbar'; +import { + Button, + 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 'react-toastify/dist/ReactToastify.css'; +import { toast, ToastContainer } from "react-toastify"; +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 { - constructor(props) { super(props); @@ -26,7 +36,7 @@ export default class Home extends Component { showScoreInfo: false, showSongInfo: false, }, - } + }; } async componentDidMount() { @@ -34,29 +44,35 @@ export default class Home extends Component { const params = Object.fromEntries(urlSearchParams.entries()); if (params.id) { - document.location.href = "/overlay/"+ window.location.search + document.location.href = "/overlay/" + window.location.search; return; } - if (localStorage.getItem('values') == undefined) { - localStorage.setItem('values', JSON.stringify({ - steamId: this.state.steamId, - values: this.state.values - })); + if (localStorage.getItem("values") == undefined) { + localStorage.setItem( + "values", + JSON.stringify({ + steamId: this.state.steamId, + values: this.state.values, + }) + ); } 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({ loading: false }); } loadPreview() { - this.setState({ isPreviewVisible: true, previewUrl: this.generateUrl(true) }); + this.setState({ + isPreviewVisible: true, + previewUrl: this.generateUrl(true), + }); } generateUrl(withTc = false) { let values = ""; - Object.entries(this.state.values).forEach(value => { + Object.entries(this.state.values).forEach((value) => { if (value[1] === undefined) { return; } @@ -67,7 +83,13 @@ export default class Home extends Component { 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) { @@ -79,122 +101,185 @@ export default class Home extends Component { updateStorage() { setTimeout(() => { - localStorage.setItem('values', JSON.stringify({ - steamId: this.state.steamId, - values: this.state.values - })); + localStorage.setItem( + "values", + JSON.stringify({ + steamId: this.state.steamId, + values: this.state.values, + }) + ); }, 5); } render() { - return this.state.loading ?

Loading...

: -
- + return this.state.loading ? ( +

Loading...

+ ) : ( +
+ - + + {/* Preview */} + {this.state.isPreviewVisible ? ( + this.setState({ isPreviewVisible: false })} + > + + Overlay Preview + + + + + + ) : ( + <> + )} - {/* Preview */} - { - this.state.isPreviewVisible ? this.setState({ isPreviewVisible: false })} - > - - - Overlay Preview - - - - - - : <> - } + + +
+ BeatSaber Overlay + Welcome to the Setup panel +
+
- - -
- BeatSaber Overlay - Welcome to the Setup panel -
-
+ + + + - - - - - - this.updateValue("socketAddr", event.target.value)} checked={true} - /> - - { - this.setState({ steamId: event.target.value }); - this.updateStorage(); - }} - /> - - Do you want to use BeatLeader rather than ScoreSaber? - this.updateValue("useBeatLeader", event.target.checked)} checked={this.state.values.useBeatLeader} size="md" /> - Do you want to show Player Stats (Current PP, global pos, etc) - this.updateValue("showPlayerStats", event.target.checked)} checked={this.state.values.showPlayerStats} size="md" /> - Do you want to show Score Info (Current swing values, total score, etc) - this.updateValue("showScoreInfo", event.target.checked)} checked={this.state.values.showScoreInfo} size="md" /> - Do you want to show Song Info (Song name, bsr, song art, etc) - this.updateValue("showSongInfo", event.target.checked)} checked={this.state.values.showSongInfo} size="md" /> - - - - - - + checked={this.state.values.useBeatLeader} + size="md" + /> + + Do you want to show Player Stats (Current PP, global pos, + etc) + + + this.updateValue("showPlayerStats", event.target.checked) + } + checked={this.state.values.showPlayerStats} + size="md" + /> + + Do you want to show Score Info (Current swing values, total + score, etc) + + + this.updateValue("showScoreInfo", event.target.checked) + } + checked={this.state.values.showScoreInfo} + size="md" + /> + + Do you want to show Song Info (Song name, bsr, song art, + etc) + + + this.updateValue("showSongInfo", event.target.checked) + } + checked={this.state.values.showSongInfo} + size="md" + /> + - { - this.state.overlayUrl !== undefined ? + + + + + + {this.state.overlayUrl !== undefined ? ( <> - - Url - - {this.state.overlayUrl} + Url + + {this.state.overlayUrl} + - : <> - } - - - -
-
+ ) : ( + <> + )} + + + + +
- -
+ +
+ ); + } } -} \ No newline at end of file diff --git a/pages/overlay.js b/pages/overlay.js index 0478978..8d1a722 100644 --- a/pages/overlay.js +++ b/pages/overlay.js @@ -1,14 +1,13 @@ -import { Link } from '@nextui-org/react'; -import {Component} from 'react' -import PlayerStats from '../src/components/PlayerStats'; -import ScoreStats from '../src/components/ScoreStats'; +import { Link } from "@nextui-org/react"; +import { Component } from "react"; +import PlayerStats from "../src/components/PlayerStats"; +import ScoreStats from "../src/components/ScoreStats"; import SongInfo from "../src/components/SongInfo"; -import Utils from '../src/utils/utils'; -import styles from '../styles/overlay.module.css'; +import Utils from "../src/utils/utils"; +import styles from "../styles/overlay.module.css"; export default class Overlay extends Component { - #_beatSaverURL = ""; constructor(props) { @@ -36,16 +35,16 @@ export default class Overlay extends Component { percentage: "100.00%", failed: false, leftHand: { - averageCut: [15.00], - averagePreSwing: [70.00], - averagePostSwing: [30.00], + averageCut: [15.0], + averagePreSwing: [70.0], + averagePostSwing: [30.0], }, rightHand: { - averageCut: [15.00], - averagePreSwing: [70.00], - averagePostSwing: [30.00], - } - } + averageCut: [15.0], + averagePreSwing: [70.0], + averagePostSwing: [30.0], + }, + }; this.setupTimer(); } @@ -60,25 +59,25 @@ export default class Overlay extends Component { setupTimer() { setInterval(() => { if (this.isCurrentSongTimeProvided) { - return + return; } if (!this.state.paused && this.state.beatSaverData !== undefined) { - this.setState({ currentSongTime: this.state.currentSongTime + 1 }) + this.setState({ currentSongTime: this.state.currentSongTime + 1 }); } }, 1000); } /** * Update the current song time - * + * * @param {[]} data The song data */ handleCurrentSongTime(data) { try { - const time = data.status.performance.currentSongTime + const time = data.status.performance.currentSongTime; if (time !== undefined && time != null) { - this.isCurrentSongTimeProvided = true - this.setState({ currentSongTime: time }) + this.isCurrentSongTimeProvided = true; + this.setState({ currentSongTime: time }); } } catch (e) { // do nothing @@ -86,24 +85,26 @@ export default class Overlay extends Component { } async componentDidMount() { - console.log("Initializing..."); - this.#_beatSaverURL = document.location.origin + "/api/beatsaver/map?hash=%s"; + console.log("Initializing..."); + this.#_beatSaverURL = + document.location.origin + "/api/beatsaver/map?hash=%s"; const urlSearchParams = new URLSearchParams(window.location.search); const params = Object.fromEntries(urlSearchParams.entries()); // Check what website the player wants to use - if (params.beatleader === 'true') { + if (params.beatleader === "true") { this.setState({ websiteType: "BeatLeader" }); } 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 }); return; } // 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 }); } @@ -114,18 +115,18 @@ export default class Overlay extends Component { let shouldConnectSocket = false; // 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 }); shouldConnectSocket = true; } // 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 }); shouldConnectSocket = true; } - // Mainly used for the preview + // Mainly used for the preview if (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 - * + * * @param {string} id The steam id of the player - * @returns + * @returns */ - async updateData(id) { - const data = await fetch(new Utils().getWebsiteApi(id == "test" ? "Test" : this.state.websiteType).ApiUrl.replace("%s", id), { - mode: 'cors' - }); + async updateData(id) { + const data = await fetch( + new Utils() + .getWebsiteApi(id == "test" ? "Test" : this.state.websiteType) + .ApiUrl.replace("%s", id), + { + mode: "cors", + } + ); const json = await data.json(); - if (json.errorMessage) { // Invalid account + if (json.errorMessage) { + // Invalid account this.setState({ loading: false, isValidSteamId: false }); return; } @@ -158,7 +165,10 @@ export default class Overlay extends Component { * Setup the HTTP Status connection */ 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.isVisible) { @@ -167,42 +177,46 @@ export default class Overlay extends Component { console.log(`Connecting to ${socketAddress}`); const socket = new WebSocket(socketAddress); - socket.addEventListener('open', () => { - console.log(`Connected to ${socketAddress}`); + socket.addEventListener("open", () => { + console.log(`Connected to ${socketAddress}`); this.setState({ isConnectedToSocket: true }); - }); - socket.addEventListener('close', () => { - console.log("Attempting to re-connect to the HTTP Status socket in 10 seconds."); + }); + socket.addEventListener("close", () => { + console.log( + "Attempting to re-connect to the HTTP Status socket in 10 seconds." + ); this.setState({ isConnectedToSocket: false }); setTimeout(() => this.connectSocket(), 10_000); }); - socket.addEventListener('message', (message) => { + socket.addEventListener("message", (message) => { const json = JSON.parse(message.data); - this.handleCurrentSongTime(json) + this.handleCurrentSongTime(json); if (!this.handlers[json.event]) { console.log("Unhandled message from HTTP Status. (" + json.event + ")"); return; } this.handlers[json.event](json || []); - }) + }); this.setState({ socket: socket }); } /** * Set the current songs beat saver url in {@link #_beatSaverURL} - * - * @param {[]} songData + * + * @param {[]} songData */ async setBeatSaver(songData) { - console.log("Updating BeatSaver info") - const data = await fetch(this.#_beatSaverURL.replace("%s", songData.levelId)); + console.log("Updating BeatSaver info"); + const data = await fetch( + this.#_beatSaverURL.replace("%s", songData.levelId) + ); const json = await data.json(); - this.setState({ beatSaverData: json }) + this.setState({ beatSaverData: json }); } /** * Cleanup the data and get ready for the next song - * + * * @param {boolean} visible Whether to show info other than the player stats */ async resetData(visible) { @@ -211,59 +225,68 @@ export default class Overlay extends Component { }, 250); this.setState({ leftHand: { - averageCut: [15.00], - averagePreSwing: [70.00], - averagePostSwing: [30.00], + averageCut: [15.0], + averagePreSwing: [70.0], + averagePostSwing: [30.0], }, rightHand: { - averageCut: [15.00], - averagePreSwing: [70.00], - averagePostSwing: [30.00], + averageCut: [15.0], + averagePreSwing: [70.0], + averagePostSwing: [30.0], }, songInfo: undefined, beatSaverData: undefined, currentSongTime: 0, currentScore: 0, percentage: "100.00%", - isVisible: visible + isVisible: visible, }); } // The HTTP Status handlers handlers = { - "hello": (data) => { + hello: (data) => { console.log("Hello from HTTP Status!"); if (data.status) { - this.setState({songData: data}); + this.setState({ songData: data }); if (data.status.beatmap) { this.setBeatSaver(data.status.beatmap); } } }, - "scoreChanged": (data) => { + scoreChanged: (data) => { const { status } = data; 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({ currentScore: score, - percentage: this.state.failed ? percent * 2 : percent + "%" - }) + percentage: this.state.failed ? percent * 2 : percent + "%", + }); }, - "noteFullyCut": (data) => { + noteFullyCut: (data) => { const { noteCut } = data; - console.log(noteCut) + console.log(noteCut); // Left Saber - if (noteCut.saberType === 'SaberA') { + if (noteCut.saberType === "SaberA") { const data = this.state.leftHand; if (data.averageCut.includes(15) && data.averageCut.length === 1) { data.averageCut = []; } - if (data.averagePreSwing.includes(70) && data.averagePreSwing.length === 1) { + if ( + data.averagePreSwing.includes(70) && + data.averagePreSwing.length === 1 + ) { data.averagePreSwing = []; } - if (data.averagePostSwing.includes(30) && data.averagePostSwing.length === 1) { + if ( + data.averagePostSwing.includes(30) && + data.averagePostSwing.length === 1 + ) { data.averagePostSwing = []; } data.averagePreSwing.push(noteCut.beforeSwingRating * 70); @@ -273,15 +296,21 @@ export default class Overlay extends Component { } // Left Saber - if (noteCut.saberType === 'SaberB') { + if (noteCut.saberType === "SaberB") { const data = this.state.rightHand; if (data.averageCut.includes(15) && data.averageCut.length === 1) { data.averageCut = []; } - if (data.averagePreSwing.includes(70) && data.averagePreSwing.length === 1) { + if ( + data.averagePreSwing.includes(70) && + data.averagePreSwing.length === 1 + ) { data.averagePreSwing = []; } - if (data.averagePostSwing.includes(30) && data.averagePostSwing.length === 1) { + if ( + data.averagePostSwing.includes(30) && + data.averagePostSwing.length === 1 + ) { data.averagePostSwing = []; } data.averagePreSwing.push(noteCut.beforeSwingRating * 70); @@ -290,77 +319,85 @@ export default class Overlay extends Component { this.setState({ rightHand: data }); } }, - "songStart": (data) => { - console.log("Going into level, resetting data.") + songStart: (data) => { + console.log("Going into level, resetting data."); this.resetData(true); - this.setState({ songData: data, paused: false }) + this.setState({ songData: data, paused: false }); this.setBeatSaver(data.status.beatmap); }, - "finished": () => { + finished: () => { this.resetData(false); }, - "softFail": () => { + softFail: () => { this.setState({ failed: true }); }, - "pause": () => { + pause: () => { this.setState({ paused: true }); }, - "resume": () => { + resume: () => { this.setState({ paused: false }); }, - "menu": () => { + menu: () => { this.resetData(false); }, - "noteCut": () => {}, - "noteMissed": () => {}, - "noteSpawned": () => {}, - "bombMissed": () => {}, - "beatmapEvent": () => {}, - "energyChanged": () => {}, - } + noteCut: () => {}, + noteMissed: () => {}, + noteSpawned: () => {}, + bombMissed: () => {}, + beatmapEvent: () => {}, + energyChanged: () => {}, + }; render() { const { loading, isValidSteamId, data, websiteType } = this.state; - if (this.state.textColor !== undefined) { - const element = document.querySelector("." + styles.main); - element.style.color = this.state.textColor - } + if (this.state.textColor !== undefined) { + const element = document.querySelector("." + styles.main); + element.style.color = this.state.textColor; + } - return
- { loading ? -
-

Loading...

+ return ( +
+ {loading ? ( +
+

Loading...

+
+ ) : !isValidSteamId ? ( +
+

Invalid player, please visit the main page.

+ + Go Home + +
+ ) : ( +
+ {this.state.showPlayerStats ? ( + + ) : ( + <> + )} + {this.state.showScore && this.state.isVisible ? ( + + ) : ( + <> + )} + {this.state.showSongInfo && + this.state.beatSaverData !== undefined && + this.state.isVisible ? ( + + ) : ( + <> + )} +
+ )}
- : !isValidSteamId ? -
-

Invalid player, please visit the main page.

- - Go Home - -
: -
- { - this.state.showPlayerStats ? - : <> - } - { - this.state.showScore && this.state.isVisible ? - : <> - } - { - this.state.showSongInfo && this.state.beatSaverData !== undefined && this.state.isVisible ? - : <> - } -
- } -
+ ); } -} \ No newline at end of file +} diff --git a/src/components/Avatar.js b/src/components/Avatar.js index 081ad80..556bce3 100644 --- a/src/components/Avatar.js +++ b/src/components/Avatar.js @@ -1,21 +1,23 @@ import Image from "next/image"; -import styles from '../../styles/avatar.module.css'; +import styles from "../../styles/avatar.module.css"; const Avatar = (props) => { - return <> - {'Avatar - -} + return ( + <> + {"Avatar + + ); +}; -export default Avatar; \ No newline at end of file +export default Avatar; diff --git a/src/components/Navbar.js b/src/components/Navbar.js index e3ec78d..afebcda 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -1,13 +1,15 @@ import { Navbar, Text } from "@nextui-org/react"; const NavBar = () => { - return - - - BeatSaber Overlay - - - -} + return ( + + + + BeatSaber Overlay + + + + ); +}; -export default NavBar; \ No newline at end of file +export default NavBar; diff --git a/src/components/PlayerStats.js b/src/components/PlayerStats.js index dee64fb..12a18e4 100644 --- a/src/components/PlayerStats.js +++ b/src/components/PlayerStats.js @@ -1,24 +1,37 @@ import ReactCountryFlag from "react-country-flag"; -import styles from '../../styles/playerStats.module.css'; +import styles from "../../styles/playerStats.module.css"; import Avatar from "./Avatar"; const PlayerStats = (props) => { - return
-
- -
-
-

{props.pp}pp ({props.websiteType})

-

#{props.globalPos}

-
-

#{props.countryRank}

- -
-
-
-} + return ( +
+
+ +
+
+

+ {props.pp}pp{" "} + + ({props.websiteType}) + +

+

#{props.globalPos}

+
+

#{props.countryRank}

+ +
+
+
+ ); +}; -export default PlayerStats; \ No newline at end of file +export default PlayerStats; diff --git a/src/components/ScoreStats.js b/src/components/ScoreStats.js index a8586e4..4d37611 100644 --- a/src/components/ScoreStats.js +++ b/src/components/ScoreStats.js @@ -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 { + constructor(params) { + super(params); + } - constructor(params) { - super(params); - } + /** + * Returns the average of the provided numbers list + * + * @param {List} hitValues + * @returns The average value + */ + getAverage(hitValues) { + return hitValues.reduce((p, c) => p + c, 0) / hitValues.length; + } - /** - * Returns the average of the provided numbers list - * - * @param {List} hitValues - * @returns The average value - */ - getAverage(hitValues) { - return hitValues.reduce((p, c) => p + c, 0) / hitValues.length; - } + render() { + const data = this.props.data; - render() { - const data = this.props.data; - - return
-
-

{data.percentage}

-

{data.currentScore.toLocaleString()}

-
-

Average Cut

-
-
-

{this.getAverage(data.leftHand.averagePreSwing).toFixed(2)}

-

{this.getAverage(data.leftHand.averagePostSwing).toFixed(2)}

-

{this.getAverage(data.leftHand.averageCut).toFixed(2)}

-
-
-

{this.getAverage(data.rightHand.averagePreSwing).toFixed(2)}

-

{this.getAverage(data.rightHand.averagePostSwing).toFixed(2)}

-

{this.getAverage(data.rightHand.averageCut).toFixed(2)}

-
-
-
- } -} \ No newline at end of file + return ( +
+
+

{data.percentage}

+

{data.currentScore.toLocaleString()}

+
+

Average Cut

+
+
+

{this.getAverage(data.leftHand.averagePreSwing).toFixed(2)}

+

{this.getAverage(data.leftHand.averagePostSwing).toFixed(2)}

+

{this.getAverage(data.leftHand.averageCut).toFixed(2)}

+
+
+

{this.getAverage(data.rightHand.averagePreSwing).toFixed(2)}

+

{this.getAverage(data.rightHand.averagePostSwing).toFixed(2)}

+

{this.getAverage(data.rightHand.averageCut).toFixed(2)}

+
+
+
+ ); + } +} diff --git a/src/components/SongInfo.js b/src/components/SongInfo.js index a265f7f..76ea60c 100644 --- a/src/components/SongInfo.js +++ b/src/components/SongInfo.js @@ -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 { - - constructor(params) { - super(params); - this.state = { - diffColor: undefined - } - } + constructor(params) { + super(params); + this.state = { + diffColor: undefined, + }; + } - componentDidMount() { - const data = this.props.data.songData.status.beatmap; - this.formatDiff(data.difficulty); - } + componentDidMount() { + const data = this.props.data.songData.status.beatmap; + this.formatDiff(data.difficulty); + } - /** - * Update the difficulity color from the given difficulity - * - * @param {string} diff - */ - formatDiff(diff) { - if (diff === "Expert+") { - this.setState({ diffColor: "#8f48db" }); - } - if (diff === "Expert") { - this.setState({ diffColor: "#bf2a42" }); - } - if (diff === "Hard") { - this.setState({ diffColor: "tomato" }); - } - if (diff === "Normal") { - this.setState({ diffColor: "#59b0f4" }); - } - if (diff === "Easy") { - this.setState({ diffColor: "MediumSeaGreen" }); - } - } + /** + * Update the difficulity color from the given difficulity + * + * @param {string} diff + */ + formatDiff(diff) { + if (diff === "Expert+") { + this.setState({ diffColor: "#8f48db" }); + } + if (diff === "Expert") { + this.setState({ diffColor: "#bf2a42" }); + } + if (diff === "Hard") { + this.setState({ diffColor: "tomato" }); + } + if (diff === "Normal") { + this.setState({ diffColor: "#59b0f4" }); + } + if (diff === "Easy") { + this.setState({ diffColor: "MediumSeaGreen" }); + } + } - /** - * Format the given ms - * - * @param {Number} millis - * @returns The formatted time - */ - msToMinSeconds(millis) { - const minutes = Math.floor(millis / 60000); - const seconds = Number(((millis % 60000) / 1000).toFixed(0)); - return seconds === 60 ? minutes + 1 + ":00" : minutes + ":" + (seconds < 10 ? "0" : "") + seconds; - } + /** + * Format the given ms + * + * @param {Number} millis + * @returns The formatted time + */ + msToMinSeconds(millis) { + const minutes = Math.floor(millis / 60000); + const seconds = Number(((millis % 60000) / 1000).toFixed(0)); + return seconds === 60 + ? minutes + 1 + ":00" + : minutes + ":" + (seconds < 10 ? "0" : "") + seconds; + } - render() { - const data = this.props.data.songData.status.beatmap; - const beatSaverData = this.props.data.beatSaverData.data; - const songArt = beatSaverData.songArt; - 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; + render() { + const data = this.props.data.songData.status.beatmap; + const beatSaverData = this.props.data.beatSaverData.data; + const songArt = beatSaverData.songArt; + 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; - return
- -
-

{songName.length > 35 ? songName.substring(0, 35) + "..." : songName}

-

{songAuthorName}

-
-

{difficulty}

-

!bsr {bsr}

-
-

{this.msToMinSeconds(this.props.data.currentSongTime * 1000)}/{this.msToMinSeconds(data.length)}

-
-
-
-
-
-
- } -} \ No newline at end of file + return ( +
+ +
+

+ {songName.length > 35 + ? songName.substring(0, 35) + "..." + : songName} +

+

{songAuthorName}

+
+

+ {difficulty} +

+

!bsr {bsr}

+
+

+ {this.msToMinSeconds(this.props.data.currentSongTime * 1000)}/ + {this.msToMinSeconds(data.length)} +

+
+
+
+
+
+
+ ); + } +} diff --git a/src/consts/WebsiteType.js b/src/consts/WebsiteType.js index 2f75a80..b10b588 100644 --- a/src/consts/WebsiteType.js +++ b/src/consts/WebsiteType.js @@ -1,15 +1,15 @@ -import Config from '../../config.json'; +import Config from "../../config.json"; const WebsiteTypes = { - ScoreSaber: { - ApiUrl: Config.proxy_url + "/https://scoresaber.com/api/player/%s/full" - }, - BeatLeader: { - ApiUrl: Config.proxy_url + "/https://api.beatleader.xyz/player/%s" - }, - Test: { - ApiUrl: "/api/mockdata" - } -} + ScoreSaber: { + ApiUrl: Config.proxy_url + "/https://scoresaber.com/api/player/%s/full", + }, + BeatLeader: { + ApiUrl: Config.proxy_url + "/https://api.beatleader.xyz/player/%s", + }, + Test: { + ApiUrl: "/api/mockdata", + }, +}; -export default WebsiteTypes \ No newline at end of file +export default WebsiteTypes; diff --git a/src/utils/utils.js b/src/utils/utils.js index e6d9656..a9a712a 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,15 +1,15 @@ import WebsiteTypes from "../consts/WebsiteType"; export default class Utils { - constructor() {}; + constructor() {} - /** - * Returns the information for the given website type. - * - * @param {WebsiteTypes} website - * @returns The website type's information. - */ - getWebsiteApi(website) { - return WebsiteTypes[website] - } -} \ No newline at end of file + /** + * Returns the information for the given website type. + * + * @param {WebsiteTypes} website + * @returns The website type's information. + */ + getWebsiteApi(website) { + return WebsiteTypes[website]; + } +} diff --git a/styles/avatar.module.css b/styles/avatar.module.css index 832775c..004d132 100644 --- a/styles/avatar.module.css +++ b/styles/avatar.module.css @@ -1,3 +1,3 @@ .playerAvatar { border-radius: 5%; -} \ No newline at end of file +} diff --git a/styles/globals.css b/styles/globals.css index 0253b0f..8e2b626 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -1,3 +1,5 @@ -*, html, body { +*, +html, +body { background-color: transparent; -} \ No newline at end of file +} diff --git a/styles/main.module.css b/styles/main.module.css index 960a5be..6a43856 100644 --- a/styles/main.module.css +++ b/styles/main.module.css @@ -1,6 +1,6 @@ .main { - font-family: 'Roboto', sans-serif !important; + font-family: "Roboto", sans-serif !important; color: white; font-size: xx-large; line-height: 1.4em !important; -} \ No newline at end of file +} diff --git a/styles/overlay.module.css b/styles/overlay.module.css index 71e6b8c..42f13ff 100644 --- a/styles/overlay.module.css +++ b/styles/overlay.module.css @@ -1,16 +1,16 @@ .main { - font-family: 'Teko', sans-serif !important; + font-family: "Teko", sans-serif !important; color: white; font-size: xx-large; line-height: 1.4em !important; } .main p { - font-family: 'Teko', sans-serif !important; + font-family: "Teko", sans-serif !important; color: white; letter-spacing: normal; } .loading { color: black; -} \ No newline at end of file +} diff --git a/styles/playerStats.module.css b/styles/playerStats.module.css index 821844d..763f125 100644 --- a/styles/playerStats.module.css +++ b/styles/playerStats.module.css @@ -38,4 +38,4 @@ .playerStats p { font-size: 37px; -} \ No newline at end of file +} diff --git a/styles/scoreStats.module.css b/styles/scoreStats.module.css index 6e7a1b8..50b86ef 100644 --- a/styles/scoreStats.module.css +++ b/styles/scoreStats.module.css @@ -1,8 +1,8 @@ .scoreStats { text-align: center; - position:absolute; - top:0; - right:0; + position: absolute; + top: 0; + right: 0; margin-right: 5px; min-width: 135px; } @@ -42,4 +42,4 @@ .scoreStatsInfo { margin-right: 10px; text-align: right; -} \ No newline at end of file +} diff --git a/styles/songInfo.module.css b/styles/songInfo.module.css index 5414b76..33e6430 100644 --- a/styles/songInfo.module.css +++ b/styles/songInfo.module.css @@ -1,8 +1,8 @@ .songInfoContainer { display: flex; position: fixed; - bottom:0; - left:0; + bottom: 0; + left: 0; margin-left: 5px; margin-bottom: 5px; } @@ -76,4 +76,4 @@ .songTimeText { margin-top: -10px; font-size: 20px; -} \ No newline at end of file +} diff --git a/utils/utils.js b/utils/utils.js index 56a7f3d..f9a6f1e 100644 --- a/utils/utils.js +++ b/utils/utils.js @@ -1,35 +1,37 @@ -import Config from '../config.json'; +import Config from "../config.json"; const mapCache = new Map(); 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 - * - * @param {string} hash - * @returns The map data - */ - async getMapData(hash) { - hash = this.BEATSAVER_MAP_API.replace("%s", hash); - if (mapCache.has(hash)) { // Return from cache - return mapCache.get(hash); - } + /** + * Gets a specified maps data from BeatSaver + * + * @param {string} hash + * @returns The map data + */ + async getMapData(hash) { + hash = this.BEATSAVER_MAP_API.replace("%s", hash); + if (mapCache.has(hash)) { + // Return from cache + return mapCache.get(hash); + } - const data = await fetch(hash, { - headers: { - "origin": "Fascinated Overlay" - } - }); - if (data.status === 404) { - return undefined; - } - const json = await data.json(); - mapCache.set(hash, json); - setTimeout(() => { - mapCache.delete(hash); - }, 60 * 60 * 1000); // 1h - return json; - } -} \ No newline at end of file + const data = await fetch(hash, { + headers: { + origin: "Fascinated Overlay", + }, + }); + if (data.status === 404) { + return undefined; + } + const json = await data.json(); + mapCache.set(hash, json); + setTimeout(() => { + mapCache.delete(hash); + }, 60 * 60 * 1000); // 1h + return json; + }, +};