export type VideoDurationCostMap = Record; export type VideoResolutionCostMap = Record; export type VideoImageGenConfigLike = { creditCostPerVideo?: number | null; videoDurationOptions?: number[] | null; creditCostPerVideoByDuration?: Record | null; creditCostPerVideoByResolution?: Record | null; }; function normalizePositiveInteger(value: unknown) { const normalized = Number(value); if (!Number.isFinite(normalized)) return null; const rounded = Math.round(normalized); if (rounded < 1) return null; return rounded; } export function normalizeVideoDurationOptions(value: unknown): number[] { if (!Array.isArray(value)) return []; return Array.from( new Set( value .map((item) => normalizePositiveInteger(item)) .filter((item): item is number => item !== null) ) ).sort((left, right) => left - right); } export function normalizeVideoDurationCostMap( value: unknown, durationOptions?: readonly number[] | null ): VideoDurationCostMap | undefined { if (!value || typeof value !== "object") return undefined; const allowedDurations = Array.isArray(durationOptions) ? new Set(durationOptions.map((item) => String(item))) : null; const entries = Object.entries(value as Record) .map(([durationKey, costValue]) => { const normalizedDuration = normalizePositiveInteger(durationKey); const normalizedCost = Number(costValue); if (normalizedDuration === null || !Number.isFinite(normalizedCost)) { return null; } const durationText = String(normalizedDuration); if (allowedDurations && !allowedDurations.has(durationText)) { return null; } return [durationText, Math.max(0, Math.round(normalizedCost))] as const; }) .filter((entry): entry is readonly [string, number] => Boolean(entry)); if (entries.length === 0) return undefined; return Object.fromEntries(entries); } export function normalizeVideoResolutionKey(value: unknown) { const normalized = String(value || "").trim().toLowerCase(); if (!normalized) return null; if (normalized === "480p" || normalized === "480") return "480p"; if (normalized === "720p" || normalized === "720") return "720p"; if (normalized === "1080p" || normalized === "1080") return "1080p"; if (normalized === "1k") return "1K"; if (normalized === "2k") return "2K"; if (normalized === "4k") return "4K"; return null; } export function normalizeVideoResolutionCostMap( value: unknown ): VideoResolutionCostMap | undefined { if (!value || typeof value !== "object") return undefined; const entries: Array = []; for (const [resolutionKey, costValue] of Object.entries( value as Record )) { const normalizedResolution = normalizeVideoResolutionKey(resolutionKey); const normalizedCost = Number(costValue); if (!normalizedResolution || !Number.isFinite(normalizedCost)) { continue; } entries.push([ normalizedResolution, Math.max(0, Math.round(normalizedCost)), ] as const); } if (entries.length === 0) return undefined; return Object.fromEntries(entries); } export function getConfiguredVideoDurationOptions( config: VideoImageGenConfigLike | null | undefined ): number[] { return normalizeVideoDurationOptions(config?.videoDurationOptions); } export function buildVideoDurationCostMap(params: { durationOptions: readonly number[]; costMap?: unknown; fallbackCost?: number | null | undefined; }): VideoDurationCostMap | undefined { const fallbackCost = Number.isFinite(Number(params.fallbackCost)) ? Math.max(0, Math.round(Number(params.fallbackCost))) : 0; const normalizedCostMap = normalizeVideoDurationCostMap(params.costMap, params.durationOptions) || {}; if (params.durationOptions.length === 0 && Object.keys(normalizedCostMap).length === 0) { return undefined; } return Object.fromEntries( params.durationOptions.map((duration) => { const key = String(duration); return [key, normalizedCostMap[key] ?? fallbackCost]; }) ); } export function getVideoCreditCostFromConfig( config: VideoImageGenConfigLike | null | undefined, duration?: number | null, resolution?: string | null ): number { const normalizedDuration = normalizePositiveInteger(duration); const normalizedResolution = normalizeVideoResolutionKey(resolution); const resolutionCostMap = normalizeVideoResolutionCostMap( config?.creditCostPerVideoByResolution ); if (normalizedResolution && resolutionCostMap) { if (Object.prototype.hasOwnProperty.call(resolutionCostMap, normalizedResolution)) { return resolutionCostMap[normalizedResolution] || 0; } } const durationOptions = getConfiguredVideoDurationOptions(config); const durationCostMap = normalizeVideoDurationCostMap( config?.creditCostPerVideoByDuration, durationOptions ); if (normalizedDuration !== null && durationCostMap) { const durationKey = String(normalizedDuration); if (Object.prototype.hasOwnProperty.call(durationCostMap, durationKey)) { return durationCostMap[durationKey] || 0; } } const fallbackCost = Number(config?.creditCostPerVideo ?? 0); return Number.isFinite(fallbackCost) ? Math.max(0, Math.round(fallbackCost)) : 0; }