// @flow import 'proxy-polyfill'; const CHECK_DAEMON_STARTED_TRY_NUMBER = 200; const Lbry = { isConnected: false, daemonConnectionString: 'http://localhost:5279', pendingPublishTimeout: 20 * 60 * 1000, }; function checkAndParse(response) { if (response.status >= 200 && response.status < 300) { return response.json(); } return response.json().then(json => { let error; if (json.error) { const errorMessage = typeof json.error === 'object' ? json.error.message : json.error; error = new Error(errorMessage); } else { error = new Error('Protocol error with unknown response signature'); } return Promise.reject(error); }); } function apiCall(method: string, params: ?{}, resolve: Function, reject: Function) { const counter = new Date().getTime(); const options = { method: 'POST', body: JSON.stringify({ jsonrpc: '2.0', method, params, id: counter, }), }; return fetch(Lbry.daemonConnectionString, options) .then(checkAndParse) .then(response => { const error = response.error || (response.result && response.result.error); if (error) { return reject(error); } return resolve(response.result); }) .catch(reject); } const daemonCallWithResult = (name, params = {}) => new Promise((resolve, reject) => { apiCall( name, params, result => { resolve(result); }, reject ); }); // blobs Lbry.blob_delete = (params = {}) => daemonCallWithResult('blob_delete', params); Lbry.blob_list = (params = {}) => daemonCallWithResult('blob_list', params); // core Lbry.status = (params = {}) => daemonCallWithResult('status', params); Lbry.version = () => daemonCallWithResult('version', {}); Lbry.file_delete = (params = {}) => daemonCallWithResult('file_delete', params); Lbry.file_set_status = (params = {}) => daemonCallWithResult('file_set_status', params); Lbry.stop = () => daemonCallWithResult('stop', {}); // claims Lbry.claim_list_by_channel = (params = {}) => daemonCallWithResult('claim_list_by_channel', params); // wallet Lbry.account_balance = (params = {}) => daemonCallWithResult('account_balance', params); Lbry.account_decrypt = () => daemonCallWithResult('account_decrypt', {}); Lbry.account_encrypt = (params = {}) => daemonCallWithResult('account_encrypt', params); Lbry.account_list = (params = {}) => daemonCallWithResult('account_list', params); Lbry.address_is_mine = (params = {}) => daemonCallWithResult('address_is_mine', params); Lbry.wallet_lock = () => daemonCallWithResult('wallet_lock', {}); Lbry.address_unused = (params = {}) => daemonCallWithResult('address_unused', params); Lbry.wallet_send = (params = {}) => daemonCallWithResult('wallet_send', params); Lbry.account_unlock = (params = {}) => daemonCallWithResult('account_unlock', params); Lbry.address_unused = () => daemonCallWithResult('address_unused', {}); Lbry.claim_tip = (params = {}) => daemonCallWithResult('claim_tip', params); // transactions Lbry.transaction_list = (params = {}) => daemonCallWithResult('transaction_list', params); Lbry.utxo_release = (params = {}) => daemonCallWithResult('utxo_release', params); Lbry.connectPromise = null; Lbry.connect = () => { if (Lbry.connectPromise === null) { Lbry.connectPromise = new Promise((resolve, reject) => { let tryNum = 0; // Check every half second to see if the daemon is accepting connections function checkDaemonStarted() { tryNum += 1; Lbry.status() .then(resolve) .catch(() => { if (tryNum <= CHECK_DAEMON_STARTED_TRY_NUMBER) { setTimeout(checkDaemonStarted, tryNum < 50 ? 400 : 1000); } else { reject(new Error('Unable to connect to LBRY')); } }); } checkDaemonStarted(); }); } return Lbry.connectPromise; }; Lbry.getMediaType = (contentType, extname) => { if (extname) { const formats = [ [/^(mp4|m4v|webm|flv|f4v|ogv)$/i, 'video'], [/^(mp3|m4a|aac|wav|flac|ogg|opus)$/i, 'audio'], [/^(html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|epub|org)$/i, 'document'], [/^(stl|obj|fbx|gcode)$/i, '3D-file'], ]; const res = formats.reduce((ret, testpair) => { switch (testpair[0].test(ret)) { case true: return testpair[1]; default: return ret; } }, extname); return res === extname ? 'unknown' : res; } else if (contentType) { return /^[^/]+/.exec(contentType)[0]; } return 'unknown'; }; /** * Wrappers for API methods to simulate missing or future behavior. Unlike the old-style stubs, * these are designed to be transparent wrappers around the corresponding API methods. */ /** * Returns results from the file_list API method, plus dummy entries for pending publishes. * (If a real publish with the same name is found, the pending publish will be ignored and removed.) */ Lbry.file_list = (params = {}) => new Promise((resolve, reject) => { apiCall( 'file_list', params, fileInfos => { resolve(fileInfos); }, reject ); }); Lbry.claim_list_mine = (params = {}) => new Promise((resolve, reject) => { apiCall( 'claim_list_mine', params, claims => { resolve(claims); }, reject ); }); Lbry.get = (params = {}) => new Promise((resolve, reject) => { apiCall( 'get', params, streamInfo => { resolve(streamInfo); }, reject ); }); Lbry.resolve = (params = {}) => new Promise((resolve, reject) => { apiCall( 'resolve', params, data => { resolve(data || {}); }, reject ); }); Lbry.publish = (params = {}) => new Promise((resolve, reject) => { if (Lbry.overrides.publish) { Lbry.overrides.publish(params).then(resolve, reject); } else { apiCall('publish', params, resolve, reject); } }); // Allow overriding Lbry methods Lbry.overrides = {}; Lbry.setOverride = (methodName, newMethod) => { Lbry.overrides[methodName] = newMethod; }; // Allow overriding daemon connection string (e.g. to `/api/proxy` for lbryweb) Lbry.setDaemonConnectionString = value => { Lbry.daemonConnectionString = value; }; const lbryProxy = new Proxy(Lbry, { get(target, name) { if (name in target) { return target[name]; } return (params = {}) => new Promise((resolve, reject) => { apiCall(name, params, resolve, reject); }); }, }); export default lbryProxy;