Skip to content

Commit 0e35b92

Browse files
authored
fix(VIDEO-19832) - playlist and playlistByTag analytics (#895)
* fix(VIDEO-19832) - playlist and playlistByTag analytics * fix: chapters & abr analytics * fix: default playlist widget should be hidden * fix: refactor and separate source & cloud config (#896) * fix: refactor and separate source & cloud config * fix: default abrStrategy * fix: styledTextTracks + transcript analytics * fix: missing import in all.js
1 parent 5079ae2 commit 0e35b92

File tree

6 files changed

+81
-57
lines changed

6 files changed

+81
-57
lines changed

src/index.all.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export * from './plugins/playlist/playlist.js';
1616
export * from './plugins/interaction-areas/interaction-areas.service.js';
1717
export * from './plugins/visual-search/visual-search.js';
1818
export * from './plugins/srt-text-tracks/srt-text-tracks.js';
19+
export * from './plugins/share/share.js';
1920
export * from './components/shoppable-bar/shoppable-widget.js';
2021
export * from './components/recommendations-overlay/recommendations-overlay.js';
2122

src/plugins/playlist/playlist.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import isPlainObject from 'lodash/isPlainObject';
21
import { sliceProperties } from 'utils/slicing';
32
import { PLAYER_EVENT } from 'utils/consts';
43
import { getCloudinaryUrl } from 'plugins/cloudinary/common';
@@ -22,7 +21,7 @@ const playlist = (player, options = {}) => {
2221
playlistWidget.dispose();
2322
}
2423

25-
if (isPlainObject(options.playlistWidget)) {
24+
if (options.playlistWidget?.show != false) {
2625
if (player.fluid_) {
2726
options.playlistWidget.fluid = true;
2827
}

src/plugins/playlist/ui/playlist.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,13 @@ class Playlist {
240240
current.recommendations(this._defaultRecommendationsResolver(current));
241241
}
242242

243-
this._context.source(current, { recommendationOptions: { disableAutoShow: true, itemBuilder } });
243+
// Extract source options for analytics
244+
const sourceOptions = current.getInitOptions ? current.getInitOptions() : {};
245+
246+
this._context.source(current, {
247+
...sourceOptions,
248+
recommendationOptions: { disableAutoShow: true, itemBuilder }
249+
});
244250

245251
const eventData = { playlist: this, current, next: this.next() };
246252

src/utils/get-analytics-player-options.js

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,35 @@ const filterDefaultsAndNulls = (obj) => Object.entries(obj).reduce((filtered, [k
1111
return filtered;
1212
}, {});
1313

14-
const getCloudinaryOptions = (cloudinaryOptions = {}) => ({
15-
autoShowRecommendations: cloudinaryOptions.autoShowRecommendations,
16-
fontFace: cloudinaryOptions.fontFace,
17-
posterOptions: hasConfig(cloudinaryOptions.posterOptions),
18-
posterOptionsPublicId: cloudinaryOptions.posterOptions && hasConfig(cloudinaryOptions.posterOptions.publicId)
14+
const getSourceOptions = (sourceOptions = {}) => ({
15+
posterOptions: hasConfig(sourceOptions.posterOptions),
16+
posterOptionsPublicId: sourceOptions.posterOptions && hasConfig(sourceOptions.posterOptions.publicId),
17+
autoShowRecommendations: sourceOptions.autoShowRecommendations,
18+
fontFace: sourceOptions.fontFace,
19+
sourceTypes: sourceOptions.sourceTypes,
20+
chapters: (() => {
21+
if (sourceOptions.chapters === true) return 'auto';
22+
if (sourceOptions.chapters && sourceOptions.chapters.url) return 'url';
23+
if (sourceOptions.chapters) return 'inline-chapters';
24+
return undefined;
25+
})(),
26+
visualSearch: sourceOptions.visualSearch,
27+
download: hasConfig(sourceOptions.download),
28+
recommendations: sourceOptions.recommendations && sourceOptions.recommendations.length,
29+
...(hasConfig(sourceOptions.adaptiveStreaming) ? {
30+
abrStrategy: sourceOptions?.adaptiveStreaming?.strategy === defaults.adaptiveStreaming.strategy ? undefined : sourceOptions?.adaptiveStreaming?.strategy,
31+
} : {}),
32+
shoppable: hasConfig(sourceOptions.shoppable),
33+
shoppableProductsLength: sourceOptions.shoppable && sourceOptions.shoppable.products && sourceOptions.shoppable.products.length,
34+
...(sourceOptions.title || sourceOptions.description || sourceOptions.info ? {
35+
sourceInfo: hasConfig(sourceOptions.info),
36+
sourceTitle: (typeof sourceOptions.title === 'string' ? sourceOptions.title : sourceOptions.info?.title),
37+
sourceDescription: (typeof sourceOptions.description === 'string' ? sourceOptions.description : sourceOptions.info?.subtitle || sourceOptions.info?.description)
38+
} : {}),
39+
...(hasConfig(sourceOptions.textTracks) ? getTextTracksOptions(sourceOptions.textTracks) : {})
1940
});
2041

21-
const getTranscriptOptions = (textTracks = {}) => {
42+
const getTextTracksOptions = (textTracks = {}) => {
2243
const tracksArr = [textTracks.captions, ...(textTracks.subtitles || [])];
2344
return {
2445
textTracks: hasConfig(textTracks),
@@ -30,40 +51,19 @@ const getTranscriptOptions = (textTracks = {}) => {
3051
transcriptAutoLoaded: tracksArr.some((track) => !track.url) || null,
3152
transcriptFromURl: tracksArr.some((track) => track.url?.endsWith('.transcript')) || null,
3253
vttFromUrl: tracksArr.some((track) => track.url?.endsWith('.vtt')) || null,
33-
srtFromUrl: tracksArr.some((track) => track.url?.endsWith('.srt')) || null
54+
srtFromUrl: tracksArr.some((track) => track.url?.endsWith('.srt')) || null,
55+
...(textTracks.options ? {
56+
styledTextTracksTheme: textTracks.options.theme,
57+
styledTextTracksFont: textTracks.options.fontFace,
58+
styledTextTracksFontSize: textTracks.options.fontSize,
59+
styledTextTracksGravity: textTracks.options.gravity,
60+
styledTextTracksBox: hasConfig(textTracks.options.box),
61+
styledTextTracksStyle: hasConfig(textTracks.options.style),
62+
styledTextTracksWordHighlightStyle: hasConfig(textTracks.options.wordHighlightStyle)
63+
} : {})
3464
};
3565
};
3666

37-
const getSourceOptions = (sourceOptions = {}) => ({
38-
sourceTypes: sourceOptions.sourceTypes,
39-
chapters: sourceOptions.chapters && (sourceOptions.chapters.url ? 'url' : 'inline-chapters'),
40-
visualSearch: hasConfig(sourceOptions.visualSearch),
41-
download: hasConfig(sourceOptions.download),
42-
recommendations: sourceOptions.recommendations && sourceOptions.recommendations.length,
43-
...(sourceOptions.adaptiveStreaming ? {
44-
abrStrategy: sourceOptions?.adaptiveStreaming?.strategy,
45-
} : {}),
46-
shoppable: hasConfig(sourceOptions.shoppable),
47-
shoppableProductsLength: sourceOptions.shoppable && sourceOptions.shoppable.products && sourceOptions.shoppable.products.length,
48-
...(sourceOptions.title || sourceOptions.description || sourceOptions.info ? {
49-
sourceInfo: hasConfig(sourceOptions.info),
50-
sourceTitle: (typeof sourceOptions.title === 'string' ? sourceOptions.title : sourceOptions.info?.title),
51-
sourceDescription: (typeof sourceOptions.description === 'string' ? sourceOptions.description : sourceOptions.info?.subtitle || sourceOptions.info?.description)
52-
} : {}),
53-
...(sourceOptions.textTracks ? {
54-
...(hasConfig(sourceOptions.textTracks) && getTranscriptOptions(sourceOptions.textTracks)),
55-
...(sourceOptions.textTracks.options ? {
56-
styledTextTracksTheme: sourceOptions.textTracks.options.theme,
57-
styledTextTracksFont: sourceOptions.textTracks.options.fontFace,
58-
styledTextTracksFontSize: sourceOptions.textTracks.options.fontSize,
59-
styledTextTracksGravity: sourceOptions.textTracks.options.gravity,
60-
styledTextTracksBox: hasConfig(sourceOptions.textTracks.options.box),
61-
styledTextTracksStyle: hasConfig(sourceOptions.textTracks.options.style),
62-
styledTextTracksWordHighlightStyle: hasConfig(sourceOptions.textTracks.options.wordHighlightStyle)
63-
} : {})
64-
} : {})
65-
});
66-
6767
const getAdsOptions = (adsOptions = {}) => ({
6868
adsAdTagUrl: adsOptions.adTagUrl,
6969
adsShowCountdown: adsOptions.showCountdown,
@@ -74,7 +74,9 @@ const getAdsOptions = (adsOptions = {}) => ({
7474
adsAdsInPlaylist: adsOptions.adsInPlaylist
7575
});
7676

77-
const getPlaylistWidgetOptions = (playlistWidgetOptions = {}) => ({
77+
const getPlaylistOptions = (playlistWidgetOptions = {}) => ({
78+
playlist: playlistWidgetOptions.playlist,
79+
playlistByTag: playlistWidgetOptions.playlistByTag,
7880
playlistWidgetDirection: playlistWidgetOptions.direction,
7981
playlistWidgetTotal: playlistWidgetOptions.total
8082
});
@@ -119,8 +121,7 @@ export const getAnalyticsFromPlayerOptions = (playerOptions) => filterDefaultsAn
119121
colors: playerOptions.colors && JSON.stringify(playerOptions.colors),
120122
controlBar: (JSON.stringify(playerOptions.controlBar) !== JSON.stringify(defaults.controlBar)) && JSON.stringify(playerOptions.controlBar),
121123

122-
...getCloudinaryOptions(playerOptions.cloudinary),
123-
...getSourceOptions(playerOptions.sourceOptions),
124+
...getSourceOptions(playerOptions.sourceOptions || {}),
124125
...getAdsOptions(playerOptions.ads),
125-
...getPlaylistWidgetOptions(playerOptions.playlistWidget)
126+
...getPlaylistOptions(playerOptions.playlistWidget)
126127
});

src/video-player.js

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import { PLAYER_EVENT, SOURCE_TYPE } from './utils/consts';
2424
import { getAnalyticsFromPlayerOptions } from './utils/get-analytics-player-options';
2525
import { extendCloudinaryConfig, normalizeOptions, isRawUrl, ERROR_CODE } from './plugins/cloudinary/common';
2626
import { isVideoInReadyState, checkIfVideoIsAvailable } from './utils/video-retry';
27-
import { SOURCE_PARAMS } from './video-player.const';
2827

2928
const INTERNAL_ANALYTICS_URL = 'https://analytics-api-s.cloudinary.com';
3029

@@ -117,7 +116,7 @@ class VideoPlayer extends Utils.mixin(Eventable) {
117116
const baseParams = new URLSearchParams({
118117
vpVersion: VERSION,
119118
vpInstanceId: this.getVPInstanceId(),
120-
cloudName: options.cloudinary.cloudinaryConfig.cloud_name,
119+
cloudName: options.cloudinary.cloud_name,
121120
...internalAnalyticsMetadata,
122121
}).toString();
123122
fetch(`${INTERNAL_ANALYTICS_URL}/video_player_source?${analyticsParams}&${baseParams}`);
@@ -410,14 +409,20 @@ class VideoPlayer extends Utils.mixin(Eventable) {
410409
}
411410

412411
_initCloudinary() {
413-
const { cloudinaryConfig } = this.playerOptions.cloudinary;
412+
const cloudinaryConfig = this.playerOptions.cloudinary;
414413
cloudinaryConfig.chainTarget = this;
415414

416415
if (cloudinaryConfig.secure !== false) {
417416
extendCloudinaryConfig(cloudinaryConfig, { secure: true });
418417
}
419418

420-
this.videojs.cloudinary(this.playerOptions.cloudinary);
419+
// Merge cloudinary config with source config for the plugin
420+
const cloudinaryOptions = {
421+
cloudinaryConfig,
422+
...this.playerOptions.sourceOptions
423+
};
424+
425+
this.videojs.cloudinary(cloudinaryOptions);
421426
}
422427

423428
_initAnalytics() {
@@ -516,11 +521,11 @@ class VideoPlayer extends Utils.mixin(Eventable) {
516521
this._setExtendedEvents();
517522

518523
// Load first video (mainly to support video tag 'source' and 'public-id' attributes)
519-
// Source parameters are set to playerOptions.cloudinary
520-
const source = this.playerOptions.cloudinary.source || this.playerOptions.cloudinary.publicId;
524+
// Source parameters are set to playerOptions.sourceOptions
525+
const source = this.playerOptions.sourceOptions.source || this.playerOptions.sourceOptions.publicId;
521526

522527
if (source) {
523-
const sourceOptions = Object.assign({}, this.playerOptions.cloudinary);
528+
const sourceOptions = Object.assign({}, this.playerOptions.sourceOptions);
524529

525530
this.source(source, sourceOptions);
526531
}
@@ -597,10 +602,8 @@ class VideoPlayer extends Utils.mixin(Eventable) {
597602
}
598603

599604
// Inherit source parameters from player options (source options take precedence)
600-
const inherited = pick(this.playerOptions, SOURCE_PARAMS);
601-
const inheritedNested = this.playerOptions.cloudinary ? pick(this.playerOptions.cloudinary, SOURCE_PARAMS) : {};
602-
603-
options = { ...inherited, ...inheritedNested, ...options };
605+
const inherited = this.playerOptions.sourceOptions || {};
606+
options = { ...inherited, ...options };
604607

605608
if (options.shoppable && this.videojs.shoppable) {
606609
this.videojs.shoppable(this.videojs, options);
@@ -629,6 +632,8 @@ class VideoPlayer extends Utils.mixin(Eventable) {
629632
}
630633

631634
playlist(sources, options = {}) {
635+
this.playerOptions.playlistWidget = { ...(this.playerOptions.playlistWidget || { show: false }), playlist: true };
636+
632637
options = Object.assign({}, options, { playlistWidget: this.playerOptions.playlistWidget });
633638

634639
this.videojs.one(PLAYER_EVENT.READY, async () => {
@@ -640,6 +645,8 @@ class VideoPlayer extends Utils.mixin(Eventable) {
640645
}
641646

642647
playlistByTag(tag, options = {}) {
648+
this.playerOptions.playlistWidget = { ...(this.playerOptions.playlistWidget || { show: false}), playlistByTag: true };
649+
643650
options = Object.assign({}, options, { playlistWidget: this.playerOptions.playlistWidget });
644651

645652
return new Promise((resolve) => {

src/video-player.utils.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import defaults from './config/defaults';
44
import {
55
PLAYER_PARAMS,
66
SOURCE_PARAMS,
7+
CLOUDINARY_CONFIG_PARAM,
78
FLUID_CLASS_NAME,
89
AUTO_PLAY_MODE
910
} from './video-player.const';
@@ -89,8 +90,17 @@ export const extractOptions = (elem, options) => {
8990
// VideoPlayer specific options
9091
const playerOptions = Utils.sliceAndUnsetProperties(options, ...PLAYER_PARAMS);
9192

92-
// Cloudinary plugin specific options
93-
playerOptions.cloudinary = Utils.sliceAndUnsetProperties(playerOptions, ...SOURCE_PARAMS);
93+
// Cloudinary SDK config (cloud_name, secure, etc.)
94+
playerOptions.cloudinary = Utils.sliceAndUnsetProperties(playerOptions, ...CLOUDINARY_CONFIG_PARAM);
95+
96+
// Merge with cloudinaryConfig from src/index.js (e.g., secureDistribution -> secure_distribution)
97+
if (playerOptions.cloudinaryConfig) {
98+
Object.assign(playerOptions.cloudinary, playerOptions.cloudinaryConfig);
99+
delete playerOptions.cloudinaryConfig;
100+
}
101+
102+
// Source-level config (visualSearch, chapters, etc.)
103+
playerOptions.sourceOptions = Utils.sliceAndUnsetProperties(playerOptions, ...SOURCE_PARAMS);
94104

95105
// Allow explicitly passing options to videojs using the `videojs` namespace, in order
96106
// to avoid param name conflicts:

0 commit comments

Comments
 (0)