Disabled .env to keep entire app on client side

This commit is contained in:
Malhar Ujawane 2024-10-12 22:20:10 -07:00
parent f303012075
commit 6e60df6b1e
3 changed files with 97 additions and 83 deletions

View File

@ -1 +0,0 @@
OPENAI_API_KEY=""

View File

@ -26,7 +26,7 @@ To set up the project locally, follow these steps:
4. Start the development server with `npm run dev`. 4. Start the development server with `npm run dev`.
5. Open `http://localhost:3000` to view it in the browser. 5. Open `http://localhost:3000` to view it in the browser.
## How to use Environment Variable ## How to use Environment Variable (Disabled and reverted to keep things purely on Client Side)
1. Copy the `.env.example` file to: 1. Copy the `.env.example` file to:
- .env.local file: For Local Development (This should not be committed to Git) - `cp .env.example .env.local` - .env.local file: For Local Development (This should not be committed to Git) - `cp .env.example .env.local`
- .env: For Production Deployment (This can be via Vercel Environment Variables under your Project's settings page. `cp .env.example .env` - .env: For Production Deployment (This can be via Vercel Environment Variables under your Project's settings page. `cp .env.example .env`

View File

@ -23,26 +23,18 @@ import {
Box, Box,
Tooltip, Tooltip,
Switch, Switch,
FormHelperText FormHelperText,
} from '@chakra-ui/react'; } from "@chakra-ui/react";
import OpenAI from "openai"; import OpenAI from "openai";
import { useState, useRef, useEffect } from 'react'; import { useState, useRef, useEffect } from "react";
import { saveAs } from 'file-saver'; // You will need to install file-saver: npm install file-saver import { saveAs } from "file-saver"; // You will need to install file-saver: npm install file-saver
export default function Home() { export default function Home() {
const apiKeyFromEnv = process.env.OPENAI_API_KEY; const [apiKeyInput, setApiKey] = useState("");
const [apiKeyInput, setApiKey] = useState('');
useEffect(() => { const [model, setModel] = useState("tts-1");
if (!!apiKeyFromEnv) { const [inputText, setInputText] = useState("");
setApiKey(apiKeyFromEnv); const [voice, setVoice] = useState("alloy");
}
}, [apiKeyFromEnv]);
const [model, setModel] = useState('tts-1');
const [inputText, setInputText] = useState('');
const [voice, setVoice] = useState('alloy');
const [speed, setSpeed] = useState(1); const [speed, setSpeed] = useState(1);
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [sliderValue, setSliderValue] = useState(1); const [sliderValue, setSliderValue] = useState(1);
@ -62,14 +54,13 @@ export default function Home() {
const toast = useToast(); const toast = useToast();
const handleModelToggle = () => { const handleModelToggle = () => {
setModel(model === 'tts-1' ? 'tts-1-hd' : 'tts-1'); setModel(model === "tts-1" ? "tts-1-hd" : "tts-1");
}; };
const handleDownload = () => { const handleDownload = () => {
saveAs(audioUrl, 'speech.mp3'); // This will save the file as "speech.mp3" saveAs(audioUrl, "speech.mp3"); // This will save the file as "speech.mp3"
}; };
// Assuming `openai.audio.speech.create` returns a stream or binary data // Assuming `openai.audio.speech.create` returns a stream or binary data
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
@ -78,7 +69,7 @@ export default function Home() {
try { try {
// Define the request headers // Define the request headers
const headers = new Headers(); const headers = new Headers();
const apiKey = apiKeyFromEnv && !!apiKeyFromEnv ? apiKeyFromEnv : apiKeyInput; const apiKey = apiKeyInput;
headers.append("Authorization", `Bearer ${apiKey}`); headers.append("Authorization", `Bearer ${apiKey}`);
headers.append("Content-Type", "application/json"); headers.append("Content-Type", "application/json");
@ -87,12 +78,12 @@ export default function Home() {
model: model, model: model,
input: inputText, input: inputText,
voice: voice, voice: voice,
speed: speed.toFixed(1) speed: speed.toFixed(1),
}); });
// Make the fetch request to the OpenAI API // Make the fetch request to the OpenAI API
const response = await fetch('https://api.openai.com/v1/audio/speech', { const response = await fetch("https://api.openai.com/v1/audio/speech", {
method: 'POST', method: "POST",
headers: headers, headers: headers,
body: body, body: body,
}); });
@ -111,13 +102,12 @@ export default function Home() {
// Update your component's state or context // Update your component's state or context
setAudioUrl(audioUrl); setAudioUrl(audioUrl);
} catch (error) { } catch (error) {
console.error("Error:", error); console.error("Error:", error);
toast({ toast({
title: 'An error occurred', title: "An error occurred",
description: error.message, description: error.message,
status: 'error', status: "error",
duration: 5000, duration: 5000,
isClosable: true, isClosable: true,
}); });
@ -126,10 +116,6 @@ export default function Home() {
} }
}; };
const handleInputChange = (e) => { const handleInputChange = (e) => {
if (e.target.value.length <= 4096) { if (e.target.value.length <= 4096) {
setInputText(e.target.value); setInputText(e.target.value);
@ -137,9 +123,15 @@ export default function Home() {
}; };
return ( return (
<Container bg={'gray.100'} maxW="container"> <Container bg={"gray.100"} maxW="container">
<Container centerContent p={4} maxW="container.md"> <Container centerContent p={4} maxW="container.md">
<Flex direction="column" align="center" justify="center" minH="100vh" w="full"> <Flex
direction="column"
align="center"
justify="center"
minH="100vh"
w="full"
>
<Box <Box
bg="white" // Assuming the card is white bg="white" // Assuming the card is white
borderRadius="lg" // Rounded corners borderRadius="lg" // Rounded corners
@ -148,62 +140,85 @@ export default function Home() {
w="full" // Full width of the parent w="full" // Full width of the parent
maxW="md" // Maximum width maxW="md" // Maximum width
> >
<VStack spacing={6} as="form" onSubmit={handleSubmit} width="full" maxW="md"> <VStack
<Box bg='black' w='100%' p={5} borderTopRadius="md" boxShadow="lg"> spacing={6}
<Heading textAlign="center" color="white">Open-Audio TTS</Heading> as="form"
<Text fontSize="xs" color="gray.100" textAlign="center" mt={2}>Powered by OpenAI TTS </Text> onSubmit={handleSubmit}
<Text fontSize="xs" color="gray.100" textAlign="center" mt={2} fontWeight={'700'}> width="full"
<a href="https://github.com/Justmalhar/open-audio" target="_blank" rel="noopener noreferrer" style={{ color: 'gray.100' }}> maxW="md"
View on GitHub >
</a> <Box
</Text> bg="black"
</Box> w="100%"
p={5}
borderTopRadius="md"
boxShadow="lg"
>
<Heading textAlign="center" color="white">
Open-Audio TTS
</Heading>
<Text fontSize="xs" color="gray.100" textAlign="center" mt={2}>
Powered by OpenAI TTS{" "}
</Text>
<Text
fontSize="xs"
color="gray.100"
textAlign="center"
mt={2}
fontWeight={"700"}
>
<a
href="https://github.com/Justmalhar/open-audio"
target="_blank"
rel="noopener noreferrer"
style={{ color: "gray.100" }}
>
View on GitHub
</a>
</Text>
</Box>
<Grid <Grid
templateColumns={{ md: '4fr 1fr' }} // 80-20 ratio templateColumns={{ md: "4fr 1fr" }} // 80-20 ratio
gap={4} gap={4}
width="full" width="full"
> >
<FormControl isRequired> <FormControl isRequired>
<FormLabel htmlFor='api-key'>API Key</FormLabel> <FormLabel htmlFor="api-key">API Key</FormLabel>
<Input <Input
id='api-key' id="api-key"
placeholder='Enter your OpenAI API key' placeholder="Enter your OpenAI API key"
type='password' type="password"
value={apiKeyInput} value={apiKeyInput}
onChange={(e) => setApiKey(e.target.value)} onChange={(e) => setApiKey(e.target.value)}
variant="outline" variant="outline"
borderColor="black" borderColor="black"
disabled={!!apiKeyFromEnv} // Disable the input if apiKeyFromEnv is truthy
/> />
</FormControl> </FormControl>
<FormControl> <FormControl>
<VStack align="start" spacing={0}> <VStack align="start" spacing={0}>
<FormLabel htmlFor='model'> <FormLabel htmlFor="model">Quality</FormLabel>
Quality <HStack align="center" h="100%" mx="0" mt="2">
</FormLabel>
<HStack align="center" h='100%' mx='0' mt='2' >
<Switch <Switch
id='model' id="model"
colorScheme="blackAlpha" colorScheme="blackAlpha"
isChecked={model === 'tts-1-hd'} isChecked={model === "tts-1-hd"}
onChange={handleModelToggle} onChange={handleModelToggle}
size="md" // Optional: if you want a larger switch size="md" // Optional: if you want a larger switch
/> />
<FormHelperText textAlign="center" mt={'-1'}> <FormHelperText textAlign="center" mt={"-1"}>
{model === 'tts-1' ? 'High' : 'HD'} {model === "tts-1" ? "High" : "HD"}
</FormHelperText> </FormHelperText>
</HStack> </HStack>
</VStack> </VStack>
</FormControl> </FormControl>
</Grid> </Grid>
<FormControl isRequired> <FormControl isRequired>
<FormLabel htmlFor='input-text'>Input Text</FormLabel> <FormLabel htmlFor="input-text">Input Text</FormLabel>
<Textarea <Textarea
id='input-text' id="input-text"
placeholder='Enter the text you want to convert to speech' placeholder="Enter the text you want to convert to speech"
value={inputText} value={inputText}
onChange={handleInputChange} onChange={handleInputChange}
resize="vertical" resize="vertical"
@ -217,9 +232,9 @@ export default function Home() {
<HStack width="full" justifyContent="space-between"> <HStack width="full" justifyContent="space-between">
<FormControl isRequired width="45%"> <FormControl isRequired width="45%">
<FormLabel htmlFor='voice'>Voice</FormLabel> <FormLabel htmlFor="voice">Voice</FormLabel>
<Select <Select
id='voice' id="voice"
value={voice} value={voice}
onChange={(e) => setVoice(e.target.value)} onChange={(e) => setVoice(e.target.value)}
variant="outline" variant="outline"
@ -227,22 +242,22 @@ export default function Home() {
borderColor="black" borderColor="black"
focusBorderColor="black" focusBorderColor="black"
colorScheme="blackAlpha" colorScheme="blackAlpha"
_hover={{ borderColor: 'gray.400' }} // Optional: style for hover state _hover={{ borderColor: "gray.400" }} // Optional: style for hover state
> >
{/* List of supported voices */} {/* List of supported voices */}
<option value='alloy'>Alloy</option> <option value="alloy">Alloy</option>
<option value='echo'>Echo</option> <option value="echo">Echo</option>
<option value='fable'>Fable</option> <option value="fable">Fable</option>
<option value='onyx'>Onyx</option> <option value="onyx">Onyx</option>
<option value='nova'>Nova</option> <option value="nova">Nova</option>
<option value='shimmer'>Shimmer</option> <option value="shimmer">Shimmer</option>
</Select> </Select>
</FormControl> </FormControl>
<FormControl width="40%" mt='-15'> <FormControl width="40%" mt="-15">
<FormLabel htmlFor='speed'>Speed </FormLabel> <FormLabel htmlFor="speed">Speed </FormLabel>
<Slider <Slider
id='speed' id="speed"
defaultValue={1} defaultValue={1}
min={0.25} min={0.25}
max={4} max={4}
@ -251,18 +266,18 @@ export default function Home() {
onMouseEnter={() => setShowTooltip(true)} onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)} onMouseLeave={() => setShowTooltip(false)}
ref={sliderRef} ref={sliderRef}
aria-label='slider-ex-1' aria-label="slider-ex-1"
> >
<SliderTrack bg="gray"> <SliderTrack bg="gray">
<SliderFilledTrack bg="black" /> <SliderFilledTrack bg="black" />
</SliderTrack> </SliderTrack>
<Tooltip <Tooltip
hasArrow hasArrow
bg='black' bg="black"
color='white' color="white"
placement='bottom' placement="bottom"
isOpen={showTooltip} isOpen={showTooltip}
label={`${(sliderValue).toFixed(2)}x`} label={`${sliderValue.toFixed(2)}x`}
> >
<SliderThumb /> <SliderThumb />
</Tooltip> </Tooltip>
@ -272,11 +287,11 @@ export default function Home() {
<Button <Button
size="lg" size="lg"
bg='black' bg="black"
color={'white'} color={"white"}
colorScheme='black' colorScheme="black"
borderColor="black" borderColor="black"
type='submit' type="submit"
isLoading={isSubmitting} isLoading={isSubmitting}
loadingText="Generating..." loadingText="Generating..."
> >