LBRYNet daemon

Adds a daemon for lbrynet. Currently commands are limited the following:

download_name(name)
resolve_name(name)
get_downloads
stop

This allows other programs to easily interact with lbrynet, such as
LBRYURIHandler. LBRYURIHandler can be built with py2app, the resulting
plist file must be edited in the same way the committed plist file has
been edited. When built and installed to the /Applications folder
lbry:// domain names will download and open the corresponding file so
long as the daemon is running.
This commit is contained in:
Jack 2015-12-06 17:32:17 -05:00
parent f15b67d386
commit 441d3c1220
8 changed files with 585 additions and 3 deletions

View file

@ -113,6 +113,142 @@ class AutoAddStream(object):
return self.downloader
class GetStream(object):
def __init__(self, console, sd_identifier, session, wallet, lbry_file_manager, max_key_fee,
console_on=True, pay_key=True):
self.finished_deferred = defer.Deferred(None)
self.console_on = console_on
self.pay_key = pay_key
self.console = console
self.wallet = wallet
self.resolved_name = None
self.description = None
self.key_fee = None
self.key_fee_address = None
self.name = None
self.session = session
self.payment_rate_manager = PaymentRateManager(self.session.base_payment_rate_manager)
self.loading_metadata_deferred = defer.Deferred()
self.lbry_file_manager = lbry_file_manager
self.sd_identifier = sd_identifier
self.metadata = None
self.loading_failed = False
self.resolved_name = None
self.description = None
self.key_fee = None
self.key_fee_address = None
self.stream_hash = None
self.max_key_fee = max_key_fee
self.stream_info = None
self.stream_info_manager = None
self.downloader = None
def start(self, stream_info):
self.stream_info = stream_info
try:
if 'stream_hash' in json.loads(self.stream_info['value']):
self.resolved_name = self.stream_info.get('name', None)
self.description = json.loads(self.stream_info['value']).get('description', None)
try:
if 'key_fee' in json.loads(self.stream_info['value']):
self.key_fee = float(json.loads(self.stream_info['value'])['key_fee'])
except ValueError:
self.key_fee = None
self.key_fee_address = json.loads(self.stream_info['value']).get('key_fee_address', None)
self.stream_hash = json.loads(self.stream_info['value'])['stream_hash']
else:
raise InvalidStreamInfoError(self.stream_info)
except:
if 'stream_hash' in self.stream_info.keys():
self.description = self.stream_info['description']
if 'key_fee' in self.stream_info.keys():
self.key_fee = float(self.stream_info['key_fee'])
self.key_fee_address = self.stream_info['key_fee_address']
else:
self.key_fee = None
self.stream_hash = self.stream_info['stream_hash']
else:
raise InvalidStreamInfoError(self.stream_info)
# if self.key_fee < self.max_key_fee:
# if self.pay_key:
# if self.console_on:
# self.console.sendLine("Key fee (" + str(self.key_fee) + ") above limit of " + str(
# self.max_key_fee) + ", didn't download lbry://" + str(self.resolved_name))
# return self.finished_deferred.callback(None)
# else:
# pass
# else:
# pass
def _get_downloader_for_return():
return defer.succeed(self.downloader)
self.loading_metadata_deferred = defer.Deferred(None)
self.loading_metadata_deferred.addCallback(
lambda _: download_sd_blob(self.session, self.stream_hash, self.payment_rate_manager))
self.loading_metadata_deferred.addCallback(self.sd_identifier.get_metadata_for_sd_blob)
self.loading_metadata_deferred.addCallback(self._handle_metadata)
self.loading_metadata_deferred.addErrback(self._handle_load_canceled)
self.loading_metadata_deferred.addErrback(self._handle_load_failed)
self.loading_metadata_deferred.addCallback(lambda _: self._make_downloader())
self.loading_metadata_deferred.addCallback(lambda _: self.downloader.start())
self.loading_metadata_deferred.addErrback(self._handle_download_error)
self.loading_metadata_deferred.addCallback(lambda _: _get_downloader_for_return())
self.loading_metadata_deferred.callback(None)
return defer.succeed(None)
def _pay_key_fee(self):
if self.key_fee is not None and self.key_fee_address is not None:
reserved_points = self.wallet.reserve_points(self.key_fee_address, self.key_fee)
if reserved_points is None:
return defer.fail(InsufficientFundsError())
return self.wallet.send_points_to_address(reserved_points, self.key_fee)
if self.console_on:
self.console.sendLine("Sent key fee" + str(self.key_fee_address) + " | " + str(self.key_fee))
return defer.succeed(None)
def _handle_load_canceled(self, err):
err.trap(defer.CancelledError)
self.finished_deferred.callback(None)
def _handle_load_failed(self, err):
self.loading_failed = True
if self.console_on:
self.console.sendLine("handle load failed: " + str(err.getTraceback()))
log.error("An exception occurred attempting to load the stream descriptor: %s", err.getTraceback())
print 'Load Failed: ', err.getTraceback()
self.finished_deferred.callback(None)
def _handle_metadata(self, metadata):
self.metadata = metadata
self.factory = self.metadata.factories[0]
return defer.succeed(None) #self._start_download()
def _handle_download_error(self, err):
if self.console_on:
if err.check(InsufficientFundsError):
self.console.sendLine("Download stopped due to insufficient funds.")
else:
self.console.sendLine(
"Autoaddstream: An unexpected error has caused the download to stop: %s" % err.getTraceback())
else:
print "Autoaddstream: An unexpected error has caused the download to stop: ", err.getTraceback()
def _make_downloader(self):
def _set_downloader(downloader):
self.downloader = downloader
print os.path.join(self.downloader.download_directory, self.downloader.file_name)
return self.downloader
self.downloader = self.factory.make_downloader(self.metadata, [0.5, True], self.payment_rate_manager)
self.downloader.addCallback(_set_downloader)
return defer.succeed(self.downloader)
class AutoFetcher(object):
def __init__(self, session, lbry_file_manager, lbry_file_metadata_manager, wallet, sd_identifier, autofetcher_conf):
self.autofetcher_conf = autofetcher_conf

View file

@ -31,7 +31,10 @@ class LBRYFileManager(object):
self.sd_identifier = sd_identifier
self.lbry_files = []
self.sql_db = None
self.download_directory = os.getcwd()
if os.name == 'posix':
self.download_directory = os.path.join(os.path.expanduser("~"), 'Downloads')
else:
self.download_directory = os.getcwd()
def setup(self):
d = self._open_db()

View file

@ -0,0 +1,282 @@
import os
from lbrynet.lbryfile.StreamDescriptor import LBRYFileStreamType
from lbrynet.lbryfile.client.LBRYFileDownloader import LBRYFileSaverFactory, LBRYFileOpenerFactory
from lbrynet.lbryfile.client.LBRYFileOptions import add_lbry_file_to_sd_identifier
from lbrynet.core.client.AutoDownloader import GetStream
from lbrynet.core.utils import generate_id
from lbrynet.lbrynet_console.LBRYSettings import LBRYSettings
from lbrynet.conf import MIN_BLOB_DATA_PAYMENT_RATE
from lbrynet.core.StreamDescriptor import StreamDescriptorIdentifier
from lbrynet.core.Session import LBRYSession
from lbrynet.core.PTCWallet import PTCWallet
from lbrynet.core.LBRYcrdWallet import LBRYcrdWallet
from lbrynet.lbryfilemanager.LBRYFileManager import LBRYFileManager
from lbrynet.lbryfile.LBRYFileMetadataManager import DBLBRYFileMetadataManager, TempLBRYFileMetadataManager
from twisted.web import xmlrpc, server
from twisted.internet import defer, threads, reactor
from datetime import datetime
class LBRYDaemon(xmlrpc.XMLRPC):
"""
LBRYnet daemon
"""
def setup(self):
def _set_vars():
self.run_server = True
self.session = None
self.known_dht_nodes = [('104.236.42.182', 4000)]
self.db_dir = os.path.join(os.path.expanduser("~"), ".lbrynet")
self.blobfile_dir = os.path.join(self.db_dir, "blobfiles")
self.peer_port = 3333
self.dht_node_port = 4444
self.first_run = False
self.current_db_revision = 1
if os.name == "nt":
from lbrynet.winhelpers.knownpaths import get_path, FOLDERID, UserHandle
self.download_directory = get_path(FOLDERID.Downloads, UserHandle.current)
self.wallet_dir = os.path.join(get_path(FOLDERID.RoamingAppData, UserHandle.current), "lbrycrd")
else:
self.download_directory = os.path.join(os.path.expanduser("~"), 'Downloads')
self.wallet_dir = os.path.join(os.path.expanduser("~"), ".lbrycrd")
self.wallet_conf = os.path.join(self.wallet_dir, "lbrycrd.conf")
self.wallet_user = None
self.wallet_password = None
self.sd_identifier = StreamDescriptorIdentifier()
self.stream_info_manager = TempLBRYFileMetadataManager()
self.wallet_rpc_port = 8332
self.download_deferreds = []
self.stream_frames = []
self.default_blob_data_payment_rate = MIN_BLOB_DATA_PAYMENT_RATE
self.use_upnp = True
self.start_lbrycrdd = True
if os.name == "nt":
self.lbrycrdd_path = "lbrycrdd.exe"
else:
self.lbrycrdd_path = "./lbrycrdd"
self.delete_blobs_on_remove = True
self.blob_request_payment_rate_manager = None
self.lbry_file_metadata_manager = None
self.lbry_file_manager = None
self.settings = LBRYSettings(self.db_dir)
self.wallet_type = "lbrycrd"
self.lbrycrd_dir = os.path.join(os.path.expanduser("~"), ".lbrycrd")
self.lbrycrd_conf = os.path.join(self.lbrycrd_dir, "lbrycrd.conf")
self.rpc_conn = None
return defer.succeed(None)
d = defer.Deferred()
d.addCallback(lambda _: _set_vars())
d.addCallback(lambda _: self._get_settings())
d.addCallback(lambda _: self.get_lbrycrdd_path())
d.addCallback(lambda _: self._get_session())
d.addCallback(lambda _: add_lbry_file_to_sd_identifier(self.sd_identifier))
d.addCallback(lambda _: self._setup_stream_identifier())
d.addCallback(lambda _: self._setup_lbry_file_manager())
d.addCallback(lambda _: self._setup_lbry_file_opener())
d.callback(None)
return defer.succeed(None)
def _get_settings(self):
d = self.settings.start()
d.addCallback(lambda _: self.settings.get_lbryid())
d.addCallback(self.set_lbryid)
d.addCallback(lambda _: self.get_lbrycrdd_path())
return d
def set_lbryid(self, lbryid):
if lbryid is None:
return self._make_lbryid()
else:
self.lbryid = lbryid
def _make_lbryid(self):
self.lbryid = generate_id()
d = self.settings.save_lbryid(self.lbryid)
return d
def _setup_lbry_file_manager(self):
self.lbry_file_metadata_manager = DBLBRYFileMetadataManager(self.db_dir)
d = self.lbry_file_metadata_manager.setup()
def set_lbry_file_manager():
self.lbry_file_manager = LBRYFileManager(self.session, self.lbry_file_metadata_manager, self.sd_identifier)
return self.lbry_file_manager.setup()
d.addCallback(lambda _: set_lbry_file_manager())
return d
def _get_session(self):
def get_default_data_rate():
d = self.settings.get_default_data_payment_rate()
d.addCallback(lambda rate: {"default_data_payment_rate":
rate if rate is not None else MIN_BLOB_DATA_PAYMENT_RATE})
return d
def get_wallet():
if self.wallet_type == "lbrycrd":
lbrycrdd_path = None
if self.start_lbrycrdd is True:
lbrycrdd_path = self.lbrycrdd_path
if not lbrycrdd_path:
lbrycrdd_path = self.default_lbrycrdd_path
d = defer.succeed(LBRYcrdWallet(self.db_dir, wallet_dir=self.lbrycrd_dir, wallet_conf=self.lbrycrd_conf,
lbrycrdd_path=lbrycrdd_path))
else:
d = defer.succeed(PTCWallet(self.db_dir))
d.addCallback(lambda wallet: {"wallet": wallet})
return d
d1 = get_default_data_rate()
d2 = get_wallet()
def combine_results(results):
r = {}
for success, result in results:
if success is True:
r.update(result)
return r
def create_session(results):
self.session = LBRYSession(results['default_data_payment_rate'], db_dir=self.db_dir, lbryid=self.lbryid,
blob_dir=self.blobfile_dir, dht_node_port=self.dht_node_port,
known_dht_nodes=self.known_dht_nodes, peer_port=self.peer_port,
use_upnp=self.use_upnp, wallet=results['wallet'])
self.rpc_conn = self.session.wallet.get_rpc_conn_x()
dl = defer.DeferredList([d1, d2], fireOnOneErrback=True)
dl.addCallback(combine_results)
dl.addCallback(create_session)
dl.addCallback(lambda _: self.session.setup())
return dl
def get_lbrycrdd_path(self):
def get_lbrycrdd_path_conf_file():
lbrycrdd_path_conf_path = os.path.join(os.path.expanduser("~"), ".lbrycrddpath.conf")
if not os.path.exists(lbrycrdd_path_conf_path):
return ""
lbrycrdd_path_conf = open(lbrycrdd_path_conf_path)
lines = lbrycrdd_path_conf.readlines()
return lines
d = threads.deferToThread(get_lbrycrdd_path_conf_file)
def load_lbrycrdd_path(conf):
for line in conf:
if len(line.strip()) and line.strip()[0] != "#":
self.lbrycrdd_path = line.strip()
print self.lbrycrdd_path
d.addCallback(load_lbrycrdd_path)
return d
def _setup_stream_identifier(self):
file_saver_factory = LBRYFileSaverFactory(self.session.peer_finder, self.session.rate_limiter,
self.session.blob_manager, self.stream_info_manager,
self.session.wallet, self.download_directory)
self.sd_identifier.add_stream_downloader_factory(LBRYFileStreamType, file_saver_factory)
file_opener_factory = LBRYFileOpenerFactory(self.session.peer_finder, self.session.rate_limiter,
self.session.blob_manager, self.stream_info_manager,
self.session.wallet)
self.sd_identifier.add_stream_downloader_factory(LBRYFileStreamType, file_opener_factory)
return defer.succeed(None)
def _setup_lbry_file_manager(self):
self.lbry_file_metadata_manager = DBLBRYFileMetadataManager(self.db_dir)
d = self.lbry_file_metadata_manager.setup()
def set_lbry_file_manager():
self.lbry_file_manager = LBRYFileManager(self.session, self.lbry_file_metadata_manager, self.sd_identifier)
return self.lbry_file_manager.setup()
d.addCallback(lambda _: set_lbry_file_manager())
return d
def _setup_lbry_file_opener(self):
downloader_factory = LBRYFileOpenerFactory(self.session.peer_finder, self.session.rate_limiter,
self.session.blob_manager, self.stream_info_manager,
self.session.wallet)
self.sd_identifier.add_stream_downloader_factory(LBRYFileStreamType, downloader_factory)
return defer.succeed(True)
def xmlrpc_getbalance(self):
"""
Get LBC balance
"""
return str(self.session.wallet.wallet_balance)
def xmlrpc_stop(self):
"""
Stop the reactor
"""
reactor.stop()
return defer.succeed('Stopping')
def xmlrpc_resolve_name(self, name):
"""
Resolve stream info from a LBRY uri
"""
def _disp(info):
print '[' + str(datetime.now()) + ']' + ' Resolved info: ' + str(info)
return info
d = defer.Deferred()
d.addCallback(lambda _: self.session.wallet.get_stream_info_for_name(name))
d.addCallback(_disp)
d.callback(None)
return d
def xmlrpc_get_downloads(self):
"""
Get downloads
"""
downloads = [{'stream_hash': stream.stream_hash,
'path': os.path.join(stream.downloader.download_directory, stream.downloader.file_name)}
for stream in self.download_deferreds]
return downloads
def xmlrpc_download_name(self, name):
"""
Download stream from a LBRY uri
"""
def _disp():
try:
stream = self.download_deferreds[-1]
print '[' + str(datetime.now()) + ']' + ' Downloading: ' + str(stream.stream_hash)
return defer.succeed(None)
except:
pass
stream = GetStream(None, self.sd_identifier, self.session, self.session.wallet,
self.lbry_file_manager, 25.0, console_on=False, pay_key=True)
self.download_deferreds.append(stream)
d = defer.Deferred()
d.addCallback(lambda _: self.session.wallet.get_stream_info_for_name(name))
d.addCallback(lambda stream_info: stream.start(stream_info))
d.addCallback(lambda _: _disp())
d.callback(None)
msg = {'ts': datetime.now(),'name': name}
return defer.succeed(str(msg))
def main():
daemon = LBRYDaemon()
daemon.setup()
reactor.listenTCP(7080, server.Site(daemon))
reactor.run()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,22 @@
import os
import webbrowser
import xmlrpclib, sys
def main(args):
if len(args) == 0:
args.append('lbry://economicsman')
daemon = xmlrpclib.ServerProxy('http://localhost:7080/')
if len(args) > 1:
print 'Too many args', args
else:
resolved = daemon.resolve_name(str(args[0])[7:])
daemon.download_name(str(args[0])[7:])
path = [h for h in daemon.get_downloads() if h['stream_hash'] == resolved['stream_hash']][0]['path']
webbrowser.open('file://' + path)
if __name__ == "__main__":
main(sys.argv[1:])

