Skip to content

Commit

Permalink
Prevent setting an incorrect value for compression quality in the wir…
Browse files Browse the repository at this point in the history
…eless driver causing an error

Show a busy cursor while calibre is working on matching books on the
device to books int he library, which can take a while if the user has a
lot of books on the device.

Merge branch 'master' of https://github.com/cbhaley/calibre
  • Loading branch information
kovidgoyal committed Jan 16, 2014
2 parents ad0c769 + 7aa3305 commit 882a280
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 70 deletions.
12 changes: 8 additions & 4 deletions src/calibre/devices/smart_device_app/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
THUMBNAIL_HEIGHT = 160
DEFAULT_THUMBNAIL_HEIGHT = 160
THUMBNAIL_COMPRESSION_QUALITY = 75
DEFAULT_THUMBNAIL_COMPRESSION_QUALITY = 75

PREFIX = ''
BACKLOADING_ERROR_MESSAGE = None
Expand Down Expand Up @@ -680,6 +681,8 @@ def _get_smartdevice_option_number(self, opt_string):
return self.OPT_PORT_NUMBER
elif opt_string == 'force_ip_address':
return self.OPT_FORCE_IP_ADDRESS
elif opt_string == 'thumbnail_compression_quality':
return self.OPT_COMPRESSION_QUALITY
else:
return None

Expand Down Expand Up @@ -1463,6 +1466,7 @@ def startup_on_demand(self):
self.connection_attempts = {}
self.client_wants_uuid_file_names = False

message = None
compression_quality_ok = True
try:
cq = int(self.settings().extra_customization[self.OPT_COMPRESSION_QUALITY])
Expand All @@ -1474,11 +1478,12 @@ def startup_on_demand(self):
compression_quality_ok = False
if not compression_quality_ok:
self.THUMBNAIL_COMPRESSION_QUALITY = 70
message = 'Bad compression quality setting. It must be a number between 50 and 99'
message = _('Bad compression quality setting. It must be a number '
'between 50 and 99. Forced to be %d.')%self.DEFAULT_THUMBNAIL_COMPRESSION_QUALITY
self._debug(message)
return message
self.set_option('thumbnail_compression_quality',
str(self.DEFAULT_THUMBNAIL_COMPRESSION_QUALITY))

message = None
try:
self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
set_socket_inherit(self.listen_socket, False)
Expand Down Expand Up @@ -1541,7 +1546,6 @@ def startup_on_demand(self):

# Now try to open a UDP socket to receive broadcasts on

message = None
try:
self.broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
except:
Expand Down
174 changes: 110 additions & 64 deletions src/calibre/gui2/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

from PyQt4.Qt import (
QMenu, QAction, QActionGroup, QIcon, SIGNAL, Qt, pyqtSignal, QDialog,
QObject, QVBoxLayout, QDialogButtonBox, QCursor)
QObject, QVBoxLayout, QDialogButtonBox, QCursor, QCoreApplication,
QApplication, QEventLoop)

from calibre.customize.ui import (available_input_formats, available_output_formats,
device_plugins, disabled_device_plugins)
Expand Down Expand Up @@ -122,11 +123,9 @@ def device_name_for_plugboards(device_class):
class BusyCursor(object):

def __enter__(self):
from PyQt4.Qt import QApplication
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))

def __exit__(self, *args):
from PyQt4.Qt import QApplication
QApplication.restoreOverrideCursor()


Expand Down Expand Up @@ -1092,8 +1091,14 @@ def metadata_downloaded(self, job):
self.device_job_exception(job)
return
self.device_manager.slow_driveinfo()

# set_books_in_library might schedule a sync_booklists job
if DEBUG:
prints('DeviceJob: metadata_downloaded: Starting set_books_in_library')
self.set_books_in_library(job.result, reset=True, add_as_step_to_job=job)

if DEBUG:
prints('DeviceJob: metadata_downloaded: updating views')
mainlist, cardalist, cardblist = job.result
self.memory_view.set_database(mainlist)
self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA,
Expand All @@ -1107,9 +1112,17 @@ def metadata_downloaded(self, job):
self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA,
self.device_manager.device.BACKLOADING_ERROR_MESSAGE
is None)
if DEBUG:
prints('DeviceJob: metadata_downloaded: syncing')
self.sync_news()
self.sync_catalogs()

if DEBUG:
prints('DeviceJob: metadata_downloaded: refreshing ondevice')
self.refresh_ondevice()

if DEBUG:
prints('DeviceJob: metadata_downloaded: sending metadata_available signal')
device_signals.device_metadata_available.emit()

def refresh_ondevice(self, reset_only=False):
Expand Down Expand Up @@ -1766,71 +1779,104 @@ def updateq(id_, book):
except:
return True

total_book_count = 0
for booklist in booklists:
for book in booklist:
book.in_library = None
if getattr(book, 'uuid', None) in self.db_book_uuid_cache:
id_ = db_book_uuid_cache[book.uuid]
if updateq(id_, book):
update_book(id_, book)
book.in_library = 'UUID'
# ensure that the correct application_id is set
book.application_id = id_
continue
# No UUID exact match. Try metadata matching.
book_title = clean_string(book.title)
d = self.db_book_title_cache.get(book_title, None)
if d is not None:
# At this point we know that the title matches. The book
# will match if any of the db_id, author, or author_sort
# also match.
if getattr(book, 'application_id', None) in d['db_ids']:
id_ = getattr(book, 'application_id', None)
update_book(id_, book)
book.in_library = 'APP_ID'
# app_id already matches a db_id. No need to set it.
continue
# Sonys know their db_id independent of the application_id
# in the metadata cache. Check that as well.
if getattr(book, 'db_id', None) in d['db_ids']:
update_book(book.db_id, book)
book.in_library = 'DB_ID'
book.application_id = book.db_id
continue
# We now know that the application_id is not right. Set it
# to None to prevent book_on_device from accidentally
# matching on it. It will be set to a correct value below if
# the book is matched with one in the library
book.application_id = None
if book.authors:
# Compare against both author and author sort, because
# either can appear as the author
book_authors = clean_string(authors_to_string(book.authors))
if book_authors in d['authors']:
id_ = d['authors'][book_authors]
if book:
total_book_count += 1
if DEBUG:
prints('DeviceJob: set_books_in_library: books to process=', total_book_count)

start_time = time.time()

with BusyCursor():
current_book_count = 0
for booklist in booklists:
for book in booklist:
if current_book_count % 100 == 0:
self.status_bar.show_message(
_('Analyzing books on the device: %d%% finished')%(
int((float(current_book_count)/total_book_count)*100.0)))

# I am assuming that this sort-of multi-threading won't break
# anything. Reasons: excluding UI events prevents the user
# from explicitly changing anything, and (in theory) no
# changes are happening because of timers and the like.
# Why every tenth book? WAG balancing performance in the
# loop with preventing App Not Responding errors
if current_book_count % 10 == 0:
QCoreApplication.processEvents(flags=
QEventLoop.ExcludeUserInputEvents|
QEventLoop.ExcludeSocketNotifiers)
current_book_count += 1
book.in_library = None
if getattr(book, 'uuid', None) in self.db_book_uuid_cache:
id_ = db_book_uuid_cache[book.uuid]
if updateq(id_, book):
update_book(id_, book)
book.in_library = 'AUTHOR'
book.application_id = id_
elif book_authors in d['author_sort']:
id_ = d['author_sort'][book_authors]
book.in_library = 'UUID'
# ensure that the correct application_id is set
book.application_id = id_
continue
# No UUID exact match. Try metadata matching.
book_title = clean_string(book.title)
d = self.db_book_title_cache.get(book_title, None)
if d is not None:
# At this point we know that the title matches. The book
# will match if any of the db_id, author, or author_sort
# also match.
if getattr(book, 'application_id', None) in d['db_ids']:
id_ = getattr(book, 'application_id', None)
update_book(id_, book)
book.in_library = 'AUTH_SORT'
book.application_id = id_
else:
# Book definitely not matched. Clear its application ID
book.application_id = None
# Set author_sort if it isn't already
asort = getattr(book, 'author_sort', None)
if not asort and book.authors:
book.author_sort = self.library_view.model().db.\
author_sort_from_authors(book.authors)

if update_metadata:
if self.device_manager.is_device_connected:
plugboards = self.library_view.model().db.prefs.get('plugboards', {})
self.device_manager.sync_booklists(
FunctionDispatcher(self.metadata_synced), booklists,
plugboards, add_as_step_to_job)
book.in_library = 'APP_ID'
# app_id already matches a db_id. No need to set it.
continue
# Sonys know their db_id independent of the application_id
# in the metadata cache. Check that as well.
if getattr(book, 'db_id', None) in d['db_ids']:
update_book(book.db_id, book)
book.in_library = 'DB_ID'
book.application_id = book.db_id
continue
# We now know that the application_id is not right. Set it
# to None to prevent book_on_device from accidentally
# matching on it. It will be set to a correct value below if
# the book is matched with one in the library
book.application_id = None
if book.authors:
# Compare against both author and author sort, because
# either can appear as the author
book_authors = clean_string(authors_to_string(book.authors))
if book_authors in d['authors']:
id_ = d['authors'][book_authors]
update_book(id_, book)
book.in_library = 'AUTHOR'
book.application_id = id_
elif book_authors in d['author_sort']:
id_ = d['author_sort'][book_authors]
update_book(id_, book)
book.in_library = 'AUTH_SORT'
book.application_id = id_
else:
# Book definitely not matched. Clear its application ID
book.application_id = None
# Set author_sort if it isn't already
asort = getattr(book, 'author_sort', None)
if not asort and book.authors:
book.author_sort = self.library_view.model().db.\
author_sort_from_authors(book.authors)

if update_metadata:
if self.device_manager.is_device_connected:
plugboards = self.library_view.model().db.prefs.get('plugboards', {})
self.device_manager.sync_booklists(
FunctionDispatcher(self.metadata_synced), booklists,
plugboards, add_as_step_to_job)

if DEBUG:
prints('DeviceJob: set_books_in_library finished: time=',
time.time() - start_time)
# The status line is reset when the job finishes
return update_metadata
# }}}

2 changes: 1 addition & 1 deletion src/calibre/gui2/dialogs/smartdevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def accept(self):

if not self.device_manager.is_running('smartdevice'):
error_dialog(self, _('Problem starting the wireless device'),
_('The wireless device driver did not start. It said "%s"')%message,
_('The wireless device driver had problems starting. It said "%s"')%message,
show=True)
self.device_manager.set_option('smartdevice', 'use_fixed_port',
self.orig_fixed_port)
Expand Down
2 changes: 1 addition & 1 deletion src/calibre/gui2/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ def start_smartdevice(self):
if message:
if not self.device_manager.is_running('Wireless Devices'):
error_dialog(self, _('Problem starting the wireless device'),
_('The wireless device driver did not start. '
_('The wireless device driver had problems starting. '
'It said "%s"')%message, show=True)
self.iactions['Connect Share'].set_smartdevice_action_state()

Expand Down

0 comments on commit 882a280

Please sign in to comment.