staraks commited on
Commit
9bf49ce
·
verified ·
1 Parent(s): f0f2431

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -7
app.py CHANGED
@@ -186,7 +186,6 @@ def extract_zip_to_temp(zip_file: UploadFile, password: Optional[str]) -> List[s
186
  detail=f"Failed to open ZIP file. Check password / integrity. {e}",
187
  )
188
 
189
- # Only top-level; nested dirs can be added if needed.
190
  files = [os.path.join(outdir, f) for f in os.listdir(outdir)]
191
  return files
192
 
@@ -223,6 +222,35 @@ def health():
223
  return "OK"
224
 
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  # ---------- 1. Multi-file transcription (JSON) ----------
227
 
228
  @app.post("/api/transcribe/files", response_model=TranscriptionResponse)
@@ -505,6 +533,56 @@ HTML_UI = """
505
  margin-top: -4px;
506
  margin-bottom: 8px;
507
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
  @media (max-width: 768px) {
509
  header {
510
  padding: 12px 16px;
@@ -518,7 +596,11 @@ HTML_UI = """
518
  <body>
519
  <header>
520
  <h1>Whisper Large V3 – Medical Batch Transcription</h1>
521
- <p>Upload multiple audio files or a password-protected ZIP. Mode: general or medical_en. API docs at <code>/docs</code>.</p>
 
 
 
 
522
  </header>
523
  <main>
524
  <div class="card">
@@ -528,7 +610,9 @@ HTML_UI = """
528
  <h3>Inputs</h3>
529
  <label for="files_input">Audio files</label>
530
  <input id="files_input" type="file" multiple accept="audio/*" />
531
- <div class="small-hint">You can select multiple audio files.</div>
 
 
532
 
533
  <label for="files_mode">Mode</label>
534
  <select id="files_mode">
@@ -578,10 +662,119 @@ HTML_UI = """
578
  </div>
579
  </div>
580
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  <div id="status"></div>
582
  </main>
583
 
584
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
  async function postForm(url, formData, expectBlob = false) {
586
  const res = await fetch(url, {
587
  method: "POST",
@@ -608,6 +801,27 @@ HTML_UI = """
608
  document.getElementById("status").innerText = text || "";
609
  }
610
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  // ------- Multi-files JSON -------
612
  document.getElementById("btn_files_json").addEventListener("click", async () => {
613
  const filesInput = document.getElementById("files_input");
@@ -627,15 +841,17 @@ HTML_UI = """
627
 
628
  setStatus("Transcribing multiple files… (this may take some time for large audio)");
629
  out.value = "";
 
630
 
631
  try {
632
  const data = await postForm("/api/transcribe/files", formData, false);
633
  out.value = data.combined_transcript || "";
634
  setStatus("Done.");
 
635
  } catch (err) {
636
  console.error(err);
637
  alert(err.message);
638
- setStatus("Error during transcription.");
639
  }
640
  });
641
 
@@ -656,6 +872,7 @@ HTML_UI = """
656
  formData.append("mode", mode);
657
 
658
  setStatus("Generating DOCX for multi-file transcription…");
 
659
 
660
  try {
661
  const blob = await postForm("/api/transcribe/files/docx", formData, true);
@@ -668,10 +885,11 @@ HTML_UI = """
668
  a.remove();
669
  window.URL.revokeObjectURL(url);
670
  setStatus("DOCX downloaded.");
 
671
  } catch (err) {
672
  console.error(err);
673
  alert(err.message);
674
- setStatus("Error during DOCX generation.");
675
  }
676
  });
677
 
@@ -694,15 +912,17 @@ HTML_UI = """
694
 
695
  setStatus("Transcribing ZIP contents…");
696
  out.value = "";
 
697
 
698
  try {
699
  const data = await postForm("/api/transcribe/zip", formData, false);
700
  out.value = data.combined_transcript || "";
701
  setStatus("Done.");
 
702
  } catch (err) {
703
  console.error(err);
704
  alert(err.message);
705
- setStatus("Error during ZIP transcription.");
706
  }
707
  });
708
 
@@ -723,6 +943,7 @@ HTML_UI = """
723
  formData.append("mode", mode);
