mfarre HF staff commited on
Commit
4ddfd8f
1 Parent(s): 8c8ef67

streaming from repo

Browse files
Files changed (1) hide show
  1. app.py +202 -153
app.py CHANGED
@@ -40,6 +40,7 @@ def load_video_list() -> List[Dict[str, str]]:
40
 
41
  return video_list
42
 
 
43
  def score_to_emoji(score):
44
  if score < 0.2:
45
  return "😴"
@@ -79,10 +80,11 @@ def format_timestamp(timestamp: str) -> str:
79
  except Exception as e:
80
  logger.error(f"Invalid timestamp format: {timestamp}")
81
  return ""
 
82
  def create_scene_table(scene: Dict[str, Any]) -> str:
83
  dynamism_score = scene.get('dynamismScore', 0)
84
  av_correlation = scene.get('audioVisualCorrelation', 0)
85
- cast = ", ".join([cast_member for cast_member in scene.get('cast', []) if cast_member and cast_member != 'None'])
86
 
87
  output = f"""
88
  <div class="scene-container">
@@ -134,29 +136,26 @@ def create_scene_table(scene: Dict[str, Any]) -> str:
134
  # Handle mood changes
135
  for mood_change in item.get('keyMoments', []):
136
  if isinstance(mood_change, dict):
137
- mood_change_description = mood_change.get('changeDescription', '')
138
- if mood_change_description and mood_change_description != 'None':
139
- scene_events.append({
140
- 'timestamp_start': mood_change.get('timestamp', ''),
141
- 'timestamp_end': '',
142
- 'type': 'Mood Change',
143
- 'description': mood_change_description
144
- })
145
  elif data_type == 'Character Interaction':
146
- characters = ', '.join([char for char in item.get('characters', []) if char and char != 'None'])
147
  description = f"{characters}: {item.get('description', '')}"
148
  else:
149
  start_time = item.get('timestamp', '')
150
  description = item.get('description', '')
151
 
152
- if description and description != 'None': # Only add the event if there's a valid description
153
- scene_events.append({
154
- 'timestamp_start': start_time,
155
- 'timestamp_end': end_time,
156
- 'type': data_type,
157
- 'description': description
158
- })
159
- elif isinstance(item, str) and item and item != 'None': # Only add non-empty and non-'None' string items
160
  scene_events.append({
161
  'timestamp_start': '',
162
  'timestamp_end': '',
@@ -286,38 +285,23 @@ def create_filmstrip(scenes: List[Dict[str, Any]], video_duration: float) -> str
286
  """
287
  return filmstrip_html
288
 
289
- # def generate_correlation_scores(metadata: Dict[str, Any]) -> str:
290
- # dynamism_score = metadata.get('dynamismscore', 0)
291
- # av_correlation = metadata.get('audiovisualcorrelation', 0)
292
-
293
- # def score_to_emoji(score):
294
- # if score < 0.2:
295
- # return "😴"
296
- # elif score < 0.4:
297
- # return "🙂"
298
- # elif score < 0.6:
299
- # return "😊"
300
- # elif score < 0.8:
301
- # return "😃"
302
- # else:
303
- # return "🤩"
304
-
305
- # return f"""
306
- # <div class="correlation-scores">
307
- # <p>Dynamism: {score_to_emoji(dynamism_score)} ({dynamism_score:.2f})</p>
308
- # <p>Audio-Visual Correlation: {score_to_emoji(av_correlation)} ({av_correlation:.2f})</p>
309
- # </div>
310
- # """
311
-
312
  def process_video(video_id: str):
313
  try:
314
- #logger.info(f"Processing video with ID: {video_id}")
315
  metadata = load_metadata(video_id)
316
- video_path = os.path.join(video_folder, f"{video_id}.mp4")
317
 
318
- if not os.path.exists(video_path):
319
- logger.error(f"Video file not found: {video_path}")
320
- return None, "", f"Error: Video file not found for ID {video_id}"
 
 
 
 
 
 
 
 
 
321
 
322
  # Character List Table
