From b762c6fcda5d426af3ded0745e477c79606a1887 Mon Sep 17 00:00:00 2001 From: BFlat Date: Thu, 22 Feb 2024 15:34:39 +0700 Subject: [PATCH] landing demo update --- .../SelectionBubbles/SelectionBubbles.tsx | 2 +- .../landing/fakeyou/FakeYouLandingHeader.tsx | 2 +- .../fakeyou/LandingDemo/DemoAudioPlayer.tsx | 36 ++++ .../{ => LandingDemo}/FakeYouLandingDemo.tsx | 169 ++++++++++-------- .../fakeyou/LandingDemo/LandingDemo.scss | 11 ++ .../fakeyou/LandingDemo/RandomTexts.ts | 88 +++++++++ 6 files changed, 235 insertions(+), 73 deletions(-) create mode 100644 packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/DemoAudioPlayer.tsx rename packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/{ => LandingDemo}/FakeYouLandingDemo.tsx (55%) create mode 100644 packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/LandingDemo.scss create mode 100644 packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/RandomTexts.ts diff --git a/packages/fakeyou.com/src/components/common/SelectionBubbles/SelectionBubbles.tsx b/packages/fakeyou.com/src/components/common/SelectionBubbles/SelectionBubbles.tsx index 5e5597bd0e..3f7c8f88a0 100644 --- a/packages/fakeyou.com/src/components/common/SelectionBubbles/SelectionBubbles.tsx +++ b/packages/fakeyou.com/src/components/common/SelectionBubbles/SelectionBubbles.tsx @@ -10,7 +10,7 @@ export default function SelectionBubbles({ options, onSelect, }: SelectionBubbleProps) { - //Select first one as default + //Select first one as defaults const [selectedOption, setSelectedOption] = useState( options.length > 0 ? options[0] : null ); diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/FakeYouLandingHeader.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/FakeYouLandingHeader.tsx index 21a8a42c4b..2a3f0fab5d 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/FakeYouLandingHeader.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/FakeYouLandingHeader.tsx @@ -4,7 +4,7 @@ import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapp import Alert from "components/common/Alert/Alert"; import { useLocalize } from "hooks"; import React from "react"; -import LandingDemo from "./FakeYouLandingDemo"; +import LandingDemo from "./LandingDemo/FakeYouLandingDemo"; import { FrontendInferenceJobType, InferenceJob, diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/DemoAudioPlayer.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/DemoAudioPlayer.tsx new file mode 100644 index 0000000000..ccc49c694c --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/DemoAudioPlayer.tsx @@ -0,0 +1,36 @@ +import React, { useCallback, useState } from "react"; +import Wavesurfer from "react-wavesurfer.js"; + +interface Props { + filename: string; + play: boolean; + onFinish?: () => void; +} + +export default function DemoTtsAudioPlayer(props: Props) { + const [position, setPosition] = useState(0); + + const handleFinish = useCallback(() => { + setPosition(0); + props.onFinish && props.onFinish(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.onFinish]); + + return ( +
+ +
+ ); +} diff --git a/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/FakeYouLandingDemo.tsx b/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/FakeYouLandingDemo.tsx similarity index 55% rename from packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/FakeYouLandingDemo.tsx rename to packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/FakeYouLandingDemo.tsx index c772df8054..86468612b3 100644 --- a/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/FakeYouLandingDemo.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/landing/fakeyou/LandingDemo/FakeYouLandingDemo.tsx @@ -1,13 +1,7 @@ -import React, { useState } from "react"; -import { faDeleteLeft, faPlay } from "@fortawesome/pro-solid-svg-icons"; +import React, { useEffect, useState } from "react"; +import { faPause, faPlay, faShuffle } from "@fortawesome/pro-solid-svg-icons"; import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper"; -import { - Panel, - Button, - TextArea, - SelectionBubbles, - Accordion, -} from "components/common"; +import { Panel, Button, TextArea, SelectionBubbles } from "components/common"; import { FrontendInferenceJobType, InferenceJob, @@ -21,8 +15,14 @@ import { GenerateTtsAudioIsOk, } from "@storyteller/components/src/api/tts/GenerateTtsAudio"; import { Analytics } from "common/Analytics"; -import { Link } from "react-router-dom"; -import { SessionTtsInferenceResultList } from "v2/view/_common/SessionTtsInferenceResultsList"; +import { BucketConfig } from "@storyteller/components/src/api/BucketConfig"; +import DemoTtsAudioPlayer from "./DemoAudioPlayer"; +import { + GetTtsInferenceJobStatus, + GetTtsInferenceJobStatusIsOk, +} from "@storyteller/components/src/api/jobs/GetTtsInferenceJobStatus"; +import RandomTexts from "./RandomTexts"; +import "./LandingDemo.scss"; interface TtsInferencePanelProps { sessionSubscriptionsWrapper: SessionSubscriptionsWrapper; @@ -45,23 +45,29 @@ export default function LandingDemo({ inferenceJobsByCategory, }: TtsInferencePanelProps) { const [textBuffer, setTextBuffer] = useState(""); - const [isEnqueuing, setIsEnqueuing] = useState(false); const [maybeTtsError, setMaybeTtsError] = useState< GenerateTtsAudioErrorType | undefined >(undefined); - const [isAudioLimitAlertVisible, setAudioLimitAlertVisible] = useState(false); + const [currentAudioUrl, setCurrentAudioUrl] = useState(null); + const [isPlaying, setIsPlaying] = useState(false); + const [jobToken, setJobToken] = useState(null); + const [lastEnqueuedText, setLastEnqueuedText] = useState(null); + const [isAudioLoading, setIsAudioLoading] = useState(false); + const [lastSelectedVoice, setLastSelectedVoice] = useState( + null + ); const handleChangeText = (ev: React.FormEvent) => { const textValue = (ev.target as HTMLTextAreaElement).value; setTextBuffer(textValue); - setAudioLimitAlertVisible(textValue.length > 100); }; + //Set voices here const voiceModelTokenMap: { [key: string]: string } = { - Spongebob: "weight_vrx7j407cxk45jenkrd769h9b", - Trump: "trump_model_token", - Messi: "messi_model_token", - // Add more voice options and their modelTokens as needed + Rick: "weight_0f762jdzgsy1dhpb86qxy4ssm", + Mickey: "weight_sfyjyr67ag1647xs0r7gmvkks", + Eric: "weight_h8ebh6fyjyrr1vsjregw6yz8y", + "Angry Male": "weight_hehgvegadf08mfp5rzd69dmh4", }; const [voiceToken, setVoiceToken] = useState(voiceModelTokenMap["Spongebob"]); @@ -78,7 +84,14 @@ export default function LandingDemo({ return false; } - setIsEnqueuing(true); + // Check if the text hasn't changed and the voice hasn't changed + if (textBuffer === lastEnqueuedText && voiceToken === lastSelectedVoice) { + setIsPlaying(!isPlaying); + setIsAudioLoading(false); + return false; + } + + setIsAudioLoading(true); const modelToken = voiceToken; @@ -103,15 +116,52 @@ export default function LandingDemo({ } else { enqueueTtsJob(response.inference_job_token); } + // Store the job token + setJobToken(response.inference_job_token); + // Store the last enqueued text + setLastEnqueuedText(textBuffer); + // Store the last selected voice + setLastSelectedVoice(voiceToken); } else if (GenerateTtsAudioIsError(response)) { setMaybeTtsError(response.error); } - setIsEnqueuing(false); - return false; }; + useEffect(() => { + if (!jobToken) return; + + const fetch = async () => { + const jobStatusResponse = await GetTtsInferenceJobStatus(jobToken); + + if (GetTtsInferenceJobStatusIsOk(jobStatusResponse)) { + const job = inferenceJobs.find(job => job.jobToken === jobToken); + if (job && jobStatusResponse.state.maybe_public_bucket_wav_audio_path) { + const audioLink = new BucketConfig().getGcsUrl( + jobStatusResponse.state.maybe_public_bucket_wav_audio_path + ); + setCurrentAudioUrl(audioLink); + + if (audioLink !== currentAudioUrl) { + setIsAudioLoading(false); + setIsPlaying(true); + } + } + } + }; + + fetch(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [jobToken, inferenceJobs, isPlaying]); + + const handleAudioFinish = () => { + setIsPlaying(false); + }; + + const voiceOptions = Object.keys(voiceModelTokenMap); + + // Show errors on TTS failure let maybeError = <>; if (!!maybeTtsError) { let hasMessage = false; @@ -119,7 +169,9 @@ export default function LandingDemo({ switch (maybeTtsError) { case GenerateTtsAudioErrorType.TooManyRequests: hasMessage = true; - message = <>Error: Too many requests. Please try again in a bit.; + message = ( + <>Too many requests! Please wait a few minutes then try again. + ); break; case GenerateTtsAudioErrorType.ServerError | GenerateTtsAudioErrorType.BadRequest | @@ -145,33 +197,17 @@ export default function LandingDemo({ } } - let audioLimitAlert = <>; - if ( - isAudioLimitAlertVisible && - !sessionSubscriptionsWrapper.hasPaidFeatures() - ) { - audioLimitAlert = ( - <> -
- - Note: Non-premium is limited to 12 seconds of audio.{" "} - - Upgrade now - - . - -
- - ); - } - - const voiceOptions = Object.keys(voiceModelTokenMap); + const generateRandomText = () => { + const randomIndex = Math.floor(Math.random() * RandomTexts.length); + const randomText = RandomTexts[randomIndex]; + setTextBuffer(randomText); + }; return (
-
+
-
- +
+ +