shaka-player 3.2.9 → 3.2.12

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 (139) hide show
  1. package/.github/workflows/build-and-test.yaml +67 -15
  2. package/.github/workflows/compute-incremental-coverage.py +251 -0
  3. package/.github/workflows/report-incremental-coverage.yaml +39 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +179 -31
  5. package/CHANGELOG.md +34 -0
  6. package/CONTRIBUTORS +2 -0
  7. package/README.md +20 -6
  8. package/demo/index.html +0 -2
  9. package/dist/controls.css +1 -1
  10. package/dist/controls.css.map +1 -1
  11. package/dist/deps.js +2 -2
  12. package/dist/locales.js +29 -29
  13. package/dist/shaka-player.compiled.d.ts +12 -1
  14. package/dist/shaka-player.compiled.debug.d.ts +12 -1
  15. package/dist/shaka-player.compiled.debug.externs.js +49 -38
  16. package/dist/shaka-player.compiled.debug.js +105 -96
  17. package/dist/shaka-player.compiled.debug.map +4 -4
  18. package/dist/shaka-player.compiled.externs.js +49 -38
  19. package/dist/shaka-player.compiled.js +759 -755
  20. package/dist/shaka-player.compiled.map +4 -4
  21. package/dist/shaka-player.ui.d.ts +12 -1
  22. package/dist/shaka-player.ui.debug.d.ts +12 -1
  23. package/dist/shaka-player.ui.debug.externs.js +49 -38
  24. package/dist/shaka-player.ui.debug.js +119 -110
  25. package/dist/shaka-player.ui.debug.map +4 -4
  26. package/dist/shaka-player.ui.externs.js +49 -38
  27. package/dist/shaka-player.ui.js +866 -863
  28. package/dist/shaka-player.ui.map +4 -4
  29. package/docs/tutorials/welcome.md +6 -3
  30. package/karma.conf.js +3 -11
  31. package/lib/dash/dash_parser.js +5 -0
  32. package/lib/hls/hls_parser.js +6 -2
  33. package/lib/media/drm_engine.js +1 -1
  34. package/lib/media/presentation_timeline.js +24 -1
  35. package/lib/media/streaming_engine.js +1 -0
  36. package/lib/player.js +7 -6
  37. package/lib/polyfill/media_capabilities.js +3 -0
  38. package/lib/polyfill/videoplaybackquality.js +3 -1
  39. package/lib/text/cue.js +0 -5
  40. package/lib/text/simple_text_displayer.js +21 -9
  41. package/lib/text/ui_text_displayer.js +91 -19
  42. package/lib/util/buffer_utils.js +3 -0
  43. package/lib/util/object_utils.js +1 -0
  44. package/lib/util/platform.js +41 -1
  45. package/lib/util/stream_utils.js +23 -3
  46. package/lib/util/string_utils.js +102 -13
  47. package/maintained-branches.md +15 -0
  48. package/package.json +3 -5
  49. package/test/player_src_equals_integration.js +2 -0
  50. package/test/test/assets/small.mp4 +0 -0
  51. package/test/text/simple_text_displayer_unit.js +27 -0
  52. package/test/text/ui_text_displayer_unit.js +65 -0
  53. package/test/util/string_utils_unit.js +46 -8
  54. package/ui/less/ad_controls.less +4 -0
  55. package/app-engine/README.md +0 -9
  56. package/app-engine/demo-version-index/README.md +0 -14
  57. package/app-engine/demo-version-index/app.yaml +0 -2
  58. package/app-engine/demo-version-index/main.py +0 -162
  59. package/app-engine/demo-version-index/requirements.txt +0 -6
  60. package/app-engine/demo-version-index/templates/index.html +0 -114
  61. package/app-engine/shaka-player-demo/README.md +0 -18
  62. package/app-engine/shaka-player-demo/app.yaml +0 -61
  63. package/app-engine/shaka-player-demo/main.py +0 -95
  64. package/app-engine/shaka-player-demo/posters/10yo.png +0 -0
  65. package/app-engine/shaka-player-demo/posters/10yo.xcf +0 -0
  66. package/app-engine/shaka-player-demo/posters/1yo.jpg +0 -0
  67. package/app-engine/shaka-player-demo/posters/2yo.png +0 -0
  68. package/app-engine/shaka-player-demo/posters/2yo.xcf +0 -0
  69. package/app-engine/shaka-player-demo/posters/3yo.png +0 -0
  70. package/app-engine/shaka-player-demo/posters/3yo.xcf +0 -0
  71. package/app-engine/shaka-player-demo/posters/4yo.png +0 -0
  72. package/app-engine/shaka-player-demo/posters/4yo.xcf +0 -0
  73. package/app-engine/shaka-player-demo/posters/5yo.png +0 -0
  74. package/app-engine/shaka-player-demo/posters/5yo.xcf +0 -0
  75. package/app-engine/shaka-player-demo/posters/6yo.png +0 -0
  76. package/app-engine/shaka-player-demo/posters/6yo.xcf +0 -0
  77. package/app-engine/shaka-player-demo/posters/7yo.png +0 -0
  78. package/app-engine/shaka-player-demo/posters/7yo.xcf +0 -0
  79. package/app-engine/shaka-player-demo/posters/8yo.png +0 -0
  80. package/app-engine/shaka-player-demo/posters/8yo.xcf +0 -0
  81. package/app-engine/shaka-player-demo/posters/9yo.png +0 -0
  82. package/app-engine/shaka-player-demo/posters/9yo.xcf +0 -0
  83. package/app-engine/shaka-player-demo/posters/audioOnly.gif +0 -0
  84. package/app-engine/shaka-player-demo/posters/audioRickRoll.gif +0 -0
  85. package/app-engine/shaka-player-demo/posters/colorbars.gif +0 -0
  86. package/app-engine/shaka-player-demo/posters/fireworks.jpg +0 -0
  87. package/app-engine/shaka-player-demo/posters/party-hat.png +0 -0
  88. package/app-engine/shaka-player-demo/posters/sources.txt +0 -46
  89. package/app-engine/shaka-player-demo/posters/standard.png +0 -0
  90. package/app-engine/shaka-player-demo/posters/trek.jpg +0 -0
  91. package/app-engine/shaka-player-demo/posters/valentines.png +0 -0
  92. package/app-engine/shaka-player-demo/posters/zombie.jpg +0 -0
  93. package/app-engine/shaka-player-demo/posters.py +0 -110
  94. package/app-engine/shaka-player-demo/requirements.txt +0 -5
  95. package/app-engine/shaka-player-demo/time.txt +0 -1
  96. package/build/README.md +0 -127
  97. package/build/__init__.py +0 -0
  98. package/build/all.py +0 -150
  99. package/build/apps.py +0 -186
  100. package/build/build.py +0 -392
  101. package/build/check.py +0 -354
  102. package/build/checkversion.py +0 -84
  103. package/build/compiler.py +0 -513
  104. package/build/conformance.textproto +0 -321
  105. package/build/docker/Dockerfile +0 -20
  106. package/build/docker/docker-compose.yml +0 -10
  107. package/build/docs.py +0 -54
  108. package/build/eslint-plugin-shaka-rules/arg-comment-spacing.js +0 -72
  109. package/build/eslint-plugin-shaka-rules/index.js +0 -24
  110. package/build/eslint-plugin-shaka-rules/package.json +0 -7
  111. package/build/eslint-plugin-shaka-rules/private.js +0 -109
  112. package/build/gendeps.py +0 -71
  113. package/build/generateExterns.js +0 -867
  114. package/build/generateLocalizations.py +0 -262
  115. package/build/generateTsDefs.py +0 -137
  116. package/build/install-linux-prereqs.sh +0 -136
  117. package/build/license-header +0 -5
  118. package/build/misspellings.txt +0 -38
  119. package/build/pixelsChanged.js +0 -35
  120. package/build/shaka-lab.yaml +0 -163
  121. package/build/shakaBuildHelpers.py +0 -356
  122. package/build/stats.py +0 -816
  123. package/build/subprocessWindowsPatch.py +0 -103
  124. package/build/test.py +0 -541
  125. package/build/types/ads +0 -8
  126. package/build/types/cast +0 -6
  127. package/build/types/cea +0 -13
  128. package/build/types/complete +0 -9
  129. package/build/types/core +0 -106
  130. package/build/types/dash +0 -8
  131. package/build/types/hls +0 -9
  132. package/build/types/manifests +0 -5
  133. package/build/types/networking +0 -6
  134. package/build/types/offline +0 -21
  135. package/build/types/polyfill +0 -19
  136. package/build/types/text +0 -11
  137. package/build/types/ui +0 -37
  138. package/build/updateScreenshots.py +0 -85
  139. package/build/wrapper.template.js +0 -52