323
  character_table = """
@@ -359,7 +343,7 @@ def process_video(video_id: str):
359
  filmstrip_html = create_filmstrip(metadata['scenes'], video_duration)
360
 
361
  logger.info("Video processing completed successfully")
362
- return video_path, filmstrip_html, additional_data + scenes_output + storylines_output + qa_output + trimming_suggestions_output
363
  except Exception as e:
364
  logger.exception(f"Error processing video: {str(e)}")
365
  return None, "", f"Error processing video: {str(e)}"
@@ -385,7 +369,6 @@ body {
385
  #logo {
386
  width: auto;
387
  height: 150px;
388
- margin-right: 20px;
389
  box-shadow: none !important;
390
  border: none !important;
391
  background: none !important;
@@ -399,7 +382,7 @@ body {
399
  }
400
  #header-content h1 {
401
  margin: 0;
402
- font-size: 45px;
403
  font-weight: bold;
404
  }
405
  #header-content a {
@@ -411,44 +394,32 @@ body {
411
  text-decoration: underline;
412
  }
413
  #top-panel {
414
- position: sticky;
415
- top: 10vh;
416
- background-color: white;
417
- z-index: 100;
418
- padding: 20px;
419
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
420
  display: flex;
421
- height: 35vh;
 
422
  overflow: hidden;
423
  }
424
  #video-list-column {
425
- display: flex;
426
- flex-direction: column;
427
- max-height: 100%;
428
- width: 30%;
429
  overflow-y: auto;
430
  }
431
- #video-list label {
432
- display: block;
433
- width: 100%;
434
- }
435
  #video-column {
 
436
  display: flex;
437
  flex-direction: column;
438
- max-height: 100%;
439
- overflow: hidden;
440
- width: 70%;
441
  }
442
- #video-column > div:first-child {
 
443
  display: flex;
444
- flex-direction: column;
445
- height: calc(100% - 100px);
 
446
  }
447
- #video-column video {
448
- max-height: 100%;
449
- object-fit: contain;
450
  width: 100%;
451
- margin: 0;
452
  }
453
  #filmstrip-container {
454
  width: 100%;
@@ -457,7 +428,6 @@ body {
457
  position: relative;
458
  overflow: hidden;
459
  cursor: pointer;
460
- margin-top: 0;
461
  }
462
  #filmstrip-container > div,
463
  #filmstrip-container > div > div,
@@ -465,10 +435,9 @@ body {
465
  height: 100% !important;
466
  }
467
  #scrollable-content {
468
- flex-grow: 1;
469
  overflow-y: auto;
470
  padding: 20px;
471
- height: calc(55vh - 40px);
472
  }
473
  #metadata-container {
474
  margin-top: 20px;
@@ -491,6 +460,7 @@ body {
491
  flex-direction: column !important;
492
  }
493
  .content-samples label {
 
494
  padding: 10px;
495
  cursor: pointer;
496
  border-bottom: 1px solid #ddd;
@@ -590,56 +560,57 @@ body {
590
  .icon-buttons button {
591
  display: none !important;
592
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
  """
594
 
595
  js = """
596
  <script>
 
597
  (function() {
598
- let isReinitializing = false;
599
- let lastVideoSrc = null;
600
-
601
- function showOverlay() {
602
- let overlay = document.getElementById('reinitialization-overlay');
603
- if (!overlay) {
604
- overlay = document.createElement('div');
605
- overlay.id = 'reinitialization-overlay';
606
- overlay.style.position = 'fixed';
607
- overlay.style.top = '0';
608
- overlay.style.left = '0';
609
- overlay.style.width = '100%';
610
- overlay.style.height = '100%';
611
- overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
612
- overlay.style.display = 'flex';
613
- overlay.style.justifyContent = 'center';
614
- overlay.style.alignItems = 'center';
615
- overlay.style.zIndex = '9999';
616
-
617
- const message = document.createElement('div');
618
- message.textContent = 'Loading assets...';
619
- message.style.color = 'white';
620
- message.style.fontSize = '24px';
621
- message.style.fontWeight = 'bold';
622
-
623
- overlay.appendChild(message);
624
- document.body.appendChild(overlay);
625
  }
626
- overlay.style.display = 'flex';
627
  }
628
 
629
- function hideOverlay() {
630
- const overlay = document.getElementById('reinitialization-overlay');
631
- if (overlay) {
632
- overlay.style.display = 'none';
 
 
 
 
 
633
  }
 
634
  }
635
 
636
  function initializeFilmstrip() {
 
 
637
  var videoElement = document.querySelector('video');
638
  var filmstripContainer = document.getElementById('filmstrip-container');
639
- var filmstripInner = filmstripContainer ? filmstripContainer.querySelector('#filmstrip-inner') : null;
640
  var scrubbingNeedle = document.getElementById('scrubbing-needle');
641
 
642
  if (!videoElement || !filmstripContainer || !filmstripInner || !scrubbingNeedle) {
 
643
  return;
644
  }
645
 
@@ -655,13 +626,22 @@ js = """
655
  var clickPosition = (event.clientX - rect.left) / rect.width;
