Last active
August 29, 2015 14:24
-
-
Save rotemdan/ce7c4489053695471fd8 to your computer and use it in GitHub Desktop.
YouTube Cinema Mode - Alternative Top Bar Behavior
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name YouTube Cinema Mode - Alternative Top Bar Behavior | |
// @description Improves the YouTube experience by maximizing video player real-estate and fixing some common annoyances. | |
// @license MIT | |
// @match https://www.youtube.com/* | |
// @version 0.1.12 | |
// @run-at document-start | |
// @grant none | |
// @require https://code.jquery.com/jquery-2.1.4.min.js | |
// @namespace https://greasyfork.org/en/users/4614 | |
// ==/UserScript== | |
//////////////////////////////////////////////////////////////// | |
// Main Operations | |
//////////////////////////////////////////////////////////////// | |
// Detect the type of YouTube page loaded | |
var isWatchPage = location.href.indexOf("https://www.youtube.com/watch?") === 0; | |
// Start or install operation handlers | |
onDocumentStart(); | |
$(document).on("ready", onDocumentEnd); | |
$(window).on("load", onWindowLoad); | |
// Operations that are run as early as possible during page load | |
function onDocumentStart() | |
{ | |
installUnsafewindowPolyfill(); | |
disableSPF(); | |
if (isWatchPage) | |
{ | |
disableMatchMedia(); | |
installFullSizePlayerStylesheet(); | |
} | |
} | |
// Operations that run when DOMContentLoaded event is fired | |
function onDocumentEnd() | |
{ | |
if (isWatchPage) | |
{ | |
installTopBarAutohide(); | |
expandVideoDescription(); | |
} | |
installPlayerAutoPause(); | |
} | |
// Operations that run when window is loaded (after YouTube scripts are run) | |
function onWindowLoad() | |
{ | |
if (isWatchPage) | |
{ | |
setTimeout(switchPlayerToTheaterMode, 2000); | |
} | |
} | |
//////////////////////////////////////////////////////////////// | |
// Methods | |
//////////////////////////////////////////////////////////////// | |
// Ensure unsafeWindow object is available both in firefox and chrome | |
function installUnsafewindowPolyfill() | |
{ | |
if (typeof unsafeWindow === 'undefined') | |
{ | |
if (typeof XPCNativeWrapper === 'function' && typeof XPCNativeWrapper.unwrap === 'function') | |
unsafeWindow = XPCNativeWrapper.unwrap(window); | |
else if (window.wrappedJSObject) | |
unsafeWindow = window.wrappedJSObject; | |
} | |
} | |
// Disable SPF (Structured Page Fragments), which prevents properly attaching to page load events when navigation occurs | |
// Will also disable the red loading bar. | |
function disableSPF() | |
{ | |
if (unsafeWindow._spf_state && unsafeWindow._spf_state.config) | |
{ | |
unsafeWindow._spf_state.config['navigate-limit'] = 0; | |
unsafeWindow._spf_state.config['navigate-part-received-callback'] = function (targetUrl) { location.href = targetUrl; } | |
} | |
setTimeout(disableSPF, 50); | |
} | |
// Disable matchMedia - allow proper resizing of the video player and its contents. | |
function disableMatchMedia() | |
{ | |
window.matchMedia = undefined; | |
} | |
// Add full-size player page stylesheet (works correctly only in theater mode). | |
function installFullSizePlayerStylesheet() | |
{ | |
$("head").append( | |
"<style type='text/css'>" + | |
"body { overflow-x: hidden !important; overflow-y: scroll !important; }" + | |
"#masthead-positioner-height-offset { display: none }" + | |
"#masthead-positioner { visibility: hidden; opacity: 0; transition: opacity 0.2s ease-in-out; }" + | |
".player-width { width: 100% !important; margin: 0px !important; left: 0px !important; right: 0px !important; }" + | |
".player-height { height: 100vh !important; }" + | |
"</style>"); | |
} | |
// Switch player to theater mode if in default mode. | |
function switchPlayerToTheaterMode() | |
{ | |
if ($("div#page").hasClass("watch-non-stage-mode")) | |
$("button.ytp-size-button, div.ytp-size-toggle-large").click(); | |
} | |
// Auto shows/hides the top bar when mouse enters/leaves (for use with watch pages). | |
function installTopBarAutohide() | |
{ | |
var topBar = $("#masthead-positioner"); | |
var videoPlayer = $('video.html5-main-video'); | |
var videoPlayerElement = videoPlayer[0]; | |
function showTopBar() | |
{ | |
topBar.css("visibility", "visible"); | |
topBar.css("opacity", "1"); | |
} | |
function hideTopBar() | |
{ | |
topBar.css("opacity", "0"); | |
topBar.css("visibility", "hidden"); | |
//setTimeout(function () { topBar.css("visibility", "hidden"); }, 200); | |
} | |
function getScrollTop() | |
{ | |
return $(document).scrollTop(); | |
} | |
function onVideoPlay() | |
{ | |
if (getScrollTop() === 0) | |
hideTopBar(); | |
} | |
function onVideoPause() | |
{ | |
showTopBar(); | |
} | |
function onPageScroll() | |
{ | |
if (getScrollTop() > 0 || videoPlayerElement.paused) | |
showTopBar(); | |
else | |
hideTopBar(); | |
} | |
//if (videoPlayerElement.paused) | |
// onVideoPause(); | |
//else | |
// onVideoPlay(); | |
videoPlayer.on("pause", onVideoPause); | |
videoPlayer.on("play", onVideoPlay); | |
$(document).on("scroll", onPageScroll); | |
} | |
// Expands video description | |
function expandVideoDescription() | |
{ | |
$("#action-panel-details").removeClass("yt-uix-expander-collapsed"); | |
} | |
// Pauses playing videos in other tabs when a video play event is detected (works in both watch and channel page videos) | |
function installPlayerAutoPause() | |
{ | |
// Note: the channel page has another hidden video except the main one (if it exists). The hidden video doesn't have an "src" attribute. | |
var videoPlayer = $('video.html5-main-video').filter(function (index) { return $(this).attr("src") !== undefined}); | |
if (videoPlayer.length === 0) | |
{ | |
//console.log("Player not found, retrying in 100ms.."); | |
setTimeout(installPlayerAutoPause, 100); | |
return; | |
} | |
var videoPlayerElement = videoPlayer.get(0); | |
// Generate a random script instance ID | |
var instanceID = Math.random().toString(); | |
function onVideoPlay() | |
{ | |
console.log("play event triggered"); | |
unsafeWindow.localStorage["FullSizeYouTubePlayer_PlayingInstanceID"] = instanceID; | |
function pauseWhenAnotherPlayerStartsPlaying() | |
{ | |
if (unsafeWindow.localStorage["FullSizeYouTubePlayer_PlayingInstanceID"] !== instanceID) | |
videoPlayerElement.pause(); | |
else | |
setTimeout(pauseWhenAnotherPlayerStartsPlaying, 10); | |
} | |
pauseWhenAnotherPlayerStartsPlaying(); | |
} | |
// If video isn't paused on startup, fire the handler immediately | |
if (!videoPlayerElement.paused) | |
onVideoPlay(); | |
// Add event handler for the "play" event. | |
videoPlayer.on("play", onVideoPlay); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment