Spaces:
Running
Running
burtenshaw
commited on
Commit
·
dfd0eac
1
Parent(s):
99f7dee
another status fix
Browse files- app/app.py +161 -51
app/app.py
CHANGED
@@ -278,16 +278,22 @@ def load_css(css_filename="style.scss"):
|
|
278 |
# --- Gradio Workflow Functions ---
|
279 |
|
280 |
|
281 |
-
def step1_fetch_and_generate_presentation(
|
282 |
-
url, status_textbox, progress=gr.Progress(track_tqdm=True)
|
283 |
-
):
|
284 |
"""Fetches content, generates presentation markdown, prepares editor, and copies template. Uses caching based on URL."""
|
285 |
if not url:
|
286 |
raise gr.Error("Please enter a URL.")
|
287 |
logger.info(f"Step 1: Fetching & Generating for {url}")
|
288 |
|
289 |
status_update = f"Starting Step 1: Fetching content from {url}..."
|
290 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
|
292 |
# --- Cache Check ---
|
293 |
try:
|
@@ -340,7 +346,15 @@ def step1_fetch_and_generate_presentation(
|
|
340 |
status_update = (
|
341 |
"Loaded presentation from cache. Preparing editor..."
|
342 |
)
|
343 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
344 |
logger.info(f"Using cached data for {len(slides_data)} slides.")
|
345 |
# Final yield must match outputs list
|
346 |
yield (
|
@@ -370,14 +384,30 @@ def step1_fetch_and_generate_presentation(
|
|
370 |
raise gr.Error("LLM Client not initialized. Check API Key.")
|
371 |
|
372 |
status_update = "Fetching webpage content..."
|
373 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
374 |
web_content = fetch_webpage_content(url)
|
375 |
if not web_content:
|
376 |
raise gr.Error("Failed to fetch or parse content from the URL.")
|
377 |
|
378 |
progress(0.3, desc="Generating presentation with LLM...")
|
379 |
status_update = "Generating presentation with LLM..."
|
380 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
381 |
try:
|
382 |
presentation_md = generate_presentation_with_llm(
|
383 |
hf_client, LLM_MODEL, PRESENTATION_PROMPT, web_content, url
|
@@ -402,7 +432,15 @@ def step1_fetch_and_generate_presentation(
|
|
402 |
|
403 |
progress(0.7, desc="Parsing presentation slides...")
|
404 |
status_update = "Parsing presentation slides..."
|
405 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
406 |
slides_data = parse_presentation_markdown(presentation_md)
|
407 |
if not slides_data:
|
408 |
logger.error("Parsing markdown resulted in zero slides.")
|
@@ -446,7 +484,15 @@ def step1_fetch_and_generate_presentation(
|
|
446 |
|
447 |
progress(0.9, desc="Preparing editor...")
|
448 |
status_update = "Generated presentation. Preparing editor..."
|
449 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
450 |
logger.info(f"Prepared data for {len(slides_data)} slides.")
|
451 |
|
452 |
# Final yield must match outputs list
|
@@ -463,7 +509,15 @@ def step1_fetch_and_generate_presentation(
|
|
463 |
except Exception as e:
|
464 |
logger.error(f"Error in step 1 (fetch/generate): {e}", exc_info=True)
|
465 |
status_update = f"Error during presentation setup: {e}"
|
466 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
467 |
# Yield a final tuple matching outputs, indicating error state if possible
|
468 |
yield (
|
469 |
gr.update(value=status_update),
|
@@ -482,7 +536,6 @@ def step2_build_slides(
|
|
482 |
state_temp_dir,
|
483 |
state_md_path,
|
484 |
state_slides_data,
|
485 |
-
status_textbox,
|
486 |
*editors,
|
487 |
progress=gr.Progress(track_tqdm=True),
|
488 |
):
|
@@ -491,7 +544,14 @@ def step2_build_slides(
|
|
491 |
raise gr.Error("Session state missing.")
|
492 |
logger.info("Step 2: Building Slides (PDF + Images)")
|
493 |
status_update = "Starting Step 2: Building slides..."
|
494 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
495 |
|
496 |
num_slides = len(state_slides_data)
|
497 |
MAX_SLIDES = 20
|
@@ -505,7 +565,14 @@ def step2_build_slides(
|
|
505 |
|
506 |
progress(0.1, desc="Saving edited markdown...")
|
507 |
status_update = "Saving edited markdown..."
|
508 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
509 |
updated_slides = []
|
510 |
for i in range(num_slides):
|
511 |
updated_slides.append(
|
@@ -518,7 +585,14 @@ def step2_build_slides(
|
|
518 |
logger.info(f"Saved edited markdown: {state_md_path}")
|
519 |
except IOError as e:
|
520 |
status_update = f"Failed to save markdown: {e}"
|
521 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
522 |
# Final yield must match outputs
|
523 |
yield (
|
524 |
gr.update(value=status_update),
|
@@ -527,12 +601,19 @@ def step2_build_slides(
|
|
527 |
gr.update(),
|
528 |
gr.update(visible=True),
|
529 |
gr.update(),
|
530 |
-
)
|
531 |
raise gr.Error(f"Failed to save markdown: {e}")
|
532 |
|
533 |
progress(0.3, desc="Generating PDF...")
|
534 |
status_update = "Generating PDF..."
|
535 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
536 |
pdf_output_path = os.path.join(state_temp_dir, "presentation.pdf")
|
537 |
try:
|
538 |
generated_pdf_path = generate_pdf_from_markdown(state_md_path, pdf_output_path)
|
@@ -540,7 +621,14 @@ def step2_build_slides(
|
|
540 |
raise gr.Error("PDF generation failed (check logs).")
|
541 |
except gr.Error as e:
|
542 |
status_update = f"PDF Generation Error: {e}"
|
543 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
544 |
# Final yield must match outputs
|
545 |
yield (
|
546 |
gr.update(value=status_update),
|
@@ -549,11 +637,18 @@ def step2_build_slides(
|
|
549 |
gr.update(),
|
550 |
gr.update(visible=True),
|
551 |
gr.update(),
|
552 |
-
)
|
553 |
raise e
|
554 |
except Exception as e:
|
555 |
status_update = f"Unexpected PDF Error: {e}"
|
556 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
557 |
# Final yield must match outputs
|
558 |
yield (
|
559 |
gr.update(value=status_update),
|
@@ -562,12 +657,19 @@ def step2_build_slides(
|
|
562 |
gr.update(),
|
563 |
gr.update(visible=True),
|
564 |
gr.update(),
|
565 |
-
)
|
566 |
raise gr.Error(f"Unexpected error generating PDF: {e}")
|
567 |
|
568 |
progress(0.7, desc="Converting PDF to images...")
|
569 |
status_update = "Converting PDF to images..."
|
570 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
571 |
pdf_images = []
|
572 |
try:
|
573 |
pdf_images = convert_pdf_to_images(
|
@@ -584,7 +686,14 @@ def step2_build_slides(
|
|
584 |
except Exception as e:
|
585 |
logger.error(f"Error converting PDF to images: {e}", exc_info=True)
|
586 |
status_update = f"Failed to convert PDF to images: {e}"
|
587 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
588 |
# Final yield must match outputs
|
589 |
yield (
|
590 |
gr.update(value=status_update),
|
@@ -593,7 +702,7 @@ def step2_build_slides(
|
|
593 |
gr.update(),
|
594 |
gr.update(visible=True),
|
595 |
gr.update(value=generated_pdf_path, visible=True),
|
596 |
-
)
|
597 |
# Proceed without images? Or raise error? Let's raise.
|
598 |
raise gr.Error(f"Failed to convert PDF to images: {e}")
|
599 |
|
@@ -617,13 +726,11 @@ def step3_generate_audio(*args, progress=gr.Progress(track_tqdm=True)):
|
|
617 |
# args[0]: state_temp_dir
|
618 |
# args[1]: state_md_path
|
619 |
# args[2]: original_slides_data
|
620 |
-
# args[3]:
|
621 |
-
# args[4...]: editors
|
622 |
|
623 |
state_temp_dir = args[0]
|
624 |
state_md_path = args[1]
|
625 |
original_slides_data = args[2]
|
626 |
-
status_textbox = args[3]
|
627 |
|
628 |
# editors = args[3:] # Old slicing
|
629 |
num_slides = len(original_slides_data)
|
@@ -632,10 +739,10 @@ def step3_generate_audio(*args, progress=gr.Progress(track_tqdm=True)):
|
|
632 |
raise gr.Error("No slide data available. Please start over.")
|
633 |
|
634 |
MAX_SLIDES = 20 # Ensure this matches UI definition
|
635 |
-
# --- Adjust indices based on
|
636 |
-
# Start editors from index
|
637 |
-
code_editors_start_index =
|
638 |
-
notes_textboxes_start_index =
|
639 |
|
640 |
# Slice the *actual* edited values based on num_slides
|
641 |
edited_contents = args[
|
@@ -659,7 +766,9 @@ def step3_generate_audio(*args, progress=gr.Progress(track_tqdm=True)):
|
|
659 |
|
660 |
logger.info(f"Processing {num_slides} slides for audio generation.")
|
661 |
status_update = "Starting Step 3: Generating audio..."
|
662 |
-
yield status_update
|
|
|
|
|
663 |
|
664 |
audio_dir = os.path.join(state_temp_dir, "audio")
|
665 |
os.makedirs(audio_dir, exist_ok=True)
|
@@ -669,7 +778,9 @@ def step3_generate_audio(*args, progress=gr.Progress(track_tqdm=True)):
|
|
669 |
# but ensures the audio matches the *latest* notes displayed.
|
670 |
progress(0.1, desc="Saving latest notes...")
|
671 |
status_update = "Saving latest notes..."
|
672 |
-
yield status_update
|
|
|
|
|
673 |
updated_slides_data = []
|
674 |
for i in range(num_slides):
|
675 |
updated_slides_data.append(
|
@@ -689,7 +800,9 @@ def step3_generate_audio(*args, progress=gr.Progress(track_tqdm=True)):
|
|
689 |
warning_msg = f"Warning: Could not save latest notes to markdown file: {e}"
|
690 |
gr.Warning(warning_msg)
|
691 |
status_update += f" ({warning_msg})"
|
692 |
-
yield status_update
|
|
|
|
|
693 |
# Note: We continue processing audio even if saving markdown fails
|
694 |
# If we need to stop and return final state, do it here:
|
695 |
# yield (gr.update(value=status_update), state_audio_dir, gr.update(), gr.update(visible=True), *[gr.update()]*N, *[gr.update()]*N)
|
@@ -708,7 +821,9 @@ def step3_generate_audio(*args, progress=gr.Progress(track_tqdm=True)):
|
|
708 |
desc=f"Audio slide {slide_num}/{num_slides}",
|
709 |
)
|
710 |
status_update = f"Generating audio for slide {slide_num}/{num_slides}..."
|
711 |
-
yield status_update
|
|
|
|
|
712 |
|
713 |
output_file_path = Path(audio_dir) / f"{slide_num}.wav"
|
714 |
if not note_text or not note_text.strip():
|
@@ -787,7 +902,6 @@ def step4_generate_video(
|
|
787 |
state_temp_dir,
|
788 |
state_audio_dir,
|
789 |
state_pdf_path, # Use PDF path from state
|
790 |
-
status_textbox,
|
791 |
progress=gr.Progress(track_tqdm=True),
|
792 |
):
|
793 |
"""Generates the final video using PDF images and audio files."""
|
@@ -802,7 +916,7 @@ def step4_generate_video(
|
|
802 |
|
803 |
video_output_path = os.path.join(state_temp_dir, "final_presentation.mp4")
|
804 |
status_update = "Starting Step 4: Generating video..."
|
805 |
-
yield status_update
|
806 |
|
807 |
progress(0.1, desc="Preparing video components...")
|
808 |
pdf_images = [] # Initialize to ensure cleanup happens
|
@@ -819,7 +933,7 @@ def step4_generate_video(
|
|
819 |
# Convert PDF to images
|
820 |
progress(0.2, desc="Converting PDF to images...")
|
821 |
status_update = "Converting PDF back to images for video..."
|
822 |
-
yield status_update
|
823 |
pdf_images = convert_pdf_to_images(state_pdf_path, dpi=150)
|
824 |
if not pdf_images:
|
825 |
raise gr.Error(f"Failed to convert PDF ({state_pdf_path}) to images.")
|
@@ -831,12 +945,12 @@ def step4_generate_video(
|
|
831 |
logger.warning(warning_msg)
|
832 |
status_update += f" ({warning_msg})"
|
833 |
# yield status_update # Old yield
|
834 |
-
yield status_update
|
835 |
|
836 |
progress(0.5, desc="Creating individual video clips...")
|
837 |
status_update = "Creating individual video clips..."
|
838 |
# yield status_update # Old yield
|
839 |
-
yield status_update
|
840 |
buffer_seconds = 1.0
|
841 |
output_fps = 10
|
842 |
video_clips = create_video_clips(
|
@@ -849,7 +963,7 @@ def step4_generate_video(
|
|
849 |
progress(0.8, desc="Concatenating clips...")
|
850 |
status_update = "Concatenating clips into final video..."
|
851 |
# yield status_update # Old yield
|
852 |
-
yield status_update
|
853 |
concatenate_clips(video_clips, video_output_path, output_fps)
|
854 |
|
855 |
logger.info(f"Video concatenation complete: {video_output_path}")
|
@@ -857,7 +971,7 @@ def step4_generate_video(
|
|
857 |
progress(0.95, desc="Cleaning up temp images...")
|
858 |
status_update = "Cleaning up temporary image files..."
|
859 |
# yield status_update # Old yield
|
860 |
-
yield status_update
|
861 |
cleanup_temp_files(pdf_images) # Pass the list of image paths
|
862 |
|
863 |
except Exception as e:
|
@@ -865,13 +979,9 @@ def step4_generate_video(
|
|
865 |
cleanup_temp_files(pdf_images)
|
866 |
logger.error(f"Video generation failed: {e}", exc_info=True)
|
867 |
status_update = f"Video generation failed: {e}"
|
868 |
-
yield status_update
|
869 |
# Final yield must match outputs
|
870 |
-
yield (
|
871 |
-
gr.update(value=status_update),
|
872 |
-
gr.update(),
|
873 |
-
gr.update(visible=True),
|
874 |
-
) # Keep button visible
|
875 |
raise gr.Error(f"Video generation failed: {e}")
|
876 |
|
877 |
info_msg = f"Video generated: {os.path.basename(video_output_path)}"
|
@@ -1131,7 +1241,7 @@ with gr.Blocks(
|
|
1131 |
]
|
1132 |
btn_fetch_generate.click(
|
1133 |
fn=step1_fetch_and_generate_presentation,
|
1134 |
-
inputs=[input_url
|
1135 |
outputs=step1_outputs,
|
1136 |
show_progress="full",
|
1137 |
).then(
|
@@ -1171,7 +1281,7 @@ with gr.Blocks(
|
|
1171 |
|
1172 |
# Step 2 Click Handler
|
1173 |
step2_inputs = (
|
1174 |
-
[state_temp_dir, state_md_path, state_slides_data
|
1175 |
+ all_code_editors
|
1176 |
+ all_notes_textboxes
|
1177 |
)
|
@@ -1209,7 +1319,7 @@ with gr.Blocks(
|
|
1209 |
|
1210 |
# Step 3 Click Handler
|
1211 |
step3_inputs = (
|
1212 |
-
[state_temp_dir, state_md_path, state_slides_data
|
1213 |
+ all_code_editors
|
1214 |
+ all_notes_textboxes
|
1215 |
)
|
@@ -1239,7 +1349,7 @@ with gr.Blocks(
|
|
1239 |
)
|
1240 |
|
1241 |
# Step 4 Click Handler
|
1242 |
-
step4_inputs = [state_temp_dir, state_audio_dir, state_pdf_path
|
1243 |
step4_outputs = [
|
1244 |
status_textbox, # Added status output
|
1245 |
video_output, # Update video output in Tab 4
|
|
|
278 |
# --- Gradio Workflow Functions ---
|
279 |
|
280 |
|
281 |
+
def step1_fetch_and_generate_presentation(url, progress=gr.Progress(track_tqdm=True)):
|
|
|
|
|
282 |
"""Fetches content, generates presentation markdown, prepares editor, and copies template. Uses caching based on URL."""
|
283 |
if not url:
|
284 |
raise gr.Error("Please enter a URL.")
|
285 |
logger.info(f"Step 1: Fetching & Generating for {url}")
|
286 |
|
287 |
status_update = f"Starting Step 1: Fetching content from {url}..."
|
288 |
+
yield (
|
289 |
+
gr.update(value=status_update),
|
290 |
+
None,
|
291 |
+
None,
|
292 |
+
[],
|
293 |
+
gr.update(),
|
294 |
+
gr.update(),
|
295 |
+
gr.update(),
|
296 |
+
)
|
297 |
|
298 |
# --- Cache Check ---
|
299 |
try:
|
|
|
346 |
status_update = (
|
347 |
"Loaded presentation from cache. Preparing editor..."
|
348 |
)
|
349 |
+
yield (
|
350 |
+
gr.update(value=status_update),
|
351 |
+
None,
|
352 |
+
None,
|
353 |
+
[],
|
354 |
+
gr.update(),
|
355 |
+
gr.update(),
|
356 |
+
gr.update(),
|
357 |
+
)
|
358 |
logger.info(f"Using cached data for {len(slides_data)} slides.")
|
359 |
# Final yield must match outputs list
|
360 |
yield (
|
|
|
384 |
raise gr.Error("LLM Client not initialized. Check API Key.")
|
385 |
|
386 |
status_update = "Fetching webpage content..."
|
387 |
+
yield (
|
388 |
+
gr.update(value=status_update),
|
389 |
+
None,
|
390 |
+
None,
|
391 |
+
[],
|
392 |
+
gr.update(),
|
393 |
+
gr.update(),
|
394 |
+
gr.update(),
|
395 |
+
)
|
396 |
web_content = fetch_webpage_content(url)
|
397 |
if not web_content:
|
398 |
raise gr.Error("Failed to fetch or parse content from the URL.")
|
399 |
|
400 |
progress(0.3, desc="Generating presentation with LLM...")
|
401 |
status_update = "Generating presentation with LLM..."
|
402 |
+
yield (
|
403 |
+
gr.update(value=status_update),
|
404 |
+
None,
|
405 |
+
None,
|
406 |
+
[],
|
407 |
+
gr.update(),
|
408 |
+
gr.update(),
|
409 |
+
gr.update(),
|
410 |
+
)
|
411 |
try:
|
412 |
presentation_md = generate_presentation_with_llm(
|
413 |
hf_client, LLM_MODEL, PRESENTATION_PROMPT, web_content, url
|
|
|
432 |
|
433 |
progress(0.7, desc="Parsing presentation slides...")
|
434 |
status_update = "Parsing presentation slides..."
|
435 |
+
yield (
|
436 |
+
gr.update(value=status_update),
|
437 |
+
None,
|
438 |
+
None,
|
439 |
+
[],
|
440 |
+
gr.update(),
|
441 |
+
gr.update(),
|
442 |
+
gr.update(),
|
443 |
+
)
|
444 |
slides_data = parse_presentation_markdown(presentation_md)
|
445 |
if not slides_data:
|
446 |
logger.error("Parsing markdown resulted in zero slides.")
|
|
|
484 |
|
485 |
progress(0.9, desc="Preparing editor...")
|
486 |
status_update = "Generated presentation. Preparing editor..."
|
487 |
+
yield (
|
488 |
+
gr.update(value=status_update),
|
489 |
+
None,
|
490 |
+
None,
|
491 |
+
[],
|
492 |
+
gr.update(),
|
493 |
+
gr.update(),
|
494 |
+
gr.update(),
|
495 |
+
)
|
496 |
logger.info(f"Prepared data for {len(slides_data)} slides.")
|
497 |
|
498 |
# Final yield must match outputs list
|
|
|
509 |
except Exception as e:
|
510 |
logger.error(f"Error in step 1 (fetch/generate): {e}", exc_info=True)
|
511 |
status_update = f"Error during presentation setup: {e}"
|
512 |
+
yield (
|
513 |
+
gr.update(value=status_update),
|
514 |
+
None,
|
515 |
+
None,
|
516 |
+
[],
|
517 |
+
gr.update(),
|
518 |
+
gr.update(),
|
519 |
+
gr.update(),
|
520 |
+
)
|
521 |
# Yield a final tuple matching outputs, indicating error state if possible
|
522 |
yield (
|
523 |
gr.update(value=status_update),
|
|
|
536 |
state_temp_dir,
|
537 |
state_md_path,
|
538 |
state_slides_data,
|
|
|
539 |
*editors,
|
540 |
progress=gr.Progress(track_tqdm=True),
|
541 |
):
|
|
|
544 |
raise gr.Error("Session state missing.")
|
545 |
logger.info("Step 2: Building Slides (PDF + Images)")
|
546 |
status_update = "Starting Step 2: Building slides..."
|
547 |
+
yield (
|
548 |
+
gr.update(value=status_update),
|
549 |
+
None,
|
550 |
+
[],
|
551 |
+
gr.update(),
|
552 |
+
gr.update(),
|
553 |
+
gr.update(),
|
554 |
+
)
|
555 |
|
556 |
num_slides = len(state_slides_data)
|
557 |
MAX_SLIDES = 20
|
|
|
565 |
|
566 |
progress(0.1, desc="Saving edited markdown...")
|
567 |
status_update = "Saving edited markdown..."
|
568 |
+
yield (
|
569 |
+
gr.update(value=status_update),
|
570 |
+
None,
|
571 |
+
[],
|
572 |
+
gr.update(),
|
573 |
+
gr.update(),
|
574 |
+
gr.update(),
|
575 |
+
)
|
576 |
updated_slides = []
|
577 |
for i in range(num_slides):
|
578 |
updated_slides.append(
|
|
|
585 |
logger.info(f"Saved edited markdown: {state_md_path}")
|
586 |
except IOError as e:
|
587 |
status_update = f"Failed to save markdown: {e}"
|
588 |
+
yield (
|
589 |
+
gr.update(value=status_update),
|
590 |
+
None,
|
591 |
+
[],
|
592 |
+
gr.update(),
|
593 |
+
gr.update(),
|
594 |
+
gr.update(),
|
595 |
+
)
|
596 |
# Final yield must match outputs
|
597 |
yield (
|
598 |
gr.update(value=status_update),
|
|
|
601 |
gr.update(),
|
602 |
gr.update(visible=True),
|
603 |
gr.update(),
|
604 |
+
)
|
605 |
raise gr.Error(f"Failed to save markdown: {e}")
|
606 |
|
607 |
progress(0.3, desc="Generating PDF...")
|
608 |
status_update = "Generating PDF..."
|
609 |
+
yield (
|
610 |
+
gr.update(value=status_update),
|
611 |
+
None,
|
612 |
+
[],
|
613 |
+
gr.update(),
|
614 |
+
gr.update(),
|
615 |
+
gr.update(),
|
616 |
+
)
|
617 |
pdf_output_path = os.path.join(state_temp_dir, "presentation.pdf")
|
618 |
try:
|
619 |
generated_pdf_path = generate_pdf_from_markdown(state_md_path, pdf_output_path)
|
|
|
621 |
raise gr.Error("PDF generation failed (check logs).")
|
622 |
except gr.Error as e:
|
623 |
status_update = f"PDF Generation Error: {e}"
|
624 |
+
yield (
|
625 |
+
gr.update(value=status_update),
|
626 |
+
None,
|
627 |
+
[],
|
628 |
+
gr.update(),
|
629 |
+
gr.update(),
|
630 |
+
gr.update(),
|
631 |
+
)
|
632 |
# Final yield must match outputs
|
633 |
yield (
|
634 |
gr.update(value=status_update),
|
|
|
637 |
gr.update(),
|
638 |
gr.update(visible=True),
|
639 |
gr.update(),
|
640 |
+
)
|
641 |
raise e
|
642 |
except Exception as e:
|
643 |
status_update = f"Unexpected PDF Error: {e}"
|
644 |
+
yield (
|
645 |
+
gr.update(value=status_update),
|
646 |
+
None,
|
647 |
+
[],
|
648 |
+
gr.update(),
|
649 |
+
gr.update(),
|
650 |
+
gr.update(),
|
651 |
+
)
|
652 |
# Final yield must match outputs
|
653 |
yield (
|
654 |
gr.update(value=status_update),
|
|
|
657 |
gr.update(),
|
658 |
gr.update(visible=True),
|
659 |
gr.update(),
|
660 |
+
)
|
661 |
raise gr.Error(f"Unexpected error generating PDF: {e}")
|
662 |
|
663 |
progress(0.7, desc="Converting PDF to images...")
|
664 |
status_update = "Converting PDF to images..."
|
665 |
+
yield (
|
666 |
+
gr.update(value=status_update),
|
667 |
+
generated_pdf_path,
|
668 |
+
[],
|
669 |
+
gr.update(),
|
670 |
+
gr.update(),
|
671 |
+
gr.update(),
|
672 |
+
)
|
673 |
pdf_images = []
|
674 |
try:
|
675 |
pdf_images = convert_pdf_to_images(
|
|
|
686 |
except Exception as e:
|
687 |
logger.error(f"Error converting PDF to images: {e}", exc_info=True)
|
688 |
status_update = f"Failed to convert PDF to images: {e}"
|
689 |
+
yield (
|
690 |
+
gr.update(value=status_update),
|
691 |
+
generated_pdf_path,
|
692 |
+
[],
|
693 |
+
gr.update(),
|
694 |
+
gr.update(),
|
695 |
+
gr.update(),
|
696 |
+
)
|
697 |
# Final yield must match outputs
|
698 |
yield (
|
699 |
gr.update(value=status_update),
|
|
|
702 |
gr.update(),
|
703 |
gr.update(visible=True),
|
704 |
gr.update(value=generated_pdf_path, visible=True),
|
705 |
+
)
|
706 |
# Proceed without images? Or raise error? Let's raise.
|
707 |
raise gr.Error(f"Failed to convert PDF to images: {e}")
|
708 |
|
|
|
726 |
# args[0]: state_temp_dir
|
727 |
# args[1]: state_md_path
|
728 |
# args[2]: original_slides_data
|
729 |
+
# args[3...]: editors
|
|
|
730 |
|
731 |
state_temp_dir = args[0]
|
732 |
state_md_path = args[1]
|
733 |
original_slides_data = args[2]
|
|
|
734 |
|
735 |
# editors = args[3:] # Old slicing
|
736 |
num_slides = len(original_slides_data)
|
|
|
739 |
raise gr.Error("No slide data available. Please start over.")
|
740 |
|
741 |
MAX_SLIDES = 20 # Ensure this matches UI definition
|
742 |
+
# --- Adjust indices based on removing status_textbox input ---
|
743 |
+
# Start editors from index 3 now
|
744 |
+
code_editors_start_index = 3
|
745 |
+
notes_textboxes_start_index = 3 + MAX_SLIDES
|
746 |
|
747 |
# Slice the *actual* edited values based on num_slides
|
748 |
edited_contents = args[
|
|
|
766 |
|
767 |
logger.info(f"Processing {num_slides} slides for audio generation.")
|
768 |
status_update = "Starting Step 3: Generating audio..."
|
769 |
+
yield (gr.update(value=status_update), None, gr.update(), gr.update()) + (
|
770 |
+
gr.update(),
|
771 |
+
) * MAX_SLIDES * 2
|
772 |
|
773 |
audio_dir = os.path.join(state_temp_dir, "audio")
|
774 |
os.makedirs(audio_dir, exist_ok=True)
|
|
|
778 |
# but ensures the audio matches the *latest* notes displayed.
|
779 |
progress(0.1, desc="Saving latest notes...")
|
780 |
status_update = "Saving latest notes..."
|
781 |
+
yield (gr.update(value=status_update), None, gr.update(), gr.update()) + (
|
782 |
+
gr.update(),
|
783 |
+
) * MAX_SLIDES * 2
|
784 |
updated_slides_data = []
|
785 |
for i in range(num_slides):
|
786 |
updated_slides_data.append(
|
|
|
800 |
warning_msg = f"Warning: Could not save latest notes to markdown file: {e}"
|
801 |
gr.Warning(warning_msg)
|
802 |
status_update += f" ({warning_msg})"
|
803 |
+
yield (gr.update(value=status_update), None, gr.update(), gr.update()) + (
|
804 |
+
gr.update(),
|
805 |
+
) * MAX_SLIDES * 2
|
806 |
# Note: We continue processing audio even if saving markdown fails
|
807 |
# If we need to stop and return final state, do it here:
|
808 |
# yield (gr.update(value=status_update), state_audio_dir, gr.update(), gr.update(visible=True), *[gr.update()]*N, *[gr.update()]*N)
|
|
|
821 |
desc=f"Audio slide {slide_num}/{num_slides}",
|
822 |
)
|
823 |
status_update = f"Generating audio for slide {slide_num}/{num_slides}..."
|
824 |
+
yield (gr.update(value=status_update), audio_dir, gr.update(), gr.update()) + (
|
825 |
+
gr.update(),
|
826 |
+
) * MAX_SLIDES * 2
|
827 |
|
828 |
output_file_path = Path(audio_dir) / f"{slide_num}.wav"
|
829 |
if not note_text or not note_text.strip():
|
|
|
902 |
state_temp_dir,
|
903 |
state_audio_dir,
|
904 |
state_pdf_path, # Use PDF path from state
|
|
|
905 |
progress=gr.Progress(track_tqdm=True),
|
906 |
):
|
907 |
"""Generates the final video using PDF images and audio files."""
|
|
|
916 |
|
917 |
video_output_path = os.path.join(state_temp_dir, "final_presentation.mp4")
|
918 |
status_update = "Starting Step 4: Generating video..."
|
919 |
+
yield (gr.update(value=status_update), gr.update(), gr.update())
|
920 |
|
921 |
progress(0.1, desc="Preparing video components...")
|
922 |
pdf_images = [] # Initialize to ensure cleanup happens
|
|
|
933 |
# Convert PDF to images
|
934 |
progress(0.2, desc="Converting PDF to images...")
|
935 |
status_update = "Converting PDF back to images for video..."
|
936 |
+
yield (gr.update(value=status_update), gr.update(), gr.update())
|
937 |
pdf_images = convert_pdf_to_images(state_pdf_path, dpi=150)
|
938 |
if not pdf_images:
|
939 |
raise gr.Error(f"Failed to convert PDF ({state_pdf_path}) to images.")
|
|
|
945 |
logger.warning(warning_msg)
|
946 |
status_update += f" ({warning_msg})"
|
947 |
# yield status_update # Old yield
|
948 |
+
yield (gr.update(value=status_update), gr.update(), gr.update())
|
949 |
|
950 |
progress(0.5, desc="Creating individual video clips...")
|
951 |
status_update = "Creating individual video clips..."
|
952 |
# yield status_update # Old yield
|
953 |
+
yield (gr.update(value=status_update), gr.update(), gr.update())
|
954 |
buffer_seconds = 1.0
|
955 |
output_fps = 10
|
956 |
video_clips = create_video_clips(
|
|
|
963 |
progress(0.8, desc="Concatenating clips...")
|
964 |
status_update = "Concatenating clips into final video..."
|
965 |
# yield status_update # Old yield
|
966 |
+
yield (gr.update(value=status_update), gr.update(), gr.update())
|
967 |
concatenate_clips(video_clips, video_output_path, output_fps)
|
968 |
|
969 |
logger.info(f"Video concatenation complete: {video_output_path}")
|
|
|
971 |
progress(0.95, desc="Cleaning up temp images...")
|
972 |
status_update = "Cleaning up temporary image files..."
|
973 |
# yield status_update # Old yield
|
974 |
+
yield (gr.update(value=status_update), gr.update(), gr.update())
|
975 |
cleanup_temp_files(pdf_images) # Pass the list of image paths
|
976 |
|
977 |
except Exception as e:
|
|
|
979 |
cleanup_temp_files(pdf_images)
|
980 |
logger.error(f"Video generation failed: {e}", exc_info=True)
|
981 |
status_update = f"Video generation failed: {e}"
|
982 |
+
yield (gr.update(value=status_update), gr.update(), gr.update())
|
983 |
# Final yield must match outputs
|
984 |
+
yield (gr.update(value=status_update), gr.update(), gr.update(visible=True))
|
|
|
|
|
|
|
|
|
985 |
raise gr.Error(f"Video generation failed: {e}")
|
986 |
|
987 |
info_msg = f"Video generated: {os.path.basename(video_output_path)}"
|
|
|
1241 |
]
|
1242 |
btn_fetch_generate.click(
|
1243 |
fn=step1_fetch_and_generate_presentation,
|
1244 |
+
inputs=[input_url],
|
1245 |
outputs=step1_outputs,
|
1246 |
show_progress="full",
|
1247 |
).then(
|
|
|
1281 |
|
1282 |
# Step 2 Click Handler
|
1283 |
step2_inputs = (
|
1284 |
+
[state_temp_dir, state_md_path, state_slides_data]
|
1285 |
+ all_code_editors
|
1286 |
+ all_notes_textboxes
|
1287 |
)
|
|
|
1319 |
|
1320 |
# Step 3 Click Handler
|
1321 |
step3_inputs = (
|
1322 |
+
[state_temp_dir, state_md_path, state_slides_data]
|
1323 |
+ all_code_editors
|
1324 |
+ all_notes_textboxes
|
1325 |
)
|
|
|
1349 |
)
|
1350 |
|
1351 |
# Step 4 Click Handler
|
1352 |
+
step4_inputs = [state_temp_dir, state_audio_dir, state_pdf_path]
|
1353 |
step4_outputs = [
|
1354 |
status_textbox, # Added status output
|
1355 |
video_output, # Update video output in Tab 4
|