@@ -11,6 +11,7 @@ goog.require('shaka.log');
11
11
  goog.require('shaka.util.BufferUtils');
12
12
  goog.require('shaka.util.Error');
13
13
  goog.require('shaka.util.Lazy');
14
+ goog.require('shaka.util.Platform');
14
15
 
15
16
 
16
17
  /**
@@ -37,18 +38,84 @@ shaka.util.StringUtils = class {
37
38
  uint8 = uint8.subarray(3);
38
39
  }
39
40
 
40
- // Use the TextDecoder interface to decode the text. This has the advantage
41
- // compared to the previously-standard decodeUriComponent that it will
42
- // continue parsing even if it finds an invalid UTF8 character, rather than
43
- // stop and throw an error.
44
- const utf8decoder = new TextDecoder();
45
- const decoded = utf8decoder.decode(uint8);
46
- if (decoded.includes('\uFFFD')) {
47
- shaka.log.alwaysError('Decoded string contains an "unknown character" ' +
48
- 'codepoint. That probably means the UTF8 ' +
49
- 'encoding was incorrect!');
41
+ if (window.TextDecoder && !shaka.util.Platform.isPS4()) {
42
+ // Use the TextDecoder interface to decode the text. This has the
43
+ // advantage compared to the previously-standard decodeUriComponent that
44
+ // it will continue parsing even if it finds an invalid UTF8 character,
45
+ // rather than stop and throw an error.
46
+ const utf8decoder = new TextDecoder();
47
+ const decoded = utf8decoder.decode(uint8);
48
+ if (decoded.includes('\uFFFD')) {
49
+ shaka.log.alwaysError('Decoded string contains an "unknown character' +
50
+ '" codepoint. That probably means the UTF8 ' +
51
+ 'encoding was incorrect!');
52
+ }
53
+ return decoded;
54
+ } else {
55
+ // Homebrewed UTF-8 decoder based on
56
+ // https://en.wikipedia.org/wiki/UTF-8#Encoding
57
+ // Unlike decodeURIComponent, won't throw on bad encoding.
58
+ // In this way, it is similar to TextDecoder.
59
+
60
+ let decoded = '';
61
+ for (let i = 0; i < uint8.length; ++i) {
62
+ // By default, the "replacement character" codepoint.
63
+ let codePoint = 0xFFFD;
64
+
65
+ // Top bit is 0, 1-byte encoding.
66
+ if ((uint8[i] & 0x80) == 0) {
67
+ codePoint = uint8[i];
68
+
69
+ // Top 3 bits of byte 0 are 110, top 2 bits of byte 1 are 10,
70
+ // 2-byte encoding.
71
+ } else if (uint8.length >= i + 2 &&
72
+ (uint8[i] & 0xe0) == 0xc0 &&
73
+ (uint8[i + 1] & 0xc0) == 0x80) {
74
+ codePoint = ((uint8[i] & 0x1f) << 6) |
75
+ ((uint8[i + 1] & 0x3f));
76
+ i += 1; // Consume one extra byte.
77
+
78
+ // Top 4 bits of byte 0 are 1110, top 2 bits of byte 1 and 2 are 10,
79
+ // 3-byte encoding.
80
+ } else if (uint8.length >= i + 3 &&
81
+ (uint8[i] & 0xf0) == 0xe0 &&
82
+ (uint8[i + 1] & 0xc0) == 0x80 &&
83
+ (uint8[i + 2] & 0xc0) == 0x80) {
84
+ codePoint = ((uint8[i] & 0x0f) << 12) |
85
+ ((uint8[i + 1] & 0x3f) << 6) |
86
+ ((uint8[i + 2] & 0x3f));
87
+ i += 2; // Consume two extra bytes.
88
+
89
+ // Top 5 bits of byte 0 are 11110, top 2 bits of byte 1, 2 and 3 are 10,
90
+ // 4-byte encoding.
91
+ } else if (uint8.length >= i + 4 &&
92
+ (uint8[i] & 0xf1) == 0xf0 &&
93
+ (uint8[i + 1] & 0xc0) == 0x80 &&
94
+ (uint8[i + 2] & 0xc0) == 0x80 &&
95
+ (uint8[i + 3] & 0xc0) == 0x80) {
96
+ codePoint = ((uint8[i] & 0x07) << 18) |
97
+ ((uint8[i + 1] & 0x3f) << 12) |
98
+ ((uint8[i + 2] & 0x3f) << 6) |
99
+ ((uint8[i + 3] & 0x3f));
100
+ i += 3; // Consume three extra bytes.
101
+ }
102
+
103
+ // JavaScript strings are a series of UTF-16 characters.
104
+ if (codePoint <= 0xffff) {
105
+ decoded += String.fromCharCode(codePoint);
106
+ } else {
107
+ // UTF-16 surrogate-pair encoding, based on
108
+ // https://en.wikipedia.org/wiki/UTF-16#Description
109
+ const baseCodePoint = codePoint - 0x10000;
110
+ const highPart = baseCodePoint >> 10;
111
+ const lowPart = baseCodePoint & 0x3ff;
112
+ decoded += String.fromCharCode(0xd800 + highPart);
113
+ decoded += String.fromCharCode(0xdc00 + lowPart);
114
+ }
115
+ }
116
+
117
+ return decoded;
50
118
  }
51
- return decoded;
52
119
  }
53
120
 
54
121
 
@@ -141,8 +208,30 @@ shaka.util.StringUtils = class {
141
208
  * @export
142
209
  */
