75 lines
2.7 KiB
TypeScript
75 lines
2.7 KiB
TypeScript
"use client"
|
|
import { useEffect, useRef, useState } from "react"
|
|
import { X, Plus, Loader2 } from "lucide-react"
|
|
import { videoUrl } from "@/lib/api"
|
|
|
|
interface Props {
|
|
jobId: string | null
|
|
open: boolean
|
|
onClose: () => void
|
|
onAddFrame: (t: number) => Promise<void>
|
|
}
|
|
|
|
export function VideoLightbox({ jobId, open, onClose, onAddFrame }: Props) {
|
|
const videoRef = useRef<HTMLVideoElement>(null)
|
|
const [currentT, setCurrentT] = useState(0)
|
|
const [adding, setAdding] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (!open) return
|
|
const onKey = (e: KeyboardEvent) => {
|
|
if (e.key === "Escape") onClose()
|
|
}
|
|
window.addEventListener("keydown", onKey)
|
|
return () => window.removeEventListener("keydown", onKey)
|
|
}, [open, onClose])
|
|
|
|
if (!open || !jobId) return null
|
|
|
|
return (
|
|
<div
|
|
className="fixed inset-0 z-[100] bg-black/85 backdrop-blur-sm flex items-center justify-center"
|
|
onClick={onClose}
|
|
>
|
|
<button
|
|
onClick={(e) => { e.stopPropagation(); onClose() }}
|
|
className="absolute top-5 right-5 h-10 w-10 rounded-full bg-white/10 hover:bg-white/20 text-white flex items-center justify-center"
|
|
aria-label="关闭"
|
|
>
|
|
<X className="h-5 w-5" />
|
|
</button>
|
|
|
|
<div onClick={(e) => e.stopPropagation()} className="flex flex-col items-center gap-4 max-w-[92vw] max-h-[92vh]">
|
|
<video
|
|
ref={videoRef}
|
|
src={videoUrl(jobId)}
|
|
controls
|
|
autoPlay
|
|
playsInline
|
|
preload="auto"
|
|
onTimeUpdate={(e) => setCurrentT((e.target as HTMLVideoElement).currentTime)}
|
|
className="max-w-[88vw] max-h-[72vh] rounded-xl shadow-2xl bg-black"
|
|
/>
|
|
<div className="flex items-center gap-4 text-white">
|
|
<div className="font-mono text-sm tabular-nums text-white/80">
|
|
当前 {currentT.toFixed(2)}s
|
|
</div>
|
|
<button
|
|
disabled={adding}
|
|
onClick={async () => {
|
|
const t = videoRef.current?.currentTime ?? currentT
|
|
setAdding(true)
|
|
try { await onAddFrame(t) } finally { setAdding(false) }
|
|
}}
|
|
className="px-5 py-2 rounded-lg text-sm font-medium inline-flex items-center gap-2 bg-emerald-500 hover:bg-emerald-400 text-white disabled:opacity-50 transition"
|
|
>
|
|
{adding ? <Loader2 className="h-4 w-4 animate-spin" /> : <Plus className="h-4 w-4" />}
|
|
{adding ? "抽帧中…" : `+ 把 ${currentT.toFixed(1)}s 加为关键帧`}
|
|
</button>
|
|
</div>
|
|
<div className="text-[11px] text-white/40 font-mono">拖动时间轴选帧 · 点 + 加为关键帧 · ESC 关闭</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|