Merge branch 'feature/mvp' into feature/select-modal

This commit is contained in:
BFlat authored and GitHub committed 2024-01-23 12:45:30 +07:00
commit 96acca81af
17 files changed
+294 -291

No files matched your search

+2 -3
View File
@@ -206,6 +206,7 @@ function initInferenceJobsByCategoryMap(): Map<
[]
);
inferenceJobsByCategory.set(FrontendInferenceJobType.VoiceDesignerTts, []);
inferenceJobsByCategory.set(FrontendInferenceJobType.VideoMotionCapture, []);
return inferenceJobsByCategory;
}
@@ -485,10 +486,8 @@ class App extends React.Component<Props, State> {
frontendJobType: FrontendInferenceJobType
) => {
const newJob = new InferenceJob(jobToken, frontendJobType);
let inferenceJobs = this.state.inferenceJobs.concat([newJob]);
this.setState({
inferenceJobs: inferenceJobs,
inferenceJobs: [...this.state.inferenceJobs, newJob],
});
};
@@ -32,5 +32,10 @@
background-color: transparent;
border-bottom: 4px solid $primary !important;
}
&.disabled{
color: rgba($white, 0.5) !important;
pointer-events: none;
}
}
}
@@ -11,10 +11,12 @@ interface TabProps {
content?: React.ReactNode;
icon?: IconDefinition;
padding?: boolean;
disabled?:boolean;
}
interface TabsProps {
tabs: TabProps[];
disabled?: boolean;
}
interface TabContentProps {
@@ -22,12 +24,12 @@ interface TabContentProps {
padding?: boolean;
}
function Tab({ to, label, icon, onClick }: TabProps & { onClick: () => void }) {
function Tab({ to, label, icon, onClick, disabled }: TabProps & { onClick: () => void }) {
return (
<li className="nav-item">
<NavLink
to={to}
className="nav-link fs-6 px-3 px-lg-4"
className={"nav-link fs-6 px-3 px-lg-4" + (disabled?" disabled":"")}
activeClassName="active"
onClick={onClick}
>
@@ -43,7 +45,7 @@ function TabContent({ children, padding }: TabContentProps) {
return <div className={`tab-content ${paddingClasses}`}>{children}</div>;
}
function Tabs({ tabs }: TabsProps) {
function Tabs({ tabs, disabled }: TabsProps) {
const location = useLocation();
const currentPath = location.pathname;
const initialTab = tabs.find(tab => tab.to === currentPath) || tabs[0];
@@ -83,6 +85,7 @@ function Tabs({ tabs }: TabsProps) {
label={tab.label}
onClick={() => handleTabClick(tab.to)}
icon={tab.icon}
disabled={disabled || tab.disabled}
/>
))}
</ul>
@@ -2,7 +2,7 @@ import React, { useEffect } from "react";
import { useSpring } from "@react-spring/web";
import { FileDetails, FileWrapper, FileLabel } from "components/common";
import { faFileVideo, faVideo } from "@fortawesome/pro-solid-svg-icons";
import "./style.scss";
import "./videoinput.scss";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
interface Props {
@@ -0,0 +1,5 @@
@import "scss/colours.scss";
.upload-details {
background-color: rgba($panel, .85);
}
@@ -18,19 +18,26 @@ export default function InferenceJobsList({ failures, jobType, onSelect }: JobsL
// let height = 0;
// const transitions = useTransition(inferenceJobs,{ not right now });
return inferenceJobs.length ? <div {...{ className: "face-animator-jobs panel" }}>
<h5>{ t("core.heading") }</h5>
{
inferenceJobs.map((job: InferenceJob, key: number) =>
<JobItem {...{
failures,
jobStatusDescription,
jobType,
key,
onSelect,
t,
// refSet: (ref: any) => console.log(ref),
...job }} />).reverse()
}
</div> : null;
if (inferenceJobs.length){
return(
<div className="face-animator-jobs panel">
<h5>{ t("core.heading") }</h5>
{
inferenceJobs.map((job: InferenceJob, key: number) =>
<JobItem {...{
failures,
jobStatusDescription,
jobType,
key,
onSelect,
t,
...job }} />).reverse()
}
</div>
)
}
else{
return null;
}
};
@@ -12,6 +12,16 @@ styles with important tags:
// Once the rollout is complete, this outer wrapper class can be removed.
.fakeyou-refresh {
//- BS5 classnames -//
.object-fit-cover{
object-fit: cover;
}
.ratio-1x1 {
aspect-ratio: 1/1;
}
//-end--//
::-webkit-scrollbar-thumb {
background-color: rgba($secondary, 1);
border-radius: 0.5rem;
@@ -196,6 +206,9 @@ styles with important tags:
color: #ffe600;
}
.fill-secondary {
fill: $secondary;
}
.alert-pricing {
font-weight: 500 !important;
font-size: 18px !important;
@@ -1,23 +1,23 @@
import React, { useState } from "react";
import React, { useReducer, useEffect } from "react";
import { Redirect, useLocation } from "react-router-dom";
import { useLocalize } from "hooks";
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
import {
FrontendInferenceJobType,
InferenceJob,
} from "@storyteller/components/src/jobs/InferenceJob";
// import { PageVideoProvision } from "./components/pageVideoProvision";
import { PageInferenceStatuses } from "./components/pageInferenceStatuses";
import { BasicVideo, Container, Panel } from "components/common";
import PageHeader from "components/layout/PageHeader";
import Tabs from "components/common/Tabs";
import { Redirect, useLocation } from "react-router-dom";
import { useLocalize } from "hooks";
import TabContentUpload from "./components/tabContentUpload";
import TabContentLibrary from "./components/tabContentLibrary";
import PageVideoMocapProgress from "./components/pageVideoMocapProgress";
import VideoMocapJobList from "./components/videoMocapJobList";
import {states, reducer} from './videoMocapReducer';
export default function VideoMotionCapture(props: {
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
enqueueInferenceJob: (
jobToken: string,
frontendInferenceJobType: FrontendInferenceJobType
@@ -27,39 +27,22 @@ export default function VideoMotionCapture(props: {
}) {
const { enqueueInferenceJob } = props;
const { t } = useLocalize("VideoMotionCapture");
enum pageStates {
VIDEO_PROVISION,
SHOW_JOB_STATUS,
}
const { VIDEO_PROVISION, SHOW_JOB_STATUS } = pageStates;
const [pageState, setPageState] = useState<{
index: number;
inputMediaToken: string;
jobToken: string;
resultMediaToken: string;
}>({
index: VIDEO_PROVISION,
inputMediaToken: "",
jobToken: "",
resultMediaToken: "",
});
const { NO_FILE, FILE_UPLOADING, MOCAPNET_ENQUEUED } = states;
const [pageState, dispatchPageState] = useReducer(reducer, {status: NO_FILE});
const handlePageState = ({
tokenType,
token,
}: {
tokenType: string;
token: string | undefined;
}) => {
if (token && tokenType === "jobToken") {
setPageState({
...pageState,
index: SHOW_JOB_STATUS,
jobToken: token,
useEffect(()=>{
if (pageState.status === states.MOCAPNET_ENQUEUED && pageState.inferenceJobToken){
enqueueInferenceJob(
pageState.inferenceJobToken,
FrontendInferenceJobType.VideoMotionCapture
);
dispatchPageState({
type: 'enqueueMocapNetSuccess',
payload:{inferenceJobToken: undefined}
});
}
};
}, [pageState, enqueueInferenceJob])
const { pathname } = useLocation();
if (pathname === `/video-mocap` || pathname === `/video-mocap/`) {
@@ -69,13 +52,13 @@ export default function VideoMotionCapture(props: {
const tabs = [
{
label: t("tabTitle.upload"),
content: <TabContentUpload t={t} pageStateCallback={handlePageState} />,
content: <TabContentUpload {...{t, pageState, dispatchPageState}} />,
to: "/video-mocap/upload",
padding: true,
},
{
label: t("tabTitle.library"),
content: <TabContentLibrary t={t} pageStateCallback={handlePageState} />,
content: <TabContentLibrary {...{t, pageState, dispatchPageState}} />,
to: "/video-mocap/select-media",
padding: true,
},
@@ -89,35 +72,34 @@ export default function VideoMotionCapture(props: {
/>
<Panel>
<div className="row g-0">
{/*Video Provision Tabs & Job Statuses*/}
<div className="col-12 col-md-6">
{pageState.index === VIDEO_PROVISION && <Tabs tabs={tabs} />}
{pageState.index === SHOW_JOB_STATUS && (
<PageInferenceStatuses
{...{
t,
enqueueInferenceJob,
pageStates,
pageState,
pageStateCallback: handlePageState,
}}
/>
)}
</div>
{/*ENDS Video Chooser Tabs*/}
<div className="col-12 col-md-6">
<Panel padding={true} clear={true}>
<BasicVideo
title="Video -> Mocap Sample"
src="/videos/face-animator-instruction-en.mp4"
/>
</Panel>
</div>
{ pageState.status < FILE_UPLOADING && <>
<div className="col-12 col-md-6">
<Tabs tabs={tabs}/>
</div>
<div className="col-12 col-md-6">
<Panel padding={true} clear={true}>
<BasicVideo
title="Video -> Mocap Sample"
src="/videos/face-animator-instruction-en.mp4"
/>
</Panel>
</div>
</>}
{ pageState.status >= FILE_UPLOADING && pageState.status < MOCAPNET_ENQUEUED &&
<div className="col-12" >
<PageVideoMocapProgress {...{t, pageState, dispatchPageState}} />
</div>
}
{ pageState.status === MOCAPNET_ENQUEUED &&
<div className="col-12" >
<h2 className="p-3 m-0">{t("tab.message.mocapNetRequestSucceed")}</h2>
</div>
}
</div>
{/*2nd row*/}
</Panel>
{/*panel*/}
<VideoMocapJobList />
</Container>
);
}
@@ -1,20 +0,0 @@
@import "scss/colours.scss";
@import "scss/_breakpoint.scss";
//TODO: PUSH TO TOP
.fill-secondary {
fill: $secondary;
}
//TODO: PUSH TO TOP, given in BS5
.object-fit-cover{
object-fit: cover;
}
.ratio-1x1 {
aspect-ratio: 1/1;
}
//-end--//
.upload-details {
background-color: rgba($panel, .85);
}
@@ -1,52 +0,0 @@
import React, { useEffect } from 'react'
import { Analytics } from "common/Analytics";
import InferenceJobsList from "components/layout/InferenceJobsList";
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
export function PageInferenceStatuses (props:{
t: Function,
pageStates: Object,
pageState: {
index:number,
inputMediaToken: string,
jobToken: string,
resultMediaToken: string
},
enqueueInferenceJob: (
jobToken: string,
frontendInferenceJobType: FrontendInferenceJobType
) => void,
pageStateCallback: (data:{tokenType:string, token:string | undefined}) => void
}){
const { t, pageState, enqueueInferenceJob } = props;
useEffect(()=>{
console.log("ENQUEUE INFERENCE JOB>>")
enqueueInferenceJob(
props.pageState.jobToken,
FrontendInferenceJobType.FaceAnimation
);
}, [props.pageState.jobToken, enqueueInferenceJob])
const failures = (fail = "") => {
switch (fail) {
case "sample case":
return "Sample Case, this should not have been shown";
default:
return "Uknown failure";
}
};
return(
<>
<h2>{t("tab.message.mocapNetRequestSucceed")}</h2>
<h4>Job Token: {pageState.jobToken}</h4>
<InferenceJobsList {...{
failures,
onSelect: () => Analytics.voiceConversionClickDownload(),
jobType: FrontendInferenceJobType.FaceAnimation,
}}/>
</>
)
}
@@ -0,0 +1,79 @@
import React from 'react';
import { v4 as uuidv4 } from "uuid";
import { EnqueueVideoMotionCapture } from "@storyteller/components/src/api/video_mocap";
import { states, Action, State } from "../videoMocapReducer";
import { Spinner } from 'components/common/';
export default function PageVideoMocapProgress ({t, pageState, dispatchPageState}: {
t: Function;
pageState: State;
dispatchPageState: (action: Action) => void;
}){
const {FILE_UPLOADING, FILE_UPLOADED, MOCAPNET_ENQUEUEING} = states;
const handleEnqueueMocapNet = () => {
if (pageState.mediaFileToken){
const request = {
video_source: pageState.mediaFileToken,
uuid_idempotency_token: uuidv4(),
};
EnqueueVideoMotionCapture(request).then(res => {
if(res.success && res.inference_job_token ){
dispatchPageState({
type: 'enqueueMocapNetSuccess',
payload:{
inferenceJobToken: res.inference_job_token,
}
});
}
});
dispatchPageState({type: 'enqueueMocapNet'})
}
};
if (pageState.status === FILE_UPLOADING) {
return (
<div className="row p-4 g-4">
<div className="col-12 d-flex justify-content-center">
<h2 className="flex-basis">{t("tab.message.fileUploading")}</h2>
</div>
<div className="col-12 d-flex justify-content-center">
<Spinner />
</div>
</div>
);
} else if (pageState.status === FILE_UPLOADED) {
return (
<div className="row p-4 g-4">
<div className="col-12 d-flex justify-content-center">
<h2>{t("tab.message.fileUploaded")}</h2>
</div>
<div className="col-12 d-flex justify-content-center">
<button className="btn btn-primary" onClick={handleEnqueueMocapNet}>
{t("button.generate")}
</button>
</div>
</div>
);
} else if (pageState.status === MOCAPNET_ENQUEUEING) {
return (
<div className="row p-4 g-4">
<div className="col-12 d-flex justify-content-center">
<h2>{t("tab.message.mocapNetRequesting")}</h2>
</div>
<div className="col-12 d-flex justify-content-center">
<Spinner />
</div>
</div>
);
}
return(
<div className="row p-4 g-4">
<div className="col-12 d-flex justify-content-center">
<h2>{t("message.UnknownError")}</h2>
</div>
</div>
);
}
@@ -1,39 +0,0 @@
import React from "react";
import TabContentUpload from "./tabContentUpload";
import TabContentLibrary from "./tabContentLibrary";
export function PageVideoProvision (props:{
t: Function
pageStateCallback: (data:{tokenType:string, token:string | undefined}) => void
}){
const {t} = props;
return(<>
<ul className="nav nav-tabs" id="vmcTab">
<li className="nav-item">
<button
className="nav-link active"
id="vmcUploadTab"
data-bs-toggle="tab"
data-bs-target="#vmcUpload"
>
{t("tabTitle.upload")}
</button>
</li>
<li className="nav-item">
<button
disabled
className="nav-link"
id="vmcLibraryTab"
data-bs-toggle="tab"
data-bs-target="#vmcLibrary"
>
{t("tabTitle.library")}
</button>
</li>
</ul>
<div className="tab-content" id="vmcTabContent">
<TabContentUpload {...props}/>
<TabContentLibrary {...props}/>
</div>
</>);
}
@@ -1,10 +1,12 @@
import SelectModal from "components/common/SelectModal/SelectModal";
import React from "react";
import { Action, State } from "../videoMocapReducer";
export default function TabContentLibrary(props: {
t: Function;
pageStateCallback: Function;
}) {
pageState: State;
dispatchPageState: (action: Action) => void;
}){
return (
<div>
<SelectModal
@@ -1,34 +1,22 @@
import React, { useEffect, useState } from "react";
import React from "react";
import { v4 as uuidv4 } from "uuid";
import { UploadMedia } from "@storyteller/components/src/api/media_files/UploadMedia";
import { EnqueueVideoMotionCapture } from "@storyteller/components/src/api/video_mocap";
import { useFile } from "hooks";
import VideoInput from "v2/view/pages/video_mocap/components/VideoInput";
import VideoInput from "components/common/VideoInput";
import { states, Action, State } from "../videoMocapReducer";
export default function TabContentUpload(props: {
export default function TabContentUpload({t, pageState, dispatchPageState}: {
t: Function;
pageStateCallback: (data: {
tokenType: string;
token: string | undefined;
}) => void;
pageState: State;
dispatchPageState: (action: Action) => void;
}) {
const { t, pageStateCallback } = props;
const videoProps = useFile({});
enum tabStates {
NO_FILE,
FILE_STAGED,
FILE_UPLOADING,
FILE_UPLOADED,
MOCAPNET,
}
const { NO_FILE, FILE_STAGED, FILE_UPLOADING, FILE_UPLOADED, MOCAPNET } =
tabStates;
const [tabState, setTabState] = useState<number>(NO_FILE);
const [token, setToken] = useState<string>("");
const {NO_FILE, FILE_STAGED} = states;
const makeRequest = () => ({
const makeVideoUploadRequest = () => ({
uuid_idempotency_token: uuidv4(),
file: videoProps.file,
source: "file",
@@ -36,42 +24,35 @@ export default function TabContentUpload(props: {
});
const handleUploadVideo = () => {
setTabState(FILE_UPLOADING);
UploadMedia(makeRequest()).then(res => {
if (res.success && "media_file_token" in res) {
setToken(res.media_file_token);
dispatchPageState({type: 'uploadFile'});
UploadMedia(makeVideoUploadRequest()).then(res => {
if (res.success && res.media_file_token) {
dispatchPageState({
type: 'uploadFileSuccess',
payload:{
mediaFileToken :res.media_file_token
}
})
}
});
};
useEffect(() => {
if (token !== "") setTabState(FILE_UPLOADED);
}, [token, FILE_UPLOADED]);
const handleEnqueueMocapNet = () => {
const request = {
video_source: token,
uuid_idempotency_token: uuidv4(),
};
EnqueueVideoMotionCapture(request).then(res => {
pageStateCallback({
tokenType: "jobToken",
token: res.inference_job_token,
});
});
setTabState(MOCAPNET);
};
// contains upload inout state and controls, see docs
if (tabState === NO_FILE || tabState === FILE_STAGED) {
if (pageState.status === NO_FILE || pageState.status === FILE_STAGED) {
return (
<div>
<>
<div className="row">
<div className="col-12">
<VideoInput
{...{
t,
...videoProps,
onStateChange: () =>
setTabState(videoProps.file ? FILE_STAGED : NO_FILE),
onStateChange: () =>{
if (pageState.status === NO_FILE && videoProps.file)
dispatchPageState({type: "stagedFile"})
else if (pageState.status === FILE_STAGED && !videoProps.file)
dispatchPageState({type: "clearedFile"})
}
}}
/>
</div>
@@ -82,7 +63,7 @@ export default function TabContentUpload(props: {
<div className="d-flex justify-content-end gap-3">
<button
className="btn btn-primary"
disabled={tabState !== FILE_STAGED}
disabled={pageState.status !== FILE_STAGED}
onClick={handleUploadVideo}
>
{t("button.upload")}
@@ -90,52 +71,14 @@ export default function TabContentUpload(props: {
</div>
</div>
</div>
</div>
);
} else if (tabState === FILE_UPLOADING) {
return (
<div className="tab-pane fade show active py-4" id="vmcUpload">
<div className="row">
<div className="col-12">
<h2>{t("tab.message.fileUploading")}</h2>
</div>
</div>
</div>
);
} else if (tabState === FILE_UPLOADED) {
return (
<div className="tab-pane fade show active py-4" id="vmcUpload">
<div className="row">
<div className="col-12">
<h2>{t("tab.message.fileUploaded")}</h2>
</div>
<div className="col-12">
<button className="btn btn-primary" onClick={handleEnqueueMocapNet}>
{t("button.generate")}
</button>
</div>
</div>
</div>
);
} else if (tabState === MOCAPNET) {
return (
<div className="tab-pane fade show active py-4" id="vmcUpload">
<div className="row">
<div className="col-12">
<h2>{t("tab.message.mocapNetRequesting")}</h2>
</div>
</div>
</div>
);
} else {
return (
<div className="tab-pane fade show active py-4" id="vmcUpload">
<div className="row">
<div className="col-12">
<h1>{t("message.UnknownError")}</h1>
</div>
</div>
</div>
</>
);
}
return (
<div className="row">
<div className="col-12">
<h1>{t("message.UnknownError")}</h1>
</div>
</div>
);
}
@@ -0,0 +1,24 @@
import React from 'react'
import { Analytics } from "common/Analytics";
import InferenceJobsList from "components/layout/InferenceJobsList";
import { FrontendInferenceJobType } from "@storyteller/components/src/jobs/InferenceJob";
export default function VideoMocapJobList(){
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.VideoMotionCapture,
}}/>
);
}
@@ -0,0 +1,52 @@
export enum states{
NO_FILE,
FILE_STAGED,
FILE_UPLOADING,
FILE_UPLOADED,
MOCAPNET_ENQUEUEING,
MOCAPNET_ENQUEUED,
}
export type State = {
status: number;
mediaFileToken?: string;
inferenceJobToken?: string;
}
export type Action =
| {type: 'uploadFile'}
| {type: 'stagedFile'}
| {type: 'clearedFile'}
| {type: 'uploadFileSuccess', payload:{ mediaFileToken: string}}
| {type: 'enqueueMocapNet'}
| {type: 'enqueueMocapNetSuccess', payload: {inferenceJobToken: string|undefined}}
export function reducer (state: State, action: Action): State {
console.log(action);
switch(action.type){
case 'stagedFile':
return {...state, status: states.FILE_STAGED}
case 'clearedFile':
return {...state, status: states.NO_FILE}
case 'uploadFile':
return {...state,status: states.FILE_UPLOADING};
case 'uploadFileSuccess':
return {
...state,
status: states.FILE_UPLOADED,
mediaFileToken: action.payload.mediaFileToken
}
case 'enqueueMocapNet':
return {...state, status:states.MOCAPNET_ENQUEUEING}
case 'enqueueMocapNetSuccess':
return {
...state,
status: states.MOCAPNET_ENQUEUED,
inferenceJobToken: action.payload.inferenceJobToken
};
default:
return {status: states.NO_FILE};
}
}