Compare commits
105 Commits
324c08f6f6
...
renovate/n
Author | SHA1 | Date | |
---|---|---|---|
d62f51c48e | |||
6b5b8bb54f | |||
403c34befd | |||
0185a9ed3e | |||
47a255e48a | |||
69f763cdb0 | |||
cd6f550035 | |||
ab7d5f909c | |||
918da6f88b | |||
1ea19aff4b | |||
a34de0aa93 | |||
a9b7d65c20 | |||
6ef4eac93c | |||
acc107baff | |||
a504051d7f | |||
e3861c1a47 | |||
e7206883d9 | |||
324f4ef0e2 | |||
393ecf0c3f | |||
3a69316f64 | |||
61b1b4194e | |||
6e777c5be5 | |||
595f7fdfe2 | |||
0f57963155 | |||
5467f31dd7 | |||
8127e43bf1 | |||
d7ff4fd9f5 | |||
0bed47986b | |||
d7773702bb | |||
ad8d211ce8 | |||
418ef17b73 | |||
90f39bb409 | |||
37e60a8425 | |||
a55027ef68 | |||
4b3a991b91 | |||
52d44d79c1 | |||
3a49341a6a | |||
d3ae2761cd | |||
01be27b415 | |||
0e6e472ded | |||
7139163ba2 | |||
462535254d | |||
5871e9b662 | |||
83d5193257 | |||
44e9687867 | |||
bced89d1ec | |||
884d3df8ec | |||
d001590405 | |||
529f6d38b3 | |||
75d6429379 | |||
4dce461cd9 | |||
2cd1efbd1d | |||
9d2cf79372 | |||
ea843e475c | |||
3b6b8389ae | |||
07616aad0e | |||
f809d61171 | |||
57a97c6caf | |||
33d5dfab3f | |||
885bf5c4b3 | |||
1ad54694a9 | |||
b7b5823d99 | |||
d2f63c6dc5 | |||
c6db350393 | |||
b9bd7f8e85 | |||
35e2ccd402 | |||
23cd272c3c | |||
d84122da3a | |||
190c19f238 | |||
34c17ea167 | |||
3b0f7e3ef2 | |||
42192e7885 | |||
8f86710cb6 | |||
f013d49c7a | |||
8737f88145 | |||
3e4c1ff437 | |||
40c65e4d88 | |||
6eb3ead4c9 | |||
7013bb4e09 | |||
9f8f6001b8 | |||
af7f5a6de2 | |||
7aa8724ba1 | |||
7443e38033 | |||
b3a2a943e7 | |||
cfdf052e25 | |||
fa9b44aebd | |||
cea026adb6 | |||
a18a77d198 | |||
caa10915a8 | |||
42ff5305f5 | |||
e12a6513cb | |||
a2357c6469 | |||
03f8ef04de | |||
a0e7901feb | |||
78cb587464 | |||
61afc15409 | |||
67525b4cc7 | |||
4d373228f2 | |||
7f4a1ea56c | |||
334c788318 | |||
faa314960f | |||
6f0fdb3778 | |||
0ae09dc3e8 | |||
4970d5a51c | |||
7f1ce8bd15 |
@ -1,15 +0,0 @@
|
|||||||
name: Gitea Actions Demo
|
|
||||||
run-name: ${{ github.actor }} is testing out Gitea Actions
|
|
||||||
on: [push]
|
|
||||||
jobs:
|
|
||||||
Explore-Gitea-Actions:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- run: echo "The job was automatically triggered by a ${{ github.event_name }} event."
|
|
||||||
- run: echo "This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
|
||||||
- run: echo "The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- run: echo "The ${{ github.repository }} repository has been cloned to the runner."
|
|
||||||
- run: echo "The workflow is now ready to test your code on the runner."
|
|
||||||
- run: echo "This job's status is ${{ job.status }}."
|
|
71
.drone.yml
71
.drone.yml
@ -1,71 +0,0 @@
|
|||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: default
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: restore-cache
|
|
||||||
image: drillster/drone-volume-cache
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /cache
|
|
||||||
settings:
|
|
||||||
restore: true
|
|
||||||
mount:
|
|
||||||
- ./node_modules
|
|
||||||
|
|
||||||
- name: install depends
|
|
||||||
image: node:18
|
|
||||||
commands:
|
|
||||||
- npm install
|
|
||||||
|
|
||||||
- name: rebuild-cache
|
|
||||||
image: drillster/drone-volume-cache
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
path: /cache
|
|
||||||
settings:
|
|
||||||
rebuild: true
|
|
||||||
mount:
|
|
||||||
- ./node_modules
|
|
||||||
|
|
||||||
# Other branches
|
|
||||||
- name: test
|
|
||||||
image: node:18
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
exclude:
|
|
||||||
- main
|
|
||||||
commands:
|
|
||||||
- npm run lint
|
|
||||||
|
|
||||||
# Main branches
|
|
||||||
- name: docker
|
|
||||||
image: plugins/docker
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- main
|
|
||||||
settings:
|
|
||||||
username:
|
|
||||||
from_secret: dockerhub_username
|
|
||||||
password:
|
|
||||||
from_secret: dockerhub_password
|
|
||||||
repo: fascinated/beatsaber-overlay
|
|
||||||
tags: latest
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: cache
|
|
||||||
host:
|
|
||||||
path: /tmp/cache
|
|
||||||
# deploy:
|
|
||||||
# image: alpine:latest
|
|
||||||
# stage: deploy
|
|
||||||
# tags:
|
|
||||||
# script:
|
|
||||||
# - chmod og= $ID_RSA
|
|
||||||
# - apk update && apk add openssh-client
|
|
||||||
# - ssh -i $ID_RSA -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP "cd /home/overlay && docker compose pull && docker compose up -d && docker image prune -f"
|
|
||||||
# environment:
|
|
||||||
# name: production
|
|
||||||
# url: https://bs-overlay.fascinated.cc
|
|
||||||
# only:
|
|
||||||
# - main
|
|
@ -1,12 +1,12 @@
|
|||||||
REACT_APP_HTTP_PROXY=https://proxy.fascinated.cc
|
REACT_APP_HTTP_PROXY=<https://proxy.fascinated.cc>
|
||||||
|
|
||||||
REACT_APP_SITE_NAME=BeatSaber Overlay
|
REACT_APP_SITE_NAME=BeatSaber Overlay
|
||||||
REACT_APP_SITE_TITLE=BeatSaber Overlay - Simple and easy to use BeatSaber overlay
|
REACT_APP_SITE_TITLE=BeatSaber Overlay - Simple and easy to use BeatSaber overlay
|
||||||
REACT_APP_SITE_DESCRIPTION=ScoreSaber and BeatLeader overlay for Twitch streamers - Elevate your Beat Saber streams with our professional, customizable overlay that displays your real-time scores, rankings, and leaderboard information for your viewers.
|
REACT_APP_SITE_DESCRIPTION=ScoreSaber and BeatLeader overlay for Twitch streamers - Elevate your Beat Saber streams with our professional, customizable overlay that displays your real-time scores, rankings, and leaderboard information for your viewers.
|
||||||
REACT_APP_SITE_COLOR=0EBFE9
|
REACT_APP_SITE_COLOR=0EBFE9
|
||||||
REACT_APP_SITE_URL=http://localhost:3000
|
REACT_APP_SITE_URL=<http://localhost:3000>
|
||||||
|
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
REDIS_HOST=localhost
|
REDIS_HOST=localhost
|
||||||
REDIS_PASSWORD=set me
|
REDIS_PASSWORD=set me
|
||||||
REDIS_DATABASE=0
|
REDIS_DATABASE=0
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
|
"parser": "@babel/eslint-parser",
|
||||||
"extends": "next/core-web-vitals"
|
"extends": "next/core-web-vitals"
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,58 @@
|
|||||||
name: Gitea Actions Demo
|
name: Publish
|
||||||
run-name: ${{ github.actor }} is testing out Gitea Actions
|
|
||||||
on: [push]
|
on:
|
||||||
jobs:
|
push:
|
||||||
Explore-Gitea-Actions:
|
branches:
|
||||||
runs-on: ubuntu-20.04
|
- "main"
|
||||||
steps:
|
- "development"
|
||||||
- run: echo "The job was automatically triggered by a ${{ github.event_name }} event."
|
|
||||||
- run: echo "This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
jobs:
|
||||||
- run: echo "The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
|
docker:
|
||||||
- name: Check out repository code
|
runs-on: ubuntu-22.04
|
||||||
uses: actions/checkout@v3
|
container: fascinated/docker-images:node-latest
|
||||||
- run: echo "The ${{ github.repository }} repository has been cloned to the runner."
|
steps:
|
||||||
- run: echo "The workflow is now ready to test your code on the runner."
|
- name: Get branch name
|
||||||
- run: echo "This job's status is ${{ job.status }}."
|
id: branch-name
|
||||||
|
uses: tj-actions/branch-names@v7
|
||||||
|
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Repo
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.REPO_USERNAME }}
|
||||||
|
password: ${{ secrets.REPO_TOKEN }}
|
||||||
|
|
||||||
|
- name: Restore Docker Build Cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
id: build-cache
|
||||||
|
with:
|
||||||
|
path: /tmp/.buildx-cache
|
||||||
|
key: ${{ runner.os }}-buildx
|
||||||
|
|
||||||
|
- name: Build and Push (Latest)
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
if: steps.branch-name.outputs.current_branch == 'main'
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
context: .
|
||||||
|
tags: fascinated/beatsaber-overlay:latest
|
||||||
|
|
||||||
|
- name: Build and Push (Other Branches)
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
if: steps.branch-name.outputs.current_branch != 'main'
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
context: .
|
||||||
|
tags: fascinated/beatsaber-overlay:${{ steps.branch-name.outputs.current_branch }}
|
||||||
|
|
||||||
|
- name: Save Docker Build Cache
|
||||||
|
id: build-cache
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
with:
|
||||||
|
path: /tmp/.buildx-caches
|
||||||
|
key: ${{ runner.os }}-buildx
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -4,6 +4,7 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
@ -37,4 +38,7 @@ yarn-error.log*
|
|||||||
.vercel
|
.vercel
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# Ignore Yarn
|
||||||
|
yarn.lock
|
59
Dockerfile
59
Dockerfile
@ -1,62 +1,39 @@
|
|||||||
# Install dependencies only when needed
|
FROM fascinated/docker-images:node-pnpm-latest AS base
|
||||||
FROM node:19 AS deps
|
|
||||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
# Install depends
|
||||||
#RUN apk add libc6-compat
|
FROM base AS deps
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
COPY package.json* pnpm-lock.yaml* ./
|
||||||
|
RUN pnpm install --production --frozen-lockfile --quiet
|
||||||
|
|
||||||
# Install dependencies based on the preferred package manager
|
# Build from source
|
||||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
FROM base AS builder
|
||||||
|
|
||||||
# Copy cached files
|
|
||||||
#COPY node_modules ./
|
|
||||||
|
|
||||||
#RUN npm i
|
|
||||||
|
|
||||||
RUN \
|
|
||||||
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
|
||||||
elif [ -f package-lock.json ]; then npm ci; \
|
|
||||||
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
|
|
||||||
else echo "Lockfile not found." && exit 1; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rebuild the source code only when needed
|
|
||||||
FROM node:19 AS builder
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
RUN pnpm build
|
||||||
|
|
||||||
# Build the project
|
# Run the app
|
||||||
RUN yarn build
|
FROM base AS runner
|
||||||
|
|
||||||
# Production image, copy all the files and run next
|
|
||||||
FROM node:19-alpine AS runner
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
RUN addgroup -g 1001 -S nodejs
|
|
||||||
RUN adduser -S nextjs -u 1001
|
|
||||||
|
|
||||||
COPY --from=builder /app/public ./public
|
COPY --from=builder /app/public ./public
|
||||||
|
|
||||||
# Automatically leverage output traces to reduce image size
|
# Automatically leverage output traces to reduce image size
|
||||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/next.config.js ./
|
COPY --from=builder /app/next.config.js ./
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
COPY --from=builder /app/.next/standalone ./
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
COPY --from=builder /app/.next/static ./.next/static
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/public ./.next/static
|
COPY --from=builder /app/public ./.next/static
|
||||||
|
|
||||||
RUN npm i -g @beam-australia/react-env
|
RUN npm i -g @beam-australia/react-env
|
||||||
|
|
||||||
RUN chown -R nextjs:nodejs /app
|
|
||||||
|
|
||||||
USER nextjs
|
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
ENV HOSTNAME "0.0.0.0"
|
||||||
ENV PORT 3000
|
ENV PORT 3000
|
||||||
|
CMD npx react-env --env APP_ENV && pnpm start
|
||||||
CMD npx react-env --env APP_ENV && yarn start
|
|
13
README.md
13
README.md
@ -1,10 +1,8 @@
|
|||||||
# BeatSaber Overlay with real-time data from HttpSiraStatus
|
# BeatSaber Overlay with real-time data from HttpSiraStatus
|
||||||
|
|
||||||
[](https://drone.fascinated.cc/Fascinated/beatsaber-overlay)
|
The project is on my [Gitea](https://git.fascinated.cc/Fascinated/beatsaber-overlay) now, but the repo will be kept in sync on GitHub. <br />
|
||||||
|
|
||||||
The project is on my [Gitea](https://git.fascinated.cc/Fascinated/beatsaber-overlay) now, but the repo will be kept in sync here. <br />
|
|
||||||
The public url to view/use: <https://bs-overlay.fascinated.cc> <br />
|
The public url to view/use: <https://bs-overlay.fascinated.cc> <br />
|
||||||
Need help? Feel free to message me at: Fascinated#7668
|
Need help? Feel free to message me on Discord: Fascinated#7668 or Matrix: @fascinated:matrix.fascinated.cc
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@ -15,13 +13,6 @@ Go to the [Wiki](https://git.fascinated.cc/Fascinated/beatsaber-overlay/wiki/Usa
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
## Todo
|
|
||||||
|
|
||||||
- Add toggle for showing pp
|
|
||||||
- Change the song time to a circular style in the song art
|
|
||||||
- Add overall stream stats (avg acc, misses, highest combo)
|
|
||||||
- Add option to scale the ui using css scale
|
|
||||||
|
|
||||||
## Getting started with developent
|
## Getting started with developent
|
||||||
|
|
||||||
- Clone the repo
|
- Clone the repo
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 178 KiB |
@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
beatsaber-overlay:
|
beatsaber-overlay:
|
||||||
image: fascinated/beatsaber-overlay:latest
|
image: git.fascinated.cc/fascinated/beatsaber-overlay:latest
|
||||||
restart: always
|
restart: always
|
||||||
env_file:
|
env_file:
|
||||||
- .env.production
|
- .env.production
|
||||||
|
11398
package-lock.json
generated
11398
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
80
package.json
80
package.json
@ -1,40 +1,44 @@
|
|||||||
{
|
{
|
||||||
"name": "beatsaber-overlay",
|
"name": "beatsaber-overlay",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "react-env -- next",
|
"dev": "react-env -- next",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "APP_ENV=production node server.js",
|
"start": "APP_ENV=production node server.js",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"update-depends": "npm-check -u"
|
"update-depends": "npm-check -u"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@beam-australia/react-env": "^3.1.1",
|
"@babel/eslint-parser": "^7.22.15",
|
||||||
"@emotion/cache": "^11.10.5",
|
"@beam-australia/react-env": "^3.1.1",
|
||||||
"@emotion/server": "^11.10.0",
|
"@emotion/cache": "^11.11.0",
|
||||||
"@nextui-org/react": "^1.0.0-beta.12",
|
"@emotion/server": "^11.11.0",
|
||||||
"axios": "^1.3.4",
|
"@nextui-org/react": "2.2.4",
|
||||||
"core-js-pure": "^3.29.1",
|
"@typescript-eslint/eslint-plugin": "^6.7.5",
|
||||||
"critters": "^0.0.16",
|
"@typescript-eslint/parser": "^6.7.5",
|
||||||
"ioredis": "^5.3.1",
|
"axios": "^1.5.1",
|
||||||
"next": "^13.2.4",
|
"core-js-pure": "^3.33.0",
|
||||||
"next-seo": "^5.15.0",
|
"critters": "^0.0.20",
|
||||||
"next-themes": "^0.2.1",
|
"ioredis": "^5.3.2",
|
||||||
"prop-types": "^15.8.1",
|
"next": "^13.5.4",
|
||||||
"react": "^18.2.0",
|
"next-seo": "^6.1.0",
|
||||||
"react-country-flag": "^3.0.2",
|
"next-themes": "^0.2.1",
|
||||||
"react-dom": "^18.2.0",
|
"prop-types": "^15.8.1",
|
||||||
"react-toastify": "^9.1.2",
|
"react": "^18.2.0",
|
||||||
"sharp": "^0.32.0",
|
"react-country-flag": "^3.1.0",
|
||||||
"websocket": "^1.0.34",
|
"react-dom": "^18.2.0",
|
||||||
"zustand": "^4.3.6"
|
"react-toastify": "^9.1.3",
|
||||||
},
|
"sharp": "^0.32.6",
|
||||||
"devDependencies": {
|
"websocket": "^1.0.34",
|
||||||
"@types/node": "^18.15.10",
|
"zustand": "^4.4.3"
|
||||||
"@types/react": "^18.0.29",
|
},
|
||||||
"@types/websocket": "^1.0.5",
|
"devDependencies": {
|
||||||
"eslint": "^8.36.0",
|
"@types/node": "^20.8.4",
|
||||||
"eslint-config-next": "^13.2.4",
|
"@types/react": "^18.2.28",
|
||||||
"typescript": "^5.0.2"
|
"@types/websocket": "^1.0.7",
|
||||||
}
|
"eslint": "^8.51.0",
|
||||||
|
"eslint-config-next": "^13.5.4",
|
||||||
|
"npm-check": "^6.0.1",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
7669
pnpm-lock.yaml
generated
Normal file
7669
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
renovate.json
Normal file
6
renovate.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"local>Fascinated/renovate-config"
|
||||||
|
]
|
||||||
|
}
|
@ -2,19 +2,19 @@ import { Navbar, Text } from "@nextui-org/react";
|
|||||||
import Settings from "./Settings";
|
import Settings from "./Settings";
|
||||||
|
|
||||||
const NavBar = (props) => {
|
const NavBar = (props) => {
|
||||||
return (
|
return (
|
||||||
<Navbar isBordered variant={"sticky"}>
|
<Navbar isBordered variant={"sticky"}>
|
||||||
<Navbar.Brand>
|
<Navbar.Brand>
|
||||||
<Text b color="inherit">
|
<Text b color="inherit">
|
||||||
BeatSaber Overlay
|
BeatSaber Overlay
|
||||||
</Text>
|
</Text>
|
||||||
</Navbar.Brand>
|
</Navbar.Brand>
|
||||||
|
|
||||||
<Navbar.Content>
|
<Navbar.Content>
|
||||||
<Settings {...props}></Settings>
|
<Settings {...props}></Settings>
|
||||||
</Navbar.Content>
|
</Navbar.Content>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NavBar;
|
export default NavBar;
|
||||||
|
@ -61,13 +61,15 @@ export default function ScoreStats() {
|
|||||||
width={30}
|
width={30}
|
||||||
height={30}
|
height={30}
|
||||||
src="https://cdn.fascinated.cc/Hc1eD7QY.png"
|
src="https://cdn.fascinated.cc/Hc1eD7QY.png"
|
||||||
|
unoptimized
|
||||||
|
alt="BeatLeader logo"
|
||||||
></Image>
|
></Image>
|
||||||
<p
|
<p
|
||||||
style={{
|
style={{
|
||||||
marginLeft: "5px",
|
marginLeft: "5px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{scoreSaberPP.toFixed(0)}pp
|
{scoreSaberPP.pp.toFixed(0)}pp
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
@ -82,6 +84,8 @@ export default function ScoreStats() {
|
|||||||
width={30}
|
width={30}
|
||||||
height={30}
|
height={30}
|
||||||
src="https://cdn.fascinated.cc/Wo9JRAfD.png"
|
src="https://cdn.fascinated.cc/Wo9JRAfD.png"
|
||||||
|
unoptimized
|
||||||
|
alt="BeatLeader logo"
|
||||||
></Image>
|
></Image>
|
||||||
<div>
|
<div>
|
||||||
{Object.entries(beatLeaderPP).map((value, i) => {
|
{Object.entries(beatLeaderPP).map((value, i) => {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Avatar as NextAvatar,
|
|
||||||
changeTheme,
|
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
Avatar as NextAvatar,
|
||||||
Text,
|
Text,
|
||||||
|
changeTheme,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@nextui-org/react";
|
} from "@nextui-org/react";
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ const Avatar = (props) => {
|
|||||||
as="button"
|
as="button"
|
||||||
color="primary"
|
color="primary"
|
||||||
src={avatarUrl}
|
src={avatarUrl}
|
||||||
|
alt="Account avatar"
|
||||||
/>
|
/>
|
||||||
</Dropdown.Trigger>
|
</Dropdown.Trigger>
|
||||||
<Dropdown.Menu aria-label="Static Actions">
|
<Dropdown.Menu aria-label="Static Actions">
|
||||||
|
@ -5,17 +5,17 @@ import { BeatSaverMapData } from "../types/BeatSaverMapData";
|
|||||||
import { getValue, setValue, valueExists } from "../utils/redisUtils";
|
import { getValue, setValue, valueExists } from "../utils/redisUtils";
|
||||||
|
|
||||||
const BEATSAVER_MAP_API =
|
const BEATSAVER_MAP_API =
|
||||||
env(VARS.HTTP_PROXY) + "/https://api.beatsaver.com/maps/hash/%s";
|
env(VARS.HTTP_PROXY) + "/https://api.beatsaver.com/maps/hash/%s";
|
||||||
|
|
||||||
const KEY = "BS_MAP_DATA_";
|
const KEY = "BS_MAP_DATA_";
|
||||||
|
|
||||||
function getLatestMapArt(data: BeatSaverMapData) {
|
function getLatestMapArt(data: BeatSaverMapData) {
|
||||||
return data.versions[data.versions.length - 1].coverURL;
|
return data.versions[data.versions.length - 1].coverURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
type MapData = {
|
type MapData = {
|
||||||
bsr: string;
|
bsr: string;
|
||||||
mapArt: string | undefined;
|
mapArt: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,34 +25,30 @@ type MapData = {
|
|||||||
* @returns The map data
|
* @returns The map data
|
||||||
*/
|
*/
|
||||||
export async function getMapData(hash: string): Promise<MapData | undefined> {
|
export async function getMapData(hash: string): Promise<MapData | undefined> {
|
||||||
const mapHash = hash.replace("custom_level_", "").toLowerCase();
|
const mapHash = hash.replace("custom_level_", "").toLowerCase();
|
||||||
|
|
||||||
const key = `${KEY}${mapHash}`;
|
const key = `${KEY}${mapHash}`;
|
||||||
const exists = await valueExists(key);
|
const exists = await valueExists(key);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
const data = await getValue(key);
|
const data = await getValue(key);
|
||||||
return JSON.parse(data);
|
return JSON.parse(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const before = Date.now();
|
const before = Date.now();
|
||||||
const response = await axios.get(BEATSAVER_MAP_API.replace("%s", mapHash), {
|
const response = await axios.get(BEATSAVER_MAP_API.replace("%s", mapHash));
|
||||||
headers: {
|
if (response.status === 404) {
|
||||||
"X-Requested-With": "BeatSaber Overlay",
|
return undefined;
|
||||||
},
|
}
|
||||||
});
|
const jsonResponse = response.data;
|
||||||
if (response.status === 404) {
|
const json = {
|
||||||
return undefined;
|
bsr: jsonResponse.id,
|
||||||
}
|
mapArt: getLatestMapArt(jsonResponse),
|
||||||
const jsonResponse = response.data;
|
};
|
||||||
const json = {
|
await setValue(key, JSON.stringify(json), 86400 * 7); // Expire in a week
|
||||||
bsr: jsonResponse.id,
|
console.log(
|
||||||
mapArt: getLatestMapArt(jsonResponse),
|
`[Cache]: Cached BS Map Data for hash ${mapHash} in ${
|
||||||
};
|
Date.now() - before
|
||||||
await setValue(key, JSON.stringify(json), 86400 * 7); // Expire in a week
|
}ms`
|
||||||
console.log(
|
);
|
||||||
`[Cache]: Cached BS Map Data for hash ${mapHash} in ${
|
return json;
|
||||||
Date.now() - before
|
|
||||||
}ms`
|
|
||||||
);
|
|
||||||
return json;
|
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,8 @@ import axios from "axios";
|
|||||||
import LeaderboardType from "../../consts/LeaderboardType";
|
import LeaderboardType from "../../consts/LeaderboardType";
|
||||||
|
|
||||||
export async function getPlayerData(leaderboardType, playerId) {
|
export async function getPlayerData(leaderboardType, playerId) {
|
||||||
const data = await axios.get(
|
const data = await axios.get(
|
||||||
LeaderboardType[leaderboardType].ApiUrl.PlayerData.replace("%s", playerId),
|
LeaderboardType[leaderboardType].ApiUrl.PlayerData.replace("%s", playerId)
|
||||||
{
|
);
|
||||||
headers: {
|
return data;
|
||||||
"x-requested-with": "BeatSaber Overlay",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
@ -6,57 +6,56 @@ import { VARS } from "../consts/EnvVars";
|
|||||||
import "../styles/globals.css";
|
import "../styles/globals.css";
|
||||||
|
|
||||||
const lightTheme = createTheme({
|
const lightTheme = createTheme({
|
||||||
type: "light",
|
type: "light",
|
||||||
theme: {
|
theme: {
|
||||||
colors: {},
|
colors: {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const darkTheme = createTheme({
|
const darkTheme = createTheme({
|
||||||
type: "dark",
|
type: "dark",
|
||||||
theme: {
|
theme: {
|
||||||
colors: {},
|
colors: {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }) {
|
function MyApp({ Component, pageProps }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DefaultSeo
|
<DefaultSeo
|
||||||
titleTemplate={`${env(VARS.SITE_NAME)} - %s`}
|
titleTemplate={`${env(VARS.SITE_NAME)} - %s`}
|
||||||
title={env(VARS.SITE_TITLE)}
|
title={env(VARS.SITE_TITLE)}
|
||||||
description={env(VARS.SITE_DESCRIPTION)}
|
description={env(VARS.SITE_DESCRIPTION)}
|
||||||
openGraph={{
|
openGraph={{
|
||||||
url: env(VARS.SITE_URL),
|
url: env(VARS.SITE_URL),
|
||||||
title: env(VARS.SITE_NAME),
|
title: env(VARS.SITE_NAME),
|
||||||
description: env(VARS.SITE_DESCRIPTION),
|
description: env(VARS.SITE_DESCRIPTION),
|
||||||
siteName: env(VARS.SITE_NAME),
|
siteName: env(VARS.SITE_NAME),
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: "https://git.fascinated.cc/Fascinated/beatsaber-overlay/media/branch/main/assets/overlay.png",
|
url: "https://git.fascinated.cc/Fascinated/beatsaber-overlay/media/branch/main/assets/overlay.png",
|
||||||
alt: "Site Example",
|
alt: "Site Example",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
twitter={{
|
twitter={{
|
||||||
cardType: "summary_large_image",
|
cardType: "summary_large_image",
|
||||||
// site: "@BeatSaber Overlay",
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
<NextThemesProvider
|
||||||
<NextThemesProvider
|
storageKey="theme"
|
||||||
storageKey="theme"
|
attribute="class"
|
||||||
attribute="class"
|
value={{
|
||||||
value={{
|
dark: darkTheme,
|
||||||
dark: darkTheme,
|
light: lightTheme,
|
||||||
light: lightTheme,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<NextUIProvider>
|
||||||
<NextUIProvider>
|
<Component {...pageProps} />
|
||||||
<Component {...pageProps} />
|
</NextUIProvider>
|
||||||
</NextUIProvider>
|
</NextThemesProvider>
|
||||||
</NextThemesProvider>
|
</>
|
||||||
</>
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MyApp;
|
export default MyApp;
|
||||||
|
@ -11,97 +11,92 @@ const KEY = "BL_MAP_DATA_";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
if (!req.query.hash || !req.query.difficulty || !req.query.characteristic) {
|
if (!req.query.hash || !req.query.difficulty || !req.query.characteristic) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
status: 404,
|
status: 404,
|
||||||
message: "Invalid request",
|
message: "Invalid request",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const mapHash = req.query.hash.replace("custom_level_", "").toLowerCase();
|
const mapHash = req.query.hash.replace("custom_level_", "").toLowerCase();
|
||||||
const difficulty = req.query.difficulty.replace(" ", "");
|
const difficulty = req.query.difficulty.replace(" ", "");
|
||||||
const characteristic = req.query.characteristic;
|
const characteristic = req.query.characteristic;
|
||||||
|
|
||||||
const key = `${KEY}${difficulty}-${characteristic}-${mapHash}`;
|
const key = `${KEY}${difficulty}-${characteristic}-${mapHash}`;
|
||||||
const exists = await valueExists(key);
|
const exists = await valueExists(key);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
const data = await getValue(key);
|
const data = await getValue(key);
|
||||||
const json = JSON.parse(data);
|
const json = JSON.parse(data);
|
||||||
res.setHeader("Cache-Status", "hit");
|
res.setHeader("Cache-Status", "hit");
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
status: "OK",
|
status: "OK",
|
||||||
difficulty: difficulty,
|
difficulty: difficulty,
|
||||||
stars: json.stars,
|
stars: json.stars,
|
||||||
modifiers: json.modifiers,
|
modifiers: json.modifiers,
|
||||||
passRating: json.passRating,
|
passRating: json.passRating,
|
||||||
accRating: json.accRating,
|
accRating: json.accRating,
|
||||||
techRating: json.techRating,
|
techRating: json.techRating,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const before = Date.now();
|
const before = Date.now();
|
||||||
const reesponse = await axios.get(
|
const reesponse = await axios.get(
|
||||||
WebsiteTypes.BeatLeader.ApiUrl.MapData.replace("%h", mapHash),
|
WebsiteTypes.BeatLeader.ApiUrl.MapData.replace("%h", mapHash)
|
||||||
{
|
);
|
||||||
headers: {
|
if (reesponse.status === 404) {
|
||||||
"X-Requested-With": "BeatSaber Overlay",
|
return res.status(404).json({
|
||||||
},
|
status: 404,
|
||||||
}
|
message: "Unknown Map Hash",
|
||||||
);
|
});
|
||||||
if (reesponse.status === 404) {
|
}
|
||||||
return res.status(404).json({
|
const json = reesponse.data;
|
||||||
status: 404,
|
let starCount = undefined;
|
||||||
message: "Unknown Map Hash",
|
let modifiers = undefined;
|
||||||
});
|
let passRating = undefined;
|
||||||
}
|
let accRating = undefined;
|
||||||
const json = reesponse.data;
|
let techRating = undefined;
|
||||||
let starCount = undefined;
|
|
||||||
let modifiers = undefined;
|
|
||||||
let passRating = undefined;
|
|
||||||
let accRating = undefined;
|
|
||||||
let techRating = undefined;
|
|
||||||
|
|
||||||
for (const diff of json.difficulties) {
|
for (const diff of json.difficulties) {
|
||||||
if (
|
if (
|
||||||
diff.difficultyName === difficulty &&
|
diff.difficultyName === difficulty &&
|
||||||
diff.modeName === characteristic
|
diff.modeName === characteristic
|
||||||
) {
|
) {
|
||||||
starCount = diff.stars;
|
starCount = diff.stars;
|
||||||
modifiers = diff.modifierValues;
|
modifiers = diff.modifierValues;
|
||||||
passRating = diff.passRating;
|
passRating = diff.passRating;
|
||||||
accRating = diff.accRating;
|
accRating = diff.accRating;
|
||||||
techRating = diff.techRating;
|
techRating = diff.techRating;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (starCount === undefined) {
|
if (starCount === undefined) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
status: 404,
|
status: 404,
|
||||||
message: "Unknown Map Hash",
|
message: "Unknown Map Hash",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await setValue(
|
await setValue(
|
||||||
key,
|
key,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
stars: starCount,
|
stars: starCount,
|
||||||
modifiers: modifiers,
|
modifiers: modifiers,
|
||||||
passRating: passRating,
|
passRating: passRating,
|
||||||
accRating: accRating,
|
accRating: accRating,
|
||||||
techRating: techRating,
|
techRating: techRating,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
console.log(
|
console.log(
|
||||||
`[Cache]: Cached BL Star Count for hash ${mapHash} in ${
|
`[Cache]: Cached BL Star Count for hash ${mapHash} in ${
|
||||||
Date.now() - before
|
Date.now() - before
|
||||||
}ms`
|
}ms`
|
||||||
);
|
);
|
||||||
res.setHeader("Cache-Status", "miss");
|
res.setHeader("Cache-Status", "miss");
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
status: "OK",
|
status: "OK",
|
||||||
difficulty: difficulty,
|
difficulty: difficulty,
|
||||||
stars: starCount,
|
stars: starCount,
|
||||||
modifiers: modifiers,
|
modifiers: modifiers,
|
||||||
passRating: passRating,
|
passRating: passRating,
|
||||||
accRating: accRating,
|
accRating: accRating,
|
||||||
techRating: techRating,
|
techRating: techRating,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -12,65 +12,60 @@ const KEY = "SS_MAP_STAR_";
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
if (!req.query.hash) {
|
if (!req.query.hash) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
status: 404,
|
status: 404,
|
||||||
message: "Invalid request",
|
message: "Invalid request",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const mapHash = req.query.hash.replace("custom_level_", "").toLowerCase();
|
const mapHash = req.query.hash.replace("custom_level_", "").toLowerCase();
|
||||||
const difficulty = req.query.difficulty.replace(" ", "");
|
const difficulty = req.query.difficulty.replace(" ", "");
|
||||||
const characteristic = req.query.characteristic;
|
const characteristic = req.query.characteristic;
|
||||||
|
|
||||||
const key = `${KEY}${difficulty}-${characteristic}-${mapHash}`;
|
const key = `${KEY}${difficulty}-${characteristic}-${mapHash}`;
|
||||||
const exists = await valueExists(key);
|
const exists = await valueExists(key);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
const data = await getValue(key);
|
const data = await getValue(key);
|
||||||
res.setHeader("Cache-Status", "hit");
|
res.setHeader("Cache-Status", "hit");
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
status: "OK",
|
status: "OK",
|
||||||
difficulty: difficulty,
|
difficulty: difficulty,
|
||||||
stars: Number.parseFloat(data),
|
stars: Number.parseFloat(data),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const before = Date.now();
|
const before = Date.now();
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
WebsiteTypes.ScoreSaber.ApiUrl.MapData.replace("%h", mapHash).replace(
|
WebsiteTypes.ScoreSaber.ApiUrl.MapData.replace("%h", mapHash).replace(
|
||||||
"%d",
|
"%d",
|
||||||
diffToScoreSaberDiff(difficulty)
|
diffToScoreSaberDiff(difficulty)
|
||||||
),
|
)
|
||||||
{
|
);
|
||||||
headers: {
|
if (response.status === 404) {
|
||||||
"X-Requested-With": "BeatSaber Overlay",
|
return res.status(404).json({
|
||||||
},
|
status: 404,
|
||||||
}
|
message: "Unknown Map Hash",
|
||||||
);
|
});
|
||||||
if (response.status === 404) {
|
}
|
||||||
return res.status(404).json({
|
const json = response.data;
|
||||||
status: 404,
|
let starCount = json.stars;
|
||||||
message: "Unknown Map Hash",
|
if (starCount === undefined) {
|
||||||
});
|
return res.status(404).json({
|
||||||
}
|
status: 404,
|
||||||
const json = response.data;
|
message: "Unknown Map Hash",
|
||||||
let starCount = json.stars;
|
});
|
||||||
if (starCount === undefined) {
|
}
|
||||||
return res.status(404).json({
|
await setValue(key, starCount);
|
||||||
status: 404,
|
console.log(
|
||||||
message: "Unknown Map Hash",
|
`[Cache]: Cached SS Star Count for hash ${mapHash} in ${
|
||||||
});
|
Date.now() - before
|
||||||
}
|
}ms`
|
||||||
await setValue(key, starCount);
|
);
|
||||||
console.log(
|
res.setHeader("Cache-Status", "miss");
|
||||||
`[Cache]: Cached SS Star Count for hash ${mapHash} in ${
|
return res.status(200).json({
|
||||||
Date.now() - before
|
status: "OK",
|
||||||
}ms`
|
difficulty: difficulty,
|
||||||
);
|
stars: starCount,
|
||||||
res.setHeader("Cache-Status", "miss");
|
});
|
||||||
return res.status(200).json({
|
|
||||||
status: "OK",
|
|
||||||
difficulty: difficulty,
|
|
||||||
stars: starCount,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,6 @@ export default class Home extends Component {
|
|||||||
<Grid xs={12} lg={9}>
|
<Grid xs={12} lg={9}>
|
||||||
<Card>
|
<Card>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Spacer y={1.2} />
|
|
||||||
<Text>
|
<Text>
|
||||||
How to use{" "}
|
How to use{" "}
|
||||||
<span>
|
<span>
|
||||||
@ -360,9 +359,9 @@ export default class Home extends Component {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Link href="https://git.fascinated.cc/Fascinated/beatsaber-overlay">
|
<Link href="https://git.fascinated.cc/Fascinated/beatsaber-overlay">
|
||||||
<a>
|
<a href="https://git.fascinated.cc/Fascinated/beatsaber-overlay">
|
||||||
If you like this project and want to support it. Come
|
If you like this project and want to help me make it
|
||||||
check out the project on Gitea!
|
better, come check out the project on Gitea!
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -12,71 +12,86 @@ import { usePlayerDataStore } from "../store/playerDataStore";
|
|||||||
import styles from "../styles/overlay.module.css";
|
import styles from "../styles/overlay.module.css";
|
||||||
|
|
||||||
export default function Overlay(props) {
|
export default function Overlay(props) {
|
||||||
const query = JSON.parse(props.query);
|
const query = JSON.parse(props.query);
|
||||||
const [setOverlaySettings, mounted, setMounted] = useSettingsStore(
|
const [setOverlaySettings, mounted, setMounted] = useSettingsStore(
|
||||||
(state) => [state.setOverlaySettings, state.mounted, state.setMounted]
|
(state) => [state.setOverlaySettings, state.mounted, state.setMounted]
|
||||||
);
|
);
|
||||||
const updatePlayerData = usePlayerDataStore(
|
const updatePlayerData = usePlayerDataStore(
|
||||||
(state) => state.updatePlayerData
|
(state) => state.updatePlayerData
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!mounted && props.isValidSteamId) {
|
if (!mounted && props.isValidSteamId) {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
|
|
||||||
async function setup() {
|
async function setup() {
|
||||||
await setOverlaySettings(query);
|
await setOverlaySettings(query);
|
||||||
const showSongInfo = useSettingsStore.getState().showSongInfo;
|
const showSongInfo = useSettingsStore.getState().showSongInfo;
|
||||||
const showScoreInfo = useSettingsStore.getState().showScoreInfo;
|
const showScoreInfo = useSettingsStore.getState().showScoreInfo;
|
||||||
if (showSongInfo || (showScoreInfo && typeof window !== "undefined")) {
|
if (showSongInfo || (showScoreInfo && typeof window !== "undefined")) {
|
||||||
await connectClient();
|
await connectClient();
|
||||||
}
|
}
|
||||||
const showPlayerStats = useSettingsStore.getState().showPlayerStats;
|
const showPlayerStats = useSettingsStore.getState().showPlayerStats;
|
||||||
if (showPlayerStats) {
|
if (showPlayerStats) {
|
||||||
await updatePlayerData();
|
await updatePlayerData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setup();
|
setup();
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
query,
|
query,
|
||||||
props.isValidSteamId,
|
props.isValidSteamId,
|
||||||
setOverlaySettings,
|
setOverlaySettings,
|
||||||
mounted,
|
mounted,
|
||||||
setMounted,
|
setMounted,
|
||||||
updatePlayerData,
|
updatePlayerData,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!props.isValidSteamId) {
|
if (!props.isValidSteamId) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.invalidPlayer}>
|
<div className={styles.invalidPlayer}>
|
||||||
<h1>Invalid Steam ID</h1>
|
<h1>Invalid Steam ID</h1>
|
||||||
<h3>Please check the id field in the url</h3>
|
<h3>Please check the id field in the url</h3>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.main}>
|
<div className={styles.main}>
|
||||||
<NextSeo title="Overlay" />
|
<NextSeo title="Overlay" />
|
||||||
<PlayerStats />
|
<PlayerStats />
|
||||||
<SongInfo />
|
<SongInfo />
|
||||||
<ScoreStats />
|
<ScoreStats />
|
||||||
<CutStats />
|
<CutStats />
|
||||||
</div>
|
|
||||||
);
|
<p
|
||||||
|
style={{
|
||||||
|
fontSize: 50,
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
paddingRight: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
This overlay is deprecated use:{" "}
|
||||||
|
<a href="https://ssr.fascinated.cc/overlay/builder">
|
||||||
|
https://ssr.fascinated.cc/overlay/builder
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getServerSideProps(context) {
|
export async function getServerSideProps(context) {
|
||||||
const steamId = context.query.id;
|
const steamId = context.query.id;
|
||||||
const steamIdResponse = await axios.get(
|
const steamIdResponse = await axios.get(
|
||||||
`${process.env.REACT_APP_SITE_URL}/api/validateid?steamid=${steamId}`
|
`${process.env.REACT_APP_SITE_URL}/api/validateid?steamid=${steamId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
isValidSteamId: steamIdResponse.data.message === "Valid",
|
isValidSteamId: steamIdResponse.data.message === "Valid",
|
||||||
query: JSON.stringify(context.query),
|
query: JSON.stringify(context.query),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,52 +4,48 @@ import Utils from "../utils/utils";
|
|||||||
import { useSettingsStore } from "./overlaySettingsStore";
|
import { useSettingsStore } from "./overlaySettingsStore";
|
||||||
|
|
||||||
interface PlayerDataState {
|
interface PlayerDataState {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
id: string;
|
id: string;
|
||||||
pp: number;
|
pp: number;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
globalPos: number;
|
globalPos: number;
|
||||||
countryRank: number;
|
countryRank: number;
|
||||||
country: string;
|
country: string;
|
||||||
updatePlayerData: () => void;
|
updatePlayerData: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usePlayerDataStore = create<PlayerDataState>()((set) => ({
|
export const usePlayerDataStore = create<PlayerDataState>()((set) => ({
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
id: "",
|
id: "",
|
||||||
pp: 0,
|
pp: 0,
|
||||||
avatar: "",
|
avatar: "",
|
||||||
globalPos: 0,
|
globalPos: 0,
|
||||||
countryRank: 0,
|
countryRank: 0,
|
||||||
country: "",
|
country: "",
|
||||||
|
|
||||||
updatePlayerData: async () => {
|
updatePlayerData: async () => {
|
||||||
const leaderboardType = useSettingsStore.getState().leaderboardType;
|
const leaderboardType = useSettingsStore.getState().leaderboardType;
|
||||||
const playerId = useSettingsStore.getState().id;
|
const playerId = useSettingsStore.getState().id;
|
||||||
|
|
||||||
const apiUrl = Utils.getWebsiteApi(
|
const apiUrl = Utils.getWebsiteApi(
|
||||||
leaderboardType
|
leaderboardType
|
||||||
).ApiUrl.PlayerData.replace("%s", playerId);
|
).ApiUrl.PlayerData.replace("%s", playerId);
|
||||||
const response = await axios.get(apiUrl, {
|
const response = await axios.get(apiUrl);
|
||||||
headers: {
|
if (response.status !== 200) {
|
||||||
"x-requested-with": "BeatSaber Overlay",
|
return;
|
||||||
},
|
}
|
||||||
});
|
const data = response.data;
|
||||||
if (response.status !== 200) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data = response.data;
|
|
||||||
|
|
||||||
console.log("Updated player data");
|
console.log("Updated player data");
|
||||||
|
|
||||||
set(() => ({
|
set(() => ({
|
||||||
id: playerId,
|
id: playerId,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
pp: data.pp,
|
pp: data.pp,
|
||||||
avatar: data.avatar || data.profilePicture,
|
avatar: data.avatar || data.profilePicture,
|
||||||
globalPos: data.rank,
|
globalPos: data.rank,
|
||||||
countryRank: data.countryRank,
|
countryRank: data.countryRank,
|
||||||
country: data.country,
|
country: data.country,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -154,6 +154,8 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
|
|||||||
}
|
}
|
||||||
const { bsr, mapArt } = mapDataresponse.data.data;
|
const { bsr, mapArt } = mapDataresponse.data.data;
|
||||||
|
|
||||||
|
console.log(scoreSaberLeaderboardData);
|
||||||
|
|
||||||
set({
|
set({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
hasError: hasError,
|
hasError: hasError,
|
||||||
|
@ -5,134 +5,130 @@ import { getScoreSaberPP } from "../curve/ScoreSaberCurve";
|
|||||||
import { useSongDataStore } from "../store/songDataStore";
|
import { useSongDataStore } from "../store/songDataStore";
|
||||||
|
|
||||||
export default class Utils {
|
export default class Utils {
|
||||||
/**
|
/**
|
||||||
* Returns the information for the given website type.
|
* Returns the information for the given website type.
|
||||||
*
|
*
|
||||||
* @param {string} website
|
* @param {string} website
|
||||||
* @returns The website type's information.
|
* @returns The website type's information.
|
||||||
*/
|
*/
|
||||||
static getWebsiteApi(website) {
|
static getWebsiteApi(website) {
|
||||||
return LeaderboardType[website];
|
return LeaderboardType[website];
|
||||||
}
|
}
|
||||||
|
|
||||||
static openInNewTab(url) {
|
static openInNewTab(url) {
|
||||||
window.open(url, "_blank");
|
window.open(url, "_blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
static async isLeaderboardValid(url, steamId) {
|
static async isLeaderboardValid(url, steamId) {
|
||||||
const response = await axios.get(url.replace("%s", steamId), {
|
const response = await axios.get(url.replace("%s", steamId));
|
||||||
headers: {
|
if (response.status === 429) {
|
||||||
"X-Requested-With": "BeatSaber Overlay",
|
return true; // Just assume it's true is we are rate limited
|
||||||
},
|
}
|
||||||
});
|
const json = response.data;
|
||||||
if (response.status === 429) {
|
return !!json.pp;
|
||||||
return true; // Just assume it's true is we are rate limited
|
}
|
||||||
}
|
|
||||||
const json = response.data;
|
|
||||||
return !!json.pp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static calculatePP(stars, acc, type) {
|
static calculatePP(stars, acc, type) {
|
||||||
if (stars <= 0) {
|
if (stars <= 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (type === "BeatLeader") {
|
if (type === "BeatLeader") {
|
||||||
const leaderboardData =
|
const leaderboardData =
|
||||||
useSongDataStore.getState().mapLeaderboardData.beatLeader;
|
useSongDataStore.getState().mapLeaderboardData.beatLeader;
|
||||||
|
|
||||||
return getBeatLeaderPP(
|
return getBeatLeaderPP(
|
||||||
acc,
|
acc,
|
||||||
leaderboardData.accRating,
|
leaderboardData.accRating,
|
||||||
leaderboardData.passRating,
|
leaderboardData.passRating,
|
||||||
leaderboardData.techRating
|
leaderboardData.techRating
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (type === "ScoreSaber") {
|
if (type === "ScoreSaber") {
|
||||||
return getScoreSaberPP(acc, stars);
|
return getScoreSaberPP(acc, stars);
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
static calculateModifierBonus() {
|
static calculateModifierBonus() {
|
||||||
const songMods = useSongDataStore.getState().songModifiers;
|
const songMods = useSongDataStore.getState().songModifiers;
|
||||||
const modifierMulipliers =
|
const modifierMulipliers =
|
||||||
useSongDataStore.getState().mapLeaderboardData.beatLeader.modifiers;
|
useSongDataStore.getState().mapLeaderboardData.beatLeader.modifiers;
|
||||||
let bonus = 1;
|
let bonus = 1;
|
||||||
|
|
||||||
// No Fail
|
// No Fail
|
||||||
if (
|
if (
|
||||||
songMods.noFail == true &&
|
songMods.noFail == true &&
|
||||||
modifierMulipliers.nf < 0 &&
|
modifierMulipliers.nf < 0 &&
|
||||||
useSongDataStore.getState().failed
|
useSongDataStore.getState().failed
|
||||||
) {
|
) {
|
||||||
bonus -= modifierMulipliers.nf;
|
bonus -= modifierMulipliers.nf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Speed Modifiers
|
// Speed Modifiers
|
||||||
if (songMods.songSpeed != "Normal") {
|
if (songMods.songSpeed != "Normal") {
|
||||||
if (songMods.songSpeed == "SuperSlow" && modifierMulipliers.ss > 0) {
|
if (songMods.songSpeed == "SuperSlow" && modifierMulipliers.ss > 0) {
|
||||||
bonus -= modifierMulipliers.ss;
|
bonus -= modifierMulipliers.ss;
|
||||||
}
|
}
|
||||||
if (songMods.songSpeed == "Faster" && modifierMulipliers.fs > 0) {
|
if (songMods.songSpeed == "Faster" && modifierMulipliers.fs > 0) {
|
||||||
bonus += modifierMulipliers.fs;
|
bonus += modifierMulipliers.fs;
|
||||||
}
|
}
|
||||||
if (songMods.songSpeed == "SuperFast" && modifierMulipliers.sf > 0) {
|
if (songMods.songSpeed == "SuperFast" && modifierMulipliers.sf > 0) {
|
||||||
bonus += modifierMulipliers.sf;
|
bonus += modifierMulipliers.sf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disappearing Arrows
|
// Disappearing Arrows
|
||||||
if (songMods.disappearingArrows == true && modifierMulipliers.da > 0) {
|
if (songMods.disappearingArrows == true && modifierMulipliers.da > 0) {
|
||||||
bonus += modifierMulipliers.da;
|
bonus += modifierMulipliers.da;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ghost Notes
|
// Ghost Notes
|
||||||
if (songMods.ghostNotes == true && modifierMulipliers.gn > 0) {
|
if (songMods.ghostNotes == true && modifierMulipliers.gn > 0) {
|
||||||
toAdd += modifierMulipliers.gn;
|
toAdd += modifierMulipliers.gn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No Arrows
|
// No Arrows
|
||||||
if (songMods.noArrows == true && modifierMulipliers.na < 0) {
|
if (songMods.noArrows == true && modifierMulipliers.na < 0) {
|
||||||
bonus -= modifierMulipliers.na;
|
bonus -= modifierMulipliers.na;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No Bombs
|
// No Bombs
|
||||||
if (songMods.noBombs == true && modifierMulipliers.nb < 0) {
|
if (songMods.noBombs == true && modifierMulipliers.nb < 0) {
|
||||||
bonus -= modifierMulipliers.nb;
|
bonus -= modifierMulipliers.nb;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No Obstacles
|
// No Obstacles
|
||||||
if (songMods.obstacles == false && modifierMulipliers.no < 0) {
|
if (songMods.obstacles == false && modifierMulipliers.no < 0) {
|
||||||
bonus -= modifierMulipliers.no;
|
bonus -= modifierMulipliers.no;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bonus;
|
return bonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
static base64ToArrayBuffer(base64) {
|
static base64ToArrayBuffer(base64) {
|
||||||
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
static stringToBoolean = (stringValue) => {
|
static stringToBoolean = (stringValue) => {
|
||||||
switch (stringValue?.toLowerCase()?.trim()) {
|
switch (stringValue?.toLowerCase()?.trim()) {
|
||||||
case "true":
|
case "true":
|
||||||
case "yes":
|
case "yes":
|
||||||
case "1":
|
case "1":
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case "false":
|
case "false":
|
||||||
case "no":
|
case "no":
|
||||||
case "0":
|
case "0":
|
||||||
case null:
|
case null:
|
||||||
case undefined:
|
case undefined:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return JSON.parse(stringValue);
|
return JSON.parse(stringValue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static capitalizeFirstLetter(string) {
|
static capitalizeFirstLetter(string) {
|
||||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user