diff --git a/.gitignore b/.gitignore index 4df9adb..c4c4ffc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ *.zip -_metadata diff --git a/README.md b/README.md index 4522800..366b1b5 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,3 @@ Contact me on discord: shirt#1337 # Based on the following extensions - https://github.com/DavidBuchanan314/Turbo-Recadmiumator - https://github.com/truedread/netflix-1080p -- https://github.com/lkmvip/netflix-4K-DDplus diff --git a/background.js b/background.js new file mode 100644 index 0000000..2b8ea29 --- /dev/null +++ b/background.js @@ -0,0 +1,51 @@ +/* eslint-disable no-undef */ +// https://stackoverflow.com/a/45985333 +function getBrowser() { + if (typeof chrome !== "undefined") { + if (typeof browser !== "undefined") { + return "Firefox"; + } else { + return "Chrome"; + } + } else { + return "Edge"; + } +} + +chrome.webRequest.onBeforeRequest.addListener( + function (details) { + /* Allow our shim to load an untouched copy */ + if (details.url.endsWith("?no_filter")) { + return {}; + } + + if (getBrowser() == "Chrome") { + return { redirectUrl: chrome.runtime.getURL("cadmium-playercore-shim.js") }; + } + + /* Work around funky CORS behaviour on Firefox */ + else if (getBrowser() == "Firefox") { + let filter = browser.webRequest.filterResponseData(details.requestId); + let encoder = new TextEncoder(); + filter.onstop = () => { + fetch(browser.runtime.getURL("cadmium-playercore-shim.js")). + then(response => response.text()). + then(text => { + filter.write(encoder.encode(text)); + filter.close(); + }); + }; + return {}; + } + + else { + console.error("Unsupported web browser."); + return {}; + } + }, { + urls: [ + "*://assets.nflxext.com/*/ffe/player/html/*", + "*://www.assets.nflxext.com/*/ffe/player/html/*" + ] + }, ["blocking"] +); diff --git a/cadmium-playercore-shim.js b/cadmium-playercore-shim.js index 2cfef06..ae233ea 100644 --- a/cadmium-playercore-shim.js +++ b/cadmium-playercore-shim.js @@ -2,28 +2,6 @@ // This script runs as a drop-in replacement of the original cadmium-playercore. This is not a content script. console.log("Netflix International script active!"); -if (window.globalOptions === undefined) { - try { - window.globalOptions = JSON.parse(document.getElementById("netflix-intl-settings").innerText); - } catch(e) { - console.error("Could not load settings:", e); - } -} - -/* eslint-disable no-undef */ -// https://stackoverflow.com/a/45985333 -function getBrowser() { - if (typeof chrome !== "undefined") { - if (typeof browser !== "undefined") { - return "Firefox"; - } else { - return "Chrome"; - } - } else { - return "Edge"; - } -} - function do_patch(desc, needle, replacement) { var match = cadmium_src.match(needle); if (!match) { @@ -41,73 +19,100 @@ function do_patch(desc, needle, replacement) { the response before the body of this script finishes executing */ var request = new XMLHttpRequest(); var cadmium_url = document.getElementById("player-core-js").src; -request.open("GET", cadmium_url, false); // synchronous +request.open("GET", cadmium_url + "?no_filter", false); // synchronous request.send(); var cadmium_src = request.responseText; // eslint-disable-next-line no-unused-vars -function get_profile_list(original_profiles) { - var profiles = original_profiles; +function get_profile_list() { // Always add h264 main profiles - if (original_profiles.includes("playready-h264mpl30-dash")) { - profiles = profiles.concat([ - "playready-h264mpl30-dash", - "playready-h264mpl31-dash", - "playready-h264mpl40-dash", - "h264mpl30-dash-playready-prk-qc", - "h264mpl31-dash-playready-prk-qc", - "h264mpl40-dash-playready-prk-qc", - ]); - } + var custom_profiles = [ + "playready-h264mpl30-dash", + "playready-h264mpl31-dash", + "playready-h264mpl40-dash", + ]; - if (!globalOptions.disableHPL) { - if (original_profiles.includes("playready-h264hpl30-dash")) { - profiles = profiles.concat([ - "playready-h264hpl30-dash", - "playready-h264hpl31-dash", - "playready-h264hpl40-dash", + if (window.MSMediaKeys) { + // PlayReady Specific + + // Always add 2.0 AAC profiles, some manifests fail without them + custom_profiles = custom_profiles.concat([ + "heaac-2-dash", + "heaac-2hq-dash", + ]); + + if (globalOptions.useDDPlus) { + // Dolby Digital + custom_profiles = custom_profiles.concat([ + "ddplus-2.0-dash", + ]); + + if (globalOptions.use6Channels) { + custom_profiles = custom_profiles.concat([ + "ddplus-5.1-dash", + "ddplus-5.1hq-dash", + "ddplus-atmos-dash", + ]); + } + } else { + // No Dolby Digital + if (globalOptions.use6Channels) { + custom_profiles = custom_profiles.concat([ + "heaac-5.1-dash", + ]); + } + } + + + } else { + // Widevine Specific + custom_profiles = custom_profiles.concat([ + "playready-h264hpl30-dash", + "playready-h264hpl31-dash", + "playready-h264hpl40-dash", + ]); + + if (!globalOptions.disableVP9) { + // Add VP9 Profiles if wanted + custom_profiles = custom_profiles.concat([ + "vp9-profile0-L30-dash-cenc", + "vp9-profile0-L31-dash-cenc", + "vp9-profile0-L40-dash-cenc", ]); } - if (original_profiles.includes("h264hpl30-dash-playready-live")) { - profiles = profiles.concat([ - "h264hpl30-dash-playready-live", - "h264hpl31-dash-playready-live", - "h264hpl40-dash-playready-live", + + custom_profiles = custom_profiles.concat([ + "heaac-2-dash", + "heaac-2hq-dash", + ]); + + if (globalOptions.use6Channels) { + custom_profiles = custom_profiles.concat([ + "heaac-5.1-dash", ]); } - } else { - profiles = profiles.filter(val => !val.includes("h264hpl")); } - if (!globalOptions.disableVP9 && original_profiles.includes("vp9-profile0-L30-dash-cenc")) { - profiles = profiles.concat([ - "vp9-profile0-L30-dash-cenc", - "vp9-profile0-L31-dash-cenc", - "vp9-profile0-L40-dash-cenc", - ]); - } else { - profiles = profiles.filter(val => !val.includes("vp9-")); - } + // Always add subtitles + custom_profiles = custom_profiles.concat([ + "simplesdh", + "nflx-cmisc", + "BIF240", + "BIF320", + ]); - if (!globalOptions.disableAV1 && original_profiles.includes("av1-main-L30-dash-cbcs-prk")) { - profiles = profiles.concat([ - "av1-main-L30-dash-cbcs-prk", - "av1-main-L31-dash-cbcs-prk", - "av1-main-L40-dash-cbcs-prk", - ]); - } else { - profiles = profiles.filter(val => !val.includes("av1-")); - } + return custom_profiles; +} - if (globalOptions.use6Channels) { - profiles = profiles.concat([ - "heaac-5.1-dash", - ]); - } +// eslint-disable-next-line no-unused-vars +function get_preferred_locale() { + return globalOptions.preferredLocale; +} - profiles = [...new Set(profiles)].sort(); - return profiles; +// eslint-disable-next-line no-unused-vars +function get_preferred_text_locale() { + return globalOptions.preferredTextLocale; } do_patch( @@ -118,18 +123,18 @@ do_patch( do_patch( "Custom profiles", - /(viewableId:.,profiles:)(.),/, - "$1 get_profile_list($2)," + /(viewableId:.,profiles:).,/, + "$1 get_profile_list()," ); do_patch( "Custom profiles 2", - /(name:"default",profiles:)(.)}/, - "$1 get_profile_list($2)}" + /(name:"default",profiles:).}/, + "$1 get_profile_list()}" ); do_patch( - "Re-enable Ctrl+Shift+Alt+B menu", + "Re-enable Ctrl+Shift+Alt+S menu", /this\...\....\s*&&\s*this\.toggle\(\);/, "this.toggle();" ); @@ -144,13 +149,13 @@ if (globalOptions.showAllTracks) { do_patch( "Set preferred audio locale", /preferredAudioLocale:.\.preferredAudioLocale/, - "preferredAudioLocale: globalOptions.preferredLocale" + "preferredAudioLocale: get_preferred_locale()" ); do_patch( "Set preferred text locale", /preferredTextLocale:.\.preferredTextLocale/, - "preferredTextLocale: globalOptions.preferredTextLocale" + "preferredTextLocale: get_preferred_text_locale()" ); // run our patched copy of playercore in a non-privileged context on the page diff --git a/content_script.js b/content_script.js index dae47d1..7e54d78 100644 --- a/content_script.js +++ b/content_script.js @@ -31,32 +31,43 @@ function chromeStorageGet(opts) { } } +function attachScript(resp) { + let xhr = resp.target; + let mainScript = document.createElement("script"); + mainScript.type = "application/javascript"; + if (xhr.status == 200) { + mainScript.text = xhr.responseText; + document.documentElement.appendChild(mainScript); + } +} + chromeStorageGet({ use6Channels: true, showAllTracks: true, setMaxBitrate: false, disableVP9: false, - disableAV1: true, - disableHPL: false, useDDPlus: false, preferredLocale: null, preferredTextLocale: null, }).then(items => { // very messy workaround for accessing chrome storage outside of background / content scripts let mainScript = document.createElement("script"); - mainScript.type = "application/json"; - mainScript.id = "netflix-intl-settings"; - mainScript.text = JSON.stringify(items); + mainScript.type = "application/javascript"; + mainScript.text = `var globalOptions = JSON.parse('${JSON.stringify(items)}');`; document.documentElement.appendChild(mainScript); }).then(() => { // attach and include additional scripts after we have loaded the main configuration + for (let i = 0; i < script_urls.length; i++) { + let script = document.createElement("script"); + script.src = script_urls[i]; + document.documentElement.appendChild(script); + } for (let i = 0; i < urls.length; i++) { - const mainScriptUrl = chrome.runtime.getURL(urls[i]); - - const mainScript = document.createElement('script'); - mainScript.type = 'application/javascript'; - mainScript.src = mainScriptUrl; - document.documentElement.appendChild(mainScript); + let mainScriptUrl = chrome.extension.getURL(urls[i]); + let xhr = new XMLHttpRequest(); + xhr.open("GET", mainScriptUrl, true); + xhr.onload = attachScript; + xhr.send(); } }); diff --git a/img/icon128.png b/img/icon128.png index 32c7b64..ba0355e 100644 Binary files a/img/icon128.png and b/img/icon128.png differ diff --git a/img/icon48.png b/img/icon48.png index 6705cdb..d987966 100644 Binary files a/img/icon48.png and b/img/icon48.png differ diff --git a/manifest.json b/manifest.json index ff4ef89..cacb77b 100644 --- a/manifest.json +++ b/manifest.json @@ -1,10 +1,10 @@ { - "manifest_version": 3, + "manifest_version": 2, "name": "Netflix International", "description": "Displays all available Netflix audio and subtitle tracks.", - "version": "2.0.23", + "version": "2.0.12", "author": "shirt", - "action": { + "browser_action": { "default_icon": "img/icon128.png", "default_popup": "pages/options.html" }, @@ -19,6 +19,8 @@ }, "content_scripts": [{ "matches": [ + "*://assets.nflxext.com/*/ffe/player/html/*", + "*://www.assets.nflxext.com/*/ffe/player/html/*", "*://netflix.com/*", "*://www.netflix.com/*" ], @@ -27,35 +29,24 @@ "js": ["content_script.js"], "run_at": "document_start" }], + "background": { + "scripts": ["background.js"] + }, "options_ui": { "page": "pages/options.html", "open_in_tab": true }, - "web_accessible_resources": [{ - "resources": [ - "cadmium-playercore-shim.js", - "netflix_max_bitrate.js", - "netflix.css" - ], - "matches": [ - "*://assets.nflxext.com/*", - "*://netflix.com/*", - "*://www.netflix.com/*" - ] - }], - "declarative_net_request" : { - "rule_resources" : [{ - "id": "1", - "enabled": true, - "path": "rules.json" - }] - }, + "web_accessible_resources": [ + "cadmium-playercore-shim.js", + "netflix_max_bitrate.js", + "netflix.css" + ], "permissions": [ "storage", - "declarativeNetRequestWithHostAccess" - ], - "host_permissions": [ - "*://assets.nflxext.com/*", + "webRequest", + "webRequestBlocking", + "*://assets.nflxext.com/*/ffe/player/html/*", + "*://www.assets.nflxext.com/*/ffe/player/html/*", "*://netflix.com/*", "*://www.netflix.com/*" ] diff --git a/netflix_max_bitrate.js b/netflix_max_bitrate.js index 654c4b7..048d2fb 100644 --- a/netflix_max_bitrate.js +++ b/netflix_max_bitrate.js @@ -5,33 +5,18 @@ let getElementByXPath = function (xpath) { ).singleNodeValue; }; -let fn = function () { - const VIDEO_SELECT = getElementByXPath("//div[text()='Video Bitrate']") || getElementByXPath("//div[text()='Video Bitrate / VMAF']"); - const AUDIO_SELECT = getElementByXPath("//div[text()='Audio Bitrate']"); - const BUTTON = getElementByXPath("//button[text()='Override']"); - - const videoPlayer = netflix.appContext.state.playerApp.getAPI().videoPlayer; - if(!videoPlayer) { - console.log("API Not Loading!"); - return false; - } - const player = videoPlayer.getVideoPlayerBySessionId(videoPlayer.getAllPlayerSessionIds()[0]); - if(!player) { - console.log("Video Not Loading!"); - return false; - } - if(!player.isPlaying()) { - console.log("Video Not Playing!"); - return false; - } - - window.dispatchEvent(new KeyboardEvent('keydown', { - keyCode: 66, +const fn = function () { + window.dispatchEvent(new KeyboardEvent("keydown", { + keyCode: 83, ctrlKey: true, altKey: true, shiftKey: true, })); + const VIDEO_SELECT = getElementByXPath("//div[text()='Video Bitrate']"); + const AUDIO_SELECT = getElementByXPath("//div[text()='Audio Bitrate']"); + const BUTTON = getElementByXPath("//button[text()='Override']"); + if (!(VIDEO_SELECT && AUDIO_SELECT && BUTTON)){ return false; } @@ -39,46 +24,40 @@ let fn = function () { [VIDEO_SELECT, AUDIO_SELECT].forEach(function (el) { let parent = el.parentElement; - let options = parent.querySelectorAll('select > option'); + let options = parent.querySelectorAll("select > option"); for (var i = 0; i < options.length - 1; i++) { - options[i].removeAttribute('selected'); + options[i].removeAttribute("selected"); } - options[options.length - 1].setAttribute('selected', 'selected'); + options[options.length - 1].setAttribute("selected", "selected"); }); - console.log("Video Playing!"); BUTTON.click(); return true; }; let run = function () { - fn() || setTimeout(run, 100) + if (!fn()) { + setTimeout(run, 100); + } }; const WATCH_REGEXP = /netflix.com\/watch\/.*/; let oldLocation; -if (window.globalOptions === undefined) { - try { - window.globalOptions = JSON.parse(document.getElementById("netflix-intl-settings").innerText); - } catch(e) { - console.error("Could not load settings:", e); - } -} -if(window.globalOptions.setMaxBitrate ) { +if(globalOptions.setMaxBitrate) { console.log("netflix_max_bitrate.js enabled"); - //setInterval(test, 500); setInterval(function () { - let newLocation = window.location.toString(); if (newLocation !== oldLocation) { oldLocation = newLocation; - WATCH_REGEXP.test(newLocation) && run(); + if (WATCH_REGEXP.test(newLocation)) { + run(); + } } - }, 500); -} \ No newline at end of file + }, 500); +} diff --git a/pages/options.html b/pages/options.html index ca16bd9..7d998c1 100644 --- a/pages/options.html +++ b/pages/options.html @@ -6,7 +6,6 @@ @@ -18,11 +17,9 @@

