119 lines
6.2 KiB
HTML
119 lines
6.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" class="dark">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}Encoder{% endblock %}</title>
|
|
<link href="{{ url_for('static', filename='css/output.css') }}" rel="stylesheet">
|
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='favicon.png') }}">
|
|
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
|
|
</head>
|
|
<body class="bg-gray-900 text-white min-h-screen">
|
|
<nav class="bg-gray-800 shadow-lg">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="flex justify-between h-16">
|
|
<div class="flex items-center">
|
|
<a href="{{ url_for('index') }}" class="flex items-center space-x-2">
|
|
<img src="{{ url_for('static', filename='favicon.png') }}" alt="Encoder" class="w-8 h-8 rounded-lg">
|
|
<span class="text-xl font-bold">Encoder</span>
|
|
</a>
|
|
</div>
|
|
<div class="flex items-center space-x-4">
|
|
<a href="{{ url_for('index') }}" class="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium flex items-center space-x-2">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
|
</svg>
|
|
<span>Dashboard</span>
|
|
</a>
|
|
<a href="{{ url_for('config') }}" class="text-gray-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium flex items-center space-x-2">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
</svg>
|
|
<span>Configuration</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<main class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
|
|
<div id="notifications" class="fixed bottom-4 right-4 z-50"></div>
|
|
|
|
<script>
|
|
// Auto-refresh status every 5 seconds
|
|
setInterval(() => {
|
|
fetch('/api/status')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Update current jobs status
|
|
updateJobStatus('video', data.current_jobs.video);
|
|
updateJobStatus('audio', data.current_jobs.audio);
|
|
|
|
// Update queue lengths
|
|
updateQueueLength('video', data.queue_lengths.video);
|
|
updateQueueLength('audio', data.queue_lengths.audio);
|
|
});
|
|
}, 5000);
|
|
|
|
function updateJobStatus(type, job) {
|
|
const statusElement = document.getElementById(`${type}-status`);
|
|
if (statusElement) {
|
|
if (job) {
|
|
const fileName = job.file_path.split('/').pop();
|
|
const duration = Math.floor((Date.now() / 1000) - job.start_time);
|
|
statusElement.innerHTML = `
|
|
<div class="flex items-center space-x-2">
|
|
<div class="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div>
|
|
<span class="text-sm">Processing: ${fileName}</span>
|
|
<span class="text-xs text-gray-400">(${duration}s)</span>
|
|
</div>
|
|
`;
|
|
} else {
|
|
statusElement.innerHTML = `
|
|
<div class="flex items-center space-x-2">
|
|
<div class="w-3 h-3 bg-gray-500 rounded-full"></div>
|
|
<span class="text-sm">Idle</span>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateQueueLength(type, length) {
|
|
const queueElement = document.getElementById(`${type}-queue-length`);
|
|
if (queueElement) {
|
|
queueElement.textContent = length;
|
|
}
|
|
}
|
|
|
|
function showNotification(message, type = 'info') {
|
|
const notification = document.createElement('div');
|
|
notification.className = `
|
|
p-4 rounded-lg shadow-lg max-w-sm mb-4 transition-all duration-300
|
|
${type === 'success' ? 'bg-green-600' : type === 'error' ? 'bg-red-600' : 'bg-blue-600'}
|
|
`;
|
|
notification.innerHTML = `
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-white text-sm">${message}</span>
|
|
<button onclick="this.parentElement.parentElement.remove()" class="text-white hover:text-gray-200">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
`;
|
|
|
|
document.getElementById('notifications').appendChild(notification);
|
|
|
|
setTimeout(() => {
|
|
notification.remove();
|
|
}, 5000);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|