From eabbe6bc4625d0d337a27f52c91040a99a541a57 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 15 Feb 2011 08:55:11 +0000 Subject: [PATCH 01/12] Make selection and dragging behave more like other grid apps: drag only if already selected --- src/calibre/gui2/library/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 61161cd5e6..c62936a46f 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -551,8 +551,10 @@ class BooksView(QTableView): # {{{ return mods & Qt.ControlModifier or mods & Qt.ShiftModifier def mousePressEvent(self, event): - if event.button() == Qt.LeftButton and not self.event_has_mods(): - self.drag_start_pos = event.pos() + ep = event.pos() + if self.indexAt(ep) in self.selectionModel().selectedIndexes() and \ + event.button() == Qt.LeftButton and not self.event_has_mods(): + self.drag_start_pos = ep return QTableView.mousePressEvent(self, event) def mouseMoveEvent(self, event): From 1c72ea27b3f9de2bdcb6a781864a00bdb782c796 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 15 Feb 2011 14:27:47 +0000 Subject: [PATCH 02/12] Add user categories from grouped search terms. Move the group_search_term tweak to the search preferences dialog. --- resources/default_tweaks.py | 13 --- src/calibre/gui2/preferences/search.py | 149 ++++++++++++++++++++++++- src/calibre/gui2/preferences/search.ui | 117 +++++++++++++++++-- src/calibre/gui2/tag_view.py | 42 ++++--- src/calibre/library/caches.py | 11 ++ src/calibre/library/database2.py | 35 ++++-- src/calibre/library/field_metadata.py | 16 ++- 7 files changed, 332 insertions(+), 51 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index c22d24a6a7..47036a7b6d 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -245,19 +245,6 @@ sony_collection_name_template='{value}{category:| (|)}' sony_collection_sorting_rules = [] -#: Create search terms to apply a query across several built-in search terms. -# Syntax: {'new term':['existing term 1', 'term 2', ...], 'new':['old'...] ...} -# Example: create the term 'myseries' that when used as myseries:foo would -# search all of the search categories 'series', '#myseries', and '#myseries2': -# grouped_search_terms={'myseries':['series','#myseries', '#myseries2']} -# Example: two search terms 'a' and 'b' both that search 'tags' and '#mytags': -# grouped_search_terms={'a':['tags','#mytags'], 'b':['tags','#mytags']} -# Note: You cannot create a search term that is a duplicate of an existing term. -# Such duplicates will be silently ignored. Also note that search terms ignore -# case. 'MySearch' and 'mysearch' are the same term. -grouped_search_terms = {} - - #: Control how tags are applied when copying books to another library # Set this to True to ensure that tags in 'Tags to add when adding # a book' are added when copying books to another library diff --git a/src/calibre/gui2/preferences/search.py b/src/calibre/gui2/preferences/search.py index 749a7c8de0..4f8d78d62b 100644 --- a/src/calibre/gui2/preferences/search.py +++ b/src/calibre/gui2/preferences/search.py @@ -5,18 +5,20 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from PyQt4.Qt import QApplication +from PyQt4.Qt import QApplication, QIcon from calibre.gui2.preferences import ConfigWidgetBase, test_widget, \ CommaSeparatedList from calibre.gui2.preferences.search_ui import Ui_Form -from calibre.gui2 import config +from calibre.gui2 import config, error_dialog from calibre.utils.config import prefs class ConfigWidget(ConfigWidgetBase, Ui_Form): def genesis(self, gui): self.gui = gui + db = gui.library_view.model().db + self.db = db r = self.register @@ -24,11 +26,152 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('highlight_search_matches', config) r('limit_search_columns', prefs) r('limit_search_columns_to', prefs, setting=CommaSeparatedList) - fl = gui.library_view.model().db.field_metadata.get_search_terms() + fl = db.field_metadata.get_search_terms() self.opt_limit_search_columns_to.update_items_cache(fl) self.clear_history_button.clicked.connect(self.clear_histories) + self.gst_explanation.setText(_( + "Grouped search terms are search names that permit a query to automatically " + "search across more than one column. For example, if you create a grouped " + "search term 'myseries' with the value 'series, #myseries, #myseries2', " + "the query 'myseries:adhoc' will find the string 'adhoc' in any of the " + "columns 'series', '#myseries', and '#myseries2'. Enter the name of the " + "grouped search term in the drop-down box, enter the list of columns " + "to search in the value box, then push the Save button. " + "Notes: You cannot create a search term that is a duplicate of an existing " + "term or user category. Search terms are forced to lower case; 'MySearch' " + "and 'mysearch' are the same term.")) + + self.gst = db.prefs.get('grouped_search_terms', {}) + self.orig_gst_keys = self.gst.keys() + + del_icon = QIcon(I('trash.png')) + self.gst_delete_button.setIcon(del_icon) + fl = [] + for f in db.all_field_keys(): + fm = db.metadata_for_field(f) + if not fm['search_terms']: + continue + if not fm['is_category']: + continue + fl.append(f) + self.gst_value.update_items_cache(fl) + self.fill_gst_box(select=None) + + self.gst_delete_button.setEnabled(False) + self.gst_save_button.setEnabled(False) + self.gst_names.currentIndexChanged[int].connect(self.gst_index_changed) + self.gst_names.editTextChanged.connect(self.gst_text_changed) + self.gst_value.textChanged.connect(self.gst_text_changed) + self.gst_save_button.clicked.connect(self.gst_save_clicked) + self.gst_delete_button.clicked.connect(self.gst_delete_clicked) + self.gst_changed = False + + self.muc_explanation.setText(_( + "Add a grouped search term name to this box to automatically generate " + "a user category with the name of the search term. The user category will be " + "populated with all the items in the categories included in the grouped " + "search term. This permits you to see easily all the category items that " + "are in the fields contained in the grouped search term. Using the above " + "'myseries' example, the automatically-generated user category would contain " + "all the series mentioned in 'series', '#myseries1', and '#myseries2'. This " + "can be useful to check for duplications or to find which column contains " + "a particular item.")) + + if not db.prefs.get('grouped_search_make_user_categories', None): + db.prefs.set('grouped_search_make_user_categories', []) + r('grouped_search_make_user_categories', db.prefs, setting=CommaSeparatedList) + self.muc_changed = False + self.opt_grouped_search_make_user_categories.editingFinished.connect( + self.muc_box_changed) + + def muc_box_changed(self): + self.muc_changed = True + + def gst_save_clicked(self): + idx = self.gst_names.currentIndex() + name = icu_lower(unicode(self.gst_names.currentText())) + if not name: + return error_dialog(self.gui, _('Grouped Search Terms'), + _('The search term cannot be blank'), + show=True) + if idx != 0: + orig_name = unicode(self.gst_names.itemData(idx).toString()) + else: + orig_name = '' + if name != orig_name: + if name in self.db.field_metadata.get_search_terms() and \ + name not in self.orig_gst_keys: + return error_dialog(self.gui, _('Grouped Search Terms'), + _('That name is already used for a column or grouped search term'), + show=True) + if name in [icu_lower(p) for p in self.db.prefs.get('user_categories', {})]: + return error_dialog(self.gui, _('Grouped Search Terms'), + _('That name is already used for user category'), + show=True) + + val = [v.strip() for v in unicode(self.gst_value.text()).split(',') if v.strip()] + if not val: + return error_dialog(self.gui, _('Grouped Search Terms'), + _('The value box cannot be empty'), show=True) + + if orig_name and name != orig_name: + del self.gst[orig_name] + self.gst_changed = True + self.gst[name] = val + self.fill_gst_box(select=name) + self.changed_signal.emit() + + def gst_delete_clicked(self): + if self.gst_names.currentIndex() == 0: + return error_dialog(self.gui, _('Grouped Search Terms'), + _('The empty grouped search term cannot be deleted'), show=True) + name = unicode(self.gst_names.currentText()) + if name in self.gst: + del self.gst[name] + self.fill_gst_box(select='') + self.changed_signal.emit() + self.gst_changed = True + + def fill_gst_box(self, select=None): + terms = sorted(self.gst.keys()) + self.opt_grouped_search_make_user_categories.update_items_cache(terms) + self.gst_names.blockSignals(True) + self.gst_names.clear() + self.gst_names.addItem('', '') + for t in terms: + self.gst_names.addItem(t, t) + self.gst_names.blockSignals(False) + if select is not None: + if select == '': + self.gst_index_changed(0) + elif select in terms: + self.gst_names.setCurrentIndex(self.gst_names.findText(select)) + + def gst_text_changed(self): + self.gst_delete_button.setEnabled(False) + self.gst_save_button.setEnabled(True) + + def gst_index_changed(self, idx): + self.gst_delete_button.setEnabled(idx != 0) + self.gst_save_button.setEnabled(False) + self.gst_value.blockSignals(True) + if idx == 0: + self.gst_value.setText('') + else: + name = unicode(self.gst_names.itemData(idx).toString()) + self.gst_value.setText(','.join(self.gst[name])) + self.gst_value.blockSignals(False) + + def commit(self): + if self.gst_changed: + self.db.prefs.set('grouped_search_terms', self.gst) + self.db.field_metadata.add_grouped_search_terms(self.gst) + return ConfigWidgetBase.commit(self) + def refresh_gui(self, gui): + if self.muc_changed: + gui.tags_view.set_new_model() gui.search.search_as_you_type(config['search_as_you_type']) gui.library_view.model().set_highlight_only(config['highlight_search_matches']) gui.search.do_search() diff --git a/src/calibre/gui2/preferences/search.ui b/src/calibre/gui2/preferences/search.ui index 7d40f723ea..8a16a517c5 100644 --- a/src/calibre/gui2/preferences/search.ui +++ b/src/calibre/gui2/preferences/search.ui @@ -7,7 +7,7 @@ 0 0 670 - 392 + 391 @@ -77,7 +77,112 @@ + + + + Clear search histories from all over calibre. Including the book list, e-book viewer, fetch news dialog, etc. + + + Clear search &histories + + + + + + Grouped Search Terms + + + + + + TextLabel + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + Grouped search terms: + + + + + + + true + + + 10 + + + + + + + Delete the current search term + + + ... + + + + + + + + + + Save the current search term. You can rename a search term by +changing the name then pressing Save. You can change the value +of a search term by changing the value box then pressing Save. + + + Save + + + + + + + + + TextLabel + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + Make user categories: + + + + + + + + + + + + Qt::Vertical @@ -90,16 +195,6 @@ - - - - Clear search histories from all over calibre. Including the book list, e-book viewer, fetch news dialog, etc. - - - Clear search &histories - - - diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 3bc5d724ba..6b5de37bbe 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -466,10 +466,7 @@ class TagTreeItem(object): # {{{ icon_map[0] = data.icon self.tag, self.icon_state_map = data, list(map(QVariant, icon_map)) if tooltip: - if tooltip.endswith(':'): - self.tooltip = tooltip + ' ' - else: - self.tooltip = tooltip + ': ' + self.tooltip = tooltip + ' ' else: self.tooltip = '' @@ -589,11 +586,17 @@ class TagsModel(QAbstractItemModel): # {{{ # get_node_tree cannot return None here, because row_map is empty data = self.get_node_tree(config['sort_tags_by']) + gst = db.prefs.get('grouped_search_terms', {}) self.root_item = TagTreeItem() for i, r in enumerate(self.row_map): if self.hidden_categories and self.categories[i] in self.hidden_categories: continue - tt = _(u'The lookup/search name is "{0}"').format(r) + if r.startswith('@') and r[1:] in gst: + tt = _(u'The grouped search term name is "{0}"').format(r[1:]) + elif r == 'news': + tt = '' + else: + tt = _(u'The lookup/search name is "{0}"').format(r) TagTreeItem(parent=self.root_item, data=self.categories[i], category_icon=self.category_icon_map[r], @@ -735,6 +738,14 @@ class TagsModel(QAbstractItemModel): # {{{ self.row_map = [] self.categories = [] + # Get the categories + if self.search_restriction: + data = self.db.get_categories(sort=sort, + icon_map=self.category_icon_map, + ids=self.db.search('', return_matches=True)) + else: + data = self.db.get_categories(sort=sort, icon_map=self.category_icon_map) + # Reconstruct the user categories, putting them into metadata self.db.field_metadata.remove_dynamic_categories() tb_cats = self.db.field_metadata @@ -746,17 +757,16 @@ class TagsModel(QAbstractItemModel): # {{{ except ValueError: import traceback traceback.print_exc() + + for cat in sorted(self.db.prefs.get('grouped_search_terms', {}), + key=sort_key): + if (u'@' + cat) in data: + tb_cats.add_user_category(label=u'@' + cat, name=cat) + self.db.data.change_search_locations(self.db.field_metadata.get_search_terms()) + if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) - # Now get the categories - if self.search_restriction: - data = self.db.get_categories(sort=sort, - icon_map=self.category_icon_map, - ids=self.db.search('', return_matches=True)) - else: - data = self.db.get_categories(sort=sort, icon_map=self.category_icon_map) - if self.filter_categories_by: for category in data.keys(): data[category] = [t for t in data[category] @@ -767,6 +777,7 @@ class TagsModel(QAbstractItemModel): # {{{ if category in data: # The search category can come and go self.row_map.append(category) self.categories.append(tb_categories[category]['name']) + if len(old_row_map) != 0 and len(old_row_map) != len(self.row_map): # A category has been added or removed. We must force a rebuild of # the model @@ -822,6 +833,7 @@ class TagsModel(QAbstractItemModel): # {{{ not self.db.field_metadata[r]['is_custom'] and \ not self.db.field_metadata[r]['kind'] == 'user' \ else False + tt = r if self.db.field_metadata[r]['kind'] == 'user' else None for idx,tag in enumerate(data[r]): if clear_rating: tag.avg_rating = None @@ -861,10 +873,10 @@ class TagsModel(QAbstractItemModel): # {{{ category_icon = category_node.icon, tooltip = None, category_key=category_node.category_key) - t = TagTreeItem(parent=sub_cat, data=tag, tooltip=r, + t = TagTreeItem(parent=sub_cat, data=tag, tooltip=tt, icon_map=self.icon_state_map) else: - t = TagTreeItem(parent=category, data=tag, tooltip=r, + t = TagTreeItem(parent=category, data=tag, tooltip=tt, icon_map=self.icon_state_map) self.endInsertRows() return True diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 70e1fec131..847a0493eb 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -433,6 +433,10 @@ class ResultCache(SearchQueryParser): # {{{ if len(candidates) == 0: return matches + if len(location) > 2 and location.startswith('@') and \ + location[1:] in self.db_prefs['grouped_search_terms']: + location = location[1:] + if query and query.strip(): # get metadata key associated with the search term. Eliminates # dealing with plurals and other aliases @@ -440,9 +444,16 @@ class ResultCache(SearchQueryParser): # {{{ # grouped search terms if isinstance(location, list): if allow_recursion: + if query.lower() == 'false': + invert = True + query = 'true' + else: + invert = False for loc in location: matches |= self.get_matches(loc, query, candidates=candidates, allow_recursion=False) + if invert: + matches = self.universal_set() - matches return matches raise ParseException(query, len(query), 'Recursive query group detected', self) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index b0497eb53e..09c5a09951 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -188,6 +188,18 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): migrate_preference('saved_searches', {}) set_saved_searches(self, 'saved_searches') + # migrate grouped_search_terms + if self.prefs.get('grouped_search_terms', None) is None: + try: + ogst = tweaks['grouped_search_terms'] + print 'migrating' + ngst = {} + for t in ogst: + ngst[icu_lower(t)] = ogst[t] + self.prefs.set('grouped_search_terms', ngst) + except: + pass + # Rename any user categories with names that differ only in case user_cats = self.prefs.get('user_categories', []) catmap = {} @@ -349,12 +361,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if len(saved_searches().names()): tb_cats.add_search_category(label='search', name=_('Searches')) - gst = tweaks['grouped_search_terms'] - for t in gst: - try: - self.field_metadata._add_search_terms_to_map(gst[t], [t]) - except ValueError: - traceback.print_exc() + self.field_metadata.add_grouped_search_terms( + self.prefs.get('grouped_search_terms', {})) self.book_on_device_func = None self.data = ResultCache(self.FIELD_MAP, self.field_metadata, db_prefs=self.prefs) @@ -1293,7 +1301,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): # icon_map is not None if get_categories is to store an icon and # possibly a tooltip in the tag structure. icon = None - tooltip = '' + tooltip = '(' + category + ')' label = tb_cats.key_to_label(category) if icon_map: if not tb_cats.is_custom_field(category): @@ -1379,7 +1387,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): categories['formats'].sort(key = lambda x:x.name) #### Now do the user-defined categories. #### - user_categories = self.prefs['user_categories'] + user_categories = dict.copy(self.prefs['user_categories']) # We want to use same node in the user category as in the source # category. To do that, we need to find the original Tag node. There is @@ -1390,6 +1398,17 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): for c in categories.keys(): taglist[c] = dict(map(lambda t:(t.name, t), categories[c])) + muc = self.prefs.get('grouped_search_make_user_categories', []) + gst = self.prefs.get('grouped_search_terms', {}) + for c in gst: + if c not in muc: + continue + user_categories[c] = [] + for sc in gst[c]: + if sc in categories.keys(): + for t in categories[sc]: + user_categories[c].append([t.name, sc, 0]) + for user_cat in sorted(user_categories.keys(), key=sort_key): items = [] for (name,label,ign) in user_categories[user_cat]: diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index d64ea54424..9b481a89d0 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -3,7 +3,7 @@ Created on 25 May 2010 @author: charles ''' -import copy +import copy, traceback from calibre.utils.ordered_dict import OrderedDict from calibre.utils.config import tweaks @@ -488,6 +488,20 @@ class FieldMetadata(dict): del self._search_term_map[k] del self._tb_cats[key] + def _remove_grouped_search_terms(self): + to_remove = [v for v in self._search_term_map + if isinstance(self._search_term_map[v], list)] + for v in to_remove: + del self._search_term_map[v] + + def add_grouped_search_terms(self, gst): + self._remove_grouped_search_terms() + for t in gst: + try: + self._add_search_terms_to_map(gst[t], [t]) + except ValueError: + traceback.print_exc() + def cc_series_index_column_for(self, key): return self._tb_cats[key]['rec_index'] + 1 From d7024023808f635478ca81c5654019666162d3d8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Feb 2011 10:56:44 -0700 Subject: [PATCH 03/12] Fix #8487 (NRC Handelsblad feed is empty) --- resources/recipes/nrc.nl.recipe | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/recipes/nrc.nl.recipe b/resources/recipes/nrc.nl.recipe index 60522ff90e..7ba56e8fc9 100644 --- a/resources/recipes/nrc.nl.recipe +++ b/resources/recipes/nrc.nl.recipe @@ -21,8 +21,8 @@ class Pagina12(BasicNewsRecipe): country = 'NL' remove_empty_feeds = True masthead_url = 'http://www.nrc.nl/nrc.nl/images/logo_nrc.png' - extra_css = """ - body{font-family: Georgia,serif } + extra_css = """ + body{font-family: Georgia,serif } img{margin-bottom: 0.4em; display: block} .bijschrift,.sectie{font-size: x-small} .sectie{color: gray} @@ -38,10 +38,10 @@ class Pagina12(BasicNewsRecipe): keep_only_tags = [dict(attrs={'class':'uitstekendekeus'})] remove_tags = [ dict(name=['meta','base','link','object','embed']) - ,dict(attrs={'class':['reclamespace','tags-and-sharing']}) + ,dict(attrs={'class':['reclamespace','tags-and-sharing','sharing-is-caring']}) ] remove_attributes=['lang'] - + feeds = [ (u'Voor nieuws', u'http://www.nrc.nl/nieuws/categorie/nieuws/rss.php' ) ,(u'Binnenland' , u'http://www.nrc.nl/nieuws/categorie/binnenland/rss.php' ) @@ -69,8 +69,8 @@ class Pagina12(BasicNewsRecipe): del item[atit] else: str = self.tag_to_string(item) - item.replaceWith(str) + item.replaceWith(str) for item in soup.findAll('img'): if not item.has_key('alt'): - item['alt'] = 'image' + item['alt'] = 'image' return soup From 15c9b4b91b653a07610cd0c6fe18d70ddb0813b4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Feb 2011 11:25:49 -0700 Subject: [PATCH 04/12] Credit Slips, EPL, Fan Graphs and The Oregonian by zotzot --- resources/recipes/aprospect.recipe | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 resources/recipes/aprospect.recipe diff --git a/resources/recipes/aprospect.recipe b/resources/recipes/aprospect.recipe old mode 100755 new mode 100644 From 46a5105fb4b1d92905743dfdd59450dfd37578fb Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 15 Feb 2011 18:26:21 +0000 Subject: [PATCH 05/12] Alter tooltips and help docs for grouped search terms to (one hopes) make it clearer. --- src/calibre/gui2/preferences/search.py | 30 +++++++++++++++----------- src/calibre/gui2/preferences/search.ui | 18 +++++++++++++--- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/calibre/gui2/preferences/search.py b/src/calibre/gui2/preferences/search.py index 4f108d7cbc..db93cbd525 100644 --- a/src/calibre/gui2/preferences/search.py +++ b/src/calibre/gui2/preferences/search.py @@ -36,22 +36,26 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): "search term allseries with the value " "series, #myseries, #myseries2, then " "the query allseries:adhoc will find 'adhoc' in any of the " - "columns 'series', '#myseries', and '#myseries2'.