656
  videoElement.currentTime = clickPosition * videoDuration;
657
  });
 
 
658
  }
659
 
660
  function initializeTimestampLinks() {
 
661
  var videoElement = document.querySelector('video');
662
  var links = document.querySelectorAll('.timestamp-link');
663
 
664
- if (!videoElement || links.length === 0) {
 
 
 
 
 
 
665
  return;
666
  }
667
 
@@ -669,75 +649,142 @@ js = """
669
  link.addEventListener('click', function(e) {
670
  e.preventDefault();
671
  var timestamp = this.getAttribute('data-timestamp');
 
672
  var parts = timestamp.split(':');
673
  var seconds = parseInt(parts[0], 10) * 3600 + parseInt(parts[1], 10) * 60 + parseFloat(parts[2]);
674
  videoElement.currentTime = seconds;
675
  });
676
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
677
  }
678
 
679
  function initializeEverything() {
680
  if (isReinitializing) {
 
681
  return;
682
  }
683
 
684
  isReinitializing = true;
685
  showOverlay();
686
-
687
- const videoElement = document.querySelector('video');
688
- if (videoElement) {
689
- const onCanPlay = function() {
690
- videoElement.removeEventListener('canplay', onCanPlay);
691
- initializeFilmstrip();
692
- initializeTimestampLinks();
693
- isReinitializing = false;
694
- hideOverlay();
695
- };
696
-
697
- videoElement.addEventListener('canplay', onCanPlay);
698
-
699
- // If the video is already loaded, trigger the event manually
700
- if (videoElement.readyState >= 3) {
701
- videoElement.dispatchEvent(new Event('canplay'));
702
- }
703
- } else {
704
- // If there's no video element, just initialize other components
705
  initializeFilmstrip();
706
  initializeTimestampLinks();
 
 
 
 
707
  isReinitializing = false;
708
  hideOverlay();
709
  }
710
  }
711
 
712
- function checkForVideoChanges() {
713
- const videoElement = document.querySelector('video');
714
- if (videoElement && videoElement.src !== lastVideoSrc) {
715
- lastVideoSrc = videoElement.src;
716
- showOverlay();
717
- setTimeout(initializeEverything, 100);
 
 
 
 
 
 
 
 
 
718
  }
719
  }
720
 
721
  // Set up a MutationObserver to watch for changes in the entire document
722
  const contentObserver = new MutationObserver((mutations) => {
723
- checkForVideoChanges();
 
 
 
 
 
 
 
 
 
 
724
  });
725
 
726
  contentObserver.observe(document.body, {
727
  childList: true,
728
  subtree: true,
729
  attributes: true,
730
- attributeFilter: ['src']
731
  });
732
 
733
- // Periodically check for video changes
734
- setInterval(checkForVideoChanges, 1000);
 
 
 
 
 
 
 
 
 
 
 
 
 
735
 
736
  // Initialize everything when the DOM is ready
737
- document.addEventListener('DOMContentLoaded', initializeEverything);
 
 
 
 
738
 
739
  // Also try to initialize after a short delay, in case DOMContentLoaded has already fired
740
- setTimeout(initializeEverything, 1000);
 
 
 
 
741
  })();
742
  </script>
743
  """
@@ -745,15 +792,15 @@ js = """
745
  with gr.Blocks(css=css, head=js) as iface:
746
  with gr.Row(elem_id="header"):
747
  with gr.Column(scale=1):
748
- gr.Image("logo.png", elem_id="logo", show_label=False, interactive=False)
749
- with gr.Column(elem_id="header-content",scale=10):
750
  gr.Markdown("""
751
- # Exploration space
752
 
753
- ## [🔗 Dataset](https://huggingface.co/datasets/HuggingFaceFV/finevideo)
754
  """)
755
  with gr.Row(elem_id="top-panel"):
756
- with gr.Column(scale=1, elem_id="video-list-column"):
757
  video_list_data = load_video_list()
758
  video_list = gr.Radio(
759
  label="Content Samples",
@@ -763,8 +810,8 @@ with gr.Blocks(css=css, head=js) as iface:
763
  container=False
764
  )
765
 
766
- with gr.Column(scale=2, elem_id="video-column"):
767
- video_output = gr.Video(label="Video", elem_id="video-player")
768
  filmstrip_output = gr.HTML(elem_id="filmstrip-container")
769
 
770
  with gr.Row(elem_id="scrollable-content"):
@@ -772,11 +819,11 @@ with gr.Blocks(css=css, head=js) as iface:
772
 
773
  def wrapped_process_video(title: str) -> tuple:
774
  if not title:
775
- return None, "", ""
776
  video_id = next(video["video_id"] for video in video_list_data if video["title"] == title)
777
  logging.info(f"Processing video with ID: {video_id}")
778
- video_path, filmstrip_html, metadata_html = process_video(video_id)
779
- return video_path, filmstrip_html, metadata_html
780
 
781
  video_list.change(
782
  fn=wrapped_process_video,
@@ -785,4 +832,6 @@ with gr.Blocks(css=css, head=js) as iface:
785
  )
786
 
787
  if __name__ == "__main__":
788
- iface.launch()
 
 
 
40
 
41
  return video_list
42
 
43
+
44
  def score_to_emoji(score):
45
  if score < 0.2:
46
  return "😴"
 
80
  except Exception as e:
81
  logger.error(f"Invalid timestamp format: {timestamp}")
82
  return ""
83
+
84
  def create_scene_table(scene: Dict[str, Any]) -> str:
85
  dynamism_score = scene.get('dynamismScore', 0)
86
  av_correlation = scene.get('audioVisualCorrelation', 0)
87
+ cast = ", ".join([cast_member for cast_member in scene.get('cast', [])])
88
 
89
  output = f"""
90
  <div class="scene-container">
 
136
  # Handle mood changes
137
  for mood_change in item.get('keyMoments', []):
138
  if isinstance(mood_change, dict):
139
+ scene_events.append({
140
+ 'timestamp_start': mood_change.get('timestamp', ''),
141
+ 'timestamp_end': '',
142
+ 'type': 'Mood Change',
143
+ 'description': mood_change.get('changeDescription', '')
144
+ })
 
 
145
  elif data_type == 'Character Interaction':
146
+ characters = ', '.join(item.get('characters', []))
147
  description = f"{characters}: {item.get('description', '')}"
148
  else:
149
  start_time = item.get('timestamp', '')
150
  description = item.get('description', '')
151
 
152
+ scene_events.append({
153
+ 'timestamp_start': start_time,
154
+ 'timestamp_end': end_time,
155
+ 'type': data_type,
156
+ 'description': description
157
+ })
158
+ elif isinstance(item, str):
 
159
  scene_events.append({
160
  'timestamp_start': '',
161
  'timestamp_end': '',
 
285
  """
286
  return filmstrip_html
287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  def process_video(video_id: str):
289
  try:
290
+ logger.info(f"Processing video with ID: {video_id}")
291
  metadata = load_metadata(video_id)
 
292
 
293
+ # Always use the test URL instead of the actual video file
294
+ video_url = f"https://huggingface.co/spaces/finevideo/viztest/resolve/main/video/{video_id}.mp4"
295
+
296
+ # Create HTML for video player
297
+ video_html = f"""
298
+ <div id="video-wrapper">
299
+ <video id="video-player" controls>
300
+ <source src="{video_url}" type="video/mp4">
301
+ Your browser does not support the video tag.
302
+ </video>
303
+ </div>
304
+ """
305
 
306
  # Character List Table
307
  character_table = """
 
343
  filmstrip_html = create_filmstrip(metadata['scenes'], video_duration)
344
 
345
  logger.info("Video processing completed successfully")
346
+ return video_html, filmstrip_html, additional_data + scenes_output + storylines_output + qa_output + trimming_suggestions_output
347
  except Exception as e:
348
  logger.exception(f"Error processing video: {str(e)}")
349
  return None, "", f"Error processing video: {str(e)}"
 
369
  #logo {
370
  width: auto;
371
  height: 150px;
 
372
  box-shadow: none !important;
373
  border: none !important;
374
  background: none !important;
 
382
  }
383
  #header-content h1 {
384
  margin: 0;
385
+ font-size: 36px;
386
  font-weight: bold;
387
  }
388
  #header-content a {
 
394
  text-decoration: underline;
395
  }
