Use video.js for audio/video #1986
7 changed files with 277 additions and 256 deletions
|
@ -85,6 +85,7 @@
|
|||
"stream-to-blob-url": "^2.1.1",
|
||||
"three": "^0.93.0",
|
||||
"tree-kill": "^1.1.0",
|
||||
"video.js": "^7.2.2",
|
||||
"y18n": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -5,13 +5,15 @@ import SemVer from 'semver';
|
|||
import findProcess from 'find-process';
|
||||
import url from 'url';
|
||||
import https from 'https';
|
||||
import { shell, app, ipcMain, dialog, session } from 'electron';
|
||||
import { shell, app, ipcMain, dialog, session, protocol } from 'electron';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import isDev from 'electron-is-dev';
|
||||
import Daemon from './Daemon';
|
||||
import createTray from './createTray';
|
||||
import createWindow from './createWindow';
|
||||
import pjson from '../../package.json';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
autoUpdater.autoDownload = true;
|
||||
|
||||
|
@ -63,6 +65,8 @@ if (isDev) {
|
|||
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true;
|
||||
}
|
||||
|
||||
protocol.registerStandardSchemes(['content']);
|
||||
|
||||
app.on('ready', async () => {
|
||||
const processList = await findProcess('name', 'lbrynet-daemon');
|
||||
const isDaemonRunning = processList.length > 0;
|
||||
|
@ -84,22 +88,48 @@ app.on('ready', async () => {
|
|||
});
|
||||
daemon.launch();
|
||||
}
|
||||
|
||||
// https://electronjs.org/docs/api/protocol#protocolregisterstreamprotocolscheme-handler-completion
|
||||
// protocol.registerStreamProtocol(
|
||||
// 'content',
|
||||
// (request, callback) => {
|
||||
// console.log('response', request);
|
||||
// const filePath = request.url.slice('content://'.length);
|
||||
//
|
||||
// callback({
|
||||
// statusCode: 209,
|
||||
// data: fs.createReadStream(filePath),
|
||||
// });
|
||||
// },
|
||||
// error => {
|
||||
// if (error) console.error('err', err);
|
||||
// if (!error) console.log('success');
|
||||
// }
|
||||
// );
|
||||
|
||||
if (isDev) {
|
||||
await installExtensions();
|
||||
}
|
||||
|
||||
rendererWindow = createWindow(appState);
|
||||
rendererWindow.webContents.on('devtools-opened', () => {
|
||||
rendererWindow.webContents.send('devtools-is-opened');
|
||||
});
|
||||
|
||||
tray = createTray(rendererWindow);
|
||||
// HACK: patch webrequest to fix devtools incompatibility with electron 2.x.
|
||||
// See https://github.com/electron/electron/issues/13008#issuecomment-400261941
|
||||
session.defaultSession.webRequest.onBeforeRequest({}, (details, callback) => {
|
||||
if (details.url.indexOf('7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33') !== -1) {
|
||||
callback({redirectURL: details.url.replace('7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33', '57c9d07b416b5a2ea23d28247300e4af36329bdc')});
|
||||
} else {
|
||||
callback({cancel: false});
|
||||
}
|
||||
if (details.url.indexOf('7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33') !== -1) {
|
||||
callback({
|
||||
redirectURL: details.url.replace(
|
||||
'7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33',
|
||||
'57c9d07b416b5a2ea23d28247300e4af36329bdc'
|
||||
),
|
||||
});
|
||||
} else {
|
||||
callback({ cancel: false });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import ThreeViewer from 'component/viewers/threeViewer';
|
|||
import DocumentViewer from 'component/viewers/documentViewer';
|
||||
import DocxViewer from 'component/viewers/docxViewer';
|
||||
import HtmlViewer from 'component/viewers/htmlViewer';
|
||||
import AudioVideoViewer from 'component/viewers/audioVideoViewer';
|
||||
|
||||
type Props = {
|
||||
mediaType: string,
|
||||
|
@ -21,7 +22,7 @@ type Props = {
|
|||
|
||||
class FileRender extends React.PureComponent<Props> {
|
||||
renderViewer() {
|
||||
const { source, mediaType, currentTheme } = this.props;
|
||||
const { source, mediaType, currentTheme, poster } = this.props;
|
||||
|
||||
// Extract relevant data to render file
|
||||
const { blob, stream, fileName, fileType, contentType, downloadPath } = source;
|
||||
|
@ -32,6 +33,15 @@ class FileRender extends React.PureComponent<Props> {
|
|||
// Supported mediaTypes
|
||||
const mediaTypes = {
|
||||
'3D-file': <ThreeViewer source={{ fileType, downloadPath }} theme={currentTheme} />,
|
||||
video: (
|
||||
<AudioVideoViewer
|
||||
source={{ downloadPath, fileName }}
|
||||
contentType={contentType}
|
||||
poster={poster}
|
||||
/>
|
||||
),
|
||||
audio: <AudioVideoViewer source={{ downloadPath, fileName }} contentType={contentType} />,
|
||||
|
||||
// Add routes to viewer...
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import { remote } from 'electron';
|
||||
import fs from 'fs';
|
||||
|
@ -9,9 +8,26 @@ import FileRender from 'component/fileRender';
|
|||
import Thumbnail from 'component/common/thumbnail';
|
||||
import LoadingScreen from 'component/common/loading-screen';
|
||||
|
||||
// Handle fullscreen change for the Windows platform
|
||||
const win32FullScreenChange = () => {
|
||||
const win = remote.BrowserWindow.getFocusedWindow();
|
||||
if (process.platform === 'win32') {
|
||||
win.setMenu(document.webkitIsFullScreen ? null : remote.Menu.getApplicationMenu());
|
||||
}
|
||||
};
|
||||
|
||||
class MediaPlayer extends React.PureComponent {
|
||||
static MP3_CONTENT_TYPES = ['audio/mpeg3', 'audio/mpeg'];
|
||||
static FILE_MEDIA_TYPES = ['text', 'script', 'e-book', 'comic-book', 'document', '3D-file'];
|
||||
// static MP3_CONTENT_TYPES = ['audio/mpeg3', 'audio/mpeg'];
|
||||
static FILE_MEDIA_TYPES = [
|
||||
'video',
|
||||
'audio',
|
||||
'text',
|
||||
'script',
|
||||
'e-book',
|
||||
'comic-book',
|
||||
'document',
|
||||
'3D-file',
|
||||
];
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -23,189 +39,40 @@ class MediaPlayer extends React.PureComponent {
|
|||
fileSource: null,
|
||||
};
|
||||
|
||||
this.togglePlayListener = this.togglePlay.bind(this);
|
||||
this.toggleFullScreenVideo = this.toggleFullScreen.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate(nextProps) {
|
||||
const el = this.refs.media.children[0];
|
||||
if (this.props.playingUri && !nextProps.playingUri && !el.paused) {
|
||||
el.pause();
|
||||
}
|
||||
// this.togglePlayListener = this.togglePlay.bind(this);
|
||||
// this.toggleFullScreenVideo = this.toggleFullScreen.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const container = this.media;
|
||||
const {
|
||||
downloadCompleted,
|
||||
contentType,
|
||||
changeVolume,
|
||||
volume,
|
||||
position,
|
||||
claim,
|
||||
startedPlayingCb,
|
||||
} = this.props;
|
||||
|
||||
const loadedMetadata = () => {
|
||||
this.setState({ hasMetadata: true, startedPlaying: true });
|
||||
|
||||
if (startedPlayingCb) {
|
||||
startedPlayingCb();
|
||||
}
|
||||
this.media.children[0].play();
|
||||
};
|
||||
|
||||
const renderMediaCallback = error => {
|
||||
if (error) this.setState({ unplayable: true });
|
||||
};
|
||||
|
||||
// Handle fullscreen change for the Windows platform
|
||||
const win32FullScreenChange = () => {
|
||||
const win = remote.BrowserWindow.getFocusedWindow();
|
||||
if (process.platform === 'win32') {
|
||||
win.setMenu(document.webkitIsFullScreen ? null : remote.Menu.getApplicationMenu());
|
||||
}
|
||||
};
|
||||
|
||||
// use renderAudio override for mp3
|
||||
if (MediaPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
|
||||
this.renderAudio(container, null, false);
|
||||
}
|
||||
// Render custom viewer: FileRender
|
||||
else if (this.fileType()) {
|
||||
downloadCompleted && this.renderFile();
|
||||
}
|
||||
// Render default viewer: render-media (video, audio, img, iframe)
|
||||
else {
|
||||
player.append(
|
||||
this.file(),
|
||||
container,
|
||||
{ autoplay: true, controls: true },
|
||||
renderMediaCallback.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', this.togglePlayListener);
|
||||
const mediaElement = this.media.children[0];
|
||||
if (mediaElement) {
|
||||
if (position) {
|
||||
mediaElement.currentTime = position;
|
||||
}
|
||||
mediaElement.addEventListener('timeupdate', () =>
|
||||
this.props.savePosition(
|
||||
claim.claim_id,
|
||||
`${claim.txid}:${claim.nout}`,
|
||||
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);
|
||||
});
|
||||
mediaElement.volume = volume;
|
||||
mediaElement.addEventListener('dblclick', this.toggleFullScreenVideo);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(next) {
|
||||
const el = this.media.children[0];
|
||||
if (!this.props.paused && next.paused && !el.paused) el.pause();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { contentType, downloadCompleted } = this.props;
|
||||
const { startedPlaying, fileSource } = this.state;
|
||||
|
||||
if (this.playableType() && !startedPlaying && downloadCompleted) {
|
||||
const container = this.media.children[0];
|
||||
|
||||
if (MediaPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
|
||||
this.renderAudio(this.media, true);
|
||||
} else {
|
||||
player.render(this.file(), container, {
|
||||
autoplay: true,
|
||||
controls: true,
|
||||
});
|
||||
}
|
||||
} else if (this.fileType() && !fileSource && downloadCompleted) {
|
||||
if (this.isSupportedFile()) {
|
||||
this.renderFile();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.togglePlayListener);
|
||||
const mediaElement = this.media.children[0];
|
||||
if (mediaElement) {
|
||||
mediaElement.removeEventListener('click', this.togglePlayListener);
|
||||
componentDidUpdate() {
|
||||
const { fileName, downloadPath, contentType } = this.props;
|
||||
const { fileSource } = this.state;
|
||||
|
||||
if (!fileSource && fileName && downloadPath && contentType) {
|
||||
this.renderFile();
|
||||
}
|
||||
}
|
||||
|
||||
toggleFullScreen(event) {
|
||||
const mediaElement = this.media.children[0];
|
||||
if (mediaElement) {
|
||||
if (document.webkitIsFullScreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else {
|
||||
mediaElement.webkitRequestFullScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
const mediaElement = this.media.children[0];
|
||||
if (mediaElement) {
|
||||
if (!mediaElement.paused) {
|
||||
mediaElement.pause();
|
||||
} else {
|
||||
mediaElement.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file() {
|
||||
const { downloadPath, fileName } = this.props;
|
||||
|
||||
return {
|
||||
name: fileName,
|
||||
createReadStream: opts => fs.createReadStream(downloadPath, opts),
|
||||
};
|
||||
}
|
||||
|
||||
playableType() {
|
||||
const { mediaType } = this.props;
|
||||
|
||||
return ['audio', 'video'].indexOf(mediaType) !== -1;
|
||||
}
|
||||
|
||||
supportedType() {
|
||||
// Files supported by render-media
|
||||
const { contentType, mediaType } = this.props;
|
||||
|
||||
return Object.values(player.mime).indexOf(contentType) !== -1;
|
||||
}
|
||||
|
||||
fileType() {
|
||||
isSupportedFile() {
|
||||
// This files are supported using a custom viewer
|
||||
const { mediaType } = this.props;
|
||||
|
||||
return MediaPlayer.FILE_MEDIA_TYPES.indexOf(mediaType) > -1;
|
||||
const isSupported = MediaPlayer.FILE_MEDIA_TYPES.includes(mediaType);
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
renderFile() {
|
||||
// This is what render-media does with unplayable files
|
||||
const { fileName, downloadPath, contentType, mediaType } = this.props;
|
||||
// We know we can render this file
|
||||
// Set the fileSource to state so the FileRender component will be rendered
|
||||
const { fileName, downloadPath, contentType } = this.props;
|
||||
|
||||
if (!fileName || !downloadPath || !contentType) {
|
||||
return;
|
||||
}
|
||||
|
||||
// File to render
|
||||
const fileSource = {
|
||||
|
@ -213,96 +80,27 @@ class MediaPlayer extends React.PureComponent {
|
|||
contentType,
|
||||
downloadPath,
|
||||
fileType: path.extname(fileName).substring(1),
|
||||
// // Readable stream from file
|
||||
// stream: opts => fs.createReadStream(downloadPath, opts),
|
||||
};
|
||||
|
||||
// Readable stream from file
|
||||
fileSource.stream = opts => fs.createReadStream(downloadPath, opts);
|
||||
|
||||
// Blob url from stream
|
||||
fileSource.blob = callback =>
|
||||
toBlobURL(fs.createReadStream(downloadPath), contentType, callback);
|
||||
|
||||
// Update state
|
||||
this.setState({ fileSource });
|
||||
}
|
||||
|
||||
renderAudio(container, autoplay) {
|
||||
if (container.firstChild) {
|
||||
container.firstChild.remove();
|
||||
}
|
||||
|
||||
// clear the container
|
||||
const { downloadPath } = this.props;
|
||||
const audio = document.createElement('audio');
|
||||
audio.autoplay = autoplay;
|
||||
audio.controls = true;
|
||||
audio.src = downloadPath;
|
||||
container.appendChild(audio);
|
||||
}
|
||||
|
||||
showLoadingScreen(isFileType, isPlayableType) {
|
||||
const { mediaType } = this.props;
|
||||
const { hasMetadata, unplayable, unsupported, fileSource } = this.state;
|
||||
|
||||
const loader = {
|
||||
isLoading: false,
|
||||
loadingStatus: null,
|
||||
};
|
||||
|
||||
// Loading message
|
||||
const noFileMessage = __('Waiting for blob.');
|
||||
const noMetadataMessage = __('Waiting for metadata.');
|
||||
|
||||
// Error message
|
||||
const unplayableMessage = __("Sorry, looks like we can't play this file.");
|
||||
const unsupportedMessage = __("Sorry, looks like we can't preview this file.");
|
||||
|
||||
// Files
|
||||
const isLoadingFile = !fileSource && isFileType;
|
||||
const isUnsupported =
|
||||
(mediaType === 'application' || !this.supportedType()) && !isFileType && !isPlayableType;
|
||||
|
||||
// Media (audio, video)
|
||||
const isUnplayable = isPlayableType && unplayable;
|
||||
const isLoadingMetadata = isPlayableType && (!hasMetadata && !unplayable);
|
||||
|
||||
// Show loading message
|
||||
if (isLoadingFile || isLoadingMetadata) {
|
||||
loader.loadingStatus = isFileType ? noFileMessage : noMetadataMessage;
|
||||
loader.isLoading = true;
|
||||
|
||||
// Show unsupported error message
|
||||
} else if (isUnsupported || isUnplayable) {
|
||||
loader.loadingStatus = isUnsupported ? unsupportedMessage : unplayableMessage;
|
||||
}
|
||||
|
||||
return loader;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { mediaType } = this.props;
|
||||
const { mediaType, poster } = this.props;
|
||||
const { fileSource } = this.state;
|
||||
|
||||
const isFileType = this.fileType();
|
||||
const isFileType = this.isSupportedFile();
|
||||
const isFileReady = fileSource && isFileType;
|
||||
const isPlayableType = this.playableType();
|
||||
const { isLoading, loadingStatus } = this.showLoadingScreen(isFileType, isPlayableType);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{loadingStatus && <LoadingScreen status={loadingStatus} spinner={isLoading} />}
|
||||
{isFileReady && <FileRender source={fileSource} mediaType={mediaType} />}
|
||||
<div
|
||||
className={'content__view--container'}
|
||||
style={{ opacity: isLoading ? 0 : 1 }}
|
||||
ref={container => {
|
||||
this.media = container;
|
||||
}}
|
||||
/>
|
||||
{!isFileReady && <LoadingScreen status="loadingStatus" spinner />}
|
||||
{isFileReady && <FileRender source={fileSource} mediaType={mediaType} poster={poster} />}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MediaPlayer;
|
||||
/* eslint-disable */
|
||||
|
|
80
src/renderer/component/viewers/audioVideoViewer.jsx
Normal file
80
src/renderer/component/viewers/audioVideoViewer.jsx
Normal file
|
@ -0,0 +1,80 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { stopContextMenu } from 'util/contextMenu';
|
||||
import videojs from 'video.js';
|
||||
import 'video.js/dist/video-js.css';
|
||||
import toBlobURL from 'stream-to-blob-url';
|
||||
import fs from 'fs';
|
||||
|
||||
type Props = {
|
||||
source: {
|
||||
downloadPath: string,
|
||||
fileName: string,
|
||||
},
|
||||
contentType: string,
|
||||
poster?: string,
|
||||
};
|
||||
|
||||
class AudioVideoViewer extends React.PureComponent<Props> {
|
||||
componentDidMount() {
|
||||
const { source, contentType, poster } = this.props;
|
||||
const { downloadPath, fileName } = source;
|
||||
|
||||
const indexOfFileName = downloadPath.indexOf(fileName);
|
||||
const basePath = downloadPath.slice(0, indexOfFileName);
|
||||
const encodedFileName = encodeURIComponent(fileName);
|
||||
|
||||
// We only want to encode the fileName so forward slashes "/" are handled properly by the file system
|
||||
// TODO: Determine changes needed for windows
|
||||
const path = `file://${basePath}${encodedFileName}`;
|
||||
|
||||
// Another alternative, maybe we don't need to do anything in the main electron process?
|
||||
// get blob url, then set as source and call videojs()
|
||||
// toBlobURL(fs.createReadStream(downloadPath), (err, url) => {
|
||||
// if (err) return console.error(err.message)
|
||||
// console.log(url);
|
||||
// const sources = [
|
||||
// {
|
||||
// src: url,
|
||||
// type: contentType
|
||||
// }
|
||||
// ]
|
||||
|
||||
const sources = [
|
||||
{
|
||||
src: path,
|
||||
type: contentType,
|
||||
},
|
||||
];
|
||||
|
||||
const videoJsOptions = {
|
||||
autoplay: true,
|
||||
controls: true,
|
||||
preload: 'auto',
|
||||
poster,
|
||||
sources,
|
||||
};
|
||||
|
||||
this.player = videojs(this.videoNode, videoJsOptions, () => {});
|
||||
// })
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.player) {
|
||||
this.player.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { source } = this.props;
|
||||
return (
|
||||
<div className="file-render__viewer" onContextMenu={stopContextMenu}>
|
||||
<div data-vjs-player>
|
||||
<video ref={node => (this.videoNode = node)} className="video-js" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AudioVideoViewer;
|
|
@ -18,10 +18,17 @@
|
|||
background-color: black;
|
||||
|
||||
iframe,
|
||||
webview {
|
||||
webview,
|
||||
.video-js {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// Removing the play button because we have autoplay turned on
|
||||
// These are classes added by video.js
|
||||
.video-js .vjs-big-play-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.document-viewer {
|
||||
|
|
99
yarn.lock
99
yarn.lock
|
@ -134,6 +134,18 @@
|
|||
version "1.13.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976"
|
||||
|
||||
"@videojs/http-streaming@1.2.4":
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@videojs/http-streaming/-/http-streaming-1.2.4.tgz#6245524b76203db5e6750153d4896d007cc7f7cd"
|
||||
dependencies:
|
||||
aes-decrypter "3.0.0"
|
||||
global "^4.3.0"
|
||||
m3u8-parser "4.2.0"
|
||||
mpd-parser "0.6.1"
|
||||
mux.js "4.5.1"
|
||||
url-toolkit "^2.1.3"
|
||||
video.js "^6.8.0 || ^7.0.0"
|
||||
|
||||
abbrev@1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
|
@ -173,6 +185,14 @@ acorn@^5.0.0, acorn@^5.2.1, acorn@^5.5.0:
|
|||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
|
||||
|
||||
aes-decrypter@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/aes-decrypter/-/aes-decrypter-3.0.0.tgz#7848a1c145b9fdbf57ae3e2b5b1bc7cf0644a8fb"
|
||||
dependencies:
|
||||
commander "^2.9.0"
|
||||
global "^4.3.2"
|
||||
pkcs7 "^1.0.2"
|
||||
|
||||
agent-base@4, agent-base@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
||||
|
@ -1134,7 +1154,7 @@ babel-register@^6.26.0:
|
|||
mkdirp "^0.5.1"
|
||||
source-map-support "^0.4.15"
|
||||
|
||||
babel-runtime@6.x, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
|
||||
babel-runtime@6.x, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
dependencies:
|
||||
|
@ -4400,7 +4420,7 @@ global-dirs@^0.1.0:
|
|||
dependencies:
|
||||
ini "^1.3.4"
|
||||
|
||||
global@~4.3.0:
|
||||
global@4.3.2, global@^4.3.0, global@^4.3.1, global@^4.3.2, global@~4.3.0:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f"
|
||||
dependencies:
|
||||
|
@ -4947,6 +4967,10 @@ indexof@0.0.1:
|
|||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
|
||||
|
||||
individual@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/individual/-/individual-2.0.0.tgz#833b097dad23294e76117a98fb38e0d9ad61bb97"
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
|
@ -6051,6 +6075,10 @@ lz-string@^1.4.4:
|
|||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||
|
||||
m3u8-parser@4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.2.0.tgz#c8e0785fd17f741f4408b49466889274a9e36447"
|
||||
|
||||
make-dir@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
|
||||
|
@ -6447,6 +6475,13 @@ mp4-stream@^2.0.0:
|
|||
next-event "^1.0.0"
|
||||
readable-stream "^2.0.3"
|
||||
|
||||
mpd-parser@0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-0.6.1.tgz#27e7aafe075817846ce55406ac03711df1ce0eb7"
|
||||
dependencies:
|
||||
global "^4.3.0"
|
||||
url-toolkit "^2.1.1"
|
||||
|
||||
ms@0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
|
||||
|
@ -6477,6 +6512,10 @@ mute-stream@0.0.7:
|
|||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
|
||||
mux.js@4.5.1:
|
||||
version "4.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-4.5.1.tgz#1d70f1ad9b951315e16390d47be8fc42fd080194"
|
||||
|
||||
nan@2.8.0:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a"
|
||||
|
@ -7231,6 +7270,10 @@ pinkie@^2.0.0:
|
|||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
||||
|
||||
pkcs7@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/pkcs7/-/pkcs7-1.0.2.tgz#b6dba527528c2942bfc122ce2dafcdb5e59074e7"
|
||||
|
||||
pkg-dir@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4"
|
||||
|
@ -8425,6 +8468,12 @@ run-queue@^1.0.0, run-queue@^1.0.3:
|
|||
dependencies:
|
||||
aproba "^1.1.1"
|
||||
|
||||
rust-result@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rust-result/-/rust-result-1.0.0.tgz#34c75b2e6dc39fe5875e5bdec85b5e0f91536f72"
|
||||
dependencies:
|
||||
individual "^2.0.0"
|
||||
|
||||
rx-lite-aggregates@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
|
||||
|
@ -8449,6 +8498,12 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2,
|
|||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
|
||||
safe-json-parse@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-4.0.0.tgz#7c0f578cfccd12d33a71c0e05413e2eca171eaac"
|
||||
dependencies:
|
||||
rust-result "^1.0.0"
|
||||
|
||||
safe-regex@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
|
||||
|
@ -9518,6 +9573,10 @@ tslib@^1.9.0:
|
|||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
||||
|
||||
tsml@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/tsml/-/tsml-1.0.1.tgz#89f8218b9d9e257f47d7f6b56d01c5a4d2c68fc3"
|
||||
|
||||
tty-browserify@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
|
||||
|
@ -9846,6 +9905,10 @@ url-to-options@^1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
|
||||
|
||||
url-toolkit@^2.1.1, url-toolkit@^2.1.3:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.1.6.tgz#6d03246499e519aad224c44044a4ae20544154f2"
|
||||
|
||||
url@0.10.3:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"
|
||||
|
@ -9946,6 +10009,29 @@ vfile@^2.0.0:
|
|||
unist-util-stringify-position "^1.0.0"
|
||||
vfile-message "^1.0.0"
|
||||
|
||||
"video.js@^6.8.0 || ^7.0.0", video.js@^7.2.2:
|
||||
version "7.2.2"
|
||||
resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.2.2.tgz#4a1197b2f0c265a1e50d5cd4ff41cd030e22a423"
|
||||
dependencies:
|
||||
"@videojs/http-streaming" "1.2.4"
|
||||
babel-runtime "^6.9.2"
|
||||
global "4.3.2"
|
||||
safe-json-parse "4.0.0"
|
||||
tsml "1.0.1"
|
||||
videojs-font "3.0.0"
|
||||
videojs-vtt.js "0.14.1"
|
||||
xhr "2.4.0"
|
||||
|
||||
videojs-font@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/videojs-font/-/videojs-font-3.0.0.tgz#90eafddcf26b407448c833523f5ca4ad8d5cc1af"
|
||||
|
||||
videojs-vtt.js@0.14.1:
|
||||
version "0.14.1"
|
||||
resolved "https://registry.yarnpkg.com/videojs-vtt.js/-/videojs-vtt.js-0.14.1.tgz#da583eb1fc9c81c826a9432b706040e8dea49911"
|
||||
dependencies:
|
||||
global "^4.3.1"
|
||||
|
||||
videostream@^2.3.0:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/videostream/-/videostream-2.4.3.tgz#bdcc252309fa1d4e7077643d2809b822270b5e60"
|
||||
|
@ -10248,6 +10334,15 @@ xdg-basedir@^3.0.0:
|
|||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||
|
||||
xhr@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.4.0.tgz#e16e66a45f869861eeefab416d5eff722dc40993"
|
||||
dependencies:
|
||||
global "~4.3.0"
|
||||
is-function "^1.0.1"
|
||||
parse-headers "^2.0.0"
|
||||
xtend "^4.0.0"
|
||||
|
||||
xhr@^2.0.1:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd"
|
||||
|
|
Loading…
Reference in a new issue