Enter the name of the " + "columns series, #myseries, and " + "#myseries2.

Enter the name of the " "grouped search term in the drop-down box, enter the list of columns " "to search in the value box, then push the Save button. " - "

Note: Search terms are forced to lower case; 'MySearch' " - "and 'mysearch' are the same term." - "

You can have your grouped search term show up as a user category in " - " the Tag Browser. Just add the grouped search term name to the user " - "category box. You can add multiple terms separated by commas. " - "The user category will be " + "

Note: Search terms are forced to lower case; MySearch " + "and mysearch are the same term." + "

You can have your grouped search term show up as user categories in " + " the Tag Browser. Just add the grouped search term names to the Make user " + "categories from box. You can add multiple terms separated by commas. " + "The new user category will be automatically " "populated with all the items in the categories included in the grouped " - "search term.

This permits you to see easily all the category items that " - "are in the fields contained in the grouped search term. Using the above " - "'myseries' example, the automatically-generated user category would contain " - "all the series mentioned in 'series', '#myseries1', and '#myseries2'. This " - "can be useful to check for duplicates or to find which column contains " - "a particular item.")) + "search term.

Automatic user categories permit you to see easily " + "all the category items that " + "are in the columns contained in the grouped search term. Using the above " + "allseries example, the automatically-generated user category " + "will contain all the series mentioned in series, " + "#myseries, and #myseries2. This " + "can be useful to check for duplicates, to find which column contains " + "a particular item, or to have hierarchical categories (categories " + "that contain categories).")) self.gst = db.prefs.get('grouped_search_terms', {}) self.orig_gst_keys = self.gst.keys() diff --git a/src/calibre/gui2/preferences/search.ui b/src/calibre/gui2/preferences/search.ui index 3466b53a24..3f5b43bbb6 100644 --- a/src/calibre/gui2/preferences/search.ui +++ b/src/calibre/gui2/preferences/search.ui @@ -98,7 +98,7 @@ - &Grouped search terms: + &Names: gst_names @@ -113,6 +113,13 @@ 10 + + Contains the names of the currently-defined group search terms. +Create a new name by entering it into the empty box, then +pressing Save. Rename a search term by selecting it then +changing the name and pressing Save. Change the value of +a search term by changing the value box then pressing Save. + @@ -161,7 +168,7 @@ of a search term by changing the value box then pressing Save. - Make &user categories: + Make &user categories from: opt_grouped_search_make_user_categories @@ -169,7 +176,12 @@ of a search term by changing the value box then pressing Save. - + + + Enter the names of any grouped search terms you wish +to be shown as user categories + + From 2a05a3b7897c73f32786e9d4517498bc9d3690a4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Feb 2011 11:37:41 -0700 Subject: [PATCH 06/12] ... --- src/calibre/devices/kindle/apnx.py | 2 +- src/calibre/translations/calibre.pot | 1146 ++++++++++++++------------ 2 files changed, 631 insertions(+), 517 deletions(-) diff --git a/src/calibre/devices/kindle/apnx.py b/src/calibre/devices/kindle/apnx.py index c98fe7a7fa..178c1091f3 100644 --- a/src/calibre/devices/kindle/apnx.py +++ b/src/calibre/devices/kindle/apnx.py @@ -25,7 +25,7 @@ class APNXBuilder(object): with open(mobi_file_path, 'rb') as mf: ident = PdbHeaderReader(mf).identity() if ident != 'BOOKMOBI': - raise Exception(_('Not a valid MOBI file. Reports identity of %s' % ident)) + raise Exception(_('Not a valid MOBI file. Reports identity of %s') % ident) # Get the pages depending on the chosen parser pages = [] diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 190a7d8f46..929e69ad72 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: calibre 0.7.45\n" -"POT-Creation-Date: 2011-02-11 11:38+MST\n" -"PO-Revision-Date: 2011-02-11 11:38+MST\n" +"POT-Creation-Date: 2011-02-15 11:37+MST\n" +"PO-Revision-Date: 2011-02-15 11:37+MST\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -25,12 +25,12 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:74 #: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:77 #: /home/kovid/work/calibre/src/calibre/devices/kobo/books.py:24 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:466 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:465 #: /home/kovid/work/calibre/src/calibre/devices/nook/driver.py:70 #: /home/kovid/work/calibre/src/calibre/devices/nook/driver.py:71 #: /home/kovid/work/calibre/src/calibre/devices/prs500/books.py:267 #: /home/kovid/work/calibre/src/calibre/devices/prs505/sony_cache.py:660 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:401 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:403 #: /home/kovid/work/calibre/src/calibre/ebooks/chm/input.py:97 #: /home/kovid/work/calibre/src/calibre/ebooks/chm/input.py:100 #: /home/kovid/work/calibre/src/calibre/ebooks/chm/metadata.py:56 @@ -114,8 +114,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:101 #: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:312 #: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:314 -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:304 -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:311 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:308 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:315 #: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:100 #: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:332 #: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:335 @@ -136,25 +136,25 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:193 #: /home/kovid/work/calibre/src/calibre/gui2/email.py:236 #: /home/kovid/work/calibre/src/calibre/gui2/email.py:245 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:422 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:441 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:978 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1171 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:421 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:440 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:977 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1170 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:70 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:167 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:185 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/bulk_download.py:112 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:191 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:197 #: /home/kovid/work/calibre/src/calibre/library/cli.py:215 #: /home/kovid/work/calibre/src/calibre/library/database.py:914 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:430 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:436 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:446 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1539 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1642 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2545 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2547 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2678 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:437 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:443 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:453 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1557 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1660 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2563 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2565 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2696 #: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:233 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:158 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:161 @@ -172,31 +172,37 @@ msgstr "" msgid "Base" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:130 +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:135 msgid "Customize" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:294 +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:143 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:39 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:44 +msgid "Cannot configure" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:305 msgid "File type" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:330 +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:341 msgid "Metadata reader" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:360 +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:371 msgid "Metadata writer" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:390 +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:401 msgid "Catalog generator" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:499 +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:510 msgid "User Interface Action" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:525 +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:536 #: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:18 #: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:23 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:190 @@ -206,243 +212,247 @@ msgstr "" msgid "Preferences" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:15 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:17 msgid "Follow all local links in an HTML file and create a ZIP file containing all linked files. This plugin is run every time you add an HTML file to the library." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:51 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:53 msgid "Character encoding for the input HTML files. Common choices include: cp1252, latin1, iso-8859-1 and utf-8." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:58 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:60 msgid "Create a PMLZ archive containing the PML file and all images in the directory pmlname_img or images. This plugin is run every time you add a PML file to the library." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:92 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:90 +msgid "Create a TXTZ archive when a TXT file is imported containing Markdown or Textile references to images. The referenced images as well as the TXT file are added to the archive." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:154 msgid "Extract cover from comic files" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:121 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:132 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:144 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:154 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:164 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:175 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:185 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:195 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:205 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:215 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:225 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:235 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:246 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:258 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:279 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:290 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:300 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:311 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:321 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:332 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:183 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:194 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:206 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:216 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:226 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:237 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:247 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:257 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:267 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:277 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:287 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:297 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:308 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:320 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:341 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:352 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:362 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:373 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:383 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:394 msgid "Read metadata from %s files" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:269 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:331 msgid "Read metadata from ebooks in RAR archives" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:343 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:405 msgid "Read metadata from ebooks in ZIP archives" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:356 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:366 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:376 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:398 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:409 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:419 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:418 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:428 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:438 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:460 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:471 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:481 msgid "Set metadata in %s files" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:387 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:430 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:449 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:492 msgid "Set metadata from %s files" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:750 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:812 msgid "Look and Feel" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:752 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:764 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:775 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:786 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:798 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:814 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:826 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:837 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:848 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:860 msgid "Interface" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:756 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:818 msgid "Adjust the look and feel of the calibre interface to suit your tastes" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:762 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:824 msgid "Behavior" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:768 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:830 msgid "Change the way calibre behaves" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:773 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:835 #: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:217 msgid "Add your own columns" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:779 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:841 msgid "Add/remove your own columns to the calibre book list" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:784 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:846 msgid "Customize the toolbar" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:790 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:852 msgid "Customize the toolbars and context menus, changing which actions are available in each" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:796 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:858 msgid "Customize searching" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:802 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:864 msgid "Customize the way searching for books works in calibre" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:807 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:869 msgid "Input Options" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:809 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:820 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:831 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:871 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:882 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:893 msgid "Conversion" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:813 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:875 msgid "Set conversion options specific to each input format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:818 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:880 msgid "Common Options" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:824 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:886 msgid "Set conversion options common to all formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:829 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:891 msgid "Output Options" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:835 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:897 msgid "Set conversion options specific to each output format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:840 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:902 msgid "Adding books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:842 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:854 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:866 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:878 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:904 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:916 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:928 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:940 msgid "Import/Export" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:846 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:908 msgid "Control how calibre reads metadata from files when adding books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:852 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:914 msgid "Saving books to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:858 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:920 msgid "Control how calibre exports files from its database to disk when using Save to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:864 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:926 msgid "Sending books to devices" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:870 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:932 msgid "Control how calibre transfers files to your ebook reader" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:876 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:938 msgid "Metadata plugboards" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:882 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:944 msgid "Change metadata fields before saving/sending" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:887 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:949 msgid "Template Functions" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:889 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:925 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:937 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:948 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:951 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:987 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:999 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1010 msgid "Advanced" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:893 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:955 msgid "Create your own template functions" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:898 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:960 msgid "Sharing books by email" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:900 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:912 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:962 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:974 msgid "Sharing" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:904 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:966 msgid "Setup sharing of books via email. Can be used for automatic sending of downloaded news to your devices" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:910 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:972 msgid "Sharing over the net" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:916 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:978 msgid "Setup the calibre Content Server which will give you access to your calibre library from anywhere, on any device, over the internet" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:923 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:985 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:267 msgid "Plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:929 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:991 msgid "Add/remove/customize various bits of calibre functionality" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:935 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:997 msgid "Tweaks" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:941 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1003 msgid "Fine tune how calibre behaves in various contexts" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:946 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1008 msgid "Miscellaneous" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:952 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1014 msgid "Miscellaneous advanced configuration" msgstr "" @@ -671,87 +681,87 @@ msgstr "" msgid "Communicate with S60 phones." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:90 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:92 msgid "Apple device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:92 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:94 msgid "Communicate with iTunes/iBooks." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:98 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:100 msgid "Apple device detected, launching iTunes, please wait ..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:100 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:102 msgid "Cannot copy books directly from iDevice. Drag from iTunes Library to desktop, then add to calibre's Library window." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:260 -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:263 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:262 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:265 msgid "Updating device metadata listing..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:339 -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:378 -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:947 -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:987 -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2972 -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:3012 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:341 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:380 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:949 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:989 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2974 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:3014 msgid "%d of %d" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:385 -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:992 -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:3018 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:387 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:994 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:3020 msgid "finished" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:560 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:562 msgid "Use Series as Category in iTunes/iBooks" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:562 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:564 msgid "Cache covers from iTunes/iBooks" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:574 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:576 msgid "" "Some books not found in iTunes database.\n" "Delete using the iBooks app.\n" "Click 'Show Details' for a list." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:911 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:913 msgid "" "Some cover art could not be converted.\n" "Click 'Show Details' for a list." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2553 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2555 #: /home/kovid/work/calibre/src/calibre/devices/nook/driver.py:100 #: /home/kovid/work/calibre/src/calibre/devices/prs505/sony_cache.py:447 #: /home/kovid/work/calibre/src/calibre/devices/prs505/sony_cache.py:470 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:886 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:892 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:922 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:908 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:914 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:944 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:262 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:244 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:257 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2409 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:255 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:268 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2427 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:150 msgid "News" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2554 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2556 #: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:65 #: /home/kovid/work/calibre/src/calibre/library/catalog.py:634 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2372 #: /home/kovid/work/calibre/src/calibre/library/database2.py:2390 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2408 msgid "Catalog" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2876 +#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2878 msgid "Communicate with iTunes." msgstr "" @@ -763,82 +773,82 @@ msgstr "" msgid "Li Fanxi" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:41 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:42 msgid "Device IP Address (restart calibre after changing)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:46 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:47 msgid "Unable to add book to library directly from Bambook. Please save the book to disk and add the file to library from disk." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:66 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:67 msgid "Unable to connect to Bambook, you need to install Bambook library first." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:74 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:75 msgid "" "Unable to connect to Bambook. \n" "If you are trying to connect via Wi-Fi, please make sure the IP address of Bambook has been correctly configured." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:111 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:112 msgid "Bambook" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:217 -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:233 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:218 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:234 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:67 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:70 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:73 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:214 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:213 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:68 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:71 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:74 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:136 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:143 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:166 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:138 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:145 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:168 msgid "Getting list of books on device..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:263 -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:267 -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:278 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:195 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:264 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:268 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:279 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:197 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:199 msgid "Transferring books to device..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:284 -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:298 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:327 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:362 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:219 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:250 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:285 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:299 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:326 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:361 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:221 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:252 msgid "Adding books to device metadata listing..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:306 -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:308 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:307 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:309 #: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:102 #: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:113 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:279 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:311 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:256 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:274 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:278 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:310 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:258 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:276 msgid "Removing books from device..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:323 -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:328 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:315 -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:322 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:281 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:286 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:324 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:329 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:314 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:321 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:283 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:288 msgid "Removing books from device metadata listing..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:396 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:316 +#: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:397 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:318 msgid "Sending metadata to device..." msgstr "" @@ -989,6 +999,14 @@ msgstr "" msgid "Communicate with the JetBook Mini reader." msgstr "" +#: /home/kovid/work/calibre/src/calibre/devices/kindle/apnx.py:28 +msgid "Not a valid MOBI file. Reports identity of %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/devices/kindle/apnx.py:44 +msgid "Could not generate page mapping." +msgstr "" + #: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:44 msgid "Communicate with the Kindle eBook reader." msgstr "" @@ -997,7 +1015,23 @@ msgstr "" msgid "Communicate with the Kindle 2/3 eBook reader." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:231 +#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:180 +msgid "Send page number information when sending books" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:182 +msgid "The Kindle 3 and newer versions can use page number information in MOBI files. With this option, calibre will calculate and send this information to the Kindle when uploading MOBI files by USB. Note that the page numbers do not correspond to any paper book." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:187 +msgid "Use slower but more accurate page number generation" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:189 +msgid "There are two ways to generate the page number information. Using the more accurate generator will produce pages that correspond better to a printed book. However, this method is slower and will slow down sending files to the Kindle." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:257 msgid "Communicate with the Kindle DX eBook reader." msgstr "" @@ -1009,12 +1043,12 @@ msgstr "" msgid "The Kobo supports only one collection currently: the \"Im_Reading\" list. Create a tag called \"Im_Reading\" " msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:446 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:295 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:445 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:296 msgid "Not Implemented" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:447 +#: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:446 msgid "\".kobo\" files do not exist on the device as books instead, they are rows in the sqlite database. Currently they cannot be exported or viewed." msgstr "" @@ -1192,49 +1226,49 @@ msgstr "" msgid "Communicate with the Stash W950 reader." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:261 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:282 msgid "Unable to detect the %s disk drive. Try rebooting." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:441 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:463 msgid "Unable to detect the %s mount point. Try rebooting." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:506 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:528 msgid "Unable to detect the %s disk drive." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:599 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:621 msgid "Could not find mount helper: %s." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:611 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:633 msgid "Unable to detect the %s disk drive. Either the device has already been ejected, or your kernel is exporting a deprecated version of SYSFS." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:620 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:642 msgid "Unable to mount main memory (Error code: %d)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:671 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:693 msgid "The main memory of %s is read only. This usually happens because of file system errors." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:819 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:821 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:841 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:843 msgid "The reader has no storage card in this slot." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:823 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:845 msgid "Selected slot: %s is not supported." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:852 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:874 msgid "There is insufficient free space in main memory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:854 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:856 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:876 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:878 msgid "There is insufficient free space on the storage card" msgstr "" @@ -1242,32 +1276,32 @@ msgstr "" msgid "Configure Device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:51 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:55 msgid "settings for device drivers" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:53 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:57 msgid "Ordered list of formats the device will accept" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:55 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:59 msgid "Place files in sub directories if the device supports them" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:57 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:61 #: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:81 msgid "Read metadata from files on device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:59 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:63 msgid "Use author sort instead of author" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:61 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:65 msgid "Template to control how books are saved" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:64 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/deviceconfig.py:68 msgid "Extra customization" msgstr "" @@ -1564,7 +1598,7 @@ msgid "Read metadata from the specified OPF file. Metadata read from this file w msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:399 -msgid "Transliterate unicode characters to an ASCII representation. Use with care because this will replace unicode characters with ASCII. For instance it will replace \"%s\" with \"Mikhail Gorbachiov\". Also, note that in cases where there are multiple representations of a character (characters shared by Chinese and Japanese for instance) the representation used by the largest number of people will be used (Chinese in the previous example)." +msgid "Transliterate unicode characters to an ASCII representation. Use with care because this will replace unicode characters with ASCII. For instance it will replace \"%s\" with \"Mikhail Gorbachiov\". Also, note that in cases where there are multiple representations of a character (characters shared by Chinese and Japanese for instance) the representation based on the current calibre interface language will be used." msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plumber.py:414 @@ -2157,8 +2191,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/delete_matching_from_device.py:75 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:60 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:65 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:419 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:983 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:418 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:982 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:304 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:590 msgid "Title" @@ -2167,8 +2201,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:616 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:61 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:67 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:424 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:984 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:423 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:983 msgid "Author(s)" msgstr "" @@ -2188,8 +2222,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:214 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:114 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:79 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:381 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1190 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:380 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1189 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:188 msgid "Comments" msgstr "" @@ -2199,8 +2233,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:30 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:60 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:73 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:369 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1186 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:368 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1185 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:161 #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:657 msgid "Tags" @@ -2211,8 +2245,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:29 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:60 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:74 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:386 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1195 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:385 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1194 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:109 msgid "Series" msgstr "" @@ -2222,7 +2256,7 @@ msgid "Language" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:626 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1178 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1177 msgid "Timestamp" msgstr "" @@ -2320,45 +2354,28 @@ msgstr "" msgid "No cover found" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:28 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:27 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/nicebooks.py:45 msgid "Cover download" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:80 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:79 msgid "Download covers from openlibrary.org" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:108 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:140 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:107 msgid "ISBN: %s not found" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:118 -msgid "Download covers from librarything.com" +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:117 +msgid "Download covers from amazon.com" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:130 -msgid "LibraryThing.com timed out. Try again later." -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:139 -msgid "Could not fetch cover as server is experiencing high load. Please try again later." -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:143 -msgid "LibraryThing.com server error. Try again later." -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:177 -msgid "To use librarything.com you must sign up for a %sfree account%s and enter your username and password separated by a : below." -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:240 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:205 msgid "Download covers from Douban.com" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:249 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/covers.py:214 msgid "Douban.com API timed out. Try again later." msgstr "" @@ -3029,34 +3046,36 @@ msgstr "" msgid " (Preface)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:32 +#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:30 msgid "" "Paragraph structure.\n" -"choices are ['auto', 'block', 'single', 'print', 'unformatted']\n" +"choices are ['auto', 'block', 'single', 'print', 'unformatted', 'off']\n" "* auto: Try to auto detect paragraph type.\n" "* block: Treat a blank line as a paragraph break.\n" "* single: Assume every line is a paragraph.\n" -"* print: Assume every line starting with 2+ spaces or a tab starts a paragraph.* unformatted: Most lines have hard line breaks, few/no blank lines or indents." +"* print: Assume every line starting with 2+ spaces or a tab starts a paragraph.\n" +"* unformatted: Most lines have hard line breaks, few/no blank lines or indents. Tries to determine structure and reformat the differentiate elements.\n" +"* off: Don't modify the paragraph structure. This is useful when combined with Markdown or Textile formatting to ensure no formatting is lost." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:42 +#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:43 msgid "" "Formatting used within the document.* auto: Automatically decide which formatting processor to use.\n" -"* none: Do not process the document formatting. Everything is a paragraph and no styling is applied.\n" +"* plain: Do not process the document formatting. Everything is a paragraph and no styling is applied.\n" "* heuristic: Process using heuristics to determine formatting such as chapter headings and italic text.\n" "* textile: Processing using textile formatting.\n" "* markdown: Processing using markdown formatting. To learn more about markdown see" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:52 +#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:53 msgid "Normally extra spaces are condensed into a single space. With this option all spaces will be displayed." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:55 +#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:56 msgid "Normally extra space at the beginning of lines is retained. With this option they will be removed." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:58 +#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:59 msgid "Do not insert a Table of Contents into the output text." msgstr "" @@ -3190,22 +3209,22 @@ msgid "Limit max simultaneous jobs to number of CPUs" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:141 -msgid "tag browser categories not to display" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:143 msgid "The layout of the user interface" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:145 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:143 msgid "Show the average rating per item indication in the tag browser" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:147 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:145 msgid "Disable UI animations" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:415 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:150 +msgid "tag browser categories not to display" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:419 msgid "Choose Files" msgstr "" @@ -3246,7 +3265,7 @@ msgid "Add from ISBN" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:175 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:236 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:237 msgid "Uploading books to device." msgstr "" @@ -3300,33 +3319,33 @@ msgstr "" msgid "Archives" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:207 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:208 msgid "Supported books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:246 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:247 msgid "Merged some books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:247 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:248 msgid "The following duplicate books were found and incoming book formats were processed and merged into your Calibre database according to your automerge settings:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:256 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:257 msgid "Failed to read metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:257 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:258 msgid "Failed to read metadata from the following" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:278 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:283 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:302 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:279 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:284 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:303 msgid "Add to library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:283 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:284 #: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:116 #: /home/kovid/work/calibre/src/calibre/gui2/actions/tweak_epub.py:28 #: /home/kovid/work/calibre/src/calibre/gui2/actions/view.py:85 @@ -3334,11 +3353,11 @@ msgstr "" msgid "No book selected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:296 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:297 msgid "The following books are virtual and cannot be added to the calibre library:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:302 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:303 msgid "No book files found" msgstr "" @@ -3595,7 +3614,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:399 #: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:167 #: /home/kovid/work/calibre/src/calibre/gui2/actions/save_to_disk.py:101 -#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:780 +#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:782 msgid "Not allowed" msgstr "" @@ -3766,7 +3785,7 @@ msgid "None of the selected books are on the device" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:200 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:289 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:290 msgid "Deleting books from device." msgstr "" @@ -3778,7 +3797,7 @@ msgstr "" msgid "The selected books will be permanently deleted and the files removed from your calibre library. Are you sure?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:274 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:282 msgid "The selected books will be permanently deleted from your device. Are you sure?" msgstr "" @@ -4028,11 +4047,6 @@ msgstr "" msgid "Restart in debug mode" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:39 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:44 -msgid "Cannot configure" -msgstr "" - #: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:40 msgid "Cannot configure while there are running jobs." msgstr "" @@ -4372,8 +4386,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:79 #: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:80 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_library_ui.py:86 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:533 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:538 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:534 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:539 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:412 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:414 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:417 @@ -4409,6 +4423,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/custom_columns_ui.py:87 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/custom_columns_ui.py:89 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/custom_columns_ui.py:90 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:127 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/toolbar_ui.py:103 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/toolbar_ui.py:105 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/toolbar_ui.py:108 @@ -4440,8 +4455,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:132 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:145 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/delete_matching_from_device.py:76 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:376 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1176 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:375 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1175 msgid "Path" msgstr "" @@ -4451,15 +4466,15 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:134 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:135 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:138 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:375 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:374 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:24 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:118 msgid "Formats" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:28 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:987 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1179 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:986 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1178 msgid "Collections" msgstr "" @@ -4469,11 +4484,11 @@ msgid "Click to open" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:56 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:368 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:374 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:380 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1185 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1189 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:367 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:373 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:379 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1184 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1188 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts.py:48 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:78 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:83 @@ -4571,7 +4586,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:86 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/save_template_ui.py:46 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/saving_ui.py:67 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:59 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:109 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/sending_ui.py:68 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/server_ui.py:123 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/template_functions_ui.py:95 @@ -5531,7 +5546,7 @@ msgid "Change the title of this book" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:166 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:498 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:499 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:420 msgid "&Author(s): " msgstr "" @@ -5545,7 +5560,7 @@ msgid "Change the author(s) of this book. Multiple authors should be separated b msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:169 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:508 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:509 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:428 msgid "&Publisher: " msgstr "" @@ -5556,13 +5571,13 @@ msgid "Ta&gs: " msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:171 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:510 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:511 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:430 msgid "Tags categorize the book. This is particularly useful while searching.