143
210
  static toUTF8(str) {
144
- const utf8Encoder = new TextEncoder();
145
- return shaka.util.BufferUtils.toArrayBuffer(utf8Encoder.encode(str));
211
+ if (window.TextEncoder && !shaka.util.Platform.isPS4()) {
212
+ const utf8Encoder = new TextEncoder();
213
+ return shaka.util.BufferUtils.toArrayBuffer(utf8Encoder.encode(str));
214
+ } else {
215
+ // http://stackoverflow.com/a/13691499
216
+ // Converts the given string to a URI encoded string. If a character
217
+ // falls in the ASCII range, it is not converted; otherwise it will be
218
+ // converted to a series of URI escape sequences according to UTF-8.
219
+ // Example: 'g#€' -> 'g#%E3%82%AC'
220
+ const encoded = encodeURIComponent(str);
221
+ // Convert each escape sequence individually into a character. Each
222
+ // escape sequence is interpreted as a code-point, so if an escape
223
+ // sequence happens to be part of a multi-byte sequence, each byte will
224
+ // be converted to a single character.
225
+ // Example: 'g#%E3%82%AC' -> '\x67\x35\xe3\x82\xac'
226
+ const utf8 = unescape(encoded);
227
+
228
+ const result = new Uint8Array(utf8.length);
229
+ for (let i = 0; i < utf8.length; i++) {
230
+ const item = utf8[i];
231
+ result[i] = item.charCodeAt(0);
232
+ }
233
+ return shaka.util.BufferUtils.toArrayBuffer(result);
234
+ }
146
235
  }
