shaka-player 3.2.2 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +1 -1
  3. package/.github/ISSUE_TEMPLATE/question.md +1 -1
  4. package/.github/workflows/build_and_test.yaml +73 -0
  5. package/.github/workflows/release.yaml +53 -0
  6. package/AUTHORS +6 -5
  7. package/CHANGELOG.md +314 -1
  8. package/CONTRIBUTORS +4 -2
  9. package/build/checkversion.py +1 -1
  10. package/build/conformance.textproto +183 -169
  11. package/build/test.py +6 -0
  12. package/build/types/core +3 -0
  13. package/build/types/offline +1 -0
  14. package/build/types/polyfill +1 -0
  15. package/build/types/ui +2 -0
  16. package/demo/common/assets.js +18 -0
  17. package/demo/common/message_ids.js +13 -1
  18. package/demo/config.js +31 -1
  19. package/demo/index.html +1 -1
  20. package/demo/locales/en.json +12 -0
  21. package/demo/locales/source.json +48 -0
  22. package/dist/controls.css +26 -2
  23. package/dist/controls.css.map +1 -1
  24. package/dist/demo.compiled.debug.js +19 -16
  25. package/dist/demo.compiled.debug.map +3 -3
  26. package/dist/demo.compiled.js +19 -16
  27. package/dist/demo.compiled.map +3 -3
  28. package/dist/deps.js +21 -14
  29. package/dist/locales.js +31 -29
  30. package/dist/receiver.compiled.debug.js +11 -9
  31. package/dist/receiver.compiled.debug.map +3 -3
  32. package/dist/receiver.compiled.js +11 -9
  33. package/dist/receiver.compiled.map +3 -3
  34. package/dist/shaka-player.compiled.d.ts +232 -18
  35. package/dist/shaka-player.compiled.debug.d.ts +232 -18
  36. package/dist/shaka-player.compiled.debug.externs.js +293 -17
  37. package/dist/shaka-player.compiled.debug.js +481 -422
  38. package/dist/shaka-player.compiled.debug.map +4 -4
  39. package/dist/shaka-player.compiled.externs.js +293 -17
  40. package/dist/shaka-player.compiled.js +846 -817
  41. package/dist/shaka-player.compiled.map +4 -4
  42. package/dist/shaka-player.ui.d.ts +250 -30
  43. package/dist/shaka-player.ui.debug.d.ts +250 -30
  44. package/dist/shaka-player.ui.debug.externs.js +322 -28
  45. package/dist/shaka-player.ui.debug.js +630 -553
  46. package/dist/shaka-player.ui.debug.map +4 -4
  47. package/dist/shaka-player.ui.externs.js +322 -28
  48. package/dist/shaka-player.ui.js +1048 -1006
  49. package/dist/shaka-player.ui.map +4 -4
  50. package/docs/design/bg-fetch-after.gv +29 -0
  51. package/docs/design/bg-fetch-after.gv.png +0 -0
  52. package/docs/design/bg-fetch-before.gv +18 -0
  53. package/docs/design/bg-fetch-before.gv.png +0 -0
  54. package/docs/design/bg-fetch.md +134 -0
  55. package/docs/tutorials/blob-url.md +15 -0
  56. package/docs/tutorials/index.json +1 -0
  57. package/docs/tutorials/ui-customization.md +66 -0
  58. package/docs/tutorials/ui.md +5 -5
  59. package/externs/cmcd.js +186 -0
  60. package/externs/ima.js +3 -0
  61. package/externs/shaka/ads.js +5 -0
  62. package/externs/shaka/manifest.js +12 -1
  63. package/externs/shaka/manifest_parser.js +10 -1
  64. package/externs/shaka/net.js +20 -4
  65. package/externs/shaka/offline.js +31 -2
  66. package/externs/shaka/player.js +138 -5
  67. package/externs/shaka/text.js +24 -0
  68. package/externs/webcrypto.js +18 -0
  69. package/karma.conf.js +19 -15
  70. package/lib/abr/ewma.js +14 -0
  71. package/lib/abr/ewma_bandwidth_estimator.js +16 -2
  72. package/lib/abr/simple_abr_manager.js +6 -0
  73. package/lib/ads/ad_manager.js +1 -0
  74. package/lib/ads/client_side_ad.js +8 -0
  75. package/lib/ads/client_side_ad_manager.js +8 -4
  76. package/lib/ads/server_side_ad.js +7 -0
  77. package/lib/cast/cast_proxy.js +3 -0
  78. package/lib/cast/cast_receiver.js +3 -0
  79. package/lib/cast/cast_utils.js +1 -0
  80. package/lib/dash/dash_parser.js +79 -33
  81. package/lib/dash/segment_base.js +25 -1
  82. package/lib/dash/segment_list.js +18 -9
  83. package/lib/dash/segment_template.js +23 -10
  84. package/lib/hls/hls_parser.js +116 -23
  85. package/lib/hls/manifest_text_parser.js +1 -0
  86. package/lib/media/quality_observer.js +274 -0
  87. package/lib/media/region_observer.js +39 -33
  88. package/lib/media/region_timeline.js +17 -19
  89. package/lib/media/segment_index.js +8 -1
  90. package/lib/media/segment_reference.js +37 -4
  91. package/lib/media/streaming_engine.js +47 -6
  92. package/lib/net/http_fetch_plugin.js +31 -11
  93. package/lib/net/http_plugin_utils.js +1 -0
  94. package/lib/net/http_xhr_plugin.js +40 -14
  95. package/lib/net/networking_engine.js +85 -18
  96. package/lib/offline/download_info.js +68 -0
  97. package/lib/offline/download_manager.js +13 -5
  98. package/lib/offline/indexeddb/base_storage_cell.js +35 -0
  99. package/lib/offline/indexeddb/storage_mechanism.js +1 -1
  100. package/lib/offline/indexeddb/v5_storage_cell.js +5 -0
  101. package/lib/offline/storage.js +401 -293
  102. package/lib/offline/stored_content_utils.js +2 -0
  103. package/lib/player.js +254 -27
  104. package/lib/polyfill/media_capabilities.js +30 -15
  105. package/lib/polyfill/random_uuid.js +52 -0
  106. package/lib/text/cue.js +18 -0
  107. package/lib/text/ttml_text_parser.js +109 -60
  108. package/lib/text/ui_text_displayer.js +19 -13
  109. package/lib/util/cmcd_manager.js +672 -0
  110. package/lib/util/error.js +6 -0
  111. package/lib/util/fake_event_target.js +21 -1
  112. package/lib/util/language_utils.js +12 -0
  113. package/lib/util/mutex.js +52 -0
  114. package/lib/util/periods.js +98 -64
  115. package/lib/util/player_configuration.js +18 -0
  116. package/lib/util/stream_utils.js +86 -10
  117. package/lib/util/xml_utils.js +19 -0
  118. package/package.json +3 -6
  119. package/shaka-player.uncompiled.js +3 -0
  120. package/test/dash/dash_parser_content_protection_unit.js +2 -0
  121. package/test/dash/dash_parser_live_unit.js +144 -1
  122. package/test/dash/dash_parser_manifest_unit.js +37 -2
  123. package/test/dash/dash_parser_segment_base_unit.js +2 -0
  124. package/test/dash/dash_parser_segment_list_unit.js +2 -1
  125. package/test/dash/dash_parser_segment_template_unit.js +2 -1
  126. package/test/hls/hls_live_unit.js +2 -0
  127. package/test/hls/hls_parser_unit.js +76 -0
  128. package/test/media/drm_engine_unit.js +3 -3
  129. package/test/media/quality_observer_unit.js +142 -0
  130. package/test/media/region_observer_unit.js +40 -5
  131. package/test/media/region_timeline_unit.js +40 -22
  132. package/test/media/segment_reference_unit.js +16 -1
  133. package/test/media/streaming_engine_integration.js +4 -2
  134. package/test/media/streaming_engine_unit.js +147 -0
  135. package/test/net/http_plugin_unit.js +20 -15
  136. package/test/offline/storage_integration.js +108 -4
  137. package/test/player_integration.js +140 -8
  138. package/test/player_src_equals_integration.js +41 -8
  139. package/test/player_unit.js +19 -9
  140. package/test/test/assets/chapters.vtt +2 -2
  141. package/test/test/assets/chapters2.vtt +10 -0
  142. package/test/test/util/dash_parser_util.js +4 -0
  143. package/test/test/util/fake_ad.js +7 -0
  144. package/test/test/util/fake_drm_engine.js +9 -2
  145. package/test/test/util/manifest_generator.js +3 -2
  146. package/test/test/util/test_scheme.js +14 -1
  147. package/test/test/util/ttml_utils.js +81 -0
  148. package/test/test/util/util.js +10 -7
  149. package/test/text/mp4_ttml_parser_unit.js +11 -18
  150. package/test/text/ttml_text_parser_unit.js +544 -228
  151. package/test/ui/text_displayer_layout_unit.js +8 -7
  152. package/test/ui/ui_unit.js +250 -0
  153. package/test/util/cmcd_manager_unit.js +291 -0
  154. package/test/util/stream_utils_unit.js +36 -30
  155. package/third_party/closure-uri/uri.js +21 -0
  156. package/ui/airplay_button.js +1 -0
  157. package/ui/audio_language_selection.js +7 -0
  158. package/ui/cast_button.js +1 -0
  159. package/ui/context_menu.js +109 -0
  160. package/ui/controls.js +44 -7
  161. package/ui/controls.less +1 -0
  162. package/ui/enums.js +4 -0
  163. package/ui/externs/ui.js +28 -1
  164. package/ui/fast_forward_button.js +14 -5
  165. package/ui/fullscreen_button.js +1 -0
  166. package/ui/less/buttons.less +0 -4
  167. package/ui/less/containers.less +74 -0
  168. package/ui/less/overflow_menu.less +0 -10
  169. package/ui/less/tooltip.less +97 -0
  170. package/ui/locales/en.json +1 -0
  171. package/ui/locales/source.json +4 -0
  172. package/ui/localization.js +12 -36
  173. package/ui/loop_button.js +5 -0
  174. package/ui/mute_button.js +2 -1
  175. package/ui/overflow_menu.js +5 -25
  176. package/ui/pip_button.js +5 -0
  177. package/ui/play_button.js +2 -2
  178. package/ui/playback_rate_selection.js +8 -9
  179. package/ui/resolution_selection.js +7 -0
  180. package/ui/rewind_button.js +15 -6
  181. package/ui/seek_bar.js +4 -2
  182. package/ui/settings_menu.js +36 -4
  183. package/ui/small_play_button.js +1 -0
  184. package/ui/statistics_button.js +253 -0
  185. package/ui/text_selection.js +12 -4
  186. package/ui/ui.js +30 -0
  187. package/ui/volume_bar.js +1 -1
  188. package/externs/media_capabilities.js +0 -16
