closer to real watch
This commit is contained in:
parent
3b29be467b
commit
280f98902c
|
@ -256,8 +256,6 @@ var App = React.createClass({
|
||||||
return <SettingsPage />;
|
return <SettingsPage />;
|
||||||
case 'help':
|
case 'help':
|
||||||
return <HelpPage />;
|
return <HelpPage />;
|
||||||
case 'watch':
|
|
||||||
return <WatchPage uri={this.state.pageArgs} />;
|
|
||||||
case 'report':
|
case 'report':
|
||||||
return <ReportPage />;
|
return <ReportPage />;
|
||||||
case 'downloaded':
|
case 'downloaded':
|
||||||
|
|
|
@ -9,59 +9,6 @@ import {DropDownMenu, DropDownMenuItem} from './menu.js';
|
||||||
|
|
||||||
const {shell} = require('electron');
|
const {shell} = require('electron');
|
||||||
|
|
||||||
let WatchLink = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
uri: React.PropTypes.string,
|
|
||||||
downloadStarted: React.PropTypes.bool,
|
|
||||||
},
|
|
||||||
startVideo: function() {
|
|
||||||
window.location = '?watch=' + this.props.uri;
|
|
||||||
},
|
|
||||||
handleClick: function() {
|
|
||||||
this.setState({
|
|
||||||
loading: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.props.downloadStarted) {
|
|
||||||
this.startVideo();
|
|
||||||
} else {
|
|
||||||
lbry.getCostInfo(this.props.uri).then(({cost}) => {
|
|
||||||
lbry.getBalance((balance) => {
|
|
||||||
if (cost > balance) {
|
|
||||||
this.setState({
|
|
||||||
modal: 'notEnoughCredits',
|
|
||||||
loading: false,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.startVideo();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
modal: null,
|
|
||||||
loading: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
closeModal: function() {
|
|
||||||
this.setState({
|
|
||||||
modal: null,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<div className="button-set-item">
|
|
||||||
<Link button={ this.props.button ? this.props.button : 'alt' } disabled={this.state.loading} label="Watch" icon="icon-play" onClick={this.handleClick} />
|
|
||||||
<Modal contentLabel="Not enough credits" isOpen={this.state.modal == 'notEnoughCredits'} onConfirmed={this.closeModal}>
|
|
||||||
You don't have enough LBRY credits to pay for this stream.
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let FileActionsRow = React.createClass({
|
let FileActionsRow = React.createClass({
|
||||||
_isMounted: false,
|
_isMounted: false,
|
||||||
_fileInfoSubscribeId: null,
|
_fileInfoSubscribeId: null,
|
||||||
|
@ -213,9 +160,6 @@ let FileActionsRow = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.props.contentType && this.props.contentType.startsWith('video/')
|
|
||||||
? <WatchLink uri={this.props.uri} downloadStarted={!!this.state.fileInfo} />
|
|
||||||
: null}
|
|
||||||
{this.state.fileInfo !== null || this.state.fileInfo.isMine
|
{this.state.fileInfo !== null || this.state.fileInfo.isMine
|
||||||
? linkBlock
|
? linkBlock
|
||||||
: null}
|
: null}
|
||||||
|
|
|
@ -30,6 +30,7 @@ let init = function() {
|
||||||
|
|
||||||
function onDaemonReady() {
|
function onDaemonReady() {
|
||||||
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
|
window.sessionStorage.setItem('loaded', 'y'); //once we've made it here once per session, we don't need to show splash again
|
||||||
|
//<AuthOverlay />
|
||||||
ReactDOM.render(<div><App /><SnackBar /></div>, canvas)
|
ReactDOM.render(<div><App /><SnackBar /></div>, canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ var PublishPage = React.createClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// rewards.claimReward(rewards.TYPE_FIRST_PUBLISH)
|
rewards.claimReward(rewards.TYPE_FIRST_PUBLISH)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -27,7 +27,7 @@ var PublishPage = React.createClass({
|
||||||
// Calls API to update displayed list of channels. If a channel name is provided, will select
|
// Calls API to update displayed list of channels. If a channel name is provided, will select
|
||||||
// that channel at the same time (used immediately after creating a channel)
|
// that channel at the same time (used immediately after creating a channel)
|
||||||
lbry.channel_list_mine().then((channels) => {
|
lbry.channel_list_mine().then((channels) => {
|
||||||
// rewards.claimReward(rewards.TYPE_FIRST_CHANNEL)
|
rewards.claimReward(rewards.TYPE_FIRST_CHANNEL)
|
||||||
this.setState({
|
this.setState({
|
||||||
channels: channels,
|
channels: channels,
|
||||||
... channel ? {channel} : {}
|
... channel ? {channel} : {}
|
||||||
|
|
|
@ -62,7 +62,6 @@ let ShowPage = React.createClass({
|
||||||
document.title = this._uri;
|
document.title = this._uri;
|
||||||
|
|
||||||
lbry.resolve({uri: this._uri}).then(({ claim: {txid, nout, has_signature, signature_is_valid, value: {stream: {metadata, source: {contentType}}}}}) => {
|
lbry.resolve({uri: this._uri}).then(({ claim: {txid, nout, has_signature, signature_is_valid, value: {stream: {metadata, source: {contentType}}}}}) => {
|
||||||
console.log({txid, nout, claim: {value: {stream: {metadata, source: {contentType}}}}} );
|
|
||||||
this.setState({
|
this.setState({
|
||||||
outpoint: txid + ':' + nout,
|
outpoint: txid + ':' + nout,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
|
@ -86,17 +85,15 @@ let ShowPage = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
// <div className="card__media" style={{ backgroundImage: "url('" + metadata.thumbnail + "')" }}></div>
|
// <div className="card__media" style={{ backgroundImage: "url('" + metadata.thumbnail + "')" }}></div>
|
||||||
|
|
||||||
const
|
const
|
||||||
metadata = this.state.uriLookupComplete ? this.state.metadata : null,
|
metadata = this.state.uriLookupComplete ? this.state.metadata : null,
|
||||||
title = this.state.uriLookupComplete ? metadata.title : this._uri;
|
title = this.state.uriLookupComplete ? metadata.title : this._uri;
|
||||||
|
|
||||||
console.log(metadata);
|
|
||||||
return (
|
return (
|
||||||
<main className="constrained-page">
|
<main className="constrained-page">
|
||||||
<section className="show-page-media">
|
<section className="show-page-media">
|
||||||
{ this.props.contentType && this.props.contentType.startsWith('video/') ?
|
{ this.state.contentType && this.state.contentType.startsWith('video/') ?
|
||||||
<Video className="video-embedded" uri={this._uri} /> :
|
<Video className="video-embedded" uri={this._uri} metadata={metadata} /> :
|
||||||
<Thumbnail src={metadata.thumbnail} /> }
|
<Thumbnail src={metadata.thumbnail} /> }
|
||||||
</section>
|
</section>
|
||||||
<section className="card">
|
<section className="card">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Icon} from '../component/common.js';
|
import {Icon, Thumbnail} from '../component/common.js';
|
||||||
import {Link} from '../component/link.js';
|
import {Link} from '../component/link.js';
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
import LoadScreen from '../component/load_screen.js'
|
import LoadScreen from '../component/load_screen.js'
|
||||||
|
@ -20,16 +20,41 @@ export let Video = React.createClass({
|
||||||
return {
|
return {
|
||||||
downloadStarted: false,
|
downloadStarted: false,
|
||||||
readyToPlay: false,
|
readyToPlay: false,
|
||||||
loadStatusMessage: "Requesting stream",
|
isPlaying: false,
|
||||||
|
isPurchased: false,
|
||||||
|
loadStatusMessage: "Requesting stream... it may sit here for like 15-20 seconds in a really awkward way... we're working on it",
|
||||||
mimeType: null,
|
mimeType: null,
|
||||||
controlsShown: false,
|
controlsShown: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
start: function() {
|
||||||
|
// lbry.getCostInfo(this.props.uri).then(({cost}) => {
|
||||||
|
// lbry.getBalance((balance) => {
|
||||||
|
// if (cost > balance) {
|
||||||
|
// this.setState({
|
||||||
|
// modal: 'notEnoughCredits',
|
||||||
|
// loading: false,
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// this.startVideo();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// // });
|
||||||
|
// <Modal contentLabel="Not enough credits" isOpen={this.state.modal == 'notEnoughCredits'} onConfirmed={this.closeModal}>
|
||||||
|
// You don't have enough LBRY credits to pay for this stream.
|
||||||
|
// </Modal>
|
||||||
lbry.get({uri: this.props.uri}).then((fileInfo) => {
|
lbry.get({uri: this.props.uri}).then((fileInfo) => {
|
||||||
this._outpoint = fileInfo.outpoint;
|
this._outpoint = fileInfo.outpoint;
|
||||||
this.updateLoadStatus();
|
this.updateLoadStatus();
|
||||||
});
|
});
|
||||||
|
this.setState({
|
||||||
|
isPlaying: true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
componentDidMount: function() {
|
||||||
|
if (this.props.autoplay) {
|
||||||
|
this.start()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
handleMouseMove: function() {
|
handleMouseMove: function() {
|
||||||
if (this._controlsTimeout) {
|
if (this._controlsTimeout) {
|
||||||
|
@ -81,7 +106,6 @@ export let Video = React.createClass({
|
||||||
readyToPlay: true,
|
readyToPlay: true,
|
||||||
mimeType: status.mime_type,
|
mimeType: status.mime_type,
|
||||||
})
|
})
|
||||||
return
|
|
||||||
const mediaFile = {
|
const mediaFile = {
|
||||||
createReadStream: function (opts) {
|
createReadStream: function (opts) {
|
||||||
// Return a readable stream that provides the bytes
|
// Return a readable stream that provides the bytes
|
||||||
|
@ -98,128 +122,16 @@ export let Video = React.createClass({
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div className={this.props.className}>{
|
<div className={"video " + this.props.className + (this.state.isPlaying && this.state.readyToPlay ? " video--active" : " video--hidden")}>{
|
||||||
!this.state.readyToPlay || true ?
|
this.state.isPlaying ?
|
||||||
<span>this is the world's world loading message and we shipped our software with it anyway... seriously it is actually loading... it might take a while though</span> :
|
!this.state.readyToPlay ?
|
||||||
<video className={this.props.className} id="video" ref="video"></video>
|
<span>this is the world's world loading screen and we shipped our software with it anyway... <br/><br/>{this.state.loadStatusMessage}</span> :
|
||||||
|
<video controls id="video" ref="video"></video> :
|
||||||
|
<div className="video__cover">
|
||||||
|
<Thumbnail src={this.props.metadata.thumbnail} />
|
||||||
|
<a className="video__play-button" onClick={this.start}><Icon icon="icon-play" /></a>
|
||||||
|
</div>
|
||||||
}</div>
|
}</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
var WatchPage = React.createClass({
|
|
||||||
_isMounted: false,
|
|
||||||
_controlsHideDelay: 3000, // Note: this needs to be shorter than the built-in delay in Electron, or Electron will hide the controls before us
|
|
||||||
_controlsHideTimeout: null,
|
|
||||||
_outpoint: null,
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
uri: React.PropTypes.string,
|
|
||||||
},
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
downloadStarted: false,
|
|
||||||
readyToPlay: false,
|
|
||||||
loadStatusMessage: "Requesting stream",
|
|
||||||
mimeType: null,
|
|
||||||
controlsShown: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
componentDidMount: function() {
|
|
||||||
lbry.get({uri: this.props.uri}).then((fileInfo) => {
|
|
||||||
this._outpoint = fileInfo.outpoint;
|
|
||||||
this.updateLoadStatus();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleBackClicked: function() {
|
|
||||||
history.back();
|
|
||||||
},
|
|
||||||
handleMouseMove: function() {
|
|
||||||
if (this._controlsTimeout) {
|
|
||||||
clearTimeout(this._controlsTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.state.controlsShown) {
|
|
||||||
this.setState({
|
|
||||||
controlsShown: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this._controlsTimeout = setTimeout(() => {
|
|
||||||
if (!this.isMounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
controlsShown: false,
|
|
||||||
});
|
|
||||||
}, this._controlsHideDelay);
|
|
||||||
},
|
|
||||||
handleMouseLeave: function() {
|
|
||||||
if (this._controlsTimeout) {
|
|
||||||
clearTimeout(this._controlsTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.controlsShown) {
|
|
||||||
this.setState({
|
|
||||||
controlsShown: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateLoadStatus: function() {
|
|
||||||
lbry.file_list({
|
|
||||||
outpoint: this._outpoint,
|
|
||||||
full_status: true,
|
|
||||||
}).then(([status]) => {
|
|
||||||
if (!status || status.written_bytes == 0) {
|
|
||||||
// Download hasn't started yet, so update status message (if available) then try again
|
|
||||||
// TODO: Would be nice to check if we have the MOOV before starting playing
|
|
||||||
if (status) {
|
|
||||||
this.setState({
|
|
||||||
loadStatusMessage: status.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setTimeout(() => { this.updateLoadStatus() }, 250);
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
readyToPlay: true,
|
|
||||||
mimeType: status.mime_type,
|
|
||||||
})
|
|
||||||
const mediaFile = {
|
|
||||||
createReadStream: function (opts) {
|
|
||||||
// Return a readable stream that provides the bytes
|
|
||||||
// between offsets "start" and "end" inclusive
|
|
||||||
console.log('Stream between ' + opts.start + ' and ' + opts.end + '.');
|
|
||||||
return fs.createReadStream(status.download_path, opts)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var elem = this.refs.video;
|
|
||||||
var videostream = VideoStream(mediaFile, elem);
|
|
||||||
elem.play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
!this.state.readyToPlay
|
|
||||||
? <LoadScreen message={'Loading video...'} details={this.state.loadStatusMessage} />
|
|
||||||
: <main className="video full-screen" onMouseMove={this.handleMouseMove} onMouseLeave={this.handleMouseLeave}>
|
|
||||||
<video controls width="100%" height="100%" id="video" ref="video"></video>
|
|
||||||
{this.state.controlsShown
|
|
||||||
? <div className="video__overlay">
|
|
||||||
<div className="video__back">
|
|
||||||
<Link icon="icon-arrow-circle-o-left" className="video__back-link" onClick={this.handleBackClicked}/>
|
|
||||||
<div className="video__back-label">
|
|
||||||
<Icon icon="icon-caret-left" className="video__back-label-arrow" />
|
|
||||||
<div className="video__back-label-content">
|
|
||||||
Back to LBRY
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
: null}
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default WatchPage;
|
|
||||||
|
|
|
@ -1,12 +1,55 @@
|
||||||
video {
|
video {
|
||||||
border: 1px solid red;
|
object-fit: contain;
|
||||||
object-fill: contain;
|
box-sizing: border-box;
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.video {
|
||||||
|
background: #000;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.video-embedded {
|
.video-embedded {
|
||||||
max-width: 100%;
|
$height-embedded: $width-page-constrained * 9 / 16;
|
||||||
height: 0;
|
max-width: $width-page-constrained;
|
||||||
padding-bottom: 63%;
|
max-height: $height-embedded;
|
||||||
video {
|
video {
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
&.video--hidden {
|
||||||
|
height: $height-embedded;
|
||||||
|
}
|
||||||
|
&.video--active {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video__cover {
|
||||||
|
text-align: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
position: relative;
|
||||||
|
&:hover {
|
||||||
|
.video__play-button { @include absolute-center(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.video__play-button {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
display: none;
|
||||||
|
font-size: $spacing-vertical * 3;
|
||||||
|
color: white;
|
||||||
|
z-index: 1;
|
||||||
|
background: $color-black-transparent;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
}
|
}
|
|
@ -3,4 +3,7 @@
|
||||||
.show-page-media {
|
.show-page-media {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: $spacing-vertical;
|
margin-bottom: $spacing-vertical;
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,3 @@
|
||||||
.video {
|
|
||||||
background: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video__overlay {
|
.video__overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
Loading…
Reference in a new issue