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. /** @type {!Array.<string>} */
  65. const sessionIds = [];
  66. await Promise.all(bucket.sessionIds.map(async (sessionId) => {
  67. // This method is in a .map(), so this starts multiple removes at once,
  68. // so this removes the sessions in parallel.
  69. try {
  70. await drmEngine.removeSession(sessionId);
  71. sessionIds.push(sessionId);
  72. } catch (e) {
  73. shaka.log.warning('Error deleting offline session', e);
  74. }
  75. }));
  76. await drmEngine.destroy();
  77. return sessionIds;
  78. }
  79. /**
  80. * Collects the given sessions into buckets that can be done at the same time.
  81. * Since querying with different parameters can give us back different CDMs,
  82. * we can't just use one CDM instance to delete everything.
  83. *
  84. * @param {!Array.<shaka.extern.EmeSessionDB>} sessions
  85. * @return {!Array.<shaka.offline.SessionDeleter.Bucket_>}
  86. * @private
  87. */
  88. static createBuckets_(sessions) {
  89. const SessionDeleter = shaka.offline.SessionDeleter;
  90. /** @type {!Array.<shaka.offline.SessionDeleter.Bucket_>} */
  91. const ret = [];
  92. for (const session of sessions) {
  93. let found = false;
  94. for (const bucket of ret) {
  95. if (SessionDeleter.isCompatible_(bucket.info, session)) {
  96. bucket.sessionIds.push(session.sessionId);
  97. found = true;
  98. break;
  99. }
  100. }
  101. if (!found) {
  102. ret.push({info: session, sessionIds: [session.sessionId]});
  103. }
  104. }
  105. return ret;
  106. }
  107. /**
  108. * Returns whether the given session infos are compatible with each other.
  109. * @param {shaka.extern.EmeSessionDB} a
  110. * @param {shaka.extern.EmeSessionDB} b
  111. * @return {boolean}
  112. * @private
  113. */
  114. static isCompatible_(a, b) {
  115. const ArrayUtils = shaka.util.ArrayUtils;
  116. // TODO: Add a way to change the license server in DrmEngine to avoid
  117. // resetting EME for different license servers.
  118. const comp = (x, y) =>
  119. x.robustness == y.robustness && x.contentType == y.contentType;
  120. return a.keySystem == b.keySystem && a.licenseUri == b.licenseUri &&
  121. ArrayUtils.hasSameElements(
  122. a.audioCapabilities, b.audioCapabilities, comp) &&
  123. ArrayUtils.hasSameElements(
  124. a.videoCapabilities, b.videoCapabilities, comp);
  125. }
  126. };
  127. /**
  128. * @typedef {{
  129. * info: shaka.extern.EmeSessionDB,
  130. * sessionIds: !Array.<string>
  131. * }}
  132. */
  133. shaka.offline.SessionDeleter.Bucket_;