diff --git a/packages/components/src/api/clone_requests/CreateVoiceCloneApplicationRequest.ts b/packages/components/src/api/clone_requests/CreateVoiceCloneApplicationRequest.ts new file mode 100644 index 0000000000..d3949a1205 --- /dev/null +++ b/packages/components/src/api/clone_requests/CreateVoiceCloneApplicationRequest.ts @@ -0,0 +1,86 @@ + +import { ApiConfig } from "@storyteller/components"; + +export interface CreateVoiceCloneApplicationRequest { + + idempotency_token: string, + + // Contact + email_address: string, + discord_username: string, + + // TODO: Make enum. + // Visibility + is_for_private_use: boolean, + is_for_public_use: boolean, + + // Use + is_for_studio: boolean, + is_for_twitch_tts: boolean, + is_for_api_use: boolean, + is_for_music: boolean, + is_for_games: boolean, + is_for_other: boolean, + optional_notes_on_use?: string, + + // Subject/Ownership + is_own_voice: boolean, + is_third_party_voice: boolean, + optional_notes_on_subject?: string, + + // Equipment + has_clean_audio_recordings: boolean, + has_good_microphone: boolean, + + // Comments + optional_questions?: string, + optional_extra_comments?: string, +} + +export interface CreateVoiceCloneApplicationSuccessResponse { + success: boolean, +} + +export interface CreateVoiceCloneApplicationErrorResponse { + success: boolean, +} + +type CreateVoiceCloneApplicationResponse = CreateVoiceCloneApplicationSuccessResponse | CreateVoiceCloneApplicationErrorResponse; + +export function CreateVoiceCloneApplicationIsSuccess(response: CreateVoiceCloneApplicationResponse): response is CreateVoiceCloneApplicationSuccessResponse { + return response?.success === true; +} + +export function CreateVoiceCloneApplicationIsError(response: CreateVoiceCloneApplicationResponse): response is CreateVoiceCloneApplicationErrorResponse { + return response?.success === false; +} + +export async function CreateVoiceCloneApplication(request: CreateVoiceCloneApplicationRequest) : Promise +{ + const endpoint = new ApiConfig().createVoiceCloneRequest(); + + return await fetch(endpoint, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + credentials: 'include', + body: JSON.stringify(request), + }) + .then(res => res.json()) + .then(res => { + if (!res) { + return { success : false }; + } + + if (res && 'success' in res) { + return res; + } else { + return { success : false }; + } + }) + .catch(e => { + return { success : false }; + }); +} diff --git a/packages/fakeyou/public/mascot/kitsune_pose4_black_2000.webp b/packages/fakeyou/public/mascot/kitsune_pose4_black_2000.webp new file mode 100644 index 0000000000..7a78887f1b Binary files /dev/null and b/packages/fakeyou/public/mascot/kitsune_pose4_black_2000.webp differ diff --git a/packages/fakeyou/src/v2/view/clone_voice_requests/VoiceCloneRequestPage.css b/packages/fakeyou/src/v2/view/clone_voice_requests/VoiceCloneRequestPage.css new file mode 100644 index 0000000000..c51c13fff8 --- /dev/null +++ b/packages/fakeyou/src/v2/view/clone_voice_requests/VoiceCloneRequestPage.css @@ -0,0 +1,8 @@ + +.checkbox-block { + margin: 5px 0px 0px 0px; +} + +.checkbox-block label { + margin: 0 0 10px; +} \ No newline at end of file diff --git a/packages/fakeyou/src/v2/view/clone_voice_requests/VoiceCloneRequestPage.tsx b/packages/fakeyou/src/v2/view/clone_voice_requests/VoiceCloneRequestPage.tsx new file mode 100644 index 0000000000..70803d4ff8 --- /dev/null +++ b/packages/fakeyou/src/v2/view/clone_voice_requests/VoiceCloneRequestPage.tsx @@ -0,0 +1,326 @@ +import './VoiceCloneRequestPage.css'; + +import { faEnvelope } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import React, { useCallback, useEffect, useState } from 'react'; + +interface Props { +} + +function VoiceCloneRequestPage(props: Props) { + //const [leaderboard, setLeaderboard] = useState(undefined); + //const [ttsLeaderboard, setTtsLeaderboard] = useState|undefined>(undefined); + //const [w2lLeaderboard, setW2lLeaderboard] = useState|undefined>(undefined); + //const [retryCount, setRetryCount] = useState(0); + + //const getLeaderboard = useCallback(async () => { + // const leaderboardReponse = await GetLeaderboard(); + + // if (GetLeaderboardIsOk(leaderboardReponse)) { + // setLeaderboard(leaderboardReponse); + // setTtsLeaderboard(leaderboardReponse.tts_leaderboard); + // setW2lLeaderboard(leaderboardReponse.w2l_leaderboard); + // } else if (GetLeaderboardIsErr(leaderboardReponse)) { + // switch(leaderboardReponse) { + // // TODO: There's an issue with the queries not returning before the deadline. + // // I should add a Redis TTL cache to store the results and an async job to warm the cache. + // case LeaderboardLookupError.NotFound: + // if (retryCount < 3) { + // setTimeout(() => getLeaderboard(), 1000); + // setRetryCount(retryCount+1); + // } + // break; + // } + // } + //}, [retryCount]); + + //useEffect(() => { + // getLeaderboard(); + //}, [getLeaderboard]); + + return ( +
+ +
+
+ +
+ +
+
+ FakeYou's mascot! +
+
+ +
+

+ Professionally Clone Your Voice +

+

+ (or any voice) for just $70 +

+ +
+ +
+
+
+ +
+
+ +

Want a Custom Voice You Can Use?

+

For Music, Videos, Twitch Rewards, API, friends, family… whatever you want!

+ +

+ We have an extremely talented staff that will personally handle your voice clone request. + Please help us understand more about your voice and how you want to use it, and we'll be in touch + shortly. +

+ +
+ +

First, how should we get in touch?

+ +
+ +
+ + + + +
+

+

+
+ +
+ +
+ + + + +
+

+ Don't forget the #0000 part of your username! +

+

+
+ +
+ +

How will you use it?

+ +

Click as many as you plan to use!

+ +
+ +
+ + +
+ + + +
+ + + +
+ + + +
+ + +
+ +
+ +
+ +
+ + + + +
+

+

+
+ +
+ +

Who's voice is this?

+ +

If it's your voice, you'll be able to do anything you want with it. If it's another person's voice, you may have + limits on how you can use it (eg. limits on commercialization.) That doesn't mean we can't help you. Even if you + just want your favorite character so you can make Twitter memes, that's fine!

+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +

How's your audio quality?

+ +

It's okay if you don't have a quality source of audio, but the results are top notch only with a good recording setup.

+ +
+ +
+ + +
+ + +
+ +
+ +

Anything else?

+ + +
+ +
+ +
+
+ +
+ +
+ +
+
+ + + {/* +

Once you've linked Twitch with Storyteller, you can add the following page as an OBS Browser Source:

+ +
+ +
+

+ + + + +

+
+ +
+ + + Open In New Tab  + + +
+
+
+ +

Setup Instructions

+ +

If you're not familiar with OBS or you need a refresher, here's a quick glance at the setup.

+ +
+ +

Step 1) Click to add a new Browser Source

+ + step 1 + +

The bottom of OBS should have a plus icon () to add new sources. + You'll want to add a new Browser Source.

+ +
+ +

Step 2) Paste the URL into the configuration window

+ + step 2 + +
+ +

Remember, the URL is {obsUrl}

+ +
+ +

Step 3) Activate the Page

+ + step 3 + +
+
+ +

Right click the layer and select “Interact”. + This will let you click the button and activate the service.

+ +
+
+ +

Good to go!

+ +

You can now hide the layer and make it invisible. The audio levels are also independently adjustable.

+ +
+ +

Have fun! We'll be back soon with some even more jaw-dropping utilities.

+ */} + +
+ +
+ +
+ ); +} + +export { VoiceCloneRequestPage } \ No newline at end of file