Skip to content

Instantly share code, notes, and snippets.

@adisib
Last active April 5, 2024 09:16
Show Gist options
  • Star 62 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • 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);
});
})();
@LOuroboros
Copy link

@adisib, I love this userscript. I found it on GreasyFork around 3 months and I've been using it ever since because I just absolutely despise the ever changing Auto Quality.

I just found now that you posted it here on GitHub and I wanted to take the chance to ask you, is there any chance you can also make it work with Youtube videos that are embedded in a website?
Those default to Auto Quality no matter what, even when using your userscript :/

@adisib
Copy link
Author

adisib commented Oct 31, 2019

Thanks for the kind feedback; I am glad you find it useful.

In regards to embedded videos, if you perform the necessary steps of adding @include * and removing @noframes, I've found that last time I checked, it will indeed say the quality is set to Auto, but will set the quality anyway, i.e. it displays Auto even when it isn't Auto. But that hasn't been consistent and might have changed.

I'll take a look again soon how it is currently behaving. If it doesn't work at all, it is most likely from YouTube's API behaving differently for embeds than it does for their website, in which case there probably isn't anything I can do, and is why I don't officially support embeds. Regardless, I want to make sure the script continues to have the ability to tell embeds to set their resolution, whether they obey or not, so I'll take another look at some point soon.

@adisib
Copy link
Author

adisib commented Oct 31, 2019

Actually, it turns at that a week ago YouTube has specifically mentioned that setting playback quality in their API is no longer supported, and this has been the case for more than a year now. As my script relies on YouTube's API, this is likely the reason it hasn't been working on embeds, and doesn't bode well.

@adisib
Copy link
Author

adisib commented Oct 31, 2019

@LOuroboros I was able to go ahead and take a look. My script seemed to work perfectly fine for an embedded video when I tested it with Brave+Tampermonkey. It did not say Auto, and appears to be working better than it did previously.

The embed I tested with was this example:
https://www.w3schools.com/html/tryit.asp?filename=tryhtml_youtubeiframe

It is possible that there are other circumstances for other pages that might cause problems, so it would be very helpful if you could provide me with an example page that doesn't work. In addition, it would be helpful to have confirmation of what browser and userscript manager you are using, as modern greasemonkey does not support iframes and wont work like tampermonkey will for example.

@LOuroboros
Copy link

LOuroboros commented Oct 31, 2019

@adisib I'm using Mozilla Firefox v70.0.1 x64, Violentmonkey v2.11.2 and I have flushBuffer and allowCookies both set to True.
I tried the example you provided, and I get the exact same result.
If I set those options to False, I also get the same result.
firefox-2019-10-31_20-05-44

I'm going to disable ViolentMonkey and try TamperMonkey instead just in case and see if I get a different result.

If there's any other piece of information you need, let me know though.

EDIT: I disabled ViolentMonkey and I've installed the latest version of TamperMonkey straight from AMO, but I'm still getting the same result for some reason.

EDIT2: I tried a Clean profile of Firefox where I only have uBlock Origin installed and nothing else. I installed TamperMonkey and the UserScript without any changes, but I still get the same result. Auto Quality in that w3schools.com example you provided defaulting to 360p,

@adisib
Copy link
Author

adisib commented Nov 1, 2019

Thanks for the detailed information. I was able to look further into now, and I can indeed confirm that it simply does not work with Firefox.

The issue is that for Firefox, the function used to set the resolution is undefined, whereas the function was provided for Brave (and probably any Chromium based browser). This isn't an issue with Firefox itself. It seems to be another possible instance of Google purposefully making their stuff not work on browsers other than theirs.

This means that there probably isn't anything I can do. Google wont fix it either since they don't support the function anyway. I'll probably look into it more at some point, and maybe a user-agent override will help. But for now, it looks like this feature will very unfortunately be only for users of Chromium based browsers.

@Gittyperson
Copy link

Gittyperson commented Dec 16, 2020

Thank you for your wonderful userscript. I just tried enabling embedded video support and it seems to work as intended. Also tested the link in your previous post. Quality switches to HD/1080 on that video (not Auto).

YouTube HD 2020.09.26, Pale Moon browser, Greasemonkey for Pale Moon

@dnmTX
Copy link

dnmTX commented Jun 22, 2021

@adisib can't you make it check for embedded by default and if not any just to exit?
Like fo example....i'm not an expert in scripting,just posting what i got in another one of my UserScripts:

isrc = /youtube.com|youtube-nocookie.com/i;
.....document.querySelectorAll('iframe').forEach(function(element) { if (isrc.test(element.src)) { .....
Therefore will remove @noframes and change @match to *://*/*

@Yumega
Copy link

Yumega commented Nov 17, 2021

thanks for your contribution, can you make it work on iOS safari m.YouTube.com?iOS safari has a extension support inject js now.

@adisib
Copy link
Author

adisib commented Nov 30, 2021

I do not have an iOS device to test my script on. If it doesn't already work, let me know the name of that extension and I can take a look to see what might need to be done to support it though and try to find someone who can test.

@alpe12
Copy link

alpe12 commented Jan 8, 2023

When I manually change the resolution of a video, it changes it back to the configured value.
It should not change when the user manually change it on the video.

PS: This happened when I configured the script to "medium" and tried changing the video to 1080p. When I configure the script to hd1080 and reduce the resolution on the video it allows me to.

@alpe12
Copy link

alpe12 commented Jan 8, 2023

An option to configure a targetres for videos running in picture-in-picture mode (on firefox, in my case) would be great too.

@adisib
Copy link
Author

adisib commented Jan 9, 2023

Hi alpe12, thanks for the feedback. I have not been able to reproduce your issue on Firefox.

For the manual resolution change, I set the script to medium, loaded a video, set it to 1080p manually, and it stayed on 360p without changing. I might need more details to investigate this one, such as which user script extension you are using, what script configuration changes you have made, and if you are using any other extensions or userscripts that run on youtube.

@dasbacon
Copy link

thank you for this script adisib. i've been using it for a long while now. however it does not appear to be working for the past few days. any ideas?

@LOuroboros
Copy link

^ It's working just fine on my end on Mozilla Firefox v120.0.1 with the script loaded through ViolentMonkey, fwiw.

Sadly, it's still not working on sites with an embedded Youtube player though.

@dasbacon
Copy link

looks like i was partially mistaken. i have my quality set to 1440p which is correctly changing. however i also have hometheater set to true which is not changing.

i'm also using premium and 1080p enhanced is not setting either.

@adisib
Copy link
Author

adisib commented Dec 11, 2023

Thanks for reporting the issues. Theater mode did indeed seem to break, and I just made a change that seems to fix it. In regards to the premium/enhanced quality, I made a change for that but only updated the greasyfork version so that it could be tested. Since people have confirmed that it seems to work, I have updated it here on github as well. So please give me a try and see if it is working for you.

@LOuroboros can you please give me an example of an embedded player it doesn't work for? I just tried it on https://www.w3schools.com/html/tryit.asp?filename=tryhtml_youtubeiframe after removing the "noframes" and replacing the match with "include *" and it seemed to set the resolution just fine for me.

@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

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