- +
- -
- +
diff --git a/pages/options.js b/pages/options.js index 386a052..55d2ad5 100644 --- a/pages/options.js +++ b/pages/options.js @@ -4,8 +4,7 @@ function save_options() { const showAllTracks = document.getElementById("showAllTracks").checked; const setMaxBitrate = document.getElementById("setMaxBitrate").checked; const disableVP9 = document.getElementById("disableVP9").checked; - const disableAV1 = document.getElementById("disableAV1").checked; - const disableHPL = document.getElementById("disableHPL").checked; + const useDDPlus = document.getElementById("useDDPlus").checked; const preferredLocale = document.getElementById("preferredLocale").value; const preferredTextLocale = document.getElementById("preferredTextLocale").value; @@ -14,8 +13,7 @@ function save_options() { showAllTracks: showAllTracks, setMaxBitrate: setMaxBitrate, disableVP9: disableVP9, - disableAV1: disableAV1, - disableHPL: disableHPL, + useDDPlus: useDDPlus, preferredLocale: preferredLocale, preferredTextLocale: preferredTextLocale, }, function() { @@ -32,8 +30,7 @@ function reset_options() { document.getElementById("showAllTracks").checked = true; document.getElementById("setMaxBitrate").checked = false; document.getElementById("disableVP9").checked = false; - document.getElementById("disableAV1").checked = true; - document.getElementById("disableHPL").checked = false; + document.getElementById("useDDPlus").checked = false; document.getElementById("preferredLocale").value = null; document.getElementById("preferredTextLocale").value = null; @@ -42,8 +39,7 @@ function reset_options() { showAllTracks: true, setMaxBitrate: false, disableVP9: false, - disableAV1: true, - disableHPL: false, + useDDPlus: false, preferredLocale: null, preferredTextLocale: null, }, function() { @@ -61,8 +57,7 @@ function restore_options() { showAllTracks: true, setMaxBitrate: false, disableVP9: false, - disableAV1: true, - disableHPL: false, + useDDPlus: false, preferredLocale: null, preferredTextLocale: null, }, function(items) { @@ -70,8 +65,7 @@ function restore_options() { document.getElementById("showAllTracks").checked = items.showAllTracks; document.getElementById("setMaxBitrate").checked = items.setMaxBitrate; document.getElementById("disableVP9").checked = items.disableVP9; - document.getElementById("disableAV1").checked = items.disableAV1; - document.getElementById("disableHPL").checked = items.disableHPL; + document.getElementById("useDDPlus").checked = items.useDDPlus; document.getElementById("preferredLocale").value = items.preferredLocale; document.getElementById("preferredTextLocale").value = items.preferredTextLocale; }); diff --git a/rules.json b/rules.json deleted file mode 100644 index a948a65..0000000 --- a/rules.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "id": 1, - "priority": 1, - "action": { - "type": "redirect", - "redirect": { "extensionPath": "/cadmium-playercore-shim.js" } - }, - "condition": { - "urlFilter": "*://assets.nflxext.com/*/ffe/player/html/*", - "resourceTypes": ["script"] - } - }, - { - "id": 2, - "priority": 1, - "action": { - "type": "redirect", - "redirect": { "extensionPath": "/cadmium-playercore-shim.js" } - }, - "condition": { - "urlFilter": "*://assets.nflxext.com/player/html/ffe/*", - "resourceTypes": ["script"] - } - } -]