forked from LBRYCommunity/lbry-sdk
Downloader options in its own class, show options in gui downloader
Put stream downloader options into its own class, and make stream downloader options global to the stream type rather than specific to each factory. Show downloader options in the lbrynet-downloader-gui. Make a class for downloader option choices, so that the descriptions can be displayed. In the console, if there are multiple choices for the download option, make it a list selected by its index. Make sure that the ConnectionManager closes properly when some of the connections fail to open (e.g. due to a host being down)
This commit is contained in:
parent
f4b3187494
commit
c8b2b7b279
18 changed files with 583 additions and 231 deletions
|
@ -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
|
||||
self.default_value = default_value
|
||||
self.default_value_description = default_value_description
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
self._next_manage_call = reactor.callLater(1, self._manage)
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
55
lbrynet/lbryfile/client/LBRYFileOptions.py
Normal file
55
lbrynet/lbryfile/client/LBRYFileOptions.py
Normal file
|
@ -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
|
|
@ -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]
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
@ -174,16 +171,3 @@ class StdinStreamProducer(object):
|
|||
|
||||
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)
|
|
@ -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
|
||||
|
@ -178,3 +160,14 @@ class FullLiveStreamDownloaderFactory(object):
|
|||
|
||||
d.addCallback(create_downloader)
|
||||
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)
|
73
lbrynet/lbrylive/client/LiveStreamOptions.py
Normal file
73
lbrynet/lbrylive/client/LiveStreamOptions.py
Normal file
|
@ -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
|
|
@ -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):
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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"))
|
||||
|
|
BIN
lbrynet/lbrynet_downloader_gui/hide_options.gif
Normal file
BIN
lbrynet/lbrynet_downloader_gui/hide_options.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 B |
BIN
lbrynet/lbrynet_downloader_gui/show_options.gif
Normal file
BIN
lbrynet/lbrynet_downloader_gui/show_options.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 155 B |
2
setup.py
2
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',
|
||||
]
|
||||
)
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue