完成视频复刻和优化
This commit is contained in:
@@ -16,7 +16,7 @@ const SHELL_COMMANDS: Command[] = [
|
||||
{ id: "projects", group: "导航", label: "视频创作", sub: "从商品或参考视频出发,选择生产方式", page: "projects", icon: "clapperboard", key: "V" },
|
||||
{ id: "asset-factory", group: "导航", label: "图片生成", sub: "模特上身图、平台套图、图片创作", page: "assetFactory", icon: "sparkles", key: "I" },
|
||||
{ id: "free-create", group: "导航", label: "自由创作", sub: "AI 视频生成 · 全能参考 / 首尾帧", page: "freeCreate", icon: "film", key: "F" },
|
||||
{ id: "quick-create", group: "导航", label: "极速成片", sub: "上传商品图片,自动完成整条视频", page: "quickCreate", icon: "wand", key: "Q" },
|
||||
{ id: "quick-create", group: "导航", label: "一键成片", sub: "上传商品图片,自动完成整条视频", page: "quickCreate", icon: "wand", key: "Q" },
|
||||
{ id: "video-remix", group: "导航", label: "提炼提示词", sub: "上传参考视频,提炼可编辑提示词", page: "videoRemix", icon: "scan", key: "R" },
|
||||
{ id: "video-replace", group: "导航", label: "视频复刻", sub: "保留参考视频的人物、场景与节奏,复刻出自己的带货内容", page: "videoReplace", icon: "replace", key: "E" },
|
||||
{ id: "library", group: "导航", label: "成品库", sub: "素材、人物、场景、成片统一管理", page: "library", icon: "folder", key: "A" },
|
||||
@@ -26,7 +26,7 @@ const SHELL_COMMANDS: Command[] = [
|
||||
{ id: "messages", group: "常用动作", label: "消息中心", sub: "任务提醒、协作评论、系统通知", page: "messages", icon: "bell", key: "M" },
|
||||
{ id: "new-product", group: "常用动作", label: "新建商品", sub: "从商品信息开始生成素材与视频", page: "productCreateUpload", icon: "productPlus" },
|
||||
{ id: "new-project", group: "常用动作", label: "新建视频项目", sub: "选择商品并进入脚本配置", page: "projectWizard", icon: "clapperboard" },
|
||||
{ id: "quick-create-action", group: "常用动作", label: "极速成片", sub: "输入商品名称并上传图片,自动生成视频", page: "quickCreate", icon: "wand" },
|
||||
{ id: "quick-create-action", group: "常用动作", label: "一键成片", sub: "输入商品名称并上传图片,自动生成视频", page: "quickCreate", icon: "wand" },
|
||||
{ id: "model-photo", group: "常用动作", label: "生成模特上身图", sub: "快速生成 3:4 商品展示素材", page: "modelPhoto", icon: "users" },
|
||||
{ id: "platform-cover", group: "常用动作", label: "生成平台套图", sub: "适配电商平台封面与详情图", page: "platformCover", icon: "images" },
|
||||
{ id: "image-optimize", group: "常用动作", label: "图片创作", sub: "对话式生成、编辑", page: "imageOptimize", icon: "images" }
|
||||
|
||||
@@ -33,6 +33,7 @@ export const MAX_IMAGES = 9;
|
||||
export const MAX_VIDEOS = 3;
|
||||
export const MAX_AUDIOS = 3;
|
||||
export const MAX_VIDEO_TOTAL_SECONDS = 15;
|
||||
export const VIDEO_DURATION_SLACK = 0.5;
|
||||
|
||||
// 任务在途状态(继续轮询);终态 = succeeded / failed / cancelled / compensating
|
||||
export const IN_FLIGHT_STATUSES = ["created", "reserved", "submitted", "polling", "postprocessing"];
|
||||
@@ -145,8 +146,9 @@ function probeMedia(file: File, kind: "video" | "audio"): Promise<FileCheck> {
|
||||
URL.revokeObjectURL(url);
|
||||
const duration = el.duration;
|
||||
if (!isFinite(duration)) resolve({ ok: true, type: kind });
|
||||
else if (duration < 2 || duration > 15) resolve({ ok: false, error: `${kind === "video" ? "视频" : "音频"}时长需在 2-15 秒之间` });
|
||||
else resolve({ ok: true, type: kind, duration: Math.round(duration * 10) / 10 });
|
||||
else if (duration < 2 - 0.05 || duration > MAX_VIDEO_TOTAL_SECONDS + VIDEO_DURATION_SLACK) {
|
||||
resolve({ ok: false, error: `${kind === "video" ? "视频" : "音频"}时长需在 2-15 秒之间` });
|
||||
} else resolve({ ok: true, type: kind, duration: Math.min(MAX_VIDEO_TOTAL_SECONDS, Math.round(duration * 10) / 10) });
|
||||
};
|
||||
el.onerror = () => { URL.revokeObjectURL(url); resolve({ ok: false, error: "媒体文件解析失败,请更换文件" }); };
|
||||
el.src = url;
|
||||
|
||||
@@ -2,6 +2,12 @@ import { useEffect, useState, type ReactNode } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { CheckCircle2, Inbox, Music, Shield, Trash2, X } from "lucide-react";
|
||||
|
||||
// 抽屉 / 弹窗 / 全屏播放器必须挂到 document.body。
|
||||
// 写在页面树里会被顶栏(sticky + z-index)盖住:搜索、余额、铃铛会浮在抽屉上。
|
||||
export function OverlayPortal({ children }: { children: ReactNode }) {
|
||||
return createPortal(children, document.body);
|
||||
}
|
||||
|
||||
// 浮层打开时锁住 body 滚动(否则滚轮会滚动遮罩后面的页面,体感"遮罩没盖住")。
|
||||
// 多浮层叠开用计数,最后一个关闭才解锁。
|
||||
let scrollLockCount = 0;
|
||||
@@ -64,7 +70,7 @@ export function MediaLightbox({ open, src, kind, name, close }: {
|
||||
return () => document.removeEventListener("keydown", onKey);
|
||||
}, [open, close]);
|
||||
if (!open || !src) return null;
|
||||
// 挂到 body:脱离 .content(z-index:1)层叠上下文,遮罩才能盖住头部/侧栏
|
||||
// 挂到 body:浮层才能盖住顶栏(搜索 / 余额 / 铃铛)和侧栏
|
||||
return createPortal(
|
||||
<div className="np-lightbox show" onClick={close}>
|
||||
<button className="lb-x" type="button" aria-label="关闭" onClick={close}><X /></button>
|
||||
@@ -114,7 +120,7 @@ export function TeamModal({ open, title, subtitle = "", icon, close, children, f
|
||||
useBodyScrollLock(open);
|
||||
const { mounted, show } = useOverlayTransition(open, close);
|
||||
if (!mounted) return null;
|
||||
// 挂到 body:脱离 .content(z-index:1)层叠上下文,遮罩才能盖住头部/侧栏
|
||||
// 挂到 body:浮层才能盖住顶栏(搜索 / 余额 / 铃铛)和侧栏
|
||||
return createPortal(
|
||||
<div className={`modal-bg${show ? " show" : ""}`} onClick={dismissable ? close : undefined}>
|
||||
<div className="modal invite-modal" onClick={(event) => event.stopPropagation()}>
|
||||
@@ -143,7 +149,7 @@ export function ConfirmModal({ open, title, detail, confirmText, subtitle = "",
|
||||
useBodyScrollLock(open);
|
||||
const { mounted, show } = useOverlayTransition(open, onCancel);
|
||||
if (!mounted) return null;
|
||||
// 挂到 body:脱离 .content(z-index:1)层叠上下文,遮罩才能盖住头部/侧栏
|
||||
// 挂到 body:浮层才能盖住顶栏(搜索 / 余额 / 铃铛)和侧栏
|
||||
return createPortal(
|
||||
<div className={`modal-bg${show ? " show" : ""}`} onClick={dismissable ? onCancel : undefined}>
|
||||
<div className="modal" onClick={(event) => event.stopPropagation()}>
|
||||
@@ -185,7 +191,6 @@ export function Drawer({ title, open, close, children, className }: { title: str
|
||||
useBodyScrollLock(open);
|
||||
const { mounted, show } = useOverlayTransition(open, close);
|
||||
if (!mounted) return null;
|
||||
// 挂到 body:脱离 .content(z-index:1)层叠上下文,遮罩才能盖住头部/侧栏
|
||||
return createPortal(
|
||||
<>
|
||||
<div className={`drawer-bg${show ? " show" : ""}`} onClick={close} />
|
||||
|
||||
Reference in New Issue
Block a user