Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name YTBetter
- // @namespace YTBetter
- // @match https://*.youtube.com/*
- // @run-at document-start
- // @grant none
- // @version 1.3
- // @author hop-step-pokosan
- // @description Patches YouTube to bypass some limitations
- // ==/UserScript==
- (() => {
- const _DEBUG = false;
- const debug = (...msg) => {
- if (_DEBUG) {
- console.log("[YTBetter]", ...msg);
- }
- }
- const PatchPlayerResponse = (playerResponse) => {
- try {
- // Patch to allow DVR to work on all streams
- if (playerResponse.videoDetails) {
- playerResponse.videoDetails.isLiveDvrEnabled = true;
- }
- } catch (err) {
- debug("Failed to patch playerResponse", err);
- }
- };
- const GetPlayerResponse = (videoInfo) => {
- return videoInfo.raw_player_response || videoInfo.embedded_player_response || videoInfo.player_response;
- };
- const TrapLoadVideoByPlayerVars = (value) => new Proxy(value, {
- apply: (target, thisArg, argumentsList) => {
- (() => {
- if (argumentsList.length !== 5) {
- return;
- }
- let videoInfo = argumentsList[0];
- if (typeof videoInfo === "undefined") {
- return;
- }
- let playerResponse = GetPlayerResponse(videoInfo);
- if (typeof playerResponse === "undefined") {
- return;
- }
- if (typeof playerResponse === "string") {
- playerResponse = JSON.parse(playerResponse);
- delete videoInfo.player_response;
- delete videoInfo.embedded_player_response;
- }
- PatchPlayerResponse(playerResponse);
- })();
- debug("TrapLoadVideoByPlayerVars", thisArg, argumentsList);
- return Reflect.apply(target, thisArg, argumentsList);
- },
- });
- const TrapConstructorPrototype = (value) => new Proxy(value, {
- defineProperty: (target, property, descriptor) => {
- (() => {
- if (property !== "loadVideoByPlayerVars") {
- return;
- }
- descriptor.value = TrapLoadVideoByPlayerVars(descriptor.value);
- })();
- return Reflect.defineProperty(target, property, descriptor);
- },
- });
- const TrapConstructorCreate = (value) => new Proxy(value, {
- apply: (target, thisArg, argumentsList) => {
- debug("TrapConstructorCreate", thisArg, argumentsList);
- (() => {
- if (argumentsList.length !== 3) {
- return;
- }
- let videoInfo = argumentsList[1]?.args;
- if (typeof videoInfo === "undefined") {
- return;
- }
- let playerResponse = GetPlayerResponse(videoInfo);
- if (typeof playerResponse === "undefined") {
- return;
- }
- if (typeof playerResponse === "string") {
- playerResponse = JSON.parse(playerResponse);
- delete videoInfo.player_response;
- delete videoInfo.embedded_player_response;
- }
- PatchPlayerResponse(playerResponse);
- })();
- return Reflect.apply(target, thisArg, argumentsList);
- },
- });
- const TrapVideoConstructor = (value) => new Proxy(value, {
- defineProperty: (target, property, descriptor) => {
- (() => {
- switch (property) {
- case "prototype":
- descriptor.value = TrapConstructorPrototype(descriptor.value);
- case "create":
- descriptor.value = TrapConstructorCreate(descriptor.value);
- default:
- return;
- }
- descriptor.value = TrapConstructorPrototype(descriptor.value);
- })();
- return Reflect.defineProperty(target, property, descriptor);
- },
- });
- const TrapUpdateVideoInfo = (value) => new Proxy(value, {
- apply: (target, thisArg, argumentsList) => {
- (() => {
- if (argumentsList.length !== 3) {
- return;
- }
- let videoInfo = argumentsList[1];
- if (typeof videoInfo === "undefined") {
- return;
- }
- let playerResponse = GetPlayerResponse(videoInfo);
- if (typeof playerResponse === "undefined") {
- return;
- }
- if (typeof playerResponse === "string") {
- playerResponse = JSON.parse(playerResponse);
- delete videoInfo.player_response;
- delete videoInfo.embedded_player_response;
- }
- PatchPlayerResponse(playerResponse);
- })();
- debug("TrapUpdateVideoInfo", thisArg, argumentsList);
- return Reflect.apply(target, thisArg, argumentsList);
- },
- });
- const TrapYTPlayer = (value) => {
- const VideoConstructorFuncRegex = /this.webPlayerContextConfig=/;
- const UpdateVideoInfoRegex = /a.errorCode=null/;
- let FoundVideoConstructor = false;
- let FoundUpdateVideoInfo = false;
- return new Proxy(value, {
- defineProperty: (target, property, descriptor) => {
- (() => {
- if (typeof descriptor.value !== "function") {
- return;
- }
- if (!FoundUpdateVideoInfo) {
- if (UpdateVideoInfoRegex.test(descriptor.value.toString())) {
- // UpdateVideoInfo is used for embeded videos, we need to trap
- // it to enable DVR on embeds.
- debug("Found UpdateVideoInfo func", property, descriptor.value);
- descriptor.value = TrapUpdateVideoInfo(descriptor.value);
- FoundUpdateVideoInfo = true;
- return;
- }
- }
- if (!FoundVideoConstructor) {
- if (VideoConstructorFuncRegex.test(descriptor.value.toString())) {
- // VideoConstructor func is the constructor for videos,
- // we use it to patch some data when new videos are loaded.
- debug("Found VideoConstructor func", property, descriptor.value);
- descriptor.value = TrapVideoConstructor(descriptor.value);
- FoundVideoConstructor = true;
- return;
- }
- }
- })();
- return Reflect.defineProperty(target, property, descriptor);
- },
- });
- }
- debug("Script start");
- Object.defineProperty(window, "_yt_player", {
- value: TrapYTPlayer({}),
- });
- })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement