-
-
Save adisib/1e6b429b9bb630fceb170f3fa77c57a3 to your computer and use it in GitHub Desktop.
// ==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); | |
}); | |
})(); |
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.
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
@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 *://*/*
thanks for your contribution, can you make it work on iOS safari m.YouTube.com?iOS safari has a extension support inject js now.
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.
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.
An option to configure a targetres for videos running in picture-in-picture mode (on firefox, in my case) would be great too.
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.
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?
^ 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.
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.
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.
theater mode and enhanced quality are both now working, thank you adisib!
@LOuroboros can you please give me an example of an embedded player it doesn't work for?
Sure, here's a quick one:
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 😆
Can you please tell me what browser and userscript manager you are using? I'm not having any issues with it with Firefox:
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?
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.
I didn't touch the default configuration at all, mind you. Was that required?
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.
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 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.
working again after update. i never disable theater so no issues there. thank you adisib!
Please add a license such as MIT
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
};
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
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:
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
Works fine here in multiple browsers.
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 :-)
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
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(", "));
}
}
@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.
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,