396
  #top-panel {
397
+ height: 33vh;
 
 
 
 
 
398
  display: flex;
399
+ padding: 10px;
400
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
401
  overflow: hidden;
402
  }
403
  #video-list-column {
404
+
405
+ max-height: 80vh; /* Adjust as needed */
 
 
406
  overflow-y: auto;
407
  }
 
 
 
 
408
  #video-column {
409
+ width: 70%;
410
  display: flex;
411
  flex-direction: column;
 
 
 
412
  }
413
+ #video-wrapper {
414
+ flex-grow: 1;
415
  display: flex;
416
+ justify-content: center;
417
+ align-items: center;
418
+ overflow: hidden;
419
  }
420
+ #video-player {
 
 
421
  width: 100%;
422
+ max-height: calc(33vh - 120px) !important;
423
  }
424
  #filmstrip-container {
425
  width: 100%;
 
428
  position: relative;
429
  overflow: hidden;
430
  cursor: pointer;
 
431
  }
432
  #filmstrip-container > div,
433
  #filmstrip-container > div > div,
 
435
  height: 100% !important;
436
  }
437
  #scrollable-content {
438
+ height: 67vh;
439
  overflow-y: auto;
440
  padding: 20px;
 
441
  }
442
  #metadata-container {
443
  margin-top: 20px;
 
