Skip to content

Instantly share code, notes, and snippets.

@adisib
Last active November 14, 2024 03:55
Show Gist options
  • Save adisib/1e6b429b9bb630fceb170f3fa77c57a3 to your computer and use it in GitHub Desktop.
Save adisib/1e6b429b9bb630fceb170f3fa77c57a3 to your computer and use it in GitHub Desktop.
Make youtube videos in HD and automatically resize
// ==UserScript==
// @name Youtube HD
// @author adisib
// @namespace namespace_adisib
// @description Select a youtube resolution and resize the player.
// @version 2024.01.17
// @match https://*.youtube.com/*
// @noframes
// @grant GM.getValue
// @grant GM.setValue
// ==/UserScript==
// The video will only resize when in theater mode on the main youtube website.
// By default only runs on youtube website, not players embeded on other websites, but there is experimental support for embeds.
// To enable experimental support for embedded players outside of YouTube website, do the following steps:
// add " @include * " to the script metadata
// remove " @noframes " from the script metadata
// 2024.01.17
// Fix issue with user script managers that don't define GM
// 2024.01.14
// Partially fix auto theater mode again after more youtube changes
// Note that if you want to turn theater mode back off after using auto theater you have to click the button a few times. This is a known issue that hasn't been fixed yet.
(function() {
"use strict";
// --- SETTINGS -------
// PLEASE NOTE:
// Settings will be saved the first time the script is loaded so that your changes aren't undone by an update.
// If you want to make adjustments, please set "overwriteStoredSettings" to true.
// Otherwise, your settings changes will NOT have an effect because it will used the saved settings.
// After the script has next been run by loading a video with "overwriteStoredSettings" as true, your settings will be updated.
// Then after that you can set it to false again to prevent your settings from being changed by an update.
let settings = {
// Target Resolution to always set to. If not available, the next best resolution will be used.
changeResolution: true,
preferPremium: true,
targetRes: "hd1080",
// Choices for targetRes are currently:
// "highres" >= ( 8K / 4320p / QUHD )
// "hd2880" = ( 5K / 2880p / UHD+ )
// "hd2160" = ( 4K / 2160p / UHD )
// "hd1440" = ( 1440p / QHD )
// "hd1080" = ( 1080p / FHD )
// "hd720" = ( 720p / HD )
// "large" = ( 480p )
// "medium" = ( 360p )
// "small" = ( 240p )
// "tiny" = ( 144p )
// Target Resolution for high framerate (60 fps) videos
// If null, it is the same as targetRes
highFramerateTargetRes: null,
// If changePlayerSize is true, then the video's size will be changed on the page
// instead of using youtube's default (if theater mode is enabled).
// If useCustomSize is false, then the player will be resized to try to match the target resolution.
// If true, then it will use the customHeight variables (theater mode is always full page width).
changePlayerSize: false,
useCustomSize: false,
customHeight: 600,
// If autoTheater is true, each video page opened will default to theater mode.
// This means the video will always be resized immediately if you are changing the size.
// NOTE: YouTube will not always allow theater mode immediately, the page must be fully loaded before theater can be set.
autoTheater: false,
// If flushBuffer is false, then the first second or so of the video may not always be the desired resolution.
// If true, then the entire video will be guaranteed to be the target resolution, but there may be
// a very small additional delay before the video starts if the buffer needs to be flushed.
flushBuffer: true,
// Setting cookies can allow some operations to perform faster or without a delay (e.g. theater mode)
// Some people don't like setting cookies, so this is false by default (which is the same as old behavior)
allowCookies: false,
// Tries to set the resolution as early as possible.
// This might cause issues on youtube polymer layout, so disable if videos fail to load.
// If videos load fine, leave as true or resolution may fail to set.
setResolutionEarly: true,
// Enables a temporary work around for an issue where users can get the wrong youtube error screen
// (Youtube has two of them for some reason and changing to theater mode moves the wrong one to the front)
// Try disabling if you can't interact with the video or you think you are missing an error message.
enableErrorScreenWorkaround: true,
// Use the iframe API to set resolution if possible. Otherwise uses simulated mouse clicks.
useAPI: true,
// Overwrite stored settings with the settings coded into the script, to apply changes.
// Set and keep as true to have settings behave like before, where you can just edit the settings here to change them.
overwriteStoredSettings: false
};
// --------------------
// --- GLOBALS --------
const DEBUG = false;
// Possible resolution choices (in decreasing order, i.e. highres is the best):
const resolutions = ['highres', 'hd2880', 'hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny'];
// youtube has to be at least 480x270 for the player UI
const heights = [4320, 2880, 2160, 1440, 1080, 720, 480, 360, 240, 144];
let doc = document, win = window;
// ID of the most recently played video
let recentVideo = "";
let foundHFR = false;
let setHeight = 0;
// --------------------
function debugLog(message)
{
if (DEBUG)
{
console.log("YTHD | " + message);
}
}
// --------------------
// Used only for compatability with webextensions version of greasemonkey
function unwrapElement(el)
{
if (el && el.wrappedJSObject)
{
return el.wrappedJSObject;
}
return el;
}
// --------------------
// Get video ID from the currently loaded video (which might be different than currently loaded page)
function getVideoIDFromURL(ytPlayer)
{
const idMatch = /(?:v=)([\w\-]+)/;
let id = "ERROR: idMatch failed; youtube changed something";
let matches = idMatch.exec(ytPlayer.getVideoUrl());
if (matches)
{
id = matches[1];
}
return id;
}
// --------------------
// Attempt to set the video resolution to desired quality or the next best quality
function setResolution(ytPlayer, resolutionList)
{
debugLog("Setting Resolution...");
const currentQuality = ytPlayer.getPlaybackQuality();
let res = settings.targetRes;
if (settings.highFramerateTargetRes && foundHFR)
{
res = settings.highFramerateTargetRes;
}
let shouldPremium = settings.preferPremium && [...ytPlayer.getAvailableQualityData()].some(q => q.quality == res && q.qualityLabel.includes("Premium") && q.isPlayable);
let useButtons = !settings.useAPI || shouldPremium;
// Youtube doesn't return "auto" for auto, so set to make sure that auto is not set by setting
// even when already at target res or above, but do so without removing the buffer for this quality
if (resolutionList.indexOf(res) < resolutionList.indexOf(currentQuality))
{
const end = resolutionList.length - 1;
let nextBestIndex = Math.max(resolutionList.indexOf(res), 0);
let ytResolutions = ytPlayer.getAvailableQualityLevels();
debugLog("Available Resolutions: " + ytResolutions.join(", "));
while ( (ytResolutions.indexOf(resolutionList[nextBestIndex]) === -1) && nextBestIndex < end )
{
++nextBestIndex;
}
if (!useButtons && settings.flushBuffer && currentQuality !== resolutionList[nextBestIndex])
{
let id = getVideoIDFromURL(ytPlayer);
if (id.indexOf("ERROR") === -1)
{
let pos = ytPlayer.getCurrentTime();
ytPlayer.loadVideoById(id, pos, resolutionList[nextBestIndex]);
}
debugLog("ID: " + id);
}
res = resolutionList[nextBestIndex];
}
if (settings.useAPI)
{
if (ytPlayer.setPlaybackQualityRange !== undefined)
{
ytPlayer.setPlaybackQualityRange(res);
}
ytPlayer.setPlaybackQuality(res);
debugLog("(API) Resolution Set To: " + res);
}
if (useButtons)
{
let resLabel = heights[resolutionList.indexOf(res)];
if (shouldPremium)
{
resLabel = [...ytPlayer.getAvailableQualityData()].find(q => q.quality == res && q.qualityLabel.includes("Premium")).qualityLabel;
}
let settingsButton = doc.querySelector(".ytp-settings-button:not(#ScaleBtn)")[0];
unwrapElement(settingsButton).click();
let qualityMenuButton = document.evaluate('.//*[contains(text(),"Quality")]/ancestor-or-self::*[@class="ytp-menuitem-label"]', ytPlayer, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
unwrapElement(qualityMenuButton).click();
let qualityButton = document.evaluate('.//*[contains(text(),"' + heights[resolutionList.indexOf(res)] + '") and not(@class)]/ancestor::*[@class="ytp-menuitem"]', ytPlayer, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
unwrapElement(qualityButton).click();
debugLog("(Buttons) Resolution Set To: " + res);
}
}
// --------------------
// Set resolution, but only when API is ready (it should normally already be ready)
function setResOnReady(ytPlayer, resolutionList)
{
if (settings.useAPI && ytPlayer.getPlaybackQuality === undefined)
{
win.setTimeout(setResOnReady, 100, ytPlayer, resolutionList);
}
else
{
let framerateUpdate = false;
if (settings.highFramerateTargetRes)
{
let features = ytPlayer.getVideoData().video_quality_features;
if (features)
{
let isHFR = features.includes("hfr");
framerateUpdate = isHFR && !foundHFR;
foundHFR = isHFR;
}
}
let curVid = getVideoIDFromURL(ytPlayer);
if ((curVid !== recentVideo) || framerateUpdate)
{
recentVideo = curVid;
setResolution(ytPlayer, resolutionList);
let storedQuality = localStorage.getItem("yt-player-quality");
if (!storedQuality || storedQuality.indexOf(settings.targetRes) === -1)
{
let tc = Date.now(), te = tc + 2592000000;
localStorage.setItem("yt-player-quality","{\"data\":\"" + settings.targetRes + "\",\"expiration\":" + te + ",\"creation\":" + tc + "}");
}
}
}
}
// --------------------
function setTheaterMode(ytPlayer)
{
debugLog("Setting Theater Mode");
if (win.location.href.indexOf("/watch") !== -1)
{
let pageManager = unwrapElement(doc.getElementsByTagName("ytd-watch-flexy")[0]);
if (pageManager && !pageManager.hasAttribute("theater"))
{
if (settings.enableErrorScreenWorkaround)
{
const styleContent = "#error-screen { z-index: 42 !important } .ytp-error { display: none !important }";
let errorStyle = doc.getElementById("ythdErrorWorkaroundStyleSheet");
if (!errorStyle)
{
errorStyle = doc.createElement("style");
errorStyle.type = "text/css";
errorStyle.id = "ythdStyleSheet";
errorStyle.innerHTML = styleContent;
doc.head.appendChild(errorStyle);
}
else
{
errorStyle.innerHTML = styleContent;
}
}
try
{
pageManager.setTheaterModeRequested(true);
pageManager.updateTheaterModeState_(true);
pageManager.onTheaterReduxValueUpdate(true);
pageManager.setPlayerTheaterMode_();
pageManager.dispatchEvent(new CustomEvent("yt-set-theater-mode-enabled", { detail: {enabled: true}, bubbles: true, cancelable: false} ));
}
catch {}
let theaterButton;
for (let i = 0; i < 3 && !pageManager.theaterValue; ++i)
{
debugLog("Clicking theater button to attempt to notify redux state");
let theaterButton = theaterButton || unwrapElement(doc.getElementsByClassName("ytp-size-button")[0]);
theaterButton.click();
}
}
}
}
// --------------------
function computeAndSetPlayerSize()
{
let height = settings.customHeight;
if (!settings.useCustomSize)
{
// don't include youtube search bar as part of the space the video can try to fit in
let heightOffsetEl = doc.getElementById("masthead");
let mastheadContainerEl = doc.getElementById("masthead-container");
let mastheadHeight = 50, mastheadPadding = 16;
if (heightOffsetEl && mastheadContainerEl)
{
mastheadHeight = parseInt(win.getComputedStyle(heightOffsetEl).height, 10);
mastheadPadding = parseInt(win.getComputedStyle(mastheadContainerEl).paddingBottom, 10) * 2;
}
let i = Math.max(resolutions.indexOf(settings.targetRes), 0);
height = Math.min(heights[i], win.innerHeight - (mastheadHeight + mastheadPadding));
height = Math.max(height, 270);
}
resizePlayer(height);
}
// --------------------
// resize the player
function resizePlayer(height)
{
debugLog("Setting video player size");
if (setHeight === height)
{
debugLog("Player size already set");
return;
}
let styleContent = "\
ytd-watch-flexy[theater]:not([fullscreen]) #player-theater-container.style-scope, \
ytd-watch-flexy[theater]:not([fullscreen]) #player-wide-container.style-scope, \
ytd-watch-flexy[theater]:not([fullscreen]) #full-bleed-container.style-scope { \
min-height: " + height + "px !important; max-height: none !important; height: " + height + "px !important }";
let ythdStyle = doc.getElementById("ythdStyleSheet");
if (!ythdStyle)
{
ythdStyle = doc.createElement("style");
ythdStyle.type = "text/css";
ythdStyle.id = "ythdStyleSheet";
ythdStyle.innerHTML = styleContent;
doc.head.appendChild(ythdStyle);
}
else
{
ythdStyle.innerHTML = styleContent;
}
setHeight = height;
win.dispatchEvent(new Event("resize"));
}
// --- MAIN -----------
function main()
{
let ytPlayer = doc.getElementById("movie_player") || doc.getElementsByClassName("html5-video-player")[0];
let ytPlayerUnwrapped = unwrapElement(ytPlayer);
if (settings.autoTheater && ytPlayerUnwrapped)
{
if (settings.allowCookies && doc.cookie.indexOf("wide=1") === -1)
{
doc.cookie = "wide=1; domain=.youtube.com";
}
setTheaterMode(ytPlayerUnwrapped);
}
if (settings.changePlayerSize && win.location.host.indexOf("youtube.com") !== -1 && win.location.host.indexOf("gaming.") === -1)
{
computeAndSetPlayerSize();
window.addEventListener("resize", computeAndSetPlayerSize, true);
}
if (settings.changeResolution && settings.setResolutionEarly && ytPlayerUnwrapped)
{
setResOnReady(ytPlayerUnwrapped, resolutions);
}
if (settings.changeResolution || settings.autoTheater)
{
win.addEventListener("loadstart", function(e) {
if (!(e.target instanceof win.HTMLMediaElement))
{
return;
}
ytPlayer = doc.getElementById("movie_player") || doc.getElementsByClassName("html5-video-player")[0];
ytPlayerUnwrapped = unwrapElement(ytPlayer);
if (ytPlayerUnwrapped)
{
debugLog("Loaded new video");
if (settings.changeResolution)
{
setResOnReady(ytPlayerUnwrapped, resolutions);
}
if (settings.autoTheater)
{
setTheaterMode(ytPlayerUnwrapped);
}
}
}, true );
}
// This will eventually be changed to use the "once" option, but I want to keep a large range of browser support.
win.removeEventListener("yt-navigate-finish", main, true);
}
async function applySettings()
{
if (typeof GM != 'undefined' && GM.getValue && GM.setValue)
{
let settingsSaved = await GM.getValue("SettingsSaved");
if (settings.overwriteStoredSettings || !settingsSaved)
{
Object.entries(settings).forEach(([k,v]) => GM.setValue(k, v));
await GM.setValue("SettingsSaved", true);
}
else
{
await Promise.all(
Object.keys(settings).map(k => { let newval = GM.getValue(k); return newval.then(v => [k,v]); })
).then((c) => c.forEach(([nk,nv]) => {
if (settings[nk] !== null && nk !== "overwriteStoredSettings")
{
settings[nk] = nv;
}
}));
}
debugLog(Object.entries(settings).map(([k,v]) => k + " | " + v).join(", "));
}
}
applySettings().then(() => {
main();
// Youtube doesn't load the page immediately in new version so you can watch before waiting for page load
// But we can only set resolution until the page finishes loading
win.addEventListener("yt-navigate-finish", main, true);
});
})();
@dasbacon
Copy link

theater mode and enhanced quality are both now working, thank you adisib!

@LOuroboros
Copy link

@LOuroboros can you please give me an example of an embedded player it doesn't work for?

Sure, here's a quick one:

firefox_20231211_153215282

Source.

Quality is being set to Auto, when in the script's configuration I have it set to 1080p, and I have changeResolution to TRUE as well.

The script always worked inside of Youtube, but outside of it? Never. I mentioned this in a comment right here. It's the very first comment in fact, which I wrote over 3 years ago 😆

@adisib
Copy link
Author

adisib commented Dec 11, 2023

Can you please tell me what browser and userscript manager you are using? I'm not having any issues with it with Firefox:

image

I'm testing with the following configuration:

// ==UserScript==
// @name          Youtube HD
// @author        adisib
// @namespace     namespace_adisib
// @description   Select a youtube resolution and resize the player.
// @version       2023.12.11
// @include       *
// @grant         GM.getValue
// @grant         GM.setValue
// @downloadURL https://update.greasyfork.org/scripts/23661/Youtube%20HD.user.js
// @updateURL https://update.greasyfork.org/scripts/23661/Youtube%20HD.meta.js
// ==/UserScript==
let settings = {

		// Target Resolution to always set to. If not available, the next best resolution will be used.
		changeResolution: true,
		preferPremium: true,
		targetRes: "hd1080",
		// Choices for targetRes are currently:
		//   "highres" >= ( 8K / 4320p / QUHD  )
		//   "hd2880"   = ( 5K / 2880p /  UHD+ )
		//   "hd2160"   = ( 4K / 2160p /  UHD  )
		//   "hd1440"   = (      1440p /  QHD  )
		//   "hd1080"   = (      1080p /  FHD  )
		//   "hd720"    = (       720p /   HD  )
		//   "large"    = (       480p         )
		//   "medium"   = (       360p         )
		//   "small"    = (       240p         )
		//   "tiny"     = (       144p         )

		// Target Resolution for high framerate (60 fps) videos
		// If null, it is the same as targetRes
		highFramerateTargetRes: null,

		// If changePlayerSize is true, then the video's size will be changed on the page
		//   instead of using youtube's default (if theater mode is enabled).
		// If useCustomSize is false, then the player will be resized to try to match the target resolution.
		//   If true, then it will use the customHeight variables (theater mode is always full page width).
		changePlayerSize: false,
		useCustomSize: false,
		customHeight: 600,

		// If autoTheater is true, each video page opened will default to theater mode.
		// This means the video will always be resized immediately if you are changing the size.
		// NOTE: YouTube will not always allow theater mode immediately, the page must be fully loaded before theater can be set.
		autoTheater: false,

		// If flushBuffer is false, then the first second or so of the video may not always be the desired resolution.
		//   If true, then the entire video will be guaranteed to be the target resolution, but there may be
		//   a very small additional delay before the video starts if the buffer needs to be flushed.
		flushBuffer: true,

		// Setting cookies can allow some operations to perform faster or without a delay (e.g. theater mode)
		// Some people don't like setting cookies, so this is false by default (which is the same as old behavior)
		allowCookies: false,

		// Tries to set the resolution as early as possible.
		// This might cause issues on youtube polymer layout, so disable if videos fail to load.
		// If videos load fine, leave as true or resolution may fail to set.
		setResolutionEarly: true,

		// Enables a temporary work around for an issue where users can get the wrong youtube error screen
		// (Youtube has two of them for some reason and changing to theater mode moves the wrong one to the front)
		// Try disabling if you can't interact with the video or you think you are missing an error message.
		enableErrorScreenWorkaround: true,

		// Use the iframe API to set resolution if possible. Otherwise uses simulated mouse clicks.
		useAPI: true,

		// Overwrite stored settings with the settings coded into the script, to apply changes.
		// Set and keep as true to have settings behave like before, where you can just edit the settings here to change them.
		overwriteStoredSettings: true

	};


Edit:
Nevermind, you already mentioned before that is was Firefox and ViolentMonkey (assuming it hasn't changed). That is the same as what I tested in my screenshot, so I'm not sure how to identify the problem if I can't reproduce it. Is there any chance you are using other userscripts or extensions that might affect the embedded youtube player?

@LOuroboros
Copy link

LOuroboros commented Dec 11, 2023

Is there any chance you are using other userscripts or extensions that might affect the embedded youtube player?

I do have another Youtube related userscript that may or may not be interfering with Youtube HD. It's called YouTube - Stay Active and Play Forever.

I'll check if Youtube HD works properly on my end after disabling it 👀

EDIT: No, even after uninstalling it completely and performing a Ctrl+F5 on the page, the embedded Youtube player still defaults to Auto on my end :/

EDIT2: I'll try a clean profile with nothing but ViolentMonkey and this script installed, to see how that goes.

EDIT3: Nope, even on a clean profile with no addons besides ViolentMonkey and no userscripts but this one it still doesn't work, I'm afraid.

firefox_20231211_191345580

I didn't touch the default configuration at all, mind you. Was that required?

@adisib
Copy link
Author

adisib commented Dec 11, 2023

I didn't touch the default configuration at all, mind you. Was that required?

The settings variable configuration shouldn't affect embeds, but the ==UserScript== header configuration does.

I can look at trying it out on some other PCs and see if maybe if it happens on a cleaner install on my end.

@dasbacon
Copy link

hey adisib, having an issue again with autoTheater not expanding when set to true.

quality settings are still working with no issues after your last update.

@adisib
Copy link
Author

adisib commented Jan 15, 2024

hey adisib, having an issue again with autoTheater not expanding when set to true.

quality settings are still working with no issues after your last update.

Hey, sorry for the delay. Things have been busy lately. I spent some time looking at this today. It looks like youtube is using some redux.js state machinery where it will not update the state even when I call the new commands to set the theater mode. As a result, the theater mode will enable after updating this script but you have to press the theater button several times to turn it back off. I also tried just clicking the button instead but that has its own problems and generally works worse. I'm not sure yet how to trigger an update in the redux state, but will try to make it work better as I have time. Probably most people who use the auto-theater feature will not be turning it on and off regularly.

Please grab the latest update to give it a try and see if auto theater is working well enough now.

@dasbacon
Copy link

working again after update. i never disable theater so no issues there. thank you adisib!

@aehlke
Copy link

aehlke commented Feb 13, 2024

Please add a license such as MIT

@Skyxim
Copy link

Skyxim commented May 20, 2024

When there is a premium option for 1080P, I always choose 1080P instead of premium. Is there something wrong with my settings? Here are my configurations:

   let settings = {

        // Target Resolution to always set to. If not available, the next best resolution will be used.
        changeResolution: true,
        preferPremium: true,
        targetRes: "highres",
        // Choices for targetRes are currently:
        //   "highres" >= ( 8K / 4320p / QUHD  )
        //   "hd2880"   = ( 5K / 2880p /  UHD+ )
        //   "hd2160"   = ( 4K / 2160p /  UHD  )
        //   "hd1440"   = (      1440p /  QHD  )
        //   "hd1080"   = (      1080p /  FHD  )
        //   "hd720"    = (       720p /   HD  )
        //   "large"    = (       480p         )
        //   "medium"   = (       360p         )
        //   "small"    = (       240p         )
        //   "tiny"     = (       144p         )

        // Target Resolution for high framerate (60 fps) videos
        // If null, it is the same as targetRes
        highFramerateTargetRes: 60,

        // If changePlayerSize is true, then the video's size will be changed on the page
        //   instead of using youtube's default (if theater mode is enabled).
        // If useCustomSize is false, then the player will be resized to try to match the target resolution.
        //   If true, then it will use the customHeight variables (theater mode is always full page width).
        changePlayerSize: false,
        useCustomSize: false,
        customHeight: 600,

        // If autoTheater is true, each video page opened will default to theater mode.
        // This means the video will always be resized immediately if you are changing the size.
        // NOTE: YouTube will not always allow theater mode immediately, the page must be fully loaded before theater can be set.
        autoTheater: true,

        // If flushBuffer is false, then the first second or so of the video may not always be the desired resolution.
        //   If true, then the entire video will be guaranteed to be the target resolution, but there may be
        //   a very small additional delay before the video starts if the buffer needs to be flushed.
        flushBuffer: true,

        // Setting cookies can allow some operations to perform faster or without a delay (e.g. theater mode)
        // Some people don't like setting cookies, so this is false by default (which is the same as old behavior)
        allowCookies: false,

        // Tries to set the resolution as early as possible.
        // This might cause issues on youtube polymer layout, so disable if videos fail to load.
        // If videos load fine, leave as true or resolution may fail to set.
        setResolutionEarly: true,

        // Enables a temporary work around for an issue where users can get the wrong youtube error screen
        // (Youtube has two of them for some reason and changing to theater mode moves the wrong one to the front)
        // Try disabling if you can't interact with the video or you think you are missing an error message.
        enableErrorScreenWorkaround: true,

        // Use the iframe API to set resolution if possible. Otherwise uses simulated mouse clicks.
        useAPI: true,

        // Overwrite stored settings with the settings coded into the script, to apply changes.
        // Set and keep as true to have settings behave like before, where you can just edit the settings here to change them.
        overwriteStoredSettings: false

    };

@sghdeveloper
Copy link

Hello
I have a request for this brilliant script.
Please consider adding an option to disable autoplay, not for the next video, but to prevent the video from starting when the tab is loaded. Currently, when a YouTube video is opened in a new tab, it automatically starts playing upon switching to that tab. I would like the video player to continue buffering but not play the video until I click play.
Thank you

@dasbacon
Copy link

Hello I have a request for this brilliant script. Please consider adding an option to disable autoplay, not for the next video, but to prevent the video from starting when the tab is loaded. Currently, when a YouTube video is opened in a new tab, it automatically starts playing upon switching to that tab. I would like the video player to continue buffering but not play the video until I click play. Thank you

Firefox can already disable autoplay of audio and video:

image

@Uj947nXmRqV2nRaWshKtHzTvckUUpD

is this still supposed to work? I tried it in brave with violentmonkey but on 4k video in theater mode, by default is selected fullhd

@black-ish
Copy link

Works fine here in multiple browsers.

@Shugojin
Copy link

Hi, sorry to ask but how could i use this please ?
I searched for an addon like this and i landed here but i have no clue how to use a .js file ^^'

Thanks for your help :-)

@Uj947nXmRqV2nRaWshKtHzTvckUUpD

Hi, sorry to ask but how could i use this please ? I searched for an addon like this and i landed here but i have no clue how to use a .js file ^^'

Thanks for your help :-)

use violentmonkey

@floriegl
Copy link

I have added a way to additionally change the resolution using menu commands @adisib
Feel free to add it to the script however you see fit. It of course only applies after setting overwriteStoredSettings to true once because GM.getValue returns undefined for the new key before that.

@@ -8,6 +8,7 @@
 // @noframes
 // @grant         GM.getValue
 // @grant         GM.setValue
+// @grant         GM.registerMenuCommand
 // ==/UserScript==
 
 // The video will only resize when in theater mode on the main youtube website.
@@ -54,6 +55,9 @@
 	//   "small"    = (       240p         )
 	//   "tiny"     = (       144p         )
 
+	// If true, menu commands for each resolution are created to change the target resolution.
+	resolutionSelectionInMenu: true,
+
 	// Target Resolution for high framerate (60 fps) videos
 	// If null, it is the same as targetRes
 	highFramerateTargetRes: null,
@@ -490,6 +494,28 @@
 		}));
 	  }
 
+	  if (settings.resolutionSelectionInMenu && GM.registerMenuCommand)
+	  {
+		[
+		  { key: "highres", label: "8K / 4320p / QUHD" },
+		  { key: "hd2880", label: "5K / 2880p / UHD+" },
+		  { key: "hd2160", label: "4K / 2160p / UHD" },
+		  { key: "hd1440", label: "1440p / QHD" },
+		  { key: "hd1080", label: "1080p / FHD" },
+		  { key: "hd720", label: "720p / HD" },
+		  { key: "large", label: "480p" },
+		  { key: "medium", label: "360p" },
+		  { key: "small", label: "240p" },
+		  { key: "tiny", label: "144p" }
+		].forEach(resolution => {
+		  const menuLabel = `${resolution.label}${(settings.targetRes === resolution.key) ? " - X" : ""}`;
+		  GM.registerMenuCommand(menuLabel, () => {
+			GM.setValue("targetRes", resolution.key);
+			document.location.reload();
+		  });
+		});
+	  }
+
 	  debugLog(Object.entries(settings).map(([k,v]) => k + " | " + v).join(", "));
 	}
   }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment