github-actions[bot] commited on
Commit
993e34a
Β·
1 Parent(s): ea14d5d

Auto-deploy from GitHub: 58852063f2367763d209f363e78d2edbb6cc8847

Browse files
Files changed (1) hide show
  1. index.html +292 -17
index.html CHANGED
@@ -171,22 +171,38 @@
171
  transition: all 0.2s ease;
172
  box-shadow: 4px 4px 0 var(--bg);
173
  letter-spacing: 1px;
 
174
  }
175
 
176
- .btn:hover {
177
  transform: translate(-2px, -2px);
178
  box-shadow: 6px 6px 0 var(--bg);
179
  }
180
 
181
- .btn:active {
182
  transform: translate(2px, 2px);
183
  box-shadow: 2px 2px 0 var(--bg);
184
  }
185
 
 
 
 
 
 
186
  .btn-secondary {
187
  background: var(--accent);
188
  }
189
 
 
 
 
 
 
 
 
 
 
 
190
  .table-section {
191
  animation: slideUp 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.4s both;
192
  }
@@ -234,7 +250,6 @@
234
 
235
  tbody tr:hover {
236
  background: rgba(0, 255, 136, 0.1);
237
- transform: translateX(5px);
238
  }
239
 
240
  td {
@@ -283,17 +298,12 @@
283
  }
284
 
285
  .caption-cell {
286
- max-width: 400px;
287
  overflow: hidden;
288
  text-overflow: ellipsis;
289
  white-space: nowrap;
290
  }
291
 
292
- .caption-cell:hover {
293
- white-space: normal;
294
- cursor: pointer;
295
- }
296
-
297
  .empty-state {
298
  text-align: center;
299
  padding: 4rem 2rem;
@@ -325,6 +335,170 @@
325
  box-shadow: 6px 6px 0 var(--bg);
326
  }
327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  @media (max-width: 768px) {
329
  .container {
330
  padding: 1rem;
@@ -340,7 +514,11 @@
340
  }
341
 
342
  .caption-cell {
343
- max-width: 200px;
 
 
 
 
344
  }
345
  }
346
 
@@ -421,13 +599,37 @@
421
 
422
  <button class="refresh-btn" id="refreshBtn" title="Refresh">πŸ”„</button>
423
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  <script>
425
- const API_URL = '/api';
426
  let selectedFile = null;
427
 
428
  // Upload zone interactions
429
  const uploadZone = document.getElementById('uploadZone');
430
  const fileInput = document.getElementById('audioFile');
 
 
431
 
432
  uploadZone.addEventListener('click', () => fileInput.click());
433
 
@@ -459,7 +661,7 @@
459
  });
460
 
461
  // Upload button
462
- document.getElementById('uploadBtn').addEventListener('click', async () => {
463
  if (!selectedFile) {
464
  showNotification('Please select a file first!', 'error');
465
  return;
@@ -468,6 +670,10 @@
468
  const formData = new FormData();
469
  formData.append('audio', selectedFile);
470
 
 
 
 
 
471
  try {
472
  const response = await fetch(`${API_URL}/upload`, {
473
  method: 'POST',
@@ -486,6 +692,10 @@
486
  }
487
  } catch (error) {
488
  showNotification('Network error: ' + error.message, 'error');
 
 
 
 
489
  }
490
  });
491
 
@@ -502,25 +712,90 @@
502
  return;
503
  }
504
 
505
- tbody.innerHTML = files.map(file => `
 
 
 
 
 
506
  <tr>
507
  <td><strong>${file.filename}</strong></td>
508
  <td><span class="status status-${file.status}">${file.status.replace('_', ' ')}</span></td>
509
- <td class="caption-cell" title="${file.caption || 'N/A'}">${file.caption || 'β€”'}</td>
 
 
 
 
 
510
  <td>${new Date(file.created_at).toLocaleString()}</td>
511
  <td>${file.processed_at ? new Date(file.processed_at).toLocaleString() : 'β€”'}</td>
512
  </tr>
513
- `).join('');
 
514
  } catch (error) {
515
  console.error('Error loading files:', error);
516
  }
517
  }
518
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
  // Refresh button
520
  document.getElementById('refreshBtn').addEventListener('click', loadFiles);
521
 
