mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 21:27:05 +00:00
simple youtube player implemted
This commit is contained in:
parent
30bce1ba11
commit
d653e35796
4 changed files with 255 additions and 3 deletions
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
<script src="https://www.youtube.com/iframe_api"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import ReactHTMLVideo from './stremio-video/ReactHTMLVideo';
|
import ReactHTMLVideo from './stremio-video/ReactHTMLVideo';
|
||||||
|
import ReactYouTubeVideo from './stremio-video/ReactYouTubeVideo';
|
||||||
import ControlBar from './ControlBar';
|
import ControlBar from './ControlBar';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
|
@ -42,9 +43,13 @@ class Player extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareStream = () => {
|
prepareStream = () => {
|
||||||
return Promise.resolve({
|
return new Promise((resolve, reject) => {
|
||||||
source: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
|
YT.ready(() => {
|
||||||
videoComponent: ReactHTMLVideo
|
resolve({
|
||||||
|
source: 'SEL97pn5aS0',
|
||||||
|
videoComponent: ReactYouTubeVideo
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
57
src/routes/Player/stremio-video/ReactYouTubeVideo.js
Normal file
57
src/routes/Player/stremio-video/ReactYouTubeVideo.js
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import YouTubeVideo from './YouTubeVideo';
|
||||||
|
|
||||||
|
class ReactYouTubeVideo extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.videoContainerRef = React.createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.video = new YouTubeVideo(this.videoContainerRef.current);
|
||||||
|
this.video.on('ended', this.props.onEnded);
|
||||||
|
this.video.on('error', this.props.onError);
|
||||||
|
this.video.on('propValue', this.props.onPropValue);
|
||||||
|
this.video.on('propChanged', this.props.onPropChanged);
|
||||||
|
this.props.observedProps.forEach((propName) => {
|
||||||
|
this.dispatch('observeProp', propName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.dispatch('stop');
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch = (...args) => {
|
||||||
|
try {
|
||||||
|
this.video && this.video.dispatch(...args);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('YouTubeVideo', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div ref={this.videoContainerRef} className={this.props.className} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactYouTubeVideo.manifest = YouTubeVideo.manifest;
|
||||||
|
|
||||||
|
ReactYouTubeVideo.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
onEnded: PropTypes.func.isRequired,
|
||||||
|
onError: PropTypes.func.isRequired,
|
||||||
|
onPropValue: PropTypes.func.isRequired,
|
||||||
|
onPropChanged: PropTypes.func.isRequired,
|
||||||
|
observedProps: PropTypes.arrayOf(PropTypes.string).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReactYouTubeVideo;
|
||||||
189
src/routes/Player/stremio-video/YouTubeVideo.js
Normal file
189
src/routes/Player/stremio-video/YouTubeVideo.js
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
var EventEmitter = require('events');
|
||||||
|
|
||||||
|
var YouTubeVideo = function(containerElement) {
|
||||||
|
var events = new EventEmitter();
|
||||||
|
var ready = false;
|
||||||
|
var observedProps = {};
|
||||||
|
var timeIntervalId = null;
|
||||||
|
var durationIntervalId = null;
|
||||||
|
var dispatchArgsQueue = [];
|
||||||
|
var onEnded = function() {
|
||||||
|
events.emit('ended');
|
||||||
|
};
|
||||||
|
var onError = function(event) {
|
||||||
|
var message;
|
||||||
|
var critical;
|
||||||
|
switch (event.data) {
|
||||||
|
case 2:
|
||||||
|
message = 'Invalid request';
|
||||||
|
critical = true;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
message = 'The requested content cannot be played';
|
||||||
|
critical = true;
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
message = 'The video has been removed or marked as private';
|
||||||
|
critical = true;
|
||||||
|
break;
|
||||||
|
case 101:
|
||||||
|
case 150:
|
||||||
|
message = 'The video cannot be played in embedded players';
|
||||||
|
critical = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message = 'Unknown error';
|
||||||
|
critical = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
events.emit('error', {
|
||||||
|
code: event.data,
|
||||||
|
message: message,
|
||||||
|
critical: critical
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var onPausedChanged = function() {
|
||||||
|
events.emit('propChanged', 'paused', video.getPlayerState() === YT.PlayerState.PAUSED);
|
||||||
|
};
|
||||||
|
var onTimeChanged = function() {
|
||||||
|
events.emit('propChanged', 'time', video.getCurrentTime() * 1000);
|
||||||
|
};
|
||||||
|
var onDurationChanged = function() {
|
||||||
|
events.emit('propChanged', 'duration', video.getDuration() !== 0 ? video.getDuration() * 1000 : null);
|
||||||
|
};
|
||||||
|
var onReady = function() {
|
||||||
|
ready = true;
|
||||||
|
dispatchArgsQueue.forEach(function(args) {
|
||||||
|
this.dispatch.apply(this, args);
|
||||||
|
}, this);
|
||||||
|
dispatchArgsQueue = [];
|
||||||
|
};
|
||||||
|
var onStateChange = function(event) {
|
||||||
|
switch (event.data) {
|
||||||
|
case YT.PlayerState.ENDED:
|
||||||
|
onEnded();
|
||||||
|
break;
|
||||||
|
case YT.PlayerState.PLAYING:
|
||||||
|
if (observedProps.paused) {
|
||||||
|
onPausedChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (observedProps.duration) {
|
||||||
|
onDurationChanged();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case YT.PlayerState.PAUSED:
|
||||||
|
if (observedProps.paused) {
|
||||||
|
onPausedChanged();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var video = new YT.Player(containerElement, {
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
playerVars: {
|
||||||
|
autoplay: 1,
|
||||||
|
cc_load_policy: 0,
|
||||||
|
controls: 0,
|
||||||
|
disablekb: 1,
|
||||||
|
enablejsapi: 1,
|
||||||
|
fs: 0,
|
||||||
|
iv_load_policy: 3,
|
||||||
|
loop: 0,
|
||||||
|
modestbranding: 1,
|
||||||
|
playsinline: 1,
|
||||||
|
rel: 0
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
onError: onError.bind(this),
|
||||||
|
onReady: onReady.bind(this),
|
||||||
|
onStateChange: onStateChange.bind(this)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on = function(eventName, listener) {
|
||||||
|
events.on(eventName, listener);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.dispatch = function() {
|
||||||
|
if (arguments[0] === 'observeProp') {
|
||||||
|
switch (arguments[1]) {
|
||||||
|
case 'paused':
|
||||||
|
if (ready) {
|
||||||
|
events.emit('propValue', 'paused', video.getPlayerState() === YT.PlayerState.PAUSED);
|
||||||
|
observedProps.paused = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'time':
|
||||||
|
if (ready) {
|
||||||
|
events.emit('propValue', 'time', video.getCurrentTime() * 1000);
|
||||||
|
if (timeIntervalId === null) {
|
||||||
|
timeIntervalId = window.setInterval(onTimeChanged, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'duration':
|
||||||
|
if (ready) {
|
||||||
|
events.emit('propValue', 'duration', video.getDuration() !== 0 ? video.getDuration() * 1000 : null);
|
||||||
|
observedProps.duration = true;
|
||||||
|
if (durationIntervalId === null) {
|
||||||
|
durationIntervalId = window.setInterval(onDurationChanged, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('observeProp not supported: ' + arguments[1]);
|
||||||
|
}
|
||||||
|
} else if (arguments[0] === 'setProp') {
|
||||||
|
switch (arguments[1]) {
|
||||||
|
case 'paused':
|
||||||
|
if (ready) {
|
||||||
|
arguments[2] ? video.pauseVideo() : video.playVideo();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'time':
|
||||||
|
if (ready) {
|
||||||
|
video.seekTo(arguments[2] / 1000);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('setProp not supported: ' + arguments[1]);
|
||||||
|
}
|
||||||
|
} else if (arguments[0] === 'command') {
|
||||||
|
switch (arguments[1]) {
|
||||||
|
case 'load':
|
||||||
|
if (ready) {
|
||||||
|
video.loadVideoById({
|
||||||
|
videoId: arguments[2].source,
|
||||||
|
startSeconds: isNaN(arguments[2].time) ? 0 : arguments[2].time / 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'stop':
|
||||||
|
if (ready) {
|
||||||
|
events.removeAllListeners();
|
||||||
|
observedProps = {};
|
||||||
|
clearInterval(timeIntervalId);
|
||||||
|
clearInterval(durationIntervalId);
|
||||||
|
video.pauseVideo();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('command not supported: ' + arguments[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ready) {
|
||||||
|
dispatchArgsQueue.push(Array.from(arguments));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
YouTubeVideo.manifest = {
|
||||||
|
name: 'YouTubeVideo',
|
||||||
|
embedded: true,
|
||||||
|
props: ['paused', 'time', 'duration', 'volume']
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = YouTubeVideo;
|
||||||
Loading…
Reference in a new issue