2016-12-09 07:54:18 +01:00
import lighthouse from './lighthouse.js' ;
2016-03-14 23:05:24 +01:00
var lbry = {
isConnected : false ,
2016-03-15 17:05:11 +01:00
rootPath : '.' ,
2016-08-18 08:57:19 +02:00
daemonConnectionString : 'http://localhost:5279/lbryapi' ,
2016-12-06 20:58:51 +01:00
webUiUri : 'http://localhost:5279' ,
2016-03-14 23:05:24 +01:00
colors : {
primary : '#155B4A'
2016-08-22 21:07:41 +02:00
} ,
2016-08-22 21:19:04 +02:00
defaultClientSettings : {
showNsfw : false ,
2017-01-20 14:54:33 +01:00
showUnavailable : true ,
2016-12-29 10:17:10 +01:00
debug : false ,
useCustomLighthouseServers : false ,
customLighthouseServers : [ ] ,
2016-08-22 21:19:04 +02:00
}
2016-03-14 23:05:24 +01:00
} ;
2016-11-11 15:22:53 +01:00
lbry . jsonrpc _call = function ( connectionString , method , params , callback , errorCallback , connectFailedCallback , timeout ) {
2016-03-21 07:31:25 +01:00
var xhr = new XMLHttpRequest ;
2016-11-11 15:22:53 +01:00
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' ) ) ;
} )
}
2016-03-21 07:31:25 +01:00
xhr . addEventListener ( 'load' , function ( ) {
2016-03-30 11:35:42 +02:00
var response = JSON . parse ( xhr . responseText ) ;
2016-03-21 07:31:25 +01:00
2016-03-30 11:35:42 +02:00
if ( response . error ) {
2016-04-12 11:00:27 +02:00
if ( errorCallback ) {
2016-08-08 02:57:12 +02:00
errorCallback ( response . error ) ;
2016-10-21 11:21:07 +02:00
} else {
2016-10-22 11:22:44 +02:00
var errorEvent = new CustomEvent ( 'unhandledError' , {
2016-10-21 11:21:07 +02:00
detail : {
connectionString : connectionString ,
method : method ,
params : params ,
2016-10-26 08:52:53 +02:00
code : response . error . code ,
message : response . error . message ,
data : response . error . data
2016-10-21 11:21:07 +02:00
}
} ) ;
document . dispatchEvent ( errorEvent )
2016-04-12 11:00:27 +02:00
}
} else if ( callback ) {
2016-08-08 02:57:12 +02:00
callback ( response . result ) ;
2016-04-03 14:45:33 +02:00
}
2016-03-21 07:31:25 +01:00
} ) ;
2016-03-25 02:20:58 +01:00
2016-10-22 11:22:44 +02:00
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 ) ;
} ) ;
}
2016-08-18 08:57:19 +02:00
xhr . open ( 'POST' , connectionString , true ) ;
2016-03-21 07:31:25 +01:00
xhr . send ( JSON . stringify ( {
'jsonrpc' : '2.0' ,
'method' : method ,
2016-08-18 08:57:19 +02:00
'params' : params ,
2016-03-21 07:31:25 +01:00
'id' : 0
2016-08-08 02:57:12 +02:00
} ) ) ;
2016-03-16 22:56:51 +01:00
}
2016-08-18 08:57:19 +02:00
lbry . call = function ( method , params , callback , errorCallback , connectFailedCallback ) {
lbry . jsonrpc _call ( lbry . daemonConnectionString , method , [ params ] , callback , errorCallback , connectFailedCallback ) ;
}
2016-03-14 23:05:24 +01:00
//core
lbry . connect = function ( callback )
{
2017-01-18 16:29:47 +01:00
// Check every half second to see if the daemon is accepting connections
// Once this returns True, can call getDaemonStatus to see where
// we are in the startup process
function checkDaemonStarted ( tryNum = 0 ) {
lbry . isDaemonAcceptingConnections ( function ( runningStatus ) {
2016-03-25 02:20:58 +01:00
if ( runningStatus ) {
2016-03-24 08:08:39 +01:00
lbry . isConnected = true ;
callback ( true ) ;
} else {
2016-04-12 11:07:17 +02:00
if ( tryNum <= 600 ) { // Move # of tries into constant or config option
2016-03-25 02:20:58 +01:00
setTimeout ( function ( ) {
2017-01-18 16:29:47 +01:00
checkDaemonStarted ( tryNum + 1 ) ;
2016-03-25 02:20:58 +01:00
} , 500 ) ;
} else {
2016-03-24 08:08:39 +01:00
callback ( false ) ;
}
}
} ) ;
2016-03-25 02:20:58 +01:00
}
2017-01-18 16:29:47 +01:00
checkDaemonStarted ( ) ;
2016-03-14 23:05:24 +01:00
}
2017-01-18 16:29:47 +01:00
lbry . isDaemonAcceptingConnections = function ( callback ) {
// Returns true/false whether the daemon is at a point it will start returning status
lbry . call ( 'status' , { } , ( ) => callback ( true ) , null , ( ) => callback ( false ) )
2016-03-25 02:20:58 +01:00
} ;
2016-04-14 06:40:31 +02:00
lbry . getDaemonStatus = function ( callback ) {
lbry . call ( 'daemon_status' , { } , callback ) ;
} ;
2016-07-04 17:29:58 +02:00
lbry . checkFirstRun = function ( callback ) {
lbry . call ( 'is_first_run' , { } , callback ) ;
}
lbry . getNewAddress = function ( callback ) {
lbry . call ( 'get_new_address' , { } , callback ) ;
}
2017-01-13 03:03:34 +01:00
lbry . checkAddressIsMine = function ( address , callback ) {
2016-09-02 07:34:30 +02:00
lbry . call ( 'address_is_mine' , { address : address } , callback ) ;
}
2016-08-22 20:13:41 +02:00
lbry . getDaemonSettings = function ( callback ) {
2016-04-03 14:45:33 +02:00
lbry . call ( 'get_settings' , { } , callback ) ;
2016-08-22 20:13:41 +02:00
}
2016-04-03 14:45:33 +02:00
2016-08-22 20:13:41 +02:00
lbry . setDaemonSettings = function ( settings , callback ) {
2016-04-03 14:45:33 +02:00
lbry . call ( 'set_settings' , settings , callback ) ;
2016-08-22 20:13:41 +02:00
}
lbry . setDaemonSetting = function ( setting , value , callback ) {
var setSettingsArgs = { } ;
setSettingsArgs [ setting ] = value ;
lbry . call ( 'set_settings' , setSettingsArgs , callback )
}
2016-04-03 14:45:33 +02:00
2016-08-22 21:07:41 +02:00
2016-03-16 22:56:51 +01:00
lbry . getBalance = function ( callback )
2016-03-14 23:05:24 +01:00
{
2016-03-16 22:56:51 +01:00
lbry . call ( "get_balance" , { } , callback ) ;
2016-03-14 23:05:24 +01:00
}
2016-07-22 17:33:05 +02:00
lbry . sendToAddress = function ( amount , address , callback , errorCallback )
2016-07-19 00:40:15 +02:00
{
2016-07-22 17:33:05 +02:00
lbry . call ( "send_amount_to_address" , { "amount" : amount , "address" : address } , callback , errorCallback ) ;
2016-07-19 00:40:15 +02:00
}
2016-07-15 14:01:51 +02:00
lbry . resolveName = function ( name , callback ) {
lbry . call ( 'resolve_name' , { 'name' : name } , callback , ( ) => {
// For now, assume any error means the name was not resolved
callback ( null ) ;
} ) ;
2016-05-16 15:17:53 +02:00
}
2016-04-21 11:47:57 +02:00
lbry . getStream = function ( name , callback ) {
lbry . call ( 'get' , { 'name' : name } , callback ) ;
} ;
2016-07-29 00:02:24 +02:00
lbry . getClaimInfo = function ( name , callback ) {
lbry . call ( 'get_claim_info' , { name : name } , callback ) ;
}
2016-09-16 19:45:33 +02:00
lbry . getMyClaim = function ( name , callback ) {
lbry . call ( 'get_my_claim' , { name : name } , callback ) ;
}
2016-12-06 19:46:02 +01:00
lbry . getKeyFee = function ( name , callback ) {
2016-07-30 09:45:31 +02:00
lbry . call ( 'get_est_cost' , { name : name } , callback ) ;
}
2016-12-06 19:46:02 +01:00
lbry . getTotalCost = function ( name , size , callback ) {
lbry . call ( 'get_est_cost' , {
name : name ,
size : size ,
} , callback ) ;
}
2016-12-07 22:29:04 +01:00
lbry . getPeersForBlobHash = function ( blobHash , callback ) {
lbry . call ( 'get_peers_for_hash' , { blob _hash : blobHash } , callback )
}
2016-12-09 07:54:18 +01:00
lbry . getCostInfoForName = function ( name , callback ) {
/ * *
* Takes a LBRY name ; will first try and calculate a total cost using
* Lighthouse . If Lighthouse can ' t be reached , it just retrives the
* key fee .
*
* Returns an object with members :
* - cost : Number ; the calculated cost of the name
* - includes _data : Boolean ; indicates whether or not the data fee info
* from Lighthouse is included .
* /
2016-12-29 10:10:08 +01:00
function getCostWithData ( name , size , callback ) {
2016-12-09 07:54:18 +01:00
lbry . getTotalCost ( name , size , ( cost ) => {
callback ( {
cost : cost ,
includesData : true ,
} ) ;
} ) ;
}
function getCostNoData ( name , callback ) {
lbry . getKeyFee ( name , ( cost ) => {
callback ( {
cost : cost ,
includesData : false ,
} ) ;
} ) ;
}
lighthouse . getSizeForName ( name , ( size ) => {
getCostWithData ( name , size , callback ) ;
} , ( ) => {
getCostNoData ( name , callback ) ;
} , ( ) => {
getCostNoData ( name , callback ) ;
} ) ;
}
2016-05-05 10:08:37 +02:00
lbry . getFileStatus = function ( name , callback ) {
lbry . call ( 'get_lbry_file' , { 'name' : name } , callback ) ;
}
2016-05-10 12:31:57 +02:00
lbry . getFilesInfo = function ( callback ) {
lbry . call ( 'get_lbry_files' , { } , callback ) ;
}
2016-11-09 12:29:42 +01:00
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 ) ;
}
2016-11-09 12:27:07 +01:00
lbry . getMyClaims = function ( callback ) {
lbry . call ( 'get_name_claims' , { } , callback ) ;
}
2016-05-14 14:23:27 +02:00
lbry . startFile = function ( name , callback ) {
lbry . call ( 'start_lbry_file' , { name : name } , callback ) ;
}
lbry . stopFile = function ( name , callback ) {
lbry . call ( 'stop_lbry_file' , { name : name } , callback ) ;
}
2017-01-17 10:04:29 +01:00
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 ) ;
2016-05-30 14:54:08 +02:00
lbry . call ( 'delete_lbry_file' , {
name : name ,
delete _target _file : deleteTargetFile ,
} , callback ) ;
}
2017-01-17 11:31:58 +01:00
lbry . openFile = function ( sdHash , callback ) {
lbry . call ( 'open' , { sd _hash : sdHash } , callback ) ;
2017-01-11 08:35:14 +01:00
}
2017-01-17 11:31:58 +01:00
lbry . revealFile = function ( sdHash , callback ) {
lbry . call ( 'reveal' , { sd _hash : sdHash } , callback ) ;
2016-05-14 14:23:27 +02:00
}
2016-09-16 17:17:12 +02:00
lbry . getFileInfoWhenListed = function ( name , callback , timeoutCallback , tryNum = 0 ) {
// Calls callback with file info when it appears in the list of files returned by lbry.getFilesInfo().
// If timeoutCallback is provided, it will be called if the file fails to appear.
2017-01-17 11:06:39 +01:00
lbry . getFilesInfo ( function ( fileInfos ) {
for ( var fileInfo of fileInfos ) {
2016-09-16 17:17:12 +02:00
if ( fileInfo . lbry _uri == name ) {
callback ( fileInfo ) ;
return ;
}
}
if ( tryNum <= 200 ) {
setTimeout ( function ( ) { lbry . getFileInfoWhenListed ( name , callback , timeoutCallback , tryNum + 1 ) } , 250 ) ;
} else if ( timeoutCallback ) {
timeoutCallback ( ) ;
}
} ) ;
}
lbry . publish = function ( params , fileListedCallback , publishedCallback , errorCallback ) {
// Publishes a file.
// The optional fileListedCallback is called when the file becomes available in
// lbry.getFilesInfo() during the publish process.
2016-07-15 14:01:51 +02:00
// Use ES6 named arguments instead of directly passing param dict?
2017-01-18 04:37:24 +01:00
lbry . call ( 'publish' , params , publishedCallback , errorCallback ) ;
2016-09-16 17:17:12 +02:00
if ( fileListedCallback ) {
lbry . getFileInfoWhenListed ( params . name , function ( fileInfo ) {
fileListedCallback ( fileInfo ) ;
} ) ;
}
2016-07-15 14:01:51 +02:00
}
2016-04-20 10:46:36 +02:00
lbry . getVersionInfo = function ( callback ) {
lbry . call ( 'version' , { } , callback ) ;
} ;
2016-04-12 12:30:25 +02:00
lbry . checkNewVersionAvailable = function ( callback ) {
2016-05-30 15:43:34 +02:00
lbry . call ( 'version' , { } , function ( versionInfo ) {
2016-07-22 06:20:16 +02:00
var ver = versionInfo . lbrynet _version . split ( '.' ) ;
2016-05-30 15:43:34 +02:00
2016-07-22 06:20:16 +02:00
var maj = parseInt ( ver [ 0 ] ) ,
min = parseInt ( ver [ 1 ] ) ,
patch = parseInt ( ver [ 2 ] ) ;
var remoteVer = versionInfo . remote _lbrynet . split ( '.' ) ;
var remoteMaj = parseInt ( remoteVer [ 0 ] ) ,
remoteMin = parseInt ( remoteVer [ 1 ] ) ,
remotePatch = parseInt ( remoteVer [ 2 ] ) ;
2016-05-30 15:43:34 +02:00
2016-06-30 07:27:31 +02:00
if ( maj < remoteMaj ) {
var newVersionAvailable = true ;
} else if ( maj == remoteMaj ) {
if ( min < remoteMin ) {
var newVersionAvailable = true ;
} else if ( min == remoteMin ) {
var newVersionAvailable = ( patch < remotePatch ) ;
} else {
var newVersionAvailable = false ;
}
} else {
var newVersionAvailable = false ;
}
2016-05-30 15:43:34 +02:00
callback ( newVersionAvailable ) ;
2016-04-12 12:30:25 +02:00
} , function ( err ) {
if ( err . fault == 'NoSuchFunction' ) {
2016-05-30 15:43:34 +02:00
// Really old daemon that can't report a version
2016-04-12 12:30:25 +02:00
callback ( true ) ;
}
} ) ;
}
2016-08-22 21:07:41 +02:00
lbry . getClientSettings = function ( ) {
var outSettings = { } ;
for ( let setting of Object . keys ( lbry . defaultClientSettings ) ) {
var localStorageVal = localStorage . getItem ( 'setting_' + setting ) ;
outSettings [ setting ] = ( localStorageVal === null ? lbry . defaultClientSettings [ setting ] : JSON . parse ( localStorageVal ) ) ;
}
return outSettings ;
}
lbry . getClientSetting = function ( setting ) {
var localStorageVal = localStorage . getItem ( 'setting_' + setting ) ;
return ( localStorageVal === null ? lbry . defaultClientSettings [ setting ] : JSON . parse ( localStorageVal ) ) ;
}
lbry . setClientSettings = function ( settings ) {
for ( let setting of Object . keys ( settings ) ) {
lbry . setClientSetting ( setting , settings [ setting ] ) ;
}
}
lbry . setClientSetting = function ( setting , value ) {
return localStorage . setItem ( 'setting_' + setting , JSON . stringify ( value ) ) ;
}
2016-11-30 06:23:45 +01:00
lbry . getSessionInfo = function ( callback ) {
lbry . call ( 'get_lbry_session_info' , { } , callback ) ;
}
2016-08-22 21:07:41 +02:00
2016-04-23 14:19:15 +02:00
lbry . reportBug = function ( message , callback ) {
2017-01-02 21:43:34 +01:00
lbry . call ( 'report_bug' , {
2016-04-23 14:19:15 +02:00
message : message
} , callback ) ;
}
2016-03-14 23:05:24 +01:00
//utilities
lbry . formatCredits = function ( amount , precision )
{
2016-08-19 09:24:12 +02:00
return amount . toFixed ( precision || 1 ) . replace ( /\.?0+$/ , '' ) ;
2016-03-14 23:05:24 +01:00
}
2016-09-01 09:28:07 +02:00
lbry . formatName = function ( name ) {
2016-11-15 08:31:28 +01:00
// Converts LBRY name to standard format (all lower case, no special characters, spaces replaced by dashes)
2016-11-16 20:56:04 +01:00
name = name . replace ( '/\s+/g' , '-' ) ;
2016-11-15 08:31:28 +01:00
name = name . toLowerCase ( ) . replace ( /[^a-z0-9\-]/g , '' ) ;
return name ;
2016-09-01 09:28:07 +02:00
}
2016-11-18 10:00:03 +01:00
lbry . nameIsValid = function ( name , checkCase = true ) {
const regexp = new RegExp ( '^[a-z0-9-]+$' , checkCase ? '' : 'i' ) ;
return regexp . test ( name ) ;
}
2016-03-14 23:05:24 +01:00
lbry . loadJs = function ( src , type , onload )
{
var lbryScriptTag = document . getElementById ( 'lbry' ) ,
newScriptTag = document . createElement ( 'script' ) ,
type = type || 'text/javascript' ;
2016-08-08 02:57:12 +02:00
2016-03-14 23:05:24 +01:00
newScriptTag . src = src ;
newScriptTag . type = type ;
if ( onload )
{
2016-11-22 21:28:16 +01:00
newScriptTag . onload = onload ;
2016-03-14 23:05:24 +01:00
}
lbryScriptTag . parentNode . insertBefore ( newScriptTag , lbryScriptTag ) ;
}
lbry . imagePath = function ( file )
2016-08-08 02:57:12 +02:00
{
return lbry . rootPath + '/img/' + file ;
2016-03-14 23:05:24 +01:00
}
2016-04-20 10:46:36 +02:00
2016-09-02 10:48:01 +02:00
lbry . getMediaType = function ( contentType , fileName ) {
if ( contentType ) {
return /^[^/]+/ . exec ( contentType ) ;
2016-09-08 10:35:41 +02:00
} else if ( fileName ) {
var dotIndex = fileName . lastIndexOf ( '.' ) ;
2016-09-02 10:48:01 +02:00
if ( dotIndex == - 1 ) {
return 'unknown' ;
}
2016-09-08 10:35:41 +02:00
var ext = fileName . substr ( dotIndex + 1 ) ;
2016-09-02 10:48:01 +02:00
if ( /^mp4|mov|m4v|flv|f4v$/i . test ( ext ) ) {
return 'video' ;
} else if ( /^mp3|m4a|aac|wav|flac|ogg$/i . test ( ext ) ) {
return 'audio' ;
} else if ( /^html|htm|pdf|odf|doc|docx|md|markdown|txt$/i . test ( ext ) ) {
return 'document' ;
} else {
return 'unknown' ;
}
2016-09-08 10:35:41 +02:00
} else {
return 'unknown' ;
2016-05-16 10:16:40 +02:00
}
}
2016-04-20 10:46:36 +02:00
lbry . stop = function ( callback ) {
lbry . call ( 'stop' , { } , callback ) ;
} ;
2016-05-16 10:16:40 +02:00
2017-01-13 03:03:34 +01:00
lbry . fileInfo = { } ;
lbry . _fileInfoSubscribeIdCounter = 0 ;
lbry . _fileInfoSubscribeCallbacks = { } ;
lbry . _fileInfoSubscribeInterval = 5000 ;
2017-01-17 10:04:29 +01:00
lbry . _removedFiles = [ ] ;
2017-01-13 03:03:34 +01:00
lbry . _claimIdOwnershipCache = { } ; // should be claimId!!! But not
lbry . _updateClaimOwnershipCache = function ( claimId ) {
2017-01-17 11:06:39 +01:00
lbry . getMyClaims ( ( claimInfos ) => {
lbry . _claimIdOwnershipCache [ claimId ] = ! ! claimInfos . reduce ( function ( match , claimInfo ) {
2017-01-13 03:03:34 +01:00
return match || claimInfo . claim _id == claimId ;
} ) ;
} ) ;
} ;
2017-01-13 23:18:37 +01:00
lbry . _updateSubscribedFileInfo = function ( sdHash ) {
2017-01-17 10:04:29 +01:00
const callSubscribedCallbacks = ( sdHash , fileInfo ) => {
2017-01-17 11:11:21 +01:00
for ( let [ subscribeId , callback ] of Object . entries ( this . _fileInfoSubscribeCallbacks [ sdHash ] ) ) {
callback ( fileInfo ) ;
}
2017-01-17 10:04:29 +01:00
}
if ( lbry . _removedFiles . includes ( sdHash ) ) {
callSubscribedCallbacks ( sdHash , false ) ;
} else {
lbry . getFileInfoBySdHash ( sdHash , ( fileInfo ) => {
if ( fileInfo ) {
if ( this . _claimIdOwnershipCache [ fileInfo . claim _id ] === undefined ) {
this . _updateClaimOwnershipCache ( fileInfo . claim _id ) ;
}
fileInfo . isMine = ! ! this . _claimIdOwnershipCache [ fileInfo . claim _id ] ;
2017-01-13 03:03:34 +01:00
}
2017-01-17 10:04:29 +01:00
callSubscribedCallbacks ( sdHash , fileInfo ) ;
2017-01-13 03:03:34 +01:00
} ) ;
2017-01-17 10:04:29 +01:00
}
2017-01-13 23:18:37 +01:00
if ( Object . keys ( this . _fileInfoSubscribeCallbacks [ sdHash ] ) . length ) {
2017-01-13 05:05:43 +01:00
setTimeout ( ( ) => {
2017-01-17 10:04:29 +01:00
this . _updateSubscribedFileInfo ( sdHash ) ;
2017-01-13 05:05:43 +01:00
} , lbry . _fileInfoSubscribeInterval ) ;
}
2017-01-13 03:03:34 +01:00
}
2017-01-13 23:18:37 +01:00
lbry . fileInfoSubscribe = function ( sdHash , callback ) {
if ( ! lbry . _fileInfoSubscribeCallbacks [ sdHash ] )
2017-01-13 03:03:34 +01:00
{
2017-01-13 23:18:37 +01:00
lbry . _fileInfoSubscribeCallbacks [ sdHash ] = { } ;
2017-01-13 03:03:34 +01:00
}
const subscribeId = ++ lbry . _fileInfoSubscribeIdCounter ;
2017-01-13 23:18:37 +01:00
lbry . _fileInfoSubscribeCallbacks [ sdHash ] [ subscribeId ] = callback ;
lbry . _updateSubscribedFileInfo ( sdHash ) ;
2017-01-13 03:03:34 +01:00
return subscribeId ;
}
lbry . fileInfoUnsubscribe = function ( name , subscribeId ) {
2017-01-13 05:05:43 +01:00
delete lbry . _fileInfoSubscribeCallbacks [ name ] [ subscribeId ] ;
2017-01-13 03:03:34 +01:00
}
2016-05-16 10:16:40 +02:00
2017-01-17 11:06:39 +01:00
export default lbry ;