522
- // Auto-refresh every 10 mins
523
- setInterval(loadFiles, 1000 * 60 * 10);
524
 
525
  // Show notification
526
  function showNotification(message, type = 'success') {
 
171
  transition: all 0.2s ease;
172
  box-shadow: 4px 4px 0 var(--bg);
173
  letter-spacing: 1px;
174
+ position: relative;
175
  }
176
 
177
+ .btn:hover:not(:disabled) {
178
  transform: translate(-2px, -2px);
179
  box-shadow: 6px 6px 0 var(--bg);
180
  }
181
 
182
+ .btn:active:not(:disabled) {
183
  transform: translate(2px, 2px);
184
  box-shadow: 2px 2px 0 var(--bg);
185
  }
186
 
187
+ .btn:disabled {
188
+ opacity: 0.6;
189
+ cursor: not-allowed;
190
+ }
191
+
192
  .btn-secondary {
193
  background: var(--accent);
194
  }
195
 
196
+ .btn-small {
197
+ padding: 0.5rem 1rem;
198
+ font-size: 0.85rem;
199
+ box-shadow: 3px 3px 0 var(--bg);
200
+ }
201
+
202
+ .btn-small:hover:not(:disabled) {
203
+ box-shadow: 4px 4px 0 var(--bg);
204
+ }
205
+
206
  .table-section {
207
  animation: slideUp 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.4s both;
208
  }
 
250
 
251
  tbody tr:hover {
252
  background: rgba(0, 255, 136, 0.1);
 
253
  }
254
 
255
  td {
 
298
  }
299
 
300
  .caption-cell {
301
+ max-width: 200px;
302
  overflow: hidden;
303
  text-overflow: ellipsis;
304
  white-space: nowrap;
305
  }
306
 
 
 
 
 
 
307
  .empty-state {
308
  text-align: center;
309
  padding: 4rem 2rem;
 
335
  box-shadow: 6px 6px 0 var(--bg);
336
  }
337
 
