jfforero commited on
Commit
d3616d7
·
verified ·
1 Parent(s): 87799cc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -25
app.py CHANGED
@@ -426,8 +426,8 @@ def add_360_metadata(img):
426
 
427
 
428
 
429
- def create_360_viewer_html(image_paths, output_path):
430
- """Create an HTML file with a 360 viewer for the given images."""
431
  # Create a list of image data URIs
432
  image_data_list = []
433
  for img_path in image_paths:
@@ -435,6 +435,16 @@ def create_360_viewer_html(image_paths, output_path):
435
  img_data = base64.b64encode(f.read()).decode("utf-8")
436
  image_data_list.append(f"data:image/jpeg;base64,{img_data}")
437
 
 
 
 
 
 
 
 
 
 
 
438
  # Create the HTML content
439
  html_content = f"""
440
  <!DOCTYPE html>
@@ -442,16 +452,17 @@ def create_360_viewer_html(image_paths, output_path):
442
  <head>
443
  <meta charset="UTF-8">
444
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
445
- <title>360 Panorama Viewer</title>
446
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pannellum@2.5.6/build/pannellum.css"/>
447
  <style>
448
  body {{
449
  margin: 0;
450
  overflow: hidden;
 
451
  }}
452
  #panorama {{
453
  width: 100vw;
454
- height: 100vh;
455
  }}
456
  .pnlm-hotspot.pnlm-info-hotspot {{
457
  background-color: rgba(0, 150, 255, 0.8);
@@ -468,41 +479,80 @@ def create_360_viewer_html(image_paths, output_path):
468
  border-radius: 3px;
469
  padding: 5px 10px;
470
  }}
471
- #image-selector {{
472
  position: absolute;
473
  top: 10px;
474
  left: 10px;
475
  z-index: 1000;
476
  background: rgba(0, 0, 0, 0.7);
477
  color: white;
478
- padding: 5px 10px;
479
- border-radius: 3px;
 
 
 
480
  }}
481
- #open-button {{
482
- position: absolute;
483
- top: 10px;
484
- right: 10px;
 
 
 
 
 
 
 
485
  z-index: 1000;
486
- background: rgba(0, 0, 0, 0.7);
 
 
 
 
 
 
 
 
 
 
487
  color: white;
488
- padding: 5px 10px;
489
- border-radius: 3px;
490
  border: none;
 
 
491
  cursor: pointer;
 
 
 
 
 
 
 
 
 
492
  }}
493
  </style>
494
  </head>
495
  <body>
496
- <select id="image-selector">
497
- {"".join([f'<option value="{i}">Chunk {i+1}</option>' for i in range(len(image_data_list))])}
498
- </select>
499
- <button id="open-button" onclick="openInNewTab()">Open in New Tab</button>
 
 
 
500
  <div id="panorama"></div>
 
 
 
 
 
501
 
502
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/pannellum@2.5.6/build/pannellum.js"></script>
503
  <script>
504
  const images = {json.dumps(image_data_list)};
 
505
  let currentViewer = null;
 
506
 
507
  function loadPanorama(index) {{
508
  if (currentViewer) {{
@@ -518,6 +568,26 @@ def create_360_viewer_html(image_paths, output_path):
518
  "showFullscreenCtrl": true,
519
  "hfov": 100
520
  }});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
  }}
522
 
523
  // Load the first image initially
@@ -525,13 +595,71 @@ def create_360_viewer_html(image_paths, output_path):
525
 
526
  // Handle image selection changes
527
  document.getElementById('image-selector').addEventListener('change', function(e) {{
528
- loadPanorama(parseInt(e.target.value));
 
529
  }});
530
 
531
  // Function to open the viewer in a new tab
532
  function openInNewTab() {{
533
  const newWindow = window.open('', '_blank');
534
- newWindow.document.write(`<!DOCTYPE html><html><head><title>360 Panorama Viewer</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pannellum@2.5.6/build/pannellum.css"/></head><body><div id="panorama"></div><script src="https://cdn.jsdelivr.net/npm/pannellum@2.5.6/build/pannellum.js"><\/script><script>pannellum.viewer('panorama', {{"type": "equirectangular", "panorama": images[document.getElementById('image-selector').value], "autoLoad": true, "autoRotate": -2, "showZoomCtrl": true, "showFullscreenCtrl": true, "hfov": 100}});<\/script></body></html>`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  }}
536
  </script>
537
  </body>
@@ -612,7 +740,6 @@ def create_fade_transition(images, fade_duration=1.0, fps=24):
612
 
613
 
614
 
615
- # Update the process_and_display function to handle the fade animation correctly
616
  # Update the process_and_display function to handle the fade animation correctly
617
  def process_and_display(audio_input, generate_audio, chunk_duration):
618
  # Validate chunk duration
@@ -636,6 +763,7 @@ def process_and_display(audio_input, generate_audio, chunk_duration):
636
  group_visibility = []
637
  all_images = [] # Collect all generated images for the fade animation
638
  all_360_images = [] # Collect all 360 images for the viewer
 
639
 
640
  # Process each result
641
  for i, result in enumerate(results):
@@ -649,10 +777,11 @@ def process_and_display(audio_input, generate_audio, chunk_duration):
649
  result['image_360'],
650
  result['music']
651
  ])
652
- # Collect the images
653
  all_images.append(result['image'])
654
  if result['image_360']:
655
  all_360_images.append(result['image_360'])
 
656
  else:
657
  # If we have more results than containers, just extend with None
658
  group_visibility.append(gr.Group(visible=False))
@@ -674,14 +803,12 @@ def process_and_display(audio_input, generate_audio, chunk_duration):
674
  viewer_html_path = None
675
  if all_360_images:
676
  with tempfile.NamedTemporaryFile(suffix=".html", delete=False) as tmp_file:
677
- viewer_html_path = create_360_viewer_html(all_360_images, tmp_file.name)
678
 
679
  # Hide loading indicator and show results
680
  yield [gr.HTML("")] + group_visibility + outputs + [fade_preview, fade_animation_path, viewer_html_path, ""]
681
 
682
 
683
-
684
-
685
  # Update the clear_all function to handle the new outputs
686
  def clear_all():
687
  # Create a list with None for all outputs
 
426
 
427
 
428
 
429
+ def create_360_viewer_html(image_paths, audio_paths, output_path):
430
+ """Create an HTML file with a 360 viewer and audio player for the given images and audio."""
431
  # Create a list of image data URIs
432
  image_data_list = []
433
  for img_path in image_paths:
 
435
  img_data = base64.b64encode(f.read()).decode("utf-8")
436
  image_data_list.append(f"data:image/jpeg;base64,{img_data}")
437
 
438
+ # Create a list of audio data URIs
439
+ audio_data_list = []
440
+ for audio_path in audio_paths:
441
+ if audio_path: # Only process if audio exists
442
+ with open(audio_path, "rb") as f:
443
+ audio_data = base64.b64encode(f.read()).decode("utf-8")
444
+ audio_data_list.append(f"data:audio/wav;base64,{audio_data}")
445
+ else:
446
+ audio_data_list.append(None) # Placeholder for chunks without audio
447
+
448
  # Create the HTML content
449
  html_content = f"""
450
  <!DOCTYPE html>
 
452
  <head>
453
  <meta charset="UTF-8">
454
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
455
+ <title>360 Panorama Viewer with Audio</title>
456
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pannellum@2.5.6/build/pannellum.css"/>
457
  <style>
458
  body {{
459
  margin: 0;
460
  overflow: hidden;
461
+ font-family: Arial, sans-serif;
462
  }}
463
  #panorama {{
464
  width: 100vw;
465
+ height: 80vh;
466
  }}
467
  .pnlm-hotspot.pnlm-info-hotspot {{
468
  background-color: rgba(0, 150, 255, 0.8);
 
479
  border-radius: 3px;
480
  padding: 5px 10px;
481
  }}
482
+ #controls {{
483
  position: absolute;
484
  top: 10px;
485
  left: 10px;
486
  z-index: 1000;
487
  background: rgba(0, 0, 0, 0.7);
488
  color: white;
489
+ padding: 10px;
490
+ border-radius: 5px;
491
+ display: flex;
492
+ flex-direction: column;
493
+ gap: 10px;
494
  }}
495
+ #audio-controls {{
496
+ position: fixed;
497
+ bottom: 0;
498
+ left: 0;
499
+ width: 100%;
500
+ background: rgba(0, 0, 0, 0.8);
501
+ color: white;
502
+ padding: 15px;
503
+ display: flex;
504
+ flex-direction: column;
505
+ align-items: center;
506
  z-index: 1000;
