diff --git a/web/.env b/web/.env index 96c129e..d6ec3a1 100644 --- a/web/.env +++ b/web/.env @@ -1,3 +1,7 @@ -# VITE_SONISORI_API_URL=https://api.sonisori.site -VITE_SONISORI_API_URL=/ -VITE_SONISORI_AI_API_URL=http://127.0.0.1:5002 \ No newline at end of file +# VITE_SONISORI_BFF_API_URL=https://api.sonisori.site +VITE_SONISORI_BFF_API_URL=/bff + +# VITE_SONISORI_AI_REST_URL=https://ai.sonisori.site +VITE_SONISORI_AI_REST_URL=/ai-rest + +VITE_SONISORI_AI_SOCKET_URL=wss://ai.sonisori.site \ No newline at end of file diff --git a/web/src/service/util/api.ts b/web/src/service/util/api.ts index 86bffe4..bbe10db 100644 --- a/web/src/service/util/api.ts +++ b/web/src/service/util/api.ts @@ -1,14 +1,14 @@ import ky from "ky"; export const client = ky.create({ - prefixUrl: import.meta.env.VITE_SONISORI_API_URL, + prefixUrl: import.meta.env.VITE_SONISORI_BFF_API_URL, throwHttpErrors: true, hooks: { afterResponse: [ async (request, options, response) => { if (response.status === 401) { await ky - .create({ prefixUrl: import.meta.env.VITE_SONISORI_API_URL }) + .create({ prefixUrl: import.meta.env.VITE_SONISORI_BFF_API_URL }) .get("api/reissue"); return ky(request, options); } diff --git a/web/src/ui/component/domain/SignDetector.tsx b/web/src/ui/component/domain/SignDetector.tsx index b969642..51a6878 100644 --- a/web/src/ui/component/domain/SignDetector.tsx +++ b/web/src/ui/component/domain/SignDetector.tsx @@ -5,6 +5,7 @@ import { } from "@mediapipe/tasks-vision"; import { createEventListener } from "@solid-primitives/event-listener"; import { createPresence } from "@solid-primitives/presence"; +import { throttle } from "@solid-primitives/scheduled"; import ky from "ky"; import { io, Socket } from "socket.io-client"; import { @@ -50,7 +51,7 @@ export const createSentence = async (sign: { }) => { const phrase = await ky .post( - `${import.meta.env.VITE_SONISORI_AI_API_URL}${SIGN_PHRASE_TYPE_API_ENDPOINT_MAP[sign.signPhraseType]}`, + `${import.meta.env.VITE_SONISORI_AI_REST_URL}${SIGN_PHRASE_TYPE_API_ENDPOINT_MAP[sign.signPhraseType]}`, { json: { prediction: sign.words }, }, @@ -100,6 +101,10 @@ const SignDetectorBody = (props: { let socket: Socket; const task = new Task(); + const send = throttle((landmarks: NormalizedLandmark[][]) => { + socket.emit("predict", landmarks); + }, 200); + const streamMedia = async () => { if (!navigator.mediaDevices?.getDisplayMedia) { throw new Error("카메라를 사용할 수 없는 디바이스입니다."); @@ -149,7 +154,7 @@ const SignDetectorBody = (props: { const { landmarks } = handLandmarker.detectForVideo(videoRef!, time); drawLandmarks(landmarks); - socket.emit("predict", landmarks); + send(landmarks); if (typeof animationFrame == "number") { animationFrame = requestAnimationFrame(predictMedia); @@ -158,7 +163,7 @@ const SignDetectorBody = (props: { const initialize = async () => { try { - socket = io(import.meta.env.VITE_SONISORI_AI_API_URL, { + socket = io(import.meta.env.VITE_SONISORI_AI_SOCKET_URL, { transports: ["websocket"], }); socket.on("prediction_result", (data: { appended: string }) => { @@ -185,6 +190,7 @@ const SignDetectorBody = (props: { animationFrame = null; } handLandmarker.close(); + send.clear(); socket.disconnect(); stream?.getTracks().forEach((track) => track.stop()); setLoaded(false); diff --git a/web/src/ui/screen/Dictionary.tsx b/web/src/ui/screen/Dictionary.tsx index 0615f9a..d11032b 100644 --- a/web/src/ui/screen/Dictionary.tsx +++ b/web/src/ui/screen/Dictionary.tsx @@ -1,45 +1,79 @@ -import { useNavigate } from "@solidjs/router"; +import { useNavigate, useParams } from "@solidjs/router"; +import { createResource, For, Show } from "solid-js"; +import { client } from "../../service/util/api"; import { ScrollArea } from "../component/base/ScrollAreaV2"; import { SignDetector } from "../component/domain/SignDetector"; export const Dictionary = () => { const navigate = useNavigate(); - const word = { - word: "사과", - videoUrl: "https://www.youtube.com/watch?v=9bZkp7q19f0", - }; + const { id } = useParams(); + const [data] = createResource(() => + client.get(`api/words/${id}`).json<{ + description: string; + resources: { + resourceType: "image" | "video"; + resourceUrl: string; + }[]; + word: string; + }>(), + ); return (
navigate(-1)} open signPhraseType="평서문" /> - -
-
-

{word.word}

- {/* video placeholder */} -