Spaces:
Running
Running
Update index.html
Browse files- index.html +97 -19
index.html
CHANGED
|
@@ -8,19 +8,24 @@
|
|
| 8 |
body { font-family: Arial, sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; background-color: #f4f4f9; }
|
| 9 |
.container { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
|
| 10 |
h2 { text-align: center; color: #333; }
|
| 11 |
-
label { font-weight: bold; display: block; margin-top:
|
| 12 |
input, textarea, select { width: 100%; padding: 10px; margin-top: 5px; border: 1px solid #ccc; border-radius: 5px; box-sizing: border-box; }
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
#audioContainer { margin-top: 20px; text-align: center; display: none; padding: 15px; background: #e9ecef; border-radius: 8px; }
|
| 18 |
|
| 19 |
-
/* Status Box Styling */
|
| 20 |
.status-box { text-align: center; margin-top: 15px; padding: 10px; border-radius: 5px; font-weight: bold; display: none; transition: 0.3s; }
|
| 21 |
.status-loading { background-color: #fff3cd; color: #856404; display: block; }
|
| 22 |
.status-success { background-color: #d4edda; color: #155724; display: block; }
|
| 23 |
.status-error { background-color: #f8d7da; color: #721c24; display: block; }
|
|
|
|
|
|
|
| 24 |
</style>
|
| 25 |
</head>
|
| 26 |
<body>
|
|
@@ -30,9 +35,18 @@
|
|
| 30 |
|
| 31 |
<label for="apiKey">ElevenLabs API Key:</label>
|
| 32 |
<input type="password" id="apiKey" placeholder="Apni API key yahan daalein..." required>
|
|
|
|
| 33 |
|
| 34 |
-
<label for="
|
| 35 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
<label for="modelSelect">Select Model:</label>
|
| 38 |
<select id="modelSelect">
|
|
@@ -44,7 +58,7 @@
|
|
| 44 |
<label for="text">Text to Speech:</label>
|
| 45 |
<textarea id="text" rows="5" placeholder="Jo bulwana hai yahan type karein..." required></textarea>
|
| 46 |
|
| 47 |
-
<button onclick="generateAudio()" id="generateBtn">Generate Audio</button>
|
| 48 |
|
| 49 |
<div id="statusBox" class="status-box"></div>
|
| 50 |
|
|
@@ -56,27 +70,93 @@
|
|
| 56 |
</div>
|
| 57 |
|
| 58 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
async function generateAudio() {
|
| 60 |
const apiKey = document.getElementById('apiKey').value.trim();
|
| 61 |
-
const voiceId = document.getElementById('voiceId').value.trim();
|
| 62 |
const modelId = document.getElementById('modelSelect').value;
|
| 63 |
const text = document.getElementById('text').value.trim();
|
|
|
|
| 64 |
|
| 65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
const audioContainer = document.getElementById('audioContainer');
|
| 67 |
const audioPlayer = document.getElementById('audioPlayer');
|
| 68 |
const downloadLink = document.getElementById('downloadLink');
|
| 69 |
const generateBtn = document.getElementById('generateBtn');
|
| 70 |
|
| 71 |
-
// Validation
|
| 72 |
if (!apiKey || !text || !voiceId) {
|
| 73 |
-
showStatus('
|
| 74 |
return;
|
| 75 |
}
|
| 76 |
|
| 77 |
-
// Reset UI for new request
|
| 78 |
audioContainer.style.display = "none";
|
| 79 |
-
showStatus('Audio
|
| 80 |
generateBtn.disabled = true;
|
| 81 |
generateBtn.innerText = "Processing...";
|
| 82 |
|
|
@@ -100,18 +180,17 @@
|
|
| 100 |
|
| 101 |
if (!response.ok) {
|
| 102 |
const errData = await response.json();
|
| 103 |
-
throw new Error(errData.detail.message || "API request
|
| 104 |
}
|
| 105 |
|
| 106 |
const blob = await response.blob();
|
| 107 |
const audioUrl = URL.createObjectURL(blob);
|
| 108 |
|
| 109 |
-
// Set audio URL to player and download link
|
| 110 |
audioPlayer.src = audioUrl;
|
| 111 |
downloadLink.href = audioUrl;
|
| 112 |
audioContainer.style.display = "block";
|
| 113 |
|
| 114 |
-
showStatus('Audio
|
| 115 |
|
| 116 |
} catch (error) {
|
| 117 |
showStatus('Error: ' + error.message, 'error');
|
|
@@ -121,11 +200,10 @@
|
|
| 121 |
}
|
| 122 |
}
|
| 123 |
|
| 124 |
-
// Status dikhane ka function
|
| 125 |
function showStatus(message, type) {
|
| 126 |
const statusBox = document.getElementById('statusBox');
|
| 127 |
statusBox.innerText = message;
|
| 128 |
-
statusBox.className = 'status-box';
|
| 129 |
|
| 130 |
if(type === 'loading') statusBox.classList.add('status-loading');
|
| 131 |
if(type === 'success') statusBox.classList.add('status-success');
|
|
|
|
| 8 |
body { font-family: Arial, sans-serif; max-width: 600px; margin: 40px auto; padding: 20px; background-color: #f4f4f9; }
|
| 9 |
.container { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
|
| 10 |
h2 { text-align: center; color: #333; }
|
| 11 |
+
label { font-weight: bold; display: block; margin-top: 15px; }
|
| 12 |
input, textarea, select { width: 100%; padding: 10px; margin-top: 5px; border: 1px solid #ccc; border-radius: 5px; box-sizing: border-box; }
|
| 13 |
+
|
| 14 |
+
.btn { width: 100%; color: white; padding: 12px; border: none; border-radius: 5px; margin-top: 15px; cursor: pointer; font-size: 16px; font-weight: bold; transition: 0.3s; }
|
| 15 |
+
.btn-blue { background-color: #007bff; }
|
| 16 |
+
.btn-blue:hover { background-color: #0056b3; }
|
| 17 |
+
.btn-green { background-color: #28a745; }
|
| 18 |
+
.btn-green:hover:not(:disabled) { background-color: #218838; }
|
| 19 |
+
.btn:disabled { background-color: #6c757d; cursor: not-allowed; }
|
| 20 |
|
| 21 |
#audioContainer { margin-top: 20px; text-align: center; display: none; padding: 15px; background: #e9ecef; border-radius: 8px; }
|
| 22 |
|
|
|
|
| 23 |
.status-box { text-align: center; margin-top: 15px; padding: 10px; border-radius: 5px; font-weight: bold; display: none; transition: 0.3s; }
|
| 24 |
.status-loading { background-color: #fff3cd; color: #856404; display: block; }
|
| 25 |
.status-success { background-color: #d4edda; color: #155724; display: block; }
|
| 26 |
.status-error { background-color: #f8d7da; color: #721c24; display: block; }
|
| 27 |
+
|
| 28 |
+
#customVoiceDiv { display: none; margin-top: 10px; padding: 10px; background: #fff8e1; border-left: 4px solid #ffc107; border-radius: 4px; }
|
| 29 |
</style>
|
| 30 |
</head>
|
| 31 |
<body>
|
|
|
|
| 35 |
|
| 36 |
<label for="apiKey">ElevenLabs API Key:</label>
|
| 37 |
<input type="password" id="apiKey" placeholder="Apni API key yahan daalein..." required>
|
| 38 |
+
<button class="btn btn-blue" onclick="connectAPI()" id="connectBtn">Connect API & Load Voices</button>
|
| 39 |
|
| 40 |
+
<label for="voiceDropdown">Select Voice:</label>
|
| 41 |
+
<select id="voiceDropdown" onchange="toggleCustomVoice()">
|
| 42 |
+
<option value="">-- Pehle API Connect Karein --</option>
|
| 43 |
+
<option value="custom">✏️ Enter Custom Voice ID</option>
|
| 44 |
+
</select>
|
| 45 |
+
|
| 46 |
+
<div id="customVoiceDiv">
|
| 47 |
+
<label for="customVoiceId">Custom Voice ID:</label>
|
| 48 |
+
<input type="text" id="customVoiceId" placeholder="Voice ID paste karein (e.g., EXAVITQu4vr4xnSDxMaL)">
|
| 49 |
+
</div>
|
| 50 |
|
| 51 |
<label for="modelSelect">Select Model:</label>
|
| 52 |
<select id="modelSelect">
|
|
|
|
| 58 |
<label for="text">Text to Speech:</label>
|
| 59 |
<textarea id="text" rows="5" placeholder="Jo bulwana hai yahan type karein..." required></textarea>
|
| 60 |
|
| 61 |
+
<button class="btn btn-green" onclick="generateAudio()" id="generateBtn">Generate Audio</button>
|
| 62 |
|
| 63 |
<div id="statusBox" class="status-box"></div>
|
| 64 |
|
|
|
|
| 70 |
</div>
|
| 71 |
|
| 72 |
<script>
|
| 73 |
+
// API Connect karke Voices load karna
|
| 74 |
+
async function connectAPI() {
|
| 75 |
+
const apiKey = document.getElementById('apiKey').value.trim();
|
| 76 |
+
const connectBtn = document.getElementById('connectBtn');
|
| 77 |
+
const voiceDropdown = document.getElementById('voiceDropdown');
|
| 78 |
+
|
| 79 |
+
if (!apiKey) {
|
| 80 |
+
showStatus('कृपया पहले API Key डालें!', 'error');
|
| 81 |
+
return;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
showStatus('Voices लोड हो रही हैं... ⏳', 'loading');
|
| 85 |
+
connectBtn.disabled = true;
|
| 86 |
+
|
| 87 |
+
try {
|
| 88 |
+
const response = await fetch('https://api.elevenlabs.io/v1/voices', {
|
| 89 |
+
method: 'GET',
|
| 90 |
+
headers: { 'xi-api-key': apiKey }
|
| 91 |
+
});
|
| 92 |
+
|
| 93 |
+
if (!response.ok) {
|
| 94 |
+
throw new Error("API Key गलत है या नेटवर्क समस्या है!");
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
const data = await response.json();
|
| 98 |
+
|
| 99 |
+
// Dropdown clear karke naye options daalna
|
| 100 |
+
voiceDropdown.innerHTML = '<option value="custom">✏️ Enter Custom Voice ID</option>';
|
| 101 |
+
|
| 102 |
+
data.voices.forEach(voice => {
|
| 103 |
+
const option = document.createElement('option');
|
| 104 |
+
option.value = voice.voice_id;
|
| 105 |
+
option.textContent = voice.name;
|
| 106 |
+
voiceDropdown.appendChild(option);
|
| 107 |
+
});
|
| 108 |
+
|
| 109 |
+
// Default pehli voice select karna agar available ho
|
| 110 |
+
if (data.voices.length > 0) {
|
| 111 |
+
voiceDropdown.value = data.voices[0].voice_id;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
toggleCustomVoice(); // Custom box ko hide karne ke liye
|
| 115 |
+
showStatus('API Connected! ' + data.voices.length + ' Voices लोड हो गईं ✅', 'success');
|
| 116 |
+
|
| 117 |
+
} catch (error) {
|
| 118 |
+
showStatus('Error: ' + error.message, 'error');
|
| 119 |
+
} finally {
|
| 120 |
+
connectBtn.disabled = false;
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
// Custom Voice ID box ko dikhana ya chhupana
|
| 125 |
+
function toggleCustomVoice() {
|
| 126 |
+
const dropdown = document.getElementById('voiceDropdown');
|
| 127 |
+
const customDiv = document.getElementById('customVoiceDiv');
|
| 128 |
+
|
| 129 |
+
if (dropdown.value === 'custom') {
|
| 130 |
+
customDiv.style.display = 'block';
|
| 131 |
+
} else {
|
| 132 |
+
customDiv.style.display = 'none';
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
// Audio Generate karna
|
| 137 |
async function generateAudio() {
|
| 138 |
const apiKey = document.getElementById('apiKey').value.trim();
|
|
|
|
| 139 |
const modelId = document.getElementById('modelSelect').value;
|
| 140 |
const text = document.getElementById('text').value.trim();
|
| 141 |
+
const dropdown = document.getElementById('voiceDropdown');
|
| 142 |
|
| 143 |
+
let voiceId = dropdown.value;
|
| 144 |
+
if (voiceId === 'custom') {
|
| 145 |
+
voiceId = document.getElementById('customVoiceId').value.trim();
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
const audioContainer = document.getElementById('audioContainer');
|
| 149 |
const audioPlayer = document.getElementById('audioPlayer');
|
| 150 |
const downloadLink = document.getElementById('downloadLink');
|
| 151 |
const generateBtn = document.getElementById('generateBtn');
|
| 152 |
|
|
|
|
| 153 |
if (!apiKey || !text || !voiceId) {
|
| 154 |
+
showStatus('कृपया API Key, Voice ID और Text भरें!', 'error');
|
| 155 |
return;
|
| 156 |
}
|
| 157 |
|
|
|
|
| 158 |
audioContainer.style.display = "none";
|
| 159 |
+
showStatus('Audio जनरेट हो रहा है, कृपया प्रतीक्षा करें... ⏳', 'loading');
|
| 160 |
generateBtn.disabled = true;
|
| 161 |
generateBtn.innerText = "Processing...";
|
| 162 |
|
|
|
|
| 180 |
|
| 181 |
if (!response.ok) {
|
| 182 |
const errData = await response.json();
|
| 183 |
+
throw new Error(errData.detail.message || "API request फेल हो गई!");
|
| 184 |
}
|
| 185 |
|
| 186 |
const blob = await response.blob();
|
| 187 |
const audioUrl = URL.createObjectURL(blob);
|
| 188 |
|
|
|
|
| 189 |
audioPlayer.src = audioUrl;
|
| 190 |
downloadLink.href = audioUrl;
|
| 191 |
audioContainer.style.display = "block";
|
| 192 |
|
| 193 |
+
showStatus('Audio तैयार है! 🎉', 'success');
|
| 194 |
|
| 195 |
} catch (error) {
|
| 196 |
showStatus('Error: ' + error.message, 'error');
|
|
|
|
| 200 |
}
|
| 201 |
}
|
| 202 |
|
|
|
|
| 203 |
function showStatus(message, type) {
|
| 204 |
const statusBox = document.getElementById('statusBox');
|
| 205 |
statusBox.innerText = message;
|
| 206 |
+
statusBox.className = 'status-box';
|
| 207 |
|
| 208 |
if(type === 'loading') statusBox.classList.add('status-loading');
|
| 209 |
if(type === 'success') statusBox.classList.add('status-success');
|