cloudinary-video-player 1.6.2-edge.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +4 -0
- package/.snyk +19 -0
- package/.travis.yml +8 -0
- package/CHANGELOG.md +329 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/cld-video-player.css +2110 -0
- package/dist/cld-video-player.js +5249 -0
- package/dist/cld-video-player.light.css +1766 -0
- package/dist/cld-video-player.light.js +1399 -0
- package/dist/cld-video-player.light.min.css +1 -0
- package/dist/cld-video-player.light.min.js +2 -0
- package/dist/cld-video-player.light.min.js.LICENSE.txt +23 -0
- package/dist/cld-video-player.min.css +1 -0
- package/dist/cld-video-player.min.js +2 -0
- package/dist/cld-video-player.min.js.LICENSE.txt +26 -0
- package/dist/fonts/cloudinary_icon_for_black_bg.svg +69 -0
- package/dist/fonts/cloudinary_icon_for_white_bg.svg +69 -0
- package/docs/360.html +102 -0
- package/docs/_template.html +93 -0
- package/docs/adaptive-streaming.html +297 -0
- package/docs/analytics.html +140 -0
- package/docs/api.html +302 -0
- package/docs/audio.html +136 -0
- package/docs/autoplay-fallback.html +138 -0
- package/docs/autoplay-on-scroll.html +107 -0
- package/docs/codec-fallback.html +158 -0
- package/docs/colors.html +135 -0
- package/docs/components.html +284 -0
- package/docs/custom-cld-errors.html +134 -0
- package/docs/floating-player.html +98 -0
- package/docs/fluid.html +117 -0
- package/docs/force-hls-subtitles-ios.html +159 -0
- package/docs/index.html +83 -0
- package/docs/interaction-area.html +398 -0
- package/docs/live-customer.html +128 -0
- package/docs/multiple-players.html +125 -0
- package/docs/playlist-by-tag-cap.html +182 -0
- package/docs/playlist-by-tag.html +133 -0
- package/docs/playlist.html +133 -0
- package/docs/poster.html +155 -0
- package/docs/raw-url.html +104 -0
- package/docs/recommendations.html +155 -0
- package/docs/scripts.js +156 -0
- package/docs/seek-thumbs.html +90 -0
- package/docs/shoppable.html +335 -0
- package/docs/subtitles-and-captions.html +267 -0
- package/docs/transformations.html +171 -0
- package/docs/ui-config.html +108 -0
- package/docs/vast-vpaid.html +149 -0
- package/env.example.js +6 -0
- package/env.js +6 -0
- package/jest-puppeteer.config.js +14 -0
- package/jest.config.js +196 -0
- package/package.json +99 -0
- package/sandbox.config.json +3 -0
- package/setupJest.js +1 -0
- package/src/assets/fonts/VideoJS.svg +120 -0
- package/src/assets/fonts/VideoJS.ttf +0 -0
- package/src/assets/fonts/VideoJS.woff +0 -0
- package/src/assets/fonts/icons.json +120 -0
- package/src/assets/icons/cloudinary_icon_for_black_bg.svg +69 -0
- package/src/assets/icons/cloudinary_icon_for_white_bg.svg +69 -0
- package/src/assets/icons/cloudinary_logo_for_dark_bg.svg +188 -0
- package/src/assets/icons/cloudinary_logo_for_white_bg.svg +188 -0
- package/src/assets/icons/info-circle.svg +17 -0
- package/src/assets/styles/ads-label.scss +16 -0
- package/src/assets/styles/components/interaction-areas.scss +158 -0
- package/src/assets/styles/components/playlist.scss +213 -0
- package/src/assets/styles/components/themedButton.scss +48 -0
- package/src/assets/styles/components/thumbnail.scss +94 -0
- package/src/assets/styles/components/title-bar.scss +67 -0
- package/src/assets/styles/components/triangle-volume-bar.scss +52 -0
- package/src/assets/styles/icons.scss +257 -0
- package/src/assets/styles/main.scss +324 -0
- package/src/assets/styles/mixins/aspect-ratio.scss +16 -0
- package/src/assets/styles/mixins/disable-transition.scss +3 -0
- package/src/assets/styles/mixins/mixins.scss +5 -0
- package/src/assets/styles/mixins/skin.scss +64 -0
- package/src/assets/styles/variables.scss +2 -0
- package/src/assets/styles/videojs-ima.scss +252 -0
- package/src/components/component-utils.js +20 -0
- package/src/components/index.js +21 -0
- package/src/components/interaction-area/interaction-area.const.js +30 -0
- package/src/components/interaction-area/interaction-area.service.js +223 -0
- package/src/components/interaction-area/interaction-area.utils.js +236 -0
- package/src/components/jumpButtons/jump-10-minus.js +21 -0
- package/src/components/jumpButtons/jump-10-plus.js +20 -0
- package/src/components/logoButton/logo-button.const.js +3 -0
- package/src/components/logoButton/logo-button.js +30 -0
- package/src/components/logoButton/logo-button.scss +15 -0
- package/src/components/playlist/components/playlist-button.js +34 -0
- package/src/components/playlist/components/playlist-next-button.js +18 -0
- package/src/components/playlist/components/playlist-previous-button.js +18 -0
- package/src/components/playlist/components/playlist.js +5 -0
- package/src/components/playlist/components/playlist.scss +15 -0
- package/src/components/playlist/components/upcoming-video-overlay.js +149 -0
- package/src/components/playlist/components/upcoming-video-overlay.scss +86 -0
- package/src/components/playlist/layout/playlist-layout-custom.js +21 -0
- package/src/components/playlist/layout/playlist-layout-horizontal.js +16 -0
- package/src/components/playlist/layout/playlist-layout-vertical.js +19 -0
- package/src/components/playlist/layout/playlist-layout.js +110 -0
- package/src/components/playlist/panel/playlist-panel-item.js +86 -0
- package/src/components/playlist/panel/playlist-panel.js +92 -0
- package/src/components/playlist/playlist-widget.js +119 -0
- package/src/components/playlist/playlist.const.js +14 -0
- package/src/components/playlist/playlist.js +413 -0
- package/src/components/playlist/thumbnail/thumbnail.js +69 -0
- package/src/components/progress-control-events-blocker/progress-control-events-blocker.js +17 -0
- package/src/components/qualitySelector/quality-selector.scss +10 -0
- package/src/components/qualitySelector/qualitySelector.js +152 -0
- package/src/components/recommendations-overlay/index.js +3 -0
- package/src/components/recommendations-overlay/recommendations-overlay-content.js +57 -0
- package/src/components/recommendations-overlay/recommendations-overlay-hide-button.js +18 -0
- package/src/components/recommendations-overlay/recommendations-overlay-item.js +35 -0
- package/src/components/recommendations-overlay/recommendations-overlay-primary-item.js +81 -0
- package/src/components/recommendations-overlay/recommendations-overlay-secondary-item.js +48 -0
- package/src/components/recommendations-overlay/recommendations-overlay-secondary-items-container.js +35 -0
- package/src/components/recommendations-overlay/recommendations-overlay.js +94 -0
- package/src/components/recommendations-overlay/recommendations-overlay.scss +182 -0
- package/src/components/shoppable-bar/layout/bar-layout.js +111 -0
- package/src/components/shoppable-bar/layout/shoppable-panel-toggle.js +64 -0
- package/src/components/shoppable-bar/layout/shoppable-products-overlay.js +87 -0
- package/src/components/shoppable-bar/panel/shoppable-panel-item.js +105 -0
- package/src/components/shoppable-bar/panel/shoppable-panel.js +172 -0
- package/src/components/shoppable-bar/shoppable-post-widget.js +110 -0
- package/src/components/shoppable-bar/shoppable-widget.const.js +52 -0
- package/src/components/shoppable-bar/shoppable-widget.js +111 -0
- package/src/components/shoppable-bar/shoppable-widget.scss +359 -0
- package/src/components/themeButton/themedButton.const.js +3 -0
- package/src/components/themeButton/themedButton.js +25 -0
- package/src/components/title-bar/title-bar.js +79 -0
- package/src/config/defaults.js +25 -0
- package/src/extended-events.js +228 -0
- package/src/index.js +18 -0
- package/src/mixins/eventable.js +54 -0
- package/src/mixins/playlistable.js +106 -0
- package/src/plugins/analytics/index.js +245 -0
- package/src/plugins/autoplay-on-scroll/index.js +86 -0
- package/src/plugins/cloudinary/common.js +216 -0
- package/src/plugins/cloudinary/event-handler-registry.js +46 -0
- package/src/plugins/cloudinary/index.js +345 -0
- package/src/plugins/cloudinary/models/audio-source/audio-source.const.js +11 -0
- package/src/plugins/cloudinary/models/audio-source/audio-source.js +82 -0
- package/src/plugins/cloudinary/models/base-source.js +107 -0
- package/src/plugins/cloudinary/models/image-source.js +26 -0
- package/src/plugins/cloudinary/models/video-source/video-source.const.js +32 -0
- package/src/plugins/cloudinary/models/video-source/video-source.js +239 -0
- package/src/plugins/cloudinary/models/video-source/video-source.utils.js +57 -0
- package/src/plugins/colors/index.js +303 -0
- package/src/plugins/context-menu/components/context-menu-item.js +12 -0
- package/src/plugins/context-menu/components/context-menu.js +63 -0
- package/src/plugins/context-menu/context-menu.scss +30 -0
- package/src/plugins/context-menu/contextMenuContent.js +53 -0
- package/src/plugins/context-menu/index.js +134 -0
- package/src/plugins/dash/index.js +26 -0
- package/src/plugins/dash/setup-audio-tracks.js +112 -0
- package/src/plugins/dash/setup-text-tracks.js +195 -0
- package/src/plugins/dash/videojs-dash.js +372 -0
- package/src/plugins/floating-player/floating-player.scss +74 -0
- package/src/plugins/floating-player/index.js +129 -0
- package/src/plugins/ima/index.js +1775 -0
- package/src/plugins/index.js +31 -0
- package/src/plugins/interactive-plugin/index.js +10 -0
- package/src/plugins/videojs-http-source-selector/components/SourceMenuButton.js +98 -0
- package/src/plugins/videojs-http-source-selector/components/SourceMenuItem.js +52 -0
- package/src/plugins/videojs-http-source-selector/plugin.js +82 -0
- package/src/plugins/videojs-http-source-selector/plugin.scss +9 -0
- package/src/plugins/vtt-thumbnails/index.js +526 -0
- package/src/plugins/vtt-thumbnails/vtt-thumbnails.scss +29 -0
- package/src/utils/api.js +32 -0
- package/src/utils/apply-with-props.js +32 -0
- package/src/utils/array.js +22 -0
- package/src/utils/assign.js +27 -0
- package/src/utils/attributes-normalizer.js +72 -0
- package/src/utils/cloudinary.js +165 -0
- package/src/utils/css-prefix.js +43 -0
- package/src/utils/dom.js +74 -0
- package/src/utils/find.js +28 -0
- package/src/utils/fontFace.js +25 -0
- package/src/utils/groupBy.js +12 -0
- package/src/utils/index.js +29 -0
- package/src/utils/matches.js +11 -0
- package/src/utils/mixin.js +5 -0
- package/src/utils/object.js +26 -0
- package/src/utils/playButton.js +9 -0
- package/src/utils/positioning.js +78 -0
- package/src/utils/querystring.js +12 -0
- package/src/utils/slicing.js +21 -0
- package/src/utils/string.js +15 -0
- package/src/utils/throttle.js +30 -0
- package/src/utils/time.js +77 -0
- package/src/utils/type-inference.js +35 -0
- package/src/validators/validators-functions.js +48 -0
- package/src/validators/validators-types.js +78 -0
- package/src/validators/validators.js +110 -0
- package/src/video-player.const.js +68 -0
- package/src/video-player.js +761 -0
- package/src/video-player.utils.js +123 -0
- package/test/adaptive-streaming.test.js +38 -0
- package/test/ads.test.js +35 -0
- package/test/analytics.test.js +111 -0
- package/test/api.test.js +111 -0
- package/test/autoplay.scroll.test.js +23 -0
- package/test/basic-ui.test.js +59 -0
- package/test/colors.test.js +58 -0
- package/test/components.test.js +21 -0
- package/test/custom-error.test.js +24 -0
- package/test/fluid.test.js +36 -0
- package/test/isValidConfig.test.js +224 -0
- package/test/mocks/cloudinary-core-mock.js +0 -0
- package/test/mocks/styleMock.js +1 -0
- package/test/multiplayer.test.js +25 -0
- package/test/playlist.test.js +60 -0
- package/test/puppeteer/vp-env.js +19 -0
- package/test/recommendations.test.js +38 -0
- package/test/title-bar.test.js +28 -0
- package/test/ui-conf.test.js +49 -0
- package/test/unit/cloudinaryConfig.test.js +22 -0
- package/test/unit/cloudinaryUtils.test.js +53 -0
- package/test/unit/utils.test.js +27 -0
- package/test/unit/videoSource.test.js +454 -0
- package/tsconfig.json +15 -0
- package/types/video-player-tests.js +12 -0
- package/types/video-player-tests.ts +31 -0
- package/types/video-player.d.ts +570 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import cloudinary from 'cloudinary-core';
|
|
2
|
+
import { assign } from 'utils/assign';
|
|
3
|
+
import { sliceAndUnsetProperties } from 'utils/slicing';
|
|
4
|
+
import { isString, isPlainObject } from 'utils/type-inference';
|
|
5
|
+
|
|
6
|
+
const normalizeOptions = (publicId, options, { tolerateMissingId = false } = {}) => {
|
|
7
|
+
if (isPlainObject(publicId)) {
|
|
8
|
+
const _options = assign({}, publicId);
|
|
9
|
+
|
|
10
|
+
publicId = sliceAndUnsetProperties(_options, 'publicId').publicId;
|
|
11
|
+
|
|
12
|
+
if (!isString(publicId) && !tolerateMissingId) {
|
|
13
|
+
throw new Error('Source is missing \'publicId\'.');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (options) {
|
|
17
|
+
options = assign({}, _options, options);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return { publicId, options };
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const isSrcEqual = (source1, source2) => {
|
|
25
|
+
let src1 = source1;
|
|
26
|
+
let src2 = source2;
|
|
27
|
+
|
|
28
|
+
if (typeof source1 === 'object') {
|
|
29
|
+
src1 = source1.src;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (typeof source2 === 'object') {
|
|
33
|
+
src2 = source2.src;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (/^\/\//.test(src1)) {
|
|
37
|
+
src2 = src2.slice(src2.indexOf('//'));
|
|
38
|
+
}
|
|
39
|
+
if (/^\/\//.test(src2)) {
|
|
40
|
+
src1 = src1.slice(src1.indexOf('//'));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return src1 === src2;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const mergeCloudinaryConfig = (config1, config2) => {
|
|
47
|
+
if (config1.constructor.name === 'Cloudinary' && config1.config) {
|
|
48
|
+
config1 = config1.config();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const newConfig = new cloudinary.Cloudinary(config1);
|
|
52
|
+
|
|
53
|
+
if (config2.constructor.name === 'Cloudinary' && config2.config) {
|
|
54
|
+
config2 = config1.config();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return newConfig.config(config2);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const mergeTransformation = (transformation1, transformation2) => {
|
|
61
|
+
if (transformation1.constructor.name === 'Transformation' && transformation1.toOptions) {
|
|
62
|
+
transformation1 = transformation1.toOptions();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const newTransformation = new cloudinary.Transformation(transformation1);
|
|
66
|
+
|
|
67
|
+
return newTransformation.fromOptions(transformation2);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const ERROR_CODE = {
|
|
71
|
+
NO_SUPPORTED_MEDIA: 6,
|
|
72
|
+
CUSTOM: 10,
|
|
73
|
+
UNKNOWN_CUSTOMER: 11,
|
|
74
|
+
RESOURCE_NOT_FOUND: 12,
|
|
75
|
+
PRIVATE_RESOURCE: 13,
|
|
76
|
+
UNAUTHENTICATED: 14
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const cloudinaryErrorsConverter = ({ errorMsg, publicId, cloudName, statusCode }) => {
|
|
80
|
+
const msg = 'Video cannot be played';
|
|
81
|
+
let error = { code: ERROR_CODE.CUSTOM, message: `${msg}${errorMsg ? '- ' + errorMsg : ''}`, statusCode: statusCode };
|
|
82
|
+
let err = errorMsg.toLowerCase();
|
|
83
|
+
if (err.startsWith('unknown customer')) {
|
|
84
|
+
error.code = ERROR_CODE.UNKNOWN_CUSTOMER;
|
|
85
|
+
error.message = `${msg} Unknown cloud-name ${cloudName}`;
|
|
86
|
+
}
|
|
87
|
+
if (err.startsWith('resource not found')) {
|
|
88
|
+
error.code = ERROR_CODE.RESOURCE_NOT_FOUND;
|
|
89
|
+
error.message = `${msg} Public ID ${publicId} not found`;
|
|
90
|
+
}
|
|
91
|
+
if (err.startsWith('private resource')) {
|
|
92
|
+
error.code = ERROR_CODE.PRIVATE_RESOURCE;
|
|
93
|
+
error.message = `${msg} Private video`;
|
|
94
|
+
}
|
|
95
|
+
if (err.startsWith('unauthenticated access')) {
|
|
96
|
+
error.message = `${msg} Requires authentication`;
|
|
97
|
+
error.code = ERROR_CODE.UNAUTHENTICATED;
|
|
98
|
+
}
|
|
99
|
+
return error;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const codecShorthandTrans = (short) => {
|
|
103
|
+
const transTable = {
|
|
104
|
+
h265: 'hev1.1.6.L93.B0',
|
|
105
|
+
vp9: 'vp09.00.50.08',
|
|
106
|
+
h264: 'avc1.42E01E'
|
|
107
|
+
};
|
|
108
|
+
return transTable[short] ? transTable[short] : short;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const ISOAVC_MAP = {
|
|
112
|
+
'avc1': 'h264',
|
|
113
|
+
'avc2': 'h264',
|
|
114
|
+
'svc1': 'Scalable Video Coding',
|
|
115
|
+
'mvc1': 'Multiview Video Coding',
|
|
116
|
+
'mvc2': 'Multiview Video Coding'
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
const PROFILE = {
|
|
121
|
+
'0': 'No', // 0 - *** when profile=RCDO and level=0 - "RCDO" - RCDO bitstream MUST obey to all the constraints of the Baseline profile
|
|
122
|
+
'42': 'baseline', // 66 in-decimal
|
|
123
|
+
'4d': 'main', // 77 in-decimal
|
|
124
|
+
'58': 'extended', // 88 in-decimal
|
|
125
|
+
'64': 'high', // 100 in-decimal
|
|
126
|
+
'6e': 'high 10', // 110 in-decimal
|
|
127
|
+
'7a': 'high 4:2:2', // 122 in-decimal
|
|
128
|
+
'f4': 'high 4:4:4', // 244 in-decimal
|
|
129
|
+
'2c': 'CAVLC 4:4:4', // 44 in-decimal
|
|
130
|
+
|
|
131
|
+
// profiles for SVC - Scalable Video Coding extension to H.264
|
|
132
|
+
'53': 'Scalable Baseline', // 83 in-decimal
|
|
133
|
+
'56': 'Scalable High', // 86 in-decimal
|
|
134
|
+
|
|
135
|
+
// profiles for MVC - Multiview Video Coding extension to H.264
|
|
136
|
+
'80': 'Stereo High', // 128 in-decimal
|
|
137
|
+
'76': 'Multiview High', // 118 in-decimal
|
|
138
|
+
'8a': 'Multiview Depth High' // 138 in-decimal
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
function avcotiToStr(s) {
|
|
143
|
+
let REGEX = /([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i;
|
|
144
|
+
|
|
145
|
+
if (REGEX.test(s) === false) {
|
|
146
|
+
throw new Error('error: please provide a 3-bytes hex-sequence for example: 42001e');
|
|
147
|
+
}
|
|
148
|
+
let matches = s.match(REGEX);
|
|
149
|
+
matches.shift(); // kills first one (regex matchs entire string)
|
|
150
|
+
|
|
151
|
+
let profile_idc = matches[0];
|
|
152
|
+
profile_idc = PROFILE[profile_idc];
|
|
153
|
+
profile_idc = typeof profile_idc === 'string' ? profile_idc : 'Unknown'; // explicit fix.
|
|
154
|
+
|
|
155
|
+
// constraint_set_flags = matches[1]; //maybe some other time..
|
|
156
|
+
|
|
157
|
+
let level_idc = matches[2];
|
|
158
|
+
level_idc = parseInt(level_idc, 16); // will give something like 30 (integer thirty)
|
|
159
|
+
level_idc = String(level_idc).split('').join('.'); // will give something like "3.0"
|
|
160
|
+
return `${profile_idc}:${level_idc}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
const h264avcToString = (s) => {
|
|
165
|
+
let REGEX = /(avc1|avc2|svc1|mvc1|mvc2)\.([0-9a-f]{6})/i;
|
|
166
|
+
if (REGEX.test('avc1.42001e') === false) {
|
|
167
|
+
throw new Error('Codec string is not formatted according to H.264/AVC standards for example avc1.42001e (maybe an iOS friendly version...)');
|
|
168
|
+
}
|
|
169
|
+
let matches = s.match(REGEX);
|
|
170
|
+
if (matches !== null) {
|
|
171
|
+
matches.shift(); // first one is the entire-string.
|
|
172
|
+
|
|
173
|
+
let vc_codec = ISOAVC_MAP[matches[0]];
|
|
174
|
+
let avc_codec = typeof avc_codec === 'string' ? avc_codec : 'Unknown'; // explicit fix
|
|
175
|
+
|
|
176
|
+
return vc_codec + ':' + avcotiToStr(matches[1]);
|
|
177
|
+
}
|
|
178
|
+
return s;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export const VIDEO_CODEC = {
|
|
182
|
+
VP9: 'vp9',
|
|
183
|
+
HEV1: 'hev1',
|
|
184
|
+
H265: 'h265',
|
|
185
|
+
H264: 'h264'
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const codecToSrcTransformation = (codec) => {
|
|
189
|
+
if (!codec) {
|
|
190
|
+
return {};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
switch (codec) {
|
|
194
|
+
case VIDEO_CODEC.VP9:
|
|
195
|
+
return { video_codec: VIDEO_CODEC.VP9 };
|
|
196
|
+
case VIDEO_CODEC.HEV1:
|
|
197
|
+
return { video_codec: VIDEO_CODEC.H265 };
|
|
198
|
+
case VIDEO_CODEC.H264:
|
|
199
|
+
return { video_codec: `${VIDEO_CODEC.H264}:baseline:3.0` };
|
|
200
|
+
default:
|
|
201
|
+
return { video_codec: h264avcToString(codec) };
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
export {
|
|
207
|
+
normalizeOptions,
|
|
208
|
+
isSrcEqual,
|
|
209
|
+
mergeCloudinaryConfig,
|
|
210
|
+
mergeTransformation,
|
|
211
|
+
cloudinaryErrorsConverter,
|
|
212
|
+
codecShorthandTrans,
|
|
213
|
+
h264avcToString,
|
|
214
|
+
codecToSrcTransformation,
|
|
215
|
+
ERROR_CODE
|
|
216
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { findIndex } from 'utils/find';
|
|
2
|
+
|
|
3
|
+
class EventHandlerRegistry {
|
|
4
|
+
constructor(emitter) {
|
|
5
|
+
this._emitter = emitter;
|
|
6
|
+
this._eventHandlers = [];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
on(type, handler) {
|
|
10
|
+
this._eventHandlers.push({ type, handler });
|
|
11
|
+
this._emitter.on(type, handler);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
one(type, handler) {
|
|
15
|
+
const wrapper = (...args) => {
|
|
16
|
+
handler(...args);
|
|
17
|
+
this.off(type, handler);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
this._eventHandlers.push({ type, handler, wrapper });
|
|
21
|
+
this._emitter.one(type, handler);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
off(type, handler) {
|
|
25
|
+
const index = findIndex(this._eventHandlers, (event) =>
|
|
26
|
+
event.type === type && event.handler === handler);
|
|
27
|
+
|
|
28
|
+
if (index === -1) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const event = this._eventHandlers[index];
|
|
33
|
+
|
|
34
|
+
this._emitter.off(type, event.wrapper || event.handler);
|
|
35
|
+
|
|
36
|
+
this._eventHandlers.splice(index, 1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
removeAllListeners() {
|
|
40
|
+
this._eventHandlers.forEach((event) => {
|
|
41
|
+
this.off(event);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default EventHandlerRegistry;
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import cloudinary from 'cloudinary-core';
|
|
2
|
+
import { default as vjs } from 'video.js';
|
|
3
|
+
import { mixin } from 'utils/mixin';
|
|
4
|
+
import { applyWithProps } from 'utils/apply-with-props';
|
|
5
|
+
import { sliceAndUnsetProperties } from 'utils/slicing';
|
|
6
|
+
import { getCloudinaryInstanceOf, isKeyInTransformation } from 'utils/cloudinary';
|
|
7
|
+
import { assign } from 'utils/assign';
|
|
8
|
+
import { normalizeOptions, mergeTransformation, mergeCloudinaryConfig, codecShorthandTrans } from './common';
|
|
9
|
+
import Playlistable from 'mixins/playlistable';
|
|
10
|
+
import VideoSource from './models/video-source/video-source';
|
|
11
|
+
import EventHandlerRegistry from './event-handler-registry';
|
|
12
|
+
import AudioSource from './models/audio-source/audio-source';
|
|
13
|
+
import { isFunction } from '../../utils/type-inference';
|
|
14
|
+
|
|
15
|
+
const DEFAULT_PARAMS = {
|
|
16
|
+
transformation: {},
|
|
17
|
+
sourceTypes: [],
|
|
18
|
+
sourceTransformation: [],
|
|
19
|
+
posterOptions: {}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const CONSTRUCTOR_PARAMS = ['cloudinaryConfig', 'transformation',
|
|
23
|
+
'sourceTypes', 'sourceTransformation', 'posterOptions', 'autoShowRecommendations'];
|
|
24
|
+
|
|
25
|
+
class CloudinaryContext extends mixin(Playlistable) {
|
|
26
|
+
constructor(player, options = {}) {
|
|
27
|
+
super(player, options);
|
|
28
|
+
|
|
29
|
+
this.player = player;
|
|
30
|
+
options = assign({}, DEFAULT_PARAMS, options);
|
|
31
|
+
|
|
32
|
+
let _source = null;
|
|
33
|
+
let _sources = null;
|
|
34
|
+
let _lastSource = null;
|
|
35
|
+
let _lastPlaylist = null;
|
|
36
|
+
let _posterOptions = null;
|
|
37
|
+
let _cloudinaryConfig = null;
|
|
38
|
+
let _transformation = null;
|
|
39
|
+
let _sourceTypes = null;
|
|
40
|
+
let _sourceTransformation = null;
|
|
41
|
+
let _chainTarget = options.chainTarget;
|
|
42
|
+
let _playerEvents = null;
|
|
43
|
+
let _recommendations = null;
|
|
44
|
+
let _autoShowRecommendations = false;
|
|
45
|
+
|
|
46
|
+
this.source = (source, options = {}) => {
|
|
47
|
+
options = assign({}, options);
|
|
48
|
+
|
|
49
|
+
if (!source) {
|
|
50
|
+
return _source;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let src = null;
|
|
54
|
+
|
|
55
|
+
if (source instanceof VideoSource) {
|
|
56
|
+
src = source;
|
|
57
|
+
} else {
|
|
58
|
+
let { publicId, options: _options } = normalizeOptions(source, options);
|
|
59
|
+
src = this.buildSource(publicId, _options);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (src.recommendations()) {
|
|
63
|
+
const recommendations = src.recommendations();
|
|
64
|
+
|
|
65
|
+
let itemBuilder = null;
|
|
66
|
+
let disableAutoShow = false;
|
|
67
|
+
|
|
68
|
+
if (options.recommendationOptions) {
|
|
69
|
+
({ disableAutoShow, itemBuilder } = sliceAndUnsetProperties(options.recommendationOptions, 'disableAutoShow', 'itemBuilder'));
|
|
70
|
+
}
|
|
71
|
+
setRecommendations(recommendations, { disableAutoShow, itemBuilder });
|
|
72
|
+
} else {
|
|
73
|
+
unsetRecommendations();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
_source = src;
|
|
77
|
+
if (!options.skipRefresh) {
|
|
78
|
+
refresh();
|
|
79
|
+
}
|
|
80
|
+
this.player.trigger('cldsourcechanged', { source: src });
|
|
81
|
+
|
|
82
|
+
return _chainTarget;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
this.buildSource = (publicId, options = {}) => {
|
|
86
|
+
let builtSrc = null;
|
|
87
|
+
({ publicId, options } = normalizeOptions(publicId, options));
|
|
88
|
+
|
|
89
|
+
options.cloudinaryConfig = mergeCloudinaryConfig(this.cloudinaryConfig(), options.cloudinaryConfig || {});
|
|
90
|
+
options.transformation = mergeTransformation(this.transformation(), options.transformation || {});
|
|
91
|
+
options.sourceTransformation = options.sourceTransformation || this.sourceTransformation();
|
|
92
|
+
options.sourceTypes = options.sourceTypes || this.sourceTypes();
|
|
93
|
+
options.poster = options.poster || posterOptionsForCurrent();
|
|
94
|
+
options.queryParams = options.usageReport ? { _s: `vp-${VERSION}` } : {};
|
|
95
|
+
|
|
96
|
+
if (options.sourceTypes.indexOf('audio') > -1) {
|
|
97
|
+
builtSrc = new AudioSource(publicId, options);
|
|
98
|
+
} else {
|
|
99
|
+
builtSrc = new VideoSource(publicId, options);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return builtSrc;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
this.posterOptions = (options) => {
|
|
106
|
+
if (!options) {
|
|
107
|
+
return _posterOptions;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
_posterOptions = options;
|
|
111
|
+
|
|
112
|
+
return _chainTarget;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
this.cloudinaryConfig = (config) => {
|
|
116
|
+
if (!config) {
|
|
117
|
+
return _cloudinaryConfig;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
_cloudinaryConfig = getCloudinaryInstanceOf(cloudinary.Cloudinary, config);
|
|
121
|
+
|
|
122
|
+
return _chainTarget;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
this.transformation = (trans) => {
|
|
126
|
+
if (!trans) {
|
|
127
|
+
return _transformation;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_transformation = getCloudinaryInstanceOf(cloudinary.Transformation, trans);
|
|
131
|
+
|
|
132
|
+
return _chainTarget;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
this.sourceTypes = (types) => {
|
|
136
|
+
if (!types) {
|
|
137
|
+
return _sourceTypes;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_sourceTypes = types;
|
|
141
|
+
|
|
142
|
+
return _chainTarget;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
this.getCurrentSources = () => _sources;
|
|
146
|
+
|
|
147
|
+
this.sourceTransformation = (trans) => {
|
|
148
|
+
if (!trans) {
|
|
149
|
+
return _sourceTransformation;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
_sourceTransformation = trans;
|
|
153
|
+
|
|
154
|
+
return _chainTarget;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
this.on = (...args) => _playerEvents.on(...args);
|
|
158
|
+
this.one = (...args) => _playerEvents.one(...args);
|
|
159
|
+
this.off = (...args) => _playerEvents.off(...args);
|
|
160
|
+
|
|
161
|
+
this.autoShowRecommendations = (autoShow) => {
|
|
162
|
+
if (autoShow === undefined) {
|
|
163
|
+
return _autoShowRecommendations;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
_autoShowRecommendations = autoShow;
|
|
167
|
+
|
|
168
|
+
return _chainTarget;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
this.dispose = () => {
|
|
172
|
+
if (this.playlist()) {
|
|
173
|
+
this.disposePlaylist();
|
|
174
|
+
}
|
|
175
|
+
unsetRecommendations();
|
|
176
|
+
_source = undefined;
|
|
177
|
+
_playerEvents.removeAllListeners();
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const setRecommendations = (recommendations, { disableAutoShow = false, itemBuilder = null }) => {
|
|
181
|
+
unsetRecommendations();
|
|
182
|
+
|
|
183
|
+
if (!Array.isArray(recommendations) && typeof recommendations !== 'function' && !recommendations.then) {
|
|
184
|
+
throw new Error('"recommendations" must be either an array or a function');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_recommendations = {};
|
|
188
|
+
|
|
189
|
+
itemBuilder = itemBuilder || ((source) => ({ source: source instanceof VideoSource ? source : this.buildSource(source), action: () => this.source(source) }));
|
|
190
|
+
|
|
191
|
+
_recommendations.sourceChangedHandler = () => {
|
|
192
|
+
const trigger = (sources) => {
|
|
193
|
+
if (typeof sources !== 'undefined' && sources.length > 0) {
|
|
194
|
+
const items = sources.map((_source) => itemBuilder(_source));
|
|
195
|
+
this.player.trigger('recommendationschanged', { items });
|
|
196
|
+
} else {
|
|
197
|
+
this.player.trigger('recommendationsnoshow');
|
|
198
|
+
}
|
|
199
|
+
_recommendations.sources = sources;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
if (isFunction(recommendations)) {
|
|
203
|
+
trigger(recommendations());
|
|
204
|
+
} else if (recommendations.then) {
|
|
205
|
+
recommendations.then(trigger);
|
|
206
|
+
} else {
|
|
207
|
+
trigger(recommendations);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
this.one('cldsourcechanged', _recommendations.sourceChangedHandler);
|
|
212
|
+
|
|
213
|
+
_recommendations.endedHandler = () => {
|
|
214
|
+
if (!disableAutoShow && this.autoShowRecommendations()) {
|
|
215
|
+
this.player.trigger('recommendationsshow');
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
this.on('ended', _recommendations.endedHandler);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const unsetRecommendations = () => {
|
|
223
|
+
if (_recommendations) {
|
|
224
|
+
this.off('cldsourcechanged', _recommendations.sourceChangedHandler);
|
|
225
|
+
this.off('ended', _recommendations.endedHandler);
|
|
226
|
+
delete _recommendations.endedHandler;
|
|
227
|
+
delete _recommendations.sourceChangedHandler;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
_recommendations = null;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const refresh = () => {
|
|
234
|
+
const src = this.source();
|
|
235
|
+
if (src.poster()) {
|
|
236
|
+
this.player.poster(src.poster().url());
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
_sources = src.generateSources().reduce((srcs, src) => {
|
|
240
|
+
if (src.isAdaptive) {
|
|
241
|
+
let codec = src.type.split('; ')[1] || null;
|
|
242
|
+
if (codec && 'MediaSource' in window) {
|
|
243
|
+
const parts = src.type.split('; ');
|
|
244
|
+
let typeStr = `video/mp4; ${parts[1] || ''}`;
|
|
245
|
+
const canPlay = testCanPlayTypeAndTypeSupported(typeStr);
|
|
246
|
+
if (vjs.browser.IS_ANY_SAFARI) {
|
|
247
|
+
// work around safari saying it cant play h265
|
|
248
|
+
src.type = `${parts[0]}; ${codecShorthandTrans('h264')}`;
|
|
249
|
+
}
|
|
250
|
+
if (canPlay) {
|
|
251
|
+
srcs.push(src);
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
srcs.push(src);
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
srcs.push(src);
|
|
258
|
+
}
|
|
259
|
+
return srcs;
|
|
260
|
+
}, []);
|
|
261
|
+
this.player.src(_sources);
|
|
262
|
+
|
|
263
|
+
_lastSource = src;
|
|
264
|
+
_lastPlaylist = this.playlist();
|
|
265
|
+
};
|
|
266
|
+
const testCanPlayTypeAndTypeSupported = (codec) => {
|
|
267
|
+
const v = document.createElement('video');
|
|
268
|
+
return v.canPlayType(codec) || 'MediaSource' in window && MediaSource.isTypeSupported(codec);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const posterOptionsForCurrent = () => {
|
|
272
|
+
const opts = assign({}, this.posterOptions());
|
|
273
|
+
if (opts.transformation) {
|
|
274
|
+
if ((opts.transformation.width || opts.transformation.height) && !opts.transformation.crop) {
|
|
275
|
+
opts.transformation.crop = 'scale';
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
opts.transformation = getCloudinaryInstanceOf(cloudinary.Transformation, opts.transformation || {});
|
|
280
|
+
|
|
281
|
+
// Set poster dimensions to player actual size.
|
|
282
|
+
// (unless they were explicitly set via `posterOptions`)
|
|
283
|
+
const playerEl = this.player.el();
|
|
284
|
+
if (playerEl && playerEl.clientWidth && playerEl.clientHeight && !isKeyInTransformation(opts.transformation, 'width') && !isKeyInTransformation(opts.transformation, 'height')) {
|
|
285
|
+
const roundUp100 = (val) => 100 * Math.ceil(val / 100);
|
|
286
|
+
|
|
287
|
+
opts.transformation
|
|
288
|
+
.width(roundUp100(playerEl.clientWidth))
|
|
289
|
+
.height(roundUp100(playerEl.clientHeight))
|
|
290
|
+
.crop('limit');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return opts;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
// Handle external (non-cloudinary plugin) source changes (e.g. by ad plugins)
|
|
297
|
+
const syncState = (_, data) => {
|
|
298
|
+
let src = data.to;
|
|
299
|
+
|
|
300
|
+
// When source is cloudinary's
|
|
301
|
+
if (_lastSource && _lastSource.contains(src)) {
|
|
302
|
+
// If plugin state doesn't have an active VideoSource
|
|
303
|
+
if (!this.source()) {
|
|
304
|
+
// We might have been running a playlist, reset playlist's state.
|
|
305
|
+
if (_lastPlaylist) {
|
|
306
|
+
this.playlist(_lastPlaylist);
|
|
307
|
+
}
|
|
308
|
+
// Rebuild last source state without calling vjs's 'src' and 'poster'
|
|
309
|
+
this.source(_lastSource, { skipRefresh: true });
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
// Used by cloudinary-only components
|
|
313
|
+
this.player.trigger('cldsourcechanged', {});
|
|
314
|
+
|
|
315
|
+
// When source isn't cloudinary's - reset the plugin's state.
|
|
316
|
+
this.dispose();
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
_playerEvents = new EventHandlerRegistry(this.player);
|
|
321
|
+
|
|
322
|
+
const constructorParams = sliceAndUnsetProperties(options, ...CONSTRUCTOR_PARAMS);
|
|
323
|
+
|
|
324
|
+
applyWithProps(this, constructorParams);
|
|
325
|
+
|
|
326
|
+
this.on('sourcechanged', syncState);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
currentSourceType() {
|
|
330
|
+
return this.source().getType();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
currentPublicId() {
|
|
334
|
+
return this.source() && this.source().publicId();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
currentPoster() {
|
|
338
|
+
return this.source() && this.source().poster();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export default function(options = {}) {
|
|
343
|
+
options.chainTarget = options.chainTarget || this;
|
|
344
|
+
this.cloudinary = new CloudinaryContext(this, options);
|
|
345
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const DEFAULT_POSTER_PARAMS = { format: 'jpg', resource_type: 'video', transformation: { flags: 'waveform' } };
|
|
2
|
+
|
|
3
|
+
export const COMMON_AUDIO_FORMATS = ['mp3', 'ogg', 'wav', 'mp4'];
|
|
4
|
+
|
|
5
|
+
export const AUDIO_SUFFIX_REMOVAL_PATTERN = RegExp(`\\.(${COMMON_AUDIO_FORMATS.join('|')})$$`);
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_AUDIO_PARAMS = {
|
|
8
|
+
resource_type: 'video',
|
|
9
|
+
type: 'upload',
|
|
10
|
+
transformation: []
|
|
11
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import VideoSource from '../video-source/video-source';
|
|
2
|
+
import ImageSource from '../image-source';
|
|
3
|
+
import { normalizeOptions } from '../../common';
|
|
4
|
+
import { sliceAndUnsetProperties } from 'utils/slicing';
|
|
5
|
+
import { assign } from 'utils/assign';
|
|
6
|
+
import { objectToQuerystring } from 'utils/querystring';
|
|
7
|
+
import { AUDIO_SUFFIX_REMOVAL_PATTERN, DEFAULT_AUDIO_PARAMS, DEFAULT_POSTER_PARAMS } from './audio-source.const';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AudioSource extends VideoSource {
|
|
11
|
+
|
|
12
|
+
constructor(publicId, options = {}) {
|
|
13
|
+
({ publicId, options } = normalizeOptions(publicId, options));
|
|
14
|
+
|
|
15
|
+
publicId = publicId.replace(AUDIO_SUFFIX_REMOVAL_PATTERN, '');
|
|
16
|
+
|
|
17
|
+
options = assign({}, DEFAULT_AUDIO_PARAMS, options);
|
|
18
|
+
const { poster } = sliceAndUnsetProperties(options, 'poster');
|
|
19
|
+
|
|
20
|
+
super(publicId, options);
|
|
21
|
+
this._poster = null;
|
|
22
|
+
this._type = 'AudioSource';
|
|
23
|
+
this.poster(poster);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getPoster () {
|
|
27
|
+
return this._poster;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
poster(publicId, options = {}) {
|
|
31
|
+
if (!publicId) {
|
|
32
|
+
return this._poster;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (publicId instanceof ImageSource) {
|
|
36
|
+
this._poster = publicId;
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
({ publicId, options } = normalizeOptions(publicId, options, { tolerateMissingId: true }));
|
|
41
|
+
|
|
42
|
+
if (!publicId) {
|
|
43
|
+
publicId = this.publicId();
|
|
44
|
+
options = assign({}, options, DEFAULT_POSTER_PARAMS);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
options.cloudinaryConfig = options.cloudinaryConfig || this.cloudinaryConfig();
|
|
48
|
+
this._poster = new ImageSource(publicId, options);
|
|
49
|
+
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
generateSources() {
|
|
54
|
+
return this.sourceTypes().map((sourceType) => {
|
|
55
|
+
|
|
56
|
+
if (sourceType === 'audio') {
|
|
57
|
+
const format = 'mp3';
|
|
58
|
+
const opts = {};
|
|
59
|
+
const srcTransformation = this.sourceTransformation()[sourceType] || [this.transformation()];
|
|
60
|
+
|
|
61
|
+
if (srcTransformation) {
|
|
62
|
+
opts.transformation = srcTransformation;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
assign(opts, { resource_type: 'video', format });
|
|
66
|
+
let queryString = '';
|
|
67
|
+
|
|
68
|
+
if (this.queryParams()) {
|
|
69
|
+
queryString = objectToQuerystring(this.queryParams());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const src = `${this.config().url(this.publicId(), opts)}${queryString}`;
|
|
73
|
+
const type = 'video/mp4';
|
|
74
|
+
return { type, src, cldSrc: this, poster: this.getPoster().url() };
|
|
75
|
+
} else {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}, this);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default AudioSource;
|