View file

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>LBRYURIHandler</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeOSTypes</key>
<array>
<string>****</string>
<string>fold</string>
<string>disk</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>LBRYURIHandler</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>LBRYURIHandler</string>
<key>CFBundleURLSchemes</key>
<array>
<string>lbry</string>
</array>
</dict>
</array>
<key>NSUIElement</key>
<true/>
<key>CFBundleIconFile</key>
<string>PythonApplet.icns</string>
<key>CFBundleIdentifier</key>
<string>org.pythonmac.unspecified.LBRYURIHandler</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>LBRYURIHandler</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.0.0</string>
<key>LSHasLocalizedDisplayName</key>
<false/>
<key>NSAppleScriptEnabled</key>
<false/>
<key>NSHumanReadableCopyright</key>
<string>Copyright not specified</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>PyMainFileNames</key>
<array>
<string>__boot__</string>
</array>
<key>PyOptions</key>
<dict>
<key>alias</key>
<false/>
<key>argv_emulation</key>
<true/>
<key>emulate_shell_environment</key>
<false/>
<key>no_chdir</key>
<false/>
<key>prefer_ppc</key>
<false/>
<key>site_packages</key>
<false/>
<key>use_faulthandler</key>
<false/>
<key>use_pythonpath</key>
<false/>
<key>verbose</key>
<false/>
</dict>
<key>PyResourcePackages</key>
<array>
</array>
<key>PyRuntimeLocations</key>
<array>
<string>@executable_path/../Frameworks/Python.framework/Versions/2.7/Python</string>
</array>
<key>PythonInfoDict</key>
<dict>
<key>PythonExecutable</key>
<string>/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python</string>
<key>PythonLongVersion</key>
<string>2.7.10 (v2.7.10:15c95b7d81dc, May 23 2015, 09:33:12)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]</string>
<key>PythonShortVersion</key>
<string>2.7</string>
<key>py2app</key>
<dict>
<key>alias</key>
<false/>
<key>template</key>
<string>app</string>
<key>version</key>
<string>0.9</string>
</dict>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,19 @@
"""
This is a setup.py script generated by py2applet
Usage:
python setup.py py2app
"""
from setuptools import setup
APP = ['LBRYURIHandler.py']
DATA_FILES = []
OPTIONS = {'argv_emulation': True}
setup(
app=APP,
data_files=DATA_FILES,
options={'py2app': OPTIONS},
setup_requires=['py2app'],
)

