mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-12-11 14:32:30 -05:00
Frontent dev env (#247)
* Added frontend development files/environment * More items-categories related removals * Improvements in pages templates (inc. static pages) * Improvements in video player * Added empty home page message + cta * Updates in media, playlist and management pages * Improvements in material icons font loading * Replaced media & playlists links in frontend dev-env * frontend package version update * chnaged frontend dev url port * static files update * Changed default position of theme switcher * enabled frontend docker container
This commit is contained in:
15
frontend/src/static/js/components/media-viewer/AttachmentViewer.js
Executable file
15
frontend/src/static/js/components/media-viewer/AttachmentViewer.js
Executable file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function AttachmentPlayer() {
|
||||
return (
|
||||
<div className="player-container viewer-attachment-container">
|
||||
<div className="player-container-inner">
|
||||
<span>
|
||||
<span>
|
||||
<i className="material-icons">insert_drive_file</i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
83
frontend/src/static/js/components/media-viewer/AudioViewer/functions.js
Executable file
83
frontend/src/static/js/components/media-viewer/AudioViewer/functions.js
Executable file
@@ -0,0 +1,83 @@
|
||||
export function extractAudioFileFormat(filename) {
|
||||
let ret = null;
|
||||
let ext = filename.split('.');
|
||||
if (ext.length) {
|
||||
ext = ext[ext.length - 1];
|
||||
switch (ext) {
|
||||
case 'webm':
|
||||
ret = 'audio/webm';
|
||||
break;
|
||||
case 'flac':
|
||||
ret = 'audio/flac';
|
||||
break;
|
||||
case 'wave':
|
||||
ret = 'audio/wave';
|
||||
break;
|
||||
case 'wav':
|
||||
ret = 'audio/wav';
|
||||
break;
|
||||
case 'ogg':
|
||||
ret = 'audio/ogg';
|
||||
break;
|
||||
case 'ogg':
|
||||
ret = 'audio/ogg';
|
||||
break;
|
||||
case 'mp3':
|
||||
case 'mpeg':
|
||||
ret = 'audio/mpeg';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// NOTE: Valid but not get used.
|
||||
/*export function orderedSupportedAudioFormats( includeAll ){
|
||||
|
||||
let order = [];
|
||||
let supports = {};
|
||||
let aud = document.createElement('audio');
|
||||
|
||||
if(!!aud.canPlayType){
|
||||
|
||||
if( 'probably' === aud.canPlayType('audio/webm') || 'maybe' === aud.canPlayType('audio/webm') ){
|
||||
supports.webm = !0;
|
||||
order.push( 'webm' );
|
||||
}
|
||||
|
||||
if( 'probably' === aud.canPlayType('audio/flac') || 'maybe' === aud.canPlayType('audio/flac') ){
|
||||
supports.flac = !0;
|
||||
order.push( 'flac' );
|
||||
}
|
||||
|
||||
if( 'probably' === aud.canPlayType('audio/wave') || 'maybe' === aud.canPlayType('audio/wave') ){
|
||||
supports.wave = !0;
|
||||
order.push( 'wave' );
|
||||
}
|
||||
|
||||
if( 'probably' === aud.canPlayType('audio/wav') || 'maybe' === aud.canPlayType('audio/wav') ){
|
||||
supports.wav = !0;
|
||||
order.push( 'wav' );
|
||||
}
|
||||
|
||||
if( 'probably' === aud.canPlayType('audio/ogg') || 'maybe' === aud.canPlayType('audio/ogg') ){
|
||||
supports.ogg = !0;
|
||||
order.push( 'ogg' );
|
||||
}
|
||||
|
||||
if( 'probably' === aud.canPlayType('audio/ogg; codecs="opus"') || 'maybe' === aud.canPlayType('audio/ogg; codecs="opus"') ){
|
||||
supports.oggOpus = !0;
|
||||
order.push( 'oggOpus' );
|
||||
}
|
||||
|
||||
if( 'probably' === aud.canPlayType('audio/mpeg') || 'maybe' === aud.canPlayType('audio/mpeg') ){
|
||||
supports.mp3 = !0;
|
||||
order.push( 'mp3' );
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
order: order,
|
||||
support: supports
|
||||
};
|
||||
}*/
|
||||
364
frontend/src/static/js/components/media-viewer/AudioViewer/index.js
Executable file
364
frontend/src/static/js/components/media-viewer/AudioViewer/index.js
Executable file
@@ -0,0 +1,364 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import MediaPlayer from 'mediacms-player/dist/mediacms-player.js';
|
||||
import 'mediacms-player/dist/mediacms-player.css';
|
||||
|
||||
import { SiteContext } from '../../../utils/contexts/';
|
||||
import { formatInnerLink } from '../../../utils/helpers/';
|
||||
import { PageStore, MediaPageStore, VideoViewerStore as AudioPlayerStore } from '../../../utils/stores/';
|
||||
import { VideoViewerActions as AudioPlayerActions } from '../../../utils/actions/';
|
||||
import { UpNextLoaderView, MediaDurationInfo, PlayerRecommendedMedia } from '../../../utils/classes/';
|
||||
import { extractAudioFileFormat } from './functions';
|
||||
|
||||
import '../VideoViewer.scss';
|
||||
|
||||
export default class AudioViewer extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
let mediaData = MediaPageStore.get('media-data');
|
||||
|
||||
this.AudioPlayerData = {};
|
||||
|
||||
this.audioStartedPlaying = false;
|
||||
|
||||
let audioURL = formatInnerLink(mediaData.original_media_url, SiteContext._currentValue.url);
|
||||
|
||||
this.videoSources = [{ src: audioURL, type: extractAudioFileFormat(audioURL) }];
|
||||
|
||||
this.videoPoster = mediaData.poster_url;
|
||||
this.videoPoster = this.videoPoster ? this.videoPoster : mediaData.thumbnail_url;
|
||||
this.videoPoster = this.videoPoster ? formatInnerLink(this.videoPoster, SiteContext._currentValue.url) : '';
|
||||
|
||||
this.updatePlayerVolume = this.updatePlayerVolume.bind(this);
|
||||
this.onAudioEnd = this.onAudioEnd.bind(this);
|
||||
this.onAudioRestart = this.onAudioRestart.bind(this);
|
||||
|
||||
PageStore.on('switched_media_auto_play', this.onUpdateMediaAutoPlay.bind(this));
|
||||
|
||||
this.wrapperClick = this.wrapperClick.bind(this);
|
||||
|
||||
const _MediaDurationInfo = new MediaDurationInfo();
|
||||
|
||||
_MediaDurationInfo.update(MediaPageStore.get('media-data').duration);
|
||||
|
||||
this.durationISO8601 = _MediaDurationInfo.ISO8601();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.videoSources.length) {
|
||||
console.warn('Audio DEBUG:', "Audio file doesn't exist");
|
||||
}
|
||||
|
||||
this.recommendedMedia = MediaPageStore.get('media-data').related_media.length
|
||||
? new PlayerRecommendedMedia(
|
||||
MediaPageStore.get('media-data').related_media,
|
||||
this.refs.AudioElem.parentNode,
|
||||
this.props.inEmbed
|
||||
)
|
||||
: null;
|
||||
|
||||
this.upNextLoaderView =
|
||||
!this.props.inEmbed && MediaPageStore.get('media-data').related_media.length
|
||||
? new UpNextLoaderView(MediaPageStore.get('media-data').related_media[0])
|
||||
: null;
|
||||
|
||||
if (document.hasFocus() || 'visible' === document.visibilityState) {
|
||||
this.initPlayerInstance();
|
||||
} else {
|
||||
this.initPlayerInstance = this.initPlayerInstance.bind(this);
|
||||
window.addEventListener('focus', this.initPlayerInstance);
|
||||
document.addEventListener('visibilitychange', this.initPlayerInstance);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.recommendedMedia) {
|
||||
this.AudioPlayerData.instance.player.off('fullscreenchange', this.recommendedMedia.onResize);
|
||||
PageStore.removeListener('window_resize', this.recommendedMedia.onResize);
|
||||
AudioPlayerStore.removeListener('changed_viewer_mode', this.recommendedMedia.onResize);
|
||||
this.recommendedMedia.destroy();
|
||||
}
|
||||
videojs(this.refs.AudioElem).dispose();
|
||||
this.AudioPlayerData.instance = null;
|
||||
delete this.AudioPlayerData.instance;
|
||||
}
|
||||
|
||||
initPlayerInstance() {
|
||||
window.removeEventListener('focus', this.initPlayerInstance);
|
||||
document.removeEventListener('visibilitychange', this.initPlayerInstance);
|
||||
|
||||
this.refs.AudioElem.focus(); // Focus on player before instance init.
|
||||
|
||||
this.initPlayerInstance = null;
|
||||
|
||||
setTimeout(
|
||||
function () {
|
||||
if (!this.AudioPlayerData.instance) {
|
||||
let titleLink = this.props.inEmbed ? document.createElement('a') : null;
|
||||
let userThumbLink = this.props.inEmbed ? document.createElement('a') : null;
|
||||
|
||||
if (titleLink) {
|
||||
titleLink.setAttribute('class', 'title-link');
|
||||
titleLink.setAttribute('href', MediaPageStore.get('media-data').url);
|
||||
titleLink.setAttribute('title', MediaPageStore.get('media-data').title);
|
||||
titleLink.setAttribute('target', '_blank');
|
||||
titleLink.innerHTML = MediaPageStore.get('media-data').title;
|
||||
}
|
||||
|
||||
if (userThumbLink) {
|
||||
userThumbLink.setAttribute('class', 'user-thumb-link');
|
||||
userThumbLink.setAttribute('href', MediaPageStore.get('media-data').author_profile);
|
||||
userThumbLink.setAttribute('title', MediaPageStore.get('media-data').author_name);
|
||||
userThumbLink.setAttribute('target', '_blank');
|
||||
userThumbLink.innerHTML = '<img src="' + MediaPageStore.get('media-author-thumbnail-url') + '" alt="" />';
|
||||
}
|
||||
|
||||
let nextLink = null;
|
||||
let previousLink = null;
|
||||
|
||||
const playlistId = this.props.inEmbed ? null : MediaPageStore.get('playlist-id');
|
||||
|
||||
if (playlistId) {
|
||||
nextLink = MediaPageStore.get('playlist-next-media-url');
|
||||
previousLink = MediaPageStore.get('playlist-previous-media-url');
|
||||
} else {
|
||||
nextLink =
|
||||
MediaPageStore.get('media-data').related_media.length && !this.props.inEmbed
|
||||
? MediaPageStore.get('media-data').related_media[0].url
|
||||
: null;
|
||||
}
|
||||
|
||||
this.AudioPlayerData.instance = new MediaPlayer(
|
||||
this.refs.AudioElem,
|
||||
{
|
||||
sources: this.videoSources,
|
||||
poster: this.videoPoster,
|
||||
autoplay: !this.props.inEmbed,
|
||||
bigPlayButton: true,
|
||||
controlBar: {
|
||||
fullscreen: false,
|
||||
theaterMode: false,
|
||||
next: !!nextLink,
|
||||
previous: !!previousLink,
|
||||
},
|
||||
cornerLayers: {
|
||||
topLeft: titleLink,
|
||||
topRight: this.upNextLoaderView ? this.upNextLoaderView.html() : null,
|
||||
bottomLeft: this.recommendedMedia ? this.recommendedMedia.html() : null,
|
||||
bottomRight: userThumbLink,
|
||||
},
|
||||
},
|
||||
{
|
||||
volume: AudioPlayerStore.get('player-volume'),
|
||||
soundMuted: AudioPlayerStore.get('player-sound-muted'),
|
||||
},
|
||||
null,
|
||||
null,
|
||||
this.onAudioPlayerStateUpdate.bind(this),
|
||||
this.onClickNextButton.bind(this),
|
||||
this.onClickPreviousButton.bind(this)
|
||||
);
|
||||
|
||||
if (this.upNextLoaderView) {
|
||||
this.upNextLoaderView.setVideoJsPlayerElem(this.AudioPlayerData.instance.player.el_);
|
||||
this.onUpdateMediaAutoPlay();
|
||||
}
|
||||
|
||||
this.refs.AudioElem.parentNode.focus(); // Focus on player.
|
||||
|
||||
this.AudioPlayerData.instance.player.one(
|
||||
'play',
|
||||
function () {
|
||||
this.audioStartedPlaying = true;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
if (this.recommendedMedia) {
|
||||
this.recommendedMedia.initWrappers(this.AudioPlayerData.instance.player.el_);
|
||||
|
||||
this.AudioPlayerData.instance.player.one('pause', this.recommendedMedia.init);
|
||||
this.AudioPlayerData.instance.player.on('fullscreenchange', this.recommendedMedia.onResize);
|
||||
|
||||
PageStore.on('window_resize', this.recommendedMedia.onResize);
|
||||
AudioPlayerStore.on('changed_viewer_mode', this.recommendedMedia.onResize);
|
||||
}
|
||||
|
||||
this.AudioPlayerData.instance.player.one('ended', this.onAudioEnd);
|
||||
}
|
||||
}.bind(this),
|
||||
50
|
||||
);
|
||||
}
|
||||
|
||||
initialDocumentFocus() {
|
||||
if (this.refs.AudioElem.parentNode) {
|
||||
this.refs.AudioElem.parentNode.focus();
|
||||
setTimeout(
|
||||
function () {
|
||||
this.AudioPlayerData.instance.player.play();
|
||||
}.bind(this),
|
||||
50
|
||||
);
|
||||
}
|
||||
window.removeEventListener('focus', this.initialDocumentFocus);
|
||||
this.initialDocumentFocus = null;
|
||||
}
|
||||
|
||||
onClickNextButton() {
|
||||
const playlistId = MediaPageStore.get('playlist-id');
|
||||
|
||||
let nextLink;
|
||||
|
||||
if (playlistId) {
|
||||
nextLink = MediaPageStore.get('playlist-next-media-url');
|
||||
|
||||
if (null === nextLink) {
|
||||
nextLink = MediaPageStore.get('media-data').related_media[0].url;
|
||||
}
|
||||
} else if (!this.props.inEmbed) {
|
||||
nextLink = MediaPageStore.get('media-data').related_media[0].url;
|
||||
}
|
||||
|
||||
window.location.href = nextLink;
|
||||
}
|
||||
|
||||
onClickPreviousButton() {
|
||||
const playlistId = MediaPageStore.get('playlist-id');
|
||||
|
||||
let previousLink;
|
||||
|
||||
if (playlistId) {
|
||||
previousLink = MediaPageStore.get('playlist-previous-media-url');
|
||||
|
||||
if (null === previousLink) {
|
||||
previousLink = MediaPageStore.get('media-data').related_media[0].url;
|
||||
}
|
||||
} else if (!this.props.inEmbed) {
|
||||
previousLink = MediaPageStore.get('media-data').related_media[0].url;
|
||||
}
|
||||
|
||||
window.location.href = previousLink;
|
||||
}
|
||||
|
||||
onUpdateMediaAutoPlay() {
|
||||
if (this.upNextLoaderView) {
|
||||
if (PageStore.get('media-auto-play')) {
|
||||
this.upNextLoaderView.showTimerView(this.AudioPlayerData.instance.isEnded());
|
||||
} else {
|
||||
this.upNextLoaderView.hideTimerView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAudioPlayerStateUpdate(newState) {
|
||||
this.updatePlayerVolume(newState.volume, newState.soundMuted);
|
||||
}
|
||||
|
||||
onAudioRestart() {
|
||||
if (this.recommendedMedia) {
|
||||
this.recommendedMedia.updateDisplayType('inline');
|
||||
this.AudioPlayerData.instance.player.one('pause', this.recommendedMedia.init);
|
||||
this.AudioPlayerData.instance.player.one('ended', this.onAudioEnd);
|
||||
}
|
||||
}
|
||||
|
||||
onAudioEnd() {
|
||||
if (this.recommendedMedia) {
|
||||
this.recommendedMedia.updateDisplayType('full');
|
||||
this.AudioPlayerData.instance.player.one('playing', this.onAudioRestart);
|
||||
}
|
||||
|
||||
const playlistId = this.props.inEmbed ? null : MediaPageStore.get('playlist-id');
|
||||
|
||||
if (playlistId) {
|
||||
const moreMediaEl = document.querySelector('.video-player .more-media');
|
||||
const actionsAnimEl = document.querySelector('.video-player .vjs-actions-anim');
|
||||
this.upNextLoaderView.cancelTimer();
|
||||
|
||||
const nextMediaUrl = MediaPageStore.get('playlist-next-media-url');
|
||||
|
||||
if (nextMediaUrl) {
|
||||
if (moreMediaEl) {
|
||||
moreMediaEl.style.display = 'none';
|
||||
}
|
||||
|
||||
if (actionsAnimEl) {
|
||||
actionsAnimEl.style.display = 'none';
|
||||
}
|
||||
|
||||
window.location.href = nextMediaUrl;
|
||||
}
|
||||
|
||||
this.upNextLoaderView.hideTimerView();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.upNextLoaderView) {
|
||||
if (PageStore.get('media-auto-play')) {
|
||||
this.upNextLoaderView.startTimer();
|
||||
this.AudioPlayerData.instance.player.one(
|
||||
'play',
|
||||
function () {
|
||||
this.upNextLoaderView.cancelTimer();
|
||||
}.bind(this)
|
||||
);
|
||||
} else {
|
||||
this.upNextLoaderView.cancelTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateMediaAutoPlay() {
|
||||
if (this.upNextLoaderView) {
|
||||
if (PageStore.get('media-auto-play')) {
|
||||
this.upNextLoaderView.showTimerView(this.AudioPlayerData.instance.isEnded());
|
||||
} else {
|
||||
this.upNextLoaderView.hideTimerView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePlayerVolume(playerVolume, playerSoundMuted) {
|
||||
if (AudioPlayerStore.get('player-volume') !== playerVolume) {
|
||||
AudioPlayerActions.set_player_volume(playerVolume);
|
||||
}
|
||||
if (AudioPlayerStore.get('player-sound-muted') !== playerSoundMuted) {
|
||||
AudioPlayerActions.set_player_sound_muted(playerSoundMuted);
|
||||
}
|
||||
}
|
||||
|
||||
wrapperClick(ev) {
|
||||
if (ev.target.parentNode === this.refs.videoPlayerWrap) {
|
||||
if (!this.AudioPlayerData.instance.player.ended()) {
|
||||
if (!this.AudioPlayerData.instance.player.hasStarted_ || this.AudioPlayerData.instance.player.paused()) {
|
||||
this.AudioPlayerData.instance.player.play();
|
||||
} else {
|
||||
this.AudioPlayerData.instance.player.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="player-container audio-player-container">
|
||||
<div className="player-container-inner">
|
||||
<div className="video-player" ref="videoPlayerWrap" onClick={this.wrapperClick}>
|
||||
<audio tabIndex="1" ref="AudioElem" className="video-js vjs-mediacms native-dimensions"></audio>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AudioViewer.defaultProps = {
|
||||
inEmbed: false,
|
||||
};
|
||||
|
||||
AudioViewer.propTypes = {
|
||||
inEmbed: PropTypes.bool,
|
||||
};
|
||||
52
frontend/src/static/js/components/media-viewer/ImageViewer.js
Executable file
52
frontend/src/static/js/components/media-viewer/ImageViewer.js
Executable file
@@ -0,0 +1,52 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { SiteContext } from '../../utils/contexts/';
|
||||
import { MediaPageStore } from '../../utils/stores/';
|
||||
|
||||
export default function ImageViewer(props) {
|
||||
const site = useContext(SiteContext);
|
||||
|
||||
let initialImage = getImageUrl();
|
||||
|
||||
initialImage = initialImage ? initialImage : MediaPageStore.get('media-data').thumbnail_url;
|
||||
initialImage = initialImage ? initialImage : '';
|
||||
|
||||
const [image, setImage] = useState(initialImage);
|
||||
|
||||
function onImageLoad() {
|
||||
setImage(getImageUrl());
|
||||
}
|
||||
|
||||
function getImageUrl() {
|
||||
const media_data = MediaPageStore.get('media-data');
|
||||
|
||||
let imgUrl = 'string' === typeof media_data.poster_url ? media_data.poster_url.trim() : '';
|
||||
|
||||
if ('' === imgUrl) {
|
||||
imgUrl = 'string' === typeof media_data.thumbnail_url ? media_data.thumbnail_url.trim() : '';
|
||||
}
|
||||
|
||||
if ('' === imgUrl) {
|
||||
imgUrl =
|
||||
'string' === typeof MediaPageStore.get('media-original-url')
|
||||
? MediaPageStore.get('media-original-url').trim()
|
||||
: '';
|
||||
}
|
||||
|
||||
if ('' === imgUrl) {
|
||||
return '#';
|
||||
}
|
||||
|
||||
return site.url + '/' + imgUrl.replace(/^\//g, '');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
MediaPageStore.on('loaded_image_data', onImageLoad);
|
||||
return () => MediaPageStore.removeListener('loaded_image_data', onImageLoad);
|
||||
}, []);
|
||||
|
||||
return !image ? null : (
|
||||
<div className="viewer-image-container">
|
||||
<img src={image} alt={MediaPageStore.get('media-data').title || null} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
15
frontend/src/static/js/components/media-viewer/PdfViewer.js
Executable file
15
frontend/src/static/js/components/media-viewer/PdfViewer.js
Executable file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function PdfViewer() {
|
||||
return (
|
||||
<div className="player-container viewer-pdf-container">
|
||||
<div className="player-container-inner">
|
||||
<span>
|
||||
<span>
|
||||
<i className="material-icons">insert_drive_file</i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
4
frontend/src/static/js/components/media-viewer/VideoViewer.scss
Executable file
4
frontend/src/static/js/components/media-viewer/VideoViewer.scss
Executable file
@@ -0,0 +1,4 @@
|
||||
.player-container {
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
}
|
||||
245
frontend/src/static/js/components/media-viewer/VideoViewer/functions.js
Executable file
245
frontend/src/static/js/components/media-viewer/VideoViewer/functions.js
Executable file
@@ -0,0 +1,245 @@
|
||||
import { SiteContext } from '../../../utils/contexts/';
|
||||
import { formatInnerLink } from '../../../utils/helpers/';
|
||||
|
||||
const validVideoFormats = ['hls', 'h265', 'vp9', 'h264', 'vp8', 'mp4', 'theora']; // NOTE: Keep array items order.
|
||||
|
||||
function browserSupports_videoCodec(what, debugLog) {
|
||||
let ret = null,
|
||||
vid = document.createElement('video');
|
||||
|
||||
if (!!vid.canPlayType) {
|
||||
try {
|
||||
switch (what) {
|
||||
case 'hls':
|
||||
// ret = 'probably' === vid.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"');
|
||||
ret = true; // NOTE: Return always 'true' and allow player to decide...
|
||||
break;
|
||||
case 'h265':
|
||||
ret =
|
||||
'probably' === vid.canPlayType('video/mp4; codecs="hvc1.1.L0.0"') ||
|
||||
'probably' === vid.canPlayType('video/mp4; codecs="hev1.1.L0.0"');
|
||||
break;
|
||||
case 'h264':
|
||||
ret =
|
||||
'probably' === vid.canPlayType('video/mp4; codecs="avc1.42E01E"') ||
|
||||
'probably' === vid.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
|
||||
break;
|
||||
case 'vp9':
|
||||
ret = 'probably' === vid.canPlayType('video/webm; codecs="vp9"');
|
||||
break;
|
||||
case 'vp8':
|
||||
ret = 'probably' === vid.canPlayType('video/webm; codecs="vp8, vorbis"');
|
||||
break;
|
||||
case 'theora':
|
||||
ret = 'probably' === vid.canPlayType('video/ogg; codecs="theora"');
|
||||
break;
|
||||
case 'mp4':
|
||||
// ret = 'probably' === vid.canPlayType('video/mp4; codecs="mp4v.20.8"');
|
||||
ret = true; // NOTE: Return always 'true', as the default video format.
|
||||
break;
|
||||
}
|
||||
|
||||
// Log BUGGY states.
|
||||
|
||||
debugLog = debugLog instanceof Boolean || 0 === debugLog || 1 == debugLog ? debugLog : false;
|
||||
|
||||
if (debugLog) {
|
||||
if ('no' === vid.canPlayType('video/nonsense')) {
|
||||
console.warn(
|
||||
'BUGGY: Codec detection bug in Firefox 3.5.0 - 3.5.1 and Safari 4.0.0 - 4.0.4 that answer "no" to unknown codecs instead of an empty string'
|
||||
);
|
||||
}
|
||||
|
||||
if ('probably' === vid.canPlayType('video/webm')) {
|
||||
console.warn(
|
||||
'BUGGY: Codec detection bug that Firefox 27 and earlier always says "probably" when asked about WebM, even when the codecs string is not present'
|
||||
);
|
||||
}
|
||||
|
||||
if ('maybe' === vid.canPlayType('video/mp4; codecs="avc1.42E01E"')) {
|
||||
switch (vid.canPlayType('video/mp4')) {
|
||||
case 'probably':
|
||||
console.warn(
|
||||
'BUGGY: Codec detection bug in iOS 4.1 and earlier that switches "maybe" and "probably" around'
|
||||
);
|
||||
break;
|
||||
case 'maybe':
|
||||
console.warn('BUGGY: Codec detection bug in Android where no better answer than "maybe" is given');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
'probably' === vid.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') &&
|
||||
'probably' !== vid.canPlayType('video/mp4; codecs="avc1.42E01E"')
|
||||
) {
|
||||
console.warn(
|
||||
'BUGGY: Codec detection bug in Internet Explorer 9 that requires both audio and video codec on test'
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* LINK: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/video.js
|
||||
*/
|
||||
export function orderedSupportedVideoFormats(includeAll) {
|
||||
let order = [];
|
||||
let supports = {};
|
||||
let vid = document.createElement('video');
|
||||
|
||||
if (!!vid.canPlayType) {
|
||||
/*if( '' === vid.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"') ){ */
|
||||
supports.hls = !0; // NOTE: Return always 'true' and allow player to decide...
|
||||
order.push('hls');
|
||||
/*}*/
|
||||
|
||||
if (
|
||||
vid.canPlayType('video/mp4; codecs="hvc1.1.L0.0"') ||
|
||||
'probably' === vid.canPlayType('video/mp4; codecs="hev1.1.L0.0"')
|
||||
) {
|
||||
supports.h265 = !0;
|
||||
order.push('h265');
|
||||
}
|
||||
|
||||
if ('probably' === vid.canPlayType('video/mp4; codecs="avc1.42E01E"')) {
|
||||
supports.h264 = !0;
|
||||
order.push('h264');
|
||||
}
|
||||
|
||||
if ('probably' === vid.canPlayType('video/webm; codecs="vp9"')) {
|
||||
supports.vp9 = !0;
|
||||
order.push('vp9');
|
||||
}
|
||||
|
||||
if (includeAll) {
|
||||
if ('probably' === vid.canPlayType('video/webm; codecs="vp8, vorbis"')) {
|
||||
supports.vp8 = !0;
|
||||
order.push('vp8');
|
||||
}
|
||||
|
||||
if ('probably' === vid.canPlayType('video/ogg; codecs="theora"')) {
|
||||
supports.theora = !0;
|
||||
order.push('theora');
|
||||
}
|
||||
}
|
||||
|
||||
if ('probably' === vid.canPlayType('video/mp4; codecs="mp4v.20.8"')) {
|
||||
supports.mp4 = !0;
|
||||
order.push('mp4');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
order: order,
|
||||
support: supports,
|
||||
};
|
||||
}
|
||||
|
||||
export function videoAvailableCodecsAndResolutions(data, hlsData, supportedFormats) {
|
||||
const ret = {};
|
||||
let i, k, fileExt;
|
||||
|
||||
supportedFormats = void 0 === supportedFormats ? orderedSupportedVideoFormats() : supportedFormats;
|
||||
|
||||
const supportedFormatsExtensions = {
|
||||
hls: ['m3u8'],
|
||||
h265: ['mp4', 'webm'],
|
||||
h264: ['mp4', 'webm'],
|
||||
vp9: ['mp4', 'webm'],
|
||||
vp8: ['mp4', 'webm'],
|
||||
theora: ['ogg'],
|
||||
mp4: ['mp4'],
|
||||
};
|
||||
|
||||
for (i in hlsData) {
|
||||
if (hlsData.hasOwnProperty(i)) {
|
||||
k = null;
|
||||
|
||||
if ('master_file' === i) {
|
||||
k = 'Auto';
|
||||
} else {
|
||||
k = i.split('_playlist');
|
||||
k = 2 === k.length ? k[0] : null;
|
||||
}
|
||||
|
||||
if (null !== k) {
|
||||
ret[k] = void 0 === ret[k] ? { format: [], url: [] } : ret[k];
|
||||
ret[k].format.push('hls');
|
||||
ret[k].url.push(formatInnerLink(hlsData[i], SiteContext._currentValue.url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (k in data) {
|
||||
if (data.hasOwnProperty(k) && Object.keys(data[k]).length) {
|
||||
// TODO: With HLS doesn't matter the height of screen?
|
||||
if (1080 >= parseInt(k, 10) || (1080 < window.screen.width && 1080 < window.screen.height)) {
|
||||
i = 0;
|
||||
while (i < validVideoFormats.length) {
|
||||
if (void 0 !== data[k][validVideoFormats[i]]) {
|
||||
if (
|
||||
browserSupports_videoCodec(validVideoFormats[i], !1) &&
|
||||
data[k][validVideoFormats[i]] &&
|
||||
data[k][validVideoFormats[i]].url
|
||||
) {
|
||||
if (100 !== data[k][validVideoFormats[i]].progress) {
|
||||
console.warn('VIDEO DEBUG:', 'PROGRESS value is', data[k][validVideoFormats[i]].progress);
|
||||
}
|
||||
|
||||
if ('success' !== data[k][validVideoFormats[i]].status) {
|
||||
console.warn('VIDEO DEBUG:', 'STATUS value is', data[k][validVideoFormats[i]].status);
|
||||
}
|
||||
|
||||
fileExt = data[k][validVideoFormats[i]].url.split('.');
|
||||
|
||||
if (
|
||||
fileExt.length &&
|
||||
0 <= supportedFormatsExtensions[validVideoFormats[i]].indexOf(fileExt[fileExt.length - 1])
|
||||
) {
|
||||
ret[k] = void 0 === ret[k] ? { format: [], url: [] } : ret[k];
|
||||
ret[k].format.push(validVideoFormats[i]);
|
||||
ret[k].url.push(formatInnerLink(data[k][validVideoFormats[i]].url, SiteContext._currentValue.url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function extractDefaultVideoResolution(def, data) {
|
||||
let i,
|
||||
keys = Object.keys(data);
|
||||
|
||||
if (void 0 !== data[def]) {
|
||||
return def;
|
||||
}
|
||||
|
||||
if (parseInt(def, 10) >= parseInt(keys[keys.length - 1], 10)) {
|
||||
return keys[keys.length - 1];
|
||||
}
|
||||
|
||||
if (parseInt(def, 10) <= parseInt(keys[0], 10)) {
|
||||
return keys[0];
|
||||
}
|
||||
|
||||
i = keys.length - 1;
|
||||
while (i >= 0) {
|
||||
if (parseInt(def, 10) >= parseInt(keys[i], 10)) {
|
||||
return keys[i + 1];
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,619 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { VideoViewerActions } from '../../../utils/actions/';
|
||||
import { SiteContext, SiteConsumer } from '../../../utils/contexts/';
|
||||
import { PageStore, MediaPageStore, VideoViewerStore } from '../../../utils/stores/';
|
||||
import { addClassname, removeClassname, formatInnerLink } from '../../../utils/helpers/';
|
||||
import { BrowserCache, UpNextLoaderView, MediaDurationInfo, PlayerRecommendedMedia } from '../../../utils/classes/';
|
||||
import {
|
||||
orderedSupportedVideoFormats,
|
||||
videoAvailableCodecsAndResolutions,
|
||||
extractDefaultVideoResolution,
|
||||
} from './functions';
|
||||
import { VideoPlayer, VideoPlayerError } from '../../video-player/VideoPlayer';
|
||||
|
||||
import '../VideoViewer.scss';
|
||||
|
||||
function filterVideoEncoding(encoding_status) {
|
||||
switch (encoding_status) {
|
||||
case 'running':
|
||||
MediaPageStore.set('media-load-error-type', 'encodingRunning');
|
||||
MediaPageStore.set('media-load-error-message', 'Media encoding is currently running. Try again in few minutes.');
|
||||
break;
|
||||
case 'pending':
|
||||
MediaPageStore.set('media-load-error-type', 'encodingPending');
|
||||
MediaPageStore.set('media-load-error-message', 'Media encoding is pending');
|
||||
break;
|
||||
case 'fail':
|
||||
MediaPageStore.set('media-load-error-type', 'encodingFailed');
|
||||
MediaPageStore.set('media-load-error-message', 'Media encoding failed');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export default class VideoViewer extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
displayPlayer: false,
|
||||
};
|
||||
|
||||
this.videoSources = [];
|
||||
|
||||
filterVideoEncoding(this.props.data.encoding_status);
|
||||
|
||||
if (null !== MediaPageStore.get('media-load-error-type')) {
|
||||
this.state.displayPlayer = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if ('string' === typeof this.props.data.poster_url) {
|
||||
this.videoPoster = formatInnerLink(this.props.data.poster_url, this.props.siteUrl);
|
||||
} else if ('string' === typeof this.props.data.thumbnail_url) {
|
||||
this.videoPoster = formatInnerLink(this.props.data.thumbnail_url, this.props.siteUrl);
|
||||
}
|
||||
|
||||
this.videoInfo = videoAvailableCodecsAndResolutions(this.props.data.encodings_info, this.props.data.hls_info);
|
||||
|
||||
const resolutionsKeys = Object.keys(this.videoInfo);
|
||||
|
||||
if (!resolutionsKeys.length) {
|
||||
this.videoInfo = null;
|
||||
} else {
|
||||
let defaultResolution = VideoViewerStore.get('video-quality');
|
||||
|
||||
if (null === defaultResolution || ('Auto' === defaultResolution && void 0 === this.videoInfo['Auto'])) {
|
||||
defaultResolution = 720; // Default resolution.
|
||||
}
|
||||
|
||||
let defaultVideoResolution = extractDefaultVideoResolution(defaultResolution, this.videoInfo);
|
||||
|
||||
if ('Auto' === defaultResolution && void 0 !== this.videoInfo['Auto']) {
|
||||
this.videoSources.push({ src: this.videoInfo['Auto'].url[0] });
|
||||
}
|
||||
|
||||
const supportedFormats = orderedSupportedVideoFormats();
|
||||
|
||||
let srcUrl, k;
|
||||
|
||||
k = 0;
|
||||
while (k < this.videoInfo[defaultVideoResolution].format.length) {
|
||||
if ('hls' === this.videoInfo[defaultVideoResolution].format[k]) {
|
||||
this.videoSources.push({ src: this.videoInfo[defaultVideoResolution].url[k] });
|
||||
break;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
|
||||
for (k in this.props.data.encodings_info[defaultVideoResolution]) {
|
||||
if (this.props.data.encodings_info[defaultVideoResolution].hasOwnProperty(k)) {
|
||||
if (supportedFormats.support[k]) {
|
||||
srcUrl = this.props.data.encodings_info[defaultVideoResolution][k].url;
|
||||
|
||||
if (!!srcUrl) {
|
||||
srcUrl = formatInnerLink(srcUrl, this.props.siteUrl);
|
||||
|
||||
this.videoSources.push({
|
||||
src: srcUrl /*.replace("http://", "//").replace("https://", "//")*/,
|
||||
encodings_status: this.props.data.encodings_info[defaultVideoResolution][k].status,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// console.log( supportedFormats );
|
||||
// console.log( this.videoInfo );
|
||||
// console.log( defaultVideoResolution );
|
||||
// console.log( this.videoSources );
|
||||
}
|
||||
|
||||
if (this.videoSources.length) {
|
||||
if (
|
||||
!this.props.inEmbed &&
|
||||
1 === this.videoSources.length &&
|
||||
'running' === this.videoSources[0].encodings_status
|
||||
) {
|
||||
MediaPageStore.set('media-load-error-type', 'encodingRunning');
|
||||
MediaPageStore.set(
|
||||
'media-load-error-message',
|
||||
'Media encoding is currently running. Try again in few minutes.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
switch (MediaPageStore.get('media-load-error-type')) {
|
||||
case 'encodingRunning':
|
||||
case 'encodingPending':
|
||||
case 'encodingFailed':
|
||||
break;
|
||||
default:
|
||||
console.warn('VIDEO DEBUG:', "Video files don't exist");
|
||||
}
|
||||
}
|
||||
|
||||
PageStore.on('switched_media_auto_play', this.onUpdateMediaAutoPlay.bind(this));
|
||||
|
||||
this.browserCache = new BrowserCache(SiteContext._currentValue.id, 86400); // Keep cache data "fresh" for one day.
|
||||
|
||||
const _MediaDurationInfo = new MediaDurationInfo();
|
||||
|
||||
_MediaDurationInfo.update(this.props.data.duration);
|
||||
|
||||
this.durationISO8601 = _MediaDurationInfo.ISO8601();
|
||||
|
||||
this.playerElem = null;
|
||||
|
||||
this.playerInstance = null;
|
||||
|
||||
this.onPlayerInit = this.onPlayerInit.bind(this);
|
||||
|
||||
this.onClickNext = this.onClickNext.bind(this);
|
||||
this.onClickPrevious = this.onClickPrevious.bind(this);
|
||||
this.onStateUpdate = this.onStateUpdate.bind(this);
|
||||
|
||||
this.onVideoEnd = this.onVideoEnd.bind(this);
|
||||
this.onVideoRestart = this.onVideoRestart.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.videoSources.length) {
|
||||
this.recommendedMedia = this.props.data.related_media.length
|
||||
? new PlayerRecommendedMedia(
|
||||
this.props.data.related_media,
|
||||
this.props.inEmbed,
|
||||
!PageStore.get('config-media-item').displayViews
|
||||
)
|
||||
: null;
|
||||
|
||||
this.upNextLoaderView =
|
||||
!this.props.inEmbed && this.props.data.related_media.length
|
||||
? new UpNextLoaderView(this.props.data.related_media[0])
|
||||
: null;
|
||||
|
||||
let topLeftHtml = null;
|
||||
|
||||
if (this.props.inEmbed) {
|
||||
let titleLink = document.createElement('a');
|
||||
let userThumbLink = document.createElement('a');
|
||||
|
||||
topLeftHtml = document.createElement('div');
|
||||
topLeftHtml.setAttribute('class', 'media-links-top-left');
|
||||
|
||||
if (titleLink) {
|
||||
titleLink.setAttribute('class', 'title-link');
|
||||
titleLink.setAttribute('href', this.props.data.url);
|
||||
titleLink.setAttribute('title', this.props.data.title);
|
||||
titleLink.setAttribute('target', '_blank');
|
||||
titleLink.innerHTML = this.props.data.title;
|
||||
}
|
||||
|
||||
if (userThumbLink) {
|
||||
userThumbLink.setAttribute('class', 'user-thumb-link');
|
||||
userThumbLink.setAttribute('href', formatInnerLink(this.props.data.author_profile, this.props.siteUrl));
|
||||
userThumbLink.setAttribute('title', this.props.data.author_name);
|
||||
userThumbLink.setAttribute('target', '_blank');
|
||||
userThumbLink.setAttribute(
|
||||
'style',
|
||||
'background-image:url(' +
|
||||
formatInnerLink(MediaPageStore.get('media-author-thumbnail-url'), this.props.siteUrl) +
|
||||
')'
|
||||
);
|
||||
}
|
||||
|
||||
topLeftHtml.appendChild(userThumbLink);
|
||||
topLeftHtml.appendChild(titleLink);
|
||||
}
|
||||
|
||||
const mediaUrl = MediaPageStore.get('media-url');
|
||||
|
||||
let bottomRightHTML =
|
||||
'<button class="share-video-btn"><i class="material-icons">share</i><span>Share</span></button>';
|
||||
bottomRightHTML +=
|
||||
'<div class="share-options-wrapper">\
|
||||
<div class="share-options">\
|
||||
<div class="share-options-inner">\
|
||||
<div class="sh-option share-email">\
|
||||
<a href="mailto:?body=' +
|
||||
mediaUrl +
|
||||
'" title=""><span><i class="material-icons">email</i></span><span>Email</span></a>\
|
||||
</div>\
|
||||
<div class="sh-option share-fb">\
|
||||
<a href="https://www.facebook.com/sharer.php?u=' +
|
||||
mediaUrl +
|
||||
'" title="" target="_blank"><span></span><span>Facebook</span></a>\
|
||||
</div>\
|
||||
<div class="sh-option share-tw">\
|
||||
<a href="https://twitter.com/intent/tweet?url=' +
|
||||
mediaUrl +
|
||||
'" title="" target="_blank"><span></span><span>Twitter</span></a>\
|
||||
</div>\
|
||||
<div class="sh-option share-whatsapp">\
|
||||
<a href="whatsapp://send?text=' +
|
||||
mediaUrl +
|
||||
'" title="" target="_blank" data-action="share/whatsapp/share"><span></span><span>WhatsApp</span></a>\
|
||||
</div>\
|
||||
<div class="sh-option share-telegram">\
|
||||
<a href="https://t.me/share/url?url=' +
|
||||
mediaUrl +
|
||||
'&text=' +
|
||||
this.props.data.title +
|
||||
'" title="" target="_blank"><span></span><span>Telegram</span></a>\
|
||||
</div>\
|
||||
<div class="sh-option share-linkedin">\
|
||||
<a href="https://www.linkedin.com/shareArticle?mini=true&url=' +
|
||||
mediaUrl +
|
||||
'" title="" target="_blank"><span></span><span>LinkedIn</span></a>\
|
||||
</div>\
|
||||
<div class="sh-option share-reddit">\
|
||||
<a href="https://reddit.com/submit?url=' +
|
||||
mediaUrl +
|
||||
'&title=' +
|
||||
this.props.data.title +
|
||||
'" title="" target="_blank"><span></span><span>reddit</span></a>\
|
||||
</div>\
|
||||
<div class="sh-option share-tumblr">\
|
||||
<a href="https://www.tumblr.com/widgets/share/tool?canonicalUrl=' +
|
||||
mediaUrl +
|
||||
'&title=' +
|
||||
this.props.data.title +
|
||||
'" title="" target="_blank"><span></span><span>Tumblr</span></a>\
|
||||
</div>\
|
||||
<div class="sh-option share-pinterest">\
|
||||
<a href="http://pinterest.com/pin/create/link/?url=' +
|
||||
mediaUrl +
|
||||
'" title="" target="_blank"><span></span><span>Pinterest</span></a>\
|
||||
</div>\
|
||||
<div class="sh-option share-more">\
|
||||
<a href="' +
|
||||
mediaUrl +
|
||||
'" title="More" target="_blank"><span><i class="material-icons">more_horiz</i></span><span></span></a>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
this.cornerLayers = {
|
||||
topLeft: topLeftHtml,
|
||||
topRight: this.upNextLoaderView ? this.upNextLoaderView.html() : null,
|
||||
bottomLeft: this.recommendedMedia ? this.recommendedMedia.html() : null,
|
||||
bottomRight: this.props.inEmbed ? bottomRightHTML : null,
|
||||
};
|
||||
|
||||
this.setState(
|
||||
{
|
||||
displayPlayer: true,
|
||||
},
|
||||
function () {
|
||||
setTimeout(function () {
|
||||
const shareBtn = document.querySelector('.share-video-btn');
|
||||
const shareWrap = document.querySelector('.share-options-wrapper');
|
||||
const shareInner = document.querySelector('.share-options-inner');
|
||||
if (shareBtn) {
|
||||
shareBtn.addEventListener('click', function (ev) {
|
||||
addClassname(document.querySelector('.video-js.vjs-mediacms'), 'vjs-visible-share-options');
|
||||
});
|
||||
}
|
||||
if (shareWrap) {
|
||||
shareWrap.addEventListener('click', function (ev) {
|
||||
if (ev.target === shareInner || ev.target === shareWrap) {
|
||||
removeClassname(document.querySelector('.video-js.vjs-mediacms'), 'vjs-visible-share-options');
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unsetRecommendedMedia();
|
||||
}
|
||||
|
||||
initRecommendedMedia() {
|
||||
if (null === this.recommendedMedia) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.props.inEmbed) {
|
||||
this.recommendedMedia.init();
|
||||
}
|
||||
|
||||
this.playerInstance.player.on('fullscreenchange', this.recommendedMedia.onResize);
|
||||
|
||||
PageStore.on('window_resize', this.recommendedMedia.onResize);
|
||||
|
||||
VideoViewerStore.on('changed_viewer_mode', this.recommendedMedia.onResize);
|
||||
}
|
||||
|
||||
unsetRecommendedMedia() {
|
||||
if (null === this.recommendedMedia) {
|
||||
return;
|
||||
}
|
||||
this.playerInstance.player.off('fullscreenchange', this.recommendedMedia.onResize);
|
||||
PageStore.removeListener('window_resize', this.recommendedMedia.onResize);
|
||||
VideoViewerStore.removeListener('changed_viewer_mode', this.recommendedMedia.onResize);
|
||||
this.recommendedMedia.destroy();
|
||||
}
|
||||
|
||||
onClickNext() {
|
||||
const playlistId = MediaPageStore.get('playlist-id');
|
||||
|
||||
let nextLink;
|
||||
|
||||
if (playlistId) {
|
||||
nextLink = MediaPageStore.get('playlist-next-media-url');
|
||||
|
||||
if (null === nextLink) {
|
||||
nextLink = this.props.data.related_media[0].url;
|
||||
}
|
||||
} else if (!this.props.inEmbed) {
|
||||
nextLink = this.props.data.related_media[0].url;
|
||||
}
|
||||
|
||||
window.location.href = nextLink;
|
||||
}
|
||||
|
||||
onClickPrevious() {
|
||||
const playlistId = MediaPageStore.get('playlist-id');
|
||||
|
||||
let previousLink;
|
||||
|
||||
if (playlistId) {
|
||||
previousLink = MediaPageStore.get('playlist-previous-media-url');
|
||||
|
||||
if (null === previousLink) {
|
||||
previousLink = this.props.data.related_media[0].url;
|
||||
}
|
||||
} else if (!this.props.inEmbed) {
|
||||
previousLink = this.props.data.related_media[0].url;
|
||||
}
|
||||
|
||||
window.location.href = previousLink;
|
||||
}
|
||||
|
||||
onStateUpdate(newState) {
|
||||
if (VideoViewerStore.get('in-theater-mode') !== newState.theaterMode) {
|
||||
VideoViewerActions.set_viewer_mode(newState.theaterMode);
|
||||
}
|
||||
|
||||
if (VideoViewerStore.get('player-volume') !== newState.volume) {
|
||||
VideoViewerActions.set_player_volume(newState.volume);
|
||||
}
|
||||
|
||||
if (VideoViewerStore.get('player-sound-muted') !== newState.soundMuted) {
|
||||
VideoViewerActions.set_player_sound_muted(newState.soundMuted);
|
||||
}
|
||||
|
||||
if (VideoViewerStore.get('video-quality') !== newState.quality) {
|
||||
VideoViewerActions.set_video_quality(newState.quality);
|
||||
}
|
||||
|
||||
if (VideoViewerStore.get('video-playback-speed') !== newState.playbackSpeed) {
|
||||
VideoViewerActions.set_video_playback_speed(newState.playbackSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerInit(instance, elem) {
|
||||
this.playerElem = elem;
|
||||
this.playerInstance = instance;
|
||||
|
||||
if (this.upNextLoaderView) {
|
||||
this.upNextLoaderView.setVideoJsPlayerElem(this.playerInstance.player.el_);
|
||||
this.onUpdateMediaAutoPlay();
|
||||
}
|
||||
|
||||
if (!this.props.inEmbed) {
|
||||
this.playerElem.parentNode.focus(); // Focus on player.
|
||||
}
|
||||
|
||||
if (null !== this.recommendedMedia) {
|
||||
this.recommendedMedia.initWrappers(this.playerElem.parentNode);
|
||||
|
||||
if (this.props.inEmbed) {
|
||||
this.playerInstance.player.one('pause', this.recommendedMedia.init);
|
||||
this.initRecommendedMedia();
|
||||
}
|
||||
}
|
||||
|
||||
this.playerInstance.player.one('ended', this.onVideoEnd);
|
||||
}
|
||||
|
||||
onVideoRestart() {
|
||||
if (null !== this.recommendedMedia) {
|
||||
this.recommendedMedia.updateDisplayType('inline');
|
||||
|
||||
if (this.props.inEmbed) {
|
||||
this.playerInstance.player.one('pause', this.recommendedMedia.init);
|
||||
}
|
||||
|
||||
this.playerInstance.player.one('ended', this.onVideoEnd);
|
||||
}
|
||||
}
|
||||
|
||||
onVideoEnd() {
|
||||
if (null !== this.recommendedMedia) {
|
||||
if (!this.props.inEmbed) {
|
||||
this.initRecommendedMedia();
|
||||
}
|
||||
|
||||
this.recommendedMedia.updateDisplayType('full');
|
||||
this.playerInstance.player.one('playing', this.onVideoRestart);
|
||||
}
|
||||
|
||||
const playlistId = this.props.inEmbed ? null : MediaPageStore.get('playlist-id');
|
||||
|
||||
if (playlistId) {
|
||||
const moreMediaEl = document.querySelector('.video-player .more-media');
|
||||
const actionsAnimEl = document.querySelector('.video-player .vjs-actions-anim');
|
||||
|
||||
this.upNextLoaderView.cancelTimer();
|
||||
|
||||
const nextMediaUrl = MediaPageStore.get('playlist-next-media-url');
|
||||
|
||||
if (nextMediaUrl) {
|
||||
if (moreMediaEl) {
|
||||
moreMediaEl.style.display = 'none';
|
||||
}
|
||||
|
||||
if (actionsAnimEl) {
|
||||
actionsAnimEl.style.display = 'none';
|
||||
}
|
||||
|
||||
window.location.href = nextMediaUrl;
|
||||
}
|
||||
|
||||
this.upNextLoaderView.hideTimerView();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.upNextLoaderView) {
|
||||
if (PageStore.get('media-auto-play')) {
|
||||
this.upNextLoaderView.startTimer();
|
||||
this.playerInstance.player.one(
|
||||
'play',
|
||||
function () {
|
||||
this.upNextLoaderView.cancelTimer();
|
||||
}.bind(this)
|
||||
);
|
||||
} else {
|
||||
this.upNextLoaderView.cancelTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateMediaAutoPlay() {
|
||||
if (this.upNextLoaderView) {
|
||||
if (PageStore.get('media-auto-play')) {
|
||||
this.upNextLoaderView.showTimerView(this.playerInstance.isEnded());
|
||||
} else {
|
||||
this.upNextLoaderView.hideTimerView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let nextLink = null;
|
||||
let previousLink = null;
|
||||
|
||||
const playlistId = this.props.inEmbed ? null : MediaPageStore.get('playlist-id');
|
||||
|
||||
if (playlistId) {
|
||||
nextLink = MediaPageStore.get('playlist-next-media-url');
|
||||
previousLink = MediaPageStore.get('playlist-previous-media-url');
|
||||
} else {
|
||||
nextLink =
|
||||
this.props.data.related_media.length && !this.props.inEmbed ? this.props.data.related_media[0].url : null;
|
||||
}
|
||||
|
||||
const previewSprite = !!this.props.data.sprites_url
|
||||
? {
|
||||
url: this.props.siteUrl + '/' + this.props.data.sprites_url.replace(/^\//g, ''),
|
||||
frame: { width: 160, height: 90, seconds: 10 },
|
||||
}
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={(this.props.inEmbed ? 'embed-' : '') + 'player-container'}
|
||||
className={'player-container' + (this.videoSources.length ? '' : ' player-container-error')}
|
||||
style={this.props.containerStyles}
|
||||
ref="playerContainer"
|
||||
>
|
||||
<div className="player-container-inner" ref="playerContainerInner" style={this.props.containerStyles}>
|
||||
{this.state.displayPlayer && null !== MediaPageStore.get('media-load-error-type') ? (
|
||||
<VideoPlayerError errorMessage={MediaPageStore.get('media-load-error-message')} />
|
||||
) : null}
|
||||
|
||||
{this.state.displayPlayer && null == MediaPageStore.get('media-load-error-type') ? (
|
||||
<div className="video-player" ref="videoPlayerWrapper" key="videoPlayerWrapper">
|
||||
<SiteConsumer>
|
||||
{(site) => (
|
||||
<VideoPlayer
|
||||
playerVolume={this.browserCache.get('player-volume')}
|
||||
playerSoundMuted={this.browserCache.get('player-sound-muted')}
|
||||
videoQuality={this.browserCache.get('video-quality')}
|
||||
videoPlaybackSpeed={parseInt(this.browserCache.get('video-playback-speed'), 10)}
|
||||
inTheaterMode={this.browserCache.get('in-theater-mode')}
|
||||
siteId={site.id}
|
||||
siteUrl={site.url}
|
||||
info={this.videoInfo}
|
||||
cornerLayers={this.cornerLayers}
|
||||
sources={this.videoSources}
|
||||
poster={this.videoPoster}
|
||||
previewSprite={previewSprite}
|
||||
subtitlesInfo={this.props.data.subtitles_info}
|
||||
enableAutoplay={!this.props.inEmbed}
|
||||
inEmbed={this.props.inEmbed}
|
||||
hasTheaterMode={!this.props.inEmbed}
|
||||
hasNextLink={!!nextLink}
|
||||
hasPreviousLink={!!previousLink}
|
||||
errorMessage={MediaPageStore.get('media-load-error-message')}
|
||||
onClickNextCallback={this.onClickNext}
|
||||
onClickPreviousCallback={this.onClickPrevious}
|
||||
onStateUpdateCallback={this.onStateUpdate}
|
||||
onPlayerInitCallback={this.onPlayerInit}
|
||||
/>
|
||||
)}
|
||||
</SiteConsumer>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
VideoViewer.defaultProps = {
|
||||
inEmbed: !0,
|
||||
siteUrl: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
VideoViewer.propTypes = {
|
||||
inEmbed: PropTypes.bool,
|
||||
};
|
||||
|
||||
function findGetParameter(parameterName) {
|
||||
let result = null;
|
||||
let tmp = [];
|
||||
var items = location.search.substr(1).split('&');
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
tmp = items[i].split('=');
|
||||
if (tmp[0] === parameterName) {
|
||||
result = decodeURIComponent(tmp[1]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function handleCanvas(canvasElem) {
|
||||
const Player = videojs(canvasElem);
|
||||
Player.playsinline(true);
|
||||
// TODO: Make them work only in embedded player...?
|
||||
if (findGetParameter('muted') == 1) {
|
||||
Player.muted(true);
|
||||
}
|
||||
if (findGetParameter('time') >= 0) {
|
||||
Player.currentTime(findGetParameter('time'));
|
||||
}
|
||||
if (findGetParameter('autoplay') == 1) {
|
||||
Player.play();
|
||||
}
|
||||
}
|
||||
|
||||
const observer = new MutationObserver(function (mutations, me) {
|
||||
const canvas = document.querySelector('.video-js.vjs-mediacms video');
|
||||
if (canvas) {
|
||||
handleCanvas(canvas);
|
||||
me.disconnect();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
Reference in New Issue
Block a user