init studio intro pages

This commit is contained in:
BFlat committed 2024-02-29 15:55:48 +07:00
1 parent 963a13c2a1
commit 5cf73ebe33
11 files changed
+741 -17

No files matched your search

@@ -11,3 +11,9 @@
width: 100%;
border-radius: 0.5rem;
}
.fy-scene-3d-fullscreen-intro {
height: calc(100vh - 66px - 66px);
width: 100%;
border-radius: 0rem;
}
@@ -12,6 +12,7 @@ interface Scene3DProps {
urlParams?: string;
objectId?: string;
sceneMediaFileToken?: string;
className?: string;
}
export default function Scene3D({
@@ -20,8 +21,8 @@ export default function Scene3D({
objectId,
sceneMediaFileToken,
urlParams,
className,
}: Scene3DProps) {
let engineUrl = `https://engine.fakeyou.com?mode=${mode}`;
if (sceneMediaFileToken) {
@@ -35,14 +36,9 @@ export default function Scene3D({
<div
className={`${
fullScreen ? "fy-scene-3d-fullscreen" : "fy-scene-3d-default"
}`.trim()}
} ${className ? className : ""}`.trim()}
>
<IframeResizer
src={engineUrl}
width="100%"
height="100%"
id=""
/>
<IframeResizer src={engineUrl} width="100%" height="100%" id="" />
</div>
);
}
@@ -123,6 +123,8 @@ import TextToImagePage from "./pages/text_to_image/TextToImagePage";
import DomainConfigProvider from "context/DomainConfigContext";
import DevUpload from "./pages/dev_upload/DevUpload";
import DevMediaInput from "./pages/dev_upload/DevMediaInput";
import { StudioIntroPage } from "./pages/storyteller_studio_intro/StudioIntroPage";
import StudioVSTPage from "./pages/storyteller_studio_intro/StudioVST/StudioVSTPage";
interface Props {
sessionWrapper: SessionWrapper;
@@ -755,6 +757,19 @@ class PageContainer extends React.Component<
/>
</Route>
<Route path="/studio-intro/style/:jobToken?">
<StudioVSTPage />
</Route>
<Route path="/studio-intro/:mediaToken?">
<StudioIntroPage
sessionWrapper={this.props.sessionWrapper}
sessionSubscriptionsWrapper={
this.props.sessionSubscriptionsWrapper
}
/>
</Route>
<Route path="/commissions">
<CommunityCommissionsPage />
</Route>
@@ -1309,15 +1324,6 @@ class PageContainer extends React.Component<
/>
</Route>
<Route path="/studio">
<StorytellerStudioListPage
sessionWrapper={this.props.sessionWrapper}
sessionSubscriptionsWrapper={
this.props.sessionSubscriptionsWrapper
}
/>
</Route>
<Route path="/commissions">
<CommunityCommissionsPage />
</Route>
@@ -0,0 +1,5 @@
.studio-intro-page {
display: flex;
flex-direction: column;
height: calc(100vh - 66px);
}
@@ -0,0 +1,71 @@
import React from "react";
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import { StudioNotAvailable } from "v2/view/_common/StudioNotAvailable";
import { Button, Scene3D } from "components/common";
import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle";
import { useParams, useHistory } from "react-router-dom";
import { useInferenceJobs } from "hooks";
import { EnqueueEngineCompositing } from "@storyteller/components/src/api/engine_compositor/EnqueueEngineCompositing";
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
import { v4 as uuidv4 } from "uuid";
import "./StudioIntro.scss";
interface Props {
sessionWrapper: SessionWrapper;
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
}
function StudioIntroPage(props: Props) {
const { mediaToken } = useParams<{ mediaToken: string }>();
const history = useHistory();
const inferenceJobs = useInferenceJobs(
FrontendInferenceJobType.EngineComposition
);
usePrefixedDocumentTitle("Storyteller Studio");
if (!props.sessionWrapper.canAccessStudio()) {
return <StudioNotAvailable />;
}
let engineParams = {};
if (mediaToken) {
engineParams = {
sceneMediaFileToken: mediaToken,
};
} else {
engineParams = {
objectId: "sample-room.gltf",
};
}
const onClick = () => {
EnqueueEngineCompositing("", {
uuid_idempotency_token: uuidv4(),
media_file_token: mediaToken || "",
}).then((res: any) => {
if (res && res.success) {
inferenceJobs.enqueue(res.inference_job_token);
history.push(`/studio-intro/style/${res.inference_job_token}`);
}
});
};
return (
<div className="studio-intro-page">
<Scene3D
fullScreen={true}
mode="studio"
className="flex-grow-1"
{...engineParams}
/>
<div className="d-flex justify-content-center p-3">
<Button label="Create Movie from 3D Scene" onClick={onClick} />
</div>
</div>
);
}
export { StudioIntroPage };
@@ -0,0 +1,140 @@
import React, { useRef, useState } from "react";
import { NavLink, useParams, useHistory } from "react-router-dom";
import {
Button,
Container,
Panel,
SelectionBubbles,
TextArea,
} from "components/common";
import VideoQuickTrim, {
QuickTrimData,
} from "components/common/VideoQuickTrim";
import EnqueueVideoStyleTransfer from "@storyteller/components/src/api/video_styleTransfer";
import { initialValues } from "./defaultValues";
import { mapRequest, VSTType } from "./helpers";
// import SectionAdvanceOptions from "./sectionAdvanceOptions";
export default function PageVSTApp() {
const { jobToken } = useParams<any>();
const videoRef = useRef<HTMLVideoElement>(null);
const history = useHistory();
const [isLoading, setIsLoading] = useState(false);
const [mediaToken, setMediaToken] = useState("");
const [vstValues, setVstValues] = useState<VSTType>({
...initialValues,
fileToken: mediaToken,
});
const handleOnChange = (val: {
[key: string]: number | string | boolean | undefined;
}) => {
setVstValues(curr => ({ ...curr, ...val }));
};
const handleGenerate = () => {
const request = mapRequest(vstValues);
EnqueueVideoStyleTransfer(request).then(res => {
if (res.success && res.inference_job_token) {
console.log("Job enqueued successfully", res.inference_job_token);
history.push(`/jobs`); // Adjusted to use a fixed path
} else {
console.log("Failed to enqueue job", res);
}
});
};
if (videoRef?.current) {
const ve = videoRef.current;
ve.onloadedmetadata = () => {
const newValues: {
width?: number;
height?: number;
maxDuration?: number;
} = {};
if (ve.duration) {
newValues.maxDuration = ve.duration;
}
setVstValues(curr => ({
...curr,
...newValues,
}));
};
}
const styleMap: { [key: string]: string } = {
Anime: "weight_yqexh77ntqyawzgh9fzash798",
Pixel: "weight_yqexh77ntqyawzgh9fzash798",
Painting: "weight_yqexh77ntqyawzgh9fzash798",
};
const handleStyleSelection = (selected: string) => {
const selectedSdModelToken = styleMap[selected];
setVstValues(curr => ({ ...curr, sdModelToken: selectedSdModelToken }));
};
const styleOptions = Object.keys(styleMap);
return (
<Container type="panel" className="mt-5">
<Panel padding={true}>
<div className="row g-4">
<div className="col-12 col-md-6">
<VideoQuickTrim
mediaToken={vstValues.fileToken}
onSelect={(val: QuickTrimData) =>
handleOnChange({
trimStart: val.trimStartSeconds,
trimEnd: val.trimEndSeconds,
})
}
/>
</div>
<div className="col-12 col-md-6 d-flex flex-column gap-3">
<div>
<label className="sub-title">Select a Style</label>
<SelectionBubbles
options={styleOptions}
onSelect={handleStyleSelection}
selectedStyle="fill"
/>
</div>
<TextArea
label="Prompt"
placeholder="Enter your prompt"
onChange={e => handleOnChange({ posPrompt: e.target.value })}
value={vstValues.posPrompt}
required={false}
/>
{/* <TextArea
label="Negative Prompt"
placeholder="Enter your negative prompt"
onChange={e => handleOnChange({ negPrompt: e.target.value })}
value={vstValues.negPrompt}
required={false}
/> */}
{/* <SectionAdvanceOptions
onChange={handleOnChange}
vstValues={vstValues}
/> */}
</div>
</div>
<div className="row g-3 mt-4">
<div className="col-12 d-flex justify-content-between">
<NavLink to="/">
<Button label="Cancel" variant="primary" />
</NavLink>
<Button
label="Enqueue"
onClick={handleGenerate}
variant="primary"
disabled={vstValues.trimEnd === 0}
/>
</div>
</div>
</Panel>
</Container>
);
}
@@ -0,0 +1,98 @@
type CNPreset =
'custom'|
'closeup'|
'default' |
'halfbody'|
'fullbody'|
'landscape'|
'typog';
const defaultPreset = {
cnCanny: 0,
cnDepth: 0,
cnLineArtAnime: 0.7,
cnLineArtRealistic: 0,
cnLipsStrength: 0,
cnOpenPose: 1,
cnPipeFace: 0,
cnSparseScribble: 0,
cnSoftEdge: 1,
cnRegularSteps: 20,
}
const closeupPreset = {
cnCanny: 0,
cnDepth: 0,
cnLineArtAnime: 0.7,
cnLineArtRealistic: 0,
cnLipsStrength: 0,
cnOpenPose: 0,
cnPipeFace: 0,
cnSparseScribble: 0.7,
cnSoftEdge: 0.7,
cnRegularSteps: 20,
}
const halfbodyPreset = {
cnCanny: 0,
cnDepth: 0,
cnLineArtAnime: 0.7,
cnLineArtRealistic: 0,
cnLipsStrength: 0,
cnOpenPose: 0.7,
cnPipeFace: 0,
cnSparseScribble: 0,
cnSoftEdge: 1,
cnRegularSteps: 20,
}
const fullbodyPreset = {
cnCanny: 0,
cnDepth: 0,
cnLineArtAnime: 0.7,
cnLineArtRealistic: 0,
cnLipsStrength: 0,
cnOpenPose: 1,
cnPipeFace: 0,
cnSparseScribble: 0,
cnSoftEdge: 1,
cnRegularSteps: 20,
}
const landscapePreset = {
cnCanny: 0,
cnDepth: 0.4,
cnLineArtAnime: 0.7,
cnLineArtRealistic: 0,
cnLipsStrength: 0,
cnOpenPose: 0,
cnPipeFace: 0,
cnSparseScribble: 0,
cnSoftEdge: 0,
cnRegularSteps: 20,
}
const typogPreset = {
cnCanny: 0,
cnDepth: 0,
cnLineArtAnime: 0.7,
cnLineArtRealistic: 0,
cnLipsStrength: 0,
cnOpenPose: 0,
cnPipeFace: 0,
cnSparseScribble: 0,
cnSoftEdge: 0.7,
cnRegularSteps: 20,
}
export type {
CNPreset
}
export {
defaultPreset,
closeupPreset,
halfbodyPreset,
fullbodyPreset,
landscapePreset,
typogPreset,
};
@@ -0,0 +1,36 @@
import { defaultPreset as defaultCN } from "./cnPresets";
export { initialValues, hiddenValues };
const initialValues = {
//files Settings
fileToken: "",
outputPath: "vid2vid/SparseUpscaleInterp_00001.mp4",
//video Settings
width: 1024,
height: 1024,
maxDuration: 0,
trimStart: 0,
trimEnd: 0,
//Presents
workflowConfig: "weight_99tz2nzbp5x9v55gqn5ekj1sd",
sdModelToken: "weight_yqexh77ntqyawzgh9fzash798",
loraModelToken: "",
loraModelStrength: 0,
//basics
inputFps: 24,
posPrompt: "perfect anime illustration,(best-quality:0.8),",
negPrompt: "",
visibility: "private",
//Control Net
...defaultCN,
};
const hiddenValues = {
posPrompt: "",
negPrompt:
"flare, lens flare, glare,naked, nsfw, text, logo, watermark:1.3, letterboxed,embedding:easynegative, embedding:badhandv4,(worst quality, low quality:1.4), lowres, blurry, monochrome, ",
};
@@ -0,0 +1,92 @@
import { v4 as uuidv4 } from "uuid";
import { hiddenValues } from "./defaultValues";
export type VSTType = {
// File Settings
fileToken: string;
outputPath: string;
// Video Settings
width: number;
height: number;
maxDuration: number;
trimStart: number;
trimEnd: number;
// Presets
workflowConfig: string;
sdModelToken: string;
// loraModelToken: string;
// loraModelStrength: number;
//basics
inputFps: number;
posPrompt: string;
negPrompt: string;
visibility: string;
//advance
cnCanny: number;
cnDepth: number;
cnLineArtAnime: number;
cnLineArtRealistic: number;
cnLipsStrength: number;
cnOpenPose: number;
cnPipeFace: number;
cnSparseScribble: number;
cnSoftEdge: number;
cnRegularSteps: number
}
export function isInputValid(keyValues: {[key:string]:number|string|boolean|undefined}){
return (
keyValues.sdModelToken
&& keyValues.sdModelToken !== ""
&& keyValues.posPrompt
&& keyValues.posPrompt !== ""
);
}
export function mapRequest(vstValues: VSTType){
//TODO: improve the typing of this
return{
"uuid_idempotency_token": uuidv4(),
"maybe_sd_model": vstValues.sdModelToken,
"maybe_workflow_config":vstValues.workflowConfig,
"maybe_input_file": vstValues.fileToken,
"maybe_trim_start_seconds": vstValues.trimStart,
"maybe_trim_end_seconds": vstValues.trimEnd,
"maybe_output_path": vstValues.outputPath,
"creator_set_visibility": vstValues.visibility,
"maybe_json_modifications": {
"$.154.inputs.Value": vstValues.inputFps,
"$.510.inputs.Text":
hiddenValues.posPrompt + vstValues.posPrompt,
"$.8.inputs.text":
hiddenValues.negPrompt+ vstValues.negPrompt,
"$.401.inputs.Value": 0.7, //Denoise First Pass
"$.140.inputs.Value": 1, // Every nth frame
"$.536.inputs.boolean_number": 1, // use LCM
"$.1075.inputs.Value": 1280,
"$.1076.inputs.Value": 720,
"$.1079.inputs.Value": 1280,
"$.1080.inputs.Value": 720,
"$.403.inputs.Value": vstValues.cnSparseScribble,
"$.771.inputs.Value": vstValues.cnOpenPose,
"$.772.inputs.Value": vstValues.cnDepth,
"$.796.inputs.Value": vstValues.cnLineArtRealistic,
"$.797.inputs.Value": vstValues.cnLineArtAnime,
"$.1398.inputs.Value": vstValues.cnSoftEdge,
"$.1531.inputs.Value": vstValues.cnRegularSteps,
//"$.800.inputs.Value": 0, // vstValues.cnCanny,
//"$.1636.inputs.Value": 0, //vstValues.cnLipsStrength,
// "$.208.inputs.lora_01": vstValues.loraModelToken,
// "$.208.inputs.strength_01": vstValues.loraModelStrength
// "$.1449.inputs.filename_prefix": "vid2vid/SparseUpscaleInterp",
},
}
}
@@ -0,0 +1 @@
export { default } from "./StudioVSTPage";
@@ -0,0 +1,273 @@
import React, {useState} from 'react';
import {
faBuildingColumns,
faFaceViewfinder,
faPerson,
// faRotateLeft,
faStar,
// faSparkles,
faText,
faUser,
}
from "@fortawesome/pro-solid-svg-icons";
import {
// Accordion,
Button,
Label,
// NumberSliderV2
} from 'components/common';
import { VSTType } from './helpers';
import {
CNPreset,
closeupPreset,
halfbodyPreset,
fullbodyPreset,
landscapePreset,
typogPreset,
defaultPreset,
} from './cnPresets';
export default function SectionAdvanceOptions({
vstValues: vstVal,
onChange: handleOnChange
}:{
vstValues : VSTType,
onChange: (val:{[key: string]: number|string|boolean|undefined})=>void
}){
const [preset, pickPreset] = useState<CNPreset>('default')
function handlePreset(newPreset: CNPreset){
switch (newPreset){
case 'closeup':
handleOnChange(closeupPreset);
break;
case 'halfbody':
handleOnChange(halfbodyPreset);
break;
case 'fullbody':
handleOnChange(fullbodyPreset);
break;
case 'landscape':
handleOnChange(landscapePreset);
break;
case 'typog':
handleOnChange(typogPreset);
break;
case 'custom':
default:
handleOnChange(defaultPreset);
break;
}
pickPreset(newPreset);
}
return (
<>
<Label label="Video's Camera Angle"/>
{/* <div className="d-flex flex-wrap w-100">
<div className="my-1 me-1">
<Button
icon={faStar}
label="Default"
isActive={preset==='default'}
onClick={()=>handlePreset('default')}
/>
</div>
<div className="my-1 me-1">
<Button
icon={faSparkles}
label="Custom"
isActive={preset==='custom'}
disabled
/>
</div>
</div> */}
<div className="d-flex flex-wrap w-100">
<div className="m-1">
<Button
icon={faStar}
label="Default"
isActive={preset==='default'}
onClick={()=>handlePreset('default')}
/>
</div>
<div className="m-1">
<Button
icon={faFaceViewfinder}
label="Face Closeups"
isActive={preset==='closeup'}
onClick={()=>handlePreset('closeup')}
/>
</div>
<div className="m-1">
<Button
icon={faUser}
label="Half Body Shots"
isActive={preset==='halfbody'}
onClick={()=>handlePreset('halfbody')}
/>
</div>
<div className="m-1">
<Button
icon={faPerson}
label="Full Body Shots"
isActive={preset==='fullbody'}
onClick={()=>handlePreset('fullbody')}
/>
</div>
<div className="m-1">
<Button
icon={faBuildingColumns}
label="Architecture or Landscape"
isActive={preset==='landscape'}
onClick={()=>handlePreset('landscape')}
/>
</div>
<div className="m-1">
<Button
icon={faText}
label="Flat Logos, or Typographies"
isActive={preset==='typog'}
onClick={()=>handlePreset('typog')}
/>
</div>
</div>
{/* <Accordion className="mt-4">
<Accordion.Item title={"Advanced Options"}>
<div className="row g-3 p-3">
<div className="col-md-6">
<NumberSliderV2 {...{
min: 0, max: 1, step: 0.1,
initialValue: vstVal.cnCanny,
label: "Canny",
thumbTip: "Canny",
onChange: (val)=>{
handleOnChange({cnCanny: val});
pickPreset('custom');
}
}}/>
</div>
<div className="col-md-6">
<NumberSliderV2 {...{
min: 0, max: 1, step: 0.1,
initialValue: vstVal.cnDepth,
label: "Depth",
thumbTip: "Depth",
onChange: (val)=>{
handleOnChange({cnDepth: val});
pickPreset('custom');
}
}}/>
</div>
</div>
<div className="row g-3 p-3">
<div className="col-md-6">
<NumberSliderV2 {...{
min: 0, max: 1, step: 0.1,
initialValue: vstVal.cnLineArtAnime,
label: "Line Art - Anime",
thumbTip: "Line Art - Anime",
onChange: (val)=>{
handleOnChange({cnLineArtAnime: val});
pickPreset('custom');
}
}}/>
</div>
<div className="col-md-6">
<NumberSliderV2 {...{
min: 0, max: 1, step: 0.1,
initialValue: vstVal.cnLineArtRealistic,
label: "Line Art - Realistic",
thumbTip: "Line Art - Realistic)",
onChange: (val)=>{
handleOnChange({cnLineArtRealistic: val});
pickPreset('custom');
}
}}/>
</div>
</div>
<div className="row g-3 p-3">
<div className="col-md-6">
<NumberSliderV2 {...{
min: 0, max: 1, step: 0.1,
initialValue: vstVal.cnOpenPose,
label: "OpenPose",
thumbTip: "OpenPose",
onChange: (val)=>{
handleOnChange({cnOpenPose: val});
pickPreset('custom');
}
}}/>
</div>
<div className="col-md-6">
<NumberSliderV2 {...{
min: 0, max: 1, step: 0.1,
initialValue: vstVal.cnPipeFace,
label: "Media Pipe Face",
thumbTip: "Media Pipe Face",
onChange: (val)=>{
handleOnChange({cnPipeFace: val})
pickPreset('custom');
}
}}/>
</div>
</div>
<div className="row g-3 p-3">
<div className="col-md-6">
<NumberSliderV2 {...{
min: 0, max: 1, step: 0.1,
initialValue: vstVal.cnSparseScribble,
label: "Sparse Scribble",
thumbTip: "Sparse Scribble",
onChange: (val)=>{
handleOnChange({cnSparseScribble: val})
pickPreset('custom');
}
}}/>
</div>
<div className="col-md-6">
<NumberSliderV2 {...{
min: 0, max: 1, step: 0.1,
initialValue: vstVal.cnSoftEdge,
label: "Soft Edge",
thumbTip: "SoftEdge",
onChange: (val)=>{
handleOnChange({cnSoftEdge: val});
pickPreset('custom');
}
}}/>
</div>
</div>
{/* <div className="row g-3 p-3">
<div className="col-md-6">
<NumberSliderV2 {...{
min: 1, max: 64, step: 1,
initialValue: vstVal.cnRegularSteps,
label: "Regular Steps",
thumbTip: "Regular Steps",
onChange: (val)=>{
handleOnChange({cnRegularSteps: val});
pickPreset('custom');
}
}}/>
</div>
</div>
<div className="d-flex p-3 justify-content-end">
<Button
icon={faRotateLeft}
label="Reset to Default"
onClick={()=>{
handlePreset('default');
pickPreset('default');
}}
/>
</div>
</Accordion.Item>
</Accordion> */}
</>
);
}