129 lines
4 KiB
JavaScript
129 lines
4 KiB
JavaScript
import React from 'react';
|
|
|
|
function dataURItoBlob(dataURI) {
|
|
// convert base64/URLEncoded data component to raw binary data held in a string
|
|
let byteString = atob(dataURI.split(',')[1]);
|
|
// separate out the mime component
|
|
let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
|
|
// write the bytes of the string to a typed array
|
|
let ia = new Uint8Array(byteString.length);
|
|
for (let i = 0; i < byteString.length; i++) {
|
|
ia[i] = byteString.charCodeAt(i);
|
|
}
|
|
return new Blob([ia], {type: mimeString});
|
|
}
|
|
|
|
class PublishThumbnailInput extends React.Component {
|
|
constructor (props) {
|
|
super(props);
|
|
this.state = {
|
|
videoSource : null,
|
|
error : null,
|
|
sliderMinRange: 1,
|
|
sliderMaxRange: null,
|
|
sliderValue : null,
|
|
};
|
|
this.handleVideoLoadedData = this.handleVideoLoadedData.bind(this);
|
|
this.handleSliderChange = this.handleSliderChange.bind(this);
|
|
this.createThumbnail = this.createThumbnail.bind(this);
|
|
}
|
|
componentDidMount () {
|
|
const { file } = this.props;
|
|
this.setVideoSource(file);
|
|
}
|
|
componentWillReceiveProps (nextProps) {
|
|
// if file changes
|
|
if (nextProps.file && nextProps.file !== this.props.file) {
|
|
const { file } = nextProps;
|
|
this.setVideoSource(file);
|
|
};
|
|
}
|
|
setVideoSource (file) {
|
|
const previewReader = new FileReader();
|
|
previewReader.readAsDataURL(file);
|
|
previewReader.onloadend = () => {
|
|
console.log('preview reader complete');
|
|
const dataUri = previewReader.result;
|
|
const blob = dataURItoBlob(dataUri);
|
|
const videoSource = URL.createObjectURL(blob);
|
|
this.setState({ videoSource });
|
|
};
|
|
}
|
|
handleVideoLoadedData (event) {
|
|
const duration = event.target.duration;
|
|
// set the slider
|
|
this.setState({
|
|
sliderMaxRange: duration * 100,
|
|
sliderValue : duration * 100 / 2,
|
|
});
|
|
// update the current time of the video
|
|
let video = document.getElementById('video-thumb-player');
|
|
video.currentTime = duration / 2;
|
|
}
|
|
handleSliderChange (event) {
|
|
const value = parseInt(event.target.value);
|
|
// update the slider value
|
|
this.setState({
|
|
sliderValue: value,
|
|
});
|
|
// update the current time of the video
|
|
let video = document.getElementById('video-thumb-player');
|
|
video.currentTime = value / 100;
|
|
}
|
|
createThumbnail () {
|
|
// take a snapshot
|
|
let video = document.getElementById('video-thumb-player');
|
|
let canvas = document.createElement('canvas');
|
|
canvas.width = video.videoWidth;
|
|
canvas.height = video.videoHeight;
|
|
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
const dataUrl = canvas.toDataURL();
|
|
const snapshot = dataURItoBlob(dataUrl);
|
|
// set the thumbnail in redux store
|
|
if (snapshot) {
|
|
this.props.onNewThumbnail(snapshot);
|
|
}
|
|
}
|
|
render () {
|
|
const { error, videoSource, sliderMinRange, sliderMaxRange, sliderValue } = this.state;
|
|
return (
|
|
<div>
|
|
<div>
|
|
{ error ? (
|
|
<p className='info-message--failure'>{error}</p>
|
|
) : (
|
|
<p className='info-message'>Use slider to set thumbnail:</p>
|
|
)}
|
|
<video
|
|
id='video-thumb-player'
|
|
preload='metadata'
|
|
muted
|
|
style={{display: 'none'}}
|
|
playsInline
|
|
onLoadedData={this.handleVideoLoadedData}
|
|
src={videoSource}
|
|
onSeeked={this.createThumbnail}
|
|
/>
|
|
{
|
|
sliderValue ? (
|
|
<div className='slide-container'>
|
|
<input
|
|
type='range'
|
|
min={sliderMinRange}
|
|
max={sliderMaxRange}
|
|
value={sliderValue}
|
|
className='slider'
|
|
onChange={this.handleSliderChange}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<p className='info-message' >loading... </p>
|
|
)
|
|
}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default PublishThumbnailInput;
|