export function tvstarTiming(goal: string, shots: Array<{ duration: string }>) { if (!shots.length) throw new Error("没有可生成的分镜"); const duration = /(\d+(?:\.\d+)?)\s*(秒钟|秒|s(?:ec(?:onds?)?)?|分钟|分|min(?:utes?)?)(?![a-z])/gi; const requests = goal.split(/[,,。;;\n]/).flatMap(part => { if (/(?:镜头\s*[0-9一二三四五六七八九十]+|片段\s*[0-9一二三四五六七八九十]+|(?:镜头|片段)[^,,。;;\n]*各|单段|每段|每个镜头|each\s+(?:shot|clip)|per\s+(?:shot|clip)|(?:shot|clip)\s+\d+|关键帧\s*[0-9一二三四五六七八九十]*)/i.test(part) && !/(?:总时长|整体时长|整片|全片|成片时长|片长)/.test(part)) return []; return [...part.matchAll(duration)].map(match => { const value = Number(match[1]); return /^(?:分钟|分|min)/i.test(match[2]) ? value * 60 : value; }); }); const requested = requests.at(-1) || 0; const original = shots.map(shot => { const duration = Number.parseFloat(shot.duration); return Number.isFinite(duration) && duration > 0 ? duration : 5; }); const sourceTotal = original.reduce((sum, duration) => sum + duration, 0); const targetDuration = Math.round((requested || sourceTotal) * 10) / 10; if (targetDuration > 300) throw Error("自动成片最长支持 300 秒,请缩短需求或拆分制作"); if (targetDuration < shots.length * 0.2) throw Error("目标时长不足以容纳全部镜头,请减少镜头或增加成片时长"); const scale = targetDuration / sourceTotal; const shotDurations = original.map(duration => Math.max(0.2, Math.round(duration * scale * 10) / 10)); const excess = Math.round((shotDurations.reduce((sum, duration) => sum + duration, 0) - targetDuration) * 10) / 10; shotDurations[shotDurations.length - 1] = Math.max(0.2, Math.round((shotDurations.at(-1)! - excess) * 10) / 10); return { targetDuration, shotDurations }; }