Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Fixes #2919 Fixes #2673 Fixes #2641 Hide Youtube fixes #3394

Merged
merged 4 commits into from
May 28, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 82 additions & 20 deletions app/src/main/assets/web_extensions/webcompat_youtube/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()) {
Expand All @@ -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)}`);
Expand All @@ -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.
Expand All @@ -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<handles.length; i++) {
handles[i].removeEventListener('contextmenu', this.onContextMenu);
Expand All @@ -169,9 +182,10 @@ class YoutubeExtension {
}

// Prevent the double click to reach the player to avoid double clicking
// to trigger a playback forward event.
// to trigger a playback forward/backward event.
// https://github.com/MozillaReality/FirefoxReality/issues/2947
videoControlsForwardFix() {
logDebug('videoControlsForwardFix');
const playbackControls = document.querySelector(YT_SELECTORS.playbackControls);
playbackControls.removeEventListener("touchstart", this.videoControlsOnTouchStart);
playbackControls.addEventListener("touchstart", this.videoControlsOnTouchStart);
Expand All @@ -189,9 +203,28 @@ class YoutubeExtension {
return false;
}

playerControlsMarginFix() {
if (youtube.isInFullscreen()) {
if (window.innerHeight < CHROME_CONTROLS_MIN_WIDTH) {
var of = CHROME_CONTROLS_MIN_WIDTH - window.innerHeight;
console.log(of)
document.querySelector(".ytp-chrome-bottom").style.setProperty("margin-bottom", `${of}px`)
} else {
document.querySelector(".ytp-chrome-bottom").style.removeProperty("margin-bottom")
}

} else {
document.querySelector(".ytp-chrome-bottom").style.removeProperty("margin-bottom")
}
}

playerFixes() {
this.fixQueueContextMenu();
this.overrideVideoProjection();
this.overrideQualityRetry();
this.hideOverlaysFix();
this.queueContextMenuFix();
this.videoControlsForwardFix();
this.playerControlsMarginFix();
}

// Runs the callback when the video is ready (has loaded the first frame).
Expand Down Expand Up @@ -252,13 +285,41 @@ class YoutubeExtension {
return video && video.readyState >=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`);
Expand All @@ -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}`);
Expand All @@ -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());