Skip to content

Commit

Permalink
feat(player): add playedPercent and playedRanges helpers
Browse files Browse the repository at this point in the history
Add two helper functions based on the `played` function native to the
`HTMLMediaElement`:
- `playedRanges` returns an array representing the start and end times of the
played portions of a media. This could make it easier to calculate the
effective playback duration of a media, without having to use a complex
mechanism for listening to the seeking/seeked events.
- `playedPercent` returns the effective percentage played of a media as a
decimal between `0` and `1`.

- add `playedPercent` and `playedRanges` functions to the `player` component
- add test coverage
  • Loading branch information
amtins committed Mar 28, 2024
1 parent b09a767 commit bbf6049
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
46 changes: 46 additions & 0 deletions src/components/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,52 @@ class Player extends vjsPlayer {
return ranges;
}

/**
* Get the percent (as a decimal) of the media that's been played.
* This method is not a part of the native HTML video API.
*
* Live streams with DVR are not currently supported.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#htmlmediaelement.played
*
* @return {number}
* A decimal between 0 and 1 representing the percent
* that is played 0 being 0% and 1 being 100%
*/
playedPercent() {
if (!Number.isFinite(this.duration())) return NaN;

let timePlayed = 0;

for (let i = 0; i != this.played().length; i++) {
timePlayed += this.played().end(i) - this.played().start(i);
}

const percentPlayed = timePlayed / this.duration();

return percentPlayed;
}

/**
* Get an array of ranges based on the `played` data.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#htmlmediaelement.played
*
* @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of played ranges.
*/
playedRanges() {
const ranges = [];

for (let i = 0; i < this.played().length; i++) {
const start = this.played().start(i);
const end = this.played().end(i);

ranges.push({ start, end });
}

return ranges;
}

/**
* Calculates an array of ranges based on the `seekable()` data.
*
Expand Down
65 changes: 65 additions & 0 deletions test/components/player.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,71 @@ describe('Player', () => {
});
});

describe('playedPercent', () => {
it('should return NaN if the duration is not finite', () => {
const player = new Player(videoEl);

player.duration = jest.fn().mockImplementation(() => {
return Infinity;
});

expect(player.playedPercent()).toBeNaN();
});

it('should return the correct percentage played', () => {
const player = new Player(videoEl);

player.duration = jest.fn().mockImplementation(() => {
return 100;
});
player.played = jest.fn().mockImplementation(() => {
return pillarbox.time.createTimeRanges([
[0, 5],
[10, 15]
]);
});

expect(player.playedPercent()).toEqual(0.1);
});
});

describe('playedRanges', () => {
it('should return an empty array if there are no played ranges', () => {
const player = new Player(videoEl);

player.duration = jest.fn().mockImplementation(() => {
return 69;
});
player.played = jest.fn().mockImplementation(() => {
return pillarbox.time.createTimeRanges();
});

expect(player.playedRanges()).toHaveLength(0);
});

it('should return an array containing two entries', () => {
const player = new Player(videoEl);

player.duration = jest.fn().mockImplementation(() => {
return 420;
});
player.played = jest.fn().mockImplementation(() => {
return pillarbox.time.createTimeRanges([
[0, 10],
[69, 420]
]);
});

expect(player.playedRanges()).toHaveLength(2);
expect(player.playedRanges()).toEqual(
expect.arrayContaining([
{ 'end': 10, 'start': 0 },
{ 'end': 420, 'start': 69 }
])
);
});
});

describe('seekableRanges', () => {
it('should return an empty array if there are no seekable ranges', () => {
const player = new Player(videoEl);
Expand Down

0 comments on commit bbf6049

Please sign in to comment.