@plone/volto
Version:
Volto
189 lines (179 loc) • 5.96 kB
JSX
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { Embed, Message } from 'semantic-ui-react';
import cx from 'classnames';
import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers/Url/Url';
import VideoEmbed from '@plone/volto/components/theme/VideoEmbed/VideoEmbed';
import config from '@plone/volto/registry';
//Extracting videoID, listID and thumbnailURL from the video URL
const getVideoIDAndPlaceholder = (url, peertubeInstances) => {
let hasMatch = false;
let videoID = null;
let listID = null;
let thumbnailURL = null;
let videoSource = null;
let videoUrl = null;
if (url) {
if (
/^(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)(?:.*)$/i.test(url)
) {
hasMatch = true;
videoSource = 'youtube';
if (url.match('list')) {
const matches = url.match(/^.*\?list=(.*)|^.*&list=(.*)$/);
listID = matches[1] || matches[2];
videoUrl = `https://www.youtube.com/embed/videoseries?list=${listID}`;
videoSource = null;
let thumbnailID = null;
if (url.match(/\?v=(.*)&list/)) {
thumbnailID = url.match(/^.*\?v=(.*)&list(.*)/)[1];
}
if (url.match(/\?v=(.*)\?list/)) {
thumbnailID = url.match(/^.*\?v=(.*)\?list(.*)/)[1];
}
thumbnailURL =
'https://img.youtube.com/vi/' + thumbnailID + '/sddefault.jpg';
} else if (url.match('live')) {
videoID = url.match(/^.*\/live\/(.*)/)[1];
} else if (url.match(/\.be\//)) {
videoID = url.match(/^.*\.be\/(.*)/)[1];
} else if (url.match(/\?v=/)) {
videoID = url.match(/^.*\?v=(.*)$/)[1];
} else if (url.match('shorts')) {
videoID = url.match(/^.*\/shorts\/(.*)/)[1];
}
if (videoID) {
let thumbnailID = videoID;
if (videoID.match(/\?si=/)) {
thumbnailID = videoID.match(/(.*)\?si=(.*)/)[1];
}
//load video preview image from youtube
thumbnailURL =
'https://img.youtube.com/vi/' + thumbnailID + '/sddefault.jpg';
}
} else if (url.match('vimeo')) {
hasMatch = true;
videoSource = 'vimeo';
videoID = url.match(/^.*\.com\/(.*)/)[1];
if (videoID) {
let thumbnailID = videoID;
if (videoID.match(/\?si=/)) {
thumbnailID = videoID.match(/(.*)\?si=(.*)/)[1];
}
thumbnailURL = 'https://vumbnail.com/' + thumbnailID + '.jpg';
}
} else if (
url &&
Array.isArray(peertubeInstances) &&
url.match(new RegExp(peertubeInstances.join('|'), 'gi'))
) {
const peertubeRegex = /^(https?:\/\/[^/]+)\/w\/([A-Za-z0-9_-]+)/i;
const match = url.match(peertubeRegex);
if (match) {
hasMatch = true;
videoSource = 'peertube';
const instance = match[1];
videoID = match[2];
videoUrl = `${instance}/videos/embed/${videoID}`;
}
}
}
return { videoID, videoUrl, thumbnailURL, videoSource, hasMatch };
};
const Body = ({ data, isEditMode }) => {
let placeholder = data.preview_image
? isInternalURL(data.preview_image)
? `${flattenToAppURL(data.preview_image)}/@@images/image`
: data.preview_image
: null;
const peertubeInstances =
config.blocks.blocksConfig.video.allowedPeertubeInstances;
const { videoID, videoUrl, thumbnailURL, videoSource, hasMatch, listID } =
getVideoIDAndPlaceholder(data.url, peertubeInstances);
placeholder = !placeholder ? thumbnailURL : placeholder;
const ref = React.createRef();
const onKeyDown = (e) => {
if (e.nativeEvent.keyCode === 13) {
ref.current.handleClick();
}
};
const embedSettings = {
placeholder: placeholder,
defaultActive: false,
autoplay: data.autoplay || false,
aspectRatio: '16:9',
tabIndex: 0,
onKeyPress: onKeyDown,
ref: ref,
title: data.title,
id: videoID,
source: videoSource,
url: videoUrl,
};
return (
<>
{data.url && (
<div
className={cx('video-inner', {
'full-width': data.align === 'full',
})}
>
{hasMatch ? (
<>
{data.url.match('list') ? (
<Embed
url={`https://www.youtube.com/embed/videoseries?list=${listID}`}
{...embedSettings}
/>
) : (
<VideoEmbed id={videoID} {...embedSettings} />
)}
</>
) : (
<>
{data.url.match('.mp4') ? (
// eslint-disable-next-line jsx-a11y/media-has-caption
<video
src={
isInternalURL(data.url)
? data.url.includes('@@download')
? data.url
: `${flattenToAppURL(data.url)}/@@download/file`
: data.url
}
controls
poster={placeholder}
type="video/mp4"
/>
) : isEditMode ? (
<div>
<Message>
<center>
<FormattedMessage
id="Please enter a valid URL by deleting the block and adding a new video block."
defaultMessage="Please enter a valid URL by deleting the block and adding a new video block."
/>
</center>
</Message>
</div>
) : (
<div className="invalidVideoFormat" />
)}
</>
)}
</div>
)}
</>
);
};
/**
* Property types.
* @property {Object} propTypes Property types.
* @static
*/
Body.propTypes = {
data: PropTypes.objectOf(PropTypes.any).isRequired,
};
export default Body;
export { getVideoIDAndPlaceholder };