mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-06 02:00:46 +00:00
Merge branch 'feature/mvp' of https://github.com/storytold/storyteller-frontend into feature/mvp
This commit is contained in:
commit
b7e8c97c93
28 files changed
+796
-129
No files matched your search
@@ -496,8 +496,8 @@ class ApiConfig {
|
||||
return `${this.getApiOrigin()}/v1/mocap/mocapnet/create`;
|
||||
}
|
||||
|
||||
// =============== Storyteller Filter ===============
|
||||
enqueueStorytellerFilter(): string {
|
||||
// =============== Video Workflow ===============
|
||||
enqueueVideoWorkflow(): string {
|
||||
return `${this.getApiOrigin()}/v1/workflow/comfy/create`;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ export interface UserInfo {
|
||||
display_name: string,
|
||||
email_gravatar_hash: string,
|
||||
|
||||
// Rollout feature flags
|
||||
can_access_studio: boolean,
|
||||
|
||||
// Usage
|
||||
can_use_tts: boolean,
|
||||
can_use_w2l: boolean,
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export type {
|
||||
EnqueueStorytellerFilterRequest, EnqueueStorytellerFilterResponse,
|
||||
} from './EnqueueStorytellerFilter';
|
||||
|
||||
export {
|
||||
EnqueueStorytellerFilter
|
||||
} from './EnqueueStorytellerFilter';
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
import { ApiConfig } from "@storyteller/components";
|
||||
|
||||
export interface EnqueueStorytellerFilterRequest {
|
||||
export interface EnqueueVideoWorkflowRequest {
|
||||
"uuid_idempotency_token": string,
|
||||
|
||||
"maybe_sd_model": string,
|
||||
@@ -29,16 +29,16 @@ export interface EnqueueStorytellerFilterRequest {
|
||||
}
|
||||
}
|
||||
|
||||
export interface EnqueueStorytellerFilterResponse {
|
||||
export interface EnqueueVideoWorkflowResponse {
|
||||
success: boolean,
|
||||
inference_job_token?: string,
|
||||
}
|
||||
|
||||
export async function EnqueueStorytellerFilter(request: EnqueueStorytellerFilterRequest) : Promise<EnqueueStorytellerFilterResponse>
|
||||
export async function EnqueueVideoWorkflow(request: EnqueueVideoWorkflowRequest) : Promise<EnqueueVideoWorkflowResponse>
|
||||
{
|
||||
// const endpoint = "/v1/workflow/comfy/create";
|
||||
|
||||
const endpoint = new ApiConfig().enqueueStorytellerFilter();
|
||||
const endpoint = new ApiConfig().enqueueVideoWorkflow();
|
||||
|
||||
return await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
@@ -0,0 +1,7 @@
|
||||
export type {
|
||||
EnqueueVideoWorkflowRequest, EnqueueVideoWorkflowResponse,
|
||||
} from './EnqueueVideoWorkflow';
|
||||
|
||||
export {
|
||||
EnqueueVideoWorkflow
|
||||
} from './EnqueueVideoWorkflow';
|
||||
@@ -12,7 +12,7 @@ export enum FrontendInferenceJobType {
|
||||
VideoMotionCapture,
|
||||
ConvertFbxtoGltf,
|
||||
EngineComposition,
|
||||
StorytellerFilter,
|
||||
VideoWorkflow,
|
||||
}
|
||||
|
||||
// NB: Many of these fields are optional despite the response payload containing them
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { GetSessionInfo, GetSessionInfoIsOk, SessionInfoSuccessResponse } from '../api/session/GetSessionInfo'
|
||||
import { StudioRolloutHostnameAllowed } from '../utils/StudioRolloutHostnameAllowed';
|
||||
|
||||
// A lot of the APIs return null or leave values absent.
|
||||
// I need to pick a single strategy and stick with it.
|
||||
@@ -44,6 +45,14 @@ export class SessionWrapper {
|
||||
return this.sessionStateResponse?.logged_in || false;
|
||||
}
|
||||
|
||||
// Feature flag rollout for Storyteller Studio
|
||||
// This depends on the domain name and the backend.
|
||||
public canAccessStudio() : boolean {
|
||||
const hostnameAllowed = StudioRolloutHostnameAllowed();
|
||||
const userAllowed = this.sessionStateResponse?.user?.can_access_studio || false;
|
||||
return hostnameAllowed && userAllowed;
|
||||
}
|
||||
|
||||
// Username is all lowercase
|
||||
public getUsername() : string | undefined {
|
||||
return this.sessionStateResponse?.user?.username;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
// Check if hostname is allowed to display studio features.
|
||||
// This should allow more frequently landing against production.
|
||||
export function StudioRolloutHostnameAllowed() : boolean {
|
||||
const allowed = StudioRolloutHostnameCheck();
|
||||
return allowed;
|
||||
}
|
||||
|
||||
function StudioRolloutHostnameCheck() : boolean {
|
||||
// Do not allow "production" or "staging" domains to use studio yet.
|
||||
// Other domains (local development and feature branches) are allowed.
|
||||
switch (window.location.hostname) {
|
||||
case 'fakeyou.com':
|
||||
case 'storyteller.ai':
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -210,7 +210,7 @@ function initInferenceJobsByCategoryMap(): Map<
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoMotionCapture, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.ImageGeneration, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.ConvertFbxtoGltf, []);
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.StorytellerFilter, [])
|
||||
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoWorkflow, [])
|
||||
return inferenceJobsByCategory;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, forwardRef } from "react";
|
||||
import { a, useSpring } from "@react-spring/web";
|
||||
import { useVideo } from "hooks";
|
||||
import makeClass from "resources/makeClass";
|
||||
@@ -13,12 +13,12 @@ interface Props {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export default function BasicVideo({
|
||||
export default forwardRef(function BasicVideo({
|
||||
className,
|
||||
src,
|
||||
title,
|
||||
description
|
||||
}: Props) {
|
||||
description,
|
||||
}: Props, ref:React.ForwardedRef<HTMLVideoElement>) {
|
||||
const [tint,tintSet] = useState(true);
|
||||
const [{ playCtrl },vidProps] = useVideo({ onEnded: () => tintSet(true) });
|
||||
const style = useSpring({
|
||||
@@ -31,7 +31,7 @@ export default function BasicVideo({
|
||||
};
|
||||
|
||||
return <div {...{ ...makeClass("fy-basic-video",className) }}>
|
||||
<video {...{ playsInline: true, ...vidProps }}>
|
||||
<video {...{ playsInline: true, ...vidProps }} ref={ref}>
|
||||
<source {...{ src, type: "video/mp4" }} />
|
||||
</video>
|
||||
<a.div {...{ className: "video-overlay", onClick, style }}>
|
||||
@@ -40,4 +40,4 @@ export default function BasicVideo({
|
||||
<FontAwesomeIcon {...{ icon: faPlay }} />
|
||||
</a.div>
|
||||
</div>;
|
||||
};
|
||||
});
|
||||
@@ -2,7 +2,7 @@ import React, { useState, memo, useEffect } from "react";
|
||||
import Searcher from "../Searcher";
|
||||
import Modal from "../Modal";
|
||||
import NonRouteTabs from "../Tabs/NonRouteTabs";
|
||||
import Input from "../Input";
|
||||
import Input from "../TempInput";
|
||||
import Button from "../Button";
|
||||
import { faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import SelectMediaList from "./SelectMediaList";
|
||||
@@ -122,7 +122,7 @@ const SelectModal = memo(
|
||||
/>
|
||||
<Input
|
||||
disabled={true}
|
||||
className="w-100"
|
||||
wrapperClassName="w-100"
|
||||
placeholder="None selected"
|
||||
onClick={openModal}
|
||||
value={selectedValue.title !==""
|
||||
|
||||
@@ -5,6 +5,7 @@ import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
|
||||
import "./Input.scss";
|
||||
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
wrapperClassName ?: string;
|
||||
className?: string;
|
||||
icon?: IconDefinition;
|
||||
invalidReason?: string;
|
||||
@@ -14,6 +15,7 @@ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
}
|
||||
|
||||
export default function TempInput({
|
||||
wrapperClassName,
|
||||
className,
|
||||
label,
|
||||
icon,
|
||||
@@ -24,8 +26,8 @@ export default function TempInput({
|
||||
}: InputProps) {
|
||||
return (
|
||||
// Changed fragment to div here just so that it can be laid out with bootstrap easily using d-flex, flex-column and responsive gaps which requires grouping.
|
||||
<div className="fy-input">
|
||||
<Label {...{ invalidReason, label, required }}/>
|
||||
<div className={`fy-input${wrapperClassName ? " "+ wrapperClassName : ""}`}>
|
||||
{label && <Label {...{ invalidReason, label, required }}/>}
|
||||
<div className={`fy-input ${icon ? "input-icon" : ""}`}>
|
||||
{icon && (
|
||||
<FontAwesomeIcon icon={icon} className="form-control-feedback" />
|
||||
|
||||
@@ -256,6 +256,96 @@ export default function SideNav({
|
||||
queueStats.legacy_tts.pending_job_count +
|
||||
queueStats.inference.by_queue.pending_tacotron2_jobs;
|
||||
|
||||
let maybeVideoGeneration = <></>;
|
||||
|
||||
if (sessionWrapper.canAccessStudio()) {
|
||||
maybeVideoGeneration = (
|
||||
<>
|
||||
<li>
|
||||
<NavLink
|
||||
to="/video-mocap"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faPersonRays}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
{t("videoMotionCapture")}
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavLink
|
||||
to="/video-workflow"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowsTurnToDots}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
{t("videoWorkflow")}
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavLink
|
||||
to="/studio"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faCameraMovie}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
Storyteller Studio
|
||||
{/* {t("videoStorytellerStudio")} */}
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink
|
||||
to="/engine-compositor"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faTransporter}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
Engine Compositor
|
||||
{/* {t("videoStorytellerStudio")} */}
|
||||
</NavLink>
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let maybeImageGeneration = <></>;
|
||||
|
||||
if (sessionWrapper.canAccessStudio()) {
|
||||
maybeImageGeneration = (
|
||||
<>
|
||||
<li className="sidebar-heading">Image Generation</li>
|
||||
<li>
|
||||
<NavLink
|
||||
to="/text-to-image"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faMessageImage}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
Text to Image
|
||||
{/* {t("videoStorytellerStudio")} */}
|
||||
</NavLink>
|
||||
</li>
|
||||
<hr className="mb-3 mt-3" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -360,80 +450,13 @@ export default function SideNav({
|
||||
{t("videoFaceAnimator")}
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink
|
||||
to="/video-mocap"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faPersonRays}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
{t("videoMotionCapture")}
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavLink
|
||||
to="/video-workflow"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowsTurnToDots}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
{t("videoWorkflow")}
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavLink
|
||||
to="/studio"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faCameraMovie}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
Storyteller Studio
|
||||
{/* {t("videoStorytellerStudio")} */}
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink
|
||||
to="/engine-compositor"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faTransporter}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
Engine Compositor
|
||||
{/* {t("videoStorytellerStudio")} */}
|
||||
</NavLink>
|
||||
</li>
|
||||
<hr className="mb-3 mt-3" />
|
||||
<li className="sidebar-heading">Image Generation</li>
|
||||
<li>
|
||||
<NavLink
|
||||
to="/text-to-image"
|
||||
activeClassName="active-link"
|
||||
onClick={handleNavLinkClick}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faMessageImage}
|
||||
className="sidebar-heading-icon"
|
||||
/>
|
||||
Text to Image
|
||||
{/* {t("videoStorytellerStudio")} */}
|
||||
</NavLink>
|
||||
</li>
|
||||
{maybeVideoGeneration}
|
||||
|
||||
<hr className="mb-3 mt-3" />
|
||||
|
||||
{maybeImageGeneration}
|
||||
|
||||
<li className="sidebar-heading">{t("communityTitle")}</li>
|
||||
<li>
|
||||
<NavLink
|
||||
|
||||
@@ -373,6 +373,7 @@ class PageContainer extends React.Component<
|
||||
render={props => (
|
||||
<WeightPage
|
||||
key={props.match.params.weight_token}
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
@@ -824,6 +825,7 @@ class PageContainer extends React.Component<
|
||||
inferenceJobs: this.props.inferenceJobs,
|
||||
inferenceJobsByCategory:
|
||||
this.props.inferenceJobsByCategory,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
@@ -837,6 +839,7 @@ class PageContainer extends React.Component<
|
||||
inferenceJobs: this.props.inferenceJobs,
|
||||
inferenceJobsByCategory:
|
||||
this.props.inferenceJobsByCategory,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
@@ -846,6 +849,7 @@ class PageContainer extends React.Component<
|
||||
sessionSubscriptionsWrapper={
|
||||
this.props.sessionSubscriptionsWrapper
|
||||
}
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
enqueueInferenceJob={this.props.enqueueInferenceJob}
|
||||
/>
|
||||
</Route>
|
||||
@@ -1312,7 +1316,9 @@ class PageContainer extends React.Component<
|
||||
</Route>
|
||||
|
||||
<Route path="/engine-compositor">
|
||||
<EngineCompositor />
|
||||
<EngineCompositor
|
||||
sessionWrapper={this.props.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path="/voice-designer">
|
||||
@@ -1331,6 +1337,7 @@ class PageContainer extends React.Component<
|
||||
this.props.sessionSubscriptionsWrapper,
|
||||
inferenceJobs: this.props.inferenceJobs,
|
||||
inferenceJobsByCategory: this.props.inferenceJobsByCategory,
|
||||
sessionWrapper: this.props.sessionWrapper,
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { DiscordLink2 } from "@storyteller/components/src/elements/DiscordLink2";
|
||||
import { Container } from "components/common";
|
||||
import PageHeader from "components/layout/PageHeader";
|
||||
import React from "react";
|
||||
|
||||
export function StudioNotAvailable() {
|
||||
const subText = (
|
||||
<p> Please visit our <DiscordLink2 /> to join the wait list. </p>
|
||||
);
|
||||
|
||||
return (
|
||||
<Container type="panel" className="mb-5">
|
||||
<PageHeader
|
||||
title={"Studio Features Not Available"}
|
||||
subText={subText}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -6,12 +6,15 @@ import { EnqueueEngineCompositing } from "@storyteller/components/src/api/engine
|
||||
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import "./EngineCompositor.scss"
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { StudioNotAvailable } from "v2/view/_common/StudioNotAvailable";
|
||||
|
||||
interface Props {
|
||||
value?: any;
|
||||
sessionWrapper: SessionWrapper;
|
||||
}
|
||||
|
||||
export default function EngineCompositor({ value }: Props) {
|
||||
export default function EngineCompositor({ value, sessionWrapper }: Props) {
|
||||
const [mediaToken,mediaTokenSet] = useState();
|
||||
const onChange = ({ target }: any) => mediaTokenSet(target.value);
|
||||
const inferenceJobs = useInferenceJobs(FrontendInferenceJobType.EngineComposition);
|
||||
@@ -23,12 +26,10 @@ export default function EngineCompositor({ value }: Props) {
|
||||
})
|
||||
.then((res: any) => {
|
||||
if (res && res.success) {
|
||||
console.log("🚛 success!",{ mediaToken, res });
|
||||
inferenceJobs.enqueue( // I need to pass this
|
||||
res.inference_job_token,
|
||||
FrontendInferenceJobType.EngineComposition
|
||||
);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -40,6 +41,10 @@ export default function EngineCompositor({ value }: Props) {
|
||||
}
|
||||
};
|
||||
|
||||
if (!sessionWrapper.canAccessStudio()) {
|
||||
return <StudioNotAvailable />
|
||||
}
|
||||
|
||||
return <div {...{ className: "fy-engine-compositor"}}>
|
||||
<div {...{ className: "panel engine-compositor-container" }}>
|
||||
<header>
|
||||
|
||||
@@ -77,21 +77,6 @@ function AboutPage(props: Props) {
|
||||
role: "Growth",
|
||||
imageSrc: "/images/avatars/default-pfp.png",
|
||||
},
|
||||
{
|
||||
name: "Evgenii",
|
||||
role: "Gen AI Artist",
|
||||
imageSrc: "/images/avatars/default-pfp.png",
|
||||
},
|
||||
{
|
||||
name: "Steven",
|
||||
role: "Gen AI Artist",
|
||||
imageSrc: "/images/avatars/default-pfp.png",
|
||||
},
|
||||
{
|
||||
name: "Willis",
|
||||
role: "Gen AI Artist",
|
||||
imageSrc: "/images/avatars/default-pfp.png",
|
||||
},
|
||||
{
|
||||
name: "Jose",
|
||||
role: "Data Team",
|
||||
@@ -104,6 +89,9 @@ function AboutPage(props: Props) {
|
||||
},
|
||||
];
|
||||
|
||||
// NB: just to prevent yarn lints from complaining
|
||||
console.log(teamMembers.length);
|
||||
|
||||
return (
|
||||
<Container type="panel" className="mb-5">
|
||||
<PageHeaderWithImage
|
||||
@@ -112,7 +100,7 @@ function AboutPage(props: Props) {
|
||||
headerImage="/mascot/kitsune_pose3.webp"
|
||||
yOffset="85%"
|
||||
/>
|
||||
<Panel padding={true} mb={true}>
|
||||
{/*<Panel padding={true} mb={true}>
|
||||
<h2 className="mb-3 fw-bold">Lorem Ipsum</h2>
|
||||
|
||||
<p>
|
||||
@@ -124,7 +112,7 @@ function AboutPage(props: Props) {
|
||||
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
|
||||
culpa qui officia deserunt mollit anim id est laborum.
|
||||
</p>
|
||||
</Panel>
|
||||
</Panel>*/}
|
||||
|
||||
<Panel padding={true} mb={true}>
|
||||
<h2 className="mb-3 fw-bold">Our Mission</h2>
|
||||
@@ -138,6 +126,7 @@ function AboutPage(props: Props) {
|
||||
</p>
|
||||
</Panel>
|
||||
|
||||
{/*
|
||||
<Panel padding={true}>
|
||||
<h2 className="mb-3 fw-bold">The Team</h2>
|
||||
|
||||
@@ -199,6 +188,7 @@ function AboutPage(props: Props) {
|
||||
))}
|
||||
</div>
|
||||
</Panel>
|
||||
*/}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
+5
@@ -5,6 +5,7 @@ import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session
|
||||
import PageHeaderWithImage from "components/layout/PageHeaderWithImage";
|
||||
import { Container, Panel } from "components/common";
|
||||
import { faCameraMovie } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { StudioNotAvailable } from "v2/view/_common/StudioNotAvailable";
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper;
|
||||
@@ -12,6 +13,10 @@ interface Props {
|
||||
}
|
||||
|
||||
function StorytellerStudioListPage(props: Props) {
|
||||
if (!props.sessionWrapper.canAccessStudio()) {
|
||||
return <StudioNotAvailable />
|
||||
}
|
||||
|
||||
return (
|
||||
<Container type="panel">
|
||||
<PageHeaderWithImage
|
||||
|
||||
@@ -7,9 +7,12 @@ import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session
|
||||
import { faMessageImage } from "@fortawesome/pro-solid-svg-icons";
|
||||
import TextToImageJobsList from "./components/TextToImageJobsList";
|
||||
import { useInferenceJobs } from "hooks";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { StudioNotAvailable } from "v2/view/_common/StudioNotAvailable";
|
||||
|
||||
interface TextToImagePageProps {
|
||||
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
|
||||
sessionWrapper: SessionWrapper;
|
||||
enqueueInferenceJob: (
|
||||
jobToken: string,
|
||||
frontendInferenceJobType: FrontendInferenceJobType
|
||||
@@ -17,6 +20,7 @@ interface TextToImagePageProps {
|
||||
}
|
||||
|
||||
export default function TextToImagePage({
|
||||
sessionWrapper,
|
||||
sessionSubscriptionsWrapper,
|
||||
enqueueInferenceJob,
|
||||
}: TextToImagePageProps) {
|
||||
@@ -25,6 +29,10 @@ export default function TextToImagePage({
|
||||
);
|
||||
const hasImageGenJobs = inferenceJobs && inferenceJobs.length > 0;
|
||||
|
||||
if (!sessionWrapper.canAccessStudio()) {
|
||||
return <StudioNotAvailable />
|
||||
}
|
||||
|
||||
return (
|
||||
<Container type="panel">
|
||||
<PageHeaderWithImage
|
||||
|
||||
@@ -21,6 +21,8 @@ import {
|
||||
faPersonRays,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import PageHeaderWithImage from "components/layout/PageHeaderWithImage";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import { StudioNotAvailable } from "v2/view/_common/StudioNotAvailable";
|
||||
|
||||
export default function VideoMotionCapture(props: {
|
||||
enqueueInferenceJob: (
|
||||
@@ -29,8 +31,9 @@ export default function VideoMotionCapture(props: {
|
||||
) => void;
|
||||
inferenceJobs: Array<InferenceJob>;
|
||||
inferenceJobsByCategory: Map<FrontendInferenceJobType, Array<InferenceJob>>;
|
||||
sessionWrapper: SessionWrapper;
|
||||
}) {
|
||||
const { enqueueInferenceJob } = props;
|
||||
const { enqueueInferenceJob, sessionWrapper } = props;
|
||||
const { t } = useLocalize("VideoMotionCapture");
|
||||
const { NO_FILE, FILE_UPLOADING, MOCAPNET_ENQUEUED } = states;
|
||||
const [pageState, dispatchPageState] = useReducer(reducer, {
|
||||
@@ -59,6 +62,10 @@ export default function VideoMotionCapture(props: {
|
||||
}, [pageState, enqueueInferenceJob]);
|
||||
const { pathname } = useLocation();
|
||||
|
||||
if (!sessionWrapper.canAccessStudio()) {
|
||||
return <StudioNotAvailable />
|
||||
}
|
||||
|
||||
if (pathname === `/video-mocap` || pathname === `/video-mocap/`) {
|
||||
return <Redirect to={`/video-mocap/upload`} />;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import PageHeader from "components/layout/PageHeader";
|
||||
|
||||
import { states, reducer } from "./videoWorkflowReducer";
|
||||
import SubRoutes from "./videoWorkflowRoutes";
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { StudioNotAvailable } from 'v2/view/_common/StudioNotAvailable';
|
||||
|
||||
export default function StorytellerFilter(props:{
|
||||
enqueueInferenceJob: (
|
||||
@@ -16,8 +18,9 @@ export default function StorytellerFilter(props:{
|
||||
) => void;
|
||||
inferenceJobs: Array<InferenceJob>;
|
||||
inferenceJobsByCategory: Map<FrontendInferenceJobType, Array<InferenceJob>>;
|
||||
sessionWrapper: SessionWrapper;
|
||||
}){
|
||||
const debug=true;
|
||||
const debug = true;
|
||||
const {t} = useLocalize("VideoWorkflow");
|
||||
const { NO_FILE } = states;
|
||||
const [pageState, dispatchPageState] = useReducer(reducer, {
|
||||
@@ -26,7 +29,7 @@ export default function StorytellerFilter(props:{
|
||||
|
||||
const { enqueueInferenceJob } = props;
|
||||
useInferenceJobs(
|
||||
FrontendInferenceJobType.StorytellerFilter
|
||||
FrontendInferenceJobType.VideoWorkflow
|
||||
);
|
||||
useEffect(() => {
|
||||
if (
|
||||
@@ -35,7 +38,7 @@ export default function StorytellerFilter(props:{
|
||||
) {
|
||||
enqueueInferenceJob(
|
||||
pageState.inferenceJobToken,
|
||||
FrontendInferenceJobType.StorytellerFilter
|
||||
FrontendInferenceJobType.VideoWorkflow
|
||||
);
|
||||
dispatchPageState({
|
||||
type: "enqueueFilterSuccess",
|
||||
@@ -44,6 +47,10 @@ export default function StorytellerFilter(props:{
|
||||
}
|
||||
}, [pageState, enqueueInferenceJob]);
|
||||
|
||||
if (!props.sessionWrapper.canAccessStudio()) {
|
||||
return <StudioNotAvailable />
|
||||
}
|
||||
|
||||
return(
|
||||
<Container type="panel" className="mb-5">
|
||||
<PageHeader
|
||||
|
||||
+6
-5
@@ -6,7 +6,7 @@ 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 { EnqueueVideoWorkflow } from "@storyteller/components/src/api/video_workflow";
|
||||
import { states, Action, State } from "../videoWorkflowReducer";
|
||||
import {
|
||||
Accordion,
|
||||
@@ -24,12 +24,13 @@ import {
|
||||
} from "components/common";
|
||||
|
||||
|
||||
export default function PageFilterControls({
|
||||
t, pageState, dispatchPageState
|
||||
export default function PageWorkflowControls({
|
||||
t, pageState, dispatchPageState, parentPath
|
||||
}: {
|
||||
debug?: boolean;
|
||||
t: Function;
|
||||
pageState: State;
|
||||
parentPath: string;
|
||||
dispatchPageState: (action: Action) => void;
|
||||
}) {
|
||||
const { mediaToken } = useParams<any>();
|
||||
@@ -115,7 +116,7 @@ export default function PageFilterControls({
|
||||
"$.403.inputs.Value": filterState.cnSparse
|
||||
},
|
||||
}
|
||||
EnqueueStorytellerFilter(request).then(res => {
|
||||
EnqueueVideoWorkflow(request).then(res => {
|
||||
if (res.success && res.inference_job_token) {
|
||||
dispatchPageState({
|
||||
type: 'enqueueFilterSuccess',
|
||||
@@ -128,7 +129,7 @@ export default function PageFilterControls({
|
||||
}
|
||||
})
|
||||
dispatchPageState({type: 'enqueueFilter'})
|
||||
history.push("/storyteller-filter/jobs");
|
||||
history.push(`${parentPath}/jobs`);
|
||||
}
|
||||
|
||||
if (pageState.mediaFile){
|
||||
@@ -6,7 +6,7 @@ import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/Infer
|
||||
import { states, Action, State } from "../videoWorkflowReducer";
|
||||
import { Spinner } from 'components/common';
|
||||
|
||||
function StorytellerJobList(){
|
||||
function VideoWorkflowJobList(){
|
||||
const failures = (fail = "") => {
|
||||
switch (fail) {
|
||||
case "sample case":
|
||||
@@ -20,7 +20,7 @@ function StorytellerJobList(){
|
||||
<InferenceJobsList {...{
|
||||
failures,
|
||||
onSelect: () => Analytics.voiceConversionClickDownload(),
|
||||
jobType: FrontendInferenceJobType.StorytellerFilter,
|
||||
jobType: FrontendInferenceJobType.VideoWorkflow,
|
||||
}}/>
|
||||
);
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export default memo (function PageJobList({
|
||||
<Spinner />
|
||||
</div>
|
||||
}
|
||||
<StorytellerJobList />
|
||||
<VideoWorkflowJobList />
|
||||
</>
|
||||
);
|
||||
});
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { default } from "./pageWorkflowControls";
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
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";
|
||||
import { EnqueueVideoWorkflow } from "@storyteller/components/src/api/video_workflow";
|
||||
import { states, Action, State } from "../../videoWorkflowReducer";
|
||||
import {
|
||||
Accordion,
|
||||
Button,
|
||||
BasicVideo,
|
||||
Checkbox,
|
||||
ErrorMessage,
|
||||
InputSeed,
|
||||
NumberSliderV2,
|
||||
Panel,
|
||||
SelectModal,
|
||||
Spinner,
|
||||
TextArea
|
||||
} from "components/common";
|
||||
|
||||
import SectionControlNets, {ControlNetsInitialValues as CnIvs} from "./sectionControlNets";
|
||||
import SectionVideoSettings from "./sectionVideoSettings";
|
||||
|
||||
export default function PageFilterControls({
|
||||
t, pageState, dispatchPageState, parentPath
|
||||
}: {
|
||||
debug?: boolean;
|
||||
t: Function;
|
||||
pageState: State;
|
||||
parentPath: string;
|
||||
dispatchPageState: (action: Action) => void;
|
||||
}) {
|
||||
const { mediaToken } = useParams<any>();
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
useMedia({
|
||||
mediaToken: pageState.mediaFileToken || mediaToken,
|
||||
onSuccess: (res: any) => {
|
||||
dispatchPageState({
|
||||
type: 'loadFileSuccess',
|
||||
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: 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,
|
||||
...CnIvs
|
||||
});
|
||||
|
||||
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
|
||||
},
|
||||
}
|
||||
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`);
|
||||
}
|
||||
|
||||
if (pageState.mediaFile){
|
||||
const mediaLink = new BucketConfig().getGcsUrl(pageState.mediaFile.public_bucket_path);
|
||||
|
||||
if (mediaLink)
|
||||
return(
|
||||
<>
|
||||
<Panel>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-5">
|
||||
<BasicVideo src={mediaLink} ref={videoRef}/>
|
||||
</div>
|
||||
<div className="col-1 d-flex align-items-center justify-items-center">
|
||||
<FontAwesomeIcon icon={faChevronRight} className="fa-6x"/>
|
||||
</div>
|
||||
<div className="col-6">
|
||||
<BasicVideo src={mediaLink} />
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
<Accordion className="mt-4">
|
||||
<Accordion.Item title={"Basics"} defaultOpen>
|
||||
<div className="row g-3 p-3">
|
||||
<SelectModal
|
||||
modalTitle="Select a Stable Diffusion Weight"
|
||||
label="Select a Stable Diffusion Weight"
|
||||
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",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<SelectModal
|
||||
modalTitle="Select a LoRA Weight"
|
||||
label="Additional LoRA Weight"
|
||||
onSelect={({token})=>{
|
||||
console.log(`calling from select modal result select ${token}`);
|
||||
handleOnChange("loraModelToken", token);
|
||||
}}
|
||||
tabs={[
|
||||
{
|
||||
label: "All LoRA Weights",
|
||||
tabKey: "allLoraWeights",
|
||||
typeFilter: "rvc_v2",
|
||||
searcher: true,
|
||||
type: "weights",
|
||||
},
|
||||
{
|
||||
label: "Bookmarked",
|
||||
tabKey: "bookmarkedLoraWeights",
|
||||
typeFilter: "rvc_v2",
|
||||
searcher: false,
|
||||
type: "weights",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</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>
|
||||
<div className="row g-3 p-3">
|
||||
<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)}
|
||||
}}/>
|
||||
</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>
|
||||
<SectionControlNets
|
||||
onChange={(key,val)=>handleOnChange(key,val)}
|
||||
/>
|
||||
</Accordion.Item>
|
||||
<Accordion.Item title="Video Settings">
|
||||
<SectionVideoSettings
|
||||
onChange={(key,val)=>handleOnChange(key,val)}
|
||||
videoElement={videoRef?.current}
|
||||
/>
|
||||
</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>
|
||||
<p>Loading Files</p>
|
||||
<Spinner />
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
return <ErrorMessage />;
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
import React, { memo } from 'react'
|
||||
|
||||
import { NumberSliderV2 } from 'components/common'
|
||||
|
||||
export const ControlNetsInitialValues = {
|
||||
cnCanny: 0,
|
||||
cnDepth: 0,
|
||||
cnLinearAnime: 0,
|
||||
cnLinearRealistic: 0,
|
||||
cnOpenPose: 0,
|
||||
cnPipeFace: 0,
|
||||
cnSparse: 0.7,
|
||||
cnTile: 0,
|
||||
}
|
||||
|
||||
export default memo( function SectionControlNets(
|
||||
{onChange : handleOnChange} : {onChange: (key:string, val:number)=>void}
|
||||
){
|
||||
const iv = ControlNetsInitialValues;
|
||||
return(
|
||||
<>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<NumberSliderV2 {...{
|
||||
min: 0, max: 1, step: 0.1,
|
||||
initialValue: iv.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: iv.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: iv.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: iv.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: iv.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: iv.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: iv.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: iv.cnTile,
|
||||
label: "Tile CN",
|
||||
thumbTip: "Tile CN",
|
||||
onChange: (val)=>{handleOnChange("cnTile", val)}
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
import React, { memo, useState } from 'react'
|
||||
|
||||
import { TempInput as Input } from 'components/common'
|
||||
|
||||
export const VideoSettingsInitialValues = {
|
||||
width: 544,
|
||||
height: 544,
|
||||
framesCap: 16,
|
||||
skipFrames: 0,
|
||||
everyNthFrame: 2,
|
||||
inputFps: 24,
|
||||
interpolationMultiplier: 2,
|
||||
}
|
||||
|
||||
export default memo( function SectionVideoSettings({
|
||||
onChange : handleOnChange,
|
||||
videoElement: ve
|
||||
} : {
|
||||
onChange: (key:string, val:number)=>void,
|
||||
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});
|
||||
}
|
||||
else if (aspectRatio < 1) {
|
||||
setDimensions({width: iv.width, height: 960});
|
||||
}else{
|
||||
console.log(`aspectRaio: ${aspectRatio}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(
|
||||
<>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<Input label="Width" value={width} readOnly/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<Input label="Height" value={height} readOnly/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<Input label="Frames Cap" value={iv.framesCap} readOnly/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<Input label="Skip Frames" value={iv.skipFrames} readOnly/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<Input label="Every n-th Frame" value={iv.everyNthFrame} readOnly />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<Input label="Input FPS" value={iv.inputFps} readOnly/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row g-3 p-3">
|
||||
<div className="col-md-6">
|
||||
<Input
|
||||
label="Interpolation Multiplier"
|
||||
value={iv.interpolationMultiplier}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
@@ -35,8 +35,11 @@ import WeightCoverImage from "components/common/WeightCoverImage";
|
||||
import { BucketConfig } from "@storyteller/components/src/api/BucketConfig";
|
||||
import SdInferencePanel from "./inference_panels/SdInferencePanel";
|
||||
import SdCoverImagePanel from "./cover_image_panels/SdCoverImagePanel";
|
||||
import { StudioNotAvailable } from "v2/view/_common/StudioNotAvailable";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
|
||||
interface WeightProps {
|
||||
sessionWrapper: SessionWrapper;
|
||||
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
|
||||
inferenceJobs: Array<InferenceJob>;
|
||||
ttsInferenceJobs: Array<TtsInferenceJob>;
|
||||
@@ -49,6 +52,7 @@ interface WeightProps {
|
||||
}
|
||||
|
||||
export default function WeightPage({
|
||||
sessionWrapper,
|
||||
sessionSubscriptionsWrapper,
|
||||
inferenceJobs,
|
||||
ttsInferenceJobs,
|
||||
@@ -100,6 +104,15 @@ export default function WeightPage({
|
||||
|
||||
const deleteWeight = () => remove(!!user?.can_ban_users);
|
||||
|
||||
switch (weight?.weight_type) {
|
||||
case WeightType.SD_15:
|
||||
case WeightType.SDXL:
|
||||
case WeightType.LORA:
|
||||
if (!sessionWrapper.canAccessStudio()) {
|
||||
return <StudioNotAvailable />
|
||||
}
|
||||
}
|
||||
|
||||
function renderWeightComponent(weight: Weight) {
|
||||
switch (weight.weight_category) {
|
||||
case WeightCategory.TTS:
|
||||
|
||||
Reference in new issue
Block a user