BotVeraAI / sastra_script.js
TheVera's picture
Create sastra_script.js
2ff2e78 verified
Raw
History Blame Contribute Delete
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;
}
}
}