forked from tpd94/CDRM-Extension
refactor: extract redundant code into helpers (part 1)
This commit is contained in:
parent
9cff5b44bd
commit
3bd2e0f465
273
src/inject.js
273
src/inject.js
@ -68,6 +68,35 @@ function safeHeaderShellEscape(str) {
|
|||||||
.replace(/\n/g, ""); // strip newlines
|
.replace(/\n/g, ""); // strip newlines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function headersToFlags(headersObj) {
|
||||||
|
return Object.entries(headersObj)
|
||||||
|
.map(
|
||||||
|
([key, val]) =>
|
||||||
|
'--add-headers "' +
|
||||||
|
safeHeaderShellEscape(key) +
|
||||||
|
": " +
|
||||||
|
safeHeaderShellEscape(val) +
|
||||||
|
'"'
|
||||||
|
)
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleManifestDetection(url, headersObj, contentType, source) {
|
||||||
|
window.postMessage({ type: "__MANIFEST_URL__", data: url }, "*");
|
||||||
|
console.log(`[Manifest][${source}]`, url, contentType);
|
||||||
|
|
||||||
|
const headerFlags = headersToFlags(headersObj);
|
||||||
|
|
||||||
|
window.postMessage(
|
||||||
|
{
|
||||||
|
type: "__MANIFEST_HEADERS__",
|
||||||
|
url,
|
||||||
|
headers: headerFlags,
|
||||||
|
},
|
||||||
|
"*"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Intercep network to find manifest
|
// Intercep network to find manifest
|
||||||
function injectManifestInterceptor() {
|
function injectManifestInterceptor() {
|
||||||
// Execute the interceptor code directly instead of injecting a script
|
// Execute the interceptor code directly instead of injecting a script
|
||||||
@ -108,33 +137,11 @@ function injectManifestInterceptor() {
|
|||||||
const url = typeof input === "string" ? input : input.url;
|
const url = typeof input === "string" ? input : input.url;
|
||||||
|
|
||||||
if (isProbablyManifest(text, contentType)) {
|
if (isProbablyManifest(text, contentType)) {
|
||||||
window.postMessage({ type: "__MANIFEST_URL__", data: url }, "*");
|
|
||||||
console.log("[Manifest][fetch]", url, contentType);
|
|
||||||
|
|
||||||
const headersObj = {};
|
const headersObj = {};
|
||||||
clone.headers.forEach((value, key) => {
|
clone.headers.forEach((value, key) => {
|
||||||
headersObj[key] = value;
|
headersObj[key] = value;
|
||||||
});
|
});
|
||||||
|
handleManifestDetection(url, headersObj, contentType, "fetch");
|
||||||
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) {}
|
} catch (e) {}
|
||||||
|
|
||||||
@ -156,9 +163,6 @@ function injectManifestInterceptor() {
|
|||||||
const text = this.responseText;
|
const text = this.responseText;
|
||||||
|
|
||||||
if (isProbablyManifest(text, contentType)) {
|
if (isProbablyManifest(text, contentType)) {
|
||||||
window.postMessage({ type: "__MANIFEST_URL__", data: this.__url }, "*");
|
|
||||||
console.log("[Manifest][xhr]", this.__url, contentType);
|
|
||||||
|
|
||||||
const xhrHeaders = {};
|
const xhrHeaders = {};
|
||||||
const rawHeaders = this.getAllResponseHeaders().trim().split(/\r?\n/);
|
const rawHeaders = this.getAllResponseHeaders().trim().split(/\r?\n/);
|
||||||
rawHeaders.forEach((line) => {
|
rawHeaders.forEach((line) => {
|
||||||
@ -167,26 +171,7 @@ function injectManifestInterceptor() {
|
|||||||
xhrHeaders[parts[0]] = parts[1];
|
xhrHeaders[parts[0]] = parts[1];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
handleManifestDetection(this.__url, xhrHeaders, contentType, "xhr");
|
||||||
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) {}
|
} catch (e) {}
|
||||||
});
|
});
|
||||||
@ -197,21 +182,19 @@ function injectManifestInterceptor() {
|
|||||||
|
|
||||||
injectManifestInterceptor();
|
injectManifestInterceptor();
|
||||||
|
|
||||||
// PlayReady Remote CDM Class
|
class RemoteCDMBase {
|
||||||
class remotePlayReadyCDM {
|
constructor({ host, secret, device_name, security_level }) {
|
||||||
constructor(security_level, host, secret, device_name) {
|
|
||||||
this.security_level = security_level;
|
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.secret = secret;
|
this.secret = secret;
|
||||||
this.device_name = device_name;
|
this.device_name = device_name;
|
||||||
|
this.security_level = security_level;
|
||||||
this.session_id = null;
|
this.session_id = null;
|
||||||
this.challenge = null;
|
this.challenge = null;
|
||||||
this.keys = null;
|
this.keys = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open PlayReady session
|
openSession(path) {
|
||||||
openSession() {
|
const url = `${this.host}${path}/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");
|
||||||
@ -219,126 +202,127 @@ class remotePlayReadyCDM {
|
|||||||
const jsonData = JSON.parse(xhr.responseText);
|
const jsonData = JSON.parse(xhr.responseText);
|
||||||
if (jsonData.data?.session_id) {
|
if (jsonData.data?.session_id) {
|
||||||
this.session_id = jsonData.data.session_id;
|
this.session_id = jsonData.data.session_id;
|
||||||
console.log("PlayReady session opened:", this.session_id);
|
console.log("Session opened:", this.session_id);
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to open PlayReady session:", jsonData.message);
|
console.error("Failed to open session:", jsonData.message);
|
||||||
throw new Error("Failed to open PlayReady session");
|
throw new Error("Failed to open session");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get PlayReady challenge
|
getChallenge(path, body) {
|
||||||
getChallenge(init_data) {
|
const url = `${this.host}${path}/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 = {
|
|
||||||
session_id: this.session_id,
|
|
||||||
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);
|
||||||
if (jsonData.data?.challenge) {
|
if (jsonData.data?.challenge) {
|
||||||
this.challenge = btoa(jsonData.data.challenge);
|
this.challenge = btoa(jsonData.data.challenge);
|
||||||
console.log("PlayReady challenge received:", this.challenge);
|
console.log("Challenge received:", this.challenge);
|
||||||
|
} else if (jsonData.data?.challenge_b64) {
|
||||||
|
this.challenge = jsonData.data.challenge_b64;
|
||||||
|
console.log("Challenge received:", this.challenge);
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to get PlayReady challenge:", jsonData.message);
|
console.error("Failed to get challenge:", jsonData.message);
|
||||||
throw new Error("Failed to get PlayReady challenge");
|
throw new Error("Failed to get challenge");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse PlayReady license response
|
parseLicense(path, body) {
|
||||||
parseLicense(license_message) {
|
const url = `${this.host}${path}/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 = {
|
|
||||||
session_id: this.session_id,
|
|
||||||
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 (
|
if (jsonData.status === 200 || jsonData.message?.includes("parsed and loaded")) {
|
||||||
jsonData.message === "Successfully parsed and loaded the Keys from the License message"
|
console.log("License response parsed successfully");
|
||||||
) {
|
|
||||||
console.log("PlayReady license response parsed successfully");
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to parse PlayReady license response:", jsonData.message);
|
console.error("Failed to parse license response:", jsonData.message);
|
||||||
throw new Error("Failed to parse PlayReady license response");
|
throw new Error("Failed to parse license response");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get PlayReady keys
|
getKeys(path, body, extraPath = "") {
|
||||||
getKeys() {
|
const url = `${this.host}${path}/get_keys${extraPath}`;
|
||||||
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 = {
|
|
||||||
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) {
|
||||||
this.keys = jsonData.data.keys;
|
this.keys = jsonData.data.keys;
|
||||||
console.log("PlayReady keys received:", this.keys);
|
console.log("Keys received:", this.keys);
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to get PlayReady keys:", jsonData.message);
|
console.error("Failed to get keys:", jsonData.message);
|
||||||
throw new Error("Failed to get PlayReady keys");
|
throw new Error("Failed to get keys");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close PlayReady session
|
closeSession(path) {
|
||||||
closeSession() {
|
const url = `${this.host}${path}/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) {
|
||||||
console.log("PlayReady session closed successfully");
|
console.log("Session closed successfully");
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to close PlayReady session:", jsonData.message);
|
console.error("Failed to close session:", jsonData.message);
|
||||||
throw new Error("Failed to close PlayReady session");
|
throw new Error("Failed to close session");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PlayReady Remote CDM Class
|
||||||
|
class remotePlayReadyCDM extends RemoteCDMBase {
|
||||||
|
constructor(security_level, host, secret, device_name) {
|
||||||
|
super({ host, secret, device_name, security_level });
|
||||||
|
}
|
||||||
|
|
||||||
|
openSession() {
|
||||||
|
super.openSession(`/remotecdm/playready/${this.device_name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getChallenge(init_data) {
|
||||||
|
super.getChallenge(`/remotecdm/playready/${this.device_name}`, {
|
||||||
|
session_id: this.session_id,
|
||||||
|
init_data: init_data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
parseLicense(license_message) {
|
||||||
|
return super.parseLicense(`/remotecdm/playready/${this.device_name}`, {
|
||||||
|
session_id: this.session_id,
|
||||||
|
license_message: license_message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getKeys() {
|
||||||
|
super.getKeys(`/remotecdm/playready/${this.device_name}`, {
|
||||||
|
session_id: this.session_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSession() {
|
||||||
|
super.closeSession(`/remotecdm/playready/${this.device_name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Widevine Remote CDM Class
|
// Widevine Remote CDM Class
|
||||||
class remoteWidevineCDM {
|
class remoteWidevineCDM extends RemoteCDMBase {
|
||||||
constructor(device_type, system_id, security_level, host, secret, device_name) {
|
constructor(device_type, system_id, security_level, host, secret, device_name) {
|
||||||
|
super({ host, secret, device_name, security_level });
|
||||||
this.device_type = device_type;
|
this.device_type = device_type;
|
||||||
this.system_id = system_id;
|
this.system_id = system_id;
|
||||||
this.security_level = security_level;
|
|
||||||
this.host = host;
|
|
||||||
this.secret = secret;
|
|
||||||
this.device_name = device_name;
|
|
||||||
this.session_id = null;
|
|
||||||
this.challenge = null;
|
|
||||||
this.keys = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open Widevine session
|
|
||||||
openSession() {
|
openSession() {
|
||||||
const url = `${this.host}/remotecdm/widevine/${this.device_name}/open`;
|
super.openSession(`/remotecdm/widevine/${this.device_name}`);
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("GET", url, false);
|
|
||||||
xhr.setRequestHeader("Content-Type", "application/json");
|
|
||||||
xhr.send();
|
|
||||||
const jsonData = JSON.parse(xhr.responseText);
|
|
||||||
if (jsonData.data?.session_id) {
|
|
||||||
this.session_id = jsonData.data.session_id;
|
|
||||||
console.log("Widevine session opened:", this.session_id);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to open Widevine session:", jsonData.message);
|
|
||||||
throw new Error("Failed to open Widevine session");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Widevine service certificate
|
|
||||||
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();
|
||||||
@ -358,7 +342,6 @@ class remoteWidevineCDM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
||||||
@ -380,61 +363,25 @@ class remoteWidevineCDM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse Widevine license response
|
|
||||||
parseLicense(license_message) {
|
parseLicense(license_message) {
|
||||||
const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`;
|
return super.parseLicense(`/remotecdm/widevine/${this.device_name}`, {
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("POST", url, false);
|
|
||||||
xhr.setRequestHeader("Content-Type", "application/json");
|
|
||||||
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));
|
|
||||||
const jsonData = JSON.parse(xhr.responseText);
|
|
||||||
if (jsonData.status === 200) {
|
|
||||||
console.log("Widevine license response parsed successfully");
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
console.error("Failed to parse Widevine license response:", jsonData.message);
|
|
||||||
throw new Error("Failed to parse Widevine license response");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Widevine keys
|
|
||||||
getKeys() {
|
getKeys() {
|
||||||
const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_keys/ALL`;
|
super.getKeys(
|
||||||
const xhr = new XMLHttpRequest();
|
`/remotecdm/widevine/${this.device_name}`,
|
||||||
xhr.open("POST", url, false);
|
{
|
||||||
xhr.setRequestHeader("Content-Type", "application/json");
|
|
||||||
const body = {
|
|
||||||
session_id: this.session_id,
|
session_id: this.session_id,
|
||||||
};
|
},
|
||||||
xhr.send(JSON.stringify(body));
|
"/ALL"
|
||||||
const jsonData = JSON.parse(xhr.responseText);
|
);
|
||||||
if (jsonData.data?.keys) {
|
|
||||||
this.keys = jsonData.data.keys;
|
|
||||||
console.log("Widevine keys received:", this.keys);
|
|
||||||
} else {
|
|
||||||
console.error("Failed to get Widevine keys:", jsonData.message);
|
|
||||||
throw new Error("Failed to get Widevine keys");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close Widevine session
|
|
||||||
closeSession() {
|
closeSession() {
|
||||||
const url = `${this.host}/remotecdm/widevine/${this.device_name}/close/${this.session_id}`;
|
super.closeSession(`/remotecdm/widevine/${this.device_name}`);
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("GET", url, false);
|
|
||||||
xhr.setRequestHeader("Content-Type", "application/json");
|
|
||||||
xhr.send();
|
|
||||||
const jsonData = JSON.parse(xhr.responseText);
|
|
||||||
if (jsonData) {
|
|
||||||
console.log("Widevine session closed successfully");
|
|
||||||
} else {
|
|
||||||
console.error("Failed to close Widevine session:", jsonData.message);
|
|
||||||
throw new Error("Failed to close Widevine session");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user