Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions lib-es5/cloudinary.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ exports.video = function video(public_id, options) {
public_id = public_id.replace(/\.(mp4|ogv|webm)$/, '');
var source_types = optionConsume(options, 'source_types', []);
var source_transformation = optionConsume(options, 'source_transformation', {});
var sources = optionConsume(options, 'sources', []);
var fallback = optionConsume(options, 'fallback_content', '');

if (source_types.length === 0) source_types = cloudinary.utils.DEFAULT_VIDEO_SOURCE_TYPES;
Expand All @@ -150,31 +151,35 @@ exports.video = function video(public_id, options) {
var html = '<video ';

if (!video_options.hasOwnProperty('resource_type')) video_options.resource_type = 'video';
var multi_source = _.isArray(source_types) && source_types.length > 1;
var multi_source_types = _.isArray(source_types) && source_types.length > 1;
var has_sources = _.isArray(sources) && sources.length > 0;
var source = public_id;
if (!multi_source) {
if (!multi_source_types && !has_sources) {
source = source + '.' + cloudinary.utils.build_array(source_types)[0];
}
var src = cloudinary.utils.url(source, video_options); // calculate src and reduce video_options
if (!multi_source) {
video_options.src = src;
}
var src = cloudinary.utils.url(source, video_options);
if (!multi_source_types && !has_sources) video_options.src = src;
if (video_options.hasOwnProperty("html_width")) video_options.width = optionConsume(video_options, 'html_width');
if (video_options.hasOwnProperty("html_height")) video_options.height = optionConsume(video_options, 'html_height');
html = html + cloudinary.utils.html_attrs(video_options) + '>';
if (multi_source) {
html += source_types.map(function (source_type) {
var transformation = source_transformation[source_type] || {};
var sourceSrc = cloudinary.utils.url(source + "." + source_type, _.extend({ resource_type: 'video' }, _.cloneDeep(options), _.cloneDeep(transformation)));
var video_type = source_type === 'ogv' ? 'ogg' : source_type;
var type = "video/" + video_type;
return `<source ${cloudinary.utils.html_attrs({ src: sourceSrc, type })}>`;
if (multi_source_types && !has_sources) {
sources = source_types.map(function (source_type) {
return {
type: source_type,
transformations: source_transformation[source_type] || {}
};
});
}
if (_.isArray(sources) && sources.length > 0) {
html += sources.map(function (source_data) {
var source_type = source_data.type;
var codecs = source_data.codecs;
var transformation = source_data.transformations || {};
src = cloudinary.utils.url(source + "." + source_type, _.extend({ resource_type: 'video' }, _.cloneDeep(options), _.cloneDeep(transformation)));
return cloudinary.utils.create_source_tag(src, source_type, codecs);
}).join('');
}

html = html + fallback;
html = html + '</video>';
return html;
return `${html}${fallback}</video>`;
};

/**
Expand Down
12 changes: 12 additions & 0 deletions lib-es5/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,18 @@ function archive_params() {
};
}

exports.create_source_tag = function create_source_tag(src, source_type) {
var codecs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

var video_type = source_type === 'ogv' ? 'ogg' : source_type;
var mime_type = `video/${video_type}`;
if (!isEmpty(codecs)) {
var codecs_str = isArray(codecs) ? codecs.join(', ') : codecs;
mime_type += `; codecs=${codecs_str}`;
}
return `<source ${utils.html_attrs({ src, type: mime_type })}>`;
};

function build_explicit_api_params(public_id) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

Expand Down
51 changes: 22 additions & 29 deletions lib/cloudinary.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ exports.video = function video(public_id, options) {
public_id = public_id.replace(/\.(mp4|ogv|webm)$/, '');
let source_types = optionConsume(options, 'source_types', []);
let source_transformation = optionConsume(options, 'source_transformation', {});
let sources = optionConsume(options, 'sources', []);
let fallback = optionConsume(options, 'fallback_content', '');

if (source_types.length === 0) source_types = cloudinary.utils.DEFAULT_VIDEO_SOURCE_TYPES;
Expand All @@ -128,53 +129,45 @@ exports.video = function video(public_id, options) {
if (video_options.poster.hasOwnProperty('public_id')) {
video_options.poster = cloudinary.utils.url(video_options.poster.public_id, video_options.poster);
} else {
video_options.poster = cloudinary.utils.url(
public_id,
_.extend(
{},
cloudinary.utils.DEFAULT_POSTER_OPTIONS,
video_options.poster,
),
);
video_options.poster = cloudinary.utils.url(public_id, _.extend({}, cloudinary.utils.DEFAULT_POSTER_OPTIONS, video_options.poster));
}
}
} else {
video_options.poster = cloudinary.utils.url(
public_id,
_.extend({}, cloudinary.utils.DEFAULT_POSTER_OPTIONS, options),
);
video_options.poster = cloudinary.utils.url(public_id, _.extend({}, cloudinary.utils.DEFAULT_POSTER_OPTIONS, options));
}

if (!video_options.poster) delete video_options.poster;

let html = '<video ';

if (!video_options.hasOwnProperty('resource_type')) video_options.resource_type = 'video';
let multi_source = _.isArray(source_types) && source_types.length > 1;
let multi_source_types = _.isArray(source_types) && source_types.length > 1;
let has_sources = _.isArray(sources) && sources.length > 0;
let source = public_id;
if (!multi_source) {
if (!multi_source_types && !has_sources) {
source = source + '.' + cloudinary.utils.build_array(source_types)[0];
}
let src = cloudinary.utils.url(source, video_options); // calculate src and reduce video_options
if (!multi_source) {
video_options.src = src;
}
let src = cloudinary.utils.url(source, video_options);
if (!multi_source_types && !has_sources) video_options.src = src;
if (video_options.hasOwnProperty("html_width")) video_options.width = optionConsume(video_options, 'html_width');
if (video_options.hasOwnProperty("html_height")) video_options.height = optionConsume(video_options, 'html_height');
html = html + cloudinary.utils.html_attrs(video_options) + '>';
if (multi_source) {
html += source_types.map((source_type) => {
let transformation = source_transformation[source_type] || {};
let sourceSrc = cloudinary.utils.url(source + "." + source_type, _.extend({ resource_type: 'video' }, _.cloneDeep(options), _.cloneDeep(transformation)));
let video_type = source_type === 'ogv' ? 'ogg' : source_type;
let type = "video/" + video_type;
return `<source ${cloudinary.utils.html_attrs({ src: sourceSrc, type })}>`;
if (multi_source_types && !has_sources) {
sources = source_types.map(source_type => ({
type: source_type,
transformations: source_transformation[source_type] || {},
}));
}
if (_.isArray(sources) && sources.length > 0) {
html += sources.map((source_data) => {
let source_type = source_data.type;
let codecs = source_data.codecs;
let transformation = source_data.transformations || {};
src = cloudinary.utils.url(source + "." + source_type, _.extend({ resource_type: 'video' }, _.cloneDeep(options), _.cloneDeep(transformation)));
return cloudinary.utils.create_source_tag(src, source_type, codecs);
}).join('');
}

html = html + fallback;
html = html + '</video>';
return html;
return `${html}${fallback}</video>`;
};


Expand Down
10 changes: 10 additions & 0 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,16 @@ function archive_params(options = {}) {
};
}

exports.create_source_tag = function create_source_tag(src, source_type, codecs = null) {
let video_type = source_type === 'ogv' ? 'ogg' : source_type;
let mime_type = `video/${video_type}`;
if (!isEmpty(codecs)) {
let codecs_str = isArray(codecs) ? codecs.join(', ') : codecs;
mime_type += `; codecs=${codecs_str}`;
}
return `<source ${utils.html_attrs({ src, type: mime_type })}>`;
};

function build_explicit_api_params(public_id, options = {}) {
return [exports.build_upload_params(extend({}, { public_id }, options))];
}
Expand Down
23 changes: 22 additions & 1 deletion test/spechelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,28 @@ exports.ICON_FILE = "test/resources/favicon.ico";
exports.VIDEO_URL = "http://res.cloudinary.com/demo/video/upload/dog.mp4";
exports.IMAGE_URL = "http://res.cloudinary.com/demo/image/upload/sample";

exports.test_cloudinary_url = function (public_id, options, expected_url, expected_options) {
exports.SAMPLE_VIDEO_SOURCES = [
{
type: 'mp4',
codecs: 'hev1',
transformations: { video_codec: 'h265' },
},
{
type: 'webm',
codecs: 'vp9',
transformations: { video_codec: 'vp9' },
},
{
type: 'mp4',
transformations: { video_codec: 'auto' },
},
{
type: 'webm',
transformations: { video_codec: 'auto' },
},
];

exports.test_cloudinary_url = function(public_id, options, expected_url, expected_options) {
var url;
url = utils.url(public_id, options);
expect(url).to.eql(expected_url);
Expand Down
147 changes: 146 additions & 1 deletion test/video_spec.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
var cloudinary, expect;
var cloudinary, expect, helper;

expect = require('expect.js');

cloudinary = require('../cloudinary');

helper = require("./spechelper");

describe("video tag helper", function () {
var DEFAULT_UPLOAD_PATH, VIDEO_UPLOAD_PATH;
VIDEO_UPLOAD_PATH = "http://res.cloudinary.com/test123/video/upload/";
Expand Down Expand Up @@ -154,4 +156,147 @@ describe("video tag helper", function () {
expect(options.video_codec).to.eql('auto');
expect(options.autoplay).to.be(true);
});

describe('sources', function() {
const expected_url = VIDEO_UPLOAD_PATH + 'movie';
const expected_url_mp4 = VIDEO_UPLOAD_PATH + 'vc_auto/movie.mp4';
const expected_url_webm = VIDEO_UPLOAD_PATH + 'vc_auto/movie.webm';
it('should generate video tag with default sources if not given sources or source_types', function() {
expect(cloudinary.video('movie')).to.eql(
`<video poster='${expected_url}.jpg'>` +
`<source src='${expected_url}.webm' type='video/webm'>` +
`<source src='${expected_url}.mp4' type='video/mp4'>` +
`<source src='${expected_url}.ogv' type='video/ogg'>` +
'</video>'
);
});
it('should generate video tag with given custom sources', function() {
var custom_sources = [
{
type: 'mp4',
},
{
type: 'webm',
},
];
expect(
cloudinary.video('movie', {
sources: custom_sources,
})
).to.eql(
`<video poster='${expected_url}.jpg'>` +
`<source src='${VIDEO_UPLOAD_PATH +
'movie.mp4'}' type='video/mp4'>` +
`<source src='${VIDEO_UPLOAD_PATH +
'movie.webm'}' type='video/webm'>` +
`</video>`
);
});
it('should generate video tag overriding source_types with sources if both are given', function() {
var custom_sources = [
{
type: 'mp4',
},
];
expect(
cloudinary.video('movie', {
sources: custom_sources,
source_types: ['ogv', 'mp4', 'webm'],
})
).to.eql(
`<video poster='${expected_url}.jpg'>` +
`<source src='${VIDEO_UPLOAD_PATH + 'movie.mp4'}' type='video/mp4'>` +
`</video>`
);
});
it('should correctly handle ogg/ogv', function() {
expect(
cloudinary.video('movie', {
sources: [{ type: 'ogv' }],
})
).to.eql(
`<video poster='${expected_url}.jpg'>` +
`<source src='${VIDEO_UPLOAD_PATH +
'movie.ogv'}' type='video/ogg'>` +
`</video>`
);
});
it('should generate video tag with sources with codecs string', function() {
var custom_sources = [
{
type: 'mp4',
codecs: 'vp8, vorbis',
transformations: { video_codec: 'auto' },
},
{
type: 'webm',
codecs: 'avc1.4D401E, mp4a.40.2',
transformations: { video_codec: 'auto' },
},
];
expect(
cloudinary.video('movie', {
sources: custom_sources,
})
).to.eql(
`<video poster='${expected_url}.jpg'>` +
`<source src='${expected_url_mp4}' type='video/mp4; codecs=vp8, vorbis'>` +
`<source src='${expected_url_webm}' type='video/webm; codecs=avc1.4D401E, mp4a.40.2'>` +
`</video>`
);
});
it('should generate video tag with sources with codecs arrays', function() {
var custom_sources = [
{
type: 'mp4',
codecs: ['vp8', 'vorbis'],
transformations: { video_codec: 'auto' },
},
{
type: 'webm',
codecs: ['avc1.4D401E', 'mp4a.40.2'],
transformations: { video_codec: 'auto' },
},
];
expect(
cloudinary.video('movie', {
sources: custom_sources,
})
).to.eql(
`<video poster='${expected_url}.jpg'>` +
`<source src='${expected_url_mp4}' type='video/mp4; codecs=vp8, vorbis'>` +
`<source src='${expected_url_webm}' type='video/webm; codecs=avc1.4D401E, mp4a.40.2'>` +
`</video>`
);
});
it('should generate video tag with sources and transformations', function() {
const options = {
source_types: 'mp4',
html_height: '100',
html_width: '200',
video_codec: { codec: 'h264' },
audio_codec: 'acc',
start_offset: 3,
sources: helper.SAMPLE_VIDEO_SOURCES,
};
const expected_poster_url =
VIDEO_UPLOAD_PATH + 'ac_acc,so_3,vc_h264/movie.jpg';
const expected_url_mp4_codecs =
VIDEO_UPLOAD_PATH + 'ac_acc,so_3,vc_h265/movie.mp4';
const expected_url_webm_codecs =
VIDEO_UPLOAD_PATH + 'ac_acc,so_3,vc_vp9/movie.webm';
const expected_url_mp4_audio =
VIDEO_UPLOAD_PATH + 'ac_acc,so_3,vc_auto/movie.mp4';
const expected_url_webm_audio =
VIDEO_UPLOAD_PATH + 'ac_acc,so_3,vc_auto/movie.webm';
expect(cloudinary.video('movie', options)).to.eql(
`<video height='100' poster='${expected_poster_url}' width='200'>` +
`<source src='${expected_url_mp4_codecs}' type='video/mp4; codecs=hev1'>` +
`<source src='${expected_url_webm_codecs}' type='video/webm; codecs=vp9'>` +
`<source src='${expected_url_mp4_audio}' type='video/mp4'>` +
`<source src='${expected_url_webm_audio}' type='video/webm'>` +
`</video>`
);
});
});
});