They can be any words or phrases, separated by commas." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:172 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:517 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:518 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:433 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:214 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:293 @@ -5571,8 +5586,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:173 #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:174 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:518 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:519 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:520 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:434 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:435 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:292 @@ -6154,10 +6169,10 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:150 #: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:184 #: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:294 -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:554 -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:584 -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:607 -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:658 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:558 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:599 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:622 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:673 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:306 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:311 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:503 @@ -6172,22 +6187,22 @@ msgid "Undefined" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:127 -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:615 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:630 msgid "star(s)" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:128 -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:616 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:631 msgid "Unrated" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:171 -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:645 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:660 msgid "Set '%s' to today" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:173 -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:647 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:662 msgid "Clear '%s'" msgstr "" @@ -6203,31 +6218,31 @@ msgstr "" msgid "Apply changes" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:691 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:706 msgid "Remove series" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:694 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:709 msgid "Automatically number books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:697 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:712 msgid "Force numbers to start with " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:768 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:783 msgid "The enumeration \"{0}\" contains invalid values that will not appear in the list" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:811 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:826 msgid "Remove all tags" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:831 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:846 msgid "tags to add" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:837 +#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:852 msgid "tags to remove" msgstr "" @@ -6411,7 +6426,17 @@ msgid "

Cannot upload books to device there is no more free space available " msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget.py:118 +#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget.py:124 +msgid "Unknown formats" +msgstr "" + +#: +#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget.py:125 +msgid "You have enabled the {0} formats for your {1}. The {1} may not support them. If you send these formats to your {1} they may not work. Are you sure?" +msgstr "" + +#: +#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget.py:137 #: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:403 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugboard.py:255 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/save_template.py:61 @@ -6419,7 +6444,7 @@ msgid "Invalid template" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget.py:119 +#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget.py:138 #: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:404 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugboard.py:256 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/save_template.py:62 @@ -6595,53 +6620,53 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:218 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:220 msgid "&Run the check again" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:221 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:223 msgid "Copy &to clipboard" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:228 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:230 msgid "Delete marked files (checked subitems)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:234 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:236 msgid "Fix marked sections (checked fixable items)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:244 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:246 msgid "Names to ignore:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:249 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:251 msgid "Enter comma-separated standard file name wildcards, such as synctoy*.dat" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:252 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:254 msgid "Extensions to ignore" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:257 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:259 msgid "Enter comma-separated extensions without a leading dot. Used only in book folders" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:306 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:308 msgid "(fixable)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:329 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:331 msgid "Path from library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:329 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:331 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/bookmarkmanager.py:89 #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:253 msgid "Name" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:354 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:360 msgid "The marked files and folders will be permanently deleted. Are you sure?" msgstr "" @@ -6654,7 +6679,7 @@ msgstr "" #: #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_format_device_ui.py:49 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/delete_matching_from_device.py:76 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1175 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1174 msgid "Format" msgstr "" @@ -6831,7 +6856,7 @@ msgstr "" #: #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/delete_matching_from_device.py:76 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:69 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:985 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:984 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:33 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:295 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:589 @@ -6859,13 +6884,13 @@ msgstr "" #: #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/edit_authors_dialog.py:117 -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:893 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:912 msgid "Invalid author name" msgstr "" #: #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/edit_authors_dialog.py:118 -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:894 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:913 msgid "Author names cannot contain & characters." msgstr "" @@ -7128,108 +7153,108 @@ msgid "" "Phase {0} {1}%%." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:920 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:560 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:921 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:561 msgid "Delete saved search/replace" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:921 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:922 msgid "The selected saved search/replace will be deleted. Are you sure?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:938 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:946 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:939 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:947 msgid "Save search/replace" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:939 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:940 msgid "Search/replace name:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:947 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:948 msgid "That saved search/replace already exists and will be overwritten. Are you sure?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:497 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:498 msgid "Edit Meta information" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:499 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:500 msgid "A&utomatically set author sort" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:500 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:501 msgid "&Swap title and author" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:501 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:502 msgid "Author s&ort: " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:502 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:503 msgid "Specify how the author(s) of this book should be sorted. For example Charles Dickens should be sorted as Dickens, Charles." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:503 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:504 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:424 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:786 msgid "&Rating:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:504 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:505 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:506 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:425 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:426 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:787 msgid "Rating of this book. 0-5 stars" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:506 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:507 msgid "No change" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:507 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:508 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:427 msgid " stars" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:509 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:510 msgid "Add ta&gs: " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:511 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:512 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:513 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:431 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:432 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:140 msgid "Open Tag Editor" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:513 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:514 msgid "&Remove tags:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:514 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:515 msgid "Comma separated list of tags to remove from the books. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:515 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:516 msgid "Check this box to remove all tags from the books." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:516 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:517 msgid "Remove &all" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:520 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:521 msgid "If checked, the series will be cleared" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:521 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:522 msgid "&Clear series" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:522 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:523 msgid "" "If not checked, the series number for the books will be set to 1.\n" "If checked, selected books will be automatically numbered, in the order\n" @@ -7237,244 +7262,244 @@ msgid "" "Book A will have series number 1 and Book B series number 2." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:526 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:527 msgid "&Automatically number books in this series" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:527 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:528 msgid "" "Series will normally be renumbered from the highest number in the database\n" "for that series. Checking this box will tell calibre to start numbering\n" "from the value in the box" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:530 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:531 msgid "&Force numbers to start with:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:531 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:532 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:440 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:978 msgid "&Date:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:532 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:533 msgid "d MMM yyyy" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:534 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:539 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:535 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:540 msgid "&Apply date" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:535 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:536 msgid "&Published:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:537 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:538 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:444 msgid "Clear published date" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:540 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:541 msgid "Remove &format:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:541 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:542 msgid "" "Force the title to be in title case. If both this and swap authors are checked,\n" "title and author are swapped before the title case is set" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:543 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:544 msgid "Change title to title &case" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:544 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:545 msgid "Update title sort based on the current title. This will be applied only after other changes to title." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:545 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:546 msgid "Update &title sort" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:546 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:547 msgid "" "Remove stored conversion settings for the selected books.\n" "\n" "Future conversion of these books will use the default settings." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:549 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:550 msgid "Remove &stored conversion settings for the selected books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:550 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:551 msgid "Change &cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:551 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:552 msgid "&Generate default cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:552 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:553 msgid "&Remove cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:553 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:554 msgid "Set from &ebook file(s)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:554 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:555 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:465 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:392 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:521 msgid "&Basic metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:555 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:556 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:466 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:399 msgid "&Custom metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:556 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:557 msgid "Load searc&h/replace:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:557 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:558 msgid "Select saved search/replace to load." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:558 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:559 msgid "Save current search/replace" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:559 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:560 msgid "Sa&ve" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:561 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:562 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/bookmarkmanager_ui.py:64 msgid "Delete" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:562 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:563 msgid "Search &field:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:563 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:564 msgid "The name of the field that you want to search" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:564 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:565 msgid "Search &mode:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:565 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:566 msgid "Choose whether to use basic text matching or advanced regular expression matching" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:566 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:567 msgid "Te&mplate:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:567 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:568 msgid "Enter a template to be used as the source for the search/replace" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:568 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:569 msgid "&Search for:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:569 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:570 msgid "Enter the what you are looking for, either plain text or a regular expression, depending on the mode" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:570 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:571 msgid "Check this box if the search string must match exactly upper and lower case. Uncheck it if case is to be ignored" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:571 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:572 msgid "Cas&e sensitive" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:572 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:573 msgid "&Replace with:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:573 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:574 msgid "The replacement text. The matched search text will be replaced with this string" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:574 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:575 msgid "&Apply function after replace:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:575 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:576 msgid "" "Specify how the text is to be processed after matching and replacement. In character mode, the entire\n" "field is processed. In regular expression mode, only the matched text is processed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:577 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:578 msgid "&Destination field:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:578 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:579 msgid "" "The field that the text will be put into after all replacements.\n" "If blank, the source field is used if the field is modifiable" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:580 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:581 msgid "M&ode:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:581 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:582 msgid "Specify how the text should be copied into the destination." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:582 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:583 msgid "" "Specifies whether result items should be split into multiple values or\n" "left as single values. This option has the most effect when the source field is\n" "not multiple and the destination field is multiple" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:585 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:586 msgid "Split &result" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:586 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:587 msgid "For multiple-valued fields, sho&w" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:587 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:588 msgid "values starting a&t" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:588 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:589 msgid "with values separated b&y" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:589 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:590 msgid "Used when displaying test results to separate values in multiple-valued fields" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:590 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:591 msgid "Test text" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:591 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:592 msgid "Test result" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:592 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:593 msgid "Your test:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:593 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:594 msgid "&Search and replace" msgstr "" @@ -8323,12 +8348,12 @@ msgid "%s (was %s)" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_list_editor.py:83 -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:883 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:902 msgid "Item is blank" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_list_editor.py:84 -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:884 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:903 msgid "An item cannot be set to nothing. Delete it instead." msgstr "" @@ -8844,7 +8869,7 @@ msgid "Show books in the main memory of the device" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/layout.py:67 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:906 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:913 msgid "Card A" msgstr "" @@ -8853,7 +8878,7 @@ msgid "Show books in storage card A" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/layout.py:69 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:908 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:915 msgid "Card B" msgstr "" @@ -8927,38 +8952,38 @@ msgstr "" msgid "Size (MB)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:387 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:386 msgid "Book %s of %s." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:736 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1295 -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:589 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:735 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1294 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:599 msgid "The lookup/search name is \"{0}\"" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:742 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1297 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:741 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1296 msgid "This book's UUID is \"{0}\"" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:982 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:981 msgid "In Library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:986 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:985 msgid "Size" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1195 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1194 msgid "Book %s of %s." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1275 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1274 msgid "Marked for deletion" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1278 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1277 msgid "Double click to edit me

