Skip to content

Instantly share code, notes, and snippets.

@cvan
Last active December 18, 2023 15:25
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cvan/6f8196c1e8fce2165eb6847eba89f2c7 to your computer and use it in GitHub Desktop.
Save cvan/6f8196c1e8fce2165eb6847eba89f2c7 to your computer and use it in GitHub Desktop.
Force video quality with YouTube (notes for Firefox Reality)

Forcing a better default playback quality of YouTube videos

If a user is browsing videos on YouTube.com from a web browser on a higher-end device (e.g., VR headsets), the user has to manually override the playback quality of a video through the playback-controls bar at the bottom of the video: Settings > Quality.


Forcing a different playback quality of a YouTube video

  1. Load a YouTube video. Example: https://www.youtube.com/watch?v=ELU-43DMNT4
  2. Then paste some of this code in the DevTools' Console.
  3. You'll notice the quality changes to the highest available for the video.
// Open the `Settings` menu.
document.querySelector('ytp-settings-button, .ytp-settings-button').click();

// Select the `Quality` sub-menu.
document.querySelector('ytp-settings-menu ytp-menuitem:last-child, .ytp-settings-menu .ytp-menuitem:last-child').click();

// Select the best `Quality`. (The best quality is always the first item. See below for how to cross-reference the `ytp-menuitem` rows with the shortnames - because there are no classes or identifiers in the DOM to query for.)
document.querySelector('ytp-quality-menu ytp-menuitem:first-child, .ytp-quality-menu .ytp-menuitem:first-child').click();

Identifying the current quality info from a YouTube video

  1. See YouTube docs on supported qualities.
  2. Load a YouTube video. Example: https://www.youtube.com/watch?v=ELU-43DMNT4
  3. Then paste some of this code in the DevTools' Console.

Note: YouTube, using heuristics of hardware and software capabilities, automatically sets the quality to the most performant, performance-wise and network-wise. (This is the default Auto quality option listed in the video settings: Settings > Quality).

// To get the current playback quality of the video.
document.querySelector('#movie_player').getPlaybackQuality();

