Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -32,9 +32,7 @@ import markdown
|
|
32 |
import PyPDF2
|
33 |
import io
|
34 |
import copy
|
35 |
-
from pathlib import Path
|
36 |
|
37 |
-
gr.set_static_paths(paths=[Path.cwd().absolute()/"outputs"])
|
38 |
def get_instructor_name(speaker):
|
39 |
instructor_names = {
|
40 |
"feynman.mp3": "Professor Richard Feynman",
|
@@ -408,8 +406,8 @@ def create_zip_of_files(file_paths):
|
|
408 |
|
409 |
# Access local files
|
410 |
def get_gradio_file_url(local_path):
|
411 |
-
|
412 |
-
|
413 |
|
414 |
# Async generate lecture materials and audio
|
415 |
async def on_generate(api_service, api_key, serpapi_key, title, lecture_content_description, lecture_type, lecture_style, speaker_audio, num_slides):
|
@@ -987,13 +985,6 @@ Example: 'Received {total_slides} slides, {total_slides} scripts, and HTML files
|
|
987 |
js_code = """
|
988 |
() => {
|
989 |
// Function to wait for an element to appear in the DOM
|
990 |
-
window.addEventListener('load', function () {
|
991 |
-
gradioURL = window.location.href
|
992 |
-
if (!gradioURL.endsWith('?__theme=light')) {
|
993 |
-
window.location.replace(gradioURL + '?__theme=light');
|
994 |
-
}
|
995 |
-
});
|
996 |
-
|
997 |
function waitForElement(selector, callback, maxAttempts = 50, interval = 100) {
|
998 |
let attempts = 0;
|
999 |
const intervalId = setInterval(() => {
|
@@ -1010,119 +1001,16 @@ js_code = """
|
|
1010 |
}, interval);
|
1011 |
}
|
1012 |
|
1013 |
-
// Function to check if a file exists with retries
|
1014 |
-
async function checkFileExists(url, maxRetries = 5, delay = 1000) {
|
1015 |
-
for (let i = 0; i < maxRetries; i++) {
|
1016 |
-
try {
|
1017 |
-
const response = await fetch(url, { method: 'HEAD' });
|
1018 |
-
if (response.ok) {
|
1019 |
-
console.log(`File exists: ${url}`);
|
1020 |
-
return true;
|
1021 |
-
}
|
1022 |
-
// Fallback: Some servers disallow HEAD, try GET request
|
1023 |
-
if (response.status === 405 || response.status === 403) {
|
1024 |
-
try {
|
1025 |
-
const getResp = await fetch(url, { method: 'GET' });
|
1026 |
-
if (getResp.ok) {
|
1027 |
-
console.log(`File exists (GET fallback): ${url}`);
|
1028 |
-
return true;
|
1029 |
-
}
|
1030 |
-
} catch (err) {
|
1031 |
-
console.error(`GET fallback failed for ${url}:`, err);
|
1032 |
-
}
|
1033 |
-
}
|
1034 |
-
console.log(`File not found (attempt ${i + 1}/${maxRetries}): ${url}`);
|
1035 |
-
await new Promise(resolve => setTimeout(resolve, delay));
|
1036 |
-
} catch (error) {
|
1037 |
-
console.error(`Error checking file (attempt ${i + 1}/${maxRetries}):`, error);
|
1038 |
-
await new Promise(resolve => setTimeout(resolve, delay));
|
1039 |
-
}
|
1040 |
-
}
|
1041 |
-
return false;
|
1042 |
-
}
|
1043 |
-
|
1044 |
-
// Function to validate and initialize audio elements
|
1045 |
-
async function initializeAudioElements(audioUrls) {
|
1046 |
-
console.log("Initializing audio elements with URLs:", audioUrls);
|
1047 |
-
const audioElements = [];
|
1048 |
-
|
1049 |
-
for (let i = 0; i < audioUrls.length; i++) {
|
1050 |
-
const url = audioUrls[i];
|
1051 |
-
const audioId = `audio-${i+1}`;
|
1052 |
-
let audio = document.getElementById(audioId);
|
1053 |
-
|
1054 |
-
if (!audio) {
|
1055 |
-
console.log(`Creating new audio element: ${audioId}`);
|
1056 |
-
audio = document.createElement('audio');
|
1057 |
-
audio.id = audioId;
|
1058 |
-
audio.controls = true;
|
1059 |
-
audio.style.display = 'inline-block';
|
1060 |
-
audio.style.margin = '0 10px';
|
1061 |
-
audio.style.width = '200px';
|
1062 |
-
|
1063 |
-
// Find the audio container and append the new element
|
1064 |
-
const audioContainer = document.querySelector('.audio-timeline');
|
1065 |
-
if (audioContainer) {
|
1066 |
-
audioContainer.appendChild(audio);
|
1067 |
-
}
|
1068 |
-
}
|
1069 |
-
|
1070 |
-
if (url) {
|
1071 |
-
const exists = await checkFileExists(url);
|
1072 |
-
if (exists) {
|
1073 |
-
audio.src = url;
|
1074 |
-
audio.load();
|
1075 |
-
console.log(`Audio source set for ${audioId}: ${url}`);
|
1076 |
-
} else {
|
1077 |
-
console.error(`Audio file not found: ${url}`);
|
1078 |
-
audio.innerHTML = "<span>Audio unavailable</span>";
|
1079 |
-
}
|
1080 |
-
} else {
|
1081 |
-
console.log(`No URL provided for ${audioId}`);
|
1082 |
-
audio.innerHTML = "<span>No audio</span>";
|
1083 |
-
}
|
1084 |
-
|
1085 |
-
audioElements.push(audio);
|
1086 |
-
}
|
1087 |
-
|
1088 |
-
return audioElements;
|
1089 |
-
}
|
1090 |
-
|
1091 |
-
// Function to render slide with retries
|
1092 |
-
async function renderSlideWithRetry(iframe, url, maxRetries = 5) {
|
1093 |
-
console.log(`Attempting to render slide: ${url}`);
|
1094 |
-
|
1095 |
-
for (let i = 0; i < maxRetries; i++) {
|
1096 |
-
try {
|
1097 |
-
const exists = await checkFileExists(url);
|
1098 |
-
if (exists) {
|
1099 |
-
iframe.src = url;
|
1100 |
-
console.log(`Slide rendered successfully: ${url}`);
|
1101 |
-
return true;
|
1102 |
-
}
|
1103 |
-
console.log(`Slide not found (attempt ${i + 1}/${maxRetries}): ${url}`);
|
1104 |
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
1105 |
-
} catch (error) {
|
1106 |
-
console.error(`Error rendering slide (attempt ${i + 1}/${maxRetries}):`, error);
|
1107 |
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
1108 |
-
}
|
1109 |
-
}
|
1110 |
-
|
1111 |
-
console.error(`Failed to render slide after ${maxRetries} attempts: ${url}`);
|
1112 |
-
return false;
|
1113 |
-
}
|
1114 |
-
|
1115 |
// Main initialization function
|
1116 |
function initializeSlides() {
|
1117 |
console.log("Initializing slides...");
|
1118 |
|
1119 |
// Wait for lecture-data to load the JSON data
|
1120 |
-
waitForElement('#lecture-data',
|
1121 |
if (!dataElement.textContent) {
|
1122 |
console.error("Lecture data element is empty");
|
1123 |
return;
|
1124 |
}
|
1125 |
-
|
1126 |
let lectureData;
|
1127 |
try {
|
1128 |
lectureData = JSON.parse(dataElement.textContent);
|
@@ -1143,68 +1031,96 @@ js_code = """
|
|
1143 |
let isPlaying = false;
|
1144 |
let hasNavigated = false;
|
1145 |
let currentAudioIndex = 0;
|
|
|
|
|
1146 |
|
1147 |
// Wait for slide-content element
|
1148 |
-
waitForElement('#slide-content',
|
1149 |
console.log("Slide content element found");
|
1150 |
|
1151 |
// Initialize audio elements
|
1152 |
-
|
1153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1154 |
|
1155 |
-
|
1156 |
console.log("Rendering slide:", currentSlide + 1);
|
1157 |
-
|
1158 |
-
|
1159 |
-
console.error("Iframe not found");
|
1160 |
return;
|
1161 |
}
|
1162 |
-
|
1163 |
if (currentSlide >= 0 && currentSlide < totalSlides && lectureData.htmlFiles[currentSlide]) {
|
1164 |
-
const
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
|
1169 |
-
|
1170 |
-
|
|
|
|
|
1171 |
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
1172 |
const body = doc.body;
|
1173 |
if (body) {
|
1174 |
const textLength = body.textContent.length;
|
1175 |
const screenWidth = window.innerWidth;
|
|
|
1176 |
|
1177 |
-
|
1178 |
-
let
|
1179 |
-
|
1180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1181 |
|
|
|
1182 |
adjustedFontSize = Math.max(14, Math.min(24, adjustedFontSize));
|
1183 |
|
|
|
1184 |
const elements = body.getElementsByTagName('*');
|
1185 |
for (let elem of elements) {
|
1186 |
elem.style.fontSize = `${adjustedFontSize}px`;
|
1187 |
}
|
1188 |
|
1189 |
-
console.log(`Adjusted font size to ${adjustedFontSize}px`);
|
1190 |
}
|
1191 |
-
}
|
1192 |
-
|
1193 |
-
}
|
1194 |
-
};
|
1195 |
} else {
|
1196 |
-
|
1197 |
-
console.error("Failed to render slide");
|
1198 |
}
|
1199 |
} else {
|
1200 |
-
iframe
|
1201 |
-
|
|
|
|
|
|
|
1202 |
}
|
1203 |
}
|
1204 |
|
1205 |
-
|
1206 |
console.log("Updating slide to index:", currentSlide);
|
1207 |
-
|
1208 |
// Pause and reset all audio elements
|
1209 |
audioElements.forEach(audio => {
|
1210 |
if (audio && audio.pause) {
|
@@ -1220,28 +1136,18 @@ js_code = """
|
|
1220 |
}, 100);
|
1221 |
}
|
1222 |
|
1223 |
-
|
1224 |
console.log("Updating audio sources:", audioUrls);
|
1225 |
-
|
1226 |
-
const
|
1227 |
-
|
1228 |
-
|
1229 |
-
|
1230 |
-
|
1231 |
-
if (audio.src !== url) {
|
1232 |
-
audio.src = url;
|
1233 |
-
audio.load();
|
1234 |
-
console.log(`Updated audio-${i+1} src to:`, url);
|
1235 |
-
}
|
1236 |
-
} else {
|
1237 |
-
console.error(`Audio file not found after retries: ${url}`);
|
1238 |
-
audio.src = "";
|
1239 |
-
audio.innerHTML = "<span>Audio unavailable</span>";
|
1240 |
-
}
|
1241 |
} else if (!audio) {
|
1242 |
-
console.error(`Audio element at index ${
|
1243 |
}
|
1244 |
-
}
|
1245 |
}
|
1246 |
|
1247 |
function prevSlide() {
|
@@ -1306,100 +1212,103 @@ js_code = """
|
|
1306 |
// Start playback
|
1307 |
isPlaying = true;
|
1308 |
playIcon.className = 'fas fa-pause';
|
|
|
|
|
1309 |
|
1310 |
-
|
1311 |
-
|
1312 |
-
|
1313 |
-
|
1314 |
-
|
1315 |
-
|
1316 |
-
|
1317 |
-
|
1318 |
-
|
|
|
|
|
1319 |
|
1320 |
-
|
1321 |
-
|
1322 |
-
|
1323 |
-
|
1324 |
-
|
1325 |
-
|
1326 |
-
|
1327 |
-
|
1328 |
-
|
1329 |
-
|
1330 |
-
audio.play().then(() => {
|
1331 |
-
console.log(`Playing audio for slide ${currentAudioIndex + 1}`);
|
1332 |
-
audio.onended = null;
|
1333 |
-
audio.addEventListener('ended', () => {
|
1334 |
-
if (isPlaying) {
|
1335 |
-
console.log(`Audio ended for slide ${currentAudioIndex + 1}`);
|
1336 |
-
currentAudioIndex++;
|
1337 |
-
if (currentAudioIndex >= totalSlides) {
|
1338 |
-
isPlaying = false;
|
1339 |
-
playIcon.className = 'fas fa-play';
|
1340 |
-
audioElements.forEach(a => a.style.border = 'none');
|
1341 |
-
return;
|
1342 |
-
}
|
1343 |
-
currentSlide = currentAudioIndex;
|
1344 |
-
updateSlide(() => {
|
1345 |
-
playCurrentAudio();
|
1346 |
-
});
|
1347 |
-
}
|
1348 |
-
}, { once: true });
|
1349 |
-
}).catch(e => {
|
1350 |
-
console.error(`Audio play failed for slide ${currentAudioIndex + 1}:`, e);
|
1351 |
-
setTimeout(() => {
|
1352 |
-
if (isPlaying) {
|
1353 |
audio.play().then(() => {
|
1354 |
-
console.log(`
|
1355 |
audio.onended = null;
|
1356 |
audio.addEventListener('ended', () => {
|
1357 |
if (isPlaying) {
|
1358 |
console.log(`Audio ended for slide ${currentAudioIndex + 1}`);
|
1359 |
currentAudioIndex++;
|
1360 |
-
|
1361 |
-
isPlaying = false;
|
1362 |
-
playIcon.className = 'fas fa-play';
|
1363 |
-
audioElements.forEach(a => a.style.border = 'none');
|
1364 |
-
return;
|
1365 |
-
}
|
1366 |
-
currentSlide = currentAudioIndex;
|
1367 |
-
updateSlide(() => {
|
1368 |
-
playCurrentAudio();
|
1369 |
-
});
|
1370 |
}
|
1371 |
}, { once: true });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1372 |
}).catch(e => {
|
1373 |
-
console.error(`
|
1374 |
-
|
1375 |
-
|
1376 |
-
isPlaying = false;
|
1377 |
-
playIcon.className = 'fas fa-play';
|
1378 |
-
audioElements.forEach(a => a.style.border = 'none');
|
1379 |
-
return;
|
1380 |
}
|
1381 |
-
currentSlide = currentAudioIndex;
|
1382 |
-
updateSlide(() => {
|
1383 |
-
playCurrentAudio();
|
1384 |
-
});
|
1385 |
});
|
1386 |
-
}
|
1387 |
-
|
1388 |
-
|
1389 |
-
|
1390 |
-
|
1391 |
-
|
1392 |
-
|
1393 |
-
playIcon.className = 'fas fa-play';
|
1394 |
-
audioElements.forEach(a => a.style.border = 'none');
|
1395 |
-
return;
|
1396 |
-
}
|
1397 |
-
currentSlide = currentAudioIndex;
|
1398 |
-
updateSlide(() => {
|
1399 |
-
playCurrentAudio();
|
1400 |
});
|
1401 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1402 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1403 |
}
|
1404 |
|
1405 |
function toggleFullScreen() {
|
@@ -1441,35 +1350,7 @@ js_code = """
|
|
1441 |
});
|
1442 |
|
1443 |
waitForElement('#reload-btn', (reloadBtn) => {
|
1444 |
-
reloadBtn.addEventListener('click',
|
1445 |
-
// Reset state
|
1446 |
-
currentSlide = 0;
|
1447 |
-
currentAudioIndex = 0;
|
1448 |
-
isPlaying = false;
|
1449 |
-
|
1450 |
-
// Reset play button
|
1451 |
-
const playBtn = document.getElementById('play-btn');
|
1452 |
-
if (playBtn) {
|
1453 |
-
const playIcon = playBtn.querySelector('i');
|
1454 |
-
if (playIcon) {
|
1455 |
-
playIcon.className = 'fas fa-play';
|
1456 |
-
}
|
1457 |
-
}
|
1458 |
-
|
1459 |
-
// Reset audio elements
|
1460 |
-
audioElements.forEach(audio => {
|
1461 |
-
if (audio) {
|
1462 |
-
audio.pause();
|
1463 |
-
audio.currentTime = 0;
|
1464 |
-
audio.style.border = 'none';
|
1465 |
-
}
|
1466 |
-
});
|
1467 |
-
|
1468 |
-
// Force reload first slide
|
1469 |
-
updateSlide(() => {
|
1470 |
-
console.log("Reloaded first slide");
|
1471 |
-
});
|
1472 |
-
});
|
1473 |
console.log("Attached event listener to reload-btn");
|
1474 |
});
|
1475 |
|
@@ -2341,13 +2222,9 @@ with gr.Blocks(
|
|
2341 |
"audience": audience_val or "University"
|
2342 |
}
|
2343 |
|
2344 |
-
def show_note_content(evt: dict, notes
|
2345 |
-
# Handle
|
2346 |
-
if
|
2347 |
-
return gr.update(value="Click any button above to generate content...")
|
2348 |
-
|
2349 |
-
# evt['index'] gives the row index
|
2350 |
-
idx = evt.get('index', 0)
|
2351 |
if 0 <= idx < len(notes):
|
2352 |
note = notes[idx]
|
2353 |
note_file = os.path.join(OUTPUT_DIR, f"{note['title']}.txt")
|
|
|
32 |
import PyPDF2
|
33 |
import io
|
34 |
import copy
|
|
|
35 |
|
|
|
36 |
def get_instructor_name(speaker):
|
37 |
instructor_names = {
|
38 |
"feynman.mp3": "Professor Richard Feynman",
|
|
|
406 |
|
407 |
# Access local files
|
408 |
def get_gradio_file_url(local_path):
|
409 |
+
relative_path = os.path.relpath(local_path, os.getcwd())
|
410 |
+
return f"/gradio_api/file={relative_path}"
|
411 |
|
412 |
# Async generate lecture materials and audio
|
413 |
async def on_generate(api_service, api_key, serpapi_key, title, lecture_content_description, lecture_type, lecture_style, speaker_audio, num_slides):
|
|
|
985 |
js_code = """
|
986 |
() => {
|
987 |
// Function to wait for an element to appear in the DOM
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
988 |
function waitForElement(selector, callback, maxAttempts = 50, interval = 100) {
|
989 |
let attempts = 0;
|
990 |
const intervalId = setInterval(() => {
|
|
|
1001 |
}, interval);
|
1002 |
}
|
1003 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1004 |
// Main initialization function
|
1005 |
function initializeSlides() {
|
1006 |
console.log("Initializing slides...");
|
1007 |
|
1008 |
// Wait for lecture-data to load the JSON data
|
1009 |
+
waitForElement('#lecture-data', (dataElement) => {
|
1010 |
if (!dataElement.textContent) {
|
1011 |
console.error("Lecture data element is empty");
|
1012 |
return;
|
1013 |
}
|
|
|
1014 |
let lectureData;
|
1015 |
try {
|
1016 |
lectureData = JSON.parse(dataElement.textContent);
|
|
|
1031 |
let isPlaying = false;
|
1032 |
let hasNavigated = false;
|
1033 |
let currentAudioIndex = 0;
|
1034 |
+
let lastRenderedSlide = -1;
|
1035 |
+
let isReloading = false;
|
1036 |
|
1037 |
// Wait for slide-content element
|
1038 |
+
waitForElement('#slide-content', (slideContent) => {
|
1039 |
console.log("Slide content element found");
|
1040 |
|
1041 |
// Initialize audio elements
|
1042 |
+
for (let i = 0; i < totalSlides; i++) {
|
1043 |
+
const audio = document.getElementById(`audio-${i+1}`);
|
1044 |
+
if (audio) {
|
1045 |
+
audioElements.push(audio);
|
1046 |
+
console.log(`Found audio element audio-${i+1}:`, audio);
|
1047 |
+
} else {
|
1048 |
+
console.error(`Audio element audio-${i+1} not found`);
|
1049 |
+
}
|
1050 |
+
}
|
1051 |
|
1052 |
+
function renderSlide() {
|
1053 |
console.log("Rendering slide:", currentSlide + 1);
|
1054 |
+
if (currentSlide === lastRenderedSlide && !isReloading) {
|
1055 |
+
console.log("Slide already rendered, skipping");
|
|
|
1056 |
return;
|
1057 |
}
|
1058 |
+
|
1059 |
if (currentSlide >= 0 && currentSlide < totalSlides && lectureData.htmlFiles[currentSlide]) {
|
1060 |
+
const iframe = document.getElementById('slide-iframe');
|
1061 |
+
if (iframe) {
|
1062 |
+
iframe.src = lectureData.htmlFiles[currentSlide];
|
1063 |
+
console.log("Set iframe src to:", lectureData.htmlFiles[currentSlide]);
|
1064 |
+
lastRenderedSlide = currentSlide;
|
1065 |
+
|
1066 |
+
// Adjust font size based on content length and screen size
|
1067 |
+
waitForElement('iframe', (iframe) => {
|
1068 |
+
iframe.onload = () => {
|
1069 |
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
1070 |
const body = doc.body;
|
1071 |
if (body) {
|
1072 |
const textLength = body.textContent.length;
|
1073 |
const screenWidth = window.innerWidth;
|
1074 |
+
const screenHeight = window.innerHeight;
|
1075 |
|
1076 |
+
// Base font size calculation
|
1077 |
+
let baseFontSize;
|
1078 |
+
if (screenWidth >= 1920) {
|
1079 |
+
baseFontSize = 20; // Large screens
|
1080 |
+
} else if (screenWidth >= 1366) {
|
1081 |
+
baseFontSize = 18; // Medium screens
|
1082 |
+
} else {
|
1083 |
+
baseFontSize = 16; // Small screens
|
1084 |
+
}
|
1085 |
+
|
1086 |
+
// Adjust based on content length
|
1087 |
+
let adjustedFontSize;
|
1088 |
+
if (textLength > 1000) {
|
1089 |
+
adjustedFontSize = baseFontSize * 0.8; // Reduce for long content
|
1090 |
+
} else if (textLength > 500) {
|
1091 |
+
adjustedFontSize = baseFontSize * 0.9; // Slightly reduce for medium content
|
1092 |
+
} else {
|
1093 |
+
adjustedFontSize = baseFontSize; // Keep base size for short content
|
1094 |
+
}
|
1095 |
|
1096 |
+
// Ensure minimum and maximum sizes
|
1097 |
adjustedFontSize = Math.max(14, Math.min(24, adjustedFontSize));
|
1098 |
|
1099 |
+
// Apply to all elements
|
1100 |
const elements = body.getElementsByTagName('*');
|
1101 |
for (let elem of elements) {
|
1102 |
elem.style.fontSize = `${adjustedFontSize}px`;
|
1103 |
}
|
1104 |
|
1105 |
+
console.log(`Adjusted font size to ${adjustedFontSize}px for ${textLength} characters on ${screenWidth}x${screenHeight} screen`);
|
1106 |
}
|
1107 |
+
};
|
1108 |
+
});
|
|
|
|
|
1109 |
} else {
|
1110 |
+
console.error("Iframe not found");
|
|
|
1111 |
}
|
1112 |
} else {
|
1113 |
+
const iframe = document.getElementById('slide-iframe');
|
1114 |
+
if (iframe) {
|
1115 |
+
iframe.src = "about:blank";
|
1116 |
+
console.log("No valid slide content for index:", currentSlide);
|
1117 |
+
}
|
1118 |
}
|
1119 |
}
|
1120 |
|
1121 |
+
function updateSlide(callback) {
|
1122 |
console.log("Updating slide to index:", currentSlide);
|
1123 |
+
renderSlide();
|
1124 |
// Pause and reset all audio elements
|
1125 |
audioElements.forEach(audio => {
|
1126 |
if (audio && audio.pause) {
|
|
|
1136 |
}, 100);
|
1137 |
}
|
1138 |
|
1139 |
+
function updateAudioSources(audioUrls) {
|
1140 |
console.log("Updating audio sources:", audioUrls);
|
1141 |
+
audioUrls.forEach((url, index) => {
|
1142 |
+
const audio = audioElements[index];
|
1143 |
+
if (audio && url && audio.src !== url) {
|
1144 |
+
audio.src = url;
|
1145 |
+
audio.load();
|
1146 |
+
console.log(`Updated audio-${index+1} src to:`, url);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1147 |
} else if (!audio) {
|
1148 |
+
console.error(`Audio element at index ${index} not found`);
|
1149 |
}
|
1150 |
+
});
|
1151 |
}
|
1152 |
|
1153 |
function prevSlide() {
|
|
|
1212 |
// Start playback
|
1213 |
isPlaying = true;
|
1214 |
playIcon.className = 'fas fa-pause';
|
1215 |
+
currentSlide = 0;
|
1216 |
+
currentAudioIndex = 0;
|
1217 |
|
1218 |
+
updateSlide(() => {
|
1219 |
+
function playNext() {
|
1220 |
+
if (currentAudioIndex >= totalSlides || !isPlaying) {
|
1221 |
+
isPlaying = false;
|
1222 |
+
playIcon.className = 'fas fa-play';
|
1223 |
+
audioElements.forEach(audio => {
|
1224 |
+
if (audio) audio.style.border = 'none';
|
1225 |
+
});
|
1226 |
+
console.log("Finished playing all slides or paused");
|
1227 |
+
return;
|
1228 |
+
}
|
1229 |
|
1230 |
+
currentSlide = currentAudioIndex;
|
1231 |
+
updateSlide(() => {
|
1232 |
+
const audio = audioElements[currentAudioIndex];
|
1233 |
+
if (audio && audio.play) {
|
1234 |
+
audioElements.forEach(a => a.style.border = 'none');
|
1235 |
+
audio.style.border = '5px solid #16cd16';
|
1236 |
+
audio.style.borderRadius = '30px';
|
1237 |
+
console.log(`Attempting to play audio for slide ${currentAudioIndex + 1}`);
|
1238 |
+
|
1239 |
+
const playAudio = () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1240 |
audio.play().then(() => {
|
1241 |
+
console.log(`Playing audio for slide ${currentAudioIndex + 1}`);
|
1242 |
audio.onended = null;
|
1243 |
audio.addEventListener('ended', () => {
|
1244 |
if (isPlaying) {
|
1245 |
console.log(`Audio ended for slide ${currentAudioIndex + 1}`);
|
1246 |
currentAudioIndex++;
|
1247 |
+
playNext();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1248 |
}
|
1249 |
}, { once: true });
|
1250 |
+
|
1251 |
+
const checkDuration = setInterval(() => {
|
1252 |
+
if (!isPlaying) {
|
1253 |
+
clearInterval(checkDuration);
|
1254 |
+
return;
|
1255 |
+
}
|
1256 |
+
if (audio.duration && audio.currentTime >= audio.duration - 0.1) {
|
1257 |
+
console.log(`Fallback: Audio for slide ${currentAudioIndex + 1} considered ended`);
|
1258 |
+
clearInterval(checkDuration);
|
1259 |
+
audio.onended = null;
|
1260 |
+
currentAudioIndex++;
|
1261 |
+
playNext();
|
1262 |
+
}
|
1263 |
+
}, 1000);
|
1264 |
}).catch(e => {
|
1265 |
+
console.error(`Audio play failed for slide ${currentAudioIndex + 1}:`, e);
|
1266 |
+
if (isPlaying) {
|
1267 |
+
setTimeout(playAudio, 500);
|
|
|
|
|
|
|
|
|
1268 |
}
|
|
|
|
|
|
|
|
|
1269 |
});
|
1270 |
+
};
|
1271 |
+
|
1272 |
+
playAudio();
|
1273 |
+
} else {
|
1274 |
+
currentAudioIndex++;
|
1275 |
+
playNext();
|
1276 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1277 |
});
|
1278 |
}
|
1279 |
+
playNext();
|
1280 |
+
});
|
1281 |
+
}
|
1282 |
+
|
1283 |
+
function reloadSlides() {
|
1284 |
+
console.log("Reloading slides");
|
1285 |
+
isReloading = true;
|
1286 |
+
lastRenderedSlide = -1;
|
1287 |
+
currentSlide = 0;
|
1288 |
+
currentAudioIndex = 0;
|
1289 |
+
isPlaying = false;
|
1290 |
+
|
1291 |
+
// Reset play button
|
1292 |
+
const playBtn = document.getElementById('play-btn');
|
1293 |
+
if (playBtn) {
|
1294 |
+
const playIcon = playBtn.querySelector('i');
|
1295 |
+
if (playIcon) {
|
1296 |
+
playIcon.className = 'fas fa-play';
|
1297 |
+
}
|
1298 |
}
|
1299 |
+
|
1300 |
+
// Reset audio elements
|
1301 |
+
audioElements.forEach(audio => {
|
1302 |
+
if (audio) {
|
1303 |
+
audio.pause();
|
1304 |
+
audio.currentTime = 0;
|
1305 |
+
audio.style.border = 'none';
|
1306 |
+
}
|
1307 |
+
});
|
1308 |
+
|
1309 |
+
updateSlide(() => {
|
1310 |
+
isReloading = false;
|
1311 |
+
});
|
1312 |
}
|
1313 |
|
1314 |
function toggleFullScreen() {
|
|
|
1350 |
});
|
1351 |
|
1352 |
waitForElement('#reload-btn', (reloadBtn) => {
|
1353 |
+
reloadBtn.addEventListener('click', reloadSlides);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1354 |
console.log("Attached event listener to reload-btn");
|
1355 |
});
|
1356 |
|
|
|
2222 |
"audience": audience_val or "University"
|
2223 |
}
|
2224 |
|
2225 |
+
def show_note_content(evt: dict, notes):
|
2226 |
+
# Handle both event dict and direct index
|
2227 |
+
idx = evt.get('index', 0) if isinstance(evt, dict) else evt
|
|
|
|
|
|
|
|
|
2228 |
if 0 <= idx < len(notes):
|
2229 |
note = notes[idx]
|
2230 |
note_file = os.path.join(OUTPUT_DIR, f"{note['title']}.txt")
|