Source: lib/offline/session_deleter.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.offline.SessionDeleter');
  7. goog.require('shaka.log');
  8. goog.require('shaka.media.DrmEngine');
  9. goog.require('shaka.util.ArrayUtils');
  10. goog.requireType('shaka.net.NetworkingEngine');
  11. /**
  12. * Contains a utility method to delete persistent EME sessions.
  13. */
  14. shaka.offline.SessionDeleter = class {
  15. /**
  16. * Deletes the given sessions. This never fails and instead logs the error.
  17. *
  18. * @param {shaka.extern.DrmConfiguration} config
  19. * @param {!shaka.net.NetworkingEngine} netEngine
  20. * @param {!Array.<shaka.extern.EmeSessionDB>} sessions
  21. * @return {!Promise.<!Array.<string>>} The session IDs that were deleted.
  22. */
  23. async delete(config, netEngine, sessions) {
  24. const SessionDeleter = shaka.offline.SessionDeleter;
  25. let deleted = [];
  26. for (const bucket of SessionDeleter.createBuckets_(sessions)) {
  27. // Run these sequentially to avoid creating multiple CDM instances at one
  28. // time. Some embedded platforms may not support multiples.
  29. const p = this.doDelete_(config, netEngine, bucket);
  30. const cur = await p; // eslint-disable-line no-await-in-loop
  31. deleted = deleted.concat(cur);
  32. }
  33. return deleted;
  34. }
  35. /**
  36. * Performs the deletion of the given session IDs.
  37. *
  38. * @param {shaka.extern.DrmConfiguration} config
  39. * @param {!shaka.net.NetworkingEngine} netEngine
  40. * @param {shaka.offline.SessionDeleter.Bucket_} bucket
  41. * @return {!Promise.<!Array.<string>>} The sessions that were deleted
  42. * @private
  43. */
  44. async doDelete_(config, netEngine, bucket) {
  45. /** @type {!shaka.media.DrmEngine} */
  46. const drmEngine = new shaka.media.DrmEngine({
  47. netEngine: netEngine,
  48. onError: () => {},
  49. onKeyStatus: () => {},
  50. onExpirationUpdated: () => {},
  51. onEvent: () => {},
  52. });
  53. try {
  54. drmEngine.configure(config);
  55. await drmEngine.initForRemoval(
  56. bucket.info.keySystem, bucket.info.licenseUri,
  57. bucket.info.serverCertificate,
  58. bucket.info.audioCapabilities, bucket.info.videoCapabilities);
  59. } catch (e) {
  60. shaka.log.warning('Error initializing EME', e);
  61. await drmEngine.destroy();
  62. return [];
  63. }
  64. try {
  65. await drmEngine.setServerCertificate();
  66. } catch (e) {
  67. shaka.log.warning('Error setting server certificate', e);
  68. await drmEngine.destroy();
  69. return [];
  70. }
  71. /** @type {!Array.<string>} */
  72. const sessionIds = [];
  73. await Promise.all(bucket.sessionIds.map(async (sessionId) => {
  74. // This method is in a .map(), so this starts multiple removes at once,
  75. // so this removes the sessions in parallel.
  76. try {
  77. await drmEngine.removeSession(sessionId);
  78. sessionIds.push(sessionId);
  79. } catch (e) {
  80. shaka.log.warning('Error deleting offline session', e);
  81. }
  82. }));
  83. await drmEngine.destroy();
  84. return sessionIds;
  85. }
  86. /**
  87. * Collects the given sessions into buckets that can be done at the same time.
  88. * Since querying with different parameters can give us back different CDMs,
  89. * we can't just use one CDM instance to delete everything.
  90. *
  91. * @param {!Array.<shaka.extern.EmeSessionDB>} sessions
  92. * @return {!Array.<shaka.offline.SessionDeleter.Bucket_>}
  93. * @private
  94. */
  95. static createBuckets_(sessions) {
  96. const SessionDeleter = shaka.offline.SessionDeleter;
  97. /** @type {!Array.<shaka.offline.SessionDeleter.Bucket_>} */
  98. const ret = [];
  99. for (const session of sessions) {
  100. let found = false;
  101. for (const bucket of ret) {
  102. if (SessionDeleter.isCompatible_(bucket.info, session)) {
  103. bucket.sessionIds.push(session.sessionId);
  104. found = true;
  105. break;
  106. }
  107. }
  108. if (!found) {
  109. ret.push({info: session, sessionIds: [session.sessionId]});
  110. }
  111. }
  112. return ret;
  113. }
  114. /**
  115. * Returns whether the given session infos are compatible with each other.
  116. * @param {shaka.extern.EmeSessionDB} a
  117. * @param {shaka.extern.EmeSessionDB} b
  118. * @return {boolean}
  119. * @private
  120. */
  121. static isCompatible_(a, b) {
  122. const ArrayUtils = shaka.util.ArrayUtils;
  123. // TODO: Add a way to change the license server in DrmEngine to avoid
  124. // resetting EME for different license servers.
  125. const comp = (x, y) =>
  126. x.robustness == y.robustness && x.contentType == y.contentType;
  127. return a.keySystem == b.keySystem && a.licenseUri == b.licenseUri &&
  128. ArrayUtils.hasSameElements(
  129. a.audioCapabilities, b.audioCapabilities, comp) &&
  130. ArrayUtils.hasSameElements(
  131. a.videoCapabilities, b.videoCapabilities, comp);
  132. }
  133. };
  134. /**
  135. * @typedef {{
  136. * info: shaka.extern.EmeSessionDB,
  137. * sessionIds: !Array.<string>
  138. * }}
  139. */
  140. shaka.offline.SessionDeleter.Bucket_;