Merge pull request #223 from lbryio/video-fallback

Metadata at end
This commit is contained in:
Jeremy Kauffman 2017-06-11 12:10:41 -04:00 committed by GitHub
commit 1fc7c92524
9 changed files with 100 additions and 58 deletions

22
app/package-lock.json generated
View file

@ -1,6 +1,6 @@
{ {
"name": "LBRY", "name": "LBRY",
"version": "0.12.0rc6", "version": "0.12.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"dependencies": { "dependencies": {
"commander": { "commander": {
@ -63,11 +63,6 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz",
"integrity": "sha1-Umao9J3Zib5Pn2gbbyoMVShdDZo=" "integrity": "sha1-Umao9J3Zib5Pn2gbbyoMVShdDZo="
}, },
"modify-filename": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/modify-filename/-/modify-filename-1.1.0.tgz",
"integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE="
},
"npm": { "npm": {
"version": "4.6.1", "version": "4.6.1",
"resolved": "https://registry.npmjs.org/npm/-/npm-4.6.1.tgz", "resolved": "https://registry.npmjs.org/npm/-/npm-4.6.1.tgz",
@ -1456,16 +1451,6 @@
} }
} }
}, },
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
},
"pupa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/pupa/-/pupa-1.0.0.tgz",
"integrity": "sha1-mpVopa9+ZXuEYqbp1TKHQ1YM7/Y="
},
"semver": { "semver": {
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
@ -1480,11 +1465,6 @@
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.1.0.tgz", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.1.0.tgz",
"integrity": "sha1-yWPc8DciiS7FnLpWnpQLcZVNFyk=" "integrity": "sha1-yWPc8DciiS7FnLpWnpQLcZVNFyk="
},
"unused-filename": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-0.1.0.tgz",
"integrity": "sha1-5fM7yeSmP4f2TTwR0xl53vXS5/s="
} }
} }
} }

View file

