返回首頁 

Greasy Fork is available in English.

Maximizer For YouTube™

Maximizes the YouTube player to fill the entire browser viewport when in theater mode, plus a few other enhancements.


安装此脚本?
// ==UserScript==// @name        Maximizer For YouTube™// @description Maximizes the YouTube player to fill the entire browser viewport when in theater mode, plus a few other enhancements.// @license     MIT// @author      Rotem Dan <[email protected]>// @match       https://www.youtube.com/*// @version     0.2.9// @run-at      document-start// @grant       none// @namespace   https://github.com/rotemdan// @homepageURL https://github.com/rotemdan/MaximizerForYouTube// @require     https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js// @require     https://code.jquery.com/jquery-3.7.1.min.js// ==/UserScript==////////////////////////////////////////////////////////////////////////////////////////////////////////// Utility definitions////////////////////////////////////////////////////////////////////////////////////////////////////////const debugModeEnabled = falsefunction log(...args) {if (debugModeEnabled) {console.log('[MaximizerForYoutube]', ...args)}}// Try to emulate setImmediate() like execution:function setImmediate(func) {const channel = new MessageChannel()channel.port1.onmessage = () => func()channel.port2.postMessage('')}////////////////////////////////////////////////////////////////////////////////////////////////////////// Core script modification functions////////////////////////////////////////////////////////////////////////////////////////////////////////// Install or uninstall full-size player page stylesheet if neededfunction installOrUninstallPlayerModIfNeeded() {if (inWatchPage() && theaterModeEnabled()) {if ($('#MaximizerForYouTube_PlayerMod').length == 0) {const styleSheet = `<style id='MaximizerForYouTube_PlayerMod' type='text/css'>ytd-page-manager { margin-top: 0px !important; }#masthead-container { visibility: hidden; opacity: 0; transition: opacity 0.2s ease-in-out; }#full-bleed-container { height: 100vh !important; min-height: 0vh !important; max-height: 100vh !important; }:focus { outline: 0; }#movie_player { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }/*body {scrollbar-width: 10px;scrollbar-color: #000000;}*/body::-webkit-scrollbar {width: 10px;background-color: #000000;}body::-webkit-scrollbar-track {border-radius: 10px;background: rgba(0,0,0,0.1);border: 1px solid #383838;}body::-webkit-scrollbar-thumb {border-radius: 10px;background: linear-gradient(left, #fff, #e4e4e4);border: 1px solid #5b5b5b;}body::-webkit-scrollbar-thumb:hover {background: #fff;}</style>`$('head').append(styleSheet)log('Player mod installed')}if (pageScrolledToTop()) {hideTopBar()}} else {if ($('#MaximizerForYouTube_PlayerMod').length > 0) {$('#MaximizerForYouTube_PlayerMod').remove()showTopBar()log('Player mod uninstalled')}}}// Automatically shows/hides the top bar based on different properties of the view.function installTopBarAutohide() {function onPageScroll() {if (!inWatchPage()) {return}if (pageScrolledToTop() && theaterModeEnabled()) {hideTopBar()} else {showTopBar()}}function onKeyDown(e) {if (!inWatchPage() || !theaterModeEnabled()) {return}if (e.which === 27) { // Handle escape keylog('esc')if (pageScrolledToTop()) {if (topBarIsVisible()) {hideTopBar()e.stopPropagation()} else {showTopBar()setTimeout(() => $('input#search').focus(), 50)}}}}function installEscHandlerToSearchInput() {let inputElement = $('input#search')if (inputElement.length > 0) {inputElement.on('keydown', onKeyDown)//inputElement[0].addEventListener('keydown', onKeyDown, true)log('Esc handler installed on search input')} else {setTimeout(() => installEscHandlerToSearchInput(), 50)}}installEscHandlerToSearchInput()$(document).on('keydown', onKeyDown)$(document).on('scroll', onPageScroll)}// Continuously auto-focus the player keyboard input when some conditions are met.function installPlayerInputAutoFocus() {function autoFocusIfNeeded() {if (inWatchPage() && !topBarIsVisible()) {getVideoContainer().focus()}setTimeout(autoFocusIfNeeded, 20)}autoFocusIfNeeded()}function installPlayerKeyboardShortcutExtensions() {// Install keyboard shortcut extensionsfunction onKeyDown(e) {if (!inWatchPage()) {return}if (getVideoContainer().is(':focus')) {if (e.ctrlKey) {if (e.which === 37) { // Handle ctl + left keyconst previousButton = $('a.ytp-prev-button')[0]if (previousButton) {previousButton.click()}} else if (e.which === 39) { // Handle ctl + right keyconst nextButton = $('a.ytp-next-button')[0]if (nextButton) {nextButton.click()}}}}}$(document).on('keydown', onKeyDown)}// Expands video descriptionfunction ensureExpandedVideoDescription() {setInterval(() => {if (!inWatchPage()) {return}if ($('#ytd-watch-info-text').attr('detailed') == null) {$('#description-inline-expander tp-yt-paper-button#expand').click()}}, 50)}// Expands video descriptionfunction ensureModdedTheaterModeButton() {setInterval(() => {if (!inWatchPage()) {return}const playerModeButton = $('button.ytp-size-button')if (playerModeButton.length === 0 || playerModeButton.hasClass('MaximizerForYouTube_PlayerMod_Modded')) {return}playerModeButton.on('click', () => {setTimeout(() => {installOrUninstallPlayerModIfNeeded()const resizeEvent = new Event('resize')window.dispatchEvent(resizeEvent)}, 0)})playerModeButton.addClass('MaximizerForYouTube_PlayerMod_Modded')}, 50)}function ensurePlayerIsAlwaysPaused() {setInterval(() => {const player = getVideoPlayer().get(0)if (player) {player.pause()}}, 1000)}function hideSPFLoadingBar() {$('head').append('<style>#progress, yt-page-navigation-progress {display: none !important}</style>')}// Pauses playing videos in other tabs when a video play event is detected (works in both watch and channel page videos)function ensurePlayerAutoPause() {const videoPlayer = getVideoPlayer()if (videoPlayer.length > 0 &&!videoPlayer.hasClass('MaximizerForYouTube_Modded_Autopause') &&!inHomePage() &&!inFeedPage() &&!inSearchPage()) {// Generate a random script instance IDconst instanceID = Math.random().toString()function onVideoPlay() {log('onVideoPlay')localStorage['MaximizerForYouTube_PlayingInstanceID'] = instanceIDfunction pauseWhenAnotherPlayerStartsPlaying() {if (localStorage['MaximizerForYouTube_PlayingInstanceID'] !== instanceID) {videoPlayer[0].pause()} else {setTimeout(pauseWhenAnotherPlayerStartsPlaying, 20)}}pauseWhenAnotherPlayerStartsPlaying()}// If video isn't paused on startup, fire the handler immediatelyif (!videoPlayer[0].paused) {onVideoPlay()}// Add event handler for the 'play' event.videoPlayer.on('play', onVideoPlay)// Mark the player as modded to ensure the autopause mod isn't installed againvideoPlayer.addClass('MaximizerForYouTube_Modded_Autopause')}setTimeout(ensurePlayerAutoPause, 50)}////////////////////////////////////////////////////////////////////////////////////////////////////////// Utility functions////////////////////////////////////////////////////////////////////////////////////////////////////////// Get the video player elementfunction getVideoContainer() {// Note: the channel page has another hidden video except the main one (if it exists). The hidden video doesn't have an 'src' attribute.return $('div.html5-video-player')}// Get the video player elementfunction getVideoPlayer() {// Note: the channel page has another hidden video except the main one (if it exists). The hidden video doesn't have an 'src' attribute.return $('.html5-main-video').filter(function (index) {return $(this).attr('src') !== undefined})}// Get the top bar elementfunction getTopBar() {return $('#masthead-container')}function showTopBar() {getTopBar().css('visibility', 'visible')getTopBar().css('opacity', '1')}function hideTopBar() {getTopBar().css('opacity', '0')getTopBar().css('visibility', 'hidden')}function pageScrolledToTop() {return $(document).scrollTop() === 0}function scrollPageToTopIfNeeded() {setTimeout(() => {if (inWatchPage() && $(document).scrollTop() > 0) {log('Scrolling page to top')$(document).scrollTop(0)}}, 20)}function topBarIsVisible() {return getTopBar().css('visibility') === 'visible'}function inWatchPage() {return locationPathname() == '/watch'}function inSearchPage() {return locationPathname().startsWith('/r###lts')}function inFeedPage() {return locationPathname().startsWith('/feed')}function inHomePage() {return locationPathname() == '/'}function locationPathname() {return (new URL(location.href)).pathname}function theaterModeEnabled() {return Cookies.get('wide') === '1'}////////////////////////////////////////////////////////////////////////////////////////////////////////// Event handlers////////////////////////////////////////////////////////////////////////////////////////////////////////let installIntervalfunction onDocumentStart() {log('onDocumentStart')log('Script loaded, theater mode enabled:', theaterModeEnabled())//installOrUninstallPlayerModIfNeeded()installInterval = setInterval(() => {installOrUninstallPlayerModIfNeeded()//log('Trying to install mod')}, 10)}function onDocumentEnd() {log('onDocumentEnd')clearInterval(installInterval)installOrUninstallPlayerModIfNeeded()//ensurePlayerIsAlwaysPaused()hideSPFLoadingBar()installTopBarAutohide()installPlayerInputAutoFocus()installPlayerKeyboardShortcutExtensions()ensureExpandedVideoDescription()}function onWindowLoad() {log('onWindowLoad')ensureModdedTheaterModeButton()ensurePlayerAutoPause()}function onNavigation() {log('onNavigation, new location:', location.href)//scrollPageToTopIfNeeded()installOrUninstallPlayerModIfNeeded()}////////////////////////////////////////////////////////////////////////////////////////////////////////// Install event handlers and start script////////////////////////////////////////////////////////////////////////////////////////////////////////function startScript() {document.addEventListener('DOMContentLoaded', onDocumentEnd, false)$(window).on('load', onWindowLoad)$(window).on('yt-navigate-start', () => {log('yt-navigate-start')onNavigation()})$(window).on('popstate', () => {log('popstate')onNavigation()})onDocumentStart()}if (window.self === window.top) {startScript()}