diff --git a/lbrynet/core/DownloadOption.py b/lbrynet/core/DownloadOption.py index 0972cbe19..0e990902e 100644 --- a/lbrynet/core/DownloadOption.py +++ b/lbrynet/core/DownloadOption.py @@ -1,6 +1,16 @@ +class DownloadChoice(object): + def __init__(self, value, short_description, long_description, bool_options_description=None): + self.value = value + self.short_description = short_description + self.long_description = long_description + self.bool_options_description = bool_options_description + + class DownloadOption(object): - def __init__(self, option_types, long_description, short_description, default): + def __init__(self, option_types, long_description, short_description, default_value, + default_value_description): self.option_types = option_types self.long_description = long_description self.short_description = short_description - self.default = default \ No newline at end of file + self.default_value = default_value + self.default_value_description = default_value_description \ No newline at end of file diff --git a/lbrynet/core/StreamDescriptor.py b/lbrynet/core/StreamDescriptor.py index 1a33dbb64..04ed216ff 100644 --- a/lbrynet/core/StreamDescriptor.py +++ b/lbrynet/core/StreamDescriptor.py @@ -110,9 +110,10 @@ class StreamDescriptorIdentifier(object): """ def __init__(self): self._sd_info_validators = {} # {stream_type: IStreamDescriptorValidator} + self._stream_options = {} # {stream_type: IStreamOptions} self._stream_downloader_factories = defaultdict(list) # {stream_type: [IStreamDownloaderFactory]} - def add_stream_info_validator(self, stream_type, sd_info_validator): + def add_stream_type(self, stream_type, sd_info_validator, stream_options): """ This is how the StreamDescriptorIdentifier learns about new types of stream descriptors. @@ -126,9 +127,13 @@ class StreamDescriptorIdentifier(object): will then be called. If the validation step fails, an exception will be thrown, preventing the stream descriptor from being further processed. + @param stream_options: A class implementing the IStreamOptions interface. This class's constructor will be + passed the sd_info_validator object containing the raw metadata from the stream descriptor file. + @return: None """ self._sd_info_validators[stream_type] = sd_info_validator + self._stream_options[stream_type] = stream_options def add_stream_downloader_factory(self, stream_type, factory): """ @@ -167,14 +172,23 @@ class StreamDescriptorIdentifier(object): assert stream_type in self._sd_info_validators, "Unrecognized stream type: " + str(stream_type) return self._sd_info_validators[stream_type] + def _get_options(self, stream_type): + assert stream_type in self._sd_info_validators, "Unrecognized stream type: " + str(stream_type) + return self._stream_options[stream_type] + def _return_info_and_factories(self, sd_info): assert 'stream_type' in sd_info, 'Invalid stream descriptor. No stream_type parameter.' stream_type = sd_info['stream_type'] - factories = self._get_factories(stream_type) validator = self._get_validator(stream_type)(sd_info) + factories = [f for f in self._get_factories(stream_type) if f.can_download(validator)] + d = validator.validate() - d.addCallback(lambda _: (validator, factories)) + def get_options(): + options = self._get_options(stream_type) + return validator, options, factories + + d.addCallback(lambda _: get_options()) return d diff --git a/lbrynet/core/client/ConnectionManager.py b/lbrynet/core/client/ConnectionManager.py index 6f8ce06b9..11602fcb4 100644 --- a/lbrynet/core/client/ConnectionManager.py +++ b/lbrynet/core/client/ConnectionManager.py @@ -7,49 +7,55 @@ from lbrynet.core.client.ClientProtocol import ClientProtocolFactory from lbrynet.core.Error import InsufficientFundsError +class PeerConnectionHandler(object): + def __init__(self, request_creators, factory): + self.request_creators = request_creators + self.factory = factory + self.connection = None + + class ConnectionManager(object): implements(interfaces.IConnectionManager) def __init__(self, downloader, rate_limiter, primary_request_creators, secondary_request_creators): self.downloader = downloader self.rate_limiter = rate_limiter - self.primary_request_creators = primary_request_creators - self.secondary_request_creators = secondary_request_creators - self.peer_connections = {} # {Peer: {'connection': connection, - # 'request_creators': [IRequestCreator if using this connection]}} - self.connections_closing = {} # {Peer: deferred (fired when the connection is closed)} - self.next_manage_call = None + self._primary_request_creators = primary_request_creators + self._secondary_request_creators = secondary_request_creators + self._peer_connections = {} # {Peer: PeerConnectionHandler} + self._connections_closing = {} # {Peer: deferred (fired when the connection is closed)} + self._next_manage_call = None def start(self): from twisted.internet import reactor - if self.next_manage_call is not None and self.next_manage_call.active() is True: - self.next_manage_call.cancel() - self.next_manage_call = reactor.callLater(0, self._manage) + if self._next_manage_call is not None and self._next_manage_call.active() is True: + self._next_manage_call.cancel() + self._next_manage_call = reactor.callLater(0, self._manage) return defer.succeed(True) def stop(self): - if self.next_manage_call is not None and self.next_manage_call.active() is True: - self.next_manage_call.cancel() - self.next_manage_call = None + if self._next_manage_call is not None and self._next_manage_call.active() is True: + self._next_manage_call.cancel() + self._next_manage_call = None closing_deferreds = [] - for peer in self.peer_connections.keys(): + for peer in self._peer_connections.keys(): def close_connection(p): logging.info("Abruptly closing a connection to %s due to downloading being paused", str(p)) - if self.peer_connections[p]['factory'].p is not None: - d = self.peer_connections[p]['factory'].p.cancel_requests() + if self._peer_connections[p].factory.p is not None: + d = self._peer_connections[p].factory.p.cancel_requests() else: d = defer.succeed(True) def disconnect_peer(): - self.peer_connections[p]['connection'].disconnect() - if p in self.peer_connections: - del self.peer_connections[p] d = defer.Deferred() - self.connections_closing[p] = d + self._connections_closing[p] = d + self._peer_connections[p].connection.disconnect() + if p in self._peer_connections: + del self._peer_connections[p] return d d.addBoth(lambda _: disconnect_peer()) @@ -62,7 +68,7 @@ class ConnectionManager(object): logging.debug("Trying to get the next request for peer %s", str(peer)) - if not peer in self.peer_connections: + if not peer in self._peer_connections: logging.debug("The peer has already been told to shut down.") return defer.succeed(False) @@ -75,11 +81,11 @@ class ConnectionManager(object): def check_if_request_sent(request_sent, request_creator): if request_sent is False: - if request_creator in self.peer_connections[peer]['request_creators']: - self.peer_connections[peer]['request_creators'].remove(request_creator) + if request_creator in self._peer_connections[peer].request_creators: + self._peer_connections[peer].request_creators.remove(request_creator) else: - if not request_creator in self.peer_connections[peer]['request_creators']: - self.peer_connections[peer]['request_creators'].append(request_creator) + if not request_creator in self._peer_connections[peer].request_creators: + self._peer_connections[peer].request_creators.append(request_creator) return request_sent def check_requests(requests): @@ -89,7 +95,7 @@ class ConnectionManager(object): def get_secondary_requests_if_necessary(have_request): if have_request is True: ds = [] - for s_r_c in self.secondary_request_creators: + for s_r_c in self._secondary_request_creators: d = s_r_c.send_next_request(peer, protocol) ds.append(d) dl = defer.DeferredList(ds) @@ -100,7 +106,7 @@ class ConnectionManager(object): ds = [] - for p_r_c in self.primary_request_creators: + for p_r_c in self._primary_request_creators: d = p_r_c.send_next_request(peer, protocol) d.addErrback(handle_error) d.addCallback(check_if_request_sent, p_r_c) @@ -112,11 +118,11 @@ class ConnectionManager(object): return dl def protocol_disconnected(self, peer, protocol): - if peer in self.peer_connections: - del self.peer_connections[peer] - if peer in self.connections_closing: - d = self.connections_closing[peer] - del self.connections_closing[peer] + if peer in self._peer_connections: + del self._peer_connections[peer] + if peer in self._connections_closing: + d = self._connections_closing[peer] + del self._connections_closing[peer] d.callback(True) def _rank_request_creator_connections(self): @@ -125,9 +131,9 @@ class ConnectionManager(object): connections open that it likes """ def count_peers(request_creator): - return len([p for p in self.peer_connections.itervalues() if request_creator in p['request_creators']]) + return len([p for p in self._peer_connections.itervalues() if request_creator in p.request_creators]) - return sorted(self.primary_request_creators, key=count_peers) + return sorted(self._primary_request_creators, key=count_peers) def _connect_to_peer(self, peer): @@ -136,10 +142,10 @@ class ConnectionManager(object): if peer is not None: logging.debug("Trying to connect to %s", str(peer)) factory = ClientProtocolFactory(peer, self.rate_limiter, self) + self._peer_connections[peer] = PeerConnectionHandler(self._primary_request_creators[:], + factory) connection = reactor.connectTCP(peer.host, peer.port, factory) - self.peer_connections[peer] = {'connection': connection, - 'request_creators': self.primary_request_creators[:], - 'factory': factory} + self._peer_connections[peer].connection = connection def _manage(self): @@ -162,16 +168,16 @@ class ConnectionManager(object): if peers is None: return None for peer in peers: - if not peer in self.peer_connections: + if not peer in self._peer_connections: logging.debug("Got a good peer. Returning peer %s", str(peer)) return peer logging.debug("Couldn't find a good peer to connect to") return None - if len(self.peer_connections) < MAX_CONNECTIONS_PER_STREAM: + if len(self._peer_connections) < MAX_CONNECTIONS_PER_STREAM: ordered_request_creators = self._rank_request_creator_connections() d = get_new_peers(ordered_request_creators) d.addCallback(pick_best_peer) d.addCallback(self._connect_to_peer) - self.next_manage_call = reactor.callLater(1, self._manage) \ No newline at end of file + self._next_manage_call = reactor.callLater(1, self._manage) \ No newline at end of file diff --git a/lbrynet/interfaces.py b/lbrynet/interfaces.py index 6eae6af0e..de5de679a 100644 --- a/lbrynet/interfaces.py +++ b/lbrynet/interfaces.py @@ -445,10 +445,7 @@ class IQueryHandlerFactory(Interface): """ -class IStreamDownloaderFactory(Interface): - """ - Construct IStreamDownloaders and provide options that will be passed to those IStreamDownloaders. - """ +class IStreamDownloaderOptions(Interface): def get_downloader_options(self, sd_validator, payment_rate_manager): """ Return the list of options that can be used to modify IStreamDownloader behavior @@ -459,8 +456,28 @@ class IStreamDownloaderFactory(Interface): @param payment_rate_manager: The payment rate manager currently in effect for the downloader @type payment_rate_manager: PaymentRateManager - @return: [(option_description, default)] - @rtype: [(string, string)] + @return: [DownloadOption] + @rtype: [DownloadOption] + """ + + +class IStreamDownloaderFactory(Interface): + """ + Construct IStreamDownloaders and provide options that will be passed to those IStreamDownloaders. + """ + + def can_download(self, sd_validator, payment_rate_manager): + """ + Decide whether the downloaders created by this factory can download the stream described by sd_validator + + @param sd_validator: object containing stream metadata + @type sd_validator: object which implements IStreamDescriptorValidator interface + + @param payment_rate_manager: The payment rate manager currently in effect for the downloader + @type payment_rate_manager: PaymentRateManager + + @return: True if the downloaders can download the stream, False otherwise + @rtype: bool """ def make_downloader(self, sd_validator, options, payment_rate_manager): @@ -470,10 +487,10 @@ class IStreamDownloaderFactory(Interface): @param sd_validator: object containing stream metadata which will be given to the IStreamDownloader @type sd_validator: object which implements IStreamDescriptorValidator interface - @param options: a list of strings that will be used by the IStreamDownloaderFactory to + @param options: a list of values that will be used by the IStreamDownloaderFactory to construct the IStreamDownloader. the options are in the same order as they were given by get_downloader_options. - @type options: [string] + @type options: [Object] @param payment_rate_manager: the PaymentRateManager which the IStreamDownloader should use. @type payment_rate_manager: PaymentRateManager diff --git a/lbrynet/lbryfile/client/LBRYFileDownloader.py b/lbrynet/lbryfile/client/LBRYFileDownloader.py index 9613ca76f..b3604b036 100644 --- a/lbrynet/lbryfile/client/LBRYFileDownloader.py +++ b/lbrynet/lbryfile/client/LBRYFileDownloader.py @@ -3,7 +3,6 @@ import binascii from zope.interface import implements -from lbrynet.core.DownloadOption import DownloadOption from lbrynet.lbryfile.StreamDescriptor import save_sd_info from lbrynet.cryptstream.client.CryptStreamDownloader import CryptStreamDownloader from lbrynet.core.client.StreamProgressManager import FullStreamProgressManager @@ -11,6 +10,7 @@ from lbrynet.interfaces import IStreamDownloaderFactory from lbrynet.lbryfile.client.LBRYFileMetadataHandler import LBRYFileMetadataHandler import os from twisted.internet import defer, threads, reactor +from distutils.spawn import find_executable class LBRYFileDownloader(CryptStreamDownloader): @@ -94,26 +94,11 @@ class LBRYFileDownloaderFactory(object): self.stream_info_manager = stream_info_manager self.wallet = wallet - def get_downloader_options(self, sd_validator, payment_rate_manager): - options = [ - DownloadOption( - [float, None], - "rate which will be paid for data (None means use application default)", - "data payment rate", - None - ), - DownloadOption( - [bool], - "allow reuploading data downloaded for this file", - "allow upload", - True - ), - ] - return options + def can_download(self, sd_validator): + return True def make_downloader(self, sd_validator, options, payment_rate_manager, **kwargs): - if options[0] is not None: - payment_rate_manager.float(options[0]) + payment_rate_manager.min_blob_data_payment_rate = options[0] upload_allowed = options[1] def create_downloader(stream_hash): @@ -276,6 +261,9 @@ class LBRYFileOpener(LBRYFileDownloader): class LBRYFileOpenerFactory(LBRYFileDownloaderFactory): + def can_download(self, sd_validator): + return bool(find_executable('vlc')) + def _make_downloader(self, stream_hash, payment_rate_manager, stream_info, upload_allowed): return LBRYFileOpener(stream_hash, self.peer_finder, self.rate_limiter, self.blob_manager, self.stream_info_manager, payment_rate_manager, self.wallet, upload_allowed) diff --git a/lbrynet/lbryfile/client/LBRYFileOptions.py b/lbrynet/lbryfile/client/LBRYFileOptions.py new file mode 100644 index 000000000..daa4f899c --- /dev/null +++ b/lbrynet/lbryfile/client/LBRYFileOptions.py @@ -0,0 +1,55 @@ +from lbrynet.lbryfile.StreamDescriptor import LBRYFileStreamType, LBRYFileStreamDescriptorValidator +from lbrynet.core.DownloadOption import DownloadOption, DownloadChoice + + +def add_lbry_file_to_sd_identifier(sd_identifier): + sd_identifier.add_stream_type(LBRYFileStreamType, LBRYFileStreamDescriptorValidator, LBRYFileOptions()) + + +class LBRYFileOptions(object): + def __init__(self): + pass + + def get_downloader_options(self, sd_validator, payment_rate_manager): + prm = payment_rate_manager + + def get_default_data_rate_description(): + if prm.min_blob_data_payment_rate is None: + return "Application default (%s LBC/MB)" % str(prm.base.min_blob_data_payment_rate) + else: + return "%f LBC/MB" % prm.min_blob_data_payment_rate + + rate_choices = [] + rate_choices.append(DownloadChoice(prm.min_blob_data_payment_rate, + "No change - %s" % get_default_data_rate_description(), + "No change - %s" % get_default_data_rate_description())) + if prm.min_blob_data_payment_rate is not None: + rate_choices.append(DownloadChoice(None, + "Application default (%s LBC/MB)" % str(prm.base.min_blob_data_payment_rate), + "Application default (%s LBC/MB)" % str(prm.base.min_blob_data_payment_rate))) + rate_choices.append(DownloadChoice(float, + "Enter rate in LBC/MB", + "Enter rate in LBC/MB")) + + options = [ + DownloadOption( + rate_choices, + "Rate which will be paid for data", + "data payment rate", + prm.min_blob_data_payment_rate, + get_default_data_rate_description() + ), + DownloadOption( + [ + DownloadChoice(bool, + None, + None, + bool_options_description=("Allow", "Disallow")), + ], + "Allow reuploading data downloaded for this file", + "allow upload", + True, + "Allow" + ), + ] + return options \ No newline at end of file diff --git a/lbrynet/lbryfilemanager/LBRYFileDownloader.py b/lbrynet/lbryfilemanager/LBRYFileDownloader.py index edb6b3f7b..fa8a093ac 100644 --- a/lbrynet/lbryfilemanager/LBRYFileDownloader.py +++ b/lbrynet/lbryfilemanager/LBRYFileDownloader.py @@ -2,7 +2,6 @@ Download LBRY Files from LBRYnet and save them to disk. """ -from lbrynet.core.DownloadOption import DownloadOption from zope.interface import implements from lbrynet.core.client.StreamProgressManager import FullStreamProgressManager from lbrynet.lbryfile.client.LBRYFileDownloader import LBRYFileSaver, LBRYFileDownloader @@ -117,22 +116,8 @@ class ManagedLBRYFileDownloaderFactory(object): def __init__(self, lbry_file_manager): self.lbry_file_manager = lbry_file_manager - def get_downloader_options(self, sd_validator, payment_rate_manager): - options = [ - DownloadOption( - [float, None], - "rate which will be paid for data (None means use application default)", - "data payment rate", - None - ), - DownloadOption( - [bool], - "allow reuploading data downloaded for this file", - "allow upload", - True - ), - ] - return options + def can_download(self, sd_validator): + return True def make_downloader(self, sd_validator, options, payment_rate_manager): data_rate = options[0] diff --git a/lbrynet/lbryfilemanager/LBRYFileManager.py b/lbrynet/lbryfilemanager/LBRYFileManager.py index 365081b93..0400269a4 100644 --- a/lbrynet/lbryfilemanager/LBRYFileManager.py +++ b/lbrynet/lbryfilemanager/LBRYFileManager.py @@ -7,7 +7,6 @@ import json import leveldb -from lbrynet.lbryfile.StreamDescriptor import LBRYFileStreamDescriptorValidator import os from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloader from lbrynet.lbryfilemanager.LBRYFileDownloader import ManagedLBRYFileDownloaderFactory @@ -98,7 +97,6 @@ class LBRYFileManager(object): def _add_to_sd_identifier(self): downloader_factory = ManagedLBRYFileDownloaderFactory(self) - self.sd_identifier.add_stream_info_validator(LBRYFileStreamType, LBRYFileStreamDescriptorValidator) self.sd_identifier.add_stream_downloader_factory(LBRYFileStreamType, downloader_factory) def _start_lbry_files(self): diff --git a/lbrynet/lbrylive/LiveStreamCreator.py b/lbrynet/lbrylive/LiveStreamCreator.py index 6b78d27ac..7641a3d30 100644 --- a/lbrynet/lbrylive/LiveStreamCreator.py +++ b/lbrynet/lbrylive/LiveStreamCreator.py @@ -1,15 +1,12 @@ from lbrynet.core.StreamDescriptor import BlobStreamDescriptorWriter -from lbrynet.lbrylive.StreamDescriptor import get_sd_info, LiveStreamType, LBRYLiveStreamDescriptorValidator +from lbrynet.lbrylive.StreamDescriptor import get_sd_info from lbrynet.cryptstream.CryptStreamCreator import CryptStreamCreator from lbrynet.lbrylive.LiveBlob import LiveStreamBlobMaker -from lbrynet.lbrylive.PaymentRateManager import BaseLiveStreamPaymentRateManager from lbrynet.core.cryptoutils import get_lbry_hash_obj, get_pub_key, sign_with_pass_phrase from Crypto import Random import binascii import logging from lbrynet.conf import CRYPTSD_FILE_EXTENSION -from lbrynet.conf import MIN_BLOB_INFO_PAYMENT_RATE -from lbrynet.lbrylive.client.LiveStreamDownloader import FullLiveStreamDownloaderFactory from twisted.internet import interfaces, defer from twisted.protocols.basic import FileSender from zope.interface import implements @@ -173,17 +170,4 @@ class StdinStreamProducer(object): self.consumer.write(data) def childConnectionLost(self, fd, reason): - self.stopProducing() - - -def add_live_stream_to_sd_identifier(session, stream_info_manager, sd_identifier): - downloader_factory = FullLiveStreamDownloaderFactory(session.peer_finder, - session.rate_limiter, - session.blob_manager, - stream_info_manager, - session.wallet, - BaseLiveStreamPaymentRateManager( - MIN_BLOB_INFO_PAYMENT_RATE - )) - sd_identifier.add_stream_info_validator(LiveStreamType, LBRYLiveStreamDescriptorValidator) - sd_identifier.add_stream_downloader_factory(LiveStreamType, downloader_factory) \ No newline at end of file + self.stopProducing() \ No newline at end of file diff --git a/lbrynet/lbrylive/client/LiveStreamDownloader.py b/lbrynet/lbrylive/client/LiveStreamDownloader.py index da7efae0f..871468191 100644 --- a/lbrynet/lbrylive/client/LiveStreamDownloader.py +++ b/lbrynet/lbrylive/client/LiveStreamDownloader.py @@ -1,5 +1,4 @@ import binascii -from lbrynet.core.DownloadOption import DownloadOption from lbrynet.cryptstream.client.CryptStreamDownloader import CryptStreamDownloader from zope.interface import implements from lbrynet.lbrylive.client.LiveStreamMetadataHandler import LiveStreamMetadataHandler @@ -9,6 +8,9 @@ from lbrynet.lbrylive.StreamDescriptor import save_sd_info from lbrynet.lbrylive.PaymentRateManager import LiveStreamPaymentRateManager from twisted.internet import defer, threads # , process from lbrynet.interfaces import IStreamDownloaderFactory +from lbrynet.lbrylive.PaymentRateManager import BaseLiveStreamPaymentRateManager +from lbrynet.conf import MIN_BLOB_INFO_PAYMENT_RATE +from lbrynet.lbrylive.StreamDescriptor import LiveStreamType class LiveStreamDownloader(CryptStreamDownloader): @@ -138,28 +140,8 @@ class FullLiveStreamDownloaderFactory(object): self.wallet = wallet self.default_payment_rate_manager = default_payment_rate_manager - def get_downloader_options(self, sd_validator, payment_rate_manager): - options = [ - DownloadOption( - [float, None], - "rate which will be paid for data (None means use application default)", - "data payment rate", - None - ), - DownloadOption( - [float, None], - "rate which will be paid for metadata (None means use application default)", - "metadata payment rate", - None - ), - DownloadOption( - [bool], - "allow reuploading data downloaded for this file", - "allow upload", - True - ), - ] - return options + def can_download(self, sd_validator): + return True def make_downloader(self, sd_validator, options, payment_rate_manager): # TODO: check options for payment rate manager parameters @@ -177,4 +159,15 @@ class FullLiveStreamDownloaderFactory(object): return d d.addCallback(create_downloader) - return d \ No newline at end of file + return d + + +def add_full_live_stream_downloader_to_sd_identifier(session, stream_info_manager, sd_identifier, + base_live_stream_payment_rate_manager): + downloader_factory = FullLiveStreamDownloaderFactory(session.peer_finder, + session.rate_limiter, + session.blob_manager, + stream_info_manager, + session.wallet, + base_live_stream_payment_rate_manager) + sd_identifier.add_stream_downloader_factory(LiveStreamType, downloader_factory) \ No newline at end of file diff --git a/lbrynet/lbrylive/client/LiveStreamOptions.py b/lbrynet/lbrylive/client/LiveStreamOptions.py new file mode 100644 index 000000000..04f78c62b --- /dev/null +++ b/lbrynet/lbrylive/client/LiveStreamOptions.py @@ -0,0 +1,73 @@ +from lbrynet.lbrylive.StreamDescriptor import LiveStreamType, LBRYLiveStreamDescriptorValidator +from lbrynet.core.DownloadOption import DownloadOption, DownloadChoice + + +def add_live_stream_to_sd_identifier(sd_identifier, base_live_stream_payment_rate_manager): + sd_identifier.add_stream_type(LiveStreamType, LBRYLiveStreamDescriptorValidator, + LiveStreamOptions(base_live_stream_payment_rate_manager)) + + +class LiveStreamOptions(object): + def __init__(self, base_live_stream_payment_rate_manager): + self.base_live_stream_prm = base_live_stream_payment_rate_manager + + def get_downloader_options(self, sd_validator, payment_rate_manager): + prm = payment_rate_manager + + def get_default_data_rate_description(): + if prm.min_blob_data_payment_rate is None: + return "Application default (%s LBC/MB)" % str(prm.base.min_blob_data_payment_rate) + else: + return "%f LBC/MB" % prm.min_blob_data_payment_rate + + options = [ + DownloadOption( + [ + DownloadChoice(None, + "No change", + "No change"), + DownloadChoice(None, + "Application default (%s LBC/MB)" % str(prm.base.min_blob_data_payment_rate), + "Default (%s LBC/MB)" % str(prm.base.min_blob_data_payment_rate)), + DownloadChoice(float, + "Rate in LBC/MB", + "Rate in LBC/MB") + ], + "rate which will be paid for data", + "data payment rate", + prm.min_blob_data_payment_rate, + get_default_data_rate_description() + ), + DownloadOption( + [ + DownloadChoice(None, + "No change", + "No change"), + DownloadChoice(None, + "Application default (%s LBC/MB)" % str(self.base_live_stream_prm.min_live_blob_info_payment_rate), + "Default (%s LBC/MB)" % str(self.base_live_stream_prm.min_live_blob_info_payment_rate)), + DownloadChoice(float, + "Rate in LBC/MB", + "Rate in LBC/MB") + ], + "rate which will be paid for metadata", + "metadata payment rate", + None, + "Application default (%s LBC/MB)" % str(self.base_live_stream_prm.min_live_blob_info_payment_rate) + ), + DownloadOption( + [ + DownloadChoice(True, + "Allow reuploading data downloaded for this file", + "Allow reuploading"), + DownloadChoice(False, + "Disallow reuploading data downloaded for this file", + "Disallow reuploading") + ], + "allow reuploading data downloaded for this file", + "allow upload", + True, + "Allow" + ), + ] + return options \ No newline at end of file diff --git a/lbrynet/lbrynet_console/ControlHandlers.py b/lbrynet/lbrynet_console/ControlHandlers.py index 92ffa5d29..79561dc9a 100644 --- a/lbrynet/lbrynet_console/ControlHandlers.py +++ b/lbrynet/lbrynet_console/ControlHandlers.py @@ -13,6 +13,10 @@ class InvalidChoiceError(Exception): pass +class InvalidValueError(Exception): + pass + + class ControlHandlerFactory(object): implements(IControlHandlerFactory) @@ -235,9 +239,11 @@ class AddStream(ControlHandler): self.factories = None self.factory = None self.info_validator = None + self.options = 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 @@ -274,18 +280,31 @@ class AddStream(ControlHandler): 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': - if self.options_left: - return False, defer.succeed(self._get_next_option_prompt()) - self.options_chosen = [option.default for option in self.options_left] - self.options_left = [] - return False, defer.succeed(self.line_prompt3) + 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: - try: - choice = self._get_choice_from_input(line) - except InvalidChoiceError: - return False, defer.succeed(self._get_next_option_prompt(invalid_response=True)) - self.options_chosen.append(choice) + 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()) @@ -299,23 +318,13 @@ class AddStream(ControlHandler): return True, d def _get_choice_from_input(self, line): - if line == "": - return self.current_option.default - for option_type in self.current_option.option_types: - if option_type == float: - try: - return float(line) - except ValueError: - pass - if option_type is None: - if line.lower() == "none": - return None - if option_type == bool: - if line.lower() == "true" or line.lower() == "t": - return True - if line.lower() == "false" or line.lower() == "f": - return False - raise InvalidChoiceError(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_info_and_factories(self, sd_file): return defer.fail(NotImplementedError()) @@ -333,7 +342,7 @@ class AddStream(ControlHandler): def _choose_factory(self, info_and_factories): self.loading_info_and_factories_deferred = None - self.info_validator, self.factories = info_and_factories + self.info_validator, self.options, self.factories = info_and_factories if len(self.factories) == 1: self.factory = self.factories[0] return self._show_info_and_options() @@ -346,38 +355,71 @@ class AddStream(ControlHandler): return str(prompt) def _show_info_and_options(self): - self.options_left = self.factory.get_downloader_options(self.info_validator, + self.options_left = self.options.get_downloader_options(self.info_validator, self.payment_rate_manager) prompt = "Stream info:\n" for info_line in self.info_validator.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) + "\n" + prompt += option.long_description + ": " + str(option.default_value_description) + "\n" prompt += "\nModify options? (y/n)" return str(prompt) - def _get_option_type_description(self, option_type): - if option_type == float: - return "floating point number (e.g. 1.0)" - if option_type == bool: - return "True or False" - if option_type is None: - return "None" + 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_next_option_prompt(self, invalid_response=False): - assert len(self.options_left), "Something went wrong. There were no options left" - choice = self.options_left[0] + def _get_choice_value_prompt(self, invalid_value=False): + choice = self.current_option.option_types[self.current_choice] choice_string = "" - if invalid_response is True: + 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 += choice.long_description + "\n" - choice_string += "Valid inputs:\n" - for option_type in choice.option_types: - choice_string += "\t" + self._get_option_type_description(option_type) + "\n" - choice_string += "Leave blank for default (" + str(choice.default) + ")\n" - choice_string += "Enter choice:" - self.current_option = choice + + 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): diff --git a/lbrynet/lbrynet_console/LBRYConsole.py b/lbrynet/lbrynet_console/LBRYConsole.py index 4b44aa358..0b3dbd150 100644 --- a/lbrynet/lbrynet_console/LBRYConsole.py +++ b/lbrynet/lbrynet_console/LBRYConsole.py @@ -15,6 +15,7 @@ from lbrynet.core.server.BlobAvailabilityHandler import BlobAvailabilityHandlerF from lbrynet.core.server.BlobRequestHandler import BlobRequestHandlerFactory from lbrynet.core.server.ServerProtocol import ServerProtocolFactory from lbrynet.core.PTCWallet import PTCWallet +from lbrynet.lbryfile.client.LBRYFileOptions import add_lbry_file_to_sd_identifier from lbrynet.lbryfile.client.LBRYFileDownloader import LBRYFileOpenerFactory from lbrynet.lbryfile.StreamDescriptor import LBRYFileStreamType from lbrynet.lbryfile.LBRYFileMetadataManager import DBLBRYFileMetadataManager, TempLBRYFileMetadataManager @@ -77,6 +78,7 @@ class LBRYConsole(): d = threads.deferToThread(self._create_directory) d.addCallback(lambda _: self._get_settings()) d.addCallback(lambda _: self._get_session()) + d.addCallback(lambda _: add_lbry_file_to_sd_identifier(self.sd_identifier)) d.addCallback(lambda _: self._setup_lbry_file_manager()) d.addCallback(lambda _: self._setup_lbry_file_opener()) d.addCallback(lambda _: self._setup_control_handlers()) diff --git a/lbrynet/lbrynet_downloader_gui/downloader.py b/lbrynet/lbrynet_downloader_gui/downloader.py index f13bf7593..29a48de04 100644 --- a/lbrynet/lbrynet_downloader_gui/downloader.py +++ b/lbrynet/lbrynet_downloader_gui/downloader.py @@ -17,7 +17,8 @@ from lbrynet.core.StreamDescriptor import StreamDescriptorIdentifier from lbrynet.core.PaymentRateManager import PaymentRateManager from lbrynet.lbryfile.LBRYFileMetadataManager import TempLBRYFileMetadataManager from lbrynet.core import StreamDescriptor -from lbrynet.lbryfile.StreamDescriptor import LBRYFileStreamType, LBRYFileStreamDescriptorValidator +from lbrynet.lbryfile.client.LBRYFileOptions import add_lbry_file_to_sd_identifier +from lbrynet.lbryfile.StreamDescriptor import LBRYFileStreamType import requests @@ -101,7 +102,7 @@ class LBRYDownloader(object): return defer.succeed(True) def _setup_stream_identifier(self): - self.sd_identifier.add_stream_info_validator(LBRYFileStreamType, LBRYFileStreamDescriptorValidator) + add_lbry_file_to_sd_identifier(self.sd_identifier) file_saver_factory = LBRYFileSaverFactory(self.session.peer_finder, self.session.rate_limiter, self.session.blob_manager, self.stream_info_manager, self.session.wallet, self.download_directory) @@ -184,7 +185,7 @@ class LBRYDownloader(object): return stream_name, stream_size def choose_download_factory(info_and_factories): - info_validator, factories = info_and_factories + info_validator, options, factories = info_and_factories stream_name, stream_size = get_info_from_validator(info_validator) if isinstance(stream_size, (int, long)): price = payment_rate_manager.get_effective_min_blob_data_payment_rate() @@ -192,26 +193,30 @@ class LBRYDownloader(object): else: estimated_cost = "unknown" - stream_frame.show_stream_metadata(stream_name, stream_size, estimated_cost) + stream_frame.show_stream_metadata(stream_name, stream_size) + + available_options = options.get_downloader_options(info_validator, payment_rate_manager) + + stream_frame.show_download_options(available_options) get_downloader_d = defer.Deferred() - def create_downloader(f): + def create_downloader(f, chosen_options): def fire_get_downloader_d(downloader): if not get_downloader_d.called: get_downloader_d.callback(downloader) stream_frame.disable_download_buttons() - download_options = [o.default for o in f.get_downloader_options(info_validator, payment_rate_manager)] - d = f.make_downloader(info_validator, download_options, + d = f.make_downloader(info_validator, chosen_options, payment_rate_manager) d.addCallback(fire_get_downloader_d) for factory in factories: def choose_factory(f=factory): - create_downloader(f) + chosen_options = stream_frame.get_chosen_options() + create_downloader(f, chosen_options) stream_frame.add_download_factory(factory, choose_factory) @@ -301,9 +306,9 @@ class StreamFrame(object): self.uri_label.grid(row=0, column=0, sticky=tk.W) if os.name == "nt": - close_cursor = "" + self.button_cursor = "" else: - close_cursor = "hand1" + self.button_cursor = "hand1" close_file_name = "close2.gif" try: @@ -316,7 +321,7 @@ class StreamFrame(object): file=close_file ) self.close_button = ttk.Button( - self.stream_frame_header, command=self.cancel, style="Stop.TButton", cursor=close_cursor + self.stream_frame_header, command=self.cancel, style="Stop.TButton", cursor=self.button_cursor ) self.close_button.config(image=self.close_picture) self.close_button.grid(row=0, column=1, sticky=tk.E + tk.N) @@ -334,26 +339,50 @@ class StreamFrame(object): self.stream_frame_body.grid_columnconfigure(0, weight=1) - self.info_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame") - self.info_frame.grid(sticky=tk.W + tk.E, row=1) - self.info_frame.grid_columnconfigure(0, weight=1) - - self.metadata_frame = ttk.Frame(self.info_frame, style="E.TFrame") - self.metadata_frame.grid(sticky=tk.W + tk.E) + self.metadata_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame") + self.metadata_frame.grid(sticky=tk.W + tk.E, row=1) self.metadata_frame.grid_columnconfigure(0, weight=1) - self.outer_button_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame") - self.outer_button_frame.grid(sticky=tk.W + tk.E, row=2) + self.options_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame") - self.button_frame = ttk.Frame(self.outer_button_frame, style="E.TFrame") - self.button_frame.pack(side=tk.TOP) + self.outer_button_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame") + self.outer_button_frame.grid(sticky=tk.W + tk.E, row=4) + + show_options_picture_file_name = "show_options.gif" + try: + show_options_picture_file = os.path.join(os.path.dirname(__file__), + show_options_picture_file_name) + except NameError: + show_options_picture_file = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), + "lbrynet", "lbrynet_downloader_gui", + show_options_picture_file_name) + + self.show_options_picture = tk.PhotoImage( + file=show_options_picture_file + ) + + hide_options_picture_file_name = "hide_options.gif" + try: + hide_options_picture_file = os.path.join(os.path.dirname(__file__), + hide_options_picture_file_name) + except NameError: + hide_options_picture_file = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), + "lbrynet", "lbrynet_downloader_gui", + hide_options_picture_file_name) + + self.hide_options_picture = tk.PhotoImage( + file=hide_options_picture_file + ) + + self.show_options_button = None self.status_label = None self.name_label = None self.bytes_downloaded_label = None self.bytes_outputted_label = None - + self.button_frame = None self.download_buttons = [] + self.option_frames = [] self.name_font = None self.description_label = None self.file_name_frame = None @@ -416,7 +445,7 @@ class StreamFrame(object): return "%.1f %s" % (round((stream_size * 1.0 / factor), 1), units) return stream_size - def show_stream_metadata(self, stream_name, stream_size, estimated_cost): + def show_stream_metadata(self, stream_name, stream_size): if self.status_label is not None: self.status_label.destroy() @@ -436,19 +465,6 @@ class StreamFrame(object): ) file_name_label.grid(row=0, column=3) - self.outer_button_frame = ttk.Frame(self.stream_frame_body, style="D.TFrame") - self.outer_button_frame.grid(sticky=tk.W + tk.E, row=2) - - self.cost_frame = ttk.Frame(self.outer_button_frame, style="F.TFrame") - self.cost_frame.grid(row=0, column=0, sticky=tk.W+tk.N, pady=(0, 12)) - - self.cost_label = ttk.Label( - self.cost_frame, - text=locale.format_string("%.2f LBC", (round(estimated_cost, 2),), grouping=True), - foreground="red" - ) - self.cost_label.grid(row=0, column=1, padx=(1, 0)) - self.button_frame = ttk.Frame(self.outer_button_frame, style="E.TFrame") self.button_frame.grid(row=0, column=1) @@ -457,13 +473,9 @@ class StreamFrame(object): self.outer_button_frame.grid_columnconfigure(2, weight=1, uniform="buttons") def add_download_factory(self, factory, download_func): - if os.name == "nt": - button_cursor = "" - else: - button_cursor = "hand1" download_button = ttk.Button( self.button_frame, text=factory.get_description(), command=download_func, - style='LBRY.TButton', cursor=button_cursor + style='LBRY.TButton', cursor=self.button_cursor ) self.download_buttons.append(download_button) download_button.grid(row=0, column=len(self.download_buttons) - 1, padx=5, pady=(1, 2)) @@ -477,11 +489,160 @@ class StreamFrame(object): download_button.destroy() self.download_buttons = [] + def get_option_widget(self, option_type, option_frame): + if option_type.value == float: + entry_frame = ttk.Frame( + option_frame, + style="H.TFrame" + ) + entry_frame.grid() + col = 0 + if option_type.short_description is not None: + entry_label = ttk.Label( + entry_frame, + #text=option_type.short_description + text="" + ) + entry_label.grid(row=0, column=0, sticky=tk.W) + col = 1 + entry = ttk.Entry( + entry_frame, + width=10, + style="Float.TEntry" + ) + entry_frame.entry = entry + entry.grid(row=0, column=col, sticky=tk.W) + return entry_frame + if option_type.value == bool: + bool_frame = ttk.Frame( + option_frame, + style="H.TFrame" + ) + bool_frame.chosen_value = tk.BooleanVar() + true_text = "True" + false_text = "False" + if option_type.bool_options_description is not None: + true_text, false_text = option_type.bool_options_description + true_radio_button = ttk.Radiobutton( + bool_frame, text=true_text, variable=bool_frame.chosen_value, value=True + ) + true_radio_button.grid(row=0, sticky=tk.W) + false_radio_button = ttk.Radiobutton( + bool_frame, text=false_text, variable=bool_frame.chosen_value, value=False + ) + false_radio_button.grid(row=1, sticky=tk.W) + return bool_frame + label = ttk.Label( + option_frame, + text="" + ) + return label + + def show_download_options(self, options): + left_padding = 20 + for option in options: + f = ttk.Frame( + self.options_frame, + style="E.TFrame" + ) + f.grid(sticky=tk.W + tk.E, padx=left_padding) + self.option_frames.append((option, f)) + description_label = ttk.Label( + f, + text=option.long_description + ) + description_label.grid(row=0, sticky=tk.W) + if len(option.option_types) > 1: + f.chosen_type = tk.IntVar() + choices_frame = ttk.Frame( + f, + style="F.TFrame" + ) + f.choices_frame = choices_frame + choices_frame.grid(row=1, sticky=tk.W, padx=left_padding) + choices_frame.choices = [] + for i, option_type in enumerate(option.option_types): + choice_frame = ttk.Frame( + choices_frame, + style="G.TFrame" + ) + choice_frame.grid(sticky=tk.W) + option_text = "" + if option_type.short_description is not None: + option_text = option_type.short_description + option_radio_button = ttk.Radiobutton( + choice_frame, text=option_text, variable=f.chosen_type, value=i + ) + option_radio_button.grid(row=0, column=0, sticky=tk.W) + option_widget = self.get_option_widget(option_type, choice_frame) + option_widget.grid(row=0, column=1, sticky=tk.W) + choices_frame.choices.append(option_widget) + if i == 0: + option_radio_button.invoke() + else: + choice_frame = ttk.Frame( + f, + style="F.TFrame" + ) + choice_frame.grid(sticky=tk.W, padx=left_padding) + option_widget = self.get_option_widget(option.option_types[0], choice_frame) + option_widget.grid(row=0, column=0, sticky=tk.W) + f.option_widget = option_widget + self.show_options_button = ttk.Button( + self.stream_frame_body, command=self._toggle_show_options, style="Stop.TButton", + cursor=self.button_cursor + ) + self.show_options_button.config(image=self.show_options_picture) + self.show_options_button.grid(sticky=tk.W, row=2, column=0) + + def _get_chosen_option(self, option_type, option_widget): + if option_type.value == float: + return float(option_widget.entry.get()) + if option_type.value == bool: + return option_widget.chosen_value.get() + return option_type.value + + def get_chosen_options(self): + chosen_options = [] + for o, f in self.option_frames: + if len(o.option_types) > 1: + chosen_index = f.chosen_type.get() + option_type = o.option_types[chosen_index] + option_widget = f.choices_frame.choices[chosen_index] + chosen_options.append(self._get_chosen_option(option_type, option_widget)) + else: + option_type = o.option_types[0] + option_widget = f.option_widget + chosen_options.append(self._get_chosen_option(option_type, option_widget)) + return chosen_options + + def _toggle_show_options(self): + if self.options_frame.winfo_ismapped(): + self.show_options_button.config(image=self.show_options_picture) + self.options_frame.grid_forget() + else: + self.show_options_button.config(image=self.hide_options_picture) + self.options_frame.grid(sticky=tk.W + tk.E, row=3) + def show_progress(self, total_bytes, bytes_left_to_download, bytes_left_to_output, points_paid, points_remaining): if self.bytes_outputted_label is None: self.remove_download_buttons() self.button_frame.destroy() + for option, frame in self.option_frames: + frame.destroy() + self.options_frame.destroy() + self.show_options_button.destroy() + + self.cost_frame = ttk.Frame(self.outer_button_frame, style="F.TFrame") + self.cost_frame.grid(row=0, column=0, sticky=tk.W+tk.N, pady=(0, 12)) + + self.cost_label = ttk.Label( + self.cost_frame, + text="", + foreground="red" + ) + self.cost_label.grid(row=0, column=1, padx=(1, 0)) self.outer_button_frame.grid_columnconfigure(2, weight=0, uniform="") self.bytes_outputted_label = ttk.Label( @@ -667,6 +828,7 @@ class App(object): ttk.Style().configure("Lookup.LBRY.TButton", padding=lookup_button_padding) ttk.Style().configure("Stop.TButton", padding=1, background="#FFFFFF", relief="flat", borderwidth=0) ttk.Style().configure("TEntry", padding=11) + ttk.Style().configure("Float.TEntry", padding=2) #ttk.Style().configure("A.TFrame", background="red") #ttk.Style().configure("B.TFrame", background="green") #ttk.Style().configure("B2.TFrame", background="#80FF80") @@ -674,6 +836,8 @@ class App(object): #ttk.Style().configure("D.TFrame", background="blue") #ttk.Style().configure("E.TFrame", background="yellow") #ttk.Style().configure("F.TFrame", background="#808080") + #ttk.Style().configure("G.TFrame", background="#FF80FF") + #ttk.Style().configure("H.TFrame", background="#0080FF") #ttk.Style().configure("LBRY.TProgressbar", background="#104639", orient="horizontal", thickness=5) #ttk.Style().configure("LBRY.TProgressbar") #ttk.Style().layout("Horizontal.LBRY.TProgressbar", ttk.Style().layout("Horizontal.TProgressbar")) diff --git a/lbrynet/lbrynet_downloader_gui/hide_options.gif b/lbrynet/lbrynet_downloader_gui/hide_options.gif new file mode 100644 index 000000000..9c95b5a9d Binary files /dev/null and b/lbrynet/lbrynet_downloader_gui/hide_options.gif differ diff --git a/lbrynet/lbrynet_downloader_gui/show_options.gif b/lbrynet/lbrynet_downloader_gui/show_options.gif new file mode 100644 index 000000000..c5ed0b6fd Binary files /dev/null and b/lbrynet/lbrynet_downloader_gui/show_options.gif differ diff --git a/setup.py b/setup.py index 5d5e63bd3..1322aff33 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,8 @@ setup(name='lbrynet', 'lbrynet/lbrynet_downloader_gui/lbry-dark-icon.xbm', 'lbrynet/lbrynet_downloader_gui/lbry-dark-icon.ico', 'lbrynet/lbrynet_downloader_gui/drop_down.gif', + 'lbrynet/lbrynet_downloader_gui/show_options.gif', + 'lbrynet/lbrynet_downloader_gui/hide_options.gif', ] ) ] diff --git a/tests/functional_tests.py b/tests/functional_tests.py index 664cce127..eeba02938 100644 --- a/tests/functional_tests.py +++ b/tests/functional_tests.py @@ -9,7 +9,7 @@ from Crypto import Random from Crypto.Hash import MD5 from lbrynet.conf import MIN_BLOB_DATA_PAYMENT_RATE from lbrynet.conf import MIN_BLOB_INFO_PAYMENT_RATE -from lbrynet.lbrylive.LiveStreamCreator import FileLiveStreamCreator, add_live_stream_to_sd_identifier +from lbrynet.lbrylive.LiveStreamCreator import FileLiveStreamCreator from lbrynet.lbrylive.PaymentRateManager import BaseLiveStreamPaymentRateManager from lbrynet.lbrylive.PaymentRateManager import LiveStreamPaymentRateManager from lbrynet.lbrylive.LiveStreamMetadataManager import DBLiveStreamMetadataManager @@ -24,6 +24,7 @@ from lbrynet.core.StreamDescriptor import BlobStreamDescriptorWriter from lbrynet.core.StreamDescriptor import StreamDescriptorIdentifier from lbrynet.core.StreamDescriptor import download_sd_blob from lbrynet.lbryfilemanager.LBRYFileCreator import create_lbry_file +from lbrynet.lbryfile.client.LBRYFileOptions import add_lbry_file_to_sd_identifier from lbrynet.lbryfile.StreamDescriptor import get_sd_info from twisted.internet import defer, threads, task from twisted.trial.unittest import TestCase @@ -35,6 +36,8 @@ from lbrynet.core.server.BlobAvailabilityHandler import BlobAvailabilityHandlerF from lbrynet.core.server.BlobRequestHandler import BlobRequestHandlerFactory from lbrynet.core.server.ServerProtocol import ServerProtocolFactory from lbrynet.lbrylive.server.LiveBlobInfoQueryHandler import CryptBlobInfoQueryHandlerFactory +from lbrynet.lbrylive.client.LiveStreamOptions import add_live_stream_to_sd_identifier +from lbrynet.lbrylive.client.LiveStreamDownloader import add_full_live_stream_downloader_to_sd_identifier log_format = "%(funcName)s(): %(message)s" @@ -248,6 +251,7 @@ def start_lbry_uploader(sd_hash_queue, kill_event, dead_event): def start_all(): d = session.setup() + d.addCallback(lambda _: add_lbry_file_to_sd_identifier(sd_identifier)) d.addCallback(lambda _: lbry_file_manager.setup()) d.addCallback(lambda _: start_server()) d.addCallback(lambda _: create_stream()) @@ -437,7 +441,12 @@ def start_live_server(sd_hash_queue, kill_event, dead_event): return d def enable_live_stream(): - return add_live_stream_to_sd_identifier(session, stream_info_manager, sd_identifier) + base_live_stream_payment_rate_manager = BaseLiveStreamPaymentRateManager( + MIN_BLOB_INFO_PAYMENT_RATE + ) + add_live_stream_to_sd_identifier(sd_identifier, base_live_stream_payment_rate_manager) + add_full_live_stream_downloader_to_sd_identifier(session, stream_info_manager, sd_identifier, + base_live_stream_payment_rate_manager) def run_server(): d = session.setup() @@ -670,9 +679,9 @@ class TestTransfer(TestCase): self.lbry_file_manager = LBRYFileManager(self.session, self.stream_info_manager, sd_identifier) def make_downloader(info_and_factories, prm): - info_validator, factories = info_and_factories - options = [o.default for o in factories[0].get_downloader_options(info_validator, prm)] - return factories[0].make_downloader(info_validator, options, prm) + info_validator, options, factories = info_and_factories + chosen_options = [o.default_value for o in options.get_downloader_options(info_validator, prm)] + return factories[0].make_downloader(info_validator, chosen_options, prm) def download_file(sd_hash): prm = PaymentRateManager(self.session.base_payment_rate_manager) @@ -693,6 +702,7 @@ class TestTransfer(TestCase): logging.debug("Starting the transfer") d = self.session.setup() + d.addCallback(lambda _: add_lbry_file_to_sd_identifier(sd_identifier)) d.addCallback(lambda _: self.lbry_file_manager.setup()) d.addCallback(lambda _: download_file(sd_hash)) d.addCallback(lambda _: check_md5_sum()) @@ -750,9 +760,9 @@ class TestTransfer(TestCase): d = self.wait_for_hash_from_queue(sd_hash_queue) def create_downloader(info_and_factories, prm): - info_validator, factories = info_and_factories - options = [o.default for o in factories[0].get_downloader_options(info_validator, prm)] - return factories[0].make_downloader(info_validator, options, prm) + info_validator, options, factories = info_and_factories + chosen_options = [o.default_value for o in options.get_downloader_options(info_validator, prm)] + return factories[0].make_downloader(info_validator, chosen_options, prm) def start_lbry_file(lbry_file): lbry_file = lbry_file @@ -776,7 +786,14 @@ class TestTransfer(TestCase): return d def enable_live_stream(): - return add_live_stream_to_sd_identifier(self.session, self.stream_info_manager, sd_identifier) + base_live_stream_payment_rate_manager = BaseLiveStreamPaymentRateManager( + MIN_BLOB_INFO_PAYMENT_RATE + ) + add_live_stream_to_sd_identifier(sd_identifier, + base_live_stream_payment_rate_manager) + add_full_live_stream_downloader_to_sd_identifier(self.session, self.stream_info_manager, + sd_identifier, + base_live_stream_payment_rate_manager) d.addCallback(do_download) @@ -941,6 +958,7 @@ class TestStreamify(TestCase): d = self.session.setup() d.addCallback(lambda _: self.stream_info_manager.setup()) + d.addCallback(lambda _: add_lbry_file_to_sd_identifier(sd_identifier)) d.addCallback(lambda _: self.lbry_file_manager.setup()) def verify_equal(sd_info): @@ -1017,6 +1035,7 @@ class TestStreamify(TestCase): d = self.session.setup() d.addCallback(lambda _: self.stream_info_manager.setup()) + d.addCallback(lambda _: add_lbry_file_to_sd_identifier(sd_identifier)) d.addCallback(lambda _: self.lbry_file_manager.setup()) d.addCallback(lambda _: create_stream()) d.addCallback(combine_stream)