From 2026024c8a8d879f6dcca77c08bdc1eaeaa44e1b Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 30 May 2016 15:49:25 -0400 Subject: [PATCH] updates from development --- lbrynet/__init__.py | 2 +- lbrynet/conf.py | 4 +- lbrynet/core/LBRYcrdWallet.py | 5 + .../lbrynet_daemon/Apps/LBRYOSXStatusBar.py | 108 -------- lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py | 60 ----- lbrynet/lbrynet_daemon/LBRYDaemon.py | 246 +++++++++++++----- lbrynet/lbrynet_daemon/LBRYDaemonControl.py | 13 +- lbrynet/lbrynet_daemon/LBRYDaemonServer.py | 207 +++------------ lbrynet/lbrynet_daemon/LBRYUIManager.py | 224 ++++++++++++++++ .../daemon_scripts/Autofetcher.py | 68 +++++ .../{Apps => daemon_scripts}/__init__.py | 0 .../daemon_scripts/migrateto025.py | 33 +++ .../daemon_scripts/network_tester.py | 160 ++++++++++++ packaging/osx/certs/cert.cer.enc | 30 +++ packaging/osx/certs/cert.p12.enc | 33 +++ setup_uri_handler.py | 25 -- 16 files changed, 780 insertions(+), 438 deletions(-) delete mode 100644 lbrynet/lbrynet_daemon/Apps/LBRYOSXStatusBar.py delete mode 100644 lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py create mode 100644 lbrynet/lbrynet_daemon/LBRYUIManager.py create mode 100644 lbrynet/lbrynet_daemon/daemon_scripts/Autofetcher.py rename lbrynet/lbrynet_daemon/{Apps => daemon_scripts}/__init__.py (100%) create mode 100644 lbrynet/lbrynet_daemon/daemon_scripts/migrateto025.py create mode 100644 lbrynet/lbrynet_daemon/daemon_scripts/network_tester.py create mode 100644 packaging/osx/certs/cert.cer.enc create mode 100644 packaging/osx/certs/cert.p12.enc delete mode 100644 setup_uri_handler.py diff --git a/lbrynet/__init__.py b/lbrynet/__init__.py index 26dafe45d..3d80f11a2 100644 --- a/lbrynet/__init__.py +++ b/lbrynet/__init__.py @@ -4,5 +4,5 @@ import logging logging.getLogger(__name__).addHandler(logging.NullHandler()) -version = (0, 2, 4) +version = (0, 2, 5) __version__ = ".".join([str(x) for x in version]) diff --git a/lbrynet/conf.py b/lbrynet/conf.py index dfae3d274..526dc711d 100644 --- a/lbrynet/conf.py +++ b/lbrynet/conf.py @@ -36,8 +36,10 @@ UI_ADDRESS = "http://" + API_INTERFACE + ":" + str(API_PORT) PROTOCOL_PREFIX = "lbry" DEFAULT_WALLET = "lbryum" +WALLET_TYPES = ["lbryum", "lbrycrd"] DEFAULT_TIMEOUT = 30 DEFAULT_MAX_SEARCH_RESULTS = 25 DEFAULT_MAX_KEY_FEE = 100.0 DEFAULT_SEARCH_TIMEOUT = 3.0 -DEFAULT_CACHE_TIME = 3600 \ No newline at end of file +DEFAULT_CACHE_TIME = 3600 +DEFAULT_UI_BRANCH = "master" diff --git a/lbrynet/core/LBRYcrdWallet.py b/lbrynet/core/LBRYcrdWallet.py index 8ec200611..17fd0e229 100644 --- a/lbrynet/core/LBRYcrdWallet.py +++ b/lbrynet/core/LBRYcrdWallet.py @@ -295,6 +295,11 @@ class LBRYWallet(object): d.addCallback(self._get_stream_info_from_value, name) return d + def get_txid_for_name(self, name): + d = self._get_value_for_name(name) + d.addCallback(lambda r: None if 'txid' not in r else r['txid']) + return d + def get_stream_info_from_txid(self, name, txid): d = self.get_claims_from_tx(txid) diff --git a/lbrynet/lbrynet_daemon/Apps/LBRYOSXStatusBar.py b/lbrynet/lbrynet_daemon/Apps/LBRYOSXStatusBar.py deleted file mode 100644 index 5ca433f72..000000000 --- a/lbrynet/lbrynet_daemon/Apps/LBRYOSXStatusBar.py +++ /dev/null @@ -1,108 +0,0 @@ -import rumps -import xmlrpclib -import os -import webbrowser -import subprocess -import argparse - - -class DaemonStatusBarApp(rumps.App): - def __init__(self): - icon_path = 'app.icns' - if os.path.isfile(icon_path): - rumps.App.__init__(self, name="LBRY", icon=icon_path, quit_button=None, - menu=["Open", "Preferences", "View balance", "Quit"]) - else: - rumps.App.__init__(self, name="LBRY", title="LBRY", quit_button=None, - menu=["Open", "Preferences", "View balance", "Quit"]) - - @rumps.timer(1) - def alert_daemon_start(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - start_msg = daemon.is_running() - if isinstance(start_msg, str): - rumps.notification(title='LBRY', subtitle='', message=str(start_msg), sound=True) - update_info = daemon.check_for_new_version() - update_msg = "" - for p in update_info: - if not p[0]: - update_msg += p[1] + "\n" - if update_msg: - update_msg += "\n Try running the installer again to fix this" - rumps.notification(title='LBRY', subtitle='', message=update_msg, sound=True) - except: - pass - - @rumps.clicked('Open') - def get_ui(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - daemon.is_running() - webbrowser.get('safari').open("lbry://lbry") - except: - try: - rumps.notification(title='LBRY', subtitle='', message="Couldn't connect to lbrynet daemon", sound=True) - except: - rumps.alert(title='LBRY', message="Couldn't connect to lbrynet daemon") - - @rumps.clicked("Preferences") - def prefs(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - daemon.is_running() - webbrowser.get('safari').open("lbry://settings") - except: - rumps.notification(title='LBRY', subtitle='', message="Couldn't connect to lbrynet daemon", sound=True) - - @rumps.clicked("View balance") - def disp_balance(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - balance = daemon.get_balance() - r = round(float(balance), 2) - try: - rumps.notification(title='LBRY', subtitle='', message=str("Your balance is %.2f LBC" % r), sound=False) - except: - rumps.alert(title='LBRY', message=str("Your balance is %.2f LBC" % r)) - - except: - try: - rumps.notification(title='LBRY', subtitle='', message="Couldn't connect to lbrynet daemon", sound=True) - except: - rumps.alert(title='LBRY', message="Couldn't connect to lbrynet daemon") - - @rumps.clicked('Quit') - def clean_quit(self): - daemon = xmlrpclib.ServerProxy("http://localhost:7080/") - try: - daemon.stop() - except: - pass - rumps.quit_application() - - -def main(): - parser = argparse.ArgumentParser(description="Launch lbrynet status bar application") - parser.add_argument("--startdaemon", - help="true or false, default true", - type=str, - default="true") - args = parser.parse_args() - - if str(args.startdaemon).lower() == "true": - daemon = xmlrpclib.ServerProxy('http://localhost:7080') - try: - daemon.is_running() - except: - subprocess.Popen("screen -dmS lbrynet bash -c " - "'PYTHONPATH=$PYTHONPATH:`cat /Users/${USER}/Library/Application\ Support/lbrynet/.python_path`; " - "PATH=$PATH:`cat /Users/${USER}/Library/Application\ Support/lbrynet/.lbry_bin_path`; " - "lbrynet-daemon --update=False'", shell=True) - - status_app = DaemonStatusBarApp() - status_app.run() - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py b/lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py deleted file mode 100644 index f6990cfea..000000000 --- a/lbrynet/lbrynet_daemon/Apps/LBRYURIHandler.py +++ /dev/null @@ -1,60 +0,0 @@ -import os -import json -import webbrowser -import subprocess -import sys - -from time import sleep -from jsonrpc.proxy import JSONRPCProxy - -API_CONNECTION_STRING = "http://localhost:5279/lbryapi" -UI_ADDRESS = "http://localhost:5279" - - -class LBRYURIHandler(object): - def __init__(self): - self.started_daemon = False - self.daemon = JSONRPCProxy.from_url(API_CONNECTION_STRING) - - def handle_osx(self, lbry_name): - try: - status = self.daemon.is_running() - except: - os.system("open /Applications/LBRY.app") - sleep(3) - - if lbry_name == "lbry" or lbry_name == "": - webbrowser.open(UI_ADDRESS) - else: - webbrowser.open(UI_ADDRESS + "/?watch=" + lbry_name) - - def handle_linux(self, lbry_name): - try: - status = self.daemon.is_running() - except: - cmd = r'DIR = "$( cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd )"' \ - r'if [-z "$(pgrep lbrynet-daemon)"]; then' \ - r'echo "running lbrynet-daemon..."' \ - r'$DIR / lbrynet - daemon &' \ - r'sleep 3 # let the daemon load before connecting' \ - r'fi' - subprocess.Popen(cmd, shell=True) - - if lbry_name == "lbry" or lbry_name == "": - webbrowser.open(UI_ADDRESS) - else: - webbrowser.open(UI_ADDRESS + "/?watch=" + lbry_name) - - -def main(args): - if len(args) != 1: - args = ['lbry://lbry'] - - name = args[0][7:] - if sys.platform == "darwin": - LBRYURIHandler().handle_osx(lbry_name=name) - else: - LBRYURIHandler().handle_linux(lbry_name=name) - -if __name__ == "__main__": - main(sys.argv[1:]) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemon.py b/lbrynet/lbrynet_daemon/LBRYDaemon.py index 0a10dff9d..cf38ee02e 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemon.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemon.py @@ -1,5 +1,6 @@ import locale import os +import subprocess import sys import simplejson as json import binascii @@ -31,13 +32,14 @@ from lbrynet.core.Error import UnknownNameError, InsufficientFundsError 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.lbrynet_daemon.LBRYUIManager import LBRYUIManager from lbrynet.lbrynet_daemon.LBRYDownloader import GetStream from lbrynet.lbrynet_daemon.LBRYPublisher import Publisher from lbrynet.core.utils import generate_id from lbrynet.lbrynet_console.LBRYSettings import LBRYSettings from lbrynet.conf import MIN_BLOB_DATA_PAYMENT_RATE, DEFAULT_MAX_SEARCH_RESULTS, KNOWN_DHT_NODES, DEFAULT_MAX_KEY_FEE, \ - DEFAULT_WALLET, DEFAULT_SEARCH_TIMEOUT, DEFAULT_CACHE_TIME -from lbrynet.conf import API_CONNECTION_STRING, API_PORT, API_ADDRESS, DEFAULT_TIMEOUT, UI_ADDRESS + DEFAULT_WALLET, DEFAULT_SEARCH_TIMEOUT, DEFAULT_CACHE_TIME, DEFAULT_UI_BRANCH +from lbrynet.conf import DEFAULT_TIMEOUT, WALLET_TYPES from lbrynet.core.StreamDescriptor import StreamDescriptorIdentifier, download_sd_blob from lbrynet.core.Session import LBRYSession from lbrynet.core.PTCWallet import PTCWallet @@ -109,7 +111,7 @@ CONNECTION_PROBLEM_CODES = [ ALLOWED_DURING_STARTUP = ['is_running', 'is_first_run', 'get_time_behind_blockchain', 'stop', 'daemon_status', 'get_start_notice', - 'version', 'check_for_new_version'] + 'version'] BAD_REQUEST = 400 NOT_FOUND = 404 @@ -129,7 +131,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): isLeaf = True - def __init__(self, ui_version_info, wallet_type=DEFAULT_WALLET): + def __init__(self, root, wallet_type=DEFAULT_WALLET): jsonrpc.JSONRPC.__init__(self) reactor.addSystemEventTrigger('before', 'shutdown', self._shutdown) @@ -139,9 +141,10 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.connected_to_internet = True self.connection_problem = None self.query_handlers = {} - self.ui_version = ui_version_info.replace('\n', '') self.git_lbrynet_version = None self.git_lbryum_version = None + self.ui_version = None + self.ip = None self.wallet_type = wallet_type self.first_run = None self.log_file = LOG_FILENAME @@ -151,20 +154,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.waiting_on = {} self.streams = {} self.known_dht_nodes = KNOWN_DHT_NODES - self.platform_info = { - "processor": platform.processor(), - "python_version: ": platform.python_version(), - "platform": platform.platform(), - "os_release": platform.release(), - "os_system": platform.system(), - "lbrynet_version: ": lbrynet_version, - "lbryum_version: ": lbryum_version, - "ui_version": self.ui_version, - } - try: - self.platform_info['ip'] = json.load(urlopen('http://jsonip.com'))['ip'] - except: - self.platform_info['ip'] = "Could not determine" + self.first_run_after_update = False if os.name == "nt": from lbrynet.winhelpers.knownpaths import get_path, FOLDERID, UserHandle @@ -197,7 +187,9 @@ class LBRYDaemon(jsonrpc.JSONRPC): 'use_upnp': True, 'start_lbrycrdd': True, 'requested_first_run_credits': False, - 'cache_time': DEFAULT_CACHE_TIME + 'cache_time': DEFAULT_CACHE_TIME, + 'startup_scripts': [], + 'last_version': {'lbrynet': lbrynet_version, 'lbryum': lbryum_version} } if os.path.isfile(self.daemon_conf): @@ -234,6 +226,20 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.session_settings = settings_dict + if 'last_version' in missing_settings.keys(): + self.session_settings['last_version'] = None + + if self.session_settings['last_version'] != self.default_settings['last_version']: + self.session_settings['last_version'] = self.default_settings['last_version'] + f = open(self.daemon_conf, "w") + f.write(json.dumps(self.session_settings)) + f.close() + + self.first_run_after_update = True + log.info("First run after update") + if lbrynet_version == '0.2.5': + self.session_settings['startup_scripts'].append({'script_name': 'migrateto025', 'run_once': True}) + self.run_on_startup = self.session_settings['run_on_startup'] self.data_rate = self.session_settings['data_rate'] self.max_key_fee = self.session_settings['max_key_fee'] @@ -244,7 +250,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.search_timeout = self.session_settings['search_timeout'] self.download_timeout = self.session_settings['download_timeout'] self.max_search_results = self.session_settings['max_search_results'] - self.wallet_type = self.session_settings['wallet_type'] if self.session_settings['wallet_type'] == wallet_type else wallet_type + self.wallet_type = self.session_settings['wallet_type'] if self.session_settings['wallet_type'] in WALLET_TYPES else wallet_type self.delete_blobs_on_remove = self.session_settings['delete_blobs_on_remove'] self.peer_port = self.session_settings['peer_port'] self.dht_node_port = self.session_settings['dht_node_port'] @@ -252,6 +258,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.start_lbrycrdd = self.session_settings['start_lbrycrdd'] self.requested_first_run_credits = self.session_settings['requested_first_run_credits'] self.cache_time = self.session_settings['cache_time'] + self.startup_scripts = self.session_settings['startup_scripts'] if os.path.isfile(os.path.join(self.db_dir, "stream_info_cache.json")): f = open(os.path.join(self.db_dir, "stream_info_cache.json"), "r") @@ -301,6 +308,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.sd_identifier = StreamDescriptorIdentifier() self.stream_info_manager = TempLBRYFileMetadataManager() self.settings = LBRYSettings(self.db_dir) + self.lbry_ui_manager = LBRYUIManager(root) self.blob_request_payment_rate_manager = None self.lbry_file_metadata_manager = None self.lbry_file_manager = None @@ -374,7 +382,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): log.error(failure) return jsonrpclib.Fault(self.FAILURE, "error") - def setup(self): + def setup(self, branch=DEFAULT_UI_BRANCH, user_specified=False, branch_specified=False): def _log_starting_vals(): d = self._get_lbry_files() d.addCallback(lambda r: json.dumps([d[1] for d in r])) @@ -394,6 +402,10 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.announced_startup = True self.startup_status = STARTUP_STAGES[5] log.info("[" + str(datetime.now()) + "] Started lbrynet-daemon") + if len(self.startup_scripts): + log.info("Scheduling scripts") + reactor.callLater(3, self._run_scripts) + # self.lbrynet_connection_checker.start(3600) if self.first_run: @@ -415,6 +427,9 @@ class LBRYDaemon(jsonrpc.JSONRPC): self.connection_problem_checker.start(1) d = defer.Deferred() + d.addCallback(lambda _: self.lbry_ui_manager.setup(branch=branch, + user_specified=user_specified, + branch_specified=branch_specified)) d.addCallback(lambda _: self._initial_setup()) d.addCallback(lambda _: threads.deferToThread(self._setup_data_directory)) d.addCallback(lambda _: self._check_db_migration()) @@ -433,9 +448,29 @@ class LBRYDaemon(jsonrpc.JSONRPC): return defer.succeed(None) + def _get_platform(self): + r = { + "processor": platform.processor(), + "python_version: ": platform.python_version(), + "platform": platform.platform(), + "os_release": platform.release(), + "os_system": platform.system(), + "lbrynet_version: ": lbrynet_version, + "lbryum_version: ": lbryum_version, + "ui_version": self.lbry_ui_manager.loaded_git_version, + } + if not self.ip: + try: + r['ip'] = json.load(urlopen('http://jsonip.com'))['ip'] + self.ip = r['ip'] + except: + r['ip'] = "Could not determine" + + return r + def _initial_setup(self): def _log_platform(): - log.info("Platform: " + json.dumps(self.platform_info)) + log.info("Platform: " + json.dumps(self._get_platform())) return defer.succeed(None) d = _log_platform() @@ -523,10 +558,13 @@ class LBRYDaemon(jsonrpc.JSONRPC): return defer.succeed(True) def _stop_server(self): - if self.lbry_server_port is not None: - self.lbry_server_port, p = None, self.lbry_server_port - return defer.maybeDeferred(p.stopListening) - else: + try: + if self.lbry_server_port is not None: + self.lbry_server_port, p = None, self.lbry_server_port + return defer.maybeDeferred(p.stopListening) + else: + return defer.succeed(True) + except AttributeError: return defer.succeed(True) def _setup_server(self): @@ -608,14 +646,21 @@ class LBRYDaemon(jsonrpc.JSONRPC): def _shutdown(self): log.info("Closing lbrynet session") log.info("Status at time of shutdown: " + self.startup_status[0]) + if self.internet_connection_checker.running: + self.internet_connection_checker.stop() + if self.version_checker.running: + self.version_checker.stop() + if self.connection_problem_checker.running: + self.connection_problem_checker.stop() d = self._upload_log(name_prefix="close", exclude_previous=False if self.first_run else True) d.addCallback(lambda _: self._stop_server()) + d.addErrback(lambda err: True) d.addCallback(lambda _: self.lbry_file_manager.stop()) - d.addErrback(lambda err: log.info("Bad server shutdown: " + err.getTraceback())) + d.addErrback(lambda err: True) if self.session is not None: d.addCallback(lambda _: self.session.shut_down()) - d.addErrback(lambda err: log.info("Bad session shutdown: " + err.getTraceback())) + d.addErrback(lambda err: True) return d def _update_settings(self, settings): @@ -794,7 +839,8 @@ class LBRYDaemon(jsonrpc.JSONRPC): log.info("Using PTC wallet") d = defer.succeed(PTCWallet(self.db_dir)) else: - d = defer.fail() + log.info("Requested unknown wallet '%s', using default lbryum" % self.wallet_type) + d = defer.succeed(LBRYumWallet(self.db_dir)) d.addCallback(lambda wallet: {"wallet": wallet}) return d @@ -1017,19 +1063,31 @@ class LBRYDaemon(jsonrpc.JSONRPC): f.close() return defer.succeed(True) - def _resolve_name(self, name): + def _resolve_name(self, name, force_refresh=False): def _cache_stream_info(stream_info): + def _add_txid(txid): + self.name_cache[name]['txid'] = txid + return defer.succeed(None) + self.name_cache[name] = {'claim_metadata': stream_info, 'timestamp': self._get_long_count_timestamp()} - d = self._update_claim_cache() + d = self.session.wallet.get_txid_for_name(name) + d.addCallback(_add_txid) + d.addCallback(lambda _: self._update_claim_cache()) d.addCallback(lambda _: self.name_cache[name]['claim_metadata']) + return d - if name in self.name_cache.keys(): - if (self._get_long_count_timestamp() - self.name_cache[name]['timestamp']) < self.cache_time: - log.info("[" + str(datetime.now()) + "] Returning cached stream info for lbry://" + name) - d = defer.succeed(self.name_cache[name]['claim_metadata']) + if not force_refresh: + if name in self.name_cache.keys(): + if (self._get_long_count_timestamp() - self.name_cache[name]['timestamp']) < self.cache_time: + log.info("[" + str(datetime.now()) + "] Returning cached stream info for lbry://" + name) + d = defer.succeed(self.name_cache[name]['claim_metadata']) + else: + log.info("[" + str(datetime.now()) + "] Refreshing stream info for lbry://" + name) + d = self.session.wallet.get_stream_info_for_name(name) + d.addCallbacks(_cache_stream_info, lambda _: defer.fail(UnknownNameError)) else: - log.info("[" + str(datetime.now()) + "] Refreshing stream info for lbry://" + name) + log.info("[" + str(datetime.now()) + "] Resolving stream info for lbry://" + name) d = self.session.wallet.get_stream_info_for_name(name) d.addCallbacks(_cache_stream_info, lambda _: defer.fail(UnknownNameError)) else: @@ -1039,7 +1097,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): return d - def _delete_lbry_file(self, lbry_file): + def _delete_lbry_file(self, lbry_file, delete_file=True): d = self.lbry_file_manager.delete_lbry_file(lbry_file) def finish_deletion(lbry_file): @@ -1052,7 +1110,8 @@ class LBRYDaemon(jsonrpc.JSONRPC): d = self.lbry_file_manager.get_count_for_stream_hash(s_h) # TODO: could possibly be a timing issue here d.addCallback(lambda c: self.stream_info_manager.delete_stream(s_h) if c == 0 else True) - d.addCallback(lambda _: os.remove(os.path.join(self.download_directory, lbry_file.file_name)) if + if delete_file: + d.addCallback(lambda _: os.remove(os.path.join(self.download_directory, lbry_file.file_name)) if os.path.isfile(os.path.join(self.download_directory, lbry_file.file_name)) else defer.succeed(None)) return d @@ -1160,7 +1219,10 @@ class LBRYDaemon(jsonrpc.JSONRPC): if status[0] == DOWNLOAD_RUNNING_CODE: d = f.status() d.addCallback(_get_file_status) - d.addCallback(lambda message: {'completed': f.completed, 'file_name': f.file_name, 'key': key, + d.addCallback(lambda message: {'completed': f.completed, 'file_name': f.file_name, + 'download_directory': f.download_directory, + 'download_path': os.path.join(f.download_directory, f.file_name), + 'key': key, 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, 'stream_name': f.stream_name, @@ -1172,6 +1234,8 @@ class LBRYDaemon(jsonrpc.JSONRPC): 'message': message}) else: d = defer.succeed({'completed': f.completed, 'file_name': f.file_name, 'key': key, + 'download_directory': f.download_directory, + 'download_path': os.path.join(f.download_directory, f.file_name), 'points_paid': f.points_paid, 'stopped': f.stopped, 'stream_hash': f.stream_hash, 'stream_name': f.stream_name, 'suggested_file_name': f.suggested_file_name, 'upload_allowed': f.upload_allowed, 'sd_hash': f.sd_hash, 'total_bytes': size, @@ -1221,6 +1285,31 @@ class LBRYDaemon(jsonrpc.JSONRPC): requests.post(URL, json.dumps({"text": msg})) return defer.succeed(None) + def _run_scripts(self): + if len([k for k in self.startup_scripts if 'run_once' in k.keys()]): + log.info("Removing one time startup scripts") + f = open(self.daemon_conf, "r") + initialsettings = json.loads(f.read()) + f.close() + t = [s for s in self.startup_scripts if 'run_once' not in s.keys()] + initialsettings['startup_scripts'] = t + f = open(self.daemon_conf, "w") + f.write(json.dumps(initialsettings)) + f.close() + + for script in self.startup_scripts: + if script['script_name'] == 'migrateto025': + log.info("Running migrator to 0.2.5") + from lbrynet.lbrynet_daemon.daemon_scripts.migrateto025 import run as run_migrate + run_migrate(self) + + if script['script_name'] == 'Autofetcher': + log.info("Starting autofetcher script") + from lbrynet.lbrynet_daemon.daemon_scripts.Autofetcher import run as run_autofetcher + run_autofetcher(self) + + return defer.succeed(None) + def _render_response(self, result, code): return defer.succeed({'result': result, 'code': code}) @@ -1330,10 +1419,11 @@ class LBRYDaemon(jsonrpc.JSONRPC): "remote_lbryum": most recent lbryum version available from github """ + platform_info = self._get_platform() msg = { - 'platform': self.platform_info['platform'], - 'os_release': self.platform_info['os_release'], - 'os_system': self.platform_info['os_system'], + 'platform': platform_info['platform'], + 'os_release': platform_info['os_release'], + 'os_system': platform_info['os_system'], 'lbrynet_version': lbrynet_version, 'lbryum_version': lbryum_version, 'ui_version': self.ui_version, @@ -1711,14 +1801,19 @@ class LBRYDaemon(jsonrpc.JSONRPC): confirmation message """ + if 'delete_target_file' in p.keys(): + delete_file = p['delete_target_file'] + else: + delete_file = True + def _delete_file(f): file_name = f.file_name - d = self._delete_lbry_file(f) + d = self._delete_lbry_file(f, delete_file=delete_file) d.addCallback(lambda _: "Deleted LBRY file" + file_name) return d - if p.keys()[0] in ['name', 'sd_hash', 'file_name']: - search_type = p.keys()[0] + if 'name' in p.keys() or 'sd_hash' in p.keys() or 'file_name' in p.keys(): + search_type = [k for k in p.keys() if k != 'delete_target_file'][0] d = self._get_lbry_file(search_type, p[search_type], return_json=False) d.addCallback(lambda l: _delete_file(l) if l else False) @@ -2051,25 +2146,18 @@ class LBRYDaemon(jsonrpc.JSONRPC): # # return d - def jsonrpc_check_for_new_version(self): + def jsonrpc_log(self, message): """ - Checks local version against versions in __init__.py and version.py in the lbrynet and lbryum repos + Log message Args: - None + message: message to be logged Returns: - true/false, true meaning that there is a new version available + True """ - def _check_version(): - if (lbrynet_version >= self.git_lbrynet_version) and (lbryum_version >= self.git_lbryum_version): - log.info("[" + str(datetime.now()) + "] Up to date") - return self._render_response(False, OK_CODE) - else: - log.info("[" + str(datetime.now()) + "] Updates available") - return self._render_response(True, OK_CODE) - - return _check_version() + log.info(message) + return self._render_response(True, OK_CODE) def jsonrpc_upload_log(self, p=None): """ @@ -2078,7 +2166,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): Args, optional: 'name_prefix': prefix to indicate what is requesting the log upload 'exclude_previous': true/false, whether or not to exclude previous sessions from upload, defaults on true - Returns + Returns: True """ @@ -2109,3 +2197,41 @@ class LBRYDaemon(jsonrpc.JSONRPC): d.addCallback(lambda _: self._log_to_slack(p['message'])) d.addCallback(lambda _: self._render_response(True, OK_CODE)) return d + + def jsonrpc_configure_ui(self, p): + """ + Configure the UI being hosted + + Args, optional: + 'branch': a branch name on lbryio/lbry-web-ui + 'path': path to a ui folder + """ + + if 'path' in p.keys(): + d = self.lbry_ui_manager.setup(user_specified=p['path']) + elif 'branch' in p.keys(): + d = self.lbry_ui_manager.setup(branch=p['branch']) + else: + d = self.lbry_ui_manager.setup() + d.addCallback(lambda r: self._render_response(r, OK_CODE)) + + return d + + def jsonrpc_reveal(self, p): + """ + Open a folder in finder/file explorer + + Args: + 'path': path to be selected in finder + Returns: + True, opens finder + """ + + path = p['path'] + if sys.platform == "darwin": + d = threads.deferToThread(subprocess.Popen, ['open', '-R', path]) + else: + d = threads.deferToThread(subprocess.Popen, ['xdg-open', '-R', path]) + + d.addCallback(lambda _: self._render_response(True, OK_CODE)) + return d \ No newline at end of file diff --git a/lbrynet/lbrynet_daemon/LBRYDaemonControl.py b/lbrynet/lbrynet_daemon/LBRYDaemonControl.py index 1d7531331..72c8d2c1e 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemonControl.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemonControl.py @@ -13,7 +13,8 @@ from twisted.internet import reactor, defer from jsonrpc.proxy import JSONRPCProxy from lbrynet.lbrynet_daemon.LBRYDaemonServer import LBRYDaemonServer -from lbrynet.conf import API_CONNECTION_STRING, API_INTERFACE, API_ADDRESS, API_PORT, DEFAULT_WALLET, UI_ADDRESS +from lbrynet.conf import API_CONNECTION_STRING, API_INTERFACE, API_ADDRESS, API_PORT, \ + DEFAULT_WALLET, UI_ADDRESS, DEFAULT_UI_BRANCH if sys.platform != "darwin": @@ -68,12 +69,11 @@ def start(): help="path to custom UI folder", default=None) parser.add_argument("--branch", - help="Branch of lbry-web-ui repo to use, defaults on master", - default="master") + help="Branch of lbry-web-ui repo to use, defaults on master") parser.add_argument('--no-launch', dest='launchui', action="store_false") parser.add_argument('--log-to-console', dest='logtoconsole', action="store_true") parser.add_argument('--quiet', dest='quiet', action="store_true") - parser.set_defaults(launchui=True, logtoconsole=False, quiet=False) + parser.set_defaults(branch=False, launchui=True, logtoconsole=False, quiet=False) args = parser.parse_args() if args.logtoconsole: @@ -104,7 +104,10 @@ def start(): if test_internet_connection(): lbry = LBRYDaemonServer() - d = lbry.start(branch=args.branch, user_specified=args.ui, wallet=args.wallet) + d = lbry.start(branch=args.branch if args.branch else DEFAULT_UI_BRANCH, + user_specified=args.ui, + wallet=args.wallet, + branch_specified=True if args.branch else False) if args.launchui: d.addCallback(lambda _: webbrowser.open(UI_ADDRESS)) diff --git a/lbrynet/lbrynet_daemon/LBRYDaemonServer.py b/lbrynet/lbrynet_daemon/LBRYDaemonServer.py index 85494d21c..393a87b72 100644 --- a/lbrynet/lbrynet_daemon/LBRYDaemonServer.py +++ b/lbrynet/lbrynet_daemon/LBRYDaemonServer.py @@ -18,7 +18,7 @@ from txjsonrpc.web import jsonrpc from zope.interface import implements from lbrynet.lbrynet_daemon.LBRYDaemon import LBRYDaemon -from lbrynet.conf import API_CONNECTION_STRING, API_ADDRESS, DEFAULT_WALLET, UI_ADDRESS +from lbrynet.conf import API_CONNECTION_STRING, API_ADDRESS, DEFAULT_WALLET, UI_ADDRESS, DEFAULT_UI_BRANCH if sys.platform != "darwin": @@ -149,22 +149,23 @@ class HostedLBRYFile(resource.Resource): self._producer = None resource.Resource.__init__(self) - def makeProducer(self, request, stream): - def _save_producer(producer): - self._producer = producer - return defer.succeed(None) - - range_header = request.getAllHeaders()['range'].replace('bytes=', '').split('-') - start, stop = int(range_header[0]), range_header[1] - log.info("[" + str(datetime.now()) + "] GET range %s-%s" % (start, stop)) - path = os.path.join(self._api.download_directory, stream.file_name) - - d = stream.get_total_bytes() - d.addCallback(lambda size: _save_producer(LBRYFileStreamer(request, path, start, stop, size))) - d.addCallback(lambda _: request.registerProducer(self._producer, streaming=True)) - # request.notifyFinish().addCallback(lambda _: self._producer.stopProducing()) - request.notifyFinish().addErrback(self._responseFailed, d) - return d + # todo: fix LBRYFileStreamer and use it instead of static.File + # def makeProducer(self, request, stream): + # def _save_producer(producer): + # self._producer = producer + # return defer.succeed(None) + # + # range_header = request.getAllHeaders()['range'].replace('bytes=', '').split('-') + # start, stop = int(range_header[0]), range_header[1] + # log.info("[" + str(datetime.now()) + "] GET range %s-%s" % (start, stop)) + # path = os.path.join(self._api.download_directory, stream.file_name) + # + # d = stream.get_total_bytes() + # d.addCallback(lambda size: _save_producer(LBRYFileStreamer(request, path, start, stop, size))) + # d.addCallback(lambda _: request.registerProducer(self._producer, streaming=True)) + # # request.notifyFinish().addCallback(lambda _: self._producer.stopProducing()) + # request.notifyFinish().addErrback(self._responseFailed, d) + # return d def render_GET(self, request): if 'name' in request.args.keys(): @@ -182,172 +183,22 @@ class HostedLBRYFile(resource.Resource): request.finish() return server.NOT_DONE_YET - def _responseFailed(self, err, call): - call.addErrback(lambda err: err.trap(error.ConnectionDone)) - call.addErrback(lambda err: err.trap(defer.CancelledError)) - call.addErrback(lambda err: log.info("Error: " + str(err))) - call.cancel() - - -class MyLBRYFiles(resource.Resource): - isLeaf = False - - def __init__(self): - resource.Resource.__init__(self) - self.files_table = None - - def delayed_render(self, request, result): - request.write(result.encode('utf-8')) - request.finish() - - def render_GET(self, request): - self.files_table = None - api = jsonrpc.Proxy(API_CONNECTION_STRING) - d = api.callRemote("get_lbry_files", {}) - d.addCallback(self._get_table) - d.addCallback(lambda results: self.delayed_render(request, results)) - - return server.NOT_DONE_YET - - def _get_table(self, files): - if not self.files_table: - self.files_table = r'My LBRY files' - self.files_table += r'' - self.files_table += r'' - self.files_table += r'' - self.files_table += r'' - self.files_table += r'' - self.files_table += r'' - return self._get_table(files) - if not len(files): - self.files_table += r'
Stream nameCompletedToggleRemove
' - return self.files_table - else: - f = files.pop() - self.files_table += r'' - self.files_table += r'%s' % (f['stream_name']) - self.files_table += r'%s' % (f['completed']) - self.files_table += r'Start' if f['stopped'] else r'Stop' - self.files_table += r'Delete' - self.files_table += r'' - return self._get_table(files) + # def _responseFailed(self, err, call): + # call.addErrback(lambda err: err.trap(error.ConnectionDone)) + # call.addErrback(lambda err: err.trap(defer.CancelledError)) + # call.addErrback(lambda err: log.info("Error: " + str(err))) + # call.cancel() class LBRYDaemonServer(object): - def __init__(self): - self.data_dir = user_data_dir("LBRY") - if not os.path.isdir(self.data_dir): - os.mkdir(self.data_dir) - self.version_dir = os.path.join(self.data_dir, "ui_version_history") - if not os.path.isdir(self.version_dir): - os.mkdir(self.version_dir) - self.config = os.path.join(self.version_dir, "active.json") - self.ui_dir = os.path.join(self.data_dir, "lbry-web-ui") - self.git_version = None - self._api = None - self.root = None - - if not os.path.isfile(os.path.join(self.config)): - self.loaded_git_version = None - else: - try: - f = open(self.config, "r") - loaded_ui = json.loads(f.read()) - f.close() - self.loaded_git_version = loaded_ui['commit'] - self.loaded_branch = loaded_ui['branch'] - version_log.info("[" + str(datetime.now()) + "] Last used " + self.loaded_branch + " commit " + str(self.loaded_git_version).replace("\n", "")) - except: - self.loaded_git_version = None - self.loaded_branch = None - - def setup(self, branch="master", user_specified=None): - self.branch = branch - if user_specified: - if os.path.isdir(user_specified): - log.info("Using user specified UI directory: " + str(user_specified)) - self.branch = "user-specified" - self.loaded_git_version = "user-specified" - self.ui_dir = user_specified - return defer.succeed("user-specified") - else: - log.info("User specified UI directory doesn't exist, using " + branch) - else: - log.info("Using UI branch: " + branch) - self._git_url = "https://api.github.com/repos/lbryio/lbry-web-ui/git/refs/heads/%s" % branch - self._dist_url = "https://raw.githubusercontent.com/lbryio/lbry-web-ui/%s/dist.zip" % branch - - d = self._up_to_date() - d.addCallback(lambda r: self._download_ui() if not r else self.branch) - return d - - def _up_to_date(self): - def _get_git_info(): - response = urlopen(self._git_url) - data = json.loads(response.read()) - return defer.succeed(data['object']['sha']) - - def _set_git(version): - self.git_version = version - version_log.info("[" + str(datetime.now()) + "] UI branch " + self.branch + " has a most recent commit of: " + str(self.git_version).replace("\n", "")) - - if self.git_version == self.loaded_git_version and os.path.isdir(self.ui_dir): - version_log.info("[" + str(datetime.now()) + "] local copy of " + self.branch + " is up to date") - return defer.succeed(True) - else: - if self.git_version == self.loaded_git_version: - version_log.info("[" + str(datetime.now()) + "] Can't find ui files, downloading them again") - else: - version_log.info("[" + str(datetime.now()) + "] local copy of " + self.branch + " branch is out of date, updating") - f = open(self.config, "w") - f.write(json.dumps({'commit': self.git_version, - 'time': str(datetime.now()), - 'branch': self.branch})) - f.close() - return defer.succeed(False) - - d = _get_git_info() - d.addCallback(_set_git) - return d - - def _download_ui(self): - def _delete_ui_dir(): - if os.path.isdir(self.ui_dir): - if self.loaded_git_version: - version_log.info("[" + str(datetime.now()) + "] Removed ui files for commit " + str(self.loaded_git_version).replace("\n", "")) - log.info("Removing out of date ui files") - shutil.rmtree(self.ui_dir) - return defer.succeed(None) - - def _dl_ui(): - url = urlopen(self._dist_url) - z = ZipFile(StringIO(url.read())) - names = [i for i in z.namelist() if '.DS_exStore' not in i and '__MACOSX' not in i] - z.extractall(self.ui_dir, members=names) - version_log.info("[" + str(datetime.now()) + "] Updated branch " + self.branch + ": " + str(self.loaded_git_version).replace("\n", "") + " --> " + self.git_version.replace("\n", "")) - log.info("Downloaded files for UI commit " + str(self.git_version).replace("\n", "")) - self.loaded_git_version = self.git_version - return self.branch - - d = _delete_ui_dir() - d.addCallback(lambda _: _dl_ui()) - return d - - def _setup_server(self, ui_ver, wallet): - self._api = LBRYDaemon(ui_ver, wallet_type=wallet) - self.root = LBRYindex(self.ui_dir) - self.root.putChild("css", static.File(os.path.join(self.ui_dir, "css"))) - self.root.putChild("font", static.File(os.path.join(self.ui_dir, "font"))) - self.root.putChild("img", static.File(os.path.join(self.ui_dir, "img"))) - self.root.putChild("js", static.File(os.path.join(self.ui_dir, "js"))) + def _setup_server(self, wallet): + self.root = LBRYindex(os.path.join(os.path.join(data_dir, "lbry-ui"), "active")) + self._api = LBRYDaemon(self.root, wallet_type=wallet) self.root.putChild("view", HostedLBRYFile(self._api)) - self.root.putChild("files", MyLBRYFiles()) self.root.putChild(API_ADDRESS, self._api) return defer.succeed(True) - def start(self, branch="master", user_specified=False, wallet=DEFAULT_WALLET): - d = self.setup(branch=branch, user_specified=user_specified) - d.addCallback(lambda v: self._setup_server(v, wallet)) - d.addCallback(lambda _: self._api.setup()) - + def start(self, branch=DEFAULT_UI_BRANCH, user_specified=False, branch_specified=False, wallet=DEFAULT_WALLET): + d = self._setup_server(self._setup_server(wallet)) + d.addCallback(lambda _: self._api.setup(branch, user_specified, branch_specified)) return d diff --git a/lbrynet/lbrynet_daemon/LBRYUIManager.py b/lbrynet/lbrynet_daemon/LBRYUIManager.py new file mode 100644 index 000000000..b4aa33cd5 --- /dev/null +++ b/lbrynet/lbrynet_daemon/LBRYUIManager.py @@ -0,0 +1,224 @@ +import os +import logging +import shutil +import sys +import json + +from urllib2 import urlopen +from StringIO import StringIO +from twisted.web import static +from twisted.internet import defer +from lbrynet.conf import DEFAULT_UI_BRANCH +from lbrynet import __version__ as lbrynet_version +from lbryum.version import ELECTRUM_VERSION as lbryum_version +from zipfile import ZipFile +from appdirs import user_data_dir + +if sys.platform != "darwin": + data_dir = os.path.join(os.path.expanduser("~"), ".lbrynet") +else: + data_dir = user_data_dir("LBRY") + +if not os.path.isdir(data_dir): + os.mkdir(data_dir) +version_dir = os.path.join(data_dir, "ui_version_history") +if not os.path.isdir(version_dir): + os.mkdir(version_dir) + +log = logging.getLogger(__name__) +log.addHandler(logging.FileHandler(os.path.join(data_dir, 'lbrynet-daemon.log'))) +log.setLevel(logging.INFO) + + +class LBRYUIManager(object): + def __init__(self, root): + self.data_dir = user_data_dir("LBRY") + self.ui_root = os.path.join(self.data_dir, "lbry-ui") + self.active_dir = os.path.join(self.ui_root, "active") + self.update_dir = os.path.join(self.ui_root, "update") + + if not os.path.isdir(self.data_dir): + os.mkdir(self.data_dir) + if not os.path.isdir(self.ui_root): + os.mkdir(self.ui_root) + if not os.path.isdir(self.ui_root): + os.mkdir(self.ui_root) + if not os.path.isdir(self.ui_root): + os.mkdir(self.ui_root) + + self.config = os.path.join(self.ui_root, "active.json") + self.update_requires = os.path.join(self.update_dir, "requirements.txt") + self.requirements = {} + self.ui_dir = self.active_dir + self.git_version = None + self.root = root + + if not os.path.isfile(os.path.join(self.config)): + self.loaded_git_version = None + self.loaded_branch = None + self.loaded_requirements = None + else: + try: + f = open(self.config, "r") + loaded_ui = json.loads(f.read()) + f.close() + self.loaded_git_version = loaded_ui['commit'] + self.loaded_branch = loaded_ui['branch'] + self.loaded_requirements = loaded_ui['requirements'] + except: + self.loaded_git_version = None + self.loaded_branch = None + self.loaded_requirements = None + + def setup(self, branch=DEFAULT_UI_BRANCH, user_specified=None, branch_specified=False): + self.branch = branch + if user_specified: + if os.path.isdir(user_specified): + log.info("Checking user specified UI directory: " + str(user_specified)) + self.branch = "user-specified" + self.loaded_git_version = "user-specified" + d = self.migrate_ui(source=user_specified) + d.addCallback(lambda _: self._load_ui()) + return d + else: + log.info("User specified UI directory doesn't exist, using " + branch) + elif self.loaded_branch == "user-specified" and not branch_specified: + log.info("Loading user provided UI") + d = self._load_ui() + return d + else: + log.info("Checking for updates for UI branch: " + branch) + self._git_url = "https://api.github.com/repos/lbryio/lbry-web-ui/git/refs/heads/%s" % branch + self._dist_url = "https://raw.githubusercontent.com/lbryio/lbry-web-ui/%s/dist.zip" % branch + + d = self._up_to_date() + d.addCallback(lambda r: self._download_ui() if not r else self._load_ui()) + return d + + def _up_to_date(self): + def _get_git_info(): + response = urlopen(self._git_url) + data = json.loads(response.read()) + return defer.succeed(data['object']['sha']) + + def _set_git(version): + self.git_version = version.replace('\n', '') + if self.git_version == self.loaded_git_version: + log.info("UI is up to date") + return defer.succeed(True) + else: + log.info("UI updates available, checking if installation meets requirements") + return defer.succeed(False) + + d = _get_git_info() + d.addCallback(_set_git) + return d + + def migrate_ui(self, source=None): + if not source: + requires_file = self.update_requires + source_dir = self.update_dir + delete_source = True + else: + requires_file = os.path.join(source, "requirements.txt") + source_dir = source + delete_source = False + + def _check_requirements(): + if not os.path.isfile(requires_file): + log.info("No requirements.txt file, rejecting request to migrate this UI") + return defer.succeed(False) + + f = open(requires_file, "r") + for requirement in [line for line in f.read().split('\n') if line]: + t = requirement.split('=') + if len(t) == 3: + self.requirements[t[0]] = {'version': t[1], 'operator': '=='} + elif t[0][-1] == ">": + self.requirements[t[0][:-1]] = {'version': t[1], 'operator': '>='} + elif t[0][-1] == "<": + self.requirements[t[0][:-1]] = {'version': t[1], 'operator': '<='} + f.close() + passed_requirements = True + for r in self.requirements: + if r == 'lbrynet': + c = lbrynet_version + elif r == 'lbryum': + c = lbryum_version + else: + c = None + if c: + if self.requirements[r]['operator'] == '==': + if not self.requirements[r]['version'] == c: + passed_requirements = False + log.info("Local version %s of %s does not meet UI requirement for version %s" % ( + c, r, self.requirements[r]['version'])) + else: + log.info("Local version of %s meets ui requirement" % r) + if self.requirements[r]['operator'] == '>=': + if not self.requirements[r]['version'] <= c: + passed_requirements = False + log.info("Local version %s of %s does not meet UI requirement for version %s" % ( + c, r, self.requirements[r]['version'])) + else: + log.info("Local version of %s meets ui requirement" % r) + if self.requirements[r]['operator'] == '<=': + if not self.requirements[r]['version'] >= c: + passed_requirements = False + log.info("Local version %s of %s does not meet UI requirement for version %s" % ( + c, r, self.requirements[r]['version'])) + else: + log.info("Local version of %s meets ui requirement" % r) + return defer.succeed(passed_requirements) + + def _disp_failure(): + log.info("Failed to satisfy requirements for branch '%s', update was not loaded" % self.branch) + return defer.succeed(False) + + def _do_migrate(): + if os.path.isdir(self.active_dir): + shutil.rmtree(self.active_dir) + shutil.copytree(source_dir, self.active_dir) + if delete_source: + shutil.rmtree(source_dir) + + log.info("Loaded UI update") + + f = open(self.config, "w") + loaded_ui = {'commit': self.git_version, 'branch': self.branch, 'requirements': self.requirements} + f.write(json.dumps(loaded_ui)) + f.close() + + self.loaded_git_version = loaded_ui['commit'] + self.loaded_branch = loaded_ui['branch'] + self.loaded_requirements = loaded_ui['requirements'] + return defer.succeed(True) + + d = _check_requirements() + d.addCallback(lambda r: _do_migrate() if r else _disp_failure()) + return d + + def _download_ui(self): + def _delete_update_dir(): + if os.path.isdir(self.update_dir): + shutil.rmtree(self.update_dir) + return defer.succeed(None) + + def _dl_ui(): + url = urlopen(self._dist_url) + z = ZipFile(StringIO(url.read())) + names = [i for i in z.namelist() if '.DS_exStore' not in i and '__MACOSX' not in i] + z.extractall(self.update_dir, members=names) + log.info("Downloaded files for UI commit " + str(self.git_version).replace("\n", "")) + return self.branch + + d = _delete_update_dir() + d.addCallback(lambda _: _dl_ui()) + d.addCallback(lambda _: self.migrate_ui()) + d.addCallback(lambda _: self._load_ui()) + return d + + def _load_ui(self): + for d in [i[0] for i in os.walk(self.active_dir) if os.path.dirname(i[0]) == self.active_dir]: + self.root.putChild(os.path.basename(d), static.File(d)) + return defer.succeed(True) \ No newline at end of file diff --git a/lbrynet/lbrynet_daemon/daemon_scripts/Autofetcher.py b/lbrynet/lbrynet_daemon/daemon_scripts/Autofetcher.py new file mode 100644 index 000000000..cd7ed02cb --- /dev/null +++ b/lbrynet/lbrynet_daemon/daemon_scripts/Autofetcher.py @@ -0,0 +1,68 @@ +import json +import logging.handlers +import sys +import os + +from appdirs import user_data_dir +from twisted.internet.task import LoopingCall +from twisted.internet import reactor + + +if sys.platform != "darwin": + log_dir = os.path.join(os.path.expanduser("~"), ".lbrynet") +else: + log_dir = user_data_dir("LBRY") + +if not os.path.isdir(log_dir): + os.mkdir(log_dir) + +LOG_FILENAME = os.path.join(log_dir, 'lbrynet-daemon.log') + +if os.path.isfile(LOG_FILENAME): + f = open(LOG_FILENAME, 'r') + PREVIOUS_LOG = len(f.read()) + f.close() +else: + PREVIOUS_LOG = 0 + +log = logging.getLogger(__name__) +handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=2097152, backupCount=5) +log.addHandler(handler) +log.setLevel(logging.INFO) + + +class Autofetcher(object): + """ + Download name claims as they occur + """ + + def __init__(self, api): + self._api = api + self._checker = LoopingCall(self._check_for_new_claims) + self.best_block = None + + def start(self): + reactor.addSystemEventTrigger('before', 'shutdown', self.stop) + self._checker.start(5) + + def stop(self): + log.info("Stopping autofetcher") + self._checker.stop() + + def _check_for_new_claims(self): + block = self._api.get_best_blockhash() + if block != self.best_block: + log.info("Checking new block for name claims, block hash: %s" % block) + self.best_block = block + transactions = self._api.get_block({'blockhash': block})['tx'] + for t in transactions: + c = self._api.get_claims_for_tx({'txid': t}) + if len(c): + for i in c: + log.info("Downloading stream for claim txid: %s" % t) + self._api.get({'name': t, 'stream_info': json.loads(i['value'])}) + + +def run(api): + fetcher = Autofetcher(api) + fetcher.start() \ No newline at end of file diff --git a/lbrynet/lbrynet_daemon/Apps/__init__.py b/lbrynet/lbrynet_daemon/daemon_scripts/__init__.py similarity index 100% rename from lbrynet/lbrynet_daemon/Apps/__init__.py rename to lbrynet/lbrynet_daemon/daemon_scripts/__init__.py diff --git a/lbrynet/lbrynet_daemon/daemon_scripts/migrateto025.py b/lbrynet/lbrynet_daemon/daemon_scripts/migrateto025.py new file mode 100644 index 000000000..9283b48aa --- /dev/null +++ b/lbrynet/lbrynet_daemon/daemon_scripts/migrateto025.py @@ -0,0 +1,33 @@ +from twisted.internet import defer + + +class migrator(object): + """ + Re-resolve lbry names to write missing data to blockchain.db and to cache the nametrie + """ + + def __init__(self, api): + self._api = api + + def start(self): + def _resolve_claims(claimtrie): + claims = [i for i in claimtrie if 'txid' in i.keys()] + r = defer.DeferredList([self._api._resolve_name(claim['name'], force_refresh=True) for claim in claims], consumeErrors=True) + return r + + def _restart_lbry_files(): + def _restart_lbry_file(lbry_file): + return lbry_file.restore() + + r = defer.DeferredList([_restart_lbry_file(lbry_file) for lbry_file in self._api.lbry_file_manager.lbry_files if not lbry_file.txid], consumeErrors=True) + r.callback(None) + return r + + d = self._api.session.wallet.get_nametrie() + d.addCallback(_resolve_claims) + d.addCallback(lambda _: _restart_lbry_files()) + + +def run(api): + refresher = migrator(api) + refresher.start() diff --git a/lbrynet/lbrynet_daemon/daemon_scripts/network_tester.py b/lbrynet/lbrynet_daemon/daemon_scripts/network_tester.py new file mode 100644 index 000000000..d62bdc3b1 --- /dev/null +++ b/lbrynet/lbrynet_daemon/daemon_scripts/network_tester.py @@ -0,0 +1,160 @@ +import json +import logging +import os +import sys + +from appdirs import user_data_dir +from datetime import datetime +from twisted.internet import defer +from twisted.internet.task import LoopingCall + +from lbrynet.core.Error import InvalidStreamInfoError, InsufficientFundsError +from lbrynet.core.PaymentRateManager import PaymentRateManager +from lbrynet.core.StreamDescriptor import download_sd_blob +from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloaderFactory +from lbrynet.conf import DEFAULT_TIMEOUT + +INITIALIZING_CODE = 'initializing' +DOWNLOAD_METADATA_CODE = 'downloading_metadata' +DOWNLOAD_TIMEOUT_CODE = 'timeout' +DOWNLOAD_RUNNING_CODE = 'running' +DOWNLOAD_STOPPED_CODE = 'stopped' +STREAM_STAGES = [ + (INITIALIZING_CODE, 'Initializing...'), + (DOWNLOAD_METADATA_CODE, 'Downloading metadata'), + (DOWNLOAD_RUNNING_CODE, 'Started stream'), + (DOWNLOAD_STOPPED_CODE, 'Paused stream'), + (DOWNLOAD_TIMEOUT_CODE, 'Stream timed out') + ] + +if sys.platform != "darwin": + log_dir = os.path.join(os.path.expanduser("~"), ".lbrynet") +else: + log_dir = user_data_dir("LBRY") + +if not os.path.isdir(log_dir): + os.mkdir(log_dir) + +LOG_FILENAME = os.path.join(log_dir, 'lbrynet-daemon.log') +log = logging.getLogger(__name__) +handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=2097152, backupCount=5) +log.addHandler(handler) +log.setLevel(logging.INFO) + + +class GetStream(object): + def __init__(self, sd_identifier, session, wallet, lbry_file_manager, max_key_fee, pay_key=True, data_rate=0.5, + timeout=DEFAULT_TIMEOUT, download_directory=None, file_name=None): + self.wallet = wallet + self.resolved_name = None + self.description = None + self.key_fee = None + self.key_fee_address = None + self.data_rate = data_rate + self.pay_key = pay_key + self.name = None + self.file_name = file_name + self.session = session + self.payment_rate_manager = PaymentRateManager(self.session.base_payment_rate_manager) + self.lbry_file_manager = lbry_file_manager + self.sd_identifier = sd_identifier + self.stream_hash = None + self.max_key_fee = max_key_fee + self.stream_info = None + self.stream_info_manager = None + self.d = defer.Deferred(None) + self.timeout = timeout + self.timeout_counter = 0 + self.download_directory = download_directory + self.download_path = None + self.downloader = None + self.finished = defer.Deferred() + self.checker = LoopingCall(self.check_status) + self.code = STREAM_STAGES[0] + + def check_status(self): + self.timeout_counter += 1 + + if self.download_path: + self.checker.stop() + self.finished.callback((self.stream_hash, self.download_path)) + + elif self.timeout_counter >= self.timeout: + log.info("Timeout downloading lbry://" + self.resolved_name + ", " + str(self.stream_info)) + self.checker.stop() + self.d.cancel() + self.code = STREAM_STAGES[4] + self.finished.callback(False) + + def start(self, stream_info, name): + self.resolved_name = name + self.stream_info = stream_info + if 'stream_hash' in self.stream_info.keys(): + self.stream_hash = self.stream_info['stream_hash'] + elif 'sources' in self.stream_info.keys(): + self.stream_hash = self.stream_info['sources']['lbry_sd_hash'] + else: + raise InvalidStreamInfoError(self.stream_info) + if 'description' 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']) + if 'key_fee_address' in self.stream_info.keys(): + self.key_fee_address = self.stream_info['key_fee_address'] + else: + self.key_fee_address = None + else: + self.key_fee = None + self.key_fee_address = None + if self.key_fee > self.max_key_fee: + if self.pay_key: + log.info("Key fee (" + str(self.key_fee) + ") above limit of " + str( + self.max_key_fee) + ", didn't download lbry://" + str(self.resolved_name)) + return defer.fail(None) + else: + pass + + def _cause_timeout(): + self.timeout_counter = self.timeout * 2 + + def _set_status(x, status): + self.code = next(s for s in STREAM_STAGES if s[0] == status) + return x + + self.checker.start(1) + + self.d.addCallback(lambda _: _set_status(None, DOWNLOAD_METADATA_CODE)) + self.d.addCallback(lambda _: download_sd_blob(self.session, self.stream_hash, self.payment_rate_manager)) + self.d.addCallback(self.sd_identifier.get_metadata_for_sd_blob) + self.d.addCallback(lambda r: _set_status(r, DOWNLOAD_RUNNING_CODE)) + self.d.addCallback(lambda metadata: ( + next(factory for factory in metadata.factories if isinstance(factory, ManagedLBRYFileDownloaderFactory)), + metadata)) + self.d.addCallback(lambda (factory, metadata): factory.make_downloader(metadata, + [self.data_rate, True], + self.payment_rate_manager, + download_directory=self.download_directory, + file_name=self.file_name)) + self.d.addCallbacks(self._start_download, lambda _: _cause_timeout()) + self.d.callback(None) + + return self.finished + + def _start_download(self, downloader): + def _pay_key_fee(): + 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()) + log.info("Key fee: " + str(self.key_fee) + " | " + str(self.key_fee_address)) + return self.wallet.send_points_to_address(reserved_points, self.key_fee) + return defer.succeed(None) + + if self.pay_key: + d = _pay_key_fee() + else: + d = defer.Deferred() + self.downloader = downloader + self.download_path = os.path.join(downloader.download_directory, downloader.file_name) + d.addCallback(lambda _: log.info("[" + str(datetime.now()) + "] Downloading " + str(self.stream_hash) + " --> " + str(self.download_path))) + d.addCallback(lambda _: self.downloader.start()) diff --git a/packaging/osx/certs/cert.cer.enc b/packaging/osx/certs/cert.cer.enc new file mode 100644 index 000000000..4dc96d9ba --- /dev/null +++ b/packaging/osx/certs/cert.cer.enc @@ -0,0 +1,30 @@ +U2FsdGVkX1/oLoj7zhPd0imh2T8RhLpKzZLk4EHQV0GUJ1g8nwGvxWov+CUMnmjh +Y+LNdomGBYWoUilhe4JaWDUYepwIXn6+TvuWBdVEMGpJhXGbmIf+ncMXo6AP8Fh/ +g9x79SE4RJxFj3utc02B2ivVehoQno5sEvNSZMVml5n9skJoJUBbAsbp1p+7Hm5j +p2Z7UI7/qiih6TmszX5KQvOl/DPezVNksn1c1mUShuaBTjxbprGlr/LvtboR3mAd +8DN4yTGLLJAQ2+FNftM4rAedrr6db2AhQ8WxRmiwTfdubnEoC6zYySFq2kmdjj3S +gPnK0atx+ZihMp+S+GqMnvfAHEtb0vqxoq6nFcSIvcQVxKPyzu5E1kMriY4Oq3xr +K6ebc1NKJHjh7niaUmR3NImBx2h1LAAf/hcKRH2+ATEVczGtI1AsSGgGhUM34eGH +7G+m7+bIkgb8AtlaIGS/VVHsIZCNSgzwZJoNd3hD6ZV65Hb2yeT6Hhos88/05iFT +ewYasa73TqFm5SJHRwt4d1W9WVIJKJPDJ910p+V+NZVUsKOx34+vMNrjCrqW9p9x +gQnza2V/F6blIHTbSzIGc+MFbeHYBO80d+v5jVxheL8z6ollDVts1SyJ5rKJBY6c +quvSgmc/ltE0dqRxLOQJ9mAFbayuMIUP6CbRkPXp8GfE55UtUJkDilalzcpCPrUC +YJpuAI61INOQZZPEVKWW8L68/tLY+oEwWpexQX7xs4FUCblIFf20T3XE2lVuBHf9 +Bp9k7cD2m4mNrbzWOJuqrVt1pr176l9+VSP/ESdDFbmPch2FHl8HK8kgfJvkV+iB +kudmAmzI9DTUpWd5lJp6Fr/rLCMjslFDs37zMg4/E5ikKFSDNeYMtgPZhCwM83kh +OAktow4QAzh3RdbVZMFxaKk9nbiGPuBEsgvraPjb2gY8U34RC9R2FINIuTnJttLK +q7CKFTdbJIf+TIIgzfNu/c978adsK/qS68iltyyx8WFflcybnlqVgja192Ptqw1M +PXBQkH4mUrAeWDfmCPPh/mhO67Bau5u9Wzv/qZ2RXcX0dgXOoMa2sO6ZpR2SzxCJ +/XZwXnElMl+pvojLURDOV16fMPpjMCbzCN+hQabiTASqFNCsz4C9hmOquNh2t+V9 +8xvU/bnOM+/SMhahjYnvdhmRMcY+5Wv32ZnKATq88dq4T7/OZI7q3IsROZ7MnucT +x4vADvcFOfOdtPK35IFfMTfl+Ri3q7REIHMts2WEwXddf8CUiVeIaf8NgrWYW0hP +f9DQbMGKFcqqCHlKrQkv2dBKX/qEbIzN7T7535Ly68zyFuBS252gsLO7nrf+CLEZ +AROOfmt2jv0BvQ4MI5dslzsXFAU11tS36gOZ303R+NJVVqySkza964h2rH5M1F7i +A5p7w/l0OVV7r6aXkmsrIcsUZuY7QnZJORQ1MxNtK20weKfrqs90nMTklUVPc4V8 +LnAW6AYem0ZaeDHn2kx947sglMYxf0h/mFECGhif9hfDTErw7TkSJ26t9ByuEyEf +vGpp3P4iTXHUx7HSh7L4KDva6CP6slGjFMAFUEETn7N5uX3VEYeztMBdHLz0XHZc +PcgVZ8kytXVTEg95upvWmliEbQqWRsy6sr9PanaN1QY6re6RLlYj4pOWVm8qgCXU +IJVTWkROMlYZTWCibCsTsY8fk8aNObZamHjzZGvnU8nEGTx7xQJS8i0r3NM1j2Ka +ehBA+WfXbTplI/Fq8Z2Nrb/O39hQpGbXp4ERsEmVbK0twwsqVNehI0CdobxmGsd5 +E9Afb9iL97DTXsna1Il6FXnHvj3iAQsxxaNLIaj0sN1GaQd9N1mbxThlFNOM3wry +jI8TKCWEfLRQzykkcR3sMg== diff --git a/packaging/osx/certs/cert.p12.enc b/packaging/osx/certs/cert.p12.enc new file mode 100644 index 000000000..40828aff6 --- /dev/null +++ b/packaging/osx/certs/cert.p12.enc @@ -0,0 +1,33 @@ +U2FsdGVkX1+DAD1J9fegD2PjAVffLjKB5urEZYVfRRsZ9uCYeGggOyopseTFPACo +IGBkauMQ1lrQWSltYzDzbzPdhe02w6xWHx8hh9QRepSSWlTUHjIxr8A1GryZo7a8 +4dLs4qxjQDcDdp+csOrBqm3AKS4oeVFRXWxvmr2AueUQ/CEyvhAR1wS3XZ1L0Pod +6XJWAhDIPtT9zfSQbCiVvHtjK7VxVjIMv9VwDfE2Gny/otaNf9Wuor6luiDMF3Z8 +H6X5yh/mkmNZvI/bcOrCmGUkDEVvw/pessdZwwTIdNSzkBE8GqC9Oc5jdOMpW7J1 +afyZDslB1SaNXm/9HDPnl67guZRUM1j6QJxBwIyj8vUhygcG4J6HOAQrWi61ebSX +5ZZrNddMycVRDhE1GphhRPJm7S/v8aeASc8dlAy3ircERJXIO/WhWpdysKgVB8/u +wtc6TVK2MUD0CvmG7hatqCQcwsgK7Goy7zFN4kkNviAegxpm5OAmEtHH5SrlHXWI +CmMAZyNAkwmcAPwviXXaSSA9z/A++KDqQHbRJJKz/fFZ98OsVs64zPwMT3uMAp2E +FiBkCqpxj6q0EFHJwhpNtEPdPF62T9CzcV2CoNr1qyDS7UqlKBi2vkGHNALtBqbm +69rN3ESpjhRzK4pbRFBM0R73JWVW8LM/jWIOFOPh1qd5yKNALKGqw4sEtZ96YJju +Y4tP17+kRknzgSVn6zuUSg/wznIVs+eQ9eYQVd+T70XDUGe2PfQTRm3bz/8W7m8u +tDqE/yhgBJDXuc0zlmXxXxH4cXEhKPA2ScrEw974nWKWrNgtmN+skaJVQELFqVm8 +47amfobRAsp/l0+d86shUg9QC3XzrI/jkPPpKsQUKoYF1OULpXwjMJs7o0e/Ajo6 +S32DWVMqHfhd/M1LBUSFqLb802Y+qFVOXRSJOV2VEqfplbsnEPnmkBrUjVT4y6x6 +HxxqPq5IQM6qLK9TCPXbYCzp3knWim8A5jDFXYNHHeTkuA1xbpkM4lCas64pYV9V +fkokG4fdFM09oileakOxt0iz0DJjXlb/XZLOvuhMeAWPcJC9UTrmMUdXCBgem3Nk +vT40dxCxMK3EREM8dvbNndC7sg9mVJ6dRY7+inDnhhdGhy9FM592lBvFDTS9oJm0 +ZX+0FeDvIGnG1kEIYSrBhCP/9X++6EzF+YzO1zo2YXtVlP2JT/9cD5g6SajvI1+5 +pdv2zzdFRfEKDpJ8bRDr6iMJLCmllWSWkeSE2VNo30+atCorc5/6vfjD/BOJtZDj +vUxPsZxulxiNp24YwDBJ+B+uid8x6xC7h1hId9QF51wUA54AzHRtypAuAOVHjdyj +W+EkCpic1eDyFMVhfy7hB/Ef9lpvuQsKfmvTu3ege8TOMQBeaKmlKBAIyGeTcTH/ +vRz/UAYXEzTRNWkfCFZQ6oucVWSSUxX53DnvD4NcT0AX7+kRY+bhZcZW/nc/NEqN +Tzs3Zv9N9h3M618FK/mqSvhqxukMIRXRhyiISEQyAJtm0SuMu9SXG9Q+G766KOWm ++votjNrHQKIojPI3BcbFHCfXET5qPoUQVPw3M5Av0E3Tm36ZAdl+bhl852H9Vf2M +TprNFmr4U/sljyetEpywG1aEzxijISCflFNBZrqMIwcdYdduLCKPcMNtqSpFiXLV +WtDPBvoz4XldIkZIA+70oBqCwJchILI5ujlo1haF7/ILIK5aynITu2zoaDE6gtE8 +VFl30aGF1uRKYYle8E+RLxv5ID/xFuPlNsBQ3ZsfNbsE9GEoVFmTTGneN+wuTl7G +NNRdyjv7Py3zgC1sqA6cmzRJkgX+CGKm3aCJTvflDKYVGRpmphsYWLqZp7i12Noj +/eHzfYkMU2uOh50IUls8l2fYRlkwPuMQxVtn2g7/3dUXna8zQ0LSqAPRf8zZAszx +nGG1kwpYyJ4YknC8oKhnt3LZWfmAEJFRNSYHDTbBncynqADoUB6EH5j5qcdI/pFG +lsrrw+lbCPbN7dDbbbg685ESKI4WZ7j0zkJIrDWdSFYCitmo437h+t9AcWBF5SEd +vOtCHu46xXuBJbDmz2mslw== diff --git a/setup_uri_handler.py b/setup_uri_handler.py deleted file mode 100644 index e9ba6749c..000000000 --- a/setup_uri_handler.py +++ /dev/null @@ -1,25 +0,0 @@ -from setuptools import setup -import os - -APP = [os.path.join('lbrynet', 'lbrynet_daemon', 'Apps', 'LBRYURIHandler.py')] -DATA_FILES = [] -OPTIONS = {'argv_emulation': True, - 'packages': ['jsonrpc'], - 'plist': { - 'LSUIElement': True, - 'CFBundleIdentifier': 'io.lbry.LBRYURIHandler', - 'CFBundleURLTypes': [ - { - 'CFBundleURLTypes': 'LBRYURIHandler', - 'CFBundleURLSchemes': ['lbry'] - } - ] - } - } - -setup( - app=APP, - data_files=DATA_FILES, - options={'py2app': OPTIONS}, - setup_requires=['py2app'], -) \ No newline at end of file