refactor: extract redundant code into helpers (part 1)
This commit is contained in:
		
							parent
							
								
									9cff5b44bd
								
							
						
					
					
						commit
						3bd2e0f465
					
				
							
								
								
									
										275
									
								
								src/inject.js
									
									
									
									
									
								
							
							
						
						
									
										275
									
								
								src/inject.js
									
									
									
									
									
								
							@ -68,6 +68,35 @@ function safeHeaderShellEscape(str) {
 | 
			
		||||
        .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
 | 
			
		||||
function injectManifestInterceptor() {
 | 
			
		||||
    // Execute the interceptor code directly instead of injecting a script
 | 
			
		||||
@ -108,33 +137,11 @@ function injectManifestInterceptor() {
 | 
			
		||||
                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,
 | 
			
		||||
                        },
 | 
			
		||||
                        "*"
 | 
			
		||||
                    );
 | 
			
		||||
                    handleManifestDetection(url, headersObj, contentType, "fetch");
 | 
			
		||||
                }
 | 
			
		||||
            } catch (e) {}
 | 
			
		||||
 | 
			
		||||
@ -156,9 +163,6 @@ function injectManifestInterceptor() {
 | 
			
		||||
                    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) => {
 | 
			
		||||
@ -167,26 +171,7 @@ function injectManifestInterceptor() {
 | 
			
		||||
                                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,
 | 
			
		||||
                            },
 | 
			
		||||
                            "*"
 | 
			
		||||
                        );
 | 
			
		||||
                        handleManifestDetection(this.__url, xhrHeaders, contentType, "xhr");
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (e) {}
 | 
			
		||||
            });
 | 
			
		||||
