Jaward commited on
Commit
1520236
·
verified ·
1 Parent(s): 175519f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +155 -278
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
- return f"/gradio_api/file={local_path}"
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', async (dataElement) => {
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', async (slideContent) => {
1149
  console.log("Slide content element found");
1150
 
1151
  // Initialize audio elements
1152
- audioElements = await initializeAudioElements(lectureData.audioFiles);
1153
- console.log(`Initialized ${audioElements.length} audio elements`);
 
 
 
 
 
 
 
1154
 
1155
- async function renderSlide() {
1156
  console.log("Rendering slide:", currentSlide + 1);
1157
- const iframe = document.getElementById('slide-iframe');
1158
- if (!iframe) {
1159
- console.error("Iframe not found");
1160
  return;
1161
  }
1162
-
1163
  if (currentSlide >= 0 && currentSlide < totalSlides && lectureData.htmlFiles[currentSlide]) {
1164
- const htmlUrl = lectureData.htmlFiles[currentSlide];
1165
- const success = await renderSlideWithRetry(iframe, htmlUrl);
1166
-
1167
- if (success) {
1168
- // Adjust font size based on content
1169
- iframe.onload = () => {
1170
- try {
 
 
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
- let baseFontSize = screenWidth >= 1920 ? 20 : screenWidth >= 1366 ? 18 : 16;
1178
- let adjustedFontSize = textLength > 1000 ? baseFontSize * 0.8 :
1179
- textLength > 500 ? baseFontSize * 0.9 :
1180
- baseFontSize;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- } catch (error) {
1192
- console.error("Error adjusting font size:", error);
1193
- }
1194
- };
1195
  } else {
1196
- iframe.src = "about:blank";
1197
- console.error("Failed to render slide");
1198
  }
1199
  } else {
1200
- iframe.src = "about:blank";
1201
- console.log("No valid slide content for index:", currentSlide);
 
 
 
1202
  }
1203
  }
1204
 
1205
- async function updateSlide(callback) {
1206
  console.log("Updating slide to index:", currentSlide);
1207
- await renderSlide();
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
- async function updateAudioSources(audioUrls) {
1224
  console.log("Updating audio sources:", audioUrls);
1225
- for (let i = 0; i < audioUrls.length; i++) {
1226
- const url = audioUrls[i];
1227
- const audio = audioElements[i];
1228
- if (audio && url) {
1229
- const exists = await checkFileExists(url);
1230
- if (exists) {
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 ${i} not found`);
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
- // Only update slide if we're not already on the correct slide
1311
- if (currentSlide !== currentAudioIndex) {
1312
- currentSlide = currentAudioIndex;
1313
- updateSlide(() => {
1314
- playCurrentAudio();
1315
- });
1316
- } else {
1317
- playCurrentAudio();
1318
- }
 
 
1319
 
1320
- function playCurrentAudio() {
1321
- if (!isPlaying) return;
1322
-
1323
- const audio = audioElements[currentAudioIndex];
1324
- if (audio && audio.play) {
1325
- audioElements.forEach(a => a.style.border = 'none');
1326
- audio.style.border = '5px solid #16cd16';
1327
- audio.style.borderRadius = '30px';
1328
- console.log(`Attempting to play audio for slide ${currentAudioIndex + 1}`);
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(`Retry succeeded for slide ${currentAudioIndex + 1}`);
1355
  audio.onended = null;
1356
  audio.addEventListener('ended', () => {
1357
  if (isPlaying) {
1358
  console.log(`Audio ended for slide ${currentAudioIndex + 1}`);
1359
  currentAudioIndex++;
1360
- if (currentAudioIndex >= totalSlides) {
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(`Retry failed for slide ${currentAudioIndex + 1}:`, e);
1374
- currentAudioIndex++;
1375
- if (currentAudioIndex >= totalSlides) {
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
- }, 500);
1388
- });
1389
- } else {
1390
- currentAudioIndex++;
1391
- if (currentAudioIndex >= totalSlides) {
1392
- isPlaying = false;
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=None):
2345
- # Handle case when only event is provided
2346
- if notes is None:
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")