Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
hedgeho committed Nov 12, 2023
2 parents 63a119c + 485dd82 commit ec529cc
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 43 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ DTW requires one-dimensional data, so we split up the data into multiple one-dim
### Architecture
![architecture.png](architecture.png)

The architecture of the product is split up into multiple parts. Two backend servers and one frontend. The decoupling of the backends was done to be able to use the latest Computer Vision models ([YOLOv8](https://github.com/ultralytics/ultralytics)), which are only easily available through Python clients while utilizing the speed of a Go-based REST API backend for all other requests.
The front end is implemented using React (Vite).


Expand Down
2 changes: 1 addition & 1 deletion dance-app/src/Loader.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
.wave {
width: 5px;
height: 100px;
background: linear-gradient(45deg, red, #fff);
background: linear-gradient(45deg, #121640, #01bfc8);
margin: 10px;
animation: wave 1s linear infinite;
border-radius: 20px;
Expand Down
4 changes: 2 additions & 2 deletions dance-app/src/Submit.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
}

.submit-container .center {
height: 20px;
height: 40px;
}

.submit-container .wave {
height: 20px;
height: 40px;
}
29 changes: 17 additions & 12 deletions dance-app/src/Submit.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { FC, useState } from "react";
import PocketBase from "pocketbase";
import { useState } from "react";
import { Recording } from "react-record-webcam/dist/useRecording";
import "./Submit.css";
import Loader from "./components/Loader";
import { Button } from "@mui/material";
import { Button, Typography } from "@mui/material";
import pb from "./pocketBase";

interface SubmitProps {
tier: number;
video: Recording;
pb: PocketBase;
setConfetti: (c: boolean) => void;
onSubmit?: (id: string) => void;
}

const Submit: FC<SubmitProps> = ({ tier, video, pb, setConfetti }) => {
const Submit = ({ tier, video, onSubmit }: SubmitProps) => {
const [isLoading, setIsLoading] = useState(false);

const upload = async () => {
Expand All @@ -28,20 +27,26 @@ const Submit: FC<SubmitProps> = ({ tier, video, pb, setConfetti }) => {
formData.append("tier", tier.toString());
formData.append("try", "1");
formData.append("score", "-1");
await pb.collection("videos").create(formData);
setTimeout(() => setIsLoading((state) => !state), 1000);
console.log("confetti on");
setConfetti(true);

const videoRecord = await pb.collection("videos").create(formData);
onSubmit?.(videoRecord.id);
setIsLoading(false);
}
};

return (
<div className="submit-container">
<Button
onClick={upload}
sx={{ backgroundColor: "rgba(1, 191, 200, 0.2)", borderRadius: "15px" }}
sx={{
backgroundColor: "rgba(1, 191, 200, 0.4)",
borderRadius: "15px",
width: "20vw",
height: "10vh",
fontSize: "20px",
}}
>
{isLoading ? <Loader /> : "Analyze"}
{isLoading ? <Loader /> : <Typography>Submit</Typography>}
</Button>
</div>
);
Expand Down
3 changes: 2 additions & 1 deletion dance-app/src/components/Leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Loader from "./Loader";
import "../Leaderboard.css";
import { Card, CardContent, CardMedia, Stack, Typography } from "@mui/material";
import pb from "../pocketBase";
import { formatScore } from "../sections/MainScore";

interface VideoData {
id: string;
Expand Down Expand Up @@ -67,7 +68,7 @@ function Leaderboard() {
<Typography variant="body2">
{expand?.user?.name}
{": "}
<b>{score}</b>
<b>{formatScore(score)}</b>
</Typography>
</CardContent>
</Card>
Expand Down
37 changes: 37 additions & 0 deletions dance-app/src/hooks/useSubscribe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useEffect, useRef, useState } from "react";
import pb from "../pocketBase";
import { RecordModel, UnsubscribeFunc } from "pocketbase";

export const useSubscribe = (collection: string) => {
const [id, setId] = useState<string | null>(null);
const [data, setData] = useState<RecordModel | null>(null);
const [subscribed, setSubscribed] = useState(false);
const unsubscribeRef = useRef<UnsubscribeFunc>();

useEffect(() => {
if (id !== null) {
pb.collection(collection)
.subscribe(id, (event) => {
setData(event.record);
})
.then((unsub) => {
console.log("sub", id);
setSubscribed(true);
unsubscribeRef.current = unsub;
});
}

return () => {
console.log("unsub", id);
unsubscribeRef?.current?.();
setSubscribed(false);
};
}, [id, collection]);

const subscribe = (id: string) => setId(id);
const unsubscribe = () => {
unsubscribeRef?.current?.();
};

return { data, subscribe, unsubscribe, subscribed };
};
32 changes: 32 additions & 0 deletions dance-app/src/sections/MainScore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Box, useTheme } from "@mui/material";
import Loader from "../components/Loader";

const formatter = Intl.NumberFormat("en", {
maximumFractionDigits: 2,
});

// eslint-disable-next-line
export const formatScore = (score?: number | null) =>
score ? formatter.format(score) : score;

const MainScore = ({
score,
subscribed,
}: {
score: number | null | undefined;
subscribed: boolean;
}) => {
const theme = useTheme();
if (!score && !subscribed) return null;

return (
<Box
className="submit-container"
sx={{ fontSize: "4rem", color: theme.palette.success.main }}
>
{subscribed && (!score || score === -1) ? <Loader /> : formatScore(score)}
</Box>
);
};

export default MainScore;
1 change: 0 additions & 1 deletion dance-app/src/sections/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const HomeSection = ({ setConfetti }: HomeSectionProps) => {
(video) => video.tier === selectedTier.toString(),
)}
tier={selectedTier}
pb={pb}
goBack={setSelectedTier}
setConfetti={setConfetti}
/>
Expand Down
72 changes: 46 additions & 26 deletions dance-app/src/sections/recording.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { useContext, useRef, useState } from "react";
import { useContext, useEffect, useRef, useState } from "react";
import { useRecordWebcam } from "react-record-webcam";
import { Recording } from "react-record-webcam/dist/useRecording";
import Leaderboard from "../components/Leaderboard";
import Submit from "../Submit";
import "../Recording.css";
import Pocketbase from "pocketbase";
import { PageContext } from "../context/PageContext";
import { Button, Typography, useTheme } from "@mui/material";
import { makeStyles } from "@mui/styles";
import { RefVideo, Tier } from "./home";
import { useSubscribe } from "../hooks/useSubscribe";
import MainScore from "./MainScore";

const useStyles = makeStyles(() => ({
backButton: {
Expand All @@ -32,15 +33,13 @@ const options = {
interface RecordingPageProps {
refVideo: RefVideo | undefined;
tier: number;
pb: Pocketbase;
goBack: (tier: Tier | null) => void;
setConfetti: (c: boolean) => void;
}

const RecordingPage = ({
refVideo,
tier,
pb,
goBack,
setConfetti,
}: RecordingPageProps) => {
Expand All @@ -66,6 +65,7 @@ const RecordingPage = ({
const page = useContext(PageContext);
const [showVideo, setShowVideo] = useState<VideoState>(VideoState.preview);
const [showCountDown, setShowCountDown] = useState<boolean>(false);
const { data, subscribe, unsubscribe, subscribed } = useSubscribe("videos");
const recordingRef = useRef<Recording | null>(null);
const hasRecording = recordingRef.current?.previewRef.current?.src;

Expand All @@ -81,29 +81,40 @@ const RecordingPage = ({
};

const countDown = () => {
console.log('countdown start')
setShowCountDown(true)
console.log("countdown start");
setShowCountDown(true);
setTimeout(() => {
record()
console.log('countdown end')
setShowCountDown(false)
}, 3000)
}
record();
console.log("countdown end");
setShowCountDown(false);
}, 3000);
};

const record = async () => {
console.log('record start')
console.log("record start");
const { current: recording } = recordingRef;
if (recording) {
setShowVideo(VideoState.recording);
await startRecording(recording.id);
setTimeout(async () => {
await stopRecording(recording.id);
setShowVideo(VideoState.preview);
console.log('record end')
console.log("record end");
}, 7000);
}
console.log(recordingRef.current);
};

useEffect(() => {
if (data?.score) {
setConfetti(true);
setTimeout(() => {
setConfetti(false);
}, 6000);
unsubscribe();
}
}, [data, unsubscribe, setConfetti]);

return (
<div className={`App ${page === "leaderboard" ? "view-change" : ""}`}>
<div className="view record-view">
Expand All @@ -123,17 +134,20 @@ const RecordingPage = ({
className={classes.recordButton}
sx={{ backgroundColor: "rgba(1, 191, 200, 0.1)" }}
>
<Typography variant="h2">
{recordingRef.current ? "Start" : "Get ready"}
</Typography>
<Typography variant="h2">
{recordingRef.current ? "Start" : "Get ready"}
</Typography>
</Button>
)}
</div>
{tier === 4 ? (
<audio autoPlay>
<source src="https://junctionb.nyman.dev/api/files/zl4ca9hay8p2v75/jfab2b6cif6dugz/crab_rave_UGU0Q9nZfl.mp3" type="audio/mp3" />
<source
src="https://junctionb.nyman.dev/api/files/zl4ca9hay8p2v75/jfab2b6cif6dugz/crab_rave_UGU0Q9nZfl.mp3"
type="audio/mp3"
/>
</audio>
): null}
) : null}
</header>
<div className="container">
{activeRecordings.map((recording) => (
Expand Down Expand Up @@ -169,14 +183,20 @@ const RecordingPage = ({
<Leaderboard />
</div>
<footer className="footer">
{recordingRef.current && hasRecording && page === "home" ? (
<Submit
tier={tier}
video={recordingRef.current}
pb={pb}
setConfetti={setConfetti}
/>
) : null}
{page === "home" &&
recordingRef.current &&
hasRecording &&
!subscribed &&
!data?.score && (
<Submit
tier={tier}
video={recordingRef.current}
onSubmit={subscribe}
/>
)}
{page === "home" && recordingRef.current && hasRecording && (
<MainScore score={data?.score} subscribed={subscribed} />
)}
</footer>
</div>
);
Expand Down

0 comments on commit ec529cc

Please sign in to comment.