Skip to content

Commit 0fc40d9

Browse files
authored
Merge branch 'master' into VIDEO-20614-poster-true
2 parents 6ec1fd1 + 2707888 commit 0fc40d9

20 files changed

Lines changed: 253 additions & 153 deletions

File tree

.github/workflows/e2e_pr.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ jobs:
1212
name: install
1313
runs-on: ubuntu-latest
1414
steps:
15+
- name: Set timestamp
16+
id: timestamp
17+
run: echo "TIMESTAMP=$(date +%Y%m%d_%H%M%S_%3N)" >> $GITHUB_ENV
18+
1519
- name: Checkout
1620
uses: actions/checkout@v4
1721
with:
@@ -45,9 +49,9 @@ jobs:
4549
uses: actions/upload-artifact@v4
4650
if: ${{ !cancelled() }}
4751
with:
48-
name: playwright-report
49-
path: playwright-report/
50-
retention-days: 30
52+
name: playwright-report
53+
path: playwright-report/
54+
retention-days: 30
5155

5256
- name: Pushes to reports repository
5357
if: always()
@@ -60,8 +64,8 @@ jobs:
6064
destination-repository-name: 'cloudinary-video-player-reports'
6165
user-email: ''
6266
target-branch: main
63-
target-directory: 'playwright-report_${{ github.run_id }}'
67+
target-directory: 'playwright-report_ts_${{ env.TIMESTAMP }}'
6468

