EME Logger

Inject EME interface and log its function calls.

目前为 2022-10-02 提交的版本。查看 最新版本

// ==UserScript==
// @name         EME Logger
// @namespace    http://gf.qytechs.cn/
// @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);
        });
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址