EME Logger

Inject EME interface and log its function calls.

ของเมื่อวันที่ 02-10-2022 ดู เวอร์ชันล่าสุด

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         EME Logger
// @namespace    http://greatest.deepsurf.us/
// @version      1.0
// @description  Inject EME interface and log its function calls.
// @author       cramer
// @match        *://*/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(async () => {
    const indent = (s,n=4) => s.split('\n').map(l=>Array(n).fill(' ').join('')+l).join('\n');

    const b64 = {
        decode: s => Uint8Array.from(atob(s), c => c.charCodeAt(0)),
        encode: b => btoa(String.fromCharCode(...new Uint8Array(b)))
    };

    const fnproxy = (object, func) => new Proxy(object, { apply: func });

    const proxy = (object, key, func) => Object.hasOwnProperty.call(object, key) && Object.defineProperty(object, key, {
        value: fnproxy(object[key], func)
    });

    function messageHandler(event) {
        const keySession = event.target;
        const {sessionId} = keySession;
        const {message, messageType} = event;
        const listeners = keySession.getEventListeners('message').filter(l => l !== messageHandler);
        console.groupCollapsed(
            `[EME] MediaKeySession::message\n` +
            `    Session ID: ${sessionId || '(not available)'}\n` +
            `    Message Type: ${messageType}\n` +
            `    Message: ${b64.encode(message)}` +
            '\n    Listeners:', listeners
        );
        console.trace();
        console.groupEnd();
    }

    function keystatuseschangeHandler(event) {
        const keySession = event.target;
        const {sessionId} = keySession;
        const listeners = keySession.getEventListeners('keystatuseschange').filter(l => l !== keystatuseschangeHandler);
        console.groupCollapsed(
            `[EME] MediaKeySession::keystatuseschange\n` +
            `    Session ID: ${sessionId || '(not available)'}\n` +
            Array.from(keySession.keyStatuses).map(([keyId, status]) =>
                                                   `    [${status.toUpperCase()}] ${b64.encode(keyId)}`
                                                  ).join('\n') +
            '\n    Listeners:', listeners
        );
        console.trace();
        console.groupEnd();
    }

    function getEventListeners(type) {
        if (this == null) return [];
        const store = this[Symbol.for(getEventListeners)];
        if (store == null || store[type] == null) return [];
        return store[type];
    }

    EventTarget.prototype.getEventListeners = getEventListeners;

    typeof Navigator !== 'undefined' && proxy(Navigator.prototype, 'requestMediaKeySystemAccess', async (_target, _this, _args) => {
        const [keySystem, supportedConfigurations] = _args;
        console.groupCollapsed(
            `[EME] Navigator::requestMediaKeySystemAccess\n` +
            `    Key System: ${keySystem}\n` +
            `    Supported Configurations:\n` +
            indent(JSON.stringify(supportedConfigurations, null, '    '))
        );
        console.trace();
        console.groupEnd();
        return _target.apply(_this, _args);
    });

    typeof MediaKeySystemAccess !== 'undefined' && proxy(MediaKeySystemAccess.prototype, 'createMediaKeys', async (_target, _this, _args) => {
        console.groupCollapsed(
            `[EME] MediaKeySystemAccess::createMediaKeys\n` +
            `    Key System: ${_this.keySystem}\n` +
            `    Configurations:\n` +
            indent(JSON.stringify(_this.getConfiguration(), null, '    '))
        );
        console.trace();
        console.groupEnd();
        return _target.apply(_this, _args);
    });

    if (typeof MediaKeys !== 'undefined') {
        proxy(MediaKeys.prototype, 'setServerCertificate', async (_target, _this, _args) => {
            const [serverCertificate] = _args;
            console.groupCollapsed(
                `[EME] MediaKeys::setServerCertificate\n` +
                `    Server Certificate: ${b64.encode(serverCertificate)}`
            );
            console.trace();
            console.groupEnd();
            return _target.apply(_this, _args);
        });

        proxy(MediaKeys.prototype, 'createSession', (_target, _this, _args) => {
            const [sessionType] = _args;
            console.groupCollapsed(
                `[EME] MediaKeys::createSession\n` +
                `    Session Type: ${sessionType || 'temporary (default)'}`
            );
            console.trace();
            console.groupEnd();
            const session = _target.apply(_this, _args);
            session.addEventListener('message', messageHandler);
            session.addEventListener('keystatuseschange', keystatuseschangeHandler);
            return session;
        });
    }

    if (typeof EventTarget !== 'undefined') {
        proxy(EventTarget.prototype, 'addEventListener', async (_target, _this, _args) => {
            if (_this != null) {
                const [type, listener] = _args;
                const storeKey = Symbol.for(getEventListeners);
                if (!(storeKey in _this)) _this[storeKey] = {};
                const store = _this[storeKey];
                if (!(type in store)) store[type] = [];
                const listeners = store[type];
                if (listeners.indexOf(listener) < 0) {
                    listeners.push(listener);
                }
            }
            return _target.apply(_this, _args);
        });

        proxy(EventTarget.prototype, 'removeEventListener', async (_target, _this, _args) => {
            if (_this != null) {
                const [type, listener] = _args;
                const storeKey = Symbol.for(getEventListeners);
                if (!(storeKey in _this)) return;
                const store = _this[storeKey];
                if (!(type in store)) return;
                const listeners = store[type];
                const index = listeners.indexOf(listener);
                if (index >= 0) {
                    if (listeners.length === 1) {
                        delete store[type];
                    } else {
                        listeners.splice(index, 1);
                    }
                }
            }
            return _target.apply(_this, _args);
        });
    }

    if (typeof MediaKeySession !== 'undefined') {
        proxy(MediaKeySession.prototype, 'generateRequest', async (_target, _this, _args) => {
            const [initDataType, initData] = _args;
            console.groupCollapsed(
                `[EME] MediaKeySession::generateRequest\n` +
                `    Session ID: ${_this.sessionId || '(not available)'}\n` +
                `    Init Data Type: ${initDataType}\n` +
                `    Init Data: ${b64.encode(initData)}`
            );
            console.trace();
            console.groupEnd();
            return _target.apply(_this, _args);
        });

        proxy(MediaKeySession.prototype, 'load', async (_target, _this, _args) => {
            const [sessionId] = _args;
            console.groupCollapsed(
                `[EME] MediaKeySession::load\n` +
                `    Session ID: ${sessionId || '(not available)'}`
            );
            console.trace();
            console.groupEnd();
            return _target.apply(_this, _args);
        });

        proxy(MediaKeySession.prototype, 'update', async (_target, _this, _args) => {
            const [response] = _args;
            console.groupCollapsed(
                `[EME] MediaKeySession::update\n` +
                `    Session ID: ${_this.sessionId || '(not available)'}\n` +
                `    Response: ${b64.encode(response)}`
            );
            console.trace();
            console.groupEnd();
            return _target.apply(_this, _args);
        });

        proxy(MediaKeySession.prototype, 'close', async (_target, _this, _args) => {
            console.groupCollapsed(
                `[EME] MediaKeySession::close\n` +
                `    Session ID: ${_this.sessionId || '(not available)'}`
            );
            console.trace();
            console.groupEnd();
            return _target.apply(_this, _args);
        });

        proxy(MediaKeySession.prototype, 'remove', async (_target, _this, _args) => {
            console.groupCollapsed(
                `[EME] MediaKeySession::remove\n` +
                `    Session ID: ${_this.sessionId || '(not available)'}`
            );
            console.trace();
            console.groupEnd();
            return _target.apply(_this, _args);
        });
    }
})();