mirror of
https://github.com/LBRYFoundation/lbry-desktop.git
synced 2025-08-30 08:51:24 +00:00
add fileRender component
This commit is contained in:
parent
5cd70d5680
commit
a8edbee7f6
8 changed files with 140 additions and 19 deletions
|
@ -76,6 +76,7 @@
|
||||||
"semver": "^5.3.0",
|
"semver": "^5.3.0",
|
||||||
"shapeshift.io": "^1.3.1",
|
"shapeshift.io": "^1.3.1",
|
||||||
"source-map-support": "^0.5.4",
|
"source-map-support": "^0.5.4",
|
||||||
|
"stream-to-blob-url": "^2.1.1",
|
||||||
"tree-kill": "^1.1.0",
|
"tree-kill": "^1.1.0",
|
||||||
"y18n": "^4.0.0"
|
"y18n": "^4.0.0"
|
||||||
},
|
},
|
||||||
|
|
16
src/renderer/component/fileRender/index.js
Normal file
16
src/renderer/component/fileRender/index.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { THEME } from 'constants/settings';
|
||||||
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
|
|
||||||
|
import FileRender from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
currentTheme: makeSelectClientSetting(THEME)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(FileRender);
|
49
src/renderer/component/fileRender/view.jsx
Normal file
49
src/renderer/component/fileRender/view.jsx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import LoadingScreen from 'component/common/loading-screen';
|
||||||
|
//import ThreeViewer from 'component/threeViewer';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
mediaType: string,
|
||||||
|
fileSource: {
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
},
|
||||||
|
currentTheme: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileRender extends React.PureComponent<Props> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
routeViewer() {
|
||||||
|
const { mediaType, fileSource, currentTheme } = this.props;
|
||||||
|
|
||||||
|
if (!mediaType || !fileSource) return null;
|
||||||
|
|
||||||
|
// Supported mediaTypes
|
||||||
|
const mediaTypes = {
|
||||||
|
// '3D-file': () => <ThreeViewer source={fileSource} theme={currentTheme}/>,
|
||||||
|
// 'e-book': () => <EbookReader />,
|
||||||
|
// 'comic-book' () => <ComicReader />,
|
||||||
|
// Add routes to viewer...
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return viewer
|
||||||
|
return mediaType ? mediaTypes[mediaType] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const Viewer = this.routeViewer();
|
||||||
|
const unsupportedMessage = "Sorry, looks like we can't preview this file.";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="file-render">
|
||||||
|
{Viewer ? <Viewer /> : <LoadingScreen status={unsupportedMessage} spinner={false} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileRender;
|
|
@ -1,13 +1,18 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { remote } from 'electron';
|
import { remote } from 'electron';
|
||||||
import Thumbnail from 'component/common/thumbnail';
|
|
||||||
import player from 'render-media';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import LoadingScreen from './loading-screen';
|
import path from 'path';
|
||||||
|
import player from 'render-media';
|
||||||
|
import toBlobURL from 'stream-to-blob-url';
|
||||||
|
import FileRender from 'component/fileRender';
|
||||||
|
import Thumbnail from 'component/common/thumbnail';
|
||||||
|
import LoadingScreen from 'component/common/loading-screen';
|
||||||
|
|
||||||
|
|
||||||
class VideoPlayer extends React.PureComponent {
|
class VideoPlayer extends React.PureComponent {
|
||||||
static MP3_CONTENT_TYPES = ['audio/mpeg3', 'audio/mpeg'];
|
static MP3_CONTENT_TYPES = ['audio/mpeg3', 'audio/mpeg'];
|
||||||
|
static FILE_MEDIA_TYPES = ['3D-file', 'e-book', 'comic-book'];
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -51,7 +56,11 @@ class VideoPlayer extends React.PureComponent {
|
||||||
// use renderAudio override for mp3
|
// use renderAudio override for mp3
|
||||||
if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
|
if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
|
||||||
this.renderAudio(container, null, false);
|
this.renderAudio(container, null, false);
|
||||||
} else {
|
}
|
||||||
|
// Render custom viewer: FileRender
|
||||||
|
if (this.fileType()) this.renderFile();
|
||||||
|
// Render default viewer: render-media (video, audio, img, iframe)
|
||||||
|
else if (this.supportedType()) {
|
||||||
player.append(
|
player.append(
|
||||||
this.file(),
|
this.file(),
|
||||||
container,
|
container,
|
||||||
|
@ -159,6 +168,38 @@ class VideoPlayer extends React.PureComponent {
|
||||||
return ['audio', 'video'].indexOf(mediaType) !== -1;
|
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() {
|
||||||
|
// This files are supported using a custom viewer
|
||||||
|
const { mediaType } = this.props;
|
||||||
|
|
||||||
|
return VideoPlayer.FILE_MEDIA_TYPES.indexOf(mediaType) > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFile() {
|
||||||
|
// This is what render-media does with unplayable files
|
||||||
|
const { filename, downloadPath, contentType, mediaType } = this.props;
|
||||||
|
toBlobURL(fs.createReadStream(downloadPath), contentType, (err, url) => {
|
||||||
|
if (err) {
|
||||||
|
this.setState({ unsupported: true });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// File to render
|
||||||
|
const fileSource = {
|
||||||
|
filePath: url,
|
||||||
|
fileType: path.extname(filename).substring(1),
|
||||||
|
};
|
||||||
|
// Update state
|
||||||
|
this.setState({ fileSource });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
renderAudio(container, autoplay) {
|
renderAudio(container, autoplay) {
|
||||||
if (container.firstChild) {
|
if (container.firstChild) {
|
||||||
container.firstChild.remove();
|
container.firstChild.remove();
|
||||||
|
@ -175,23 +216,27 @@ class VideoPlayer extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { mediaType, poster } = this.props;
|
const { mediaType, poster } = this.props;
|
||||||
const { hasMetadata, unplayable } = this.state;
|
const { hasMetadata, unplayable, unsupported, fileSource } = this.state;
|
||||||
|
const noFileMessage = 'Waiting for blob.';
|
||||||
const noMetadataMessage = 'Waiting for metadata.';
|
const noMetadataMessage = 'Waiting for metadata.';
|
||||||
const unplayableMessage = "Sorry, looks like we can't play this file.";
|
const unplayableMessage = "Sorry, looks like we can't play this file.";
|
||||||
const hideMedia = this.playableType() && !hasMetadata && !unplayable;
|
const unsupportedMessage = "Sorry, looks like we can't preview this file.";
|
||||||
|
const isLoadingFile = !fileSource && this.fileType();
|
||||||
|
const isLoadingMetadata = this.playableType() && (!hasMetadata && !unplayable);
|
||||||
|
const isUnplayable = this.playableType() && unplayable;
|
||||||
|
const isUnsupported = !this.supportedType() && !this.playableType() && !this.fileType();
|
||||||
|
const isFile = fileSource && this.fileType();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{['audio', 'application'].indexOf(mediaType) !== -1 &&
|
{isLoadingFile && <LoadingScreen status={noFileMessage} />}
|
||||||
(!this.playableType() || hasMetadata) &&
|
{isLoadingMetadata && <LoadingScreen status={noMetadataMessage} />}
|
||||||
!unplayable && <Thumbnail src={poster} />}
|
{isUnplayable && <LoadingScreen status={unplayableMessage} spinner={false} />}
|
||||||
{this.playableType() &&
|
{unsupported || isUnsupported && <LoadingScreen status={unsupportedMessage} spinner={false} />}
|
||||||
!hasMetadata &&
|
{isFile && <FileRender source={fileSource} mediaType={mediaType} />}
|
||||||
!unplayable && <LoadingScreen status={noMetadataMessage} />}
|
|
||||||
{unplayable && <LoadingScreen status={unplayableMessage} spinner={false} />}
|
|
||||||
<div
|
<div
|
||||||
className={'content__view--container'}
|
className={'content__view--container'}
|
||||||
style={{ opacity: hideMedia ? 0 : 1 }}
|
style={{ opacity: isLoadingMetadata || isUnplayable ? 0 : 1 }}
|
||||||
ref={container => {
|
ref={container => {
|
||||||
this.media = container;
|
this.media = container;
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import classnames from 'classnames';
|
||||||
import type { Claim } from 'types/claim';
|
import type { Claim } from 'types/claim';
|
||||||
import VideoPlayer from './internal/player';
|
import VideoPlayer from './internal/player';
|
||||||
import VideoPlayButton from './internal/play-button';
|
import VideoPlayButton from './internal/play-button';
|
||||||
import LoadingScreen from './internal/loading-screen';
|
import LoadingScreen from 'component/common/loading-screen';
|
||||||
|
|
||||||
const SPACE_BAR_KEYCODE = 32;
|
const SPACE_BAR_KEYCODE = 32;
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ class Video extends React.PureComponent<Props> {
|
||||||
const isPlaying = playingUri === uri;
|
const isPlaying = playingUri === uri;
|
||||||
const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0;
|
const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0;
|
||||||
const shouldObscureNsfw = obscureNsfw && metadata && metadata.nsfw;
|
const shouldObscureNsfw = obscureNsfw && metadata && metadata.nsfw;
|
||||||
const mediaType = Lbry.getMediaType(contentType, fileInfo && fileInfo.file_name);
|
const mediaType = (fileInfo && Lbry.getMediaType(null, fileInfo.file_name)) || Lbry.getMediaType(contentType);
|
||||||
|
|
||||||
let loadStatusMessage = '';
|
let loadStatusMessage = '';
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,8 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class FilePage extends React.Component<Props> {
|
class FilePage extends React.Component<Props> {
|
||||||
|
static MEDIA_TYPES = ['audio', '3D-file', 'e-book', 'comic-book'];
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
@ -111,12 +113,14 @@ class FilePage extends React.Component<Props> {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// File info
|
// File info
|
||||||
const { title, thumbnail } = metadata;
|
const { title, thumbnail, filename } = metadata;
|
||||||
const isRewardContent = rewardedContentClaimIds.includes(claim.claim_id);
|
const isRewardContent = rewardedContentClaimIds.includes(claim.claim_id);
|
||||||
const shouldObscureThumbnail = obscureNsfw && metadata.nsfw;
|
const shouldObscureThumbnail = obscureNsfw && metadata.nsfw;
|
||||||
const { height, channel_name: channelName, value } = claim;
|
const { height, channel_name: channelName, value } = claim;
|
||||||
const mediaType = Lbry.getMediaType(contentType);
|
// TODO: fix getMediaType logic (lbry-redux)
|
||||||
const isPlayable = Object.values(player.mime).includes(contentType) || mediaType === 'audio';
|
const mediaType = Lbry.getMediaType(null, filename) || Lbry.getMediaType(contentType);
|
||||||
|
const isPlayable =
|
||||||
|
Object.values(player.mime).indexOf(contentType) !== -1 || FilePage.MEDIA_TYPES.indexOf(mediaType);
|
||||||
const channelClaimId =
|
const channelClaimId =
|
||||||
value && value.publisherSignature && value.publisherSignature.certificateId;
|
value && value.publisherSignature && value.publisherSignature.certificateId;
|
||||||
let subscriptionUri;
|
let subscriptionUri;
|
||||||
|
|
|
@ -8726,6 +8726,12 @@ stream-to-blob-url@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
stream-to-blob "^1.0.0"
|
stream-to-blob "^1.0.0"
|
||||||
|
|
||||||
|
stream-to-blob-url@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/stream-to-blob-url/-/stream-to-blob-url-2.1.1.tgz#e1ac97f86ca8e9f512329a48e7830ce9a50beef2"
|
||||||
|
dependencies:
|
||||||
|
stream-to-blob "^1.0.0"
|
||||||
|
|
||||||
stream-to-blob@^1.0.0:
|
stream-to-blob@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/stream-to-blob/-/stream-to-blob-1.0.0.tgz#9f7a1ada39e16ea282ebb7e4cda307edabde658d"
|
resolved "https://registry.yarnpkg.com/stream-to-blob/-/stream-to-blob-1.0.0.tgz#9f7a1ada39e16ea282ebb7e4cda307edabde658d"
|
||||||
|
|
Loading…
Add table
Reference in a new issue