Update index.html
Browse files- index.html +31 -34
index.html
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
-
<title>Alpha TTS - رابط کاربری
|
7 |
<style>
|
8 |
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700;800&display=swap');
|
9 |
|
@@ -50,16 +50,16 @@
|
|
50 |
textarea, input[type="text"] { width: 100%; padding: 1rem; border-radius: var(--radius-input); border: 2px solid var(--app-border-color); background-color: var(--app-input-bg); box-shadow: none; font-family: var(--app-font); font-size: 1rem; box-sizing: border-box; transition: all 0.2s ease-in-out; }
|
51 |
textarea:focus, input[type="text"]:focus { outline: none; border-color: var(--app-button-bg); box-shadow: 0 0 0 3px rgba(95, 39, 205, 0.2); background-color: #fff; }
|
52 |
|
53 |
-
/* ---
|
54 |
#selected-speaker-display { text-align: center; }
|
55 |
#selected-speaker-card { display: inline-flex; align-items: center; background: #fff; border-radius: 99px; padding: 10px; box-shadow: 0 5px 20px rgba(0,0,0,0.1); border: 2px solid var(--app-border-color); }
|
56 |
-
#selected-speaker-card img { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; margin-left: 15px; }
|
57 |
#selected-speaker-info h3 { margin: 0; font-size: 1.4em; }
|
58 |
#selected-speaker-info p { margin: 5px 0 0; color: var(--app-text-secondary); font-size: 0.9em; }
|
59 |
#change-speaker-btn { display: block; margin: 1rem auto 0; padding: 8px 20px; border-radius: 8px; background-color: var(--app-input-bg); border: 1px solid var(--app-border-color); cursor: pointer; font-family: var(--app-font); font-weight: 500; transition: all 0.2s ease; }
|
60 |
#change-speaker-btn:hover { background-color: #e9ecef; border-color: #ced4da; }
|
61 |
|
62 |
-
/* ---
|
63 |
#speaker-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); backdrop-filter: blur(5px); display: none; align-items: center; justify-content: center; z-index: 1000; opacity: 0; transition: opacity 0.3s ease; }
|
64 |
#speaker-modal.visible { display: flex; opacity: 1; }
|
65 |
.modal-content { background: #fff; padding: 2rem; border-radius: var(--radius-card); width: 90%; max-width: 700px; max-height: 80vh; overflow-y: auto; transform: scale(0.95); transition: transform 0.3s ease; }
|
@@ -75,7 +75,7 @@
|
|
75 |
.speaker-card .speaker-visual { border: 3px solid transparent; border-radius: var(--radius-card); overflow: hidden; text-align: center; box-shadow: 0 4px 10px rgba(0,0,0,0.05); position: relative; background-color: #fff; }
|
76 |
.speaker-card:hover .speaker-visual { transform: translateY(-3px); box-shadow: 0 6px 15px rgba(0,0,0,0.1); }
|
77 |
.speaker-card input[type="radio"] { display: none; }
|
78 |
-
.speaker-card img { width: 100%; height: 120px; object-fit: cover; display: block; }
|
79 |
.speaker-card .speaker-name { padding: 0.7rem 0.5rem; font-weight: 500; font-size: 0.9em; }
|
80 |
.speaker-card input[type="radio"]:checked + .speaker-visual { border-color: var(--app-button-bg); box-shadow: var(--speaker-selected-glow); }
|
81 |
|
@@ -141,7 +141,6 @@
|
|
141 |
</main>
|
142 |
</div>
|
143 |
|
144 |
-
<!-- Modal برای انتخاب گوینده -->
|
145 |
<div id="speaker-modal">
|
146 |
<div class="modal-content">
|
147 |
<div class="modal-header">
|
@@ -152,12 +151,10 @@
|
|
152 |
</div>
|
153 |
</div>
|
154 |
|
155 |
-
|
156 |
-
<input type="hidden" id="selected_speaker_id" value="Charon">
|
157 |
|
158 |
<script>
|
159 |
document.addEventListener('DOMContentLoaded', () => {
|
160 |
-
// --- پیکربندی و متغیرها ---
|
161 |
const HF_SPACE_URL = "https://hamed744-ttspro.hf.space";
|
162 |
const JOIN_QUEUE_URL = `${HF_SPACE_URL}/gradio_api/queue/join`;
|
163 |
const GET_DATA_URL_BASE = `${HF_SPACE_URL}/gradio_api/queue/data`;
|
@@ -167,8 +164,7 @@
|
|
167 |
const speakers = [
|
168 |
{ id: "Charon", name: "شهاب (مرد)" }, { id: "Zephyr", name: "آوا (زن)" }, { id: "Achird", name: "نوید (مرد)" }, { id: "Zubenelgenubi", name: "رویا (زن)" }, { id: "Vindemiatrix", name: "کیان (مرد)" }, { id: "Sadachbia", name: "پریسا (زن)" }, { id: "Sadaltager", name: "آرش (مرد)" }, { id: "Sulafat", name: "شبنم (زن)" }, { id: "Laomedeia", name: "سهیل (م��د)" }, { id: "Achernar", name: "مریم (زن)" }, { id: "Alnilam", name: "بهرام (مرد)" }, { id: "Schedar", name: "نگار (زن)" }, { id: "Gacrux", name: "فرید (مرد)" }, { id: "Pulcherrima", name: "سارا (زن)" }, { id: "Umbriel", name: "مانی (مرد)" }, { id: "Algieba", name: "آناهیتا (زن)" }, { id: "Despina", name: "دلنواز (زن)" }, { id: "Erinome", name: "رسا (مرد)" }, { id: "Algenib", name: "امید (مرد)" }, { id: "Rasalthgeti", name: "الهه (زن)" }, { id: "Orus", name: "بردیا (مرد)" }, { id: "Aoede", name: "ترانه (زن)" }, { id: "Callirrhoe", name: "نیما (مرد)" }, { id: "Autonoe", name: "هستی (زن)" }, { id: "Enceladus", name: "کامیار (مرد)" }, { id: "Iapetus", name: "ستاره (زن)" }, { id: "Puck", name: "پویا (مرد)" }, { id: "Kore", name: "مهتاب (زن)" }, { id: "Fenrir", name: "سام (مرد)" }, { id: "Leda", name: "لیدا (زن)" }
|
169 |
];
|
170 |
-
|
171 |
-
// --- عناصر DOM ---
|
172 |
const form = document.getElementById('tts-form');
|
173 |
const textInput = document.getElementById('text-input');
|
174 |
const promptInput = document.getElementById('prompt-input');
|
@@ -177,41 +173,48 @@
|
|
177 |
const generateBtn = document.getElementById('generate-btn');
|
178 |
const statusMessage = document.getElementById('status-message');
|
179 |
const audioPlayer = document.getElementById('audio-player');
|
180 |
-
|
|
|
181 |
const speakerModal = document.getElementById('speaker-modal');
|
182 |
const changeSpeakerBtn = document.getElementById('change-speaker-btn');
|
183 |
const closeModalBtn = document.querySelector('.close-modal-btn');
|
184 |
-
const
|
185 |
-
const
|
186 |
-
const
|
187 |
|
188 |
-
// --- توابع ---
|
189 |
function getSpeakerById(id) {
|
190 |
return speakers.find(s => s.id === id);
|
191 |
}
|
192 |
|
193 |
function getImageUrl(speaker, index) {
|
194 |
-
|
195 |
-
|
|
|
|
|
|
|
196 |
}
|
197 |
|
198 |
function updateSelectedSpeakerDisplay(speakerId) {
|
199 |
const speaker = getSpeakerById(speakerId);
|
200 |
if (speaker) {
|
201 |
-
|
202 |
-
|
203 |
-
|
|
|
204 |
}
|
205 |
}
|
206 |
|
207 |
-
function
|
|
|
208 |
speakers.forEach((speaker, index) => {
|
209 |
const card = document.createElement('label');
|
210 |
card.className = 'speaker-card';
|
211 |
-
card.setAttribute('for', `speaker-${speaker.id}`);
|
|
|
|
|
212 |
|
213 |
card.innerHTML = `
|
214 |
-
<input type="radio" name="
|
215 |
<div class="speaker-visual">
|
216 |
<img src="${getImageUrl(speaker, index)}" alt="عکس گوینده ${speaker.name}" loading="lazy">
|
217 |
<div class="speaker-name">${speaker.name}</div>
|
@@ -220,19 +223,15 @@
|
|
220 |
|
221 |
card.addEventListener('click', () => {
|
222 |
updateSelectedSpeakerDisplay(speaker.id);
|
223 |
-
// برای بستن خودکار مودال بعد از انتخاب
|
224 |
setTimeout(() => speakerModal.classList.remove('visible'), 100);
|
225 |
});
|
226 |
|
227 |
-
|
228 |
});
|
229 |
}
|
230 |
|
231 |
-
// --- رویدادهای مودال ---
|
232 |
changeSpeakerBtn.addEventListener('click', () => {
|
233 |
-
//
|
234 |
-
const currentSelected = document.querySelector(`input[name="speaker"][value="${selectedSpeakerIdInput.value}"]`);
|
235 |
-
if(currentSelected) currentSelected.checked = true;
|
236 |
speakerModal.classList.add('visible');
|
237 |
});
|
238 |
closeModalBtn.addEventListener('click', () => speakerModal.classList.remove('visible'));
|
@@ -255,7 +254,7 @@
|
|
255 |
const text = textInput.value;
|
256 |
const prompt = promptInput.value;
|
257 |
const temperature = parseFloat(tempSlider.value);
|
258 |
-
const selectedSpeaker =
|
259 |
const sessionHash = Math.random().toString(36).substring(2);
|
260 |
|
261 |
if (!text.trim()) {
|
@@ -340,9 +339,7 @@
|
|
340 |
}
|
341 |
}
|
342 |
|
343 |
-
//
|
344 |
-
createSpeakerCards();
|
345 |
-
updateSelectedSpeakerDisplay(selectedSpeakerIdInput.value); // نمایش گوینده پیشفرض
|
346 |
form.addEventListener('submit', generateAudio);
|
347 |
});
|
348 |
</script>
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Alpha TTS - رابط کاربری هوشمند با عکس</title>
|
7 |
<style>
|
8 |
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700;800&display=swap');
|
9 |
|
|
|
50 |
textarea, input[type="text"] { width: 100%; padding: 1rem; border-radius: var(--radius-input); border: 2px solid var(--app-border-color); background-color: var(--app-input-bg); box-shadow: none; font-family: var(--app-font); font-size: 1rem; box-sizing: border-box; transition: all 0.2s ease-in-out; }
|
51 |
textarea:focus, input[type="text"]:focus { outline: none; border-color: var(--app-button-bg); box-shadow: 0 0 0 3px rgba(95, 39, 205, 0.2); background-color: #fff; }
|
52 |
|
53 |
+
/* --- نمایش گوینده منتخب --- */
|
54 |
#selected-speaker-display { text-align: center; }
|
55 |
#selected-speaker-card { display: inline-flex; align-items: center; background: #fff; border-radius: 99px; padding: 10px; box-shadow: 0 5px 20px rgba(0,0,0,0.1); border: 2px solid var(--app-border-color); }
|
56 |
+
#selected-speaker-card img { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; margin-left: 15px; background-color: #eee; }
|
57 |
#selected-speaker-info h3 { margin: 0; font-size: 1.4em; }
|
58 |
#selected-speaker-info p { margin: 5px 0 0; color: var(--app-text-secondary); font-size: 0.9em; }
|
59 |
#change-speaker-btn { display: block; margin: 1rem auto 0; padding: 8px 20px; border-radius: 8px; background-color: var(--app-input-bg); border: 1px solid var(--app-border-color); cursor: pointer; font-family: var(--app-font); font-weight: 500; transition: all 0.2s ease; }
|
60 |
#change-speaker-btn:hover { background-color: #e9ecef; border-color: #ced4da; }
|
61 |
|
62 |
+
/* --- مودال گالری گویندگان --- */
|
63 |
#speaker-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); backdrop-filter: blur(5px); display: none; align-items: center; justify-content: center; z-index: 1000; opacity: 0; transition: opacity 0.3s ease; }
|
64 |
#speaker-modal.visible { display: flex; opacity: 1; }
|
65 |
.modal-content { background: #fff; padding: 2rem; border-radius: var(--radius-card); width: 90%; max-width: 700px; max-height: 80vh; overflow-y: auto; transform: scale(0.95); transition: transform 0.3s ease; }
|
|
|
75 |
.speaker-card .speaker-visual { border: 3px solid transparent; border-radius: var(--radius-card); overflow: hidden; text-align: center; box-shadow: 0 4px 10px rgba(0,0,0,0.05); position: relative; background-color: #fff; }
|
76 |
.speaker-card:hover .speaker-visual { transform: translateY(-3px); box-shadow: 0 6px 15px rgba(0,0,0,0.1); }
|
77 |
.speaker-card input[type="radio"] { display: none; }
|
78 |
+
.speaker-card img { width: 100%; height: 120px; object-fit: cover; display: block; background-color: #eee; /* Placeholder color */ }
|
79 |
.speaker-card .speaker-name { padding: 0.7rem 0.5rem; font-weight: 500; font-size: 0.9em; }
|
80 |
.speaker-card input[type="radio"]:checked + .speaker-visual { border-color: var(--app-button-bg); box-shadow: var(--speaker-selected-glow); }
|
81 |
|
|
|
141 |
</main>
|
142 |
</div>
|
143 |
|
|
|
144 |
<div id="speaker-modal">
|
145 |
<div class="modal-content">
|
146 |
<div class="modal-header">
|
|
|
151 |
</div>
|
152 |
</div>
|
153 |
|
154 |
+
<input type="hidden" id="selected_speaker_id_storage" value="Charon">
|
|
|
155 |
|
156 |
<script>
|
157 |
document.addEventListener('DOMContentLoaded', () => {
|
|
|
158 |
const HF_SPACE_URL = "https://hamed744-ttspro.hf.space";
|
159 |
const JOIN_QUEUE_URL = `${HF_SPACE_URL}/gradio_api/queue/join`;
|
160 |
const GET_DATA_URL_BASE = `${HF_SPACE_URL}/gradio_api/queue/data`;
|
|
|
164 |
const speakers = [
|
165 |
{ id: "Charon", name: "شهاب (مرد)" }, { id: "Zephyr", name: "آوا (زن)" }, { id: "Achird", name: "نوید (مرد)" }, { id: "Zubenelgenubi", name: "رویا (زن)" }, { id: "Vindemiatrix", name: "کیان (مرد)" }, { id: "Sadachbia", name: "پریسا (زن)" }, { id: "Sadaltager", name: "آرش (مرد)" }, { id: "Sulafat", name: "شبنم (زن)" }, { id: "Laomedeia", name: "سهیل (م��د)" }, { id: "Achernar", name: "مریم (زن)" }, { id: "Alnilam", name: "بهرام (مرد)" }, { id: "Schedar", name: "نگار (زن)" }, { id: "Gacrux", name: "فرید (مرد)" }, { id: "Pulcherrima", name: "سارا (زن)" }, { id: "Umbriel", name: "مانی (مرد)" }, { id: "Algieba", name: "آناهیتا (زن)" }, { id: "Despina", name: "دلنواز (زن)" }, { id: "Erinome", name: "رسا (مرد)" }, { id: "Algenib", name: "امید (مرد)" }, { id: "Rasalthgeti", name: "الهه (زن)" }, { id: "Orus", name: "بردیا (مرد)" }, { id: "Aoede", name: "ترانه (زن)" }, { id: "Callirrhoe", name: "نیما (مرد)" }, { id: "Autonoe", name: "هستی (زن)" }, { id: "Enceladus", name: "کامیار (مرد)" }, { id: "Iapetus", name: "ستاره (زن)" }, { id: "Puck", name: "پویا (مرد)" }, { id: "Kore", name: "مهتاب (زن)" }, { id: "Fenrir", name: "سام (مرد)" }, { id: "Leda", name: "لیدا (زن)" }
|
166 |
];
|
167 |
+
|
|
|
168 |
const form = document.getElementById('tts-form');
|
169 |
const textInput = document.getElementById('text-input');
|
170 |
const promptInput = document.getElementById('prompt-input');
|
|
|
173 |
const generateBtn = document.getElementById('generate-btn');
|
174 |
const statusMessage = document.getElementById('status-message');
|
175 |
const audioPlayer = document.getElementById('audio-player');
|
176 |
+
|
177 |
+
const selectedSpeakerIdStorage = document.getElementById('selected_speaker_id_storage'); // برای نگهداری مقدار
|
178 |
const speakerModal = document.getElementById('speaker-modal');
|
179 |
const changeSpeakerBtn = document.getElementById('change-speaker-btn');
|
180 |
const closeModalBtn = document.querySelector('.close-modal-btn');
|
181 |
+
const speakerGridInModal = document.getElementById('speaker-grid'); // گرید داخل مودال
|
182 |
+
const selectedSpeakerImgDisplay = document.getElementById('selected-speaker-img'); // نمایش عکس اصلی
|
183 |
+
const selectedSpeakerNameDisplay = document.getElementById('selected-speaker-name'); // نمایش نام اصلی
|
184 |
|
|
|
185 |
function getSpeakerById(id) {
|
186 |
return speakers.find(s => s.id === id);
|
187 |
}
|
188 |
|
189 |
function getImageUrl(speaker, index) {
|
190 |
+
// از یک سرویس عکس پرتره رندوم استفاده می کنیم
|
191 |
+
// برای تنوع بیشتر، از یک اندیس متفاوت برای هر گوینده استفاده می کنیم
|
192 |
+
const gender = speaker.name.includes('(مرد)') ? 'men' : (speaker.name.includes('(زن)') ? 'women' : 'lego'); // اگر جنسیت مشخص نیست، از لگو استفاده می کنیم
|
193 |
+
const imageIndex = (index * 7 + 13) % 100; // یک فرمول ساده برای ایجاد تنوع در اندیس عکس
|
194 |
+
return `https://randomuser.me/api/portraits/${gender}/${imageIndex}.jpg`;
|
195 |
}
|
196 |
|
197 |
function updateSelectedSpeakerDisplay(speakerId) {
|
198 |
const speaker = getSpeakerById(speakerId);
|
199 |
if (speaker) {
|
200 |
+
const speakerIndex = speakers.findIndex(s => s.id === speakerId);
|
201 |
+
selectedSpeakerImgDisplay.src = getImageUrl(speaker, speakerIndex);
|
202 |
+
selectedSpeakerNameDisplay.textContent = speaker.name;
|
203 |
+
selectedSpeakerIdStorage.value = speaker.id; // ذخیره ID انتخاب شده
|
204 |
}
|
205 |
}
|
206 |
|
207 |
+
function createSpeakerCardsInModal() {
|
208 |
+
speakerGridInModal.innerHTML = ''; // پاک کردن گرید قبلی
|
209 |
speakers.forEach((speaker, index) => {
|
210 |
const card = document.createElement('label');
|
211 |
card.className = 'speaker-card';
|
212 |
+
card.setAttribute('for', `modal-speaker-${speaker.id}`);
|
213 |
+
|
214 |
+
const isChecked = speaker.id === selectedSpeakerIdStorage.value ? 'checked' : '';
|
215 |
|
216 |
card.innerHTML = `
|
217 |
+
<input type="radio" name="modal_speaker_selection" value="${speaker.id}" id="modal-speaker-${speaker.id}" ${isChecked}>
|
218 |
<div class="speaker-visual">
|
219 |
<img src="${getImageUrl(speaker, index)}" alt="عکس گوینده ${speaker.name}" loading="lazy">
|
220 |
<div class="speaker-name">${speaker.name}</div>
|
|
|
223 |
|
224 |
card.addEventListener('click', () => {
|
225 |
updateSelectedSpeakerDisplay(speaker.id);
|
|
|
226 |
setTimeout(() => speakerModal.classList.remove('visible'), 100);
|
227 |
});
|
228 |
|
229 |
+
speakerGridInModal.appendChild(card);
|
230 |
});
|
231 |
}
|
232 |
|
|
|
233 |
changeSpeakerBtn.addEventListener('click', () => {
|
234 |
+
createSpeakerCardsInModal(); // هر بار مودال باز می شود، کارت ها را با انتخاب فعلی می سازیم
|
|
|
|
|
235 |
speakerModal.classList.add('visible');
|
236 |
});
|
237 |
closeModalBtn.addEventListener('click', () => speakerModal.classList.remove('visible'));
|
|
|
254 |
const text = textInput.value;
|
255 |
const prompt = promptInput.value;
|
256 |
const temperature = parseFloat(tempSlider.value);
|
257 |
+
const selectedSpeaker = selectedSpeakerIdStorage.value; // از فیلد مخفی میخوانیم
|
258 |
const sessionHash = Math.random().toString(36).substring(2);
|
259 |
|
260 |
if (!text.trim()) {
|
|
|
339 |
}
|
340 |
}
|
341 |
|
342 |
+
updateSelectedSpeakerDisplay(selectedSpeakerIdStorage.value); // نمایش گوینده پیشفرض
|
|
|
|
|
343 |
form.addEventListener('submit', generateAudio);
|
344 |
});
|
345 |
</script>
|