128 lines
5.4 KiB
TypeScript
128 lines
5.4 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { ModelPage } from "@storyteller/ui-model-selector";
|
|
import { Model, VideoModel, resolveVideoDuration, resolveVideoGenerationCount, resolveVideoOutputFormat, videoResolutionValue } from "@storyteller/model-list";
|
|
import { GenerationProvider } from "@storyteller/api-enums";
|
|
import { usePromptVideoStore } from "@storyteller/ui-promptbox";
|
|
import {
|
|
EstimateVideoCost,
|
|
isEstimateVideoCostSuccess,
|
|
} from "@storyteller/tauri-api";
|
|
import { useCostBreakdownModalStore } from "./cost-breakdown-modal-store";
|
|
import {
|
|
videoAspectRatioToCommonAspectRatio,
|
|
videoStoreToGenerationMode,
|
|
} from "./convert/index.js";
|
|
|
|
export function useVideoCostEstimate(
|
|
activePage: ModelPage,
|
|
selectedModel: Model | null | undefined,
|
|
selectedProvider: string | null | undefined,
|
|
): { isLoading: boolean } {
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const setEstimatedCreditsForPage = useCostBreakdownModalStore(
|
|
(s) => s.setEstimatedCreditsForPage,
|
|
);
|
|
|
|
const duration = usePromptVideoStore((s) => s.duration);
|
|
const aspectRatio = usePromptVideoStore((s) => s.aspectRatio);
|
|
const resolution = usePromptVideoStore((s) => s.resolution);
|
|
const inputMode = usePromptVideoStore((s) => s.inputMode);
|
|
const referenceImages = usePromptVideoStore((s) => s.referenceImages);
|
|
const endFrameImage = usePromptVideoStore((s) => s.endFrameImage);
|
|
const referenceVideos = usePromptVideoStore((s) => s.referenceVideos);
|
|
const referenceAudios = usePromptVideoStore((s) => s.referenceAudios);
|
|
const bitrate = usePromptVideoStore((s) => s.bitrate);
|
|
const outputFormat = usePromptVideoStore((s) => s.outputFormat);
|
|
const generationCount = usePromptVideoStore((s) => s.generationCount);
|
|
const generateWithSound = usePromptVideoStore((s) => s.generateWithSound);
|
|
|
|
useEffect(() => {
|
|
if (activePage !== ModelPage.ImageToVideo || !selectedModel) {
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
const videoModel = selectedModel as VideoModel;
|
|
const commonAspectRatio = videoModel.supportsCommonAspectRatio
|
|
? videoAspectRatioToCommonAspectRatio(aspectRatio, videoModel.sizeOptions)
|
|
?? videoModel.defaultAspectRatio ?? videoModel.sizeOptions[0]?.tauriValue
|
|
: undefined;
|
|
const commonResolution = videoModel.resolutionOptions?.length ? videoResolutionValue(resolution) : undefined;
|
|
const isReferenceMode = inputMode === "reference" && !!videoModel.supportsReferenceMode;
|
|
const generationMode = videoStoreToGenerationMode(
|
|
inputMode,
|
|
referenceImages,
|
|
endFrameImage,
|
|
videoModel.supportsReferenceMode,
|
|
);
|
|
|
|
const provider =
|
|
(selectedProvider as GenerationProvider | null | undefined) ??
|
|
GenerationProvider.Artcraft;
|
|
|
|
let cancelled = false;
|
|
setIsLoading(true);
|
|
setEstimatedCreditsForPage(ModelPage.ImageToVideo, null);
|
|
|
|
EstimateVideoCost({
|
|
model: videoModel.tauriId,
|
|
provider,
|
|
generation_mode: generationMode,
|
|
aspect_ratio: commonAspectRatio ?? undefined,
|
|
resolution: commonResolution ?? undefined,
|
|
duration_seconds: resolveVideoDuration(videoModel, duration, isReferenceMode) ?? undefined,
|
|
generate_audio: videoModel.generateWithSound ? generateWithSound : undefined,
|
|
bitrate: videoModel.bitrateOptions?.length ? bitrate ?? videoModel.defaultBitrate ?? videoModel.bitrateOptions[0] : undefined,
|
|
output_format: resolveVideoOutputFormat(videoModel, outputFormat),
|
|
start_frame_image_media_token: !isReferenceMode ? referenceImages[0]?.mediaToken : undefined,
|
|
end_frame_image_media_token: !isReferenceMode ? endFrameImage?.mediaToken : undefined,
|
|
reference_image_media_tokens: isReferenceMode ? referenceImages.map((image) => image.mediaToken) : undefined,
|
|
reference_video_media_tokens: isReferenceMode ? referenceVideos.map((video) => video.mediaToken) : undefined,
|
|
reference_audio_media_tokens: isReferenceMode ? referenceAudios.map((audio) => audio.mediaToken) : undefined,
|
|
estimate_only: isReferenceMode ? {
|
|
total_input_video_duration_millis: referenceVideos.length
|
|
? Math.round(referenceVideos.reduce((sum, video) => sum + video.duration, 0) * 1000) : undefined,
|
|
total_input_audio_duration_millis: referenceAudios.length
|
|
? Math.round(referenceAudios.reduce((sum, audio) => sum + audio.duration, 0) * 1000) : undefined,
|
|
} : undefined,
|
|
})
|
|
.then((result) => {
|
|
if (cancelled) return;
|
|
if (isEstimateVideoCostSuccess(result)) {
|
|
const unitCredits = result.payload.cost_in_credits;
|
|
const count = resolveVideoGenerationCount(videoModel, generationCount);
|
|
const credits = unitCredits == null ? null : unitCredits * count;
|
|
setEstimatedCreditsForPage(ModelPage.ImageToVideo, credits);
|
|
} else {
|
|
setEstimatedCreditsForPage(ModelPage.ImageToVideo, null);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
if (cancelled) return;
|
|
setEstimatedCreditsForPage(ModelPage.ImageToVideo, null);
|
|
})
|
|
.finally(() => {
|
|
if (!cancelled) setIsLoading(false);
|
|
});
|
|
return () => { cancelled = true; };
|
|
}, [
|
|
activePage,
|
|
selectedModel,
|
|
selectedProvider,
|
|
duration,
|
|
aspectRatio,
|
|
resolution,
|
|
inputMode,
|
|
referenceImages,
|
|
referenceVideos,
|
|
referenceAudios,
|
|
bitrate,
|
|
outputFormat,
|
|
generationCount,
|
|
endFrameImage,
|
|
generateWithSound,
|
|
]);
|
|
|
|
return { isLoading };
|
|
}
|