[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 David Buchanan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Turbo-Recadmiumator [WIP]\nA remake of truedread/netflix-1080p which auto-patches cadmium-playercore at runtime to enable enhanced playback features.\n\nWorks in both Firefox and Chrom{e,ium}.\n\nRather than bundling a hand-patched cadmium-playercore.js, this extension\nperforms the patches at runtime using regex. Therefore, it should be publishable\nto the chrome/ff webstores without any copyright issues etc.\n\nIt remains to be seen how robust my regexes will be to playercore updates.\n\n## Current features:\n\n- Enable <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Alt</kbd>+<kbd>S</kbd> bitrate selection window.\n\n- 1080p video.\n\n- Disable VP9 profiles.\n\n- Enable 5.1 audio profile.\n\n- Auto-select stream with max available bitrate.\n\n## Undocumented Keyboard Shortcuts:\n\nNetflix has a bunch of undocumented keyboard shortcuts, that do useful things. This list may be incomplete.\n\n- <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Alt</kbd>+<kbd>B</kbd> - Bitrate selection menu (re-enabled by this project) (NOTE: This used to be S, not B!)\n\n- <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Alt</kbd>+<kbd>D</kbd> - Debug overlay - displays lots of useful info and stats, including current resolution and bitrate.\n\n- <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Alt</kbd>+<kbd>T</kbd> - Upload custom subtitle file, in DFXP/TTML format.\n\n- <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Alt</kbd>+<kbd>L</kbd> - Log viewer.\n\n## TODO:\n\n- Add settings UI (right now, you have to edit the source...)\n\n- Add comments detailing where I stole the code from...\n\n## Credits:\n\nThis codebase is cobbled together with bits and pieces from [truedread/netflix-1080p](https://github.com/truedread/netflix-1080p) and its various forks. Notably:\n\n- https://github.com/vladikoff/netflix-1080p-firefox\n\n- https://github.com/TheGoddessInari/netflix-1080p-firefox\n\n- https://github.com/OothecaPickle/netflix-1080p\n\n- https://github.com/jangxx/netflix-1080p\n"
  },
  {
    "path": "src/background.js",
    "content": "// https://stackoverflow.com/a/45985333\nfunction getBrowser() {\n\tif (typeof chrome !== \"undefined\") {\n\t\tif (typeof browser !== \"undefined\") {\n\t\t\treturn \"Firefox\";\n\t\t} else {\n\t\t\treturn \"Chrome\";\n\t\t}\n\t} else {\n\t\treturn \"Edge\";\n\t}\n}\n\nchrome.webRequest.onBeforeRequest.addListener(\n\tfunction (details) {\n\t\t/* Allow our shim to load an untouched copy */\n\t\tif (details.url.endsWith(\"?no_filter\")) {\n\t\t\treturn {};\n\t\t}\n\t\t\n\t\tif (getBrowser() == \"Chrome\") {\n\t\t\treturn {\n\t\t\t\tredirectUrl: chrome.extension.getURL(\"cadmium-playercore-shim.js\")\n\t\t\t};\n\t\t}\n\t\t\n\t\t/* Work around funky CORS behaviour on Firefox */\n\t\telse if (getBrowser() == \"Firefox\") {\n\t\t\tlet filter = browser.webRequest.filterResponseData(details.requestId);\n\t\t\tlet encoder = new TextEncoder();\n\t\t\tfilter.onstop = event => {\n\t\t\t\tfetch(browser.extension.getURL(\"cadmium-playercore-shim.js\")).\n\t\t\t\t\tthen(response => response.text()).\n\t\t\t\t\tthen(text => {\n\t\t\t\t\t\tfilter.write(encoder.encode(text));\n\t\t\t\t\t\tfilter.close();\n\t\t\t\t\t});\n\t\t\t};\n\t\t\treturn {};\n\t\t}\n\t\t\n\t\telse {\n\t\t\tconsole.error(\"Unsupported web browser :(\");\n\t\t}\n\t}, {\n\t\turls: [\n\t\t\t\"*://assets.nflxext.com/player/html/ffe/*\",\n\t\t\t\"*://*.a.nflxso.net/sec/player/html/ffe/*\"\n\t\t]\n\t}, [\"blocking\"]\n);\n"
  },
  {
    "path": "src/cadmium-playercore-shim.js",
    "content": "/* This script runs as a drop-in replacement of the original cadmium-playercore */\nconsole.log(\"Hello, I am running instead of playercore\");\n\nvar my_config = {\n\t\"use_VP9\": false,\n\t\"use_5.1\": false,\n\t\"set_max_bitrate\": true,\n}\n\nfunction repr(obj) {\n\t// can you tell I'm a python programmer?\n\treturn JSON.stringify(obj);\n}\n\nfunction do_patch(desc, needle, replacement) {\n\tvar match = cadmium_src.match(needle);\n\tif (!match) {\n\t\talert(\"Failed to find patch: \" + repr(desc));\n\t} else {\n\t\tcadmium_src = cadmium_src.replace(needle, replacement);\n\t\tconsole.log(\"[+] Patched: \" + repr(desc));\n\t\tif (match[0].length < 200) { // avoid spamming the console\n\t\t\tconsole.log(repr(match[0]) + \" -> \" + repr(replacement));\n\t\t}\n\t}\n}\n\n/* We need to do a synchronous request because we need to eval\nthe response before the body of this script finishes executing */\nvar request = new XMLHttpRequest();\nvar cadmium_url = document.getElementById(\"player-core-js\").src;\nrequest.open(\"GET\", cadmium_url + \"?no_filter\", false); // synchronous\nrequest.send(null);\n\nvar cadmium_src = request.responseText;\n\nfunction get_profile_list() {\n\tcustom_profiles = [\n\t\t\"playready-h264mpl30-dash\",\n\t\t\"playready-h264mpl31-dash\",\n\t\t\"playready-h264mpl40-dash\",\n\t\t\n\t\t\"playready-h264hpl30-dash\",\n\t\t\"playready-h264hpl31-dash\",\n\t\t\"playready-h264hpl40-dash\",\n\t\t\n\t\t\"heaac-2-dash\",\n\t\t\"heaac-2hq-dash\",\n\n\t\t\"simplesdh\",\n\t\t\"nflx-cmisc\",\n\t\t\"BIF240\",\n\t\t\"BIF320\"\n\t];\n\n\tif (my_config[\"use_VP9\"]) {\n\t\tcustom_profiles = custom_profiles.concat([\n\t\t\t\"vp9-profile0-L30-dash-cenc\",\n\t\t\t\"vp9-profile0-L31-dash-cenc\",\n\t\t\t\"vp9-profile0-L40-dash-cenc\",\n\t\t]);\n\t}\n\n\tif (my_config[\"use_5.1\"]) {\n\t\tcustom_profiles.push(\"heaac-5.1-dash\");\n\t}\n\n\treturn custom_profiles;\n}\n\ndo_patch(\n\t\"Hello world\",\n\t/(.*)/,\n\t\"console.log('Hello, I am code which has been injected into playercore!'); $1\"\n);\n\ndo_patch(\n\t\"Custom profiles\",\n\t/(viewableId:.,profiles:).,/,\n\t\"$1 get_profile_list(),\"\n);\n\ndo_patch(\n\t\"Custom profile group\",\n\t/(name:\"default\",profiles:)./,\n\t\"$1 get_profile_list()\"\n);\n\ndo_patch(\n\t\"Re-enable Ctrl+Shift+Alt+S menu\",\n\t/this\\...\\....\\s*\\&\\&\\s*this\\.toggle\\(\\);/,\n\t\"this.toggle();\");\n\n// run our patched copy of playercore\neval(cadmium_src);\n\n\n\n/* netflix_max_bitrate.js */\n\nfunction getElementByXPath(xpath) {\n\treturn document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\n}\n\nfunction set_max_bitrate() {\n\tconst VIDEO_SELECT = getElementByXPath(\"//div[text()='Video Bitrate / VMAF']\");\n\tconst AUDIO_SELECT = getElementByXPath(\"//div[text()='Audio Bitrate']\");\n\tconst BUTTON = getElementByXPath(\"//button[text()='Override']\");\n\n\tif (!(VIDEO_SELECT && AUDIO_SELECT && BUTTON)){\n\t\twindow.dispatchEvent(new KeyboardEvent('keydown', {\n\t\t\tkeyCode: 66,\n\t\t\tctrlKey: true,\n\t\t\taltKey: true,\n\t\t\tshiftKey: true,\n\t\t}));\n\n\t\treturn false;\n\t}\n\n\tlet SELECT_LISTS = [VIDEO_SELECT, AUDIO_SELECT];\n\tlet result = false;\n\n\tfor (var index = 0; index < SELECT_LISTS.length; index++) {\n\t\tlet list = SELECT_LISTS[index];\n\t\tlet parent = list.parentElement;\n\t\tlet select = parent.querySelector('select');\n\n\t\tif (select.disabled){\n\t\t\treturn false;\n\t\t}\n\n\t\tlet options = parent.querySelectorAll('select > option');\n\n\t\tif (options.length == 0){\n\t\t\treturn false;\n\t\t}\n\n\t\tif (options.length > 1 && options[0].selected == false){\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (var i = 0; i < options.length - 1; i++) {\n\t\t\toptions[i].selected = false;\n\t\t}\n\n\t\toptions[options.length - 1].selected = true;\n\t\tresult = options[options.length - 1].selected;\n\t}\n\n\tif (result){\n\t\tconsole.log(\"max bitrate selected, closing window\");\n\t\tBUTTON.click();\n\t}\n\n\treturn result;\n}\n\nfunction set_max_bitrate_run(attempts) {\n\tif (!attempts) {\n\t\tconsole.log(\"failed to select max bitrate\");\n\t\treturn;\n\t}\n\n\tset_max_bitrate() || setTimeout(() => set_max_bitrate_run(attempts - 1), 200);\n}\n\nconst WATCH_REGEXP = /netflix.com\\/watch\\/.*/;\n\nlet oldLocation;\n\nif(my_config[\"set_max_bitrate\"]) {\n\tconsole.log(\"netflix_max_bitrate.js enabled\");\n\tsetInterval(function () {\n\t\tlet newLocation = window.location.toString();\n\n\t\tif (newLocation !== oldLocation) {\n\t\t\toldLocation = newLocation;\n\t\t\tWATCH_REGEXP.test(newLocation) && set_max_bitrate_run(10);\n\t\t}\n\t}, 500);\n}\n"
  },
  {
    "path": "src/manifest.json",
    "content": "{\n\t\"manifest_version\": 2,\n\t\"name\": \"Turbo-Recadmiumator\",\n\t\"description\": \"Hotpatches Netflix cadmium-playercore.js to enhance features\",\n\t\"version\": \"0.0.1\",\n\t\"author\": \"Retr0id\",\n\t\"background\": {\n\t\t\"scripts\": [\n\t\t\t\"background.js\"\n\t\t]\n\t},\n\t\"web_accessible_resources\": [\n\t\t\"cadmium-playercore-shim.js\"\n\t],\n\t\"permissions\": [\n\t\t\"storage\",\n\t\t\"webRequest\",\n\t\t\"webRequestBlocking\",\n\t\t\"*://assets.nflxext.com/player/html/ffe/*\",\n\t\t\"*://*.a.nflxso.net/sec/player/html/ffe/*\",\n\t\t\"*://netflix.com/*\",\n\t\t\"*://www.netflix.com/*\"\n\t]\n}\n"
  }
]