github-actions[bot]
commited on
Commit
Β·
249f430
1
Parent(s):
444be0b
Auto-deploy from GitHub: 8986b16862923441dcd086b63345a47c011d42b5
Browse files- app.py +27 -2
- index.html +79 -3
app.py
CHANGED
|
@@ -201,11 +201,25 @@ def get_files():
|
|
| 201 |
c = conn.cursor()
|
| 202 |
c.execute('SELECT * FROM tasks ORDER BY created_at DESC')
|
| 203 |
rows = c.fetchall()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
conn.close()
|
| 205 |
|
|
|
|
|
|
|
|
|
|
| 206 |
files = []
|
| 207 |
for row in rows:
|
| 208 |
-
|
| 209 |
'id': row['id'],
|
| 210 |
'text': row['text'],
|
| 211 |
'status': row['status'],
|
|
@@ -213,7 +227,18 @@ def get_files():
|
|
| 213 |
'created_at': row['created_at'],
|
| 214 |
'processed_at': row['processed_at'],
|
| 215 |
'error': row['error']
|
| 216 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
|
| 218 |
return jsonify(files)
|
| 219 |
|
|
|
|
| 201 |
c = conn.cursor()
|
| 202 |
c.execute('SELECT * FROM tasks ORDER BY created_at DESC')
|
| 203 |
rows = c.fetchall()
|
| 204 |
+
|
| 205 |
+
# Get queue order for not_started tasks (oldest first = position 1)
|
| 206 |
+
c.execute('''SELECT id FROM tasks
|
| 207 |
+
WHERE status = 'not_started'
|
| 208 |
+
ORDER BY created_at ASC''')
|
| 209 |
+
queue_order = [r['id'] for r in c.fetchall()]
|
| 210 |
+
|
| 211 |
+
# Check if any task is currently processing
|
| 212 |
+
c.execute('SELECT COUNT(*) as count FROM tasks WHERE status = "processing"')
|
| 213 |
+
processing_count = c.fetchone()['count']
|
| 214 |
+
|
| 215 |
conn.close()
|
| 216 |
|
| 217 |
+
# Average processing time in seconds (can be adjusted based on actual metrics)
|
| 218 |
+
AVG_PROCESSING_TIME = 30
|
| 219 |
+
|
| 220 |
files = []
|
| 221 |
for row in rows:
|
| 222 |
+
file_data = {
|
| 223 |
'id': row['id'],
|
| 224 |
'text': row['text'],
|
| 225 |
'status': row['status'],
|
|
|
|
| 227 |
'created_at': row['created_at'],
|
| 228 |
'processed_at': row['processed_at'],
|
| 229 |
'error': row['error']
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
# Add queue position for not_started tasks
|
| 233 |
+
if row['status'] == 'not_started' and row['id'] in queue_order:
|
| 234 |
+
queue_position = queue_order.index(row['id']) + 1 # 1-indexed
|
| 235 |
+
file_data['queue_position'] = queue_position
|
| 236 |
+
# Estimated time = (position - 1 + processing_count) * avg_time
|
| 237 |
+
# If something is processing, add that to the wait
|
| 238 |
+
tasks_ahead = queue_position - 1 + processing_count
|
| 239 |
+
file_data['estimated_start_seconds'] = tasks_ahead * AVG_PROCESSING_TIME
|
| 240 |
+
|
| 241 |
+
files.append(file_data)
|
| 242 |
|
| 243 |
return jsonify(files)
|
| 244 |
|
index.html
CHANGED
|
@@ -524,9 +524,15 @@
|
|
| 524 |
</div>
|
| 525 |
</div>
|
| 526 |
|
| 527 |
-
<
|
| 528 |
-
|
| 529 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 530 |
</div>
|
| 531 |
|
| 532 |
<div class="table-section">
|
|
@@ -571,6 +577,8 @@
|
|
| 571 |
const voiceSelect = document.getElementById('voiceSelect');
|
| 572 |
const speedInput = document.getElementById('speedInput');
|
| 573 |
const generateBtn = document.getElementById('generateBtn');
|
|
|
|
|
|
|
| 574 |
const loader = document.getElementById('loader');
|
| 575 |
const refreshBtn = document.getElementById('refreshBtn');
|
| 576 |
|
|
@@ -620,6 +628,74 @@
|
|
| 620 |
}
|
| 621 |
});
|
| 622 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 623 |
// Load files
|
| 624 |
async function loadFiles() {
|
| 625 |
try {
|
|
|
|
| 524 |
</div>
|
| 525 |
</div>
|
| 526 |
|
| 527 |
+
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
| 528 |
+
<button class="btn" id="generateBtn" style="flex: 1; min-width: 200px;">
|
| 529 |
+
π Generate Audio
|
| 530 |
+
</button>
|
| 531 |
+
<button class="btn btn-secondary" id="uploadBtn" style="flex: 1; min-width: 200px;">
|
| 532 |
+
π Upload File
|
| 533 |
+
</button>
|
| 534 |
+
<input type="file" id="fileInput" accept=".txt,.md,.text" style="display: none;">
|
| 535 |
+
</div>
|
| 536 |
</div>
|
| 537 |
|
| 538 |
<div class="table-section">
|
|
|
|
| 577 |
const voiceSelect = document.getElementById('voiceSelect');
|
| 578 |
const speedInput = document.getElementById('speedInput');
|
| 579 |
const generateBtn = document.getElementById('generateBtn');
|
| 580 |
+
const uploadBtn = document.getElementById('uploadBtn');
|
| 581 |
+
const fileInput = document.getElementById('fileInput');
|
| 582 |
const loader = document.getElementById('loader');
|
| 583 |
const refreshBtn = document.getElementById('refreshBtn');
|
| 584 |
|
|
|
|
| 628 |
}
|
| 629 |
});
|
| 630 |
|
| 631 |
+
// Upload button - opens file picker
|
| 632 |
+
uploadBtn.addEventListener('click', () => {
|
| 633 |
+
fileInput.click();
|
| 634 |
+
});
|
| 635 |
+
|
| 636 |
+
// File input change - auto-submit for TTS
|
| 637 |
+
fileInput.addEventListener('change', async (e) => {
|
| 638 |
+
const file = e.target.files[0];
|
| 639 |
+
if (!file) return;
|
| 640 |
+
|
| 641 |
+
// Read file content
|
| 642 |
+
const reader = new FileReader();
|
| 643 |
+
reader.onload = async (event) => {
|
| 644 |
+
const text = event.target.result.trim();
|
| 645 |
+
|
| 646 |
+
if (!text) {
|
| 647 |
+
showNotification('File is empty!', 'error');
|
| 648 |
+
fileInput.value = ''; // Reset file input
|
| 649 |
+
return;
|
| 650 |
+
}
|
| 651 |
+
|
| 652 |
+
const voice = voiceSelect.value;
|
| 653 |
+
const speed = parseFloat(speedInput.value);
|
| 654 |
+
|
| 655 |
+
// Show loader
|
| 656 |
+
loader.style.display = 'flex';
|
| 657 |
+
uploadBtn.disabled = true;
|
| 658 |
+
generateBtn.disabled = true;
|
| 659 |
+
|
| 660 |
+
try {
|
| 661 |
+
const response = await fetch(`${API_URL}/generate`, {
|
| 662 |
+
method: 'POST',
|
| 663 |
+
headers: {
|
| 664 |
+
'Content-Type': 'application/json'
|
| 665 |
+
},
|
| 666 |
+
body: JSON.stringify({
|
| 667 |
+
text,
|
| 668 |
+
voice,
|
| 669 |
+
speed
|
| 670 |
+
})
|
| 671 |
+
});
|
| 672 |
+
|
| 673 |
+
const data = await response.json();
|
| 674 |
+
|
| 675 |
+
if (response.ok) {
|
| 676 |
+
showNotification(`File "${file.name}" queued for TTS! π`);
|
| 677 |
+
loadFiles();
|
| 678 |
+
} else {
|
| 679 |
+
showNotification(data.error || 'Upload failed', 'error');
|
| 680 |
+
}
|
| 681 |
+
} catch (error) {
|
| 682 |
+
showNotification('Network error: ' + error.message, 'error');
|
| 683 |
+
} finally {
|
| 684 |
+
loader.style.display = 'none';
|
| 685 |
+
uploadBtn.disabled = false;
|
| 686 |
+
generateBtn.disabled = false;
|
| 687 |
+
fileInput.value = ''; // Reset file input for next upload
|
| 688 |
+
}
|
| 689 |
+
};
|
| 690 |
+
|
| 691 |
+
reader.onerror = () => {
|
| 692 |
+
showNotification('Error reading file!', 'error');
|
| 693 |
+
fileInput.value = '';
|
| 694 |
+
};
|
| 695 |
+
|
| 696 |
+
reader.readAsText(file);
|
| 697 |
+
});
|
| 698 |
+
|
| 699 |
// Load files
|
| 700 |
async function loadFiles() {
|
| 701 |
try {
|