YTBetter - Enable Rewind/DVR

Unlocks rewind for YouTube live streams with disabled DVR and longer than 12 hours

安裝腳本?
作者推薦腳本

您可能也會喜歡 YouTube All Videos Playlists (YAVP)

安裝腳本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         YTBetter - Enable Rewind/DVR
// @namespace    YTBetter
// @version      3.0
// @description  Unlocks rewind for YouTube live streams with disabled DVR and longer than 12 hours
// @author       copyMister
// @match        https://www.youtube.com/*
// @match        https://m.youtube.com/*
// @run-at       document-start
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        none
// @license      MIT
// ==/UserScript==

"use strict";

// Interop with "Simple YouTube Age Restriction Bypass"
const {
    get: getter,
    set: setter,
} = Object.getOwnPropertyDescriptor(Object.prototype, "playerResponse") ?? {
    set(value) {
        this[Symbol.for("YTBetter")] = value;
    },
    get() {
        return this[Symbol.for("YTBetter")];
    },
};

const isObject = (value) => value != null && typeof value === "object";
const getKeyByPropName = (object, value) => Object.keys(object).find(key => object[key] && object[key][value]);

Object.defineProperty(Object.prototype, "playerResponse", {
    set(value) {
        if (isObject(value)) {
            const { streamingData, videoDetails, playerConfig, microformat } = value;
            // Only affect live streams
            if (isObject(videoDetails) && videoDetails.isLive) {
                // Enable DVR if it's disabled
                videoDetails.isLiveDvrEnabled = true;

                // Disable server-side ads that block live rewind
                // https://greasyfork.org/en/scripts/485020-ytbetter-enable-rewind-dvr/discussions/295167
                if (isObject(playerConfig) && playerConfig.mediaCommonConfig) {
                    const mcConf = playerConfig.mediaCommonConfig;
                    mcConf.useServerDrivenAbr = false;
                    // https://greasyfork.org/en/scripts/485020-ytbetter-enable-rewind-dvr/discussions/313024
                    if (mcConf.serverPlaybackStartConfig) {
                        mcConf.serverPlaybackStartConfig.enable = false;
                    }
                }

                if (isObject(streamingData)) {
                    // Remove the stream URL with server-side ads if others are available
                    if (streamingData.serverAbrStreamingUrl && (streamingData.hlsManifestUrl || streamingData.dashManifestUrl)) {
                        delete streamingData.serverAbrStreamingUrl;
                    }

                    // Unlock rewind past 12 hours (43200 seconds), up to 7 days
                    // https://greasyfork.org/en/scripts/485020-ytbetter-enable-rewind-dvr/discussions/312558
                    const maxDefault = 43200;
                    const maxDvrSecs = maxDefault * 14;
                    let durationSecs = 0;

                    const s1 = 'playerMicroformatRenderer';
                    const s2 = 'liveBroadcastDetails';
                    const s3 = 'html5_max_live_dvr_window_plus_margin_secs';

                    if (isObject(microformat) && isObject(microformat[s1]) && isObject(microformat[s1][s2])) {
                        const nowSecs = new Date();
                        const startSecs = new Date(microformat[s1][s2].startTimestamp);
                        durationSecs = Math.floor((nowSecs - startSecs) / 1000);
                    }

                    // Proceed if the stream is longer than 12 hours
                    if (durationSecs > maxDefault) {
                        if (isObject(streamingData.adaptiveFormats)) {
                            for (const format of streamingData.adaptiveFormats) {
                                format.maxDvrDurationSec = maxDvrSecs;
                            }
                        }

                        const key = getKeyByPropName(this, 'experiments');
                        if (key) {
                            this[key].experiments.flags[s3] = maxDvrSecs;
                        }
                    }
                }
            }
        }
        setter.call(this, value);
    },
    get() {
        return getter.call(this);
    },
    configurable: true,
});