forked from LBRYCommunity/lbry-sdk
1529 lines
No EOL
58 KiB
Python
1529 lines
No EOL
58 KiB
Python
import logging
|
|
from zope.interface import implements
|
|
from lbrynet.core.StreamDescriptor import PlainStreamDescriptorWriter, BlobStreamDescriptorWriter
|
|
from lbrynet.core.PaymentRateManager import PaymentRateManager
|
|
from lbrynet.lbryfilemanager.LBRYFileCreator import create_lbry_file
|
|
from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloader
|
|
from lbrynet.lbryfile.StreamDescriptor import get_sd_info
|
|
from lbrynet.lbrynet_console.interfaces import IControlHandler, IControlHandlerFactory
|
|
from lbrynet.core.StreamDescriptor import download_sd_blob, BlobStreamDescriptorReader
|
|
from lbrynet.core.Error import UnknownNameError, InvalidBlobHashError, InsufficientFundsError
|
|
from lbrynet.core.Error import InvalidStreamInfoError
|
|
from twisted.internet import defer, threads
|
|
import os
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
class InvalidChoiceError(Exception):
|
|
pass
|
|
|
|
|
|
class InvalidValueError(Exception):
|
|
pass
|
|
|
|
|
|
class ControlHandlerFactory(object):
|
|
implements(IControlHandlerFactory)
|
|
|
|
control_handler_class = None
|
|
|
|
def get_prompt_description(self):
|
|
return self.control_handler_class.prompt_description
|
|
|
|
def __init__(self, *args):
|
|
self.args = args
|
|
|
|
def get_handler(self):
|
|
args = self.args
|
|
return self.control_handler_class(*args)
|
|
|
|
|
|
class ControlHandler(object):
|
|
implements(IControlHandler)
|
|
|
|
prompt_description = None
|
|
|
|
|
|
class RecursiveControlHandler(ControlHandler):
|
|
|
|
def __init__(self, exit_after_one_done=False, reset_after_each_done=False):
|
|
self.current_handler = None
|
|
self.exit_after_one_done = exit_after_one_done
|
|
self.reset_after_each_done = reset_after_each_done
|
|
self._set_control_handlers()
|
|
|
|
def _get_control_handler_factories(self):
|
|
raise NotImplementedError()
|
|
|
|
def _set_control_handlers(self):
|
|
self.control_handlers = {i + 1: handler for i, handler in enumerate(self._get_control_handler_factories())}
|
|
|
|
def handle_line(self, line):
|
|
if self.current_handler is None:
|
|
if line is None:
|
|
num = None
|
|
else:
|
|
try:
|
|
num = int(line)
|
|
except ValueError:
|
|
num = None
|
|
if num == 0:
|
|
return True, None
|
|
if num in self.control_handlers:
|
|
self.current_handler = self.control_handlers[num].get_handler()
|
|
line = None
|
|
ds = []
|
|
if self.current_handler is not None:
|
|
r = self.current_handler.handle_line(line)
|
|
done, ds = r[0], list(r[1:])
|
|
if done is True:
|
|
self.current_handler = None
|
|
if self.exit_after_one_done is True:
|
|
return r
|
|
if self.reset_after_each_done:
|
|
self._set_control_handlers()
|
|
if self.current_handler is None:
|
|
ds += [self.get_prompt()]
|
|
return (False,) + tuple(ds)
|
|
|
|
def get_prompt(self):
|
|
prompt_string = "Options:\n"
|
|
prompt_string += "[0] Exit this menu\n"
|
|
for num, handler in self.control_handlers.iteritems():
|
|
prompt_string += "[" + str(num) + "] " + handler.get_prompt_description() + "\n"
|
|
return defer.succeed(prompt_string)
|
|
|
|
|
|
class ModifyPaymentRate(ControlHandler):
|
|
|
|
def __init__(self):
|
|
self._prompt_choices = {'cancel': (self._cancel, "Don't change anything")}
|
|
|
|
def handle_line(self, line):
|
|
if line is None:
|
|
return False, defer.succeed(self._get_prompt_string())
|
|
elif line.lower() in self._prompt_choices:
|
|
return self._prompt_choices[line.lower()][0]()
|
|
else:
|
|
try:
|
|
rate = float(line)
|
|
except ValueError:
|
|
return True, defer.succeed("Rate must be a number")
|
|
d = self._set_rate(rate)
|
|
d.addCallback(lambda _: "Successfully set the rate")
|
|
return True, d
|
|
|
|
def _cancel(self):
|
|
return True, defer.succeed("No change was made")
|
|
|
|
def _set_rate(self, rate):
|
|
pass
|
|
|
|
def _get_current_status(self):
|
|
pass
|
|
|
|
def _get_prompt_string(self):
|
|
prompt_string = self._get_current_status() + "\n"
|
|
for prompt_choice, (func, help_string) in self._prompt_choices.iteritems():
|
|
prompt_string += prompt_choice + ": " + help_string + "\n"
|
|
prompt_string += "To change the current rate, enter the desired rate\n"
|
|
prompt_string += "Then hit enter\n"
|
|
return prompt_string
|
|
|
|
|
|
class ApplicationStatus(ControlHandler):
|
|
prompt_description = "Application Status"
|
|
|
|
def __init__(self, rate_limiter, dht_node):
|
|
self.rate_limiter = rate_limiter
|
|
self.dht_node = dht_node
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "Application status should not be passed any arguments"
|
|
status = "Total bytes uploaded: " + str(self.rate_limiter.total_ul_bytes) + "\n"
|
|
status += "Total bytes downloaded: " + str(self.rate_limiter.total_dl_bytes) + "\n"
|
|
if self.dht_node is not None:
|
|
status += "Approximate number of nodes in DHT: " + str(self.dht_node.getApproximateTotalDHTNodes()) + "\n"
|
|
status += "Approximate number of blobs in DHT: " + str(self.dht_node.getApproximateTotalHashes()) + "\n"
|
|
return True, defer.succeed(status)
|
|
|
|
|
|
class ApplicationStatusFactory(ControlHandlerFactory):
|
|
control_handler_class = ApplicationStatus
|
|
|
|
|
|
class GetWalletBalances(ControlHandler):
|
|
prompt_description = "Show wallet point balances"
|
|
|
|
def __init__(self, wallet):
|
|
self.wallet = wallet
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "Show wallet balances should not be passed any arguments"
|
|
return True, self._get_wallet_balances()
|
|
|
|
def _get_wallet_balances(self):
|
|
d = self.wallet.get_balance()
|
|
|
|
def format_balance(balance):
|
|
if balance == 0:
|
|
balance = 0
|
|
balance_string = "balance: " + str(balance) + " LBC\n"
|
|
return balance_string
|
|
|
|
d.addCallback(format_balance)
|
|
return d
|
|
|
|
|
|
class GetWalletBalancesFactory(ControlHandlerFactory):
|
|
control_handler_class = GetWalletBalances
|
|
|
|
|
|
class GetNewWalletAddress(ControlHandler):
|
|
prompt_description = "Get a new LBRYcrd address"
|
|
|
|
def __init__(self, wallet):
|
|
self.wallet = wallet
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "Get new LBRYcrd address should not be passed any arguments"
|
|
d = self.wallet.get_new_address()
|
|
d.addCallback(lambda address: str(address))
|
|
return True, d
|
|
|
|
|
|
class GetNewWalletAddressFactory(ControlHandlerFactory):
|
|
control_handler_class = GetNewWalletAddress
|
|
|
|
|
|
class ShutDown(ControlHandler):
|
|
prompt_description = "Shut down"
|
|
|
|
def __init__(self, lbry_service):
|
|
self.lbry_service = lbry_service
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "Shut down should not be passed any arguments"
|
|
return True, self._shut_down()
|
|
|
|
def _shut_down(self):
|
|
d = self.lbry_service.shut_down()
|
|
|
|
def stop_reactor():
|
|
from twisted.internet import reactor
|
|
reactor.stop()
|
|
|
|
d.addBoth(lambda _: stop_reactor())
|
|
|
|
d.addCallback(lambda _: "Shut down successfully")
|
|
return d
|
|
|
|
|
|
class ShutDownFactory(ControlHandlerFactory):
|
|
control_handler_class = ShutDown
|
|
|
|
|
|
class LBRYFileStatus(ControlHandler):
|
|
prompt_description = "Print status information for all LBRY Files"
|
|
|
|
def __init__(self, lbry_file_manager):
|
|
self.lbry_file_manager = lbry_file_manager
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "print status should not be passed any arguments"
|
|
d = self.lbry_file_manager.get_lbry_file_status_reports()
|
|
d.addCallback(self.format_statuses)
|
|
return True, d
|
|
|
|
def format_statuses(self, status_reports):
|
|
status_strings = []
|
|
for status_report in status_reports:
|
|
s = status_report.name + " status: " + status_report.running_status + "\n"
|
|
s += str(status_report.num_completed) + " completed out of " + str(status_report.num_known) + "\n"
|
|
status_strings.append(s)
|
|
return ''.join(status_strings)
|
|
|
|
|
|
class LBRYFileStatusFactory(ControlHandlerFactory):
|
|
control_handler_class = LBRYFileStatus
|
|
|
|
|
|
class AddStream(ControlHandler):
|
|
prompt_description = None
|
|
line_prompt = None
|
|
cancel_prompt = "Trying to locate the stream's metadata. Type \"cancel\" to cancel..."
|
|
canceled_message = "Canceled locating the stream's metadata."
|
|
line_prompt2 = "Modify options? (y/n)"
|
|
line_prompt3 = "Start download? (y/n)"
|
|
|
|
def __init__(self, sd_identifier, base_payment_rate_manager):
|
|
self.sd_identifier = sd_identifier
|
|
self.loading_metadata_deferred = None
|
|
self.metadata = None
|
|
self.factory = None
|
|
self.options_left = []
|
|
self.options_chosen = []
|
|
self.current_option = None
|
|
self.current_choice = None
|
|
self.downloader = None
|
|
self.got_options_response = False
|
|
self.loading_failed = False
|
|
self.payment_rate_manager = PaymentRateManager(base_payment_rate_manager)
|
|
|
|
def handle_line(self, line):
|
|
if line is None:
|
|
return False, defer.succeed(self.line_prompt)
|
|
if self.loading_failed is True:
|
|
return True, None
|
|
if self.loading_metadata_deferred is not None:
|
|
if line.lower() == "cancel":
|
|
self.loading_metadata_deferred.cancel()
|
|
self.loading_metadata_deferred = None
|
|
return True, None
|
|
else:
|
|
return False, defer.succeed(self.cancel_prompt)
|
|
if self.metadata is None:
|
|
self.loading_metadata_deferred = self._load_metadata(line)
|
|
cancel_prompt_d = defer.succeed(self.cancel_prompt)
|
|
self.loading_metadata_deferred.addCallback(self._choose_factory)
|
|
self.loading_metadata_deferred.addErrback(self._handle_load_canceled)
|
|
self.loading_metadata_deferred.addErrback(self._handle_load_failed)
|
|
return False, cancel_prompt_d, self.loading_metadata_deferred
|
|
if self.factory is None:
|
|
try:
|
|
choice = int(line)
|
|
except ValueError:
|
|
return False, defer.succeed(self._show_factory_choices())
|
|
if choice in xrange(len(self.metadata.factories)):
|
|
self.factory = self.metadata.factories[choice]
|
|
return False, defer.succeed(self._show_info_and_options())
|
|
else:
|
|
return False, defer.succeed(self._show_factory_choices())
|
|
if self.got_options_response is False:
|
|
self.got_options_response = True
|
|
if line == 'y' or line == 'Y' and self.options_left:
|
|
return False, defer.succeed(self._get_next_option_prompt())
|
|
else:
|
|
self.options_chosen = [option.default_value for option in self.options_left]
|
|
self.options_left = []
|
|
return False, defer.succeed(self.line_prompt3)
|
|
if self.current_option is not None:
|
|
if self.current_choice is None:
|
|
try:
|
|
self.current_choice = self._get_choice_from_input(line)
|
|
except InvalidChoiceError:
|
|
return False, defer.succeed(self._get_next_option_prompt(invalid_choice=True))
|
|
choice = self.current_option.option_types[self.current_choice]
|
|
if choice.value == float or choice.value == bool:
|
|
return False, defer.succeed(self._get_choice_value_prompt())
|
|
else:
|
|
value = choice.value
|
|
else:
|
|
try:
|
|
value = self._get_value_for_choice(line)
|
|
except InvalidValueError:
|
|
return False, defer.succeed(self._get_choice_value_prompt(invalid_value=True))
|
|
self.options_chosen.append(value)
|
|
self.current_choice = None
|
|
self.current_option = None
|
|
self.options_left = self.options_left[1:]
|
|
if self.options_left:
|
|
return False, defer.succeed(self._get_next_option_prompt())
|
|
else:
|
|
self.current_option = None
|
|
return False, defer.succeed(self.line_prompt3)
|
|
if line == 'y' or line == 'Y':
|
|
d = self._start_download()
|
|
else:
|
|
d = defer.succeed("Download cancelled")
|
|
return True, d
|
|
|
|
def _get_choice_from_input(self, line):
|
|
try:
|
|
choice_num = int(line)
|
|
except ValueError:
|
|
raise InvalidChoiceError()
|
|
if 0 <= choice_num < len(self.current_option.option_types):
|
|
return choice_num
|
|
raise InvalidChoiceError()
|
|
|
|
def _load_metadata(self, sd_file):
|
|
return defer.fail(NotImplementedError())
|
|
|
|
def _handle_load_canceled(self, err):
|
|
err.trap(defer.CancelledError)
|
|
return defer.succeed(self.canceled_message)
|
|
|
|
def _handle_load_failed(self, err):
|
|
self.loading_failed = True
|
|
log.error("An exception occurred attempting to load the stream descriptor: %s", err.getTraceback())
|
|
return defer.succeed("An unexpected error occurred attempting to load the stream's metadata.\n"
|
|
"See console.log for further details.\n\n"
|
|
"Press enter to continue")
|
|
|
|
def _choose_factory(self, metadata):
|
|
self.loading_metadata_deferred = None
|
|
self.metadata = metadata
|
|
if len(self.metadata.factories) == 1:
|
|
self.factory = self.metadata.factories[0]
|
|
return self._show_info_and_options()
|
|
return self._show_factory_choices()
|
|
|
|
def _show_factory_choices(self):
|
|
prompt = "Choose what to do with the file:\n"
|
|
for i, factory in enumerate(self.metadata.factories):
|
|
prompt += "[" + str(i) + "] " + factory.get_description() + '\n'
|
|
return str(prompt)
|
|
|
|
def _show_info_and_options(self):
|
|
self.options_left = self.metadata.options.get_downloader_options(self.metadata.validator,
|
|
self.payment_rate_manager)
|
|
prompt = "Stream info:\n"
|
|
for info_line in self._get_info_to_show():
|
|
prompt += info_line[0] + ": " + info_line[1] + "\n"
|
|
prompt += "\nOptions:\n"
|
|
for option in self.options_left:
|
|
prompt += option.long_description + ": " + str(option.default_value_description) + "\n"
|
|
prompt += "\nModify options? (y/n)"
|
|
return str(prompt)
|
|
|
|
def _get_info_to_show(self):
|
|
return self.metadata.validator.info_to_show()
|
|
|
|
def _get_list_of_option_types(self):
|
|
options_string = ""
|
|
for i, option_type in enumerate(self.current_option.option_types):
|
|
options_string += "[%s] %s\n" % (str(i), option_type.long_description)
|
|
options_string += "Enter choice:"
|
|
return options_string
|
|
|
|
def _get_choice_value_prompt(self, invalid_value=False):
|
|
choice = self.current_option.option_types[self.current_choice]
|
|
choice_string = ""
|
|
if invalid_value is True:
|
|
"Invalid value entered. Try again.\n"
|
|
if choice.short_description is not None:
|
|
choice_string += choice.short_description + "\n"
|
|
if choice.value == float:
|
|
choice_string += "Enter floating point number (e.g. 1.0):"
|
|
elif choice.value == bool:
|
|
true_string = "Yes"
|
|
false_string = "No"
|
|
if choice.bool_options_description is not None:
|
|
true_string, false_string = choice.bool_options_description
|
|
choice_string += "[0] %s\n[1] %s\nEnter choice:" % (true_string, false_string)
|
|
else:
|
|
NotImplementedError()
|
|
return choice_string
|
|
|
|
def _get_value_for_choice(self, input):
|
|
choice = self.current_option.option_types[self.current_choice]
|
|
if choice.value == float:
|
|
try:
|
|
return float(input)
|
|
except ValueError:
|
|
raise InvalidValueError()
|
|
elif choice.value == bool:
|
|
if input == "0":
|
|
return True
|
|
elif input == "1":
|
|
return False
|
|
raise InvalidValueError()
|
|
raise NotImplementedError()
|
|
|
|
def _get_next_option_prompt(self, invalid_choice=False):
|
|
assert len(self.options_left), "Something went wrong. There were no options left"
|
|
self.current_option = self.options_left[0]
|
|
choice_string = ""
|
|
if invalid_choice is True:
|
|
choice_string += "Invalid response entered. Try again.\n"
|
|
|
|
choice_string += self.current_option.long_description + "\n"
|
|
if len(self.current_option.option_types) > 1:
|
|
choice_string += self._get_list_of_option_types()
|
|
elif len(self.current_option.option_types) == 1:
|
|
self.current_choice = 0
|
|
choice_string += self._get_choice_value_prompt()
|
|
return choice_string
|
|
|
|
def _start_download(self):
|
|
d = self._make_downloader()
|
|
d.addCallback(lambda stream_downloader: stream_downloader.start())
|
|
d.addErrback(self._handle_download_error)
|
|
return d
|
|
|
|
def _handle_download_error(self, err):
|
|
if err.check(InsufficientFundsError):
|
|
return "Download stopped due to insufficient funds."
|
|
else:
|
|
log.error("An unexpected error has caused the download to stop: %s" % err.getTraceback())
|
|
return "An unexpected error has caused the download to stop. See console.log for details."
|
|
|
|
def _make_downloader(self):
|
|
return self.factory.make_downloader(self.metadata, self.options_chosen,
|
|
self.payment_rate_manager)
|
|
|
|
|
|
class AddStreamFromSD(AddStream):
|
|
prompt_description = "Add a stream from a stream descriptor file"
|
|
line_prompt = "Stream descriptor file name:"
|
|
|
|
def _load_metadata(self, sd_file):
|
|
return self.sd_identifier.get_metadata_for_sd_file(sd_file)
|
|
|
|
|
|
class AddStreamFromSDFactory(ControlHandlerFactory):
|
|
control_handler_class = AddStreamFromSD
|
|
|
|
|
|
class AddStreamFromHash(AddStream):
|
|
prompt_description = "Add a stream from a hash"
|
|
line_prompt = "Stream descriptor hash:"
|
|
|
|
def __init__(self, sd_identifier, session):
|
|
AddStream.__init__(self, sd_identifier, session.base_payment_rate_manager)
|
|
self.session = session
|
|
|
|
def _load_metadata(self, sd_hash):
|
|
d = download_sd_blob(self.session, sd_hash, self.payment_rate_manager)
|
|
d.addCallback(self.sd_identifier.get_metadata_for_sd_blob)
|
|
return d
|
|
|
|
def _handle_load_failed(self, err):
|
|
self.loading_failed = True
|
|
if err.check(InvalidBlobHashError):
|
|
return defer.succeed("The hash you entered is invalid. It must be 96 characters long and "
|
|
"contain only hex characters.\n\nPress enter to continue")
|
|
if err.check(InsufficientFundsError):
|
|
return defer.succeed("Insufficient funds to download the metadata blob.\n\nPress enter to continue")
|
|
return AddStream._handle_load_failed(self, err)
|
|
|
|
|
|
class AddStreamFromHashFactory(ControlHandlerFactory):
|
|
control_handler_class = AddStreamFromHash
|
|
|
|
|
|
class AddStreamFromLBRYcrdName(AddStreamFromHash):
|
|
prompt_description = "Add a stream from a short name"
|
|
line_prompt = "Short name:"
|
|
|
|
def __init__(self, sd_identifier, session, wallet):
|
|
AddStreamFromHash.__init__(self, sd_identifier, session)
|
|
self.wallet = wallet
|
|
self.resolved_name = None
|
|
self.description = None
|
|
self.key_fee = None
|
|
self.key_fee_address = None
|
|
|
|
def _load_metadata(self, name):
|
|
d = self._resolve_name(name)
|
|
d.addCallback(lambda stream_hash: AddStreamFromHash._load_metadata(self, stream_hash))
|
|
return d
|
|
|
|
def _resolve_name(self, name):
|
|
def get_name_from_info(stream_info):
|
|
if 'stream_hash' not in stream_info:
|
|
raise InvalidStreamInfoError(name)
|
|
self.resolved_name = stream_info.get('name', None)
|
|
self.description = stream_info.get('description', None)
|
|
try:
|
|
if 'key_fee' in stream_info:
|
|
self.key_fee = float(stream_info['key_fee'])
|
|
except ValueError:
|
|
self.key_fee = None
|
|
self.key_fee_address = stream_info.get('key_fee_address', None)
|
|
return stream_info['stream_hash']
|
|
d = self.wallet.get_stream_info_for_name(name)
|
|
d.addCallback(get_name_from_info)
|
|
return d
|
|
|
|
def _handle_load_failed(self, err):
|
|
self.loading_failed = True
|
|
if err.check(UnknownNameError):
|
|
return defer.succeed("The name %s could not be found.\n\n"
|
|
"Press enter to continue" % err.getErrorMessage())
|
|
elif err.check(InvalidBlobHashError):
|
|
return defer.succeed("The metadata for this name is invalid. The stream cannot be downloaded.\n\n" +
|
|
"Press enter to continue")
|
|
return AddStreamFromHash._handle_load_failed(self, err)
|
|
|
|
def _start_download(self):
|
|
d = self._pay_key_fee()
|
|
d.addCallback(lambda _: AddStream._start_download(self))
|
|
return d
|
|
|
|
def _pay_key_fee(self):
|
|
if self.key_fee is not None and self.key_fee_address is not None:
|
|
reserved_points = self.wallet.reserve_points(self.key_fee_address, self.key_fee)
|
|
if reserved_points is None:
|
|
return defer.fail(InsufficientFundsError())
|
|
return self.wallet.send_points_to_address(reserved_points, self.key_fee)
|
|
return defer.succeed(True)
|
|
|
|
def _get_info_to_show(self):
|
|
i = AddStream._get_info_to_show(self)
|
|
if self.description is not None:
|
|
i.append(("description", self.description))
|
|
if self.key_fee is None or self.key_fee_address is None:
|
|
i.append(("decryption key fee", "Free"))
|
|
else:
|
|
i.append(("decription key fee", str(self.key_fee)))
|
|
i.append(("address to pay key fee", str(self.key_fee_address)))
|
|
return i
|
|
|
|
|
|
class AddStreamFromLBRYcrdNameFactory(ControlHandlerFactory):
|
|
control_handler_class = AddStreamFromLBRYcrdName
|
|
|
|
|
|
class LBRYFileChooser(RecursiveControlHandler):
|
|
|
|
def __init__(self, lbry_file_manager, factory_class, *args, **kwargs):
|
|
"""
|
|
@param lbry_file_manager:
|
|
|
|
@param factory_class:
|
|
|
|
@param args: all arguments that will be passed to the factory
|
|
|
|
@param kwargs: all arguments that will be passed to the superclass' __init__
|
|
|
|
@return:
|
|
"""
|
|
self.lbry_file_manager = lbry_file_manager
|
|
self.factory_class = factory_class
|
|
self.args = args
|
|
RecursiveControlHandler.__init__(self, **kwargs)
|
|
|
|
def _get_control_handler_factories(self):
|
|
control_handler_factories = []
|
|
for lbry_file in self.lbry_file_manager.lbry_files:
|
|
control_handler_factories.append(self.factory_class(lbry_file, *self.args))
|
|
return control_handler_factories
|
|
|
|
|
|
class LBRYFileChooserFactory(ControlHandlerFactory):
|
|
def get_prompt_description(self):
|
|
lbry_file = self.args[0]
|
|
return lbry_file.file_name
|
|
|
|
|
|
class DeleteLBRYFileChooser(LBRYFileChooser):
|
|
prompt_description = "Delete LBRY File"
|
|
|
|
def __init__(self, stream_info_manager, blob_manager, lbry_file_manager):
|
|
LBRYFileChooser.__init__(self, lbry_file_manager, DeleteLBRYFileFactory, stream_info_manager,
|
|
blob_manager, lbry_file_manager, exit_after_one_done=True)
|
|
|
|
|
|
class DeleteLBRYFileChooserFactory(ControlHandlerFactory):
|
|
control_handler_class = DeleteLBRYFileChooser
|
|
|
|
|
|
class DeleteLBRYFile(ControlHandler):
|
|
prompt_description = "Delete LBRY File"
|
|
line_prompt = "Also delete data? (y/n):"
|
|
|
|
def __init__(self, lbry_file, stream_info_manager, blob_manager, lbry_file_manager):
|
|
self.lbry_file = lbry_file
|
|
self.stream_info_manager = stream_info_manager
|
|
self.blob_manager = blob_manager
|
|
self.lbry_file_manager = lbry_file_manager
|
|
|
|
def handle_line(self, line):
|
|
if line is None:
|
|
return False, defer.succeed(self.line_prompt)
|
|
delete_data = False
|
|
if line == 'y' or line == 'Y':
|
|
delete_data = True
|
|
d = self._delete_lbry_file(delete_data)
|
|
d.addCallback(lambda _: "Successfully deleted " + str(self.lbry_file.stream_name))
|
|
return True, d
|
|
|
|
def _delete_lbry_file(self, delete_data):
|
|
d = self.lbry_file_manager.delete_lbry_file(self.lbry_file.stream_hash)
|
|
|
|
def finish_deletion():
|
|
if delete_data is True:
|
|
d = self._delete_data()
|
|
else:
|
|
d = defer.succeed(True)
|
|
d.addCallback(lambda _: self._delete_stream_data())
|
|
return d
|
|
|
|
d.addCallback(lambda _: finish_deletion())
|
|
return d
|
|
|
|
def _delete_data(self):
|
|
d1 = self.stream_info_manager.get_blobs_for_stream(self.lbry_file.stream_hash)
|
|
|
|
def get_blob_hashes(blob_infos):
|
|
return [b[0] for b in blob_infos if b[0] is not None]
|
|
|
|
d1.addCallback(get_blob_hashes)
|
|
d2 = self.stream_info_manager.get_sd_blob_hashes_for_stream(self.lbry_file.stream_hash)
|
|
|
|
def combine_blob_hashes(results):
|
|
blob_hashes = []
|
|
for success, result in results:
|
|
if success is True:
|
|
blob_hashes.extend(result)
|
|
return blob_hashes
|
|
|
|
def delete_blobs(blob_hashes):
|
|
self.blob_manager.delete_blobs(blob_hashes)
|
|
return True
|
|
|
|
dl = defer.DeferredList([d1, d2], fireOnOneErrback=True)
|
|
dl.addCallback(combine_blob_hashes)
|
|
dl.addCallback(delete_blobs)
|
|
return dl
|
|
|
|
def _delete_stream_data(self):
|
|
return self.stream_info_manager.delete_stream(self.lbry_file.stream_hash)
|
|
|
|
|
|
class DeleteLBRYFileFactory(LBRYFileChooserFactory):
|
|
control_handler_class = DeleteLBRYFile
|
|
|
|
|
|
class ToggleLBRYFileRunningChooser(LBRYFileChooser):
|
|
prompt_description = "Toggle whether an LBRY File is running"
|
|
|
|
def __init__(self, lbry_file_manager):
|
|
LBRYFileChooser.__init__(self, lbry_file_manager, ToggleLBRYFileRunningFactory, lbry_file_manager,
|
|
exit_after_one_done=True)
|
|
|
|
|
|
class ToggleLBRYFileRunningChooserFactory(ControlHandlerFactory):
|
|
control_handler_class = ToggleLBRYFileRunningChooser
|
|
|
|
|
|
class ToggleLBRYFileRunning(ControlHandler):
|
|
prompt_description = "Toggle whether an LBRY File is running"
|
|
|
|
def __init__(self, lbry_file, lbry_file_manager):
|
|
self.lbry_file = lbry_file
|
|
self.lbry_file_manager = lbry_file_manager
|
|
|
|
def handle_line(self, line):
|
|
d = self.lbry_file_manager.toggle_lbry_file_running(self.lbry_file.stream_hash)
|
|
d.addErrback(self._handle_download_error)
|
|
return True, d
|
|
|
|
def _handle_download_error(self, err):
|
|
if err.check(InsufficientFundsError):
|
|
return "Download stopped due to insufficient funds."
|
|
else:
|
|
log.error("An unexpected error occurred due to toggling an LBRY file running. %s", err.getTraceback())
|
|
return "An unexpected error occurred. See console.log for details."
|
|
|
|
|
|
class ToggleLBRYFileRunningFactory(LBRYFileChooserFactory):
|
|
control_handler_class = ToggleLBRYFileRunning
|
|
|
|
|
|
class CreateLBRYFile(ControlHandler):
|
|
prompt_description = "Create an LBRY File from file"
|
|
line_prompt = "File name: "
|
|
|
|
def __init__(self, session, lbry_file_manager):
|
|
self.session = session
|
|
self.lbry_file_manager = lbry_file_manager
|
|
|
|
def handle_line(self, line):
|
|
if line is None:
|
|
return False, defer.succeed(self.line_prompt)
|
|
else:
|
|
d = create_lbry_file(self.session, self.lbry_file_manager, line, open(line))
|
|
d.addCallback(self.add_to_lbry_files)
|
|
d.addCallback(lambda _: "Successfully created " + str(line))
|
|
return True, d
|
|
|
|
def add_to_lbry_files(self, stream_hash):
|
|
prm = PaymentRateManager(self.session.base_payment_rate_manager)
|
|
d = self.lbry_file_manager.add_lbry_file(stream_hash, prm)
|
|
d.addCallback(self.set_status, stream_hash)
|
|
return d
|
|
|
|
def set_status(self, lbry_file_downloader, stream_hash):
|
|
d = self.lbry_file_manager.change_lbry_file_status(stream_hash,
|
|
ManagedLBRYFileDownloader.STATUS_FINISHED)
|
|
d.addCallback(lambda _: lbry_file_downloader.restore())
|
|
return d
|
|
|
|
|
|
class CreateLBRYFileFactory(ControlHandlerFactory):
|
|
control_handler_class = CreateLBRYFile
|
|
|
|
|
|
class PublishStreamDescriptorChooser(LBRYFileChooser):
|
|
prompt_description = "Publish a stream descriptor file to the DHT for an LBRY File"
|
|
|
|
def __init__(self, stream_info_manager, blob_manager, lbry_file_manager):
|
|
LBRYFileChooser.__init__(self, lbry_file_manager, PublishStreamDescriptorFactory, stream_info_manager,
|
|
blob_manager, lbry_file_manager, exit_after_one_done=True)
|
|
|
|
|
|
class PublishStreamDescriptorChooserFactory(ControlHandlerFactory):
|
|
control_handler_class = PublishStreamDescriptorChooser
|
|
|
|
|
|
class PublishStreamDescriptor(ControlHandler):
|
|
prompt_description = "Publish a stream descriptor file to the DHT for an LBRY File"
|
|
|
|
def __init__(self, lbry_file, stream_info_manager, blob_manager, lbry_file_manager):
|
|
self.lbry_file = lbry_file
|
|
self.stream_info_manager = stream_info_manager
|
|
self.blob_manager = blob_manager
|
|
self.lbry_file_manager = lbry_file_manager
|
|
|
|
def handle_line(self, line):
|
|
return True, self._publish_sd_blob()
|
|
|
|
def _publish_sd_blob(self):
|
|
descriptor_writer = BlobStreamDescriptorWriter(self.blob_manager)
|
|
|
|
d = get_sd_info(self.lbry_file_manager.stream_info_manager, self.lbry_file.stream_hash, True)
|
|
d.addCallback(descriptor_writer.create_descriptor)
|
|
|
|
def add_sd_blob_to_stream(sd_blob_hash):
|
|
d = self.stream_info_manager.save_sd_blob_hash_to_stream(self.lbry_file.stream_hash, sd_blob_hash)
|
|
d.addCallback(lambda _: sd_blob_hash)
|
|
return d
|
|
|
|
d.addCallback(add_sd_blob_to_stream)
|
|
return d
|
|
|
|
|
|
class PublishStreamDescriptorFactory(LBRYFileChooserFactory):
|
|
control_handler_class = PublishStreamDescriptor
|
|
|
|
|
|
class ShowPublishedSDHashesChooser(LBRYFileChooser):
|
|
prompt_description = "Show published stream descriptors for an LBRY File"
|
|
|
|
def __init__(self, stream_info_manager, lbry_file_manager):
|
|
LBRYFileChooser.__init__(self, lbry_file_manager, ShowPublishedSDHashesFactory, stream_info_manager,
|
|
lbry_file_manager)
|
|
|
|
|
|
class ShowPublishedSDHashesChooserFactory(ControlHandlerFactory):
|
|
control_handler_class = ShowPublishedSDHashesChooser
|
|
|
|
|
|
class ShowPublishedSDHashes(ControlHandler):
|
|
prompt_description = "Show published stream descriptors for an LBRY File"
|
|
|
|
def __init__(self, lbry_file, stream_info_manager, lbry_file_manager):
|
|
self.lbry_file = lbry_file
|
|
self.stream_info_manager = stream_info_manager
|
|
self.lbry_file_manager = lbry_file_manager
|
|
|
|
def handle_line(self, line):
|
|
return True, self._show_sd_hashes()
|
|
|
|
def _show_sd_hashes(self):
|
|
d = self.stream_info_manager.get_sd_blob_hashes_for_stream(self.lbry_file.stream_hash)
|
|
|
|
def format_blob_hashes(sd_blob_hashes):
|
|
return "\n".join([str(b) for b in sd_blob_hashes])
|
|
|
|
d.addCallback(format_blob_hashes)
|
|
return d
|
|
|
|
|
|
class ShowPublishedSDHashesFactory(LBRYFileChooserFactory):
|
|
control_handler_class = ShowPublishedSDHashes
|
|
|
|
|
|
class CreatePlainStreamDescriptorChooser(LBRYFileChooser):
|
|
prompt_description = "Create a plain stream descriptor file for an LBRY File"
|
|
|
|
def __init__(self, lbry_file_manager):
|
|
LBRYFileChooser.__init__(self, lbry_file_manager, CreatePlainStreamDescriptorFactory, lbry_file_manager,
|
|
exit_after_one_done=True)
|
|
|
|
|
|
class CreatePlainStreamDescriptorChooserFactory(ControlHandlerFactory):
|
|
control_handler_class = CreatePlainStreamDescriptorChooser
|
|
|
|
|
|
class CreatePlainStreamDescriptor(ControlHandler):
|
|
prompt_description = "Create a plain stream descriptor file for an LBRY File"
|
|
|
|
def __init__(self, lbry_file, lbry_file_manager):
|
|
self.lbry_file = lbry_file
|
|
self.lbry_file_manager = lbry_file_manager
|
|
self.sd_file_name = None
|
|
|
|
def handle_line(self, line):
|
|
if line is None:
|
|
return False, self._get_file_name_prompt()
|
|
self.sd_file_name = line
|
|
d = threads.deferToThread(self._get_file_name)
|
|
d.addCallback(self._create_sd)
|
|
return True, d
|
|
|
|
def _get_file_name_prompt(self):
|
|
file_name = self.lbry_file.file_name
|
|
if not file_name:
|
|
file_name = "_"
|
|
file_name += ".cryptsd"
|
|
return defer.succeed("Stream Descriptor file name (blank for default, %s):" % file_name)
|
|
|
|
def _get_file_name(self):
|
|
if self.sd_file_name:
|
|
file_name = self.sd_file_name
|
|
else:
|
|
file_name = self.lbry_file.file_name
|
|
if not file_name:
|
|
file_name = "_"
|
|
file_name += ".cryptsd"
|
|
if os.path.exists(file_name):
|
|
ext_num = 1
|
|
while os.path.exists(file_name + "_" + str(ext_num)):
|
|
ext_num += 1
|
|
file_name = file_name + "_" + str(ext_num)
|
|
return file_name
|
|
|
|
def _create_sd(self, file_name):
|
|
descriptor_writer = PlainStreamDescriptorWriter(file_name)
|
|
d = get_sd_info(self.lbry_file_manager.stream_info_manager, self.lbry_file.stream_hash, True)
|
|
d.addCallback(descriptor_writer.create_descriptor)
|
|
d.addCallback(lambda sd_file_name: "Wrote stream metadata to " + sd_file_name)
|
|
return d
|
|
|
|
|
|
class CreatePlainStreamDescriptorFactory(LBRYFileChooserFactory):
|
|
control_handler_class = CreatePlainStreamDescriptor
|
|
|
|
|
|
class ShowLBRYFileStreamHashChooser(LBRYFileChooser):
|
|
prompt_description = "Show an LBRY File's stream hash (not usually what you want)"
|
|
|
|
def __init__(self, lbry_file_manager):
|
|
LBRYFileChooser.__init__(self, lbry_file_manager, ShowLBRYFileStreamHashFactory)
|
|
|
|
|
|
class ShowLBRYFileStreamHashChooserFactory(ControlHandlerFactory):
|
|
control_handler_class = ShowLBRYFileStreamHashChooser
|
|
|
|
|
|
class ShowLBRYFileStreamHash(ControlHandler):
|
|
prompt_description = "Show an LBRY File's stream hash (not usually what you want)"
|
|
|
|
def __init__(self, lbry_file):
|
|
self.lbry_file = lbry_file
|
|
|
|
def handle_line(self, line):
|
|
return True, defer.succeed(str(self.lbry_file.stream_hash))
|
|
|
|
|
|
class ShowLBRYFileStreamHashFactory(LBRYFileChooserFactory):
|
|
control_handler_class = ShowLBRYFileStreamHash
|
|
|
|
|
|
class ModifyLBRYFileDataPaymentRate(ModifyPaymentRate):
|
|
prompt_description = "Modify LBRY File data payment rate"
|
|
|
|
def __init__(self, lbry_file, lbry_file_manager):
|
|
ModifyPaymentRate.__init__(self)
|
|
self._prompt_choices['unset'] = (self._unset, "Use the default LBRY file data rate")
|
|
self.lbry_file = lbry_file
|
|
self.lbry_file_manager = lbry_file_manager
|
|
self.payment_rate_manager = lbry_file.payment_rate_manager
|
|
|
|
def _unset(self):
|
|
d = self._set_rate(None)
|
|
d.addCallback(lambda _: "Using the default LBRY file data rate")
|
|
return True, d
|
|
|
|
def _set_rate(self, rate):
|
|
self.payment_rate_manager.min_blob_data_payment_rate = rate
|
|
return self.lbry_file_manager.set_lbry_file_data_payment_rate(self.lbry_file.stream_hash, rate)
|
|
|
|
def _get_current_status(self):
|
|
status = "The LBRY file's current data payment rate is "
|
|
effective_rate = self.payment_rate_manager.get_effective_min_blob_data_payment_rate()
|
|
if self.payment_rate_manager.min_blob_data_payment_rate is None:
|
|
status += "set to use the default LBRY file data payment rate, "
|
|
status += str(effective_rate)
|
|
return status
|
|
|
|
|
|
class ModifyLBRYFileDataPaymentRateFactory(ControlHandlerFactory):
|
|
control_handler_class = ModifyLBRYFileDataPaymentRate
|
|
|
|
|
|
class ModifyLBRYFileOptionsChooser(LBRYFileChooser):
|
|
prompt_description = "Modify an LBRY File's options"
|
|
|
|
def __init__(self, lbry_file_manager):
|
|
LBRYFileChooser.__init__(self, lbry_file_manager, ModifyLBRYFileOptionsFactory, lbry_file_manager)
|
|
|
|
|
|
class ModifyLBRYFileOptionsChooserFactory(ControlHandlerFactory):
|
|
control_handler_class = ModifyLBRYFileOptionsChooser
|
|
|
|
|
|
class ModifyLBRYFileOptions(RecursiveControlHandler):
|
|
prompt_description = "Modify an LBRY File's options"
|
|
|
|
def __init__(self, lbry_file, lbry_file_manager):
|
|
self.lbry_file = lbry_file
|
|
self.lbry_file_manager = lbry_file_manager
|
|
RecursiveControlHandler.__init__(self)
|
|
|
|
def _get_control_handler_factories(self):
|
|
factories = []
|
|
factories.append(ModifyLBRYFileDataPaymentRateFactory(self.lbry_file, self.lbry_file_manager))
|
|
return factories
|
|
|
|
|
|
class ModifyLBRYFileOptionsFactory(LBRYFileChooserFactory):
|
|
control_handler_class = ModifyLBRYFileOptions
|
|
|
|
|
|
class ClaimName(ControlHandler):
|
|
prompt_description = "Publish to an lbry:// address"
|
|
other_hash_prompt = "Enter the hash you would like to publish:"
|
|
short_desc_prompt = "Enter a short description:"
|
|
sd_failure_message = "Unable to find a stream descriptor for that file.\n\nPress enter to continue"
|
|
requested_price_prompt = "Enter the fee others should pay for the decryption key for this stream. Leave blank for no fee:"
|
|
lbrycrd_address_prompt = "Enter the LBRYcrd address to which the key fee should be sent. If left blank a new address will be used from the wallet:"
|
|
bid_amount_prompt = "Enter the number of credits you wish to use to support your bid for the name:"
|
|
choose_name_prompt = "Enter the name to which you would like to publish:"
|
|
|
|
def __init__(self, wallet, lbry_file_manager, blob_manager, sd_identifier):
|
|
self.wallet = wallet
|
|
self.lbry_file_manager = lbry_file_manager
|
|
self.blob_manager = blob_manager
|
|
self.sd_identifier = sd_identifier
|
|
self.file_type_options = []
|
|
self.file_type_chosen = None
|
|
self.lbry_file_list = []
|
|
self.sd_hash = None
|
|
self.key_fee = None
|
|
self.key_fee_chosen = False
|
|
self.need_address = True
|
|
self.chosen_address = None
|
|
self.bid_amount = None
|
|
self.chosen_name = None
|
|
self.failed = False
|
|
self.short_description = None
|
|
self.verified = False
|
|
|
|
def handle_line(self, line):
|
|
if line is None:
|
|
return False, defer.succeed(self._get_file_type_options())
|
|
if self.failed is True:
|
|
return True, defer.succeed(None)
|
|
if self.file_type_chosen is None:
|
|
try:
|
|
choice = int(line)
|
|
except ValueError:
|
|
choice = -1
|
|
if choice < 0 or choice >= len(self.file_type_options):
|
|
return False, defer.succeed("You must enter a valid number.\n\n%s" % self._get_file_type_options())
|
|
if self.file_type_options[choice][0] is None:
|
|
return True, defer.succeed("Publishing canceled.")
|
|
self.file_type_chosen = self.file_type_options[choice][0]
|
|
if self.file_type_chosen == "hash":
|
|
return False, defer.succeed(self.other_hash_prompt)
|
|
else:
|
|
return False, self._set_sd_hash_and_get_desc_prompt()
|
|
if self.sd_hash is None:
|
|
self.sd_hash = line
|
|
return False, defer.succeed(self.short_desc_prompt)
|
|
if self.short_description is None:
|
|
self.short_description = line
|
|
return False, defer.succeed(self.requested_price_prompt)
|
|
if self.key_fee_chosen is False:
|
|
if line:
|
|
try:
|
|
self.key_fee = float(line)
|
|
except ValueError:
|
|
return False, defer.succeed("Leave blank or enter a floating point number.\n\n%s" % self.requested_price_prompt)
|
|
self.key_fee_chosen = True
|
|
if self.key_fee is None or self.key_fee <= 0:
|
|
self.need_address = False
|
|
return False, defer.succeed(self.bid_amount_prompt)
|
|
return False, defer.succeed(self.lbrycrd_address_prompt)
|
|
if self.need_address is True:
|
|
if line:
|
|
self.chosen_address = line
|
|
d = defer.succeed(None)
|
|
else:
|
|
d = self._get_new_address()
|
|
self.need_address = False
|
|
d.addCallback(lambda _: self.bid_amount_prompt)
|
|
return False, d
|
|
if self.bid_amount is None:
|
|
try:
|
|
self.bid_amount = float(line)
|
|
except ValueError:
|
|
return False, defer.succeed("Must be a floating point number.\n\n%s" % self.bid_amount_prompt)
|
|
return False, defer.succeed(self.choose_name_prompt)
|
|
if self.chosen_name is None:
|
|
self.chosen_name = line
|
|
return False, defer.succeed(self._get_verification_prompt())
|
|
if self.verified is False:
|
|
if line.lower() == "yes":
|
|
return True, self._claim_name()
|
|
else:
|
|
return True, defer.succeed("Claim canceled")
|
|
|
|
def _get_file_type_options(self):
|
|
options = []
|
|
pattern = "[%d] %s\n"
|
|
prompt_string = "What would you like to publish?\n"
|
|
prompt_string += "LBRY Files:\n"
|
|
i = 0
|
|
for lbry_file in self.lbry_file_manager.lbry_files:
|
|
options.append((lbry_file, lbry_file.file_name))
|
|
prompt_string += pattern % (i, lbry_file.file_name)
|
|
i += 1
|
|
prompt_string += "Other:\n"
|
|
options.append(("hash", "Enter a hash"))
|
|
prompt_string += pattern % (i, "Enter a hash")
|
|
i += 1
|
|
options.append((None, "Cancel"))
|
|
prompt_string += pattern % (i, "Cancel")
|
|
self.file_type_options = options
|
|
return prompt_string
|
|
|
|
def _choose_sd(self, sd_blob_hashes):
|
|
if not sd_blob_hashes:
|
|
self.failed = True
|
|
return defer.succeed(False)
|
|
self.sd_hash = sd_blob_hashes[0]
|
|
return defer.succeed(True)
|
|
|
|
def _set_sd_hash_and_get_desc_prompt(self):
|
|
d = self.lbry_file_manager.stream_info_manager.get_sd_blob_hashes_for_stream(self.file_type_chosen.stream_hash)
|
|
d.addCallback(self._choose_sd)
|
|
d.addCallback(lambda success: self.short_desc_prompt if success else self.sd_failure_message)
|
|
return d
|
|
|
|
def _get_new_address(self):
|
|
d = self.wallet.get_new_address()
|
|
|
|
def set_address(address):
|
|
self.chosen_address = address
|
|
|
|
d.addCallback(set_address)
|
|
return d
|
|
|
|
def _get_verification_prompt(self):
|
|
v_string = "Ensure the following details are correct:\n"
|
|
if self.file_type_chosen != "hash":
|
|
v_string += "File name: %s\n" % str(self.file_type_chosen.file_name)
|
|
v_string += "Hash: %s\n" % str(self.sd_hash)
|
|
v_string += "Description: %s\n" % str(self.short_description)
|
|
v_string += "Key fee: %s\n" % str(self.key_fee)
|
|
if self.chosen_address is not None:
|
|
v_string += "Key fee address: %s\n" % str(self.chosen_address)
|
|
v_string += "Bid amount: %s\n" % str(self.bid_amount)
|
|
v_string += "Name: %s\n" % str(self.chosen_name)
|
|
v_string += "\nIf this is correct, type 'yes'. Otherwise, type 'no' and the bid will be aborted:"
|
|
return v_string
|
|
|
|
def _claim_name(self):
|
|
d = self.wallet.claim_name(self.chosen_name, self.sd_hash, float(self.bid_amount),
|
|
description=self.short_description, key_fee=self.key_fee,
|
|
key_fee_address=self.chosen_address)
|
|
d.addCallback(lambda response: str(response))
|
|
return d
|
|
|
|
|
|
class ClaimNameFactory(ControlHandlerFactory):
|
|
control_handler_class = ClaimName
|
|
|
|
|
|
class ModifyDefaultDataPaymentRate(ModifyPaymentRate):
|
|
prompt_description = "Modify default data payment rate"
|
|
|
|
def __init__(self, payment_rate_manager, settings):
|
|
ModifyPaymentRate.__init__(self)
|
|
self.settings = settings
|
|
self.payment_rate_manager = payment_rate_manager
|
|
|
|
def _set_rate(self, rate):
|
|
self.payment_rate_manager.min_blob_data_payment_rate = rate
|
|
return self.settings.save_default_data_payment_rate(rate)
|
|
|
|
def _get_current_status(self):
|
|
status = "The current default data payment rate is "
|
|
status += str(self.payment_rate_manager.min_blob_data_payment_rate)
|
|
return status
|
|
|
|
|
|
class ModifyDefaultDataPaymentRateFactory(ControlHandlerFactory):
|
|
control_handler_class = ModifyDefaultDataPaymentRate
|
|
|
|
|
|
class ForceCheckBlobFileConsistency(ControlHandler):
|
|
prompt_description = "Verify consistency of stored blobs"
|
|
|
|
def __init__(self, blob_manager):
|
|
self.blob_manager = blob_manager
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "Check consistency should not be passed any arguments"
|
|
return True, self._check_consistency()
|
|
|
|
def _check_consistency(self):
|
|
d = self.blob_manager.check_consistency()
|
|
d.addCallback(lambda _: "Finished checking stored blobs")
|
|
return d
|
|
|
|
|
|
class ForceCheckBlobFileConsistencyFactory(ControlHandlerFactory):
|
|
control_handler_class = ForceCheckBlobFileConsistency
|
|
|
|
|
|
class ModifyApplicationDefaults(RecursiveControlHandler):
|
|
prompt_description = "Modify application settings"
|
|
|
|
def __init__(self, lbry_service):
|
|
self.lbry_service = lbry_service
|
|
RecursiveControlHandler.__init__(self)
|
|
|
|
def _get_control_handler_factories(self):
|
|
return [ModifyDefaultDataPaymentRateFactory(self.lbry_service.session.base_payment_rate_manager,
|
|
self.lbry_service.settings),
|
|
ForceCheckBlobFileConsistencyFactory(self.lbry_service.session.blob_manager)]
|
|
|
|
|
|
class ModifyApplicationDefaultsFactory(ControlHandlerFactory):
|
|
control_handler_class = ModifyApplicationDefaults
|
|
|
|
|
|
class ShowServerStatus(ControlHandler):
|
|
prompt_description = "Show the status of the server"
|
|
|
|
def __init__(self, lbry_service):
|
|
self.lbry_service = lbry_service
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "Show server status should not be passed any arguments"
|
|
return True, self._get_status()
|
|
|
|
def _get_status(self):
|
|
status_string = "Server status:\n"
|
|
status_string += "Port: " + str(self.lbry_service.peer_port) + "\n"
|
|
status_string += "Running: " + str(self.lbry_service.lbry_server_port is not None) + "\n"
|
|
if self.lbry_service.blob_request_payment_rate_manager is not None:
|
|
rate = self.lbry_service.blob_request_payment_rate_manager.get_effective_min_blob_data_payment_rate()
|
|
status_string += "Min blob data payment rate: "
|
|
if self.lbry_service.blob_request_payment_rate_manager.min_blob_data_payment_rate is None:
|
|
status_string += "Using application default (" + str(rate) + ")\n"
|
|
else:
|
|
status_string += str(rate)
|
|
status_string += "\n"
|
|
#status_string += "Min crypt info payment rate: "
|
|
#status_string += str(self.lbry_service._server_payment_rate_manager.get_min_live_blob_info_payment_rate())
|
|
#status_string += "\n"
|
|
return defer.succeed(status_string)
|
|
|
|
|
|
class ShowServerStatusFactory(ControlHandlerFactory):
|
|
control_handler_class = ShowServerStatus
|
|
|
|
|
|
class StartServer(ControlHandler):
|
|
prompt_description = "Start the server"
|
|
|
|
def __init__(self, lbry_service):
|
|
self.lbry_service = lbry_service
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "Start server should not be passed any arguments"
|
|
d = self.lbry_service.start_server()
|
|
d.addCallback(lambda _: self.lbry_service.settings.save_server_running_status(running=True))
|
|
d.addCallback(lambda _: "Successfully started the server")
|
|
return True, d
|
|
|
|
|
|
class StartServerFactory(ControlHandlerFactory):
|
|
control_handler_class = StartServer
|
|
|
|
|
|
class StopServer(ControlHandler):
|
|
prompt_description = "Stop the server"
|
|
|
|
def __init__(self, lbry_service):
|
|
self.lbry_service = lbry_service
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "Stop server should not be passed any arguments"
|
|
d = self.lbry_service.stop_server()
|
|
d.addCallback(lambda _: self.lbry_service.settings.save_server_running_status(running=False))
|
|
d.addCallback(lambda _: "Successfully stopped the server")
|
|
return True, d
|
|
|
|
|
|
class StopServerFactory(ControlHandlerFactory):
|
|
control_handler_class = StopServer
|
|
|
|
|
|
class ModifyServerDataPaymentRate(ModifyPaymentRate):
|
|
prompt_description = "Modify server data payment rate"
|
|
|
|
def __init__(self, payment_rate_manager, settings):
|
|
ModifyPaymentRate.__init__(self)
|
|
self._prompt_choices['unset'] = (self._unset, "Use the application default data rate")
|
|
self.settings = settings
|
|
self.payment_rate_manager = payment_rate_manager
|
|
|
|
def _unset(self):
|
|
d = self._set_rate(None)
|
|
d.addCallback(lambda _: "Using the application default data rate")
|
|
return True, d
|
|
|
|
def _set_rate(self, rate):
|
|
self.payment_rate_manager.min_blob_data_payment_rate = rate
|
|
return self.settings.save_server_data_payment_rate(rate)
|
|
|
|
def _get_current_status(self):
|
|
effective_rate = self.payment_rate_manager.get_effective_min_blob_data_payment_rate()
|
|
status = "The current server data payment rate is "
|
|
if self.payment_rate_manager.min_blob_data_payment_rate is None:
|
|
status += "set to use the application default, "
|
|
status += str(effective_rate)
|
|
return status
|
|
|
|
|
|
class ModifyServerDataPaymentRateFactory(ControlHandlerFactory):
|
|
control_handler_class = ModifyServerDataPaymentRate
|
|
|
|
|
|
# class ModifyServerCryptInfoPaymentRate(ModifyPaymentRate):
|
|
# prompt_description = "Modify server live stream metadata payment rate"
|
|
#
|
|
# def __init__(self, payment_rate_manager, settings):
|
|
# ModifyPaymentRate.__init__(self)
|
|
# self._prompt_choices['unset'] = (self._unset,
|
|
# "Use the application default live stream metadata rate")
|
|
# self.settings = settings
|
|
# self.payment_rate_manager = payment_rate_manager
|
|
#
|
|
# def _unset(self):
|
|
# d = self._set_rate(None)
|
|
# d.addCallback(lambda _: "Using the application default live stream metadata rate")
|
|
# return True, d
|
|
#
|
|
# def _set_rate(self, rate):
|
|
# self.payment_rate_manager.min_live_blob_info_payment_rate = rate
|
|
# return self.settings.save_server_crypt_info_payment_rate(rate)
|
|
#
|
|
# def _get_current_status(self):
|
|
# effective_rate = self.payment_rate_manager.get_effective_min_live_blob_info_payment_rate()
|
|
# status = "The current server live stream metadata payment rate is "
|
|
# if self.payment_rate_manager.get_min_blob_data_payment_rate() is None:
|
|
# status += "set to use the application default, "
|
|
# status += str(effective_rate)
|
|
# else:
|
|
# status += str(effective_rate)
|
|
# return status
|
|
#
|
|
#
|
|
# class ModifyServerCryptInfoPaymentRateFactory(ControlHandlerFactory):
|
|
# control_handler_class = ModifyServerCryptInfoPaymentRate
|
|
|
|
|
|
class DisableQueryHandler(ControlHandler):
|
|
def __init__(self, query_handlers, query_handler, settings):
|
|
self.query_handlers = query_handlers
|
|
self.query_handler = query_handler
|
|
self.settings = settings
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "DisableQueryHandler should not be passed any arguments"
|
|
self.query_handlers[self.query_handler] = False
|
|
d = self.settings.disable_query_handler(self.query_handler.get_primary_query_identifier())
|
|
d.addCallback(lambda _: "Disabled the query handler")
|
|
return True, d
|
|
|
|
|
|
class DisableQueryHandlerFactory(ControlHandlerFactory):
|
|
control_handler_class = DisableQueryHandler
|
|
|
|
def get_prompt_description(self):
|
|
query_handler = self.args[1]
|
|
return "Disable " + str(query_handler.get_description())
|
|
|
|
|
|
class EnableQueryHandler(ControlHandler):
|
|
def __init__(self, query_handlers, query_handler, settings):
|
|
self.query_handlers = query_handlers
|
|
self.query_handler = query_handler
|
|
self.settings = settings
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "EnableQueryHandler should not be passed any arguments"
|
|
self.query_handlers[self.query_handler] = True
|
|
d = self.settings.enable_query_handler(self.query_handler.get_primary_query_identifier())
|
|
d.addCallback(lambda _: "Enabled the query handler")
|
|
return True, d
|
|
|
|
|
|
class EnableQueryHandlerFactory(ControlHandlerFactory):
|
|
control_handler_class = EnableQueryHandler
|
|
|
|
def get_prompt_description(self):
|
|
query_handler = self.args[1]
|
|
return "Enable " + str(query_handler.get_description())
|
|
|
|
|
|
class ModifyServerEnabledQueries(RecursiveControlHandler):
|
|
prompt_description = "Modify which queries the server will respond to"
|
|
|
|
def __init__(self, query_handlers, settings):
|
|
self.query_handlers = query_handlers
|
|
self.settings = settings
|
|
RecursiveControlHandler.__init__(self, reset_after_each_done=True)
|
|
|
|
def _get_control_handler_factories(self):
|
|
factories = []
|
|
for query_handler, enabled in self.query_handlers.iteritems():
|
|
if enabled:
|
|
factories.append(DisableQueryHandlerFactory(self.query_handlers, query_handler, self.settings))
|
|
else:
|
|
factories.append(EnableQueryHandlerFactory(self.query_handlers, query_handler, self.settings))
|
|
return factories
|
|
|
|
|
|
class ModifyServerEnabledQueriesFactory(ControlHandlerFactory):
|
|
control_handler_class = ModifyServerEnabledQueries
|
|
|
|
|
|
class ImmediateAnnounceAllBlobs(ControlHandler):
|
|
prompt_description = "Immediately announce all blob hashes to the DHT"
|
|
|
|
def __init__(self, blob_manager):
|
|
self.blob_manager = blob_manager
|
|
|
|
def handle_line(self, line):
|
|
assert line is None, "Immediate Announce should not be passed any arguments"
|
|
d = self.blob_manager.immediate_announce_all_blobs()
|
|
d.addCallback(lambda _: "Done announcing")
|
|
return True, d
|
|
|
|
|
|
class ImmediateAnnounceAllBlobsFactory(ControlHandlerFactory):
|
|
control_handler_class = ImmediateAnnounceAllBlobs
|
|
|
|
|
|
class ModifyServerSettings(RecursiveControlHandler):
|
|
prompt_description = "Modify server settings"
|
|
|
|
def __init__(self, lbry_service):
|
|
self.lbry_service = lbry_service
|
|
RecursiveControlHandler.__init__(self, reset_after_each_done=True)
|
|
|
|
def _get_control_handler_factories(self):
|
|
factories = []
|
|
if self.lbry_service.lbry_server_port is not None:
|
|
factories.append(StopServerFactory(self.lbry_service))
|
|
else:
|
|
factories.append(StartServerFactory(self.lbry_service))
|
|
factories.append(
|
|
ModifyServerDataPaymentRateFactory(
|
|
self.lbry_service.blob_request_payment_rate_manager,
|
|
self.lbry_service.settings
|
|
)
|
|
)
|
|
#factories.append(ModifyServerCryptInfoPaymentRateFactory(self.lbry_service._server_payment_rate_manager,
|
|
# self.lbry_service.settings))
|
|
factories.append(ModifyServerEnabledQueriesFactory(self.lbry_service.query_handlers,
|
|
self.lbry_service.settings))
|
|
factories.append(ImmediateAnnounceAllBlobsFactory(self.lbry_service.session.blob_manager))
|
|
return factories
|
|
|
|
|
|
class ModifyServerSettingsFactory(ControlHandlerFactory):
|
|
control_handler_class = ModifyServerSettings
|
|
|
|
|
|
class PeerChooser(RecursiveControlHandler):
|
|
|
|
def __init__(self, peer_manager, factory_class, *args, **kwargs):
|
|
"""
|
|
@param peer_manager:
|
|
|
|
@param factory_class:
|
|
|
|
@param args: all arguments that will be passed to the factory
|
|
|
|
@param kwargs: all arguments that will be passed to the superclass' __init__
|
|
|
|
@return:
|
|
"""
|
|
self.peer_manager = peer_manager
|
|
self.factory_class = factory_class
|
|
self.args = args
|
|
RecursiveControlHandler.__init__(self, **kwargs)
|
|
|
|
def _get_control_handler_factories(self):
|
|
control_handler_factories = []
|
|
for peer in self.peer_manager.peers:
|
|
control_handler_factories.append(self.factory_class(peer, *self.args))
|
|
return control_handler_factories
|
|
|
|
|
|
class PeerChooserFactory(ControlHandlerFactory):
|
|
def get_prompt_description(self):
|
|
peer = self.args[0]
|
|
return str(peer)
|
|
|
|
|
|
class ShowPeerStats(ControlHandler):
|
|
prompt_description = "Show the peer's stats"
|
|
|
|
def __init__(self, peer):
|
|
self.peer = peer
|
|
|
|
def handle_line(self, line):
|
|
return True, defer.succeed(self._get_peer_stats_string())
|
|
|
|
def _get_peer_stats_string(self):
|
|
stats = "Statistics for " + str(self.peer) + '\n'
|
|
stats += " current_score: " + str(self.peer.score) + '\n'
|
|
stats += " is_available: " + str(self.peer.is_available()) + '\n'
|
|
for stat_type, amount in self.peer.stats.iteritems():
|
|
stats += " " + stat_type + ": " + str(amount) + "\n"
|
|
return stats
|
|
|
|
|
|
class ShowPeerStatsFactory(ControlHandlerFactory):
|
|
control_handler_class = ShowPeerStats
|
|
|
|
|
|
class PeerStatsAndSettings(RecursiveControlHandler):
|
|
def __init__(self, peer):
|
|
self.peer = peer
|
|
RecursiveControlHandler.__init__(self, reset_after_each_done=True)
|
|
|
|
def _get_control_handler_factories(self):
|
|
factories = []
|
|
factories.append(ShowPeerStatsFactory(self.peer))
|
|
return factories
|
|
|
|
|
|
class PeerStatsAndSettingsFactory(PeerChooserFactory):
|
|
control_handler_class = PeerStatsAndSettings
|
|
|
|
|
|
class PeerStatsAndSettingsChooser(PeerChooser):
|
|
prompt_description = "View peer stats and modify peer settings"
|
|
|
|
def __init__(self, peer_manager):
|
|
PeerChooser.__init__(self, peer_manager, PeerStatsAndSettingsFactory)
|
|
|
|
|
|
class PeerStatsAndSettingsChooserFactory(ControlHandlerFactory):
|
|
control_handler_class = PeerStatsAndSettingsChooser |