Advertisement
Guest User

YTBetter v1.3

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