mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 01:18:34 +00:00
F5-TTS countdown timer page (#719)
* f5-tts page * ai tools image and fixes * isLoading session update * update * update * styling updates faq section
This commit is contained in:
1 parent
8831b4aafe
commit
2c8f8b3303
9 files changed
+346
-10
No files matched your search
@@ -41,6 +41,7 @@
|
||||
/edit-cover-image* /website/index.html 200
|
||||
/engine-compositor* /website/index.html 200
|
||||
/explore* /website/index.html 200
|
||||
/f5-tts* /website/index.html 200
|
||||
/face-animator* /website/index.html 200
|
||||
/fbx-to-gltf* /website/index.html 200
|
||||
/firehose* /website/index.html 200
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="1440" height="400" preserveAspectRatio="none" viewBox="0 0 1440 400">
|
||||
<g mask="url("#SvgjsMask1003")" fill="none">
|
||||
<path d="M41 400L441 0L717 0L317 400z" fill="url("#SvgjsLinearGradient1004")"></path>
|
||||
<path d="M243.60000000000002 400L643.6 0L945.1 0L545.1 400z" fill="url("#SvgjsLinearGradient1004")"></path>
|
||||
<path d="M500.20000000000005 400L900.2 0L1231.7 0L831.7 400z" fill="url("#SvgjsLinearGradient1004")"></path>
|
||||
<path d="M741.8000000000001 400L1141.8000000000002 0L1249.8000000000002 0L849.8000000000001 400z" fill="url("#SvgjsLinearGradient1004")"></path>
|
||||
<path d="M1406 400L1006 0L752.5 0L1152.5 400z" fill="url("#SvgjsLinearGradient1005")"></path>
|
||||
<path d="M1181.4 400L781.4000000000001 0L691.4000000000001 0L1091.4 400z" fill="url("#SvgjsLinearGradient1005")"></path>
|
||||
<path d="M953.8 400L553.8 0L403.79999999999995 0L803.8 400z" fill="url("#SvgjsLinearGradient1005")"></path>
|
||||
<path d="M716.1999999999999 400L316.19999999999993 0L149.69999999999993 0L549.6999999999999 400z" fill="url("#SvgjsLinearGradient1005")"></path>
|
||||
<path d="M1219.8180752761318 400L1440 179.81807527613176L1440 400z" fill="url("#SvgjsLinearGradient1004")"></path>
|
||||
<path d="M0 400L220.18192472386824 400L 0 179.81807527613176z" fill="url("#SvgjsLinearGradient1005")"></path>
|
||||
</g>
|
||||
<defs>
|
||||
<mask id="SvgjsMask1003">
|
||||
<rect width="1440" height="400" fill="#ffffff"></rect>
|
||||
</mask>
|
||||
<linearGradient x1="0%" y1="100%" x2="100%" y2="0%" id="SvgjsLinearGradient1004">
|
||||
<stop stop-color="rgba(57, 57, 76, 0.1)" offset="0"></stop>
|
||||
<stop stop-opacity="0" stop-color="rgba(57, 57, 76, 0.1)" offset="0.66"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="100%" y1="100%" x2="0%" y2="0%" id="SvgjsLinearGradient1005">
|
||||
<stop stop-color="rgba(57, 57, 76, 0.1)" offset="0"></stop>
|
||||
<stop stop-opacity="0" stop-color="rgba(57, 57, 76, 0.1)" offset="0.66"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -100,6 +100,13 @@ export default function AITools() {
|
||||
imgSrc: "/images/landing/select-v2v.webp",
|
||||
imgAlt: "Voice Conversion",
|
||||
},
|
||||
{
|
||||
to: "/f5-tts",
|
||||
title: "F5-TTS Voice Cloning",
|
||||
text: "Zero-shot voice cloning",
|
||||
imgSrc: "/images/landing/select-f5-tts.webp",
|
||||
imgAlt: "Voice Cloning",
|
||||
},
|
||||
{
|
||||
to: "/voice-designer",
|
||||
title: t("productVdTitle"),
|
||||
@@ -201,11 +208,15 @@ export default function AITools() {
|
||||
}
|
||||
|
||||
if (currentPath.includes("/style-video")) {
|
||||
items = [items[1], items[2], items[9]];
|
||||
items = [items[1], items[2], items[10]];
|
||||
}
|
||||
|
||||
if (currentPath.includes("/face-animator")) {
|
||||
items = [items[0], items[1], items[9]];
|
||||
items = [items[0], items[1], items[10]];
|
||||
}
|
||||
|
||||
if (currentPath.includes("/webcam-acting")) {
|
||||
items = [items[0], items[1], items[10]];
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -214,7 +225,7 @@ export default function AITools() {
|
||||
currentPath.includes("/live-portrait") ||
|
||||
currentPath.includes("/dev-lp")
|
||||
) {
|
||||
items = [items[0], items[2], items[9]];
|
||||
items = [items[0], items[2], items[10]];
|
||||
}
|
||||
|
||||
return <AIToolsRow {...{ items }} />;
|
||||
|
||||
@@ -441,6 +441,10 @@ const routes = [
|
||||
() => import("./pages/voice_designer/VoiceDesignerMainPage")
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/f5-tts",
|
||||
component: lazy(() => import("./pages/voice_designer_v2/VoiceDesignerV2")),
|
||||
},
|
||||
{
|
||||
path: "/style-video/:mediaToken?",
|
||||
component: lazy(() => import("./pages/style-video")),
|
||||
|
||||
@@ -134,10 +134,12 @@ function FooterNav() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isOnF5TtsPage = location.pathname === "/f5-tts";
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isOnStudioPage && (
|
||||
<footer id="footer">
|
||||
<footer id="footer" className={isOnF5TtsPage ? "mt-0" : ""}>
|
||||
<Container type="panel" className="py-5">
|
||||
<div className="row g-5 m-0">
|
||||
<div className="col-12 col-lg-3 d-flex flex-column gap-4 align-items-center align-items-lg-start pb-2">
|
||||
|
||||
+13
-6
@@ -57,7 +57,7 @@ export default function CreatorToolsPage() {
|
||||
title: t("productVideoStyleTransferTitle"),
|
||||
text: t("productVideoStyleTransferText"),
|
||||
videoSrc: "/videos/ai-tools/vst_video.mp4",
|
||||
videoPosterSrc: "/images/ai-tools/vst_video_poster.jpg",
|
||||
videoPosterSrc: "/images/ai-tools/vst",
|
||||
imgAlt: "Video Style Transfer",
|
||||
badgeContent: {
|
||||
type: "new",
|
||||
@@ -71,7 +71,7 @@ export default function CreatorToolsPage() {
|
||||
title: t("productLivePortraitTitle"),
|
||||
text: t("productLivePortraitText"),
|
||||
videoSrc: "/videos/ai-tools/lp_video.mp4",
|
||||
videoPosterSrc: "/images/ai-tools/lp_video_poster.jpg",
|
||||
videoPosterSrc: "/images/ai-tools/live_portrait",
|
||||
imgAlt: "Live Portrait",
|
||||
badgeContent: {
|
||||
type: "new",
|
||||
@@ -84,7 +84,7 @@ export default function CreatorToolsPage() {
|
||||
title: t("productLipsyncTitle"),
|
||||
text: t("productLipsyncText"),
|
||||
videoSrc: "/videos/ai-tools/ls_video.mp4",
|
||||
videoPosterSrc: "/images/ai-tools/ls_video_poster.jpg",
|
||||
videoPosterSrc: "/images/ai-tools/lipsync",
|
||||
imgAlt: "Lipsync",
|
||||
},
|
||||
{
|
||||
@@ -92,7 +92,7 @@ export default function CreatorToolsPage() {
|
||||
title: "2D Video Compositor",
|
||||
text: "Compose videos and images with AI",
|
||||
videoSrc: "/videos/ai-tools/vcomp_video_2.mp4",
|
||||
videoPosterSrc: "/images/ai-tools/vcomp_video_poster_2.jpg",
|
||||
videoPosterSrc: "/images/ai-tools/2d_vid_com",
|
||||
imgAlt: "Video Compositor",
|
||||
videoPosition: "top",
|
||||
},
|
||||
@@ -101,7 +101,7 @@ export default function CreatorToolsPage() {
|
||||
title: "3D Video Compositor",
|
||||
text: "Build videos with AI 3D engine",
|
||||
videoSrc: "/videos/ai-tools/vcomp_video.mp4",
|
||||
videoPosterSrc: "/images/ai-tools/vcomp_video_poster.jpg",
|
||||
videoPosterSrc: "/images/ai-tools/3d_vid_com",
|
||||
imgAlt: "Video Compositor",
|
||||
},
|
||||
{
|
||||
@@ -109,7 +109,7 @@ export default function CreatorToolsPage() {
|
||||
title: "Webcam Acting",
|
||||
text: "Act as your character through your camera",
|
||||
videoSrc: "/videos/ai-tools/ca_video.mp4",
|
||||
videoPosterSrc: "/images/ai-tools/ca_video_poster.jpg",
|
||||
videoPosterSrc: "/images/ai-tools/webcam_acting",
|
||||
imgAlt: "Video Compositor",
|
||||
},
|
||||
];
|
||||
@@ -129,6 +129,13 @@ export default function CreatorToolsPage() {
|
||||
imgSrc: "/images/landing/select-v2v.webp",
|
||||
imgAlt: "Voice Conversion",
|
||||
},
|
||||
{
|
||||
to: "/f5-tts",
|
||||
title: "F5-TTS Voice Cloning",
|
||||
text: "Zero-shot voice cloning",
|
||||
imgSrc: "/images/landing/select-f5-tts.webp",
|
||||
imgAlt: "Voice Cloning",
|
||||
},
|
||||
{
|
||||
to: "/voice-designer",
|
||||
title: t("productVdTitle"),
|
||||
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
@import "scss/colours.scss";
|
||||
|
||||
.countdown-container {
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
width: 300px;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
@media (max-width: 768px) {
|
||||
padding: 20px 0px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 20px;
|
||||
color: rgba($white, 0.6);
|
||||
margin-bottom: 32px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.countdown-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
background-color: $secondary;
|
||||
border-radius: 8px;
|
||||
padding: 24px 10px;
|
||||
border: 3px solid $primary;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
|
||||
.countdown-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.countdown-value {
|
||||
font-size: 46px;
|
||||
font-weight: bold;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.countdown-label {
|
||||
font-size: 16px;
|
||||
color: rgba($white, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 46px;
|
||||
width: 46px;
|
||||
padding: 20px;
|
||||
background-color: $secondary;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
border: 3px solid rgba($white, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.faq-section {
|
||||
background-color: rgba($secondary, 0.8);
|
||||
border-radius: 12px;
|
||||
padding: 3rem;
|
||||
padding-bottom: 2rem;
|
||||
margin-top: 4rem;
|
||||
margin-bottom: 4rem;
|
||||
max-width: 1100px !important;
|
||||
|
||||
h2 {
|
||||
color: $white;
|
||||
text-align: center;
|
||||
margin-bottom: 36px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.faq-item {
|
||||
margin-bottom: 30px;
|
||||
|
||||
h3 {
|
||||
color: $primary;
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p {
|
||||
color: rgba($white, 0.8);
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.faq-section {
|
||||
padding: 20px;
|
||||
padding-top: 2.5rem;
|
||||
border-radius: 0px;
|
||||
|
||||
.faq-item {
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
import React, { useState, useEffect, useContext } from "react";
|
||||
import "./VoiceDesignerV2.scss";
|
||||
import { Button, Container, Panel } from "components/common";
|
||||
import {
|
||||
faArrowRight,
|
||||
faMicrophoneAlt,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
|
||||
import { AppStateContext } from "components/providers/AppStateProvider";
|
||||
|
||||
const endDate = new Date("2024-10-18T12:00:00-04:00");
|
||||
|
||||
interface TimeLeft {
|
||||
days?: number;
|
||||
hours?: number;
|
||||
minutes?: number;
|
||||
seconds?: number;
|
||||
}
|
||||
|
||||
function calculateTimeLeft(): TimeLeft {
|
||||
const difference = +endDate - +new Date();
|
||||
let timeLeft: TimeLeft = {};
|
||||
|
||||
if (difference > 0) {
|
||||
timeLeft = {
|
||||
days: Math.floor(difference / (1000 * 60 * 60 * 24)),
|
||||
hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
|
||||
minutes: Math.floor((difference / 1000 / 60) % 60),
|
||||
seconds: Math.floor((difference / 1000) % 60),
|
||||
};
|
||||
}
|
||||
|
||||
return timeLeft;
|
||||
}
|
||||
|
||||
export default function VoiceDesignerV2(): JSX.Element {
|
||||
const [timeLeft, setTimeLeft] = useState<TimeLeft>(calculateTimeLeft());
|
||||
const [localEndTime, setLocalEndTime] = useState<string>("");
|
||||
const { sessionWrapper } = useContext(AppStateContext);
|
||||
|
||||
usePrefixedDocumentTitle("F5-TTS Zero-Shot Voice Cloning");
|
||||
|
||||
useEffect(() => {
|
||||
updateLocalEndTime();
|
||||
|
||||
const timer = setInterval(() => {
|
||||
setTimeLeft(calculateTimeLeft());
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
function updateLocalEndTime() {
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
timeZoneName: "short",
|
||||
};
|
||||
setLocalEndTime(endDate.toLocaleDateString("en-US", options));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
background: `url("/images/bg-svg.svg") no-repeat center center`,
|
||||
backgroundSize: "cover",
|
||||
width: "100%",
|
||||
height: "100vh",
|
||||
}}
|
||||
>
|
||||
<Container
|
||||
type="panel"
|
||||
className="d-flex align-items-center justify-content-center"
|
||||
style={{ height: "calc(100vh - 65px)", maxWidth: "768px" }}
|
||||
>
|
||||
<Panel padding={true}>
|
||||
<div className="countdown-container">
|
||||
<FontAwesomeIcon icon={faMicrophoneAlt} className="icon" />
|
||||
<h1 className="fw-bold mb-1">F5-TTS Voice Cloning</h1>
|
||||
<p className="description">
|
||||
New zero-shot voice cloning coming soon...
|
||||
</p>
|
||||
<div className="countdown-wrapper">
|
||||
{Object.entries(timeLeft).map(([interval, value]) => (
|
||||
<div key={interval} className="countdown-item">
|
||||
<span className="countdown-value">{value}</span>
|
||||
<span className="countdown-label">{interval}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="local-time mt-3 text-white opacity-50">
|
||||
<small>Countdown ends local time: {localEndTime}</small>
|
||||
</div>
|
||||
|
||||
{!sessionWrapper.isLoggedIn() && (
|
||||
<Button
|
||||
label="Sign up before launch"
|
||||
className="mt-4"
|
||||
icon={faArrowRight}
|
||||
iconFlip={true}
|
||||
to="/signup"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Panel>
|
||||
</Container>
|
||||
</div>
|
||||
<Container type="panel" className="faq-section">
|
||||
<h2>What is F5-TTS?</h2>
|
||||
<div className="row g-5">
|
||||
<div className="col-md-6 faq-item">
|
||||
<h3>What is F5-TTS?</h3>
|
||||
<p>
|
||||
F5-TTS is an AI-driven text-to-speech tool that transforms written
|
||||
text into lifelike speech. With real-time processing, it’s great
|
||||
for generating dynamic audio content, whether it's for
|
||||
voice-overs, digital storytelling, or any other project that
|
||||
requires high-quality spoken output.
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-md-6 faq-item">
|
||||
<h3>How does F5-TTS work?</h3>
|
||||
<p>
|
||||
F5-TTS leverages advanced AI techniques, like Flow Matching and
|
||||
Diffusion Transformers, to turn text into speech. It skips some of
|
||||
the traditional steps like phoneme alignment and duration
|
||||
prediction, producing more natural-sounding audio directly from
|
||||
your input text.
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-md-6 faq-item">
|
||||
<h3>What kind of audio quality can I expect from F5-TTS?</h3>
|
||||
<p>
|
||||
F5-TTS delivers high-quality audio with clear, natural intonation,
|
||||
making it a perfect fit for professional projects like podcasts,
|
||||
audiobooks, and educational content. The speech it generates is
|
||||
crisp and lifelike, designed to meet the standards of any polished
|
||||
audio production.
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-md-6 faq-item">
|
||||
<h3>Can F5-TTS be used for voice-over work?</h3>
|
||||
<p>
|
||||
Absolutely! F5-TTS is a fantastic tool for voice-over production.
|
||||
Its zero-shot voice cloning feature allows you to create different
|
||||
voices for various characters or narrators, and it even supports
|
||||
emotional expression to add extra nuance and depth to your audio
|
||||
content.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user