document.addEventListener('DOMContentLoaded', () => { lucide.createIcons(); const submitBtn = document.getElementById('submitBtn'); const pointsDisplay = document.getElementById('pointsDisplay'); const headerPoints = document.getElementById('headerPoints'); const resultVideo = document.getElementById('resultVideo'); const finalWrapper = document.getElementById('finalWrapper'); const placeholder = document.getElementById('placeholder'); const statusInfo = document.getElementById('statusInfo'); const promptInput = document.getElementById('promptInput'); const fileInput = document.getElementById('fileInput'); const imagePreview = document.getElementById('imagePreview'); const modelSelect = document.getElementById('modelSelect'); const ratioSelect = document.getElementById('ratioSelect'); const promptTemplates = document.getElementById('promptTemplates'); const enhancePrompt = document.getElementById('enhancePrompt'); // 历史记录相关 const historyList = document.getElementById('historyList'); const historyCount = document.getElementById('historyCount'); const historyEmpty = document.getElementById('historyEmpty'); const loadMoreBtn = document.getElementById('loadMoreBtn'); let uploadedImageUrl = null; let historyPage = 1; let isLoadingHistory = false; // 初始化配置 async function initConfig() { try { const r = await fetch('/api/config'); if (!r.ok) throw new Error('API 响应失败'); const d = await r.json(); if (d.video_models && d.video_models.length > 0) { modelSelect.innerHTML = d.video_models.map(m => `` ).join(''); } if (d.video_prompts && d.video_prompts.length > 0) { promptTemplates.innerHTML = d.video_prompts.map(p => `` ).join(''); } } catch (e) { console.error('加载系统配置失败:', e); } } // 载入历史记录 async function loadHistory(page = 1, append = false) { if (isLoadingHistory) return; isLoadingHistory = true; try { const r = await fetch(`/api/history?page=${page}&per_page=10&filter_type=video`); const d = await r.json(); // 服务端已完成过滤 const videoRecords = d.history; if (videoRecords.length > 0) { const html = videoRecords.map(item => { const videoObj = item.urls.find(u => u.type === 'video') || { url: item.urls[0] }; const videoUrl = typeof videoObj === 'string' ? videoObj : videoObj.url; return `

${item.prompt || '无描述'}

${item.created_at}
`; }).join(''); if (append) { historyList.insertAdjacentHTML('beforeend', html); } else { historyList.innerHTML = html; } historyEmpty.classList.add('hidden'); historyCount.innerText = videoRecords.length + (append ? parseInt(historyCount.innerText) : 0); if (d.history.length >= 10) { loadMoreBtn.classList.remove('hidden'); } else { loadMoreBtn.classList.add('hidden'); } } else if (!append) { historyEmpty.classList.remove('hidden'); historyList.innerHTML = ''; } lucide.createIcons(); } catch (e) { console.error('加载历史失败:', e); } finally { isLoadingHistory = false; } } window.playHistoryVideo = (url) => { showVideo(url); window.scrollTo({ top: 0, behavior: 'smooth' }); }; window.downloadUrl = (url) => { // 使用后端代理强制下载,绕过跨域限制 showToast('开始下载...', 'info'); const filename = `vision-video-${Date.now()}.mp4`; const proxyUrl = `/api/download_proxy?url=${encodeURIComponent(url)}&filename=${filename}`; // 创建隐藏的 iframe 触发下载,相比 a 标签兼容性更好 const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = proxyUrl; document.body.appendChild(iframe); // 1分钟后清理 iframe setTimeout(() => document.body.removeChild(iframe), 60000); }; loadMoreBtn.onclick = () => { historyPage++; loadHistory(historyPage, true); }; initConfig(); loadHistory(); window.applyTemplate = (text) => { promptInput.value = text; showToast('已应用提示词模板', 'success'); }; // 上传图片逻辑 fileInput.onchange = async (e) => { const files = e.target.files; if (files.length === 0) return; const formData = new FormData(); formData.append('images', files[0]); try { submitBtn.disabled = true; const r = await fetch('/api/upload', { method: 'POST', body: formData }); const d = await r.json(); if (d.urls && d.urls.length > 0) { uploadedImageUrl = d.urls[0]; imagePreview.innerHTML = `
`; lucide.createIcons(); } } catch (err) { showToast('图片上传失败', 'error'); } finally { submitBtn.disabled = false; } }; window.removeImage = () => { uploadedImageUrl = null; imagePreview.innerHTML = ''; fileInput.value = ''; }; // 提交生成任务 submitBtn.onclick = async () => { const prompt = promptInput.value.trim(); if (!prompt) return showToast('请输入视频描述', 'warning'); const payload = { prompt, model: modelSelect.value, enhance_prompt: enhancePrompt ? enhancePrompt.checked : false, aspect_ratio: ratioSelect ? ratioSelect.value : '9:16', images: uploadedImageUrl ? [uploadedImageUrl] : [] }; try { setLoading(true); const r = await fetch('/api/video/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const d = await r.json(); if (d.error) { showToast(d.error, 'error'); setLoading(false); } else if (d.task_id) { showToast(d.message || '任务已提交', 'info'); pollTaskStatus(d.task_id); } else { showToast('未知异常,请重试', 'error'); setLoading(false); } } catch (err) { console.error('提交生成异常:', err); showToast('任务提交失败', 'error'); setLoading(false); } }; async function pollTaskStatus(taskId) { let attempts = 0; const maxAttempts = 180; // 提升到 15 分钟 const check = async () => { try { const r = await fetch(`/api/task_status/${taskId}?t=${Date.now()}`); if (!r.ok) { setTimeout(check, 5000); return; } const d = await r.json(); if (d.status === 'complete') { setLoading(false); showVideo(d.video_url); refreshUserPoints(); // 刷新历史列表 loadHistory(1, false); } else if (d.status === 'error') { showToast(d.message || '生成失败', 'error'); setLoading(false); refreshUserPoints(); } else { attempts++; if (attempts >= maxAttempts) { showToast('生成超时,请稍后在历史记录中查看', 'warning'); setLoading(false); return; } setTimeout(check, 5000); } } catch (e) { setTimeout(check, 5000); } }; check(); } function showVideo(url) { if (!url) return; try { placeholder.classList.add('hidden'); finalWrapper.classList.remove('hidden'); resultVideo.src = url; resultVideo.load(); const playPromise = resultVideo.play(); if (playPromise !== undefined) { playPromise.catch(e => console.warn('自动播放被拦截')); } } catch (err) { console.error('展示视频失败:', err); } } const closePreviewBtn = document.getElementById('closePreviewBtn'); if (closePreviewBtn) { closePreviewBtn.onclick = () => { finalWrapper.classList.add('hidden'); placeholder.classList.remove('hidden'); resultVideo.pause(); resultVideo.src = ""; }; } function setLoading(isLoading) { submitBtn.disabled = isLoading; if (isLoading) { statusInfo.classList.remove('hidden'); submitBtn.innerHTML = '导演创作中...'; // 如果正在生成,确保回到预览背景状态(如果当前正在播放旧视频) if (!finalWrapper.classList.contains('hidden')) { finalWrapper.classList.add('hidden'); placeholder.classList.remove('hidden'); } } else { statusInfo.classList.add('hidden'); submitBtn.innerHTML = '开始生成视频'; } if (window.lucide) lucide.createIcons(); } async function refreshUserPoints() { try { const r = await fetch('/api/auth/me'); const d = await r.json(); if (d.points !== undefined) { if (pointsDisplay) pointsDisplay.innerText = d.points; if (headerPoints) headerPoints.innerText = d.points; } } catch (e) { } } document.getElementById('downloadBtn').onclick = () => { const url = resultVideo.src; if (!url) return; downloadUrl(url); }; if (regenBtn) { regenBtn.onclick = () => { submitBtn.click(); }; } });