diff --git a/lbryum b/lbryum index 07882aa76..65703001c 160000 --- a/lbryum +++ b/lbryum @@ -1 +1 @@ -Subproject commit 07882aa766acf296a494bd641d88397a27bea83a +Subproject commit 65703001c7f199492c0aa5ef4dc976c8fde0fad5 diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index 6a03869e7..0226d5f88 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -8,9 +8,9 @@ Web UI version numbers should always match the corresponding version of LBRY App ## [Unreleased] ### 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 * diff --git a/ui/js/component/file-actions.js b/ui/js/component/file-actions.js index c5987dd4b..9a1d42af3 100644 --- a/ui/js/component/file-actions.js +++ b/ui/js/component/file-actions.js @@ -68,7 +68,7 @@ let FileActionsRow = React.createClass({ propTypes: { streamName: React.PropTypes.string, - sdHash: React.PropTypes.string.isRequired, + outpoint: React.PropTypes.string.isRequired, metadata: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.string]), }, getInitialState: function() { @@ -145,11 +145,7 @@ let FileActionsRow = React.createClass({ }); }, handleRemoveConfirmed: function() { - if (this.props.streamName) { - 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'); - } + lbry.removeFile(this.props.outpoint, this.state.deleteChecked); this.setState({ modal: null, fileInfo: false, @@ -163,12 +159,12 @@ let FileActionsRow = React.createClass({ }, componentDidMount: function() { this._isMounted = true; - this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.sdHash, this.onFileInfoUpdate); + this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate); }, componentWillUnmount: function() { this._isMounted = false; if (this._fileInfoSubscribeId) { - lbry.fileInfoUnsubscribe(this.props.sdHash, this._fileInfoSubscribeId); + lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId); } }, render: function() { @@ -238,7 +234,7 @@ export let FileActions = React.createClass({ propTypes: { streamName: React.PropTypes.string, - sdHash: React.PropTypes.string.isRequired, + outpoint: React.PropTypes.string.isRequired, metadata: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.string]), }, getInitialState: function() { @@ -262,7 +258,7 @@ export let FileActions = React.createClass({ }, componentDidMount: function() { 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) => { if (this._isMounted) { this.setState({ @@ -290,7 +286,7 @@ export let FileActions = React.createClass({ return (
{ fileInfo || this.state.available || this.state.forceShowActions - ? + ? :
This file is not currently available.
- +

{isConfirmed @@ -168,7 +168,7 @@ export let FileTile = React.createClass({ getInitialState: function() { return { - sdHash: null, + outpoint: null, metadata: null } }, @@ -176,12 +176,12 @@ export let FileTile = React.createClass({ componentDidMount: function() { this._isMounted = true; - lbry.resolveName(this.props.name, (metadata) => { - if (this._isMounted && metadata) { + lbry.claim_show({name: this.props.name}).then(({value, txid, nout}) => { + if (this._isMounted && value) { // In case of a failed lookup, metadata will be null, in which case the component will never display this.setState({ - sdHash: metadata.sources.lbry_sd_hash, - metadata: metadata, + outpoint: txid + ':' + nout, + metadata: value, }); } }); @@ -190,10 +190,10 @@ export let FileTile = React.createClass({ this._isMounted = false; }, render: function() { - if (!this.state.metadata || !this.state.sdHash) { + if (!this.state.metadata || !this.state.outpoint) { return null; } - return ; + return ; } }); diff --git a/ui/js/jsonrpc.js b/ui/js/jsonrpc.js new file mode 100644 index 000000000..654931bb5 --- /dev/null +++ b/ui/js/jsonrpc.js @@ -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; diff --git a/ui/js/lbry.js b/ui/js/lbry.js index b2729cf6a..7d598a833 100644 --- a/ui/js/lbry.js +++ b/ui/js/lbry.js @@ -1,9 +1,10 @@ import lighthouse from './lighthouse.js'; +import jsonrpc from './jsonrpc.js'; const {remote} = require('electron'); const menu = remote.require('./menu/main-menu'); -var lbry = { +let lbry = { isConnected: false, rootPath: '.', 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.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); } - lighthouse.getSizeForName(name, (size) => { + lighthouse.get_size_for_name(name).then((size) => { getCostWithData(name, size, 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.call('get_name_claims', {}, callback); } @@ -308,14 +221,14 @@ lbry.stopFile = function(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.) - this._removedFiles.push(sdHash); - this._updateSubscribedFileInfo(sdHash); +lbry.removeFile = function(outpoint, deleteTargetFile=true, callback) { + this._removedFiles.push(outpoint); + this._updateSubscribedFileInfo(outpoint); - lbry.call('delete_lbry_file', { - name: name, + lbry.file_delete({ + outpoint: outpoint, delete_target_file: deleteTargetFile, - }, callback); + }).then(callback); } 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. // 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) { callback(fileInfo); } else { @@ -496,7 +409,7 @@ lbry._fileInfoSubscribeIdCounter = 0; lbry._fileInfoSubscribeCallbacks = {}; lbry._fileInfoSubscribeInterval = 5000; lbry._removedFiles = []; -lbry._claimIdOwnershipCache = {}; // should be claimId!!! But not +lbry._claimIdOwnershipCache = {}; lbry._updateClaimOwnershipCache = function(claimId) { lbry.getMyClaims((claimInfos) => { @@ -506,17 +419,17 @@ lbry._updateClaimOwnershipCache = function(claimId) { }); }; -lbry._updateSubscribedFileInfo = function(sdHash) { - const callSubscribedCallbacks = (sdHash, fileInfo) => { - for (let [subscribeId, callback] of Object.entries(this._fileInfoSubscribeCallbacks[sdHash])) { +lbry._updateSubscribedFileInfo = function(outpoint) { + const callSubscribedCallbacks = (outpoint, fileInfo) => { + for (let [subscribeId, callback] of Object.entries(this._fileInfoSubscribeCallbacks[outpoint])) { callback(fileInfo); } } - if (lbry._removedFiles.includes(sdHash)) { - callSubscribedCallbacks(sdHash, false); + if (lbry._removedFiles.includes(outpoint)) { + callSubscribedCallbacks(outpoint, false); } else { - lbry.getFileInfoBySdHash(sdHash, (fileInfo) => { + lbry.file_list({outpoint: outpoint}).then(([fileInfo]) => { if (fileInfo) { if (this._claimIdOwnershipCache[fileInfo.claim_id] === undefined) { this._updateClaimOwnershipCache(fileInfo.claim_id); @@ -524,26 +437,26 @@ lbry._updateSubscribedFileInfo = function(sdHash) { 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(() => { - this._updateSubscribedFileInfo(sdHash); + this._updateSubscribedFileInfo(outpoint); }, lbry._fileInfoSubscribeInterval); } } -lbry.fileInfoSubscribe = function(sdHash, callback) { - if (!lbry._fileInfoSubscribeCallbacks[sdHash]) +lbry.fileInfoSubscribe = function(outpoint, callback) { + if (!lbry._fileInfoSubscribeCallbacks[outpoint]) { - lbry._fileInfoSubscribeCallbacks[sdHash] = {}; + lbry._fileInfoSubscribeCallbacks[outpoint] = {}; } const subscribeId = ++lbry._fileInfoSubscribeIdCounter; - lbry._fileInfoSubscribeCallbacks[sdHash][subscribeId] = callback; - lbry._updateSubscribedFileInfo(sdHash); + lbry._fileInfoSubscribeCallbacks[outpoint][subscribeId] = callback; + lbry._updateSubscribedFileInfo(outpoint); return subscribeId; } @@ -560,4 +473,18 @@ lbry.showMenuIfNeeded = function() { 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; diff --git a/ui/js/lighthouse.js b/ui/js/lighthouse.js index 6d349a3a8..a8b60f0fa 100644 --- a/ui/js/lighthouse.js +++ b/ui/js/lighthouse.js @@ -1,67 +1,64 @@ import lbry from './lbry.js'; +import jsonrpc from './jsonrpc.js'; -var lighthouse = { - _query_timeout: 5000, - _max_query_tries: 5, +const queryTimeout = 5000; +const maxQueryTries = 5; +const defaultServers = [ + 'http://lighthouse4.lbry.io:50005', + 'http://lighthouse5.lbry.io:50005', + 'http://lighthouse6.lbry.io:50005', +]; +const path = '/'; - servers: [ - 'http://lighthouse4.lbry.io:50005', - 'http://lighthouse5.lbry.io:50005', - 'http://lighthouse6.lbry.io:50005', - ], - path: '/', +let server = null; +let connectTryNum = 0; - call: function(method, params, callback, errorCallback, connectFailedCallback, timeout) { - const handleConnectFailed = function(tryNum=0) { - if (tryNum > lighthouse._max_query_tries) { - if (connectFailedCallback) { - 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); - } - } +function getServers() { + return lbry.getClientSetting('useCustomLighthouseServers') + ? lbry.getClientSetting('customLighthouseServers') + : defaultServers; +} - // Set the Lighthouse server if it hasn't been set yet, or if the current server is not in - // the current set of servers (most likely because of a settings change). - if (typeof lighthouse.server === 'undefined' || lighthouse.getServers().indexOf(lighthouse.server) == -1) { - lighthouse.chooseNewServer(); - } - - 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; +function call(method, params, callback, errorCallback) { + if (connectTryNum > maxQueryTries) { + if (connectFailedCallback) { + connectFailedCallback(); } else { - newServerChoices = lighthouse.getServers().slice(); - newServerChoices.splice(newServerChoices.indexOf(lighthouse.server), 1); + throw new Error(`Could not connect to Lighthouse server. Last server attempted: ${server}`); } - 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; diff --git a/ui/js/main.js b/ui/js/main.js index 6c8eb3634..eb5863fb1 100644 --- a/ui/js/main.js +++ b/ui/js/main.js @@ -9,9 +9,7 @@ lbry.showMenuIfNeeded(); var init = function() { window.lbry = lbry; - if (lbry.getClientSetting('debug')) { - window.lighthouse = lighthouse; - } + window.lighthouse = lighthouse; var canvas = document.getElementById('canvas'); if (window.sessionStorage.getItem('loaded') == 'y') { diff --git a/ui/js/page/discover.js b/ui/js/page/discover.js index e7a722c47..c01cbe75b 100644 --- a/ui/js/page/discover.js +++ b/ui/js/page/discover.js @@ -125,7 +125,7 @@ var DiscoverPage = React.createClass({ query: query, }); - lighthouse.search(query, this.searchCallback); + lighthouse.search(query).then(this.searchCallback); }, componentWillMount: function() { diff --git a/ui/js/page/file-list.js b/ui/js/page/file-list.js index bff3b30ae..085449303 100644 --- a/ui/js/page/file-list.js +++ b/ui/js/page/file-list.js @@ -18,23 +18,15 @@ export let FileListDownloaded = React.createClass({ this._isMounted = true; document.title = "Downloaded Files"; - let publishedFilesSdHashes = []; - lbry.getMyClaims((claimInfos) => { - + lbry.claim_list_mine().then((myClaimInfos) => { if (!this._isMounted) { return; } - for (let claimInfo of claimInfos) { - let metadata = JSON.parse(claimInfo.value); - publishedFilesSdHashes.push(metadata.sources.lbry_sd_hash); - } - - lbry.getFilesInfo((fileInfos) => { + lbry.file_list().then((fileInfos) => { if (!this._isMounted) { return; } + const myClaimOutpoints = myClaimInfos.map(({txid, nOut}) => txid + ':' + nOut); this.setState({ - fileInfos: fileInfos.filter(({sd_hash}) => { - return publishedFilesSdHashes.indexOf(sd_hash) == -1; - }) + fileInfos: fileInfos.filter(({outpoint}) => !myClaimOutpoints.includes(outpoint)), }); }); }); @@ -74,41 +66,17 @@ export let FileListPublished = React.createClass({ this._isMounted = true; document.title = "Published Files"; - lbry.getMyClaims((claimInfos) => { - if (claimInfos.length == 0) { + lbry.claim_list_mine().then((claimInfos) => { + if (!this._isMounted) { return; } + + lbry.file_list().then((fileInfos) => { + if (!this._isMounted) { return; } + + const myClaimOutpoints = claimInfos.map(({txid, nOut}) => txid + ':' + nOut); 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 () { @@ -192,15 +160,13 @@ export let FileList = React.createClass({ seenUris = {}; const fileInfosSorted = this._sortFunctions[this.state.sortBy](this.props.fileInfos); - for (let fileInfo of fileInfosSorted) { - let {lbry_uri, sd_hash, metadata} = fileInfo; - - if (!metadata || seenUris[lbry_uri]) { + for (let {name, outpoint, metadata} of fileInfosSorted) { + if (!metadata || seenUris[name]) { continue; } - seenUris[lbry_uri] = true; - content.push(); } diff --git a/ui/js/page/show.js b/ui/js/page/show.js index d469e426d..a2bb1b5f1 100644 --- a/ui/js/page/show.js +++ b/ui/js/page/show.js @@ -19,6 +19,7 @@ var FormatItem = React.createClass({ claimInfo: React.PropTypes.object, cost: React.PropTypes.number, name: React.PropTypes.string, + outpoint: React.PropTypes.string, costIncludesData: React.PropTypes.bool, }, render: function() { @@ -62,7 +63,7 @@ var FormatItem = React.createClass({

- +
@@ -120,9 +121,10 @@ var DetailPage = React.createClass({ componentWillMount: function() { 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({ - metadata: metadata, + outpoint: txid + ':' + nout, + metadata: value, nameLookupComplete: true, }); }); @@ -143,12 +145,13 @@ var DetailPage = React.createClass({ const costIncludesData = this.state.costIncludesData; const metadata = this.state.metadata; const cost = this.state.cost; + const outpoint = this.state.outpoint; return (
{this.state.nameLookupComplete ? ( - + ) : (

No content

diff --git a/ui/js/page/watch.js b/ui/js/page/watch.js index fa2f70ef0..7d67ea56a 100644 --- a/ui/js/page/watch.js +++ b/ui/js/page/watch.js @@ -12,6 +12,7 @@ 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: { name: React.PropTypes.string, @@ -26,8 +27,10 @@ var WatchPage = React.createClass({ }; }, componentDidMount: function() { - lbry.getStream(this.props.name); - this.updateLoadStatus(); + lbry.get({name: this.props.name}, (fileInfo) => { + this._outpoint = fileInfo.outpoint; + this.updateLoadStatus(); + }); }, handleBackClicked: function() { history.back(); @@ -64,10 +67,13 @@ var WatchPage = React.createClass({ } }, 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) { // 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) { this.setState({ loadStatusMessage: status.message @@ -79,17 +85,17 @@ var WatchPage = React.createClass({ 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(); + 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(); } }); },