HtmlEzmar / index.html
Hamed744's picture
Update index.html
f9f73d3 verified
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>تبدیل متن به صدا - Gemini TTS Pro</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800;900&display=swap');
:root {
--app-font: 'Vazirmatn', sans-serif;
--app-bg: #f8f9fc;
--panel-bg: #ffffff;
--panel-border: #eaeff7;
--input-bg: #f6f8fb;
--input-border: #e1e7ef;
--text-primary: #1a202c;
--text-secondary: #626f86;
--text-tertiary: #8a94a6;
--accent-primary: #4a6cfa;
--accent-primary-hover: #3553d6;
--accent-primary-glow: rgba(74, 108, 250, 0.25);
--accent-secondary: #0fd4a8;
--accent-secondary-hover: #0da986;
--waveform-color-active: var(--accent-primary);
--waveform-color-inactive: #d0d9e6;
--shadow-sm: 0 1px 2px 0 rgba(26, 32, 44, 0.03);
--shadow-md: 0 4px 6px -1px rgba(26, 32, 44, 0.05), 0 2px 4px -2px rgba(26, 32, 44, 0.04);
--shadow-lg: 0 10px 15px -3px rgba(26, 32, 44, 0.06), 0 4px 6px -4px rgba(26, 32, 44, 0.05);
--shadow-xl: 0 20px 25px -5px rgba(26, 32, 44, 0.07), 0 8px 10px -6px rgba(26, 32, 44, 0.05);
--radius-card: 24px;
--radius-btn: 14px;
--radius-input: 12px;
--transition-fast: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
--transition-smooth: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
body {
font-family: var(--app-font);
direction: rtl;
background-color: var(--app-bg);
color: var(--text-primary);
margin: 0;
padding: 2rem;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: flex-start;
}
.container {
max-width: 800px;
width: 100%;
}
.app-header {
text-align: center;
margin-bottom: 2rem;
}
.app-header h1 {
font-size: 2.2rem;
color: var(--accent-primary);
margin-bottom: 0.5rem;
}
.app-header p {
color: var(--text-secondary);
margin-top: 0;
}
.main-panel {
background-color: var(--panel-bg);
border-radius: var(--radius-card);
padding: 2rem;
box-shadow: var(--shadow-xl);
border: 1px solid var(--panel-border);
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--text-primary);
}
textarea, select {
width: 100%;
padding: 0.8rem;
border-radius: var(--radius-input);
border: 1px solid var(--input-border);
background-color: var(--input-bg);
font-family: var(--app-font);
resize: vertical;
transition: var(--transition-smooth);
}
textarea:focus, select:focus {
outline: none;
border-color: var(--accent-primary);
box-shadow: 0 0 0 3px var(--accent-primary-glow);
}
textarea {
min-height: 120px;
}
.char-counter {
font-size: 0.85rem;
color: var(--text-secondary);
text-align: left;
margin-top: 0.5rem;
}
.char-counter .count {
font-weight: 600;
color: var(--accent-primary);
}
.btn {
background-color: var(--accent-primary);
color: white;
border: none;
padding: 0.8rem 1.5rem;
border-radius: var(--radius-btn);
font-family: var(--app-font);
font-weight: 600;
cursor: pointer;
transition: var(--transition-smooth);
width: 100%;
font-size: 1rem;
}
.btn:hover {
background-color: var(--accent-primary-hover);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.btn:disabled {
background-color: var(--text-tertiary);
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.output-section {
margin-top: 2rem;
padding: 1.5rem;
background-color: var(--input-bg);
border-radius: var(--radius-card);
border: 2px dashed var(--input-border);
text-align: center;
min-height: 150px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.output-section.has-audio {
background-color: var(--panel-bg);
border: 1px solid var(--panel-border);
}
.status-message {
color: var(--text-secondary);
}
.loading-spinner {
display: none;
width: 40px;
height: 40px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: var(--accent-primary);
animation: spin 1s linear infinite;
margin-bottom: 1rem;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.audio-player {
width: 100%;
margin-top: 1rem;
display: none;
}
.audio-controls {
display: flex;
justify-content: center;
gap: 1rem;
margin-top: 1rem;
}
.audio-controls button {
background: none;
border: none;
cursor: pointer;
font-size: 1.2rem;
color: var(--text-primary);
}
.error-message {
color: #e74c3c;
margin-top: 1rem;
display: none;
}
@media (max-width: 600px) {
body {
padding: 1rem;
}
.main-panel {
padding: 1.5rem;
}
}
</style>
</head>
<body>
<div class="container">
<header class="app-header">
<h1>تبدیل متن به صدا با هوش مصنوعی</h1>
<p>متن خود را وارد کنید و به صدای طبیعی تبدیل کنید</p>
</header>
<main class="main-panel">
<form id="tts-form">
<div class="form-group">
<label for="text-input">متن فارسی</label>
<textarea id="text-input" placeholder="متن خود را اینجا وارد کنید..."></textarea>
<div class="char-counter">
<span class="count">0</span> / 50000 نویسه
</div>
</div>
<div class="form-group">
<label for="prompt-input">راهنمای لحن (اختیاری)</label>
<textarea id="prompt-input" placeholder="مثال: با لحنی شاد و پرانرژی" rows="2"></textarea>
</div>
<div class="form-group">
<label for="voice-select">انتخاب صدا</label>
<select id="voice-select">
<option value="Charon">چارون (مرد - پیش‌فرض)</option>
<option value="Zephyr">زفیر (زن - ملایم)</option>
<option value="Achird">آچیرد (مرد - جوان)</option>
<option value="Zubenelgenubi">زوبن الجنوبی (مرد - گرم)</option>
<option value="Vindemiatrix">ویندیمیاتریکس (زن - رسمی)</option>
<option value="Sadachbia">ساداخبیا (مرد - شاداب)</option>
</select>
</div>
<div class="form-group">
<label for="temperature-slider">سطح خلاقیت صدا (0.1 تا 1.5)</label>
<input type="range" id="temperature-slider" min="0.1" max="1.5" step="0.1" value="0.9">
<div style="text-align: center; margin-top: 0.5rem;">
<span id="temperature-value">0.9</span>
</div>
</div>
<button type="submit" class="btn" id="generate-btn">تبدیل به صدا</button>
</form>
<div class="output-section" id="output-section">
<div class="status-message" id="status-message">صدای تولید شده در اینجا نمایش داده می‌شود</div>
<div class="loading-spinner" id="loading-spinner"></div>
<audio controls class="audio-player" id="audio-player"></audio>
<div class="error-message" id="error-message"></div>
</div>
</main>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// تنظیمات API
const API_URL = 'https://www.aisada.ir/api/v1/audio/generate';
const API_KEY = 'sk-a1b2c3d4e5f6g7h8i9j0k1l2m3n4'; // کلید API شما
// عناصر DOM
const form = document.getElementById('tts-form');
const textInput = document.getElementById('text-input');
const promptInput = document.getElementById('prompt-input');
const voiceSelect = document.getElementById('voice-select');
const temperatureSlider = document.getElementById('temperature-slider');
const temperatureValue = document.getElementById('temperature-value');
const generateBtn = document.getElementById('generate-btn');
const outputSection = document.getElementById('output-section');
const statusMessage = document.getElementById('status-message');
const loadingSpinner = document.getElementById('loading-spinner');
const audioPlayer = document.getElementById('audio-player');
const errorMessage = document.getElementById('error-message');
const charCount = document.querySelector('.char-counter .count');
// نمایش مقدار temperature
temperatureSlider.addEventListener('input', function() {
temperatureValue.textContent = this.value;
});
// شمارشگر کاراکترها
textInput.addEventListener('input', function() {
const count = this.value.length;
charCount.textContent = count;
charCount.style.color = count > 50000 ? 'red' : 'var(--accent-primary)';
});
// ارسال فرم
form.addEventListener('submit', async function(e) {
e.preventDefault();
const text = textInput.value.trim();
if (!text) {
showError('لطفاً متن مورد نظر را وارد کنید');
return;
}
if (text.length > 50000) {
showError('متن نمی‌تواند بیشتر از 50000 نویسه باشد');
return;
}
startLoading();
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
text: text,
prompt: promptInput.value.trim(),
voice: voiceSelect.value,
temperature: parseFloat(temperatureSlider.value)
})
});
const data = await response.json();
if (response.ok) {
if (data.success) {
showAudio(data.data.audio_url);
} else {
showError(data.error?.message || 'خطایی در پردازش رخ داد');
}
} else {
showError(data.error?.message || `خطای سرور: ${response.status}`);
}
} catch (error) {
showError('خطا در ارتباط با سرور: ' + error.message);
} finally {
stopLoading();
}
});
// توابع کمکی
function startLoading() {
generateBtn.disabled = true;
generateBtn.textContent = 'در حال پردازش...';
loadingSpinner.style.display = 'block';
statusMessage.style.display = 'none';
errorMessage.style.display = 'none';
audioPlayer.style.display = 'none';
outputSection.classList.remove('has-audio');
}
function stopLoading() {
generateBtn.disabled = false;
generateBtn.textContent = 'تبدیل به صدا';
loadingSpinner.style.display = 'none';
}
function showAudio(audioUrl) {
audioPlayer.src = audioUrl;
audioPlayer.style.display = 'block';
outputSection.classList.add('has-audio');
statusMessage.style.display = 'none';
errorMessage.style.display = 'none';
// پخش خودکار صدا
setTimeout(() => {
audioPlayer.play().catch(e => {
console.log('اتوماتیک پخش نشد:', e);
});
}, 500);
}
function showError(message) {
errorMessage.textContent = message;
errorMessage.style.display = 'block';
statusMessage.style.display = 'none';
audioPlayer.style.display = 'none';
outputSection.classList.remove('has-audio');
}
});
</script>
</body>
</html>