better workflow control UI, framescap is more-o-less accurate

This commit is contained in:
wil committed 2024-02-08 12:37:34 -05:00
1 parent a09c53acdb
commit 64d8d7700d
5 files changed
+150 -67

No files matched your search

@@ -1,3 +1,46 @@
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
@@ -5,4 +48,35 @@ export function isInputValid(keyValues: {[key:string]:number|string|boolean|unde
&& 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
},
}
}
@@ -1,8 +1,5 @@
import React, {useState, useRef} from "react";
import { useParams, useHistory } from "react-router-dom";
import { v4 as uuidv4 } from "uuid";
import { faChevronRight } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useMedia } from "hooks";
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
@@ -11,7 +8,6 @@ import { states, Action, State } from "../../videoWorkflowReducer";
import {
Accordion,
Button,
BasicVideo,
ErrorMessage,
InputSeed,
Panel,
@@ -21,9 +17,13 @@ import {
} from "components/common";
import SectionControlNets, {ControlNetsInitialValues as CnIvs} from "./sectionControlNets";
import SectionVideoSettings from "./sectionVideoSettings";
import SectionVideoSettings, {VideoSettingsInitialValues as VideoIvs} from "./sectionVideoSettings";
import SectionAdvanceOptions, {AdvanceOptionsInitialValues as AdvIvs} from "./sectionAdvanceOptions";
import { isInputValid } from "./helpers";
import {
isInputValid,
mapRequest,
WorkflowValuesType
} from "./helpers";
export default function PageFilterControls({
debug, t, pageState, dispatchPageState, parentPath
@@ -49,7 +49,8 @@ export default function PageFilterControls({
},
});
const [workflowValues, setworkflowValues] = useState({
const [workflowValues, setworkflowValues] = useState<WorkflowValuesType>({
fileToken: pageState.mediaFileToken || mediaToken,
outputPath: "vid2vid/SparseUpscaleInterp_00001.mp4",
workflowConfig: "weight_q8sz47gmfw2zx02snrbz88ns9",
seed: "",
@@ -57,11 +58,7 @@ export default function PageFilterControls({
loraModelToken: "",
posPrompt: "",
negPrompt: "",
framesCap:16,
skipFrames:0,
nthFrames:2,
inputFps: 24,
interpolationMutiplier: 2,
...VideoIvs,
...AdvIvs,
...CnIvs
});
@@ -75,33 +72,8 @@ export default function PageFilterControls({
if(debug) console.log(workflowValues)
if (isInputValid(workflowValues)){
const request = {
"uuid_idempotency_token": uuidv4(),
"maybe_sd_model": workflowValues.sdModelToken,
"maybe_workflow_config": workflowValues.workflowConfig,
"maybe_input_file": pageState.mediaFileToken || mediaToken,
"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.nthFrames,
"$.154.inputs.Value": workflowValues.inputFps,
"$.445.inputs.number": workflowValues.interpolationMutiplier,
"$.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
},
}
const request = mapRequest(workflowValues);
console.log(request);
EnqueueVideoWorkflow(request).then(res => {
if (res.success && res.inference_job_token) {
dispatchPageState({
@@ -129,28 +101,43 @@ export default function PageFilterControls({
<>
<Panel>
<div className="row g-3 p-3">
<div className="col-5">
<div className="col-6">
<div className="fy-basic-video">
<video controls ref={videoRef}>
<video controls ref={videoRef}
style={{
maxHeight: "500px",
objectFit: "contain"
}}>
<source src={mediaLink} type="video/mp4"/>
</video>
</div>
</div>
<div className="col-2 d-flex align-items-center justify-content-center">
<FontAwesomeIcon icon={faChevronRight} className="fa-7x"/>
</div>
<div className="col-5">
<BasicVideo src={mediaLink} />
<div className="col-6">
<SectionVideoSettings
workflowValues={workflowValues}
onChange={(key,val)=>handleOnChange(key,val)}
videoElement={videoRef?.current}
/>
<div className="d-flex my-3 justify-content-end">
<Button
className="me-3"
label={t("previewWorkflow")}
tooltip="feature not available yet"
variant="primary"
disabled
/>
<Button
className="me-3"
label={t("enqueueWorkflow")}
onClick={handleGenerate}
variant="primary"
/>
</div>
</div>
</div>
</Panel>
<Accordion className="mt-4">
<Accordion.Item title="Video Settings">
<SectionVideoSettings
onChange={(key,val)=>handleOnChange(key,val)}
videoElement={videoRef?.current}
/>
</Accordion.Item>
<Accordion.Item title={"Basics"} defaultOpen>
<div className="row g-3 p-3">
<SelectModal
@@ -250,7 +237,7 @@ export default function PageFilterControls({
<div className="row g-3 py-3">
<div className="col-12 d-flex justify-content-end">
<Button
label={t("Process Filter")}
label={t("enqueueWorkflow")}
onClick={handleGenerate}
variant="primary"
/>
@@ -1,5 +1,6 @@
import React, { memo } from 'react'
import { NumberSliderV2, Checkbox } from 'components/common'
import { WorkflowValuesType } from './helpers';
export const AdvanceOptionsInitialValues = {
firstPass: 1,
@@ -18,7 +19,7 @@ export default memo( function SectionAdvanceOptions({
workflowValues: wfVal,
onChange : handleOnChange
} : {
workflowValues : any
workflowValues : WorkflowValuesType
onChange: (key:string, val:number|boolean)=>void}
){
@@ -1,5 +1,6 @@
import React, { memo } from 'react'
import { NumberSliderV2 } from 'components/common'
import { WorkflowValuesType } from './helpers';
export const ControlNetsInitialValues = {
cnCanny: 0,
@@ -16,7 +17,7 @@ export default memo( function SectionControlNets({
workflowValues: wfVal,
onChange : handleOnChange
} : {
workflowValues : any
workflowValues : WorkflowValuesType
onChange: (key:string, val:number)=>void}
){
@@ -1,10 +1,11 @@
import React, { memo, useState } from 'react'
import { TempInput as Input } from 'components/common'
import React, { memo } from 'react'
import { TempInput as Input, NumberSliderV2 } from 'components/common'
import { WorkflowValuesType } from './helpers';
export const VideoSettingsInitialValues = {
width: 544,
height: 544,
maxFrames: 17,
framesCap: 16,
skipFrames: 0,
everyNthFrame: 2,
@@ -14,46 +15,65 @@ export const VideoSettingsInitialValues = {
export default memo( function SectionVideoSettings({
onChange : handleOnChange,
workflowValues: wfVal,
videoElement: ve
} : {
onChange: (key:string, val:number)=>void,
workflowValues: WorkflowValuesType,
videoElement: HTMLVideoElement | null,
}){
const iv = VideoSettingsInitialValues;
const [{width, height}, setDimensions] = useState({
width:iv.width, height:iv.height
});
if(ve && ve!==null){
ve.onloadedmetadata = () =>{
if (ve.videoWidth && ve.videoHeight) {
const aspectRatio = ve.videoWidth/ve.videoHeight
if (aspectRatio > 1){
setDimensions({width: 960, height: iv.height});
handleOnChange("width",960);
}
else if (aspectRatio < 1) {
setDimensions({width: iv.width, height: 960});
handleOnChange("height",960);
}else{
console.log(`aspectRaio: ${aspectRatio}`);
}
}
if(ve.duration){
handleOnChange("maxFrames",
Math.floor(ve.duration)*wfVal.inputFps
);
}
//TODO: Optimizer to make ONE handleOnChange only
//TODO: deal with maxFrames with more reliable math
}
}
return(
<>
<div className="row g-3 p-3">
<div className="col-md-6">
<Input label="Width" value={width} readOnly/>
<Input label="Width" value={wfVal.width} readOnly/>
</div>
<div className="col-md-6">
<Input label="Height" value={height} readOnly/>
<Input label="Height" value={wfVal.height} readOnly/>
</div>
</div>
<div className="row g-3 p-3">
<div className="col-md-6">
<Input label="Frames Cap" value={iv.framesCap} readOnly/>
<NumberSliderV2 {...{
min: 16, max: wfVal.maxFrames, step: 1,
initialValue: wfVal.framesCap,
label: "Frames Cap",
thumbTip: "24 frames = 1 sec",
onChange: (val)=>{handleOnChange("framesCap",val)}
}}/>
</div>
<div className="col-md-6">
<Input label="Skip Frames" value={iv.skipFrames} readOnly/>
<NumberSliderV2 {...{
min: 0, max: wfVal.maxFrames-16, step: 1,
initialValue: wfVal.skipFrames,
label: "Skip Frames",
thumbTip: "24 frames = 1 sec",
onChange: (val)=>{handleOnChange("skipFrames",val)}
}}/>
</div>
</div>
<div className="row g-3 p-3">