├── LICENSE
├── README.md
└── src
├── background.js
├── cadmium-playercore-shim.js
└── manifest.json
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 David Buchanan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Turbo-Recadmiumator [WIP]
2 | A remake of truedread/netflix-1080p which auto-patches cadmium-playercore at runtime to enable enhanced playback features.
3 |
4 | Works in both Firefox and Chrom{e,ium}.
5 |
6 | Rather than bundling a hand-patched cadmium-playercore.js, this extension
7 | performs the patches at runtime using regex. Therefore, it should be publishable
8 | to the chrome/ff webstores without any copyright issues etc.
9 |
10 | It remains to be seen how robust my regexes will be to playercore updates.
11 |
12 | ## Current features:
13 |
14 | - Enable Ctrl+Shift+Alt+S bitrate selection window.
15 |
16 | - 1080p video.
17 |
18 | - Disable VP9 profiles.
19 |
20 | - Enable 5.1 audio profile.
21 |
22 | - Auto-select stream with max available bitrate.
23 |
24 | ## Undocumented Keyboard Shortcuts:
25 |
26 | Netflix has a bunch of undocumented keyboard shortcuts, that do useful things. This list may be incomplete.
27 |
28 | - Ctrl+Shift+Alt+B - Bitrate selection menu (re-enabled by this project) (NOTE: This used to be S, not B!)
29 |
30 | - Ctrl+Shift+Alt+D - Debug overlay - displays lots of useful info and stats, including current resolution and bitrate.
31 |
32 | - Ctrl+Shift+Alt+T - Upload custom subtitle file, in DFXP/TTML format.
33 |
34 | - Ctrl+Shift+Alt+L - Log viewer.
35 |
36 | ## TODO:
37 |
38 | - Add settings UI (right now, you have to edit the source...)
39 |
40 | - Add comments detailing where I stole the code from...
41 |
42 | ## Credits:
43 |
44 | This codebase is cobbled together with bits and pieces from [truedread/netflix-1080p](https://github.com/truedread/netflix-1080p) and its various forks. Notably:
45 |
46 | - https://github.com/vladikoff/netflix-1080p-firefox
47 |
48 | - https://github.com/TheGoddessInari/netflix-1080p-firefox
49 |
50 | - https://github.com/OothecaPickle/netflix-1080p
51 |
52 | - https://github.com/jangxx/netflix-1080p
53 |
--------------------------------------------------------------------------------
/src/background.js:
--------------------------------------------------------------------------------
1 | // https://stackoverflow.com/a/45985333
2 | function getBrowser() {
3 | if (typeof chrome !== "undefined") {
4 | if (typeof browser !== "undefined") {
5 | return "Firefox";
6 | } else {
7 | return "Chrome";
8 | }
9 | } else {
10 | return "Edge";
11 | }
12 | }
13 |
14 | chrome.webRequest.onBeforeRequest.addListener(
15 | function (details) {
16 | /* Allow our shim to load an untouched copy */
17 | if (details.url.endsWith("?no_filter")) {
18 | return {};
19 | }
20 |
21 | if (getBrowser() == "Chrome") {
22 | return {
23 | redirectUrl: chrome.extension.getURL("cadmium-playercore-shim.js")
24 | };
25 | }
26 |
27 | /* Work around funky CORS behaviour on Firefox */
28 | else if (getBrowser() == "Firefox") {
29 | let filter = browser.webRequest.filterResponseData(details.requestId);
30 | let encoder = new TextEncoder();
31 | filter.onstop = event => {
32 | fetch(browser.extension.getURL("cadmium-playercore-shim.js")).
33 | then(response => response.text()).
34 | then(text => {
35 | filter.write(encoder.encode(text));
36 | filter.close();
37 | });
38 | };
39 | return {};
40 | }
41 |
42 | else {
43 | console.error("Unsupported web browser :(");
44 | }
45 | }, {
46 | urls: [
47 | "*://assets.nflxext.com/player/html/ffe/*",
48 | "*://*.a.nflxso.net/sec/player/html/ffe/*"
49 | ]
50 | }, ["blocking"]
51 | );
52 |
--------------------------------------------------------------------------------
/src/cadmium-playercore-shim.js:
--------------------------------------------------------------------------------
1 | /* This script runs as a drop-in replacement of the original cadmium-playercore */
2 | console.log("Hello, I am running instead of playercore");
3 |
4 | var my_config = {
5 | "use_VP9": false,
6 | "use_5.1": false,
7 | "set_max_bitrate": true,
8 | }
9 |
10 | function repr(obj) {
11 | // can you tell I'm a python programmer?
12 | return JSON.stringify(obj);
13 | }
14 |
15 | function do_patch(desc, needle, replacement) {
16 | var match = cadmium_src.match(needle);
17 | if (!match) {
18 | alert("Failed to find patch: " + repr(desc));
19 | } else {
20 | cadmium_src = cadmium_src.replace(needle, replacement);
21 | console.log("[+] Patched: " + repr(desc));
22 | if (match[0].length < 200) { // avoid spamming the console
23 | console.log(repr(match[0]) + " -> " + repr(replacement));
24 | }
25 | }
26 | }
27 |
28 | /* We need to do a synchronous request because we need to eval
29 | the response before the body of this script finishes executing */
30 | var request = new XMLHttpRequest();
31 | var cadmium_url = document.getElementById("player-core-js").src;
32 | request.open("GET", cadmium_url + "?no_filter", false); // synchronous
33 | request.send(null);
34 |
35 | var cadmium_src = request.responseText;
36 |
37 | function get_profile_list() {
38 | custom_profiles = [
39 | "playready-h264mpl30-dash",
40 | "playready-h264mpl31-dash",
41 | "playready-h264mpl40-dash",
42 |
43 | "playready-h264hpl30-dash",
44 | "playready-h264hpl31-dash",
45 | "playready-h264hpl40-dash",
46 |
47 | "heaac-2-dash",
48 | "heaac-2hq-dash",
49 |
50 | "simplesdh",
51 | "nflx-cmisc",
52 | "BIF240",
53 | "BIF320"
54 | ];
55 |
56 | if (my_config["use_VP9"]) {
57 | custom_profiles = custom_profiles.concat([
58 | "vp9-profile0-L30-dash-cenc",
59 | "vp9-profile0-L31-dash-cenc",
60 | "vp9-profile0-L40-dash-cenc",
61 | ]);
62 | }
63 |
64 | if (my_config["use_5.1"]) {
65 | custom_profiles.push("heaac-5.1-dash");
66 | }
67 |
68 | return custom_profiles;
69 | }
70 |
71 | do_patch(
72 | "Hello world",
73 | /(.*)/,
74 | "console.log('Hello, I am code which has been injected into playercore!'); $1"
75 | );
76 |
77 | do_patch(
78 | "Custom profiles",
79 | /(viewableId:.,profiles:).,/,
80 | "$1 get_profile_list(),"
81 | );
82 |
83 | do_patch(
84 | "Custom profile group",
85 | /(name:"default",profiles:)./,
86 | "$1 get_profile_list()"
87 | );
88 |
89 | do_patch(
90 | "Re-enable Ctrl+Shift+Alt+S menu",
91 | /this\...\....\s*\&\&\s*this\.toggle\(\);/,
92 | "this.toggle();");
93 |
94 | // run our patched copy of playercore
95 | eval(cadmium_src);
96 |
97 |
98 |
99 | /* netflix_max_bitrate.js */
100 |
101 | function getElementByXPath(xpath) {
102 | return document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
103 | }
104 |
105 | function set_max_bitrate() {
106 | const VIDEO_SELECT = getElementByXPath("//div[text()='Video Bitrate / VMAF']");
107 | const AUDIO_SELECT = getElementByXPath("//div[text()='Audio Bitrate']");
108 | const BUTTON = getElementByXPath("//button[text()='Override']");
109 |
110 | if (!(VIDEO_SELECT && AUDIO_SELECT && BUTTON)){
111 | window.dispatchEvent(new KeyboardEvent('keydown', {
112 | keyCode: 66,
113 | ctrlKey: true,
114 | altKey: true,
115 | shiftKey: true,
116 | }));
117 |
118 | return false;
119 | }
120 |
121 | let SELECT_LISTS = [VIDEO_SELECT, AUDIO_SELECT];
122 | let result = false;
123 |
124 | for (var index = 0; index < SELECT_LISTS.length; index++) {
125 | let list = SELECT_LISTS[index];
126 | let parent = list.parentElement;
127 | let select = parent.querySelector('select');
128 |
129 | if (select.disabled){
130 | return false;
131 | }
132 |
133 | let options = parent.querySelectorAll('select > option');
134 |
135 | if (options.length == 0){
136 | return false;
137 | }
138 |
139 | if (options.length > 1 && options[0].selected == false){
140 | return false;
141 | }
142 |
143 | for (var i = 0; i < options.length - 1; i++) {
144 | options[i].selected = false;
145 | }
146 |
147 | options[options.length - 1].selected = true;
148 | result = options[options.length - 1].selected;
149 | }
150 |
151 | if (result){
152 | console.log("max bitrate selected, closing window");
153 | BUTTON.click();
154 | }
155 |
156 | return result;
157 | }
158 |
159 | function set_max_bitrate_run(attempts) {
160 | if (!attempts) {
161 | console.log("failed to select max bitrate");
162 | return;
163 | }
164 |
165 | set_max_bitrate() || setTimeout(() => set_max_bitrate_run(attempts - 1), 200);
166 | }
167 |
168 | const WATCH_REGEXP = /netflix.com\/watch\/.*/;
169 |
170 | let oldLocation;
171 |
172 | if(my_config["set_max_bitrate"]) {
173 | console.log("netflix_max_bitrate.js enabled");
174 | setInterval(function () {
175 | let newLocation = window.location.toString();
176 |
177 | if (newLocation !== oldLocation) {
178 | oldLocation = newLocation;
179 | WATCH_REGEXP.test(newLocation) && set_max_bitrate_run(10);
180 | }
181 | }, 500);
182 | }
183 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "Turbo-Recadmiumator",
4 | "description": "Hotpatches Netflix cadmium-playercore.js to enhance features",
5 | "version": "0.0.1",
6 | "author": "Retr0id",
7 | "background": {
8 | "scripts": [
9 | "background.js"
10 | ]
11 | },
12 | "web_accessible_resources": [
13 | "cadmium-playercore-shim.js"
14 | ],
15 | "permissions": [
16 | "storage",
17 | "webRequest",
18 | "webRequestBlocking",
19 | "*://assets.nflxext.com/player/html/ffe/*",
20 | "*://*.a.nflxso.net/sec/player/html/ffe/*",
21 | "*://netflix.com/*",
22 | "*://www.netflix.com/*"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------