Download app.html from Offex/GMT: direct link, hf CLI and curl.
- Browser
- Download file 11.7 kB
-
https://huggingface.co/Offex/GMT/resolve/main/app.html
- Command line
-
hf download hf://Offex/GMT/app.html
-
curl -L -o app.html https://huggingface.co/Offex/GMT/resolve/main/app.html
11.7 kB
| ```html | |
| <html lang="hi"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Cartesia AI TTS Tool</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;600;700&display=swap'); | |
| body { | |
| font-family: 'Plus Jakarta Sans', sans-serif; | |
| background-color: #050505; | |
| color: #ffffff; | |
| } | |
| .cartesia-card { | |
| background: rgba(20, 20, 20, 0.8); | |
| border: 1px solid #333; | |
| backdrop-filter: blur(10px); | |
| } | |
| .status-pill { | |
| padding: 2px 8px; | |
| border-radius: 99px; | |
| font-size: 0.7rem; | |
| font-weight: bold; | |
| text-transform: uppercase; | |
| } | |
| .pulse-red { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); animation: pulse-red 2s infinite; } | |
| @keyframes pulse-red { 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); } 70% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); } 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } } | |
| </style> | |
| </head> | |
| <body class="p-4 md:p-10 min-h-screen"> | |
| <div class="max-w-5xl mx-auto"> | |
| <!-- Header Section --> | |
| <div class="flex flex-col md:flex-row justify-between items-center mb-10 gap-4"> | |
| <div> | |
| <h1 class="text-4xl font-extrabold tracking-tight text-white">Cartesia <span class="text-blue-500">AI</span></h1> | |
| <p class="text-gray-400">High-performance text-to-speech engine</p> | |
| </div> | |
| <div class="flex items-center gap-3 cartesia-card p-2 px-4 rounded-full"> | |
| <span id="statusDot" class="h-3 w-3 rounded-full bg-red-500 pulse-red"></span> | |
| <span id="statusText" class="text-sm font-medium text-gray-300 uppercase tracking-widest">Disconnected</span> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 lg:grid-cols-12 gap-8"> | |
| <!-- Left Column: Settings --> | |
| <div class="lg:col-span-4 space-y-6"> | |
| <div class="cartesia-card p-6 rounded-3xl"> | |
| <h2 class="text-lg font-bold mb-4 flex items-center gap-2"> | |
| <span>🔑</span> API Settings | |
| </h2> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-xs font-bold text-gray-500 mb-1">API KEY</label> | |
| <input type="password" id="apiKey" placeholder="sk_car_..." | |
| class="w-full bg-black border border-gray-800 rounded-xl p-3 text-sm focus:border-blue-500 outline-none transition-all"> | |
| </div> | |
| <button id="connectBtn" class="w-full bg-white text-black font-bold py-3 rounded-xl hover:bg-gray-200 transition-colors"> | |
| Connect Account | |
| </button> | |
| </div> | |
| </div> | |
| <div class="cartesia-card p-6 rounded-3xl"> | |
| <h2 class="text-lg font-bold mb-4 flex items-center gap-2"> | |
| <span>🎙️</span> Voice Engine | |
| </h2> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-xs font-bold text-gray-500 mb-1">CHOOSE VOICE</label> | |
| <select id="voiceSelect" class="w-full bg-black border border-gray-800 rounded-xl p-3 text-sm outline-none focus:border-blue-500"> | |
| <option value="">Select a voice...</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-xs font-bold text-gray-500 mb-1">CUSTOM VOICE ID</label> | |
| <input type="text" id="customVoiceId" placeholder="Paste Voice ID here..." | |
| class="w-full bg-black border border-gray-800 rounded-xl p-3 text-sm outline-none focus:border-blue-500"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Right Column: Workspace --> | |
| <div class="lg:col-span-8 space-y-6"> | |
| <!-- Message Output Box --> | |
| <div id="logBox" class="hidden cartesia-card p-4 rounded-2xl text-sm border-l-4"> | |
| <p id="logMessage"></p> | |
| </div> | |
| <div class="cartesia-card p-8 rounded-3xl"> | |
| <label class="block text-xs font-bold text-gray-500 mb-2 uppercase tracking-widest text-center">Your Script</label> | |
| <textarea id="textInput" rows="6" placeholder="Yahan apna text likhen..." | |
| class="w-full bg-transparent border-b border-gray-800 text-2xl text-center focus:border-blue-500 outline-none transition-all resize-none mb-8"></textarea> | |
| <div class="flex flex-col items-center gap-6"> | |
| <button id="generateBtn" class="bg-blue-600 hover:bg-blue-500 text-white px-10 py-4 rounded-full font-bold text-lg shadow-lg shadow-blue-900/40 disabled:opacity-50 transition-all flex items-center gap-2"> | |
| <span id="btnIcon">⚡</span> <span id="btnText">Convert to Speech</span> | |
| </button> | |
| <div id="audioArea" class="w-full hidden text-center animate-fade-in"> | |
| <audio id="audioPlayer" controls class="w-full mx-auto"></audio> | |
| <p class="text-xs text-gray-500 mt-2">Niche audio play karen</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const apiKeyInput = document.getElementById('apiKey'); | |
| const connectBtn = document.getElementById('connectBtn'); | |
| const voiceSelect = document.getElementById('voiceSelect'); | |
| const customVoiceId = document.getElementById('customVoiceId'); | |
| const textInput = document.getElementById('textInput'); | |
| const generateBtn = document.getElementById('generateBtn'); | |
| const logBox = document.getElementById('logBox'); | |
| const logMessage = document.getElementById('logMessage'); | |
| const statusDot = document.getElementById('statusDot'); | |
| const statusText = document.getElementById('statusText'); | |
| const audioArea = document.getElementById('audioArea'); | |
| const audioPlayer = document.getElementById('audioPlayer'); | |
| // Cartesia API Base URL | |
| const CARTESIA_API_URL = "https://api.cartesia.ai"; | |
| function updateLog(msg, type = 'info') { | |
| logBox.classList.remove('hidden'); | |
| logMessage.textContent = msg; | |
| if (type === 'error') { | |
| logBox.className = "cartesia-card p-4 rounded-2xl text-sm border-l-4 border-red-500 text-red-400"; | |
| } else if (type === 'success') { | |
| logBox.className = "cartesia-card p-4 rounded-2xl text-sm border-l-4 border-green-500 text-green-400"; | |
| } else { | |
| logBox.className = "cartesia-card p-4 rounded-2xl text-sm border-l-4 border-blue-500 text-blue-400"; | |
| } | |
| } | |
| async function fetchVoices() { | |
| const key = apiKeyInput.value.trim(); | |
| if (!key) return updateLog("Pehle API Key daalen", "error"); | |
| connectBtn.disabled = true; | |
| connectBtn.textContent = "Connecting..."; | |
| try { | |
| const response = await fetch(`${CARTESIA_API_URL}/voices`, { | |
| headers: { | |
| "X-API-Key": key, | |
| "Cartesia-Version": "2024-06-10" | |
| } | |
| }); | |
| if (!response.ok) throw new Error("API Key galat hai ya expiry ho gayi hai."); | |
| const voices = await response.json(); | |
| voiceSelect.innerHTML = '<option value="">Select a voice...</option>'; | |
| voices.forEach(voice => { | |
| const option = document.createElement('option'); | |
| option.value = voice.id; | |
| option.textContent = `${voice.name} (${voice.language})`; | |
| voiceSelect.appendChild(option); | |
| }); | |
| statusDot.className = "h-3 w-3 rounded-full bg-green-500 shadow-lg shadow-green-900"; | |
| statusText.textContent = "CONNECTED"; | |
| updateLog("Account successfully connected! Voices load ho chuki hain.", "success"); | |
| } catch (err) { | |
| updateLog(err.message, "error"); | |
| statusDot.className = "h-3 w-3 rounded-full bg-red-500 pulse-red"; | |
| statusText.textContent = "ERROR"; | |
| } finally { | |
| connectBtn.disabled = false; | |
| connectBtn.textContent = "Connect Account"; | |
| } | |
| } | |
| connectBtn.addEventListener('click', fetchVoices); | |
| generateBtn.addEventListener('click', async () => { | |
| const key = apiKeyInput.value.trim(); | |
| const voiceId = customVoiceId.value.trim() || voiceSelect.value; | |
| const text = textInput.value.trim(); | |
| if (!key) return updateLog("API Key missing!", "error"); | |
| if (!voiceId) return updateLog("Koi voice select karen ya custom ID daalen", "error"); | |
| if (!text) return updateLog("Kuch text to likhen!", "error"); | |
| generateBtn.disabled = true; | |
| document.getElementById('btnText').textContent = "Processing..."; | |
| document.getElementById('btnIcon').textContent = "⌛"; | |
| audioArea.classList.add('hidden'); | |
| try { | |
| const response = await fetch(`${CARTESIA_API_URL}/tts/bytes`, { | |
| method: 'POST', | |
| headers: { | |
| "X-API-Key": key, | |
| "Cartesia-Version": "2024-06-10", | |
| "Content-Type": "application/json" | |
| }, | |
| body: JSON.stringify({ | |
| model_id: "sonic-english", // Cartesia basic model | |
| transcript: text, | |
| voice: { | |
| mode: "id", | |
| id: voiceId | |
| }, | |
| output_format: { | |
| container: "wav", | |
| encoding: "pcm_f32le", | |
| sample_rate: 44100 | |
| } | |
| }) | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.json(); | |
| throw new Error(errorData.message || "TTS generation failed"); | |
| } | |
| const blob = await response.blob(); | |
| const url = URL.createObjectURL(blob); | |
| audioPlayer.src = url; | |
| audioArea.classList.remove('hidden'); | |
| updateLog("Audio generate ho gaya!", "success"); | |
| audioPlayer.play(); | |
| } catch (err) { | |
| updateLog(err.message, "error"); | |
| } finally { | |
| generateBtn.disabled = false; | |
| document.getElementById('btnText').textContent = "Convert to Speech"; | |
| document.getElementById('btnIcon').textContent = "⚡"; | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |
| ``` | |