diff --git a/.gitignore b/.gitignore index 819306a87..1bd0eea94 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ lbrynet.egg-info/PKG-INFO *.pem *.decTest + +.coverage diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 000000000..5e5aab35c --- /dev/null +++ b/.pylintrc @@ -0,0 +1,379 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Allow optimization of some AST trees. This will activate a peephole AST +# optimizer, which will apply various small optimizations. For instance, it can +# be used to obtain the result of joining multiple strings with the addition +# operator. Joining a lot of strings can lead to a maximum recursion error in +# Pylint and this flag can prevent that. It has one side effect, the resulting +# AST will be different than the one from reality. +optimize-ast=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[BASIC] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,input + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules=twisted.internet.reactor,leveldb + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). This supports can work +# with qualified names. +ignored-classes=twisted.internet,RequestMessage + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/.travis.yml b/.travis.yml index 64dbd86cc..865ea7427 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,8 @@ before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then sudo pip install --upgrade pip virtualenv; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then virtualenv $HOME/venv; source $HOME/venv/bin/activate; fi -install: true +install: + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./packaging/travis/install_dependencies_and_run_tests.sh; fi before_script: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then openssl aes-256-cbc -k "$ENCRYPTION_SECRET" -in packaging/osx/certs/dist.cer.enc -d -a -out packaging/osx/certs/dist.cer; fi @@ -25,8 +26,7 @@ script: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew upgrade gmp; fi # the default py2app (v0.9) has a bug that is fixed in the head of /metachris/py2app - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pip install git+https://github.com/metachris/py2app; fi - # py2app fails to find jsonrpc unless json-rpc is installed. why? I don't know. - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pip install json-rpc; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then pip install jsonrpc; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cd packaging/osx/lbry-osx-app; ./setup_app.sh; cd $TRAVIS_BUILD_DIR; fi # fail the build if this is a build for a tag and we don't have the versions matching - if [[ -n "${TRAVIS_TAG}" ]]; then if [[ "v`python setup.py -V`" = "${TRAVIS_TAG}" ]]; then true; else false; fi; fi 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/core/client/DownloadManager.py b/lbrynet/core/client/DownloadManager.py index fced77969..d601833dd 100644 --- a/lbrynet/core/client/DownloadManager.py +++ b/lbrynet/core/client/DownloadManager.py @@ -52,7 +52,7 @@ class DownloadManager(object): def check_stop(result, manager): if isinstance(result, failure.Failure): - log.error("Failed to stop the %s: %s", manager. result.getErrorMessage()) + log.error("Failed to stop the %s: %s", manager, result.getErrorMessage()) return False return True @@ -115,4 +115,4 @@ class DownloadManager(object): if not self.blobs: return self.calculate_total_bytes() else: - return sum([b.length for b in self.needed_blobs() if b.length is not None]) \ No newline at end of file + return sum([b.length for b in self.needed_blobs() if b.length is not None]) diff --git a/lbrynet/lbrylive/LBRYStdinUploader.py b/lbrynet/lbrylive/LBRYStdinUploader.py index 03570a975..7637e29e1 100644 --- a/lbrynet/lbrylive/LBRYStdinUploader.py +++ b/lbrynet/lbrylive/LBRYStdinUploader.py @@ -1,3 +1,6 @@ +# pylint: skip-file +# This file is not maintained, but might be used in the future +# import logging import sys from lbrynet.lbrylive.LiveStreamCreator import StdOutLiveStreamCreator @@ -114,4 +117,4 @@ def launch_stdin_uploader(): d.addCallback(lambda _: start_stdin_uploader()) d.addCallback(lambda _: shut_down()) reactor.addSystemEventTrigger('before', 'shutdown', uploader.shut_down) - reactor.run() \ No newline at end of file + reactor.run() diff --git a/lbrynet/lbrylive/LBRYStdoutDownloader.py b/lbrynet/lbrylive/LBRYStdoutDownloader.py index 0c1fe8c60..e23cb36f1 100644 --- a/lbrynet/lbrylive/LBRYStdoutDownloader.py +++ b/lbrynet/lbrylive/LBRYStdoutDownloader.py @@ -1,7 +1,10 @@ +# pylint: skip-file +# This file is not maintained, but might be used in the future +# import logging import sys -from lbrynet.lbrynet_console.plugins.LBRYLive.LBRYLiveStreamDownloader import LBRYLiveStreamDownloader +from lbrynet.lbrylive.client.LiveStreamDownloader import LBRYLiveStreamDownloader from lbrynet.core.BlobManager import TempBlobManager from lbrynet.core.Session import LBRYSession from lbrynet.core.client.StandaloneBlobDownloader import StandaloneBlobDownloader @@ -93,4 +96,4 @@ def launch_stdout_downloader(): d.addErrback(print_error) d.addCallback(lambda _: shut_down()) reactor.addSystemEventTrigger('before', 'shutdown', downloader.shut_down) - reactor.run() \ No newline at end of file + reactor.run() diff --git a/lbrynet/lbrynet_console/ControlHandlers.py b/lbrynet/lbrynet_console/ControlHandlers.py index cbe5dd270..d19d55c88 100644 --- a/lbrynet/lbrynet_console/ControlHandlers.py +++ b/lbrynet/lbrynet_console/ControlHandlers.py @@ -120,7 +120,7 @@ class CommandHandlerFactory(object): return self.control_handler_class.prompt_description def get_handler(self, console): - return self.control_handler_class(console, *self.args) + return self.control_handler_class(console, *self.args) # pylint: disable=not-callable class CommandHandler(object): diff --git a/lbrynet/lbrynet_console/plugins/BlindRepeater/ValuableBlobQueryHandler.py b/lbrynet/lbrynet_console/plugins/BlindRepeater/ValuableBlobQueryHandler.py index 08d5a57a0..d8dd0009e 100644 --- a/lbrynet/lbrynet_console/plugins/BlindRepeater/ValuableBlobQueryHandler.py +++ b/lbrynet/lbrynet_console/plugins/BlindRepeater/ValuableBlobQueryHandler.py @@ -101,7 +101,7 @@ class ValuableBlobHashQueryHandler(ValuableQueryHandler): for blob_hash, count in valuable_hashes: hashes_and_scores.append((blob_hash, 1.0 * count / 10.0)) if len(hashes_and_scores) != 0: - log.info("Responding to a valuable blob hashes request with %s blob hashes: %s", + log.info("Responding to a valuable blob hashes request with %s blob hashes", str(len(hashes_and_scores))) expected_payment = 1.0 * len(hashes_and_scores) * self.valuable_blob_hash_payment_rate / 1000.0 self.wallet.add_expected_payment(self.peer, expected_payment) @@ -193,10 +193,10 @@ class ValuableBlobLengthQueryHandler(ValuableQueryHandler): if success is True: lengths.append(response_pair) if len(lengths) > 0: - log.info("Responding with %s blob lengths: %s", str(len(lengths))) + log.info("Responding with %s blob lengths", str(len(lengths))) expected_payment = 1.0 * len(lengths) * self.blob_length_payment_rate / 1000.0 self.wallet.add_expected_payment(self.peer, expected_payment) self.peer.update_stats('uploaded_valuable_blob_infos', len(lengths)) return {'blob_length': {'blob_lengths': lengths}} - dl.addCallback(make_response) \ No newline at end of file + dl.addCallback(make_response) 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..b68e1feca 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,12 +402,16 @@ 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: - d = self._upload_log(name_prefix="fr") + d = self._upload_log(log_type="first_run") else: - d = self._upload_log(exclude_previous=True, name_prefix="start") + d = self._upload_log(exclude_previous=True, log_type="start") if float(self.session.wallet.wallet_balance) == 0.0: d.addCallback(lambda _: self._check_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): @@ -579,12 +617,7 @@ class LBRYDaemon(jsonrpc.JSONRPC): dl.addCallback(_set_query_handlers) return dl - def _upload_log(self, name_prefix=None, exclude_previous=False, force=False): - if name_prefix: - name_prefix = name_prefix + "-" + platform.system() - else: - name_prefix = platform.system() - + def _upload_log(self, log_type=None, exclude_previous=False, force=False): if self.upload_log or force: LOG_URL = "https://lbry.io/log-upload" if exclude_previous: @@ -596,9 +629,13 @@ class LBRYDaemon(jsonrpc.JSONRPC): f = open(self.log_file, "r") log_contents = f.read() f.close() - t = datetime.now() - log_name = name_prefix + "-" + base58.b58encode(self.lbryid)[:20] + "-" + str(t.month) + "-" + str(t.day) + "-" + str(t.year) + "-" + str(t.hour) + "-" + str(t.minute) - params = {'name': log_name, 'log': log_contents} + params = { + 'date': datetime.utcnow().strftime('%Y%m%d-%H%M%S'), + 'hash': base58.b58encode(self.lbryid)[:20], + 'sys': platform.system(), + 'type': log_type, + 'log': log_contents + } requests.post(LOG_URL, params) return defer.succeed(None) @@ -608,14 +645,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 = self._upload_log(log_type="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 +838,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 +1062,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 +1096,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 +1109,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 +1218,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 +1233,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 +1284,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 +1418,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 +1800,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 +2145,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,15 +2165,15 @@ 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 """ if p: if 'name_prefix' in p.keys(): - prefix = p['name_prefix'] + '_api' + log_type = p['name_prefix'] + '_api' else: - prefix = None + log_type = None if 'exclude_previous' in p.keys(): exclude_previous = p['exclude_previous'] @@ -2101,11 +2188,49 @@ class LBRYDaemon(jsonrpc.JSONRPC): else: force = False else: - prefix = "api" + log_type = "api" exclude_previous = True - d = self._upload_log(name_prefix=prefix, exclude_previous=exclude_previous, force=force) + d = self._upload_log(log_type=log_type, exclude_previous=exclude_previous, force=force) if 'message' in p.keys(): 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'
Stream name | ' - self.files_table += r'Completed | ' - self.files_table += r'Toggle | ' - self.files_table += r'Remove | ' - self.files_table += r'