mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-12 04:42:16 +00:00
add progress to live portrait jobs
This commit is contained in:
1 parent
829d05b225
commit
5b1e130fbb
3 files changed
+66
-7
No files matched your search
@@ -46,6 +46,7 @@ import SessionLpInferenceResultsList from "./SessionLpInferenceResultsList";
|
||||
import { GetMedia } from "@storyteller/components/src/api/media_files/GetMedia";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { LivePortraitDetails } from "@storyteller/components/src/api/model_inference/GetModelInferenceJobStatus";
|
||||
import { useDocumentTitle } from "@storyteller/components/src/hooks/UseDocumentTitle";
|
||||
|
||||
interface LivePortraitProps {
|
||||
sessionSubscriptionsWrapper: SessionSubscriptionsWrapper;
|
||||
@@ -64,6 +65,7 @@ interface GeneratedVideo {
|
||||
export default function LivePortrait({
|
||||
sessionSubscriptionsWrapper,
|
||||
}: LivePortraitProps) {
|
||||
useDocumentTitle("Live Portrait AI. Free Video Animation");
|
||||
const { enqueueInferenceJob } = useInferenceJobs();
|
||||
const { open, close } = useModal();
|
||||
const [isEnqueuing, setIsEnqueuing] = useState(false);
|
||||
@@ -99,6 +101,9 @@ export default function LivePortrait({
|
||||
const [currentlyGeneratingList, setCurrentlyGeneratingList] = useState<
|
||||
{ sourceIndex: number; motionIndex: number }[]
|
||||
>([]);
|
||||
const [jobProgressPercentage, setJobProgressPercentage] = useState<
|
||||
number | null
|
||||
>(null);
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
@@ -146,6 +151,15 @@ export default function LivePortrait({
|
||||
}
|
||||
};
|
||||
|
||||
const handleJobProgress = (progress: number | null) => {
|
||||
setJobProgressPercentage(prevProgress => {
|
||||
if (prevProgress !== progress) {
|
||||
return progress;
|
||||
}
|
||||
return prevProgress;
|
||||
});
|
||||
};
|
||||
|
||||
const handleSourceSelect = (index: number) => {
|
||||
setIsUserUploaded(index >= numberOfInitialSourceTokens);
|
||||
setSelectedSourceIndex(index);
|
||||
@@ -201,6 +215,10 @@ export default function LivePortrait({
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Updated jobProgressPercentage:", jobProgressPercentage);
|
||||
}, [jobProgressPercentage]);
|
||||
|
||||
const renderVideoOrPlaceholder = () => {
|
||||
// Check if the current combination is being generated
|
||||
const isCurrentlyGenerating = currentlyGeneratingList.some(
|
||||
@@ -225,7 +243,9 @@ export default function LivePortrait({
|
||||
<h4 className="fw-medium">
|
||||
<div className="d-flex flex-column align-items-center gap-3 justify-content-center">
|
||||
<LoadingSpinner padding={false} />
|
||||
Generating video...
|
||||
{jobProgressPercentage !== null
|
||||
? `Generating video... ${jobProgressPercentage}%`
|
||||
: "Generating video..."}
|
||||
</div>
|
||||
</h4>
|
||||
</div>
|
||||
@@ -238,7 +258,7 @@ export default function LivePortrait({
|
||||
</div>
|
||||
);
|
||||
} else if (generatedVideoSrc) {
|
||||
// Show dynamically generated video if available
|
||||
// Show generated video if available
|
||||
return (
|
||||
<video
|
||||
loop
|
||||
@@ -270,7 +290,7 @@ export default function LivePortrait({
|
||||
</video>
|
||||
);
|
||||
} else {
|
||||
// Show "Click to Animate" if nothing is generating or precomputed
|
||||
// Show "Click to Animate" if nothing is generating or samples
|
||||
return (
|
||||
<div className="w-100 h-100 position-relative">
|
||||
<div
|
||||
@@ -440,7 +460,7 @@ export default function LivePortrait({
|
||||
sourceToken: livePortraitDetails.source_media_file_token,
|
||||
motionToken: livePortraitDetails.face_driver_media_file_token,
|
||||
videoSrc: mediaLink,
|
||||
jobToken, // Store the job token with the video
|
||||
jobToken,
|
||||
createdAt,
|
||||
};
|
||||
|
||||
@@ -678,6 +698,7 @@ export default function LivePortrait({
|
||||
onUploadClick={handleOpenUploadSourceModal}
|
||||
onSelectedMediaChange={handleSelectedMediaChange}
|
||||
uploadFocusPoint={uploadFocusPointSource}
|
||||
uploadButtonText="Upload your image/video"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -705,6 +726,7 @@ export default function LivePortrait({
|
||||
stepNumber={2}
|
||||
onUploadClick={handleOpenUploadMotionModal}
|
||||
uploadFocusPoint={uploadFocusPointMotion}
|
||||
uploadButtonText="Upload your motion video"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -822,6 +844,7 @@ export default function LivePortrait({
|
||||
)
|
||||
}
|
||||
onJobClick={handleJobClick}
|
||||
onJobProgress={handleJobProgress}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+35
-1
@@ -25,6 +25,7 @@ interface SessionLpInferenceResultsListProps {
|
||||
addSourceToken: (token: string) => void;
|
||||
addMotionToken: (token: string) => void;
|
||||
onJobClick: (job: InferenceJob) => void;
|
||||
onJobProgress: (progress: number | null) => void;
|
||||
}
|
||||
|
||||
export default function SessionLpInferenceResultsList({
|
||||
@@ -33,10 +34,13 @@ export default function SessionLpInferenceResultsList({
|
||||
addMotionToken,
|
||||
addSourceToken,
|
||||
onJobClick,
|
||||
onJobProgress,
|
||||
}: SessionLpInferenceResultsListProps) {
|
||||
const { inferenceJobsByCategory } = useInferenceJobs();
|
||||
const hasInitialized = useRef(false);
|
||||
|
||||
const lastProgressRef = useRef<{ [key: string]: number | null }>({});
|
||||
|
||||
const livePortraitJobs = useMemo(() => {
|
||||
return (
|
||||
inferenceJobsByCategory.get(FrontendInferenceJobType.LivePortrait) || []
|
||||
@@ -77,6 +81,34 @@ export default function SessionLpInferenceResultsList({
|
||||
}
|
||||
}, [livePortraitJobs, onJobTokens, addSourceToken, addMotionToken]);
|
||||
|
||||
useEffect(() => {
|
||||
livePortraitJobs.forEach((job: InferenceJob) => {
|
||||
const currentProgress = job.progressPercentage;
|
||||
|
||||
if (job.jobState === JobState.STARTED && currentProgress !== null) {
|
||||
if (lastProgressRef.current[job.jobToken] !== currentProgress) {
|
||||
console.log(
|
||||
`Updating progress for Job ${job.jobToken}: ${currentProgress}%`
|
||||
);
|
||||
onJobProgress(currentProgress);
|
||||
lastProgressRef.current[job.jobToken] = currentProgress;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(job.jobState === JobState.COMPLETE_SUCCESS ||
|
||||
job.jobState === JobState.COMPLETE_FAILURE) &&
|
||||
lastProgressRef.current[job.jobToken] !== null
|
||||
) {
|
||||
console.log(
|
||||
`Job ${job.jobToken} is complete. Resetting progress to null.`
|
||||
);
|
||||
onJobProgress(null);
|
||||
lastProgressRef.current[job.jobToken] = null;
|
||||
}
|
||||
});
|
||||
}, [livePortraitJobs, onJobProgress]);
|
||||
|
||||
const [mediaSrc, setMediaSrc] = useState<{ [key: string]: string }>({});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -166,7 +198,9 @@ export default function SessionLpInferenceResultsList({
|
||||
More Details
|
||||
</Link>
|
||||
) : (
|
||||
<div className="fs-7 opacity-50 mt-1">Waiting...</div>
|
||||
<div className="fs-7 opacity-50 fw-medium mt-1">
|
||||
{job.progressPercentage}% complete
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+4
-2
@@ -36,6 +36,7 @@ interface ThumbnailMediaPickerProps {
|
||||
onUploadClick?: () => void;
|
||||
onSelectedMediaChange?: (media: any) => void;
|
||||
uploadFocusPoint?: boolean;
|
||||
uploadButtonText?: string;
|
||||
}
|
||||
|
||||
interface MediaData {
|
||||
@@ -58,6 +59,7 @@ const ThumbnailMediaPicker: React.FC<ThumbnailMediaPickerProps> = React.memo(
|
||||
onUploadClick,
|
||||
onSelectedMediaChange,
|
||||
uploadFocusPoint,
|
||||
uploadButtonText = "Upload your own media",
|
||||
}) => {
|
||||
const [zoom, setZoom] = useState(1);
|
||||
const [isCropping, setIsCropping] = useState(false);
|
||||
@@ -289,8 +291,8 @@ const ThumbnailMediaPicker: React.FC<ThumbnailMediaPickerProps> = React.memo(
|
||||
|
||||
<Button
|
||||
icon={faUpload}
|
||||
label="Upload your media"
|
||||
variant="secondary"
|
||||
label={uploadButtonText}
|
||||
variant="action"
|
||||
className="order-3 order-lg-4"
|
||||
onClick={onUploadClick}
|
||||
focusPoint={uploadFocusPoint}
|
||||
|
||||
Reference in new issue
Block a user