File size: 8,325 Bytes
e129c85 8d2af7a e129c85 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
import gradio as gr
import requests
import json
import os
import time
from collections import defaultdict
BASE_URL = "https://api.jigsawstack.com/v1"
headers = {
"x-api-key": os.getenv("JIGSAWSTACK_API_KEY")
}
# Rate limiting configuration
request_times = defaultdict(list)
MAX_REQUESTS = 20 # Maximum requests per time window
TIME_WINDOW = 3600 # Time window in seconds (1 hour)
def get_real_ip(request: gr.Request):
"""Extract real IP address using x-forwarded-for header or fallback"""
if not request:
return "unknown"
forwarded = request.headers.get("x-forwarded-for")
if forwarded:
ip = forwarded.split(",")[0].strip() # First IP in the list is the client's
else:
ip = request.client.host # fallback
return ip
def check_rate_limit(request: gr.Request):
"""Check if the current request exceeds rate limits"""
if not request:
return True, "Rate limit check failed - no request info"
ip = get_real_ip(request)
now = time.time()
# Clean up old timestamps outside the time window
request_times[ip] = [t for t in request_times[ip] if now - t < TIME_WINDOW]
# Check if rate limit exceeded
if len(request_times[ip]) >= MAX_REQUESTS:
time_remaining = int(TIME_WINDOW - (now - request_times[ip][0]))
time_remaining_minutes = round(time_remaining / 60, 1)
time_window_minutes = round(TIME_WINDOW / 60, 1)
return False, f"Rate limit exceeded. You can make {MAX_REQUESTS} requests per {time_window_minutes} minutes. Try again in {time_remaining_minutes} minutes."
# Add current request timestamp
request_times[ip].append(now)
return True, ""
def text_to_speech(text, accent, voice_clone_id, request: gr.Request):
rate_limit_ok, rate_limit_msg = check_rate_limit(request)
if not rate_limit_ok:
return None, rate_limit_msg
if not text or not text.strip():
return None, "Error: Text input is required."
payload = {"text": text.strip()}
if accent and accent.strip():
payload["accent"] = accent.strip()
if voice_clone_id and voice_clone_id.strip():
payload["voice_clone_id"] = voice_clone_id.strip()
# Validate text length
max_len = 500 if "voice_clone_id" in payload else 1500
if len(payload["text"]) > max_len:
mode = "Voice Cloning" if "voice_clone_id" in payload else "Standard"
return None, f"Error: Text for {mode} is limited to {max_len} characters."
try:
response = requests.post(f"{BASE_URL}/ai/tts", headers=headers, json=payload)
response.raise_for_status()
if response.headers.get("content-type", "").startswith("audio/"):
import tempfile
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
tmp_file.write(response.content)
return tmp_file.name, "β
Speech generated successfully!"
else:
try:
error_data = response.json()
return None, f"Error: API returned an error - {error_data.get('message', 'Unknown error')}"
except:
return None, "Error: Received an unexpected response from the API."
except requests.exceptions.RequestException as e:
return None, f"Request failed: {str(e)}"
except Exception as e:
return None, f"An unexpected error occurred: {str(e)}"
# ----------------- Gradio UI ------------------
with gr.Blocks() as demo:
gr.Markdown("""
<div style='text-align: center; margin-bottom: 24px;'>
<h1 style='font-size:2.2em; margin-bottom: 0.2em;'>π§© Text to Speech</h1>
<p style='font-size:1.2em; margin-top: 0;'>Transform text into natural-sounding human-like AI voices with low latency and exceptional quality.</p>
<p style='font-size:1em; margin-top: 0.5em;'>For more details and API usage, see the <a href='https://jigsawstack.com/docs/api-reference/audio/tts/text-to-speech' target='_blank'>documentation</a>.</p>
</div>
""")
with gr.Row():
with gr.Column():
gr.Markdown("#### Input")
tts_text = gr.Textbox(
label="Text to Convert",
lines=5,
placeholder="Enter the text you want to convert to speech here."
)
gr.Markdown("#### Quick Examples")
gr.Markdown("Click any example below to auto-fill the text:")
with gr.Row():
tts_example_btn1 = gr.Button("π Greeting", size="sm")
tts_example_btn2 = gr.Button("π’ Announcement", size="sm")
tts_example_btn3 = gr.Button("π Story", size="sm")
with gr.Row():
tts_example_btn4 = gr.Button("π΅ Song Lyrics", size="sm")
tts_example_btn5 = gr.Button("π Poem", size="sm")
tts_example_btn6 = gr.Button("πΌ Business", size="sm")
with gr.Row():
tts_example_btn7 = gr.Button("π Drama", size="sm")
tts_example_btn8 = gr.Button("π Notification", size="sm")
accent = gr.Textbox(
label="Voice Accent (Optional)",
placeholder="e.g., en-US-female-27, en-GB-male-2",
info="Default voice is used if left blank. Ignored if Voice Clone ID is provided."
)
voice_clone_id = gr.Textbox(
label="Voice Clone ID (Optional)",
placeholder="Enter a voice clone ID to use a custom voice.",
info="Overrides the accent if provided."
)
with gr.Column():
gr.Markdown("#### Generated Audio")
audio_output = gr.Audio(label="Speech Output")
tts_status = gr.Textbox(label="Status", interactive=False, placeholder="Ready to generate speech...")
tts_btn = gr.Button("Generate Speech", variant="primary")
# Example functions to auto-fill text field
def fill_tts_example_1():
return "Hello! Welcome to our AI-powered text-to-speech system. This technology can convert any written text into natural-sounding speech."
def fill_tts_example_2():
return "Attention all passengers. Flight 1234 to New York is now boarding at gate 15. Please have your boarding pass ready."
def fill_tts_example_3():
return "Once upon a time, in a magical forest, there lived a wise old owl who loved to share stories with all the woodland creatures."
def fill_tts_example_4():
return "Twinkle twinkle little star, how I wonder what you are. Up above the world so high, like a diamond in the sky."
def fill_tts_example_5():
return "The road not taken by Robert Frost. Two roads diverged in a yellow wood, and sorry I could not travel both."
def fill_tts_example_6():
return "Thank you for your business. Your order has been confirmed and will be shipped within 2-3 business days."
def fill_tts_example_7():
return "To be or not to be, that is the question. Whether tis nobler in the mind to suffer the slings and arrows of outrageous fortune."
def fill_tts_example_8():
return "You have a new message. Please check your inbox for important updates regarding your account."
# Connect example buttons to auto-fill functions
tts_example_btn1.click(fill_tts_example_1, outputs=[tts_text])
tts_example_btn2.click(fill_tts_example_2, outputs=[tts_text])
tts_example_btn3.click(fill_tts_example_3, outputs=[tts_text])
tts_example_btn4.click(fill_tts_example_4, outputs=[tts_text])
tts_example_btn5.click(fill_tts_example_5, outputs=[tts_text])
tts_example_btn6.click(fill_tts_example_6, outputs=[tts_text])
tts_example_btn7.click(fill_tts_example_7, outputs=[tts_text])
tts_example_btn8.click(fill_tts_example_8, outputs=[tts_text])
tts_btn.click(
text_to_speech,
inputs=[tts_text, accent, voice_clone_id],
outputs=[audio_output, tts_status]
)
demo.launch()
|