添加seedance2.5

This commit is contained in:
Azmat@qq.com
2026-08-28 16:03:23 +08:00
parent c0a0b9603b
commit a618f02315
16 changed files with 416 additions and 80 deletions
+12 -10
View File
@@ -38,13 +38,12 @@ const JOB_KEY = "airshelf:video-replace-job";
const MODE_KEY = "airshelf:video-replace-mode";
const CHARACTER_MARK = "[视频复刻·角色]";
const VIDEO_ACCEPT = "video/mp4,video/quicktime";
// 商品复刻的参考视频只用来提炼分镜稿,不发火山,所以能到 60 秒;
// 角色复刻仍把参考视频直传火山,卡死在火山的 15 秒。
// 复刻(商品 / 角色)统一走 Seedance 2.5,单次能出 30 秒,参考视频也按 30 秒收口。
const PRODUCT_SOURCE_PURPOSE = "video_replace_product";
const REF_SECONDS_MAX = { product: 60, character: 15 } as const;
const REF_BYTES_MAX = { product: 200 * 1024 * 1024, character: 50 * 1024 * 1024 } as const;
// 火山单次出片上限。参考视频更长时不报错,成片按这个截断,提示词里会要求模型压缩改编。
const SEEDANCE_MAX_OUTPUT_SECONDS = 15;
const REF_SECONDS_MAX = { product: 30, character: 30 } as const;
const REF_BYTES_MAX = { product: 200 * 1024 * 1024, character: 200 * 1024 * 1024 } as const;
// Seedance 2.5 单次出片上限。参考视频更长时不报错,成片按这个截断。
const SEEDANCE_MAX_OUTPUT_SECONDS = 30;
type ProductSource = "library" | "temporary" | "";
type ReplaceMode = "product" | "character";
@@ -471,9 +470,11 @@ export function VideoReplacePage({
const data = await api.pollVideoReplace(jobId);
if (cancelled) return;
setJob(data.task);
const jobMode = modeFromTask(data.task);
setReplaceMode(jobMode);
rememberReplaceMode(jobMode);
if (isInFlight(data.task.status)) {
const jobMode = modeFromTask(data.task);
setReplaceMode(jobMode);
rememberReplaceMode(jobMode);
}
const stillDigesting = isDigesting(data.task);
const stillReviewing = data.task.review_stage === "reviewing" || data.task.status === "created";
if (stillReviewing) wasDigestingRef.current = stillDigesting || wasDigestingRef.current;
@@ -498,6 +499,7 @@ export function VideoReplacePage({
onNotify("error", data.task.error_message || "视频复刻未完成,请重试");
onTaskSettled?.();
}
setJobId("");
forgetJob();
} catch (error) {
if (cancelled) return;
@@ -538,7 +540,7 @@ export function VideoReplacePage({
if (job && !isInFlight(job.status)) setJob(null);
const form = new FormData();
form.append("file", file);
if (replaceMode === "product") form.append("purpose", PRODUCT_SOURCE_PURPOSE);
form.append("purpose", PRODUCT_SOURCE_PURPOSE);
try {
const uploaded = await api.uploadFreeVideoRef(form);
setVideoRef({