J Z commited on
Commit
91a90e8
·
verified ·
1 Parent(s): eca8bb5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +25 -5
app.py CHANGED
@@ -315,6 +315,7 @@ HTML_PAGE = """\
315
  .video-section {
316
  position: relative;
317
  width: 100%;
 
318
  max-height: 45vh;
319
  background: #000;
320
  border-radius: 12px;
@@ -504,8 +505,8 @@ HTML_PAGE = """\
504
  <!-- VIDEO PLAYER with crossfade -->
505
  <div class="video-section">
506
  <div class="video-container">
507
- <video id="videoA" muted playsinline></video>
508
- <video id="videoB" muted playsinline></video>
509
  </div>
510
  <div class="video-status" id="videoStatus">Loading...</div>
511
  </div>
@@ -547,13 +548,15 @@ const btnReset = document.getElementById('btnReset');
547
  function getActiveVideo() { return currentVideo === 'A' ? videoA : videoB; }
548
  function getInactiveVideo() { return currentVideo === 'A' ? videoB : videoA; }
549
 
550
- function crossfadeTo(videoUrl) {
551
  if (isTransitioning) return;
552
  isTransitioning = true;
553
 
554
  const prev = getActiveVideo();
555
  const next = getInactiveVideo();
556
 
 
 
557
  next.src = videoUrl;
558
  next.load();
559
 
@@ -578,6 +581,23 @@ function crossfadeTo(videoUrl) {
578
  }).catch(err => {
579
  console.error('[VIDEO] Play failed:', err);
580
  isTransitioning = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  });
582
  };
583
 
@@ -602,7 +622,7 @@ function playNextIdle() {
602
  if (!clip) return;
603
  lastPlayed = clip.filename;
604
  videoStatus.textContent = 'idle: ' + clip.name;
605
- crossfadeTo('/api/stream/' + encodeURIComponent(clip.filename));
606
  }
607
 
608
  // When active video ends → play next idle (unless processing)
@@ -715,7 +735,7 @@ async function sendMessage() {
715
  // Play lipsync video if available (Phase 2)
716
  if (data.lipsync_video_url) {
717
  videoStatus.textContent = 'speaking...';
718
- crossfadeTo(data.lipsync_video_url);
719
  // After lipsync ends, resume idle
720
  const activeVid = getInactiveVideo(); // will become active after crossfade
721
  const onLipsyncEnd = () => {
 
315
  .video-section {
316
  position: relative;
317
  width: 100%;
318
+ min-height: 300px;
319
  max-height: 45vh;
320
  background: #000;
321
  border-radius: 12px;
 
505
  <!-- VIDEO PLAYER with crossfade -->
506
  <div class="video-section">
507
  <div class="video-container">
508
+ <video id="videoA" muted autoplay playsinline></video>
509
+ <video id="videoB" muted autoplay playsinline></video>
510
  </div>
511
  <div class="video-status" id="videoStatus">Loading...</div>
512
  </div>
 
548
  function getActiveVideo() { return currentVideo === 'A' ? videoA : videoB; }
549
  function getInactiveVideo() { return currentVideo === 'A' ? videoB : videoA; }
550
 
551
+ function crossfadeTo(videoUrl, isMuted) {
552
  if (isTransitioning) return;
553
  isTransitioning = true;
554
 
555
  const prev = getActiveVideo();
556
  const next = getInactiveVideo();
557
 
558
+ // Set muted state (idle=muted, lipsync=unmuted if audio separate)
559
+ next.muted = (isMuted !== false);
560
  next.src = videoUrl;
561
  next.load();
562
 
 
581
  }).catch(err => {
582
  console.error('[VIDEO] Play failed:', err);
583
  isTransitioning = false;
584
+ // Retry once on autoplay failure
585
+ next.muted = true;
586
+ next.play().then(() => {
587
+ next.style.opacity = '1';
588
+ setTimeout(() => {
589
+ prev.style.opacity = '0';
590
+ prev.pause();
591
+ prev.src = '';
592
+ prev.style.zIndex = '0';
593
+ next.style.zIndex = '1';
594
+ currentVideo = currentVideo === 'A' ? 'B' : 'A';
595
+ isTransitioning = false;
596
+ }, 300);
597
+ }).catch(e => {
598
+ console.error('[VIDEO] Muted retry also failed:', e);
599
+ isTransitioning = false;
600
+ });
601
  });
602
  };
603
 
 
622
  if (!clip) return;
623
  lastPlayed = clip.filename;
624
  videoStatus.textContent = 'idle: ' + clip.name;
625
+ crossfadeTo('/api/stream/' + encodeURIComponent(clip.filename), true);
626
  }
627
 
628
  // When active video ends → play next idle (unless processing)
 
735
  // Play lipsync video if available (Phase 2)
736
  if (data.lipsync_video_url) {
737
  videoStatus.textContent = 'speaking...';
738
+ crossfadeTo(data.lipsync_video_url, true);
739
  // After lipsync ends, resume idle
740
  const activeVid = getInactiveVideo(); // will become active after crossfade
741
  const onLipsyncEnd = () => {