441 lines
14 KiB
JavaScript
441 lines
14 KiB
JavaScript
// 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();
|
||
});
|
||
|