cytopa99's picture
Upload 46 files
8235752 verified
Raw History Blame
12.7 kB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<style>
.gradient-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card {
backdrop-filter: blur(10px);
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.progress-bar {
transition: width 0.3s ease;
}
</style>
</head>
<body class="gradient-bg min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- 头部 -->
<div class="text-center mb-8">
<h1 class="text-4xl font-bold text-white mb-2">🎬 Universal Fast Dubbing</h1>
<p class="text-xl text-gray-200">AI驱动的视频配音系统 v{{ version }}</p>
</div>
<!-- 主要内容区域 -->
<div class="max-w-4xl mx-auto">
<!-- 配音处理卡片 -->
<div class="card rounded-lg p-6 mb-6">
<h2 class="text-2xl font-semibold text-white mb-4">配音处理</h2>
<form id="dubbingForm" class="space-y-4">
<!-- 处理模式选择 -->
<div>
<label class="block text-white text-sm font-medium mb-2">处理模式</label>
<div class="flex space-x-4">
<label class="flex items-center text-white">
<input type="radio" name="mode" value="auto" checked class="mr-2">
自动检测
</label>
<label class="flex items-center text-white">
<input type="radio" name="mode" value="url" class="mr-2">
URL模式
</label>
<label class="flex items-center text-white">
<input type="radio" name="mode" value="record" class="mr-2">
录制模式
</label>
</div>
</div>
<!-- URL 输入 -->
<div id="urlInput">
<label class="block text-white text-sm font-medium mb-2">视频URL</label>
<input type="url" name="url" placeholder="https://www.youtube.com/watch?v=..."
class="w-full px-3 py-2 bg-white bg-opacity-20 border border-gray-300 rounded-md text-white placeholder-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<!-- 音频文件上传 -->
<div id="audioInput" style="display: none;">
<label class="block text-white text-sm font-medium mb-2">录制音频</label>
<input type="file" name="audio_file" accept="audio/*"
class="w-full px-3 py-2 bg-white bg-opacity-20 border border-gray-300 rounded-md text-white file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
</div>
<!-- 客户端配置(隐藏) -->
<input type="hidden" name="client_config" value="{}">
<!-- 提交按钮 -->
<button type="submit" id="processBtn"
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition duration-200">
🎬 开始配音
</button>
</form>
<!-- 进度条 -->
<div id="progressContainer" class="mt-4" style="display: none;">
<div class="bg-gray-200 rounded-full h-2">
<div id="progressBar" class="progress-bar bg-blue-600 h-2 rounded-full" style="width: 0%"></div>
</div>
<p id="progressText" class="text-white text-sm mt-2">处理中...</p>
</div>
<!-- 结果显示 -->
<div id="resultContainer" class="mt-4" style="display: none;">
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
<p id="resultMessage">配音完成!</p>
</div>
<audio id="resultAudio" controls class="w-full">
您的浏览器不支持音频播放。
</audio>
</div>
<!-- 错误显示 -->
<div id="errorContainer" class="mt-4" style="display: none;">
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
<p id="errorMessage">处理失败</p>
</div>
</div>
</div>
<!-- 系统状态卡片 -->
<div class="card rounded-lg p-6 mb-6">
<h2 class="text-2xl font-semibold text-white mb-4">系统状态</h2>
<button id="refreshStatusBtn"
class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded mb-4">
🔄 刷新状态
</button>
<div id="statusContainer" class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-white bg-opacity-10 rounded p-4">
<h3 class="text-white font-semibold mb-2">系统健康</h3>
<p id="healthStatus" class="text-gray-200">检查中...</p>
</div>
<div class="bg-white bg-opacity-10 rounded p-4">
<h3 class="text-white font-semibold mb-2">内存使用</h3>
<p id="memoryUsage" class="text-gray-200">-- MB</p>
</div>
<div class="bg-white bg-opacity-10 rounded p-4">
<h3 class="text-white font-semibold mb-2">CPU使用率</h3>
<p id="cpuUsage" class="text-gray-200">--%</p>
</div>
<div class="bg-white bg-opacity-10 rounded p-4">
<h3 class="text-white font-semibold mb-2">成功率</h3>
<p id="successRate" class="text-gray-200">--%</p>
</div>
</div>
</div>
<!-- API 文档卡片 -->
<div class="card rounded-lg p-6">
<h2 class="text-2xl font-semibold text-white mb-4">API 文档</h2>
<div class="text-gray-200 space-y-4">
<div>
<h3 class="text-lg font-semibold text-white">主要端点</h3>
<ul class="list-disc list-inside mt-2 space-y-1">
<li><code class="bg-gray-800 px-2 py-1 rounded">POST /api/process</code> - 配音处理</li>
<li><code class="bg-gray-800 px-2 py-1 rounded">POST /api/ping</code> - 连接测试</li>
<li><code class="bg-gray-800 px-2 py-1 rounded">GET /api/status</code> - 系统状态</li>
<li><code class="bg-gray-800 px-2 py-1 rounded">GET /api/config</code> - 后端配置</li>
</ul>
</div>
<div>
<h3 class="text-lg font-semibold text-white">支持平台</h3>
<p class="mt-2">YouTube ✓ | Bilibili ✓ | Netflix (录制模式) | TikTok ✓ | Twitter/X ✓</p>
</div>
</div>
</div>
</div>
</div>
<script>
// DOM 元素
const form = document.getElementById('dubbingForm');
const modeRadios = document.querySelectorAll('input[name="mode"]');
const urlInput = document.getElementById('urlInput');
const audioInput = document.getElementById('audioInput');
const processBtn = document.getElementById('processBtn');
const progressContainer = document.getElementById('progressContainer');
const progressBar = document.getElementById('progressBar');
const progressText = document.getElementById('progressText');
const resultContainer = document.getElementById('resultContainer');
const errorContainer = document.getElementById('errorContainer');
const refreshStatusBtn = document.getElementById('refreshStatusBtn');
// 模式切换处理
modeRadios.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'record') {
urlInput.style.display = 'none';
audioInput.style.display = 'block';
} else {
urlInput.style.display = 'block';
audioInput.style.display = 'none';
}
});
});
// 表单提交处理
form.addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(form);
// 显示进度条
progressContainer.style.display = 'block';
resultContainer.style.display = 'none';
errorContainer.style.display = 'none';
processBtn.disabled = true;
processBtn.textContent = '处理中...';
try {
const response = await fetch('/api/process', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
// 显示成功结果
document.getElementById('resultMessage').textContent =
`配音完成!处理时间: ${result.processing_time?.toFixed(1) || 0}秒`;
if (result.audio_url) {
document.getElementById('resultAudio').src = result.audio_url;
}
resultContainer.style.display = 'block';
} else {
// 显示错误
document.getElementById('errorMessage').textContent = result.error || '处理失败';
errorContainer.style.display = 'block';
}
} catch (error) {
console.error('处理失败:', error);
document.getElementById('errorMessage').textContent = '网络错误: ' + error.message;
errorContainer.style.display = 'block';
} finally {
progressContainer.style.display = 'none';
processBtn.disabled = false;
processBtn.textContent = '🎬 开始配音';
}
});
// 刷新系统状态
refreshStatusBtn.addEventListener('click', async function() {
try {
const response = await fetch('/api/status');
const status = await response.json();
document.getElementById('healthStatus').textContent =
status.healthy ? '✓ 正常' : '✗ 异常';
document.getElementById('memoryUsage').textContent =
`${status.performance?.memory_mb || 0} MB`;
document.getElementById('cpuUsage').textContent =
`${status.performance?.cpu_percent || 0}%`;
document.getElementById('successRate').textContent =
`${(status.performance?.success_rate * 100 || 0).toFixed(1)}%`;
} catch (error) {
console.error('获取状态失败:', error);
}
});
// 页面加载时获取状态
refreshStatusBtn.click();
</script>
</body>
</html>