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]
|
ip_address, port = loop.bound_address[:2]
|
||||||
self.zeroconf_ip_address = zipa = verify_ipV4_address(ip_address) or get_external_ip()
|
self.zeroconf_ip_address = zipa = verify_ipV4_address(ip_address) or get_external_ip()
|
||||||
prefix = loop.opts.url_prefix or ''
|
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 = (
|
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():
|
if self.shutdown.is_set():
|
||||||
return
|
return
|
||||||
|
@ -12,6 +12,7 @@ from unittest import skipIf
|
|||||||
from glob import glob
|
from glob import glob
|
||||||
from threading import Event
|
from threading import Event
|
||||||
|
|
||||||
|
from calibre.constants import ispy3
|
||||||
from calibre.srv.pre_activated import has_preactivated_support
|
from calibre.srv.pre_activated import has_preactivated_support
|
||||||
from calibre.srv.tests.base import BaseTest, TestServer
|
from calibre.srv.tests.base import BaseTest, TestServer
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
@ -114,15 +115,18 @@ class LoopTest(BaseTest):
|
|||||||
def test_bonjour(self):
|
def test_bonjour(self):
|
||||||
'Test advertising via BonJour'
|
'Test advertising via BonJour'
|
||||||
from calibre.srv.bonjour import BonJour
|
from calibre.srv.bonjour import BonJour
|
||||||
|
if ispy3:
|
||||||
|
from zeroconf import Zeroconf
|
||||||
|
else:
|
||||||
from calibre.utils.Zeroconf import Zeroconf
|
from calibre.utils.Zeroconf import Zeroconf
|
||||||
b = BonJour()
|
b = BonJour()
|
||||||
with TestServer(lambda data:(data.path[0] + data.read()), plugins=(b,), shutdown_timeout=5) as server:
|
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.assertTrue(b.started.wait(5), 'BonJour not started')
|
||||||
self.ae(b.advertised_port, server.address[1])
|
self.ae(b.advertised_port, server.address[1])
|
||||||
service = b.services[0]
|
service = b.services[0]
|
||||||
self.ae(service.type, b'_calibre._tcp.local.')
|
self.ae(service.type, '_calibre._tcp.local.')
|
||||||
r = Zeroconf()
|
r = Zeroconf()
|
||||||
info = r.getServiceInfo(service.type, service.name)
|
info = r.get_service_info(service.type, service.name)
|
||||||
self.assertIsNotNone(info)
|
self.assertIsNotNone(info)
|
||||||
self.ae(info.text, b'\npath=/opds')
|
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
|
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
|
from polyglot.builtins import iteritems, map, unicode_type
|
||||||
|
|
||||||
is_ci = os.environ.get('CI', '').lower() == 'true'
|
is_ci = os.environ.get('CI', '').lower() == 'true'
|
||||||
@ -77,6 +77,15 @@ class BuildTest(unittest.TestCase):
|
|||||||
import soupsieve, bs4
|
import soupsieve, bs4
|
||||||
del 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):
|
def test_plugins(self):
|
||||||
exclusions = set()
|
exclusions = set()
|
||||||
if is_ci:
|
if is_ci:
|
||||||
|
@ -1384,6 +1384,7 @@ class Zeroconf(object):
|
|||||||
if info.request(self, timeout):
|
if info.request(self, timeout):
|
||||||
return info
|
return info
|
||||||
return None
|
return None
|
||||||
|
get_service_info = getServiceInfo
|
||||||
|
|
||||||
def addServiceListener(self, type, listener):
|
def addServiceListener(self, type, listener):
|
||||||
"""Adds a listener for a particular service type. This object
|
"""Adds a listener for a particular service type. This object
|
||||||
@ -1424,6 +1425,7 @@ class Zeroconf(object):
|
|||||||
self.send(out)
|
self.send(out)
|
||||||
i += 1
|
i += 1
|
||||||
nextTime += _REGISTER_TIME
|
nextTime += _REGISTER_TIME
|
||||||
|
register_service = registerService
|
||||||
|
|
||||||
def unregisterService(self, info):
|
def unregisterService(self, info):
|
||||||
"""Unregister a service."""
|
"""Unregister a service."""
|
||||||
@ -1452,6 +1454,7 @@ class Zeroconf(object):
|
|||||||
self.send(out)
|
self.send(out)
|
||||||
i += 1
|
i += 1
|
||||||
nextTime += _UNREGISTER_TIME
|
nextTime += _UNREGISTER_TIME
|
||||||
|
unregister_service = unregisterService
|
||||||
|
|
||||||
def unregisterAllServices(self):
|
def unregisterAllServices(self):
|
||||||
"""Unregister all registered services."""
|
"""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'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
@ -9,10 +9,12 @@ from threading import Thread
|
|||||||
|
|
||||||
from calibre.utils.filenames import ascii_text
|
from calibre.utils.filenames import ascii_text
|
||||||
from calibre import force_unicode
|
from calibre import force_unicode
|
||||||
|
from calibre.constants import ispy3
|
||||||
|
from polyglot.builtins import iteritems, unicode_type
|
||||||
|
|
||||||
_server = None
|
_server = None
|
||||||
|
|
||||||
_all_ip_addresses = dict()
|
_all_ip_addresses = {}
|
||||||
|
|
||||||
|
|
||||||
class AllIpAddressesGetter(Thread):
|
class AllIpAddressesGetter(Thread):
|
||||||
@ -50,9 +52,9 @@ def get_all_ips(reinitialize=False):
|
|||||||
global _all_ip_addresses, _ip_address_getter_thread
|
global _all_ip_addresses, _ip_address_getter_thread
|
||||||
if not _ip_address_getter_thread or (reinitialize and not
|
if not _ip_address_getter_thread or (reinitialize and not
|
||||||
_ip_address_getter_thread.is_alive()):
|
_ip_address_getter_thread.is_alive()):
|
||||||
_all_ip_addresses = dict()
|
_all_ip_addresses = {}
|
||||||
_ip_address_getter_thread = AllIpAddressesGetter()
|
_ip_address_getter_thread = AllIpAddressesGetter()
|
||||||
_ip_address_getter_thread.setDaemon(True)
|
_ip_address_getter_thread.daemon = True
|
||||||
_ip_address_getter_thread.start()
|
_ip_address_getter_thread.start()
|
||||||
return _all_ip_addresses
|
return _all_ip_addresses
|
||||||
|
|
||||||
@ -61,7 +63,7 @@ def _get_external_ip():
|
|||||||
'Get IP address of interface used to connect to the outside world'
|
'Get IP address of interface used to connect to the outside world'
|
||||||
try:
|
try:
|
||||||
ipaddr = socket.gethostbyname(socket.gethostname())
|
ipaddr = socket.gethostbyname(socket.gethostname())
|
||||||
except:
|
except Exception:
|
||||||
ipaddr = '127.0.0.1'
|
ipaddr = '127.0.0.1'
|
||||||
if ipaddr.startswith('127.'):
|
if ipaddr.startswith('127.'):
|
||||||
for addr in ('192.0.2.0', '198.51.100.0', 'google.com'):
|
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)
|
socket.inet_aton(ip_address)
|
||||||
if len(ip_address.split('.')) == 4:
|
if len(ip_address.split('.')) == 4:
|
||||||
result = ip_address
|
result = ip_address
|
||||||
except socket.error:
|
except (socket.error, OSError):
|
||||||
# Not legal ip address
|
# Not legal ip address
|
||||||
pass
|
pass
|
||||||
return result
|
return result
|
||||||
@ -108,6 +110,9 @@ def get_external_ip():
|
|||||||
def start_server():
|
def start_server():
|
||||||
global _server
|
global _server
|
||||||
if _server is None:
|
if _server is None:
|
||||||
|
if ispy3:
|
||||||
|
from zeroconf import Zeroconf
|
||||||
|
else:
|
||||||
from calibre.utils.Zeroconf import Zeroconf
|
from calibre.utils.Zeroconf import Zeroconf
|
||||||
try:
|
try:
|
||||||
_server = Zeroconf()
|
_server = Zeroconf()
|
||||||
@ -120,7 +125,7 @@ def start_server():
|
|||||||
return _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)
|
port = int(port)
|
||||||
try:
|
try:
|
||||||
hostname = ascii_text(force_unicode(socket.gethostname())).partition('.')[0]
|
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()
|
local_ip = get_external_ip()
|
||||||
if not local_ip:
|
if not local_ip:
|
||||||
raise ValueError('Failed to determine local IP address to advertise via BonJour')
|
raise ValueError('Failed to determine local IP address to advertise via BonJour')
|
||||||
type = type+'.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
|
from calibre.utils.Zeroconf import ServiceInfo
|
||||||
return ServiceInfo(type, desc+'.'+type,
|
|
||||||
|
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),
|
address=socket.inet_aton(local_ip),
|
||||||
port=port,
|
port=port,
|
||||||
properties=properties,
|
properties=properties,
|
||||||
server=hostname+'.local.')
|
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.
|
Publish a service.
|
||||||
|
|
||||||
:param desc: Description of 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 port: Port the service listens on
|
||||||
:param properties: An optional dictionary whose keys and values will be put
|
:param properties: An optional dictionary whose keys and values will be put
|
||||||
into the TXT record.
|
into the TXT record.
|
||||||
'''
|
'''
|
||||||
server = start_server()
|
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)
|
use_ip_address)
|
||||||
server.registerService(service)
|
server.register_service(service)
|
||||||
return 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.
|
Unpublish a service.
|
||||||
|
|
||||||
The parameters must be the same as used in the corresponding call to publish
|
The parameters must be the same as used in the corresponding call to publish
|
||||||
'''
|
'''
|
||||||
server = start_server()
|
server = start_server()
|
||||||
service = create_service(desc, type, port, properties, add_hostname)
|
service = create_service(desc, service_type, port, properties, add_hostname)
|
||||||
server.unregisterService(service)
|
server.unregister_service(service)
|
||||||
if server.countRegisteredServices() == 0:
|
if len(server.services) == 0:
|
||||||
stop_server()
|
stop_server()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user