'use client'; import React, { useEffect, useRef, useState } from 'react'; import { Checkbox } from 'antd'; import { createRoot, Root } from 'react-dom/client'; import { X, Download, ArrowDownWideNarrow } from 'lucide-react'; import { baseUrl } from '@/lib/env'; interface DownloadOptionsModalProps { onDownloadCurrent: (withWatermark: boolean) => void; onDownloadAll: (withWatermark: boolean) => void; onClose: () => void; currentVideoIndex: number; totalVideos: number; /** 当前视频是否生成失败 */ isCurrentVideoFailed: boolean; /** 是否为最终视频阶段 */ isFinalStage?: boolean; /** 项目ID */ projectId?: string; /** 视频ID(分镜视频可用) */ videoId?: string; } /** * Download options modal component with glass morphism style. * @param {DownloadOptionsModalProps} props - modal properties. */ function DownloadOptionsModal(props: DownloadOptionsModalProps) { const { onDownloadCurrent, onDownloadAll, onClose, currentVideoIndex, totalVideos, isCurrentVideoFailed, isFinalStage = false, projectId, videoId } = props; const containerRef = useRef(null); const [withWatermark, setWithWatermark] = useState(true); const [baseAmount, setBaseAmount] = useState(0); useEffect(() => { const originalOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = originalOverflow; }; }, []); // 监听水印选择变化,请求价格信息 useEffect(() => { let aborted = false; const checkBalance = async () => { try { if (!projectId) { setBaseAmount(0); return; } const token = typeof window !== 'undefined' ? (localStorage?.getItem('token') || '') : ''; const res = await fetch(`${baseUrl}/movie/download_video`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { 'Authorization': `Bearer ${token}` } : {}) }, body: JSON.stringify({ project_id: projectId, video_id: videoId, watermark: !withWatermark, check_balance: true }) }); if (!res.ok) { if (!aborted) setBaseAmount(0); return; } const json = await res.json().catch(() => null); const amount = json?.data?.base_amount; if (!aborted) setBaseAmount(Number.isFinite(amount) ? Number(amount) : 0); } catch { if (!aborted) setBaseAmount(0); } }; void checkBalance(); return () => { aborted = true; }; }, [withWatermark]); return (
e.stopPropagation()} >

Download Options

Choose your download preference

{baseAmount && baseAmount !== 0 ? ( -{baseAmount} credits ) : ( free )}
setWithWatermark(!e.target.checked)} /> without watermark
{/* stats-info hidden temporarily due to no batch billing support */}
); } /** * Opens a download options modal with glass morphism style. * @param {DownloadOptionsModalProps} options - download options and callbacks. */ export function showDownloadOptionsModal(options: Omit): void { if (typeof window === 'undefined' || typeof document === 'undefined') { return; } const mount = document.createElement('div'); mount.setAttribute('data-alt', 'download-options-modal-root'); document.body.appendChild(mount); let root: Root | null = null; try { root = createRoot(mount); } catch { if (mount.parentNode) { mount.parentNode.removeChild(mount); } return; } const close = () => { try { root?.unmount(); } finally { if (mount.parentNode) { mount.parentNode.removeChild(mount); } } }; root.render( ); }