YouTube固定影片畫質

記住選取的 YouTube 影片畫質,不需任何額外的操作介面。

  1. // ==UserScript==
  2. // @name YouTube Fixed Video Quality
  3. // @name:zh-TW YouTube固定影片畫質
  4. // @name:zh-CN YouTube固定视频画质
  5.  
  6. // @description Remember video quality without any additional user interface.
  7. // @description:zh-TW 記住選取的 YouTube 影片畫質,不需任何額外的操作介面。
  8. // @description:zh-CN 记住选取的 YouTube 视频画质,不需任何额外的操作介面。
  9.  
  10. // @license MIT
  11. // @namespace https://greasyfork.org/users/1086571
  12. // @version 1.2
  13. // @author IzsKon
  14. // @match https://www.youtube.com/*
  15. // @icon https://raw.githubusercontent.com/IzsKon/YouTube-Fixed-Video-Quality/main/icon.png
  16. // @grant GM.getValue
  17. // @grant GM.setValue
  18. // ==/UserScript==
  19.  
  20. (async function() {
  21. 'use strict';
  22.  
  23. let vidQuality = await GM.getValue( 'videoQuality', 1 );
  24. let player = null;
  25.  
  26. document.addEventListener('yt-player-updated', () => {
  27.  
  28. /* Check page type. Video url should be /watch or /live */
  29. if ( /^\/watch|^\/live/.test(window.location.pathname) ) {
  30. setVideoQuality();
  31. }
  32. });
  33.  
  34.  
  35. async function setVideoQuality() {
  36.  
  37. /* Load settings panel. */
  38. let settingsBtn = document.querySelector('.ytp-settings-button');
  39. settingsBtn.click();
  40. settingsBtn.click();
  41.  
  42. /* Open quality selection panel. */
  43. let qualityBtn = document.querySelector('.ytp-menuitem-content div:not(.ytp-menuitem-toggle-checkbox)');
  44. if (!qualityBtn) { /* Video not loaded: stream not started or other issues. */
  45. detectVideoStart();
  46. return;
  47. }
  48. qualityBtn.click();
  49. let qualityOptions = document.querySelectorAll('.ytp-quality-menu .ytp-menuitem:not(:has(.ytp-premium-label))');
  50.  
  51. /* Close quality selection panel. */
  52. settingsBtn.click();
  53. settingsBtn.click();
  54.  
  55. /* Select video quality. */
  56. let nth_option = qualityOptions.length - vidQuality;
  57. qualityOptions[ Math.max(0, nth_option) ].click();
  58.  
  59. /* Add event listener to quality selection. */
  60. for ( let i = 0; i < qualityOptions.length; ++i ) {
  61. qualityOptions[i].addEventListener('click', () => {
  62. GM.setValue( 'videoQuality', qualityOptions.length - i );
  63. });
  64. }
  65. }
  66.  
  67.  
  68. function detectVideoStart() {
  69.  
  70. /* Prevent infinite loop b/w detectVideoStart() & setVideoQuality() in case sth go wrong. */
  71. if (player) return;
  72.  
  73. const observer = new MutationObserver((mutations) => {
  74. mutations.forEach((mutation) => {
  75. if (mutation.type != 'attributes') return;
  76. if (mutation.attributeName != 'class') return;
  77.  
  78. /* Detect when a stream starts. */
  79. if ( player.classList.contains('unstarted-mode') ) return;
  80.  
  81. observer.disconnect();
  82. setVideoQuality();
  83. });
  84. });
  85.  
  86. player = document.getElementById('movie_player');
  87. observer.observe(player, { attributes: true });
  88. }
  89.  
  90. })();