Greasy Fork 还支持 简体中文。

Netflix Plus

Enable best audio and video and more features on Netflix

Versión del día 8/7/2024. Echa un vistazo a la versión más reciente.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name                 Netflix Plus
// @name:ja              Netflix Plus
// @name:zh-CN           Netflix Plus
// @name:zh-TW           Netflix Plus
// @namespace            http://tampermonkey.net/
// @version              2.1
// @description          Enable best audio and video and more features on Netflix
// @description:ja       Netflixで最高の音質と画質、そしてもっと多くの機能を体験しましょう
// @description:zh-CN    在 Netflix 上开启最佳音视频质量和更多功能
// @description:zh-TW    在 Netflix 上啓用最佳影音品質和更多功能
// @author               TGSAN
// @match                https://www.netflix.com/*
// @icon                 https://www.google.com/s2/favicons?sz=64&domain=netflix.com
// @run-at               document-start
// @grant                unsafeWindow
// @grant                GM_setValue
// @grant                GM_getValue
// @grant                GM_registerMenuCommand
// @grant                GM_unregisterMenuCommand
// ==/UserScript==

(async () => {
    "use strict";

    let windowCtx = self.window;
    if (self.unsafeWindow) {
        console.log("[Netflix Plus] use unsafeWindow mode");
        windowCtx = self.unsafeWindow;
    } else {
        console.log("[Netflix Plus] use window mode (your userscript extensions not support unsafeWindow)");
    }

    // Register Netflix Plus Functions

    windowCtx._videoElementNetflixPlus;
    Object.defineProperty(windowCtx, "videoElementNetflixPlus", {
        get: function () { return windowCtx._videoElementNetflixPlus; },
        set: function (element) {
            let backup = windowCtx._videoElementNetflixPlus;
            windowCtx._videoElementNetflixPlus = element;
            element.addEventListener('playing', function () {
                if (backup === element) {
                    return;
                }

                if (!windowCtx.globalOptions.setMaxBitrate) {
                    return;
                }

                let getElementByXPath = function (xpath) {
                    return document.evaluate(
                        xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
                    ).singleNodeValue;
                };

                let selectFun = function () {
                    windowCtx.dispatchEvent(new KeyboardEvent('keydown', {
                        keyCode: 83, // S (Old)
                        ctrlKey: true,
                        altKey: true,
                        shiftKey: true,
                    }));

                    windowCtx.dispatchEvent(new KeyboardEvent('keydown', {
                        keyCode: 66, // B
                        ctrlKey: true,
                        altKey: true,
                        shiftKey: true,
                    }));

                    const VIDEO_SELECT = getElementByXPath("//div[text()='Video Bitrate / VMAF']");
                    const AUDIO_SELECT = getElementByXPath("//div[text()='Audio Bitrate']");
                    const BUTTON = getElementByXPath("//button[text()='Override']");
                    if (VIDEO_SELECT && AUDIO_SELECT && BUTTON) {
                        [VIDEO_SELECT, AUDIO_SELECT].forEach(function (el) {
                            let parent = el.parentElement;

                            let selects = parent.querySelectorAll('select');

                            selects.forEach(function (select) {
                                select.removeAttribute("disabled");
                            });

                            let options = parent.querySelectorAll('select > option');

                            for (var i = 0; i < options.length - 1; i++) {
                                options[i].removeAttribute('selected');
                            }

                            options[options.length - 1].setAttribute('selected', 'selected');
                        });

                        BUTTON.click();

                        backup = element;
                    } else {
                        setTimeout(selectFun, 100);
                    }
                }
                selectFun();
            });
        }
    });

    windowCtx.modifyFilterNetflixPlus = function (ModList, ModConfig, DRMType) {
        let DrmVersion = "playready" === DRMType ? 30 : 0;
        if (windowCtx.globalOptions.useprk) {
            ModList.push("h264mpl30-dash-playready-prk-qc");
            ModList.push("h264mpl31-dash-playready-prk-qc");
            ModList.push("h264mpl40-dash-playready-prk-qc");
        }
        if (DrmVersion == 30) {
            if (windowCtx.globalOptions.useddplus) {
                ModList.push("ddplus-2.0-dash");
                ModList.push("ddplus-5.1-dash");
                ModList.push("ddplus-5.1hq-dash");
                ModList.push("ddplus-atmos-dash");
                // ModList = ModList.filter(item => { if (!new RegExp(/heaac/g).test(JSON.stringify(item))) return item; });
            }
            if (windowCtx.globalOptions.usehevc) {
                ModList = ModList.filter(item => { if (!new RegExp(/main10-L5/g).test(JSON.stringify(item))) return item; });
            }
            if (windowCtx.globalOptions.usef12k) {
                ModList = ModList.filter(item => { if (!new RegExp(/hevc-main10-L.*-dash-cenc-prk-do/g).test(JSON.stringify(item))) return item; });
            }
            if (windowCtx.globalOptions.usef4k) {
                ModList.push("hevc-main10-L30-dash-cenc");
                ModList.push("hevc-main10-L31-dash-cenc");
                ModList.push("hevc-main10-L40-dash-cenc");
                ModList.push("hevc-main10-L41-dash-cenc");
            }
        } else {
            if (windowCtx.globalOptions.useFHD) {
                ModList.push("playready-h264mpl40-dash");
                ModList.push("playready-h264hpl40-dash");
                ModList.push("vp9-profile0-L40-dash-cenc");
                ModList.push("av1-main-L50-dash-cbcs-prk");
                ModList.push("av1-main-L51-dash-cbcs-prk");
            }
            if (windowCtx.globalOptions.useHA) {
                ModList.push("heaac-5.1-dash");
            }
            if (!windowCtx.globalOptions.usedef) {
                if (windowCtx.globalOptions.useav1) {
                    ModList.push("av1-main-L20-dash-cbcs-prk");
                    ModList.push("av1-main-L21-dash-cbcs-prk");
                    ModList = ModList.filter(item => { if (!new RegExp(/h264/g).test(JSON.stringify(item))) return item; });
                    ModList = ModList.filter(item => { if (!new RegExp(/vp9-profile/g).test(JSON.stringify(item))) return item; });
                }
                if (windowCtx.globalOptions.usevp9) {
                    ModList.push("vp9-profile0-L21-dash-cenc");
                    ModList = ModList.filter(item => { if (!new RegExp(/h264/g).test(JSON.stringify(item))) return item; });
                    ModList = ModList.filter(item => { if (!new RegExp(/av1-main/g).test(JSON.stringify(item))) return item; });
                }
                if (windowCtx.globalOptions.useAVCH) {
                    ModList = ModList.filter(item => { if (!new RegExp(/vp9-profile/g).test(JSON.stringify(item))) return item; });
                    ModList = ModList.filter(item => { if (!new RegExp(/h264mp/g).test(JSON.stringify(item))) return item; });
                    ModList = ModList.filter(item => { if (!new RegExp(/av1-main/g).test(JSON.stringify(item))) return item; });
                }
                if (windowCtx.globalOptions.useAVC) {
                    ModList = ModList.filter(item => { if (!new RegExp(/vp9-profile/g).test(JSON.stringify(item))) return item; });
                    ModList = ModList.filter(item => { if (!new RegExp(/h264hp/g).test(JSON.stringify(item))) return item; });
                    ModList = ModList.filter(item => { if (!new RegExp(/av1-main/g).test(JSON.stringify(item))) return item; });
                }
            }
        }
        if (windowCtx.globalOptions.useallSub) {
            ModConfig.showAllSubDubTracks = 1
        }
        if (windowCtx.globalOptions.closeimsc) {
            ModList = ModList.filter(item => { if (!new RegExp(/imsc1.1/g).test(JSON.stringify(item))) return item; });
        }
        return [ModList, ModConfig, DRMType];
    };

    // Main Logic

    const Event = class {
        constructor(script, target) {
            this.script = script;
            this.target = target;

            this._cancel = false;
            this._replace = null;
            this._stop = false;
        }

        preventDefault() {
            this._cancel = true;
        }
        stopPropagation() {
            this._stop = true;
        }
        replacePayload(payload) {
            this._replace = payload;
        }
    };

    let callbacks = [];
    windowCtx.addBeforeScriptExecuteListener = (f) => {
        if (typeof f !== "function") {
            throw new Error("Event handler must be a function.");
        }
        callbacks.push(f);
    };
    windowCtx.removeBeforeScriptExecuteListener = (f) => {
        let i = callbacks.length;
        while (i--) {
            if (callbacks[i] === f) {
                callbacks.splice(i, 1);
            }
        }
    };

    const dispatch = (script, target) => {
        if (script.tagName !== "SCRIPT") {
            return;
        }

        const e = new Event(script, target);

        if (typeof windowCtx.onbeforescriptexecute === "function") {
            try {
                windowCtx.onbeforescriptexecute(e);
            } catch (err) {
                console.error(err);
            }
        }

        for (const func of callbacks) {
            if (e._stop) {
                break;
            }
            try {
                func(e);
            } catch (err) {
                console.error(err);
            }
        }

        if (e._cancel) {
            script.textContent = "";
            script.remove();
        } else if (typeof e._replace === "string") {
            script.textContent = e._replace;
        }
    };
    const observer = new MutationObserver((mutations) => {
        for (const m of mutations) {
            for (const n of m.addedNodes) {
                dispatch(n, m.target);
            }
        }
    });
    observer.observe(document, {
        childList: true,
        subtree: true,
    });

    const menuItems = [
        ["setMaxBitrate", "Automatically select best bitrate available"],
        ["useallSub", "Show all audio-tracks and subs"],
        ["closeimsc", "Use SUP subtitle replace IMSC subtitle"],
        ["useDDPandHA", "Enable Dolby and HE-AAC 5.1 Audio"],
        ["useFHDAndAVCH", "Focus 1080P and High-AVC"],
    ];
    let menuCommandList = [];

    windowCtx.globalOptions = {
        useFHDAndAVCH: !checkAdvancedDrm(),
        useDDPandHA: true,
        setMaxBitrate: true,
        useallSub: true,
        get ["useddplus"]() {
            return windowCtx.globalOptions.useDDPandHA;
        },
        useAVC: false,
        get ["useFHD"]() {
            return windowCtx.globalOptions.useFHDAndAVCH;
        },
        usedef: false,
        get ["useHA"]() {
            return windowCtx.globalOptions.useDDPandHA;
        },
        get ["useAVCH"]() {
            return windowCtx.globalOptions.useFHDAndAVCH;
        },
        usevp9: false,
        useav1: false,
        useprk: true,
        usehevc: false,
        usef4k: true,
        usef12k: false,
        closeimsc: true
    };

    windowCtx.onbeforescriptexecute = function (e) {
        let scripts = document.getElementsByTagName("script");
        if (scripts.length === 0) return;
        for (let i = 0; scripts.length > i; i++) {
            let dom = scripts[i];
            if (dom.src.includes("cadmium-playercore")) {
                let playercore = document.createElement('script');
                playercore.src = "https://static-os.kumo.moe/js/netflix/cadmium-playercore.js";
                playercore.crossOrigin = dom.crossOrigin;
                playercore.async = dom.async;
                playercore.id = dom.id;
                document.head.appendChild(playercore);
                dom.remove();
                windowCtx.onbeforescriptexecute = null;
                break;
            }
        }
    };

    async function checkAdvancedDrm() {
        let supported = false;
        if (windowCtx.MSMediaKeys) {
            supported = true;
        }
        if (windowCtx.WebKitMediaKeys) {
            supported = true;
        }
        // Check L1
        let options = [
            {
                "videoCapabilities": [
                    {
                        "contentType": "video/mp4;codecs=avc1.42E01E",
                        "robustness": "HW_SECURE_ALL"
                    }
                ]
            }
        ];

        try {
            await navigator.requestMediaKeySystemAccess("com.widevine.alpha.experiment", options);
            supported = true;
        } catch { }
        console.log("Supported advanced DRM: " + supported);
        return supported;
    }

    function checkSelected(type) {
        let selected = GM_getValue("NETFLIX_PLUS_" + type);
        if (typeof selected == "boolean") {
            return selected;
        } else {
            return windowCtx.globalOptions[type];
        }
    }

    function registerSelectableVideoProcessingMenuCommand(name, type) {
        let selected = checkSelected(type);
        windowCtx.globalOptions[type] = selected;
        return GM_registerMenuCommand((checkSelected(type) ? "✅" : "🔲") + " " + name, function () {
            GM_setValue("NETFLIX_PLUS_" + type, !selected);
            windowCtx.globalOptions[type] = !selected;
            updateMenuCommand();
        });
    }

    async function updateMenuCommand() {
        for (let command of menuCommandList) {
            await GM_unregisterMenuCommand(command);
        }
        menuCommandList = [];
        for (let menuItem of menuItems) {
            menuCommandList.push(await registerSelectableVideoProcessingMenuCommand(menuItem[1], menuItem[0]));
        }
    }

    updateMenuCommand();
})();