commit
66745b0820
11 changed files with 158 additions and 86 deletions
|
@ -25,11 +25,12 @@ class BlobAvailabilityTracker(object):
|
||||||
self._check_mine = LoopingCall(self._update_mine)
|
self._check_mine = LoopingCall(self._update_mine)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
log.info("Starting blob tracker")
|
log.info("Starting %s", self)
|
||||||
self._check_popular.start(30)
|
self._check_popular.start(30)
|
||||||
self._check_mine.start(120)
|
self._check_mine.start(120)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
log.info("Stopping %s", self)
|
||||||
if self._check_popular.running:
|
if self._check_popular.running:
|
||||||
self._check_popular.stop()
|
self._check_popular.stop()
|
||||||
if self._check_mine.running:
|
if self._check_mine.running:
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from lbrynet.interfaces import IRateLimiter
|
from lbrynet.interfaces import IRateLimiter
|
||||||
from twisted.internet import task
|
from twisted.internet import task
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DummyRateLimiter(object):
|
class DummyRateLimiter(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.dl_bytes_this_second = 0
|
self.dl_bytes_this_second = 0
|
||||||
|
@ -64,6 +69,7 @@ class RateLimiter(object):
|
||||||
self.protocols = []
|
self.protocols = []
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
log.info("Starting %s", self)
|
||||||
self.tick_call = task.LoopingCall(self.tick)
|
self.tick_call = task.LoopingCall(self.tick)
|
||||||
self.tick_call.start(self.tick_interval)
|
self.tick_call.start(self.tick_interval)
|
||||||
|
|
||||||
|
@ -74,6 +80,7 @@ class RateLimiter(object):
|
||||||
self.unthrottle_ul()
|
self.unthrottle_ul()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
log.info("Stopping %s", self)
|
||||||
if self.tick_call is not None:
|
if self.tick_call is not None:
|
||||||
self.tick_call.stop()
|
self.tick_call.stop()
|
||||||
self.tick_call = None
|
self.tick_call = None
|
||||||
|
@ -144,4 +151,4 @@ class RateLimiter(object):
|
||||||
|
|
||||||
def unregister_protocol(self, protocol):
|
def unregister_protocol(self, protocol):
|
||||||
if protocol in self.protocols:
|
if protocol in self.protocols:
|
||||||
self.protocols.remove(protocol)
|
self.protocols.remove(protocol)
|
||||||
|
|
|
@ -18,60 +18,87 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Session(object):
|
class Session(object):
|
||||||
"""This class manages all important services common to any application that uses the network:
|
"""This class manages all important services common to any application that uses the network.
|
||||||
the hash announcer, which informs other peers that this peer is associated with some hash. Usually,
|
|
||||||
this means this peer has a blob identified by the hash in question, but it can be used for other
|
the hash announcer, which informs other peers that this peer is
|
||||||
purposes.
|
associated with some hash. Usually, this means this peer has a
|
||||||
the peer finder, which finds peers that are associated with some hash.
|
blob identified by the hash in question, but it can be used for
|
||||||
the blob manager, which keeps track of which blobs have been downloaded and provides access to them,
|
other purposes.
|
||||||
the rate limiter, which attempts to ensure download and upload rates stay below a set maximum,
|
|
||||||
and upnp, which opens holes in compatible firewalls so that remote peers can connect to this peer."""
|
the peer finder, which finds peers that are associated with some
|
||||||
def __init__(self, blob_data_payment_rate, db_dir=None, lbryid=None, peer_manager=None, dht_node_port=None,
|
hash.
|
||||||
known_dht_nodes=None, peer_finder=None, hash_announcer=None, blob_dir=None, blob_manager=None,
|
|
||||||
peer_port=None, use_upnp=True, rate_limiter=None, wallet=None, dht_node_class=node.Node,
|
the blob manager, which keeps track of which blobs have been
|
||||||
blob_tracker_class=None, payment_rate_manager_class=None, is_generous=True):
|
downloaded and provides access to them,
|
||||||
"""
|
|
||||||
@param blob_data_payment_rate: The default payment rate for blob data
|
the rate limiter, which attempts to ensure download and upload
|
||||||
|
rates stay below a set maximum
|
||||||
|
|
||||||
|
upnp, which opens holes in compatible firewalls so that remote
|
||||||
|
peers can connect to this peer.
|
||||||
|
"""
|
||||||
|
def __init__(self, blob_data_payment_rate, db_dir=None,
|
||||||
|
lbryid=None, peer_manager=None, dht_node_port=None,
|
||||||
|
known_dht_nodes=None, peer_finder=None,
|
||||||
|
hash_announcer=None, blob_dir=None,
|
||||||
|
blob_manager=None, peer_port=None, use_upnp=True,
|
||||||
|
rate_limiter=None, wallet=None,
|
||||||
|
dht_node_class=node.Node, blob_tracker_class=None,
|
||||||
|
payment_rate_manager_class=None, is_generous=True):
|
||||||
|
"""@param blob_data_payment_rate: The default payment rate for blob data
|
||||||
|
|
||||||
@param db_dir: The directory in which levelDB files should be stored
|
@param db_dir: The directory in which levelDB files should be stored
|
||||||
|
|
||||||
@param lbryid: The unique ID of this node
|
@param lbryid: The unique ID of this node
|
||||||
|
|
||||||
@param peer_manager: An object which keeps track of all known peers. If None, a PeerManager will be created
|
@param peer_manager: An object which keeps track of all known
|
||||||
|
peers. If None, a PeerManager will be created
|
||||||
|
|
||||||
@param dht_node_port: The port on which the dht node should listen for incoming connections
|
@param dht_node_port: The port on which the dht node should
|
||||||
|
listen for incoming connections
|
||||||
|
|
||||||
@param known_dht_nodes: A list of nodes which the dht node should use to bootstrap into the dht
|
@param known_dht_nodes: A list of nodes which the dht node
|
||||||
|
should use to bootstrap into the dht
|
||||||
|
|
||||||
@param peer_finder: An object which is used to look up peers that are associated with some hash. If None,
|
@param peer_finder: An object which is used to look up peers
|
||||||
a DHTPeerFinder will be used, which looks for peers in the distributed hash table.
|
that are associated with some hash. If None, a
|
||||||
|
DHTPeerFinder will be used, which looks for peers in the
|
||||||
|
distributed hash table.
|
||||||
|
|
||||||
@param hash_announcer: An object which announces to other peers that this peer is associated with some hash.
|
@param hash_announcer: An object which announces to other
|
||||||
If None, and peer_port is not None, a DHTHashAnnouncer will be used. If None and
|
peers that this peer is associated with some hash. If
|
||||||
peer_port is None, a DummyHashAnnouncer will be used, which will not actually announce
|
None, and peer_port is not None, a DHTHashAnnouncer will
|
||||||
anything.
|
be used. If None and peer_port is None, a
|
||||||
|
DummyHashAnnouncer will be used, which will not actually
|
||||||
|
announce anything.
|
||||||
|
|
||||||
@param blob_dir: The directory in which blobs will be stored. If None and blob_manager is None, blobs will
|
@param blob_dir: The directory in which blobs will be
|
||||||
be stored in memory only.
|
stored. If None and blob_manager is None, blobs will be
|
||||||
|
stored in memory only.
|
||||||
|
|
||||||
@param blob_manager: An object which keeps track of downloaded blobs and provides access to them. If None,
|
@param blob_manager: An object which keeps track of downloaded
|
||||||
and blob_dir is not None, a DiskBlobManager will be used, with the given blob_dir.
|
blobs and provides access to them. If None, and blob_dir
|
||||||
If None and blob_dir is None, a TempBlobManager will be used, which stores blobs in
|
is not None, a DiskBlobManager will be used, with the
|
||||||
memory only.
|
given blob_dir. If None and blob_dir is None, a
|
||||||
|
TempBlobManager will be used, which stores blobs in memory
|
||||||
|
only.
|
||||||
|
|
||||||
@param peer_port: The port on which other peers should connect to this peer
|
@param peer_port: The port on which other peers should connect
|
||||||
|
to this peer
|
||||||
|
|
||||||
@param use_upnp: Whether or not to try to open a hole in the firewall so that outside peers can connect to
|
@param use_upnp: Whether or not to try to open a hole in the
|
||||||
this peer's peer_port and dht_node_port
|
firewall so that outside peers can connect to this peer's
|
||||||
|
peer_port and dht_node_port
|
||||||
|
|
||||||
@param rate_limiter: An object which keeps track of the amount of data transferred to and from this peer,
|
@param rate_limiter: An object which keeps track of the amount
|
||||||
and can limit that rate if desired
|
of data transferred to and from this peer, and can limit
|
||||||
|
that rate if desired
|
||||||
|
|
||||||
@param wallet: An object which will be used to keep track of expected payments and which will pay peers.
|
@param wallet: An object which will be used to keep track of
|
||||||
If None, a wallet which uses the Point Trader system will be used, which is meant for testing
|
expected payments and which will pay peers. If None, a
|
||||||
only
|
wallet which uses the Point Trader system will be used,
|
||||||
|
which is meant for testing only
|
||||||
|
|
||||||
@return:
|
|
||||||
"""
|
"""
|
||||||
self.db_dir = db_dir
|
self.db_dir = db_dir
|
||||||
|
|
||||||
|
@ -142,6 +169,7 @@ class Session(object):
|
||||||
|
|
||||||
def shut_down(self):
|
def shut_down(self):
|
||||||
"""Stop all services"""
|
"""Stop all services"""
|
||||||
|
log.info('Shutting down %s', self)
|
||||||
ds = []
|
ds = []
|
||||||
if self.blob_manager is not None:
|
if self.blob_manager is not None:
|
||||||
ds.append(defer.maybeDeferred(self.blob_tracker.stop))
|
ds.append(defer.maybeDeferred(self.blob_tracker.stop))
|
||||||
|
@ -178,7 +206,9 @@ class Session(object):
|
||||||
self.external_ip = external_ip
|
self.external_ip = external_ip
|
||||||
if self.peer_port is not None:
|
if self.peer_port is not None:
|
||||||
if u.getspecificportmapping(self.peer_port, 'TCP') is None:
|
if u.getspecificportmapping(self.peer_port, 'TCP') is None:
|
||||||
u.addportmapping(self.peer_port, 'TCP', u.lanaddr, self.peer_port, 'LBRY peer port', '')
|
u.addportmapping(
|
||||||
|
self.peer_port, 'TCP', u.lanaddr, self.peer_port,
|
||||||
|
'LBRY peer port', '')
|
||||||
self.upnp_redirects.append((self.peer_port, 'TCP'))
|
self.upnp_redirects.append((self.peer_port, 'TCP'))
|
||||||
log.info("Set UPnP redirect for TCP port %d", self.peer_port)
|
log.info("Set UPnP redirect for TCP port %d", self.peer_port)
|
||||||
else:
|
else:
|
||||||
|
@ -187,16 +217,23 @@ class Session(object):
|
||||||
self.upnp_redirects.append((self.peer_port, 'TCP'))
|
self.upnp_redirects.append((self.peer_port, 'TCP'))
|
||||||
if self.dht_node_port is not None:
|
if self.dht_node_port is not None:
|
||||||
if u.getspecificportmapping(self.dht_node_port, 'UDP') is None:
|
if u.getspecificportmapping(self.dht_node_port, 'UDP') is None:
|
||||||
u.addportmapping(self.dht_node_port, 'UDP', u.lanaddr, self.dht_node_port, 'LBRY DHT port', '')
|
u.addportmapping(
|
||||||
|
self.dht_node_port, 'UDP', u.lanaddr, self.dht_node_port,
|
||||||
|
'LBRY DHT port', '')
|
||||||
self.upnp_redirects.append((self.dht_node_port, 'UDP'))
|
self.upnp_redirects.append((self.dht_node_port, 'UDP'))
|
||||||
log.info("Set UPnP redirect for UPD port %d", self.dht_node_port)
|
log.info("Set UPnP redirect for UPD port %d", self.dht_node_port)
|
||||||
else:
|
else:
|
||||||
# TODO: check that the existing redirect was put up by an old lbrynet session before grabbing it
|
# TODO: check that the existing redirect was
|
||||||
# if such a disconnected redirect exists, then upnp won't work unless the redirect is appended
|
# put up by an old lbrynet session before
|
||||||
# or is torn down and set back up. a bad shutdown of lbrynet could leave such a redirect up
|
# grabbing it if such a disconnected redirect
|
||||||
# and cause problems on the next start.
|
# exists, then upnp won't work unless the
|
||||||
# this could be problematic if a previous lbrynet session didn't make the redirect, and it was
|
# redirect is appended or is torn down and set
|
||||||
# made by another application
|
# back up. a bad shutdown of lbrynet could
|
||||||
|
# leave such a redirect up and cause problems
|
||||||
|
# on the next start. this could be
|
||||||
|
# problematic if a previous lbrynet session
|
||||||
|
# didn't make the redirect, and it was made by
|
||||||
|
# another application
|
||||||
log.warning("UPnP redirect already set for UDP port %d", self.dht_node_port)
|
log.warning("UPnP redirect already set for UDP port %d", self.dht_node_port)
|
||||||
self.upnp_redirects.append((self.dht_node_port, 'UDP'))
|
self.upnp_redirects.append((self.dht_node_port, 'UDP'))
|
||||||
return True
|
return True
|
||||||
|
@ -271,9 +308,10 @@ class Session(object):
|
||||||
self.peer_finder,
|
self.peer_finder,
|
||||||
self.dht_node)
|
self.dht_node)
|
||||||
if self.payment_rate_manager is None:
|
if self.payment_rate_manager is None:
|
||||||
self.payment_rate_manager = self.payment_rate_manager_class(self.base_payment_rate_manager,
|
self.payment_rate_manager = self.payment_rate_manager_class(
|
||||||
self.blob_tracker,
|
self.base_payment_rate_manager,
|
||||||
self.is_generous)
|
self.blob_tracker,
|
||||||
|
self.is_generous)
|
||||||
|
|
||||||
self.rate_limiter.start()
|
self.rate_limiter.start()
|
||||||
d1 = self.blob_manager.setup()
|
d1 = self.blob_manager.setup()
|
||||||
|
@ -286,7 +324,7 @@ class Session(object):
|
||||||
return dl
|
return dl
|
||||||
|
|
||||||
def _unset_upnp(self):
|
def _unset_upnp(self):
|
||||||
|
log.info("Unsetting upnp for %s", self)
|
||||||
def threaded_unset_upnp():
|
def threaded_unset_upnp():
|
||||||
u = miniupnpc.UPnP()
|
u = miniupnpc.UPnP()
|
||||||
num_devices_found = u.discover()
|
num_devices_found = u.discover()
|
||||||
|
@ -294,7 +332,9 @@ class Session(object):
|
||||||
u.selectigd()
|
u.selectigd()
|
||||||
for port, protocol in self.upnp_redirects:
|
for port, protocol in self.upnp_redirects:
|
||||||
if u.getspecificportmapping(port, protocol) is None:
|
if u.getspecificportmapping(port, protocol) is None:
|
||||||
log.warning("UPnP redirect for %s %d was removed by something else.", protocol, port)
|
log.warning(
|
||||||
|
"UPnP redirect for %s %d was removed by something else.",
|
||||||
|
protocol, port)
|
||||||
else:
|
else:
|
||||||
u.deleteportmapping(port, protocol)
|
u.deleteportmapping(port, protocol)
|
||||||
log.info("Removed UPnP redirect for %s %d.", protocol, port)
|
log.info("Removed UPnP redirect for %s %d.", protocol, port)
|
||||||
|
@ -307,5 +347,3 @@ class Session(object):
|
||||||
def _subfailure(self, err):
|
def _subfailure(self, err):
|
||||||
log.error(err.getTraceback())
|
log.error(err.getTraceback())
|
||||||
return err.value
|
return err.value
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -97,9 +97,8 @@ class Wallet(object):
|
||||||
log.error("An error occurred stopping the wallet: %s", err.getTraceback())
|
log.error("An error occurred stopping the wallet: %s", err.getTraceback())
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
log.info("Stopping %s", self)
|
||||||
self.stopped = True
|
self.stopped = True
|
||||||
|
|
||||||
# If self.next_manage_call is None, then manage is currently running or else
|
# If self.next_manage_call is None, then manage is currently running or else
|
||||||
# start has not been called, so set stopped and do nothing else.
|
# start has not been called, so set stopped and do nothing else.
|
||||||
if self.next_manage_call is not None:
|
if self.next_manage_call is not None:
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import binascii
|
import binascii
|
||||||
|
import logging
|
||||||
|
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from lbrynet.interfaces import IPeerFinder
|
from lbrynet.interfaces import IPeerFinder
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DHTPeerFinder(object):
|
class DHTPeerFinder(object):
|
||||||
"""This class finds peers which have announced to the DHT that they have certain blobs"""
|
"""This class finds peers which have announced to the DHT that they have certain blobs"""
|
||||||
implements(IPeerFinder)
|
implements(IPeerFinder)
|
||||||
|
@ -21,6 +26,7 @@ class DHTPeerFinder(object):
|
||||||
self.next_manage_call = reactor.callLater(60, self.run_manage_loop)
|
self.next_manage_call = reactor.callLater(60, self.run_manage_loop)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
log.info("Stopping %s", self)
|
||||||
if self.next_manage_call is not None and self.next_manage_call.active():
|
if self.next_manage_call is not None and self.next_manage_call.active():
|
||||||
self.next_manage_call.cancel()
|
self.next_manage_call.cancel()
|
||||||
self.next_manage_call = None
|
self.next_manage_call = None
|
||||||
|
@ -45,4 +51,4 @@ class DHTPeerFinder(object):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def get_most_popular_hashes(self, num_to_return):
|
def get_most_popular_hashes(self, num_to_return):
|
||||||
return self.dht_node.get_most_popular_hashes(num_to_return)
|
return self.dht_node.get_most_popular_hashes(num_to_return)
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import binascii
|
import binascii
|
||||||
from twisted.internet import defer, reactor
|
|
||||||
import collections
|
import collections
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from twisted.internet import defer, reactor
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DHTHashAnnouncer(object):
|
class DHTHashAnnouncer(object):
|
||||||
|
@ -22,6 +27,7 @@ class DHTHashAnnouncer(object):
|
||||||
self.next_manage_call = reactor.callLater(60, self.run_manage_loop)
|
self.next_manage_call = reactor.callLater(60, self.run_manage_loop)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
log.info("Stopping %s", self)
|
||||||
if self.next_manage_call is not None:
|
if self.next_manage_call is not None:
|
||||||
self.next_manage_call.cancel()
|
self.next_manage_call.cancel()
|
||||||
self.next_manage_call = None
|
self.next_manage_call = None
|
||||||
|
|
|
@ -21,6 +21,7 @@ import msgtypes
|
||||||
import msgformat
|
import msgformat
|
||||||
from contact import Contact
|
from contact import Contact
|
||||||
|
|
||||||
|
|
||||||
reactor = twisted.internet.reactor
|
reactor = twisted.internet.reactor
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -124,10 +125,10 @@ class KademliaProtocol(protocol.DatagramProtocol):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
log.warning("Couldn't decode dht datagram from %s", address)
|
log.warning("Couldn't decode dht datagram from %s", address)
|
||||||
return
|
return
|
||||||
|
|
||||||
message = self._translator.fromPrimitive(msgPrimitive)
|
message = self._translator.fromPrimitive(msgPrimitive)
|
||||||
remoteContact = Contact(message.nodeID, address[0], address[1], self)
|
remoteContact = Contact(message.nodeID, address[0], address[1], self)
|
||||||
|
|
||||||
# Refresh the remote node's details in the local node's k-buckets
|
# Refresh the remote node's details in the local node's k-buckets
|
||||||
self._node.addContact(remoteContact)
|
self._node.addContact(remoteContact)
|
||||||
|
|
||||||
|
@ -143,7 +144,9 @@ class KademliaProtocol(protocol.DatagramProtocol):
|
||||||
del self._sentMessages[message.id]
|
del self._sentMessages[message.id]
|
||||||
|
|
||||||
if hasattr(df, '_rpcRawResponse'):
|
if hasattr(df, '_rpcRawResponse'):
|
||||||
# The RPC requested that the raw response message and originating address be returned; do not interpret it
|
# The RPC requested that the raw response message
|
||||||
|
# and originating address be returned; do not
|
||||||
|
# interpret it
|
||||||
df.callback((message, address))
|
df.callback((message, address))
|
||||||
elif isinstance(message, msgtypes.ErrorMessage):
|
elif isinstance(message, msgtypes.ErrorMessage):
|
||||||
# The RPC request raised a remote exception; raise it locally
|
# The RPC request raised a remote exception; raise it locally
|
||||||
|
@ -175,7 +178,7 @@ class KademliaProtocol(protocol.DatagramProtocol):
|
||||||
def _send(self, data, rpcID, address):
|
def _send(self, data, rpcID, address):
|
||||||
""" Transmit the specified data over UDP, breaking it up into several
|
""" Transmit the specified data over UDP, breaking it up into several
|
||||||
packets if necessary
|
packets if necessary
|
||||||
|
|
||||||
If the data is spread over multiple UDP datagrams, the packets have the
|
If the data is spread over multiple UDP datagrams, the packets have the
|
||||||
following structure::
|
following structure::
|
||||||
| | | | | |||||||||||| 0x00 |
|
| | | | | |||||||||||| 0x00 |
|
||||||
|
@ -183,7 +186,7 @@ class KademliaProtocol(protocol.DatagramProtocol):
|
||||||
| type ID | of packets |of this packet | | indicator|
|
| type ID | of packets |of this packet | | indicator|
|
||||||
| (1 byte) | (2 bytes) | (2 bytes) |(20 bytes)| (1 byte) |
|
| (1 byte) | (2 bytes) | (2 bytes) |(20 bytes)| (1 byte) |
|
||||||
| | | | | |||||||||||| |
|
| | | | | |||||||||||| |
|
||||||
|
|
||||||
@note: The header used for breaking up large data segments will
|
@note: The header used for breaking up large data segments will
|
||||||
possibly be moved out of the KademliaProtocol class in the
|
possibly be moved out of the KademliaProtocol class in the
|
||||||
future, into something similar to a message translator/encoder
|
future, into something similar to a message translator/encoder
|
||||||
|
@ -301,19 +304,19 @@ class KademliaProtocol(protocol.DatagramProtocol):
|
||||||
df.errback(failure.Failure(TimeoutError(remoteContactID)))
|
df.errback(failure.Failure(TimeoutError(remoteContactID)))
|
||||||
else:
|
else:
|
||||||
# This should never be reached
|
# This should never be reached
|
||||||
print "ERROR: deferred timed out, but is not present in sent messages list!"
|
log.error("deferred timed out, but is not present in sent messages list!")
|
||||||
|
|
||||||
def stopProtocol(self):
|
def stopProtocol(self):
|
||||||
""" Called when the transport is disconnected.
|
""" Called when the transport is disconnected.
|
||||||
|
|
||||||
Will only be called once, after all ports are disconnected.
|
Will only be called once, after all ports are disconnected.
|
||||||
"""
|
"""
|
||||||
|
log.info('Stopping dht')
|
||||||
for key in self._callLaterList.keys():
|
for key in self._callLaterList.keys():
|
||||||
try:
|
try:
|
||||||
if key > time.time():
|
if key > time.time():
|
||||||
|
log.info('Cancelling %s', self._callLaterList[key])
|
||||||
self._callLaterList[key].cancel()
|
self._callLaterList[key].cancel()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print e
|
log.exception('Failed to cancel %s', self._callLaterList[key])
|
||||||
del self._callLaterList[key]
|
del self._callLaterList[key]
|
||||||
#TODO: test: do we really need the reactor.iterate() call?
|
|
||||||
reactor.iterate()
|
|
||||||
|
|
|
@ -160,7 +160,7 @@ class EncryptedFileManager(object):
|
||||||
return defer.fail(Failure(ValueError("Could not find that LBRY file")))
|
return defer.fail(Failure(ValueError("Could not find that LBRY file")))
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
log.info('Stopping %s', self)
|
||||||
ds = []
|
ds = []
|
||||||
|
|
||||||
def wait_for_finished(lbry_file, count=2):
|
def wait_for_finished(lbry_file, count=2):
|
||||||
|
@ -243,4 +243,4 @@ class EncryptedFileManager(object):
|
||||||
@rerun_if_locked
|
@rerun_if_locked
|
||||||
def _get_count_for_stream_hash(self, stream_hash):
|
def _get_count_for_stream_hash(self, stream_hash):
|
||||||
return self.sql_db.runQuery("select count(*) from lbry_file_options where stream_hash = ?",
|
return self.sql_db.runQuery("select count(*) from lbry_file_options where stream_hash = ?",
|
||||||
(stream_hash,))
|
(stream_hash,))
|
||||||
|
|
|
@ -560,6 +560,7 @@ class Daemon(AuthJSONRPCServer):
|
||||||
try:
|
try:
|
||||||
if self.lbry_server_port is not None:
|
if self.lbry_server_port is not None:
|
||||||
self.lbry_server_port, p = None, self.lbry_server_port
|
self.lbry_server_port, p = None, self.lbry_server_port
|
||||||
|
log.info('Stop listening to %s', p)
|
||||||
return defer.maybeDeferred(p.stopListening)
|
return defer.maybeDeferred(p.stopListening)
|
||||||
else:
|
else:
|
||||||
return defer.succeed(True)
|
return defer.succeed(True)
|
||||||
|
@ -650,13 +651,14 @@ class Daemon(AuthJSONRPCServer):
|
||||||
log.warn('Failed to upload log', exc_info=True)
|
log.warn('Failed to upload log', exc_info=True)
|
||||||
d = defer.succeed(None)
|
d = defer.succeed(None)
|
||||||
d.addCallback(lambda _: self._stop_server())
|
d.addCallback(lambda _: self._stop_server())
|
||||||
|
d.addErrback(log_support.failure, log, 'Failure while shutting down: %s')
|
||||||
d.addCallback(lambda _: self._stop_reflector())
|
d.addCallback(lambda _: self._stop_reflector())
|
||||||
d.addErrback(lambda err: True)
|
d.addErrback(log_support.failure, log, 'Failure while shutting down: %s')
|
||||||
d.addCallback(lambda _: self.lbry_file_manager.stop())
|
d.addCallback(lambda _: self.lbry_file_manager.stop())
|
||||||
d.addErrback(lambda err: True)
|
d.addErrback(log_support.failure, log, 'Failure while shutting down: %s')
|
||||||
if self.session is not None:
|
if self.session is not None:
|
||||||
d.addCallback(lambda _: self.session.shut_down())
|
d.addCallback(lambda _: self.session.shut_down())
|
||||||
d.addErrback(lambda err: True)
|
d.addErrback(log_support.failure, log, 'Failure while shutting down: %s')
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def _update_settings(self, settings):
|
def _update_settings(self, settings):
|
||||||
|
@ -770,14 +772,11 @@ class Daemon(AuthJSONRPCServer):
|
||||||
d = self.lbry_file_metadata_manager.setup()
|
d = self.lbry_file_metadata_manager.setup()
|
||||||
|
|
||||||
def set_lbry_file_manager():
|
def set_lbry_file_manager():
|
||||||
self.lbry_file_manager = EncryptedFileManager(self.session,
|
self.lbry_file_manager = EncryptedFileManager(
|
||||||
self.lbry_file_metadata_manager,
|
self.session, self.lbry_file_metadata_manager,
|
||||||
self.sd_identifier,
|
self.sd_identifier, download_directory=self.download_directory)
|
||||||
download_directory=self.download_directory)
|
|
||||||
return self.lbry_file_manager.setup()
|
return self.lbry_file_manager.setup()
|
||||||
|
|
||||||
d.addCallback(lambda _: set_lbry_file_manager())
|
d.addCallback(lambda _: set_lbry_file_manager())
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def _get_analytics(self):
|
def _get_analytics(self):
|
||||||
|
|
|
@ -29,6 +29,7 @@ from lbrynet.core import utils
|
||||||
if platform.mac_ver()[0] >= "10.10":
|
if platform.mac_ver()[0] >= "10.10":
|
||||||
from LBRYNotify import LBRYNotify
|
from LBRYNotify import LBRYNotify
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,9 +47,11 @@ class LBRYDaemonApp(AppKit.NSApplication):
|
||||||
self.icon.setSize_((20, 20))
|
self.icon.setSize_((20, 20))
|
||||||
self.statusitem.setImage_(self.icon)
|
self.statusitem.setImage_(self.icon)
|
||||||
self.menubarMenu = AppKit.NSMenu.alloc().init()
|
self.menubarMenu = AppKit.NSMenu.alloc().init()
|
||||||
self.open = AppKit.NSMenuItem.alloc().initWithTitle_action_keyEquivalent_("Open", "openui:", "")
|
self.open = AppKit.NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||||
|
"Open", "openui:", "")
|
||||||
self.menubarMenu.addItem_(self.open)
|
self.menubarMenu.addItem_(self.open)
|
||||||
self.quit = AppKit.NSMenuItem.alloc().initWithTitle_action_keyEquivalent_("Quit", "replyToApplicationShouldTerminate:", "")
|
self.quit = AppKit.NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
|
||||||
|
"Quit", "applicationShouldTerminate:", "")
|
||||||
self.menubarMenu.addItem_(self.quit)
|
self.menubarMenu.addItem_(self.quit)
|
||||||
self.statusitem.setMenu_(self.menubarMenu)
|
self.statusitem.setMenu_(self.menubarMenu)
|
||||||
self.statusitem.setToolTip_(settings.APP_NAME)
|
self.statusitem.setToolTip_(settings.APP_NAME)
|
||||||
|
@ -64,9 +67,14 @@ class LBRYDaemonApp(AppKit.NSApplication):
|
||||||
def openui_(self, sender):
|
def openui_(self, sender):
|
||||||
webbrowser.open(settings.UI_ADDRESS)
|
webbrowser.open(settings.UI_ADDRESS)
|
||||||
|
|
||||||
def replyToApplicationShouldTerminate_(self, shouldTerminate):
|
# this code is from the example https://pythonhosted.org/pyobjc/examples/Cocoa/Twisted/WebServicesTool/index.html
|
||||||
notify("Goodbye!")
|
def applicationShouldTerminate_(self, sender):
|
||||||
reactor.stop()
|
if reactor.running:
|
||||||
|
log.info('Stopping twisted event loop')
|
||||||
|
notify("Goodbye!")
|
||||||
|
reactor.stop()
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def notify(msg):
|
def notify(msg):
|
||||||
|
|
|
@ -18,9 +18,14 @@ def main():
|
||||||
log_file = conf.settings.get_log_filename()
|
log_file = conf.settings.get_log_filename()
|
||||||
log_support.configure_logging(log_file, console=True)
|
log_support.configure_logging(log_file, console=True)
|
||||||
app = LBRYDaemonApp.sharedApplication()
|
app = LBRYDaemonApp.sharedApplication()
|
||||||
reactor.addSystemEventTrigger("after", "shutdown", AppHelper.stopEventLoop)
|
reactor.addSystemEventTrigger("after", "shutdown", shutdown)
|
||||||
reactor.run()
|
reactor.run()
|
||||||
|
|
||||||
|
|
||||||
|
def shutdown():
|
||||||
|
log.info('Stopping event loop')
|
||||||
|
AppHelper.stopEventLoop()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Add table
Reference in a new issue