mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Wireless device connections: Add an option to force calibre ot listen on a particular IP address. Access it by customizing the plugin in Preferences->Plugins
This commit is contained in:
commit
fc0b2732a6
@ -33,7 +33,7 @@ from calibre.utils.config import from_json, tweaks
|
|||||||
from calibre.utils.date import isoformat, now
|
from calibre.utils.date import isoformat, now
|
||||||
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
|
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
|
||||||
from calibre.utils.mdns import (publish as publish_zeroconf, unpublish as
|
from calibre.utils.mdns import (publish as publish_zeroconf, unpublish as
|
||||||
unpublish_zeroconf)
|
unpublish_zeroconf, get_all_ips)
|
||||||
|
|
||||||
def synchronous(tlockname):
|
def synchronous(tlockname):
|
||||||
"""A decorator to place an instance based lock around a method """
|
"""A decorator to place an instance based lock around a method """
|
||||||
@ -46,10 +46,6 @@ def synchronous(tlockname):
|
|||||||
return _synchronizer
|
return _synchronizer
|
||||||
return _synched
|
return _synched
|
||||||
|
|
||||||
def do_zeroconf(f, port):
|
|
||||||
f('calibre smart device client',
|
|
||||||
'_calibresmartdeviceapp._tcp', port, {})
|
|
||||||
|
|
||||||
|
|
||||||
class SDBook(Book):
|
class SDBook(Book):
|
||||||
def __init__(self, prefix, lpath, size=None, other=None):
|
def __init__(self, prefix, lpath, size=None, other=None):
|
||||||
@ -80,7 +76,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
CAN_DO_DEVICE_DB_PLUGBOARD = False
|
CAN_DO_DEVICE_DB_PLUGBOARD = False
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
MUST_READ_METADATA = True
|
MUST_READ_METADATA = True
|
||||||
NEWS_IN_FOLDER = False
|
NEWS_IN_FOLDER = True
|
||||||
SUPPORTS_USE_AUTHOR_SORT = False
|
SUPPORTS_USE_AUTHOR_SORT = False
|
||||||
WANTS_UPDATED_THUMBNAILS = True
|
WANTS_UPDATED_THUMBNAILS = True
|
||||||
MAX_PATH_LEN = 250
|
MAX_PATH_LEN = 250
|
||||||
@ -97,7 +93,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
SEND_NOOP_EVERY_NTH_PROBE = 5
|
SEND_NOOP_EVERY_NTH_PROBE = 5
|
||||||
DISCONNECT_AFTER_N_SECONDS = 30*60 # 30 minutes
|
DISCONNECT_AFTER_N_SECONDS = 30*60 # 30 minutes
|
||||||
|
|
||||||
ZEROCONF_CLIENT_STRING = b'calibre smart device client'
|
ZEROCONF_CLIENT_STRING = b'calibre wireless device client'
|
||||||
|
|
||||||
# A few "random" port numbers to use for detecting clients using broadcast
|
# A few "random" port numbers to use for detecting clients using broadcast
|
||||||
# The clients are expected to broadcast a UDP 'hi there' on all of these
|
# The clients are expected to broadcast a UDP 'hi there' on all of these
|
||||||
@ -130,8 +126,9 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
}
|
}
|
||||||
reverse_opcodes = dict([(v, k) for k,v in opcodes.iteritems()])
|
reverse_opcodes = dict([(v, k) for k,v in opcodes.iteritems()])
|
||||||
|
|
||||||
ALL_BY_TITLE = _('All by title')
|
ALL_BY_TITLE = _('All by title')
|
||||||
ALL_BY_AUTHOR = _('All by author')
|
ALL_BY_AUTHOR = _('All by author')
|
||||||
|
ALL_BY_SOMETHING = _('All by something')
|
||||||
|
|
||||||
EXTRA_CUSTOMIZATION_MESSAGE = [
|
EXTRA_CUSTOMIZATION_MESSAGE = [
|
||||||
_('Enable connections at startup') + ':::<p>' +
|
_('Enable connections at startup') + ':::<p>' +
|
||||||
@ -149,18 +146,25 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
_('Check this box if requested when reporting problems') + '</p>',
|
_('Check this box if requested when reporting problems') + '</p>',
|
||||||
'',
|
'',
|
||||||
_('Comma separated list of metadata fields '
|
_('Comma separated list of metadata fields '
|
||||||
'to turn into collections on the device. Possibilities include: ')+\
|
'to turn into collections on the device.') + ':::<p>' +
|
||||||
'series, tags, authors' +\
|
_('Possibilities include: series, tags, authors, etc' +
|
||||||
_('. Two special collections are available: %(abt)s:%(abtv)s and %(aba)s:%(abav)s. Add '
|
'. Three special collections are available: %(abt)s:%(abtv)s, '
|
||||||
'these values to the list to enable them. The collections will be '
|
'%(aba)s:%(abav)s, and %(abs)s:%(absv)s. Add '
|
||||||
'given the name provided after the ":" character.')%dict(
|
'these values to the list to enable them. The collections will be '
|
||||||
abt='abt', abtv=ALL_BY_TITLE, aba='aba', abav=ALL_BY_AUTHOR),
|
'given the name provided after the ":" character.')%dict(
|
||||||
|
abt='abt', abtv=ALL_BY_TITLE, aba='aba', abav=ALL_BY_AUTHOR,
|
||||||
|
abs='abs', absv=ALL_BY_SOMETHING),
|
||||||
'',
|
'',
|
||||||
_('Enable the no-activity timeout') + ':::<p>' +
|
_('Enable the no-activity timeout') + ':::<p>' +
|
||||||
_('If this box is checked, calibre will automatically disconnect if '
|
_('If this box is checked, calibre will automatically disconnect if '
|
||||||
'a connected device does nothing for %d minutes. Unchecking this '
|
'a connected device does nothing for %d minutes. Unchecking this '
|
||||||
' box disables this timeout, so calibre will never automatically '
|
' box disables this timeout, so calibre will never automatically '
|
||||||
'disconnect.')%(DISCONNECT_AFTER_N_SECONDS/60,) + '</p>',
|
'disconnect.')%(DISCONNECT_AFTER_N_SECONDS/60,) + '</p>',
|
||||||
|
_('Use this IP address') + ':::<p>' +
|
||||||
|
_('Use this option if you want to force the driver to listen on a '
|
||||||
|
'particular IP address. The driver will listen only on the '
|
||||||
|
'entered address, and this address will be the one advertized '
|
||||||
|
'over mDNS (bonjour).') + '</p>',
|
||||||
]
|
]
|
||||||
EXTRA_CUSTOMIZATION_DEFAULT = [
|
EXTRA_CUSTOMIZATION_DEFAULT = [
|
||||||
False,
|
False,
|
||||||
@ -173,6 +177,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
True,
|
True,
|
||||||
|
''
|
||||||
]
|
]
|
||||||
OPT_AUTOSTART = 0
|
OPT_AUTOSTART = 0
|
||||||
OPT_PASSWORD = 2
|
OPT_PASSWORD = 2
|
||||||
@ -181,6 +186,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
OPT_EXTRA_DEBUG = 6
|
OPT_EXTRA_DEBUG = 6
|
||||||
OPT_COLLECTIONS = 8
|
OPT_COLLECTIONS = 8
|
||||||
OPT_AUTODISCONNECT = 10
|
OPT_AUTODISCONNECT = 10
|
||||||
|
OPT_FORCE_IP_ADDRESS = 11
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
@ -499,6 +505,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
return self.OPT_USE_PORT
|
return self.OPT_USE_PORT
|
||||||
elif opt_string == 'port_number':
|
elif opt_string == 'port_number':
|
||||||
return self.OPT_PORT_NUMBER
|
return self.OPT_PORT_NUMBER
|
||||||
|
elif opt_string == 'force_ip_address':
|
||||||
|
return self.OPT_FORCE_IP_ADDRESS
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -527,8 +535,12 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
def _attach_to_port(self, sock, port):
|
def _attach_to_port(self, sock, port):
|
||||||
try:
|
try:
|
||||||
self._debug('try port', port)
|
ip_addr = self.settings().extra_customization[self.OPT_FORCE_IP_ADDRESS]
|
||||||
sock.bind(('', port))
|
self._debug('try ip address "'+ ip_addr + '"', 'on port', port)
|
||||||
|
if ip_addr:
|
||||||
|
sock.bind((ip_addr, port))
|
||||||
|
else:
|
||||||
|
sock.bind(('', port))
|
||||||
except socket.error:
|
except socket.error:
|
||||||
self._debug('socket error on port', port)
|
self._debug('socket error on port', port)
|
||||||
port = 0
|
port = 0
|
||||||
@ -996,6 +1008,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
self.client_can_stream_books = False
|
self.client_can_stream_books = False
|
||||||
self.client_can_stream_metadata = False
|
self.client_can_stream_metadata = False
|
||||||
|
|
||||||
|
self._debug("All IP addresses", get_all_ips())
|
||||||
|
|
||||||
message = None
|
message = None
|
||||||
try:
|
try:
|
||||||
self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
@ -1044,7 +1058,10 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
return message
|
return message
|
||||||
|
|
||||||
try:
|
try:
|
||||||
do_zeroconf(publish_zeroconf, port)
|
ip_addr = self.settings().extra_customization[self.OPT_FORCE_IP_ADDRESS]
|
||||||
|
publish_zeroconf('calibre smart device client',
|
||||||
|
'_calibresmartdeviceapp._tcp', port, {},
|
||||||
|
use_ip_address=ip_addr)
|
||||||
except:
|
except:
|
||||||
message = 'registration with bonjour failed'
|
message = 'registration with bonjour failed'
|
||||||
self._debug(message)
|
self._debug(message)
|
||||||
@ -1080,7 +1097,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
@synchronous('sync_lock')
|
@synchronous('sync_lock')
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
if getattr(self, 'listen_socket', None) is not None:
|
if getattr(self, 'listen_socket', None) is not None:
|
||||||
do_zeroconf(unpublish_zeroconf, self.port)
|
unpublish_zeroconf('calibre smart device client',
|
||||||
|
'_calibresmartdeviceapp._tcp', self.port, {})
|
||||||
self._close_listen_socket()
|
self._close_listen_socket()
|
||||||
|
|
||||||
# Methods for dynamic control
|
# Methods for dynamic control
|
||||||
|
@ -240,13 +240,18 @@ class ConnectShareAction(InterfaceAction):
|
|||||||
from calibre.gui2.dialogs.smartdevice import get_all_ip_addresses
|
from calibre.gui2.dialogs.smartdevice import get_all_ip_addresses
|
||||||
dm = self.gui.device_manager
|
dm = self.gui.device_manager
|
||||||
|
|
||||||
all_ips = get_all_ip_addresses()
|
forced_ip = dm.get_option('smartdevice', 'force_ip_address')
|
||||||
if len(all_ips) > 3:
|
if forced_ip:
|
||||||
formatted_addresses = _('Many IP addresses. See Start/Stop dialog.')
|
formatted_addresses = forced_ip
|
||||||
show_port = False
|
|
||||||
else:
|
|
||||||
formatted_addresses = ' or '.join(get_all_ip_addresses())
|
|
||||||
show_port = True
|
show_port = True
|
||||||
|
else:
|
||||||
|
all_ips = get_all_ip_addresses()
|
||||||
|
if len(all_ips) > 3:
|
||||||
|
formatted_addresses = _('Many IP addresses. See Start/Stop dialog.')
|
||||||
|
show_port = False
|
||||||
|
else:
|
||||||
|
formatted_addresses = ' or '.join(get_all_ip_addresses())
|
||||||
|
show_port = True
|
||||||
|
|
||||||
running = dm.is_running('smartdevice')
|
running = dm.is_running('smartdevice')
|
||||||
if not running:
|
if not running:
|
||||||
|
@ -115,7 +115,11 @@ class SmartdeviceDialog(QDialog, Ui_Dialog):
|
|||||||
self.auto_mgmt_button.setText(_('Automatic metadata management is enabled'))
|
self.auto_mgmt_button.setText(_('Automatic metadata management is enabled'))
|
||||||
self.auto_mgmt_button.setEnabled(False)
|
self.auto_mgmt_button.setEnabled(False)
|
||||||
|
|
||||||
self.ip_addresses.setText(', '.join(get_all_ip_addresses()))
|
forced_ip = self.device_manager.get_option('smartdevice', 'force_ip_address')
|
||||||
|
if forced_ip:
|
||||||
|
self.ip_addresses.setText(forced_ip)
|
||||||
|
else:
|
||||||
|
self.ip_addresses.setText(', '.join(get_all_ip_addresses()))
|
||||||
|
|
||||||
self.resize(self.sizeHint())
|
self.resize(self.sizeHint())
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ def start_server():
|
|||||||
|
|
||||||
return _server
|
return _server
|
||||||
|
|
||||||
def create_service(desc, type, port, properties, add_hostname):
|
def create_service(desc, type, port, properties, add_hostname, use_ip_address=None):
|
||||||
port = int(port)
|
port = int(port)
|
||||||
try:
|
try:
|
||||||
hostname = socket.gethostname().partition('.')[0]
|
hostname = socket.gethostname().partition('.')[0]
|
||||||
@ -69,7 +69,10 @@ def create_service(desc, type, port, properties, add_hostname):
|
|||||||
|
|
||||||
if add_hostname:
|
if add_hostname:
|
||||||
desc += ' (on %s)'%hostname
|
desc += ' (on %s)'%hostname
|
||||||
local_ip = get_external_ip()
|
if use_ip_address:
|
||||||
|
local_ip = use_ip_address
|
||||||
|
else:
|
||||||
|
local_ip = get_external_ip()
|
||||||
type = type+'.local.'
|
type = type+'.local.'
|
||||||
from calibre.utils.Zeroconf import ServiceInfo
|
from calibre.utils.Zeroconf import ServiceInfo
|
||||||
return ServiceInfo(type, desc+'.'+type,
|
return ServiceInfo(type, desc+'.'+type,
|
||||||
@ -79,7 +82,7 @@ def create_service(desc, type, port, properties, add_hostname):
|
|||||||
server=hostname+'.local.')
|
server=hostname+'.local.')
|
||||||
|
|
||||||
|
|
||||||
def publish(desc, type, port, properties=None, add_hostname=True):
|
def publish(desc, type, port, properties=None, add_hostname=True, use_ip_address=None):
|
||||||
'''
|
'''
|
||||||
Publish a service.
|
Publish a service.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user