Skip to content

Commit

Permalink
feat(srg-analytics): standardize seek event tracking
Browse files Browse the repository at this point in the history
Standardizes `seek` event tracking according to the new specification.
This avoids sending a `seek` event when the player is `paused`, thus reducing code complexity.

- add an `isSeeking` property
- `pause` sends `seek` event when seek bar is used
- `play` resets the `isSeeking` property to `false` if it was `true`
- `seeking` sends the `seek` event when the `currentTime` API is used
- `timeupdate` tracks current time delayed by a tick
- update unit tests

Resolves #36
  • Loading branch information
amtins committed Oct 16, 2023
1 parent 548be84 commit 080156a
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 40 deletions.
60 changes: 24 additions & 36 deletions src/analytics/SRGAnalytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class SRGAnalytics {
this.heartBeatIntervalId = undefined;
/* Set to true when 'init' event is sent or queued. */
this.initialized = false;
this.isSeeking = false;
this.isWaiting = false;
this.mediaSession = 0;
this.pendingQueue = [];
Expand Down Expand Up @@ -729,6 +730,8 @@ class SRGAnalytics {
this.isPlaybackResumed = true;
this.notify('play');
}

if (this.isSeeking) this.isSeeking = false;
}

/**
Expand All @@ -750,11 +753,17 @@ class SRGAnalytics {

if (
!this.player.seeking() &&
!this.player.scrubbing() &&
!this.isMediaLive() &&
this.player.currentTime() < this.player.duration()
) {
this.notify('pause');

return;
}

if (!this.isSeeking){
this.notify('seek');
this.isSeeking = true;
}
}

Expand All @@ -781,46 +790,25 @@ class SRGAnalytics {
}

/**
* Calculate the time elapsed between two timeUpdate events.
* It allows to know when a seek does start and when it ends.
*
* __Rules__:
* - A seek action must start with a buffer_start notification
* - A seek event must be fired with the position where the seek starts
* - A seek action must end with a notification of play or pause according to the state of the player before seeking
* - Finally a buffer_stop must be notified
* Sent when the current time is modified by the player's currentTime API.
*
* @see https://docs.videojs.com/player#event:timeupdate
* @see https://docs.videojs.com/player#event:seeking
*/
timeUpdate() {
if (!this.hasFirstStart) return;

if (
!this.isMediaLive() &&
Math.abs(this.trackedCurrentTime - this.player.currentTime()) > 1.5 &&
!this.isSeeking
) {
this.isSeeking = true;
this.isPlaybackResumed = false;

seeking() {

Check warning on line 797 in src/analytics/SRGAnalytics.js

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🕹️ Function is not covered

Warning! Not covered function
if (!this.player.paused() && !this.isSeeking) {

Check warning on line 798 in src/analytics/SRGAnalytics.js

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 798 in src/analytics/SRGAnalytics.js

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
this.notify('seek');
this.isSeeking = true;

Check warning on line 800 in src/analytics/SRGAnalytics.js

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 801 in src/analytics/SRGAnalytics.js

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

Check warning on line 801 in src/analytics/SRGAnalytics.js

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
}

this.trackedCurrentTime = this.player.currentTime();

const hasDoneSeeking = this.isSeeking && !this.player.scrubbing();

if (!hasDoneSeeking) return;

this.isSeeking = false;

if (!this.player.paused() && !this.isPlaybackResumed) {
this.isPlaybackResumed = true;
this.notify('play');
}

if (this.player.paused()) {
this.notify('pause');
/**
* Track current time updates delayed by a tick.
*
* @see https://docs.videojs.com/player#event:timeupdate
*/
timeUpdate() {
if (!this.player.paused()) {
this.trackedCurrentTime = this.player.currentTime();
}
}

Expand Down
27 changes: 23 additions & 4 deletions test/analytics/srg-analytics.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,29 @@ jest.mock('../../src/pillarbox.js', () => ({
}));

const playbackSequences = (player, timeRanges = [[]]) => {
timeRanges.forEach(([start, end]) => {
for (let x = end - start, i = 0; i <= x; i++) {
player.currentTime.mockReturnValue(start + i);
timeRanges.forEach(([start, end], i) => {
let shouldSeek = i > 0;

for (let x = end - start, j = 0; j <= x; j++) {
const currentTimeNextTick = start + j;
const isSeeking = shouldSeek && currentTimeNextTick === start;

if (isSeeking) {
player.seeking.mockReturnValue(true);
player.trigger('pause');
player.trigger('seeking');
}

player.currentTime.mockReturnValue(currentTimeNextTick);
player.trigger('timeupdate');

if (isSeeking) {
player.trigger('seeked');
player.seeking.mockReturnValue(false);
player.trigger('play');
player.trigger('playing');
}

jest.advanceTimersByTime(1_000);
}
});
Expand Down Expand Up @@ -165,8 +184,8 @@ describe('SRGAnalytics', () => {
player.trigger('loadeddata');

player.duration.mockReturnValue(720);
player.play();
player.paused.mockReturnValue(false);
player.play();

playbackSequences(player, [
[0, 10],
Expand Down

0 comments on commit 080156a

Please sign in to comment.