mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
build out adhoc video saving graph
This commit is contained in:
committed by
psychedelicious
parent
af8ad1de3d
commit
d6651ba9a7
@@ -1,21 +1,21 @@
|
||||
import { MenuItem } from '@invoke-ai/ui-library';
|
||||
import { useImageDTOContext } from 'features/gallery/contexts/ImageDTOContext';
|
||||
import { selectVideoFirstFrameImage, videoFirstFrameImageChanged } from 'features/parameters/store/videoSlice';
|
||||
import { navigationApi } from 'features/ui/layouts/navigation-api';
|
||||
import { setCurrentVideo } from 'features/ui/layouts/video-store';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiVideoBold } from 'react-icons/pi';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
export const ImageMenuItemSendToVideo = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const imageDTO = useImageDTOContext();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
// For now, we'll use the image URL as a video source
|
||||
// In a real implementation, you might want to convert the image to video or use a different approach
|
||||
setCurrentVideo(imageDTO.image_url);
|
||||
dispatch(videoFirstFrameImageChanged(imageDTO));
|
||||
navigationApi.switchToTab('video');
|
||||
}, [imageDTO.image_url]);
|
||||
}, [imageDTO]);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import { selectVideoFirstFrameImage, selectVideoLastFrameImage } from 'features/parameters/store/videoSlice';
|
||||
import { zImageField } from 'features/nodes/types/common';
|
||||
import { Graph } from 'features/nodes/util/graph/generation/Graph';
|
||||
import { selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils';
|
||||
import type { GraphBuilderArg, GraphBuilderReturn } from 'features/nodes/util/graph/types';
|
||||
import { UnsupportedGenerationModeError } from 'features/nodes/util/graph/types';
|
||||
import { t } from 'i18next';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
// Default video parameters - these could be moved to a video params slice in the future
|
||||
const DEFAULT_VIDEO_DURATION = 5;
|
||||
const DEFAULT_VIDEO_ASPECT_RATIO = "1280:768"; // Default landscape
|
||||
const DEFAULT_ENHANCE_PROMPT = true;
|
||||
|
||||
// Video parameter extraction helper
|
||||
const getVideoParameters = (state: RootState) => {
|
||||
// In the future, these could come from a dedicated video parameters slice
|
||||
// For now, we use defaults but allow them to be overridden by any video-specific state
|
||||
return {
|
||||
duration: DEFAULT_VIDEO_DURATION,
|
||||
aspectRatio: DEFAULT_VIDEO_ASPECT_RATIO,
|
||||
enhancePrompt: DEFAULT_ENHANCE_PROMPT,
|
||||
};
|
||||
};
|
||||
|
||||
export const buildRunwayVideoGraph = (arg: GraphBuilderArg): GraphBuilderReturn => {
|
||||
const { generationMode, state, manager } = arg;
|
||||
|
||||
log.debug({ generationMode, manager: manager?.id }, 'Building Runway video graph');
|
||||
|
||||
// Runway video generation supports text-to-video and image-to-video
|
||||
// We can support multiple generation modes depending on whether frame images are provided
|
||||
const supportedModes = ['txt2img'] as const;
|
||||
if (!supportedModes.includes(generationMode as any)) {
|
||||
throw new UnsupportedGenerationModeError(t('toast.runwayIncompatibleGenerationMode'));
|
||||
}
|
||||
|
||||
const params = selectParamsSlice(state);
|
||||
const prompts = selectPresetModifiedPrompts(state);
|
||||
const videoFirstFrameImage = selectVideoFirstFrameImage(state);
|
||||
const videoLastFrameImage = selectVideoLastFrameImage(state);
|
||||
const videoParams = getVideoParameters(state);
|
||||
|
||||
// Get seed from params
|
||||
const { seed, shouldRandomizeSeed } = params;
|
||||
const finalSeed = shouldRandomizeSeed ? undefined : seed;
|
||||
|
||||
// Determine if this is image-to-video or text-to-video
|
||||
const hasFrameImages = videoFirstFrameImage || videoLastFrameImage;
|
||||
|
||||
const g = new Graph(getPrefixedId('runway_video_graph'));
|
||||
|
||||
const positivePrompt = g.addNode({
|
||||
id: getPrefixedId('positive_prompt'),
|
||||
type: 'string',
|
||||
value: prompts.positive,
|
||||
});
|
||||
|
||||
// Create the runway video generation node
|
||||
const runwayVideoNode = g.addNode({
|
||||
id: getPrefixedId('runway_generate_video'),
|
||||
// @ts-expect-error: This node is not available in the OSS application
|
||||
type: 'runway_generate_video',
|
||||
duration: videoParams.duration,
|
||||
aspect_ratio: videoParams.aspectRatio,
|
||||
seed: finalSeed,
|
||||
});
|
||||
|
||||
// @ts-expect-error: This node is not available in the OSS application
|
||||
g.addEdge(positivePrompt, 'value', runwayVideoNode, 'prompt');
|
||||
|
||||
|
||||
// Add first frame image if provided
|
||||
if (videoFirstFrameImage) {
|
||||
const firstFrameImageField = zImageField.parse(videoFirstFrameImage);
|
||||
// @ts-expect-error: This connection is specific to runway node
|
||||
runwayVideoNode.first_frame_image = firstFrameImageField;
|
||||
}
|
||||
|
||||
// Add last frame image if provided
|
||||
if (videoLastFrameImage) {
|
||||
const lastFrameImageField = zImageField.parse(videoLastFrameImage);
|
||||
// @ts-expect-error: This connection is specific to runway node
|
||||
runwayVideoNode.last_frame_image = lastFrameImageField;
|
||||
}
|
||||
|
||||
// Set up metadata
|
||||
g.upsertMetadata({
|
||||
positive_prompt: prompts.positive,
|
||||
negative_prompt: prompts.negative || '',
|
||||
video_duration: videoParams.duration,
|
||||
video_aspect_ratio: videoParams.aspectRatio,
|
||||
seed: finalSeed,
|
||||
enhance_prompt: videoParams.enhancePrompt,
|
||||
generation_type: hasFrameImages ? 'image-to-video' : 'text-to-video',
|
||||
});
|
||||
|
||||
// Add video frame images to metadata if they exist
|
||||
if (hasFrameImages) {
|
||||
g.upsertMetadata({
|
||||
first_frame_image: videoFirstFrameImage,
|
||||
last_frame_image: videoLastFrameImage,
|
||||
}, 'merge');
|
||||
}
|
||||
|
||||
g.setMetadataReceivingNode(runwayVideoNode);
|
||||
|
||||
return {
|
||||
g,
|
||||
positivePrompt,
|
||||
};
|
||||
};
|
||||
@@ -12,7 +12,10 @@ const zVideoState = z.object({
|
||||
_version: z.literal(1),
|
||||
videoFirstFrameImage: zImageWithDims.nullable(),
|
||||
videoLastFrameImage: zImageWithDims.nullable(),
|
||||
generatedVideoUrl: z.string().nullable(),
|
||||
generatedVideo: z.object({
|
||||
url: z.string(),
|
||||
taskId: z.number(),
|
||||
}).nullable(),
|
||||
});
|
||||
|
||||
export type VideoState = z.infer<typeof zVideoState>;
|
||||
@@ -21,7 +24,7 @@ const getInitialState = (): VideoState => ({
|
||||
_version: 1,
|
||||
videoFirstFrameImage: null,
|
||||
videoLastFrameImage: null,
|
||||
generatedVideoUrl: null,
|
||||
generatedVideo: null,
|
||||
});
|
||||
|
||||
const slice = createSlice({
|
||||
@@ -36,8 +39,8 @@ const slice = createSlice({
|
||||
state.videoLastFrameImage = action.payload;
|
||||
},
|
||||
|
||||
generatedVideoUrlChanged: (state, action: PayloadAction<string | null>) => {
|
||||
state.generatedVideoUrl = action.payload;
|
||||
generatedVideoChanged: (state, action: PayloadAction<{ url: string, taskId: number } | null>) => {
|
||||
state.generatedVideo = action.payload;
|
||||
},
|
||||
|
||||
},
|
||||
@@ -46,7 +49,7 @@ const slice = createSlice({
|
||||
export const {
|
||||
videoFirstFrameImageChanged,
|
||||
videoLastFrameImageChanged,
|
||||
generatedVideoUrlChanged,
|
||||
generatedVideoChanged,
|
||||
} = slice.actions;
|
||||
|
||||
export const videoSliceConfig: SliceConfig<typeof slice> = {
|
||||
@@ -69,4 +72,4 @@ const createVideoSelector = <T>(selector: Selector<VideoState, T>) => createSele
|
||||
|
||||
export const selectVideoFirstFrameImage = createVideoSelector((video) => video.videoFirstFrameImage);
|
||||
export const selectVideoLastFrameImage = createVideoSelector((video) => video.videoLastFrameImage);
|
||||
export const selectGeneratedVideoUrl = createVideoSelector((video) => video.generatedVideoUrl);
|
||||
export const selectGeneratedVideo = createVideoSelector((video) => video.generatedVideo);
|
||||
@@ -1,33 +1,55 @@
|
||||
import { Box, Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { Box, Button, Flex, Text } from '@invoke-ai/ui-library';
|
||||
import { useFocusRegion } from 'common/hooks/focus';
|
||||
import { memo, useRef } from 'react';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ReactPlayer from 'react-player';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectGeneratedVideoUrl } from 'features/parameters/store/videoSlice';
|
||||
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
|
||||
import { selectGeneratedVideo } from 'features/parameters/store/videoSlice';
|
||||
import { PiCheckBold } from 'react-icons/pi';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { saveVideo } from 'features/video/saveVideo';
|
||||
|
||||
|
||||
export const VideoPlayerPanel = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const generatedVideoUrl = useAppSelector(selectGeneratedVideoUrl);
|
||||
|
||||
const generatedVideo = useAppSelector(selectGeneratedVideo);
|
||||
|
||||
useFocusRegion('video', ref);
|
||||
|
||||
const { dispatch, getState } = useAppStore();
|
||||
|
||||
const handleSaveVideo = useCallback(() => {
|
||||
console.log('generatedVideo', generatedVideo);
|
||||
if (!generatedVideo?.taskId) {
|
||||
return
|
||||
}
|
||||
console.log('saving video', generatedVideo.taskId);
|
||||
saveVideo({ dispatch, getState, taskId: `${generatedVideo.taskId}` });
|
||||
}, [dispatch, getState, generatedVideo]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Flex ref={ref} w="full" h="full" flexDirection="column" gap={4}>
|
||||
<Box flex={1} position="relative">
|
||||
{generatedVideoUrl && <ReactPlayer
|
||||
src={generatedVideoUrl}
|
||||
width="75%"
|
||||
height="75%"
|
||||
controls={true}
|
||||
style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', maxWidth: '900px'}}
|
||||
/>}
|
||||
{!generatedVideoUrl && <Text>No video generated</Text>}
|
||||
</Box>
|
||||
|
||||
{generatedVideo &&
|
||||
<>
|
||||
<Box flex={0.75} position="relative" >
|
||||
<ReactPlayer
|
||||
src={generatedVideo.url}
|
||||
width="75%"
|
||||
height="75%"
|
||||
controls={true}
|
||||
style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', maxWidth: '900px' }}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Button leftIcon={<PiCheckBold />} colorScheme="invokeBlue" onClick={handleSaveVideo}>Keep</Button>
|
||||
|
||||
</>}
|
||||
{!generatedVideo && <Text>No video generated</Text>}
|
||||
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
25
invokeai/frontend/web/src/features/video/graph.ts
Normal file
25
invokeai/frontend/web/src/features/video/graph.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { Graph } from 'features/nodes/util/graph/generation/Graph';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export const buildSaveVideoGraph = ({
|
||||
state,
|
||||
}: {
|
||||
state: RootState;
|
||||
}): { graph: Graph; outputNodeId: string } => {
|
||||
|
||||
const taskId = state.video.generatedVideo?.taskId;
|
||||
|
||||
assert(taskId, 'No task ID found in state');
|
||||
|
||||
const graph = new Graph(getPrefixedId('save-video-graph'));
|
||||
const outputNode = graph.addNode({
|
||||
// @ts-expect-error: These nodes are not available in the OSS application
|
||||
type: 'save_runway_video',
|
||||
id: getPrefixedId('save_runway_video'),
|
||||
runway_task_id: taskId,
|
||||
});
|
||||
return { graph, outputNodeId: outputNode.id };
|
||||
|
||||
};
|
||||
41
invokeai/frontend/web/src/features/video/saveVideo.ts
Normal file
41
invokeai/frontend/web/src/features/video/saveVideo.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { AppDispatch, AppGetState } from 'app/store/store';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { t } from 'i18next';
|
||||
import { buildRunGraphDependencies, runGraph } from 'services/api/run-graph';
|
||||
import { $socket } from 'services/events/stores';
|
||||
import { assert } from 'tsafe';
|
||||
import { buildSaveVideoGraph } from './graph';
|
||||
import { saveVideoApi } from './state';
|
||||
|
||||
|
||||
|
||||
export const saveVideo = async (arg: { dispatch: AppDispatch; getState: AppGetState; taskId?: string }) => {
|
||||
const { dispatch, getState, taskId } = arg;
|
||||
const socket = $socket.get();
|
||||
if (!socket) {
|
||||
return;
|
||||
}
|
||||
const { graph, outputNodeId } = buildSaveVideoGraph({
|
||||
state: getState(),
|
||||
});
|
||||
const dependencies = buildRunGraphDependencies(dispatch, socket);
|
||||
try {
|
||||
const { output } = await runGraph({
|
||||
graph,
|
||||
outputNodeId,
|
||||
dependencies,
|
||||
options: {
|
||||
prepend: true,
|
||||
},
|
||||
});
|
||||
assert(output.type === 'string_output');
|
||||
saveVideoApi.setSuccess(output.value);
|
||||
} catch {
|
||||
saveVideoApi.reset();
|
||||
toast({
|
||||
id: 'SAVE_VIDEO_FAILED',
|
||||
title: t('toast.saveVideoFailed'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
};
|
||||
98
invokeai/frontend/web/src/features/video/state.ts
Normal file
98
invokeai/frontend/web/src/features/video/state.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { deepClone } from 'common/util/deepClone';
|
||||
import { atom } from 'nanostores';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
type SuccessState = {
|
||||
isSuccess: true;
|
||||
isError: false;
|
||||
isPending: false;
|
||||
result: string;
|
||||
error: null;
|
||||
imageDTO?: ImageDTO;
|
||||
};
|
||||
|
||||
type ErrorState = {
|
||||
isSuccess: false;
|
||||
isError: true;
|
||||
isPending: false;
|
||||
result: null;
|
||||
error: Error;
|
||||
imageDTO?: ImageDTO;
|
||||
};
|
||||
|
||||
type PendingState = {
|
||||
isSuccess: false;
|
||||
isError: false;
|
||||
isPending: true;
|
||||
result: null;
|
||||
error: null;
|
||||
imageDTO?: ImageDTO;
|
||||
};
|
||||
|
||||
type IdleState = {
|
||||
isSuccess: false;
|
||||
isError: false;
|
||||
isPending: false;
|
||||
result: null;
|
||||
error: null;
|
||||
imageDTO?: ImageDTO;
|
||||
};
|
||||
|
||||
export type PromptExpansionRequestState = IdleState | PendingState | SuccessState | ErrorState;
|
||||
|
||||
const IDLE_STATE: IdleState = {
|
||||
isSuccess: false,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
result: null,
|
||||
error: null,
|
||||
imageDTO: undefined,
|
||||
};
|
||||
|
||||
const $state = atom<PromptExpansionRequestState>(deepClone(IDLE_STATE));
|
||||
|
||||
const reset = () => {
|
||||
$state.set(deepClone(IDLE_STATE));
|
||||
};
|
||||
|
||||
const setPending = (imageDTO?: ImageDTO) => {
|
||||
$state.set({
|
||||
...$state.get(),
|
||||
isSuccess: false,
|
||||
isError: false,
|
||||
isPending: true,
|
||||
result: null,
|
||||
error: null,
|
||||
imageDTO,
|
||||
});
|
||||
};
|
||||
|
||||
const setSuccess = (result: string) => {
|
||||
$state.set({
|
||||
...$state.get(),
|
||||
isSuccess: true,
|
||||
isError: false,
|
||||
isPending: false,
|
||||
result,
|
||||
error: null,
|
||||
});
|
||||
};
|
||||
|
||||
const setError = (error: Error) => {
|
||||
$state.set({
|
||||
...$state.get(),
|
||||
isSuccess: false,
|
||||
isError: true,
|
||||
isPending: false,
|
||||
result: null,
|
||||
error,
|
||||
});
|
||||
};
|
||||
|
||||
export const saveVideoApi = {
|
||||
$state,
|
||||
reset,
|
||||
setPending,
|
||||
setSuccess,
|
||||
setError,
|
||||
};
|
||||
@@ -9094,7 +9094,7 @@ export type components = {
|
||||
* @description The results of node executions
|
||||
*/
|
||||
results: {
|
||||
[key: string]: components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"];
|
||||
[key: string]: components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["RunwayVideoOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["VideoOutput"];
|
||||
};
|
||||
/**
|
||||
* Errors
|
||||
@@ -11880,7 +11880,7 @@ export type components = {
|
||||
* Result
|
||||
* @description The result of the invocation
|
||||
*/
|
||||
result: components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"];
|
||||
result: components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["BoundingBoxCollectionOutput"] | components["schemas"]["BoundingBoxOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["CogView4ConditioningOutput"] | components["schemas"]["CogView4ModelLoaderOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FloatGeneratorOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["FluxConditioningCollectionOutput"] | components["schemas"]["FluxConditioningOutput"] | components["schemas"]["FluxControlLoRALoaderOutput"] | components["schemas"]["FluxControlNetOutput"] | components["schemas"]["FluxFillOutput"] | components["schemas"]["FluxKontextOutput"] | components["schemas"]["FluxLoRALoaderOutput"] | components["schemas"]["FluxModelLoaderOutput"] | components["schemas"]["FluxReduxOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["ImageGeneratorOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["ImagePanelCoordinateOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["IntegerGeneratorOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["LatentsMetaOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["MDControlListOutput"] | components["schemas"]["MDIPAdapterListOutput"] | components["schemas"]["MDT2IAdapterListOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["MetadataToLorasCollectionOutput"] | components["schemas"]["MetadataToModelOutput"] | components["schemas"]["MetadataToSDXLModelOutput"] | components["schemas"]["ModelIdentifierOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["RunwayVideoOutput"] | components["schemas"]["SD3ConditioningOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["Sd3ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["String2Output"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["StringGeneratorOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["VideoOutput"];
|
||||
};
|
||||
/**
|
||||
* InvocationErrorEvent
|
||||
@@ -18285,6 +18285,28 @@ export type components = {
|
||||
*/
|
||||
type: "round_float";
|
||||
};
|
||||
/**
|
||||
* RunwayVideoOutput
|
||||
* @description Base class for nodes that output a runway result
|
||||
*/
|
||||
RunwayVideoOutput: {
|
||||
/**
|
||||
* Video Url
|
||||
* @description The output video url
|
||||
*/
|
||||
video_url: string;
|
||||
/**
|
||||
* Runway Task Id
|
||||
* @description The runway task id
|
||||
*/
|
||||
runway_task_id: string;
|
||||
/**
|
||||
* type
|
||||
* @default runway_video_output
|
||||
* @constant
|
||||
*/
|
||||
type: "runway_video_output";
|
||||
};
|
||||
/** SAMPoint */
|
||||
SAMPoint: {
|
||||
/**
|
||||
@@ -21459,7 +21481,7 @@ export type components = {
|
||||
* used, and the type will be ignored. They are included here for backwards compatibility.
|
||||
* @enum {string}
|
||||
*/
|
||||
UIType: "MainModelField" | "CogView4MainModelField" | "FluxMainModelField" | "SD3MainModelField" | "SDXLMainModelField" | "SDXLRefinerModelField" | "ONNXModelField" | "VAEModelField" | "FluxVAEModelField" | "LoRAModelField" | "ControlNetModelField" | "IPAdapterModelField" | "T2IAdapterModelField" | "T5EncoderModelField" | "CLIPEmbedModelField" | "CLIPLEmbedModelField" | "CLIPGEmbedModelField" | "SpandrelImageToImageModelField" | "ControlLoRAModelField" | "SigLipModelField" | "FluxReduxModelField" | "LLaVAModelField" | "Imagen3ModelField" | "Imagen4ModelField" | "ChatGPT4oModelField" | "Gemini2_5ModelField" | "FluxKontextModelField" | "SchedulerField" | "AnyField" | "CollectionField" | "CollectionItemField" | "DEPRECATED_Boolean" | "DEPRECATED_Color" | "DEPRECATED_Conditioning" | "DEPRECATED_Control" | "DEPRECATED_Float" | "DEPRECATED_Image" | "DEPRECATED_Integer" | "DEPRECATED_Latents" | "DEPRECATED_String" | "DEPRECATED_BooleanCollection" | "DEPRECATED_ColorCollection" | "DEPRECATED_ConditioningCollection" | "DEPRECATED_ControlCollection" | "DEPRECATED_FloatCollection" | "DEPRECATED_ImageCollection" | "DEPRECATED_IntegerCollection" | "DEPRECATED_LatentsCollection" | "DEPRECATED_StringCollection" | "DEPRECATED_BooleanPolymorphic" | "DEPRECATED_ColorPolymorphic" | "DEPRECATED_ConditioningPolymorphic" | "DEPRECATED_ControlPolymorphic" | "DEPRECATED_FloatPolymorphic" | "DEPRECATED_ImagePolymorphic" | "DEPRECATED_IntegerPolymorphic" | "DEPRECATED_LatentsPolymorphic" | "DEPRECATED_StringPolymorphic" | "DEPRECATED_UNet" | "DEPRECATED_Vae" | "DEPRECATED_CLIP" | "DEPRECATED_Collection" | "DEPRECATED_CollectionItem" | "DEPRECATED_Enum" | "DEPRECATED_WorkflowField" | "DEPRECATED_IsIntermediate" | "DEPRECATED_BoardField" | "DEPRECATED_MetadataItem" | "DEPRECATED_MetadataItemCollection" | "DEPRECATED_MetadataItemPolymorphic" | "DEPRECATED_MetadataDict";
|
||||
UIType: "MainModelField" | "CogView4MainModelField" | "FluxMainModelField" | "SD3MainModelField" | "SDXLMainModelField" | "SDXLRefinerModelField" | "ONNXModelField" | "VAEModelField" | "FluxVAEModelField" | "LoRAModelField" | "ControlNetModelField" | "IPAdapterModelField" | "T2IAdapterModelField" | "T5EncoderModelField" | "CLIPEmbedModelField" | "CLIPLEmbedModelField" | "CLIPGEmbedModelField" | "SpandrelImageToImageModelField" | "ControlLoRAModelField" | "SigLipModelField" | "FluxReduxModelField" | "LLaVAModelField" | "Imagen3ModelField" | "Imagen4ModelField" | "ChatGPT4oModelField" | "Gemini2_5ModelField" | "FluxKontextModelField" | "SchedulerField" | "AnyField" | "VideoField" | "CollectionField" | "CollectionItemField" | "DEPRECATED_Boolean" | "DEPRECATED_Color" | "DEPRECATED_Conditioning" | "DEPRECATED_Control" | "DEPRECATED_Float" | "DEPRECATED_Image" | "DEPRECATED_Integer" | "DEPRECATED_Latents" | "DEPRECATED_String" | "DEPRECATED_BooleanCollection" | "DEPRECATED_ColorCollection" | "DEPRECATED_ConditioningCollection" | "DEPRECATED_ControlCollection" | "DEPRECATED_FloatCollection" | "DEPRECATED_ImageCollection" | "DEPRECATED_IntegerCollection" | "DEPRECATED_LatentsCollection" | "DEPRECATED_StringCollection" | "DEPRECATED_BooleanPolymorphic" | "DEPRECATED_ColorPolymorphic" | "DEPRECATED_ConditioningPolymorphic" | "DEPRECATED_ControlPolymorphic" | "DEPRECATED_FloatPolymorphic" | "DEPRECATED_ImagePolymorphic" | "DEPRECATED_IntegerPolymorphic" | "DEPRECATED_LatentsPolymorphic" | "DEPRECATED_StringPolymorphic" | "DEPRECATED_UNet" | "DEPRECATED_Vae" | "DEPRECATED_CLIP" | "DEPRECATED_Collection" | "DEPRECATED_CollectionItem" | "DEPRECATED_Enum" | "DEPRECATED_WorkflowField" | "DEPRECATED_IsIntermediate" | "DEPRECATED_BoardField" | "DEPRECATED_MetadataItem" | "DEPRECATED_MetadataItemCollection" | "DEPRECATED_MetadataItemPolymorphic" | "DEPRECATED_MetadataDict";
|
||||
/** UNetField */
|
||||
UNetField: {
|
||||
/** @description Info to load unet submodel */
|
||||
@@ -21870,6 +21892,46 @@ export type components = {
|
||||
*/
|
||||
output_fields: components["schemas"]["FieldIdentifier"][];
|
||||
};
|
||||
/**
|
||||
* VideoField
|
||||
* @description A video primitive field
|
||||
*/
|
||||
VideoField: {
|
||||
/**
|
||||
* Video Id
|
||||
* @description The id of the video
|
||||
*/
|
||||
video_id: string;
|
||||
/**
|
||||
* Width
|
||||
* @description The width of the video in pixels
|
||||
*/
|
||||
width: number;
|
||||
/**
|
||||
* Height
|
||||
* @description The height of the video in pixels
|
||||
*/
|
||||
height: number;
|
||||
/**
|
||||
* Duration Seconds
|
||||
* @description The duration of the video in seconds
|
||||
*/
|
||||
duration_seconds: number;
|
||||
};
|
||||
/**
|
||||
* VideoOutput
|
||||
* @description Base class for nodes that output a video
|
||||
*/
|
||||
VideoOutput: {
|
||||
/** @description The output video */
|
||||
video: components["schemas"]["VideoField"];
|
||||
/**
|
||||
* type
|
||||
* @default video_output
|
||||
* @constant
|
||||
*/
|
||||
type: "video_output";
|
||||
};
|
||||
/** Workflow */
|
||||
Workflow: {
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ import { boardIdSelected, galleryViewChanged, imageSelected } from 'features/gal
|
||||
import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useNodeExecutionState';
|
||||
import { isImageField, isImageFieldCollection } from 'features/nodes/types/common';
|
||||
import { zNodeStatus } from 'features/nodes/types/invocation';
|
||||
import { generatedVideoUrlChanged } from 'features/parameters/store/videoSlice';
|
||||
import { generatedVideoChanged } from 'features/parameters/store/videoSlice';
|
||||
import type { LRUCache } from 'lru-cache';
|
||||
import { boardsApi } from 'services/api/endpoints/boards';
|
||||
import { getImageDTOSafe, imagesApi } from 'services/api/endpoints/images';
|
||||
@@ -205,11 +205,11 @@ export const buildOnInvocationComplete = (
|
||||
return imageDTOs;
|
||||
};
|
||||
|
||||
const getResultVideoDTOs = async (data: S['InvocationCompleteEvent']): Promise<string | null> => {
|
||||
const getResultVideoDTOs = async (data: S['InvocationCompleteEvent']): Promise<{url: string , taskId: number} | null> => {
|
||||
// @ts-expect-error: This is a workaround to get the video name from the result
|
||||
if (data.invocation.type === 'runway_generate_video') {
|
||||
// @ts-expect-error: This is a workaround to get the video name from the result
|
||||
return data.result.video.video_name;
|
||||
return {url: data.result.video_url, taskId: data.result.runway_task_id};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -235,9 +235,9 @@ export const buildOnInvocationComplete = (
|
||||
|
||||
await addImagesToGallery(data);
|
||||
|
||||
const videoUrl = await getResultVideoDTOs(data);
|
||||
if (videoUrl) {
|
||||
dispatch(generatedVideoUrlChanged(videoUrl));
|
||||
const videoResult = await getResultVideoDTOs(data);
|
||||
if (videoResult) {
|
||||
dispatch(generatedVideoChanged(videoResult));
|
||||
}
|
||||
|
||||
$lastProgressEvent.set(null);
|
||||
|
||||
Reference in New Issue
Block a user