View file

View file

@ -57,7 +57,8 @@ class LBRYDownloader(object):
if os.name == "nt":
self.lbrycrdd_path = "lbrycrdd.exe"
else:
self.lbrycrdd_path = "./lbrycrdd"
self.lbrycrdd_path = None
self.default_lbrycrdd_path = "./lbrycrdd"
self.delete_blobs_on_remove = True
self.blob_request_payment_rate_manager = None
@ -123,7 +124,7 @@ class LBRYDownloader(object):
if not os.path.exists(lbrycrdd_path_conf_path):
return ""
lbrycrdd_path_conf = open(lbrycrdd_path_conf_path)
lines = lbrycrdd_path_conf.readline()
lines = lbrycrdd_path_conf.readlines()
return lines
d = threads.deferToThread(get_lbrycrdd_path_conf_file)
@ -293,6 +294,8 @@ class LBRYDownloader(object):
lbrycrdd_path = None
if self.start_lbrycrdd is True:
lbrycrdd_path = self.lbrycrdd_path
if not lbrycrdd_path:
lbrycrdd_path = self.default_lbrycrdd_path
wallet = LBRYcrdWallet(self.db_dir, wallet_dir=self.wallet_dir, wallet_conf=self.wallet_conf,
lbrycrdd_path=lbrycrdd_path)