diff --git a/app/src/main/assets/web_extensions/webcompat_youtube/main.js b/app/src/main/assets/web_extensions/webcompat_youtube/main.js index 7f54ea42e..93622aaa2 100644 --- a/app/src/main/assets/web_extensions/webcompat_youtube/main.js +++ b/app/src/main/assets/web_extensions/webcompat_youtube/main.js @@ -10,11 +10,13 @@ const YT_SELECTORS = { thumbnail: '.ytp-cued-thumbnail-overlay-image', embedTitle: '.ytp-title-text', queueHandle: 'ytd-playlist-panel-video-renderer', - playbackControls: '.ytp-left-controls' + playbackControls: '.ytp-chrome-bottom', + overlays: '.videowall-endscreen, .ytp-upnext, .ytp-ce-element' }; const ENABLE_LOGS = true; const logDebug = (...args) => ENABLE_LOGS && console.log(LOGTAG, ...args); const logError = (...args) => ENABLE_LOGS && console.error(LOGTAG, ...args); +const CHROME_CONTROLS_MIN_WIDTH = 480; class YoutubeExtension { // We set a custom UA to force Youtube to display the most optimal @@ -75,11 +77,27 @@ class YoutubeExtension { return text.includes('360'); } + is360Video() { + const targets = [ + document.querySelector(YT_SELECTORS.disclaimer), + document.querySelector(YT_SELECTORS.embedTitle) + ]; + return targets.some((node) => node && this.is360(node.textContent));; + } + isStereo(text) { const words = text.toLowerCase().split(/\s+|\./); return words.includes('stereo') || words.includes('3d') || words.includes('vr'); } + isStereoVideo() { + const targets = [ + document.querySelector(YT_SELECTORS.disclaimer), + document.querySelector(YT_SELECTORS.embedTitle) + ]; + return targets.some((node) => node && this.isStereo(node.textContent));; + } + // Automatically select a video projection if needed overrideVideoProjection() { if (!this.isWatchingPage()) { @@ -94,14 +112,8 @@ class YoutubeExtension { } // There is no standard API to detect video projection yet. // Try to infer it from the video disclaimer or title for now. - const targets = [ - document.querySelector(YT_SELECTORS.disclaimer), - document.querySelector(YT_SELECTORS.embedTitle) - ]; - const is360 = targets.some((node) => node && this.is360(node.textContent)); - if (is360) { - const stereo = targets.some((node) => node && this.isStereo(node.textContent)); - qs.set('mozVideoProjection', stereo ? '360s_auto' : '360_auto'); + if (this.is360Video()) { + qs.set('mozVideoProjection', this.isStereoVideo() ? '360s_auto' : '360_auto'); this.updateURL(qs); this.updateVideoStyle(); logDebug(`Video projection set to: ${qs.get(VIDEO_PROJECTION_PARAM)}`); @@ -119,7 +131,7 @@ class YoutubeExtension { } overrideClick(event) { - this.overrideVideoProjection(); + this.playerFixes(); const player = this.getPlayer(); if (!this.isWatchingPage() || !this.hasVideoProjection() || document.fullscreenElement || !player) { return; // Only override click in the Youtube watching page for 360 videos. @@ -146,7 +158,8 @@ class YoutubeExtension { // Fix for the draggable items to continue being draggable when a context menu is displayed. // https://github.com/MozillaReality/FirefoxReality/issues/2611 - fixQueueContextMenu() { + queueContextMenuFix() { + logDebug('queueContextMenuFix'); const handles = document.querySelectorAll(YT_SELECTORS.queueHandle); for (var i=0; i=2; } + // Hide overlays when in immersive mode + // https://github.com/MozillaReality/FirefoxReality/issues/2673 + hideOverlaysFix() { + if (youtube.is360Video() || youtube.isStereoVideo()) { + logDebug('hideOverlaysFix'); + var overlays = document.querySelectorAll(YT_SELECTORS.overlays); + var observer = new MutationObserver((mutations) => { + if (youtube.isInFullscreen()) { + for (const mutation of mutations) { + if (mutation.target) { + mutation.target.style.display = 'none'; + } + } + } + }); + for(const overlay of overlays) { + observer.observe(overlay, { attributes: true }); + } + } + } + + isInFullscreen() { + return !((document.fullScreenElement !== undefined && document.fullScreenElement === null) || + (document.msFullscreenElement !== undefined && document.msFullscreenElement === null) || + (document.mozFullScreen !== undefined && !document.mozFullScreen) || + (document.webkitIsFullScreen !== undefined && !document.webkitIsFullScreen)); + } + // Utility function to retry tasks max n times until the execution is successful. retry(taskName, task, attempts = 10, interval = 200) { let succeeded = false; try { succeeded = task(); } catch (ex) { - logError(`Got exception runnning ${taskName} task: ${ex}`); + logError(`Got exception running ${taskName} task: ${ex}`); } if (succeeded) { logDebug(`${taskName} succeeded`); @@ -278,6 +339,8 @@ class YoutubeExtension { window.history.replaceState({}, document.title, newUrl); logDebug(`update URL to ${newUrl}`); } + + } logDebug(`Initializing youtube extension in frame: ${window.location.href}`); @@ -286,18 +349,17 @@ youtube.overrideUA(); youtube.overrideViewport(); window.addEventListener('load', () => { logDebug('page load'); - youtube.overrideVideoProjection(); - youtube.fixQueueContextMenu(); // Wait until video has loaded the first frame to force quality change. // This prevents the infinite spinner problem. // See https://github.com/MozillaReality/FirefoxReality/issues/1433 if (youtube.isWatchingPage()) { - youtube.waitForVideoReady(() => youtube.overrideQualityRetry()); + youtube.waitForVideoReady(() => youtube.playerFixes()); } }); - +window.addEventListener("resize", () => youtube.playerControlsMarginFix()); +document.addEventListener("fullscreenchange", () => youtube.playerControlsMarginFix()); window.addEventListener('pushstate', () => youtube.overrideVideoProjection()); window.addEventListener('popstate', () => youtube.overrideVideoProjection()); window.addEventListener('click', event => youtube.overrideClick(event)); -window.addEventListener('mouseup', event => youtube.playerFixes()); -window.addEventListener("yt-navigate-start", () => youtube.playerFixes()); +window.addEventListener('mouseup', event => youtube.queueContextMenuFix()); +window.addEventListener("yt-navigate-finish", () => youtube.playerFixes()); \ No newline at end of file