" msgstr "" @@ -9002,7 +9027,7 @@ msgstr "" msgid "Restore default layout" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:781 +#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:783 msgid "Dropping onto a device is not supported. First add the book to the calibre library." msgstr "" @@ -9031,7 +9056,7 @@ msgid "No matches for the search phrase %s were found." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main.py:160 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:454 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:481 msgid "No matches found" msgstr "" @@ -10224,34 +10249,113 @@ msgstr "" msgid "Save metadata in &OPF file" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:60 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:33 +msgid "Grouped search terms are search names that permit a query to automatically search across more than one column. For example, if you create a grouped search term allseries with the value series, #myseries, #myseries2, then the query allseries:adhoc will find 'adhoc' in any of the columns series, #myseries, and #myseries2.

Enter the name of the grouped search term in the drop-down box, enter the list of columns to search in the value box, then push the Save button.

Note: Search terms are forced to lower case; MySearch and mysearch are the same term.

You can have your grouped search term show up as user categories in the Tag Browser. Just add the grouped search term names to the Make user categories from box. You can add multiple terms separated by commas. The new user category will be automatically populated with all the items in the categories included in the grouped search term.

Automatic user categories permit you to see easily all the category items that are in the columns contained in the grouped search term. Using the above allseries example, the automatically-generated user category will contain all the series mentioned in series, #myseries, and #myseries2. This can be useful to check for duplicates, to find which column contains a particular item, or to have hierarchical categories (categories that contain categories)." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:96 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:106 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:110 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:116 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:128 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:119 +msgid "Grouped Search Terms" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:97 +msgid "The search term cannot be blank" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:107 +msgid "That name is already used for a column or grouped search term" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:111 +msgid "That name is already used for user category" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:117 +msgid "The value box cannot be empty" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search.py:129 +msgid "The empty grouped search term cannot be deleted" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:110 msgid "Search as you &type" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:61 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:111 msgid "&Highlight search results instead of restricting the book list to the results" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:62 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:112 msgid "What to search by default" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:63 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:113 msgid "When you enter a search term without a prefix, by default calibre will search all metadata for matches. For example, entering, \"asimov\" will search not just authors but title/tags/series/comments/etc. Use these options if you would like to change this behavior." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:64 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:114 msgid "&Limit the searched metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:65 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:115 msgid "&Columns that non-prefixed searches are limited to:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:66 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:116 msgid "Note that this option affects all searches, including saved searches and restrictions. Therefore, if you use this option, it is best to ensure that you always use prefixes in your saved searches. For example, use \"series:Foundation\" rather than just \"Foundation\" in a saved search" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:117 +msgid "Clear search histories from all over calibre. Including the book list, e-book viewer, fetch news dialog, etc." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:118 +msgid "Clear search &histories" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:120 +msgid "&Names:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:121 +msgid "" +"Contains the names of the currently-defined group search terms.\n" +"Create a new name by entering it into the empty box, then\n" +"pressing Save. Rename a search term by selecting it then\n" +"changing the name and pressing Save. Change the value of\n" +"a search term by changing the value box then pressing Save." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:126 +msgid "Delete the current search term" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:128 +msgid "" +"Save the current search term. You can rename a search term by\n" +"changing the name then pressing Save. You can change the value\n" +"of a search term by changing the value box then pressing Save." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:131 +msgid "&Save" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:132 +msgid "Make &user categories from:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:133 +msgid "" +"Enter the names of any grouped search terms you wish\n" +"to be shown as user categories" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/preferences/sending.py:28 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/sending_ui.py:70 msgid "Manual management" @@ -10621,25 +10725,25 @@ msgid "Apply any changes you made to this tweak" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:93 -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:270 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:273 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:616 #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:277 msgid "Search" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:314 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:317 msgid "The selected search will be permanently deleted. Are you sure?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:357 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:360 msgid "Search (For Advanced Search click the button to the left)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:424 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:427 msgid "Saved Searches" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:426 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:429 msgid "Choose saved search or enter name for new saved search" msgstr "" @@ -10715,86 +10819,90 @@ msgstr "" msgid "&Alternate shortcut:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:273 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:280 msgid "Rename %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:277 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:284 msgid "Edit sort for %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:282 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:289 msgid "Search for %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:287 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:294 msgid "Search for everything but %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:293 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:300 msgid "Hide category %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:296 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:303 msgid "Show category" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:304 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:311 msgid "Search for books in category %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:308 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:315 msgid "Search for books not in category %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:315 -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:319 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:322 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:326 msgid "Manage %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:322 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:329 msgid "Manage Saved Searches" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:329 -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:333 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:336 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:340 msgid "Manage User Categories" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:340 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:347 msgid "Show all categories" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:343 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:350 msgid "Change sub-categorization scheme" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:677 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:595 +msgid "The grouped search term name is \"{0}\"" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:687 msgid "Changing the authors for several books can take a while. Are you sure?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:682 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:692 msgid "Changing the metadata for that many books can take a while. Are you sure?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:743 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:350 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:768 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:361 msgid "Searches" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:898 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:917 msgid "Duplicate search name" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:899 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:918 msgid "The saved search name %s is already used." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1320 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1339 msgid "Find item in tag browser" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1323 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1342 msgid "" "Search for items. This is a \"contains\" search; items containing the\n" "text anywhere in the name will be found. You can limit the search\n" @@ -10804,59 +10912,59 @@ msgid "" "containing the text \"foo\"" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1332 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1351 msgid "ALT+f" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1336 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1355 msgid "F&ind" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1337 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1356 msgid "Find the first/next matching item" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1344 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1363 msgid "Collapse all categories" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1365 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1384 msgid "No More Matches.

