diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 28a5f21a46..fd7ce8a6c3 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -514,8 +514,9 @@ class Metadata(object): fmt('Rights', unicode(self.rights)) for key in self.user_metadata_keys(): val = self.get(key, None) - (name, val) = self.format_field(key) - fmt(name, unicode(val)) + if val: + (name, val) = self.format_field(key) + fmt(name, unicode(val)) return u'\n'.join(ans) def to_html(self): @@ -538,7 +539,7 @@ class Metadata(object): ans += [(_('Rights'), unicode(self.rights))] for key in self.user_metadata_keys(): val = self.get(key, None) - if val is not None: + if val: (name, val) = self.format_field(key) ans += [(name, val)] for i, x in enumerate(ans): diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 88a8c68572..6b04f6fa1f 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -533,6 +533,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ # Save the current field_metadata for applications like calibre2opds # Goes here, because if cf is valid, db is valid. db.prefs['field_metadata'] = db.field_metadata.all_metadata() + db.commit_dirty_cache() if DEBUG and db.gm_count > 0: print 'get_metadata cache: {0:d} calls, {1:4.2f}% misses'.format( db.gm_count, (db.gm_missed*100.0)/db.gm_count) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 4775e13818..94550f2804 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -340,6 +340,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): setattr(self, 'title_sort', functools.partial(self.get_property, loc=self.FIELD_MAP['sort'])) + self.dirtied_cache = set() d = self.conn.get('SELECT book FROM metadata_dirtied', all=True) for x in d: self.dirtied_queue.put(x[0]) @@ -585,12 +586,19 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if remove_from_dirtied: self.conn.execute('DELETE FROM metadata_dirtied WHERE book=?', (book_id,)) + # if a later exception prevents the commit, then the dirtied + # table will still have the book. No big deal, because the OPF + # is there and correct. We will simply do it again on next + # start + self.dirtied_cache.discard(book_id) if commit: self.conn.commit() return True def dirtied(self, book_ids, commit=True): for book in book_ids: + if book in self.dirtied_cache: + continue try: self.conn.execute( 'INSERT INTO metadata_dirtied (book) VALUES (?)', @@ -598,10 +606,28 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.dirtied_queue.put(book) except IntegrityError: # Already in table - continue + pass + # If the commit doesn't happen, then our cache will be wrong. This + # could lead to a problem because we won't put the book back into + # the dirtied table. We deal with this by writing the dirty cache + # back to the table on GUI exit. Not perfect, but probably OK + self.dirtied_cache.add(book) if commit: self.conn.commit() + def commit_dirty_cache(self): + ''' + Set the dirty indication for every book in the cache. The vast majority + of the time, the indication will already be set. However, sometimes + exceptions may have prevented a commit, which may remove some dirty + indications from the DB. This call will put them back. Note that there + is no problem with setting a dirty indication for a book that isn't in + fact dirty. Just wastes a few cycles. + ''' + book_ids = list(self.dirtied_cache) + self.dirtied_cache = set() + self.dirtied(book_ids) + def get_metadata(self, idx, index_is_id=False, get_cover=False): ''' Convenience method to return metadata as a :class:`Metadata` object. diff --git a/src/calibre/manual/template_lang.rst b/src/calibre/manual/template_lang.rst index 0c3a87a157..1ab004f3f3 100644 --- a/src/calibre/manual/template_lang.rst +++ b/src/calibre/manual/template_lang.rst @@ -17,14 +17,14 @@ For the book "The Foundation" by "Isaac Asimov" it will become:: Asimov, Isaac/The Foundation/The Foundation - Isaac Asimov -You can use all the various metadata fields available in calibre in a template, including any custom columns you have created yourself. To find out the template name for a column simply hover your mouse over the column header. Names for custom fields (columns you have created yourself) always have a # as the first character. For series type custom fields, there is always an additional field named ``#seriesname_index`` that becomes the series index for that series. So if you have a custom series field named #myseries, there will also be a field named #myseries_index. +You can use all the various metadata fields available in calibre in a template, including any custom columns you have created yourself. To find out the template name for a column simply hover your mouse over the column header. Names for custom fields (columns you have created yourself) always have a # as the first character. For series type custom fields, there is always an additional field named ``#seriesname_index`` that becomes the series index for that series. So if you have a custom series field named ``#myseries``, there will also be a field named ``#myseries_index``. In addition to the column based fields, you also can use:: {formats} - A list of formats available in the calibre library for a book {isbn} - The ISBN number of the book -If a particular book does not have a particular piece of metadata, the field in the template is automatically removed for that book. So for example:: +If a particular book does not have a particular piece of metadata, the field in the template is automatically removed for that book. Consider, for example:: {author_sort}/{series}/{title} {series_index} @@ -44,19 +44,19 @@ Advanced formatting You can do more than just simple substitution with the templates. You can also conditionally include text and control how the substituted data is formatted. -First, conditionally including text. There are cases where you might want to have text appear in the output only if a field is not empty. A common case is series and series_index, where you want either nothing or the two values with a hyphen between them. Calibre handles this case using a special field syntax. +First, conditionally including text. There are cases where you might want to have text appear in the output only if a field is not empty. A common case is ``series`` and ``series_index``, where you want either nothing or the two values with a hyphen between them. Calibre handles this case using a special field syntax. For example, assume you want to use the template:: {series} - {series_index} - {title} -If the book has no series, the answer will be '- - title'. Many people would rather the result be simply 'title', without the hyphens. To do this, use the extended syntax ``{field:|prefix_text|suffix_text}``. When you use this syntax, if field has the value SERIES then the result will be prefix_textSERIESsuffix_text. If field has no value, then the result will be the empty string (nothing). The prefix and suffix can contain blanks. +If the book has no series, the answer will be ``- - title``. Many people would rather the result be simply ``title``, without the hyphens. To do this, use the extended syntax ``{field:|prefix_text|suffix_text}``. When you use this syntax, if field has the value SERIES then the result will be ``prefix_textSERIESsuffix_text``. If field has no value, then the result will be the empty string (nothing); the prefix and suffix are ignored. The prefix and suffix can contain blanks. Using this syntax, we can solve the above series problem with the template:: {series}{series_index:| - | - }{title} -The hyphens will be included only if the book has a series index. +The hyphens will be included only if the book has a series index, which it will have only if it has a series. Notes: you must include the : character if you want to use a prefix or a suffix. You must either use no \| characters or both of them; using one, as in ``{field:| - }``, is not allowed. It is OK not to provide any text for one side or the other, such as in ``{series:|| - }``. Using ``{title:||}`` is the same as using ``{title}``. @@ -85,7 +85,7 @@ Advanced features Using templates in custom columns ---------------------------------- -There are sometimes cases where you want to display metadata that |app| does not normally display, or to display data in a way different from how |app| normally does. For example, you might want to display the ISBN, a field that |app| does not display. You can use custom columns for this. To do so, you create a column with the type 'column built from other columns' (hereafter called composite columns), enter a template, and |app| will display in the column the result of evaluating that template. To display the isbn, create the column and enter ``{isbn}`` into the template box. To display a column containing the values of two series custom columns separated by a comma, use ``{#series1:||,}{#series2}``. +There are sometimes cases where you want to display metadata that |app| does not normally display, or to display data in a way different from how |app| normally does. For example, you might want to display the ISBN, a field that |app| does not display. You can use custom columns for this by creating a column with the type 'column built from other columns' (hereafter called composite columns), and entering a template. Result: |app| will display a column showing the result of evaluating that template. To display the ISBN, create the column and enter ``{isbn}`` into the template box. To display a column containing the values of two series custom columns separated by a comma, use ``{#series1:||,}{#series2}``. Composite columns can use any template option, including formatting. @@ -98,7 +98,7 @@ Suppose you want to display the value of a field in upper case, when that field Function references replace the formatting specification, going after the : and before the first ``|`` or the closing ``}``. Functions must always end with ``()``. Some functions take extra values (arguments), and these go inside the ``()``. -The syntax for using functions is ``{field:function(arguments)}``, or ``{field:function(arguments)|prefix|suffix}``. Argument values cannot contain a comma, because it is used to separate arguments. Functions return the value of the field used in the template, suitably modified. +The syntax for using functions is ``{field:function(arguments)}``, or ``{field:function(arguments)|prefix|suffix}``. Argument values cannot contain a comma, because it is used to separate arguments. The last (or only) argument cannot contain a closing parenthesis ( ')' ). Functions return the value of the field used in the template, suitably modified. The functions available are: