Simple YouTube Age Restriction Bypass

View age restricted videos on YouTube without verification and login :)

Mint 2021.07.09.. Lásd a legutóbbi verzió

  1. // ==UserScript==
  2. // @name Simple YouTube Age Restriction Bypass
  3. // @name:de Simple YouTube Age Restriction Bypass
  4. // @version 0.9.4
  5. // @description View age restricted videos on YouTube without verification and login :)
  6. // @description:de Schaue YouTube Videos mit Altersbeschränkungen ohne Anmeldung und ohne dein Alter zu bestätigen :)
  7. // @author ZerodyOne (https://github.com/zerodytrash)
  8. // @namespace https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/
  9. // @supportURL https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues
  10. // @license MIT
  11. // @match https://www.youtube.com/*
  12. // @grant none
  13. // @run-at document-start
  14. // ==/UserScript==
  15.  
  16. (function() {
  17.  
  18. var nativeParse = window.JSON.parse; // Backup the original parse function
  19. var nativeDefineProperty = getNativeDefineProperty(); // Backup the original defineProperty function to intercept setter & getter on the ytInitialPlayerResponse
  20. var wrappedPlayerResponse = null;
  21. var unlockablePlayerStates = ["AGE_VERIFICATION_REQUIRED", "LOGIN_REQUIRED", "UNPLAYABLE"];
  22. var playerResponsePropertyAliases = ["ytInitialPlayerResponse", "playerResponse"];
  23. var responseCache = {};
  24.  
  25. // Just for compatibility: Backup original getter/setter for 'ytInitialPlayerResponse', defined by other extensions like AdBlock
  26. var initialPlayerResponseDescriptor = window.Object.getOwnPropertyDescriptor(window, "ytInitialPlayerResponse");
  27. var chainedSetter = initialPlayerResponseDescriptor ? initialPlayerResponseDescriptor.set : null;
  28. var chainedGetter = initialPlayerResponseDescriptor ? initialPlayerResponseDescriptor.get : null;
  29.  
  30. // Just for compatibility: Intercept (re-)definitions on Youtube's initial player response property to chain setter/getter from other extensions by hijacking the Object.defineProperty function
  31. window.Object.defineProperty = function(obj, prop, descriptor) {
  32. if(obj === window && playerResponsePropertyAliases.includes(prop)) {
  33. console.info("Another extension tries to re-define '" + prop + "' (probably an AdBlock extension). Chain it...");
  34.  
  35. if(descriptor && descriptor.set) chainedSetter = descriptor.set;
  36. if(descriptor && descriptor.get) chainedGetter = descriptor.get;
  37. } else {
  38. nativeDefineProperty(obj, prop, descriptor);
  39. }
  40. }
  41.  
  42. // Re-define 'ytInitialPlayerResponse' to inspect and modify the initial player response as soon as the variable is set on page load
  43. nativeDefineProperty(window, "ytInitialPlayerResponse", {
  44. set: function(playerResponse) {
  45.  
  46. // prevent recursive setter calls by ignoring unchanged data (this fixes a problem caused by brave browser shield)
  47. if(playerResponse === wrappedPlayerResponse) return;
  48.  
  49. wrappedPlayerResponse = inspectJsonData(playerResponse);
  50. if(typeof chainedSetter === "function") chainedSetter(wrappedPlayerResponse);
  51. },
  52. get: function() {
  53. if(typeof chainedGetter === "function") try { return chainedGetter() } catch(err) { };
  54. return wrappedPlayerResponse || {};
  55. },
  56. configurable: true
  57. });
  58.  
  59. // Intercept, inspect and modify JSON-based communication to unlock player responses by hijacking the JSON.parse function
  60. window.JSON.parse = function(text, reviver) {
  61. return inspectJsonData(nativeParse(text, reviver));
  62. }
  63.  
  64. function inspectJsonData(parsedData) {
  65. try {
  66. // Unlock #1: Array based in "&pbj=1" AJAX response on any navigation
  67. if(Array.isArray(parsedData)) {
  68. var playerResponseArrayItem = parsedData.find(e => typeof e.playerResponse === "object");
  69. var playerResponse = playerResponseArrayItem ? playerResponseArrayItem.playerResponse : null;
  70.  
  71. if(playerResponse && isUnlockable(playerResponse.playabilityStatus)) {
  72. playerResponseArrayItem.playerResponse = unlockPlayerResponse(playerResponse);
  73. }
  74. }
  75.  
  76. // Unlock #2: Another JSON-Object containing the 'playerResponse'
  77. if(parsedData.playerResponse && parsedData.playerResponse.playabilityStatus && parsedData.playerResponse.videoDetails && isUnlockable(parsedData.playerResponse.playabilityStatus)) {
  78. parsedData.playerResponse = unlockPlayerResponse(parsedData.playerResponse);
  79. }
  80.  
  81. // Unlock #3: Initial page data structure and raw player response
  82. if(parsedData.playabilityStatus && parsedData.videoDetails && isUnlockable(parsedData.playabilityStatus)) {
  83. parsedData = unlockPlayerResponse(parsedData);
  84. }
  85.  
  86. } catch(err) {
  87. console.error("Simple-YouTube-Age-Restriction-Bypass-Error:", err);
  88. }
  89.  
  90. return parsedData;
  91. }
  92.  
  93. function isUnlockable(playabilityStatus) {
  94. if(!playabilityStatus || !playabilityStatus.status) return false;
  95. return unlockablePlayerStates.includes(playabilityStatus.status);
  96. }
  97.  
  98. function unlockPlayerResponse(playerResponse) {
  99. var videoId = playerResponse.videoDetails.videoId;
  100. var unlockedPayerResponse = getUnlockedPlayerResponse(videoId);
  101.  
  102. // check if the unlocked response isn't playable
  103. if(unlockedPayerResponse.playabilityStatus.status !== "OK")
  104. throw ("Simple-YouTube-Age-Restriction-Bypass: Unlock Failed, playabilityStatus: " + unlockedPayerResponse.playabilityStatus.status);
  105.  
  106. return unlockedPayerResponse;
  107. }
  108.  
  109. function getUnlockedPlayerResponse(videoId) {
  110.  
  111. // Check if is cached
  112. if(responseCache.videoId === videoId) return responseCache.content;
  113.  
  114. // Query YT's unrestricted api endpoint
  115. var xmlhttp = new XMLHttpRequest();
  116. xmlhttp.open("GET", "/get_video_info?html5=1&video_id=" + encodeURIComponent(videoId) + "&eurl=https%3A%2F%2Fyoutube.googleapis.com%2Fv%2F" + encodeURIComponent(videoId) + "&c=TVHTML5&cver=7.20190319", false); // Synchronous!!!
  117. xmlhttp.send(null);
  118. var playerResponse = nativeParse(new URLSearchParams(xmlhttp.responseText).get("player_response"));
  119.  
  120. // Cache response for 10 seconds
  121. responseCache = { videoId: videoId, content: playerResponse };
  122. setTimeout(function() { responseCache = {} }, 10000);
  123.  
  124. return playerResponse;
  125. }
  126.  
  127. // Some extensions like AdBlock override the Object.defineProperty function to prevent a re-definition of the 'ytInitialPlayerResponse' variable by YouTube.
  128. // But we need to define a custom descriptor to that variable to intercept his value. This behavior causes a race condition depending on the execution order with this script :(
  129. // This function tries to restore the native Object.defineProperty function...
  130. function getNativeDefineProperty() {
  131.  
  132. // Check if the Object.defineProperty function is native (original)
  133. if(window.Object.defineProperty && window.Object.defineProperty.toString().indexOf("[native code]") > -1) {
  134. return window.Object.defineProperty;
  135. }
  136.  
  137. // if the Object.defineProperty function is already overidden, try to restore the native function from another window...
  138. try {
  139. if(!document.body) document.body = document.createElement("body");
  140.  
  141. var tempFrame = document.createElement("iframe");
  142. tempFrame.style.display = "none";
  143.  
  144. document.body.insertAdjacentElement("beforeend", tempFrame);
  145. var nativeDefineProperty = tempFrame.contentWindow.Object.defineProperty;
  146. tempFrame.remove();
  147.  
  148. console.info("Simple-YouTube-Age-Restriction-Bypass: Overidden Object.defineProperty function successfully restored!");
  149.  
  150. return nativeDefineProperty;
  151. } catch(err) {
  152. console.warn("Simple-YouTube-Age-Restriction-Bypass: Unable to restore the original Object.defineProperty function", err);
  153. return window.Object.defineProperty;
  154. }
  155. }
  156.  
  157. })();