Click Find again to go to first match" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1378 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1397 msgid "Sort by name" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1378 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1397 msgid "Sort by popularity" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1379 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1398 msgid "Sort by average rating" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1382 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1401 msgid "Set the sort order for entries in the Tag Browser" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1388 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1407 msgid "Match all" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1388 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1407 msgid "Match any" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1393 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1412 msgid "When selecting multiple entries in the Tag Browser match any or all of them" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1397 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1416 msgid "Manage &user categories" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1400 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1419 msgid "Add your own categories to the Tag Browser" msgstr "" @@ -11144,7 +11252,7 @@ msgid "Options to customize the ebook viewer" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:47 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:729 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:767 msgid "Remember last used window size" msgstr "" @@ -11302,87 +11410,87 @@ msgstr "" msgid "Book format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:190 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:196 msgid "Position in book" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:197 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:203 msgid "Go to a reference. To get reference numbers, use the reference mode." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:205 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:211 msgid "Search for text in book" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:278 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:284 msgid "Print Preview" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:315 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:339 msgid "Connecting to dict.org to lookup: %s…" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:421 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:445 msgid "Choose ebook" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:422 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:446 msgid "Ebooks" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:455 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:482 msgid "No matches found for: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:498 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:525 msgid "Loading flow..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:536 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:563 msgid "Laying out %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:567 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:594 msgid "Bookmark #%d" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:571 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:598 msgid "Add bookmark" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:572 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:599 msgid "Enter title for bookmark:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:582 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:609 msgid "Manage Bookmarks" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:622 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:649 msgid "Loading ebook..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:634 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:661 msgid "Could not open ebook" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:716 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:754 msgid "Options to control the ebook viewer" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:723 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:761 msgid "If specified, viewer window will try to come to the front when started." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:726 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:764 msgid "If specified, viewer window will try to open full screen when started." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:731 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:769 msgid "Print javascript alert and console messages to the console" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:737 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:775 msgid "" "%prog [options] file\n" "\n" @@ -11765,33 +11873,33 @@ msgstr "" msgid "daysago" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/caches.py:548 -#: /home/kovid/work/calibre/src/calibre/library/caches.py:558 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:559 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:569 msgid "unchecked" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/caches.py:548 -#: /home/kovid/work/calibre/src/calibre/library/caches.py:558 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:559 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:569 #: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:192 msgid "no" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/caches.py:551 -#: /home/kovid/work/calibre/src/calibre/library/caches.py:561 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:562 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:572 msgid "checked" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/caches.py:551 -#: /home/kovid/work/calibre/src/calibre/library/caches.py:561 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:562 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:572 #: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:192 msgid "yes" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/caches.py:555 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:566 msgid "blank" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/caches.py:555 +#: /home/kovid/work/calibre/src/calibre/library/caches.py:566 msgid "empty" msgstr "" @@ -12011,28 +12119,34 @@ msgstr "" msgid "No books available to catalog" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/catalog.py:1486 +#: /home/kovid/work/calibre/src/calibre/library/catalog.py:1487 msgid "" +"Inconsistent Author Sort values for\n" +"Author '{0}':\n" +"'{1}' <> '{2}'\n" +"Unable to build MOBI catalog.\n" "\n" -"Inconsistent Author Sort values for Author '{0}':\n" -"'{1}' <> '{2}',\n" -"unable to build catalog.\n" -"\n" -"Select all books by '{0}', apply correct Author Sort value in Edit Metadata dialog,\n" -"then rebuild the catalog.\n" +"Select all books by '{0}', apply correct Author Sort value in Edit Metadata dialog, then rebuild the catalog.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/catalog.py:1687 +#: /home/kovid/work/calibre/src/calibre/library/catalog.py:1504 +msgid "" +"Warning: inconsistent Author Sort values for\n" +"Author '{0}':\n" +"'{1}' <> '{2}'\n" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/catalog.py:1700 msgid "" "No books found to catalog.\n" "Check 'Excluded books' criteria in E-book options.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/catalog.py:1689 +#: /home/kovid/work/calibre/src/calibre/library/catalog.py:1702 msgid "No books available to include in catalog" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/catalog.py:5017 +#: /home/kovid/work/calibre/src/calibre/library/catalog.py:5030 msgid "" "\n" "*** Adding 'By Authors' Section required for MOBI output ***" @@ -12543,19 +12657,19 @@ msgstr "" msgid "%sAverage rating is %3.1f" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:904 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:911 msgid "Main" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2704 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2722 msgid "

