Hamed744 commited on
Commit
6acf0b3
·
verified ·
1 Parent(s): 6a16ddb

Update index.html

Browse files
Files changed (1) hide show
  1. 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
- /* --- Single Light Theme Palette --- */
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
- const speakers = [ { id: "Charon", name: "شهاب (مرد)", desc: "صدایی قدرتمند و رسا" }, { id: "Zephyr", name: "آوا (زن)", desc: "لطیف و دلنشین" }, { id: "Achird", name: "نوید (مرد)", desc: "جوان و پرانرژی" }, { id: "Zubenelgenubi", name: "رویا (زن)", desc: "گرم و صمیمی" }, { id: "Vindemiatrix", name: "کیان (مرد)", desc: "باوقار و رسمی" }, { id: "Sadachbia", name: "پریسا (زن)", desc: "شاداب و پویا" }, { id: "Sadaltager", name: "آرش (مرد)", desc: "مطمئن و تاثیرگذار" }, { id: "Sulafat", name: "شبنم (زن)", desc: "آرام و متین" }, { id: "Laomedeia", name: "سهیل (مرد)", desc: "دوستانه و گیرا" }, { id: "Achernar", name: "مریم (زن)", desc: "حرفه‌ای و واضح" }, { id: "Alnilam", name: "بهرام (مرد)", desc: "حماسی و نافذ" }, { id: "Schedar", name: "نگار (زن)", desc: "مهربان و شیرین" }, { id: "Gacrux", name: "فرید (مرد)", desc: "پخته و قابل اعتماد" }, { id: "Pulcherrima", name: "سارا (زن)", desc: "جذاب و مدرن" }, { id: "Umbriel", name: "مانی (مرد)", desc: "خلاق و متفاوت" }, { id: "Algieba", name: "آناهیتا (زن)", desc: "با اصالت و شیک" }, { id: "Despina", name: "دلنواز (زن)", desc: "هنری و احساسی" }, { id: "Erinome", name: "رسا (مرد)", desc: "شفاف و گویا" }, { id: "Algenib", name: "امید (مرد)", desc: "انگیزه بخش و مثبت" }, { id: "Rasalthgeti", name: "الهه (زن)", desc: "اسرارآمیز و فریبنده" }, { id: "Orus", name: "بردیا (مرد)", desc: "ورزشی و پرهیجان" }, { id: "Aoede", name: "ترانه (زن)", desc: "موزیکال و خوش‌آهنگ" }, { id: "Callirrhoe", name: "نیما (مرد)", desc: "روایتگر و قصه‌گو" }, { id: "Autonoe", name: "هستی (زن)", "desc": "طبیعی و خودمانی" }, { id: "Enceladus", name: "کامیار (مرد)", desc: "مصمم و جدی" }, { id: "Iapetus", name: "ستاره (زن)", desc: "درخشان و گیرا" }, { id: "Puck", name: "پویا (مرد)", desc: "بازیگوش و سرزنده" }, { id: "Kore", name: "مهتاب (زن)", desc: "نجواگر و آرامش‌بخش" }, { id: "Fenrir", name: "سام (مرد)", desc: "جسور و بی‌باک" }, { id: "Leda", name: "لیدا (زن)", desc: "کلاسیک و باوقار" } ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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]; const getImageUrl = (speaker, index, size = 'thumb') => { const gender = speaker.name.includes('(مرد)') ? 'men' : 'women'; const imageIndex = (speaker.id.charCodeAt(0) + index) % 100; return `https://randomuser.me/api/portraits/${size === 'large' ? '' : 'med/'}${gender}/${imageIndex}.jpg`; }; const updateSelectedSpeakerDisplay = (speakerId) => { const speaker = getSpeakerById(speakerId); const speakerIndex = speakers.findIndex(s => s.id === speaker.id); selectedSpeakerImgDisplay.src = getImageUrl(speaker, speakerIndex, 'large'); selectedSpeakerNameDisplay.textContent = speaker.name; selectedSpeakerDescDisplay.textContent = speaker.desc; selectedSpeakerIdStorage.value = speaker.id; };
319
- const createSpeakerCardsInModal = () => { speakerGridInModal.innerHTML = ''; speakers.forEach((speaker, index) => { 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, index, 'thumb')}" alt="${speaker.name}" loading="lazy"> </div> <div class="speaker-name">${speaker.name}</div> `; cardLabel.addEventListener('click', () => { updateSelectedSpeakerDisplay(speaker.id); hideModal(speakerModal); }); speakerGridInModal.appendChild(cardLabel); }); };
 
 
 
 
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
- updateSelectedSpeakerDisplay(selectedSpeakerIdStorage.value || speakers[0].id);
 
 
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');