landing demo update

This commit is contained in:
BFlat committed 2024-02-22 15:34:39 +07:00
1 parent 533023211e
commit b762c6fcda
6 files changed
+235 -73

No files matched your search

@@ -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<string | null>(
options.length > 0 ? options[0] : null
);
@@ -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,
@@ -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 (
<div className="flex-grow-1 h-100 overflow-hidden">
<Wavesurfer
key={props.filename}
onFinish={handleFinish}
pos={position}
src={props.filename || ""}
height={36}
progressColor="#fc8481"
waveColor="#b09e9e"
cursorColor="#fc8481"
playing={props.play}
responsive={true}
normalize={true}
/>
</div>
);
}
@@ -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<string | null>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [jobToken, setJobToken] = useState<string | null>(null);
const [lastEnqueuedText, setLastEnqueuedText] = useState<string | null>(null);
const [isAudioLoading, setIsAudioLoading] = useState(false);
const [lastSelectedVoice, setLastSelectedVoice] = useState<string | null>(
null
);
const handleChangeText = (ev: React.FormEvent<HTMLTextAreaElement>) => {
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 = (
<>
<div className="alert alert-warning fs-7 mb-0">
<span className="fw-semibold">
<u>Note:</u> Non-premium is limited to 12 seconds of audio.{" "}
<Link className="fw-semibold" to="/pricing">
Upgrade now
</Link>
.
</span>
</div>
</>
);
}
const voiceOptions = Object.keys(voiceModelTokenMap);
const generateRandomText = () => {
const randomIndex = Math.floor(Math.random() * RandomTexts.length);
const randomText = RandomTexts[randomIndex];
setTextBuffer(randomText);
};
return (
<Panel padding={true}>
<form className="d-flex flex-column">
<div>
<div>
<div className="d-flex">
<label className="sub-title flex-grow-1">Select a Voice</label>
</div>
<SelectionBubbles
@@ -181,8 +217,15 @@ export default function LandingDemo({
</div>
<div className="d-flex flex-column mt-3">
<div>
<label className="sub-title flex-grow-1">Your Text</label>
<div className="d-flex justify-content-center pb-2">
<label className="sub-title flex-grow-1 pb-0">Your Text</label>
<Button
icon={faShuffle}
label="Randomize Text"
onClick={generateRandomText}
variant="link"
className="fs-7 randomize-text-button"
/>
</div>
<TextArea
placeholder="Enter the text you want the voice to say here..."
@@ -191,41 +234,25 @@ export default function LandingDemo({
rows={4}
resize={false}
/>
{audioLimitAlert}
</div>
{maybeError}
<div className="d-flex gap-3 align-items-center mt-4">
<Button
icon={faPlay}
icon={isPlaying ? faPause : faPlay}
square={true}
onClick={handleEnqueueTts}
isLoading={isEnqueuing}
isLoading={isAudioLoading}
disabled={textBuffer.length === 0}
/>
<>Autoplaying audio player here</>
<DemoTtsAudioPlayer
filename={currentAudioUrl || ""}
play={isPlaying}
onFinish={handleAudioFinish}
/>
</div>
</form>
{inferenceJobs[0] && (
<div className="mt-4">
<Accordion>
<Accordion.Item title="Session TTS Results" defaultOpen={true}>
<div className="p-3">
<SessionTtsInferenceResultList
inferenceJobs={
inferenceJobsByCategory.get(
FrontendInferenceJobType.TextToSpeech
)!
}
ttsInferenceJobs={ttsInferenceJobs}
sessionSubscriptionsWrapper={sessionSubscriptionsWrapper}
/>
</div>
</Accordion.Item>
</Accordion>
{maybeError}
</div>
)}
</Panel>
);
}
@@ -0,0 +1,11 @@
@import "scss/colours.scss";
.randomize-text-button {
color: desaturate($primary, 30%) !important;
opacity: 1;
&:hover {
color: desaturate($primary, 10%) !important;
opacity: 0.85;
}
}
@@ -0,0 +1,88 @@
const RandomTexts = [
"Hello, world!",
"Welcome to the future.",
"The quick brown fox jumps over the lazy dog.",
"In a hole in the ground there lived a hobbit.",
"Once upon a time in a land far, far away.",
"Can't believe you've done this.",
"Sometimes, I wish I was an octopus, so I could slap eight people at once.",
"Explaining why one is right is not arguing, it's just clarifying.",
"Wallets are like onions; opening them brings tears.",
"I'm not lazy, I'm on energy-saving mode.",
"When life gives you melons, dyslexia might be involved.",
"Atoms are untrustworthy; they make up everything.",
"Glue history books are impossible to put down.",
"Time travel jokes are funny in any era, but you might have missed it.",
"I would lose weight, but I hate losing.",
"A messy room is just an open-air museum.",
"Trying to fail and succeeding is a real conundrum.",
"Procrastination is just engaging in numerous side quests.",
"Being early for tomorrow is a new kind of punctuality.",
"Adulthood is the longest test run that never ends.",
"Beauty sleep's advertising is misleading.",
"I'm on a whiskey diet. I've lost three days already.",
"Magical beds remember all forgotten tasks.",
"Pillows are the unsung heroes of hairstyling.",
"I'm not saying I'm Batman, but have you ever seen me and Batman in the same room?",
"Couches are the battlegrounds for avoiding problems.",
"Life's brevity demands self-laughter or outsourcing it.",
"Being a limited edition is far from weird.",
"Office chairs that spin make the job worth it.",
"Wake-up time defines personhood, not the time of day.",
"Math problems multiply in silence.",
"Physical conservatism is the new fitness trend.",
"Extra fries misunderstood as exercise is a common error.",
"I'm fluent in three languages: English, sarcasm, and profanity.",
"Multitasking involves listening, ignoring, and forgetting simultaneously.",
"Indecisiveness is a choice, sometimes.",
"I'm really good at stuff until people watch me do that stuff.",
"Correctness needs no argument, just explanation.",
"Coffee relationships are the most committed ones.",
"Rush hour is the greatest paradox.",
"I'm not bossy. I just have better ideas.",
"Silence and duct tape both have their merits.",
"I'm not procrastinating. I'm under a lot of pressure and very well could be a diamond soon.",
"Battery saver mode is essential for work avoidance.",
"Exploration is just being lost with style.",
"Pressing harder on a remote control seems like a valid effort when batteries are weak.",
"I'm convinced that bugs in software are just features in disguise.",
"Adulthood is getting excited about a new sponge for the kitchen, I've accepted this.",
"Life's too short, I always smile while I still have all my teeth.",
"In video games, I've learned that encountering enemies often means you're going in the right direction.",
"I'm often on the hunt for motivation, but it's like searching for a 404 error.",
"Debating with a computer, I've realized, is as effective as trying to solve algebra by chewing bubblegum.",
"During rush hour, I remind myself that patience is indeed a virtue.",
"I transformed a shy friend into a social butterfly, and now I wonder what I've unleashed.",
"The quest for sleep, sanity, and a peaceful hobbit life is something I'm still on.",
"My bed has a magical ability to remind me of forgotten tasks at night.",
"When the Wi-Fi goes down, I discover family members are actually nice people.",
"A cookie in each hand, I believe, is the true definition of a balanced diet.",
"Why use Google when I'm convinced someone believes they have all the answers?",
"Exploring the great indoors, I've found, is because outside is overrated.",
"I'm fluent in three languages: English, sarcasm, and late-night coding.",
"Why wait for a time machine when I have memories and music?",
"The best way to avoid chores, I've found, is to pretend not to know they exist.",
"If I can't find the sunshine, I've learned to be the person who switches on the light.",
"Realizing too late that adulthood is a trap is a lesson I've learned the hard way.",
"The art of being wise, I've discovered, is knowing what to overlook, like unread emails.",
"Why call it fast food if I have to wait in line for it?",
"I've discovered that my spirit animal is a sloth on weekdays.",
"Making a 'quick' phone call, I've realized, is never quick.",
"The mystery of how one sock always disappears in the laundry remains unsolved, I've noticed.",
"Finding out that my emergency contact is a pizza delivery service was a surprise.",
"The realization that autocorrect has become my worst enema... I mean, enemy, is frustrating.",
"Wondering why my coffee tastes like I should have made it myself is a daily ritual.",
"The silent joy of canceling plans is an underrated pleasure I cherish.",
"Discovering that my daily workout is trying to dodge spoilers online is a modern struggle.",
"Realizing that my phone's battery life lasts longer than my motivation is a harsh reality.",
"The paradox of being a perfectionist procrastinator is a real struggle I face.",
"Finding comfort in the fact that chocolate understands me is a sweet relief.",
"Realizing I'm not late, I'm just on a creative time schedule is how I cope.",
"Discovering that my hobby is collecting dust on my gym membership was an eye-opener.",
"The irony of being a tech expert but unable to fix my sleep schedule is not lost on me.",
"Understanding that my to-do list is more of a suggestion than a requirement helps me sleep at night.",
"The thrill of solving a bug in code is the coder's version of a plot twist, I've found.",
"Realizing that my idea of a balanced diet is having a piece of tech in both hands is a modern dilemma.",
];
export default RandomTexts;