@ -197,21 +182,19 @@ function injectManifestInterceptor() {
 | 
			
		||||
 | 
			
		||||
injectManifestInterceptor();
 | 
			
		||||
 | 
			
		||||
// PlayReady Remote CDM Class
 | 
			
		||||
class remotePlayReadyCDM {
 | 
			
		||||
    constructor(security_level, host, secret, device_name) {
 | 
			
		||||
        this.security_level = security_level;
 | 
			
		||||
class RemoteCDMBase {
 | 
			
		||||
    constructor({ host, secret, device_name, security_level }) {
 | 
			
		||||
        this.host = host;
 | 
			
		||||
        this.secret = secret;
 | 
			
		||||
        this.device_name = device_name;
 | 
			
		||||
        this.security_level = security_level;
 | 
			
		||||
        this.session_id = null;
 | 
			
		||||
        this.challenge = null;
 | 
			
		||||
        this.keys = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Open PlayReady session
 | 
			
		||||
    openSession() {
 | 
			
		||||
        const url = `${this.host}/remotecdm/playready/${this.device_name}/open`;
 | 
			
		||||
    openSession(path) {
 | 
			
		||||
        const url = `${this.host}${path}/open`;
 | 
			
		||||
        const xhr = new XMLHttpRequest();
 | 
			
		||||
        xhr.open("GET", url, false);
 | 
			
		||||
        xhr.setRequestHeader("Content-Type", "application/json");
 | 
			
		||||
@ -219,126 +202,127 @@ class remotePlayReadyCDM {
 | 
			
		||||
        const jsonData = JSON.parse(xhr.responseText);
 | 
			
		||||
        if (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 {
 | 
			
		||||
            console.error("Failed to open PlayReady session:", jsonData.message);
 | 
			
		||||
            throw new Error("Failed to open PlayReady session");
 | 
			
		||||
            console.error("Failed to open session:", jsonData.message);
 | 
			
		||||
            throw new Error("Failed to open session");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get PlayReady challenge
 | 
			
		||||
    getChallenge(init_data) {
 | 
			
		||||
        const url = `${this.host}/remotecdm/playready/${this.device_name}/get_license_challenge`;
 | 
			
		||||
    getChallenge(path, body) {
 | 
			
		||||
        const url = `${this.host}${path}/get_license_challenge`;
 | 
			
		||||
        const xhr = new XMLHttpRequest();
 | 
			
		||||
        xhr.open("POST", url, false);
 | 
			
		||||
        xhr.setRequestHeader("Content-Type", "application/json");
 | 
			
		||||
        const body = {
 | 
			
		||||
            session_id: this.session_id,
 | 
			
		||||
            init_data: init_data,
 | 
			
		||||
        };
 | 
			
		||||
        xhr.send(JSON.stringify(body));
 | 
			
		||||
        const jsonData = JSON.parse(xhr.responseText);
 | 
			
		||||
        if (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 {
 | 
			
		||||
            console.error("Failed to get PlayReady challenge:", jsonData.message);
 | 
			
		||||
            throw new Error("Failed to get PlayReady challenge");
 | 
			
		||||
            console.error("Failed to get challenge:", jsonData.message);
 | 
			
		||||
            throw new Error("Failed to get challenge");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Parse PlayReady license response
 | 
			
		||||
    parseLicense(license_message) {
 | 
			
		||||
        const url = `${this.host}/remotecdm/playready/${this.device_name}/parse_license`;
 | 
			
		||||
    parseLicense(path, body) {
 | 
			
		||||
        const url = `${this.host}${path}/parse_license`;
 | 
			
		||||
        const xhr = new XMLHttpRequest();
 | 
			
		||||
        xhr.open("POST", url, false);
 | 
			
		||||
        xhr.setRequestHeader("Content-Type", "application/json");
 | 
			
		||||
        const body = {
 | 
			
		||||
            session_id: this.session_id,
 | 
			
		||||
            license_message: license_message,
 | 
			
		||||
        };
 | 
			
		||||
        xhr.send(JSON.stringify(body));
 | 
			
		||||
        const jsonData = JSON.parse(xhr.responseText);
 | 
			
		||||
        if (
 | 
			
		||||
            jsonData.message === "Successfully parsed and loaded the Keys from the License message"
 | 
			
		||||
        ) {
 | 
			
		||||
            console.log("PlayReady license response parsed successfully");
 | 
			
		||||
        if (jsonData.status === 200 || jsonData.message?.includes("parsed and loaded")) {
 | 
			
		||||
            console.log("License response parsed successfully");
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            console.error("Failed to parse PlayReady license response:", jsonData.message);
 | 
			
		||||
            throw new Error("Failed to parse PlayReady license response");
 | 
			
		||||
            console.error("Failed to parse license response:", jsonData.message);
 | 
			
		||||
            throw new Error("Failed to parse license response");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get PlayReady keys
 | 
			
		||||
    getKeys() {
 | 
			
		||||
        const url = `${this.host}/remotecdm/playready/${this.device_name}/get_keys`;
 | 
			
		||||
    getKeys(path, body, extraPath = "") {
 | 
			
		||||
        const url = `${this.host}${path}/get_keys${extraPath}`;
 | 
			
		||||
        const xhr = new XMLHttpRequest();
 | 
			
		||||
        xhr.open("POST", url, false);
 | 
			
		||||
        xhr.setRequestHeader("Content-Type", "application/json");
 | 
			
		||||
        const body = {
 | 
			
		||||
            session_id: this.session_id,
 | 
			
		||||
        };
 | 
			
		||||
        xhr.send(JSON.stringify(body));
 | 
			
		||||
        const jsonData = JSON.parse(xhr.responseText);
 | 
			
		||||
        if (jsonData.data?.keys) {
 | 
			
		||||
            this.keys = jsonData.data.keys;
 | 
			
		||||
            console.log("PlayReady keys received:", this.keys);
 | 
			
		||||
            console.log("Keys received:", this.keys);
 | 
			
		||||
        } else {
 | 
			
		||||
            console.error("Failed to get PlayReady keys:", jsonData.message);
 | 
			
		||||
            throw new Error("Failed to get PlayReady keys");
 | 
			
		||||
            console.error("Failed to get keys:", jsonData.message);
 | 
			
		||||
            throw new Error("Failed to get keys");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Close PlayReady session
 | 
			
		||||
    closeSession() {
 | 
			
		||||
        const url = `${this.host}/remotecdm/playready/${this.device_name}/close/${this.session_id}`;
 | 
			
		||||
    closeSession(path) {
 | 
			
		||||
        const url = `${this.host}${path}/close/${this.session_id}`;
 | 
			
		||||
        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("PlayReady session closed successfully");
 | 
			
		||||
            console.log("Session closed successfully");
 | 
			
		||||
        } else {
 | 
			
		||||
            console.error("Failed to close PlayReady session:", jsonData.message);
 | 
			
		||||
            throw new Error("Failed to close PlayReady session");
 | 
			
		||||
            console.error("Failed to close session:", jsonData.message);
 | 
			
		||||
            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
 | 
			
		||||
class remoteWidevineCDM {
 | 
			
		||||
class remoteWidevineCDM extends RemoteCDMBase {
 | 
			
		||||
    constructor(device_type, system_id, security_level, host, secret, device_name) {
 | 
			
		||||
        super({ host, secret, device_name, security_level });
 | 
			
		||||
        this.device_type = device_type;
 | 
			
		||||
        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() {
 | 
			
		||||
        const url = `${this.host}/remotecdm/widevine/${this.device_name}/open`;
 | 
			
		||||
        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");
 | 
			
		||||
        }
 | 
			
		||||
        super.openSession(`/remotecdm/widevine/${this.device_name}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set Widevine service certificate
 | 
			
		||||
    setServiceCertificate(certificate) {
 | 
			
		||||
        const url = `${this.host}/remotecdm/widevine/${this.device_name}/set_service_certificate`;
 | 
			
		||||
        const xhr = new XMLHttpRequest();
 | 
			
		||||
@ -358,7 +342,6 @@ class remoteWidevineCDM {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get Widevine challenge
 | 
			
		||||
    getChallenge(init_data, license_type = "STREAMING") {
 | 
			
		||||
        const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_license_challenge/${license_type}`;
 | 
			
		||||
        const xhr = new XMLHttpRequest();
 | 
			
		||||
@ -380,61 +363,25 @@ class remoteWidevineCDM {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Parse Widevine license response
 | 
			
		||||
    parseLicense(license_message) {
 | 
			
		||||
        const url = `${this.host}/remotecdm/widevine/${this.device_name}/parse_license`;
 | 
			
		||||
        const xhr = new XMLHttpRequest();
 | 
			
		||||
        xhr.open("POST", url, false);
 | 
			
		||||
        xhr.setRequestHeader("Content-Type", "application/json");
 | 
			
		||||
        const body = {
 | 
			
		||||
        return super.parseLicense(`/remotecdm/widevine/${this.device_name}`, {
 | 
			
		||||
            session_id: this.session_id,
 | 
			
		||||
            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() {
 | 
			
		||||
        const url = `${this.host}/remotecdm/widevine/${this.device_name}/get_keys/ALL`;
 | 
			
		||||
        const xhr = new XMLHttpRequest();
 | 
			
		||||
        xhr.open("POST", url, false);
 | 
			
		||||
        xhr.setRequestHeader("Content-Type", "application/json");
 | 
			
		||||
        const body = {
 | 
			
		||||
            session_id: this.session_id,
 | 
			
		||||
        };
 | 
			
		||||
        xhr.send(JSON.stringify(body));
 | 
			
		||||
        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");
 | 
			
		||||
        }
 | 
			
		||||
        super.getKeys(
 | 
			
		||||
            `/remotecdm/widevine/${this.device_name}`,
 | 
			
		||||
            {
 | 
			
		||||
                session_id: this.session_id,
 | 
			
		||||
            },
 | 
			
		||||
            "/ALL"
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Close Widevine session
 | 
			
		||||
    closeSession() {
 | 
			
		||||
        const url = `${this.host}/remotecdm/widevine/${this.device_name}/close/${this.session_id}`;
 | 
			
		||||
        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");
 | 
			
		||||
        }
 | 
			
		||||
        super.closeSession(`/remotecdm/widevine/${this.device_name}`);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user