document.addEventListener("DOMContentLoaded", function () { const menuButton = document.querySelector(".menu-button"); const sidebar = document.querySelector(".sidebar"); menuButton.addEventListener("click", function () { sidebar.classList.toggle("shown"); }); }); // Fetch data and initialize the page fetch('https://thevera-botveradb.hf.space/get_chapters') .then(response => response.json()) .then(responseData => { if (responseData.success) { initializePage(responseData.data); } else { console.error('Error loading data:', responseData.message); } }) .catch(error => console.error('Error loading data:', error)); function initializePage(data) { const shastraList = document.getElementById('shastraList'); if (!shastraList) { console.error("Element with ID 'shastraList' not found."); return; } // Display collection name as the main item Object.keys(data).forEach((collection) => { const mainGroupItem = document.createElement('li'); mainGroupItem.textContent = collection; mainGroupItem.classList.add('main-group'); // For styling // Create a sub-list for chapters and Skandas const itemList = document.createElement('ul'); itemList.classList.add('nested-list'); // For nested list styling itemList.style.display = 'none'; // Hide initially // Load chapters or Skandas data[collection].forEach((item) => { if (item.skandaTitle) { // Skanda Group const skandaGroup = document.createElement('li'); skandaGroup.textContent = item.skandaTitle; skandaGroup.classList.add('skanda-group'); // For styling const skandaChapterList = document.createElement('ul'); skandaChapterList.classList.add('nested-list'); skandaChapterList.style.display = 'none'; item.chapters.forEach((chapter) => { const chapterItem = document.createElement('li'); chapterItem.textContent = chapter.chapterTitle; chapterItem.classList.add('chapter-item'); chapterItem.addEventListener('click', (event) => { event.stopPropagation(); // Prevent parent click event loadChapter(chapter); }); skandaChapterList.appendChild(chapterItem); }); skandaGroup.addEventListener('click', (event) => { event.stopPropagation(); // Prevent parent click event skandaChapterList.style.display = skandaChapterList.style.display === 'none' ? 'block' : 'none'; }); skandaGroup.appendChild(skandaChapterList); itemList.appendChild(skandaGroup); } else { // Individual Chapter const chapterItem = document.createElement('li'); chapterItem.textContent = item.chapterTitle; chapterItem.classList.add('chapter-item'); chapterItem.addEventListener('click', (event) => { event.stopPropagation(); loadChapter(item); }); itemList.appendChild(chapterItem); } }); mainGroupItem.addEventListener('click', () => { itemList.style.display = itemList.style.display === 'none' ? 'block' : 'none'; }); mainGroupItem.appendChild(itemList); shastraList.appendChild(mainGroupItem); }); } // function initializePage(data) { // const shastraList = document.getElementById('shastraList'); // if (!shastraList) { // console.error("Element with ID 'shastraList' not found."); // return; // } // // Only show the collection name as the main item // Object.keys(data).forEach((collection) => { // const groupItem = document.createElement('li'); // groupItem.textContent = collection; // // Create a sub-list for chapters // const chapterList = document.createElement('ul'); // chapterList.style.display = 'none'; // Hide initially // // Load chapter names in the sub-list // data[collection].forEach((chapter) => { // const chapterItem = document.createElement('li'); // chapterItem.textContent = chapter.chapterTitle; // chapterItem.addEventListener('click', (event) => { // event.stopPropagation(); // Prevent parent click event // loadChapter(chapter); // }); // chapterList.appendChild(chapterItem); // }); // groupItem.appendChild(chapterList); // groupItem.addEventListener('click', () => { // // Toggle visibility of the chapter list // chapterList.style.display = chapterList.style.display === 'none' ? 'block' : 'none'; // }); // shastraList.appendChild(groupItem); // }); // } function loadChapter(chapter) { const chapterTitle = document.getElementById('chapterTitle'); const slokeContent = document.getElementById('slokeContent'); const videoPlayer = document.getElementById('videoPlayer'); const transcriptElement = document.getElementById('transcript'); if (!chapterTitle || !slokeContent || !videoPlayer || !transcriptElement) { console.error("One or more elements with IDs 'slokeContent', 'videoPlayer', or 'transcript' not found."); return; } // Set chapter about text as the title chapterTitle.textContent = chapter.about || 'Select a chapter to view details.'; // Clear previous content slokeContent.innerHTML = ''; videoPlayer.src = ''; transcriptElement.textContent = ''; // Set chapter title const chapterTitleElement = document.createElement('h2'); chapterTitleElement.textContent = chapter.chapterTitle; slokeContent.appendChild(chapterTitleElement); // Display slokes const slokesArray = chapter.slokes.split('\n'); slokesArray.forEach((slokeText) => { const slokeElement = document.createElement('p'); slokeElement.classList.add('sloke-text'); slokeElement.innerHTML = slokeText.replace(/श्लोक \d+:/g, '$&'); const speakIcon = createSpeakIcon(slokeText); slokeElement.appendChild(speakIcon); slokeContent.appendChild(slokeElement); }); // Set video source videoPlayer.src = chapter.video; // Set transcript transcriptElement.textContent = chapter.transcript; // Setup Play All button if necessary setupPlayAllButton(slokesArray); } function createSpeakIcon(slokeText) { const speakIcon = document.createElement('i'); speakIcon.classList.add('fas', 'fa-volume-up', 'speak-icon'); speakIcon.setAttribute('title', 'Speak Slok'); speakIcon.addEventListener('click', () => { console.log(`Speaking: ${slokeText}`); playTTS(slokeText); }); return speakIcon; } function cleanSlokText(text) { // Regex to match and remove introductory (e.g., "श्लोक 1:") and ending markers (e.g., "१") return text.replace(/श्लोक \d+: /g, '').replace(/ \d+$/g, ''); } let currentSlokeIndex = 0; // Track the current sloke index const audioPlayer = document.getElementById('audioPlayer'); // Play all slokes function (replace your existing setupPlayAllButton function) function setupPlayAllButton(slokesArray) { const playAllButton = document.getElementById('playAllButton'); playAllButton.onclick = async () => { currentSlokeIndex = 0; // Reset to start playNextSloke(slokesArray); }; } // Function to play each sloke in sequence async function playNextSloke(slokesArray) { if (currentSlokeIndex >= slokesArray.length) { return; // End if all slokes have been played } // Clean and highlight the current sloke text const slokeText = cleanSlokText(slokesArray[currentSlokeIndex]); highlightCurrentSloke(currentSlokeIndex); // Generate TTS audio for the current sloke text playTTS(slokeText).then(() => { // Move to the next sloke after the current one finishes currentSlokeIndex++; playNextSloke(slokesArray); }); } // Function to highlight the current sloke and scroll it into view function highlightCurrentSloke(index) { const slokeElements = document.querySelectorAll('.sloke-text'); // Assume each sloke text has this class // Remove previous highlights slokeElements.forEach(el => el.classList.remove('highlight')); // Highlight and scroll the current sloke const currentSloke = slokeElements[index]; if (currentSloke) { currentSloke.classList.add('highlight'); currentSloke.scrollIntoView({ behavior: "smooth", block: "center" }); } } let currentAudio; // Global variable to hold the audio object for server-generated audio let isPlaying = false; // Track play/pause state for playback let currentUtterance; // Track current SpeechSynthesisUtterance for sa-IN playback async function playTTS(text) { console.log('Speaking mantra'); text = cleanSlokText(text); if (!text || text.trim() === '') { console.error('No text provided'); return; } const synth = window.speechSynthesis; let voices = synth.getVoices(); if (voices.length === 0) { voices = await new Promise(resolve => { const id = setInterval(() => { voices = synth.getVoices(); if (voices.length > 0) { clearInterval(id); resolve(voices); } }, 10); }); } const sanskritVoice = voices.find(voice => voice.lang === 'sa-IN'); if (sanskritVoice) { // Browser Speech Synthesis using sa-IN voice currentUtterance = new SpeechSynthesisUtterance(text); currentUtterance.voice = sanskritVoice; currentUtterance.lang = 'sa-IN'; currentUtterance.rate = 0.8; currentUtterance.pitch = 1.0; synth.cancel(); // Stop any ongoing speech synthesis return new Promise((resolve, reject) => { currentUtterance.onend = () => { isPlaying = false; resolve(); // Resolve after playback finishes }; currentUtterance.onerror = (event) => { console.error('TTS playback error:', event); reject(event); }; synth.speak(currentUtterance); isPlaying = true; }); } else { // Fallback to server-based TTS audio console.warn('sa-IN voice not available, using server.'); const serverUrl = "https://thevera-botveradb.hf.space/generate_sanskrit_audio"; try { const response = await fetch(serverUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text }) }); if (!response.ok) { throw new Error('Failed to fetch audio from server'); } const audioBlob = await response.blob(); const audioUrl = URL.createObjectURL(audioBlob); return new Promise((resolve, reject) => { // Use the default audio player for playback const audioPlayer = document.getElementById('audioPlayer'); audioPlayer.src = audioUrl; audioPlayer.play(); // Resolve the promise when playback ends audioPlayer.onended = () => { isPlaying = false; URL.revokeObjectURL(audioUrl); // Clean up URL resolve(); }; // Reject the promise if there's an error audioPlayer.onerror = (event) => { console.error('Server-based TTS playback error:', event); reject(event); }; isPlaying = true; }); } catch (error) { console.error('Error calling server:', error); throw error; } } }