Greasy Fork is available in English.
Enable best audio and video and more features on Netflix
// ==UserScript== // @name Netflix Plus // @name:ja Netflix Plus // @name:zh-CN Netflix Plus // @name:zh-TW Netflix Plus // @namespace http://tampermonkey.net/ // @version 3.4 // @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 // @sandbox raw // @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)"); } // Disable Cache { const meta = document.createElement('meta'); meta.httpEquiv = "Cache-Control"; meta.content = "no-cache"; windowCtx.document.head.appendChild(meta); } { const meta = document.createElement('meta'); meta.httpEquiv = "Pragma"; meta.content = "no-cache"; windowCtx.document.head.appendChild(meta); } { const meta = document.createElement('meta'); meta.httpEquiv = "Expires"; meta.content = "-1"; windowCtx.document.head.appendChild(meta); } function createToast() { let toast = document.createElement("div"); toast.style.position = "fixed"; toast.style.top = "20px"; toast.style.left = "50%"; toast.style.transform = "translateX(-50%)"; toast.style.padding = "10px 20px"; toast.style.backgroundColor = "rgba(250, 250, 250, 1.0)"; toast.style.color = "rgba(32, 32, 32, 1.0)"; toast.style.fontSize = "12px"; toast.style.textAlign = "center"; toast.style.fontWeight = "600"; toast.style.zIndex = "9999"; toast.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.25)"; toast.style.borderRadius = "30px"; toast.style.opacity = "0.0"; toast.style.transition = "opacity 0.5s"; document.body.appendChild(toast); return toast; } function showToast(message, time = 1500) { let toast = createToast(); toast.innerText = message; setTimeout(function () { toast.style.opacity = "1.0"; setTimeout(function () { toast.style.opacity = "0.0"; setTimeout(function () { document.body.removeChild(toast); }, 500); }, time); }, 1); } let playercoreDom = undefined; let startCaptureFunctionExec = true; windowCtx.Function.prototype.callNetflixPlusOriginal = windowCtx.Function.prototype.call; windowCtx.Function.prototype.call = function (...args) { if (startCaptureFunctionExec) { let funcStr = this.toString(); let funcLen = funcStr.length; if (funcLen > 1000000) { // find original playercore not netflix plus playercore if (funcStr.indexOf("h264mpl") > -1 && funcStr.indexOf("videoElementNetflixPlus") < 0) { console.log("PlayerCore found len: " + funcStr.length); loadCustomPlayerCore(); return undefined; } } } return this.callNetflixPlusOriginal(...args); } if (windowCtx.netflix !== undefined && windowCtx.netflix.player !== undefined) { showToast("Netflix Plus is executing too late and is being forced to run, which may cause issues.\n\nRefresh the page while holding down the \"Shift\" key to resolve the issue.", 10000); console.warn("The user script is executing too late and is being forced to run, which may cause issues."); loadCustomPlayerCore(); } function loadCustomPlayerCore() { startCaptureFunctionExec = false; if (playercoreDom == undefined) { for (let element of windowCtx.document.getElementsByTagName("script")) { if (element.src && element.src.indexOf("cadmium-playercore") > -1) { playercoreDom = element; break; } } } let playercore = document.createElement('script'); playercore.src = "https://www.cloudmoe.com/static/userscript/netflix-plus/cadmium-playercore.js"; // playercore.crossOrigin = playercoreDom.crossOrigin; playercore.async = playercoreDom.async; playercore.id = playercoreDom.id; playercoreDom.replaceWith(playercore); } // 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.setMaxBitrateOld) { return; } let getElementByXPath = function (xpath) { return document.evaluate( xpath, document, null, XPathR###lt.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'); }); setTimeout(function() { BUTTON.click(); }, 100); 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"); ModList.push("av1-hdr10plus-main-L40-dash-cbcs-prk"); ModList.push("av1-hdr10plus-main-L41-dash-cbcs-prk"); ModList.push("av1-hdr10plus-main-L50-dash-cbcs-prk"); ModList.push("av1-hdr10plus-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 = [ ["onlyMaxBitrate", "Only use best bitrate available"], // ["setMaxBitrateOld", "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"], // ["useXHA", "Focus xHE-AAC Audio"], ["alwaysUseHDR", "Always use HDR or Dolby Vision when available"], ["useFHD", "Focus 1080P"], ]; let menuCommandList = []; windowCtx.globalOptions = { useDDPandHA: true, useXHA: false, alwaysUseHDR: false, onlyMaxBitrate: true, get ["onlyVideoMaxBitrate"]() { return windowCtx.globalOptions.onlyMaxBitrate; }, get ["onlyAudioMaxBitrate"]() { return windowCtx.globalOptions.onlyMaxBitrate; }, setMaxBitrateOld: false, useallSub: true, get ["useddplus"]() { return windowCtx.globalOptions.useDDPandHA; }, useAVC: false, usedef: false, get ["useHA"]() { return windowCtx.globalOptions.useDDPandHA; }, useAVCH: true, usevp9: false, useav1: false, useprk: true, usehevc: false, usef4k: true, usef12k: false, closeimsc: true }; windowCtx.globalOptions.useFHD = !await checkAdvancedDrm(); 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")) { // firefox cannot reload src after change src url // dom.src = "https://static.cloudmoe.com/res/userscript/netflix-plus/cadmium-playercore.js"; playercoreDom = dom; console.warn("parsing playercore dom"); 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; } async function checkSelected(type) { let selected = await GM_getValue("NETFLIX_PLUS_" + type); if (typeof selected == "boolean") { return selected; } else { return windowCtx.globalOptions[type]; } } async function registerSelectableVideoProcessingMenuCommand(name, type) { let selected = await checkSelected(type); windowCtx.globalOptions[type] = selected; return await GM_registerMenuCommand((await checkSelected(type) ? "✅" : "🔲") + " " + name, async function () { await 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(); })();