6569
- name: Write URL in summary
6670
if: always()
67-
run: echo "### Test results https://cloudinary.github.io/cloudinary-video-player-reports/playwright-report_${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY
71+
run: echo "### Test results https://cloudinary.github.io/cloudinary-video-player-reports/playwright-report_ts_${{ env.TIMESTAMP }}" >> $GITHUB_STEP_SUMMARY

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
## [3.6.5](https://github.com/cloudinary/cloudinary-video-player/compare/v3.6.4...v3.6.5) (2026-01-14)
2+
3+
4+
### Bug Fixes
5+
6+
* **VIDEO-20638:** improved URL and search-queries handling ([#967](https://github.com/cloudinary/cloudinary-video-player/issues/967)) ([66cfc40](https://github.com/cloudinary/cloudinary-video-player/commit/66cfc405f6aa2d25be3c843dd9a40cffd7b61900))
7+
8+
9+
10+
## [3.6.4](https://github.com/cloudinary/cloudinary-video-player/compare/v3.6.3...v3.6.4) (2026-01-08)
11+
12+
13+
### Bug Fixes
14+
15+
* **VIDEO-20100:** analytics improvements ([#959](https://github.com/cloudinary/cloudinary-video-player/issues/959)) ([e4571d2](https://github.com/cloudinary/cloudinary-video-player/commit/e4571d2597fb17b7bf509e6df04c249f7d1556d9))
16+
17+
18+
119
## [3.6.3](https://github.com/cloudinary/cloudinary-video-player/compare/v3.6.2...v3.6.3) (2025-12-30)
220

321

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cloudinary-video-player",
3-
"version": "3.6.3",
3+
"version": "3.6.5",
44
"description": "Cloudinary Video Player",
55
"author": "Cloudinary",
66
"license": "MIT",

src/components/title-bar/title-bar.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'assets/styles/components/title-bar.scss';
33
import componentUtils from '../component-utils';
44
import { utf8ToBase64 } from '../../utils/utf8Base64';
55
import { getCloudinaryUrlPrefix } from 'plugins/cloudinary/common';
6+
import { appendQueryParams } from '../../utils/querystring';
67

78
// support VJS5 & VJS6 at the same time
89
const dom = videojs.dom || videojs;
@@ -47,7 +48,10 @@ class TitleBar extends Component {
4748

4849
const urlPrefix = getCloudinaryUrlPrefix(config);
4950
const deliveryType = source.getInitOptions().type || 'upload';
50-
const metadataUrl = `${urlPrefix}/_applet_/video_service/video_metadata/${deliveryType}/${utf8ToBase64(publicId)}.json`;
51+
const metadataUrl = appendQueryParams(
52+
`${urlPrefix}/_applet_/video_service/video_metadata/${deliveryType}/${utf8ToBase64(publicId)}.json`,
53+
source.queryParams()
54+
);
5155

5256
fetch(metadataUrl, {
5357
headers: {

src/extended-events.js

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,36 @@ const EVENT_DEFAULTS = {
99
}
1010
};
1111

12+
// Always-enabled events that the player will fire
1213
const DEFAULT_EVENTS = [
13-
PLAYER_EVENT.PERCENTS_PLAYED,
14+
PLAYER_EVENT.PLAYER_LOAD,
15+
PLAYER_EVENT.VIDEO_LOAD,
16+
PLAYER_EVENT.START,
1417
PLAYER_EVENT.PAUSE_NO_SEEK,
1518
PLAYER_EVENT.SEEK,
1619
PLAYER_EVENT.MUTE,
1720
PLAYER_EVENT.UNMUTE,
18-
PLAYER_EVENT.QUALITY_CHANGED
21+
PLAYER_EVENT.QUALITY_CHANGED,
22+
PLAYER_EVENT.PERCENTS_PLAYED
1923
];
2024

2125
const DEFAULT_OPTIONS = {
2226
events: DEFAULT_EVENTS
2327
};
2428

25-
// Emits the following additional events:
26-
// percentsplayed, timeplayed, pausenoseek, seek, mute, unmute
29+
// Emits custom Cloudinary player events:
30+
// playerload, videoload, start, percentsplayed, timeplayed, pausenoseek, seek, mute, unmute, qualitychanged
2731
class ExtendedEvents extends EventEmitter {
2832

2933
constructor(player, initOptions = {}) {
3034
super();
3135
this.player = player;
36+
37+
// Merge events arrays instead of replacing them
3238
const options = videojs.obj.merge(DEFAULT_OPTIONS, initOptions);
39+
if (initOptions.events) {
40+
options.events = [...DEFAULT_EVENTS, ...initOptions.events];
41+
}
3342

3443
let _muteData = { lastState: undefined };
3544
let _seekStart = 0;
@@ -39,6 +48,7 @@ class ExtendedEvents extends EventEmitter {
3948
let _timesTracked = [];
4049
let _currentSource = null;
4150
let _ended = false;
51+
let _startTracked = false;
4252

4353
const volumechange = (event) => {
4454
if (this.player.muted() && _muteData.lastState !== 'muted') {
@@ -114,9 +124,18 @@ class ExtendedEvents extends EventEmitter {
114124
};
115125

116126
const loadedmetadata = () => {
117-
if (this.player.currentSource().src !== _currentSource) {
127+
const src = this.player.currentSource().src;
128+
if (src !== _currentSource) {
118129
resetPerVideoState();
119-
_currentSource = this.player.currentSource().src;
130+
_currentSource = src;
131+
this.emit(PLAYER_EVENT.VIDEO_LOAD, null, { src });
132+
}
133+
};
134+
135+
const playing = () => {
136+
if (!_startTracked) {
137+
_startTracked = true;
138+
this.emit(PLAYER_EVENT.START);
120139
}
121140
};
122141

@@ -157,12 +176,14 @@ class ExtendedEvents extends EventEmitter {
157176
_muteData = { lastState: undefined };
158177
_seekStart = _seekEnd = 0;
159178
_seeking = false;
179+
_startTracked = false;
160180
resetPerVideoState();
161181
};
162182

163183
const resetPerVideoState = () => {
164184
_percentsTracked = [];
165185
_timesTracked = [];
186+
_startTracked = false;
166187
};
167188

168189
const ended = () => {
@@ -172,6 +193,12 @@ class ExtendedEvents extends EventEmitter {
172193
this.events = normalizeEventsParam(options.events, EVENT_DEFAULTS);
173194
resetState();
174195

196+
// Always fire playerload event immediately
197+
this.emit(PLAYER_EVENT.PLAYER_LOAD, null, { url: window.location.href });
198+
199+
// Always listen for start event
200+
this.player.on(PLAYER_EVENT.PLAYING, playing.bind(this));
201+
175202
this.player.on(PLAYER_EVENT.PLAY, replay.bind(this));
176203
this.player.on(PLAYER_EVENT.ENDED, ended.bind(this));
177204
if (this.events.percentsplayed || this.events.timeplayed ||

src/mixins/eventable.js

Lines changed: 0 additions & 54 deletions
This file was deleted.

src/plugins/analytics/index.js

Lines changed: 14 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import videojs from 'video.js';
2-
import { sliceProperties } from 'utils/slicing';
3-
import { normalizeEventsParam, default as ExtendedEvents } from 'extended-events';
2+
import { normalizeEventsParam } from 'extended-events';
43
import { PLAYER_EVENT } from '../../utils/consts';
54

65
const DEFAULT_EVENTS = [
@@ -38,36 +37,24 @@ class AnalyticsPlugin {
3837
this.options = videojs.obj.merge(DEFAULT_OPTIONS, initOptions);
3938
this.events = normalizeEventsParam(this.options.events, EVENT_DEFAULTS);
4039

41-
const extendedEvents = sliceProperties(this.events, PLAYER_EVENT.PERCENTS_PLAYED, PLAYER_EVENT.TIME_PLAYED, PLAYER_EVENT.PAUSE, PLAYER_EVENT.SEEK);
42-
43-
if (extendedEvents.pause) {
44-
delete extendedEvents.pause;
45-
extendedEvents.pausenoseek = {};
46-
}
47-
48-
this._extendedEvents = new ExtendedEvents(player, { events: extendedEvents });
49-
50-
this._currentSource = null;
5140
this._startTracked = null;
5241
this._endTracked = null;
5342

5443
this.resetState();
5544
}
5645

5746
init() {
58-
const playerLoad = () => {
59-
this.track({ action: 'Player Load', label: window.location.href, nonInteraction: true });
47+
const playerLoad = (event, data) => {
48+
const label = data?.url || window.location.href;
49+
this.track({ action: 'Player Load', label, nonInteraction: true });
6050
};
6151

6252
const play = () => {
6353
this.track({ action: 'Play' });
6454
};
6555

6656
const start = () => {
67-
if (this._startTracked) {
68-
this.track({ action: 'Start' });
69-
this._startTracked = true;
70-
}
57+
this.track({ action: 'Start' });
7158
};
7259

7360
const pause = () => {
@@ -174,34 +161,36 @@ class AnalyticsPlugin {
174161
}
175162

176163
if (this.events.start) {
177-
this.player.on(PLAYER_EVENT.PLAYING, start.bind(this));
164+
this.player.on(PLAYER_EVENT.START, start.bind(this));
178165
}
179166

180167
if (this.events.fullscreenchange) {
181168
this.player.on(PLAYER_EVENT.FULL_SCREEN_CHANGE, fullscreenchange.bind(this));
182169
}
183170

184171
if (this.events.percentsplayed) {
185-
this._extendedEvents.on(PLAYER_EVENT.PERCENTS_PLAYED, percentsPlayed.bind(this));
172+
this.player.on(PLAYER_EVENT.PERCENTS_PLAYED, percentsPlayed.bind(this));
186173
}
187174

188175
if (this.events.timeplayed) {
189-
this._extendedEvents.on(PLAYER_EVENT.TIME_PLAYED, timePlayed.bind(this));
176+
this.player.on(PLAYER_EVENT.TIME_PLAYED, timePlayed.bind(this));
190177
}
191178

192179
if (this.events.pause) {
193-
this._extendedEvents.on(PLAYER_EVENT.PAUSE_NO_SEEK, pause.bind(this));
180+
this.player.on(PLAYER_EVENT.PAUSE_NO_SEEK, pause.bind(this));
194181
}
195182

196183
if (this.events.seek) {
197-
this._extendedEvents.on(PLAYER_EVENT.SEEK, seek.bind(this));
184+
this.player.on(PLAYER_EVENT.SEEK, seek.bind(this));
198185
}
199186

200187
if (this.events.playerload) {
201-
playerLoad();
188+
this.player.on(PLAYER_EVENT.PLAYER_LOAD, playerLoad.bind(this));
202189
}
203190

204-
this.player.on(PLAYER_EVENT.LOADED_METADATA, this.loadedmetadata.bind(this));
191+
if (this.events.videoload) {
192+
this.player.on(PLAYER_EVENT.VIDEO_LOAD, this.videoload.bind(this));
193+
}
205194
}
206195

207196
track({ action, label, value = null, nonInteraction = false }) {
@@ -219,24 +208,10 @@ class AnalyticsPlugin {
219208
}
220209

221210
resetState() {
222-
this._currentSource = '';
223211
this._startTracked = false;
224212
this._endTracked = false;
225213
}
226214

227-
loadedmetadata() {
228-
const src = this.player.currentSource().src;
229-
230-
if (src !== this._currentSource) {
231-
this.resetState();
232-
this._currentSource = src;
233-
234-
if (this.events.videoload) {
235-
this.videoload();
236-
}
237-
}
238-
}
239-
240215
}
241216

242217
export default function(opts = {}) {

src/plugins/chapters/chapters.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import videojs from 'video.js';
33
import './chapters.scss';
44
import { getCloudinaryUrlPrefix } from '../cloudinary/common';
55
import { utf8ToBase64 } from '../../utils/utf8Base64';
6+
import { appendQueryParams } from '../../utils/querystring';
67

78
/**
89
* Chapters plugin.
@@ -68,10 +69,11 @@ const ChaptersPlugin = (function () {
6869
return null;
6970
}
7071

71-
const { type: deliveryType } = this.player.cloudinary.source().resourceConfig();
72+
const source = this.player.cloudinary.source();
73+
const { type: deliveryType } = source.resourceConfig();
7274
const urlPrefix = getCloudinaryUrlPrefix(this.player.cloudinary.cloudinaryConfig());
7375
const fullUrl = `${urlPrefix}/_applet_/video_service/chapters/${deliveryType}/${utf8ToBase64(currentPublicId)}.vtt`;
74-
return `${fullUrl}?t=${Date.now()}`;
76+
return appendQueryParams(fullUrl, source.queryParams());
7577
};
7678

7779
/**

0 commit comments

Comments
 (0)