mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-15 05:18:03 -05:00
feat(ui): metadata recall for videos
This commit is contained in:
committed by
Mary Hipp Rogers
parent
47ffe365bc
commit
82893804ff
@@ -810,7 +810,11 @@
|
||||
"vae": "VAE",
|
||||
"width": "Width",
|
||||
"workflow": "Workflow",
|
||||
"canvasV2Metadata": "Canvas Layers"
|
||||
"canvasV2Metadata": "Canvas Layers",
|
||||
"videoModel": "Model",
|
||||
"videoDuration": "Duration",
|
||||
"videoAspectRatio": "Aspect Ratio",
|
||||
"videoResolution": "Resolution"
|
||||
},
|
||||
"modelManager": {
|
||||
"active": "active",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useIsRegionFocused } from 'common/hooks/focus';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { useLoadWorkflow } from 'features/gallery/hooks/useLoadWorkflow';
|
||||
import { useRecallAll } from 'features/gallery/hooks/useRecallAll';
|
||||
import { useRecallAll } from 'features/gallery/hooks/useRecallAllImageMetadata';
|
||||
import { useRecallDimensions } from 'features/gallery/hooks/useRecallDimensions';
|
||||
import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
|
||||
import { useRecallRemix } from 'features/gallery/hooks/useRecallRemix';
|
||||
|
||||
@@ -118,7 +118,7 @@ export const useStudioInitAction = (action?: StudioInitAction) => {
|
||||
const metadata = getImageMetadataResult.value;
|
||||
store.dispatch(canvasReset());
|
||||
// This shows a toast
|
||||
await MetadataUtils.recallAll(metadata, store);
|
||||
await MetadataUtils.recallAllImageMetadata(metadata, store);
|
||||
},
|
||||
[store, t]
|
||||
);
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
} from 'features/controlLayers/util/getScaledBoundingBoxDimensions';
|
||||
import { simplifyFlatNumbersArray } from 'features/controlLayers/util/simplify';
|
||||
import { isMainModelBase, zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { API_BASE_MODELS, VIDEO_BASE_MODELS } from 'features/parameters/types/constants';
|
||||
import { API_BASE_MODELS } from 'features/parameters/types/constants';
|
||||
import { getGridSize, getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
||||
import type { IRect } from 'konva/lib/types';
|
||||
import type { UndoableOptions } from 'redux-undo';
|
||||
|
||||
@@ -501,6 +501,10 @@ export const RUNWAY_ASPECT_RATIOS: Record<RunwayAspectRatio, Dimensions> = {
|
||||
'21:9': { width: 1584, height: 672 },
|
||||
};
|
||||
|
||||
export const zVideoAspectRatio = z.union([zVeo3AspectRatioID, zRunwayAspectRatioID]);
|
||||
export type VideoAspectRatio = z.infer<typeof zVideoAspectRatio>;
|
||||
export const isVideoAspectRatio = (v: unknown): v is VideoAspectRatio => zVideoAspectRatio.safeParse(v).success;
|
||||
|
||||
export const zVeo3Resolution = z.enum(['720p', '1080p']);
|
||||
export type Veo3Resolution = z.infer<typeof zVeo3Resolution>;
|
||||
export const isVeo3Resolution = (v: unknown): v is Veo3Resolution => zVeo3Resolution.safeParse(v).success;
|
||||
@@ -513,6 +517,9 @@ export const zRunwayResolution = z.enum(['720p']);
|
||||
export type RunwayResolution = z.infer<typeof zRunwayResolution>;
|
||||
export const isRunwayResolution = (v: unknown): v is RunwayResolution => zRunwayResolution.safeParse(v).success;
|
||||
|
||||
export const zVideoResolution = z.union([zVeo3Resolution, zRunwayResolution]);
|
||||
export type VideoResolution = z.infer<typeof zVideoResolution>;
|
||||
|
||||
const zAspectRatioConfig = z.object({
|
||||
id: zAspectRatioID,
|
||||
value: z.number().gt(0),
|
||||
@@ -541,6 +548,9 @@ export const RUNWAY_DURATIONS: Record<RunwayDuration, string> = {
|
||||
'10': '10 seconds',
|
||||
};
|
||||
|
||||
export const zVideoDuration = z.union([zVeo3DurationID, zRunwayDurationID]);
|
||||
export type VideoDuration = z.infer<typeof zVideoDuration>;
|
||||
|
||||
const zBboxState = z.object({
|
||||
rect: z.object({
|
||||
x: z.number().int(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||
import { SubMenuButtonContent, useSubMenu } from 'common/hooks/useSubMenu';
|
||||
import { useItemDTOContext } from 'features/gallery/contexts/ItemDTOContext';
|
||||
import { useRecallAll } from 'features/gallery/hooks/useRecallAll';
|
||||
import { useRecallAll } from 'features/gallery/hooks/useRecallAllImageMetadata';
|
||||
import { useRecallCLIPSkip } from 'features/gallery/hooks/useRecallCLIPSkip';
|
||||
import { useRecallDimensions } from 'features/gallery/hooks/useRecallDimensions';
|
||||
import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
|
||||
|
||||
@@ -9,10 +9,11 @@ import type {
|
||||
UnrecallableMetadataHandler,
|
||||
} from 'features/metadata/parsing';
|
||||
import {
|
||||
MetadataHandlers,
|
||||
ImageMetadataHandlers,
|
||||
useCollectionMetadataDatum,
|
||||
useSingleMetadataDatum,
|
||||
useUnrecallableMetadataDatum,
|
||||
VideoMetadataHandlers,
|
||||
} from 'features/metadata/parsing';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { PiArrowBendUpLeftBold } from 'react-icons/pi';
|
||||
@@ -21,7 +22,7 @@ type Props = {
|
||||
metadata?: unknown;
|
||||
};
|
||||
|
||||
const ImageMetadataActions = (props: Props) => {
|
||||
export const ImageMetadataActions = memo((props: Props) => {
|
||||
const { metadata } = props;
|
||||
|
||||
if (!metadata || Object.keys(metadata).length === 0) {
|
||||
@@ -30,38 +31,60 @@ const ImageMetadataActions = (props: Props) => {
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" ps={8}>
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={MetadataHandlers.GenerationMode} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.PositivePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.NegativePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.MainModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.VAEModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Width} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Height} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Seed} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Steps} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Scheduler} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.CLIPSkip} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.CFGScale} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.CFGRescaleMultiplier} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.Guidance} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.DenoisingStrength} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.SeamlessX} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.SeamlessY} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerCFGScale} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerPositiveAestheticScore} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerNegativeAestheticScore} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerScheduler} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerDenoisingStart} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.RefinerSteps} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={MetadataHandlers.CanvasLayers} />
|
||||
<CollectionMetadataDatum metadata={metadata} handler={MetadataHandlers.RefImages} />
|
||||
<CollectionMetadataDatum metadata={metadata} handler={MetadataHandlers.LoRAs} />
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.GenerationMode} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.PositivePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.NegativePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.MainModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.VAEModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.Width} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.Height} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.Seed} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.Steps} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.Scheduler} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.CLIPSkip} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.CFGScale} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.CFGRescaleMultiplier} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.Guidance} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.DenoisingStrength} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.SeamlessX} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.SeamlessY} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.RefinerModel} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.RefinerCFGScale} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.RefinerPositiveAestheticScore} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.RefinerNegativeAestheticScore} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.RefinerScheduler} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.RefinerDenoisingStart} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.RefinerSteps} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.CanvasLayers} />
|
||||
<CollectionMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.RefImages} />
|
||||
<CollectionMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.LoRAs} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default memo(ImageMetadataActions);
|
||||
ImageMetadataActions.displayName = 'ImageMetadataActions';
|
||||
|
||||
export const VideoMetadataActions = memo((props: Props) => {
|
||||
const { metadata } = props;
|
||||
|
||||
if (!metadata || Object.keys(metadata).length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" ps={8}>
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={VideoMetadataHandlers.GenerationMode} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={VideoMetadataHandlers.PositivePrompt} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={VideoMetadataHandlers.Seed} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={VideoMetadataHandlers.VideoAspectRatio} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={VideoMetadataHandlers.VideoDuration} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={VideoMetadataHandlers.VideoResolution} />
|
||||
<SingleMetadataDatum metadata={metadata} handler={VideoMetadataHandlers.VideoModel} />
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
VideoMetadataActions.displayName = 'VideoMetadataActions';
|
||||
|
||||
export const UnrecallableMetadataDatum = typedMemo(
|
||||
<T,>({ metadata, handler }: { metadata: unknown; handler: UnrecallableMetadataHandler<T> }) => {
|
||||
|
||||
@@ -2,14 +2,14 @@ import { ExternalLink, Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@in
|
||||
import { IAINoContentFallback, IAINoContentFallbackWithSpinner } from 'common/components/IAIImageFallback';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import ImageMetadataGraphTabContent from 'features/gallery/components/ImageMetadataViewer/ImageMetadataGraphTabContent';
|
||||
import { MetadataHandlers } from 'features/metadata/parsing';
|
||||
import { ImageMetadataHandlers } from 'features/metadata/parsing';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
import DataViewer from './DataViewer';
|
||||
import ImageMetadataActions, { UnrecallableMetadataDatum } from './ImageMetadataActions';
|
||||
import { ImageMetadataActions, UnrecallableMetadataDatum } from './ImageMetadataActions';
|
||||
import ImageMetadataWorkflowTabContent from './ImageMetadataWorkflowTabContent';
|
||||
|
||||
type ImageMetadataViewerProps = {
|
||||
@@ -39,7 +39,7 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
|
||||
overflow="hidden"
|
||||
>
|
||||
<ExternalLink href={image.image_url} label={image.image_name} />
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={MetadataHandlers.CreatedBy} />
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.CreatedBy} />
|
||||
|
||||
<Tabs variant="line" isLazy={true} display="flex" flexDir="column" w="full" h="full">
|
||||
<TabList>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { ExternalLink, Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||
import { IAINoContentFallback, IAINoContentFallbackWithSpinner } from 'common/components/IAIImageFallback';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { MetadataHandlers } from 'features/metadata/parsing';
|
||||
import { ImageMetadataHandlers } from 'features/metadata/parsing';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDebouncedVideoMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
||||
import type { VideoDTO } from 'services/api/types';
|
||||
|
||||
import DataViewer from './DataViewer';
|
||||
import ImageMetadataActions, { UnrecallableMetadataDatum } from './ImageMetadataActions';
|
||||
import { UnrecallableMetadataDatum, VideoMetadataActions } from './ImageMetadataActions';
|
||||
|
||||
type VideoMetadataViewerProps = {
|
||||
video: VideoDTO;
|
||||
@@ -32,15 +32,13 @@ const VideoMetadataViewer = ({ video }: VideoMetadataViewerProps) => {
|
||||
overflow="hidden"
|
||||
>
|
||||
<ExternalLink href={video.video_url} label={video.video_id} />
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={MetadataHandlers.CreatedBy} />
|
||||
<UnrecallableMetadataDatum metadata={metadata} handler={ImageMetadataHandlers.CreatedBy} />
|
||||
|
||||
<Tabs variant="line" isLazy={true} display="flex" flexDir="column" w="full" h="full">
|
||||
<TabList>
|
||||
<Tab>{t('metadata.recallParameters')}</Tab>
|
||||
<Tab>{t('metadata.metadata')}</Tab>
|
||||
<Tab>{t('metadata.imageDetails')}</Tab>
|
||||
<Tab>{t('metadata.workflow')}</Tab>
|
||||
<Tab>{t('nodes.graph')}</Tab>
|
||||
</TabList>
|
||||
|
||||
<TabPanels>
|
||||
@@ -48,7 +46,7 @@ const VideoMetadataViewer = ({ video }: VideoMetadataViewerProps) => {
|
||||
{isLoading && <IAINoContentFallbackWithSpinner label="Loading metadata..." />}
|
||||
{metadata && !isLoading && (
|
||||
<ScrollableContent>
|
||||
<ImageMetadataActions metadata={metadata} />
|
||||
<VideoMetadataActions metadata={metadata} />
|
||||
</ScrollableContent>
|
||||
)}
|
||||
{!metadata && !isLoading && <IAINoContentFallback label={t('metadata.noRecallParameters')} />}
|
||||
@@ -75,7 +73,7 @@ const VideoMetadataViewer = ({ video }: VideoMetadataViewerProps) => {
|
||||
<IAINoContentFallback label={t('metadata.noImageDetails')} />
|
||||
)}
|
||||
</TabPanel>
|
||||
{/* <TabPanel>
|
||||
{/* <TabPanel>
|
||||
<ImageMetadataWorkflowTabContent image={image} />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
|
||||
@@ -5,7 +5,7 @@ import SingleSelectionMenuItems from 'features/gallery/components/ContextMenu/Si
|
||||
import { useDeleteImage } from 'features/gallery/hooks/useDeleteImage';
|
||||
import { useEditImage } from 'features/gallery/hooks/useEditImage';
|
||||
import { useLoadWorkflow } from 'features/gallery/hooks/useLoadWorkflow';
|
||||
import { useRecallAll } from 'features/gallery/hooks/useRecallAll';
|
||||
import { useRecallAll } from 'features/gallery/hooks/useRecallAllImageMetadata';
|
||||
import { useRecallDimensions } from 'features/gallery/hooks/useRecallDimensions';
|
||||
import { useRecallPrompts } from 'features/gallery/hooks/useRecallPrompts';
|
||||
import { useRecallRemix } from 'features/gallery/hooks/useRecallRemix';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useAppStore } from 'app/store/storeHooks';
|
||||
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { ImageMetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { $stylePresetModalState } from 'features/stylePresets/store/stylePresetModal';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
||||
@@ -13,7 +13,7 @@ export const useCreateStylePresetFromMetadata = (imageDTO?: ImageDTO | null) =>
|
||||
|
||||
useEffect(() => {
|
||||
MetadataUtils.hasMetadataByHandlers({
|
||||
handlers: [MetadataHandlers.PositivePrompt, MetadataHandlers.NegativePrompt],
|
||||
handlers: [ImageMetadataHandlers.PositivePrompt, ImageMetadataHandlers.NegativePrompt],
|
||||
metadata,
|
||||
store,
|
||||
require: 'some',
|
||||
@@ -51,12 +51,12 @@ export const useCreateStylePresetFromMetadata = (imageDTO?: ImageDTO | null) =>
|
||||
let negativePrompt: string;
|
||||
|
||||
try {
|
||||
positivePrompt = await MetadataHandlers.PositivePrompt.parse(metadata, store);
|
||||
positivePrompt = await ImageMetadataHandlers.PositivePrompt.parse(metadata, store);
|
||||
} catch {
|
||||
positivePrompt = '';
|
||||
}
|
||||
try {
|
||||
negativePrompt = (await MetadataHandlers.NegativePrompt.parse(metadata, store)) ?? '';
|
||||
negativePrompt = (await ImageMetadataHandlers.NegativePrompt.parse(metadata, store)) ?? '';
|
||||
} catch {
|
||||
negativePrompt = '';
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
|
||||
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { ImageMetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
||||
@@ -34,7 +34,7 @@ export const useRecallAll = (imageDTO: ImageDTO) => {
|
||||
const handlersToSkip = useMemo(() => {
|
||||
if (tab === 'canvas' && isStaging) {
|
||||
// When we are staging and on canvas, the bbox is locked - we cannot recall width and height
|
||||
return [MetadataHandlers.Width, MetadataHandlers.Height];
|
||||
return [ImageMetadataHandlers.Width, ImageMetadataHandlers.Height];
|
||||
}
|
||||
return undefined;
|
||||
}, [isStaging, tab]);
|
||||
@@ -46,7 +46,7 @@ export const useRecallAll = (imageDTO: ImageDTO) => {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
MetadataUtils.recallAll(metadata, store, handlersToSkip);
|
||||
MetadataUtils.recallAllImageMetadata(metadata, store, handlersToSkip);
|
||||
clearStylePreset();
|
||||
}, [metadata, isEnabled, store, handlersToSkip, clearStylePreset]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
|
||||
import { selectHasModelCLIPSkip } from 'features/controlLayers/store/paramsSlice';
|
||||
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { ImageMetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
@@ -20,7 +20,7 @@ export const useRecallCLIPSkip = (imageDTO: ImageDTO) => {
|
||||
useEffect(() => {
|
||||
const parse = async () => {
|
||||
try {
|
||||
await MetadataHandlers.CLIPSkip.parse(metadata, store);
|
||||
await ImageMetadataHandlers.CLIPSkip.parse(metadata, store);
|
||||
setHasCLIPSkip(true);
|
||||
} catch {
|
||||
setHasCLIPSkip(false);
|
||||
@@ -62,7 +62,7 @@ export const useRecallCLIPSkip = (imageDTO: ImageDTO) => {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
MetadataUtils.recallByHandler({ metadata, handler: MetadataHandlers.CLIPSkip, store });
|
||||
MetadataUtils.recallByHandler({ metadata, handler: ImageMetadataHandlers.CLIPSkip, store });
|
||||
}, [metadata, isEnabled, store]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -26,7 +26,7 @@ export const useRecallDimensions = (imageDTO: ImageDTO) => {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
MetadataUtils.recallDimensions(imageDTO, store);
|
||||
MetadataUtils.recallImageDimensions(imageDTO, store);
|
||||
}, [isEnabled, imageDTO, store]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
|
||||
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { ImageMetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
@@ -22,7 +22,7 @@ export const useRecallPrompts = (imageDTO: ImageDTO) => {
|
||||
const parse = async () => {
|
||||
try {
|
||||
const result = await MetadataUtils.hasMetadataByHandlers({
|
||||
handlers: [MetadataHandlers.PositivePrompt, MetadataHandlers.NegativePrompt],
|
||||
handlers: [ImageMetadataHandlers.PositivePrompt, ImageMetadataHandlers.NegativePrompt],
|
||||
metadata,
|
||||
store,
|
||||
require: 'some',
|
||||
@@ -59,7 +59,7 @@ export const useRecallPrompts = (imageDTO: ImageDTO) => {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
MetadataUtils.recallPrompts(metadata, store);
|
||||
MetadataUtils.recallImagePrompts(metadata, store);
|
||||
clearStylePreset();
|
||||
}, [metadata, isEnabled, store, clearStylePreset]);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
|
||||
import { useCanvasIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice';
|
||||
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { ImageMetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
|
||||
@@ -34,10 +34,10 @@ export const useRecallRemix = (imageDTO: ImageDTO) => {
|
||||
|
||||
const handlersToSkip = useMemo(() => {
|
||||
// Remix always skips the seed handler
|
||||
const _handlersToSkip = [MetadataHandlers.Seed];
|
||||
const _handlersToSkip = [ImageMetadataHandlers.Seed];
|
||||
if (tab === 'canvas' && isStaging) {
|
||||
// When we are staging and on canvas, the bbox is locked - we cannot recall width and height
|
||||
_handlersToSkip.push(MetadataHandlers.Width, MetadataHandlers.Height);
|
||||
_handlersToSkip.push(ImageMetadataHandlers.Width, ImageMetadataHandlers.Height);
|
||||
}
|
||||
return _handlersToSkip;
|
||||
}, [isStaging, tab]);
|
||||
@@ -49,7 +49,7 @@ export const useRecallRemix = (imageDTO: ImageDTO) => {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
MetadataUtils.recallAll(metadata, store, handlersToSkip);
|
||||
MetadataUtils.recallAllImageMetadata(metadata, store, handlersToSkip);
|
||||
clearStylePreset();
|
||||
}, [metadata, isEnabled, store, handlersToSkip, clearStylePreset]);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useAppSelector, useAppStore } from 'app/store/storeHooks';
|
||||
import { MetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { ImageMetadataHandlers, MetadataUtils } from 'features/metadata/parsing';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
@@ -18,7 +18,7 @@ export const useRecallSeed = (imageDTO: ImageDTO) => {
|
||||
useEffect(() => {
|
||||
const parse = async () => {
|
||||
try {
|
||||
await MetadataHandlers.Seed.parse(metadata, store);
|
||||
await ImageMetadataHandlers.Seed.parse(metadata, store);
|
||||
setHasSeed(true);
|
||||
} catch {
|
||||
setHasSeed(false);
|
||||
@@ -55,7 +55,7 @@ export const useRecallSeed = (imageDTO: ImageDTO) => {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
MetadataUtils.recallByHandler({ metadata, handler: MetadataHandlers.Seed, store });
|
||||
MetadataUtils.recallByHandler({ metadata, handler: ImageMetadataHandlers.Seed, store });
|
||||
}, [metadata, isEnabled, store]);
|
||||
|
||||
return {
|
||||
|
||||
@@ -33,12 +33,32 @@ import {
|
||||
widthChanged,
|
||||
} from 'features/controlLayers/store/paramsSlice';
|
||||
import { refImagesRecalled } from 'features/controlLayers/store/refImagesSlice';
|
||||
import type { CanvasMetadata, LoRA, RefImageState } from 'features/controlLayers/store/types';
|
||||
import { zCanvasMetadata, zCanvasReferenceImageState_OLD, zRefImageState } from 'features/controlLayers/store/types';
|
||||
import type {
|
||||
CanvasMetadata,
|
||||
LoRA,
|
||||
RefImageState,
|
||||
VideoAspectRatio as ParameterVideoAspectRatio,
|
||||
VideoDuration as ParameterVideoDuration,
|
||||
VideoResolution as ParameterVideoResolution,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import {
|
||||
zCanvasMetadata,
|
||||
zCanvasReferenceImageState_OLD,
|
||||
zRefImageState,
|
||||
zVideoAspectRatio,
|
||||
zVideoDuration,
|
||||
zVideoResolution,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import type { ModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import { zModelIdentifier } from 'features/nodes/types/v2/common';
|
||||
import { modelSelected } from 'features/parameters/store/actions';
|
||||
import {
|
||||
videoAspectRatioChanged,
|
||||
videoDurationChanged,
|
||||
videoModelChanged,
|
||||
videoResolutionChanged,
|
||||
} from 'features/parameters/store/videoSlice';
|
||||
import type {
|
||||
ParameterCFGRescaleMultiplier,
|
||||
ParameterCFGScale,
|
||||
@@ -694,6 +714,87 @@ const VAEModel: SingleMetadataHandler<ParameterVAEModel> = {
|
||||
};
|
||||
//#endregion VAEModel
|
||||
|
||||
//#region VideoModel
|
||||
const VideoModel: SingleMetadataHandler<ModelIdentifierField> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'VideoModel',
|
||||
parse: async (metadata, store) => {
|
||||
const raw = getProperty(metadata, 'model');
|
||||
const parsed = await parseModelIdentifier(raw, store, 'video');
|
||||
assert(parsed.type === 'video');
|
||||
return Promise.resolve(parsed);
|
||||
},
|
||||
recall: (value, store) => {
|
||||
store.dispatch(videoModelChanged({ videoModel: value }));
|
||||
},
|
||||
i18nKey: 'metadata.videoModel',
|
||||
LabelComponent: MetadataLabel,
|
||||
ValueComponent: ({ value }: SingleMetadataValueProps<ModelIdentifierField>) => (
|
||||
<MetadataPrimitiveValue value={`${value.name} (${value.base.toUpperCase()})`} />
|
||||
),
|
||||
};
|
||||
//#endregion VideoModel
|
||||
|
||||
//#region VideoDuration
|
||||
const VideoDuration: SingleMetadataHandler<ParameterVideoDuration> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'VideoDuration',
|
||||
parse: (metadata) => {
|
||||
const raw = getProperty(metadata, 'duration');
|
||||
const parsed = zVideoDuration.parse(raw);
|
||||
return Promise.resolve(parsed);
|
||||
},
|
||||
recall: (value, store) => {
|
||||
store.dispatch(videoDurationChanged(value));
|
||||
},
|
||||
i18nKey: 'metadata.videoDuration',
|
||||
LabelComponent: MetadataLabel,
|
||||
ValueComponent: ({ value }: SingleMetadataValueProps<ParameterVideoDuration>) => (
|
||||
<MetadataPrimitiveValue value={value} />
|
||||
),
|
||||
};
|
||||
//#endregion VideoDuration
|
||||
|
||||
//#region VideoResolution
|
||||
const VideoResolution: SingleMetadataHandler<ParameterVideoResolution> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'VideoResolution',
|
||||
parse: (metadata) => {
|
||||
const raw = getProperty(metadata, 'resolution');
|
||||
const parsed = zVideoResolution.parse(raw);
|
||||
return Promise.resolve(parsed);
|
||||
},
|
||||
recall: (value, store) => {
|
||||
store.dispatch(videoResolutionChanged(value));
|
||||
},
|
||||
i18nKey: 'metadata.videoResolution',
|
||||
LabelComponent: MetadataLabel,
|
||||
ValueComponent: ({ value }: SingleMetadataValueProps<ParameterVideoResolution>) => (
|
||||
<MetadataPrimitiveValue value={value} />
|
||||
),
|
||||
};
|
||||
//#endregion VideoResolution
|
||||
|
||||
//#region VideoAspectRatio
|
||||
const VideoAspectRatio: SingleMetadataHandler<ParameterVideoAspectRatio> = {
|
||||
[SingleMetadataKey]: true,
|
||||
type: 'VideoAspectRatio',
|
||||
parse: (metadata) => {
|
||||
const raw = getProperty(metadata, 'aspect_ratio');
|
||||
const parsed = zVideoAspectRatio.parse(raw);
|
||||
return Promise.resolve(parsed);
|
||||
},
|
||||
recall: (value, store) => {
|
||||
store.dispatch(videoAspectRatioChanged(value));
|
||||
},
|
||||
i18nKey: 'metadata.videoAspectRatio',
|
||||
LabelComponent: MetadataLabel,
|
||||
ValueComponent: ({ value }: SingleMetadataValueProps<ParameterVideoAspectRatio>) => (
|
||||
<MetadataPrimitiveValue value={value} />
|
||||
),
|
||||
};
|
||||
//#endregion VideoAspectRatio
|
||||
|
||||
//#region LoRAs
|
||||
const LoRAs: CollectionMetadataHandler<LoRA[]> = {
|
||||
[CollectionMetadataKey]: true,
|
||||
@@ -897,7 +998,7 @@ const RefImages: CollectionMetadataHandler<RefImageState[]> = {
|
||||
};
|
||||
//#endregion RefImages
|
||||
|
||||
export const MetadataHandlers = {
|
||||
export const ImageMetadataHandlers = {
|
||||
CreatedBy,
|
||||
GenerationMode,
|
||||
PositivePrompt,
|
||||
@@ -938,6 +1039,17 @@ export const MetadataHandlers = {
|
||||
// ipAdapterToIPAdapterLayer: parseIPAdapterToIPAdapterLayer,
|
||||
} as const;
|
||||
|
||||
export const VideoMetadataHandlers = {
|
||||
CreatedBy,
|
||||
GenerationMode,
|
||||
PositivePrompt,
|
||||
VideoModel,
|
||||
Seed,
|
||||
VideoAspectRatio,
|
||||
VideoDuration,
|
||||
VideoResolution,
|
||||
};
|
||||
|
||||
const successToast = (parameter: string) => {
|
||||
toast({
|
||||
id: 'PARAMETER_SET',
|
||||
@@ -1007,9 +1119,9 @@ const recallByHandlers = async (arg: {
|
||||
// model is recalled first, so it doesn't accidentally override the width and height. This is the only known case
|
||||
// where the order of recall matters.
|
||||
const sortedHandlers = filteredHandlers.sort((a, b) => {
|
||||
if (a === MetadataHandlers.MainModel) {
|
||||
if (a === ImageMetadataHandlers.MainModel) {
|
||||
return -1; // MainModel should be recalled first
|
||||
} else if (b === MetadataHandlers.MainModel) {
|
||||
} else if (b === ImageMetadataHandlers.MainModel) {
|
||||
return 1; // MainModel should be recalled first
|
||||
} else {
|
||||
return 0; // Keep the original order for other handlers
|
||||
@@ -1045,10 +1157,10 @@ const recallByHandlers = async (arg: {
|
||||
return recalled;
|
||||
};
|
||||
|
||||
const recallPrompts = async (metadata: unknown, store: AppStore) => {
|
||||
const recallImagePrompts = async (metadata: unknown, store: AppStore) => {
|
||||
const recalled = await recallByHandlers({
|
||||
metadata,
|
||||
handlers: [MetadataHandlers.PositivePrompt, MetadataHandlers.NegativePrompt],
|
||||
handlers: [ImageMetadataHandlers.PositivePrompt, ImageMetadataHandlers.NegativePrompt],
|
||||
store,
|
||||
silent: true,
|
||||
});
|
||||
@@ -1079,10 +1191,10 @@ const hasMetadataByHandlers = async (arg: {
|
||||
return true;
|
||||
};
|
||||
|
||||
const recallDimensions = async (metadata: unknown, store: AppStore) => {
|
||||
const recallImageDimensions = async (metadata: unknown, store: AppStore) => {
|
||||
const recalled = await recallByHandlers({
|
||||
metadata,
|
||||
handlers: [MetadataHandlers.Width, MetadataHandlers.Height],
|
||||
handlers: [ImageMetadataHandlers.Width, ImageMetadataHandlers.Height],
|
||||
store,
|
||||
silent: true,
|
||||
});
|
||||
@@ -1091,12 +1203,12 @@ const recallDimensions = async (metadata: unknown, store: AppStore) => {
|
||||
}
|
||||
};
|
||||
|
||||
const recallAll = async (
|
||||
const recallAllImageMetadata = async (
|
||||
metadata: unknown,
|
||||
store: AppStore,
|
||||
skip?: (SingleMetadataHandler<any> | CollectionMetadataHandler<any[]>)[]
|
||||
) => {
|
||||
const handlers = Object.values(MetadataHandlers).filter(
|
||||
const handlers = Object.values(ImageMetadataHandlers).filter(
|
||||
(handler) => isSingleMetadataHandler(handler) || isCollectionMetadataHandler(handler)
|
||||
);
|
||||
await recallByHandlers({
|
||||
@@ -1111,9 +1223,9 @@ export const MetadataUtils = {
|
||||
hasMetadataByHandlers,
|
||||
recallByHandler,
|
||||
recallByHandlers,
|
||||
recallAll,
|
||||
recallPrompts,
|
||||
recallDimensions,
|
||||
recallAllImageMetadata,
|
||||
recallImagePrompts,
|
||||
recallImageDimensions,
|
||||
} as const;
|
||||
|
||||
export function useSingleMetadataDatum<T>(metadata: unknown, handler: SingleMetadataHandler<T>) {
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
selectVideoSlice,
|
||||
} from 'features/parameters/store/videoSlice';
|
||||
import { t } from 'i18next';
|
||||
import type { VideoApiModelConfig } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
selectVideoSlice,
|
||||
} from 'features/parameters/store/videoSlice';
|
||||
import { t } from 'i18next';
|
||||
import type { VideoApiModelConfig } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
@@ -2,12 +2,7 @@ import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
||||
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||
import {
|
||||
isAspectRatioID,
|
||||
zAspectRatioID,
|
||||
zRunwayAspectRatioID,
|
||||
zVeo3AspectRatioID,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import { isVideoAspectRatio, zRunwayAspectRatioID, zVeo3AspectRatioID } from 'features/controlLayers/store/types';
|
||||
import {
|
||||
selectIsRunway,
|
||||
selectIsVeo3,
|
||||
@@ -31,12 +26,12 @@ export const VideoDimensionsAspectRatioSelect = memo(() => {
|
||||
return zRunwayAspectRatioID.options.map((o) => ({ label: o, value: o }));
|
||||
}
|
||||
// All other models
|
||||
return zAspectRatioID.options.map((o) => ({ label: o, value: o }));
|
||||
return [];
|
||||
}, [isVeo3, isRunway]);
|
||||
|
||||
const onChange = useCallback<ComboboxOnChange>(
|
||||
(v) => {
|
||||
if (!isAspectRatioID(v?.value)) {
|
||||
if (!isVideoAspectRatio(v?.value)) {
|
||||
return;
|
||||
}
|
||||
dispatch(videoAspectRatioChanged(v.value));
|
||||
|
||||
@@ -13,7 +13,7 @@ export const VideoDimensionsPreview = memo(() => {
|
||||
const currentVideoDimensions = useCurrentVideoDimensions();
|
||||
|
||||
const previewBoxSize = useMemo(() => {
|
||||
if (!dims || aspectRatio === 'Free') {
|
||||
if (!dims) {
|
||||
return { width: 0, height: 0 };
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,10 @@ import type { RootState } from 'app/store/store';
|
||||
import type { SliceConfig } from 'app/store/types';
|
||||
import { isPlainObject } from 'es-toolkit';
|
||||
import type {
|
||||
AspectRatioID,
|
||||
ImageWithDims,
|
||||
RunwayDuration,
|
||||
RunwayResolution,
|
||||
Veo3Duration,
|
||||
Veo3Resolution,
|
||||
VideoAspectRatio,
|
||||
VideoDuration,
|
||||
VideoResolution,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import {
|
||||
isRunwayAspectRatioID,
|
||||
@@ -18,17 +16,15 @@ import {
|
||||
isVeo3AspectRatioID,
|
||||
isVeo3DurationID,
|
||||
isVeo3Resolution,
|
||||
zAspectRatioID,
|
||||
zImageWithDims,
|
||||
zRunwayDurationID,
|
||||
zRunwayResolution,
|
||||
zVeo3DurationID,
|
||||
zVeo3Resolution,
|
||||
zVideoAspectRatio,
|
||||
zVideoDuration,
|
||||
zVideoResolution,
|
||||
} from 'features/controlLayers/store/types';
|
||||
import type { ModelIdentifierField, VideoField } from 'features/nodes/types/common';
|
||||
import { zModelIdentifierField, zVideoField } from 'features/nodes/types/common';
|
||||
import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models';
|
||||
import { isVideoModelConfig, type RunwayModelConfig, type Veo3ModelConfig } from 'services/api/types';
|
||||
import { isVideoModelConfig } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
import z from 'zod';
|
||||
|
||||
@@ -37,9 +33,9 @@ const zVideoState = z.object({
|
||||
startingFrameImage: zImageWithDims.nullable(),
|
||||
generatedVideo: zVideoField.nullable(),
|
||||
videoModel: zModelIdentifierField.nullable(),
|
||||
videoResolution: zVeo3Resolution.or(zRunwayResolution),
|
||||
videoDuration: zVeo3DurationID.or(zRunwayDurationID),
|
||||
videoAspectRatio: zAspectRatioID,
|
||||
videoResolution: zVideoResolution,
|
||||
videoDuration: zVideoDuration,
|
||||
videoAspectRatio: zVideoAspectRatio,
|
||||
});
|
||||
|
||||
export type VideoState = z.infer<typeof zVideoState>;
|
||||
@@ -97,15 +93,15 @@ const slice = createSlice({
|
||||
}
|
||||
},
|
||||
|
||||
videoResolutionChanged: (state, action: PayloadAction<Veo3Resolution | RunwayResolution>) => {
|
||||
videoResolutionChanged: (state, action: PayloadAction<VideoResolution>) => {
|
||||
state.videoResolution = action.payload;
|
||||
},
|
||||
|
||||
videoDurationChanged: (state, action: PayloadAction<Veo3Duration | RunwayDuration>) => {
|
||||
videoDurationChanged: (state, action: PayloadAction<VideoDuration>) => {
|
||||
state.videoDuration = action.payload;
|
||||
},
|
||||
|
||||
videoAspectRatioChanged: (state, action: PayloadAction<AspectRatioID>) => {
|
||||
videoAspectRatioChanged: (state, action: PayloadAction<VideoAspectRatio>) => {
|
||||
state.videoAspectRatio = action.payload;
|
||||
},
|
||||
},
|
||||
@@ -136,7 +132,7 @@ export const videoSliceConfig: SliceConfig<typeof slice> = {
|
||||
};
|
||||
|
||||
export const selectVideoSlice = (state: RootState) => state.video;
|
||||
const createVideoSelector = <T,>(selector: Selector<VideoState, T>) => createSelector(selectVideoSlice, selector);
|
||||
const createVideoSelector = <T>(selector: Selector<VideoState, T>) => createSelector(selectVideoSlice, selector);
|
||||
|
||||
export const selectStartingFrameImage = createVideoSelector((video) => video.startingFrameImage);
|
||||
export const selectGeneratedVideo = createVideoSelector((video) => video.generatedVideo);
|
||||
|
||||
Reference in New Issue
Block a user