@@ -48,8 +48,12 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
48
48
  * @param {function(number, number)=} onProgressUpdated Called when a progress
49
49
  * event is triggered. Passed the duration, in milliseconds, that the
50
50
  * request took, and the number of bytes transferred.
51
+ * @param {shaka.net.NetworkingEngine.OnHeadersReceived=} onHeadersReceived
52
+ * Called when the headers are received for a download.
53
+ * @param {shaka.net.NetworkingEngine.OnDownloadFailed=} onDownloadFailed
54
+ * Called when a download fails, for any reason.
51
55
  */
52
- constructor(onProgressUpdated) {
56
+ constructor(onProgressUpdated, onHeadersReceived, onDownloadFailed) {
53
57
  super();
54
58
 
55
59
  /** @private {boolean} */
@@ -67,6 +71,12 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
67
71
  /** @private {?function(number, number)} */
68
72
  this.onProgressUpdated_ = onProgressUpdated || null;
69
73
 
74
+ /** @private {?shaka.net.NetworkingEngine.OnHeadersReceived} */
75
+ this.onHeadersReceived_ = onHeadersReceived || null;
76
+
77
+ /** @private {?shaka.net.NetworkingEngine.OnDownloadFailed} */
78
+ this.onDownloadFailed_ = onDownloadFailed || null;
79
+
70
80
  /** @private {boolean} */
71
81
  this.forceHTTPS_ = false;
72
82
  }
@@ -222,6 +232,10 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
222
232
  this.destroyed_ = true;
223
233
  this.requestFilters_.clear();
224
234
  this.responseFilters_.clear();
235
+
236
+ // FakeEventTarget implements IReleasable
237
+ super.release();
238
+
225
239
  return this.operationManager_.destroy();
226
240
  }
