GMT / app.html
Offex's picture
Create app.html
bf42e9f verified
```html
<!DOCTYPE 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>
```