Spaces:
Sleeping
Sleeping
| <html lang="en" dir="ltr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Text To Speech</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700&display=swap" rel="stylesheet"> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'Tajawal', sans-serif; | |
| background: linear-gradient(135deg, #000000 0%, #0d0a2e 50%, #1a0a3e 100%); | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 20px; | |
| } | |
| .container { | |
| background: #1a1a2e; | |
| border-radius: 20px; | |
| padding: 40px; | |
| width: 100%; | |
| max-width: 700px; | |
| border: 1px solid #2a2a4a; | |
| box-shadow: 0 20px 60px rgba(0,0,0,0.5); | |
| } | |
| h1 { | |
| color: #7c6bff; | |
| font-size: 28px; | |
| margin-bottom: 8px; | |
| text-align: center; | |
| } | |
| .subtitle { | |
| color: #888; | |
| text-align: center; | |
| margin-bottom: 30px; | |
| font-size: 14px; | |
| } | |
| textarea { | |
| width: 100%; | |
| height: 180px; | |
| background: #0f0f1a; | |
| border: 1px solid #2a2a4a; | |
| border-radius: 12px; | |
| padding: 16px; | |
| color: #fff; | |
| font-family: 'Tajawal', sans-serif; | |
| font-size: 15px; | |
| resize: vertical; | |
| outline: none; | |
| transition: border-color 0.3s; | |
| direction: ltr; | |
| text-align: left; | |
| } | |
| textarea:focus { border-color: #7c6bff; } | |
| .controls { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr 1fr; | |
| gap: 16px; | |
| margin: 20px 0; | |
| direction: ltr; | |
| } | |
| .control-group label { | |
| display: block; | |
| color: #888; | |
| font-size: 13px; | |
| margin-bottom: 8px; | |
| text-align: left; | |
| } | |
| select { | |
| width: 100%; | |
| background: #0f0f1a; | |
| border: 1px solid #2a2a4a; | |
| border-radius: 10px; | |
| padding: 10px 14px; | |
| color: #fff; | |
| font-family: 'Tajawal', sans-serif; | |
| font-size: 14px; | |
| outline: none; | |
| cursor: pointer; | |
| transition: border-color 0.3s; | |
| direction: ltr; | |
| text-align: left; | |
| } | |
| select:focus { border-color: #7c6bff; } | |
| button { | |
| width: 100%; | |
| background: linear-gradient(135deg, #7c6bff, #5b4de8); | |
| color: white; | |
| border: none; | |
| border-radius: 12px; | |
| padding: 16px; | |
| font-family: 'Tajawal', sans-serif; | |
| font-size: 17px; | |
| font-weight: 700; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| margin-top: 10px; | |
| } | |
| button:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(74,158,255,0.4); } | |
| button:disabled { opacity: 0.5; cursor: not-allowed; transform: none; } | |
| .status { | |
| text-align: center; | |
| margin-top: 16px; | |
| padding: 12px; | |
| border-radius: 10px; | |
| font-size: 14px; | |
| display: none; | |
| } | |
| .status.loading { background: #1e1e3a; color: #4a9eff; display: block; } | |
| .status.success { background: #0d2a1a; color: #4caf50; display: block; } | |
| .status.error { background: #2a0d0d; color: #f44336; display: block; } | |
| audio { | |
| width: 100%; | |
| margin-top: 16px; | |
| display: none; | |
| border-radius: 10px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>Text To Speech</h1> | |
| <textarea id="text" placeholder="Enter the text"></textarea> | |
| <div class="controls"> | |
| <div class="control-group"> | |
| <label>Language</label> | |
| <select id="language"> | |
| <option value="ar">Arabic</option> | |
| <option value="en" selected>English (US)</option> | |
| <option value="en-gb">English (UK)</option> | |
| <option value="fr">Français</option> | |
| <option value="de">German</option> | |
| <option value="es">Español</option> | |
| <option value="it">Italiano</option> | |
| <option value="tr">Türkçe</option> | |
| <option value="ru">Russian</option> | |
| <option value="hi">India</option> | |
| </select> | |
| </div> | |
| <div class="control-group"> | |
| <label>Gender</label> | |
| <select id="gender"> | |
| <option value="female">Female</option> | |
| <option value="male">Male</option> | |
| </select> | |
| </div> | |
| <div class="control-group"> | |
| <label>Speed</label> | |
| <select id="rate"> | |
| <option value="-50%">Very slow</option> | |
| <option value="-25%">Slow</option> | |
| <option value="+0%" selected>Normal</option> | |
| <option value="+25%">Fast</option> | |
| <option value="+50%">Very fast</option> | |
| </select> | |
| </div> | |
| </div> | |
| <button id="btn" onclick="generateSpeech()">Sound generation</button> | |
| <div class="status" id="status"></div> | |
| <audio id="audio" controls></audio> | |
| </div> | |
| <script> | |
| async function generateSpeech() { | |
| const text = document.getElementById('text').value.trim(); | |
| const language = document.getElementById('language').value; | |
| const gender = document.getElementById('gender').value; | |
| const rate = document.getElementById('rate').value; | |
| const btn = document.getElementById('btn'); | |
| const status = document.getElementById('status'); | |
| const audio = document.getElementById('audio'); | |
| if (!text) { | |
| status.className = 'status error'; | |
| status.textContent = 'Please enter text'; | |
| return; | |
| } | |
| btn.disabled = true; | |
| status.className = 'status loading'; | |
| status.textContent = 'Sound is being generated...'; | |
| audio.style.display = 'none'; | |
| try { | |
| const response = await fetch('https://tts-api-edge-tts.hf.space/tts', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ text, language, gender, rate }) | |
| }); | |
| if (!response.ok) throw new Error('Failed to generate sound'); | |
| const blob = await response.blob(); | |
| const url = URL.createObjectURL(blob); | |
| audio.src = url; | |
| audio.style.display = 'block'; | |
| audio.play(); | |
| status.className = 'status success'; | |
| status.textContent = 'Sound was successfully generated!'; | |
| } catch (err) { | |
| status.className = 'status error'; | |
| status.textContent = 'An error occurred: ' + err.message; | |
| } finally { | |
| btn.disabled = false; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |