control panel ui is done

This commit is contained in:
wil committed 2024-02-15 14:48:04 -05:00
1 parent 094cda6525
commit 3e023a5d81
6 files changed
+336 -12

No files matched your search

-1
View File
@@ -211,7 +211,6 @@ function initInferenceJobsByCategoryMap(): Map<
inferenceJobsByCategory.set(FrontendInferenceJobType.ImageGeneration, []);
inferenceJobsByCategory.set(FrontendInferenceJobType.ConvertFbxtoGltf, []);
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoWorkflow, [])
return inferenceJobsByCategory;
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoStyleTransfer, [])
return inferenceJobsByCategory;
}
@@ -1,23 +1,29 @@
import React, { useState } from "react";
import React, { useState, forwardRef } from "react";
import { useMedia } from "hooks";
import { MediaFile } from "@storyteller/components/src/api/media_files/GetMediaFile";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
import makeClass from "resources/makeClass";
import { Label } from "components/common"
interface Props {
className?: string;
src?: string;
mediaToken?: string;
label?: string;
}
export default function VideoFromFakeyou({
type Ref = HTMLVideoElement;
const VideoFromFakeyou = forwardRef<Ref, Props>(({
className,
src,
mediaToken,
label,
...rest
}: Props) {
}: Props, ref) => {
const [mediaFile, setMediaFile] = useState<MediaFile>();
useMedia({
mediaToken: mediaToken,
@@ -31,11 +37,14 @@ export default function VideoFromFakeyou({
if (mediaLink){
return (
<div {...{ ...makeClass("fy-basic-video",className) }}>
<video controls key={mediaToken} {...rest}>
{label && <Label label={label}/>}
<video controls key={mediaToken} {...rest} ref={ref}>
<source src={mediaLink} type="video/mp4" />
</video>
</div>
)
}
return null;
};
});
export default VideoFromFakeyou;
@@ -39,6 +39,7 @@ import TextArea from "./TextArea";
import TextAreaV2 from "./TextAreaV2";
import TintSpinner from "./TintSpinner";
import VideoBasic from "./VideoBasic";
import VideoFakeyou from "./VideoFakeyou";
import VideoInput from "./VideoInput";
export {
@@ -83,5 +84,6 @@ export {
TextAreaV2,
TintSpinner,
VideoBasic,
VideoFakeyou,
VideoInput
};
@@ -0,0 +1,105 @@
import React from 'react';
import { v4 as uuidv4 } from "uuid";
export type WorkflowValuesType = {
// File Settings
fileToken: string;
outputPath: string;
// Video Settings
width: number;
height: number;
maxFrames: number;
framesCap: number;
skipFrames: number;
everyNthFrame: number;
inputFps: number;
interpolationMultiplier: number;
// Basic Inputs
workflowConfig: string;
seed: string;
sdModelToken: string;
loraModelToken: string;
posPrompt: string;
negPrompt: string;
// Advance Options Values
firstPass: number;
upscalePass: number;
motionScale: number;
upscaleMultiplier: number;
useEmptyLatent: boolean;
useFaceDetailer: boolean;
denoiseFaceDetailer: number;
useLCM: boolean;
lcmCFG: number;
lcmSteps: number;
// Control Net Values
cnCanny: number;
cnDepth: number;
cnLinearAnime: number;
cnLinearRealistic: number;
cnOpenPose: number;
cnPipeFace: number;
cnSparse: number;
cnTile: number;
}
export function isInputValid(keyValues: {[key:string]:number|string|boolean|undefined}){
return (
keyValues.sdModelToken
&& keyValues.sdModelToken !== ""
&& keyValues.posPrompt
&& keyValues.posPrompt !== ""
);
}
export function mapRequest(workflowValues: WorkflowValuesType){
//TODO: improve the typing of this
return {
"uuid_idempotency_token": uuidv4(),
"maybe_sd_model": workflowValues.sdModelToken,
"maybe_workflow_config": workflowValues.workflowConfig,
"maybe_input_file": workflowValues.fileToken,
"maybe_output_path": workflowValues.outputPath,
"maybe_json_modifications": {
"$.510.inputs.Text": workflowValues.posPrompt,
"$.8.inputs.text": workflowValues.negPrompt,
"$.173.inputs.seed": workflowValues.seed,
"$.401.inputs.Value": workflowValues.firstPass,
"$.918.inputs.Value": workflowValues.motionScale,
"$.137.inputs.Value": workflowValues.framesCap,
"$.186.inputs.Value": workflowValues.skipFrames,
"$.140.inputs.Value": workflowValues.everyNthFrame,
"$.154.inputs.Value": workflowValues.inputFps,
"$.445.inputs.number": workflowValues.interpolationMultiplier,
"$.947.inputs.Value": workflowValues.cnTile,
"$.800.inputs.Value": workflowValues.cnCanny,
"$.797.inputs.Value": workflowValues.cnLinearAnime,
"$.796.inputs.Value": workflowValues.cnLinearRealistic,
"$.772.inputs.Value": workflowValues.cnDepth,
"$.771.inputs.Value": workflowValues.cnOpenPose,
"$.527.inputs.Value": workflowValues.cnPipeFace,
"$.403.inputs.Value": workflowValues.cnSparse
},
}
}
export function TableOfKeyValues(
{
keyValues,
maxHeight=500
}:{
keyValues:{
[key:string]:number|string|boolean|undefined
};
maxHeight?: number
}){
return(<table style={{
maxHeight: maxHeight,
overflow: "scroll",
border: "1px solid white",
borderRadius: "1rem",
}}>{
Object.entries(keyValues).map(([key, val], index: number)=>{
return <tr key={index}><td>{`${key}`}</td><td>{`${val}`}</td></tr>
})
}</table>);
}
@@ -0,0 +1,45 @@
const initialValues = {
//config
outputPath: "vid2vid/SparseUpscaleInterp_00001.mp4",
workflowConfig: "weight_q8sz47gmfw2zx02snrbz88ns9",
//video Settings
width: 544,
height: 544,
maxFrames: 17,
framesCap: 16,
skipFrames: 0,
everyNthFrame: 2,
inputFps: 24,
interpolationMultiplier: 2,
//basics
seed: "",
sdModelToken: "",
loraModelToken: "",
posPrompt: "",
negPrompt: "",
//advance
firstPass: 1,
upscalePass: 0.42,
motionScale:1,
upscaleMultiplier: 1.5,
useEmptyLatent: false,
useFaceDetailer: false,
denoiseFaceDetailer: 0.45,
useLCM: false,
lcmCFG: 2,
lcmSteps: 8,
//Control Net
cnCanny: 0,
cnDepth: 0,
cnLinearAnime: 0,
cnLinearRealistic: 0,
cnOpenPose: 0,
cnPipeFace: 0,
cnSparse: 0.7,
cnTile: 0,
};
export default initialValues;
@@ -1,13 +1,29 @@
import React from "react";
import React, { useRef, useState } from "react";
import { useParams, useHistory } from "react-router-dom";
import { useMedia } from "hooks";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
import { EnqueueVideoWorkflow } from "@storyteller/components/src/api/video_workflow";
import { states, Action, State } from "../../reducer";
import ivs from "./initialValues";
import {
isInputValid,
mapRequest,
WorkflowValuesType,
TableOfKeyValues
} from "./helpers";
export default function pageVSTApp({
debug, t, pageState, dispatchPageState, parentPath
import {
Button,
Label,
NumberSliderV2,
Panel,
SelectModal,
TextArea,
VideoFakeyou
} from "components/common";
export default function PageVSTApp({
debug=true, t, pageState, dispatchPageState, parentPath
}: {
debug?: boolean;
t: Function;
@@ -15,7 +31,155 @@ export default function pageVSTApp({
parentPath: string;
dispatchPageState: (action: Action) => void;
}) {
const { mediaToken } = useParams<any>();
const [workflowValues, setWorkflowValues] = useState<WorkflowValuesType>({
fileToken: pageState.mediaFileToken || mediaToken,
...ivs
});
const videoRef = useRef<HTMLVideoElement>(null);
if(videoRef?.current){
const ve = videoRef.current
ve.onloadedmetadata = () =>{
if (ve.videoWidth && ve.videoHeight) {
const aspectRatio = ve.videoWidth/ve.videoHeight
if (aspectRatio > 1){
handleOnChange("width",960);
}
else if (aspectRatio < 1) {
handleOnChange("height",960);
}else{
if (debug) console.log(`aspectRaio: ${aspectRatio}`);
}
}
if(ve.duration){
handleOnChange("maxFrames",
Math.floor(ve.duration)*workflowValues.inputFps
);
}
//TODO: Optimizer to make ONE handleOnChange only
//TODO: deal with maxFrames with more reliable math
}
}
const handleOnChange = (key: string, newValue:any,) => {
setWorkflowValues((curr)=>({...curr, [key]: newValue}));
}
const history = useHistory();
const handleGenerate = ()=>{
if(debug) console.log(workflowValues)
if (isInputValid(workflowValues)){
const request = mapRequest(workflowValues);
console.log(request);
EnqueueVideoWorkflow(request).then(res => {
if (res.success && res.inference_job_token) {
dispatchPageState({
type: 'enqueueFilterSuccess',
payload: {
inferenceJobToken: res.inference_job_token
}
})
}else{
console.log(res);
}
})
dispatchPageState({type: 'enqueueFilter'})
history.push(`${parentPath}/jobs`);
}else{
alert("you must pick an sd weight and input a prompt");
}
}
return(
<p>hi</p>
<Panel className="mb-4 p-4">
<div className="row g-3 mb-4">
<div className="col-12 col-md-6">
<VideoFakeyou
label={t("video.label.original")}
mediaToken={workflowValues.fileToken}
ref={videoRef}
/>
</div>
<div className="col-12 col-md-6">
<Label label={t("image.label.preview")}/>
{debug && <p><TableOfKeyValues keyValues={workflowValues}/></p>}
</div>
</div>
<div className="row g-3 mb-4">
<div className="col-12 col-md-6">
<SelectModal
modalTitle={t("modal.title.selectStyle")}
label={t("input.label.selectStyle")}
onSelect={({token})=>{
handleOnChange("sdModelToken", token);
}}
tabs={[
{
label: "All Weights",
tabKey: "allWeights",
typeFilter: "sd_1.5",
searcher: true,
type: "weights",
},
{
label: "Bookmarked",
tabKey: "bookmarkedWeights",
typeFilter: "sd_1.5",
searcher: false,
type: "weights",
},
]}
/>
<br/>
<NumberSliderV2 {...{
min: 1, max: 10, step: 1,
initialValue: workflowValues.upscaleMultiplier,
label: t("input.label.styleStrength"),
thumbTip: "24 frames = 1 sec",
withRevert:true,
onChange: (val)=>{handleOnChange("upscaleMultiplier",val)}
}}/>
</div>
<div className="col-12 col-md-6">
<TextArea
{...{
label: t("input.label.prompt"),
placeholder: t("input.placeholder.prompt"),
onChange: (e:React.ChangeEvent<HTMLTextAreaElement>)=>handleOnChange("posPrompt", e.target.value),
value: workflowValues.posPrompt,
required: false,
}}
/>
<br/>
<br/>
<TextArea
{...{
label: t("input.label.negPrompt"),
placeholder: t("input.placeholder.negPrompt"),
onChange: (e:React.ChangeEvent<HTMLTextAreaElement>)=>handleOnChange("negPrompt", e.target.value),
value: workflowValues.negPrompt,
required: false,
}}
/>
</div>
</div>
<div className="row g-3">
<div className="col-12 d-flex justify-content-between">
<Button
label={t("button.cancel")}
// onClick={handleGenerate}
variant="primary"
/>
<Button
label={t("button.enqueueWorkflow")}
onClick={handleGenerate}
variant="primary"
/>
</div>
</div>
</Panel>
);
};