bloom-player 2.20.0-alpha.2 → 2.20.0-alpha.3

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.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "A library for displaying Bloom books in iframes or WebViews",
4
4
  "author": "SIL Global",
5
5
  "license": "MIT",
6
- "version": "2.20.0-alpha.2",
6
+ "version": "2.20.0-alpha.3",
7
7
  "packageManager": "pnpm@11.1.2",
8
8
  "private": false,
9
9
  "// sideeffects might need to be ['*.css'] to avoid 'shaking' our CSS, if we ever get tree shaking working": "",
@@ -1454,6 +1454,74 @@ function playAllVideoInternal(
1454
1454
  hideVideoError(video);
1455
1455
  hideVideoAutoplayBlockedHint(video);
1456
1456
  setCurrentPlaybackMode(PlaybackMode.VideoPlaying);
1457
+ // Always play each queued video from the beginning.
1458
+ // Without this, a previously played element may remain at end-of-stream
1459
+ // and fail to raise the expected ended event for sequencing.
1460
+ video.currentTime = 0;
1461
+ // Note that we get a new instance of this for each recursive call to playAllVideoInternal.
1462
+ // It serves to make sure we don't try to advance twice if we get both an ended and a pause event.
1463
+ let advanced = false;
1464
+ let watchdogTimerId: number | undefined;
1465
+ const watchdogGraceMs = 250;
1466
+ const clearWatchdog = () => {
1467
+ if (watchdogTimerId !== undefined) {
1468
+ window.clearTimeout(watchdogTimerId);
1469
+ watchdogTimerId = undefined;
1470
+ }
1471
+ };
1472
+ const scheduleWatchdogFromDuration = () => {
1473
+ const duration = video.duration;
1474
+ if (!Number.isFinite(duration) || duration <= 0) {
1475
+ return;
1476
+ }
1477
+ clearWatchdog();
1478
+ const playbackRate =
1479
+ Number.isFinite(video.playbackRate) && video.playbackRate > 0
1480
+ ? video.playbackRate
1481
+ : 1;
1482
+ const timeoutMs = Math.max(
1483
+ duration * 1000 / playbackRate + watchdogGraceMs,
1484
+ watchdogGraceMs,
1485
+ );
1486
+ watchdogTimerId = window.setTimeout(() => {
1487
+ advanceToNextVideo();
1488
+ }, timeoutMs);
1489
+ };
1490
+ const advanceToNextVideo = () => {
1491
+ if (advanced) {
1492
+ return;
1493
+ }
1494
+ advanced = true;
1495
+ clearWatchdog();
1496
+ video.removeEventListener("ended", endedHandler);
1497
+ video.removeEventListener("pause", pauseHandler);
1498
+ video.removeEventListener("loadedmetadata", metadataHandler);
1499
+ if (generation !== playAllVideoGeneration) {
1500
+ return;
1501
+ }
1502
+ playAllVideoInternal(elements.slice(1), then, generation);
1503
+ };
1504
+ const endedHandler = () => {
1505
+ advanceToNextVideo();
1506
+ };
1507
+ // In some environments, playback can pause at the end without raising ended.
1508
+ // Treat that as completion so a short first video can't stall the sequence.
1509
+ const pauseHandler = () => {
1510
+ const duration = video.duration;
1511
+ if (!Number.isFinite(duration) || duration <= 0) {
1512
+ return;
1513
+ }
1514
+ if (video.currentTime >= duration - 0.05) {
1515
+ advanceToNextVideo();
1516
+ }
1517
+ };
1518
+ const metadataHandler = () => {
1519
+ scheduleWatchdogFromDuration();
1520
+ };
1521
+ // Register ended before play() so we don't miss extremely fast end transitions.
1522
+ video.addEventListener("ended", endedHandler, { once: true });
1523
+ video.addEventListener("pause", pauseHandler);
1524
+ video.addEventListener("loadedmetadata", metadataHandler);
1457
1525
  const promise = video.play();
1458
1526
  promise
1459
1527
  .then(() => {
@@ -1462,34 +1530,16 @@ function playAllVideoInternal(
1462
1530
  }
1463
1531
  transientVideoRetryCounts.delete(video);
1464
1532
  hideVideoAutoplayBlockedHint(video);
1465
- // The promise resolves when the video starts playing. We want to know when it ends.
1466
- // Note: in Bloom Desktop, sometimes this event does not fire normally, even when the video is
1467
- // played to the end. I have not figured out why. It may be something to do with how we are
1468
- // trimming the videos.
1469
- // In Bloom Desktop, this is worked around by raising the ended event when we detect that it has
1470
- // paused past the end point in resetToStartAfterPlayingToEndPoint.
1471
- // In BloomPlayer,I don't think this is a problem. Videos are trimmed when published, so we always
1472
- // play to the real end (unless the user pauses). So one way or another, we should get the ended
1473
- // event.
1474
- video.addEventListener(
1475
- "ended",
1476
- () => {
1477
- if (generation !== playAllVideoGeneration) {
1478
- return;
1479
- }
1480
- playAllVideoInternal(
1481
- elements.slice(1),
1482
- then,
1483
- generation,
1484
- );
1485
- },
1486
- { once: true },
1487
- );
1533
+ scheduleWatchdogFromDuration();
1488
1534
  })
1489
1535
  .catch((reason) => {
1490
1536
  if (generation !== playAllVideoGeneration) {
1491
1537
  return;
1492
1538
  }
1539
+ clearWatchdog();
1540
+ video.removeEventListener("ended", endedHandler);
1541
+ video.removeEventListener("pause", pauseHandler);
1542
+ video.removeEventListener("loadedmetadata", metadataHandler);
1493
1543
  if (reason?.name === "NotAllowedError") {
1494
1544
  console.debug(
1495
1545
  "Video autoplay blocked until user interaction",