147
236
 
148
237
 
@@ -0,0 +1,15 @@
1
+ # Maintained Shaka Player Branches
2
+
3
+ We are currently maintaining the following branches of Shaka Player:
4
+
5
+ - v4.1 (latest)
6
+ - v4.0 (previous)
7
+ - v3.3 (LTS until April 30, 2023)
8
+ - v3.2 (in use by the Cast Application Framework)
9
+
10
+ Other branches are no longer receiving bug fixes, and we recommend you upgrade
11
+ to a maintained branch. For details on upgrading, please see our
12
+ [upgrade guide](https://shaka-player-demo.appspot.com/docs/api/tutorial-upgrade.html).
13
+
14
+ For more information on how these branches are chosen, please refer to our
15
+ [Branch Maintenance Policy](https://shaka-project.github.io/maintenance/shaka-player.html#branch-maintenance-policy).
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "3.2.9",
4
+ "version": "3.2.12",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -27,7 +27,6 @@
27
27
  "eslint-config-google": "^0.14.0",
28
28
  "eslint-plugin-shaka-rules": "file:./build/eslint-plugin-shaka-rules",
29
29
  "esprima": "^4.0.1",
30
- "fastestsmallesttextencoderdecoder": "^1.0.22",
31
30
  "fontfaceonload": "^1.0.2",
32
31
  "google-closure-compiler-java": "^20220301.0.0",
33
32
  "google-closure-deps": "https://gitpkg.now.sh/google/closure-library/closure-deps?d7736da6",
@@ -50,7 +49,7 @@
50
49
  "less": "https://gitpkg.now.sh/joeyparrish/less.js/packages/less?28c63a43",
51
50
  "less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
52
51
  "material-design-lite": "^1.3.0",
53
- "mux.js": "^6.1.0",
52
+ "mux.js": "^6.2.0",
54
53
  "open-sans-fonts": "^1.6.2",
55
54
  "postcss-less": "^6.0.0",
56
55
  "pwacompat": "^2.0.17",
@@ -71,7 +70,6 @@
71
70
  "dialog-polyfill/dist/dialog-polyfill.js",
72
71
  "eme-encryption-scheme-polyfill/index.js",
73
72
  "es6-promise-polyfill/promise.min.js",
74
- "fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js",
75
73
  "google-closure-library/closure/goog/base.js",
76
74
  "less/dist/less.js",
77
75
  "material-design-lite/dist/material.indigo-blue.min.css",
@@ -96,7 +94,7 @@
96
94
  "prepublishOnly": "python build/checkversion.py && python build/all.py --force"
97
95
  },
98
96
  "dependencies": {
99
- "eme-encryption-scheme-polyfill": "^2.0.5"
97
+ "eme-encryption-scheme-polyfill": "^2.1.1"
100
98
  },
101
99
  "engines": {
102
100
  "node": ">=14"
@@ -305,6 +305,8 @@ describe('Player Src Equals', () => {
305
305
  expect(stats.loadLatency).toBeGreaterThan(0);
306
306
  expect(stats.manifestTimeSeconds).toBeNaN(); // There's no manifest.
307
307
  expect(stats.drmTimeSeconds).toBeNaN(); // There's no DRM.
308
+ expect(stats.height).toBe(110);
309
+ expect(stats.width).toBe(256);
308
310
  });
309
311
 
310
312
  it('plays with external text tracks', async () => {
Binary file
@@ -127,6 +127,33 @@ describe('SimpleTextDisplayer', () => {
127
127
  [shakaCue]);
128
128
  });
129
129
 
130
+ it('flattens nested cue payloads correctly', () => {
131
+ const level0ContainerCue = new shaka.text.Cue(10, 30, '');
132
+ level0ContainerCue.isContainer = true;
133
+
134
+ const level1NonContainerCueA = new shaka.text.Cue(10, 20, '');
135
+ const level1NonContainerCueB = new shaka.text.Cue(20, 30, '');
136
+
137
+ // Add a trailing whitespace character to get a space-delimited expected
138
+ // result.
139
+ const cueANestedCue0 = new shaka.text.Cue(10, 20, 'Cue A Test0 ');
140
+ const cueANestedCue1 = new shaka.text.Cue(10, 20, 'Cue A Test1');
141
+ const cueBNestedCue0 = new shaka.text.Cue(20, 30, 'Cue B Test0 ');
142
+ const cueBNestedCue1 = new shaka.text.Cue(20, 30, 'Cue B Test1');
143
+
144
+ level1NonContainerCueA.nestedCues = [cueANestedCue0, cueANestedCue1];
145
+ level1NonContainerCueB.nestedCues = [cueBNestedCue0, cueBNestedCue1];
146
+ level0ContainerCue.nestedCues =
147
+ [level1NonContainerCueA, level1NonContainerCueB];
148
+
149
+ verifyHelper(
150
+ [
151
+ {startTime: 10, endTime: 20, text: 'Cue A Test0 Cue A Test1'},
152
+ {startTime: 20, endTime: 30, text: 'Cue B Test0 Cue B Test1'},
153
+ ],
154
+ [level0ContainerCue]);
155
+ });
156
+
130
157
  // Regression test for b/159050711
131
158
  it('maintains the styles of the parent cue', () => {
132
159
  const shakaCue = new shaka.text.Cue(10, 20, '');
@@ -50,6 +50,7 @@ describe('UITextDisplayer', () => {
50
50
  });
51
51
 
52
52
  beforeEach(() => {
53
+ video.currentTime = 0;
53
54
  textDisplayer = new shaka.text.UITextDisplayer(video, videoContainer);
54
55
  });
55
56
 
@@ -396,4 +397,68 @@ describe('UITextDisplayer', () => {
396
397
  expect(parentCueElements.length).toBe(1);
397
398
  expect(parentCueElements[0].textContent).toBe('');
398
399
  });
400
+
401
+ it('creates separate elements for cue regions', () => {
402
+ const cueRegion = new shaka.text.CueRegion();
403
+ cueRegion.id = 'regionId';
404
+ cueRegion.height = 80;
405
+ cueRegion.heightUnits = shaka.text.CueRegion.units.PERCENTAGE;
406
+ cueRegion.width = 80;
407
+ cueRegion.widthUnits = shaka.text.CueRegion.units.PERCENTAGE;
408
+ cueRegion.viewportAnchorX = 10;
409
+ cueRegion.viewportAnchorY = 10;
410
+ cueRegion.viewportAnchorUnits = shaka.text.CueRegion.units.PERCENTAGE;
411
+
412
+ // These all attach to the same region, but only one region element should
413
+ // be created.
414
+ const cues = [
415
+ new shaka.text.Cue(0, 100, ''),
416
+ new shaka.text.Cue(0, 100, ''),
417
+ new shaka.text.Cue(0, 100, ''),
418
+ ];
419
+ for (const cue of cues) {
420
+ cue.displayAlign = shaka.text.Cue.displayAlign.CENTER;
421
+ cue.region = cueRegion;
422
+ }
423
+
424
+ textDisplayer.setTextVisibility(true);
425
+ textDisplayer.append(cues);
426
+ updateCaptions();
427
+
428
+ const textContainer = videoContainer.querySelector('.shaka-text-container');
429
+ const allRegionElements = textContainer.querySelectorAll(
430
+ '.shaka-text-region');
431
+
432
+ // Verify that the nested cues are all attached to a single region element.
433
+ expect(allRegionElements.length).toBe(1);
434
+ const regionElement = allRegionElements[0];
435
+ const children = Array.from(regionElement.childNodes).filter(
436
+ (e) => e.nodeType == Node.ELEMENT_NODE);
437
+ expect(children.length).toBe(3);
438
+
439
+ // Verify styles applied to the region element.
440
+ const regionCssObj = parseCssText(regionElement.style.cssText);
441
+ const expectRegionCssObj = {
442
+ 'position': 'absolute',
443
+ 'height': '80%',
444
+ 'width': '80%',
445
+ 'top': '10%',
446
+ 'left': '10%',
447
+ 'display': 'flex',
448
+ 'flex-direction': 'column',
449
+ 'align-items': 'center',
450
+ 'justify-content': 'center',
451
+ };
452
+ expect(regionCssObj).toEqual(jasmine.objectContaining(expectRegionCssObj));
453
+
454
+ for (const caption of children) {
455
+ // Verify that styles applied to the nested cues _DO NOT_ include region
456
+ // placement.
457
+ const cueCssObj = parseCssText(caption.style.cssText);
458
+ expect(Object.keys(cueCssObj)).not.toContain('height');
459
+ expect(Object.keys(cueCssObj)).not.toContain('width');
460
+ expect(Object.keys(cueCssObj)).not.toContain('top');
461
+ expect(Object.keys(cueCssObj)).not.toContain('left');
462
+ }
463
+ });
399
464
  });
@@ -5,6 +5,29 @@
5
5
  */
6
6
 
7
7
  describe('StringUtils', () => {
8
+ describe('with TextDecoder', () => {
9
+ if (window.TextDecoder) {
10
+ defineStringUtilTests();
11
+ }
12
+ });
13
+
14
+ describe('without TextDecoder', () => {
15
+ let originalTextDecoder;
16
+
17
+ beforeAll(() => {
18
+ originalTextDecoder = window.TextDecoder;
19
+ window['TextDecoder'] = null;
20
+ });
21
+
22
+ afterAll(() => {
23
+ window.TextDecoder = originalTextDecoder;
24
+ });
25
+
26
+ defineStringUtilTests();
27
+ });
28
+ });
29
+
30
+ function defineStringUtilTests() {
8
31
  const StringUtils = shaka.util.StringUtils;
9
32
 
10
33
  it('parses fromUTF8', () => {
@@ -16,19 +39,34 @@ describe('StringUtils', () => {
16
39
  });
17
40
 
18
41
  it('won\'t break if given cut-off UTF8 character', () => {
19
- // This array contains the first half of a 2-byte UTF8 character, stranded
20
- // at the very end of the string.
21
- const arr1 = [0x53, 0x61, 0x6e, 0x20, 0x4a, 0x6f, 0x73, 0x81];
42
+ const arr1 = [0x53, 0x61, 0x6e, 0x20, 0x4a, 0x6f, 0x73, 0xc3, 0xa9];
22
43
  expect(StringUtils.fromUTF8(new Uint8Array(arr1)))
44
+ .toBe('San Jos\u00E9');
45
+
46
+ // This array contains the first half of a 2-byte UTF8 character
47
+ // (0xc3 0xa9 = é). The half-character is stranded at the very end of the
48
+ // string.
49
+ const arr = [0x53, 0x61, 0x6e, 0x20, 0x4a, 0x6f, 0x73, 0xc3];
50
+ expect(StringUtils.fromUTF8(new Uint8Array(arr)))
23
51
  .toBe('San Jos\uFFFD');
52
+ });
24
53
 
25
- // For reasons I don't know, it seems like 0xE9 cannot be the start of a
26
- // UTF8 character. Perhaps it is a reserved number?
27
- const arr2 = [0x4a, 0x6f, 0x73, 0xE9, 0x33, 0x33, 0x20, 0x53, 0x61, 0x6e];
28
- expect(StringUtils.fromUTF8(new Uint8Array(arr2)))
54
+ it('won\'t break if given an invalid UTF-8 sequence', () => {
55
+ // 0xe9 0x33 0x33 is an invalid UTF-8 sequence.
56
+ const arr = [0x4a, 0x6f, 0x73, 0xE9, 0x33, 0x33, 0x20, 0x53, 0x61, 0x6e];
57
+ expect(StringUtils.fromUTF8(new Uint8Array(arr)))
29
58
  .toBe('Jos\uFFFD33 San');
30
59
  });
31
60
 
61
+ it('can handle an 8-byte character', () => {
62
+ // This is the UTF-8 encoding of the US flag emoji.
63
+ // It decodes into two Unicode codepoints, which becomes 4 JavaScript
64
+ // UTF-16 characters.
65
+ const arr = [0xf0, 0x9f, 0x87, 0xba, 0xf0, 0x9f, 0x87, 0xb8];
66
+ expect(StringUtils.fromUTF8(new Uint8Array(arr)))
67
+ .toBe('\uD83C\uDDFA\uD83C\uDDF8');
68
+ });
69
+
32
70
  it('strips the BOM in fromUTF8', () => {
33
71
  // This is 4 Unicode characters, the last will be split into a surrogate
34
72
  // pair.
@@ -125,4 +163,4 @@ describe('StringUtils', () => {
125
163
  expect(StringUtils.fromUTF16(buffer, true).length)
126
164
  .toBe(buffer.byteLength / 2);
127
165
  });
128
- });
166
+ }
@@ -71,6 +71,10 @@
71
71
  .ad-text-shadow();
72
72
  }
73
73
 
74
+ .shaka-ad-controls .shaka-ad-position:not(.shaka-hidden) + .shaka-ad-counter {
75
+ margin-left: 0.75em;
76
+ }
77
+
74
78
  .shaka-skip-ad-container {
75
79
  /* Skip button is positioned at the very right edge of the
76
80
  * video container unlike the rest of the bottom controls. */
@@ -1,9 +0,0 @@
1
- # Google App Engine Code
2
-
3
- This folder contains source code for the services we run on Google App Engine
4
- (appspot.com).
5
-
6
- - shaka-player-demo: Our hosted demo on https://shaka-player-demo.appspot.com/
7
-
8
- - demo-version-index: An index of Shaka Player releases and demos, hosted at
9
- https://index-dot-shaka-player-demo.appspot.com/
@@ -1,14 +0,0 @@
1
- # Google App Engine Version Index
2
-
3
- This folder contains everything necessary to host an index of Shaka Player
4
- releases and demos at https://index-dot-shaka-player-demo.appspot.com/
5
-
6
- - app.yaml: App Engine config file. Defines the runtime (Python 3).
7
-
8
- - main.py: A python service that queries available versions and generates the
9
- index from a template.
10
-
11
- - requirements.txt: Used by App Engine to install the necessary Python server
12
- requirements (Flask, App Engine API).
13
-
14
- - templates/index.html: A Jinja2 template used to generate the index HTML.
@@ -1,2 +0,0 @@
1
- runtime: python39
2
- default_expiration: 1s
@@ -1,162 +0,0 @@
1
- # Shaka Player Version Index - Appspot Entrypoint
2
- # Copyright 2022 Google LLC
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- # Generate an index of Shaka Player versions on appspot.
6
-
7
- import collections
8
- import google.appengine.api.modules
9
- import jinja2
10
- import os
11
- import random
12
- import re
13
-
14
- from flask import Flask, render_template
15
-
16
- DEMO_URL_TEMPLATE = 'https://{0}-dot-shaka-player-demo.appspot.com/'
17
-
18
- # In Google Hosted Libraries
19
- HOSTED_URL_TEMPLATE = 'https://ajax.googleapis.com/ajax/libs/shaka-player/{0}/shaka-player.{1}'
20
-
21
- # Before Google Hosted Libraries, v1 appspot URLs
22
- V1_URL_TEMPLATE = 'https://{0}-dot-shaka-player-demo.appspot.com/shaka-player.{1}'
23
- # Before Google Hosted Libraries, v2 appspot URLs
24
- V2_URL_TEMPLATE = 'https://{0}-dot-shaka-player-demo.appspot.com/dist/shaka-player.{1}'
25
-
26
- def version_to_demo_url(v):
27
- return DEMO_URL_TEMPLATE.format(v.replace('.', '-'))
28
-
29
- def version_to_lib_url(v):
30
- if v == 'nightly':
31
- return V2_URL_TEMPLATE.format(v, 'compiled.js')
32
- elif (version_key(v) == version_key('v1.6.5') or
33
- version_key(v) >= version_key('v2.0.6')):
34
- return HOSTED_URL_TEMPLATE.format(v.replace('v', ''), 'compiled.js')
35
- elif version_key(v) >= version_key('v2.0.0-beta'):
36
- return V2_URL_TEMPLATE.format(v.replace('.', '-'), 'compiled.js')
37
- else:
38
- return V1_URL_TEMPLATE.format(v.replace('.', '-'), 'compiled.js')
39
-
40
- def version_to_ui_lib_url(v):
41
- if v == 'nightly':
42
- return V2_URL_TEMPLATE.format(v, 'ui.js')
43
- elif version_key(v) >= version_key('v2.5.0'):
44
- return HOSTED_URL_TEMPLATE.format(v.replace('v', ''), 'ui.js')
45
- else:
46
- return None
47
-
48
- def version_to_lib_externs_url(v):
49
- if v == 'nightly':
50
- return V2_URL_TEMPLATE.format(v, 'compiled.externs.js')
51
- elif version_key(v) >= version_key('v2.0.6'):
52
- return HOSTED_URL_TEMPLATE.format(v.replace('v', ''), 'compiled.externs.js')
53
- else:
54
- return None
55
-
56
- def version_to_ui_lib_externs_url(v):
57
- if v == 'nightly':
58
- return V2_URL_TEMPLATE.format(v, 'ui.externs.js')
59
- elif version_key(v) >= version_key('v2.5.0'):
60
- return HOSTED_URL_TEMPLATE.format(v.replace('v', ''), 'ui.externs.js')
61
- else:
62
- return None
63
-
64
- def version_to_lib_defs_url(v):
65
- if v == 'nightly':
66
- return V2_URL_TEMPLATE.format(v, 'compiled.d.ts')
67
- elif version_key(v) >= version_key('v3.0.6'):
68
- return HOSTED_URL_TEMPLATE.format(v.replace('v', ''), 'compiled.d.ts')
69
- else:
70
- return None
71
-
72
- def version_to_ui_lib_defs_url(v):
73
- if v == 'nightly':
74
- return V2_URL_TEMPLATE.format(v, 'ui.d.ts')
75
- elif version_key(v) >= version_key('v3.0.6'):
76
- return HOSTED_URL_TEMPLATE.format(v.replace('v', ''), 'ui.d.ts')
77
- else:
78
- return None
79
-
80
- def version_to_metadata(v):
81
- return {
82
- 'version': v,
83
- 'best': False, # Corrected later in another loop
84
- 'demo': version_to_demo_url(v),
85
- 'lib': version_to_lib_url(v),
86
- 'ui_lib': version_to_ui_lib_url(v),
87
- 'lib_externs': version_to_lib_externs_url(v),
88
- 'ui_lib_externs': version_to_ui_lib_externs_url(v),
89
- 'lib_defs': version_to_lib_defs_url(v),
90
- 'ui_lib_defs': version_to_ui_lib_defs_url(v),
91
- }
92
-
93
- def is_release_version(name):
94
- return re.match(r'v\d+-\d+-\d+(?:-.+)?', name)
95
-
96
- def appengine_version_to_package_version(version):
97
- # Replace the first two dashes with dots. More dashes indicate a prerelease
98
- # version, as seen in "v2.0.0-beta3".
99
- return version.replace('-', '.', 2)
100
-
101
- def version_key(version):
102
- if version == 'nightly':
103
- # A false version number for nightly, greater than any actual release
104
- # version.
105
- return [float('inf')]
106
-
107
- assert version[0] == 'v'
108
- main_version, _, suffix = version[1:].partition('-')
109
- if not suffix:
110
- suffix = '}}}' # this puts main releases after prerelease versions
111
- version_tuple = [int(x) for x in main_version.split('.')]
112
- return version_tuple + [suffix]
113
-
114
- def get_appengine_versions():
115
- if os.getenv('SERVER_SOFTWARE', '').startswith('Google App Engine/'):
116
- # NOTE: this doesn't return anything useful in a local dev server.
117
- return google.appengine.api.modules.modules.get_versions()
118
-
119
- # For a local dev server, fake it so we can test sorting.
120
- fake_versions = [
121
- 'v1-6-0', 'v1-6-1', 'v1-6-2', 'v1-6-3', 'v1-6-4', 'v1-6-5',
122
- 'v2-0-0-beta', 'v2-0-0-beta2', 'v2-0-0-beta3', 'v2-0-0',
123
- 'v2-1-0', 'v2-1-1', 'v2-1-2',
124
- 'v2-2-1', 'v2-2-2-beta', 'v2-2-2-beta2', 'v2-2-2', 'v2-2-9', 'v2-2-10',
125
- ]
126
- random.shuffle(fake_versions) # in-place shuffle
127
- return fake_versions
128
-
129
-
130
- app = Flask(__name__)
131
-
132
- @app.route('/')
133
- def root():
134
- appengine_versions = get_appengine_versions()
135
- # Filter for release versions only.
136
- appengine_versions = filter(is_release_version, appengine_versions)
137
-
138
- # Now convert from appengine versions (v2-0-0) to package versions (v2.0.0).
139
- versions = list(map(appengine_version_to_package_version, appengine_versions))
140
- # Now sort, putting prerelease versions ahead of the corresponding release.
141
- versions.sort(key=version_key)
142
-
143
- version_metadata = collections.OrderedDict()
144
- latest_by_branch = {}
145
- for v in versions:
146
- version_metadata[v] = version_to_metadata(v)
147
-
148
- # Because |versions| is already sorted, we can just overwrite the entry
149
- # in the latest_by_branch dictionary.
150
- branch_key = tuple(version_key(v)[0:2])
151
- latest_by_branch[branch_key] = v
152
-
153
- for v in latest_by_branch.values():
154
- version_metadata[v]['best'] = True
155
-
156
- # Append nightly rather than filter for it, so that it always appears at the
157
- # end of the list.
158
- version_metadata['nightly'] = version_to_metadata('nightly')
159
- version_metadata['nightly']['best'] = True
160
-
161
- for i in version_metadata.values(): print(i)
162
- return render_template('index.html', versions=version_metadata.values())
@@ -1,6 +0,0 @@
1
- # Shaka Player Version Index - Appspot Python Runtime Requirements
2
- # Copyright 2022 Google LLC
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- Flask==2.0.3
6
- appengine-python-standard==0.3.1