Update app.py
Browse files
app.py
CHANGED
|
@@ -9,42 +9,6 @@ import time
|
|
| 9 |
import tempfile
|
| 10 |
from concurrent.futures import ThreadPoolExecutor
|
| 11 |
from typing import List, Tuple, Optional, Dict, Any
|
| 12 |
-
from pathlib import Path
|
| 13 |
-
|
| 14 |
-
def create_download_link(audio_path, label="Download"):
|
| 15 |
-
if audio_path is None:
|
| 16 |
-
return "" # Return empty string instead of None for HTML component
|
| 17 |
-
|
| 18 |
-
filename = Path(audio_path).name
|
| 19 |
-
# IMPORTANT: For Hugging Face Spaces, ensure your file URLs are correctly formed
|
| 20 |
-
# This might need to point to /file= if Gradio serves static files that way
|
| 21 |
-
# Or, if deployed on Hugging Face Spaces, the file serving might be handled by the backend directly
|
| 22 |
-
# and not necessarily through gradio_api/file=. For now, keep as is, but be aware.
|
| 23 |
-
base_url = "hivecorp-insta-maker-3.hf.space" # Replace with your actual deployment domain
|
| 24 |
-
file_url = f"https://{base_url}/gradio_api/file={audio_path}" # This path might be problematic on Spaces
|
| 25 |
-
|
| 26 |
-
return f"""
|
| 27 |
-
<a href="{file_url}"
|
| 28 |
-
download="{filename}"
|
| 29 |
-
target="_blank"
|
| 30 |
-
rel="noopener noreferrer"
|
| 31 |
-
style="display: inline-block; padding: 10px 20px; background: linear-gradient(135deg, #4776E6, #8E54E9); color: white; text-decoration: none; border-radius: 8px; font-weight: 600; transition: all 0.3s ease;"
|
| 32 |
-
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 5px 15px rgba(71, 118, 230, 0.3)';"
|
| 33 |
-
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none';"
|
| 34 |
-
onclick="event.preventDefault(); fetch(this.href).then(resp => resp.blob()).then(blob => {{
|
| 35 |
-
const url = window.URL.createObjectURL(blob);
|
| 36 |
-
const a = document.createElement('a');
|
| 37 |
-
a.style.display = 'none';
|
| 38 |
-
a.href = url;
|
| 39 |
-
a.download = '{filename}';
|
| 40 |
-
document.body.appendChild(a);
|
| 41 |
-
a.click();
|
| 42 |
-
window.URL.revokeObjectURL(url);
|
| 43 |
-
document.body.removeChild(a);
|
| 44 |
-
}});">
|
| 45 |
-
⬇️ {label}
|
| 46 |
-
</a>
|
| 47 |
-
"""
|
| 48 |
import math
|
| 49 |
from dataclasses import dataclass
|
| 50 |
|
|
@@ -65,7 +29,7 @@ def get_audio_length(audio_file):
|
|
| 65 |
|
| 66 |
def format_time_ms(milliseconds):
|
| 67 |
seconds, ms = divmod(int(milliseconds), 1000)
|
| 68 |
-
mins, secs = divmod(
|
| 69 |
hrs, mins = divmod(mins, 60)
|
| 70 |
return f"{hrs:02}:{mins:02}:{secs:02},{ms:03}"
|
| 71 |
|
|
@@ -453,52 +417,32 @@ async def generate_accurate_srt(
|
|
| 453 |
|
| 454 |
return srt_path, audio_path
|
| 455 |
|
|
|
|
| 456 |
async def process_text_with_progress(
|
| 457 |
-
text,
|
| 458 |
-
pitch,
|
| 459 |
-
rate,
|
| 460 |
-
voice,
|
| 461 |
-
words_per_line,
|
| 462 |
-
lines_per_segment,
|
| 463 |
parallel_processing,
|
| 464 |
progress=gr.Progress()
|
| 465 |
):
|
| 466 |
-
# Initialize all outputs to their "cleared" state.
|
| 467 |
-
# We now control visibility of the *groups* not individual items directly.
|
| 468 |
-
srt_file_update = gr.File(value=None) # No visible here, controlled by group
|
| 469 |
-
audio_file_update = gr.File(value=None) # No visible here, controlled by group
|
| 470 |
-
audio_output_val = None
|
| 471 |
-
error_output_update = gr.Textbox(value="", visible=False)
|
| 472 |
-
srt_download_link_html = "" # HTML content, not a gr.HTML object
|
| 473 |
-
audio_download_link_html = "" # HTML content, not a gr.HTML object
|
| 474 |
-
|
| 475 |
-
# Group visibility controls
|
| 476 |
-
file_group_visible = gr.update(visible=False)
|
| 477 |
-
download_group_visible = gr.update(visible=False)
|
| 478 |
-
|
| 479 |
# Input validation
|
| 480 |
if not text or text.strip() == "":
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
audio_file_update,
|
| 485 |
-
audio_output_val,
|
| 486 |
-
error_output_update,
|
| 487 |
-
file_group_visible, # Output for the file group visibility
|
| 488 |
-
download_group_visible, # Output for the download link group visibility
|
| 489 |
-
srt_download_link_html, # Output for srt_download (HTML string)
|
| 490 |
-
audio_download_link_html # Output for audio_download (HTML string)
|
| 491 |
-
)
|
| 492 |
-
|
| 493 |
pitch_str = f"{pitch:+d}Hz" if pitch != 0 else "+0Hz"
|
| 494 |
rate_str = f"{rate:+d}%" if rate != 0 else "+0%"
|
| 495 |
-
|
| 496 |
try:
|
|
|
|
| 497 |
progress(0, "Preparing text...")
|
| 498 |
-
|
| 499 |
def update_progress(value, status):
|
| 500 |
progress(value, status)
|
| 501 |
-
|
| 502 |
srt_path, audio_path = await generate_accurate_srt(
|
| 503 |
text,
|
| 504 |
voice_options[voice],
|
|
@@ -509,33 +453,25 @@ async def process_text_with_progress(
|
|
| 509 |
progress_callback=update_progress,
|
| 510 |
parallel=parallel_processing
|
| 511 |
)
|
| 512 |
-
|
| 513 |
-
srt_file_update = gr.File(value=srt_path)
|
| 514 |
-
audio_file_update = gr.File(value=audio_path)
|
| 515 |
-
audio_output_val = audio_path
|
| 516 |
|
| 517 |
-
|
| 518 |
-
|
|
|
|
| 519 |
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 523 |
except TTSError as e:
|
| 524 |
-
|
|
|
|
| 525 |
except Exception as e:
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
# Always return the full tuple of updated components
|
| 529 |
-
return (
|
| 530 |
-
srt_file_update,
|
| 531 |
-
audio_file_update,
|
| 532 |
-
audio_output_val,
|
| 533 |
-
error_output_update,
|
| 534 |
-
file_group_visible,
|
| 535 |
-
download_group_visible,
|
| 536 |
-
srt_download_link_html,
|
| 537 |
-
audio_download_link_html
|
| 538 |
-
)
|
| 539 |
|
| 540 |
|
| 541 |
# Voice options dictionary
|
|
@@ -643,28 +579,18 @@ with gr.Blocks(title="Advanced TTS with Configurable SRT Generation") as app:
|
|
| 643 |
|
| 644 |
submit_btn = gr.Button("Generate Audio & Subtitles")
|
| 645 |
|
| 646 |
-
#
|
| 647 |
-
error_output = gr.Textbox(label="Status",
|
| 648 |
|
| 649 |
with gr.Row():
|
| 650 |
with gr.Column():
|
| 651 |
audio_output = gr.Audio(label="Preview Audio")
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
file_download_group.visible = False # Control group visibility
|
| 659 |
-
|
| 660 |
-
# --- NEW: Group for gr.HTML download links ---
|
| 661 |
-
with gr.Column() as html_download_group:
|
| 662 |
-
# gr.HTML components initialized with empty string
|
| 663 |
-
srt_download = gr.HTML(value="")
|
| 664 |
-
audio_download = gr.HTML(value="")
|
| 665 |
-
html_download_group.visible = False # Control group visibility
|
| 666 |
-
|
| 667 |
-
# Handle button click
|
| 668 |
submit_btn.click(
|
| 669 |
fn=process_text_with_progress,
|
| 670 |
inputs=[
|
|
@@ -677,14 +603,11 @@ with gr.Blocks(title="Advanced TTS with Configurable SRT Generation") as app:
|
|
| 677 |
parallel_processing
|
| 678 |
],
|
| 679 |
outputs=[
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
error_output,
|
| 684 |
-
|
| 685 |
-
html_download_group,# Control visibility of this group
|
| 686 |
-
srt_download, # gr.HTML (will receive the HTML string directly)
|
| 687 |
-
audio_download # gr.HTML (will receive the HTML string directly)
|
| 688 |
],
|
| 689 |
api_name="generate"
|
| 690 |
)
|
|
|
|
| 9 |
import tempfile
|
| 10 |
from concurrent.futures import ThreadPoolExecutor
|
| 11 |
from typing import List, Tuple, Optional, Dict, Any
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
import math
|
| 13 |
from dataclasses import dataclass
|
| 14 |
|
|
|
|
| 29 |
|
| 30 |
def format_time_ms(milliseconds):
|
| 31 |
seconds, ms = divmod(int(milliseconds), 1000)
|
| 32 |
+
mins, secs = divmod(seconds, 60)
|
| 33 |
hrs, mins = divmod(mins, 60)
|
| 34 |
return f"{hrs:02}:{mins:02}:{secs:02},{ms:03}"
|
| 35 |
|
|
|
|
| 417 |
|
| 418 |
return srt_path, audio_path
|
| 419 |
|
| 420 |
+
# IMPROVEMENT 4: Progress Reporting with proper error handling for older Gradio versions
|
| 421 |
async def process_text_with_progress(
|
| 422 |
+
text,
|
| 423 |
+
pitch,
|
| 424 |
+
rate,
|
| 425 |
+
voice,
|
| 426 |
+
words_per_line,
|
| 427 |
+
lines_per_segment,
|
| 428 |
parallel_processing,
|
| 429 |
progress=gr.Progress()
|
| 430 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 431 |
# Input validation
|
| 432 |
if not text or text.strip() == "":
|
| 433 |
+
return None, None, None, gr.update(value="", visible=True), gr.update(value="", visible=False), "Please enter some text to convert to speech."
|
| 434 |
+
|
| 435 |
+
# Format pitch and rate strings
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 436 |
pitch_str = f"{pitch:+d}Hz" if pitch != 0 else "+0Hz"
|
| 437 |
rate_str = f"{rate:+d}%" if rate != 0 else "+0%"
|
| 438 |
+
|
| 439 |
try:
|
| 440 |
+
# Start progress tracking
|
| 441 |
progress(0, "Preparing text...")
|
| 442 |
+
|
| 443 |
def update_progress(value, status):
|
| 444 |
progress(value, status)
|
| 445 |
+
|
| 446 |
srt_path, audio_path = await generate_accurate_srt(
|
| 447 |
text,
|
| 448 |
voice_options[voice],
|
|
|
|
| 453 |
progress_callback=update_progress,
|
| 454 |
parallel=parallel_processing
|
| 455 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 456 |
|
| 457 |
+
# Generate Markdown links for download that open in a new tab
|
| 458 |
+
srt_download_link = f'<a href="file={srt_path}" download="subtitles.srt" target="_blank">Download SRT</a>'
|
| 459 |
+
audio_download_link = f'<a href="file={audio_path}" download="audio.mp3" target="_blank">Download Audio</a>'
|
| 460 |
|
| 461 |
+
# Return the paths for gr.Audio and Markdown for download links
|
| 462 |
+
return (
|
| 463 |
+
audio_path,
|
| 464 |
+
gr.update(value=srt_download_link, visible=True), # Use gr.Markdown for SRT download
|
| 465 |
+
gr.update(value=audio_download_link, visible=True), # Use gr.Markdown for Audio download
|
| 466 |
+
gr.update(value="", visible=False), # Hide error message
|
| 467 |
+
"" # Clear error message
|
| 468 |
+
)
|
| 469 |
except TTSError as e:
|
| 470 |
+
# Return specific TTS error
|
| 471 |
+
return None, gr.update(value="", visible=False), gr.update(value="", visible=False), gr.update(value=f"TTS Error: {str(e)}", visible=True), f"TTS Error: {str(e)}"
|
| 472 |
except Exception as e:
|
| 473 |
+
# Return any other error
|
| 474 |
+
return None, gr.update(value="", visible=False), gr.update(value="", visible=False), gr.update(value=f"Unexpected error: {str(e)}", visible=True), f"Unexpected error: {str(e)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
|
| 476 |
|
| 477 |
# Voice options dictionary
|
|
|
|
| 579 |
|
| 580 |
submit_btn = gr.Button("Generate Audio & Subtitles")
|
| 581 |
|
| 582 |
+
# Add error message component
|
| 583 |
+
error_output = gr.Textbox(label="Status", visible=False)
|
| 584 |
|
| 585 |
with gr.Row():
|
| 586 |
with gr.Column():
|
| 587 |
audio_output = gr.Audio(label="Preview Audio")
|
| 588 |
+
with gr.Column():
|
| 589 |
+
# Change gr.File to gr.Markdown for download links
|
| 590 |
+
srt_download_link = gr.Markdown(value="", visible=False, label="Download SRT")
|
| 591 |
+
audio_download_link = gr.Markdown(value="", visible=False, label="Download Audio")
|
| 592 |
+
|
| 593 |
+
# Handle button click with manual error handling instead of .catch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 594 |
submit_btn.click(
|
| 595 |
fn=process_text_with_progress,
|
| 596 |
inputs=[
|
|
|
|
| 603 |
parallel_processing
|
| 604 |
],
|
| 605 |
outputs=[
|
| 606 |
+
audio_output,
|
| 607 |
+
srt_download_link, # Output to Markdown component
|
| 608 |
+
audio_download_link, # Output to Markdown component
|
| 609 |
+
error_output,
|
| 610 |
+
error_output
|
|
|
|
|
|
|
|
|
|
| 611 |
],
|
| 612 |
api_name="generate"
|
| 613 |
)
|