Revamp API wrapper code #10
12 changed files with 235 additions and 269 deletions
2
lbryum
2
lbryum
|
@ -1 +1 @@
|
||||||
Subproject commit 07882aa766acf296a494bd641d88397a27bea83a
|
Subproject commit 65703001c7f199492c0aa5ef4dc976c8fde0fad5
|
|
@ -8,9 +8,9 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
*
|
* You can now make API calls directly on the lbry module, e.g. lbry.peer_list()
|
||||||
*
|
* New-style API calls return promises instead of using callbacks
|
||||||
*
|
* Wherever possible, use outpoints for unique IDs instead of names or SD hashes
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
*
|
*
|
||||||
|
|
|
@ -68,7 +68,7 @@ let FileActionsRow = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
streamName: React.PropTypes.string,
|
streamName: React.PropTypes.string,
|
||||||
sdHash: React.PropTypes.string.isRequired,
|
outpoint: React.PropTypes.string.isRequired,
|
||||||
metadata: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.string]),
|
metadata: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.string]),
|
||||||
},
|
},
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -145,11 +145,7 @@ let FileActionsRow = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleRemoveConfirmed: function() {
|
handleRemoveConfirmed: function() {
|
||||||
if (this.props.streamName) {
|
lbry.removeFile(this.props.outpoint, this.state.deleteChecked);
|
||||||
lbry.removeFile(this.props.sdHash, this.props.streamName, this.state.deleteChecked);
|
|
||||||
} else {
|
|
||||||
alert('this file cannot be deleted because lbry is a retarded piece of shit');
|
|
||||||
}
|
|
||||||
this.setState({
|
this.setState({
|
||||||
modal: null,
|
modal: null,
|
||||||
fileInfo: false,
|
fileInfo: false,
|
||||||
|
@ -163,12 +159,12 @@ let FileActionsRow = React.createClass({
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this._isMounted = true;
|
this._isMounted = true;
|
||||||
this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.sdHash, this.onFileInfoUpdate);
|
this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate);
|
||||||
},
|
},
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
this._isMounted = false;
|
this._isMounted = false;
|
||||||
if (this._fileInfoSubscribeId) {
|
if (this._fileInfoSubscribeId) {
|
||||||
lbry.fileInfoUnsubscribe(this.props.sdHash, this._fileInfoSubscribeId);
|
lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
|
@ -238,7 +234,7 @@ export let FileActions = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
streamName: React.PropTypes.string,
|
streamName: React.PropTypes.string,
|
||||||
sdHash: React.PropTypes.string.isRequired,
|
outpoint: React.PropTypes.string.isRequired,
|
||||||
metadata: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.string]),
|
metadata: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.string]),
|
||||||
},
|
},
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -262,7 +258,7 @@ export let FileActions = React.createClass({
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this._isMounted = true;
|
this._isMounted = true;
|
||||||
this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.sdHash, this.onFileInfoUpdate);
|
this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate);
|
||||||
lbry.getStreamAvailability(this.props.streamName, (availability) => {
|
lbry.getStreamAvailability(this.props.streamName, (availability) => {
|
||||||
if (this._isMounted) {
|
if (this._isMounted) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -290,7 +286,7 @@ export let FileActions = React.createClass({
|
||||||
return (<section className="file-actions">
|
return (<section className="file-actions">
|
||||||
{
|
{
|
||||||
fileInfo || this.state.available || this.state.forceShowActions
|
fileInfo || this.state.available || this.state.forceShowActions
|
||||||
? <FileActionsRow sdHash={this.props.sdHash} metadata={this.props.metadata} streamName={this.props.streamName} />
|
? <FileActionsRow outpoint={this.props.outpoint} metadata={this.props.metadata} streamName={this.props.streamName} />
|
||||||
: <div>
|
: <div>
|
||||||
<div className="button-set-item empty">This file is not currently available.</div>
|
<div className="button-set-item empty">This file is not currently available.</div>
|
||||||
<ToolTip label="Why?"
|
<ToolTip label="Why?"
|
||||||
|
|
|
@ -58,7 +58,7 @@ export let FileTileStream = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
metadata: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]),
|
metadata: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]),
|
||||||
sdHash: React.PropTypes.string,
|
outpoint: React.PropTypes.string,
|
||||||
hideOnRemove: React.PropTypes.bool,
|
hideOnRemove: React.PropTypes.bool,
|
||||||
hidePrice: React.PropTypes.bool,
|
hidePrice: React.PropTypes.bool,
|
||||||
obscureNsfw: React.PropTypes.bool
|
obscureNsfw: React.PropTypes.bool
|
||||||
|
@ -79,12 +79,12 @@ export let FileTileStream = React.createClass({
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this._isMounted = true;
|
this._isMounted = true;
|
||||||
if (this.props.hideOnRemove) {
|
if (this.props.hideOnRemove) {
|
||||||
lbry.fileInfoSubscribe(this.props.sdHash, this.onFileInfoUpdate);
|
lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
if (this._fileInfoSubscribeId) {
|
if (this._fileInfoSubscribeId) {
|
||||||
lbry.fileInfoUnsubscribe(this.props.sdHash, this._fileInfoSubscribeId);
|
lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onFileInfoUpdate: function(fileInfo) {
|
onFileInfoUpdate: function(fileInfo) {
|
||||||
|
@ -135,7 +135,7 @@ export let FileTileStream = React.createClass({
|
||||||
</TruncatedText>
|
</TruncatedText>
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
<FileActions streamName={this.props.name} sdHash={this.props.sdHash} metadata={metadata} />
|
<FileActions streamName={this.props.name} outpoint={this.props.outpoint} metadata={metadata} />
|
||||||
<p className="file-tile__description">
|
<p className="file-tile__description">
|
||||||
<TruncatedText lines={3}>
|
<TruncatedText lines={3}>
|
||||||
{isConfirmed
|
{isConfirmed
|
||||||
|
@ -168,7 +168,7 @@ export let FileTile = React.createClass({
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
sdHash: null,
|
outpoint: null,
|
||||||
metadata: null
|
metadata: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -176,12 +176,12 @@ export let FileTile = React.createClass({
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this._isMounted = true;
|
this._isMounted = true;
|
||||||
|
|
||||||
lbry.resolveName(this.props.name, (metadata) => {
|
lbry.claim_show({name: this.props.name}).then(({value, txid, nout}) => {
|
||||||
if (this._isMounted && metadata) {
|
if (this._isMounted && value) {
|
||||||
// In case of a failed lookup, metadata will be null, in which case the component will never display
|
// In case of a failed lookup, metadata will be null, in which case the component will never display
|
||||||
this.setState({
|
this.setState({
|
||||||
sdHash: metadata.sources.lbry_sd_hash,
|
outpoint: txid + ':' + nout,
|
||||||
metadata: metadata,
|
metadata: value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -190,10 +190,10 @@ export let FileTile = React.createClass({
|
||||||
this._isMounted = false;
|
this._isMounted = false;
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
if (!this.state.metadata || !this.state.sdHash) {
|
if (!this.state.metadata || !this.state.outpoint) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <FileTileStream sdHash={this.state.sdHash} metadata={this.state.metadata} {... this.props} />;
|
return <FileTileStream outpoint={this.state.outpoint} metadata={this.state.metadata} {... this.props} />;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
73
ui/js/jsonrpc.js
Normal file
73
ui/js/jsonrpc.js
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
const jsonrpc = {};
|
||||||
|
|
||||||
|
jsonrpc.call = function (connectionString, method, params, callback, errorCallback, connectFailedCallback, timeout) {
|
||||||
|
var xhr = new XMLHttpRequest;
|
||||||
|
if (typeof connectFailedCallback !== 'undefined') {
|
||||||
|
if (timeout) {
|
||||||
|
xhr.timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.addEventListener('error', function (e) {
|
||||||
|
connectFailedCallback(e);
|
||||||
|
});
|
||||||
|
xhr.addEventListener('timeout', function() {
|
||||||
|
connectFailedCallback(new Error('XMLHttpRequest connection timed out'));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
xhr.addEventListener('load', function() {
|
||||||
|
var response = JSON.parse(xhr.responseText);
|
||||||
|
|
||||||
|
if (response.error) {
|
||||||
|
if (errorCallback) {
|
||||||
|
errorCallback(response.error);
|
||||||
|
} else {
|
||||||
|
var errorEvent = new CustomEvent('unhandledError', {
|
||||||
|
detail: {
|
||||||
|
connectionString: connectionString,
|
||||||
|
method: method,
|
||||||
|
params: params,
|
||||||
|
code: response.error.code,
|
||||||
|
message: response.error.message,
|
||||||
|
data: response.error.data
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.dispatchEvent(errorEvent)
|
||||||
|
}
|
||||||
|
} else if (callback) {
|
||||||
|
callback(response.result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (connectFailedCallback) {
|
||||||
|
xhr.addEventListener('error', function (event) {
|
||||||
|
connectFailedCallback(event);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
xhr.addEventListener('error', function (event) {
|
||||||
|
var errorEvent = new CustomEvent('unhandledError', {
|
||||||
|
detail: {
|
||||||
|
connectionString: connectionString,
|
||||||
|
method: method,
|
||||||
|
params: params,
|
||||||
|
code: xhr.status,
|
||||||
|
message: 'Connection to API server failed'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.dispatchEvent(errorEvent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const counter = parseInt(sessionStorage.getItem('JSONRPCCounter') || 0);
|
||||||
|
|
||||||
|
xhr.open('POST', connectionString, true);
|
||||||
|
xhr.send(JSON.stringify({
|
||||||
|
'jsonrpc': '2.0',
|
||||||
|
'method': method,
|
||||||
|
'params': params,
|
||||||
|
'id': counter,
|
||||||
|
}));
|
||||||
|
|
||||||
|
sessionStorage.setItem('JSONRPCCounter', counter + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default jsonrpc;
|
153
ui/js/lbry.js
153
ui/js/lbry.js
|
@ -1,9 +1,10 @@
|
||||||
import lighthouse from './lighthouse.js';
|
import lighthouse from './lighthouse.js';
|
||||||
|
import jsonrpc from './jsonrpc.js';
|
||||||
|
|
||||||
const {remote} = require('electron');
|
const {remote} = require('electron');
|
||||||
const menu = remote.require('./menu/main-menu');
|
const menu = remote.require('./menu/main-menu');
|
||||||
|
|
||||||
var lbry = {
|
let lbry = {
|
||||||
isConnected: false,
|
isConnected: false,
|
||||||
rootPath: '.',
|
rootPath: '.',
|
||||||
daemonConnectionString: 'http://localhost:5279/lbryapi',
|
daemonConnectionString: 'http://localhost:5279/lbryapi',
|
||||||
|
@ -22,74 +23,8 @@ var lbry = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
lbry.jsonrpc_call = function (connectionString, method, params, callback, errorCallback, connectFailedCallback, timeout) {
|
|
||||||
var xhr = new XMLHttpRequest;
|
|
||||||
if (typeof connectFailedCallback !== 'undefined') {
|
|
||||||
if (timeout) {
|
|
||||||
xhr.timeout = timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
xhr.addEventListener('error', function (e) {
|
|
||||||
connectFailedCallback(e);
|
|
||||||
});
|
|
||||||
xhr.addEventListener('timeout', function() {
|
|
||||||
connectFailedCallback(new Error('XMLHttpRequest connection timed out'));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
xhr.addEventListener('load', function() {
|
|
||||||
var response = JSON.parse(xhr.responseText);
|
|
||||||
|
|
||||||
if (response.error) {
|
|
||||||
if (errorCallback) {
|
|
||||||
errorCallback(response.error);
|
|
||||||
} else {
|
|
||||||
var errorEvent = new CustomEvent('unhandledError', {
|
|
||||||
detail: {
|
|
||||||
connectionString: connectionString,
|
|
||||||
method: method,
|
|
||||||
params: params,
|
|
||||||
code: response.error.code,
|
|
||||||
message: response.error.message,
|
|
||||||
data: response.error.data
|
|
||||||
}
|
|
||||||
});
|
|
||||||
document.dispatchEvent(errorEvent)
|
|
||||||
}
|
|
||||||
} else if (callback) {
|
|
||||||
callback(response.result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (connectFailedCallback) {
|
|
||||||
xhr.addEventListener('error', function (event) {
|
|
||||||
connectFailedCallback(event);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
xhr.addEventListener('error', function (event) {
|
|
||||||
var errorEvent = new CustomEvent('unhandledError', {
|
|
||||||
detail: {
|
|
||||||
connectionString: connectionString,
|
|
||||||
method: method,
|
|
||||||
params: params,
|
|
||||||
code: xhr.status,
|
|
||||||
message: 'Connection to API server failed'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
document.dispatchEvent(errorEvent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
xhr.open('POST', connectionString, true);
|
|
||||||
xhr.send(JSON.stringify({
|
|
||||||
'jsonrpc': '2.0',
|
|
||||||
'method': method,
|
|
||||||
'params': params,
|
|
||||||
'id': 0
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.call = function (method, params, callback, errorCallback, connectFailedCallback) {
|
lbry.call = function (method, params, callback, errorCallback, connectFailedCallback) {
|
||||||
lbry.jsonrpc_call(lbry.daemonConnectionString, method, [params], callback, errorCallback, connectFailedCallback);
|
jsonrpc.call(lbry.daemonConnectionString, method, [params], callback, errorCallback, connectFailedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -244,12 +179,10 @@ lbry.getCostInfoForName = function(name, callback, errorCallback) {
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
lighthouse.getSizeForName(name, (size) => {
|
lighthouse.get_size_for_name(name).then((size) => {
|
||||||
getCostWithData(name, size, callback, errorCallback);
|
getCostWithData(name, size, callback, errorCallback);
|
||||||
}, () => {
|
}, () => {
|
||||||
getCostNoData(name, callback, errorCallback);
|
getCostNoData(name, callback, errorCallback);
|
||||||
}, () => {
|
|
||||||
getCostNoData(name, callback, errorCallback);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,26 +209,6 @@ lbry.getFeaturedDiscoverNames = function(callback) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.getFileStatus = function(name, callback, errorCallback) {
|
|
||||||
lbry.call('get_lbry_file', { 'name': name }, callback, errorCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getFilesInfo = function(callback) {
|
|
||||||
lbry.call('get_lbry_files', {}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getFileInfoByName = function(name, callback) {
|
|
||||||
lbry.call('get_lbry_file', {name: name}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getFileInfoBySdHash = function(sdHash, callback) {
|
|
||||||
lbry.call('get_lbry_file', {sd_hash: sdHash}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getFileInfoByFilename = function(filename, callback) {
|
|
||||||
lbry.call('get_lbry_file', {file_name: filename}, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getMyClaims = function(callback) {
|
lbry.getMyClaims = function(callback) {
|
||||||
lbry.call('get_name_claims', {}, callback);
|
lbry.call('get_name_claims', {}, callback);
|
||||||
}
|
}
|
||||||
|
@ -308,14 +221,14 @@ lbry.stopFile = function(name, callback) {
|
||||||
lbry.call('stop_lbry_file', { name: name }, callback);
|
lbry.call('stop_lbry_file', { name: name }, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.removeFile = function(sdHash, name, deleteTargetFile=true, callback) { // Name param is temporary until the API can delete by unique ID (SD hash, claim ID etc.)
|
lbry.removeFile = function(outpoint, deleteTargetFile=true, callback) {
|
||||||
this._removedFiles.push(sdHash);
|
this._removedFiles.push(outpoint);
|
||||||
this._updateSubscribedFileInfo(sdHash);
|
this._updateSubscribedFileInfo(outpoint);
|
||||||
|
|
||||||
lbry.call('delete_lbry_file', {
|
lbry.file_delete({
|
||||||
name: name,
|
outpoint: outpoint,
|
||||||
delete_target_file: deleteTargetFile,
|
delete_target_file: deleteTargetFile,
|
||||||
}, callback);
|
}).then(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.getFileInfoWhenListed = function(name, callback, timeoutCallback, tryNum=0) {
|
lbry.getFileInfoWhenListed = function(name, callback, timeoutCallback, tryNum=0) {
|
||||||
|
@ -329,7 +242,7 @@ lbry.getFileInfoWhenListed = function(name, callback, timeoutCallback, tryNum=0)
|
||||||
|
|
||||||
// Calls callback with file info when it appears in the lbrynet file manager.
|
// Calls callback with file info when it appears in the lbrynet file manager.
|
||||||
// If timeoutCallback is provided, it will be called if the file fails to appear.
|
// If timeoutCallback is provided, it will be called if the file fails to appear.
|
||||||
lbry.getFileStatus(name, (fileInfo) => {
|
lbry.file_list({name: name}).then(([fileInfo]) => {
|
||||||
if (fileInfo) {
|
if (fileInfo) {
|
||||||
callback(fileInfo);
|
callback(fileInfo);
|
||||||
} else {
|
} else {
|
||||||
|
@ -496,7 +409,7 @@ lbry._fileInfoSubscribeIdCounter = 0;
|
||||||
lbry._fileInfoSubscribeCallbacks = {};
|
lbry._fileInfoSubscribeCallbacks = {};
|
||||||
lbry._fileInfoSubscribeInterval = 5000;
|
lbry._fileInfoSubscribeInterval = 5000;
|
||||||
lbry._removedFiles = [];
|
lbry._removedFiles = [];
|
||||||
lbry._claimIdOwnershipCache = {}; // should be claimId!!! But not
|
lbry._claimIdOwnershipCache = {};
|
||||||
|
|
||||||
lbry._updateClaimOwnershipCache = function(claimId) {
|
lbry._updateClaimOwnershipCache = function(claimId) {
|
||||||
lbry.getMyClaims((claimInfos) => {
|
lbry.getMyClaims((claimInfos) => {
|
||||||
|
@ -506,17 +419,17 @@ lbry._updateClaimOwnershipCache = function(claimId) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
lbry._updateSubscribedFileInfo = function(sdHash) {
|
lbry._updateSubscribedFileInfo = function(outpoint) {
|
||||||
const callSubscribedCallbacks = (sdHash, fileInfo) => {
|
const callSubscribedCallbacks = (outpoint, fileInfo) => {
|
||||||
for (let [subscribeId, callback] of Object.entries(this._fileInfoSubscribeCallbacks[sdHash])) {
|
for (let [subscribeId, callback] of Object.entries(this._fileInfoSubscribeCallbacks[outpoint])) {
|
||||||
callback(fileInfo);
|
callback(fileInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lbry._removedFiles.includes(sdHash)) {
|
if (lbry._removedFiles.includes(outpoint)) {
|
||||||
callSubscribedCallbacks(sdHash, false);
|
callSubscribedCallbacks(outpoint, false);
|
||||||
} else {
|
} else {
|
||||||
lbry.getFileInfoBySdHash(sdHash, (fileInfo) => {
|
lbry.file_list({outpoint: outpoint}).then(([fileInfo]) => {
|
||||||
if (fileInfo) {
|
if (fileInfo) {
|
||||||
if (this._claimIdOwnershipCache[fileInfo.claim_id] === undefined) {
|
if (this._claimIdOwnershipCache[fileInfo.claim_id] === undefined) {
|
||||||
this._updateClaimOwnershipCache(fileInfo.claim_id);
|
this._updateClaimOwnershipCache(fileInfo.claim_id);
|
||||||
|
@ -524,26 +437,26 @@ lbry._updateSubscribedFileInfo = function(sdHash) {
|
||||||
fileInfo.isMine = !!this._claimIdOwnershipCache[fileInfo.claim_id];
|
fileInfo.isMine = !!this._claimIdOwnershipCache[fileInfo.claim_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
callSubscribedCallbacks(sdHash, fileInfo);
|
callSubscribedCallbacks(outpoint, fileInfo);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Object.keys(this._fileInfoSubscribeCallbacks[sdHash]).length) {
|
if (Object.keys(this._fileInfoSubscribeCallbacks[outpoint]).length) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this._updateSubscribedFileInfo(sdHash);
|
this._updateSubscribedFileInfo(outpoint);
|
||||||
}, lbry._fileInfoSubscribeInterval);
|
}, lbry._fileInfoSubscribeInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lbry.fileInfoSubscribe = function(sdHash, callback) {
|
lbry.fileInfoSubscribe = function(outpoint, callback) {
|
||||||
if (!lbry._fileInfoSubscribeCallbacks[sdHash])
|
if (!lbry._fileInfoSubscribeCallbacks[outpoint])
|
||||||
{
|
{
|
||||||
lbry._fileInfoSubscribeCallbacks[sdHash] = {};
|
lbry._fileInfoSubscribeCallbacks[outpoint] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscribeId = ++lbry._fileInfoSubscribeIdCounter;
|
const subscribeId = ++lbry._fileInfoSubscribeIdCounter;
|
||||||
lbry._fileInfoSubscribeCallbacks[sdHash][subscribeId] = callback;
|
lbry._fileInfoSubscribeCallbacks[outpoint][subscribeId] = callback;
|
||||||
lbry._updateSubscribedFileInfo(sdHash);
|
lbry._updateSubscribedFileInfo(outpoint);
|
||||||
return subscribeId;
|
return subscribeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,4 +473,18 @@ lbry.showMenuIfNeeded = function() {
|
||||||
sessionStorage.setItem('menuShown', chosenMenu);
|
sessionStorage.setItem('menuShown', chosenMenu);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
lbry = new Proxy(lbry, {
|
||||||
|
get: function(target, name) {
|
||||||
|
if (name in target) {
|
||||||
|
return target[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
return function(params={}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
jsonrpc.call(lbry.daemonConnectionString, name, [params], resolve, reject, reject);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default lbry;
|
export default lbry;
|
||||||
|
|
|
@ -1,67 +1,64 @@
|
||||||
import lbry from './lbry.js';
|
import lbry from './lbry.js';
|
||||||
|
import jsonrpc from './jsonrpc.js';
|
||||||
|
|
||||||
var lighthouse = {
|
const queryTimeout = 5000;
|
||||||
_query_timeout: 5000,
|
const maxQueryTries = 5;
|
||||||
_max_query_tries: 5,
|
const defaultServers = [
|
||||||
|
'http://lighthouse4.lbry.io:50005',
|
||||||
|
'http://lighthouse5.lbry.io:50005',
|
||||||
|
'http://lighthouse6.lbry.io:50005',
|
||||||
|
];
|
||||||
|
const path = '/';
|
||||||
|
|
||||||
servers: [
|
let server = null;
|
||||||
'http://lighthouse4.lbry.io:50005',
|
let connectTryNum = 0;
|
||||||
'http://lighthouse5.lbry.io:50005',
|
|
||||||
'http://lighthouse6.lbry.io:50005',
|
|
||||||
],
|
|
||||||
path: '/',
|
|
||||||
|
|
||||||
call: function(method, params, callback, errorCallback, connectFailedCallback, timeout) {
|
function getServers() {
|
||||||
const handleConnectFailed = function(tryNum=0) {
|
return lbry.getClientSetting('useCustomLighthouseServers')
|
||||||
if (tryNum > lighthouse._max_query_tries) {
|
? lbry.getClientSetting('customLighthouseServers')
|
||||||
if (connectFailedCallback) {
|
: defaultServers;
|
||||||
connectFailedCallback();
|
}
|
||||||
} else {
|
|
||||||
throw new Error(`Could not connect to Lighthouse server. Last server attempted: ${lighthouse.server}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lbry.call(method, params, callback, errorCallback, () => { handleConnectFailed(tryNum + 1) }, timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the Lighthouse server if it hasn't been set yet, or if the current server is not in
|
function call(method, params, callback, errorCallback) {
|
||||||
// the current set of servers (most likely because of a settings change).
|
if (connectTryNum > maxQueryTries) {
|
||||||
if (typeof lighthouse.server === 'undefined' || lighthouse.getServers().indexOf(lighthouse.server) == -1) {
|
if (connectFailedCallback) {
|
||||||
lighthouse.chooseNewServer();
|
connectFailedCallback();
|
||||||
}
|
|
||||||
|
|
||||||
lbry.jsonrpc_call(this.server + this.path, method, params, callback, errorCallback,
|
|
||||||
() => { handleConnectFailed() }, timeout || lighthouse.query_timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
getServers: function() {
|
|
||||||
return lbry.getClientSetting('useCustomLighthouseServers')
|
|
||||||
? lbry.getClientSetting('customLighthouseServers')
|
|
||||||
: lighthouse.servers;
|
|
||||||
},
|
|
||||||
|
|
||||||
chooseNewServer: function() {
|
|
||||||
// Randomly choose a new Lighthouse server and switch to it. If a server is already set, this
|
|
||||||
// will choose a different one (used for switching servers after a failed query).
|
|
||||||
const servers = lighthouse.getServers();
|
|
||||||
let newServerChoices;
|
|
||||||
if (!lighthouse.server) {
|
|
||||||
newServerChoices = servers;
|
|
||||||
} else {
|
} else {
|
||||||
newServerChoices = lighthouse.getServers().slice();
|
throw new Error(`Could not connect to Lighthouse server. Last server attempted: ${server}`);
|
||||||
newServerChoices.splice(newServerChoices.indexOf(lighthouse.server), 1);
|
|
||||||
}
|
}
|
||||||
lighthouse.server = newServerChoices[Math.round(Math.random() * (newServerChoices.length - 1))];
|
|
||||||
},
|
|
||||||
|
|
||||||
search: function(query, callback, errorCallback, connectFailedCallback, timeout) {
|
|
||||||
lighthouse.call('search', [query], callback, errorCallback, connectFailedCallback, timeout);
|
|
||||||
},
|
|
||||||
|
|
||||||
getSizeForName: function(name, callback, errorCallback, connectFailedCallback, timeout) {
|
|
||||||
lighthouse.call('get_size_for_name', [name], callback, errorCallback, connectFailedCallback, timeout);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Lighthouse server if it hasn't been set yet, if the current server is not in current
|
||||||
|
* set of servers (most likely because of a settings change), or we're re-trying after a failed
|
||||||
|
* query.
|
||||||
|
*/
|
||||||
|
if (!server || !getServers().includes(server) || connectTryNum > 0) {
|
||||||
|
// If there's a current server, filter it out so we get a new one
|
||||||
|
const newServerChoices = server ? getServers().filter((s) => s != server) : getServers();
|
||||||
|
server = newServerChoices[Math.round(Math.random() * (newServerChoices.length - 1))];
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonrpc.call(server + path, method, params, (response) => {
|
||||||
|
connectTryNum = 0;
|
||||||
|
callback(response);
|
||||||
|
}, (error) => {
|
||||||
|
connectTryNum = 0;
|
||||||
|
errorCallback(error);
|
||||||
|
}, () => {
|
||||||
|
connectTryNum++;
|
||||||
|
call(method, params, callback, errorCallback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const lighthouse = new Proxy({}, {
|
||||||
|
get: function(target, name) {
|
||||||
|
return function(...params) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
call(name, params, resolve, reject);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default lighthouse;
|
export default lighthouse;
|
||||||
|
|
|
@ -9,9 +9,7 @@ lbry.showMenuIfNeeded();
|
||||||
|
|
||||||
var init = function() {
|
var init = function() {
|
||||||
window.lbry = lbry;
|
window.lbry = lbry;
|
||||||
if (lbry.getClientSetting('debug')) {
|
window.lighthouse = lighthouse;
|
||||||
window.lighthouse = lighthouse;
|
|
||||||
}
|
|
||||||
|
|
||||||
var canvas = document.getElementById('canvas');
|
var canvas = document.getElementById('canvas');
|
||||||
if (window.sessionStorage.getItem('loaded') == 'y') {
|
if (window.sessionStorage.getItem('loaded') == 'y') {
|
||||||
|
|
|
@ -125,7 +125,7 @@ var DiscoverPage = React.createClass({
|
||||||
query: query,
|
query: query,
|
||||||
});
|
});
|
||||||
|
|
||||||
lighthouse.search(query, this.searchCallback);
|
lighthouse.search(query).then(this.searchCallback);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
|
|
|
@ -18,23 +18,15 @@ export let FileListDownloaded = React.createClass({
|
||||||
this._isMounted = true;
|
this._isMounted = true;
|
||||||
document.title = "Downloaded Files";
|
document.title = "Downloaded Files";
|
||||||
|
|
||||||
let publishedFilesSdHashes = [];
|
lbry.claim_list_mine().then((myClaimInfos) => {
|
||||||
lbry.getMyClaims((claimInfos) => {
|
|
||||||
|
|
||||||
if (!this._isMounted) { return; }
|
if (!this._isMounted) { return; }
|
||||||
|
|
||||||
for (let claimInfo of claimInfos) {
|
lbry.file_list().then((fileInfos) => {
|
||||||
let metadata = JSON.parse(claimInfo.value);
|
|
||||||
publishedFilesSdHashes.push(metadata.sources.lbry_sd_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
lbry.getFilesInfo((fileInfos) => {
|
|
||||||
if (!this._isMounted) { return; }
|
if (!this._isMounted) { return; }
|
||||||
|
|
||||||
|
const myClaimOutpoints = myClaimInfos.map(({txid, nOut}) => txid + ':' + nOut);
|
||||||
this.setState({
|
this.setState({
|
||||||
fileInfos: fileInfos.filter(({sd_hash}) => {
|
fileInfos: fileInfos.filter(({outpoint}) => !myClaimOutpoints.includes(outpoint)),
|
||||||
return publishedFilesSdHashes.indexOf(sd_hash) == -1;
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -74,41 +66,17 @@ export let FileListPublished = React.createClass({
|
||||||
this._isMounted = true;
|
this._isMounted = true;
|
||||||
document.title = "Published Files";
|
document.title = "Published Files";
|
||||||
|
|
||||||
lbry.getMyClaims((claimInfos) => {
|
lbry.claim_list_mine().then((claimInfos) => {
|
||||||
if (claimInfos.length == 0) {
|
if (!this._isMounted) { return; }
|
||||||
|
|
||||||
|
lbry.file_list().then((fileInfos) => {
|
||||||
|
if (!this._isMounted) { return; }
|
||||||
|
|
||||||
|
const myClaimOutpoints = claimInfos.map(({txid, nOut}) => txid + ':' + nOut);
|
||||||
this.setState({
|
this.setState({
|
||||||
fileInfos: [],
|
fileInfos: fileInfos.filter(({outpoint}) => myClaimOutpoints.includes(outpoint)),
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Build newFileInfos as a sparse array and drop elements in at the same position they
|
|
||||||
* occur in claimInfos, so the order is preserved even if the API calls inside this loop
|
|
||||||
* return out of order.
|
|
||||||
*/
|
|
||||||
let newFileInfos = Array(claimInfos.length),
|
|
||||||
claimInfoProcessedCount = 0;
|
|
||||||
|
|
||||||
for (let [i, claimInfo] of claimInfos.entries()) {
|
|
||||||
let metadata = JSON.parse(claimInfo.value);
|
|
||||||
lbry.getFileInfoBySdHash(metadata.sources.lbry_sd_hash, (fileInfo) => {
|
|
||||||
claimInfoProcessedCount++;
|
|
||||||
if (fileInfo !== false) {
|
|
||||||
newFileInfos[i] = fileInfo;
|
|
||||||
}
|
|
||||||
if (claimInfoProcessedCount >= claimInfos.length) {
|
|
||||||
/**
|
|
||||||
* newfileInfos may have gaps from claims that don't have associated files in
|
|
||||||
* lbrynet, so filter out any missing elements
|
|
||||||
*/
|
|
||||||
this.setState({
|
|
||||||
fileInfos: newFileInfos.filter(function () {
|
|
||||||
return true
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
render: function () {
|
render: function () {
|
||||||
|
@ -192,15 +160,13 @@ export let FileList = React.createClass({
|
||||||
seenUris = {};
|
seenUris = {};
|
||||||
|
|
||||||
const fileInfosSorted = this._sortFunctions[this.state.sortBy](this.props.fileInfos);
|
const fileInfosSorted = this._sortFunctions[this.state.sortBy](this.props.fileInfos);
|
||||||
for (let fileInfo of fileInfosSorted) {
|
for (let {name, outpoint, metadata} of fileInfosSorted) {
|
||||||
let {lbry_uri, sd_hash, metadata} = fileInfo;
|
if (!metadata || seenUris[name]) {
|
||||||
|
|
||||||
if (!metadata || seenUris[lbry_uri]) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
seenUris[lbry_uri] = true;
|
seenUris[name] = true;
|
||||||
content.push(<FileTileStream key={lbry_uri} name={lbry_uri} hideOnRemove={true} sdHash={sd_hash}
|
content.push(<FileTileStream key={outpoint} outpoint={outpoint} name={name} hideOnRemove={true}
|
||||||
hidePrice={this.props.hidePrices} metadata={metadata} />);
|
hidePrice={this.props.hidePrices} metadata={metadata} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ var FormatItem = React.createClass({
|
||||||
claimInfo: React.PropTypes.object,
|
claimInfo: React.PropTypes.object,
|
||||||
cost: React.PropTypes.number,
|
cost: React.PropTypes.number,
|
||||||
name: React.PropTypes.string,
|
name: React.PropTypes.string,
|
||||||
|
outpoint: React.PropTypes.string,
|
||||||
costIncludesData: React.PropTypes.bool,
|
costIncludesData: React.PropTypes.bool,
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
|
@ -62,7 +63,7 @@ var FormatItem = React.createClass({
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
<FileActions streamName={this.props.name} sdHash={claimInfo.sources.lbry_sd_hash} metadata={claimInfo} />
|
<FileActions streamName={this.props.name} outpoint={this.props.outpoint} metadata={claimInfo} />
|
||||||
<section>
|
<section>
|
||||||
<Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
|
<Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
|
||||||
</section>
|
</section>
|
||||||
|
@ -120,9 +121,10 @@ var DetailPage = React.createClass({
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
document.title = 'lbry://' + this.props.name;
|
document.title = 'lbry://' + this.props.name;
|
||||||
|
|
||||||
lbry.resolveName(this.props.name, (metadata) => {
|
lbry.claim_show({name: this.props.name}, ({name, txid, nout, value}) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
metadata: metadata,
|
outpoint: txid + ':' + nout,
|
||||||
|
metadata: value,
|
||||||
nameLookupComplete: true,
|
nameLookupComplete: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -143,12 +145,13 @@ var DetailPage = React.createClass({
|
||||||
const costIncludesData = this.state.costIncludesData;
|
const costIncludesData = this.state.costIncludesData;
|
||||||
const metadata = this.state.metadata;
|
const metadata = this.state.metadata;
|
||||||
const cost = this.state.cost;
|
const cost = this.state.cost;
|
||||||
|
const outpoint = this.state.outpoint;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<section className="card">
|
<section className="card">
|
||||||
{this.state.nameLookupComplete ? (
|
{this.state.nameLookupComplete ? (
|
||||||
<FormatsSection name={name} claimInfo={metadata} cost={cost} costIncludesData={costIncludesData} />
|
<FormatsSection name={name} outpoint={outpoint} claimInfo={metadata} cost={cost} costIncludesData={costIncludesData} />
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<h2>No content</h2>
|
<h2>No content</h2>
|
||||||
|
|
|
@ -12,6 +12,7 @@ var WatchPage = React.createClass({
|
||||||
_isMounted: false,
|
_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
|
_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,
|
_controlsHideTimeout: null,
|
||||||
|
_outpoint: null,
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
name: React.PropTypes.string,
|
name: React.PropTypes.string,
|
||||||
|
@ -26,8 +27,10 @@ var WatchPage = React.createClass({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
lbry.getStream(this.props.name);
|
lbry.get({name: this.props.name}, (fileInfo) => {
|
||||||
this.updateLoadStatus();
|
this._outpoint = fileInfo.outpoint;
|
||||||
|
this.updateLoadStatus();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
handleBackClicked: function() {
|
handleBackClicked: function() {
|
||||||
history.back();
|
history.back();
|
||||||
|
@ -64,10 +67,13 @@ var WatchPage = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateLoadStatus: function() {
|
updateLoadStatus: function() {
|
||||||
lbry.getFileStatus(this.props.name, (status) => {
|
api.file_list({
|
||||||
|
outpoint: this._outpoint,
|
||||||
|
full_status: true,
|
||||||
|
}, ([status]) => {
|
||||||
if (!status || !['running', 'stopped'].includes(status.code) || status.written_bytes == 0) {
|
if (!status || !['running', 'stopped'].includes(status.code) || status.written_bytes == 0) {
|
||||||
// Download hasn't started yet, so update status message (if available) then try again
|
// 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
|
// TODO: Would be nice to check if we have the MOOV before starting playing
|
||||||
if (status) {
|
if (status) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loadStatusMessage: status.message
|
loadStatusMessage: status.message
|
||||||
|
@ -79,17 +85,17 @@ var WatchPage = React.createClass({
|
||||||
readyToPlay: true,
|
readyToPlay: true,
|
||||||
mimeType: status.mime_type,
|
mimeType: status.mime_type,
|
||||||
})
|
})
|
||||||
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
|
||||||
// between offsets "start" and "end" inclusive
|
// between offsets "start" and "end" inclusive
|
||||||
console.log('Stream between ' + opts.start + ' and ' + opts.end + '.');
|
console.log('Stream between ' + opts.start + ' and ' + opts.end + '.');
|
||||||
return fs.createReadStream(status.download_path, opts)
|
return fs.createReadStream(status.download_path, opts)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
var elem = this.refs.video;
|
var elem = this.refs.video;
|
||||||
var videostream = VideoStream(mediaFile, elem);
|
var videostream = VideoStream(mediaFile, elem);
|
||||||
elem.play();
|
elem.play();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue