mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-12 12:53:04 +00:00
Merge pull request #452 from storytold/bflat-fy-video-mocap-page
changes to video mocap page
This commit is contained in:
6 files changed
+401
-337
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { a, useSpring } from '@react-spring/web';
|
||||
import React from "react";
|
||||
import { a, useSpring } from "@react-spring/web";
|
||||
import { FontAwesomeIcon as Icon } from "@fortawesome/react-fontawesome";
|
||||
import { faTrash } from "@fortawesome/pro-solid-svg-icons";
|
||||
import "./FileDetails.scss";
|
||||
@@ -10,51 +10,65 @@ interface Props {
|
||||
file?: any;
|
||||
hideClearDetails?: boolean;
|
||||
icon?: any;
|
||||
className?: string
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function FileDetails({ className, clear = () => {}, disabled, file, hideClearDetails, icon }: Props) {
|
||||
export default function FileDetails({
|
||||
className,
|
||||
clear = () => {},
|
||||
disabled,
|
||||
file,
|
||||
hideClearDetails,
|
||||
icon,
|
||||
}: Props) {
|
||||
const fileSize =
|
||||
file && file.size >= 1024 * 1024
|
||||
? (file.size / 1024 / 1024).toFixed(2) + " MB"
|
||||
: file
|
||||
? `${Math.floor(file.size / 1024)} KB`
|
||||
: null;
|
||||
? `${Math.floor(file.size / 1024)} KB`
|
||||
: null;
|
||||
|
||||
const style = useSpring({
|
||||
config: { mass: 1, tension: 120, friction: 14 },
|
||||
opacity: disabled ? 0 : 1
|
||||
opacity: disabled ? 0 : 1,
|
||||
});
|
||||
|
||||
return <a.div {...{ className: `fy-uploader-layout upload-details${ className ? " " + className : "" }`, style }}>
|
||||
{ icon && <Icon {...{ className: "fy-uploader-layout-icon", icon }}/> }
|
||||
<div>
|
||||
<div {...{ className: "filename" }}>
|
||||
{ file.name.slice(0, file.name.lastIndexOf(".")) }
|
||||
return (
|
||||
<a.div
|
||||
{...{
|
||||
className: `fy-uploader-layout upload-details ${
|
||||
className ? " " + className : ""
|
||||
}`,
|
||||
style,
|
||||
}}
|
||||
>
|
||||
{icon && <Icon {...{ className: "fy-uploader-layout-icon", icon }} />}
|
||||
<div>
|
||||
<div {...{ className: "filename d-flex flex-column" }}>
|
||||
{file.name.slice(0, file.name.lastIndexOf("."))}
|
||||
|
||||
<span className="opacity-50">
|
||||
{`${ file.name.split(".").pop().toUpperCase() } file size: ${ fileSize }`}
|
||||
</span>
|
||||
<span className="opacity-50">
|
||||
{`${file.name
|
||||
.split(".")
|
||||
.pop()
|
||||
.toUpperCase()} file size: ${fileSize}`}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{ !hideClearDetails &&
|
||||
<button
|
||||
className="
|
||||
upload-details-clear
|
||||
btn btn-destructive
|
||||
align-items-center
|
||||
justify-content-center
|
||||
"
|
||||
onClick={e=>{
|
||||
e.preventDefault();
|
||||
if (clear) clear();
|
||||
{!hideClearDetails && (
|
||||
<button
|
||||
{...{
|
||||
className:
|
||||
"upload-details-clear btn btn-destructive align-items-center justify-content-center",
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
clear();
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Icon icon= {faTrash }/>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
{ !hideClearDetails && <button {...{ className: "upload-details-clear btn btn-destructive align-items-center justify-content-center", onClick: e => { e.preventDefault(); clear() } }}>
|
||||
<Icon {...{ icon: faTrash }}/>
|
||||
</button> }
|
||||
</a.div>;
|
||||
};
|
||||
<Icon {...{ icon: faTrash }} />
|
||||
</button>
|
||||
)}
|
||||
</a.div>
|
||||
);
|
||||
}
|
||||
@@ -9,84 +9,88 @@ import { faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
interface TabConfig {
|
||||
label: string;
|
||||
searcherKey: string;
|
||||
searcherKey?: string;
|
||||
weightTypeFilter?: string;
|
||||
mediaTypeFilter?: string; // NOT DONE
|
||||
}
|
||||
interface SelectSearcherProps {
|
||||
label?: string;
|
||||
tabs: TabConfig[];
|
||||
modalTitle?: string;
|
||||
}
|
||||
|
||||
const SelectSearcher = memo(({ label, tabs }: SelectSearcherProps) => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const { weightTitle, setWeightTitle } = useToken();
|
||||
const SelectSearcher = memo(
|
||||
({ label, tabs, modalTitle = "Select" }: SelectSearcherProps) => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const { weightTitle, setWeightTitle } = useToken();
|
||||
|
||||
const openModal = () => {
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
const openModal = () => {
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
||||
const handleRemove = () => {
|
||||
setWeightTitle && setWeightTitle("");
|
||||
};
|
||||
const handleRemove = () => {
|
||||
setWeightTitle && setWeightTitle("");
|
||||
};
|
||||
|
||||
const searchTabs = tabs.map(tab => ({
|
||||
label: tab.label,
|
||||
content: (
|
||||
<Searcher
|
||||
type="modal"
|
||||
onResultSelect={closeModal}
|
||||
searcherKey={tab.searcherKey}
|
||||
weightType={tab.weightTypeFilter}
|
||||
/>
|
||||
),
|
||||
padding: true,
|
||||
}));
|
||||
const searchTabs = tabs.map(tab => ({
|
||||
label: tab.label,
|
||||
content: tab.searcherKey ? (
|
||||
<Searcher
|
||||
type="modal"
|
||||
onResultSelect={closeModal}
|
||||
searcherKey={tab.searcherKey}
|
||||
weightType={tab.weightTypeFilter}
|
||||
/>
|
||||
) : null,
|
||||
padding: true,
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{label && <label className="sub-title">{label}</label>}
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{label && <label className="sub-title">{label}</label>}
|
||||
|
||||
<div className="d-flex gap-2">
|
||||
<Input
|
||||
disabled={true}
|
||||
className="w-100"
|
||||
placeholder="None selected"
|
||||
value={weightTitle ? weightTitle : ""}
|
||||
/>
|
||||
<Button
|
||||
label={weightTitle ? "Change" : "Select"}
|
||||
onClick={openModal}
|
||||
/>
|
||||
{weightTitle && (
|
||||
<Button
|
||||
square={true}
|
||||
variant="danger"
|
||||
icon={faTrash}
|
||||
onClick={handleRemove}
|
||||
tooltip="Remove"
|
||||
<div className="d-flex gap-2">
|
||||
<Input
|
||||
disabled={true}
|
||||
className="w-100"
|
||||
placeholder="None selected"
|
||||
value={weightTitle ? weightTitle : ""}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
label={weightTitle ? "Change" : "Select"}
|
||||
onClick={openModal}
|
||||
/>
|
||||
{weightTitle && (
|
||||
<Button
|
||||
square={true}
|
||||
variant="danger"
|
||||
icon={faTrash}
|
||||
onClick={handleRemove}
|
||||
tooltip="Remove"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
show={isModalOpen}
|
||||
handleClose={closeModal}
|
||||
title="Select a LoRA Weight"
|
||||
content={() => <NonRouteTabs tabs={searchTabs} />}
|
||||
showButtons={false}
|
||||
padding={false}
|
||||
large={true}
|
||||
position="top"
|
||||
mobileFullscreen={true}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
<Modal
|
||||
show={isModalOpen}
|
||||
handleClose={closeModal}
|
||||
title={modalTitle}
|
||||
content={() => <NonRouteTabs tabs={searchTabs} />}
|
||||
showButtons={false}
|
||||
padding={false}
|
||||
large={true}
|
||||
position="top"
|
||||
mobileFullscreen={true}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default SelectSearcher;
|
||||
@@ -2,90 +2,122 @@ import React, { useState } from "react";
|
||||
|
||||
import { useLocalize } from "hooks";
|
||||
import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper";
|
||||
import { FrontendInferenceJobType, InferenceJob } from "@storyteller/components/src/jobs/InferenceJob";
|
||||
import {
|
||||
FrontendInferenceJobType,
|
||||
InferenceJob,
|
||||
} from "@storyteller/components/src/jobs/InferenceJob";
|
||||
|
||||
import { PageVideoProvision } from "./components/pageVideoProvision";
|
||||
// import { PageVideoProvision } from "./components/pageVideoProvision";
|
||||
import { PageInferenceStatuses } from "./components/pageInferenceStatuses";
|
||||
import { BasicVideo } from "components/common";
|
||||
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 TabContentUpload from "./components/tabContentUpload";
|
||||
import TabContentLibrary from "./components/tabContentLibrary";
|
||||
|
||||
export default function VideoMotionCapture(props: {
|
||||
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper,
|
||||
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
|
||||
enqueueInferenceJob: (
|
||||
jobToken: string,
|
||||
frontendInferenceJobType: FrontendInferenceJobType
|
||||
) => void,
|
||||
inferenceJobs: Array<InferenceJob>,
|
||||
inferenceJobsByCategory: Map<FrontendInferenceJobType, Array<InferenceJob>>,
|
||||
}){
|
||||
|
||||
) => void;
|
||||
inferenceJobs: Array<InferenceJob>;
|
||||
inferenceJobsByCategory: Map<FrontendInferenceJobType, Array<InferenceJob>>;
|
||||
}) {
|
||||
const { enqueueInferenceJob } = props;
|
||||
const { t } = useLocalize("VideoMotionCapture");
|
||||
enum pageStates { VIDEO_PROVISION, SHOW_JOB_STATUS }
|
||||
const { VIDEO_PROVISION, SHOW_JOB_STATUS} = pageStates;
|
||||
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 handlePageState = (
|
||||
{tokenType, token }:{tokenType:string, token:string | undefined}
|
||||
) => {
|
||||
if(token && tokenType === "jobToken"){
|
||||
index: number;
|
||||
inputMediaToken: string;
|
||||
jobToken: string;
|
||||
resultMediaToken: string;
|
||||
}>({
|
||||
index: VIDEO_PROVISION,
|
||||
inputMediaToken: "",
|
||||
jobToken: "",
|
||||
resultMediaToken: "",
|
||||
});
|
||||
|
||||
const handlePageState = ({
|
||||
tokenType,
|
||||
token,
|
||||
}: {
|
||||
tokenType: string;
|
||||
token: string | undefined;
|
||||
}) => {
|
||||
if (token && tokenType === "jobToken") {
|
||||
setPageState({
|
||||
...pageState,
|
||||
index: SHOW_JOB_STATUS,
|
||||
jobToken: token
|
||||
})
|
||||
jobToken: token,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const { pathname } = useLocation();
|
||||
|
||||
if (pathname === `/video-mocap` || pathname === `/video-mocap/`) {
|
||||
return <Redirect to={`/video-mocap/upload`} />;
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
label: t("tabTitle.upload"),
|
||||
content: <TabContentUpload t={t} pageStateCallback={handlePageState} />,
|
||||
to: "/video-mocap/upload",
|
||||
padding: true,
|
||||
},
|
||||
{
|
||||
label: t("tabTitle.library"),
|
||||
content: <TabContentLibrary t={t} pageStateCallback={handlePageState} />,
|
||||
to: "/video-mocap/select-media",
|
||||
padding: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="container-panel py-4">
|
||||
<div className="panel p-4">
|
||||
|
||||
{/*Header section*/}
|
||||
<div className="row g-5">
|
||||
<h1 className="fw-bold">{t("headings.title")}</h1>
|
||||
<p className="fa-light-txt opacity-75 mt-1">{t("headings.subtitle")}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="row g-5 mt-1">
|
||||
|
||||
<Container type="panel" className="mb-5">
|
||||
<PageHeader
|
||||
title={t("headings.title")}
|
||||
subText={t("headings.subtitle")}
|
||||
/>
|
||||
<Panel>
|
||||
<div className="row g-0">
|
||||
{/*Video Provision Tabs & Job Statuses*/}
|
||||
<div className="col-12 col-md-6">
|
||||
{ pageState.index === VIDEO_PROVISION &&
|
||||
<PageVideoProvision
|
||||
t={t}
|
||||
pageStateCallback={handlePageState}
|
||||
/>
|
||||
}
|
||||
{ pageState.index === SHOW_JOB_STATUS &&
|
||||
{pageState.index === VIDEO_PROVISION && <Tabs tabs={tabs} />}
|
||||
{pageState.index === SHOW_JOB_STATUS && (
|
||||
<PageInferenceStatuses
|
||||
{...{
|
||||
t,
|
||||
enqueueInferenceJob,
|
||||
pageStates,
|
||||
pageState,
|
||||
pageStateCallback: handlePageState
|
||||
pageStateCallback: handlePageState,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
{/*ENDS Video Chooser Tabs*/}
|
||||
|
||||
|
||||
<div className="col-12 col-md-6">
|
||||
<BasicVideo
|
||||
title="Video -> Mocap Sample"
|
||||
src="/videos/face-animator-instruction-en.mp4"
|
||||
/>
|
||||
<Panel padding={true} clear={true}>
|
||||
<BasicVideo
|
||||
title="Video -> Mocap Sample"
|
||||
src="/videos/face-animator-instruction-en.mp4"
|
||||
/>
|
||||
</Panel>
|
||||
</div>
|
||||
|
||||
</div>{/*2nd row*/}
|
||||
|
||||
</div>{/*panel*/}
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
{/*2nd row*/}
|
||||
</Panel>
|
||||
{/*panel*/}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
+62
-56
@@ -1,83 +1,89 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useSpring } from '@react-spring/web';
|
||||
import { useSpring } from "@react-spring/web";
|
||||
import { FileDetails, FileWrapper, FileLabel } from "components/common";
|
||||
import { faFileVideo } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faFileVideo, faVideo } from "@fortawesome/pro-solid-svg-icons";
|
||||
import "./style.scss";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
interface Props {
|
||||
//blob, clear, file, inputProps comes from file
|
||||
blob?: string,
|
||||
clear?: (file?: any) => void,
|
||||
file?: any,
|
||||
hideActions?: boolean,
|
||||
hideClearDetails?: boolean,
|
||||
inputProps?: any,
|
||||
onStateChange?: () => void,
|
||||
success?: boolean,
|
||||
submit?: () => void,
|
||||
working?: boolean,
|
||||
t: Function,
|
||||
[x:string]: any,
|
||||
blob?: string;
|
||||
clear?: (file?: any) => void;
|
||||
file?: any;
|
||||
hideActions?: boolean;
|
||||
hideClearDetails?: boolean;
|
||||
inputProps?: any;
|
||||
onStateChange?: () => void;
|
||||
success?: boolean;
|
||||
submit?: () => void;
|
||||
working?: boolean;
|
||||
t: Function;
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
export default function UploadFieldVideo({
|
||||
blob = "",
|
||||
clear = ()=>{},
|
||||
clear = () => {},
|
||||
file,
|
||||
hideActions,
|
||||
hideClearDetails,
|
||||
inputProps,
|
||||
onStateChange = ()=>{},
|
||||
onStateChange = () => {},
|
||||
success = false,
|
||||
submit = ()=>{},
|
||||
submit = () => {},
|
||||
working = false,
|
||||
t,
|
||||
...rest }: Props)
|
||||
{
|
||||
useEffect(()=>{
|
||||
...rest
|
||||
}: Props) {
|
||||
useEffect(() => {
|
||||
//only fire for file unload
|
||||
//on file load is fired in video elements
|
||||
if (!file) onStateChange();
|
||||
}, [file, onStateChange])
|
||||
}, [file, onStateChange]);
|
||||
|
||||
const style = useSpring({
|
||||
config: { mass: 1, tension: 120, friction: 14 }
|
||||
config: { mass: 1, tension: 120, friction: 14 },
|
||||
});
|
||||
|
||||
|
||||
const fileTypes = ["MP4"];
|
||||
|
||||
return <FileWrapper {...{ fileTypes, panelClass: "ratio-1x1", ...inputProps, ...rest }}>
|
||||
<div className="d-flex justify-content-center align-items-center w-100 h-100 overflow-hidden">
|
||||
{ file ?
|
||||
<>
|
||||
<FileDetails
|
||||
className="
|
||||
position-absolute top-0
|
||||
return (
|
||||
<FileWrapper {...{ fileTypes, panelClass: "", ...inputProps, ...rest }}>
|
||||
<div className="d-flex flex-column justify-content-center align-items-center w-100 h-100 overflow-hidden">
|
||||
{file ? (
|
||||
<>
|
||||
<FileDetails
|
||||
className="d-flex w-100 p-3"
|
||||
{...{ clear, file, hideClearDetails, icon: faFileVideo }}
|
||||
/>
|
||||
<video
|
||||
controls
|
||||
src={blob}
|
||||
className="mh-100 mw-100 object-fit-cover"
|
||||
onLoadStart={onStateChange}
|
||||
{...{ style }}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FileLabel
|
||||
className="
|
||||
upload-details
|
||||
d-flex w-100 p-3
|
||||
"
|
||||
{...{ clear, file, hideClearDetails, icon: faFileVideo }}
|
||||
/>
|
||||
<video
|
||||
controls
|
||||
src={blob}
|
||||
className="mh-100 mw-100 object-fit-cover"
|
||||
onLoadStart={onStateChange}
|
||||
{...{style}}
|
||||
/>
|
||||
|
||||
</> :
|
||||
<>
|
||||
<FileLabel className="
|
||||
upload-details
|
||||
position-absolute top-0
|
||||
d-flex w-100 p-3
|
||||
"
|
||||
{...{ fileTypes }}
|
||||
/>
|
||||
{/* this is the fontAwesome faVideo */}
|
||||
<svg className="fill-secondary" xmlns="http://www.w3.org/2000/svg" height="160" width="200" viewBox="0 0 576 512"><path d="M0 128C0 92.7 28.7 64 64 64H320c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128zM559.1 99.8c10.4 5.6 16.9 16.4 16.9 28.2V384c0 11.8-6.5 22.6-16.9 28.2s-23 5-32.9-1.6l-96-64L416 337.1V320 192 174.9l14.2-9.5 96-64c9.8-6.5 22.4-7.2 32.9-1.6z"/></svg>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</FileWrapper>;
|
||||
};
|
||||
{...{ fileTypes }}
|
||||
/>
|
||||
<div className="ratio ratio-16x9">
|
||||
<div className="d-flex align-items-center justify-content-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faVideo}
|
||||
className="opacity-25 display-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</FileWrapper>
|
||||
);
|
||||
}
|
||||
+24
-27
@@ -1,31 +1,28 @@
|
||||
import SelectSearcher from "components/common/SelectSearcher/SelectSearcher";
|
||||
import React from "react";
|
||||
|
||||
export default function TabContentLibrary(props: {
|
||||
t: Function,
|
||||
pageStateCallback: Function
|
||||
}){
|
||||
const { t } = props
|
||||
return(
|
||||
<div
|
||||
className="tab-pane fade py-4"
|
||||
id="vmcLibrary"
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<div className="d-flex gap-3">
|
||||
<div className="flex-grow-1">
|
||||
<input className="form-control w-100" placeholder={t("input.filenamePlaceholder")} />
|
||||
</div>
|
||||
<button className="btn btn-primary">{t("button.select")}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 py-4">
|
||||
<div className="d-flex justify-content-end">
|
||||
<button className="btn btn-primary" disabled>{t("button.generate")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
t: Function;
|
||||
pageStateCallback: Function;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<SelectSearcher
|
||||
modalTitle="Select a Video"
|
||||
label="Select a Video"
|
||||
tabs={[
|
||||
{
|
||||
label: "All Videos",
|
||||
searcherKey: "allVideos",
|
||||
mediaTypeFilter: "",
|
||||
},
|
||||
{
|
||||
label: "Bookmarked",
|
||||
searcherKey: "bookmarkedVideos",
|
||||
mediaTypeFilter: "",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
+112
-101
@@ -1,24 +1,30 @@
|
||||
import React, { useEffect, useState } 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 { 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";
|
||||
|
||||
export default function TabContentUpload(props:{
|
||||
t: Function
|
||||
pageStateCallback: (data:{tokenType:string, token:string | undefined}) => void
|
||||
}){
|
||||
const { t, pageStateCallback } = props
|
||||
const videoProps = useFile({})
|
||||
export default function TabContentUpload(props: {
|
||||
t: Function;
|
||||
pageStateCallback: (data: {
|
||||
tokenType: string;
|
||||
token: string | undefined;
|
||||
}) => void;
|
||||
}) {
|
||||
const { t, pageStateCallback } = props;
|
||||
const videoProps = useFile({});
|
||||
enum tabStates {
|
||||
NO_FILE, FILE_STAGED, FILE_UPLOADING, FILE_UPLOADED, MOCAPNET
|
||||
NO_FILE,
|
||||
FILE_STAGED,
|
||||
FILE_UPLOADING,
|
||||
FILE_UPLOADED,
|
||||
MOCAPNET,
|
||||
}
|
||||
const { NO_FILE, FILE_STAGED, FILE_UPLOADING, FILE_UPLOADED, MOCAPNET } = tabStates;
|
||||
const { NO_FILE, FILE_STAGED, FILE_UPLOADING, FILE_UPLOADED, MOCAPNET } =
|
||||
tabStates;
|
||||
const [tabState, setTabState] = useState<number>(NO_FILE);
|
||||
const [token, setToken] = useState<string>("");
|
||||
|
||||
@@ -26,105 +32,110 @@ export default function TabContentUpload(props:{
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
file: videoProps.file,
|
||||
source: "file",
|
||||
type:"video",
|
||||
type: "video",
|
||||
});
|
||||
|
||||
const handleUploadVideo = ()=>{
|
||||
setTabState(FILE_UPLOADING)
|
||||
UploadMedia(makeRequest()).then((res=>{
|
||||
if(res.success && "media_file_token" in res){
|
||||
setToken(res.media_file_token)
|
||||
const handleUploadVideo = () => {
|
||||
setTabState(FILE_UPLOADING);
|
||||
UploadMedia(makeRequest()).then(res => {
|
||||
if (res.success && "media_file_token" in res) {
|
||||
setToken(res.media_file_token);
|
||||
}
|
||||
}));
|
||||
}
|
||||
useEffect(()=>{
|
||||
if (token!=="") setTabState(FILE_UPLOADED)
|
||||
}, [token, FILE_UPLOADED])
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
if (token !== "") setTabState(FILE_UPLOADED);
|
||||
}, [token, FILE_UPLOADED]);
|
||||
|
||||
const handleEnqueueMocapNet = ()=>{
|
||||
const handleEnqueueMocapNet = () => {
|
||||
const request = {
|
||||
video_source: token,
|
||||
uuid_idempotency_token: uuidv4()
|
||||
}
|
||||
EnqueueVideoMotionCapture(request)
|
||||
.then(res=>{
|
||||
pageStateCallback({
|
||||
tokenType: "jobToken",
|
||||
token: res.inference_job_token
|
||||
})
|
||||
uuid_idempotency_token: uuidv4(),
|
||||
};
|
||||
EnqueueVideoMotionCapture(request).then(res => {
|
||||
pageStateCallback({
|
||||
tokenType: "jobToken",
|
||||
token: res.inference_job_token,
|
||||
});
|
||||
setTabState(MOCAPNET)
|
||||
}
|
||||
});
|
||||
setTabState(MOCAPNET);
|
||||
};
|
||||
// contains upload inout state and controls, see docs
|
||||
if (tabState === NO_FILE || tabState === FILE_STAGED){
|
||||
return(
|
||||
<div className="tab-pane fade show active py-4" id="vmcUpload">
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<VideoInput {...{t, ...videoProps,
|
||||
onStateChange: () => setTabState(videoProps.file ? FILE_STAGED : NO_FILE),
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row py-3">
|
||||
<div className="col-12">
|
||||
<div className="d-flex justify-content-end gap-3">
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
disabled={tabState!==FILE_STAGED}
|
||||
onClick={handleUploadVideo}
|
||||
>{t("button.upload")}</button>
|
||||
</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">
|
||||
if (tabState === NO_FILE || tabState === FILE_STAGED) {
|
||||
return (
|
||||
<div>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<h1>{t("message.UnknownError")}</h1>
|
||||
<div className="col-12">
|
||||
<VideoInput
|
||||
{...{
|
||||
t,
|
||||
...videoProps,
|
||||
onStateChange: () =>
|
||||
setTabState(videoProps.file ? FILE_STAGED : NO_FILE),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row py-3">
|
||||
<div className="col-12">
|
||||
<div className="d-flex justify-content-end gap-3">
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
disabled={tabState !== FILE_STAGED}
|
||||
onClick={handleUploadVideo}
|
||||
>
|
||||
{t("button.upload")}
|
||||
</button>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user