mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-14 13:52:11 +00:00
Merge branch 'feature/mvp' of https://github.com/storytold/storyteller-frontend into feature/mvp
This commit is contained in:
commit
a95124b094
17 files changed
+642
-62
No files matched your search
@@ -496,6 +496,11 @@ class ApiConfig {
|
||||
return `${this.getApiOrigin()}/v1/mocap/mocapnet/create`;
|
||||
}
|
||||
|
||||
// =============== Storyteller Filter ===============
|
||||
enqueueStorytellerFilter(): string {
|
||||
return `${this.getApiOrigin()}/v1/workflow/comfy/create`;
|
||||
}
|
||||
|
||||
// =============== Convert FBX to glTF ===============
|
||||
enqueueFbxToGltf(): string {
|
||||
return `${this.getApiOrigin()}/v1/conversion/enqueue_fbx_to_gltf`;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
|
||||
export interface EnqueueStorytellerFilterRequest {
|
||||
"uuid_idempotency_token": string,
|
||||
|
||||
"maybe_sd_model": string,
|
||||
"maybe_workflow_config": string,
|
||||
"maybe_input_file": string,
|
||||
"maybe_output_path": string,
|
||||
"maybe_json_modifications": {
|
||||
"$.510.inputs.Text": string,
|
||||
"$.8.inputs.text": string,
|
||||
"$.173.inputs.seed": string,
|
||||
"$.401.inputs.Value": number,
|
||||
"$.918.inputs.Value": number,
|
||||
"$.137.inputs.Value": number,
|
||||
"$.186.inputs.Value": number,
|
||||
"$.140.inputs.Value":number,
|
||||
"$.154.inputs.Value": number,
|
||||
"$.445.inputs.number": number,
|
||||
"$.947.inputs.Value": number,
|
||||
"$.800.inputs.Value": number,
|
||||
"$.797.inputs.Value": number,
|
||||
"$.796.inputs.Value": number,
|
||||
"$.772.inputs.Value": number,
|
||||
"$.771.inputs.Value": number,
|
||||
"$.527.inputs.Value": number,
|
||||
"$.403.inputs.Value": number,
|
||||
}
|
||||
}
|
||||
|
||||
export interface EnqueueStorytellerFilterResponse {
|
||||
success: boolean,
|
||||
inference_job_token?: string,
|
||||
}
|
||||
|
||||
export async function EnqueueStorytellerFilter(request: EnqueueStorytellerFilterRequest) : Promise<EnqueueStorytellerFilterResponse>
|
||||
{
|
||||
// const endpoint = "/v1/workflow/comfy/create";
|
||||
|
||||
const endpoint = new ApiConfig().enqueueStorytellerFilter();
|
||||
|
||||
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 };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export type {
|
||||
EnqueueStorytellerFilterRequest, EnqueueStorytellerFilterResponse,
|
||||
} from './EnqueueStorytellerFilter';
|
||||
|
||||
export {
|
||||
EnqueueStorytellerFilter
|
||||
} from './EnqueueStorytellerFilter';
|
||||
@@ -11,7 +11,8 @@ export enum FrontendInferenceJobType {
|
||||
ImageGeneration,
|
||||
VideoMotionCapture,
|
||||
ConvertFbxtoGltf,
|
||||
EngineComposition
|
||||
EngineComposition,
|
||||
StorytellerFilter,
|
||||
}
|
||||
|
||||
// NB: Many of these fields are optional despite the response payload containing them
|
||||
|
||||
@@ -210,6 +210,7 @@ function initInferenceJobsByCategoryMap(): Map<
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoMotionCapture, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.ImageGeneration, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.ConvertFbxtoGltf, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.StorytellerFilter, [])
|
||||
return inferenceJobsByCategory;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@ import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
icon?: IconDefinition;
|
||||
label?: string;
|
||||
textArea?: boolean;
|
||||
// textArea?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Input({
|
||||
label,
|
||||
icon,
|
||||
textArea,
|
||||
// textArea,
|
||||
className,
|
||||
...rest
|
||||
}: InputProps) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./inputSeed";
|
||||
@@ -0,0 +1,97 @@
|
||||
import React, {useState, useEffect, useCallback, memo} from 'react';
|
||||
import { useLocalize } from 'hooks';
|
||||
import {
|
||||
Input,
|
||||
SegmentButtons
|
||||
} from "components/common";
|
||||
|
||||
import generateRandomSeed from 'resources/generateRandomSeed';
|
||||
|
||||
export default memo (function InputSeed({
|
||||
t: tProps,
|
||||
label,
|
||||
initialValue : initialValueProps,
|
||||
onChange : onChangeCallback
|
||||
}:{
|
||||
t?: Function
|
||||
label:string;
|
||||
initialValue?: string;
|
||||
onChange: (newSeed: string)=>void;
|
||||
}){
|
||||
const {t:tHook} = useLocalize("SeedInput");
|
||||
const t = tProps ? tProps : tHook;
|
||||
const[{firstLoad, inputType, seedValue}, setState] = useState<{
|
||||
firstLoad: boolean;
|
||||
inputType: "random" | "custom";
|
||||
seedValue: string;
|
||||
}>({
|
||||
firstLoad: false,
|
||||
inputType: "random",
|
||||
seedValue: ""
|
||||
})
|
||||
const setNewRandomSeed = useCallback((newType:"custom"|"random")=>{
|
||||
const newRandom = generateRandomSeed();
|
||||
setState({
|
||||
firstLoad: true,
|
||||
inputType: newType,
|
||||
seedValue: newRandom
|
||||
});
|
||||
onChangeCallback(newRandom);
|
||||
}, [onChangeCallback]);
|
||||
useEffect(()=>{
|
||||
if (!initialValueProps && !firstLoad){
|
||||
setNewRandomSeed("random");
|
||||
}
|
||||
},[initialValueProps, firstLoad, setNewRandomSeed]);
|
||||
|
||||
|
||||
const handleInputTypeChange = (e:any) => {
|
||||
const newValue = e.target.value;
|
||||
if (newValue === "custom") {
|
||||
setNewRandomSeed("custom");
|
||||
} else {
|
||||
setNewRandomSeed("random");
|
||||
}
|
||||
};
|
||||
const handleSeedChange = (e: any) => {
|
||||
const customSeed = e.target.value;
|
||||
setState({
|
||||
firstLoad: true,
|
||||
inputType: "custom",
|
||||
seedValue: customSeed
|
||||
})
|
||||
onChangeCallback(customSeed);
|
||||
};
|
||||
const handleOnBlur = () => {
|
||||
if(inputType==="custom" && seedValue==="")
|
||||
setNewRandomSeed(inputType);
|
||||
}
|
||||
const seedOpts = [
|
||||
{ label: t("random"), value: "random" },
|
||||
{ label: t("custom"), value: "custom" },
|
||||
];
|
||||
|
||||
return(
|
||||
<div>
|
||||
<label className="sub-title">{label}</label>
|
||||
<div className="d-flex gap-2 align-items-center">
|
||||
<SegmentButtons
|
||||
{...{
|
||||
name: t("seed"),
|
||||
onChange: handleInputTypeChange,
|
||||
options: seedOpts,
|
||||
value: inputType,
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
className="numberInputNoArrows"
|
||||
placeholder={t("randomPlaceholder")}
|
||||
value={inputType === "custom" ? seedValue : ""}
|
||||
onChange={handleSeedChange}
|
||||
onBlur={handleOnBlur}
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
@@ -56,7 +56,7 @@ export default function NumberSlider({
|
||||
}: Props) {
|
||||
const step = stepProps && stepProps <= max-min ? stepProps
|
||||
: max-min >= 1 ? 1 : (max-min) / 10;
|
||||
const initialValue = initialValueProps && initialValueProps <= max && initialValueProps >= min ? initialValueProps
|
||||
const initialValue = initialValueProps !== undefined && initialValueProps <= max && initialValueProps >= min ? initialValueProps
|
||||
: max-min === step ? min : roundToStep((max+min)/2, step);
|
||||
|
||||
function handleInputOnChange(e: React.ChangeEvent<HTMLInputElement>){
|
||||
|
||||
@@ -14,6 +14,7 @@ import FileWrapper from "./FileWrapper";
|
||||
import FileLabel from "./FileLabel";
|
||||
import ImageInput from "./ImageInput";
|
||||
import Input from "./Input";
|
||||
import InputSeed from "./InputSeed";
|
||||
import Label from "./Label";
|
||||
import LikeButton from "./LikeButton";
|
||||
import MocapInput from "./MocapInput";
|
||||
@@ -56,6 +57,7 @@ export {
|
||||
FileLabel,
|
||||
ImageInput,
|
||||
Input,
|
||||
InputSeed,
|
||||
Label,
|
||||
LikeButton,
|
||||
MocapInput,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function generateRandomSeed(){
|
||||
return Math.floor(Math.random() * Math.pow(2, 32)).toString();
|
||||
};
|
||||
@@ -36,6 +36,20 @@ styles with important tags:
|
||||
.ratio-1x1 {
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
/* Chrome, Safari, Edge, Opera */
|
||||
.numberInputNoArrows input::-webkit-outer-spin-button,
|
||||
.numberInputNoArrows input::-webkit-inner-spin-button,
|
||||
input.numberInputNoArrows::-webkit-outer-spin-button,
|
||||
input.numberInputNoArrows::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
/* Firefox */
|
||||
.numberInputNoArrows input[type=number],
|
||||
input.numberInputNoArrows[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
//-end--//
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, {useReducer } from 'react';
|
||||
import React, {useReducer, useEffect } from 'react';
|
||||
|
||||
import { useLocalize } from "hooks";
|
||||
import { useInferenceJobs, useLocalize } from "hooks";
|
||||
import { FrontendInferenceJobType, InferenceJob } from '@storyteller/components/src/jobs/InferenceJob';
|
||||
|
||||
import { Container } from "components/common";
|
||||
@@ -24,6 +24,26 @@ export default function StorytellerFilter(props:{
|
||||
status: NO_FILE,
|
||||
});
|
||||
|
||||
const { enqueueInferenceJob } = props;
|
||||
useInferenceJobs(
|
||||
FrontendInferenceJobType.StorytellerFilter
|
||||
);
|
||||
useEffect(() => {
|
||||
if (
|
||||
pageState.status === states.FILTER_ENQUEUED &&
|
||||
pageState.inferenceJobToken
|
||||
) {
|
||||
enqueueInferenceJob(
|
||||
pageState.inferenceJobToken,
|
||||
FrontendInferenceJobType.StorytellerFilter
|
||||
);
|
||||
dispatchPageState({
|
||||
type: "enqueueFilterSuccess",
|
||||
payload: { inferenceJobToken: undefined },
|
||||
});
|
||||
}
|
||||
}, [pageState, enqueueInferenceJob]);
|
||||
|
||||
return(
|
||||
<Container type="panel" className="mb-5">
|
||||
<PageHeader
|
||||
|
||||
+344
-54
@@ -1,25 +1,31 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import React, {useState} 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";
|
||||
import { EnqueueStorytellerFilter } from "@storyteller/components/src/api/storyteller_filter";
|
||||
import { states, Action, State } from "../storytellerFilterReducer";
|
||||
import {
|
||||
Accordion,
|
||||
Button,
|
||||
BasicVideo,
|
||||
Checkbox,
|
||||
ErrorMessage,
|
||||
Input,
|
||||
InputSeed,
|
||||
NumberSliderV2,
|
||||
Panel,
|
||||
SelectModal,
|
||||
Spinner,
|
||||
TextAreaV2
|
||||
TextArea
|
||||
} from "components/common";
|
||||
|
||||
import { faChevronRight } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export default function PageFilterControls({
|
||||
debug=false, t, pageState, dispatchPageState
|
||||
t, pageState, dispatchPageState
|
||||
}: {
|
||||
debug?: boolean;
|
||||
t: Function;
|
||||
@@ -27,42 +33,112 @@ export default function PageFilterControls({
|
||||
dispatchPageState: (action: Action) => void;
|
||||
}) {
|
||||
const { mediaToken } = useParams<any>();
|
||||
|
||||
useMedia({
|
||||
mediaToken: pageState.mediaFileToken || mediaToken,
|
||||
onSuccess: (res: any) => {
|
||||
// ratings.gather({ res, key: "token" });
|
||||
dispatchPageState({
|
||||
type: 'loadFileSuccess',
|
||||
payload: {mediaFile: res}
|
||||
payload: {
|
||||
mediaFile: res,
|
||||
mediaFileToken: pageState.mediaFileToken || mediaToken
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
const [filterState, setFilterState] = useState({
|
||||
outputPath: "vid2vid/SparseUpscaleInterp_00001.mp4",
|
||||
workflowConfig: "weight_q8sz47gmfw2zx02snrbz88ns9",
|
||||
seed: "",
|
||||
sdModelToken: "",
|
||||
loraModelToken: "",
|
||||
posPrompt: "",
|
||||
negPrompt: "",
|
||||
firstPass: 15,
|
||||
upScalePass: 15
|
||||
firstPass: 1,
|
||||
upscalePass: 0.42,
|
||||
motionScale:1,
|
||||
upscaleMultiplier: 1.5,
|
||||
useEmptyLatent: false,
|
||||
useFaceDetailer: false,
|
||||
denoiseFaceDetailer: 0.45,
|
||||
useLCM: false,
|
||||
lcmCFG: 2,
|
||||
lcmSteps: 8,
|
||||
framesCap:16,
|
||||
skipFrames:0,
|
||||
nthFrames:2,
|
||||
inputFps: 24,
|
||||
interpolationMutiplier: 2,
|
||||
cnTile: 0,
|
||||
cnCanny: 0,
|
||||
cnLinearAnime: 0,
|
||||
cnLinearRealistic: 0,
|
||||
cnDepth: 0,
|
||||
cnOpenPose: 0,
|
||||
cnPipeFace: 0,
|
||||
cnSparse: 0.7,
|
||||
});
|
||||
useEffect(()=>{
|
||||
if(debug) console.log(filterState)
|
||||
},[filterState, debug])
|
||||
|
||||
const handleOnChange = (key: string, newValue:any,) => {
|
||||
setFilterState((curr)=>({...curr, [key]: newValue}));
|
||||
}
|
||||
|
||||
const history = useHistory();
|
||||
const handleGenerate = ()=>{
|
||||
console.log("filterState:")
|
||||
console.log(filterState)
|
||||
|
||||
const request = {
|
||||
"uuid_idempotency_token": uuidv4(),
|
||||
"maybe_sd_model": filterState.sdModelToken,
|
||||
"maybe_workflow_config": filterState.workflowConfig,
|
||||
"maybe_input_file": pageState.mediaFileToken || mediaToken,
|
||||
"maybe_output_path": filterState.outputPath,
|
||||
"maybe_json_modifications": {
|
||||
"$.510.inputs.Text": filterState.posPrompt,
|
||||
"$.8.inputs.text": filterState.negPrompt,
|
||||
"$.173.inputs.seed": filterState.seed,
|
||||
"$.401.inputs.Value": filterState.firstPass,
|
||||
"$.918.inputs.Value": filterState.motionScale,
|
||||
"$.137.inputs.Value": filterState.framesCap,
|
||||
"$.186.inputs.Value": filterState.skipFrames,
|
||||
"$.140.inputs.Value": filterState.nthFrames,
|
||||
"$.154.inputs.Value": filterState.inputFps,
|
||||
"$.445.inputs.number": filterState.interpolationMutiplier,
|
||||
"$.947.inputs.Value": filterState.cnTile,
|
||||
"$.800.inputs.Value": filterState.cnCanny,
|
||||
"$.797.inputs.Value": filterState.cnLinearAnime,
|
||||
"$.796.inputs.Value": filterState.cnLinearRealistic,
|
||||
"$.772.inputs.Value": filterState.cnDepth,
|
||||
"$.771.inputs.Value": filterState.cnOpenPose,
|
||||
"$.527.inputs.Value": filterState.cnPipeFace,
|
||||
"$.403.inputs.Value": filterState.cnSparse
|
||||
},
|
||||
}
|
||||
EnqueueStorytellerFilter(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("/storyteller-filter/jobs");
|
||||
}
|
||||
|
||||
if (pageState.mediaFile){
|
||||
const mediaLink = new BucketConfig().getGcsUrl(pageState.mediaFile.public_bucket_path);
|
||||
|
||||
if (mediaLink)
|
||||
return(
|
||||
<>
|
||||
<Panel>
|
||||
<div className="row g-3 p-3">
|
||||
{debug && <p>{`File Token: ${pageState.mediaFileToken}`}</p> }
|
||||
<div className="col-5">
|
||||
<BasicVideo src={mediaLink} />
|
||||
</div>
|
||||
@@ -75,7 +151,7 @@ export default function PageFilterControls({
|
||||
</div>
|
||||
</Panel>
|
||||
<Accordion className="mt-4">
|
||||
<Accordion.Item title={t("heading1")}>
|
||||
<Accordion.Item title={"Basics"} defaultOpen>
|
||||
<div className="row g-3 p-3">
|
||||
<SelectModal
|
||||
modalTitle="Select a Stable Diffusion Weight"
|
||||
@@ -100,6 +176,8 @@ export default function PageFilterControls({
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<SelectModal
|
||||
modalTitle="Select a LoRA Weight"
|
||||
label="Additional LoRA Weight"
|
||||
@@ -124,55 +202,267 @@ export default function PageFilterControls({
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<TextAreaV2
|
||||
{...{
|
||||
label: "Prompt",
|
||||
placeholder: "Enter a prompt",
|
||||
onChange: (val:string)=>handleOnChange("posPrompt", val),
|
||||
value: filterState.posPrompt,
|
||||
required: false,
|
||||
}}
|
||||
/>
|
||||
<TextAreaV2
|
||||
{...{
|
||||
label: "Negative Prompt",
|
||||
placeholder: "Enter Negative Prompt",
|
||||
onChange: (val:string)=>handleOnChange("negPrompt", val),
|
||||
value: filterState.negPrompt,
|
||||
required: false,
|
||||
}}
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<TextArea
|
||||
{...{
|
||||
label: "Prompt",
|
||||
placeholder: "Enter a prompt",
|
||||
onChange: (e:React.ChangeEvent<HTMLTextAreaElement>)=>handleOnChange("posPrompt", e.target.value),
|
||||
value: filterState.posPrompt,
|
||||
required: false,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<TextArea
|
||||
{...{
|
||||
label: "Negative Prompt",
|
||||
placeholder: "Enter Negative Prompt",
|
||||
onChange: (e:React.ChangeEvent<HTMLTextAreaElement>)=>handleOnChange("negPrompt", e.target.value),
|
||||
value: filterState.negPrompt,
|
||||
required: false,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<InputSeed label="Seed" onChange={
|
||||
(val:string)=>handleOnChange("seed", val)
|
||||
}/>
|
||||
</div>
|
||||
|
||||
</Accordion.Item>
|
||||
<Accordion.Item title="Advance" defaultOpen>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0.1, max: 1, step: 0.01,
|
||||
initialValue: filterState.firstPass,
|
||||
label: " Denoise First Pass",
|
||||
thumbTip: "Denoise First Pass",
|
||||
onChange: (val)=>{handleOnChange("firstPass",val)}
|
||||
}}/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0.1, max: 1, step: 0.01,
|
||||
initialValue: filterState.upscalePass,
|
||||
label: "Denoise Upscale Pass",
|
||||
thumbTip: "Denoise Upscale Pass",
|
||||
onChange: (val)=>{handleOnChange("upscalePass", val)}
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0.25, max: 2, step: 0.05,
|
||||
initialValue: filterState.motionScale,
|
||||
label: "Motion Scale",
|
||||
thumbTip: "Motion Scale",
|
||||
onChange: (val)=>{handleOnChange("motionScale", val)}
|
||||
}}/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 1.5, max: 2, step: 0.5,
|
||||
initialValue: filterState.upscaleMultiplier,
|
||||
label: "Upscale Multiplier",
|
||||
thumbTip: "Upscale Multiplier",
|
||||
onChange: (val)=>{handleOnChange("upscaleMultiplier", val)}
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<Checkbox
|
||||
label="Use Empty Latent"
|
||||
checked={filterState.useEmptyLatent}
|
||||
onChange={(e:{target:{ checked: boolean, name:string, type: string }})=>{handleOnChange("useEmptyLatent", e.target.checked)}}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<Checkbox
|
||||
label="Use Face Detailer"
|
||||
checked={filterState.useFaceDetailer}
|
||||
onChange={(e:{target:{ checked: boolean, name:string, type: string }})=>{handleOnChange("useFaceDetailer", e.target.checked)}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0.1, max: 0.7, step: 0.01,
|
||||
initialValue: filterState.denoiseFaceDetailer,
|
||||
label: "Denoise Face Detailer",
|
||||
thumbTip: "Denoise Face Detailer",
|
||||
onChange: (val)=>{handleOnChange("denoiseFaceDetailer", val)}
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<Checkbox
|
||||
label="Use LCM"
|
||||
checked={filterState.useLCM}
|
||||
onChange={(e:{target:{ checked: boolean, name:string, type: string }})=>{handleOnChange("useLCM", e.target.checked)}}
|
||||
/>
|
||||
</div>
|
||||
</Accordion.Item>
|
||||
<Accordion.Item title={t("headings2")}>
|
||||
<div className="row g-3 p-3">
|
||||
<NumberSliderV2 {...{
|
||||
min: 1,
|
||||
max: 30,
|
||||
step: 0.5,
|
||||
initialValue: filterState.firstPass,
|
||||
label: " Denoise First Pass",
|
||||
thumbTip: "Denoise First Pass",
|
||||
onChange: (val)=>{handleOnChange("firstPass",val)}
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 1, max: 10, step: 1,
|
||||
initialValue: filterState.lcmCFG,
|
||||
label: "LCM CFG",
|
||||
thumbTip: "LCM CFG",
|
||||
onChange: (val)=>{handleOnChange("lcmCFG", val)}
|
||||
}}/>
|
||||
<NumberSliderV2 {...{
|
||||
min: 1,
|
||||
max: 30,
|
||||
initialValue: filterState.upScalePass,
|
||||
label: "Denoise Upscale Pass",
|
||||
thumbTip: "Denoise Upscale Pass",
|
||||
onChange: (val)=>{handleOnChange("upScalePass", val)}
|
||||
}}/>
|
||||
<br />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 5, max: 15, step: 1,
|
||||
initialValue: filterState.lcmSteps,
|
||||
label: "LCM Steps",
|
||||
thumbTip: "LCM Steps",
|
||||
onChange: (val)=>{handleOnChange("lcmSteps", val)}
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Item>
|
||||
<Accordion.Item title="Control Nets" defaultOpen>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: 1, step: 0.1,
|
||||
initialValue: filterState.cnCanny,
|
||||
label: "Canny",
|
||||
thumbTip: "Canny",
|
||||
onChange: (val)=>{handleOnChange("cnCanny", val)}
|
||||
}}/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: 1, step: 0.1,
|
||||
initialValue: filterState.cnLinearAnime,
|
||||
label: "Line Art Anime",
|
||||
thumbTip: "Line Art Anime",
|
||||
onChange: (val)=>{handleOnChange("cnLinearAnime", val)}
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: 1, step: 0.1,
|
||||
initialValue: filterState.cnDepth,
|
||||
label: "Depth",
|
||||
thumbTip: "Depth",
|
||||
onChange: (val)=>{handleOnChange("cnDepth", val)}
|
||||
}}/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: 1, step: 0.1,
|
||||
initialValue: filterState.cnOpenPose,
|
||||
label: "OpenPose",
|
||||
thumbTip: "OpenPose",
|
||||
onChange: (val)=>{handleOnChange("cnOpenPose", val)}
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: 1, step: 0.1,
|
||||
initialValue: filterState.cnPipeFace,
|
||||
label: "Media Pipe Face",
|
||||
thumbTip: "Media Pipe Face",
|
||||
onChange: (val)=>{handleOnChange("cnPipeFace", val)}
|
||||
}}/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: 1, step: 0.1,
|
||||
initialValue: filterState.cnSparse,
|
||||
label: "Sparse Scribble",
|
||||
thumbTip: "Sparse Scribble",
|
||||
onChange: (val)=>{handleOnChange("cnSparse", val)}
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: 1, step: 0.1,
|
||||
initialValue: filterState.cnLinearRealistic,
|
||||
label: "Video CN (Linear Realistic)",
|
||||
thumbTip: "Video CN",
|
||||
onChange: (val)=>{handleOnChange("cnLinearRealistic", val)}
|
||||
}}/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: 1, step: 0.1,
|
||||
initialValue: filterState.cnTile,
|
||||
label: "Tile CN",
|
||||
thumbTip: "Tile CN",
|
||||
onChange: (val)=>{handleOnChange("cnTile", val)}
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Item>
|
||||
<Accordion.Item title="Video Settings">
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<Input label="Width" />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<Input label="Height" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<Input label="Frames Cap" />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<Input label="Skip Frames" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<Input label="Every n-th Frame" />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<Input label="Input FPS" readOnly/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<Input label="Interpolation Multiplier" />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
<div className="row g-3 py-3">
|
||||
<div className="col-12 d-flex justify-content-end">
|
||||
<Button
|
||||
label={t("Process Filter")}
|
||||
onClick={handleGenerate}
|
||||
variant="primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}else if (pageState.status <= states.FILE_LOADING){
|
||||
return (
|
||||
<Panel>
|
||||
{debug && <p>{`File Token: ${pageState.mediaFileToken}`}</p> }
|
||||
<p>Loading Files</p>
|
||||
<Spinner />
|
||||
</Panel>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Analytics } from "common/Analytics";
|
||||
import InferenceJobsList from "components/layout/InferenceJobsList";
|
||||
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import { states, Action, State } from "../storytellerFilterReducer";
|
||||
import { Spinner } from 'components/common';
|
||||
|
||||
function StorytellerJobList(){
|
||||
const failures = (fail = "") => {
|
||||
switch (fail) {
|
||||
case "sample case":
|
||||
return "Sample Case, this should not have been shown";
|
||||
default:
|
||||
return "Unknown failure";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<InferenceJobsList {...{
|
||||
failures,
|
||||
onSelect: () => Analytics.voiceConversionClickDownload(),
|
||||
jobType: FrontendInferenceJobType.StorytellerFilter,
|
||||
}}/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PageJobList({
|
||||
t, pageState, dispatchPageState
|
||||
}: {
|
||||
debug?: boolean;
|
||||
t: Function;
|
||||
pageState: State;
|
||||
dispatchPageState: (action: Action) => void;
|
||||
}) {
|
||||
console.log(pageState);
|
||||
return(
|
||||
<>
|
||||
<h1>Jobs</h1>
|
||||
{pageState.status === states.FILTER_ENQUEUEING &&
|
||||
<div>
|
||||
<h2> Requesting Filter Job</h2>
|
||||
<Spinner />
|
||||
</div>
|
||||
}
|
||||
<StorytellerJobList />
|
||||
</>
|
||||
);
|
||||
}
|
||||
+20
-2
@@ -8,6 +8,8 @@ export enum states{
|
||||
FILE_UPLOADED,
|
||||
FILE_LOADING,
|
||||
FILE_LOADED,
|
||||
FILTER_ENQUEUEING,
|
||||
FILTER_ENQUEUED
|
||||
}
|
||||
|
||||
export type State = {
|
||||
@@ -24,7 +26,11 @@ export type Action =
|
||||
| {type: 'uploadFile'}
|
||||
| {type: 'uploadFileSuccess', payload:{ mediaFileToken: string}}
|
||||
| {type: 'loadFile'}
|
||||
| {type: 'loadFileSuccess', payload:{mediaFile: MediaFile}}
|
||||
| {type: 'loadFileSuccess', payload:{
|
||||
mediaFileToken: string, mediaFile: MediaFile
|
||||
}}
|
||||
| {type: 'enqueueFilter'}
|
||||
| {type: 'enqueueFilterSuccess', payload: {inferenceJobToken: string|undefined}}
|
||||
|
||||
|
||||
export function reducer (state: State, action: Action): State {
|
||||
@@ -55,7 +61,19 @@ export function reducer (state: State, action: Action): State {
|
||||
return {
|
||||
...state,
|
||||
status: states.FILE_LOADED,
|
||||
mediaFile: action.payload.mediaFile
|
||||
mediaFile: action.payload.mediaFile,
|
||||
mediaFileToken : action.payload.mediaFileToken,
|
||||
}
|
||||
case 'enqueueFilter':
|
||||
return {
|
||||
...state,
|
||||
status: states.FILTER_ENQUEUEING
|
||||
}
|
||||
case 'enqueueFilterSuccess':
|
||||
return {
|
||||
...state,
|
||||
status: states.FILTER_ENQUEUED,
|
||||
inferenceJobToken: action.payload.inferenceJobToken
|
||||
}
|
||||
default:
|
||||
return {status: states.NO_FILE};
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from "react";
|
||||
import {Switch, Route, useRouteMatch, Redirect} from "react-router-dom";
|
||||
import PageFilterControls from "./components/pageFilterControls";
|
||||
import PageVideoProvision from "./components/pageVideoProvision";
|
||||
import PageJobList from "./components/pageJobList";
|
||||
import {State,Action} from "./storytellerFilterReducer";
|
||||
|
||||
export default function SubRoutes ({
|
||||
@@ -25,6 +26,9 @@ export default function SubRoutes ({
|
||||
<Route exact path={`${path}/select-media`}>
|
||||
<PageVideoProvision {...{debug, t, pageState, dispatchPageState}}/>
|
||||
</Route>
|
||||
<Route exact path={`${path}/jobs`}>
|
||||
<PageJobList {...{debug, t, pageState, dispatchPageState}}/>
|
||||
</Route>
|
||||
<Route path={`${path}`}>
|
||||
<Redirect to={`${path}/upload`} />
|
||||
</Route>
|
||||
|
||||
Reference in new issue
Block a user