// ==UserScript==
// @name Resize YT To Window Size
// @description Moves the YouTube video to the top of the website and fill the window with the video player.
// @author Chris H (Zren / Shade)
// @license MIT
// @icon https://s.ytimg.com/yts/img/favicon_32-vflOogEID.png
// @homepageURL https://github.com/Zren/ResizeYoutubePlayerToWindowSize/
// @namespace http://xshade.ca
// @version 139
// @include http*://*.youtube.com/*
// @include http*://youtube.com/*
// @include http*://*.youtu.be/*
// @include http*://youtu.be/*
// @grant none
// ==/UserScript==
// Github: https://github.com/Zren/ResizeYoutubePlayerToWindowSize
// GreasyFork: https://greasyfork.org/scripts/811-resize-yt-to-window-size
// OpenUserJS.org: https://openuserjs.org/scripts/zren/Resize_YT_To_Window_Size
// Userscripts.org: http://userscripts-mirror.org/scripts/show/153699
(function (window) {
"use strict";
//--- Settings
var playerHeight = '100vh';
var enableOnLoad = true;
var scriptToggleKey = 'w';
//--- Imported Globals
// yt
// ytcenter
// html5Patched (Youtube+)
// ytplayer
var uw = window;
//--- Already Loaded?
// GreaseMonkey loads this script twice for some reason.
if (uw.ytwp) return;
//--- Is iframe?
function inIframe () {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
if (inIframe()) return;
//--- Utils
function isStringType(obj) { return typeof obj === 'string'; }
function isArrayType(obj) { return obj instanceof Array; }
function isObjectType(obj) { return typeof obj === 'object'; }
function isUndefined(obj) { return typeof obj === 'undefined'; }
function buildVenderPropertyDict(propertyNames, value) {
var d = {};
for (var i in propertyNames)
d[propertyNames[i]] = value;
return d;
}
function observe(selector, config, callback) {
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation){
callback(mutation);
});
});
var target = document.querySelector(selector);
if (!target) {
return null;
}
observer.observe(target, config);
return observer;
}
//--- Stylesheet
var JSStyleSheet = function(id) {
this.id = id;
this.stylesheet = '';
};
JSStyleSheet.prototype.buildRule = function(selector, styles) {
var s = "";
for (var key in styles) {
s += "\t" + key + ": " + styles[key] + ";\n";
}
return selector + " {\n" + s + "}\n";
};
JSStyleSheet.prototype.appendRule = function(selector, k, v) {
if (isArrayType(selector))
selector = selector.join(',\n');
var newStyle;
if (!isUndefined(k) && !isUndefined(v) && isStringType(k)) { // v can be any type (as we stringify it).
var d = {};
d[k] = v;
newStyle = this.buildRule(selector, d);
} else if (!isUndefined(k) && isUndefined(v) && isObjectType(k)) {
newStyle = this.buildRule(selector, k);
} else {
// Invalid Arguments
console.log('Illegal arguments', arguments);
return;
}
this.stylesheet += newStyle;
};
JSStyleSheet.injectIntoHeader = function(injectedStyleId, stylesheet) {
var styleElement = document.getElementById(injectedStyleId);
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.type = 'text/css';
styleElement.id = injectedStyleId;
document.getElementsByTagName('head')[0].appendChild(styleElement);
}
styleElement.appendChild(document.createTextNode(stylesheet));
};
JSStyleSheet.prototype.injectIntoHeader = function() {
JSStyleSheet.injectIntoHeader(this.id, this.stylesheet);
};
//--- History
var HistoryEvent = function() {}
HistoryEvent.listeners = []
HistoryEvent.dispatch = function(state, title, url) {
var stack = this.listeners
for (var i = 0, l = stack.length; i < l; i++) {
stack[i].call(this, state, title, url)
}
}
HistoryEvent.onPushState = function(state, title, url) {
HistoryEvent.dispatch(state, title, url)
return HistoryEvent.origPushState.apply(window.history, arguments)
}
HistoryEvent.onReplaceState = function(state, title, url) {
HistoryEvent.dispatch(state, title, url)
return HistoryEvent.origReplaceState.apply(window.history, arguments)
}
HistoryEvent.inject = function() {
if (!HistoryEvent.injected) {
HistoryEvent.origPushState = window.history.pushState
HistoryEvent.origReplaceState = window.history.replaceState
window.history.pushState = HistoryEvent.onPushState
window.history.replaceState = HistoryEvent.onReplaceState
HistoryEvent.injected = true
}
}
HistoryEvent.timerId = 0
HistoryEvent.onTick = function() {
var currentPage = window.location.pathname + window.location.search
if (HistoryEvent.lastPage != currentPage) {
HistoryEvent.dispatch({}, document.title, window.location.href)
HistoryEvent.lastPage = currentPage
}
}
HistoryEvent.startTimer = function() {
HistoryEvent.lastPage = window.location.pathname + window.location.search
HistoryEvent.timerId = setInterval(HistoryEvent.onTick, 500)
}
HistoryEvent.stopTimer = function() {
clearInterval(HistoryEvent.timerId)
}
window.ytwpHistoryEvent = HistoryEvent
//--- Constants
var scriptShortName = 'ytwp'; // YT Window Player
var scriptStyleId = scriptShortName + '-style'; // ytwp-style
var scriptBodyClassId = scriptShortName + '-window-player'; // .ytwp-window-player
var viewingVideoClassId = scriptShortName + '-viewing-video'; // .ytwp-viewing-video
var topOfPageClassId = scriptShortName + '-scrolltop'; // .ytwp-scrolltop
var scriptHtmlSelector = 'html:not([fullscreen="true"])';
var scriptBodySelector = 'body.' + scriptBodyClassId; // body.ytwp-window-player
scriptBodySelector += ':not(.enhancer-for-youtube-pinned-player)'; // Support "Enhancer for Youtube" (Pull Request #51)
var scriptSelector = scriptHtmlSelector + ' ' + scriptBodySelector;
var videoContainerId = 'player';
var videoContainerPlacemarkerId = scriptShortName + '-placemarker'; // ytwp-placemarker
var transitionProperties = ["transition", "-ms-transition", "-moz-transition", "-webkit-transition", "-o-transition"];
var transformProperties = ["transform", "-ms-transform", "-moz-transform", "-webkit-transform", "-o-transform"];
//--- YTWP
var ytwp = uw.ytwp = {
scriptShortName: scriptShortName, // YT Window Player
log_: function(logger, args) { logger.apply(console, ['[' + this.scriptShortName + '] '].concat(Array.prototype.slice.call(args))); return 1; },
log: function() { return this.log_(console.log, arguments); },
error: function() { return this.log_(console.error, arguments); },
initialized: false,
pageReady: false,
isWatchPage: false,
};
ytwp.debugPage = function() {
function prettyHtml(el) {
var s = el.outerHTML
return s.substr(0, s.indexOf('>')+1)
}
var defStyle = {
'display':'block', 'position': 'static',
'left': 'auto', 'right': 'auto', 'top': 'auto', 'bottom': 'auto',
'padding-left':'0px', 'padding-right':'0px', 'padding-top':'0px', 'padding-bottom':'0px',
'margin-left':'0px', 'margin-right':'0px', 'margin-top':'0px', 'margin-bottom':'0px',
'width': 'auto', 'min-width': 'auto', 'max-width': 'auto',
'height': 'auto', 'min-height': 'auto', 'max-height': 'auto',
}
var keyFilter = Object.keys(defStyle)
var node = document.querySelector('#movie_player video')
var outStr = ''
while (node && node.parentNode) {
var style = getComputedStyle(node)
var styleDiff = {}
for (var key of style) {
if (keyFilter.includes(key) && style[key] != defStyle[key]) {
styleDiff[key] = style[key]
}
}
outStr += prettyHtml(node) + ' ' + JSON.stringify(styleDiff) + '\n'
node = node.parentNode
}
outStr = outStr.split('\n').reverse().join('\n')
ytwp.log('debugPage', outStr)
}
ytwp.hasYoutubeChanged = function() {
var tree = [
'html',
'body',
'ytd-app',
'#content.ytd-app',
'ytd-page-manager#page-manager.ytd-app',
'ytd-watch-flexy.ytd-page-manager',
'#full-bleed-container.ytd-watch-flexy',
'#player-full-bleed-container.ytd-watch-flexy',
'#player-container.ytd-watch-flexy',
'ytd-player#ytd-player.ytd-watch-flexy',
'#container.ytd-player',
'.html5-video-player',
'.html5-video-container',
'video.html5-main-video',
]
tree = tree.reverse()
var node = document.querySelector(tree[0])
if (!node) {
ytwp.error('YT has changed!', tree[0], 'no longer exists!')
return true
}
for (var i = 1; i < tree.length; i++) {
var parent = node.parentNode
var selector = tree[i]
if (parent.matches(selector)) {
node = parent
} else {
ytwp.error('YT has changed!', selector, 'no longer exists!')
}
}
return false
}
ytwp.isWatchUrl = function (url) {
if (!url)
url = uw.location.href;
if (url.match(/https?:\/\/(www\.)?youtube.com\/(c|channel|user)\/[^\/]+\/live/)) {
if (document.querySelector('ytd-browse')) {
return false
} else {
return true
}
}
return url.match(/https?:\/\/(www\.)?youtube.com\/watch\?/);
};
ytwp.setTheaterMode = function(enable) {
// ytwp.log('setTheaterMode', enable)
var watchElement = document.querySelector('ytd-watch:not([hidden])') || document.querySelector('ytd-watch-flexy:not([hidden])') || document.querySelector('ytd-watch-grid:not([hidden])')
if (watchElement) {
var isTheater = watchElement.hasAttribute('theater')
if (enable != isTheater) {
// Note: (Issue #75) ytd-watch-flexy watchElement.querySelector() will find
// Nothing for some reason. We need to query from the document scope.
var sizeButton = document.querySelector(watchElement.tagName + ':not([hidden]) button.ytp-size-button')
if (!sizeButton) {
var screenModeButtons = document.querySelectorAll(watchElement.tagName + ':not([hidden]) button.ytp-screen-mode-settings-button')
sizeButton = screenModeButtons[1] // 2nd button is "Theater mode (t)"
}
if (sizeButton) {
sizeButton.click()
}
}
watchElement.canFitTheater_ = true // When it's too small, it disables the theater mode.
} else if (watchElement = document.querySelector('#page.watch')) {
var isTheater = watchElement.classList.contains('watch-stage-mode')
if (enable != isTheater) {
var sizeButton = watchElement.querySelector('button.ytp-size-button')
if (sizeButton) {
sizeButton.click()
}
}
}
}
ytwp.enterTheaterMode = function() {
// ytwp.log('enterTheaterMode')
if (!document.body.classList.contains(scriptBodyClassId)) {
return
}
ytwp.setTheaterMode(true)
}
ytwp.enterTheaterMode();
uw.addEventListener('resize', ytwp.enterTheaterMode);
ytwp.detectPlayerUnavailable = function() {
if (document.querySelector('[player-unavailable]')) {
ytwp.event.removeBodyClass()
}
}
ytwp.init = function() {
ytwp.log('init');
if (!ytwp.initialized) {
ytwp.isWatchPage = ytwp.isWatchUrl();
if (ytwp.isWatchPage) {
ytwp.removeSearchAutofocus();
if (!document.getElementById(scriptStyleId)) {
ytwp.event.initStyle();
}
ytwp.initScroller();
ytwp.initialized = true;
ytwp.pageReady = false;
}
}
ytwp.event.onWatchInit();
if (ytwp.isWatchPage) {
ytwp.html5PlayerFix();
}
}
ytwp.initScroller = function() {
// Register listener & Call it now.
uw.addEventListener('scroll', ytwp.onScroll, false);
uw.addEventListener('resize', ytwp.onScroll, false);
ytwp.onScroll();
}
ytwp.onScroll = function() {
var viewportHeight = document.documentElement.clientHeight;
// topOfPageClassId
if (ytwp.isWatchPage && uw.scrollY == 0) {
document.body.classList.add(topOfPageClassId);
//var player = document.getElementById('movie_player');
//if (player)
// player.focus();
} else {
document.body.classList.remove(topOfPageClassId);
}
// viewingVideoClassId
if (ytwp.isWatchPage && uw.scrollY <= viewportHeight) {
document.body.classList.add(viewingVideoClassId);
} else {
document.body.classList.remove(viewingVideoClassId);
}
}
ytwp.event = {
initStyle: function() {
ytwp.log('initStyle');
ytwp.style = new JSStyleSheet(scriptStyleId);
ytwp.event.buildStylesheet();
// Duplicate stylesheet targeting data-spf-name if enabled.
if (uw.spf) {
var temp = scriptBodySelector;
scriptBodySelector = 'body[data-spf-name="watch"]';
scriptSelector = scriptHtmlSelector + ' ' + scriptBodySelector
ytwp.event.buildStylesheet();
ytwp.style.appendRule('body[data-spf-name="watch"]:not(.ytwp-window-player) #masthead-positioner', {
'position': 'absolute',
'top': playerHeight + ' !important'
});
}
ytwp.style.injectIntoHeader();
},
buildStylesheet: function() {
ytwp.log('buildStylesheet');
//--- Browser Scrollbar
// Chrome/Webkit
ytwp.style.appendRule(scriptBodySelector + '::-webkit-scrollbar', {
'width': '0 !important',
'height': '0 !important',
});
// Firefox/Gecko
// Requires about:config flag to be toggled as of FireFox v63
// https://github.com/Zren/ResizeYoutubePlayerToWindowSize/issues/42
ytwp.style.appendRule('html', {
'scrollbar-width': 'none',
});
//--- Video Player
var d;
d = buildVenderPropertyDict(transitionProperties, 'left 0s linear, padding-left 0s linear');
d['padding'] = '0 !important';
d['margin'] = '0 !important';
ytwp.style.appendRule([
scriptBodySelector + ' #player',
scriptBodySelector + '.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible #player',
scriptBodySelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #player',
scriptBodySelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #player-legacy',
scriptBodySelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #watch7-main-container',
], d);
//
d = buildVenderPropertyDict(transitionProperties, 'width 0s linear, left 0s linear');
// Bugfix for Firefox
// Parts of the header (search box) are hidden under the player.
// Firefox doesn't seem to be using the fixed header+guide yet.
d['float'] = 'initial';
// Skinny mode
d['left'] = 0;
d['margin-left'] = 0;
ytwp.style.appendRule(scriptBodySelector + ' #player-api', d);
// Theater mode
ytwp.style.appendRule(scriptBodySelector + ' .watch-stage-mode #player .player-api', {
'left': 'initial !important',
'margin-left': 'initial !important',
});
// Hide the cinema/wide mode button since it's useless.
//ytwp.style.appendRule(scriptBodySelector + ' #movie_player .ytp-size-button', 'display', 'none');
// !important is mainly for simplicity, but is needed to override the !important styling when the Guide is open due to:
// .sidebar-collapsed #watch7-video, .sidebar-collapsed #watch7-main, .sidebar-collapsed .watch7-playlist { width: 945px!important; }
// Also, Youtube Center resizes #player at element level.
// Don't resize if Youtube+'s html.floater is detected.
// Dont' resize if Youtube+ (Iridium/Material)'s html.iri-always-visible is detected.
ytwp.style.appendRule(
[
scriptSelector + ' #player',
scriptSelector + ' #player-wrap',
scriptSelector + ' #player-api',
scriptHtmlSelector + ':not(.floater):not(.iri-always-visible) ' + scriptBodySelector + ' #movie_player',
scriptSelector + ' #player-mole-container',
scriptHtmlSelector + ':not(.floater):not(.iri-always-visible) ' + scriptBodySelector + ' .html5-video-container',
scriptHtmlSelector + ':not(.floater):not(.iri-always-visible) ' + scriptBodySelector + ' .html5-main-video',
scriptSelector + ' ytd-watch-flexy[theater] #player-theater-container.ytd-watch-flexy',
scriptSelector + ' ytd-watch-flexy[flexy] #player-container-outer.ytd-watch-flexy',
scriptSelector + ' ytd-watch-flexy[flexy] #player-container-inner.ytd-watch-flexy',
scriptSelector + ' ytd-watch-flexy[flexy] #player-container.ytd-watch-flexy',
scriptSelector + ' ytd-watch-grid[theater] #player-theater-container.ytd-watch-grid',
scriptSelector + ' ytd-watch-grid[flexy] #player-container-outer.ytd-watch-grid',
scriptSelector + ' ytd-watch-grid[flexy] #player-container-inner.ytd-watch-grid',
scriptSelector + ' ytd-watch-grid[flexy] #player-container.ytd-watch-grid',
],
{
'width': '100% !important',
'min-width': '100% !important',
'max-width': '100% !important',
'height': playerHeight + ' !important',
'min-height': playerHeight + ' !important',
'max-height': playerHeight + ' !important',
}
);
ytwp.style.appendRule(
[
scriptSelector + ' #player',
scriptSelector + ' .html5-main-video',
],
{
'top': '0 !important',
'right': '0 !important',
'bottom': '0 !important',
'left': '0 !important',
}
);
// Resize #player-unavailable, #player-api
// Using min/max width/height will keep
ytwp.style.appendRule(scriptSelector + ' #player .player-width', 'width', '100% !important');
ytwp.style.appendRule(scriptSelector + ' #player .player-height', 'height', '100% !important');
// Fix video overlays
ytwp.style.appendRule([
scriptSelector + ' .html5-video-player .ad-container-single-media-element-annotations', // Ad
scriptSelector + ' .html5-video-player .ytp-upnext', // Autoplay Next Video
], 'top', '0');
// Fix video cropping (object-fit: cover) (Issue #70)
ytwp.style.appendRule(scriptSelector + ' .ytp-fit-cover-video .html5-main-video', 'object-fit', 'contain !important');
// Thumbnail cropping
ytwp.style.appendRule(scriptSelector + ' .ytp-cued-thumbnail-overlay-image', {
'background-size': 'contain !important',
'-moz-background-size': 'contain !important',
'-webkit-background-size': 'contain !important',
});
//--- Video Container Background
ytwp.style.appendRule(scriptSelector + ' #movie_player', 'background-color', '#000000');
//--- Move Video Player
ytwp.style.appendRule(scriptSelector + ' #player', {
'position': 'absolute',
// Already top:0; left: 0;
});
ytwp.style.appendRule(scriptSelector, { // body
'margin-top': playerHeight,
});
// Fix the top right avatar button
ytwp.style.appendRule(scriptSelector + ' button.ytp-button.ytp-cards-button', 'top', '0');
//--- Sidebar
// Remove the transition delay as you can see it moving on page load.
d = buildVenderPropertyDict(transitionProperties, 'margin-top 0s linear, padding-top 0s linear');
d['margin-top'] = '0 !important';
d['top'] = '0 !important';
ytwp.style.appendRule(scriptSelector + ' #watch7-sidebar', d);
ytwp.style.appendRule(scriptSelector + '.cardified-page #watch7-sidebar-contents', 'padding-top', '0');
//--- Absolutely position the fixed header.
// Masthead
ytwp.style.appendRule('#skip-navigation.ytd-masthead', 'top', '-150vh'); // Normally -1000px can be shorter than screen (Issue #77)
d = buildVenderPropertyDict(transitionProperties, 'top 0s linear !important');
ytwp.style.appendRule(scriptSelector + '.hide-header-transition #masthead-positioner', d);
ytwp.style.appendRule(scriptSelector + '.' + viewingVideoClassId + ' #masthead-positioner', {
'position': 'absolute',
'top': playerHeight + ' !important'
});
// Lower masthead below Youtube+'s html.floater
ytwp.style.appendRule('html.floater ' + scriptBodySelector + '.' + viewingVideoClassId + ' #masthead-positioner', {
'z-index': '5',
});
// Autocomplete popup
ytwp.style.appendRule(scriptSelector + ' .sbdd_a', {
'top': '56px',
});
ytwp.style.appendRule(scriptSelector + '.' + viewingVideoClassId + ' .sbdd_a', {
'top': 'calc(' + playerHeight + ' + 56px) !important',
'position': 'absolute !important',
});
// Guide
// When watching the video, we need to line it up with the masthead.
ytwp.style.appendRule(scriptSelector + '.' + viewingVideoClassId + ' #appbar-guide-menu', {
'display': 'initial',
'position': 'absolute',
'top': '100% !important' // Masthead height
});
ytwp.style.appendRule(scriptSelector + '.' + viewingVideoClassId + ' #page.watch #guide', {
'display': 'initial',
'margin': '0',
'position': 'initial'
});
// When the guide is open, it adds body{top:-1000px} which messes with the top position (Issue #77)
ytwp.style.appendRule(scriptSelector + '.lock-scrollbar', {
'top': '0 !important',
'position': 'static !important',
});
//---
// MiniPlayer-Bar
ytwp.style.appendRule(scriptSelector + ' #miniplayer-bar #player', {
'position': 'static',
});
ytwp.style.appendRule(
[
scriptSelector + ' #miniplayer-bar #player',
scriptSelector + ' #miniplayer-bar #player-api',
scriptHtmlSelector + ':not(.floater):not(.iri-always-visible) ' + scriptBodySelector + ' #miniplayer-bar #movie_player',
scriptSelector + ' #player-mole-container',
scriptHtmlSelector + ':not(.floater):not(.iri-always-visible) ' + scriptBodySelector + ' #miniplayer-bar .html5-video-container',
scriptHtmlSelector + ':not(.floater):not(.iri-always-visible) ' + scriptBodySelector + ' #miniplayer-bar .html5-main-video',
],
{
'width': '252px !important',
'min-width': '252px !important',
'max-width': '252px !important',
'height': '142px !important',
'min-height': '142px !important',
'max-height': '142px !important',
}
);
// Override inline style (caused by a JS animation) that breaks the miniplayer video
// https://github.com/Zren/ResizeYoutubePlayerToWindowSize/issues/41#issuecomment-439710130
ytwp.style.appendRule('.video-stream.html5-main-video', {
'top': '0 !important',
});
//---
// Hide Scrollbars
ytwp.style.appendRule(scriptSelector + '.' + topOfPageClassId, 'overflow-x', 'hidden');
//--- Fix Other Possible Style Issues
ytwp.style.appendRule(scriptSelector + ' #placeholder-player', 'display', 'none');
ytwp.style.appendRule(scriptSelector + ' #watch-sidebar-spacer', 'display', 'none');
ytwp.style.appendRule(scriptSelector + ' .skip-nav', 'display', 'none');
//--- Whitespace Leftover From Moving The Video
ytwp.style.appendRule(scriptSelector + ' #page.watch', 'padding-top', '0');
ytwp.style.appendRule(scriptSelector + ' .player-branded-banner', 'height', '0');
//--- Youtube+ Compatiblity
ytwp.style.appendRule(scriptSelector + ' #body-container', 'position', 'static');
ytwp.style.appendRule(scriptHtmlSelector + '.part_static_size:not(.content-snap-width-skinny-mode) ' + scriptBodySelector + ' .watch-non-stage-mode #player-playlist', 'width', '1066px');
//--- Playlist Bar
ytwp.style.appendRule([
scriptSelector + ' #placeholder-playlist',
scriptSelector + ' #player .player-height#watch-appbar-playlist',
], {
'height': '540px !important',
'max-height': '540px !important',
});
d = buildVenderPropertyDict(transitionProperties, 'transform 0s linear');
ytwp.style.appendRule(scriptSelector + ' #watch-appbar-playlist', d);
d = buildVenderPropertyDict(transformProperties, 'translateY(0px)');
d['margin-left'] = '0';
d['top'] = 'calc(' + playerHeight + ' + 60px)';
ytwp.style.appendRule(scriptSelector + ' #player .player-height#watch-appbar-playlist', d);
ytwp.style.appendRule(scriptSelector + ' .playlist-videos-list', {
'max-height': '470px !important',
'height': 'initial !important',
});
// Old layout `&disable_polymer=true`
ytwp.style.appendRule(scriptSelector + ' #player .player-height#watch-appbar-playlist', {
'left': 'calc((100vw - 1066px)/2 + 640px + 10px)',
'width': '416px',
});
ytwp.style.stylesheet += '@media screen and (min-height: 630px) and (min-width: 1294px) {\n';
ytwp.style.appendRule(scriptSelector + ' #player .player-height#watch-appbar-playlist', {
'left': 'calc((100vw - 1280px)/2 + 854px + 10px)',
});
ytwp.style.stylesheet += '}\n @media screen and (min-width: 1720px) and (min-height:980px) {\n';
ytwp.style.appendRule(scriptSelector + ' #player .player-height#watch-appbar-playlist', {
'left': 'calc((100vw - 1706px)/2 + 1280px + 10px)',
});
ytwp.style.stylesheet += '}\n';
//---
// Material UI
ytwp.style.appendRule(scriptSelector + '.ytwp-scrolltop #extra-buttons', 'display', 'none !important');
// ytwp.style.appendRule('body > #player:not(.ytd-watch)', 'display', 'none');
// ytwp.style.appendRule('body.ytwp-viewing-video #content:not(app-header-layout) ytd-page-manager', 'margin-top', '0 !important');
// ytwp.style.appendRule('.ytd-watch-0 #content-separator.ytd-watch', 'margin-top', '0');
ytwp.style.appendRule('ytd-app', 'position', 'static !important');
ytwp.style.appendRule('ytd-watch #top', 'margin-top', '71px !important'); // 56px (topnav height) + 15px (margin)
ytwp.style.appendRule('ytd-watch #container', 'margin-top', '0 !important');
ytwp.style.appendRule('ytd-watch #content-separator', 'margin-top', '0 !important');
// Note: Container is now relative since 2023 June (Issue #77)
// Note: Container is now a full-bleed-player (Issue #79)
ytwp.style.appendRule([
scriptSelector + ' ytd-watch-flexy[theater] #player-wide-container.ytd-watch-flexy',
scriptSelector + ' ytd-watch-flexy[fullscreen] #player-wide-container.ytd-watch-flexy',
scriptSelector + ' ytd-watch-flexy[full-bleed-player] #player-full-bleed-container.ytd-watch-flexy', // Issue #79 (2023-08-17)
scriptSelector + ' ytd-watch-flexy[full-bleed-player] #full-bleed-container.ytd-watch-flexy', // Issue #79 (2023-08-22)
scriptSelector + ' ytd-watch-grid[theater] #player-wide-container.ytd-watch-grid',
scriptSelector + ' ytd-watch-grid[fullscreen] #player-wide-container.ytd-watch-grid',
scriptSelector + ' ytd-watch-grid[full-bleed-player] #player-full-bleed-container.ytd-watch-grid', // Issue #81 (2023-08-30)
scriptSelector + ' ytd-watch-grid[full-bleed-player] #full-bleed-container.ytd-watch-grid', // Issue #81 (2023-08-30)
], {
'position': 'static',
'height': 0,
'min-height': 0,
});
ytwp.style.appendRule(scriptSelector + '.ytwp-viewing-video ytd-app #masthead-container.ytd-app', {
'position': 'absolute',
'top': playerHeight,
'z-index': 0,
});
ytwp.style.appendRule(scriptSelector + '.ytwp-viewing-video ytd-watch #masthead-positioner', {
'top': playerHeight + ' !important',
});
ytwp.style.appendRule(scriptSelector + ' .ytp-cued-thumbnail-overlay', 'z-index', '10');
//---
// Flexy UI
ytwp.style.appendRule([
scriptSelector + ' ytd-watch-flexy[theater] #player-theater-container.ytd-watch-flexy',
scriptSelector + ' ytd-watch-grid[theater] #player-theater-container.ytd-watch-grid',
], {
'position': 'absolute',
'top': '0',
});
// Youtube seems to be ignoring the margin/padding top in certain elements for some reason (Issue #88)
// ytwp.style.appendRule([
// scriptSelector + ' ytd-watch-flexy',
// scriptSelector + ' ytd-watch-grid',
// ], 'padding-top', '71px'); // 56px (topnav height) + 15px (margin)
ytwp.style.appendRule('#page-manager.ytd-app', 'padding-top', 'var(--ytd-masthead-height,var(--ytd-toolbar-height))');
ytwp.style.appendRule(scriptSelector + ' #error-screen', 'z-index', '11');
},
onWatchInit: function() {
ytwp.log('onWatchInit');
if (!ytwp.initialized) return;
if (ytwp.pageReady) return;
if (enableOnLoad) {
ytwp.event.addBodyClass();
}
if (ytwp.hasYoutubeChanged()) {
ytwp.debugPage()
}
ytwp.pageReady = true;
},
onDispose: function() {
ytwp.log('onDispose');
ytwp.initialized = false;
ytwp.pageReady = false;
ytwp.isWatchPage = false;
},
addBodyClass: function() {
// Insert CSS Into the body so people can style around the effects of this script.
document.body.classList.add(scriptBodyClassId);
ytwp.log('Applied ' + scriptBodySelector);
},
removeBodyClass: function() {
document.body.classList.remove(scriptBodyClassId);
ytwp.log('Removed ' + scriptBodySelector);
},
};
ytwp.html5PlayerFix = function() {
ytwp.log('html5PlayerFix');
return;
try {
if (!uw.ytcenter // Youtube Center
&& !uw.html5Patched // Youtube+
&& (!ytwp.html5.app)
&& (uw.ytplayer && uw.ytplayer.config)
&& (uw.yt && uw.yt.player && uw.yt.player.Application && uw.yt.player.Application.create)
) {
ytwp.html5.app = ytwp.html5.getPlayerInstance();
}
ytwp.html5.update();
ytwp.html5.autohideControls();
} catch (e) {
ytwp.error(e);
}
}
ytwp.fixMasthead = function() {
ytwp.log('fixMasthead');
var el = document.querySelector('#masthead-positioner-height-offset');
if (el) {
ytwp.fixMastheadElement(el);
}
}
ytwp.fixMastheadElement = function(el) {
ytwp.log('fixMastheadElement', el);
if (el.style.height) { // != ""
setTimeout(function(){
el.style.height = ""
document.querySelector('#appbar-guide-menu').style.marginTop = "";
}, 0);
}
}
JSStyleSheet.injectIntoHeader(scriptStyleId + '-focusfix', 'input#search[autofocus] { display: none; }');
ytwp.removeSearchAutofocus = function() {
var e = document.querySelector('input#search');
// ytwp.log('removeSearchAutofocus', e)
if (e) {
e.removeAttribute('autofocus')
}
}
ytwp.registerMastheadFix = function() {
ytwp.log('registerMastheadFix');
// Fix the offset when closing the Share widget (element.style.height = ~275px).
observe('#masthead-positioner-height-offset', {
attributes: true,
}, function(mutation) {
console.log(mutation.type, mutation)
if (mutation.attributeName === 'style') {
var el = mutation.target;
if (el.style.height) { // != ""
setTimeout(function(){
el.style.height = ""
document.querySelector('#appbar-guide-menu').style.marginTop = "";
}, 0);
}
}
});
}
//--- Material UI
ytwp.materialPageTransition = function() {
ytwp.log('materialPageTransition')
ytwp.init();
if (ytwp.isWatchUrl()) {
ytwp.removeSearchAutofocus();
if (enableOnLoad) {
ytwp.event.addBodyClass();
}
// if (!ytwp.html5.app) {
if (!ytwp.initialized) {
ytwp.log('materialPageTransition !ytwp.html5.app', ytwp.html5.app)
setTimeout(ytwp.materialPageTransition, 100);
}
// Focus player
// var moviePlayer = document.querySelector('#movie_player')
// if (moviePlayer) {
// moviePlayer.click()
// }
} else {
ytwp.event.onDispose();
document.body.classList.remove(scriptBodyClassId);
}
ytwp.onScroll();
ytwp.fixMasthead();
ytwp.attemptToUpdatePlayer();
};
//--- Listeners
ytwp.registerListeners = function() {
ytwp.registerMaterialListeners();
ytwp.registerMastheadFix();
};
ytwp.registerMaterialListeners = function() {
// For Material UI
// HistoryEvent.listeners.push(ytwp.materialPageTransition);
// HistoryEvent.startTimer();
// HistoryEvent.inject();
// HistoryEvent.listeners.push(console.log.bind(console));
document.addEventListener('yt-page-data-fetched', ytwp.materialPageTransition)
document.addEventListener('yt-navigate-finish', ytwp.materialPageTransition)
// Debugging
// document.addEventListener('yt-navigate-start', function(e){ ytwp.log('document.yt-navigate-start', e)})
document.addEventListener('yt-page-data-fetched', function(e){ ytwp.log('document.yt-page-data-fetched', e)})
document.addEventListener('yt-navigate-finish', function(e){ ytwp.log('document.yt-navigate-finish', e)})
// document.addEventListener('yt-navigate-error', function(e){ ytwp.log('document.yt-navigate-error', e)})
// document.addEventListener('yt-navigate-cache', function(e){ ytwp.log('document.yt-navigate-cache', e)})
// document.addEventListener('yt-navigate-redirect', function(e){ ytwp.log('document.yt-navigate-redirect', e)})
// document.addEventListener('yt-navigate-action', function(e){ ytwp.log('document.yt-navigate-action', e)})
// document.addEventListener('yt-navigate-home-action', function(e){ ytwp.log('document.yt-navigate-home-action', e)})
};
ytwp.main = function() {
ytwp.registerListeners();
ytwp.init();
ytwp.fixMasthead();
};
ytwp.main();
// ytwp.updatePlayerTimerId = 0;
ytwp.updatePlayerAttempts = -1;
ytwp.updatePlayerMaxAttempts = 150; // 60fps = 2.5sec
ytwp.attemptToUpdatePlayer = function() {
// console.log('ytwp.attemptToUpdatePlayer')
if (0 <= ytwp.updatePlayerAttempts && ytwp.updatePlayerAttempts < ytwp.updatePlayerMaxAttempts) {
ytwp.updatePlayerAttempts = 0;
} else {
ytwp.updatePlayerAttempts = 0;
ytwp.attemptToUpdatePlayerTick();
}
// setTimeout(ytwp.updatePlayer, 10000); /// Just in case it's not caught
}
ytwp.attemptToUpdatePlayerTick = function() {
// console.log('ytwp.attemptToUpdatePlayerTick', ytwp.updatePlayerAttempts)
if (ytwp.updatePlayerAttempts < ytwp.updatePlayerMaxAttempts) {
ytwp.updatePlayerAttempts += 1;
ytwp.updatePlayer();
// ytwp.updatePlayerTimerId = setTimeout(ytwp.attemptToUpdatePlayerTick, 200);
requestAnimationFrame(ytwp.attemptToUpdatePlayerTick);
}
}
ytwp.updatePlayer = function() {
ytwp.removeSearchAutofocus();
ytwp.enterTheaterMode();
ytwp.detectPlayerUnavailable();
}
ytwp.toggleExtension = function() {
document.body.classList.toggle('ytwp-window-player')
ytwp.setTheaterMode(document.body.classList.contains('ytwp-window-player'))
}
//--- Main
ytwp.materialPageTransition()
setInterval(ytwp.updatePlayer, 2500);
//--- Keyboard Shortcut
function childOf(child, ancestor) {
var parent = child.parentNode
while (parent) {
if (parent == ancestor) {
return true
}
parent = parent.parentNode
}
return false
}
function cancelIfToggleKey(validKeyCallback, e) {
var isKey = e.key === scriptToggleKey
var validTarget = (
e.target === document.body
|| e.target.id === 'player-api'
|| e.target.id === 'movie_player'
|| childOf(e.target, document.querySelector('#movie_player'))
)
if (validTarget && isKey) {
e.preventDefault()
e.stopPropagation()
console.log('cancelIfToggleKey.validKeyCallback', validKeyCallback, 'e', e)
if (validKeyCallback) {
validKeyCallback()
}
}
}
window.addEventListener('keydown', cancelIfToggleKey.bind(null, ytwp.toggleExtension), true)
window.addEventListener('keyup', cancelIfToggleKey.bind(null, null), true)
// Note: keypress is deprecated
// https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event
window.addEventListener('keypress', cancelIfToggleKey.bind(null, null), true)
//--- Browser Extension
if (typeof browser !== "undefined") {
browser.runtime.onMessage.addListener(request => {
if (request.id == "toggle") {
ytwp.toggleExtension()
return Promise.resolve({
enabled: document.body.classList.contains('ytwp-window-player'),
})
} else {
return Promise.reject(new Error('Unreconized message.id'))
}
});
}
})(window);