460
  flex-direction: column !important;
461
  }
462
  .content-samples label {
463
+ display: block;
464
  padding: 10px;
465
  cursor: pointer;
466
  border-bottom: 1px solid #ddd;
 
560
  .icon-buttons button {
561
  display: none !important;
562
  }
563
+
564
+ /* Ensure one element per row in Gradio list */
565
+ #video-list-column .wrap {
566
+ display: flex;
567
+ flex-direction: column;
568
+ }
569
+
570
+ #video-list-column .wrap > .wrap {
571
+ display: flex !important;
572
+ flex-direction: column !important;
573
+ }
574
+
575
+ #video-list-column label {
576
+ display: block;
577
+ width: 100%;
578
+ }
579
  """
580
 
581
  js = """
582
  <script>
583
+ // Wrap everything in an IIFE to avoid polluting the global scope
584
  (function() {
585
+ function safeLog(message) {
586
+ if (typeof console !== 'undefined' && console.log) {
587
+ console.log(message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
588
  }
 
589
  }
590
 
591
+ function findFilmstripInner(container) {
592
+ if (container.id === 'filmstrip-inner') {
593
+ return container;
594
+ }
595
+ for (let child of container.children) {
596
+ let result = findFilmstripInner(child);
597
+ if (result) {
598
+ return result;
599
+ }
600
  }
601
+ return null;
602
  }
603
 
604
  function initializeFilmstrip() {
605
+ //safeLog("Initializing filmstrip...");
606
+
607
  var videoElement = document.querySelector('video');
608
  var filmstripContainer = document.getElementById('filmstrip-container');
609
+ var filmstripInner = findFilmstripInner(filmstripContainer);
610
  var scrubbingNeedle = document.getElementById('scrubbing-needle');
611
 
612
  if (!videoElement || !filmstripContainer || !filmstripInner || !scrubbingNeedle) {
613
+ //safeLog("Required elements not found for filmstrip");
614
  return;
615
  }
616
 
 
626
  var clickPosition = (event.clientX - rect.left) / rect.width;
627
  videoElement.currentTime = clickPosition * videoDuration;
628
  });
629
+
630
+ //safeLog("Filmstrip initialization complete");
631
  }
632
 
633
  function initializeTimestampLinks() {
634
+ //safeLog("Initializing timestamp links...");
635
  var videoElement = document.querySelector('video');
636
  var links = document.querySelectorAll('.timestamp-link');
637
 
638
+ if (!videoElement) {
639
+ //safeLog("Video element not found for timestamp links");
640
+ return;
641
+ }
642
+
643
+ if (links.length === 0) {
644
+ //safeLog("No timestamp links found");
645
  return;
646
  }
647
 
 
649
  link.addEventListener('click', function(e) {
650
  e.preventDefault();
651
  var timestamp = this.getAttribute('data-timestamp');
652
+ //safeLog("Timestamp link clicked: " + timestamp);
653
  var parts = timestamp.split(':');
654
  var seconds = parseInt(parts[0], 10) * 3600 + parseInt(parts[1], 10) * 60 + parseFloat(parts[2]);
655
  videoElement.currentTime = seconds;
656
  });
657
  });
