thecyberd3m0n / Youtube TV in browser / Youtube Leaning

// ==UserScript==
// @namespace     https://openuserjs.org/users/thecyberd3m0n
// @name          Youtube TV in browser / Youtube Leaning
// @description   Enjoy fully-fledged Youtube TV in browser. Script mocks User Agent (to Xbox X)
// and sets some config flags to enable chat or animations. Ads were not disabled
// ads some CSS to not display ugly desktop scrollbars. App can be controlled
// from iOS/Android client, just like in consoles or smart TV's.
// It's fully pairable with fully working Google Cast features.
// @copyright     2024, thecyberd3m0n (https://openuserjs.org/users/thecyberd3m0n)
// @license       MIT
// @version       0.0.1
// @include       https://www.youtube.com/tv*
// @grant none
// ==/UserScript==

// ==OpenUserJS==
// @author thecyberd3m0n
// ==/OpenUserJS==

/**
 *
 * Please begin typing or paste your User script now.
 *
 * NOTE: It is still strongly recommended to use the Author Tools panel to
 *       add your `@updateURL` even if we are not in lockdown.
 *
 */

(function () {
  'use strict';
  // Override the User-Agent property to not be redirected to youtube.com
  Object.defineProperty(navigator, 'userAgent', {
    get: function () {
      return 'Mozilla/5.0 (Windows NT 10.0; Xbox; Xbox Series X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.0.0';
    },
    configurable: true
  });

  // Override the properties directly on the window object
  Object.defineProperty(window, 'tectonicConfig', {
    configurable: true,
    enumerable: true,
    writable: true,
    value: window.tectonicConfig || {}
  });

  Object.defineProperty(window.tectonicConfig, 'featureSwitches', {
    configurable: true,
    enumerable: true,
    writable: true,
    value: window.tectonicConfig.featureSwitches || {}
  });

  // Override 'enableAnimations' to always return true and prevent setting to false
  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enableAnimations', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  // Override 'enableAmbientInterludes' to always return true and prevent setting to false
  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enableAmbientInterludes', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  // Override 'enableStartupSound' to always return true and prevent setting to false
  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enableStartupSound', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  // Override 'enableAndroidAttestation' to always return true and prevent setting to false
  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enableAndroidAttestation', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  // Override 'enableListAnimations' to always return true and prevent setting to false
  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enableListAnimations', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  // Override 'enableTouchSupport' to always return true and prevent setting to false
  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enableTouchSupport', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  Object.defineProperty(window.tectonicConfig.featureSwitches, 'isLimitedMemory', {
    get: function () {
      return false; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  Object.defineProperty(window.tectonicConfig.featureSwitches, 'isServiceWorkerCapable', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enableLightweightTimely', {
    get: function () {
      return false; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enableLiveChat', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enablePageServiceCache', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  Object.defineProperty(window.tectonicConfig.featureSwitches, 'enableRoundedProgressBar', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  Object.defineProperty(window.tectonicConfig.featureSwitches, 'forceVoiceIncapable', {
    get: function () {
      return false; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  Object.defineProperty(window.tectonicConfig.featureSwitches, 'forceVoiceEnabled', {
    get: function () {
      return true; // Always return true
    },
    set: function () {
      // Do nothing, prevent setting it to false or any other value
    },
    configurable: false // Prevent reconfiguring this property
  });

  // Apply CSS to modify scrollbars to look darker, semi-transparent, and hide tracks
  const style = document.createElement('style');
  style.innerHTML = `
        /* Mobile-like scrollbars with dark and semi-transparent appearance */
        ::-webkit-scrollbar {
            width: 0px;  /* Width of the scrollbar */
            height: 0px; /* Height of horizontal scrollbar */
        }

        ::-webkit-scrollbar-thumb {
            background-color: rgba(0, 0, 0, 0.6);  /* Dark and semi-transparent thumb */
            border-radius: 10px;
            border: 0px solid rgba(255, 255, 255, 0.3); /* Adds a little space around the thumb */
        }

        ::-webkit-scrollbar-thumb:hover {
            background-color: rgba(0, 0, 0, 0.8);  /* Darker color when hovered */
        }

        ::-webkit-scrollbar-track {
            background: transparent;  /* Make the track invisible */
        }
    `;
  document.head.appendChild(style);

})();