724
 
725
  setStatus("Generating DOCX from ZIP contents…");
 
726
 
727
  try {
728
  const blob = await postForm("/api/transcribe/zip/docx", formData, true);
@@ -735,12 +956,16 @@ HTML_UI = """
735
  a.remove();
736
  window.URL.revokeObjectURL(url);
737
  setStatus("DOCX downloaded.");
 
738
  } catch (err) {
739
  console.error(err);
740
  alert(err.message);
741
- setStatus("Error during ZIP DOCX generation.");
742
  }
743
  });
 
 
 
744
  </script>
745
  </body>
746
  </html>
 
186
  detail=f"Failed to open ZIP file. Check password / integrity. {e}",
187
  )
188
 
 
189
  files = [os.path.join(outdir, f) for f in os.listdir(outdir)]
190
  return files
191
 
 
222
  return "OK"
223
 
224
 
225
+ @app.get("/self-test")
226
+ def self_test():
227
+ """
228
+ Basic self-check:
229
+ - can we create/load the pipeline?
230
+ - what device are we using?
231
+ """
232
+ try:
233
+ pipe = get_pipeline()
234
+ model_name = getattr(pipe.model, "name_or_path", MODEL_NAME)
235
+ dev = "cuda" if device == 0 else str(device)
236
+ return JSONResponse(
237
+ {
238
+ "status": "ok",
239
+ "message": "Pipeline loaded successfully.",
240
+ "model": model_name,
241
+ "device": dev,
242
+ }
243
+ )
244
+ except Exception as e:
245
+ return JSONResponse(
246
+ {
247
+ "status": "error",
248
+ "message": f"Pipeline failed to load: {e}",
249
+ },
250
+ status_code=500,
251
+ )
252
+
253
+
254
  # ---------- 1. Multi-file transcription (JSON) ----------
255
 
256
  @app.post("/api/transcribe/files", response_model=TranscriptionResponse)
 
533
  margin-top: -4px;
534
  margin-bottom: 8px;
535
  }
536
+ code {
537
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
538
+ font-size: 12px;
539
+ }
540
+ pre {
541
+ background: #0b1120;
542
+ color: #e5e7eb;
543
+ padding: 10px 12px;
544
+ border-radius: 10px;
545
+ overflow-x: auto;
546
+ font-size: 12px;
547
+ line-height: 1.5;
548
+ }
549
+ a {
550
+ color: #1d4ed8;
551
+ text-decoration: none;
552
+ }
553
+ a:hover {
554
+ text-decoration: underline;
555
+ }
556
+
557
+ /* Progress bar */
558
+ #progress-wrapper {
559
+ margin-top: 10px;
560
+ margin-bottom: 4px;
561
+ font-size: 12px;
562
+ color: #4b5563;
563
+ }
564
+ #progress-track {
565
+ width: 100%;
566
+ height: 8px;
567
+ background: #e5e7eb;
568
+ border-radius: 999px;
569
+ overflow: hidden;
570
+ margin-top: 4px;
571
+ }
572
+ #progress-fill {
573
+ height: 100%;
574
+ width: 0%;
575
+ background: #111827;
576
+ border-radius: 999px;
577
+ transition: width 0.2s ease-out;
578
+ }
579
+ #progress-text {
580
+ font-size: 11px;
581
+ color: #6b7280;
582
+ margin-top: 3px;
583
+ min-height: 14px;
584
+ }
585
+
586
  @media (max-width: 768px) {
587
  header {
588
  padding: 12px 16px;
 
596
  <body>
597
  <header>
598
  <h1>Whisper Large V3 – Medical Batch Transcription</h1>
599
+ <p>
600
+ Upload multiple audio files or a password-protected ZIP.
601
+ Mode: <code>general</code> or <code>medical_en</code>.
602
+ API docs at <code>/docs</code>.
603
+ </p>
604
  </header>
605
  <main>
606
  <div class="card">
 
610
  <h3>Inputs</h3>
611
  <label for="files_input">Audio files</label>
612
  <input id="files_input" type="file" multiple accept="audio/*" />
613
+ <div class="small-hint">
614
+ You can select multiple audio files.
615
+ </div>
616
 
617
  <label for="files_mode">Mode</label>
618
  <select id="files_mode">
 
662
  </div>
663
  </div>
664
 
665
+ <!-- 3. Quick examples -->
666
+ <div class="card">
667
+ <h2>3. Quick examples <span class="pill">API & sample audio</span></h2>
668
+ <h3>Sample audio for testing (download & upload above)</h3>
669
+ <p class="small-hint">
670
+ 1. Download this small public sample file<br>
671
+ 2. Upload it in section 1 and click <strong>Transcribe → JSON</strong>
672
+ </p>
673
+ <p>
674
+ 👉 <a href="https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac" target="_blank" rel="noopener">
675
+ Download example audio (mlk.flac)
676
+ </a>
677
+ </p>
678
+
679
+ <h3>Example: cURL for multi-file JSON</h3>
680
+ <p class="small-hint">Replace <code>@path/to/audio1.flac</code> with your local file path.</p>
681
+ <pre><code>curl -X POST \\
682
+ "https://staraks-whisper-large-v3.hf.space/api/transcribe/files" \\
683
+ -H "Accept: application/json" \\
684
+ -F "mode=medical_en" \\
685
+ -F "files=@path/to/audio1.flac" \\
686
+ -F "files=@path/to/audio2.wav"</code></pre>
687
+
688
+ <h3>Example: cURL for ZIP JSON</h3>
689
+ <p class="small-hint">ZIP file contains multiple audio files. Password field is optional.</p>
690
+ <pre><code>curl -X POST \\
691
+ "https://staraks-whisper-large-v3.hf.space/api/transcribe/zip" \\
692
+ -H "Accept: application/json" \\
693
+ -F "mode=medical_en" \\
694
+ -F "file=@path/to/audios.zip" \\
695
+ -F "password="</code></pre>
696
+ </div>
697
+
698
+ <!-- 4. System self-check -->
699
+ <div class="card">
700
+ <h2>4. System self-check <span class="pill">Model & API status</span></h2>
701
+ <p class="small-hint">
702
+ Use this to quickly verify that the API is running and the Whisper pipeline can be loaded.
703
+ </p>
704
+ <button class="btn-primary" id="btn_self_test">Run self-test</button>
705
+ <pre id="self_test_output"><code>Click "Run self-test" to see status...</code></pre>
706
+ </div>
707
+
708
+ <!-- Progress & status -->
709
+ <div id="progress-wrapper">
710
+ <div>Transcription progress</div>
711
+ <div id="progress-track">
712
+ <div id="progress-fill"></div>
713
+ </div>
714
+ <div id="progress-text">Idle</div>
715
+ </div>
716
  <div id="status"></div>
717
  </main>
718
 
719
  <script>
720
+ let __progressTimer = null;
721
+ let __progressValue = 0;
722
+
723
+ function setProgress(value, label) {
724
+ __progressValue = Math.max(0, Math.min(100, value));
725
+ const fill = document.getElementById("progress-fill");
726
+ const text = document.getElementById("progress-text");
727
+ if (fill) {
728
+ fill.style.width = __progressValue + "%";
729
+ }
730
+ if (text) {
731
+ text.innerText = label + " (" + __progressValue.toFixed(0) + "%)";
732
+ }
733
+ }
734
+
735
+ function resetProgress() {
736
+ if (__progressTimer) {
737
+ clearInterval(__progressTimer);
738
+ __progressTimer = null;
739
+ }
740
+ setProgress(0, "Idle");
741
+ }
742
+
743
+ function startSimulatedProgress(label) {
744
+ if (__progressTimer) {
745
+ clearInterval(__progressTimer);
746
+ }
747
+ let p = 5;
748
+ setProgress(p, label);
749
+ __progressTimer = setInterval(() => {
750
+ if (p < 90) {
751
+ p += Math.random() * 10;
752
+ if (p > 90) p = 90;
753
+ setProgress(p, label);
754
+ }
755
+ }, 600);
756
+ }
757
+
758
+ function finishProgress(label) {
759
+ if (__progressTimer) {
760
+ clearInterval(__progressTimer);
761
+ __progressTimer = null;
762
+ }
763
+ setProgress(100, label);
764
+ setTimeout(() => {
765
+ resetProgress();
766
+ }, 2000);
767
+ }
768
+
769
+ function errorProgress(message) {
770
+ if (__progressTimer) {
771
+ clearInterval(__progressTimer);
772
+ __progressTimer = null;
773
+ }
774
+ setProgress(0, "Error");
775
+ setStatus(message);
776
+ }
777
+
778
  async function postForm(url, formData, expectBlob = false) {
779
  const res = await fetch(url, {
780
  method: "POST",
 
801
  document.getElementById("status").innerText = text || "";
802
  }
803
 
804
+ // ------- Self test -------
805
+ document.getElementById("btn_self_test").addEventListener("click", async () => {
806
+ const out = document.getElementById("self_test_output");
807
+ out.textContent = "Running self-test...";
808
+ setStatus("Running self-test…");
809
+ try {
810
+ const res = await fetch("/self-test");
811
+ const data = await res.json();
812
+ out.textContent = JSON.stringify(data, null, 2);
813
+ if (data.status === "ok") {
814
+ setStatus("Self-test OK – model and API are working.");
815
+ } else {
816
+ setStatus("Self-test reported an error.");
817
+ }
818
+ } catch (err) {
819
+ console.error(err);
820
+ out.textContent = "Self-test failed: " + err.message;
821
+ setStatus("Self-test failed.");
822
+ }
823
+ });
824
+
825
  // ------- Multi-files JSON -------
826
  document.getElementById("btn_files_json").addEventListener("click", async () => {
827
  const filesInput = document.getElementById("files_input");
 
841
 
842
  setStatus("Transcribing multiple files… (this may take some time for large audio)");
843
  out.value = "";
844
+ startSimulatedProgress("Transcribing files");
845
 
846
  try {
847
  const data = await postForm("/api/transcribe/files", formData, false);
848
  out.value = data.combined_transcript || "";
849
  setStatus("Done.");
850
+ finishProgress("Transcription complete");
851
  } catch (err) {
852
  console.error(err);
853
  alert(err.message);
854
+ errorProgress("Error during transcription.");
855
  }
856
  });
857
 
 
872
  formData.append("mode", mode);
873
 
874
  setStatus("Generating DOCX for multi-file transcription…");
875
+ startSimulatedProgress("Generating DOCX");
876
 
877
  try {
878
  const blob = await postForm("/api/transcribe/files/docx", formData, true);
 
885
  a.remove();
886
  window.URL.revokeObjectURL(url);
887
  setStatus("DOCX downloaded.");
888
+ finishProgress("DOCX ready");
889
  } catch (err) {
890
  console.error(err);
891
  alert(err.message);
892
+ errorProgress("Error during DOCX generation.");
893
  }
894
  });
895
 
 
912
 
913
  setStatus("Transcribing ZIP contents…");
914
  out.value = "";
915
+ startSimulatedProgress("Transcribing ZIP");
916
 
917
  try {
918
  const data = await postForm("/api/transcribe/zip", formData, false);
919
  out.value = data.combined_transcript || "";
920
  setStatus("Done.");
921
+ finishProgress("ZIP transcription complete");
922
  } catch (err) {
923
  console.error(err);
924
  alert(err.message);
925
+ errorProgress("Error during ZIP transcription.");
926
  }
927
  });
928
 
 
943
  formData.append("mode", mode);
944
 
945
  setStatus("Generating DOCX from ZIP contents…");
946
+ startSimulatedProgress("Generating ZIP DOCX");
947
 
948
  try {
949
  const blob = await postForm("/api/transcribe/zip/docx", formData, true);
 
956
  a.remove();
957
  window.URL.revokeObjectURL(url);
958
  setStatus("DOCX downloaded.");
959
+ finishProgress("ZIP DOCX ready");
960
  } catch (err) {
961
  console.error(err);
962
  alert(err.message);
963
+ errorProgress("Error during ZIP DOCX generation.");
964
  }
965
  });
966
+
967
+ // Initial state
968
+ resetProgress();
969
  </script>
970
  </body>
971
  </html>