// ==UserScript== // @name Disable YouTube 60 FPS (Force 30 FPS) // @namespace SteveJobzniak // @version 1.3 // @description Tells YouTube that your browser only supports videos at 30FPS or less, which switches all 60FPS videos to 30FPS and allows old computers to watch high-resolution videos without stutter! // @author SteveJobzniak // @match *://www.youtube.com/* // @exclude *://www.youtube.com/tv* // @exclude *://www.youtube.com/embed/* // @run-at document-start // @grant none // @noframes // @downloadURL none // ==/UserScript== /* This script tells YouTube that your browser only supports 30FPS or less, which means that you will see regular 30FPS versions of all HD videos. WHY DO THIS?: For my six year old laptop, switching from 1080p60 to 1080p30 reduces the CPU usage by 2-4x, and removes all CPU overloads that used to make my browser and video playback freeze! This means longer battery life, and a much happier video watching experience! (Furthermore, most older graphics cards only support hardware acceleration of 1080p30 or lower, which means that using this script may allow your graphics card to perform the video decoding for great battery savings!) INSTALLATION: Install the Tampermonkey (https://tampermonkey.net) extension for your specific browser, and then install this script into Tampermonkey. This script has been tested and confirmed working in Safari 9 for Mac and Google Chrome for Mac. But it should work in *all* browsers and OS's that support the Tampermonkey extension - on Windows, Mac and Linux! (This script does NOT work via Chrome's own basic built-in script support!) IMPORTANT NOTE TO ALL USERS: We DO NOT affect embedded YouTube videos, because embedded players *only* check for high-FPS support, so blocking those queries would mean completely losing *all* HD resolutions for embedded videos! I suggest clicking the "Watch on YouTube" button to play embedded high-FPS videos directly on YouTube in 30FPS instead! VERY IMPORTANT NOTE FOR SAFARI BROWSER USERS: This script contains a workaround for a Safari bug. The Safari browser uses "lazy" extension loading, which means that your extensions (such as Tampermonkey) don't start running in new tabs until you actually visit a webpage in your new tab. And if the first page you visit in your new tab is YouTube, it may perform its codec check *before* the extension has been fully loaded, in which case YouTube will revert to giving you high-FPS videos in that tab. This "too late extension loading" will happen every time you write/paste a YouTube video URL in a brand new empty tab, or right-click a video link and choose "open in new tab/window", or use the History menu to browse to a video page. In other words, it's quite rare but common enough to be annoying. Our chosen workaround is that whenever we detect that Safari has loaded Tampermonkey too late in the current tab, we perform a very quick reload of the current YouTube page so that the extension can run before YouTube does its codec check. This reloading is automatic and happens so quickly that most people won't even notice anything at all. I just mention it here for the few people who pay attention to such things. SCRIPT VERSION 1.3 NEWS (AFFECTS APPLE'S SAFARI BROWSER): The Safari web browser's extension loading bug is now much more common and much more severe since Safari 10 (and it affects ALL extensions, such as adblockers too, which is why you might now see ads in your Safari tabs). To prevent an endless loop of page reloads when your browser tab is completely bugged, this script will now (from v1.3) count the page reloads and will quickly give up if the current web tab seems to be hopelessly bugged. In that case, it displays a message bar letting you know that videos in the tab will play in high FPS mode if available, and it gives you some options to either retry or to restart your browser to fix the Safari bug. This is the best we can do for all of you Safari users! :) If you don't like it, complain to APPLE. They're the ones responsible for the browser bug! */ (function () { 'use strict'; function createNewTypeChecker( originalChecker, debugLogging ) { return function( videoType ) { if( videoType === undefined ) { return false; } if( debugLogging ) { console.log( 'Format Query: "' + videoType + '", originalAnswer: "' + originalChecker( videoType ) + '"' ); } // Block all queries regarding high-framerate support. var matches = videoType.match( /framerate=(\d+)/ ); if( matches && ( matches[1] > 30 ) ) { if( debugLogging ) { console.log( 'Blocking High-FPS format: "' + videoType + '"' ); } return false; } // Let the browser answer all other codec queries. return originalChecker( videoType ); }; } /* Override the browser's Media Source Extensions codec checker, to tell YouTube that we don't support any formats above 30FPS. (It's very important that this userscript is @run-at document-start, before any of the YouTube scripts have had time to query the MSE!) */ if( window.MediaSource ) { var originalChecker = window.MediaSource.isTypeSupported.bind( window.MediaSource ); window.MediaSource.isTypeSupported = createNewTypeChecker( originalChecker, false ); } /* Safari browser bug workaround (and any other browsers with the same bug): In the Safari browser, ALL new tabs/windows begin their life without *any* extensions loaded into memory. They're only loaded *after* you've actually visited at least one webpage in that tab. Then they stay loaded in that tab, except if you manually type a URL into the location bar or change webpage via the History menu, in which case Safari often unloads and then re-loads all extensions in that tab again. The result of this Safari behavior is that Tampermonkey MAY not always be immediately ready when you're visiting a YouTube video page. And if the extension is loaded *after* YouTube has already checked for supported video formats, then you'll be served with high-FPS video in that tab until you reload the webpage. Annoying! It happens in quite a LOT of situations, such as opening a brand new tab and typing/pasting the URL of a video into it and pressing enter, or visiting a video via your History menu, or right-clicking a video link and choosing "open in new tab/window", etc. The user can solve it by manually reloading the webpage after Tampermonkey is done initializing itself in that new tab, but it's such a common issue in Safari (it never happens in Google Chrome) that I decided to automate the page reloading process as follows: * This script is specified to run at "document-start" (before anything else on the YouTube webpage is loaded). * If we can find the "window.ytplayer" object, it means that we've been loaded TOO LATE, after YouTube has already performed its codec detection. * But if we're on the YouTube homepage or a search results page, then it doesn't MATTER if we're loaded late, since those pages don't contain any video players. (And forcing an auto-reload in those cases would just needlessly annoy people since it would happen every time time they manually type in "youtube.com".) * If we're loaded late on a video player page (/watch URL), then we automatically reload the webpage to try again. Because the ONLY way to solve the late injection is to perform a page reload. It causes a very, very brief (almost unnoticeable) flicker as it rapidly reloads that tab. That's the low price of fixing this annoying Safari bug! ;) * Sometimes, the Safari tab is COMPLETELY bugged and can't be fixed (this happens more and more frequently since Safari 10). In that situation, we give up after too many reload attempts (to avoid an endless reload loop), and we instead notify the user that the FPS replacement has failed and that the tab is playing in 60fps if available. We also give them some options to help them resolve the Safari bug manually. */ var removeReloadCountHash = true; if( window.ytplayer ) { if( location.pathname && location.pathname === '/watch' ) { // Start reload counter at one. var nextReloadCount = 1, oldHash = '', newHash = ''; // Get the current hash value and reload counter if one exists. if( location.hash && location.hash !== '' && location.hash !== '#' ) { oldHash = location.hash; if( oldHash ) { if( oldHash.charAt(0) === '#' ) { oldHash = oldHash.substr(1); // skip the leading # symbol } var matches = oldHash.match( /fpsreloads=(\d+)/ ); if( matches ) { // We found an existing count, so increment to prepare for reload. nextReloadCount = parseInt( matches[1], 10 ); nextReloadCount++; oldHash = oldHash.replace( /&?fpsreloads=\d+/, '' ); } } } // We allow a limited amount of reload attempts to try to inject before the YouTube player. // The user can click the "failure" message at the bottom of the video to try again, if they want to. // NOTE: The 2nd "reload attempt" is actually the 3rd load of the page. And if we haven't successfully // injected before the YouTube player in 3 load attempts, then we have almost no chance of doing it // even with further reloads (there's only about a 10% chance that it would work within 9 loads in such // an incredibly bugged browser tab). Most proper tabs work within 1-2, maybe 3 loads. So we abort after 3. // That way, the user has a chance to decide quickly instead of waiting for reloads. Most videos are not high-FPS! if( nextReloadCount <= 2 ) { // 1 load + 2 reloads = 3 attempts total // Determine which new hash to use. if( oldHash !== '' ) { newHash = oldHash + '&fpsreloads=' + nextReloadCount; } else { newHash = 'fpsreloads=' + nextReloadCount; } // Tell ourselves that we don't want to remove the reload count from the hash. removeReloadCountHash = false; // Set the hash, which will track the number of page reloads we've attempted. location.hash = newHash; // Reload the current video page (since merely setting the hash is not enough to cause a reload in most browsers). // NOTE: Waiting LONGER (via a timer) before reloading the page does NOT help the user's browser "react faster" // during the next reload (it STILL won't inject the script in time). The ONLY thing we can do is reload the page // repeatedly until we either succeed or give up. Because if we've seen window.ytplayer, it means we were injected // AFTER the codec check and therefore TOO LATE to fix the framerate on the video page. We MUST reload. // ALSO NOTE: In Safari 10 on Mac, it can take anywhere from 2 to 8 reloads in *some* cases (usually 1-3, avg 2), and in very // rare cases it can't be fixed *at all* without closing that tab and opening the video in a new tab or restarting Safari. location.reload(); } else { // It's time to give up. The repeatedly failed reloads are enough to know that the user's current browser tab // is totally bugged out and won't recover. So we'll stop trying and will tell the user instead. // This creates a nice, floating, fixed bar at the bottom of the YouTube video page. Most importantly, // the bar is non-blocking (unlike an alert()), which means music playlists won't pause waiting for user input. var errorDiv = document.createElement( 'div' ); errorDiv.style.position = 'fixed'; errorDiv.style.bottom = 0; errorDiv.style.left = 0; errorDiv.style.width = '100%'; errorDiv.style.padding = '10px'; errorDiv.style.textAlign = 'center'; errorDiv.style.fontSize = '120%'; errorDiv.style.fontWeight = 'bold'; errorDiv.style.color = '#fff'; errorDiv.style.backgroundColor = 'rgba(244, 67, 54, 0.9)'; errorDiv.style.zIndex = '99999'; errorDiv.innerHTML = '

Your browser failed to disable 60 FPS playback in this tab. Videos in this tab will play in 60 FPS if available.

You can try again by reloading the page, using a new tab or restarting your browser.

'; document.body.appendChild( errorDiv ); } } } if( removeReloadCountHash ) { // We'll remove the "fpsreloads=X" location hash from the location (without causing a page reload) if we're either done injecting, // OR if the injection reloads have failed too many times in a row (due to a bad browser with late extension injection). // This means that people can copy and paste the video link to share it with others without sharing the "fpsreloads=X" value. if( window.history && window.history.replaceState ) { var newUrl = location.href.replace( /(#?)fpsreloads=\d+&?/g, '$1' ).replace( /[#&]+$/, '' ); window.history.replaceState( {}, document.title, newUrl ); } } })();