507
+ }}
508
+ #audio-player {{
509
+ width: 80%;
510
+ margin-bottom: 10px;
511
+ }}
512
+ #audio-info {{
513
+ text-align: center;
514
+ font-size: 14px;
515
+ }}
516
+ button {{
517
+ background: #3498db;
518
  color: white;
 
 
519
  border: none;
520
+ padding: 8px 15px;
521
+ border-radius: 3px;
522
  cursor: pointer;
523
+ margin: 5px;
524
+ }}
525
+ button:hover {{
526
+ background: #2980b9;
527
+ }}
528
+ select {{
529
+ padding: 5px;
530
+ border-radius: 3px;
531
+ border: 1px solid #ccc;
532
  }}
533
  </style>
534
  </head>
535
  <body>
536
+ <div id="controls">
537
+ <select id="image-selector">
538
+ {"".join([f'<option value="{i}">Chunk {i+1}</option>' for i in range(len(image_data_list))])}
539
+ </select>
540
+ <button id="open-button" onclick="openInNewTab()">Open in New Tab</button>
541
+ </div>
542
+
543
  <div id="panorama"></div>
544
+
545
+ <div id="audio-controls">
546
+ <audio id="audio-player" controls></audio>
547
+ <div id="audio-info">No audio available for this chunk</div>
548
+ </div>
549
 
550
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/pannellum@2.5.6/build/pannellum.js"></script>
551
  <script>
552
  const images = {json.dumps(image_data_list)};
553
+ const audioFiles = {json.dumps(audio_data_list)};
554
  let currentViewer = null;
555
+ let currentAudioIndex = 0;
556
 
557
  function loadPanorama(index) {{
558
  if (currentViewer) {{
 
568
  "showFullscreenCtrl": true,
569
  "hfov": 100
570
  }});
571
+
572
+ // Update audio player
573
+ updateAudioPlayer(index);
574
+ }}
575
+
576
+ function updateAudioPlayer(index) {{
577
+ const audioPlayer = document.getElementById('audio-player');
578
+ const audioInfo = document.getElementById('audio-info');
579
+
580
+ if (audioFiles[index]) {{
581
+ audioPlayer.src = audioFiles[index];
582
+ audioInfo.textContent = `Playing audio for Chunk {index + 1}`;
583
+ // Try to play automatically (may be blocked by browser policies)
584
+ audioPlayer.play().catch(e => {{
585
+ audioInfo.textContent = `Click play to listen to audio for Chunk {index + 1}`;
586
+ }});
587
+ }} else {{
588
+ audioPlayer.src = '';
589
+ audioInfo.textContent = 'No audio available for this chunk';
590
+ }}
591
  }}
592
 
593
  // Load the first image initially
 
595
 
596
  // Handle image selection changes
597
  document.getElementById('image-selector').addEventListener('change', function(e) {{
598
+ const selectedIndex = parseInt(e.target.value);
599
+ loadPanorama(selectedIndex);
600
  }});
601
 
602
  // Function to open the viewer in a new tab
603
  function openInNewTab() {{
604
  const newWindow = window.open('', '_blank');
605
+ const selectedIndex = document.getElementById('image-selector').value;
606
+ newWindow.document.write(`
607
+ <!DOCTYPE html>
608
+ <html>
609
+ <head>
610
+ <title>360 Panorama Viewer with Audio</title>
611
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pannellum@2.5.6/build/pannellum.css"/>
612
+ <style>
613
+ body {{ margin: 0; overflow: hidden; font-family: Arial, sans-serif; }}
614
+ #panorama {{ width: 100vw; height: 80vh; }}
615
+ #audio-controls {{
616
+ position: fixed;
617
+ bottom: 0;
618
+ left: 0;
619
+ width: 100%;
620
+ background: rgba(0, 0, 0, 0.8);
621
+ color: white;
622
+ padding: 15px;
623
+ display: flex;
624
+ flex-direction: column;
625
+ align-items: center;
626
+ z-index: 1000;
627
+ }}
628
+ #audio-player {{ width: 80%; margin-bottom: 10px; }}
629
+ #audio-info {{ text-align: center; font-size: 14px; }}
630
+ </style>
631
+ </head>
632
+ <body>
633
+ <div id="panorama"></div>
634
+ <div id="audio-controls">
635
+ <audio id="audio-player" controls></audio>
636
+ <div id="audio-info">Audio for Chunk {parseInt(selectedIndex) + 1}</div>
637
+ </div>
638
+ <script src="https://cdn.jsdelivr.net/npm/pannellum@2.5.6/build/pannellum.js"><\/script>
639
+ <script>
640
+ pannellum.viewer('panorama', {{
641
+ "type": "equirectangular",
642
+ "panorama": "{images[selectedIndex]}",
643
+ "autoLoad": true,
644
+ "autoRotate": -2,
645
+ "showZoomCtrl": true,
646
+ "showFullscreenCtrl": true,
647
+ "hfov": 100
648
+ }});
649
+
650
+ const audioPlayer = document.getElementById('audio-player');
651
+ if ("{audioFiles[selectedIndex]}") {{
652
+ audioPlayer.src = "{audioFiles[selectedIndex]}";
653
+ audioPlayer.play().catch(e => {{
654
+ document.getElementById('audio-info').textContent = 'Click play to listen to audio';
655
+ }});
656
+ }} else {{
657
+ document.getElementById('audio-info').textContent = 'No audio available for this chunk';
658
+ }}
659
+ <\/script>
660
+ </body>
661
+ </html>
662
+ `);
663
  }}
664
  </script>
665
  </body>
 
740
 
741
 
742
 
 
743
  # Update the process_and_display function to handle the fade animation correctly
744
  def process_and_display(audio_input, generate_audio, chunk_duration):
745
  # Validate chunk duration
 
763
  group_visibility = []
764
  all_images = [] # Collect all generated images for the fade animation
765
  all_360_images = [] # Collect all 360 images for the viewer
766
+ all_music_paths = [] # Collect all music paths for the viewer
767
 
768
  # Process each result
769
  for i, result in enumerate(results):
 
777
  result['image_360'],
778
  result['music']
779
  ])
780
+ # Collect the images and music
781
  all_images.append(result['image'])
782
  if result['image_360']:
783
  all_360_images.append(result['image_360'])
784
+ all_music_paths.append(result['music']) # Can be None if no music generated
785
  else:
786
  # If we have more results than containers, just extend with None
787
  group_visibility.append(gr.Group(visible=False))
 
803
  viewer_html_path = None
804
  if all_360_images:
805
  with tempfile.NamedTemporaryFile(suffix=".html", delete=False) as tmp_file:
806
+ viewer_html_path = create_360_viewer_html(all_360_images, all_music_paths, tmp_file.name)
807
 
808
  # Hide loading indicator and show results
809
  yield [gr.HTML("")] + group_visibility + outputs + [fade_preview, fade_animation_path, viewer_html_path, ""]
810
 
811
 
 
 
812
  # Update the clear_all function to handle the new outputs
813
  def clear_all():
814
  # Create a list with None for all outputs