prettier formatting, add manifest url in console.log

This commit is contained in:
voldemort 2025-07-20 12:18:15 +07:00
parent bafe525510
commit c9ff17558d
18 changed files with 4754 additions and 4436 deletions

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
react/
frontend/dist/
frontend/src/assets/

8
.prettierrc.json Normal file
View File

@ -0,0 +1,8 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": false,
"useTabs": false,
"printWidth": 100
}

View File

@ -4,7 +4,7 @@ chrome.browserAction.onClicked.addListener(() => {
url: chrome.runtime.getURL("react/index.html"), url: chrome.runtime.getURL("react/index.html"),
type: "popup", // opens as a floating window type: "popup", // opens as a floating window
width: 800, width: 800,
height: 600 height: 600,
}); });
}); });
@ -30,7 +30,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
case "LICENSE_URL": case "LICENSE_URL":
console.log("Storling License URL " + data); console.log("Storling License URL " + data);
chrome.storage.local.set({licenseURL: data}); chrome.storage.local.set({ licenseURL: data });
break; break;
default: default:

View File

@ -1,26 +1,29 @@
// Inject `inject.js` into the page context // Inject `inject.js` into the page context
(function injectScript() { (function injectScript() {
const script = document.createElement('script'); const script = document.createElement("script");
script.src = chrome.runtime.getURL('inject.js'); script.src = chrome.runtime.getURL("inject.js");
script.type = 'text/javascript'; script.type = "text/javascript";
script.onload = () => script.remove(); // Clean up script.onload = () => script.remove(); // Clean up
// Inject directly into <html> or <head> // Inject directly into <html> or <head>
(document.documentElement || document.head || document.body).appendChild(script); (document.documentElement || document.head || document.body).appendChild(script);
})(); })();
// Listen for messages from the injected script // Listen for messages from the injected script
window.addEventListener("message", function(event) { window.addEventListener("message", function (event) {
if (event.source !== window) return; if (event.source !== window) return;
if (["__DRM_TYPE__", "__PSSH_DATA__", "__KEYS_DATA__", "__LICENSE_URL__"].includes(event.data?.type)) { if (
["__DRM_TYPE__", "__PSSH_DATA__", "__KEYS_DATA__", "__LICENSE_URL__"].includes(
event.data?.type
)
) {
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
type: event.data.type.replace("__", "").replace("__", ""), type: event.data.type.replace("__", "").replace("__", ""),
data: event.data.data data: event.data.data,
}); });
} }
if (event.data.type === "__GET_CDM_DEVICES__") { if (event.data.type === "__GET_CDM_DEVICES__") {
chrome.storage.local.get(["widevine_device", "playready_device"], (result) => { chrome.storage.local.get(["widevine_device", "playready_device"], (result) => {
const widevine_device = result.widevine_device || null; const widevine_device = result.widevine_device || null;
const playready_device = result.playready_device || null; const playready_device = result.playready_device || null;
@ -29,7 +32,7 @@ window.addEventListener("message", function(event) {
{ {
type: "__CDM_DEVICES__", type: "__CDM_DEVICES__",
widevine_device, widevine_device,
playready_device playready_device,
}, },
"*" "*"
); );
@ -37,31 +40,57 @@ window.addEventListener("message", function(event) {
} }
if (event.data.type === "__GET_INJECTION_TYPE__") { if (event.data.type === "__GET_INJECTION_TYPE__") {
chrome.storage.local.get("injection_type", (result) => { chrome.storage.local.get("injection_type", (result) => {
const injectionType = result.injection_type || "LICENSE"; const injectionType = result.injection_type || "LICENSE";
window.postMessage( window.postMessage(
{ {
type: "__INJECTION_TYPE__", type: "__INJECTION_TYPE__",
injectionType injectionType,
}, },
"*" "*"
); );
}); });
} }
if (event.data.type === "__GET_DRM_OVERRIDE__") {
if (event.data.type === "__GET_DRM_OVERRIDE__") {
chrome.storage.local.get("drm_override", (result) => { chrome.storage.local.get("drm_override", (result) => {
const drmOverride = result.drm_override || "DISABLED"; const drmOverride = result.drm_override || "DISABLED";
window.postMessage( window.postMessage(
{ {
type: "__DRM_OVERRIDE__", type: "__DRM_OVERRIDE__",
drmOverride drmOverride,
}, },
"*" "*"
); );
}); });
} }
// Manifest header and URL
const seenManifestUrls = new Set();
if (event.data?.type === "__MANIFEST_URL__") {
const url = event.data.data;
if (seenManifestUrls.has(url)) return;
seenManifestUrls.add(url);
console.log("✅ [Content] Unique manifest URL:", url);
chrome.runtime.sendMessage({
type: "MANIFEST_URL_FOUND",
data: url,
});
}
if (event.data?.type === "__MANIFEST_HEADERS__") {
const { url, headers } = event.data;
console.log("[Content.js] Manifest Headers:", url, headers);
chrome.runtime.sendMessage({
type: "MANIFEST_HEADERS",
url,
headers,
});
}
}); });

View File

@ -1,33 +1,30 @@
import js from '@eslint/js' import js from "@eslint/js";
import globals from 'globals' import globals from "globals";
import reactHooks from 'eslint-plugin-react-hooks' import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from 'eslint-plugin-react-refresh' import reactRefresh from "eslint-plugin-react-refresh";
export default [ export default [
{ ignores: ['dist'] }, { ignores: ["dist"] },
{ {
files: ['**/*.{js,jsx}'], files: ["**/*.{js,jsx}"],
languageOptions: { languageOptions: {
ecmaVersion: 2020, ecmaVersion: 2020,
globals: globals.browser, globals: globals.browser,
parserOptions: { parserOptions: {
ecmaVersion: 'latest', ecmaVersion: "latest",
ecmaFeatures: { jsx: true }, ecmaFeatures: { jsx: true },
sourceType: 'module', sourceType: "module",
}, },
}, },
plugins: { plugins: {
'react-hooks': reactHooks, "react-hooks": reactHooks,
'react-refresh': reactRefresh, "react-refresh": reactRefresh,
}, },
rules: { rules: {
...js.configs.recommended.rules, ...js.configs.recommended.rules,
...reactHooks.configs.recommended.rules, ...reactHooks.configs.recommended.rules,
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], "no-unused-vars": ["error", { varsIgnorePattern: "^[A-Z_]" }],
'react-refresh/only-export-components': [ "react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
'warn',
{ allowConstantExport: true },
],
}, },
}, },
] ];

View File

@ -1,10 +1,5 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { import { HashRouter as Router, Routes, Route, Navigate } from "react-router-dom";
HashRouter as Router,
Routes,
Route,
Navigate,
} from "react-router-dom";
import TopNav from "./components/topnav"; import TopNav from "./components/topnav";
import SideNav from "./components/sidenav"; import SideNav from "./components/sidenav";
import Results from "./components/results"; import Results from "./components/results";

View File

@ -8,13 +8,7 @@ function Results() {
useEffect(() => { useEffect(() => {
chrome.storage.local.get( chrome.storage.local.get(
[ ["drmType", "latestPSSH", "latestLicenseRequest", "latestKeys", "licenseURL"],
"drmType",
"latestPSSH",
"latestLicenseRequest",
"latestKeys",
"licenseURL",
],
(result) => { (result) => {
if (result.drmType) setDrmType(result.drmType); if (result.drmType) setDrmType(result.drmType);
if (result.latestPSSH) setPssh(result.latestPSSH); if (result.latestPSSH) setPssh(result.latestPSSH);
@ -64,9 +58,7 @@ function Results() {
}); });
// Get all normal windows to exclude your popup // Get all normal windows to exclude your popup
chrome.windows.getAll( chrome.windows.getAll({ populate: true, windowTypes: ["normal"] }, (windows) => {
{ populate: true, windowTypes: ["normal"] },
(windows) => {
if (!windows || windows.length === 0) { if (!windows || windows.length === 0) {
console.warn("No normal Chrome windows found"); console.warn("No normal Chrome windows found");
return; return;
@ -94,8 +86,7 @@ function Results() {
} else { } else {
console.warn("No active tab found in the last focused normal window"); console.warn("No active tab found in the last focused normal window");
} }
} });
);
}; };
return ( return (
@ -132,8 +123,7 @@ function Results() {
/> />
<p className="text-2xl mt-5">Keys</p> <p className="text-2xl mt-5">Keys</p>
<div className="w-full min-h-64 h-64 flex items-center justify-center text-center overflow-y-auto bg-slate-800/50 rounded-md p-2 mt-2 text-white whitespace-pre-line"> <div className="w-full min-h-64 h-64 flex items-center justify-center text-center overflow-y-auto bg-slate-800/50 rounded-md p-2 mt-2 text-white whitespace-pre-line">
{Array.isArray(keys) && {Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0 ? (
keys.filter((k) => k.type !== "SIGNING").length > 0 ? (
keys keys
.filter((k) => k.type !== "SIGNING") .filter((k) => k.type !== "SIGNING")
.map((k) => `${k.key_id || k.keyId}:${k.key}`) .map((k) => `${k.key_id || k.keyId}:${k.key}`)

View File

@ -13,10 +13,7 @@ function Settings({ onConfigSaved }) {
useEffect(() => { useEffect(() => {
chrome.storage.local.get("cdrm_instance", (result) => { chrome.storage.local.get("cdrm_instance", (result) => {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
console.error( console.error("Error fetching CDRM instance:", chrome.runtime.lastError);
"Error fetching CDRM instance:",
chrome.runtime.lastError
);
} else if (result.cdrm_instance) { } else if (result.cdrm_instance) {
setStoredUrl(result.cdrm_instance); setStoredUrl(result.cdrm_instance);
} }
@ -49,18 +46,12 @@ function Settings({ onConfigSaved }) {
setMessage("Successfully connected to CDRM Instance."); setMessage("Successfully connected to CDRM Instance.");
setMessageType("success"); setMessageType("success");
const widevineRes = await fetch( const widevineRes = await fetch(`${trimmedUrl}/remotecdm/widevine/deviceinfo`);
`${trimmedUrl}/remotecdm/widevine/deviceinfo` if (!widevineRes.ok) throw new Error("Failed to fetch Widevine device info");
);
if (!widevineRes.ok)
throw new Error("Failed to fetch Widevine device info");
const widevineData = await widevineRes.json(); const widevineData = await widevineRes.json();
const playreadyRes = await fetch( const playreadyRes = await fetch(`${trimmedUrl}/remotecdm/playready/deviceinfo`);
`${trimmedUrl}/remotecdm/playready/deviceinfo` if (!playreadyRes.ok) throw new Error("Failed to fetch PlayReady device info");
);
if (!playreadyRes.ok)
throw new Error("Failed to fetch PlayReady device info");
const playreadyData = await playreadyRes.json(); const playreadyData = await playreadyRes.json();
chrome.storage.local.set( chrome.storage.local.set(

View File

@ -7,10 +7,7 @@ function SideNav({ onClose }) {
return ( return (
<div className="w-full h-full overflow-y-auto overflow-x-auto flex flex-col bg-black"> <div className="w-full h-full overflow-y-auto overflow-x-auto flex flex-col bg-black">
<div className="w-full min-h-16 max-h-16 h-16 shrink-0 flex sticky top-0 z-20 border-b border-b-white bg-black"> <div className="w-full min-h-16 max-h-16 h-16 shrink-0 flex sticky top-0 z-20 border-b border-b-white bg-black">
<button <button onClick={onClose} className="h-full ml-auto p-3 hover:cursor-pointer">
onClick={onClose}
className="h-full ml-auto p-3 hover:cursor-pointer"
>
<img src={closeIcon} alt="Close" className="h-full" /> <img src={closeIcon} alt="Close" className="h-full" />
</button> </button>
</div> </div>

View File

@ -19,10 +19,7 @@ function TopNav({ onMenuClick }) {
const handleInjectionTypeChange = (type) => { const handleInjectionTypeChange = (type) => {
chrome.storage.local.set({ injection_type: type }, () => { chrome.storage.local.set({ injection_type: type }, () => {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
console.error( console.error("Error updating injection_type:", chrome.runtime.lastError);
"Error updating injection_type:",
chrome.runtime.lastError
);
} else { } else {
setInjectionType(type); setInjectionType(type);
console.log(`Injection type updated to ${type}`); console.log(`Injection type updated to ${type}`);

View File

@ -1,6 +1,8 @@
@import "tailwindcss"; @import "tailwindcss";
html, body, #root { html,
body,
#root {
height: 100%; height: 100%;
width: 100%; width: 100%;
margin: 0; margin: 0;

View File

@ -1,10 +1,10 @@
import { StrictMode } from 'react' import { StrictMode } from "react";
import { createRoot } from 'react-dom/client' import { createRoot } from "react-dom/client";
import './index.css' import "./index.css";
import App from './App.jsx' import App from "./App.jsx";
createRoot(document.getElementById('root')).render( createRoot(document.getElementById("root")).render(
<StrictMode> <StrictMode>
<App /> <App />
</StrictMode>, </StrictMode>
) );

View File

@ -1,9 +1,9 @@
import { defineConfig } from 'vite' import { defineConfig } from "vite";
import react from '@vitejs/plugin-react' import react from "@vitejs/plugin-react";
import tailwindcss from '@tailwindcss/vite' import tailwindcss from "@tailwindcss/vite";
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
base: './', base: "./",
plugins: [react(), tailwindcss()], plugins: [react(), tailwindcss()],
}) });

593
inject.js
View File

@ -1,6 +1,6 @@
let widevineDeviceInfo = null; let widevineDeviceInfo = null;
let playreadyDeviceInfo = null; let playreadyDeviceInfo = null;
let originalChallenge = null let originalChallenge = null;
let serviceCertFound = false; let serviceCertFound = false;
let drmType = "NONE"; let drmType = "NONE";
let psshFound = false; let psshFound = false;
@ -21,7 +21,7 @@ let keysRetrieved = false;
window.postMessage({ type: "__GET_DRM_OVERRIDE__" }, "*"); window.postMessage({ type: "__GET_DRM_OVERRIDE__" }, "*");
// Add listener for DRM override messages // Add listener for DRM override messages
window.addEventListener("message", function(event) { window.addEventListener("message", function (event) {
if (event.source !== window) return; if (event.source !== window) return;
if (event.data.type === "__DRM_OVERRIDE__") { if (event.data.type === "__DRM_OVERRIDE__") {
drmOverride = event.data.drmOverride || "DISABLED"; drmOverride = event.data.drmOverride || "DISABLED";
@ -33,7 +33,7 @@ window.addEventListener("message", function(event) {
window.postMessage({ type: "__GET_INJECTION_TYPE__" }, "*"); window.postMessage({ type: "__GET_INJECTION_TYPE__" }, "*");
// Add listener for injection type messages // Add listener for injection type messages
window.addEventListener("message", function(event) { window.addEventListener("message", function (event) {
if (event.source !== window) return; if (event.source !== window) return;
if (event.data.type === "__INJECTION_TYPE__") { if (event.data.type === "__INJECTION_TYPE__") {
@ -46,7 +46,7 @@ window.addEventListener("message", function(event) {
window.postMessage({ type: "__GET_CDM_DEVICES__" }, "*"); window.postMessage({ type: "__GET_CDM_DEVICES__" }, "*");
// Add listener for CDM device messages // Add listener for CDM device messages
window.addEventListener("message", function(event) { window.addEventListener("message", function (event) {
if (event.source !== window) return; if (event.source !== window) return;
if (event.data.type === "__CDM_DEVICES__") { if (event.data.type === "__CDM_DEVICES__") {
@ -59,6 +59,122 @@ window.addEventListener("message", function(event) {
} }
}); });
function safeHeaderShellEscape(str) {
return str
.replace(/\\/g, "\\\\")
.replace(/"/g, '\\"')
.replace(/\$/g, "\\$") // escape shell expansion
.replace(/`/g, "\\`")
.replace(/\n/g, ""); // strip newlines
}
// Intercep network to find manifest
function injectManifestInterceptor() {
const script = document.createElement("script");
script.textContent = `
(function() {
function isProbablyManifest(text = "", contentType = "") {
const lowerCT = contentType?.toLowerCase() ?? "";
const sample = text.slice(0, 2000);
const isHLSMime = lowerCT.includes("mpegurl");
const isDASHMime = lowerCT.includes("dash+xml");
const isSmoothMime = lowerCT.includes("sstr+xml");
const isHLSKeyword = sample.includes("#EXTM3U") || sample.includes("#EXT-X-STREAM-INF");
const isDASHKeyword = sample.includes("<MPD") || sample.includes("<AdaptationSet");
const isSmoothKeyword = sample.includes("<SmoothStreamingMedia");
const isJsonManifest = sample.includes('"playlist"') && sample.includes('"segments"');
return (
isHLSMime || isDASHMime || isSmoothMime ||
isHLSKeyword || isDASHKeyword || isSmoothKeyword || isJsonManifest
);
}
const originalFetch = window.fetch;
window.fetch = async function(input, init) {
const response = await originalFetch.apply(this, arguments);
try {
const clone = response.clone();
const contentType = clone.headers.get("content-type") || "";
const text = await clone.text();
const url = typeof input === "string" ? input : input.url;
if (isProbablyManifest(text, contentType)) {
window.postMessage({ type: "__MANIFEST_URL__", data: url }, "*");
console.log("[Manifest][fetch]", url, contentType);
const headersObj = {};
clone.headers.forEach((value, key) => {
headersObj[key] = value;
});
const headerFlags = Object.entries(headersObj)
.map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"')
.join(" ");
window.postMessage({
type: "__MANIFEST_HEADERS__",
url,
headers: headerFlags
}, "*");
}
} catch (e) {}
return response;
};
const originalXHROpen = XMLHttpRequest.prototype.open;
const originalXHRSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this.__url = url;
return originalXHROpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function(body) {
this.addEventListener("load", function () {
try {
const contentType = this.getResponseHeader("content-type") || "";
const text = this.responseText;
if (isProbablyManifest(text, contentType)) {
window.postMessage({ type: "__MANIFEST_URL__", data: this.__url }, "*");
console.log("[Manifest][xhr]", this.__url, contentType);
const xhrHeaders = {};
const rawHeaders = this.getAllResponseHeaders().trim().split(/\\r?\\n/);
rawHeaders.forEach(line => {
const parts = line.split(": ");
if (parts.length === 2) {
xhrHeaders[parts[0]] = parts[1];
}
});
const headerFlags = Object.entries(xhrHeaders)
.map(([key, val]) => '--add-headers "' + safeHeaderShellEscape(key) + ': ' + safeHeaderShellEscape(val) + '"')
.join(" ");
window.postMessage({
type: "__MANIFEST_HEADERS__",
url: this.__url,
headers: headerFlags
}, "*");
}
} catch (e) {}
});
return originalXHRSend.apply(this, arguments);
};
})();
`;
document.documentElement.appendChild(script);
script.remove();
}
injectManifestInterceptor();
// PlayReady Remote CDM Class // PlayReady Remote CDM Class
class remotePlayReadyCDM { class remotePlayReadyCDM {
@ -76,8 +192,8 @@ class remotePlayReadyCDM {
openSession() { openSession() {
const url = `${this.host}/remotecdm/playready/${this.device_name}/open`; const url = `${this.host}/remotecdm/playready/${this.device_name}/open`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('GET', url, false); xhr.open("GET", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(); xhr.send();
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
if (jsonData.data?.session_id) { if (jsonData.data?.session_id) {
@ -93,11 +209,11 @@ class remotePlayReadyCDM {
getChallenge(init_data) { getChallenge(init_data) {
const url = `${this.host}/remotecdm/playready/${this.device_name}/get_license_challenge`; const url = `${this.host}/remotecdm/playready/${this.device_name}/get_license_challenge`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', url, false); xhr.open("POST", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
const body = { const body = {
session_id: this.session_id, session_id: this.session_id,
init_data: init_data init_data: init_data,
}; };
xhr.send(JSON.stringify(body)); xhr.send(JSON.stringify(body));
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
@ -114,16 +230,17 @@ class remotePlayReadyCDM {
parseLicense(license_message) { parseLicense(license_message) {
const url = `${this.host}/remotecdm/playready/${this.device_name}/parse_license`; const url = `${this.host}/remotecdm/playready/${this.device_name}/parse_license`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', url, false); xhr.open("POST", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
const body = { const body = {
session_id: this.session_id, session_id: this.session_id,
license_message: license_message license_message: license_message,
} };
xhr.send(JSON.stringify(body)); xhr.send(JSON.stringify(body));
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
if (jsonData.message === "Successfully parsed and loaded the Keys from the License message") if (
{ jsonData.message === "Successfully parsed and loaded the Keys from the License message"
) {
console.log("PlayReady license response parsed successfully"); console.log("PlayReady license response parsed successfully");
return true; return true;
} else { } else {
@ -136,11 +253,11 @@ class remotePlayReadyCDM {
getKeys() { getKeys() {
const url = `${this.host}/remotecdm/playready/${this.device_name}/get_keys`; const url = `${this.host}/remotecdm/playready/${this.device_name}/get_keys`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', url, false); xhr.open("POST", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
const body = { const body = {
session_id: this.session_id session_id: this.session_id,
} };
xhr.send(JSON.stringify(body)); xhr.send(JSON.stringify(body));
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
if (jsonData.data?.keys) { if (jsonData.data?.keys) {
@ -153,11 +270,11 @@ class remotePlayReadyCDM {
} }
// Close PlayReady session // Close PlayReady session
closeSession () { closeSession() {
const url = `${this.host}/remotecdm/playready/${this.device_name}/close/${this.session_id}`; const url = `${this.host}/remotecdm/playready/${this.device_name}/close/${this.session_id}`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('GET', url, false); xhr.open("GET", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(); xhr.send();
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
if (jsonData) { if (jsonData) {
@ -184,11 +301,11 @@ class remoteWidevineCDM {
} }
// Open Widevine session // Open Widevine session
openSession () { openSession() {
const url = `${this.host}/remotecdm/widevine/${this.device_name}/open`; const url = `${this.host}/remotecdm/widevine/${this.device_name}/open`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('GET', url, false); xhr.open("GET", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(); xhr.send();
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
if (jsonData.data?.session_id) { if (jsonData.data?.session_id) {
@ -204,12 +321,12 @@ class remoteWidevineCDM {
setServiceCertificate(certificate) { setServiceCertificate(certificate) {
const url = `${this.host}/remotecdm/widevine/${this.device_name}/set_service_certificate`; const url = `${this.host}/remotecdm/widevine/${this.device_name}/set_service_certificate`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', url, false); xhr.open("POST", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
const body = { const body = {
session_id: this.session_id, session_id: this.session_id,
certificate: certificate ?? null certificate: certificate ?? null,
} };
xhr.send(JSON.stringify(body)); xhr.send(JSON.stringify(body));
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
if (jsonData.status === 200) { if (jsonData.status === 200) {
@ -221,15 +338,15 @@ class remoteWidevineCDM {
} }
// Get Widevine challenge // Get Widevine challenge
getChallenge(init_data, license_type = 'STREAMING') { getChallenge(init_data, license_type = "STREAMING") {
const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_license_challenge/${license_type}`; const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_license_challenge/${license_type}`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', url, false); xhr.open("POST", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
const body = { const body = {
session_id: this.session_id, session_id: this.session_id,
init_data: init_data, init_data: init_data,
privacy_mode: serviceCertFound privacy_mode: serviceCertFound,
}; };
xhr.send(JSON.stringify(body)); xhr.send(JSON.stringify(body));
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
@ -246,11 +363,11 @@ class remoteWidevineCDM {
parseLicense(license_message) { parseLicense(license_message) {
const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`; const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', url, false); xhr.open("POST", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
const body = { const body = {
session_id: this.session_id, session_id: this.session_id,
license_message: license_message license_message: license_message,
}; };
xhr.send(JSON.stringify(body)); xhr.send(JSON.stringify(body));
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
@ -267,10 +384,10 @@ class remoteWidevineCDM {
getKeys() { getKeys() {
const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_keys/ALL`; const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_keys/ALL`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', url, false); xhr.open("POST", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
const body = { const body = {
session_id: this.session_id session_id: this.session_id,
}; };
xhr.send(JSON.stringify(body)); xhr.send(JSON.stringify(body));
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
@ -287,8 +404,8 @@ class remoteWidevineCDM {
closeSession() { closeSession() {
const url = `${this.host}/remotecdm/widevine/${this.device_name}/close/${this.session_id}`; const url = `${this.host}/remotecdm/widevine/${this.device_name}/close/${this.session_id}`;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('GET', url, false); xhr.open("GET", url, false);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(); xhr.send();
const jsonData = JSON.parse(xhr.responseText); const jsonData = JSON.parse(xhr.responseText);
if (jsonData) { if (jsonData) {
@ -302,22 +419,22 @@ class remoteWidevineCDM {
// Utility functions // Utility functions
function hexStrToU8(hexString) { function hexStrToU8(hexString) {
return Uint8Array.from(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); return Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
} }
function u8ToHexStr(bytes) { function u8ToHexStr(bytes) {
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), ''); return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
} }
function b64ToHexStr(b64) { function b64ToHexStr(b64) {
return [...atob(b64)].map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join``; return [...atob(b64)].map((c) => c.charCodeAt(0).toString(16).padStart(2, "0")).join``;
} }
function jsonContainsValue(obj, prefix = "CAES") { function jsonContainsValue(obj, prefix = "CAES") {
if (typeof obj === "string") return obj.startsWith(prefix); if (typeof obj === "string") return obj.startsWith(prefix);
if (Array.isArray(obj)) return obj.some(val => jsonContainsValue(val, prefix)); if (Array.isArray(obj)) return obj.some((val) => jsonContainsValue(val, prefix));
if (typeof obj === "object" && obj !== null) { if (typeof obj === "object" && obj !== null) {
return Object.values(obj).some(val => jsonContainsValue(val, prefix)); return Object.values(obj).some((val) => jsonContainsValue(val, prefix));
} }
return false; return false;
} }
@ -328,7 +445,7 @@ function jsonReplaceValue(obj, newValue) {
} }
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
return obj.map(item => jsonReplaceValue(item, newValue)); return obj.map((item) => jsonReplaceValue(item, newValue));
} }
if (typeof obj === "object" && obj !== null) { if (typeof obj === "object" && obj !== null) {
@ -379,10 +496,10 @@ function getPlayReadyPssh(buffer) {
} }
function getClearkey(response) { function getClearkey(response) {
let obj = JSON.parse((new TextDecoder("utf-8")).decode(response)); let obj = JSON.parse(new TextDecoder("utf-8").decode(response));
return obj["keys"].map(o => ({ return obj["keys"].map((o) => ({
key_id: b64ToHexStr(o["kid"].replace(/-/g, '+').replace(/_/g, '/')), key_id: b64ToHexStr(o["kid"].replace(/-/g, "+").replace(/_/g, "/")),
key: b64ToHexStr(o["k"].replace(/-/g, '+').replace(/_/g, '/')), key: b64ToHexStr(o["k"].replace(/-/g, "+").replace(/_/g, "/")),
})); }));
} }
@ -397,7 +514,7 @@ function base64ToUint8Array(base64) {
} }
function arrayBufferToBase64(uint8array) { function arrayBufferToBase64(uint8array) {
let binary = ''; let binary = "";
const len = uint8array.length; const len = uint8array.length;
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
@ -409,20 +526,20 @@ function arrayBufferToBase64(uint8array) {
// Challenge generator interceptor // Challenge generator interceptor
const originalGenerateRequest = MediaKeySession.prototype.generateRequest; const originalGenerateRequest = MediaKeySession.prototype.generateRequest;
MediaKeySession.prototype.generateRequest = function(initDataType, initData) { MediaKeySession.prototype.generateRequest = function (initDataType, initData) {
const session = this; const session = this;
let playReadyPssh = getPlayReadyPssh(initData); let playReadyPssh = getPlayReadyPssh(initData);
if (playReadyPssh) { if (playReadyPssh) {
console.log("[DRM Detected] PlayReady"); console.log("[DRM Detected] PlayReady");
foundPlayreadyPssh = playReadyPssh; foundPlayreadyPssh = playReadyPssh;
console.log("[PlayReady PSSH found] " + playReadyPssh) console.log("[PlayReady PSSH found] " + playReadyPssh);
} }
let wideVinePssh = getWidevinePssh(initData) let wideVinePssh = getWidevinePssh(initData);
if (wideVinePssh) { if (wideVinePssh) {
// Widevine code // Widevine code
console.log("[DRM Detected] Widevine"); console.log("[DRM Detected] Widevine");
foundWidevinePssh = wideVinePssh; foundWidevinePssh = wideVinePssh;
console.log("[Widevine PSSH found] " + wideVinePssh) console.log("[Widevine PSSH found] " + wideVinePssh);
} }
// Challenge message interceptor // Challenge message interceptor
if (!remoteListenerMounted) { if (!remoteListenerMounted) {
@ -432,10 +549,16 @@ MediaKeySession.prototype.generateRequest = function(initDataType, initData) {
const uint8Array = new Uint8Array(event.message); const uint8Array = new Uint8Array(event.message);
const base64challenge = arrayBufferToBase64(uint8Array); const base64challenge = arrayBufferToBase64(uint8Array);
if (base64challenge === "CAQ=" && interceptType !== "DISABLED" && !serviceCertFound) { if (base64challenge === "CAQ=" && interceptType !== "DISABLED" && !serviceCertFound) {
const { const { device_type, system_id, security_level, host, secret, device_name } =
device_type, system_id, security_level, host, secret, device_name widevineDeviceInfo;
} = widevineDeviceInfo; remoteCDM = new remoteWidevineCDM(
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); device_type,
system_id,
security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
} }
if (!injectionSuccess && base64challenge !== "CAQ=" && interceptType !== "DISABLED") { if (!injectionSuccess && base64challenge !== "CAQ=" && interceptType !== "DISABLED") {
@ -450,30 +573,53 @@ MediaKeySession.prototype.generateRequest = function(initDataType, initData) {
window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*"); window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*");
if (interceptType === "EME" && !remoteCDM) { if (interceptType === "EME" && !remoteCDM) {
const { const {
device_type, system_id, security_level, host, secret, device_name device_type,
system_id,
security_level,
host,
secret,
device_name,
} = widevineDeviceInfo; } = widevineDeviceInfo;
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); remoteCDM = new remoteWidevineCDM(
device_type,
system_id,
security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundWidevinePssh); remoteCDM.getChallenge(foundWidevinePssh);
}} }
}
if (!originalChallenge.startsWith("CAES")) { if (!originalChallenge.startsWith("CAES")) {
const buffer = event.message; const buffer = event.message;
const decoder = new TextDecoder('utf-16'); const decoder = new TextDecoder("utf-16");
const decodedText = decoder.decode(buffer); const decodedText = decoder.decode(buffer);
const match = decodedText.match(/<Challenge encoding="base64encoded">([^<]+)<\/Challenge>/); const match = decodedText.match(
/<Challenge encoding="base64encoded">([^<]+)<\/Challenge>/
);
if (match) { if (match) {
window.postMessage({ type: "__DRM_TYPE__", data: "PlayReady" }, "*"); window.postMessage({ type: "__DRM_TYPE__", data: "PlayReady" }, "*");
window.postMessage({ type: "__PSSH_DATA__", data: foundPlayreadyPssh }, "*"); window.postMessage(
{ type: "__PSSH_DATA__", data: foundPlayreadyPssh },
"*"
);
originalChallenge = match[1]; originalChallenge = match[1];
if (interceptType === "EME" && !remoteCDM) { if (interceptType === "EME" && !remoteCDM) {
const { const { security_level, host, secret, device_name } =
security_level, host, secret, device_name playreadyDeviceInfo;
} = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM(
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name) security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundPlayreadyPssh); remoteCDM.getChallenge(foundPlayreadyPssh);
} }
}} }
}
if (interceptType === "EME" && remoteCDM) { if (interceptType === "EME" && remoteCDM) {
const uint8challenge = base64ToUint8Array(remoteCDM.challenge); const uint8challenge = base64ToUint8Array(remoteCDM.challenge);
const challengeBuffer = uint8challenge.buffer; const challengeBuffer = uint8challenge.buffer;
@ -482,24 +628,24 @@ MediaKeySession.prototype.generateRequest = function(initDataType, initData) {
origin: event.origin, origin: event.origin,
lastEventId: event.lastEventId, lastEventId: event.lastEventId,
source: event.source, source: event.source,
ports: event.ports ports: event.ports,
}); });
Object.defineProperty(syntheticEvent, "message", { Object.defineProperty(syntheticEvent, "message", {
get: () => challengeBuffer get: () => challengeBuffer,
}); });
console.log("Intercepted EME Challenge and injected custom one.") console.log("Intercepted EME Challenge and injected custom one.");
session.dispatchEvent(syntheticEvent); session.dispatchEvent(syntheticEvent);
} }
} }
}) });
console.log("Message interceptor mounted."); console.log("Message interceptor mounted.");
} }
return originalGenerateRequest.call(session, initDataType, initData); return originalGenerateRequest.call(session, initDataType, initData);
}; };
// Message update interceptors // Message update interceptors
const originalUpdate = MediaKeySession.prototype.update; const originalUpdate = MediaKeySession.prototype.update;
MediaKeySession.prototype.update = function(response) { MediaKeySession.prototype.update = function (response) {
const uint8 = response instanceof Uint8Array ? response : new Uint8Array(response); const uint8 = response instanceof Uint8Array ? response : new Uint8Array(response);
const base64Response = window.btoa(String.fromCharCode(...uint8)); const base64Response = window.btoa(String.fromCharCode(...uint8));
if (base64Response.startsWith("CAUS") && foundWidevinePssh && remoteCDM && !serviceCertFound) { if (base64Response.startsWith("CAUS") && foundWidevinePssh && remoteCDM && !serviceCertFound) {
@ -511,7 +657,11 @@ MediaKeySession.prototype.update = function(response) {
window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*"); window.postMessage({ type: "__PSSH_DATA__", data: foundWidevinePssh }, "*");
serviceCertFound = true; serviceCertFound = true;
} }
if (!base64Response.startsWith("CAUS") && (foundWidevinePssh || foundPlayreadyPssh) && !keysRetrieved) { if (
!base64Response.startsWith("CAUS") &&
(foundWidevinePssh || foundPlayreadyPssh) &&
!keysRetrieved
) {
if (licenseResponseCounter === 1 || foundChallengeInBody) { if (licenseResponseCounter === 1 || foundChallengeInBody) {
remoteCDM.parseLicense(base64Response); remoteCDM.parseLicense(base64Response);
remoteCDM.getKeys(); remoteCDM.getKeys();
@ -530,17 +680,17 @@ MediaKeySession.prototype.update = function(response) {
console.log("[CLEARKEY] ", clearKeys); console.log("[CLEARKEY] ", clearKeys);
const drmType = { const drmType = {
type: "__DRM_TYPE__", type: "__DRM_TYPE__",
data: 'ClearKey' data: "ClearKey",
}; };
window.postMessage(drmType, "*"); window.postMessage(drmType, "*");
const keysData = { const keysData = {
type: "__KEYS_DATA__", type: "__KEYS_DATA__",
data: clearKeys data: clearKeys,
}; };
window.postMessage(keysData, "*"); window.postMessage(keysData, "*");
} }
}) })
.catch(e => { .catch((e) => {
console.log("[CLEARKEY] Not found"); console.log("[CLEARKEY] Not found");
}); });
} }
@ -549,41 +699,67 @@ MediaKeySession.prototype.update = function(response) {
}; };
// fetch POST interceptor // fetch POST interceptor
(function() { (function () {
const originalFetch = window.fetch; const originalFetch = window.fetch;
window.fetch = async function(resource, config = {}) { window.fetch = async function (resource, config = {}) {
const method = (config.method || 'GET').toUpperCase(); const method = (config.method || "GET").toUpperCase();
if (method === 'POST') { if (method === "POST") {
let body = config.body; let body = config.body;
if (body) { if (body) {
if (body instanceof ArrayBuffer || body instanceof Uint8Array) { if (body instanceof ArrayBuffer || body instanceof Uint8Array) {
const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); const buffer = body instanceof Uint8Array ? body : new Uint8Array(body);
const base64Body = window.btoa(String.fromCharCode(...buffer)); const base64Body = window.btoa(String.fromCharCode(...buffer));
if ((base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64Body !== remoteCDM.challenge) && interceptType === "EME") { if (
(base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) &&
(!remoteCDM ||
remoteCDM.challenge === null ||
base64Body !== remoteCDM.challenge) &&
interceptType === "EME"
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*");
// Block the request // Block the request
return; return;
} }
if ((base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && interceptType == "LICENSE" &&!foundChallengeInBody) { if (
(base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) &&
interceptType == "LICENSE" &&
!foundChallengeInBody
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*");
if (!remoteCDM) { if (!remoteCDM) {
if (base64Body.startsWith("CAES")) { if (base64Body.startsWith("CAES")) {
const { const {
device_type, system_id, security_level, host, secret, device_name device_type,
system_id,
security_level,
host,
secret,
device_name,
} = widevineDeviceInfo; } = widevineDeviceInfo;
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); remoteCDM = new remoteWidevineCDM(
device_type,
system_id,
security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundWidevinePssh); remoteCDM.getChallenge(foundWidevinePssh);
} }
if (base64Body.startsWith("PD94")) { if (base64Body.startsWith("PD94")) {
const { const { security_level, host, secret, device_name } =
security_level, host, secret, device_name playreadyDeviceInfo;
} = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM(
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundPlayreadyPssh); remoteCDM.getChallenge(foundPlayreadyPssh);
} }
@ -596,31 +772,59 @@ MediaKeySession.prototype.update = function(response) {
return originalFetch(resource, config); return originalFetch(resource, config);
} }
} }
if (typeof body === 'string' && !isJson(body)) { if (typeof body === "string" && !isJson(body)) {
const base64EncodedBody = btoa(body); const base64EncodedBody = btoa(body);
if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64EncodedBody !== remoteCDM.challenge) && interceptType === "EME") { if (
(base64EncodedBody.startsWith("CAES") ||
base64EncodedBody.startsWith("PD94")) &&
(!remoteCDM ||
remoteCDM.challenge === null ||
base64EncodedBody !== remoteCDM.challenge) &&
interceptType === "EME"
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*");
// Block the request // Block the request
return; return;
} }
if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && interceptType == "LICENSE" && !foundChallengeInBody) { if (
(base64EncodedBody.startsWith("CAES") ||
base64EncodedBody.startsWith("PD94")) &&
interceptType == "LICENSE" &&
!foundChallengeInBody
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*");
if (!remoteCDM) { if (!remoteCDM) {
if (base64EncodedBody.startsWith("CAES")) { if (base64EncodedBody.startsWith("CAES")) {
const { const {
device_type, system_id, security_level, host, secret, device_name device_type,
system_id,
security_level,
host,
secret,
device_name,
} = widevineDeviceInfo; } = widevineDeviceInfo;
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); remoteCDM = new remoteWidevineCDM(
device_type,
system_id,
security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundWidevinePssh); remoteCDM.getChallenge(foundWidevinePssh);
} }
if (base64EncodedBody.startsWith("PD94")) { if (base64EncodedBody.startsWith("PD94")) {
const { const { security_level, host, secret, device_name } =
security_level, host, secret, device_name playreadyDeviceInfo;
} = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM(
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundPlayreadyPssh); remoteCDM.getChallenge(foundPlayreadyPssh);
} }
@ -633,33 +837,59 @@ MediaKeySession.prototype.update = function(response) {
return originalFetch(resource, config); return originalFetch(resource, config);
} }
} }
if (typeof body === 'string' && isJson(body)) { if (typeof body === "string" && isJson(body)) {
const jsonBody = JSON.parse(body); const jsonBody = JSON.parse(body);
if ((jsonContainsValue(jsonBody, "CAES") || jsonContainsValue(jsonBody, "PD94")) && (!remoteCDM || remoteCDM.challenge === null) && interceptType === "EME") { if (
(jsonContainsValue(jsonBody, "CAES") ||
jsonContainsValue(jsonBody, "PD94")) &&
(!remoteCDM || remoteCDM.challenge === null) &&
interceptType === "EME"
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*");
// Block the request // Block the request
return; return;
} }
if ((jsonContainsValue(jsonBody, "CAES") || jsonContainsValue(jsonBody, "PD94")) && interceptType === "LICENSE" && !foundChallengeInBody) { if (
(jsonContainsValue(jsonBody, "CAES") ||
jsonContainsValue(jsonBody, "PD94")) &&
interceptType === "LICENSE" &&
!foundChallengeInBody
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: resource }, "*");
if (!remoteCDM) { if (!remoteCDM) {
if (jsonContainsValue(jsonBody, "CAES")) { if (jsonContainsValue(jsonBody, "CAES")) {
const { const {
device_type, system_id, security_level, host, secret, device_name device_type,
system_id,
security_level,
host,
secret,
device_name,
} = widevineDeviceInfo; } = widevineDeviceInfo;
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); remoteCDM = new remoteWidevineCDM(
device_type,
system_id,
security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundWidevinePssh); remoteCDM.getChallenge(foundWidevinePssh);
} }
if (jsonContainsValue(jsonBody, "PD94")) { if (jsonContainsValue(jsonBody, "PD94")) {
const { const { security_level, host, secret, device_name } =
security_level, host, secret, device_name playreadyDeviceInfo;
} = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM(
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundPlayreadyPssh); remoteCDM.getChallenge(foundPlayreadyPssh);
} }
@ -679,46 +909,71 @@ MediaKeySession.prototype.update = function(response) {
})(); })();
// XHR POST interceptor // XHR POST interceptor
(function() { (function () {
const originalOpen = XMLHttpRequest.prototype.open; const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send; const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url, async, user, password) { XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this._method = method; this._method = method;
this._url = url; this._url = url;
return originalOpen.apply(this, arguments); return originalOpen.apply(this, arguments);
}; };
XMLHttpRequest.prototype.send = function(body) { XMLHttpRequest.prototype.send = function (body) {
if (this._method && this._method.toUpperCase() === 'POST') { if (this._method && this._method.toUpperCase() === "POST") {
if (body) { if (body) {
if (body instanceof ArrayBuffer || body instanceof Uint8Array) { if (body instanceof ArrayBuffer || body instanceof Uint8Array) {
const buffer = body instanceof Uint8Array ? body : new Uint8Array(body); const buffer = body instanceof Uint8Array ? body : new Uint8Array(body);
const base64Body = window.btoa(String.fromCharCode(...buffer)); const base64Body = window.btoa(String.fromCharCode(...buffer));
if ((base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64Body !== remoteCDM.challenge) && interceptType === "EME") { if (
(base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) &&
(!remoteCDM ||
remoteCDM.challenge === null ||
base64Body !== remoteCDM.challenge) &&
interceptType === "EME"
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
// Block the request // Block the request
return; return;
} }
if ((base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) && interceptType == "LICENSE" &&!foundChallengeInBody) { if (
(base64Body.startsWith("CAES") || base64Body.startsWith("PD94")) &&
interceptType == "LICENSE" &&
!foundChallengeInBody
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
if (!remoteCDM) { if (!remoteCDM) {
if (base64Body.startsWith("CAES")) { if (base64Body.startsWith("CAES")) {
const { const {
device_type, system_id, security_level, host, secret, device_name device_type,
system_id,
security_level,
host,
secret,
device_name,
} = widevineDeviceInfo; } = widevineDeviceInfo;
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); remoteCDM = new remoteWidevineCDM(
device_type,
system_id,
security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundWidevinePssh); remoteCDM.getChallenge(foundWidevinePssh);
} }
if (base64Body.startsWith("PD94")) { if (base64Body.startsWith("PD94")) {
const { const { security_level, host, secret, device_name } =
security_level, host, secret, device_name playreadyDeviceInfo;
} = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM(
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundPlayreadyPssh); remoteCDM.getChallenge(foundPlayreadyPssh);
} }
@ -731,31 +986,59 @@ MediaKeySession.prototype.update = function(response) {
} }
} }
if (typeof body === 'string' && !isJson(body)) { if (typeof body === "string" && !isJson(body)) {
const base64EncodedBody = btoa(body); const base64EncodedBody = btoa(body);
if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && (!remoteCDM || remoteCDM.challenge === null || base64EncodedBody !== remoteCDM.challenge) && interceptType === "EME") { if (
(base64EncodedBody.startsWith("CAES") ||
base64EncodedBody.startsWith("PD94")) &&
(!remoteCDM ||
remoteCDM.challenge === null ||
base64EncodedBody !== remoteCDM.challenge) &&
interceptType === "EME"
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
// Block the request // Block the request
return; return;
} }
if ((base64EncodedBody.startsWith("CAES") || base64EncodedBody.startsWith("PD94")) && interceptType == "LICENSE" && !foundChallengeInBody) { if (
(base64EncodedBody.startsWith("CAES") ||
base64EncodedBody.startsWith("PD94")) &&
interceptType == "LICENSE" &&
!foundChallengeInBody
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
if (!remoteCDM) { if (!remoteCDM) {
if (base64EncodedBody.startsWith("CAES")) { if (base64EncodedBody.startsWith("CAES")) {
const { const {
device_type, system_id, security_level, host, secret, device_name device_type,
system_id,
security_level,
host,
secret,
device_name,
} = widevineDeviceInfo; } = widevineDeviceInfo;
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); remoteCDM = new remoteWidevineCDM(
device_type,
system_id,
security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundWidevinePssh); remoteCDM.getChallenge(foundWidevinePssh);
} }
if (base64EncodedBody.startsWith("PD94")) { if (base64EncodedBody.startsWith("PD94")) {
const { const { security_level, host, secret, device_name } =
security_level, host, secret, device_name playreadyDeviceInfo;
} = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM(
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundPlayreadyPssh); remoteCDM.getChallenge(foundPlayreadyPssh);
} }
@ -768,33 +1051,59 @@ MediaKeySession.prototype.update = function(response) {
} }
} }
if (typeof body === 'string' && isJson(body)) { if (typeof body === "string" && isJson(body)) {
const jsonBody = JSON.parse(body); const jsonBody = JSON.parse(body);
if ((jsonContainsValue(jsonBody, "CAES") || jsonContainsValue(jsonBody, "PD94")) && (!remoteCDM || remoteCDM.challenge === null) && interceptType === "EME") { if (
(jsonContainsValue(jsonBody, "CAES") ||
jsonContainsValue(jsonBody, "PD94")) &&
(!remoteCDM || remoteCDM.challenge === null) &&
interceptType === "EME"
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
// Block the request // Block the request
return; return;
} }
if ((jsonContainsValue(jsonBody, "CAES") || jsonContainsValue(jsonBody, "PD94")) && interceptType === "LICENSE" && !foundChallengeInBody) { if (
(jsonContainsValue(jsonBody, "CAES") ||
jsonContainsValue(jsonBody, "PD94")) &&
interceptType === "LICENSE" &&
!foundChallengeInBody
) {
foundChallengeInBody = true; foundChallengeInBody = true;
window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*"); window.postMessage({ type: "__LICENSE_URL__", data: this._url }, "*");
if (!remoteCDM) { if (!remoteCDM) {
if (jsonContainsValue(jsonBody, "CAES")) { if (jsonContainsValue(jsonBody, "CAES")) {
const { const {
device_type, system_id, security_level, host, secret, device_name device_type,
system_id,
security_level,
host,
secret,
device_name,
} = widevineDeviceInfo; } = widevineDeviceInfo;
remoteCDM = new remoteWidevineCDM(device_type, system_id, security_level, host, secret, device_name); remoteCDM = new remoteWidevineCDM(
device_type,
system_id,
security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundWidevinePssh); remoteCDM.getChallenge(foundWidevinePssh);
} }
if (jsonContainsValue(jsonBody, "PD94")) { if (jsonContainsValue(jsonBody, "PD94")) {
const { const { security_level, host, secret, device_name } =
security_level, host, secret, device_name playreadyDeviceInfo;
} = playreadyDeviceInfo; remoteCDM = new remotePlayReadyCDM(
remoteCDM = new remotePlayReadyCDM(security_level, host, secret, device_name); security_level,
host,
secret,
device_name
);
remoteCDM.openSession(); remoteCDM.openSession();
remoteCDM.getChallenge(foundPlayreadyPssh); remoteCDM.getChallenge(foundPlayreadyPssh);
} }