658
+ //safeLog("Timestamp links initialization complete");
659
+ }
660
+
661
+ let isReinitializing = false;
662
+
663
+ function showOverlay() {
664
+ let overlay = document.getElementById('reinitialization-overlay');
665
+ if (!overlay) {
666
+ overlay = document.createElement('div');
667
+ overlay.id = 'reinitialization-overlay';
668
+ overlay.style.position = 'fixed';
669
+ overlay.style.top = '0';
670
+ overlay.style.left = '0';
671
+ overlay.style.width = '100%';
672
+ overlay.style.height = '100%';
673
+ overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
674
+ overlay.style.display = 'flex';
675
+ overlay.style.justifyContent = 'center';
676
+ overlay.style.alignItems = 'center';
677
+ overlay.style.zIndex = '9999';
678
+
679
+ const message = document.createElement('div');
680
+ message.textContent = 'Loading assets...';
681
+ message.style.color = 'white';
682
+ message.style.fontSize = '24px';
683
+ message.style.fontWeight = 'bold';
684
+
685
+ overlay.appendChild(message);
686
+ document.body.appendChild(overlay);
687
+ }
688
+ overlay.style.display = 'flex';
689
+ }
690
+
691
+ function hideOverlay() {
692
+ const overlay = document.getElementById('reinitialization-overlay');
693
+ if (overlay) {
694
+ overlay.style.display = 'none';
695
+ }
696
  }
697
 
698
  function initializeEverything() {
699
  if (isReinitializing) {
700
+ //safeLog("Already reinitializing, skipping...");
701
  return;
702
  }
703
 
704
  isReinitializing = true;
705
  showOverlay();
706
+ //safeLog("Initializing everything...");
707
+ try {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
708
  initializeFilmstrip();
709
  initializeTimestampLinks();
710
+ //safeLog("Initialization complete");
711
+ } catch (error) {
712
+ //safeLog("Error during initialization: " + error.message);
713
+ } finally {
714
  isReinitializing = false;
715
  hideOverlay();
716
  }
717
  }
718
 
719
+ let lastVideoSelection = null;
720
+
721
+ function checkVideoSelection() {
722
+ const videoList = document.getElementById('video-list');
723
+ if (videoList) {
724
+ const currentSelection = videoList.querySelector('input:checked');
725
+ if (currentSelection && currentSelection.value !== lastVideoSelection) {
726
+ //safeLog("Video selection changed, reinitializing...");
727
+ lastVideoSelection = currentSelection.value;
728
+ showOverlay();
729
+ setTimeout(() => {
730
+ initializeEverything();
731
+ hideOverlay();
732
+ }, 1000); // Delay to ensure new video is loaded
733
+ }
734
  }
735
  }
