2025-12-02 18:54:14 +08:00

441 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// API base URL - automatically detect from current location
const API_BASE = `${window.location.protocol}//${window.location.host}/api/videos`;
// Tab switching
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
const tab = btn.dataset.tab;
switchTab(tab);
});
});
function switchTab(tabName) {
// Update buttons
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
if (btn.dataset.tab === tabName) {
btn.classList.add('active');
}
});
// Update content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(`${tabName}-tab`).classList.add('active');
// Load data when switching tabs
if (tabName === 'list') {
loadVideos();
} else if (tabName === 'compare') {
loadVideosForCompare();
}
}
// File upload
const fileInput = document.getElementById('file-input');
const uploadArea = document.getElementById('upload-area');
fileInput.addEventListener('change', handleFileSelect);
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.style.borderColor = '#764ba2';
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.style.borderColor = '#667eea';
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.style.borderColor = '#667eea';
const files = e.dataTransfer.files;
if (files.length > 0) {
fileInput.files = files;
handleFileSelect({ target: fileInput });
}
});
function handleFileSelect(e) {
const file = e.target.files[0];
if (!file) return;
uploadFile(file);
}
function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
const progressDiv = document.getElementById('upload-progress');
const progressFill = document.getElementById('progress-fill');
const progressText = document.getElementById('progress-text');
const resultDiv = document.getElementById('upload-result');
// Show progress
document.querySelector('.upload-placeholder').style.display = 'none';
progressDiv.style.display = 'block';
progressFill.style.width = '0%';
resultDiv.style.display = 'none';
// Upload using fetch
fetch(`${API_BASE}/upload`, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
progressFill.style.width = '100%';
progressText.textContent = '上传完成!';
setTimeout(() => {
if (data.success) {
showResult('upload-result', `视频上传成功ID: ${data.data.video_id}`, 'success');
// Reset form
fileInput.value = '';
document.querySelector('.upload-placeholder').style.display = 'block';
progressDiv.style.display = 'none';
} else {
showResult('upload-result', data.message || '上传失败', 'error');
}
}, 500);
})
.catch(error => {
showResult('upload-result', `上传失败: ${error.message}`, 'error');
progressDiv.style.display = 'none';
document.querySelector('.upload-placeholder').style.display = 'block';
});
}
// Load videos list
function loadVideos() {
const videoList = document.getElementById('video-list');
videoList.innerHTML = '<p class="loading">加载中...</p>';
fetch(`${API_BASE}`)
.then(response => response.json())
.then(data => {
if (data.success && data.data.length > 0) {
videoList.innerHTML = '';
data.data.forEach(video => {
const videoItem = createVideoItem(video);
videoList.appendChild(videoItem);
});
} else {
videoList.innerHTML = '<p class="loading">暂无视频</p>';
}
})
.catch(error => {
videoList.innerHTML = `<p class="loading" style="color: red;">加载失败: ${error.message}</p>`;
});
}
function createVideoItem(video) {
const div = document.createElement('div');
div.className = 'video-item';
const sizeMB = (video.file_size / (1024 * 1024)).toFixed(2);
const uploadTime = new Date(video.upload_time).toLocaleString('zh-CN');
div.innerHTML = `
<div class="video-item-header">
<div class="video-item-title">${video.filename}</div>
<span class="badge" style="background: ${getStatusColor(video.status)}; color: white; padding: 4px 12px; border-radius: 12px; font-size: 12px;">
${getStatusText(video.status)}
</span>
</div>
<div class="video-item-meta">
大小: ${sizeMB} MB | 上传时间: ${uploadTime}
</div>
<div class="video-item-actions">
<button class="btn btn-small btn-info" onclick="viewVideoDetails('${video.id}')">查看详情</button>
<button class="btn btn-small btn-primary" onclick="analyzeVideo('${video.id}')">分析视频</button>
<button class="btn btn-small btn-success" onclick="summarizeVideo('${video.id}')">生成总结</button>
</div>
`;
return div;
}
function getStatusColor(status) {
const colors = {
'uploaded': '#6c757d',
'analyzing': '#ffc107',
'analyzed': '#28a745',
'failed': '#dc3545'
};
return colors[status] || '#6c757d';
}
function getStatusText(status) {
const texts = {
'uploaded': '已上传',
'analyzing': '分析中',
'analyzed': '已分析',
'failed': '失败'
};
return texts[status] || status;
}
// Video details modal
function viewVideoDetails(videoId) {
fetch(`${API_BASE}/${videoId}`)
.then(response => response.json())
.then(data => {
if (data.success) {
showVideoModal(data.data);
} else {
alert(data.message || '获取视频详情失败');
}
})
.catch(error => {
alert(`获取视频详情失败: ${error.message}`);
});
}
function showVideoModal(video) {
const modal = document.getElementById('video-modal');
const modalTitle = document.getElementById('modal-title');
const modalBody = document.getElementById('modal-body');
modalTitle.textContent = video.filename;
let html = `
<div class="detail-section">
<h3>基本信息</h3>
<p><strong>文件名:</strong> ${video.filename}</p>
<p><strong>大小:</strong> ${(video.file_size / (1024 * 1024)).toFixed(2)} MB</p>
<p><strong>状态:</strong> ${getStatusText(video.status)}</p>
<p><strong>上传时间:</strong> ${new Date(video.upload_time).toLocaleString('zh-CN')}</p>
</div>
`;
if (video.analysis) {
html += `
<div class="detail-section">
<h3>分析结果</h3>
<p>${video.analysis.content}</p>
<p style="margin-top: 10px; color: #666; font-size: 14px;">
<strong>FPS:</strong> ${video.analysis.fps} |
<strong>创建时间:</strong> ${new Date(video.analysis.created_at).toLocaleString('zh-CN')}
</p>
</div>
`;
}
if (video.summary) {
html += `
<div class="detail-section">
<h3>视频总结</h3>
<p>${video.summary.summary_text}</p>
<p style="margin-top: 10px; color: #666; font-size: 14px;">
<strong>创建时间:</strong> ${new Date(video.summary.created_at).toLocaleString('zh-CN')}
</p>
</div>
`;
}
modalBody.innerHTML = html;
modal.style.display = 'block';
}
function closeModal() {
document.getElementById('video-modal').style.display = 'none';
}
window.onclick = function(event) {
const modal = document.getElementById('video-modal');
if (event.target === modal) {
closeModal();
}
}
// Analyze video
function analyzeVideo(videoId) {
if (!confirm('确定要分析这个视频吗?这可能需要一些时间。')) {
return;
}
// Show loading message
const loadingMsg = document.createElement('div');
loadingMsg.id = 'analysis-loading';
loadingMsg.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); z-index: 10000;';
loadingMsg.innerHTML = '<p>正在分析视频,请稍候...</p>';
document.body.appendChild(loadingMsg);
fetch(`${API_BASE}/${videoId}/analyze`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(data => {
// Remove loading message
const loadingEl = document.getElementById('analysis-loading');
if (loadingEl) {
loadingEl.remove();
}
if (data.success) {
// Refresh video list first
loadVideos();
// Then automatically open video details to show the analysis
setTimeout(() => {
viewVideoDetails(videoId);
}, 500);
} else {
alert(data.message || '分析失败');
}
})
.catch(error => {
// Remove loading message
const loadingEl = document.getElementById('analysis-loading');
if (loadingEl) {
loadingEl.remove();
}
alert(`分析失败: ${error.message}`);
});
}
// Summarize video
function summarizeVideo(videoId) {
if (!confirm('确定要生成视频总结吗?这可能需要一些时间。')) {
return;
}
// Show loading message
const loadingMsg = document.createElement('div');
loadingMsg.id = 'summary-loading';
loadingMsg.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); z-index: 10000;';
loadingMsg.innerHTML = '<p>正在生成视频总结,请稍候...</p>';
document.body.appendChild(loadingMsg);
fetch(`${API_BASE}/${videoId}/summarize`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(data => {
// Remove loading message
const loadingEl = document.getElementById('summary-loading');
if (loadingEl) {
loadingEl.remove();
}
if (data.success) {
// Refresh video list first
loadVideos();
// Then automatically open video details to show the summary
setTimeout(() => {
viewVideoDetails(videoId);
}, 500);
} else {
alert(data.message || '总结生成失败');
}
})
.catch(error => {
// Remove loading message
const loadingEl = document.getElementById('summary-loading');
if (loadingEl) {
loadingEl.remove();
}
alert(`总结生成失败: ${error.message}`);
});
}
// Compare videos
function loadVideosForCompare() {
const compareList = document.getElementById('compare-video-list');
compareList.innerHTML = '<p class="loading">加载中...</p>';
fetch(`${API_BASE}`)
.then(response => response.json())
.then(data => {
if (data.success && data.data.length > 0) {
compareList.innerHTML = '';
data.data.forEach(video => {
const checkbox = document.createElement('div');
checkbox.className = 'video-checkbox-item';
checkbox.innerHTML = `
<input type="checkbox" value="${video.id}" id="video-${video.id}" onchange="updateCompareButton()">
<label for="video-${video.id}">${video.filename}</label>
`;
compareList.appendChild(checkbox);
});
} else {
compareList.innerHTML = '<p class="loading">暂无视频,请先上传视频</p>';
}
})
.catch(error => {
compareList.innerHTML = `<p class="loading" style="color: red;">加载失败: ${error.message}</p>`;
});
}
function updateCompareButton() {
const checked = document.querySelectorAll('#compare-video-list input[type="checkbox"]:checked');
const compareBtn = document.getElementById('compare-btn');
compareBtn.disabled = checked.length < 2;
}
function compareVideos() {
const checked = document.querySelectorAll('#compare-video-list input[type="checkbox"]:checked');
if (checked.length < 2) {
alert('请至少选择2个视频进行对比');
return;
}
if (!confirm(`确定要对比这 ${checked.length} 个视频吗?这可能需要一些时间。`)) {
return;
}
const videoIds = Array.from(checked).map(cb => cb.value);
const resultDiv = document.getElementById('compare-result');
resultDiv.innerHTML = '<p class="loading">对比中,请稍候...</p>';
resultDiv.className = 'result-message';
resultDiv.style.display = 'block';
fetch(`${API_BASE}/compare`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ video_ids: videoIds })
})
.then(response => response.json())
.then(data => {
if (data.success) {
resultDiv.className = 'result-message success';
resultDiv.innerHTML = `
<h3>对比结果</h3>
<div class="compare-result-content">${data.data.comparison_result}</div>
`;
} else {
resultDiv.className = 'result-message error';
resultDiv.textContent = data.message || '对比失败';
}
})
.catch(error => {
resultDiv.className = 'result-message error';
resultDiv.textContent = `对比失败: ${error.message}`;
});
}
function showResult(elementId, message, type) {
const element = document.getElementById(elementId);
element.textContent = message;
element.className = `result-message ${type}`;
element.style.display = 'block';
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
loadVideos();
});