import type { AiConfig } from "@/stores/use-config-store"; export const SEEDANCE_REFERENCE_LIMITS = { images: 9, videos: 3, audios: 3, imageMaxBytes: 30 * 1024 * 1024, videoMaxBytes: 50 * 1024 * 1024, audioMaxBytes: 15 * 1024 * 1024, }; export const seedanceResolutionOptions = [ { value: "480p", label: "480p" }, { value: "720p", label: "720p" }, { value: "1080p", label: "1080p" }, ] as const; export const seedanceRatioOptions = [ { value: "16:9", label: "横屏" }, { value: "9:16", label: "竖屏" }, { value: "1:1", label: "方形" }, { value: "4:3", label: "标准横屏" }, { value: "3:4", label: "标准竖屏" }, { value: "21:9", label: "宽银幕" }, { value: "adaptive", label: "自适应" }, ] as const; export const seedanceDurationOptions = [-1, 4, 5, 6, 8, 10, 12, 15] as const; const seedancePixels = { "480p": { "16:9": "864x496", "4:3": "752x560", "1:1": "640x640", "3:4": "560x752", "9:16": "496x864", "21:9": "992x432", }, "720p": { "16:9": "1280x720", "4:3": "1112x834", "1:1": "960x960", "3:4": "834x1112", "9:16": "720x1280", "21:9": "1470x630", }, "1080p": { "16:9": "1920x1080", "4:3": "1664x1248", "1:1": "1440x1440", "3:4": "1248x1664", "9:16": "1080x1920", "21:9": "2206x946", }, } as const; export function isSeedanceVideoConfig(config: Pick) { return isSeedanceVideoModel(config.model || config.videoModel) || isArkPlanBaseUrl(config.baseUrl); } export function isSeedanceVideoModel(model: string) { const value = model.toLowerCase(); return value.includes("seedance") || value.includes("doubao-seedance"); } export function isSeedanceFastModel(model: string) { const value = model.toLowerCase(); return isSeedanceVideoModel(value) && value.includes("fast"); } export function isArkPlanBaseUrl(baseUrl: string) { return baseUrl.toLowerCase().includes("ark.cn-beijing.volces.com/api/plan/v3") || baseUrl.toLowerCase().includes("/api/plan/v3"); } export function normalizeSeedanceResolution(value: string, model = "") { const normalized = normalizeResolutionToken(value); if (isSeedanceFastModel(model) && normalized === "1080p") return "720p"; return seedanceResolutionOptions.some((item) => item.value === normalized) ? normalized : "720p"; } export function normalizeResolutionToken(value: string) { if (value === "low") return "480p"; if (value === "auto" || value === "high" || value === "medium") return "720p"; const resolution = String(value || "").replace(/p$/i, "") || "720"; return `${resolution}p`; } export function normalizeSeedanceDuration(value: string) { if (String(value).trim() === "-1") return -1; const seconds = Math.floor(Number(value) || 5); return Math.max(4, Math.min(15, seconds)); } export function normalizeSeedanceRatio(value: string) { if (!value || value === "auto" || value === "adaptive") return "adaptive"; if (seedanceRatioOptions.some((item) => item.value === value)) return value; const match = value.match(/^(\d+)x(\d+)$/); if (!match) return "adaptive"; const width = Number(match[1]); const height = Number(match[2]); if (!width || !height) return "adaptive"; const ratio = width / height; const options = [ ["16:9", 16 / 9], ["4:3", 4 / 3], ["1:1", 1], ["3:4", 3 / 4], ["9:16", 9 / 16], ["21:9", 21 / 9], ] as const; return options.reduce((best, item) => (Math.abs(item[1] - ratio) < Math.abs(best[1] - ratio) ? item : best), options[0])[0]; } export function seedancePixelLabel(resolution: string, ratio: string) { const normalizedResolution = normalizeSeedanceResolution(resolution) as keyof typeof seedancePixels; const normalizedRatio = normalizeSeedanceRatio(ratio) as keyof (typeof seedancePixels)[typeof normalizedResolution] | "adaptive"; if (normalizedRatio === "adaptive") return "自动匹配"; return seedancePixels[normalizedResolution][normalizedRatio] || ""; } export function boolConfig(value: string | undefined, fallback: boolean) { if (value === "true") return true; if (value === "false") return false; return fallback; }