PageSourceSearch

https://rozetta-webxr-prod-tokyo.web.app/js/video-position-sync.js

js rozetta-webxr-prod-tokyo.web.app collected 2026-10-03 12:35:34 UTC 18,850 bytes, 497 lines download raw bytes

1function loadVideo(videoEl, src) {
2  const isHls = getQueryStringValue('isHls');
3
4  if (isHls) {
5    if (src.endsWith('.m3u8')) {
6      const config = {
7        maxBufferLength: 30,
8        debug: true,
9        autoStartLoad: true
10      };
11      let hls = new Hls(config);
12      hls.attachMedia(videoEl);
13
14      hls.on(Hls.Events.MEDIA_ATTACHED, function () {
15          hls.loadSource(src);
16      });
17    } else if (src.endsWith('.mpd')) {
18      let dashPlayer = dashjs.MediaPlayer().create();
19      dashPlayer.initialize(videoEl, src, false);
20      // dashPlayer.on('playbackPlaying', function () {
21      //     console.log("videoEl.currentTime playing: ", videoEl.currentTime);
22      // });
23    }
24  } else {
25    videoEl.setAttribute('src', src);
26  }
27}
28
29export async function loadPlayer() {
30  const firebaseDb = firebase.database();
31  const previewVideoLink = getQueryStringValue('previewVideoLink');
32  const roomId = getQueryStringValue('roomId');
33  let isSelfHost = /selfhost/.test(roomId);
34  let invitedSelfHost = isSelfHost && getQueryStringValue('notAuth') === 'true';
35  let isWorld = getQueryStringValue('type') === 'world';
36  let isKaraoke = getQueryStringValue('isKaraoke') === 'true';
37  let staticVideoLink = previewVideoLink ? previewVideoLink : getQueryStringValue('staticVideoLink');
38  let isPreview = !!previewVideoLink;
39  const isSimpleMenu = getQueryStringValue('isSimpleMenu') === 'true';
40  let videoPosListener, hostVideoPos = {}, isPlaying = false;
41  const oculusRightControl = document.querySelector('#oculus-right');
42
43  window.Sentry?.setTag("isVideoMode", !!staticVideoLink);
44
45  if (previewVideoLink) {
46    try {
47      console.log("=== has preview video, so checking which video is playing ===")
48      const snapshot = await firebaseDb.ref(`opentok/rooms/${roomId}/videoPos`).get();
49
50      if (snapshot.exists()) {
51        const val = snapshot.val();
52        if (val && isPreview !== val.isPreview) {
53          isPreview = val.isPreview;
54          staticVideoLink = getQueryStringValue(isPreview ? 'previewVideoLink' : 'staticVideoLink');
55          console.log("isPreview: ", isPreview);
56          console.log("videoLink: ", staticVideoLink);
57          console.log("=== check completed ===");
58        }
59      }
60    } catch (err) {
61      console.error(err);
62    }
63  }
64
65  // Encode S3 URL to avoid special char in filename
66  let staticVideoLinkBaseHost = staticVideoLink.substring(0, staticVideoLink.lastIndexOf("/") + 1);
67  let staticVideoLinkFilename = staticVideoLink.substring(staticVideoLink.lastIndexOf("/") + 1, staticVideoLink.length);
68  staticVideoLink = staticVideoLinkBaseHost + encodeURIComponent(staticVideoLinkFilename);
69  console.log("Encoded staticVideoLink: ", staticVideoLink);
70
71  if (staticVideoLink) {
72    let subtitlePc = document.getElementById('subtitle-pc');
73    let subtitleTxtAframe = document.getElementById('subtitle-message-content');
74    let subtitleTxtAframeBoard = document.getElementById('subtitle-message-board');
75    let subtitleTxtAframeWrapper = document.getElementById('subtitle-message-board-wrapper');
76    
77    console.log("is2dVideo: ", is2dVideo);
78    const videoEl = createVideoPlayer(staticVideoLink, is2dVideo);
79    const videoControlButtonEl = document.getElementById('videoControlButton');
80
81    if (isKaraoke){
82      videoEl.volume = 0.2;
83    }
84
85    const soundBtn = document.getElementById('soundBtn');
86    const soundBtnAframe = document.getElementById('soundButton');
87
88    // let soundMuted = true;
89    let soundMuted = false;
90    if(!isChatbotDisabled && !isMsAIWorld) {
91      soundMuted = switchSoundMode(videoEl, 'html5', soundMuted, soundBtn, soundBtnAframe);
92    } else if (!isSimpleMenu && (!isKaraoke || !hasVrMusicQuery)) {
93      soundBtn.style.display = 'block';
94      soundBtn.addEventListener('click', function () {
95        soundMuted = switchSoundMode(videoEl, 'html5', soundMuted, soundBtn, soundBtnAframe);
96      }, false);
97    } else {
98      soundBtn.style.display = 'none';
99      soundMuted = switchSoundMode(videoEl, 'html5', soundMuted, soundBtn, soundBtnAframe);
100      soundBtnAframe.setAttribute('material', {visible: false});
101    }
102
103    let pausePressed = false;
104
105    oculusRightControl.addEventListener('triggerdown', evt => {
106      if (oculusIntersected === 'sound-control' && !isKaraoke) {
107        soundMuted = switchSoundMode(videoEl, 'html5', soundMuted, soundBtn, soundBtnAframe);
108      }
109
110      if (oculusIntersected === 'play-video-control') {
111        if (videoControlButtonEl.getAttribute('src') === "#playButtonImg") {
112          videoEl.play();
113        } else {
114          pausePressed = true;
115          videoEl.pause();
116        }
117      }
118    });
119
120    // Left controller not in use
121    /*const oculusLeftControl = document.querySelector('#oculus-left');
122
123    oculusLeftControl.removeEventListener('triggerdown', function () {
124      console.log("remove event from oculus left control");
125    });
126
127    oculusLeftControl.addEventListener('triggerdown', evt => {
128      if (oculusIntersected === 'sound-control') {
129        soundMuted = switchSoundMode(videoEl, 'html5', soundMuted, soundBtn, soundBtnAframe);
130      }
131      if (oculusIntersected === 'play-video-control') {
132        if (videoControlButtonEl.getAttribute('src') === "#playButtonImg") {
133          videoEl.play();
134        } else {
135          videoEl.pause();
136        }
137      }
138    });*/
139    console.log("isHost: ", isHost);
140    const videoSlider = document.getElementById('videoSlider');
141    const videoSliderValue = document.getElementById('video-slider-value');
142    const bufferedAmount = document.getElementById('buffered-amount');
143    const progressAmount = document.getElementById('progress-amount');
144    const videoBufferedEl = document.getElementById('buffered-el');
145    const videoProgressEl = document.getElementById('progress-el');
146    const playControlBtn = document.getElementById('playBtn');
147
148    let playPromise = null;
149    let isSeeked = false;
150
151    if (sampleVideo && !isFullSampleVideo) {
152      videoEl.addEventListener("timeupdate", function(){
153        if (videoEl.currentTime >= 90) {
154          pausePressed = true;
155          videoEl.pause();
156          isPlaying = false;
157        }
158
159        if (videoEl.currentTime >= 91) {
160          setTimeout(() => {
161            videoEl.currentTime = 0;
162          }, 250); // to avoid flickering
163        }
164      });
165    }
166
167    if (isHost || (sampleVideo && !isFullSampleVideo)) {
168      videoEl.addEventListener('progress', function() {
169        if (videoEl.buffered.length > 0) {
170          const bufferedEnd = videoEl.buffered.end(videoEl.buffered.length - 1);
171          const duration =  videoEl.duration;
172          if (duration > 0 && bufferedAmount) {
173            bufferedAmount.style.width = ((bufferedEnd / duration)*100) + "%";
174          }
175        }
176      });
177
178      if (videoControlButtonEl) {
179        if(isChatbotDisabled || isMsAIWorld) 
180          videoControlButtonEl.setAttribute('visible', true);
181        videoControlButtonEl.setAttribute('src', '#playButtonImg');
182      }
183      if (videoSlider) {
184        videoSlider.style.display = 'block';
185        videoSlider.oninput = function() {
186          let currentPosition = (this.value / 1000) * videoEl.duration;
187          videoSliderValue.innerHTML = new Date(currentPosition * 1000).toISOString().substr(11, 8);
188          videoEl.currentTime = currentPosition;
189          isSeeked = true;
190        }
191      }
192      if (videoSliderValue) {
193        videoSliderValue.style.display = 'block';
194      }
195      if (videoBufferedEl) {
196        videoBufferedEl.style.display = 'block';
197      }
198      if (videoProgressEl) {
199        videoProgressEl.style.display = 'block';
200      }
201      if (playControlBtn) {
202        playControlBtn.style.display = 'block';
203
204        playControlBtn.addEventListener("click", function() {
205          console.log("Clicked play/pause button, current isPlaying: ", isPlaying);
206          if (isPlaying == false) {
207            requestDeviceOrientationListener();
208            playPromise = videoEl.play();
209            if (!!playPromise) {
210              playPromise.then(_ => {
211                isPlaying = true
212                // playControlBtn.setAttribute('src', '../../img/pause.png');
213              }).catch(error => {
214                console.log("Unable to set isPlaying value with error: ", error);
215              });
216            }
217          } else {
218            if (!!playPromise) {
219              playPromise.then(_ => {
220                pausePressed = true;
221                videoEl.pause();
222                isPlaying = false;
223                // playControlBtn.setAttribute('src', '../../img/play-black.png');
224              }).catch(error => {
225                console.log("Unable to pause with error: ", error);
226              });
227            }
228          }
229        });
230      }
231
232      videoEl.onseeked = async function(event) {
233        console.log('Video seeked to: ', videoEl.currentTime);
234        if (isSeeked && meId) {
235          hostVideoPos["host"] = meId;
236          hostVideoPos["isPlaying"] = !videoEl.paused;
237          hostVideoPos["currentTime"] = videoEl.currentTime;
238          hostVideoPos["isPreview"] = isPreview;
239          hostVideoPos["last_synced_at"] = new Date();
240
241          console.log("=== syncing video position (seeked) ===");
242          console.log("sync data: ", hostVideoPos);
243          await firebaseDb.ref(`opentok/rooms/${roomId}/videoPos`).update(hostVideoPos);
244          isSeeked = false;
245          console.log("=== video sync completed ===");
246        }
247      };
248      videoEl.onplay = async function () {
249        console.log("on play: ", videoEl.currentTime);
250
251        if (sampleVideo && !isFullSampleVideo && videoEl.currentTime >= 90) {
252          videoEl.currentTime = 0;
253        }
254
255        if (meId) {
256          hostVideoPos["host"] = meId;
257          hostVideoPos["isPlaying"] = true;
258          hostVideoPos["currentTime"] = videoEl.currentTime;
259          hostVideoPos["isPreview"] = isPreview;
260          hostVideoPos["last_synced_at"] = new Date();
261  
262          console.log("=== syncing video position (played) ===");
263          console.log("sync data: ", hostVideoPos);
264          await firebaseDb.ref(`opentok/rooms/${roomId}/videoPos`).update(hostVideoPos);
265        }
266        if (videoControlButtonEl) {
267          videoControlButtonEl.setAttribute('src', '#pauseButtonImg');
268        }
269        if (playControlBtn) {
270          playControlBtn.setAttribute('src', '../../img/pause.png');
271        }
272        console.log("=== video sync completed ===");
273      }
274      videoEl.onpause = async function () {
275        if (pausePressed && meId) {
276          console.log("on pause: ", videoEl.currentTime);
277          hostVideoPos["host"] = meId;
278          hostVideoPos["isPlaying"] = false;
279          hostVideoPos["currentTime"] = videoEl.currentTime;
280          hostVideoPos["isPreview"] = isPreview;
281          hostVideoPos["last_synced_at"] = new Date();
282
283          console.log("=== syncing video position (paused) ===");
284          console.log("sync data: ", hostVideoPos);
285          await firebaseDb.ref(`opentok/rooms/${roomId}/videoPos`).update(hostVideoPos);
286          pausePressed = false;
287          console.log("=== video sync completed ===");
288        }
289
290        // isPlaying = false;
291        if (videoControlButtonEl) {
292          videoControlButtonEl.setAttribute('src', '#playButtonImg');
293        }
294        if (playControlBtn) {
295          playControlBtn.setAttribute('src', '../../img/play-black.png');
296        }
297      }
298      if (!is2dVideo) document.getElementById("html-control-panel").prepend(videoEl);
299    } else {
300      // If is guest user
301      if (!is2dVideo) document.getElementById("html-control-panel").appendChild(videoEl);
302      if (videoControlButtonEl) {
303        videoControlButtonEl.remove();
304      }
305    }
306
307    videoEl.onended = async function() {
308      isPlaying = false;
309
310      if (isPreview) {
311        const nextVideoLink = getQueryStringValue('staticVideoLink');
312
313        if (nextVideoLink) {
314          isPreview = false;
315          staticVideoLink = nextVideoLink;
316          loadVideo(videoEl, staticVideoLink);
317          videoEl.play();
318          isPlaying = true;
319        }
320      } else {
321        if (isHost) {
322          if (meId) {
323            hostVideoPos["host"] = meId;
324            hostVideoPos["isPlaying"] = false;
325            hostVideoPos["currentTime"] = videoEl.currentTime;
326            hostVideoPos["last_synced_at"] = new Date();
327  
328            console.log("=== syncing video position (ended) ===");
329            console.log("sync data: ", hostVideoPos);
330            await firebaseDb.ref(`opentok/rooms/${roomId}/videoPos`).update(hostVideoPos);
331            console.log("=== video sync completed ===")
332          }
333
334          if ((isSelfHost || isWorld) && !window.hasEvaluation) {
335              window.openModal('#evaluation-modal');
336          }
337        } else if (!isKaraoke && !isTherapy && !invitedSelfHost && !window.hasEvaluation) {
338          window.openModal('#evaluation-modal');
339        }
340      }
341    }
342
343    loadVideo(videoEl, staticVideoLink);
344
345    // const videoSphereEl = document.querySelector("a-videosphere");
346    // videoSphereEl.setAttribute('src', '#staticVideo');
347
348    videoEl.onloadedmetadata = async function() {
349      if (!videoPosListener) {
350        videoPosListener = function(snapshot) {
351          const val = snapshot.val();
352          if (!val || val.host === meId) return;
353
354          console.log("[Sync Video Position] data: ", val);
355
356          if (isPreview !== val.isPreview) {
357            isPreview = val.isPreview;
358            staticVideoLink = getQueryStringValue(isPreview ? 'previewVideoLink' : 'staticVideoLink');
359            loadVideo(videoEl, staticVideoLink);
360            if (val.isPlaying) {
361                videoEl.play();
362            }
363            console.log("[Sync Video Position] video switched");
364            return;
365          }
366
367          let hostCurrentTime = val.currentTime || 0;
368          if (val.isPlaying) {
369            const diff = (new Date() - new Date(val.last_synced_at)) / 1000 + hostCurrentTime;
370            videoEl.currentTime = diff % videoEl.duration;
371            console.debug(`[Sync Video Position] Set video postion = ${videoEl.currentTime}`);
372            playPromise = videoEl.play();
373            isPlaying = true;
374            console.debug('[Sync Video Position] Video position was synced');
375          } else {
376            videoEl.currentTime = hostCurrentTime;
377            if (playPromise !== undefined && playPromise !== null) {
378              playPromise.then(_ => {
379                videoEl.pause();
380                isPlaying = false;
381              })
382              .catch(error => {
383                console.error("Unable to pause with error: ", error);
384              });
385            }
386          }
387        };
388
389        firebaseDb.ref(`opentok/rooms/${roomId}/videoPos`).on('value', videoPosListener);
390      }
391    }
392    videoEl.ontimeupdate = function () {
393      if ((srtSubtitleEnabled && srtSubtitleModeValue && srtSubtitleModeValue === 'on')) {
394        let subtitle = getSubtitleByTime(subtitles, videoEl.currentTime*1000);
395        console.log("subtitle: ", subtitle);
396
397        let userColor = '#FFFFFF';
398
399        if (subtitleTxtAframe.childElementCount > 0) {
400          subtitleTxtAframe.removeChild(subtitleTxtAframe.childNodes[0]);
401        } else {
402          subtitleTxtAframeWrapper.object3D.visible = false;
403          subtitlePc.innerHTML = "";
404          subtitleTxtAframe.innerHTML = "";
405        }
406        if (subtitlePc.childElementCount > 0) {
407          subtitlePc.removeChild(subtitlePc.childNodes[0]);
408        } else {
409          subtitlePc.style.display = 'none';
410        }
411
412        // Only show subtitle elements when there is subtitle text
413        if (subtitle.length > 0) {
414          subtitlePc.insertAdjacentHTML('beforeend', '<div style="color:white;"><span style="font-weight:bolder;font-style:italic;color: ' + userColor + ';"> </span>' + subtitle + '</div>');
415          subtitleTxtAframe.insertAdjacentHTML('beforeend', '<div style="color:white;"><span style="font-weight:bolder;font-style:italic;color: ' + userColor + ';"> </span>' + subtitle + '</div>');
416
417          if (subtitleTxtAframe.childElementCount > 0 && window.isEnterVR) {
418            subtitleTxtAframeWrapper.object3D.visible = true;
419          }
420
421          if (subtitlePc.childElementCount > 0 && !window.isEnterVR) {
422            subtitlePc.style.display = 'block';
423          }
424        } else {
425          subtitlePc.style.display = 'none';
426          subtitleTxtAframeWrapper.object3D.visible = false;
427        }
428      }
429
430      const time = new Date(videoEl.currentTime * 1000).toISOString().substr(11, 8)
431      const elapsedTimer = document.getElementById('elapsed-timer');
432      elapsedTimer.innerHTML = time;
433
434      videoSliderValue.innerHTML = time;
435      if (videoSlider) {
436        videoSlider.value = (videoEl.currentTime/videoEl.duration)*1000;
437      }
438
439      const aframeElapsedTimer = document.getElementById('aframe-elapsed-timer');
440      aframeElapsedTimer.setAttribute('value', time);
441
442      // console.log("duration: ", videoEl.duration);
443      var duration =  videoEl.duration;
444      if (duration > 0 && progressAmount) {
445        progressAmount.style.width = ((videoEl.currentTime / duration)*100) + "%";
446      }
447    }
448  }
449};
450
451function createVideoPlayer(src, is2d) {
452  let videoEl = document.createElement('video');
453  videoEl.setAttribute('id', 'staticVideo');
454  videoEl.setAttribute('preload', 'auto');
455  videoEl.setAttribute('src', src);
456  videoEl.setAttribute('crossorigin', 'anonymous');
457  videoEl.setAttribute('playsinline', '');
458  // videoEl.setAttribute('muted', '');
459  // videoEl.muted = true;
460
461  if (is2d) {
462    videoEl.addEventListener('loadeddata', (evt) => {
463      let player2dEl = document.createElement('a-video');
464      player2dEl.setAttribute('id', 'youtube-player');
465      player2dEl.setAttribute('position', {x:0, y:5, z:-20});
466      player2dEl.setAttribute('width', 40);
467      player2dEl.setAttribute('height', 23);
468      player2dEl.setAttribute('src', '#staticVideo');
469      document.querySelector('a-scene').appendChild(player2dEl);
470      player2dEl.addEventListener('loaded', () => {
471        evt.target.play();
472      });
473    });
474  
475    document.querySelector('a-assets').appendChild(videoEl);
476  } else {
477    const videoSphereEl = document.querySelector("a-videosphere");
478    videoSphereEl.setAttribute('src', '#staticVideo');
479  }
480
481  return videoEl;
482}
483
484function requestDeviceOrientationListener() {
485  // request for access to device orientation sensor
486  if (typeof DeviceOrientationEvent.requestPermission === 'function') {
487    DeviceOrientationEvent.requestPermission().then((state) => {
488      if (state !== 'granted') {
489        return;
490      }
491      const el = document.querySelector('#aframe-view a-scene');
492      if (el) {
493        el.emit('deviceorientationpermissiongranted');
494      }
495    }).catch(console.error);
496  }
497}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.