Posssible enhacements for later

  • When navigating between videos, it'd be ideal to persist the user's choice in local storage (either in window.localStorage or WebExtension's browser.storage).)_
  • Allow a user to pass ?mozVideoQuality= in YouTube URLs to force a quality. If the video doesn't support that quality, we could downgrade to the next available quality, ultimately falling back to the 'Auto' setting if needed.

Sample code for supporting vq= query-string parameter in YouTube video URLs

// (function () {
// const
playerEl = document.getElementById('movie_player');
if (!playerEl) {
  return;
}

// const
YOUTUBE_QUALITY_SIZES = [4320, 2880, 2160, 1440, 1080, 720, 480, 360, 240, 144];
// const
YOUTUBE_QUALITY_DEFAULT_SIZE = 1440;
// const
YOUTUBE_QUALITY_LABELS = {
  4320: 'highres',
  2160: 'hd2160',
  1440: 'hd1440',
  1080: 'hd1080',
  720: 'hd720',
  480: 'large',
  360: 'medium',
  240: 'small',
  144: 'tiny'
};

// const
YOUTUBE_QUALITY_SIZES_BY_LABEL = {};
YOUTUBE_QUALITY_SIZES.forEach(size => {
  YOUTUBE_QUALITY_SIZES_BY_LABEL[YOUTUBE_QUALITY_LABELS[size]] = size;
});

// const
YOUTUBE_QUALITY_DEFAULT_SIZE_LABEL = YOUTUBE_QUALITY_LABELS[YOUTUBE_QUALITY_DEFAULT_SIZE];

function getBestDefaultQuality (choices, defaultQuality) {
  //let
  defaultQualitySize = defaultQuality;
  //let
  defaultQualityLabel = YOUTUBE_QUALITY_LABELS[defaultQuality];

  if (!Number.isInteger(defaultQuality)) {
    defaultQualitySize = YOUTUBE_QUALITY_SIZES_BY_LABEL[defaultQuality];
    defaultQualityLabel = defaultQuality;
  }

  if (!defaultQualitySize) {
    throw new Error(`Quality "${defaultQualitySize}" not supported on YouTube`);
  }

  //let
  idx = choices.indexOf(defaultQualityLabel);
  if (idx > -1) {
    return idx;
  }

  //let
  label = '';
  for (idx = 0; idx < choices.length; idx++) {
    label = choices[idx];
    if (label in YOUTUBE_QUALITY_SIZES_BY_LABEL && YOUTUBE_QUALITY_SIZES_BY_LABEL[label] <= YOUTUBE_QUALITY_DEFAULT_SIZE) {
      return idx;
    }
  }

  return idx;
}

// const
playbackQuality = playerEl.getPlaybackQuality();

// let
desiredQuality = YOUTUBE_QUALITY_DEFAULT_SIZE;

// const
qs = window.URLSearchParams && new URLSearchParams(window.location.search);
if (qs && qs.has('vq') || qs.has('quality')) {
  desiredQuality = (qs.get('vq') || qs.get('quality') || '').trim().toLowerCase();
  if (desiredQuality === 'auto' || desiredQuality === 'default') {
    // Open the `Settings` menu.
    console.log('Opened video "Settings" menu');
    document.querySelector('ytp-settings-button, .ytp-settings-button').click();

    // Select the `Quality` sub-menu.
    console.log('Opened video "Quality" sub-menu');
    document.querySelector('ytp-settings-menu ytp-menuitem:last-child, .ytp-settings-menu .ytp-menuitem:last-child').click();

    // Select the best `Quality`.
    // NOTE: The best quality is always the first item.
    //       See below for how to cross-reference the `ytp-menuitem` rows with the shortnames -
    //       because there are no classes or identifiers in the DOM to query for.)

    // const
    autoQualityEl = document.querySelector(`ytp-quality-menu ytp-menuitem:first-child, .ytp-quality-menu .ytp-menuitem:last-child`);
    autoQualityEl.click();
    console.log(`Changed changed to "Auto"`);
  }
}

if (YOUTUBE_QUALITY_SIZES_BY_LABEL[playbackQuality] >= desiredQuality) {
  console.log(`Quality is already playing at "${playbackQuality}" (requested "${YOUTUBE_QUALITY_LABELS[desiredQuality]}")`);
  // return;
} else {
  console.log(playerEl.getAvailableQualityLevels());

  // const
  newQualityIdx = getBestDefaultQuality(playerEl.getAvailableQualityLevels(), desiredQuality);
  console.log(newQualityIdx);

  // Open the `Settings` menu.
  console.log('Opened video "Settings" menu');
  document.querySelector('ytp-settings-button, .ytp-settings-button').click();

  // Select the `Quality` sub-menu.
  console.log('Opened video "Quality" sub-menu');
  document.querySelector('ytp-settings-menu ytp-menuitem:last-child, .ytp-settings-menu .ytp-menuitem:last-child').click();

  // Select the best `Quality`. (The best quality is always the first item. See below for how to cross-reference the `ytp-menuitem` rows with the shortnames - because there are no classes or identifiers in the DOM to query for.)

  // const
  newQualityEl = document.querySelector(`ytp-quality-menu ytp-menuitem:first-child, .ytp-quality-menu .ytp-menuitem:nth-child(${newQualityIdx})`);
  newQualityEl.click();
  console.log(`Changed changed to "${newQualityEl.textContent}" ("${playerEl.getPlaybackQuality()}")`);

}
// })();
@MortimerGoro
Copy link

MortimerGoro commented Mar 29, 2019

Another way I found to get the available qualities:

let player =  document.getElementById('movie_player');
player.getAvailableQualityLevels(); // Get an array of available qualities
player.getPlaybackQuality(); // Get selected quality

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