From 7c704c78c14e77618d486308cb2d690d31e5a38a Mon Sep 17 00:00:00 2001 From: Lucas Zawacki <lfzawacki@gmail.com> Date: Thu, 26 Nov 2020 17:30:37 -0300 Subject: [PATCH] PeerTube player, first version --- .../external-video-player/component.jsx | 5 + .../custom-players/peertube.jsx | 211 ++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 bigbluebutton-html5/imports/ui/components/external-video-player/custom-players/peertube.jsx diff --git a/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx b/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx index f77e40abde..49b61df680 100644 --- a/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/external-video-player/component.jsx @@ -6,6 +6,7 @@ import { sendMessage, onMessage, removeAllListeners } from './service'; import logger from '/imports/startup/client/logger'; import ArcPlayer from './custom-players/arc-player'; +import PeerTubePlayer from './custom-players/peertube'; import { styles } from './styles'; @@ -20,6 +21,7 @@ const SYNC_INTERVAL_SECONDS = 5; const THROTTLE_INTERVAL_SECONDS = 0.5; const AUTO_PLAY_BLOCK_DETECTION_TIMEOUT_SECONDS = 5; +ReactPlayer.addCustomPlayer(PeerTubePlayer); ReactPlayer.addCustomPlayer(ArcPlayer); class VideoPlayer extends Component { @@ -75,6 +77,9 @@ class VideoPlayer extends Component { controls: isPresenter ? 1 : 2, }, }, + peertube: { + isPresenter, + }, twitch: { options: { controls: true, diff --git a/bigbluebutton-html5/imports/ui/components/external-video-player/custom-players/peertube.jsx b/bigbluebutton-html5/imports/ui/components/external-video-player/custom-players/peertube.jsx new file mode 100644 index 0000000000..ea0c0d88a2 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/external-video-player/custom-players/peertube.jsx @@ -0,0 +1,211 @@ +import loadScript from 'load-script'; +import React, { Component } from 'react' + +const MATCH_URL = new RegExp("(https?)://(.*)/videos/watch/(.*)"); + +const SDK_URL = 'https://unpkg.com/@peertube/embed-api/build/player.min.js'; + +// Util function to load an external SDK or return the SDK if it is already loaded +// From https://github.com/CookPete/react-player/blob/master/src/utils.js +const resolves = {}; +export function getSDK (url, sdkGlobal, sdkReady = null, isLoaded = () => true, fetchScript = loadScript) { + if (window[sdkGlobal] && isLoaded(window[sdkGlobal])) { + return Promise.resolve(window[sdkGlobal]) + } + return new Promise((resolve, reject) => { + // If we are already loading the SDK, add the resolve + // function to the existing array of resolve functions + if (resolves[url]) { + resolves[url].push(resolve); + return + } + resolves[url] = [resolve]; + const onLoaded = sdk => { + // When loaded, resolve all pending promises + resolves[url].forEach(resolve => resolve(sdk)) + }; + if (sdkReady) { + const previousOnReady = window[sdkReady]; + window[sdkReady] = function () { + if (previousOnReady) previousOnReady(); + onLoaded(window[sdkGlobal]) + } + } + fetchScript(url, err => { + if (err) { + reject(err); + } + window[sdkGlobal] = url; + if (!sdkReady) { + onLoaded(window[sdkGlobal]) + } + }) + }) +} + +export class PeerTubePlayer extends Component { + static displayName = 'PeerTubePlayer'; + + static canPlay = url => { + return MATCH_URL.test(url) + }; + + constructor(props) { + super(props); + + this.player = this; + this._player = null; + + this.currentTime = 0; + this.playbackRate = 1; + this.getCurrentTime = this.getCurrentTime.bind(this); + this.getEmbedUrl = this.getEmbedUrl.bind(this); + this.setupEvents = this.setupEvents.bind(this); + } + + componentDidMount () { + this.props.onMount && this.props.onMount(this) + } + + getEmbedUrl = () => { + const { config, url } = this.props; + const m = MATCH_URL.exec(url); + + const isPresenter = config && config.peertube && config.peertube.isPresenter; + + return `${m[1]}://${m[2]}/videos/embed/${m[3]}?api=1&controls=${true}`; + }; + + load() { + new Promise((resolve, reject) => { + this.render(); + resolve(); + }) + .then(() => { return getSDK(SDK_URL, 'PeerTube') }) + .then(() => { + this._player = new window['PeerTubePlayer'](this.container); + + this.setupEvents(); + + return this._player.ready.then(() => { + return this.props.onReady(); + }); + }); + } + + setupEvents(event) { + const player = this._player; + + if (!player) { + return; + } + + player.addEventListener("playbackStatusUpdate", (data) => { + this.currentTime = data.position; + }); + player.addEventListener("playbackStatusChange", (data) => { + if (data === 'playing') { + this.props.onPlay(); + } else { + this.props.onPause(); + } + }); + + } + + play() { + if (this._player) { + this._player.play(); + } + } + + pause() { + if (this._player) { + this._player.pause(); + } + } + + stop() { + } + + seekTo(seconds) { + if (this._player) { + this._player.seek(seconds); + } + } + + setVolume(fraction) { + // console.log("SET VOLUME"); + } + + setLoop(loop) { + // console.log("SET LOOP"); + } + + mute() { + // console.log("SET MUTE"); + } + + unmute() { + // console.log("SET UNMUTE"); + } + + getDuration() { + //console.log("GET DURATION"); + } + + getCurrentTime () { + return this.currentTime; + } + + getSecondsLoaded () { + } + + getPlaybackRate () { + + if (this._player) { + this._player.getPlaybackRate().then((rate) => { + this.playbackRate = rate; + }); + } + + return this.playbackRate; + } + + setPlaybackRate (rate) { + + if (this._player) { + this._player.setPlaybackRate(rate); + } + } + + render () { + const style = { + width: '100%', + height: '100%', + margin: 0, + padding: 0, + border: 0, + overflow: 'hidden', + }; + const { url } = this.props; + + return ( + <iframe + key={url} + style={style} + src={this.getEmbedUrl(url)} + id={"peerTubeContainer"} + allow="autoplay; fullscreen" + sandbox="allow-same-origin allow-scripts allow-popups" + ref={(container) => { + this.container = container; + }} + > + </iframe> + ) + } +} + +export default PeerTubePlayer; + -- GitLab