338
+ /* Loader styles */
339
+ .loader-overlay {
340
+ position: fixed;
341
+ top: 0;
342
+ left: 0;
343
+ width: 100%;
344
+ height: 100%;
345
+ background: rgba(10, 14, 39, 0.95);
346
+ display: flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ z-index: 9999;
350
+ animation: fadeIn 0.3s ease;
351
+ }
352
+
353
+ .loader {
354
+ width: 80px;
355
+ height: 80px;
356
+ border: 6px solid var(--surface);
357
+ border-top: 6px solid var(--primary);
358
+ border-right: 6px solid var(--accent);
359
+ border-bottom: 6px solid var(--secondary);
360
+ border-radius: 50%;
361
+ animation: spin 1s linear infinite;
362
+ }
363
+
364
+ @keyframes spin {
365
+ 0% { transform: rotate(0deg); }
366
+ 100% { transform: rotate(360deg); }
367
+ }
368
+
369
+ .loader-text {
370
+ position: absolute;
371
+ margin-top: 120px;
372
+ font-size: 1.2rem;
373
+ font-weight: 900;
374
+ color: var(--primary);
375
+ text-transform: uppercase;
376
+ letter-spacing: 2px;
377
+ }
378
+
379
+ /* Modal styles */
380
+ .modal {
381
+ display: none;
382
+ position: fixed;
383
+ top: 0;
384
+ left: 0;
385
+ width: 100%;
386
+ height: 100%;
387
+ background: rgba(10, 14, 39, 0.95);
388
+ z-index: 2000;
389
+ animation: fadeIn 0.3s ease;
390
+ overflow-y: auto;
391
+ }
392
+
393
+ .modal.active {
394
+ display: flex;
395
+ align-items: center;
396
+ justify-content: center;
397
+ padding: 2rem;
398
+ }
399
+
400
+ .modal-content {
401
+ background: var(--surface);
402
+ border: var(--border) solid var(--primary);
403
+ box-shadow: 12px 12px 0 var(--primary);
404
+ padding: 2rem;
405
+ max-width: 800px;
406
+ width: 100%;
407
+ max-height: 80vh;
408
+ overflow-y: auto;
409
+ position: relative;
410
+ animation: modalSlideIn 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
411
+ }
412
+
413
+ @keyframes modalSlideIn {
414
+ from {
415
+ opacity: 0;
416
+ transform: translateY(-50px) scale(0.9);
417
+ }
418
+ to {
419
+ opacity: 1;
420
+ transform: translateY(0) scale(1);
421
+ }
422
+ }
423
+
424
+ .modal-header {
425
+ display: flex;
426
+ justify-content: space-between;
427
+ align-items: center;
428
+ margin-bottom: 1.5rem;
429
+ padding-bottom: 1rem;
430
+ border-bottom: 3px solid var(--primary);
431
+ }
432
+
433
+ .modal-title {
434
+ font-size: 1.5rem;
435
+ font-weight: 900;
436
+ color: var(--primary);
437
+ text-transform: uppercase;
438
+ }
439
+
440
+ .modal-close {
441
+ background: var(--error);
442
+ color: var(--text);
443
+ border: 3px solid var(--bg);
444
+ width: 40px;
445
+ height: 40px;
446
+ border-radius: 0;
447
+ cursor: pointer;
448
+ font-size: 1.5rem;
449
+ font-weight: 900;
450
+ transition: all 0.2s ease;
451
+ box-shadow: 3px 3px 0 var(--bg);
452
+ }
453
+
454
+ .modal-close:hover {
455
+ transform: translate(-2px, -2px);
456
+ box-shadow: 5px 5px 0 var(--bg);
457
+ }
458
+
459
+ .code-block {
460
+ background: var(--bg);
461
+ border: 3px solid var(--accent);
462
+ padding: 1.5rem;
463
+ border-radius: 0;
464
+ overflow-x: auto;
465
+ margin-bottom: 1rem;
466
+ position: relative;
467
+ }
468
+
469
+ .code-block code {
470
+ font-family: 'Courier New', monospace;
471
+ color: var(--primary);
472
+ font-size: 0.95rem;
473
+ line-height: 1.6;
474
+ white-space: pre-wrap;
475
+ word-break: break-all;
476
+ }
477
+
478
+ .copy-btn {
479
+ position: absolute;
480
+ top: 1rem;
481
+ right: 1rem;
482
+ background: var(--accent);
483
+ color: var(--bg);
484
+ border: 3px solid var(--bg);
485
+ padding: 0.5rem 1rem;
486
+ font-size: 0.8rem;
487
+ font-weight: 900;
488
+ cursor: pointer;
489
+ transition: all 0.2s ease;
490
+ box-shadow: 3px 3px 0 var(--bg);
491
+ }
492
+
493
+ .copy-btn:hover {
494
+ transform: translate(-2px, -2px);
495
+ box-shadow: 4px 4px 0 var(--bg);
496
+ }
497
+
498
+ .copy-btn.copied {
499
+ background: var(--primary);
500
+ }
501
+
502
  @media (max-width: 768px) {
503
  .container {
504
  padding: 1rem;
 
514
  }
515
 
516
  .caption-cell {
517
+ max-width: 100px;
518
+ }
519
+
520
+ .modal-content {
521
+ padding: 1.5rem;
522
  }
523
  }
524
 
 
599
 
600
  <button class="refresh-btn" id="refreshBtn" title="Refresh">πŸ”„</button>
601
 
602
+ <!-- Loader -->
603
+ <div class="loader-overlay" id="loader" style="display: none;">
604
+ <div>
605
+ <div class="loader"></div>
606
+ <div class="loader-text">Uploading...</div>
607
+ </div>
608
+ </div>
609
+
610
+ <!-- Modal -->
611
+ <div class="modal" id="captionModal">
612
+ <div class="modal-content">
613
+ <div class="modal-header">
614
+ <div class="modal-title">πŸ“„ Caption Details</div>
615
+ <button class="modal-close" onclick="closeModal()">Γ—</button>
616
+ </div>
617
+ <div class="code-block">
618
+ <button class="copy-btn" onclick="copyCaption()">πŸ“‹ Copy</button>
619
+ <code id="captionCode"></code>
620
+ </div>
621
+ </div>
622
+ </div>
623
+
624
  <script>
625
+ const API_URL = 'http://localhost:7860/api';
626
  let selectedFile = null;
627
 
628
  // Upload zone interactions
629
  const uploadZone = document.getElementById('uploadZone');
630
  const fileInput = document.getElementById('audioFile');
