TheVera commited on
Commit
2ff2e78
·
verified ·
1 Parent(s): 3672d41

Create sastra_script.js

Browse files
Files changed (1) hide show
  1. sastra_script.js +340 -0
sastra_script.js ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener("DOMContentLoaded", function () {
2
+ const menuButton = document.querySelector(".menu-button");
3
+ const sidebar = document.querySelector(".sidebar");
4
+
5
+
6
+ menuButton.addEventListener("click", function () {
7
+ sidebar.classList.toggle("shown");
8
+ });
9
+ });
10
+
11
+
12
+ // Fetch data and initialize the page
13
+ fetch('https://thevera-botveradb.hf.space/get_chapters')
14
+ .then(response => response.json())
15
+ .then(responseData => {
16
+ if (responseData.success) {
17
+ initializePage(responseData.data);
18
+ } else {
19
+ console.error('Error loading data:', responseData.message);
20
+ }
21
+ })
22
+ .catch(error => console.error('Error loading data:', error));
23
+
24
+
25
+ function initializePage(data) {
26
+ const shastraList = document.getElementById('shastraList');
27
+ if (!shastraList) {
28
+ console.error("Element with ID 'shastraList' not found.");
29
+ return;
30
+ }
31
+
32
+ // Display collection name as the main item
33
+ Object.keys(data).forEach((collection) => {
34
+ const mainGroupItem = document.createElement('li');
35
+ mainGroupItem.textContent = collection;
36
+ mainGroupItem.classList.add('main-group'); // For styling
37
+
38
+ // Create a sub-list for chapters and Skandas
39
+ const itemList = document.createElement('ul');
40
+ itemList.classList.add('nested-list'); // For nested list styling
41
+ itemList.style.display = 'none'; // Hide initially
42
+
43
+ // Load chapters or Skandas
44
+ data[collection].forEach((item) => {
45
+ if (item.skandaTitle) {
46
+ // Skanda Group
47
+ const skandaGroup = document.createElement('li');
48
+ skandaGroup.textContent = item.skandaTitle;
49
+ skandaGroup.classList.add('skanda-group'); // For styling
50
+
51
+ const skandaChapterList = document.createElement('ul');
52
+ skandaChapterList.classList.add('nested-list');
53
+ skandaChapterList.style.display = 'none';
54
+
55
+ item.chapters.forEach((chapter) => {
56
+ const chapterItem = document.createElement('li');
57
+ chapterItem.textContent = chapter.chapterTitle;
58
+ chapterItem.classList.add('chapter-item');
59
+ chapterItem.addEventListener('click', (event) => {
60
+ event.stopPropagation(); // Prevent parent click event
61
+ loadChapter(chapter);
62
+ });
63
+ skandaChapterList.appendChild(chapterItem);
64
+ });
65
+ skandaGroup.addEventListener('click', (event) => {
66
+ event.stopPropagation(); // Prevent parent click event
67
+ skandaChapterList.style.display = skandaChapterList.style.display === 'none' ? 'block' : 'none';
68
+ });
69
+
70
+ skandaGroup.appendChild(skandaChapterList);
71
+ itemList.appendChild(skandaGroup);
72
+ } else {
73
+ // Individual Chapter
74
+ const chapterItem = document.createElement('li');
75
+ chapterItem.textContent = item.chapterTitle;
76
+ chapterItem.classList.add('chapter-item');
77
+ chapterItem.addEventListener('click', (event) => {
78
+ event.stopPropagation();
79
+ loadChapter(item);
80
+ });
81
+ itemList.appendChild(chapterItem);
82
+ }
83
+ });
84
+
85
+ mainGroupItem.addEventListener('click', () => {
86
+ itemList.style.display = itemList.style.display === 'none' ? 'block' : 'none';
87
+ });
88
+ mainGroupItem.appendChild(itemList);
89
+ shastraList.appendChild(mainGroupItem);
90
+ });
91
+ }
92
+
93
+
94
+
95
+ // function initializePage(data) {
96
+ // const shastraList = document.getElementById('shastraList');
97
+ // if (!shastraList) {
98
+ // console.error("Element with ID 'shastraList' not found.");
99
+ // return;
100
+ // }
101
+
102
+ // // Only show the collection name as the main item
103
+ // Object.keys(data).forEach((collection) => {
104
+ // const groupItem = document.createElement('li');
105
+ // groupItem.textContent = collection;
106
+
107
+ // // Create a sub-list for chapters
108
+ // const chapterList = document.createElement('ul');
109
+ // chapterList.style.display = 'none'; // Hide initially
110
+
111
+ // // Load chapter names in the sub-list
112
+ // data[collection].forEach((chapter) => {
113
+ // const chapterItem = document.createElement('li');
114
+ // chapterItem.textContent = chapter.chapterTitle;
115
+ // chapterItem.addEventListener('click', (event) => {
116
+ // event.stopPropagation(); // Prevent parent click event
117
+ // loadChapter(chapter);
118
+ // });
119
+ // chapterList.appendChild(chapterItem);
120
+ // });
121
+
122
+ // groupItem.appendChild(chapterList);
123
+ // groupItem.addEventListener('click', () => {
124
+ // // Toggle visibility of the chapter list
125
+ // chapterList.style.display = chapterList.style.display === 'none' ? 'block' : 'none';
126
+ // });
127
+ // shastraList.appendChild(groupItem);
128
+ // });
129
+ // }
130
+
131
+
132
+ function loadChapter(chapter) {
133
+ const chapterTitle = document.getElementById('chapterTitle');
134
+ const slokeContent = document.getElementById('slokeContent');
135
+ const videoPlayer = document.getElementById('videoPlayer');
136
+ const transcriptElement = document.getElementById('transcript');
137
+
138
+ if (!chapterTitle || !slokeContent || !videoPlayer || !transcriptElement) {
139
+ console.error("One or more elements with IDs 'slokeContent', 'videoPlayer', or 'transcript' not found.");
140
+ return;
141
+ }
142
+
143
+ // Set chapter about text as the title
144
+ chapterTitle.textContent = chapter.about || 'Select a chapter to view details.';
145
+
146
+ // Clear previous content
147
+ slokeContent.innerHTML = '';
148
+ videoPlayer.src = '';
149
+ transcriptElement.textContent = '';
150
+
151
+ // Set chapter title
152
+ const chapterTitleElement = document.createElement('h2');
153
+ chapterTitleElement.textContent = chapter.chapterTitle;
154
+ slokeContent.appendChild(chapterTitleElement);
155
+
156
+ // Display slokes
157
+ const slokesArray = chapter.slokes.split('\n');
158
+ slokesArray.forEach((slokeText) => {
159
+ const slokeElement = document.createElement('p');
160
+ slokeElement.classList.add('sloke-text');
161
+ slokeElement.innerHTML = slokeText.replace(/श्लोक \d+:/g, '<strong>$&</strong>');
162
+
163
+ const speakIcon = createSpeakIcon(slokeText);
164
+ slokeElement.appendChild(speakIcon);
165
+ slokeContent.appendChild(slokeElement);
166
+ });
167
+
168
+ // Set video source
169
+ videoPlayer.src = chapter.video;
170
+
171
+ // Set transcript
172
+ transcriptElement.textContent = chapter.transcript;
173
+
174
+ // Setup Play All button if necessary
175
+ setupPlayAllButton(slokesArray);
176
+ }
177
+
178
+ function createSpeakIcon(slokeText) {
179
+ const speakIcon = document.createElement('i');
180
+ speakIcon.classList.add('fas', 'fa-volume-up', 'speak-icon');
181
+ speakIcon.setAttribute('title', 'Speak Slok');
182
+ speakIcon.addEventListener('click', () => {
183
+ console.log(`Speaking: ${slokeText}`);
184
+ playTTS(slokeText);
185
+ });
186
+ return speakIcon;
187
+ }
188
+
189
+ function cleanSlokText(text) {
190
+ // Regex to match and remove introductory (e.g., "श्लोक 1:") and ending markers (e.g., "१")
191
+ return text.replace(/श्लोक \d+: /g, '').replace(/ \d+$/g, '');
192
+ }
193
+
194
+ let currentSlokeIndex = 0; // Track the current sloke index
195
+ const audioPlayer = document.getElementById('audioPlayer');
196
+
197
+ // Play all slokes function (replace your existing setupPlayAllButton function)
198
+ function setupPlayAllButton(slokesArray) {
199
+ const playAllButton = document.getElementById('playAllButton');
200
+
201
+ playAllButton.onclick = async () => {
202
+ currentSlokeIndex = 0; // Reset to start
203
+ playNextSloke(slokesArray);
204
+ };
205
+ }
206
+
207
+ // Function to play each sloke in sequence
208
+ async function playNextSloke(slokesArray) {
209
+ if (currentSlokeIndex >= slokesArray.length) {
210
+ return; // End if all slokes have been played
211
+ }
212
+
213
+ // Clean and highlight the current sloke text
214
+ const slokeText = cleanSlokText(slokesArray[currentSlokeIndex]);
215
+ highlightCurrentSloke(currentSlokeIndex);
216
+
217
+ // Generate TTS audio for the current sloke text
218
+ playTTS(slokeText).then(() => {
219
+ // Move to the next sloke after the current one finishes
220
+ currentSlokeIndex++;
221
+ playNextSloke(slokesArray);
222
+ });
223
+ }
224
+
225
+ // Function to highlight the current sloke and scroll it into view
226
+ function highlightCurrentSloke(index) {
227
+ const slokeElements = document.querySelectorAll('.sloke-text'); // Assume each sloke text has this class
228
+
229
+ // Remove previous highlights
230
+ slokeElements.forEach(el => el.classList.remove('highlight'));
231
+
232
+ // Highlight and scroll the current sloke
233
+ const currentSloke = slokeElements[index];
234
+ if (currentSloke) {
235
+ currentSloke.classList.add('highlight');
236
+ currentSloke.scrollIntoView({ behavior: "smooth", block: "center" });
237
+ }
238
+ }
239
+
240
+ let currentAudio; // Global variable to hold the audio object for server-generated audio
241
+ let isPlaying = false; // Track play/pause state for playback
242
+ let currentUtterance; // Track current SpeechSynthesisUtterance for sa-IN playback
243
+
244
+ async function playTTS(text) {
245
+ console.log('Speaking mantra');
246
+ text = cleanSlokText(text);
247
+
248
+ if (!text || text.trim() === '') {
249
+ console.error('No text provided');
250
+ return;
251
+ }
252
+
253
+ const synth = window.speechSynthesis;
254
+ let voices = synth.getVoices();
255
+
256
+ if (voices.length === 0) {
257
+ voices = await new Promise(resolve => {
258
+ const id = setInterval(() => {
259
+ voices = synth.getVoices();
260
+ if (voices.length > 0) {
261
+ clearInterval(id);
262
+ resolve(voices);
263
+ }
264
+ }, 10);
265
+ });
266
+ }
267
+
268
+ const sanskritVoice = voices.find(voice => voice.lang === 'sa-IN');
269
+
270
+ if (sanskritVoice) {
271
+ // Browser Speech Synthesis using sa-IN voice
272
+ currentUtterance = new SpeechSynthesisUtterance(text);
273
+ currentUtterance.voice = sanskritVoice;
274
+ currentUtterance.lang = 'sa-IN';
275
+ currentUtterance.rate = 0.8;
276
+ currentUtterance.pitch = 1.0;
277
+
278
+ synth.cancel(); // Stop any ongoing speech synthesis
279
+
280
+ return new Promise((resolve, reject) => {
281
+ currentUtterance.onend = () => {
282
+ isPlaying = false;
283
+ resolve(); // Resolve after playback finishes
284
+ };
285
+ currentUtterance.onerror = (event) => {
286
+ console.error('TTS playback error:', event);
287
+ reject(event);
288
+ };
289
+
290
+ synth.speak(currentUtterance);
291
+ isPlaying = true;
292
+ });
293
+ } else {
294
+ // Fallback to server-based TTS audio
295
+ console.warn('sa-IN voice not available, using server.');
296
+
297
+ const serverUrl = "https://thevera-botveradb.hf.space/generate_sanskrit_audio";
298
+ try {
299
+ const response = await fetch(serverUrl, {
300
+ method: "POST",
301
+ headers: { "Content-Type": "application/json" },
302
+ body: JSON.stringify({ text })
303
+ });
304
+
305
+ if (!response.ok) {
306
+ throw new Error('Failed to fetch audio from server');
307
+ }
308
+
309
+ const audioBlob = await response.blob();
310
+ const audioUrl = URL.createObjectURL(audioBlob);
311
+
312
+ return new Promise((resolve, reject) => {
313
+ // Use the default audio player for playback
314
+ const audioPlayer = document.getElementById('audioPlayer');
315
+ audioPlayer.src = audioUrl;
316
+ audioPlayer.play();
317
+
318
+ // Resolve the promise when playback ends
319
+ audioPlayer.onended = () => {
320
+ isPlaying = false;
321
+ URL.revokeObjectURL(audioUrl); // Clean up URL
322
+ resolve();
323
+ };
324
+
325
+ // Reject the promise if there's an error
326
+ audioPlayer.onerror = (event) => {
327
+ console.error('Server-based TTS playback error:', event);
328
+ reject(event);
329
+ };
330
+
331
+ isPlaying = true;
332
+ });
333
+ } catch (error) {
334
+ console.error('Error calling server:', error);
335
+ throw error;
336
+ }
337
+ }
338
+ }
339
+
340
+