EME Logger

Inject EME interface and log its function calls.

目前为 2018-11-21 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name EME Logger
  3. // @namespace http://greasyfork.org/
  4. // @version 0.4
  5. // @description Inject EME interface and log its function calls.
  6. // @author cramer
  7. // @match *://*/*
  8. // @run-at document-start
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (async () => {
  13. const indent = (s,n=4) => s.split('\n').map(l=>Array(n).fill(' ').join('')+l).join('\n');
  14.  
  15. const b64 = {
  16. decode: s => Uint8Array.from(atob(s), c => c.charCodeAt(0)),
  17. encode: b => btoa(String.fromCharCode(...new Uint8Array(b)))
  18. };
  19.  
  20. const fnproxy = (object, func) => new Proxy(object, { apply: func });
  21.  
  22. const proxy = (object, key, func) => Object.defineProperty(object, key, {
  23. value: fnproxy(object[key], func)
  24. });
  25.  
  26. proxy(Navigator.prototype, 'requestMediaKeySystemAccess', async (_target, _this, _args) => {
  27. const [keySystem, supportedConfigurations] = _args;
  28. console.log(
  29. `[EME] Navigator::requestMediaKeySystemAccess\n` +
  30. ` Key System: ${keySystem}\n` +
  31. ` Supported Configurations:\n` +
  32. indent(JSON.stringify(supportedConfigurations, null, ' '))
  33. );
  34. console.trace();
  35. return _target.apply(_this, _args);
  36. });
  37.  
  38. proxy(MediaKeySystemAccess.prototype, 'createMediaKeys', async (_target, _this, _args) => {
  39. console.log(
  40. `[EME] MediaKeySystemAccess::createMediaKeys\n` +
  41. ` Key System: ${_this.keySystem}\n` +
  42. ` Configurations:\n` +
  43. indent(JSON.stringify(_this.getConfiguration(), null, ' '))
  44. );
  45. console.trace();
  46. return _target.apply(_this, _args);
  47. });
  48.  
  49. proxy(MediaKeys.prototype, 'setServerCertificate', async (_target, _this, _args) => {
  50. const [serverCertificate] = _args;
  51. console.log(
  52. `[EME] MediaKeys::setServerCertificate\n` +
  53. ` Server Certificate: ${b64.encode(serverCertificate)}`
  54. );
  55. console.trace();
  56. return _target.apply(_this, _args);
  57. });
  58.  
  59. function messageHandler(event) {
  60. const keySession = event.target;
  61. const {sessionId} = keySession;
  62. const {message, messageType} = event;
  63. const listeners = keySession.getEventListeners('message').filter(l => l !== messageHandler);
  64. console.log(
  65. `[EME] MediaKeySession::message\n` +
  66. ` Session ID: ${sessionId || '(not available)'}\n` +
  67. ` Message Type: ${messageType}\n` +
  68. ` Message: ${b64.encode(message)}` +
  69. '\n Listeners:', listeners
  70. );
  71. console.trace();
  72. }
  73.  
  74. function keystatuseschangeHandler(event) {
  75. const keySession = event.target;
  76. const {sessionId} = keySession;
  77. const listeners = keySession.getEventListeners('keystatuseschange').filter(l => l !== keystatuseschangeHandler);
  78. console.log(
  79. `[EME] MediaKeySession::keystatuseschange\n` +
  80. ` Session ID: ${sessionId || '(not available)'}\n` +
  81. Array.from(keySession.keyStatuses).map(([keyId, status]) =>
  82. ` [${status.toUpperCase()}] ${b64.encode(keyId)}`
  83. ).join('\n') +
  84. '\n Listeners:', listeners
  85. );
  86. console.trace();
  87. }
  88.  
  89. proxy(MediaKeys.prototype, 'createSession', (_target, _this, _args) => {
  90. const [sessionType] = _args;
  91. console.log(
  92. `[EME] MediaKeys::createSession\n` +
  93. ` Session Type: ${sessionType || 'temporary (default)'}`
  94. );
  95. console.trace();
  96. const session = _target.apply(_this, _args);
  97. session.addEventListener('message', messageHandler);
  98. session.addEventListener('keystatuseschange', keystatuseschangeHandler);
  99. return session;
  100. });
  101.  
  102. function getEventListeners(type) {
  103. const store = this[Symbol.for(getEventListeners)];
  104. if (store == null || store[type] == null) return [];
  105. return store[type];
  106. }
  107.  
  108. EventTarget.prototype.getEventListeners = getEventListeners;
  109.  
  110. proxy(EventTarget.prototype, 'addEventListener', async (_target, _this, _args) => {
  111. const [type, listener] = _args;
  112. const storeKey = Symbol.for(getEventListeners);
  113. if (!(storeKey in _this)) _this[storeKey] = {};
  114. const store = _this[storeKey];
  115. if (!(type in store)) store[type] = [];
  116. const listeners = store[type];
  117. if (listeners.indexOf(listener) < 0) {
  118. listeners.push(listener);
  119. }
  120. return _target.apply(_this, _args);
  121. });
  122.  
  123. proxy(EventTarget.prototype, 'removeEventListener', async (_target, _this, _args) => {
  124. const [type, listener] = _args;
  125. const storeKey = Symbol.for(getEventListeners);
  126. if (!(storeKey in _this)) return;
  127. const store = _this[storeKey];
  128. if (!(type in store)) return;
  129. const listeners = store[type];
  130. const index = listeners.indexOf(listener);
  131. if (index >= 0) {
  132. if (listeners.length === 1) {
  133. delete store[type];
  134. } else {
  135. listeners.splice(index, 1);
  136. }
  137. }
  138. return _target.apply(_this, _args);
  139. });
  140.  
  141. proxy(MediaKeySession.prototype, 'generateRequest', async (_target, _this, _args) => {
  142. const [initDataType, initData] = _args;
  143. console.log(
  144. `[EME] MediaKeySession::generateRequest\n` +
  145. ` Session ID: ${_this.sessionId || '(not available)'}\n` +
  146. ` Init Data Type: ${initDataType}\n` +
  147. ` Init Data: ${b64.encode(initData)}`
  148. );
  149. console.trace();
  150. return _target.apply(_this, _args);
  151. });
  152.  
  153. proxy(MediaKeySession.prototype, 'load', async (_target, _this, _args) => {
  154. const [sessionId] = _args;
  155. console.log(
  156. `[EME] MediaKeySession::load\n` +
  157. ` Session ID: ${sessionId || '(not available)'}`
  158. );
  159. console.trace();
  160. return _target.apply(_this, _args);
  161. });
  162.  
  163. proxy(MediaKeySession.prototype, 'update', async (_target, _this, _args) => {
  164. const [response] = _args;
  165. console.log(
  166. `[EME] MediaKeySession::update\n` +
  167. ` Session ID: ${_this.sessionId || '(not available)'}\n` +
  168. ` Response: ${b64.encode(response)}`
  169. );
  170. console.trace();
  171. return _target.apply(_this, _args);
  172. });
  173.  
  174. proxy(MediaKeySession.prototype, 'close', async (_target, _this, _args) => {
  175. console.log(
  176. `[EME] MediaKeySession::close\n` +
  177. ` Session ID: ${_this.sessionId || '(not available)'}`
  178. );
  179. console.trace();
  180. return _target.apply(_this, _args);
  181. });
  182.  
  183. proxy(MediaKeySession.prototype, 'remove', async (_target, _this, _args) => {
  184. console.log(
  185. `[EME] MediaKeySession::remove\n` +
  186. ` Session ID: ${_this.sessionId || '(not available)'}`
  187. );
  188. console.trace();
  189. return _target.apply(_this, _args);
  190. });
  191. })();