Advertisement
Guest User

YTBetter

a guest
Apr 20th, 2024
22
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.16 KB | None | 0 0
  1. // ==UserScript==
  2. // @name YTBetter
  3. // @namespace YTBetter
  4. // @match https://*.youtube.com/*
  5. // @run-at document-start
  6. // @grant none
  7. // @version 1.3
  8. // @author hop-step-pokosan
  9. // @description Patches YouTube to bypass some limitations
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (() => {
  14. const _DEBUG = false;
  15. const debug = (...msg) => {
  16. if (_DEBUG) {
  17. console.log("[YTBetter]", ...msg);
  18. }
  19. }
  20.  
  21. const PatchPlayerResponse = (playerResponse) => {
  22. try {
  23. // Patch to allow DVR to work on all streams
  24. if (playerResponse.videoDetails) {
  25. playerResponse.videoDetails.isLiveDvrEnabled = true;
  26. }
  27. } catch (err) {
  28. debug("Failed to patch playerResponse", err);
  29. }
  30. };
  31.  
  32. const GetPlayerResponse = (videoInfo) => {
  33. return videoInfo.raw_player_response || videoInfo.embedded_player_response || videoInfo.player_response;
  34. };
  35.  
  36. const TrapLoadVideoByPlayerVars = (value) => new Proxy(value, {
  37. apply: (target, thisArg, argumentsList) => {
  38. (() => {
  39. if (argumentsList.length !== 5) {
  40. return;
  41. }
  42.  
  43. let videoInfo = argumentsList[0];
  44. if (typeof videoInfo === "undefined") {
  45. return;
  46. }
  47.  
  48. let playerResponse = GetPlayerResponse(videoInfo);
  49. if (typeof playerResponse === "undefined") {
  50. return;
  51. }
  52.  
  53. if (typeof playerResponse === "string") {
  54. playerResponse = JSON.parse(playerResponse);
  55. delete videoInfo.player_response;
  56. delete videoInfo.embedded_player_response;
  57. }
  58.  
  59. PatchPlayerResponse(playerResponse);
  60. })();
  61. debug("TrapLoadVideoByPlayerVars", thisArg, argumentsList);
  62. return Reflect.apply(target, thisArg, argumentsList);
  63. },
  64. });
  65.  
  66. const TrapConstructorPrototype = (value) => new Proxy(value, {
  67. defineProperty: (target, property, descriptor) => {
  68.  
  69. (() => {
  70. if (property !== "loadVideoByPlayerVars") {
  71. return;
  72. }
  73.  
  74. descriptor.value = TrapLoadVideoByPlayerVars(descriptor.value);
  75. })();
  76. return Reflect.defineProperty(target, property, descriptor);
  77. },
  78. });
  79.  
  80. const TrapConstructorCreate = (value) => new Proxy(value, {
  81. apply: (target, thisArg, argumentsList) => {
  82. debug("TrapConstructorCreate", thisArg, argumentsList);
  83. (() => {
  84. if (argumentsList.length !== 3) {
  85. return;
  86. }
  87.  
  88. let videoInfo = argumentsList[1]?.args;
  89. if (typeof videoInfo === "undefined") {
  90. return;
  91. }
  92.  
  93. let playerResponse = GetPlayerResponse(videoInfo);
  94. if (typeof playerResponse === "undefined") {
  95. return;
  96. }
  97.  
  98. if (typeof playerResponse === "string") {
  99. playerResponse = JSON.parse(playerResponse);
  100. delete videoInfo.player_response;
  101. delete videoInfo.embedded_player_response;
  102. }
  103.  
  104. PatchPlayerResponse(playerResponse);
  105. })();
  106. return Reflect.apply(target, thisArg, argumentsList);
  107. },
  108. });
  109.  
  110. const TrapVideoConstructor = (value) => new Proxy(value, {
  111. defineProperty: (target, property, descriptor) => {
  112. (() => {
  113. switch (property) {
  114. case "prototype":
  115. descriptor.value = TrapConstructorPrototype(descriptor.value);
  116. case "create":
  117. descriptor.value = TrapConstructorCreate(descriptor.value);
  118. default:
  119. return;
  120. }
  121.  
  122. descriptor.value = TrapConstructorPrototype(descriptor.value);
  123. })();
  124. return Reflect.defineProperty(target, property, descriptor);
  125. },
  126. });
  127.  
  128. const TrapUpdateVideoInfo = (value) => new Proxy(value, {
  129. apply: (target, thisArg, argumentsList) => {
  130. (() => {
  131. if (argumentsList.length !== 3) {
  132. return;
  133. }
  134.  
  135. let videoInfo = argumentsList[1];
  136. if (typeof videoInfo === "undefined") {
  137. return;
  138. }
  139.  
  140. let playerResponse = GetPlayerResponse(videoInfo);
  141. if (typeof playerResponse === "undefined") {
  142. return;
  143. }
  144.  
  145. if (typeof playerResponse === "string") {
  146. playerResponse = JSON.parse(playerResponse);
  147. delete videoInfo.player_response;
  148. delete videoInfo.embedded_player_response;
  149. }
  150.  
  151. PatchPlayerResponse(playerResponse);
  152. })();
  153. debug("TrapUpdateVideoInfo", thisArg, argumentsList);
  154. return Reflect.apply(target, thisArg, argumentsList);
  155. },
  156. });
  157.  
  158. const TrapYTPlayer = (value) => {
  159. const VideoConstructorFuncRegex = /this.webPlayerContextConfig=/;
  160. const UpdateVideoInfoRegex = /a.errorCode=null/;
  161.  
  162. let FoundVideoConstructor = false;
  163. let FoundUpdateVideoInfo = false;
  164.  
  165. return new Proxy(value, {
  166. defineProperty: (target, property, descriptor) => {
  167. (() => {
  168. if (typeof descriptor.value !== "function") {
  169. return;
  170. }
  171.  
  172. if (!FoundUpdateVideoInfo) {
  173. if (UpdateVideoInfoRegex.test(descriptor.value.toString())) {
  174. // UpdateVideoInfo is used for embeded videos, we need to trap
  175. // it to enable DVR on embeds.
  176. debug("Found UpdateVideoInfo func", property, descriptor.value);
  177. descriptor.value = TrapUpdateVideoInfo(descriptor.value);
  178. FoundUpdateVideoInfo = true;
  179. return;
  180. }
  181. }
  182.  
  183. if (!FoundVideoConstructor) {
  184. if (VideoConstructorFuncRegex.test(descriptor.value.toString())) {
  185. // VideoConstructor func is the constructor for videos,
  186. // we use it to patch some data when new videos are loaded.
  187. debug("Found VideoConstructor func", property, descriptor.value);
  188. descriptor.value = TrapVideoConstructor(descriptor.value);
  189. FoundVideoConstructor = true;
  190. return;
  191. }
  192. }
  193. })();
  194.  
  195. return Reflect.defineProperty(target, property, descriptor);
  196. },
  197. });
  198. }
  199.  
  200. debug("Script start");
  201.  
  202. Object.defineProperty(window, "_yt_player", {
  203. value: TrapYTPlayer({}),
  204. });
  205. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement