STT-Bot / templates /index.html
slim-S's picture
feat: first commit
132ace4
Raw
History Blame Contribute Delete
11.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Algerian Darija Transcription Service</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
color: #333;
}
.container {
background-color: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
h1 {
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
transition: background-color 0.3s;
}
button:hover {
background-color: #2980b9;
}
button:disabled {
background-color: #95a5a6;
cursor: not-allowed;
}
.status {
margin-top: 20px;
}
.status-card {
background-color: #ecf0f1;
border-radius: 4px;
padding: 15px;
margin-bottom: 20px;
}
.users-section {
margin-top: 30px;
}
.user-card {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 10px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.user-stats {
display: flex;
gap: 15px;
}
.loader {
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 2s linear infinite;
display: inline-block;
margin-left: 10px;
vertical-align: middle;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.hidden {
display: none;
}
.alert {
padding: 10px;
border-radius: 4px;
margin-bottom: 10px;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.warning-banner {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeeba;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.uptime {
font-size: 0.9em;
color: #666;
margin-left: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>Algerian Darija Transcription Service</h1>
<div class="warning-banner">
<strong>Note:</strong> This service is running on Hugging Face Spaces with ephemeral storage.
Download processed data regularly to avoid data loss.
</div>
<div id="alert" class="hidden"></div>
<div class="controls">
<button id="startBtn">Start Bot</button>
<button id="stopBtn">Stop Bot</button>
<button id="refreshBtn">Refresh Status</button>
<button id="downloadAllBtn">Download All Processed Data</button>
</div>
<div class="status">
<h2>Status <span id="loader" class="loader hidden"></span></h2>
<div class="status-card">
<p>
<strong>Bot Status:</strong> <span id="botStatus">Unknown</span>
<span id="uptime" class="uptime"></span>
</p>
<p><strong>Pending Voice Files:</strong> <span id="pendingVoice">-</span></p>
<p><strong>Pending Text Files:</strong> <span id="pendingText">-</span></p>
<p><strong>Matching Pending Pairs:</strong> <span id="matchingPairs">-</span></p>
<p><strong>Processed Voice Files:</strong> <span id="processedVoice">-</span></p>
<p><strong>Processed Text Files:</strong> <span id="processedText">-</span></p>
</div>
</div>
<div class="users-section">
<h2>User Data</h2>
<div id="users-container">
<p>Loading user data...</p>
</div>
</div>
</div>
<script>
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const refreshBtn = document.getElementById('refreshBtn');
const downloadAllBtn = document.getElementById('downloadAllBtn');
const loader = document.getElementById('loader');
const alert = document.getElementById('alert');
// Initial load
updateStatus();
// Set up auto-refresh (every 30 seconds)
setInterval(updateStatus, 30000);
startBtn.addEventListener('click', async () => {
try {
showLoader();
const response = await fetch('/start-bot', {
method: 'POST'
});
const data = await response.json();
if (data.status === 'success') {
showAlert('success', data.message);
} else {
showAlert('error', data.message);
}
updateStatus();
} catch (error) {
showAlert('error', 'Failed to start bot: ' + error.message);
hideLoader();
}
});
stopBtn.addEventListener('click', async () => {
try {
showLoader();
const response = await fetch('/stop-bot', {
method: 'POST'
});
const data = await response.json();
if (data.status === 'success') {
showAlert('success', data.message);
} else {
showAlert('error', data.message);
}
updateStatus();
} catch (error) {
showAlert('error', 'Failed to stop bot: ' + error.message);
hideLoader();
}
});
refreshBtn.addEventListener('click', () => {
updateStatus();
});
downloadAllBtn.addEventListener('click', () => {
window.location.href = '/download-all';
});
function formatUptime(seconds) {
if (!seconds) return '';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
return `(Running for ${hours}h ${minutes}m ${secs}s)`;
}
async function updateStatus() {
try {
showLoader();
// Get status
const statusResponse = await fetch('/status');
const statusData = await statusResponse.json();
// Update status display
document.getElementById('botStatus').textContent = statusData.bot_running ? 'Running' : 'Stopped';
document.getElementById('uptime').textContent = statusData.bot_running ? formatUptime(statusData.uptime_seconds) : '';
document.getElementById('pendingVoice').textContent = statusData.pending_voice_count;
document.getElementById('pendingText').textContent = statusData.pending_text_count;
document.getElementById('matchingPairs').textContent = statusData.matching_pairs;
document.getElementById('processedVoice').textContent = statusData.processed_voice_count;
document.getElementById('processedText').textContent = statusData.processed_text_count;
// Update button states
startBtn.disabled = statusData.bot_running;
stopBtn.disabled = !statusData.bot_running;
// Update user list
const usersContainer = document.getElementById('users-container');
if (Object.keys(statusData.user_stats).length > 0) {
let usersHtml = '';
for (const [userId, stats] of Object.entries(statusData.user_stats)) {
usersHtml += `
<div class="user-card">
<div>
<strong>${userId}</strong>
<div class="user-stats">
<span>Voice Files: ${stats.voice}</span>
<span>Text Files: ${stats.text}</span>
</div>
</div>
<a href="/download/${userId}" target="_blank">
<button>Download Data</button>
</a>
</div>
`;
}
usersContainer.innerHTML = usersHtml;
} else {
usersContainer.innerHTML = '<p>No processed data available yet.</p>';
}
hideLoader();
} catch (error) {
console.error('Error updating status:', error);
showAlert('error', 'Failed to update status: ' + error.message);
hideLoader();
}
}
function showLoader() {
loader.classList.remove('hidden');
}
function hideLoader() {
loader.classList.add('hidden');
}
function showAlert(type, message) {
alert.className = `alert alert-${type}`;
alert.textContent = message;
alert.classList.remove('hidden');
// Hide after 5 seconds
setTimeout(() => {
alert.classList.add('hidden');
}, 5000);
}
</script>
</body>
</html>