736
 
737
  // Set up a MutationObserver to watch for changes in the entire document
738
  const contentObserver = new MutationObserver((mutations) => {
739
+ mutations.forEach((mutation) => {
740
+ if (mutation.type === 'childList' || mutation.type === 'attributes') {
741
+ checkVideoSelection();
742
+ if (mutation.target.id === 'video-container' ||
743
+ mutation.target.id === 'filmstrip-container' ||
744
+ mutation.target.id === 'metadata-container') {
745
+ //safeLog("Relevant content updated, reinitializing...");
746
+ setTimeout(initializeEverything, 100); // Small delay to ensure elements are ready
747
+ }
748
+ }
749
+ });
750
  });
751
 
752
  contentObserver.observe(document.body, {
753
  childList: true,
754
  subtree: true,
755
  attributes: true,
756
+ attributeFilter: ['value', 'checked']
757
  });
758
 
759
+ // Function to set up Gradio event listeners
760
+ function setupGradioEventListeners() {
761
+ if (typeof gradio !== 'undefined') {
762
+ //safeLog("Setting up Gradio event listeners...");
763
+ gradio('#video-list').change(function(evt) {
764
+ //safeLog("Gradio detected video selection change, reinitializing...");
765
+ setTimeout(initializeEverything, 1000);
766
+ });
767
+ } /*else {
768
+ safeLog("Gradio not found, using fallback method");
769
+ }*/
770
+ }
771
+
772
+ // Periodically check for video selection changes
773
+ setInterval(checkVideoSelection, 1000);
774
 
775
  // Initialize everything when the DOM is ready
776
+ document.addEventListener('DOMContentLoaded', function() {
777
+ initializeEverything();
778
+ setupGradioEventListeners();
779
+ checkVideoSelection();
780
+ });
781
 
782
  // Also try to initialize after a short delay, in case DOMContentLoaded has already fired
783
+ setTimeout(function() {
784
+ initializeEverything();
785
+ setupGradioEventListeners();
786
+ checkVideoSelection();
787
+ }, 1000);
788
  })();
789
  </script>
790
  """
 
792
  with gr.Blocks(css=css, head=js) as iface:
793
  with gr.Row(elem_id="header"):
794
  with gr.Column(scale=1):
795
+ gr.Image("logo11.png", elem_id="logo", show_label=False, interactive=False)
796
+ with gr.Column(elem_id="header-content", scale=10):
797
  gr.Markdown("""
798
+ # Exploration page
799
 
800
+ ## [🔗 Dataset](https://huggingface.co/mfarre/Video-LLaVA-7B-hf-CinePile)
801
  """)
802
  with gr.Row(elem_id="top-panel"):
803
+ with gr.Column(scale=3, elem_id="video-list-column"):
804
  video_list_data = load_video_list()
805
  video_list = gr.Radio(
806
  label="Content Samples",
 
810
  container=False
811
  )
812
 
813
+ with gr.Column(scale=7, elem_id="video-column"):
814
+ video_output = gr.HTML(elem_id="video-container")
815
  filmstrip_output = gr.HTML(elem_id="filmstrip-container")
816
 
817
  with gr.Row(elem_id="scrollable-content"):
 
819
 
820
  def wrapped_process_video(title: str) -> tuple:
821
  if not title:
822
+ return "", "", ""
823
  video_id = next(video["video_id"] for video in video_list_data if video["title"] == title)
824
  logging.info(f"Processing video with ID: {video_id}")
825
+ video_html, filmstrip_html, metadata_html = process_video(video_id)
826
+ return video_html, filmstrip_html, metadata_html
827
 
828
  video_list.change(
829
  fn=wrapped_process_video,
 
832
  )
833
 
834
  if __name__ == "__main__":
835
+ iface.launch()
836
+
837
+