227
241
 
@@ -447,6 +461,8 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
447
461
 
448
462
  let aborted = false;
449
463
 
464
+ let headersReceivedCalled = false;
465
+
450
466
  let startTimeMs;
451
467
  const sendOperation = backoffOperation.chain(() => {
452
468
  if (this.destroyed_) {
@@ -456,23 +472,27 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
456
472
  startTimeMs = Date.now();
457
473
  const segment = shaka.net.NetworkingEngine.RequestType.SEGMENT;
458
474
 
459
- const requestPlugin = plugin(request.uris[index],
460
- request,
461
- type,
462
- // The following function is passed to plugin.
463
- (time, bytes, numBytesRemaining) => {
464
- if (connectionTimer) {
465
- connectionTimer.stop();
466
- }
467
- if (stallTimer) {
468
- stallTimer.tickAfter(stallTimeoutMs / 1000);
469
- }
470
- if (this.onProgressUpdated_ && type == segment) {
471
- this.onProgressUpdated_(time, bytes);
472
- gotProgress = true;
473
- numBytesRemainingObj.setBytes(numBytesRemaining);
474
- }
475
- });
475
+ const progressUpdated = (time, bytes, numBytesRemaining) => {
476
+ if (connectionTimer) {
477
+ connectionTimer.stop();
478
+ }
479
+ if (stallTimer) {
480
+ stallTimer.tickAfter(stallTimeoutMs / 1000);
481
+ }
482
+ if (this.onProgressUpdated_ && type == segment) {
483
+ this.onProgressUpdated_(time, bytes);
484
+ gotProgress = true;
485
+ numBytesRemainingObj.setBytes(numBytesRemaining);
486
+ }
487
+ };
488
+ const headersReceived = (headers) => {
489
+ if (this.onHeadersReceived_) {
490
+ this.onHeadersReceived_(headers, request, type);
491
+ }
492
+ headersReceivedCalled = true;
493
+ };
494
+ const requestPlugin = plugin(
495
+ request.uris[index], request, type, progressUpdated, headersReceived);
476
496
 
477
497
  if (!progressSupport) {
478
498
  return requestPlugin;
@@ -511,6 +531,13 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
511
531
  response: response,
512
532
  gotProgress: gotProgress,
513
533
  };
534
+ if (!headersReceivedCalled) {
535
+ // The plugin did not call headersReceived, perhaps because it is not
536
+ // able to track that information. So, fire the event manually.
537
+ if (this.onHeadersReceived_) {
538
+ this.onHeadersReceived_(response.headers, request, type);
539
+ }
540
+ }
514
541
 
515
542
  return responseAndGotProgress;
516
543
  }, (error) => {
@@ -520,6 +547,17 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
520
547
  if (stallTimer) {
521
548
  stallTimer.stop();
522
549
  }
550
+ if (this.onDownloadFailed_) {
551
+ let shakaError = null;
552
+ let httpResponseCode = 0;
553
+ if (error instanceof shaka.util.Error) {
554
+ shakaError = error;
555
+ if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) {
556
+ httpResponseCode = /** @type {number} */ (error.data[1]);
557
+ }
558
+ }
559
+ this.onDownloadFailed_(request, shakaError, httpResponseCode, aborted);
560
+ }
523
561
  if (this.destroyed_) {
524
562
  return shaka.util.AbortableOperation.aborted();
525
563
  }
@@ -764,3 +802,32 @@ shaka.net.NetworkingEngine.schemes_ = {};
764
802
  * @private
765
803
  */
766
804
  shaka.net.NetworkingEngine.ResponseAndGotProgress;
805
+
806
+
807
+ /**
808
+ * @typedef {function(
809
+ * !Object.<string, string>,
810
+ * !shaka.extern.Request,
811
+ * !shaka.net.NetworkingEngine.RequestType)}
812
+ *
813
+ * @description
814
+ * A callback function that passes the shaka.extern.HeadersReceived along to
815
+ * the player, plus some extra data.
816
+ * @export
817
+ */
818
+ shaka.net.NetworkingEngine.OnHeadersReceived;
819
+
820
+
821
+ /**
822
+ * @typedef {function(
823
+ * !shaka.extern.Request,
824
+ * ?shaka.util.Error,
825
+ * number,
826
+ * boolean)}
827
+ *
828
+ * @description
829
+ * A callback function that notifies the player when a download fails, for any
830
+ * reason (e.g. even if the download was aborted).
831
+ * @export
832
+ */
833
+ shaka.net.NetworkingEngine.OnDownloadFailed;
@@ -0,0 +1,68 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.offline.DownloadInfo');
8
+
9
+ goog.require('shaka.util.Networking');
10
+ goog.requireType('shaka.media.InitSegmentReference');
11
+ goog.requireType('shaka.media.SegmentReference');
12
+
13
+
14
+ /**
15
+ * An object that represents a single segment, that the storage system will soon
16
+ * download, but has not yet started downloading.
17
+ */
18
+ shaka.offline.DownloadInfo = class {
19
+ /**
20
+ * @param {shaka.media.SegmentReference|shaka.media.InitSegmentReference} ref
21
+ * @param {number} estimateId
22
+ * @param {number} groupId
23
+ * @param {boolean} isInitSegment
24
+ */
25
+ constructor(ref, estimateId, groupId, isInitSegment) {
26
+ /** @type {shaka.media.SegmentReference|shaka.media.InitSegmentReference} */
27
+ this.ref = ref;
28
+
29
+ /** @type {number} */
30
+ this.estimateId = estimateId;
31
+
32
+ /** @type {number} */
33
+ this.groupId = groupId;
34
+
35
+ /** @type {boolean} */
36
+ this.isInitSegment = isInitSegment;
37
+ }
38
+
39
+ /**
40
+ * Creates an ID that encapsulates all important information in the ref, which
41
+ * can then be used to check for equality.
42
+ * @param {shaka.media.SegmentReference|shaka.media.InitSegmentReference} ref
43
+ * @return {string}
44
+ */
45
+ static idForSegmentRef(ref) {
46
+ // Escape the URIs using encodeURI, to make sure that a weirdly formed URI
47
+ // cannot cause two unrelated refs to be considered equivalent.
48
+ return ref.getUris().map((uri) => '{' + encodeURI(uri) + '}').join('') +
49
+ ':' + ref.startByte + ':' + ref.endByte;
50
+ }
51
+
52
+ /** @return {string} */
53
+ getRefId() {
54
+ return shaka.offline.DownloadInfo.idForSegmentRef(this.ref);
55
+ }
56
+
57
+ /**
58
+ * @param {shaka.extern.PlayerConfiguration} config
59
+ * @return {!shaka.extern.Request}
60
+ */
61
+ makeSegmentRequest(config) {
62
+ return shaka.util.Networking.createSegmentRequest(
63
+ this.ref.getUris(),
64
+ this.ref.startByte,
65
+ this.ref.endByte,
66
+ config.streaming.retryParameters);
67
+ }
68
+ };
@@ -102,6 +102,16 @@ shaka.offline.DownloadManager = class {
102
102
  return Promise.all(promises);
103
103
  }
104
104
 
105
+ /**
106
+ * Adds a byte length to the download estimate.
107
+ *
108
+ * @param {number} estimatedByteLength
109
+ * @return {number} estimateId
110
+ */
111
+ addDownloadEstimate(estimatedByteLength) {
112
+ return this.estimator_.open(estimatedByteLength);
113
+ }
114
+
105
115
  /**
106
116
  * Add a request to be downloaded as part of a group.
107
117
  *
@@ -109,18 +119,16 @@ shaka.offline.DownloadManager = class {
109
119
  * The group to add this segment to. If the group does not exist, a new
110
120
  * group will be created.
111
121
  * @param {shaka.extern.Request} request
112
- * @param {number} estimatedByteLength
122
+ * @param {number} estimateId
113
123
  * @param {boolean} isInitSegment
114
124
  * @param {function(BufferSource):!Promise} onDownloaded
115
125
  * The callback for when this request has been downloaded. Downloading for
116
126
  * |group| will pause until the promise returned by |onDownloaded| resolves.
117
127
  * @return {!Promise} Resolved when this request is complete.
118
128
  */
119
- queue(groupId, request, estimatedByteLength, isInitSegment, onDownloaded) {
129
+ queue(groupId, request, estimateId, isInitSegment, onDownloaded) {
120
130
  this.destroyer_.ensureNotDestroyed();
121
131
 
122
- const id = this.estimator_.open(estimatedByteLength);
123
-
124
132
  const group = this.groups_.get(groupId) || Promise.resolve();
125
133
 
126
134
  // Add another download to the group.
@@ -148,7 +156,7 @@ shaka.offline.DownloadManager = class {
148
156
  }
149
157
 
150
158
  // Update all our internal stats.
151
- this.estimator_.close(id, response.byteLength);
159
+ this.estimator_.close(estimateId, response.byteLength);
152
160
  this.onProgress_(
153
161
  this.estimator_.getEstimatedProgress(),
154
162
  this.estimator_.getTotalDownloaded());
@@ -68,6 +68,28 @@ shaka.offline.indexeddb.BaseStorageCell = class {
68
68
  return this.rejectAdd(this.manifestStore_);
69
69
  }
70
70
 
71
+ /** @override */
72
+ updateManifest(key, manifest) {
73
+ // By default, reject all updates.
74
+ return this.rejectUpdate(this.manifestStore_);
75
+ }
76
+
77
+ /**
78
+ * @param {number} key
79
+ * @param {!shaka.extern.ManifestDB} manifest
80
+ * @return {!Promise}
81
+ * @protected
82
+ */
83
+ updateManifestImplementation(key, manifest) {
84
+ const op = this.connection_.startReadWriteOperation(this.manifestStore_);
85
+ const store = op.store();
86
+ store.get(key).onsuccess = (e) => {
87
+ store.put(manifest, key);
88
+ };
89
+
90
+ return op.promise();
91
+ }
92
+
71
93
  /** @override */
72
94
  updateManifestExpiration(key, newExpiration) {
73
95
  const op = this.connection_.startReadWriteOperation(this.manifestStore_);
@@ -145,6 +167,19 @@ shaka.offline.indexeddb.BaseStorageCell = class {
145
167
  'Cannot add new value to ' + storeName));
146
168
  }
147
169
 
170
+ /**
171
+ * @param {string} storeName
172
+ * @return {!Promise}
173
+ * @protected
174
+ */
175
+ rejectUpdate(storeName) {
176
+ return Promise.reject(new shaka.util.Error(
177
+ shaka.util.Error.Severity.CRITICAL,
178
+ shaka.util.Error.Category.STORAGE,
179
+ shaka.util.Error.Code.MODIFY_OPERATION_NOT_SUPPORTED,
180
+ 'Cannot modify values in ' + storeName));
181
+ }
182
+
148
183
  /**
149
184
  * @param {string} storeName
150
185
  * @param {!Array.<T>} values
@@ -315,7 +315,7 @@ shaka.offline.indexeddb.StorageMechanism = class {
315
315
 
316
316
  const del = window.indexedDB.deleteDatabase(name);
317
317
  del.onblocked = (event) => {
318
- shaka.log.warning('Deleting', name, 'is being blocked');
318
+ shaka.log.warning('Deleting', name, 'is being blocked', event);
319
319
  };
320
320
  del.onsuccess = (event) => {
321
321
  p.resolve();
@@ -33,6 +33,11 @@ shaka.offline.indexeddb.V5StorageCell = class
33
33
  return this.add(this.manifestStore_, manifests);
34
34
  }
35
35
 
36
+ /** @override */
37
+ updateManifest(key, manifest) {
38
+ return this.updateManifestImplementation(key, manifest);
39
+ }
40
+
36
41
  /** @override */
37
42
  convertManifest(old) {
38
43
  // JSON serialization turns Infinity into null, so turn it back now.