From eea052b5d2fee9fbdfa1850773f18216a3822ea8 Mon Sep 17 00:00:00 2001 From: GRiker Date: Tue, 15 Feb 2011 09:32:02 -0700 Subject: [PATCH 01/24] catalog author_sort error handling improved, improved OPF search for --- src/calibre/devices/apple/driver.py | 37 +++++++++++++------------- src/calibre/library/catalog.py | 41 +++++++++++++++++++---------- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 369c470e2b..96768209c1 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -24,6 +24,7 @@ from calibre.utils.logging import Log from calibre.utils.zipfile import ZipFile from PIL import Image as PILImage +from lxml import etree if isosx: try: @@ -2514,23 +2515,23 @@ class ITUNES(DriverBase): fnames = zf_opf.namelist() opf = [x for x in fnames if '.opf' in x][0] if opf: - opf_raw = cStringIO.StringIO(zf_opf.read(opf)) - soup = BeautifulSoup(opf_raw.getvalue()) - opf_raw.close() - - # Touch existing calibre timestamp - md = soup.find('metadata') - if md: - ts = md.find('meta',attrs={'name':'calibre:timestamp'}) - if ts: - timestamp = ts['content'] - old_ts = parse_date(timestamp) - metadata.timestamp = datetime.datetime(old_ts.year, old_ts.month, old_ts.day, old_ts.hour, - old_ts.minute, old_ts.second, old_ts.microsecond+1, old_ts.tzinfo) - else: - metadata.timestamp = now() - if DEBUG: - self.log.info(" add timestamp: %s" % metadata.timestamp) + opf_tree = etree.fromstring(zf_opf.read(opf)) + ns_map = opf_tree.nsmap.keys() + for item in ns_map: + ns = opf_tree.nsmap[item] + md_el = opf_tree.find(".//{%s}metadata" % ns) + if md_el is not None: + ts = md_el.find('.//{%s}meta[@name="calibre:timestamp"]') + if ts: + timestamp = ts.get('content') + old_ts = parse_date(timestamp) + metadata.timestamp = datetime.datetime(old_ts.year, old_ts.month, old_ts.day, old_ts.hour, + old_ts.minute, old_ts.second, old_ts.microsecond+1, old_ts.tzinfo) + else: + metadata.timestamp = now() + if DEBUG: + self.log.info(" add timestamp: %s" % metadata.timestamp) + break else: metadata.timestamp = now() if DEBUG: @@ -2838,7 +2839,7 @@ class ITUNES(DriverBase): def _xform_metadata_via_plugboard(self, book, format): ''' Transform book metadata from plugboard templates ''' if DEBUG: - self.log.info(" ITUNES._update_metadata_from_plugboard()") + self.log.info(" ITUNES._xform_metadata_via_plugboard()") if self.plugboard_func: pb = self.plugboard_func(self.DEVICE_PLUGBOARD_NAME, format, self.plugboards) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index cb55b2318d..f3640af4f0 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -1481,23 +1481,36 @@ class EPUB_MOBI(CatalogPlugin): current_author = authors[0] for (i,author) in enumerate(authors): if author != current_author and i: - # Exit if author matches previous, but author_sort doesn't match if author[0] == current_author[0]: - error_msg = _(''' -Inconsistent Author Sort values for Author '{0}': -'{1}' <> '{2}', -unable to build catalog.\n -Select all books by '{0}', apply correct Author Sort value in Edit Metadata dialog, -then rebuild the catalog.\n''').format(author[0],author[1],current_author[1]) - self.opts.log.warn('\n*** Metadata error ***') - self.opts.log.warn(error_msg) + if self.opts.fmt == 'mobi': + # Exit if building MOBI + error_msg = _( +'''Inconsistent Author Sort values for +Author '{0}': +'{1}' <> '{2}' +Unable to build MOBI catalog.\n +Select all books by '{0}', apply correct Author Sort value in Edit Metadata dialog, then rebuild the catalog.\n''').format(author[0],author[1],current_author[1]) + self.opts.log.warn('\n*** Metadata error ***') + self.opts.log.warn(error_msg) + + self.error.append('Author Sort mismatch') + self.error.append(error_msg) + return False + else: + # Warning if building non-MOBI + if not self.error: + self.error.append('Author Sort mismatch') + + error_msg = _( +'''Warning: inconsistent Author Sort values for +Author '{0}': +'{1}' <> '{2}'\n''').format(author[0],author[1],current_author[1]) + self.opts.log.warn('\n*** Metadata warning ***') + self.opts.log.warn(error_msg) + self.error.append(error_msg) - self.error.append('Metadata error') - self.error.append(error_msg) - return False current_author = author - self.booksByAuthor = sorted(self.booksByAuthor, key=self.booksByAuthorSorter_author_sort) # Build the unique_authors set from existing data @@ -2135,7 +2148,7 @@ then rebuild the catalog.\n''').format(author[0],author[1],current_author[1]) if author_count == 1: divOpeningTag.insert(dotc, pBookTag) dotc += 1 - else: + elif divRunningTag: divRunningTag.insert(drtc,pBookTag) drtc += 1 From d7024023808f635478ca81c5654019666162d3d8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Feb 2011 10:56:44 -0700 Subject: [PATCH 02/24] 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 03/24] 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 2a05a3b7897c73f32786e9d4517498bc9d3690a4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Feb 2011 11:37:41 -0700 Subject: [PATCH 04/24] ... --- 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 05/24] 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 06/24] 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 07/24] ... --- 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 08/24] ... --- 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 09/24] 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 10/24] 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() From 0083f92f9e074b643bf89f7f669a6ee5cc708a37 Mon Sep 17 00:00:00 2001 From: GRiker Date: Wed, 16 Feb 2011 05:10:15 -0700 Subject: [PATCH 11/24] using local-name()= --- src/calibre/devices/apple/driver.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 5a0496e863..8d8f7c378f 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -2517,6 +2517,26 @@ class ITUNES(DriverBase): opf = [x for x in fnames if '.opf' in x][0] if opf: opf_tree = etree.fromstring(zf_opf.read(opf)) + md_el = opf_tree.xpath('.//*[local-name()="metadata"]')[0] + if md_el is not None: + ts = md_el.find('.//*[@name="calibre:timestamp"]') + if ts is not None: + timestamp = ts.get('content') + old_ts = parse_date(timestamp) + metadata.timestamp = datetime.datetime(old_ts.year, old_ts.month, old_ts.day, old_ts.hour, + old_ts.minute, old_ts.second, old_ts.microsecond+1, old_ts.tzinfo) + if DEBUG: + self.log.info(" existing timestamp: %s" % metadata.timestamp) + else: + metadata.timestamp = now() + if DEBUG: + self.log.info(" add timestamp: %s" % metadata.timestamp) + else: + metadata.timestamp = now() + if DEBUG: + self.log.warning(" missing block in OPF file") + self.log.info(" add timestamp: %s" % metadata.timestamp) + ''' ns_map = opf_tree.nsmap.keys() for item in ns_map: ns = opf_tree.nsmap[item] @@ -2538,7 +2558,7 @@ class ITUNES(DriverBase): if DEBUG: self.log.warning(" missing block in OPF file") self.log.info(" add timestamp: %s" % metadata.timestamp) - + ''' # Force the language declaration for iBooks 1.1 #metadata.language = get_lang().replace('_', '-') From 9e5d8ee682a13e2a7cdaeb278916c3171e3abc0c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 10:11:11 -0700 Subject: [PATCH 12/24] ... --- src/calibre/ebooks/mobi/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index b3f8160e3a..0c33dffef2 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -1818,7 +1818,7 @@ class MobiWriter(object): text = text.strip() if not isinstance(text, unicode): text = text.decode('utf-8', 'replace') - text = text.encode('utf-8') + text = normalize(text).encode('utf-8') else : text = "(none)".encode('utf-8') return text From fb28d0392d104244c95645288136df31f7952fef Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 10:17:18 -0700 Subject: [PATCH 13/24] Media Indonesia by bakthariq --- resources/recipes/mediaindonesia.recipe | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 resources/recipes/mediaindonesia.recipe diff --git a/resources/recipes/mediaindonesia.recipe b/resources/recipes/mediaindonesia.recipe new file mode 100644 index 0000000000..aceaca6042 --- /dev/null +++ b/resources/recipes/mediaindonesia.recipe @@ -0,0 +1,40 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2011, bakthariq AT gmail.com' +''' +m.mediaindonesia.com +''' + +from calibre.web.feeds.recipes import BasicNewsRecipe + + +class Media(BasicNewsRecipe): + title = u'Media Indonesia' + __author__ = 'bakthariq' + oldest_article = 1 + max_articles_per_feed = 500 + timefmt = ' [%a, %b %d, %Y]' + language = 'id' + category = 'News, Indonesia' + publisher = 'Media Indonesia' + encoding = 'utf-8' + no_stylesheets = True + description = 'Indonesian Media Indonesia newsportal' + cover_url = 'http://m.mediaindonesia.com/public/img/logo.gif' + no_javascript = True + + remove_tags = [dict(id=['atas','merah','putih']), dict(name='a')] + remove_tags_after = [dict(id="putih")] + + extra_css = ''' + .judul {font-size: x-large;} + .tgl {font-size: x-small;color:#333333;} + .foto {font-size: x-small;} + ''' + + feeds = [(u'Polhukam', u'http://www.mediaindonesia.com/rss/1/polhukam'), (u'Ekonomi dan Bisnis', u'http://www.mediaindonesia.com/rss/2/ekonomi-dan-bisnis'), +(u'Internasional', u'http://www.mediaindonesia.com/rss/6/internasional'), (u'Olahraga', u'http://www.mediaindonesia.com/rss/3/olahraga'),(u'Sepakbola', +u'http://www.mediaindonesia.com/rss/4/sepakbola'),(u'Megapolitan', u'http://www.mediaindonesia.com/rss/5/megapolitan'), (u'Sains dan Teknologi', +u'http://www.mediaindonesia.com/rss/7/sains-dan-teknologi'), (u'Humaniora', u'http://www.mediaindonesia.com/rss/14/humaniora'), (u'Hiburan', +u'http://www.mediaindonesia.com/rss/10/hiburan'), (u'Opini', u'http://www.mediaindonesia.com/rss/11/opini')] + From ab9da85031c8a0a313d8936cd6d80ad123242dcd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 11:29:38 -0700 Subject: [PATCH 14/24] Fix regresion that caused calibre to loose track of whether the content server is runing if the content server is started from the preferences --- src/calibre/gui2/actions/device.py | 22 +++++++++++++++--- src/calibre/gui2/preferences/server.py | 32 ++++++++++++++++++-------- src/calibre/gui2/ui.py | 5 ++-- src/calibre/library/server/base.py | 8 +++++++ 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/calibre/gui2/actions/device.py b/src/calibre/gui2/actions/device.py index 429bc641d0..0b0492228e 100644 --- a/src/calibre/gui2/actions/device.py +++ b/src/calibre/gui2/actions/device.py @@ -7,13 +7,14 @@ __docformat__ = 'restructuredtext en' from functools import partial -from PyQt4.Qt import QToolButton, QMenu, pyqtSignal, QIcon +from PyQt4.Qt import QToolButton, QMenu, pyqtSignal, QIcon, QTimer from calibre.gui2.actions import InterfaceAction from calibre.utils.smtp import config as email_config from calibre.constants import iswindows, isosx from calibre.customize.ui import is_disabled from calibre.devices.bambook.driver import BAMBOOK +from calibre.gui2 import info_dialog class ShareConnMenu(QMenu): # {{{ @@ -169,5 +170,20 @@ class ConnectShareAction(InterfaceAction): if self.gui.content_server is None: self.gui.start_content_server() else: - self.gui.content_server.exit() - self.gui.content_server = None + self.gui.content_server.threaded_exit() + self.stopping_msg = info_dialog(self.gui, _('Stopping'), + _('Stopping server, this could take upto a minute, please wait...'), + show_copy_button=False) + QTimer.singleShot(1000, self.check_exited) + + def check_exited(self): + if self.gui.content_server.is_running: + QTimer.singleShot(20, self.check_exited) + if not self.stopping_msg.isVisible(): + self.stopping_msg.exec_() + return + + + self.gui.content_server = None + self.stopping_msg.accept() + diff --git a/src/calibre/gui2/preferences/server.py b/src/calibre/gui2/preferences/server.py index 4db1244d75..82519f17cd 100644 --- a/src/calibre/gui2/preferences/server.py +++ b/src/calibre/gui2/preferences/server.py @@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en' import time from PyQt4.Qt import Qt, QUrl, QDialog, QSize, QVBoxLayout, QLabel, \ - QPlainTextEdit, QDialogButtonBox + QPlainTextEdit, QDialogButtonBox, QTimer from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.server_ui import Ui_Form @@ -16,7 +16,8 @@ from calibre.utils.search_query_parser import saved_searches from calibre.library.server import server_config from calibre.utils.config import ConfigProxy from calibre.gui2 import error_dialog, config, open_url, warning_dialog, \ - Dispatcher + Dispatcher, info_dialog +from calibre import as_unicode class ConfigWidget(ConfigWidgetBase, Ui_Form): @@ -67,25 +68,36 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): def start_server(self): self.set_server_options() - from calibre.library.server.main import start_threaded_server - self.server = start_threaded_server(self.db, server_config().parse()) - while not self.server.is_running and self.server.exception is None: + self.gui.start_content_server(check_started=False) + while not self.gui.content_server.is_running and self.gui.content_server.exception is None: time.sleep(1) - if self.server.exception is not None: + if self.gui.content_server.exception is not None: error_dialog(self, _('Failed to start content server'), - unicode(self.server.exception)).exec_() + as_unicode(self.gui.content_server.exception)).exec_() return self.start_button.setEnabled(False) self.test_button.setEnabled(True) self.stop_button.setEnabled(True) def stop_server(self): - from calibre.library.server.main import stop_threaded_server - stop_threaded_server(self.server) - self.server = None + self.gui.content_server.threaded_exit() + self.stopping_msg = info_dialog(self, _('Stopping'), + _('Stopping server, this could take upto a minute, please wait...'), + show_copy_button=False) + QTimer.singleShot(500, self.check_exited) + + def check_exited(self): + if self.gui.content_server.is_running: + QTimer.singleShot(20, self.check_exited) + if not self.stopping_msg.isVisible(): + self.stopping_msg.exec_() + return + + self.gui.content_server = None self.start_button.setEnabled(True) self.test_button.setEnabled(False) self.stop_button.setEnabled(False) + self.stopping_msg.accept() def test_server(self): open_url(QUrl('http://127.0.0.1:'+str(self.opt_port.value()))) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 5ac7e6a45d..4ac92e7806 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -311,7 +311,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ def esc(self, *args): self.search.clear() - def start_content_server(self): + def start_content_server(self, check_started=True): from calibre.library.server.main import start_threaded_server from calibre.library.server import server_config self.content_server = start_threaded_server( @@ -319,7 +319,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ self.content_server.state_callback = Dispatcher( self.iactions['Connect Share'].content_server_state_changed) self.content_server.state_callback(True) - self.test_server_timer = QTimer.singleShot(10000, self.test_server) + if check_started: + QTimer.singleShot(10000, self.test_server) def resizeEvent(self, ev): MainWindow.resizeEvent(self, ev) diff --git a/src/calibre/library/server/base.py b/src/calibre/library/server/base.py index b4f2b663a5..83d395dec5 100644 --- a/src/calibre/library/server/base.py +++ b/src/calibre/library/server/base.py @@ -120,6 +120,8 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache, self.set_database(db) + st = 0.1 if opts.develop else 1 + cherrypy.config.update({ 'log.screen' : opts.develop, 'engine.autoreload_on' : getattr(opts, @@ -131,6 +133,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache, 'server.socket_port' : opts.port, 'server.socket_timeout' : opts.timeout, #seconds 'server.thread_pool' : opts.thread_pool, # number of threads + 'server.shutdown_timeout': st, # minutes }) if embedded or wsgi: cherrypy.config.update({'engine.SIGHUP' : None, @@ -241,4 +244,9 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache, except: pass + def threaded_exit(self): + from threading import Thread + t = Thread(target=self.exit) + t.daemon = True + t.start() From ec6bee57e37fc036e271d96983127cac4f527510 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 12:21:25 -0700 Subject: [PATCH 15/24] ... --- src/calibre/library/server/browse.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index a18def29de..c1903d0bdf 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -690,8 +690,9 @@ class BrowseServer(object): if m['is_custom'] and field not in displayed_custom_fields: continue if m['datatype'] == 'comments' or field == 'comments': - comments.append((m['name'], comments_to_html(mi.get(field, - '')))) + val = mi.get(field, '') + if val and val.strip(): + comments.append((m['name'], comments_to_html(val))) continue if field in ('title', 'formats') or not args.get(field, False) \ or not m['name']: From a904d5d192cd14debd808a62ab112e3c56e37321 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 13:30:31 -0700 Subject: [PATCH 16/24] Content server: Make the authors/series/tags/publisher int he book details box clickable so that similar books can be found easily. Fixes #8929 (Improve Content Server usability) --- resources/content_server/browse/browse.css | 9 +++++ src/calibre/library/server/browse.py | 39 ++++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css index 75832e47f9..2c50853ae9 100644 --- a/resources/content_server/browse/browse.css +++ b/resources/content_server/browse/browse.css @@ -464,5 +464,14 @@ h2.library_name { max-height: 50%; } +.details a.details_category_link { + text-decoration: none; + color: blue +} + +.details a.details_category_link:hover { + color: red +} + /* }}} */ diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index c1903d0bdf..2749f0651f 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -584,7 +584,7 @@ class BrowseServer(object): title=_('Books in') + " " +category_name, script='booklist(%s);'%hide_sort, main=html) - def browse_get_book_args(self, mi, id_): + def browse_get_book_args(self, mi, id_, add_category_links=False): fmts = self.db.formats(id_, index_is_id=True) if not fmts: fmts = '' @@ -596,11 +596,43 @@ class BrowseServer(object): fmt = None args = {'id':id_, 'mi':mi, } + ccache = self.categories_cache() if add_category_links else {} for key in mi.all_field_keys(): val = mi.format_field(key)[1] if not val: val = '' - args[key] = xml(val, True) + if add_category_links: + added_key = False + if val and key in ('authors', 'publisher', 'series', 'tags'): + categories = mi.get(key) + if isinstance(categories, basestring): + categories = [categories] + dbtags = [] + for category in categories: + dbtag = None + for tag in ccache[key]: + if tag.name == category: + dbtag = tag + break + dbtags.append(dbtag) + if None not in dbtags: + vals = [] + for tag in dbtags: + tval = ('{2}') + href='/browse/matches/%s/%s' % \ + (quote(tag.category), quote(str(tag.id))) + vals.append(tval.format(xml(tag.name, True), + xml(href, True), + xml(val if len(dbtags) == 1 else tag.name), + xml(key, True))) + join = ' & ' if key == 'authors' else ', ' + args[key] = join.join(vals) + added_key = True + if not added_key: + args[key] = xml(val, True) + else: + args[key] = xml(val, True) fname = quote(ascii_filename(args['title']) + ' - ' + ascii_filename(args['authors'])) return args, fmt, fmts, fname @@ -674,7 +706,8 @@ class BrowseServer(object): except: return _('This book has been deleted') else: - args, fmt, fmts, fname = self.browse_get_book_args(mi, id_) + args, fmt, fmts, fname = self.browse_get_book_args(mi, id_, + add_category_links=True) args['formats'] = '' if fmts: ofmts = [u'{3}'\ From fd2e3db07abf0541d3750ed21bfaa39f40d19561 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 14:34:09 -0700 Subject: [PATCH 17/24] When trying to detect the encoding of html, do not use more than the first 10KB so that detection is not too slow --- src/calibre/ebooks/chardet/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/chardet/__init__.py b/src/calibre/ebooks/chardet/__init__.py index f9bca3c8d4..10cd0a7112 100644 --- a/src/calibre/ebooks/chardet/__init__.py +++ b/src/calibre/ebooks/chardet/__init__.py @@ -53,7 +53,7 @@ _CHARSET_ALIASES = { "macintosh" : "mac-roman", def force_encoding(raw, verbose, assume_utf8=False): from calibre.constants import preferred_encoding try: - chardet = detect(raw) + chardet = detect(raw[:1024*10]) except: chardet = {'encoding':preferred_encoding, 'confidence':0} encoding = chardet['encoding'] From 32c1415aae40e509951d561748704ba7c483f5b8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 14:37:02 -0700 Subject: [PATCH 18/24] Make that 50KB --- src/calibre/ebooks/chardet/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/chardet/__init__.py b/src/calibre/ebooks/chardet/__init__.py index 10cd0a7112..571ceafe53 100644 --- a/src/calibre/ebooks/chardet/__init__.py +++ b/src/calibre/ebooks/chardet/__init__.py @@ -53,7 +53,7 @@ _CHARSET_ALIASES = { "macintosh" : "mac-roman", def force_encoding(raw, verbose, assume_utf8=False): from calibre.constants import preferred_encoding try: - chardet = detect(raw[:1024*10]) + chardet = detect(raw[:1024*50]) except: chardet = {'encoding':preferred_encoding, 'confidence':0} encoding = chardet['encoding'] From d6c25bf7f6e7b55624930a7115e0cc8f0aaa4bae Mon Sep 17 00:00:00 2001 From: John Schember Date: Wed, 16 Feb 2011 17:39:12 -0500 Subject: [PATCH 19/24] Fix bug #8986: Italicize patterns causing mess up html tags. --- src/calibre/ebooks/conversion/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py index 2e26f927f5..3559f13440 100644 --- a/src/calibre/ebooks/conversion/utils.py +++ b/src/calibre/ebooks/conversion/utils.py @@ -157,7 +157,7 @@ class HeuristicProcessor(object): ITALICIZE_STYLE_PATS = [ r'(?msu)(?<=[\s>])_(?P[^_]+)_', - r'(?msu)(?<=[\s>])/(?P[^/]+)/', + r'(?msu)(?<=[\s>])/(?P[^/\*>]+)/', r'(?msu)(?<=[\s>])~~(?P[^~]+)~~', r'(?msu)(?<=[\s>])\*(?P[^\*]+)\*', r'(?msu)(?<=[\s>])~(?P[^~]+)~', From 5ec3d6e5ee6e8990a69a2a2f38ecab69f6db89e8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 16:03:38 -0700 Subject: [PATCH 20/24] ... --- src/calibre/ebooks/metadata/sources/amazon.py | 22 +++++++++++++++++++ src/calibre/ebooks/metadata/sources/google.py | 5 ++--- src/calibre/ebooks/metadata/sources/test.py | 3 ++- 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 src/calibre/ebooks/metadata/sources/amazon.py diff --git a/src/calibre/ebooks/metadata/sources/amazon.py b/src/calibre/ebooks/metadata/sources/amazon.py new file mode 100644 index 0000000000..88ac1213c5 --- /dev/null +++ b/src/calibre/ebooks/metadata/sources/amazon.py @@ -0,0 +1,22 @@ +#!/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' + + +from calibre.ebooks.metadata.sources.base import Source + +class Amazon(Source): + + name = 'Amazon' + description = _('Downloads metadata from Amazon') + + capabilities = frozenset(['identify', 'cover']) + touched_fields = frozenset(['title', 'authors', 'isbn', 'pubdate', + 'comments', 'cover_data']) + + diff --git a/src/calibre/ebooks/metadata/sources/google.py b/src/calibre/ebooks/metadata/sources/google.py index fbc3aaa226..c59bbe6dc5 100644 --- a/src/calibre/ebooks/metadata/sources/google.py +++ b/src/calibre/ebooks/metadata/sources/google.py @@ -162,8 +162,7 @@ class GoogleBooks(Source): 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): + def create_query(self, log, title=None, authors=None, identifiers={}): BASE_URL = 'http://books.google.com/books/feeds/volumes?' isbn = identifiers.get('isbn', None) q = '' @@ -188,7 +187,7 @@ class GoogleBooks(Source): return BASE_URL+urlencode({ 'q':q, 'max-results':20, - 'start-index':start_index, + 'start-index':1, 'min-viewability':'none', }) diff --git a/src/calibre/ebooks/metadata/sources/test.py b/src/calibre/ebooks/metadata/sources/test.py index 3892f31623..cd7e7ab6e8 100644 --- a/src/calibre/ebooks/metadata/sources/test.py +++ b/src/calibre/ebooks/metadata/sources/test.py @@ -87,5 +87,6 @@ def test_identify_plugin(name, tests): prints('Log saved to', lf) raise SystemExit(1) - prints('Log saved to', lf) + if os.stat(lf).st_size > 10: + prints('There were some errors, see log', lf) From 7234c3e5f2ea183a37d2fb06b9b185e3db74b361 Mon Sep 17 00:00:00 2001 From: John Schember Date: Wed, 16 Feb 2011 20:28:09 -0500 Subject: [PATCH 21/24] PML Input: Fix bug #9009: TOC entries specified by \x and \X not being included in TOC. --- src/calibre/ebooks/pml/pmlconverter.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/calibre/ebooks/pml/pmlconverter.py b/src/calibre/ebooks/pml/pmlconverter.py index d55a45ee94..04813888ce 100644 --- a/src/calibre/ebooks/pml/pmlconverter.py +++ b/src/calibre/ebooks/pml/pmlconverter.py @@ -151,8 +151,8 @@ class PML_HTMLizer(object): def prepare_pml(self, pml): # Give Chapters the form \\*='text'text\\*. This is used for generating # the TOC later. - pml = re.sub(r'(?<=\\x)(?P.*?)(?=\\x)', lambda match: '="%s"%s' % (self.strip_pml(match.group('text')), match.group('text')), pml) - pml = re.sub(r'(?<=\\X[0-4])(?P.*?)(?=\\X[0-4])', lambda match: '="%s"%s' % (self.strip_pml(match.group('text')), match.group('text')), pml) + pml = re.sub(r'(?msu)(?P\\x)(?P.*?)(?P=c)', lambda match: '%s="%s"%s%s' % (match.group('c'), self.strip_pml(match.group('text')), match.group('text'), match.group('c')), pml) + pml = re.sub(r'(?msu)(?P\\X[0-4])(?P.*?)(?P=c)', lambda match: '%s="%s"%s%s' % (match.group('c'), self.strip_pml(match.group('text')), match.group('text'), match.group('c')), pml) # Remove comments pml = re.sub(r'(?mus)\\v(?P.*?)\\v', '', pml) @@ -190,9 +190,10 @@ class PML_HTMLizer(object): pml = re.sub(r'\\a\d\d\d', '', pml) pml = re.sub(r'\\U\d\d\d\d', '', pml) pml = re.sub(r'\\.', '', pml) - pml.replace('\r\n', ' ') - pml.replace('\n', ' ') - pml.replace('\r', ' ') + pml = pml.replace('\r\n', ' ') + pml = pml.replace('\n', ' ') + pml = pml.replace('\r', ' ') + pml = pml.strip() return pml From 540032080c59d2ae7bead1045f5f329265127d7e Mon Sep 17 00:00:00 2001 From: Timothy Legge Date: Wed, 16 Feb 2011 22:08:12 -0400 Subject: [PATCH 22/24] Fix bugs 8449, 8485, 8844, 8915 - Some fields in database include binary characters that cannot be intrepreted as Unicode --- src/calibre/devices/kobo/driver.py | 31 +++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index c5e8f5ca0f..6ffd542a06 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -22,7 +22,7 @@ class KOBO(USBMS): gui_name = 'Kobo Reader' description = _('Communicate with the Kobo Reader') author = 'Timothy Legge and Kovid Goyal' - version = (1, 0, 7) + version = (1, 0, 9) dbversion = 0 fwversion = 0 @@ -125,9 +125,12 @@ class KOBO(USBMS): if imagename is not None: bl[idx].thumbnail = ImageWrapper(imagename) if (ContentType != '6' and MimeType != 'Shortcover'): - if self.update_metadata_item(bl[idx]): - # print 'update_metadata_item returned true' - changed = True + if os.path.exists(self.normalize_path(os.path.join(prefix, lpath))): + if self.update_metadata_item(bl[idx]): + # print 'update_metadata_item returned true' + changed = True + else: + debug_print(" Strange: The file: ", prefix, lpath, " does mot exist!") if lpath in playlist_map and \ playlist_map[lpath] not in bl[idx].device_collections: bl[idx].device_collections.append(playlist_map[lpath]) @@ -136,7 +139,13 @@ class KOBO(USBMS): book = Book(prefix, lpath, title, authors, mime, date, ContentType, ImageID, size=1048576) else: try: - book = self.book_from_path(prefix, lpath, title, authors, mime, date, ContentType, ImageID) + if os.path.exists(self.normalize_path(os.path.join(prefix, lpath))): + book = self.book_from_path(prefix, lpath, title, authors, mime, date, ContentType, ImageID) + else: + debug_print(" Strange: The file: ", prefix, lpath, " does mot exist!") + title = "FILE MISSING: " + title + book = Book(prefix, lpath, title, authors, mime, date, ContentType, ImageID, size=1048576) + except: debug_print("prefix: ", prefix, "lpath: ", lpath, "title: ", title, "authors: ", authors, \ "mime: ", mime, "date: ", date, "ContentType: ", ContentType, "ImageID: ", ImageID) @@ -153,6 +162,10 @@ class KOBO(USBMS): return changed connection = sqlite.connect(self.normalize_path(self._main_prefix + '.kobo/KoboReader.sqlite')) + + # return bytestrings if the content cannot the decoded as unicode + connection.text_factory = lambda x: unicode(x, "utf-8", "ignore") + cursor = connection.cursor() #query = 'select count(distinct volumeId) from volume_shortcovers' @@ -222,6 +235,10 @@ class KOBO(USBMS): debug_print('delete_via_sql: ContentID: ', ContentID, 'ContentType: ', ContentType) connection = sqlite.connect(self.normalize_path(self._main_prefix + '.kobo/KoboReader.sqlite')) + + # return bytestrings if the content cannot the decoded as unicode + connection.text_factory = lambda x: unicode(x, "utf-8", "ignore") + cursor = connection.cursor() t = (ContentID,) cursor.execute('select ImageID from content where ContentID = ?', t) @@ -495,6 +512,10 @@ class KOBO(USBMS): # the last book from the collection the list of books is empty # and the removal of the last book would not occur connection = sqlite.connect(self.normalize_path(self._main_prefix + '.kobo/KoboReader.sqlite')) + + # return bytestrings if the content cannot the decoded as unicode + connection.text_factory = lambda x: unicode(x, "utf-8", "ignore") + cursor = connection.cursor() From fdf2dc8dce6efc81b61bac498cd9f820a149a9b4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 19:11:56 -0700 Subject: [PATCH 23/24] Adding from ISBN: Show a progress dialog for record creation as well as metadata download. Fixes #9005 (Calibre unresponsive for several minutes when adding many books from ISBN) --- src/calibre/gui2/actions/add.py | 59 ++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/src/calibre/gui2/actions/add.py b/src/calibre/gui2/actions/add.py index 83600c3227..49a7a4677a 100644 --- a/src/calibre/gui2/actions/add.py +++ b/src/calibre/gui2/actions/add.py @@ -8,18 +8,20 @@ __docformat__ = 'restructuredtext en' import os from functools import partial -from PyQt4.Qt import QPixmap, QMenu +from PyQt4.Qt import QPixmap, QMenu, QTimer from calibre.gui2 import error_dialog, choose_files, \ choose_dir, warning_dialog, info_dialog from calibre.gui2.dialogs.add_empty_book import AddEmptyBookDialog +from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2.widgets import IMAGE_EXTENSIONS from calibre.ebooks import BOOK_EXTENSIONS from calibre.utils.filenames import ascii_filename from calibre.constants import preferred_encoding, filesystem_encoding from calibre.gui2.actions import InterfaceAction from calibre.gui2 import config +from calibre.ebooks.metadata import MetaInformation class AddAction(InterfaceAction): @@ -95,7 +97,6 @@ class AddAction(InterfaceAction): dlg = AddEmptyBookDialog(self.gui, self.gui.library_view.model().db, author) if dlg.exec_() == dlg.Accepted: num = dlg.qty_to_add - from calibre.ebooks.metadata import MetaInformation for x in xrange(num): mi = MetaInformation(_('Unknown'), dlg.selected_authors) self.gui.library_view.model().db.import_book(mi, []) @@ -105,27 +106,45 @@ class AddAction(InterfaceAction): self.gui.tags_view.recount() def add_isbns(self, books, add_tags=[]): - from calibre.ebooks.metadata import MetaInformation - ids = set([]) - db = self.gui.library_view.model().db + self.isbn_books = list(books) + self.add_by_isbn_ids = set() + self.isbn_add_tags = add_tags + QTimer.singleShot(10, self.do_one_isbn_add) + self.isbn_add_dialog = ProgressDialog(_('Adding'), + _('Creating book records from ISBNs'), max=len(books), + cancelable=False, parent=self.gui) + self.isbn_add_dialog.exec_() + + def do_one_isbn_add(self): + try: + db = self.gui.library_view.model().db + + try: + x = self.isbn_books.pop(0) + except IndexError: + self.gui.library_view.model().books_added(self.isbn_add_dialog.value) + self.isbn_add_dialog.accept() + orig = config['overwrite_author_title_metadata'] + config['overwrite_author_title_metadata'] = True + try: + self.gui.iactions['Edit Metadata'].do_download_metadata( + self.add_by_isbn_ids) + finally: + config['overwrite_author_title_metadata'] = orig + return + - for x in books: mi = MetaInformation(None) mi.isbn = x['isbn'] - if x['path'] is not None: - ids.add(db.import_book(mi, [x['path']])) - else: - ids.add(db.import_book(mi, [])) - self.gui.library_view.model().books_added(len(books)) - orig = config['overwrite_author_title_metadata'] - config['overwrite_author_title_metadata'] = True - try: - self.gui.iactions['Edit Metadata'].do_download_metadata(ids) - finally: - config['overwrite_author_title_metadata'] = orig - if add_tags and ids: - db.bulk_modify_tags(ids, add=add_tags) - + if self.isbn_add_tags: + mi.tags = list(self.isbn_add_tags) + fmts = [] if x['path'] is None else [x['path']] + self.add_by_isbn_ids.add(db.import_book(mi, fmts)) + self.isbn_add_dialog.value += 1 + QTimer.singleShot(10, self.do_one_isbn_add) + except: + self.isbn_add_dialog.accept() + raise def files_dropped(self, paths): to_device = self.gui.stack.currentIndex() != 0 From 0637a16738e94d5b663c9c106179181896eac0e9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Feb 2011 19:13:51 -0700 Subject: [PATCH 24/24] Detroit News by DTM --- resources/recipes/detroit_news.recipe | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 resources/recipes/detroit_news.recipe diff --git a/resources/recipes/detroit_news.recipe b/resources/recipes/detroit_news.recipe new file mode 100644 index 0000000000..407fb98b13 --- /dev/null +++ b/resources/recipes/detroit_news.recipe @@ -0,0 +1,64 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +import re +class AdvancedUserRecipe1297291961(BasicNewsRecipe): + title = u'Detroit News' + language = 'en' + __author__ = 'DTM' + oldest_article = 2 + max_articles_per_feed = 20 + no_stylesheets = True + conversion_options = { + 'linearize_tables' : True, + } + + feeds = [ + (u'Headlines', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss&mime=xml'), + (u'Nation/World', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss09&mime=xml'), + (u'Metro/State', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss36&mime=xml'), + (u'Wayne County', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss01&mime=xml'), + (u'Oakland County', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss02&mime=xml'), + (u'Macomb County', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss03&mime=xml'), + (u'Livingston County', u'http://detnews.com/apps/pbcs.dll/section?category=rss04&mime=xml'), + (u'Politics/Government', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss10&mime=xml'), + (u'Editorials', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss07&mime=xml'), + (u'Columnists', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss43&mime=xml'), + (u'Charlie LeDuff', u'http://detnews.com/apps/pbcs.dll/section?category=rss54&mime=xml'), + (u'Religion', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss11&mime=xml'), + (u'Technology', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss12&mime=xml'), + (u'Commuting', u'http://detnews.com/apps/pbcs.dll/section?category=rss05&mime=xml'), + (u'Schools', u'http://detnews.com/apps/pbcs.dll/section?category=rss06&mime=xml'), + (u'Obituaries', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss08&mime=xml'), + (u'Autos Insider', u'http://detnews.com/apps/pbcs.dll/section?category=rss25&mime=xml'), + (u'Drive', u'http://detnews.com/apps/pbcs.dll/section?category=rss26&mime=xml'), + (u'Business', u'http://detnews.com/apps/pbcs.dll/section?category=rss21&mime=xml'), + (u'Personal Finance', u'http://detnews.com/apps/pbcs.dll/section?category=rss23&mime=xml'), + (u'Real Estate', u'http://detnews.com/apps/pbcs.dll/section?category=rss24&mime=xml'), + (u'Movies', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss28&mime=xml'), + (u'TV', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss40&mime=xml'), + (u'Music/Nightlife', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss30&mime=xml'), + (u'Celebrities', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss51&mime=xml'), + (u'The Arts', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss27&mime=xml'), + (u'Food', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss29&mime=xml'), + (u'Homestyle', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss31&mime=xml'), + (u'The Green Life', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss53&mime=xml'), + (u'Lifestyle', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss32&mime=xml'), + (u'Health', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss34&mime=xml'), + (u'Travel', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss52&mime=xml'), + (u'Advice', u'http://www.detnews.com/apps/pbcs.dll/section?category=rss50&mime=xml'), + (u'Pistons', u'http://detnews.com/apps/pbcs.dll/section?category=rss13&mime=xml'), + (u'Lions', u'http://detnews.com/apps/pbcs.dll/section?category=rss14&mime=xml'), + (u'Tigers', u'http://detnews.com/apps/pbcs.dll/section?category=rss15&mime=xml'), + (u'Red Wings', u'http://detnews.com/apps/pbcs.dll/section?category=rss16&mime=xml'), + (u'Michigan State', u'http://detnews.com/apps/pbcs.dll/section?category=rss18&mime=xml'), + (u'University of Michigan', u'http://detnews.com/apps/pbcs.dll/section?category=rss17&mime=xml'), + (u'Motor Sports', u'http://detnews.com/apps/pbcs.dll/section?category=rss20&mime=xml'), + (u'Golf', u'http://detnews.com/apps/pbcs.dll/section?category=rss47&mime=xml'), + (u'Outdoors', u'http://detnews.com/apps/pbcs.dll/section?category=rss19&mime=xml') + ] + + def print_version(self, url): + p = re.compile('(/\d{4}|/-1)/(rss|ENT|LIFESTYLE|OPINION|METRO)\d*') + m = p.search(url) + return url.replace(m.group(), '&template=printart') +