lbry-desktop/src/renderer/component/video/internal/player.jsx

189 lines
5.4 KiB
React
Raw Normal View History

const { remote } = require('electron');
import React from 'react';
import { Thumbnail } from 'component/common';
import player from 'render-media';
import fs from 'fs';
import LoadingScreen from './loading-screen';
2017-12-18 18:02:55 +01:00
2017-06-09 01:41:44 +02:00
class VideoPlayer extends React.PureComponent {
static MP3_CONTENT_TYPES = ['audio/mpeg3', 'audio/mpeg'];
2017-07-06 19:33:44 +02:00
constructor(props) {
super(props);
this.state = {
hasMetadata: false,
startedPlaying: false,
unplayable: false,
};
2017-07-01 23:57:14 +02:00
this.togglePlayListener = this.togglePlay.bind(this);
}
2017-12-21 00:38:11 +01:00
componentWillReceiveProps(next) {
const el = this.refs.media.children[0];
if (!this.props.paused && next.paused && !el.paused) el.pause();
}
componentDidMount() {
const container = this.refs.media;
const {
contentType,
downloadPath,
mediaType,
changeVolume,
volume,
2017-12-21 00:38:11 +01:00
position,
2018-01-05 20:27:58 +01:00
claim,
uri,
} = this.props;
2017-12-21 00:38:11 +01:00
const loadedMetadata = e => {
2017-07-06 19:33:44 +02:00
this.setState({ hasMetadata: true, startedPlaying: true });
this.refs.media.children[0].play();
};
const renderMediaCallback = err => {
if (err) this.setState({ unplayable: true });
};
// Handle fullscreen change for the Windows platform
const win32FullScreenChange = e => {
const win = remote.BrowserWindow.getFocusedWindow();
if (process.platform === 'win32') {
win.setMenu(document.webkitIsFullScreen ? null : remote.Menu.getApplicationMenu());
}
};
2017-07-06 19:33:44 +02:00
// use renderAudio override for mp3
if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
2017-07-06 21:03:31 +02:00
this.renderAudio(container, null, false);
2017-07-06 19:33:44 +02:00
} else {
player.append(
this.file(),
container,
{ autoplay: false, controls: true },
renderMediaCallback.bind(this)
);
}
document.addEventListener('keydown', this.togglePlayListener);
const mediaElement = this.refs.media.children[0];
if (mediaElement) {
2018-01-05 20:27:58 +01:00
mediaElement.currentTime = position || 0;
2018-01-06 00:57:24 +01:00
mediaElement.addEventListener('play', () => this.props.doPlay());
mediaElement.addEventListener('pause', () => this.props.doPause());
mediaElement.addEventListener('timeupdate', () =>
2018-01-05 20:27:58 +01:00
this.props.savePosition(claim.claim_id, mediaElement.currentTime)
);
mediaElement.addEventListener('click', this.togglePlayListener);
mediaElement.addEventListener('loadedmetadata', loadedMetadata.bind(this), {
once: true,
});
mediaElement.addEventListener('webkitfullscreenchange', win32FullScreenChange.bind(this));
mediaElement.addEventListener('volumechange', () => {
changeVolume(mediaElement.volume);
2017-07-01 23:38:09 +02:00
});
mediaElement.volume = volume;
2017-07-01 23:57:14 +02:00
}
}
componentWillUnmount() {
document.removeEventListener('keydown', this.togglePlayListener);
2017-07-01 23:57:14 +02:00
const mediaElement = this.refs.media.children[0];
if (mediaElement) {
mediaElement.removeEventListener('click', this.togglePlayListener);
}
this.props.doPause();
}
2017-07-06 19:33:44 +02:00
renderAudio(container, autoplay) {
2017-07-06 21:03:31 +02:00
if (container.firstChild) {
container.firstChild.remove();
}
// clear the container
2017-07-06 19:33:44 +02:00
const { downloadPath } = this.props;
const audio = document.createElement('audio');
2017-07-06 19:33:44 +02:00
audio.autoplay = autoplay;
audio.controls = true;
audio.src = downloadPath;
container.appendChild(audio);
}
2017-07-01 22:33:23 +02:00
togglePlay(event) {
// ignore all events except click and spacebar keydown, or input events in a form control
if (
event.type === 'keydown' &&
(event.code !== 'Space' || event.target.tagName.toLowerCase() === 'input')
) {
2017-07-01 22:33:23 +02:00
return;
}
event.preventDefault();
const mediaElement = this.refs.media.children[0];
if (mediaElement) {
if (!mediaElement.paused) {
mediaElement.pause();
} else {
mediaElement.play();
}
}
}
componentDidUpdate() {
2017-07-06 19:33:44 +02:00
const { contentType, downloadCompleted } = this.props;
const { startedPlaying } = this.state;
if (this.playableType() && !startedPlaying && downloadCompleted) {
const container = this.refs.media.children[0];
2017-07-06 19:33:44 +02:00
if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
2017-07-06 21:03:31 +02:00
this.renderAudio(this.refs.media, true);
2017-07-06 19:33:44 +02:00
} else {
player.render(this.file(), container, {
autoplay: true,
controls: true,
});
}
}
}
file() {
const { downloadPath, filename } = this.props;
2017-07-06 19:44:52 +02:00
return {
name: filename,
createReadStream: opts => fs.createReadStream(downloadPath, opts),
};
}
2017-07-06 19:44:52 +02:00
playableType() {
const { mediaType } = this.props;
return ['audio', 'video'].indexOf(mediaType) !== -1;
}
render() {
const { mediaType, poster } = this.props;
const { hasMetadata, unplayable } = this.state;
const noMetadataMessage = 'Waiting for metadata.';
const unplayableMessage = "Sorry, looks like we can't play this file.";
const needsMetadata = this.playableType();
return (
<div>
{['audio', 'application'].indexOf(mediaType) !== -1 &&
(!this.playableType() || hasMetadata) &&
!unplayable && <Thumbnail src={poster} className="video-embedded" />}
{this.playableType() &&
!hasMetadata &&
!unplayable && <LoadingScreen status={noMetadataMessage} />}
{unplayable && <LoadingScreen status={unplayableMessage} spinner={false} />}
<div ref="media" className="media" />
</div>
);
}
}
export default VideoPlayer;