diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py
index 208db0ce75a1..95fea47d206a 100644
--- a/src/calibre/devices/smart_device_app/driver.py
+++ b/src/calibre/devices/smart_device_app/driver.py
@@ -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
@@ -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
 
@@ -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])
@@ -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)
@@ -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:
diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py
index 1200df2bcfe2..8fac7ed6a393 100644
--- a/src/calibre/gui2/device.py
+++ b/src/calibre/gui2/device.py
@@ -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)
@@ -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()
 
 
@@ -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,
@@ -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):
@@ -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
     # }}}
 
diff --git a/src/calibre/gui2/dialogs/smartdevice.py b/src/calibre/gui2/dialogs/smartdevice.py
index bebec4abee96..9bd570a12e1d 100644
--- a/src/calibre/gui2/dialogs/smartdevice.py
+++ b/src/calibre/gui2/dialogs/smartdevice.py
@@ -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)
diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py
index a019ae11b66e..8888ba7a075b 100644
--- a/src/calibre/gui2/ui.py
+++ b/src/calibre/gui2/ui.py
@@ -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()