'Set the ondevice indications in the device database.
This method should be called before book_on_device is called, because
it sets the application_id for matched books. Book_on_device uses that
to both speed up matching and to count matches.'

def set_books_in_library(self, booklists, reset=False, add_as_step_to_job=None, force_send=False, do_device_sync=True):

    if (not self.device_manager.is_device_connected):
        return False
    try:
        db = self.library_view.model().db
    except:
        return False
    string_pat = re.compile('(?u)\\W|[_]')
    def clean_string(x):
        x = (x.lower() if x else '')
        return string_pat.sub('', x)
    update_metadata = ((device_prefs['manage_device_metadata'] == 'on_connect') or force_send)
    get_covers = False
    desired_thumbnail_height = 0
    if (update_metadata and self.device_manager.is_device_connected):
        if self.device_manager.device.WANTS_UPDATED_THUMBNAILS:
            get_covers = True
            desired_thumbnail_height = self.device_manager.device.THUMBNAIL_HEIGHT
    if (reset or (not hasattr(self, 'db_book_title_cache'))):
        db_book_title_cache = {}
        db_book_uuid_cache = {}
        for id_ in db.data.iterallids():
            title = clean_string(db.title(id_, index_is_id=True))
            if (title not in db_book_title_cache):
                db_book_title_cache[title] = {'authors': {}, 'author_sort': {}, 'db_ids': {}}
            authors = clean_string(db.authors(id_, index_is_id=True))
            if authors:
                db_book_title_cache[title]['authors'][authors] = id_
            if db.author_sort(id_, index_is_id=True):
                aus = clean_string(db.author_sort(id_, index_is_id=True))
                db_book_title_cache[title]['author_sort'][aus] = id_
            db_book_title_cache[title]['db_ids'][id_] = id_
            db_book_uuid_cache[db.uuid(id_, index_is_id=True)] = id_
        self.db_book_title_cache = db_book_title_cache
        self.db_book_uuid_cache = db_book_uuid_cache
    book_ids_to_refresh = set()
    book_formats_to_send = []
    books_with_future_dates = []
    first_call_to_synchronize_with_db = [True]
    def update_book(id_, book):
        if (not update_metadata):
            return
        mi = db.get_metadata(id_, index_is_id=True, get_cover=get_covers)
        book.smart_update(mi, replace_metadata=True)
        if (get_covers and (desired_thumbnail_height != 0)):
            self.update_thumbnail(book)
    def updateq(id_, book):
        try:
            if (not update_metadata):
                return False
            if (do_device_sync and (self.device_manager.device is not None)):
                (set_of_ids, (fmt_name, date_bad)) = self.device_manager.device.synchronize_with_db(db, id_, book, first_call_to_synchronize_with_db[0])
                first_call_to_synchronize_with_db[0] = False
                if date_bad:
                    books_with_future_dates.append(book.title)
                elif (fmt_name is not None):
                    book_formats_to_send.append((id_, fmt_name))
                if (set_of_ids is not None):
                    book_ids_to_refresh.update(set_of_ids)
                    return True
            return ((db.metadata_last_modified(id_, index_is_id=True) != getattr(book, 'last_modified', None)) or (isinstance(getattr(book, 'thumbnail', None), (list, tuple)) and (max(book.thumbnail[0], book.thumbnail[1]) != desired_thumbnail_height)))
        except:
            return True
    total_book_count = 0
    for booklist in booklists:
        for book in booklist:
            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))), show_notification=False)
                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 = 'UUID'
                    book.application_id = id_
                    continue
                book_title = clean_string(book.title)
                d = self.db_book_title_cache.get(book_title, None)
                if (d is not None):
                    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'
                        continue
                    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
                    book.application_id = None
                    if book.authors:
                        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.application_id = None
                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 book_ids_to_refresh:
            try:
                prints('DeviceJob:    set_books_in_library    refreshing    GUI    for    ', len(book_ids_to_refresh), 'books')
                self.library_view.model().refresh_ids(book_ids_to_refresh, current_row=self.library_view.currentIndex().row())
            except:
                traceback.print_exc()
        try:
            (files, names, metadata) = ([], [], [])
            for (id_, fmt_name) in book_formats_to_send:
                if DEBUG:
                    prints('DeviceJob:    Syncing    book.    id:', id_, 'name    from    device', fmt_name)
                ext = os.path.splitext(fmt_name)[1][1:]
                fmt_info = db.new_api.format_metadata(id_, ext)
                if fmt_info:
                    try:
                        pt = PersistentTemporaryFile(suffix=('caltmpfmt.' + ext))
                        db.new_api.copy_format_to(id_, ext, pt)
                        pt.close()
                        files.append(filename_to_unicode(os.path.abspath(pt.name)))
                        names.append(fmt_name)
                        mi = db.new_api.get_metadata(id_, get_cover=True)
                        self.update_thumbnail(mi)
                        metadata.append(mi)
                    except:
                        prints('Problem    creating    temporary    file    for', fmt_name)
                        traceback.print_exc()
                elif DEBUG:
                    prints("DeviceJob:    book    doesn't    have    that    format")
            if files:
                self.upload_books(files, names, metadata)
        except:
            traceback.print_exc()
        try:
            if books_with_future_dates:
                d = error_dialog(self, _('Book    format    sync    problem'), _('Some    book    formats    in    your    library    cannot    be    synced    because    they    have    dates    in    the    future'), det_msg='\n'.join(books_with_future_dates), show=False, show_copy_button=True)
                d.show()
        except:
            traceback.print_exc()
    if DEBUG:
        prints('DeviceJob:    set_books_in_library    finished:    time=', (time.time() - start_time))
    return update_metadata
