fix(canvas): 分镜视频参数 - 按模型能力约束同步音频与水印

This commit is contained in:
ddCat
2026-09-02 00:47:57 +08:00
parent 58272e683f
commit 54c29b3fb4
3 changed files with 79 additions and 3 deletions

View File

@@ -248,6 +248,8 @@ export function defaultImageParamsForModel(config: AiConfig, model: string): Pic
export type ModelGenerationDefaults = Pick<AiConfig, "size" | "quality" | "transparentBackground" | "count" | "videoSeconds" | "vquality" | "videoGenerateAudio" | "videoWatermark">;
type ModelVideoBooleanOptions = Pick<AiConfig, "videoGenerateAudio" | "videoWatermark">;
export function resolveModelGenerationDefaults(
config: AiConfig,
model: string,
@@ -294,6 +296,20 @@ export function resolveModelGenerationDefaults(
return {};
}
export function resolveModelVideoBooleanOptions(
config: AiConfig,
model: string,
explicit: Partial<ModelVideoBooleanOptions> = {},
fallback: Partial<ModelVideoBooleanOptions> = {},
): ModelVideoBooleanOptions {
const profile = modelCapabilityConfigFor(config, model).video!;
const defaults = resolveModelGenerationDefaults(config, model, "video", explicit, fallback);
return {
videoGenerateAudio: profile.generateAudio.supported ? defaults.videoGenerateAudio ?? String(profile.generateAudio.default) : "false",
videoWatermark: profile.watermark.supported ? defaults.videoWatermark ?? String(profile.watermark.default) : "false",
};
}
export function maxModelInputCapacity(config: AiConfig, capability: "image" | "video", kind: "image" | "video" | "audio") {
const options = selectableModelsByCapability(config, capability);
if (!options.length) return null;

View File

@@ -11,7 +11,7 @@ import { CreditSymbol, requestCreditCost } from "@/constant/credits";
import { modelCapabilityConfigFor, normalizeImageValue, normalizeVideoValue, videoDurationOptions } from "@/lib/model-capabilities";
import { modelQuoteRequest } from "@/lib/model-pricing";
import { customShotTitle, formatShotOrdinal, normalizeDefaultShotTitle } from "@/lib/shot-label";
import { modelCompatibilityError, resolveCompatibleModel, type ModelRequirements } from "@/lib/model-selection";
import { modelCompatibilityError, resolveCompatibleModel, resolveModelVideoBooleanOptions, type ModelRequirements } from "@/lib/model-selection";
import { formatVideoResolutionLabel } from "@/lib/video-generation-options";
import { submitBackendGenerationTask } from "@/services/api/generation-task";
import { quoteLogicalModel } from "@/services/api/logical-models";
@@ -145,6 +145,12 @@ export default function WorkflowProductionWorkbench(props: Props) {
const activeProfile = useMemo(() => modelCapabilityConfigFor(effectiveConfig, routedModel), [effectiveConfig, routedModel]);
const videoProfile = generationCapability === "video" ? activeProfile.video : undefined;
const imageProfile = generationCapability === "image" ? activeProfile.image : undefined;
const videoBooleanOptions = useMemo(() => generationCapability === "video"
? resolveModelVideoBooleanOptions(effectiveConfig, routedModel, {}, {
videoGenerateAudio: effectiveConfig.videoGenerateAudio,
videoWatermark: effectiveConfig.videoWatermark,
})
: undefined, [effectiveConfig, generationCapability, routedModel]);
const generationConfig = useMemo(() => ({
...effectiveConfig,
model: routedModel,
@@ -154,7 +160,8 @@ export default function WorkflowProductionWorkbench(props: Props) {
quality: imageQuality,
vquality: resolution,
videoSeconds: generationSeconds,
}), [aspectRatio, effectiveConfig, generationCapability, generationSeconds, imageQuality, resolution, routedModel]);
...(videoBooleanOptions || {}),
}), [aspectRatio, effectiveConfig, generationCapability, generationSeconds, imageQuality, resolution, routedModel, videoBooleanOptions]);
const priceChannel = resolveModelChannel(generationConfig, routedModel);
const configuredCredits = requestCreditCost({
channelMode: priceChannel.scope === "system" ? "remote" : "local",