mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Changes required for smart device driver
This commit is contained in:
commit
07ce6e0a4f
@ -15,6 +15,8 @@ class DevicePlugin(Plugin):
|
||||
|
||||
#: Ordered list of supported formats
|
||||
FORMATS = ["lrf", "rtf", "pdf", "txt"]
|
||||
# If True, the config dialog will not show the formats box
|
||||
HIDE_FORMATS_CONFIG_BOX = False
|
||||
|
||||
#: VENDOR_ID can be either an integer, a list of integers or a dictionary
|
||||
#: If it is a dictionary, it must be a dictionary of dictionaries,
|
||||
@ -496,6 +498,22 @@ class DevicePlugin(Plugin):
|
||||
'''
|
||||
return paths
|
||||
|
||||
def startup(self):
|
||||
'''
|
||||
Called when calibre is is starting the device. Do any initialization
|
||||
required. Note that multiple instances of the class can be instantiated,
|
||||
and thus __init__ can be called multiple times, but only one instance
|
||||
will have this method called.
|
||||
'''
|
||||
pass
|
||||
|
||||
def shutdown(self):
|
||||
'''
|
||||
Called when calibre is shutting down, either for good or in preparation
|
||||
to restart. Do any cleanup required.
|
||||
'''
|
||||
pass
|
||||
|
||||
class BookList(list):
|
||||
'''
|
||||
A list of books. Each Book object must have the fields
|
||||
|
@ -117,8 +117,8 @@ class JsonCodec(object):
|
||||
def __init__(self):
|
||||
self.field_metadata = FieldMetadata()
|
||||
|
||||
def encode_to_file(self, file, booklist):
|
||||
file.write(json.dumps(self.encode_booklist_metadata(booklist),
|
||||
def encode_to_file(self, file_, booklist):
|
||||
file_.write(json.dumps(self.encode_booklist_metadata(booklist),
|
||||
indent=2, encoding='utf-8'))
|
||||
|
||||
def encode_booklist_metadata(self, booklist):
|
||||
@ -156,21 +156,28 @@ class JsonCodec(object):
|
||||
else:
|
||||
return object_to_unicode(value)
|
||||
|
||||
def decode_from_file(self, file, booklist, book_class, prefix):
|
||||
def decode_from_file(self, file_, booklist, book_class, prefix):
|
||||
js = []
|
||||
try:
|
||||
js = json.load(file, encoding='utf-8')
|
||||
js = json.load(file_, encoding='utf-8')
|
||||
for item in js:
|
||||
book = book_class(prefix, item.get('lpath', None))
|
||||
for key in item.keys():
|
||||
meta = self.decode_metadata(key, item[key])
|
||||
booklist.append(self.raw_to_book(item, book_class, prefix))
|
||||
except:
|
||||
print 'exception during JSON decode_from_file'
|
||||
traceback.print_exc()
|
||||
|
||||
def raw_to_book(self, json_book, book_class, prefix):
|
||||
try:
|
||||
book = book_class(prefix, json_book.get('lpath', None))
|
||||
for key,val in json_book.iteritems():
|
||||
meta = self.decode_metadata(key, val)
|
||||
if key == 'user_metadata':
|
||||
book.set_all_user_metadata(meta)
|
||||
else:
|
||||
if key == 'classifiers':
|
||||
key = 'identifiers'
|
||||
setattr(book, key, meta)
|
||||
booklist.append(book)
|
||||
return book
|
||||
except:
|
||||
print 'exception during JSON decoding'
|
||||
traceback.print_exc()
|
||||
|
@ -144,6 +144,7 @@ class DeviceManager(Thread): # {{{
|
||||
self.open_feedback_msg = open_feedback_msg
|
||||
self._device_information = None
|
||||
self.current_library_uuid = None
|
||||
self.call_shutdown_on_disconnect = False
|
||||
|
||||
def report_progress(self, *args):
|
||||
pass
|
||||
@ -197,6 +198,13 @@ class DeviceManager(Thread): # {{{
|
||||
self.ejected_devices.remove(self.connected_device)
|
||||
else:
|
||||
self.connected_slot(False, self.connected_device_kind)
|
||||
if self.call_shutdown_on_disconnect:
|
||||
# The current device is an instance of a plugin class instantiated
|
||||
# to handle this connection, probably as a mounted device. We are
|
||||
# now abandoning the instance that we created, so we tell it that it
|
||||
# is being shut down.
|
||||
self.connected_device.shutdown()
|
||||
self.call_shutdown_on_disconnect = False
|
||||
self.connected_device = None
|
||||
self._device_information = None
|
||||
|
||||
@ -265,7 +273,20 @@ class DeviceManager(Thread): # {{{
|
||||
except Queue.Empty:
|
||||
pass
|
||||
|
||||
def run_startup(self, dev):
|
||||
name = 'unknown'
|
||||
try:
|
||||
name = dev.__class__.__name__
|
||||
dev.startup()
|
||||
except:
|
||||
prints('Startup method for device %s threw exception'%name)
|
||||
traceback.print_exc()
|
||||
|
||||
def run(self):
|
||||
# Do any device-specific startup processing.
|
||||
for d in self.devices:
|
||||
self.run_startup(d)
|
||||
|
||||
while self.keep_going:
|
||||
kls = None
|
||||
while True:
|
||||
@ -277,6 +298,11 @@ class DeviceManager(Thread): # {{{
|
||||
if kls is not None:
|
||||
try:
|
||||
dev = kls(folder_path)
|
||||
# We just created a new device instance. Call its startup
|
||||
# method and set the flag to call the shutdown method when
|
||||
# it disconnects.
|
||||
self.run_startup(dev)
|
||||
self.call_shutdown_on_disconnect = True
|
||||
self.do_connect([[dev, None],], device_kind=device_kind)
|
||||
except:
|
||||
prints('Unable to open %s as device (%s)'%(device_kind, folder_path))
|
||||
@ -295,6 +321,13 @@ class DeviceManager(Thread): # {{{
|
||||
break
|
||||
time.sleep(self.sleep_time)
|
||||
|
||||
# We are exiting. Call the shutdown method for each plugin
|
||||
for p in self.devices:
|
||||
try:
|
||||
p.shutdown()
|
||||
except:
|
||||
pass
|
||||
|
||||
def create_job_step(self, func, done, description, to_job, args=[], kwargs={}):
|
||||
job = DeviceJob(func, done, self.job_manager,
|
||||
args=args, kwargs=kwargs, description=description)
|
||||
|
@ -43,6 +43,9 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
|
||||
self.connect(self.column_up, SIGNAL('clicked()'), self.up_column)
|
||||
self.connect(self.column_down, SIGNAL('clicked()'), self.down_column)
|
||||
|
||||
if device.HIDE_FORMATS_CONFIG_BOX:
|
||||
self.groupBox.hide()
|
||||
|
||||
if supports_subdirs:
|
||||
self.opt_use_subdirs.setChecked(self.settings.use_subdirs)
|
||||
else:
|
||||
|
@ -103,6 +103,19 @@
|
||||
<item row="6" column="0">
|
||||
<layout class="QGridLayout" name="extra_layout"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
|
@ -17,7 +17,7 @@ from calibre.utils.date import fromtimestamp
|
||||
from calibre.library.server import listen_on, log_access_file, log_error_file
|
||||
from calibre.library.server.utils import expose, AuthController
|
||||
from calibre.utils.mdns import publish as publish_zeroconf, \
|
||||
stop_server as stop_zeroconf, get_external_ip
|
||||
unpublish as unpublish_zeroconf, get_external_ip
|
||||
from calibre.library.server.content import ContentServer
|
||||
from calibre.library.server.mobile import MobileServer
|
||||
from calibre.library.server.xml import XMLServer
|
||||
@ -78,13 +78,18 @@ class BonJour(SimplePlugin): # {{{
|
||||
SimplePlugin.__init__(self, engine)
|
||||
self.port = port
|
||||
self.prefix = prefix
|
||||
self.mdns_services = [
|
||||
('Books in calibre', '_stanza._tcp', self.port,
|
||||
{'path':self.prefix+'/stanza'}),
|
||||
('Books in calibre', '_calibre._tcp', self.port,
|
||||
{'path':self.prefix+'/opds'}),
|
||||
]
|
||||
|
||||
|
||||
def start(self):
|
||||
try:
|
||||
publish_zeroconf('Books in calibre', '_stanza._tcp',
|
||||
self.port, {'path':self.prefix+'/stanza'})
|
||||
publish_zeroconf('Books in calibre', '_calibre._tcp',
|
||||
self.port, {'path':self.prefix+'/opds'})
|
||||
for s in self.mdns_services:
|
||||
publish_zeroconf(*s)
|
||||
except:
|
||||
import traceback
|
||||
cherrypy.log.error('Failed to start BonJour:')
|
||||
@ -94,7 +99,8 @@ class BonJour(SimplePlugin): # {{{
|
||||
|
||||
def stop(self):
|
||||
try:
|
||||
stop_zeroconf()
|
||||
for s in self.mdns_services:
|
||||
unpublish_zeroconf(*s)
|
||||
except:
|
||||
import traceback
|
||||
cherrypy.log.error('Failed to stop BonJour:')
|
||||
|
@ -871,6 +871,8 @@ class Engine(threading.Thread):
|
||||
from calibre.constants import DEBUG
|
||||
try:
|
||||
rr, wr, er = select.select(rs, [], [], self.timeout)
|
||||
if globals()['_GLOBAL_DONE']:
|
||||
continue
|
||||
for socket in rr:
|
||||
try:
|
||||
self.readers[socket].handle_read()
|
||||
@ -1419,6 +1421,9 @@ class Zeroconf(object):
|
||||
i += 1
|
||||
nextTime += _UNREGISTER_TIME
|
||||
|
||||
def countRegisteredServices(self):
|
||||
return len(self.services)
|
||||
|
||||
def checkService(self, info):
|
||||
"""Checks the network for a unique service name, modifying the
|
||||
ServiceInfo passed in if it is not unique."""
|
||||
|
@ -47,18 +47,8 @@ def start_server():
|
||||
|
||||
return _server
|
||||
|
||||
def publish(desc, type, port, properties=None, add_hostname=True):
|
||||
'''
|
||||
Publish a service.
|
||||
|
||||
:param desc: Description of service
|
||||
:param 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.
|
||||
'''
|
||||
def create_service(desc, type, port, properties, add_hostname):
|
||||
port = int(port)
|
||||
server = start_server()
|
||||
try:
|
||||
hostname = socket.gethostname().partition('.')[0]
|
||||
except:
|
||||
@ -69,13 +59,39 @@ def publish(desc, type, port, properties=None, add_hostname=True):
|
||||
local_ip = get_external_ip()
|
||||
type = type+'.local.'
|
||||
from calibre.utils.Zeroconf import ServiceInfo
|
||||
service = ServiceInfo(type, desc+'.'+type,
|
||||
return ServiceInfo(type, desc+'.'+type,
|
||||
address=socket.inet_aton(local_ip),
|
||||
port=port,
|
||||
properties=properties,
|
||||
server=hostname+'.local.')
|
||||
|
||||
|
||||
def publish(desc, type, port, properties=None, add_hostname=True):
|
||||
'''
|
||||
Publish a service.
|
||||
|
||||
:param desc: Description of service
|
||||
:param 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)
|
||||
server.registerService(service)
|
||||
|
||||
def unpublish(desc, 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:
|
||||
stop_server()
|
||||
|
||||
def stop_server():
|
||||
global _server
|
||||
if _server is not None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user