mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
py3: Port Zeroconf/mdns
Requires zeroconf and ifaddr dependencies on python3
This commit is contained in:
parent
ef3d0cfe6c
commit
7548a52e3d
@ -27,14 +27,8 @@ class BonJour(object): # {{{
|
||||
ip_address, port = loop.bound_address[:2]
|
||||
self.zeroconf_ip_address = zipa = verify_ipV4_address(ip_address) or get_external_ip()
|
||||
prefix = loop.opts.url_prefix or ''
|
||||
# The Zeroconf module requires everything to be bytestrings
|
||||
|
||||
def enc(x):
|
||||
if not isinstance(x, bytes):
|
||||
x = x.encode('ascii')
|
||||
return x
|
||||
mdns_services = (
|
||||
(enc(self.service_name), enc(self.service_type), port, {b'path':enc(prefix + self.path)}),
|
||||
(self.service_name, self.service_type, port, {'path':prefix + self.path}),
|
||||
)
|
||||
if self.shutdown.is_set():
|
||||
return
|
||||
|
@ -12,6 +12,7 @@ from unittest import skipIf
|
||||
from glob import glob
|
||||
from threading import Event
|
||||
|
||||
from calibre.constants import ispy3
|
||||
from calibre.srv.pre_activated import has_preactivated_support
|
||||
from calibre.srv.tests.base import BaseTest, TestServer
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
@ -114,15 +115,18 @@ class LoopTest(BaseTest):
|
||||
def test_bonjour(self):
|
||||
'Test advertising via BonJour'
|
||||
from calibre.srv.bonjour import BonJour
|
||||
from calibre.utils.Zeroconf import Zeroconf
|
||||
if ispy3:
|
||||
from zeroconf import Zeroconf
|
||||
else:
|
||||
from calibre.utils.Zeroconf import Zeroconf
|
||||
b = BonJour()
|
||||
with TestServer(lambda data:(data.path[0] + data.read()), plugins=(b,), shutdown_timeout=5) as server:
|
||||
self.assertTrue(b.started.wait(5), 'BonJour not started')
|
||||
self.ae(b.advertised_port, server.address[1])
|
||||
service = b.services[0]
|
||||
self.ae(service.type, b'_calibre._tcp.local.')
|
||||
self.ae(service.type, '_calibre._tcp.local.')
|
||||
r = Zeroconf()
|
||||
info = r.getServiceInfo(service.type, service.name)
|
||||
info = r.get_service_info(service.type, service.name)
|
||||
self.assertIsNotNone(info)
|
||||
self.ae(info.text, b'\npath=/opds')
|
||||
|
||||
|
@ -13,7 +13,7 @@ Test a binary calibre build to ensure that all needed binary images/libraries ha
|
||||
|
||||
import os, ctypes, sys, unittest, time
|
||||
|
||||
from calibre.constants import plugins, iswindows, islinux, isosx
|
||||
from calibre.constants import plugins, iswindows, islinux, isosx, ispy3
|
||||
from polyglot.builtins import iteritems, map, unicode_type
|
||||
|
||||
is_ci = os.environ.get('CI', '').lower() == 'true'
|
||||
@ -77,6 +77,15 @@ class BuildTest(unittest.TestCase):
|
||||
import soupsieve, bs4
|
||||
del soupsieve, bs4
|
||||
|
||||
def test_zeroconf(self):
|
||||
if ispy3:
|
||||
import zeroconf as z, ifaddr
|
||||
else:
|
||||
import calibre.utils.Zeroconf as z
|
||||
ifaddr = None
|
||||
del z
|
||||
del ifaddr
|
||||
|
||||
def test_plugins(self):
|
||||
exclusions = set()
|
||||
if is_ci:
|
||||
|
@ -1384,6 +1384,7 @@ class Zeroconf(object):
|
||||
if info.request(self, timeout):
|
||||
return info
|
||||
return None
|
||||
get_service_info = getServiceInfo
|
||||
|
||||
def addServiceListener(self, type, listener):
|
||||
"""Adds a listener for a particular service type. This object
|
||||
@ -1424,6 +1425,7 @@ class Zeroconf(object):
|
||||
self.send(out)
|
||||
i += 1
|
||||
nextTime += _REGISTER_TIME
|
||||
register_service = registerService
|
||||
|
||||
def unregisterService(self, info):
|
||||
"""Unregister a service."""
|
||||
@ -1452,6 +1454,7 @@ class Zeroconf(object):
|
||||
self.send(out)
|
||||
i += 1
|
||||
nextTime += _UNREGISTER_TIME
|
||||
unregister_service = unregisterService
|
||||
|
||||
def unregisterAllServices(self):
|
||||
"""Unregister all registered services."""
|
||||
|
@ -1,4 +1,4 @@
|
||||
from __future__ import with_statement
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
__license__ = 'GPL 3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
@ -9,10 +9,12 @@ from threading import Thread
|
||||
|
||||
from calibre.utils.filenames import ascii_text
|
||||
from calibre import force_unicode
|
||||
from calibre.constants import ispy3
|
||||
from polyglot.builtins import iteritems, unicode_type
|
||||
|
||||
_server = None
|
||||
|
||||
_all_ip_addresses = dict()
|
||||
_all_ip_addresses = {}
|
||||
|
||||
|
||||
class AllIpAddressesGetter(Thread):
|
||||
@ -50,9 +52,9 @@ def get_all_ips(reinitialize=False):
|
||||
global _all_ip_addresses, _ip_address_getter_thread
|
||||
if not _ip_address_getter_thread or (reinitialize and not
|
||||
_ip_address_getter_thread.is_alive()):
|
||||
_all_ip_addresses = dict()
|
||||
_all_ip_addresses = {}
|
||||
_ip_address_getter_thread = AllIpAddressesGetter()
|
||||
_ip_address_getter_thread.setDaemon(True)
|
||||
_ip_address_getter_thread.daemon = True
|
||||
_ip_address_getter_thread.start()
|
||||
return _all_ip_addresses
|
||||
|
||||
@ -61,7 +63,7 @@ def _get_external_ip():
|
||||
'Get IP address of interface used to connect to the outside world'
|
||||
try:
|
||||
ipaddr = socket.gethostbyname(socket.gethostname())
|
||||
except:
|
||||
except Exception:
|
||||
ipaddr = '127.0.0.1'
|
||||
if ipaddr.startswith('127.'):
|
||||
for addr in ('192.0.2.0', '198.51.100.0', 'google.com'):
|
||||
@ -85,7 +87,7 @@ def verify_ipV4_address(ip_address):
|
||||
socket.inet_aton(ip_address)
|
||||
if len(ip_address.split('.')) == 4:
|
||||
result = ip_address
|
||||
except socket.error:
|
||||
except (socket.error, OSError):
|
||||
# Not legal ip address
|
||||
pass
|
||||
return result
|
||||
@ -108,7 +110,10 @@ def get_external_ip():
|
||||
def start_server():
|
||||
global _server
|
||||
if _server is None:
|
||||
from calibre.utils.Zeroconf import Zeroconf
|
||||
if ispy3:
|
||||
from zeroconf import Zeroconf
|
||||
else:
|
||||
from calibre.utils.Zeroconf import Zeroconf
|
||||
try:
|
||||
_server = Zeroconf()
|
||||
except Exception:
|
||||
@ -120,7 +125,7 @@ def start_server():
|
||||
return _server
|
||||
|
||||
|
||||
def create_service(desc, type, port, properties, add_hostname, use_ip_address=None):
|
||||
def create_service(desc, service_type, port, properties, add_hostname, use_ip_address=None):
|
||||
port = int(port)
|
||||
try:
|
||||
hostname = ascii_text(force_unicode(socket.gethostname())).partition('.')[0]
|
||||
@ -142,42 +147,60 @@ def create_service(desc, type, port, properties, add_hostname, use_ip_address=No
|
||||
local_ip = get_external_ip()
|
||||
if not local_ip:
|
||||
raise ValueError('Failed to determine local IP address to advertise via BonJour')
|
||||
type = type+'.local.'
|
||||
from calibre.utils.Zeroconf import ServiceInfo
|
||||
return ServiceInfo(type, desc+'.'+type,
|
||||
address=socket.inet_aton(local_ip),
|
||||
port=port,
|
||||
properties=properties,
|
||||
server=hostname+'.local.')
|
||||
service_type = service_type+'.local.'
|
||||
service_name = desc + '.' + service_type
|
||||
server_name = hostname+'.local.'
|
||||
if ispy3:
|
||||
from zeroconf import ServiceInfo
|
||||
else:
|
||||
from calibre.utils.Zeroconf import ServiceInfo
|
||||
|
||||
def enc(x):
|
||||
if isinstance(x, unicode_type):
|
||||
x = x.encode('ascii')
|
||||
return x
|
||||
|
||||
service_type = enc(service_type)
|
||||
service_name = enc(service_name)
|
||||
server_name = enc(server_name)
|
||||
if properties:
|
||||
properties = {enc(k): enc(v) for k, v in iteritems(properties)}
|
||||
|
||||
return ServiceInfo(
|
||||
service_type, service_name,
|
||||
address=socket.inet_aton(local_ip),
|
||||
port=port,
|
||||
properties=properties,
|
||||
server=server_name)
|
||||
|
||||
|
||||
def publish(desc, type, port, properties=None, add_hostname=True, use_ip_address=None):
|
||||
def publish(desc, service_type, port, properties=None, add_hostname=True, use_ip_address=None):
|
||||
'''
|
||||
Publish a service.
|
||||
|
||||
:param desc: Description of service
|
||||
:param type: Name and type of service. For example _stanza._tcp
|
||||
:param service_type: Name and type of service. For example _stanza._tcp
|
||||
:param port: Port the service listens on
|
||||
:param properties: An optional dictionary whose keys and values will be put
|
||||
into the TXT record.
|
||||
'''
|
||||
server = start_server()
|
||||
service = create_service(desc, type, port, properties, add_hostname,
|
||||
service = create_service(desc, service_type, port, properties, add_hostname,
|
||||
use_ip_address)
|
||||
server.registerService(service)
|
||||
server.register_service(service)
|
||||
return service
|
||||
|
||||
|
||||
def unpublish(desc, type, port, properties=None, add_hostname=True):
|
||||
def unpublish(desc, service_type, port, properties=None, add_hostname=True):
|
||||
'''
|
||||
Unpublish a service.
|
||||
|
||||
The parameters must be the same as used in the corresponding call to publish
|
||||
'''
|
||||
server = start_server()
|
||||
service = create_service(desc, type, port, properties, add_hostname)
|
||||
server.unregisterService(service)
|
||||
if server.countRegisteredServices() == 0:
|
||||
service = create_service(desc, service_type, port, properties, add_hostname)
|
||||
server.unregister_service(service)
|
||||
if len(server.services) == 0:
|
||||
stop_server()
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user