631
+ const loader = document.getElementById('loader');
632
+ const uploadBtn = document.getElementById('uploadBtn');
633
 
634
  uploadZone.addEventListener('click', () => fileInput.click());
635
 
 
661
  });
662
 
663
  // Upload button
664
+ uploadBtn.addEventListener('click', async () => {
665
  if (!selectedFile) {
666
  showNotification('Please select a file first!', 'error');
667
  return;
 
670
  const formData = new FormData();
671
  formData.append('audio', selectedFile);
672
 
673
+ // Show loader
674
+ loader.style.display = 'flex';
675
+ uploadBtn.disabled = true;
676
+
677
  try {
678
  const response = await fetch(`${API_URL}/upload`, {
679
  method: 'POST',
 
692
  }
693
  } catch (error) {
694
  showNotification('Network error: ' + error.message, 'error');
695
+ } finally {
696
+ // Hide loader
697
+ loader.style.display = 'none';
698
+ uploadBtn.disabled = false;
699
  }
700
  });
701
 
 
712
  return;
713
  }
714
 
715
+ tbody.innerHTML = files.map(file => {
716
+ const captionPreview = file.caption ?
717
+ (file.caption.length > 50 ? file.caption.substring(0, 50) + '...' : file.caption) :
718
+ 'β€”';
719
+
720
+ return `
721
  <tr>
722
  <td><strong>${file.filename}</strong></td>
723
  <td><span class="status status-${file.status}">${file.status.replace('_', ' ')}</span></td>
724
+ <td class="caption-cell">
725
+ ${file.caption ?
726
+ `<span>${captionPreview}</span>
727
+ <button class="btn btn-small btn-secondary" onclick='showCaption(${JSON.stringify(file.caption)})' style="margin-left: 0.5rem;">Show</button>`
728
+ : 'β€”'}
729
+ </td>
730
  <td>${new Date(file.created_at).toLocaleString()}</td>
731
  <td>${file.processed_at ? new Date(file.processed_at).toLocaleString() : 'β€”'}</td>
732
  </tr>
733
+ `;
734
+ }).join('');
735
  } catch (error) {
736
  console.error('Error loading files:', error);
737
  }
738
  }
739
 
740
+ // Modal functions
741
+ function showCaption(caption) {
742
+ const modal = document.getElementById('captionModal');
743
+ const codeBlock = document.getElementById('captionCode');
744
+
745
+ // Parse JSON if it's a string
746
+ let formattedCaption = caption;
747
+ try {
748
+ const parsed = JSON.parse(caption);
749
+ formattedCaption = JSON.stringify(parsed, null, 2);
750
+ } catch (e) {
751
+ // If not JSON, use as is
752
+ formattedCaption = caption;
753
+ }
754
+
755
+ codeBlock.textContent = formattedCaption;
756
+ modal.classList.add('active');
757
+ }
758
+
759
+ function closeModal() {
760
+ const modal = document.getElementById('captionModal');
761
+ modal.classList.remove('active');
762
+ }
763
+
764
+ function copyCaption() {
765
+ const codeBlock = document.getElementById('captionCode');
766
+ const copyBtn = event.target;
767
+
768
+ navigator.clipboard.writeText(codeBlock.textContent).then(() => {
769
+ const originalText = copyBtn.textContent;
770
+ copyBtn.textContent = 'βœ“ Copied!';
771
+ copyBtn.classList.add('copied');
772
+
773
+ setTimeout(() => {
774
+ copyBtn.textContent = originalText;
775
+ copyBtn.classList.remove('copied');
776
+ }, 2000);
777
+ });
778
+ }
779
+
780
+ // Close modal on background click
781
+ document.getElementById('captionModal').addEventListener('click', (e) => {
782
+ if (e.target.id === 'captionModal') {
783
+ closeModal();
784
+ }
785
+ });
786
+
787
+ // Close modal on ESC key
788
+ document.addEventListener('keydown', (e) => {
789
+ if (e.key === 'Escape') {
790
+ closeModal();
791
+ }
792
+ });
793
+
794
  // Refresh button
795
  document.getElementById('refreshBtn').addEventListener('click', loadFiles);
796
 
797
+ // Auto-refresh every 3 seconds
798
+ setInterval(loadFiles, 3000);
799
 
800
  // Show notification
801
  function showNotification(message, type = 'success') {