Download sastra_script.js from TheVera/BotVeraAI: direct link, hf CLI and curl.
- Browser
- Download file 12.4 kB
-
https://huggingface.co/spaces/TheVera/BotVeraAI/resolve/main/sastra_script.js
- Command line
-
hf download hf://spaces/TheVera/BotVeraAI/sastra_script.js
-
curl -L -o sastra_script.js https://huggingface.co/spaces/TheVera/BotVeraAI/resolve/main/sastra_script.js
12.4 kB
| 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, '<strong>$&</strong>'); | |
| 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; | |
| } | |
| } | |
| } | |