Migrating old database to ebook library in %s

" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2733 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2751 msgid "Copying %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2750 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2768 msgid "Compacting database" msgstr "" From 6cd2a567f0e34faa2ce8c02de49d36c6be816d1b Mon Sep 17 00:00:00 2001 From: ldolse Date: Wed, 16 Feb 2011 06:55:39 +0800 Subject: [PATCH 07/12] replace nbsp indents before running smarten punctuation --- src/calibre/ebooks/conversion/preprocess.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py index 6fafbb992e..32efa15b66 100644 --- a/src/calibre/ebooks/conversion/preprocess.py +++ b/src/calibre/ebooks/conversion/preprocess.py @@ -568,11 +568,14 @@ class HTMLPreProcessor(object): def smarten_punctuation(self, html): from calibre.utils.smartypants import smartyPants from calibre.ebooks.chardet import substitute_entites + from calibre.ebooks.conversion.utils import HeuristicProcessor + preprocessor = HeuristicProcessor(self.extra_opts, self.log) from uuid import uuid4 start = 'calibre-smartypants-'+str(uuid4()) stop = 'calibre-smartypants-'+str(uuid4()) html = html.replace('', stop) + html = preprocessor.fix_nbsp_indents(html) html = smartyPants(html) html = html.replace(start, '') From a25a236f324291ae94ea50c58f7a7cf37b93e0d0 Mon Sep 17 00:00:00 2001 From: John Schember Date: Tue, 15 Feb 2011 18:39:00 -0500 Subject: [PATCH 08/12] TXTZ Import: Add metadata.opf file to archive when importing if present or create it if not present. --- src/calibre/customize/builtins.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 87c83eff52..7358b4ccda 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -8,6 +8,7 @@ from calibre.customize import FileTypePlugin, MetadataReaderPlugin, \ MetadataWriterPlugin, PreferencesPlugin, InterfaceActionBase from calibre.constants import numeric_version from calibre.ebooks.metadata.archive import ArchiveExtract, get_cbz_metadata +from calibre.ebooks.metadata.opf2 import metadata_to_opf from calibre.ebooks.oeb.base import OEB_IMAGES # To archive plugins {{{ @@ -134,7 +135,18 @@ class TXT2TXTZ(FileTypePlugin): import zipfile of = self.temporary_file('_plugin_txt2txtz.txtz') txtz = zipfile.ZipFile(of.name, 'w') + # Add selected TXT file to archive. txtz.write(path_to_ebook, os.path.basename(path_to_ebook), zipfile.ZIP_DEFLATED) + # metadat.opf + if os.path.exists(os.path.join(base_dir, 'metadata.opf')): + txtz.write(os.path.join(base_dir, 'metadata.opf'), 'metadata.opf', zipfile.ZIP_DEFLATED) + else: + from calibre.ebooks.metadata.txt import get_metadata + with open(path_to_ebook, 'rb') as ebf: + mi = get_metadata(ebf) + opf = metadata_to_opf(mi) + txtz.writestr('metadata.opf', opf, zipfile.ZIP_DEFLATED) + # images for image in images: txtz.write(os.path.join(base_dir, image), image) txtz.close() From 40d6c59e196970c100df33f7fb7dcce4befe698a Mon Sep 17 00:00:00 2001 From: John Schember Date: Tue, 15 Feb 2011 18:39:48 -0500 Subject: [PATCH 09/12] ... --- src/calibre/customize/builtins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 7358b4ccda..a61ae2cb97 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -137,7 +137,7 @@ class TXT2TXTZ(FileTypePlugin): txtz = zipfile.ZipFile(of.name, 'w') # Add selected TXT file to archive. txtz.write(path_to_ebook, os.path.basename(path_to_ebook), zipfile.ZIP_DEFLATED) - # metadat.opf + # metadata.opf if os.path.exists(os.path.join(base_dir, 'metadata.opf')): txtz.write(os.path.join(base_dir, 'metadata.opf'), 'metadata.opf', zipfile.ZIP_DEFLATED) else: From 6db09a6dc13fe70bf308a9d8d4b87dbcb4a884b9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Feb 2011 19:58:27 -0700 Subject: [PATCH 10/12] ... --- src/calibre/ebooks/metadata/sources/base.py | 44 +++++++++++++++---- src/calibre/ebooks/metadata/sources/google.py | 44 +++++++++++-------- 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py index 89ad8a7956..937245cfa9 100644 --- a/src/calibre/ebooks/metadata/sources/base.py +++ b/src/calibre/ebooks/metadata/sources/base.py @@ -18,14 +18,42 @@ class Source(Plugin): result_of_identify_is_complete = True - def get_author_tokens(self, authors): - 'Take a list of authors and return a list of tokens useful for a ' - 'AND search query' - # Leave ' in there for Irish names - pat = re.compile(r'[-,:;+!@#$%^&*(){}.`~"\s\[\]/]') - for au in authors: - for tok in au.split(): - yield pat.sub('', tok) + def get_author_tokens(self, authors, only_first_author=True): + ''' + Take a list of authors and return a list of tokens useful for an + AND search query. This function tries to return tokens in + first name middle names last name order, by assuming that if a comma is + in the author name, the name is in lastname, other names form. + ''' + + if authors: + # Leave ' in there for Irish names + pat = re.compile(r'[-,:;+!@#$%^&*(){}.`~"\s\[\]/]') + if only_first_author: + authors = authors[:1] + for au in authors: + parts = au.split() + if ',' in au: + # au probably in ln, fn form + parts = parts[1:] + parts[:1] + for tok in parts: + tok = pat.sub('', tok).strip() + yield tok + + + def get_title_tokens(self, title): + ''' + Take a title and return a list of tokens useful for an AND search query. + Excludes connectives and punctuation. + ''' + if title: + pat = re.compile(r'''[-,:;+!@#$%^&*(){}.`~"'\s\[\]/]''') + title = pat.sub(' ', title) + tokens = title.split() + for token in tokens: + token = token.strip() + if token and token.lower() not in ('a', 'and', 'the'): + yield token def split_jobs(self, jobs, num): 'Split a list of jobs into at most num groups, as evenly as possible' diff --git a/src/calibre/ebooks/metadata/sources/google.py b/src/calibre/ebooks/metadata/sources/google.py index d9efb65ae0..7e0e3a0901 100644 --- a/src/calibre/ebooks/metadata/sources/google.py +++ b/src/calibre/ebooks/metadata/sources/google.py @@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en' import time from urllib import urlencode from functools import partial -from threading import Thread +from threading import Thread, RLock from lxml import etree @@ -38,7 +38,7 @@ subject = XPath('descendant::dc:subject') description = XPath('descendant::dc:description') language = XPath('descendant::dc:language') - +_log_lock = RLock() def to_metadata(browser, log, entry_): @@ -50,7 +50,8 @@ def to_metadata(browser, log, entry_): if ans and ans.strip(): return ans.strip() except: - log.exception('Programming error:') + with _log_lock: + log.exception('Programming error:') return None @@ -69,7 +70,8 @@ def to_metadata(browser, log, entry_): feed = etree.fromstring(raw) extra = entry(feed)[0] except: - log.exception('Failed to get additional details for', mi.title) + with _log_lock: + log.exception('Failed to get additional details for', mi.title) return mi mi.comments = get_text(extra, description) @@ -100,7 +102,8 @@ def to_metadata(browser, log, entry_): tags.extend([y.strip() for y in t.split('/')]) tags = list(sorted(list(set(tags)))) except: - log.exception('Failed to parse tags:') + with _log_lock: + log.exception('Failed to parse tags:') tags = [] if tags: mi.tags = [x.replace(',', ';') for x in tags] @@ -112,7 +115,8 @@ def to_metadata(browser, log, entry_): default = utcnow().replace(day=15) mi.pubdate = parse_date(pubdate, assume_utc=True, default=default) except: - log.exception('Failed to parse pubdate') + with _log_lock: + log.exception('Failed to parse pubdate') return mi @@ -132,9 +136,10 @@ class Worker(Thread): if isinstance(ans, Metadata): self.result_queue.put(ans) except: - self.log.exception( - 'Failed to get metadata for identify entry:', - etree.tostring(i)) + with _log_lock: + self.log.exception( + 'Failed to get metadata for identify entry:', + etree.tostring(i)) if self.abort.is_set(): break @@ -153,11 +158,14 @@ class GoogleBooks(Source): elif title or authors: def build_term(prefix, parts): return ' '.join('in'+prefix + ':' + x for x in parts) - if title is not None: - q += build_term('title', title.split()) - if authors: - q += ('+' if q else '')+build_term('author', - self.get_author_tokens(authors)) + title_tokens = list(self.get_title_tokens()) + if title_tokens: + q += build_term('title', title_tokens) + author_tokens = self.get_author_tokens(authors, + only_first_author=True) + if author_tokens: + q += ('+' if q else '') + build_term('author', + author_tokens) if isinstance(q, unicode): q = q.encode('utf-8') @@ -191,25 +199,23 @@ class GoogleBooks(Source): groups = self.split_jobs(entries, 5) # At most 5 threads if not groups: - return + return None workers = [Worker(log, entries, abort, result_queue) for entries in groups] if abort.is_set(): - return + return None for worker in workers: worker.start() has_alive_worker = True while has_alive_worker and not abort.is_set(): + time.sleep(0.1) has_alive_worker = False for worker in workers: if worker.is_alive(): has_alive_worker = True - time.sleep(0.1) return None - - From c0e78379c30b57bf315f51ebde0da6b8813d15f7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Feb 2011 20:34:11 -0700 Subject: [PATCH 11/12] oops --- resources/recipes/credit_slips.recipe | 35 +++++++++++++++++++++ resources/recipes/epl_talk.recipe | 37 ++++++++++++++++++++++ resources/recipes/fan_graphs.recipe | 39 ++++++++++++++++++++++++ resources/recipes/oregonian.recipe | 44 +++++++++++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 resources/recipes/credit_slips.recipe create mode 100644 resources/recipes/epl_talk.recipe create mode 100644 resources/recipes/fan_graphs.recipe create mode 100644 resources/recipes/oregonian.recipe diff --git a/resources/recipes/credit_slips.recipe b/resources/recipes/credit_slips.recipe new file mode 100644 index 0000000000..19e19ca2fb --- /dev/null +++ b/resources/recipes/credit_slips.recipe @@ -0,0 +1,35 @@ +#!/usr/bin/env python +__license__ = 'GPL 3' +__copyright__ = 'zotzot' +__docformat__ = 'restructuredtext en' + +from calibre.web.feeds.news import BasicNewsRecipe + + +class CreditSlips(BasicNewsRecipe): + __license__ = 'GPL v3' + __author__ = 'zotzot' + language = 'en' + version = 1 + title = u'Credit Slips.org' + publisher = u'Bankr-L' + category = u'Economic blog' + description = u'All things about credit.' + cover_url = 'http://bit.ly/hyZSTr' + oldest_article = 50 + max_articles_per_feed = 100 + use_embedded_content = True + + feeds = [ +(u'Credit Slips', u'http://www.creditslips.org/creditslips/atom.xml') +] + conversion_options = { +'comments': description, +'tags': category, +'language': 'en', +'publisher': publisher +} + extra_css = ''' + body{font-family:verdana,arial,helvetica,geneva,sans-serif;} + img {float: left; margin-right: 0.5em;} + ''' diff --git a/resources/recipes/epl_talk.recipe b/resources/recipes/epl_talk.recipe new file mode 100644 index 0000000000..297dffd89c --- /dev/null +++ b/resources/recipes/epl_talk.recipe @@ -0,0 +1,37 @@ +#!/usr/bin/env python +__license__ = 'GPL 3' +__copyright__ = 'zotzot' +__docformat__ = 'restructuredtext en' +''' +http://www.epltalk.com +''' +from calibre.web.feeds.news import BasicNewsRecipe + + +class EPLTalkRecipe(BasicNewsRecipe): + __license__ = 'GPL v3' + __author__ = u'The Gaffer' + language = 'en' + version = 1 + + title = u'EPL Talk' + publisher = u'The Gaffer' + publication_type = 'Blog' + category = u'Soccer' + description = u'News and Analysis from the English Premier League' + cover_url = 'http://bit.ly/hJxZPu' + + oldest_article = 45 + max_articles_per_feed = 150 + use_embedded_content = True + remove_javascript = True + encoding = 'utf8' + + remove_tags_after = [dict(name='div', attrs={'class':'pd-rating'})] + + feeds = [(u'EPL Talk', u'http://feeds.feedburner.com/EPLTalk')] + + extra_css = ''' + body{font-family:verdana,arial,helvetica,geneva,sans-serif;} + img {float: left; margin-right: 0.5em;} + ''' diff --git a/resources/recipes/fan_graphs.recipe b/resources/recipes/fan_graphs.recipe new file mode 100644 index 0000000000..56b8a4e3a4 --- /dev/null +++ b/resources/recipes/fan_graphs.recipe @@ -0,0 +1,39 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2011 zotzot' +__docformat__ = 'PEP8' +''' +www.fangraphs.com +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class FanGraphs(BasicNewsRecipe): + title = u'FanGraphs' + oldest_article = 21 + max_articles_per_feed = 100 + no_stylesheets = True + #delay = 1 + use_embedded_content = False + encoding = 'utf8' + publisher = 'Fangraphs' + category = 'Baseball' + language = 'en' + publication_type = 'Blog' + + description = 'Baseball statistical analysis, graphs, and projections.' + __author__ = 'David Appelman' + cover_url = 'http://bit.ly/g0BTdQ' + + feeds = [ + (u'Fangraphs', u'http://feeds.feedburner.com/FanGraphs?format=xml'), + (u'Rotographs', u'http://www.wizardrss.com/feed/feeds.feedburner.com/RotoGraphs?format=xml'), + (u'Community', u'http://www.wizardrss.com/feed/www.fangraphs.com/community/?feed=rss2'), + (u'NotGraphs', u'http://www.wizardrss.com/feed/www.fangraphs.com/not/?feed=rss2')] + + extra_css = ''' + h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} + h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;} + p{font-family:Arial,Helvetica,sans-serif;font-size:small;} + body{font-family:Helvetica,Arial,sans-serif;font-size:small;} +''' diff --git a/resources/recipes/oregonian.recipe b/resources/recipes/oregonian.recipe new file mode 100644 index 0000000000..1412ae3e53 --- /dev/null +++ b/resources/recipes/oregonian.recipe @@ -0,0 +1,44 @@ +from __future__ import with_statement +__license__ = 'GPL 3' +__copyright__ = 'zotzot' +__docformat__ = 'restructuredtext en' + +from calibre.web.feeds.news import BasicNewsRecipe + + +class Oregonian(BasicNewsRecipe): + title = u'The Oregonian' + oldest_article = 2 + max_articles_per_feed = 100 + language = 'en' + __author__ = 'Zotzot' + description = 'Portland, Oregon local newspaper' + publisher = 'Advance Publications' + category = 'news, Portland' + cover_url = 'http://bit.ly/gUgxGd' + no_stylesheets = True + masthead_url = 'http://bit.ly/eocL70' + remove_tags = [dict(name='div', attrs={'class':['footer', 'content']})] + use_embedded_content = False + remove_tags_before = dict(id='article') + remove_tags_after = dict(id='article') + feeds = [ +#(u'Timbers', u'feed://blog.oregonlive.com/timbers_impact/atom.xml'), +(u'News', u'http://blog.oregonlive.com/news_impact/atom.xml'), +(u'Opinion', u'http://blog.oregonlive.com/opinion_impact/atom.xml'), +(u'Living', u'http://blog.oregonlive.com/living_impact/atom.xml'), +(u'Sports', u'http://blog.oregonlive.com/sports_impact/atom.xml'), +(u'Business', u'http://blog.oregonlive.com/business_impact/atom.xml')] + + extra_css = ''' + h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;} + h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;} + p{font-family:Arial,Helvetica,sans-serif;font-size:small;} + body{font-family:Helvetica,Arial,sans-serif;font-size:small;} +''' + + +def get_article_url(self, article): + url = BasicNewsRecipe.get_article_url(self, article) + if '/video/' not in url: + return url From 2b4016901ef7cfb138f8ee810063af86f680a7e4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Feb 2011 22:17:02 -0700 Subject: [PATCH 12/12] Google books metadata download plugin ported to new infrastructure --- src/calibre/customize/builtins.py | 21 +++-- src/calibre/customize/ui.py | 12 +++ src/calibre/ebooks/metadata/sources/base.py | 19 ++++ src/calibre/ebooks/metadata/sources/google.py | 65 +++++++++---- src/calibre/ebooks/metadata/sources/test.py | 91 +++++++++++++++++++ src/calibre/utils/logging.py | 27 +++++- 6 files changed, 205 insertions(+), 30 deletions(-) create mode 100644 src/calibre/ebooks/metadata/sources/test.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index a61ae2cb97..6cfe915036 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -95,22 +95,22 @@ class TXT2TXTZ(FileTypePlugin): file_types = set(['txt']) supported_platforms = ['windows', 'osx', 'linux'] on_import = True - + def _get_image_references(self, txt, base_dir): images = [] - + # Textile for m in re.finditer(ur'(?mu)(?:[\[{])?\!(?:\. )?(?P[^\s(!]+)\s?(?:\(([^\)]+)\))?\!(?::(\S+))?(?:[\]}]|(?=\s|$))', txt): path = m.group('path') if path and not os.path.isabs(path) and guess_type(path)[0] in OEB_IMAGES and os.path.exists(os.path.join(base_dir, path)): images.append(path) - - # Markdown inline + + # Markdown inline for m in re.finditer(ur'(?mu)\!\[([^\]\[]*(\[[^\]\[]*(\[[^\]\[]*(\[[^\]\[]*(\[[^\]\[]*(\[[^\]\[]*(\[[^\]\[]*\])*[^\]\[]*\])*[^\]\[]*\])*[^\]\[]*\])*[^\]\[]*\])*[^\]\[]*\])*[^\]\[]*)\]\s*\((?P[^\)]*)\)', txt): path = m.group('path') if path and not os.path.isabs(path) and guess_type(path)[0] in OEB_IMAGES and os.path.exists(os.path.join(base_dir, path)): images.append(path) - + # Markdown reference refs = {} for m in re.finditer(ur'(?mu)^(\ ?\ ?\ ?)\[(?P[^\]]*)\]:\s*(?P[^\s]*)$', txt): @@ -123,13 +123,13 @@ class TXT2TXTZ(FileTypePlugin): # Remove duplicates return list(set(images)) - + def run(self, path_to_ebook): with open(path_to_ebook, 'rb') as ebf: txt = ebf.read() base_dir = os.path.dirname(path_to_ebook) images = self._get_image_references(txt, base_dir) - + if images: # Create TXTZ and put file plus images inside of it. import zipfile @@ -1030,3 +1030,10 @@ plugins += [LookAndFeel, Behavior, Columns, Toolbar, Search, InputOptions, Email, Server, Plugins, Tweaks, Misc, TemplateFunctions] #}}} + +# New metadata download plugins {{{ +from calibre.ebooks.metadata.sources.google import GoogleBooks + +plugins += [GoogleBooks] + +# }}} diff --git a/src/calibre/customize/ui.py b/src/calibre/customize/ui.py index c360122842..5f67e23d92 100644 --- a/src/calibre/customize/ui.py +++ b/src/calibre/customize/ui.py @@ -20,6 +20,7 @@ from calibre.ebooks.metadata.fetch import MetadataSource from calibre.utils.config import make_config_dir, Config, ConfigProxy, \ plugin_dir, OptionParser, prefs from calibre.ebooks.epub.fix import ePubFixer +from calibre.ebooks.metadata.sources.base import Source platform = 'linux' if iswindows: @@ -493,6 +494,17 @@ def epub_fixers(): yield plugin # }}} +# Metadata sources2 {{{ +def metadata_plugins(capabilities): + capabilities = frozenset(capabilities) + for plugin in _initialized_plugins: + if isinstance(plugin, Source) and \ + plugin.capabilities.intersection(capabilities) and \ + not is_disabled(plugin): + yield plugin + +# }}} + # Initialize plugins {{{ _initialized_plugins = [] diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py index 937245cfa9..e5490ef56e 100644 --- a/src/calibre/ebooks/metadata/sources/base.py +++ b/src/calibre/ebooks/metadata/sources/base.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' @@ -8,6 +10,12 @@ __docformat__ = 'restructuredtext en' import re from calibre.customize import Plugin +from calibre.utils.logging import ThreadSafeLog, FileStream + +def create_log(ostream=None): + log = ThreadSafeLog(level=ThreadSafeLog.DEBUG) + log.outputs = [FileStream(ostream)] + return log class Source(Plugin): @@ -18,6 +26,11 @@ class Source(Plugin): result_of_identify_is_complete = True + capabilities = frozenset() + + touched_fields = frozenset() + + # Utility functions {{{ def get_author_tokens(self, authors, only_first_author=True): ''' Take a list of authors and return a list of tokens useful for an @@ -68,6 +81,10 @@ class Source(Plugin): gr.append(job) return [g for g in groups if g] + # }}} + + # Metadata API {{{ + def identify(self, log, result_queue, abort, title=None, authors=None, identifiers={}): ''' Identify a book by its title/author/isbn/etc. @@ -87,3 +104,5 @@ class Source(Plugin): ''' return None + # }}} + diff --git a/src/calibre/ebooks/metadata/sources/google.py b/src/calibre/ebooks/metadata/sources/google.py index 7e0e3a0901..fbc3aaa226 100644 --- a/src/calibre/ebooks/metadata/sources/google.py +++ b/src/calibre/ebooks/metadata/sources/google.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' @@ -8,12 +10,13 @@ __docformat__ = 'restructuredtext en' import time from urllib import urlencode from functools import partial -from threading import Thread, RLock +from threading import Thread from lxml import etree -from calibre.ebooks.metadata.sources import Source +from calibre.ebooks.metadata.sources.base import Source from calibre.ebooks.metadata.book.base import Metadata +from calibre.ebooks.chardet import xml_to_unicode from calibre.utils.date import parse_date, utcnow from calibre import browser, as_unicode @@ -38,7 +41,18 @@ subject = XPath('descendant::dc:subject') description = XPath('descendant::dc:description') language = XPath('descendant::dc:language') -_log_lock = RLock() +def get_details(browser, url): + try: + raw = browser.open_novisit(url).read() + except Exception as e: + gc = getattr(e, 'getcode', lambda : -1) + if gc() != 403: + raise + # Google is throttling us, wait a little + time.sleep(2) + raw = browser.open_novisit(url).read() + + return raw def to_metadata(browser, log, entry_): @@ -50,8 +64,7 @@ def to_metadata(browser, log, entry_): if ans and ans.strip(): return ans.strip() except: - with _log_lock: - log.exception('Programming error:') + log.exception('Programming error:') return None @@ -66,12 +79,11 @@ def to_metadata(browser, log, entry_): mi = Metadata(title_, authors) try: - raw = browser.open_novisit(id_url).read() - feed = etree.fromstring(raw) + raw = get_details(browser, id_url) + feed = etree.fromstring(xml_to_unicode(raw, strip_encoding_pats=True)[0]) extra = entry(feed)[0] except: - with _log_lock: - log.exception('Failed to get additional details for', mi.title) + log.exception('Failed to get additional details for', mi.title) return mi mi.comments = get_text(extra, description) @@ -102,8 +114,7 @@ def to_metadata(browser, log, entry_): tags.extend([y.strip() for y in t.split('/')]) tags = list(sorted(list(set(tags)))) except: - with _log_lock: - log.exception('Failed to parse tags:') + log.exception('Failed to parse tags:') tags = [] if tags: mi.tags = [x.replace(',', ';') for x in tags] @@ -115,8 +126,7 @@ def to_metadata(browser, log, entry_): default = utcnow().replace(day=15) mi.pubdate = parse_date(pubdate, assume_utc=True, default=default) except: - with _log_lock: - log.exception('Failed to parse pubdate') + log.exception('Failed to parse pubdate') return mi @@ -136,10 +146,9 @@ class Worker(Thread): if isinstance(ans, Metadata): self.result_queue.put(ans) except: - with _log_lock: - self.log.exception( - 'Failed to get metadata for identify entry:', - etree.tostring(i)) + self.log.exception( + 'Failed to get metadata for identify entry:', + etree.tostring(i)) if self.abort.is_set(): break @@ -147,6 +156,11 @@ class Worker(Thread): class GoogleBooks(Source): name = 'Google Books' + description = _('Downloads metadata from Google Books') + + capabilities = frozenset(['identify']) + touched_fields = frozenset(['title', 'authors', 'isbn', 'tags', 'pubdate', + 'comments', 'publisher', 'author_sort']) # language currently disabled def create_query(self, log, title=None, authors=None, identifiers={}, start_index=1): @@ -158,7 +172,7 @@ class GoogleBooks(Source): elif title or authors: def build_term(prefix, parts): return ' '.join('in'+prefix + ':' + x for x in parts) - title_tokens = list(self.get_title_tokens()) + title_tokens = list(self.get_title_tokens(title)) if title_tokens: q += build_term('title', title_tokens) author_tokens = self.get_author_tokens(authors, @@ -190,7 +204,8 @@ class GoogleBooks(Source): try: parser = etree.XMLParser(recover=True, no_network=True) - feed = etree.fromstring(raw, parser=parser) + feed = etree.fromstring(xml_to_unicode(raw, + strip_encoding_pats=True)[0], parser=parser) entries = entry(feed) except Exception, e: log.exception('Failed to parse identify results') @@ -218,4 +233,14 @@ class GoogleBooks(Source): return None - +if __name__ == '__main__': + # To run these test use: calibre-debug -e src/calibre/ebooks/metadata/sources/google.py + from calibre.ebooks.metadata.sources.test import (test_identify_plugin, + isbn_test) + test_identify_plugin(GoogleBooks.name, + [ + ( + {'title': 'Great Expectations', 'authors':['Charles Dickens']}, + [isbn_test('9781607541592')] + ), + ]) diff --git a/src/calibre/ebooks/metadata/sources/test.py b/src/calibre/ebooks/metadata/sources/test.py new file mode 100644 index 0000000000..3892f31623 --- /dev/null +++ b/src/calibre/ebooks/metadata/sources/test.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2011, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import os, tempfile +from Queue import Queue, Empty +from threading import Event + + +from calibre.customize.ui import metadata_plugins +from calibre import prints +from calibre.ebooks.metadata import check_isbn +from calibre.ebooks.metadata.sources.base import create_log + +def isbn_test(isbn): + isbn_ = check_isbn(isbn) + + def test(mi): + misbn = check_isbn(mi.isbn) + return misbn and misbn == isbn_ + + return test + +def test_identify_plugin(name, tests): + ''' + :param name: Plugin name + :param tests: List of 2-tuples. Each two tuple is of the form (args, + test_funcs). args is a dict of keyword arguments to pass to + the identify method. test_funcs are callables that accept a + Metadata object and return True iff the object passes the + test. + ''' + plugin = None + for x in metadata_plugins(['identify']): + if x.name == name: + plugin = x + break + prints('Testing the identify function of', plugin.name) + + tdir = tempfile.gettempdir() + lf = os.path.join(tdir, plugin.name.replace(' ', '')+'_identify_test.txt') + log = create_log(open(lf, 'wb')) + abort = Event() + prints('Log saved to', lf) + + for kwargs, test_funcs in tests: + prints('Running test with:', kwargs) + rq = Queue() + args = (log, rq, abort) + err = plugin.identify(*args, **kwargs) + if err is not None: + prints('identify returned an error for args', args) + prints(err) + break + + results = [] + while True: + try: + results.append(rq.get_nowait()) + except Empty: + break + + prints('Found', len(results), 'matches:') + + for mi in results: + prints(mi) + prints('\n\n') + + match_found = None + for mi in results: + test_failed = False + for tfunc in test_funcs: + if not tfunc(mi): + test_failed = True + break + if not test_failed: + match_found = mi + break + + if match_found is None: + prints('ERROR: No results that passed all tests were found') + prints('Log saved to', lf) + raise SystemExit(1) + + prints('Log saved to', lf) + diff --git a/src/calibre/utils/logging.py b/src/calibre/utils/logging.py index 98c7da178e..e7d8774b85 100644 --- a/src/calibre/utils/logging.py +++ b/src/calibre/utils/logging.py @@ -10,17 +10,19 @@ INFO = 1 WARN = 2 ERROR = 3 -import sys, traceback +import sys, traceback, cStringIO from functools import partial - +from threading import RLock class Stream(object): - def __init__(self, stream): + def __init__(self, stream=None): from calibre import prints self._prints = partial(prints, safe_encode=True) + if stream is None: + stream = cStringIO.StringIO() self.stream = stream def flush(self): @@ -50,6 +52,15 @@ class ANSIStream(Stream): def flush(self): self.stream.flush() +class FileStream(Stream): + + def __init__(self, stream=None): + Stream.__init__(self, stream) + + def prints(self, level, *args, **kwargs): + kwargs['file'] = self.stream + self._prints(*args, **kwargs) + class HTMLStream(Stream): def __init__(self, stream=sys.stdout): @@ -103,4 +114,14 @@ class Log(object): def __call__(self, *args, **kwargs): self.prints(INFO, *args, **kwargs) +class ThreadSafeLog(Log): + + def __init__(self, level=Log.INFO): + Log.__init__(self, level=level) + self._lock = RLock() + + def prints(self, *args, **kwargs): + with self._lock: + Log.prints(self, *args, **kwargs) + default_log = Log()