diff --git a/src/index.all.js b/src/index.all.js index b17324d5..5ebcb71a 100644 --- a/src/index.all.js +++ b/src/index.all.js @@ -16,6 +16,7 @@ export * from './plugins/playlist/playlist.js'; export * from './plugins/interaction-areas/interaction-areas.service.js'; export * from './plugins/visual-search/visual-search.js'; export * from './plugins/srt-text-tracks/srt-text-tracks.js'; +export * from './plugins/share/share.js'; export * from './components/shoppable-bar/shoppable-widget.js'; export * from './components/recommendations-overlay/recommendations-overlay.js'; diff --git a/src/utils/get-analytics-player-options.js b/src/utils/get-analytics-player-options.js index c321bdb1..ee9b4ecb 100644 --- a/src/utils/get-analytics-player-options.js +++ b/src/utils/get-analytics-player-options.js @@ -11,30 +11,11 @@ const filterDefaultsAndNulls = (obj) => Object.entries(obj).reduce((filtered, [k return filtered; }, {}); -const getCloudinaryOptions = (cloudinaryOptions = {}) => ({ - autoShowRecommendations: cloudinaryOptions.autoShowRecommendations, - fontFace: cloudinaryOptions.fontFace, - posterOptions: hasConfig(cloudinaryOptions.posterOptions), - posterOptionsPublicId: cloudinaryOptions.posterOptions && hasConfig(cloudinaryOptions.posterOptions.publicId) -}); - -const getTranscriptOptions = (textTracks = {}) => { - const tracksArr = [textTracks.captions, ...(textTracks.subtitles || [])]; - return { - textTracks: hasConfig(textTracks), - textTracksLength: tracksArr.length, - textTracksOptions: hasConfig(textTracks.options) && Object.keys(textTracks.options).join(','), - pacedTextTracks: hasConfig(textTracks) && JSON.stringify(textTracks || {}).includes('"maxWords":') || null, - wordHighlight: hasConfig(textTracks) && JSON.stringify(textTracks || {}).includes('"wordHighlight":') || null, - transcriptLanguages: tracksArr.filter((track) => !track.url).map((track) => track.language || '').join(',') || null, - transcriptAutoLoaded: tracksArr.some((track) => !track.url) || null, - transcriptFromURl: tracksArr.some((track) => track.url?.endsWith('.transcript')) || null, - vttFromUrl: tracksArr.some((track) => track.url?.endsWith('.vtt')) || null, - srtFromUrl: tracksArr.some((track) => track.url?.endsWith('.srt')) || null - }; -}; - const getSourceOptions = (sourceOptions = {}) => ({ + posterOptions: hasConfig(sourceOptions.posterOptions), + posterOptionsPublicId: sourceOptions.posterOptions && hasConfig(sourceOptions.posterOptions.publicId), + autoShowRecommendations: sourceOptions.autoShowRecommendations, + fontFace: sourceOptions.fontFace, sourceTypes: sourceOptions.sourceTypes, chapters: (() => { if (sourceOptions.chapters === true) return 'auto'; @@ -46,7 +27,7 @@ const getSourceOptions = (sourceOptions = {}) => ({ download: hasConfig(sourceOptions.download), recommendations: sourceOptions.recommendations && sourceOptions.recommendations.length, ...(hasConfig(sourceOptions.adaptiveStreaming) ? { - abrStrategy: sourceOptions?.adaptiveStreaming?.strategy, + abrStrategy: sourceOptions?.adaptiveStreaming?.strategy === defaults.adaptiveStreaming.strategy ? undefined : sourceOptions?.adaptiveStreaming?.strategy, } : {}), shoppable: hasConfig(sourceOptions.shoppable), shoppableProductsLength: sourceOptions.shoppable && sourceOptions.shoppable.products && sourceOptions.shoppable.products.length, @@ -55,20 +36,34 @@ const getSourceOptions = (sourceOptions = {}) => ({ sourceTitle: (typeof sourceOptions.title === 'string' ? sourceOptions.title : sourceOptions.info?.title), sourceDescription: (typeof sourceOptions.description === 'string' ? sourceOptions.description : sourceOptions.info?.subtitle || sourceOptions.info?.description) } : {}), - ...(sourceOptions.textTracks ? { - ...(hasConfig(sourceOptions.textTracks) && getTranscriptOptions(sourceOptions.textTracks)), - ...(sourceOptions.textTracks.options ? { - styledTextTracksTheme: sourceOptions.textTracks.options.theme, - styledTextTracksFont: sourceOptions.textTracks.options.fontFace, - styledTextTracksFontSize: sourceOptions.textTracks.options.fontSize, - styledTextTracksGravity: sourceOptions.textTracks.options.gravity, - styledTextTracksBox: hasConfig(sourceOptions.textTracks.options.box), - styledTextTracksStyle: hasConfig(sourceOptions.textTracks.options.style), - styledTextTracksWordHighlightStyle: hasConfig(sourceOptions.textTracks.options.wordHighlightStyle) - } : {}) - } : {}) + ...(hasConfig(sourceOptions.textTracks) ? getTextTracksOptions(sourceOptions.textTracks) : {}) }); +const getTextTracksOptions = (textTracks = {}) => { + const tracksArr = [textTracks.captions, ...(textTracks.subtitles || [])]; + return { + textTracks: hasConfig(textTracks), + textTracksLength: tracksArr.length, + textTracksOptions: hasConfig(textTracks.options) && Object.keys(textTracks.options).join(','), + pacedTextTracks: hasConfig(textTracks) && JSON.stringify(textTracks || {}).includes('"maxWords":') || null, + wordHighlight: hasConfig(textTracks) && JSON.stringify(textTracks || {}).includes('"wordHighlight":') || null, + transcriptLanguages: tracksArr.filter((track) => !track.url).map((track) => track.language || '').join(',') || null, + transcriptAutoLoaded: tracksArr.some((track) => !track.url) || null, + transcriptFromURl: tracksArr.some((track) => track.url?.endsWith('.transcript')) || null, + vttFromUrl: tracksArr.some((track) => track.url?.endsWith('.vtt')) || null, + srtFromUrl: tracksArr.some((track) => track.url?.endsWith('.srt')) || null, + ...(textTracks.options ? { + styledTextTracksTheme: textTracks.options.theme, + styledTextTracksFont: textTracks.options.fontFace, + styledTextTracksFontSize: textTracks.options.fontSize, + styledTextTracksGravity: textTracks.options.gravity, + styledTextTracksBox: hasConfig(textTracks.options.box), + styledTextTracksStyle: hasConfig(textTracks.options.style), + styledTextTracksWordHighlightStyle: hasConfig(textTracks.options.wordHighlightStyle) + } : {}) + }; +}; + const getAdsOptions = (adsOptions = {}) => ({ adsAdTagUrl: adsOptions.adTagUrl, adsShowCountdown: adsOptions.showCountdown, @@ -126,8 +121,7 @@ export const getAnalyticsFromPlayerOptions = (playerOptions) => filterDefaultsAn colors: playerOptions.colors && JSON.stringify(playerOptions.colors), controlBar: (JSON.stringify(playerOptions.controlBar) !== JSON.stringify(defaults.controlBar)) && JSON.stringify(playerOptions.controlBar), - ...getCloudinaryOptions(playerOptions.cloudinary), - ...getSourceOptions(playerOptions.sourceOptions), + ...getSourceOptions(playerOptions.sourceOptions || {}), ...getAdsOptions(playerOptions.ads), ...getPlaylistOptions(playerOptions.playlistWidget) }); diff --git a/src/video-player.js b/src/video-player.js index 304ca2ec..74af9305 100644 --- a/src/video-player.js +++ b/src/video-player.js @@ -24,7 +24,6 @@ import { PLAYER_EVENT, SOURCE_TYPE } from './utils/consts'; import { getAnalyticsFromPlayerOptions } from './utils/get-analytics-player-options'; import { extendCloudinaryConfig, normalizeOptions, isRawUrl, ERROR_CODE } from './plugins/cloudinary/common'; import { isVideoInReadyState, checkIfVideoIsAvailable } from './utils/video-retry'; -import { SOURCE_PARAMS } from './video-player.const'; const INTERNAL_ANALYTICS_URL = 'https://analytics-api-s.cloudinary.com'; @@ -117,7 +116,7 @@ class VideoPlayer extends Utils.mixin(Eventable) { const baseParams = new URLSearchParams({ vpVersion: VERSION, vpInstanceId: this.getVPInstanceId(), - cloudName: options.cloudinary.cloudinaryConfig.cloud_name, + cloudName: options.cloudinary.cloud_name, ...internalAnalyticsMetadata, }).toString(); fetch(`${INTERNAL_ANALYTICS_URL}/video_player_source?${analyticsParams}&${baseParams}`); @@ -410,14 +409,20 @@ class VideoPlayer extends Utils.mixin(Eventable) { } _initCloudinary() { - const { cloudinaryConfig } = this.playerOptions.cloudinary; + const cloudinaryConfig = this.playerOptions.cloudinary; cloudinaryConfig.chainTarget = this; if (cloudinaryConfig.secure !== false) { extendCloudinaryConfig(cloudinaryConfig, { secure: true }); } - this.videojs.cloudinary(this.playerOptions.cloudinary); + // Merge cloudinary config with source config for the plugin + const cloudinaryOptions = { + cloudinaryConfig, + ...this.playerOptions.sourceOptions + }; + + this.videojs.cloudinary(cloudinaryOptions); } _initAnalytics() { @@ -516,11 +521,11 @@ class VideoPlayer extends Utils.mixin(Eventable) { this._setExtendedEvents(); // Load first video (mainly to support video tag 'source' and 'public-id' attributes) - // Source parameters are set to playerOptions.cloudinary - const source = this.playerOptions.cloudinary.source || this.playerOptions.cloudinary.publicId; + // Source parameters are set to playerOptions.sourceOptions + const source = this.playerOptions.sourceOptions.source || this.playerOptions.sourceOptions.publicId; if (source) { - const sourceOptions = Object.assign({}, this.playerOptions.cloudinary); + const sourceOptions = Object.assign({}, this.playerOptions.sourceOptions); this.source(source, sourceOptions); } @@ -597,10 +602,8 @@ class VideoPlayer extends Utils.mixin(Eventable) { } // Inherit source parameters from player options (source options take precedence) - const inherited = pick(this.playerOptions, SOURCE_PARAMS); - const inheritedNested = this.playerOptions.cloudinary ? pick(this.playerOptions.cloudinary, SOURCE_PARAMS) : {}; - - options = { ...inherited, ...inheritedNested, ...options }; + const inherited = this.playerOptions.sourceOptions || {}; + options = { ...inherited, ...options }; if (options.shoppable && this.videojs.shoppable) { this.videojs.shoppable(this.videojs, options); diff --git a/src/video-player.utils.js b/src/video-player.utils.js index 4c49934e..187f5475 100644 --- a/src/video-player.utils.js +++ b/src/video-player.utils.js @@ -4,6 +4,7 @@ import defaults from './config/defaults'; import { PLAYER_PARAMS, SOURCE_PARAMS, + CLOUDINARY_CONFIG_PARAM, FLUID_CLASS_NAME, AUTO_PLAY_MODE } from './video-player.const'; @@ -89,8 +90,17 @@ export const extractOptions = (elem, options) => { // VideoPlayer specific options const playerOptions = Utils.sliceAndUnsetProperties(options, ...PLAYER_PARAMS); - // Cloudinary plugin specific options - playerOptions.cloudinary = Utils.sliceAndUnsetProperties(playerOptions, ...SOURCE_PARAMS); + // Cloudinary SDK config (cloud_name, secure, etc.) + playerOptions.cloudinary = Utils.sliceAndUnsetProperties(playerOptions, ...CLOUDINARY_CONFIG_PARAM); + + // Merge with cloudinaryConfig from src/index.js (e.g., secureDistribution -> secure_distribution) + if (playerOptions.cloudinaryConfig) { + Object.assign(playerOptions.cloudinary, playerOptions.cloudinaryConfig); + delete playerOptions.cloudinaryConfig; + } + + // Source-level config (visualSearch, chapters, etc.) + playerOptions.sourceOptions = Utils.sliceAndUnsetProperties(playerOptions, ...SOURCE_PARAMS); // Allow explicitly passing options to videojs using the `videojs` namespace, in order // to avoid param name conflicts: