Update index.html
Browse files- index.html +43 -5
index.html
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800;900&display=swap');
|
9 |
|
10 |
:root {
|
11 |
-
/* ---
|
12 |
--app-font: 'Vazirmatn', sans-serif;
|
13 |
--app-bg: #F8F9FC;
|
14 |
--panel-bg: #FFFFFF;
|
@@ -303,7 +303,39 @@
|
|
303 |
const FILE_URL_BASE = `${HF_SPACE_URL}/gradio_api/file=`;
|
304 |
const FN_INDEX = 1;
|
305 |
|
306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
307 |
|
308 |
// --- DOM Elements ---
|
309 |
const form = document.getElementById('tts-form'); const textInput = document.getElementById('text-input'); const promptInput = document.getElementById('prompt-input'); const tempSlider = document.getElementById('temperature-slider'); const tempValueSpan = document.getElementById('temperature-value'); const generateBtn = document.getElementById('generate-btn'); const outputSection = document.getElementById('output-section'); const statusMessage = document.getElementById('status-message'); const loadingAnimationWrapper = document.getElementById('loading-animation-wrapper'); const selectedSpeakerIdStorage = document.getElementById('selected_speaker_id_storage'); const changeSpeakerBtn = document.getElementById('change-speaker-btn'); const selectedSpeakerCard = document.getElementById('selected-speaker-card'); const speakerGridInModal = document.getElementById('speaker-grid'); const selectedSpeakerImgDisplay = document.getElementById('selected-speaker-img'); const selectedSpeakerNameDisplay = document.getElementById('selected-speaker-name'); const selectedSpeakerDescDisplay = document.getElementById('selected-speaker-desc'); const speakerModal = document.getElementById('speaker-modal'); const infoModal = document.getElementById('info-modal'); const tempInfoIcon = document.getElementById('temp-info-icon'); const hiddenAudioPlayer = document.getElementById('hidden-audio-player'); const audioPlayerContent = document.getElementById('audio-player-content'); const audioCurrentTimeSpan = audioPlayerContent.querySelector('.audio-current-time'); const audioTotalTimeSpan = audioPlayerContent.querySelector('.audio-total-time'); const audioWaveformCanvas = document.getElementById('audio-waveform-canvas'); const waveformCtx = audioWaveformCanvas.getContext('2d'); const playPauseBtn = audioPlayerContent.querySelector('.audio-play-pause-btn-large'); const playIcon = playPauseBtn.querySelector('.play-icon'); const pauseIcon = playPauseBtn.querySelector('.pause-icon'); const skipBackwardBtn = audioPlayerContent.querySelector('.audio-skip-btn.backward'); const skipForwardBtn = audioPlayerContent.querySelector('.audio-skip-btn.forward'); const volumeBtn = audioPlayerContent.querySelector('.audio-volume-btn'); const volumeHighIcon = volumeBtn.querySelector('.volume-high-icon'); const volumeMuteIcon = volumeBtn.querySelector('.volume-mute-icon'); const speedBtn = audioPlayerContent.querySelector('.audio-speed-btn');
|
@@ -315,8 +347,12 @@
|
|
315 |
textInput.addEventListener('input', () => { const currentLength = textInput.value.length; charCountSpan.textContent = currentLength.toLocaleString('fa-IR'); charCountSpan.style.color = currentLength > MAX_CHARS ? 'red' : 'var(--accent-primary)'; });
|
316 |
|
317 |
// --- Speaker Logic ---
|
318 |
-
const getSpeakerById = (id) => speakers.find(s => s.id === id) || speakers[0];
|
319 |
-
|
|
|
|
|
|
|
|
|
320 |
|
321 |
// --- Modal Management ---
|
322 |
const showModal = (modalElement) => { modalElement.classList.add('visible'); setTimeout(() => modalElement.querySelector('button')?.focus(), 50); }; const hideModal = (modalElement) => modalElement.classList.remove('visible'); changeSpeakerBtn.addEventListener('click', () => { createSpeakerCardsInModal(); showModal(speakerModal); }); selectedSpeakerCard.addEventListener('click', () => { createSpeakerCardsInModal(); showModal(speakerModal); }); tempInfoIcon.addEventListener('click', () => showModal(infoModal)); document.querySelectorAll('.modal-overlay').forEach(o => o.addEventListener('click', (e) => (e.target === o) && hideModal(o))); document.querySelectorAll('.close-modal-btn').forEach(b => b.addEventListener('click', () => hideModal(document.getElementById(b.dataset.modalId)))); document.addEventListener('keydown', (e) => (e.key === 'Escape') && document.querySelectorAll('.modal-overlay.visible').forEach(hideModal)); tempSlider.addEventListener('input', () => { tempValueSpan.textContent = tempSlider.value; });
|
@@ -455,7 +491,9 @@
|
|
455 |
}
|
456 |
|
457 |
// --- Initial Setup ---
|
458 |
-
|
|
|
|
|
459 |
form.addEventListener('submit', generateAudio);
|
460 |
statusMessage.style.display = 'block'; loadingAnimationWrapper.style.display = 'none';
|
461 |
audioPlayerContent.style.display = 'none'; outputSection.classList.remove('has-content');
|
|
|
8 |
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800;900&display=swap');
|
9 |
|
10 |
:root {
|
11 |
+
/* --- Light Theme Palette --- */
|
12 |
--app-font: 'Vazirmatn', sans-serif;
|
13 |
--app-bg: #F8F9FC;
|
14 |
--panel-bg: #FFFFFF;
|
|
|
303 |
const FILE_URL_BASE = `${HF_SPACE_URL}/gradio_api/file=`;
|
304 |
const FN_INDEX = 1;
|
305 |
|
306 |
+
// UPDATED SPEAKERS ARRAY WITH NEW NAMES, GENDERS, AND IMAGE URLs
|
307 |
+
const speakers = [
|
308 |
+
{ id: "Charon", name: "شهاب (مرد)", desc: "صدایی قدرتمند و رسا", imgUrl: "https://uploadkon.ir/uploads/a18705_25IMG-۲۰۲۵۰۷۰۵-۱۱۰۵۴۹.jpg" },
|
309 |
+
{ id: "Zephyr", name: "آوا (زن)", desc: "لطیف و دلنشین", imgUrl: "https://uploadkon.ir/uploads/029605_25IMG-۲۰۲۵۰۷۰۵-۱۱۱۲۵۲.jpg" },
|
310 |
+
{ id: "Achird", name: "نوید (مرد)", desc: "جوان و پرانرژی", imgUrl: "https://uploadkon.ir/uploads/697e05_25IMG-۲۰۲۵۰۶۰۹-۰۶۴۶۳۷.jpg" },
|
311 |
+
{ id: "Zubenelgenubi", name: "آرمان (مرد)", desc: "گرم و صمیمی", imgUrl: "https://uploadkon.ir/uploads/a8a705_25IMG-۲۰۲۵۰۷۰۵-۱۱۱۶۲۹.jpg" }, // Changed to male
|
312 |
+
{ id: "Vindemiatrix", name: "مهسا (زن)", desc: "باوقار و رسمی", imgUrl: "https://uploadkon.ir/uploads/d74d05_25IMG-۲۰۲۵۰۷۰۵-۱۱۱۸۳۸.jpg" }, // Changed to female
|
313 |
+
{ id: "Sadachbia", name: "سامان (مرد)", desc: "شاداب و پویا", imgUrl: "https://uploadkon.ir/uploads/580205_25IMG-۲۰۲۵۰۷۰۵-۱۱۳۳۳۰.jpg" }, // Changed to male
|
314 |
+
{ id: "Sadaltager", name: "آرش (مرد)", desc: "مطمئن و تاثیرگذار", imgUrl: "https://uploadkon.ir/uploads/c4db05_25IMG-۲۰۲۵۰۷۰۵-۱۱۳۵۰۰.jpg" },
|
315 |
+
{ id: "Sulafat", name: "شبنم (زن)", desc: "آرام و متین", imgUrl: "https://uploadkon.ir/uploads/995005_25IMG-۲۰۲۵۰۷۰۵-۱۱۳۶۱۱.jpg" },
|
316 |
+
{ id: "Laomedeia", name: "سحر (زن)", desc: "دوستانه و گیرا", imgUrl: "https://uploadkon.ir/uploads/660705_25IMG-۲۰۲۵۰۷۰۵-۱۱۳۷۵۴.jpg" }, // Changed to female
|
317 |
+
{ id: "Achernar", name: "مریم (زن)", desc: "حرفهای و واضح", imgUrl: "https://uploadkon.ir/uploads/4c2905_25IMG-۲۰۲۵۰۷۰۵-۱۱۴۰۳۶.jpg" },
|
318 |
+
{ id: "Alnilam", name: "بهرام (مرد)", desc: "حماسی و نافذ", imgUrl: "https://uploadkon.ir/uploads/f0c205_25IMG-۲۰۲۵۰۷۰۵-۱۱۴۲۲۰.jpg" },
|
319 |
+
{ id: "Schedar", name: "نیکان (مرد)", desc: "مهربان و شیرین", imgUrl: "https://uploadkon.ir/uploads/d37a05_25IMG-۲۰۲۵۰۷۰۵-۱۱۴۳۲۵.jpg" }, // Changed to male
|
320 |
+
{ id: "Gacrux", name: "فرناز (زن)", desc: "پخته و قابل اعتماد", imgUrl: "https://uploadkon.ir/uploads/cff705_25IMG-۲۰۲۵۰۷۰۵-۱۱۴۶۰۵.jpg" }, // Changed to female
|
321 |
+
{ id: "Pulcherrima", name: "سارا (زن)", desc: "جذاب و مدرن", imgUrl: "https://uploadkon.ir/uploads/acb105_25IMG-۲۰۲۵۰۷۰۵-۱۱۴۷۴۳.jpg" },
|
322 |
+
{ id: "Umbriel", name: "مانی (مرد)", desc: "خلاق و متفاوت", imgUrl: "https://uploadkon.ir/uploads/68b505_25IMG-۲۰۲۵۰۷۰۵-۱۱۴۹۱۴.jpg" },
|
323 |
+
{ id: "Algieba", name: "آرتین (مرد)", desc: "با اصالت و شیک", imgUrl: "https://uploadkon.ir/uploads/571005_25IMG-۲۰۲۵۰۷۰۵-۱۱۵۰۳۹.jpg" }, // Changed to male
|
324 |
+
{ id: "Despina", name: "دلنواز (زن)", desc: "هنری و احساسی", imgUrl: "https://uploadkon.ir/uploads/5d7805_25IMG-۲۰۲۵۰۷۰۵-۱۱۵۲۲۲.jpg" },
|
325 |
+
{ id: "Erinome", name: "روژان (زن)", desc: "شفاف و گویا", imgUrl: "https://uploadkon.ir/uploads/aa8805_25IMG-۲۰۲۵۰۷۰۵-۱۱۵۳۴۹.jpg" }, // Changed to female
|
326 |
+
{ id: "Algenib", name: "امید (مرد)", desc: "انگیزه بخش و مثبت", imgUrl: "https://uploadkon.ir/uploads/a63c05_25IMG-۲۰۲۵۰۷۰۵-۱۱۵۹۲۱.jpg" },
|
327 |
+
// { id: "Rasalthgeti", name: "الهه (زن)", desc: "اسرارآمیز و فریبنده" }, // REMOVED as per request
|
328 |
+
{ id: "Orus", name: "بردیا (مرد)", desc: "ورزشی و پرهیجان", imgUrl: "https://uploadkon.ir/uploads/8bc405_25IMG-۲۰۲۵۰۷۰۵-۱۲۱۴۳۳.jpg" },
|
329 |
+
{ id: "Aoede", name: "ترانه (زن)", desc: "موزیکال و خوشآهنگ", imgUrl: "https://uploadkon.ir/uploads/9cb405_25IMG-۲۰۲۵۰۷۰۵-۱۲۱۸۵۰.jpg" },
|
330 |
+
{ id: "Callirrhoe", name: "نیکو (زن)", desc: "روایتگر و قصهگو", imgUrl: "https://uploadkon.ir/uploads/ee5f05_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۰۴۷.jpg" }, // Changed to female
|
331 |
+
{ id: "Autonoe", name: "هستی (زن)", desc: "طبیعی و خودمانی", imgUrl: "https://uploadkon.ir/uploads/9b0505_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۲۲۲.jpg" },
|
332 |
+
{ id: "Enceladus", name: "کامیار (مرد)", desc: "مصمم و جدی", imgUrl: "https://uploadkon.ir/uploads/127805_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۴۱۴.jpg" },
|
333 |
+
{ id: "Iapetus", name: "کیانوش (مرد)", desc: "درخشان و گیرا", imgUrl: "https://uploadkon.ir/uploads/c98b05_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۶۰۵.jpg" }, // Changed to male
|
334 |
+
{ id: "Puck", name: "پویا (مرد)", desc: "بازیگوش و سرزنده", imgUrl: "https://uploadkon.ir/uploads/ca3605_25IMG-۲۰۲۵۰۷۰۵-۱۲۲۸۳۹.jpg" },
|
335 |
+
{ id: "Kore", name: "مهتاب (زن)", desc: "نجواگر و آرامشبخش", imgUrl: "https://uploadkon.ir/uploads/b66605_25IMG-۲۰۲۵۰۷۰۵-۱۲۳۰۳۵.jpg" },
|
336 |
+
{ id: "Fenrir", name: "سام (مرد)", desc: "جسور و بیباک", imgUrl: "https://uploadkon.ir/uploads/03c005_25IMG-۲۰۲۵۰۷۰۵-۱۲۳۴۱۳.jpg" },
|
337 |
+
{ id: "Leda", name: "لیدا (زن)", desc: "کلاسیک و باوقار", imgUrl: "https://uploadkon.ir/uploads/710305_25IMG-۲۰۲۵۰۷۰۵-۱۲۳۷۳۱.jpg" }
|
338 |
+
];
|
339 |
|
340 |
// --- DOM Elements ---
|
341 |
const form = document.getElementById('tts-form'); const textInput = document.getElementById('text-input'); const promptInput = document.getElementById('prompt-input'); const tempSlider = document.getElementById('temperature-slider'); const tempValueSpan = document.getElementById('temperature-value'); const generateBtn = document.getElementById('generate-btn'); const outputSection = document.getElementById('output-section'); const statusMessage = document.getElementById('status-message'); const loadingAnimationWrapper = document.getElementById('loading-animation-wrapper'); const selectedSpeakerIdStorage = document.getElementById('selected_speaker_id_storage'); const changeSpeakerBtn = document.getElementById('change-speaker-btn'); const selectedSpeakerCard = document.getElementById('selected-speaker-card'); const speakerGridInModal = document.getElementById('speaker-grid'); const selectedSpeakerImgDisplay = document.getElementById('selected-speaker-img'); const selectedSpeakerNameDisplay = document.getElementById('selected-speaker-name'); const selectedSpeakerDescDisplay = document.getElementById('selected-speaker-desc'); const speakerModal = document.getElementById('speaker-modal'); const infoModal = document.getElementById('info-modal'); const tempInfoIcon = document.getElementById('temp-info-icon'); const hiddenAudioPlayer = document.getElementById('hidden-audio-player'); const audioPlayerContent = document.getElementById('audio-player-content'); const audioCurrentTimeSpan = audioPlayerContent.querySelector('.audio-current-time'); const audioTotalTimeSpan = audioPlayerContent.querySelector('.audio-total-time'); const audioWaveformCanvas = document.getElementById('audio-waveform-canvas'); const waveformCtx = audioWaveformCanvas.getContext('2d'); const playPauseBtn = audioPlayerContent.querySelector('.audio-play-pause-btn-large'); const playIcon = playPauseBtn.querySelector('.play-icon'); const pauseIcon = playPauseBtn.querySelector('.pause-icon'); const skipBackwardBtn = audioPlayerContent.querySelector('.audio-skip-btn.backward'); const skipForwardBtn = audioPlayerContent.querySelector('.audio-skip-btn.forward'); const volumeBtn = audioPlayerContent.querySelector('.audio-volume-btn'); const volumeHighIcon = volumeBtn.querySelector('.volume-high-icon'); const volumeMuteIcon = volumeBtn.querySelector('.volume-mute-icon'); const speedBtn = audioPlayerContent.querySelector('.audio-speed-btn');
|
|
|
347 |
textInput.addEventListener('input', () => { const currentLength = textInput.value.length; charCountSpan.textContent = currentLength.toLocaleString('fa-IR'); charCountSpan.style.color = currentLength > MAX_CHARS ? 'red' : 'var(--accent-primary)'; });
|
348 |
|
349 |
// --- Speaker Logic ---
|
350 |
+
const getSpeakerById = (id) => speakers.find(s => s.id === id) || speakers[0];
|
351 |
+
// MODIFIED: getImageUrl now uses the pre-defined imgUrl from the speaker object
|
352 |
+
const getImageUrl = (speaker) => speaker.imgUrl;
|
353 |
+
|
354 |
+
const updateSelectedSpeakerDisplay = (speakerId) => { const speaker = getSpeakerById(speakerId); selectedSpeakerImgDisplay.src = getImageUrl(speaker); selectedSpeakerNameDisplay.textContent = speaker.name; selectedSpeakerDescDisplay.textContent = speaker.desc; selectedSpeakerIdStorage.value = speaker.id; };
|
355 |
+
const createSpeakerCardsInModal = () => { speakerGridInModal.innerHTML = ''; speakers.forEach((speaker) => { const cardLabel = document.createElement('label'); cardLabel.className = 'speaker-card'; cardLabel.setAttribute('for', `modal-speaker-${speaker.id}`); const isChecked = speaker.id === selectedSpeakerIdStorage.value ? 'checked' : ''; cardLabel.innerHTML = ` <input type="radio" name="modal_speaker_selection" value="${speaker.id}" id="modal-speaker-${speaker.id}" ${isChecked}> <div class="speaker-visual"> <img src="${getImageUrl(speaker)}" alt="${speaker.name}" loading="lazy"> </div> <div class="speaker-name">${speaker.name}</div> `; cardLabel.addEventListener('click', () => { updateSelectedSpeakerDisplay(speaker.id); hideModal(speakerModal); }); speakerGridInModal.appendChild(cardLabel); }); };
|
356 |
|
357 |
// --- Modal Management ---
|
358 |
const showModal = (modalElement) => { modalElement.classList.add('visible'); setTimeout(() => modalElement.querySelector('button')?.focus(), 50); }; const hideModal = (modalElement) => modalElement.classList.remove('visible'); changeSpeakerBtn.addEventListener('click', () => { createSpeakerCardsInModal(); showModal(speakerModal); }); selectedSpeakerCard.addEventListener('click', () => { createSpeakerCardsInModal(); showModal(speakerModal); }); tempInfoIcon.addEventListener('click', () => showModal(infoModal)); document.querySelectorAll('.modal-overlay').forEach(o => o.addEventListener('click', (e) => (e.target === o) && hideModal(o))); document.querySelectorAll('.close-modal-btn').forEach(b => b.addEventListener('click', () => hideModal(document.getElementById(b.dataset.modalId)))); document.addEventListener('keydown', (e) => (e.key === 'Escape') && document.querySelectorAll('.modal-overlay.visible').forEach(hideModal)); tempSlider.addEventListener('input', () => { tempValueSpan.textContent = tempSlider.value; });
|
|
|
491 |
}
|
492 |
|
493 |
// --- Initial Setup ---
|
494 |
+
// Ensure default speaker exists in the updated list
|
495 |
+
const defaultSpeakerId = speakers[0] ? speakers[0].id : "Charon"; // Fallback to Charon if list is empty (shouldn't happen)
|
496 |
+
updateSelectedSpeakerDisplay(selectedSpeakerIdStorage.value || defaultSpeakerId);
|
497 |
form.addEventListener('submit', generateAudio);
|
498 |
statusMessage.style.display = 'block'; loadingAnimationWrapper.style.display = 'none';
|
499 |
audioPlayerContent.style.display = 'none'; outputSection.classList.remove('has-content');
|