@ -12,7 +12,7 @@ export function doFetchCostInfoForUri(uri) {
claim = selectClaimsByUri(state)[uri], claim = selectClaimsByUri(state)[uri],
isGenerous = selectSettingsIsGenerous(state); isGenerous = selectSettingsIsGenerous(state);
if (!claim) return null if (!claim) return null;
function begin() { function begin() {
dispatch({ dispatch({

View file

@ -1,23 +1,19 @@
import React from 'react' import React from "react";
import { import { connect } from "react-redux";
connect import { doUserEmailNew } from "actions/user";
} from 'react-redux'
import {
doUserEmailNew
} from 'actions/user'
import { import {
selectEmailNewIsPending, selectEmailNewIsPending,
selectEmailNewErrorMessage, selectEmailNewErrorMessage,
} from 'selectors/user' } from "selectors/user";
import UserEmailNew from './view' import UserEmailNew from "./view";
const select = (state) => ({ const select = state => ({
isPending: selectEmailNewIsPending(state), isPending: selectEmailNewIsPending(state),
errorMessage: selectEmailNewErrorMessage(state), errorMessage: selectEmailNewErrorMessage(state),
}) });
const perform = (dispatch) => ({ const perform = dispatch => ({
addUserEmail: (email) => dispatch(doUserEmailNew(email)) addUserEmail: email => dispatch(doUserEmailNew(email)),
}) });
export default connect(select, perform)(UserEmailNew) export default connect(select, perform)(UserEmailNew);

View file

@ -1,9 +1,9 @@
import React from "react"; import React from "react";
const LoadingScreen = ({ status }) => const LoadingScreen = ({ status, spinner = true }) =>
<div className="video__loading-screen"> <div className="video__loading-screen">
<div> <div>
<div className="video__loading-spinner" /> {spinner && <div className="video__loading-spinner" />}
<div className="video__loading-status"> <div className="video__loading-status">
{status} {status}

View file

@ -2,30 +2,97 @@ import React from "react";
import { Thumbnail } from "component/common"; import { Thumbnail } from "component/common";
import player from "render-media"; import player from "render-media";
import fs from "fs"; import fs from "fs";
import LoadingScreen from "./loading-screen";
class VideoPlayer extends React.PureComponent { class VideoPlayer extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
hasMetadata: false,
startedPlaying: false,
unplayable: false,
};
}
componentDidMount() { componentDidMount() {
const elem = this.refs.media; const container = this.refs.media;
const { mediaType } = this.props;
const loadedMetadata = e => {
this.setState({ hasMetadata: true, startedPlaying: true });
this.refs.media.children[0].play();
};
const renderMediaCallback = err => {
if (err) this.setState({ unplayable: true });
};
player.append(
this.file(),
container,
{ autoplay: false, controls: true },
renderMediaCallback.bind(this)
);
const mediaElement = this.refs.media.children[0];
if (mediaElement) {
mediaElement.addEventListener(
"loadedmetadata",
loadedMetadata.bind(this),
{
once: true,
}
);
}
}
componentDidUpdate() {
const { mediaType, downloadCompleted } = this.props;
const { startedPlaying } = this.state;
if (this.playableType() && !startedPlaying && downloadCompleted) {
const container = this.refs.media.children[0];
player.render(this.file(), container, { autoplay: true, controls: true });
}
}
file() {
const { downloadPath, filename } = this.props; const { downloadPath, filename } = this.props;
const file = {
return {
name: filename, name: filename,
createReadStream: opts => { createReadStream: opts => {
return fs.createReadStream(downloadPath, opts); return fs.createReadStream(downloadPath, opts);
}, },
}; };
player.append(file, elem, { }
autoplay: true,
controls: true, playableType() {
}); const { mediaType } = this.props;
return ["audio", "video"].indexOf(mediaType) !== -1;
} }
render() { render() {
const { downloadPath, mediaType, poster } = this.props; const { mediaType, poster } = this.props;
const { hasMetadata, unplayable } = this.state;
const noMetadataMessage = "Waiting for metadata.";
const unplayableMessage = "Sorry, looks like we can't play this file.";
const needsMetadata = this.playableType();
return ( return (
<div> <div>
{["audio", "application"].indexOf(mediaType) !== -1 && {["audio", "application"].indexOf(mediaType) !== -1 &&
(!this.playableType() || hasMetadata) &&
!unplayable &&
<Thumbnail src={poster} className="video-embedded" />} <Thumbnail src={poster} className="video-embedded" />}
{this.playableType() &&
!hasMetadata &&
!unplayable &&
<LoadingScreen status={noMetadataMessage} />}
{unplayable &&
<LoadingScreen status={unplayableMessage} spinner={false} />}
<div ref="media" /> <div ref="media" />
</div> </div>
); );

View file

@ -68,6 +68,7 @@ class Video extends React.PureComponent {
poster={poster} poster={poster}
downloadPath={fileInfo.download_path} downloadPath={fileInfo.download_path}
mediaType={mediaType} mediaType={mediaType}
downloadCompleted={fileInfo.completed}
/>)} />)}
{!isPlaying && {!isPlaying &&
<div <div

View file

@ -22,9 +22,9 @@ reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
return Object.assign({}, state, { return Object.assign({}, state, {
byId, byId,
claimsByUri: byUri claimsByUri: byUri,
}); });
} };
reducers[types.RESOLVE_URI_CANCELED] = function(state, action) { reducers[types.RESOLVE_URI_CANCELED] = function(state, action) {
const uri = action.data.uri; const uri = action.data.uri;
@ -42,9 +42,7 @@ reducers[types.FETCH_CLAIM_LIST_MINE_STARTED] = function(state, action) {
}; };
reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) { reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
const { const { claims } = action.data;
claims,
} = action.data;
const myClaims = new Set(state.myClaims); const myClaims = new Set(state.myClaims);
const byUri = Object.assign({}, state.claimsByUri); const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
@ -52,14 +50,14 @@ reducers[types.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
claims.forEach(claim => { claims.forEach(claim => {
myClaims.add(claim.claim_id); myClaims.add(claim.claim_id);
byId[claim.claim_id] = claim; byId[claim.claim_id] = claim;
}) });
return Object.assign({}, state, { return Object.assign({}, state, {
isClaimListMinePending: false, isClaimListMinePending: false,
myClaims: myClaims, myClaims: myClaims,
byId, byId,
}); });
} };
// reducers[types.FETCH_CHANNEL_CLAIMS_STARTED] = function(state, action) { // reducers[types.FETCH_CHANNEL_CLAIMS_STARTED] = function(state, action) {
// const { // const {

View file

@ -5,7 +5,7 @@ const _selectState = state => state.claims || {};
export const selectClaimsById = createSelector( export const selectClaimsById = createSelector(
_selectState, _selectState,
(state) => state.byId || {} state => state.byId || {}
); );
export const selectClaimsByUri = createSelector( export const selectClaimsByUri = createSelector(
@ -28,11 +28,11 @@ export const selectClaimsByUri = createSelector(
claims[uri] = claim; claims[uri] = claim;
} }
}) });
return claims; return claims;
} }
) );
export const selectAllClaimsByChannel = createSelector( export const selectAllClaimsByChannel = createSelector(
_selectState, _selectState,
@ -107,7 +107,7 @@ export const selectMyClaimsOutpoints = createSelector(
claimIds.forEach(claimId => { claimIds.forEach(claimId => {
const claim = byId[claimId]; const claim = byId[claimId];
if (claim) outpoints.push(`${claim.txid}:${claim.nout}`); if (claim) outpoints.push(`${claim.txid}:${claim.nout}`);
}) });
return outpoints; return outpoints;
} }

2
ui/package-lock.json generated
View file

@ -1,6 +1,6 @@
{ {
"name": "lbry-web-ui", "name": "lbry-web-ui",
"version": "0.12.0rc6", "version": "0.12.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"dependencies": { "dependencies": {
"abbrev": { "abbrev": {