From 41514c8241b0e4463ee95e1d40740ad003549148 Mon Sep 17 00:00:00 2001
From: Hiroshi Miura
Date: Fri, 21 Jan 2011 01:02:20 +0900
Subject: [PATCH 01/35] recipe: update: Endgadget japanese, add tags removal
---
resources/recipes/endgadget_ja.recipe | 3 +++
1 file changed, 3 insertions(+)
diff --git a/resources/recipes/endgadget_ja.recipe b/resources/recipes/endgadget_ja.recipe
index 891e6720a5..3c20380e9b 100644
--- a/resources/recipes/endgadget_ja.recipe
+++ b/resources/recipes/endgadget_ja.recipe
@@ -18,3 +18,6 @@ class EndgadgetJapan(BasicNewsRecipe):
language = 'ja'
encoding = 'utf-8'
feeds = [(u'engadget', u'http://japanese.engadget.com/rss.xml')]
+
+ remove_tags_before = dict(name="div", attrs={'id':"content_wrap"})
+ remove_tags_after = dict(name='h3', attrs={'id':'addcomments'})
From cdf61487ffaa8d6caf0037680bd4d7c4901ab066 Mon Sep 17 00:00:00 2001
From: ldolse
Date: Tue, 25 Jan 2011 17:29:55 +0800
Subject: [PATCH 02/35] add strong tags to list of empty tags removed during
cleanup
---
src/calibre/ebooks/conversion/utils.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py
index f6e259b6f9..d4b03b8d55 100644
--- a/src/calibre/ebooks/conversion/utils.py
+++ b/src/calibre/ebooks/conversion/utils.py
@@ -375,9 +375,9 @@ class HeuristicProcessor(object):
html = re.sub('', '', html)
# Get rid of empty span, bold, font, em, & italics tags
html = re.sub(r"\s*]*>\s*(]*>\s*){0,2}\s*\s*", " ", html)
- html = re.sub(r"\s*<(font|[ibu]|em)[^>]*>\s*(<(font|[ibu]|em)[^>]*>\s*(font|[ibu]|em)>\s*){0,2}\s*(font|[ibu]|em)>", " ", html)
+ html = re.sub(r"\s*<(font|[ibu]|em|strong)[^>]*>\s*(<(font|[ibu]|em|strong)[^>]*>\s*(font|[ibu]|em|strong)>\s*){0,2}\s*(font|[ibu]|em|strong)>", " ", html)
html = re.sub(r"\s*]*>\s*(]>\s*){0,2}\s*\s*", " ", html)
- html = re.sub(r"\s*<(font|[ibu]|em)[^>]*>\s*(<(font|[ibu]|em)[^>]*>\s*(font|[ibu]|em)>\s*){0,2}\s*(font|[ibu]|em)>", " ", html)
+ html = re.sub(r"\s*<(font|[ibu]|em|strong)[^>]*>\s*(<(font|[ibu]|em|strong)[^>]*>\s*(font|[ibu]|em|strong)>\s*){0,2}\s*(font|[ibu]|em|strong)>", " ", html)
self.deleted_nbsps = True
return html
From 6ae347962e13c0aaff2abd134fb967aea825e9a6 Mon Sep 17 00:00:00 2001
From: John Schember
Date: Fri, 28 Jan 2011 18:54:43 -0500
Subject: [PATCH 03/35] Add Cybook Gen 3 comic size to profile.
---
src/calibre/customize/profiles.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/calibre/customize/profiles.py b/src/calibre/customize/profiles.py
index 44628c22db..bebaebced6 100644
--- a/src/calibre/customize/profiles.py
+++ b/src/calibre/customize/profiles.py
@@ -583,6 +583,7 @@ class CybookG3Output(OutputProfile):
# Screen size is a best guess
screen_size = (600, 800)
+ comic_screen_size = (600, 757)
dpi = 168.451
fbase = 16
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
From b45099206a100cd6a8613fd5f31ea0e3a1b014c0 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Fri, 28 Jan 2011 17:52:27 -0700
Subject: [PATCH 04/35] Fix regression that broke content server when user
categories are present
---
src/calibre/library/server/browse.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py
index 3e4687be95..a18def29de 100644
--- a/src/calibre/library/server/browse.py
+++ b/src/calibre/library/server/browse.py
@@ -356,9 +356,9 @@ class BrowseServer(object):
if category in category_icon_map:
icon = category_icon_map[category]
elif meta['is_custom']:
- icon = category_icon_map[':custom']
+ icon = category_icon_map['custom:']
elif meta['kind'] == 'user':
- icon = category_icon_map[':user']
+ icon = category_icon_map['user:']
else:
icon = 'blank.png'
cats.append((meta['name'], category, icon))
From 72b82bb0bf3749c5e28a7f2a4b271ef1681c28f3 Mon Sep 17 00:00:00 2001
From: John Schember
Date: Fri, 28 Jan 2011 22:35:23 -0500
Subject: [PATCH 05/35] TXT Output: Add support for output textile formatted
text. Clean up TXT output GUI option panel.
---
src/calibre/ebooks/txt/output.py | 23 ++-
src/calibre/ebooks/txt/textileml.py | 64 ++++++++
src/calibre/gui2/convert/txt_output.py | 18 +--
src/calibre/gui2/convert/txt_output.ui | 181 +++++++++++----------
src/calibre/utils/html2textile.py | 209 +++++++++++++++++++++++++
5 files changed, 390 insertions(+), 105 deletions(-)
create mode 100644 src/calibre/ebooks/txt/textileml.py
create mode 100644 src/calibre/utils/html2textile.py
diff --git a/src/calibre/ebooks/txt/output.py b/src/calibre/ebooks/txt/output.py
index 29b3d899bc..c498e7093e 100644
--- a/src/calibre/ebooks/txt/output.py
+++ b/src/calibre/ebooks/txt/output.py
@@ -9,6 +9,7 @@ import os
from calibre.customize.conversion import OutputFormatPlugin, \
OptionRecommendation
from calibre.ebooks.txt.markdownml import MarkdownMLizer
+from calibre.ebooks.txt.textileml import TextileMLizer
from calibre.ebooks.txt.txtml import TXTMLizer
from calibre.ebooks.txt.newlines import TxtNewlines, specified_newlines
@@ -44,24 +45,30 @@ class TXTOutput(OutputFormatPlugin):
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Force splitting on the max-line-length value when no space '
'is present. Also allows max-line-length to be below the minimum')),
- OptionRecommendation(name='markdown_format',
- recommended_value=False, level=OptionRecommendation.LOW,
- help=_('Produce Markdown formatted text.')),
+ OptionRecommendation(name='txt_output_formatting',
+ recommended_value='none',
+ choices=['none', 'markdown', 'textile'],
+ help=_('Formatting used within the document.\n'
+ '* none: Produce plain text.\n'
+ '* markdown: Produce Markdown formatted text.\n'
+ '* textile: Produce Textile formatted text.')),
OptionRecommendation(name='keep_links',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Do not remove links within the document. This is only ' \
- 'useful when paired with the markdown-format option because' \
- ' links are always removed with plain text output.')),
+ 'useful when paired with a txt-output-formatting option that '
+ 'is not none because links are always removed with plain text output.')),
OptionRecommendation(name='keep_image_references',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Do not remove image references within the document. This is only ' \
- 'useful when paired with the markdown-format option because' \
- ' image references are always removed with plain text output.')),
+ 'useful when paired with a txt-output-formatting option that '
+ 'is not none because links are always removed with plain text output.')),
])
def convert(self, oeb_book, output_path, input_plugin, opts, log):
- if opts.markdown_format:
+ if opts.txt_output_formatting.lower() == 'markdown':
writer = MarkdownMLizer(log)
+ elif opts.txt_output_formatting.lower() == 'textile':
+ writer = TextileMLizer(log)
else:
writer = TXTMLizer(log)
diff --git a/src/calibre/ebooks/txt/textileml.py b/src/calibre/ebooks/txt/textileml.py
new file mode 100644
index 0000000000..fa65b68bee
--- /dev/null
+++ b/src/calibre/ebooks/txt/textileml.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+__license__ = 'GPL 3'
+__copyright__ = '2011, John Schember '
+__docformat__ = 'restructuredtext en'
+
+'''
+Transform OEB content into Textile formatted plain text
+'''
+
+import re
+
+from lxml import etree
+
+from calibre.ebooks.oeb.base import XHTML
+from calibre.utils.html2textile import html2textile
+
+class TextileMLizer(object):
+
+ def __init__(self, log):
+ self.log = log
+
+ def extract_content(self, oeb_book, opts):
+ self.log.info('Converting XHTML to Textile formatted TXT...')
+ self.oeb_book = oeb_book
+ self.opts = opts
+
+ return self.mlize_spine()
+
+ def mlize_spine(self):
+ output = [u'']
+
+ for item in self.oeb_book.spine:
+ self.log.debug('Converting %s to Textile formatted TXT...' % item.href)
+
+ html = unicode(etree.tostring(item.data.find(XHTML('body')), encoding=unicode))
+
+ if not self.opts.keep_links:
+ html = re.sub(r'<\s*a[^>]*>', '', html)
+ html = re.sub(r'<\s*/\s*a\s*>', '', html)
+ if not self.opts.keep_image_references:
+ html = re.sub(r'<\s*img[^>]*>', '', html)
+ html = re.sub(r'<\s*img\s*>', '', html)
+
+ text = html2textile(html)
+
+ # Ensure the section ends with at least two new line characters.
+ # This is to prevent the last paragraph from a section being
+ # combined into the fist paragraph of the next.
+ end_chars = text[-4:]
+ # Convert all newlines to \n
+ end_chars = end_chars.replace('\r\n', '\n')
+ end_chars = end_chars.replace('\r', '\n')
+ end_chars = end_chars[-2:]
+ if not end_chars[1] == '\n':
+ text += '\n\n'
+ if end_chars[1] == '\n' and not end_chars[0] == '\n':
+ text += '\n'
+
+ output += text
+
+ output = u''.join(output)
+
+ return output
diff --git a/src/calibre/gui2/convert/txt_output.py b/src/calibre/gui2/convert/txt_output.py
index 0e6a6b9574..4233799cd8 100644
--- a/src/calibre/gui2/convert/txt_output.py
+++ b/src/calibre/gui2/convert/txt_output.py
@@ -21,26 +21,14 @@ class PluginWidget(Widget, Ui_Form):
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent,
['newline', 'max_line_length', 'force_max_line_length',
- 'inline_toc', 'markdown_format', 'keep_links', 'keep_image_references',
+ 'inline_toc', 'txt_output_formatting', 'keep_links', 'keep_image_references',
'txt_output_encoding'])
self.db, self.book_id = db, book_id
for x in get_option('newline').option.choices:
self.opt_newline.addItem(x)
+ for x in get_option('txt_output_formatting').option.choices:
+ self.opt_txt_output_formatting.addItem(x)
self.initialize_options(get_option, get_help, db, book_id)
- self.opt_markdown_format.stateChanged.connect(self.enable_markdown_format)
- self.enable_markdown_format(self.opt_markdown_format.checkState())
-
def break_cycles(self):
Widget.break_cycles(self)
-
- try:
- self.opt_markdown_format.stateChanged.disconnect()
- except:
- pass
-
- def enable_markdown_format(self, state):
- state = state == Qt.Checked
- self.opt_keep_links.setEnabled(state)
- self.opt_keep_image_references.setEnabled(state)
-
diff --git a/src/calibre/gui2/convert/txt_output.ui b/src/calibre/gui2/convert/txt_output.ui
index 57fe702db7..55e7ea113d 100644
--- a/src/calibre/gui2/convert/txt_output.ui
+++ b/src/calibre/gui2/convert/txt_output.ui
@@ -6,100 +6,117 @@
0
0
- 477
- 300
+ 392
+ 343
Form
-
- -
-
-
- &Line ending style:
-
-
- opt_newline
+
+
-
+
+
+ General
+
+
-
+
+
+ Output Encoding:
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ &Line ending style:
+
+
+ opt_newline
+
+
+
+ -
+
+
+ -
+
+
+ Formatting
+
+
+
+ -
+
+
+
- -
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 246
-
-
-
-
- -
-
-
- &Inline TOC
+
-
+
+
+ Plain
+
+
-
+
+
+ &Maximum line length:
+
+
+ opt_max_line_length
+
+
+
+ -
+
+
+ -
+
+
+ Force maximum line length
+
+
+
+ -
+
+
+ &Inline TOC
+
+
+
+
- -
-
-
- -
-
-
- &Maximum line length:
-
-
- opt_max_line_length
-
-
-
- -
-
-
- Force maximum line length
-
-
-
- -
-
-
- Apply Markdown formatting to text
-
-
-
- -
-
-
- Do not remove links (<a> tags) before processing
-
-
-
- -
-
-
- Do not remove image references before processing
-
-
-
- -
-
-
- Output Encoding:
-
-
-
- -
-
-
- true
+
-
+
+
+ Markdown, Textile
+
+
-
+
+
+ Do not remove links (<a> tags) before processing
+
+
+
+ -
+
+
+ Do not remove image references before processing
+
+
+
+
diff --git a/src/calibre/utils/html2textile.py b/src/calibre/utils/html2textile.py
new file mode 100644
index 0000000000..9e468b00c8
--- /dev/null
+++ b/src/calibre/utils/html2textile.py
@@ -0,0 +1,209 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010, Webreactor - Marcin Lulek
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+from lxml import etree
+from calibre.ebooks.oeb.base import barename
+
+class EchoTarget:
+
+ def __init__(self):
+ self.final_output = []
+ self.block = False
+ self.ol_ident = 0
+ self.ul_ident = 0
+ self.list_types = []
+ self.haystack = []
+
+ def start(self, tag, attrib):
+ tag = barename(tag)
+
+ newline = '\n'
+ dot = ''
+ new_tag = ''
+
+ if tag in ('h1', 'h2', 'h3', 'h4', 'h5', 'h6'):
+ new_tag = tag
+ dot = '. '
+ elif tag == 'p':
+ new_tag = ''
+ dot = ''
+ elif tag == 'blockquote':
+ new_tag = 'bq'
+ dot = '. '
+ elif tag in ('b', 'strong'):
+ new_tag = '*'
+ newline = ''
+ elif tag in ('em', 'i'):
+ new_tag = '_'
+ newline = ''
+ elif tag == 'cite':
+ new_tag = '??'
+ newline = ''
+ elif tag == 'del':
+ new_tag = '-'
+ newline = ''
+ elif tag == 'ins':
+ new_tag = '+'
+ newline = ''
+ elif tag == 'sup':
+ new_tag = '^'
+ newline = ''
+ elif tag == 'sub':
+ new_tag = '~'
+ newline = ''
+ elif tag == 'span':
+ new_tag = '%'
+ newline = ''
+ elif tag == 'a':
+ self.block = True
+ if 'title' in attrib:
+ self.a_part = {'title':attrib.get('title'),
+ 'href':attrib.get('href', '')}
+ else:
+ self.a_part = {'title':None, 'href':attrib.get('href', '')}
+ new_tag = ''
+ newline = ''
+
+ elif tag == 'img':
+ if 'alt' in attrib:
+ new_tag = ' !%s(%s)' % (attrib.get('src'), attrib.get('title'),)
+ else:
+ new_tag = ' !%s' % attrib.get('src')
+ newline = ''
+
+ elif tag in ('ul', 'ol'):
+ new_tag = ''
+ newline = ''
+ self.list_types.append(tag)
+ if tag == 'ul':
+ self.ul_ident += 1
+ else:
+ self.ol_ident += 1
+
+ elif tag == 'li':
+ indent = self.ul_ident + self.ol_ident
+ if self.list_types[-1] == 'ul':
+ new_tag = '*' * indent + ' '
+ newline = '\n'
+ else:
+ new_tag = '#' * indent + ' '
+ newline = '\n'
+
+
+ if tag not in ('ul', 'ol'):
+ textile = '%(newline)s%(tag)s%(dot)s' % \
+ {
+ 'newline':newline,
+ 'tag':new_tag,
+ 'dot':dot
+ }
+ if not self.block:
+ self.final_output.append(textile)
+ else:
+ self.haystack.append(textile)
+
+ def end(self, tag):
+ tag = barename(tag)
+
+ if tag in ('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'):
+ self.final_output.append('\n')
+ elif tag in ('b', 'strong'):
+ self.final_output.append('*')
+ elif tag in ('em', 'i'):
+ self.final_output.append('_')
+ elif tag == 'cite':
+ self.final_output.append('??')
+ elif tag == 'del':
+ self.final_output.append('-')
+ elif tag == 'ins':
+ self.final_output.append('+')
+ elif tag == 'sup':
+ self.final_output.append('^')
+ elif tag == 'sub':
+ self.final_output.append('~')
+ elif tag == 'span':
+ self.final_output.append('%')
+ elif tag == 'a':
+ if self.a_part['title']:
+ textilized = ' "%s (%s)":%s ' % (
+ ''.join(self.haystack),
+ self.a_part.get('title'),
+ self.a_part.get('href'),
+ )
+ self.haystack = []
+ else:
+ textilized = ' "%s":%s ' % (
+ ''.join(self.haystack),
+ self.a_part.get('href'),
+ )
+ self.haystack = []
+ self.final_output.append(textilized)
+ self.block = False
+ elif tag == 'img':
+ self.final_output.append('!')
+ elif tag == 'ul':
+ self.ul_ident -= 1
+ self.list_types.pop()
+ if len(self.list_types) == 0:
+ self.final_output.append('\n')
+ elif tag == 'ol':
+ self.ol_ident -= 1
+ self.list_types.pop()
+ if len(self.list_types) == 0:
+ self.final_output.append('\n')
+
+ def data(self, data):
+ #we dont want any linebreaks inside our tags
+ node_data = data.replace('\n','')
+ if not self.block:
+ self.final_output.append(node_data)
+ else:
+ self.haystack.append(node_data)
+
+ def comment(self, text):
+ pass
+
+ def close(self):
+ return "closed!"
+
+
+def html2textile(html):
+ #1st pass
+ #clean the whitespace and convert html to xhtml
+ parser = etree.HTMLParser()
+ tree = etree.fromstring(html, parser)
+ xhtml = etree.tostring(tree, method="xml")
+ parser = etree.XMLParser(remove_blank_text=True)
+ root = etree.XML(xhtml, parser)
+ cleaned_html = etree.tostring(root)
+ #2nd pass build textile
+ target = EchoTarget()
+ parser = etree.XMLParser(target=target)
+ root = etree.fromstring(cleaned_html, parser)
+ textilized_text = ''.join(target.final_output).lstrip().rstrip()
+ return textilized_text
From 33d1770d725a94a7a6ab9faf9e295e7fddc66659 Mon Sep 17 00:00:00 2001
From: John Schember
Date: Fri, 28 Jan 2011 22:39:19 -0500
Subject: [PATCH 06/35] TXT Output: rename txt_output_formatting choice none
plain.
---
src/calibre/ebooks/txt/output.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/calibre/ebooks/txt/output.py b/src/calibre/ebooks/txt/output.py
index c498e7093e..d54119753b 100644
--- a/src/calibre/ebooks/txt/output.py
+++ b/src/calibre/ebooks/txt/output.py
@@ -47,9 +47,9 @@ class TXTOutput(OutputFormatPlugin):
'is present. Also allows max-line-length to be below the minimum')),
OptionRecommendation(name='txt_output_formatting',
recommended_value='none',
- choices=['none', 'markdown', 'textile'],
+ choices=['plain', 'markdown', 'textile'],
help=_('Formatting used within the document.\n'
- '* none: Produce plain text.\n'
+ '* plain: Produce plain text.\n'
'* markdown: Produce Markdown formatted text.\n'
'* textile: Produce Textile formatted text.')),
OptionRecommendation(name='keep_links',
From 8ebd1ac7ff2c246877f49bdaa92ecff5e6817f54 Mon Sep 17 00:00:00 2001
From: John Schember
Date: Fri, 28 Jan 2011 22:39:49 -0500
Subject: [PATCH 07/35] ...
---
src/calibre/ebooks/txt/output.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/ebooks/txt/output.py b/src/calibre/ebooks/txt/output.py
index d54119753b..a7a1b7814d 100644
--- a/src/calibre/ebooks/txt/output.py
+++ b/src/calibre/ebooks/txt/output.py
@@ -46,7 +46,7 @@ class TXTOutput(OutputFormatPlugin):
help=_('Force splitting on the max-line-length value when no space '
'is present. Also allows max-line-length to be below the minimum')),
OptionRecommendation(name='txt_output_formatting',
- recommended_value='none',
+ recommended_value='plain',
choices=['plain', 'markdown', 'textile'],
help=_('Formatting used within the document.\n'
'* plain: Produce plain text.\n'
From a489c90adf8f521e81024e10d63192f2382d2ce9 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Fri, 28 Jan 2011 20:44:34 -0700
Subject: [PATCH 08/35] Add search to the plugin preferences dialog
---
src/calibre/gui2/__init__.py | 3 +
src/calibre/gui2/preferences/plugins.py | 116 +++++++++++++++++++++--
src/calibre/gui2/preferences/plugins.ui | 48 ++++++++++
src/calibre/utils/search_query_parser.py | 4 +-
4 files changed, 161 insertions(+), 10 deletions(-)
diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py
index b00097f5b2..9150172fc1 100644
--- a/src/calibre/gui2/__init__.py
+++ b/src/calibre/gui2/__init__.py
@@ -120,6 +120,8 @@ def _config():
help='Search history for the LRF viewer')
c.add_opt('scheduler_search_history', default=[],
help='Search history for the recipe scheduler')
+ c.add_opt('plugin_search_history', default=[],
+ help='Search history for the recipe scheduler')
c.add_opt('worker_limit', default=6,
help=_('Maximum number of waiting worker processes'))
c.add_opt('get_social_metadata', default=True,
@@ -138,6 +140,7 @@ def _config():
help=_('Show the average rating per item indication in the tag browser'))
c.add_opt('disable_animations', default=False,
help=_('Disable UI animations'))
+ c.add_opt
return ConfigProxy(c)
config = _config()
diff --git a/src/calibre/gui2/preferences/plugins.py b/src/calibre/gui2/preferences/plugins.py
index ba5b921d44..1edd4fe5f9 100644
--- a/src/calibre/gui2/preferences/plugins.py
+++ b/src/calibre/gui2/preferences/plugins.py
@@ -17,11 +17,14 @@ from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin
remove_plugin
from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files, \
question_dialog
+from calibre.utils.search_query_parser import SearchQueryParser
+from calibre.utils.icu import lower
-class PluginModel(QAbstractItemModel): # {{{
+class PluginModel(QAbstractItemModel, SearchQueryParser): # {{{
def __init__(self, *args):
QAbstractItemModel.__init__(self, *args)
+ SearchQueryParser.__init__(self, ['all'])
self.icon = QVariant(QIcon(I('plugins.png')))
p = QIcon(self.icon).pixmap(32, 32, QIcon.Disabled, QIcon.On)
self.disabled_icon = QVariant(QIcon(p))
@@ -40,6 +43,72 @@ class PluginModel(QAbstractItemModel): # {{{
for plugins in self._data.values():
plugins.sort(cmp=lambda x, y: cmp(x.name.lower(), y.name.lower()))
+ def universal_set(self):
+ ans = set([])
+ for c, category in enumerate(self.categories):
+ ans.add((c, -1))
+ for p, plugin in enumerate(self._data[category]):
+ ans.add((c, p))
+ return ans
+
+ def get_matches(self, location, query, candidates=None):
+ if candidates is None:
+ candidates = self.universal_set()
+ ans = set([])
+ if not query:
+ return ans
+ query = lower(query)
+ for c, p in candidates:
+ if p < 0:
+ if query in lower(self.categories[c]):
+ ans.add((c, p))
+ else:
+ try:
+ plugin = self._data[self.categories[c]][p]
+ except:
+ continue
+ if query in lower(plugin.name) or query in lower(plugin.author) or \
+ query in lower(plugin.description):
+ ans.add((c, p))
+ return ans
+
+ def find(self, query):
+ query = query.strip()
+ matches = self.parse(query)
+ if not matches:
+ return QModelIndex()
+ matches = list(sorted(matches))
+ c, p = matches[0]
+ cat_idx = self.index(c, 0, QModelIndex())
+ if p == -1:
+ return cat_idx
+ return self.index(p, 0, cat_idx)
+
+ def find_next(self, idx, query, backwards=False):
+ query = query.strip()
+ matches = self.parse(query)
+ if not matches:
+ return idx
+ if idx.parent().isValid():
+ loc = (idx.parent().row(), idx.row())
+ else:
+ loc = (idx.row(), -1)
+ if loc not in matches:
+ return self.find(query)
+ if len(matches) == 1:
+ return QModelIndex()
+ matches = list(sorted(matches))
+ i = matches.index(loc)
+ if backwards:
+ ans = i - 1 if i - 1 >= 0 else len(matches)-1
+ else:
+ ans = i + 1 if i + 1 < len(matches) else 0
+
+ ans = matches[ans]
+
+ return self.index(ans[0], 0, QModelIndex()) if ans[1] < 0 else \
+ self.index(ans[1], 0, self.index(ans[0], 0, QModelIndex()))
+
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QModelIndex()
@@ -127,6 +196,7 @@ class PluginModel(QAbstractItemModel): # {{{
return plugin
return NONE
+
# }}}
class ConfigWidget(ConfigWidgetBase, Ui_Form):
@@ -144,6 +214,42 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.customize_plugin_button.clicked.connect(self.customize_plugin)
self.remove_plugin_button.clicked.connect(self.remove_plugin)
self.button_plugin_add.clicked.connect(self.add_plugin)
+ self.search.initialize('plugin_search_history',
+ help_text=_('Search for plugin'))
+ self.search.search.connect(self.find)
+ self.next_button.clicked.connect(self.find_next)
+ self.previous_button.clicked.connect(self.find_previous)
+
+ def find(self, query):
+ idx = self._plugin_model.find(query)
+ if not idx.isValid():
+ return info_dialog(self, _('No matches'),
+ _('Could not find any matching plugins'), show=True,
+ show_copy_button=False)
+ self.highlight_index(idx)
+
+ def highlight_index(self, idx):
+ self.plugin_view.scrollTo(idx)
+ self.plugin_view.selectionModel().select(idx,
+ self.plugin_view.selectionModel().ClearAndSelect)
+ self.plugin_view.setCurrentIndex(idx)
+
+ def find_next(self, *args):
+ idx = self.plugin_view.currentIndex()
+ if not idx.isValid():
+ idx = self._plugin_model.index(0, 0)
+ idx = self._plugin_model.find_next(idx,
+ unicode(self.search.currentText()))
+ self.highlight_index(idx)
+
+ def find_previous(self, *args):
+ idx = self.plugin_view.currentIndex()
+ if not idx.isValid():
+ idx = self._plugin_model.index(0, 0)
+ idx = self._plugin_model.find_next(idx,
+ unicode(self.search.currentText()), backwards=True)
+ self.highlight_index(idx)
+
def toggle_plugin(self, *args):
self.modify_plugin(op='toggle')
@@ -184,13 +290,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
show=True, show_copy_button=False)
idx = self._plugin_model.plugin_to_index_by_properties(plugin)
if idx.isValid():
- self.plugin_view.scrollTo(idx,
- self.plugin_view.PositionAtCenter)
- self.plugin_view.scrollTo(idx,
- self.plugin_view.PositionAtCenter)
- self.plugin_view.selectionModel().select(idx,
- self.plugin_view.selectionModel().ClearAndSelect)
- self.plugin_view.setCurrentIndex(idx)
+ self.highlight_index(idx)
else:
error_dialog(self, _('No valid plugin path'),
_('%s is not a valid plugin path')%path).exec_()
diff --git a/src/calibre/gui2/preferences/plugins.ui b/src/calibre/gui2/preferences/plugins.ui
index 83a904eb08..ebf422dfe3 100644
--- a/src/calibre/gui2/preferences/plugins.ui
+++ b/src/calibre/gui2/preferences/plugins.ui
@@ -24,6 +24,47 @@
+ -
+
+
-
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ &Next
+
+
+
+ :/images/arrow-down.png:/images/arrow-down.png
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ &Previous
+
+
+
+ :/images/arrow-up.png:/images/arrow-up.png
+
+
+
+
+
-
@@ -84,6 +125,13 @@
+
+
+ SearchBox2
+ QComboBox
+ calibre/gui2/search_box.h
+
+
diff --git a/src/calibre/utils/search_query_parser.py b/src/calibre/utils/search_query_parser.py
index 4e4da9d1df..a50ca20fc1 100644
--- a/src/calibre/utils/search_query_parser.py
+++ b/src/calibre/utils/search_query_parser.py
@@ -260,12 +260,12 @@ class SearchQueryParser(object):
'''
Should return the set of matches for :param:'location` and :param:`query`.
- The search must be performed over all entries is :param:`candidates` is
+ The search must be performed over all entries if :param:`candidates` is
None otherwise only over the items in candidates.
:param:`location` is one of the items in :member:`SearchQueryParser.DEFAULT_LOCATIONS`.
:param:`query` is a string literal.
- :param: None or a subset of the set returned by :meth:`universal_set`.
+ :return: None or a subset of the set returned by :meth:`universal_set`.
'''
return set([])
From ad33d230e6a666401d897311d388057ef1ff0bdf Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Fri, 28 Jan 2011 23:06:30 -0700
Subject: [PATCH 09/35] Implement restore database in the GUI
---
src/calibre/gui2/actions/choose_library.py | 17 ++-
src/calibre/gui2/dialogs/restore_library.py | 115 ++++++++++++++++++++
2 files changed, 131 insertions(+), 1 deletion(-)
create mode 100644 src/calibre/gui2/dialogs/restore_library.py
diff --git a/src/calibre/gui2/actions/choose_library.py b/src/calibre/gui2/actions/choose_library.py
index fd20d88049..7034380a56 100644
--- a/src/calibre/gui2/actions/choose_library.py
+++ b/src/calibre/gui2/actions/choose_library.py
@@ -16,7 +16,6 @@ from calibre.utils.config import prefs
from calibre.gui2 import gprefs, warning_dialog, Dispatcher, error_dialog, \
question_dialog, info_dialog
from calibre.gui2.actions import InterfaceAction
-from calibre.gui2.dialogs.check_library import CheckLibraryDialog, DBCheck
class LibraryUsageStats(object): # {{{
@@ -139,6 +138,12 @@ class ChooseLibraryAction(InterfaceAction):
None, None), attr='action_check_library')
ac.triggered.connect(self.check_library, type=Qt.QueuedConnection)
self.maintenance_menu.addAction(ac)
+ ac = self.create_action(spec=(_('Restore database'), 'lt.png',
+ None, None),
+ attr='action_restore_database')
+ ac.triggered.connect(self.restore_database, type=Qt.QueuedConnection)
+ self.maintenance_menu.addAction(ac)
+
self.choose_menu.addMenu(self.maintenance_menu)
def pick_random(self, *args):
@@ -267,7 +272,17 @@ class ChooseLibraryAction(InterfaceAction):
_('Metadata will be backed up while calibre is running, at the '
'rate of approximately 1 book every three seconds.'), show=True)
+ def restore_database(self):
+ from calibre.gui2.dialogs.restore_library import restore_database
+ m = self.gui.library_view.model()
+ m.stop_metadata_backup()
+ db = m.db
+ db.prefs.disable_setting = True
+ if restore_database(db, self.gui):
+ self.gui.library_moved(db.library_path, call_close=False)
+
def check_library(self):
+ from calibre.gui2.dialogs.check_library import CheckLibraryDialog, DBCheck
self.gui.library_view.save_state()
m = self.gui.library_view.model()
m.stop_metadata_backup()
diff --git a/src/calibre/gui2/dialogs/restore_library.py b/src/calibre/gui2/dialogs/restore_library.py
new file mode 100644
index 0000000000..dd1befc11b
--- /dev/null
+++ b/src/calibre/gui2/dialogs/restore_library.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
+
+__license__ = 'GPL v3'
+__copyright__ = '2011, Kovid Goyal '
+__docformat__ = 'restructuredtext en'
+
+from PyQt4.Qt import QDialog, QLabel, QVBoxLayout, QDialogButtonBox, \
+ QProgressBar, QSize, QTimer, pyqtSignal, Qt
+
+from calibre.library.restore import Restore
+from calibre.gui2 import error_dialog, question_dialog, warning_dialog, \
+ info_dialog
+
+class DBRestore(QDialog):
+
+ update_signal = pyqtSignal(object, object)
+
+ def __init__(self, parent, library_path):
+ QDialog.__init__(self, parent)
+ self.l = QVBoxLayout()
+ self.setLayout(self.l)
+ self.l1 = QLabel(''+_('Restoring database from backups, do not'
+ ' interrupt, this will happen in two stages')+'...')
+ self.setWindowTitle(_('Restoring database'))
+ self.l.addWidget(self.l1)
+ self.pb = QProgressBar(self)
+ self.l.addWidget(self.pb)
+ self.pb.setMaximum(0)
+ self.pb.setMinimum(0)
+ self.msg = QLabel('')
+ self.l.addWidget(self.msg)
+ self.msg.setWordWrap(True)
+ self.bb = QDialogButtonBox(QDialogButtonBox.Cancel)
+ self.l.addWidget(self.bb)
+ self.bb.rejected.connect(self.reject)
+ self.resize(self.sizeHint() + QSize(100, 50))
+ self.error = None
+ self.rejected = False
+ self.library_path = library_path
+ self.update_signal.connect(self.do_update, type=Qt.QueuedConnection)
+
+ self.restorer = Restore(library_path, self)
+ self.restorer.daemon = True
+
+ # Give the metadata backup thread time to stop
+ QTimer.singleShot(2000, self.start)
+
+
+ def start(self):
+ self.restorer.start()
+ QTimer.singleShot(10, self.update)
+
+ def reject(self):
+ self.rejected = True
+ self.restorer.progress_callback = lambda x, y: x
+ QDialog.rejecet(self)
+
+ def update(self):
+ if self.restorer.is_alive():
+ QTimer.singleShot(10, self.update)
+ else:
+ self.restorer.progress_callback = lambda x, y: x
+ self.accept()
+
+ def __call__(self, msg, step):
+ self.update_signal.emit(msg, step)
+
+ def do_update(self, msg, step):
+ if msg is None:
+ self.pb.setMaximum(step)
+ else:
+ self.msg.setText(msg)
+ self.pb.setValue(step)
+
+
+def restore_database(db, parent=None):
+ if not question_dialog(parent, _('Are you sure?'), ''+
+ _('Your list of books, with all their metadata is '
+ 'stored in a single file, called a database. '
+ 'In addition, metadata for each individual '
+ 'book is stored in that books\' folder, as '
+ 'a backup.'
+ '
This operation will rebuild '
+ 'the database from the individual book '
+ 'metadata. This is useful if the '
+ 'database has been corrupted and you get a '
+ 'blank list of books. Note that restoring only '
+ 'restores books, not any settings stored in the '
+ 'database, or any custom recipes.'
+ '
Do you want to restore the database?')):
+ return False
+ db.conn.close()
+ d = DBRestore(parent, db.library_path)
+ d.exec_()
+ r = d.restorer
+ d.restorer = None
+ if d.rejected:
+ return True
+ if r.tb is not None:
+ error_dialog(parent, _('Failed'),
+ _('Restoring database failed, click Show details to see details'),
+ det_msg=r.tb, show=True)
+ else:
+ if r.errors_occurred:
+ warning_dialog(parent, _('Success'),
+ _('Restoring the database succeeded with some warnings',
+ ' click Show details to see the details.'),
+ det_msg=r.report, show=True)
+ else:
+ info_dialog(parent, _('Success'),
+ _('Restoring database was successful'), show=True,
+ show_copy_button=False)
+ return True
+
From 00f0e37bffe5f3795b3ddb79b5ace2bb18fed88e Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sat, 29 Jan 2011 09:26:51 +0000
Subject: [PATCH 10/35] Fix exception in restore_database caused by comma in a
chained message
---
src/calibre/gui2/dialogs/restore_library.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/gui2/dialogs/restore_library.py b/src/calibre/gui2/dialogs/restore_library.py
index dd1befc11b..b5af0fcadd 100644
--- a/src/calibre/gui2/dialogs/restore_library.py
+++ b/src/calibre/gui2/dialogs/restore_library.py
@@ -104,7 +104,7 @@ def restore_database(db, parent=None):
else:
if r.errors_occurred:
warning_dialog(parent, _('Success'),
- _('Restoring the database succeeded with some warnings',
+ _('Restoring the database succeeded with some warnings'
' click Show details to see the details.'),
det_msg=r.report, show=True)
else:
From 5c988788a05aec3284b29268dad1a4748cd89948 Mon Sep 17 00:00:00 2001
From: ldolse
Date: Sat, 29 Jan 2011 19:06:02 +0800
Subject: [PATCH 11/35] started scene break detection in heuristics, improved
text conversion of Sigil compatible chapter titles
---
src/calibre/ebooks/conversion/utils.py | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py
index 683eaac6d2..786bb79bae 100644
--- a/src/calibre/ebooks/conversion/utils.py
+++ b/src/calibre/ebooks/conversion/utils.py
@@ -27,7 +27,7 @@ class HeuristicProcessor(object):
self.linereg = re.compile('(?<=)', re.IGNORECASE|re.DOTALL)
self.blankreg = re.compile(r'\s*(?P]*>)\s*(?P
)', re.IGNORECASE)
self.softbreak = re.compile(r'\s*(?P]*>)\s*(?P
)', re.IGNORECASE)
- self.multi_blank = re.compile(r'(\s*]*>\s*
){2,}', re.IGNORECASE)
+ self.multi_blank = re.compile(r'(\s*]*>\s*
){2,}(?!\s*' in src[:1000]
@@ -42,8 +42,10 @@ class HeuristicProcessor(object):
" chapters. - " + unicode(chap))
return ''+chap+'
\n'
else:
- txt_chap = html2text(chap)
- txt_title = html2text(title)
+ delete_whitespace = re.compile('^\s*(?P.*?)\s*$')
+ delete_quotes = re.compile('\'\"')
+ txt_chap = delete_quotes.sub('', delete_whitespace.sub('\g', html2text(chap)))
+ txt_title = delete_quotes.sub('', delete_whitespace.sub('\g', html2text(title)))
self.html_preprocess_sections = self.html_preprocess_sections + 1
self.log.debug("marked " + unicode(self.html_preprocess_sections) +
" chapters & titles. - " + unicode(chap) + ", " + unicode(title))
@@ -416,6 +418,12 @@ class HeuristicProcessor(object):
return True
return False
+ def detect_blank_formatting(self, html):
+ blanks_before_headings = re.compile(r'(\s*]*>\s*
){2,}(?=\s*'+'\n'+'
', html)
if getattr(self.extra_opts, 'format_scene_breaks', False):
+ html = self.detect_blank_formatting(html)
+ html = self.detect_soft_breaks(html)
# Center separator lines
html = re.sub(u'<(?Pp|div)[^>]*>\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(?P([*#•=✦]+\s*)+)\s*((?P=inner3)>)?\s*((?P=inner2)>)?\s*((?P=inner1)>)?\s*(?P=outer)>', '' + '\g' + '
', html)
if not self.blanks_deleted:
From 2dafa291845e380c7a1b00bb098b82d944b90961 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sat, 29 Jan 2011 12:30:12 +0000
Subject: [PATCH 12/35] 1) restore: provide OPF with the path to the folder
containing the OPF file. Make the cover path be correct. 2) check_library.py:
change the wording of the cover messages.
---
src/calibre/library/check_library.py | 4 ++--
src/calibre/library/restore.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/calibre/library/check_library.py b/src/calibre/library/check_library.py
index b49330db3e..19ecb97308 100644
--- a/src/calibre/library/check_library.py
+++ b/src/calibre/library/check_library.py
@@ -30,8 +30,8 @@ CHECKS = [('invalid_titles', _('Invalid titles'), True, False),
('missing_formats', _('Missing book formats'), False, False),
('extra_formats', _('Extra book formats'), True, False),
('extra_files', _('Unknown files in books'), True, False),
- ('missing_covers', _('Missing covers in books'), False, True),
- ('extra_covers', _('Extra covers in books'), True, True),
+ ('missing_covers', _('Missing covers files'), False, True),
+ ('extra_covers', _('Cover files not in database'), True, True),
('failed_folders', _('Folders raising exception'), False, False)
]
diff --git a/src/calibre/library/restore.py b/src/calibre/library/restore.py
index bc2c740279..76f3c0333d 100644
--- a/src/calibre/library/restore.py
+++ b/src/calibre/library/restore.py
@@ -141,7 +141,7 @@ class Restore(Thread):
sizes = [os.path.getsize(os.path.join(dirpath, x)) for x in formats]
names = [os.path.splitext(x)[0] for x in formats]
opf = os.path.join(dirpath, 'metadata.opf')
- mi = OPF(opf).to_book_metadata()
+ mi = OPF(opf, basedir=dirpath).to_book_metadata()
timestamp = os.path.getmtime(opf)
path = os.path.relpath(dirpath, self.src_library_path).replace(os.sep,
'/')
From 60687f9ba60cb4f20aefffe991e63d0d4ed8ea61 Mon Sep 17 00:00:00 2001
From: GRiker
Date: Sat, 29 Jan 2011 06:10:08 -0700
Subject: [PATCH 13/35] GwR patch for EB600 driver, add gui_name
---
src/calibre/devices/eb600/driver.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py
index 95f6dc6ab0..1f723ce46a 100644
--- a/src/calibre/devices/eb600/driver.py
+++ b/src/calibre/devices/eb600/driver.py
@@ -21,6 +21,7 @@ from calibre.devices.usbms.driver import USBMS
class EB600(USBMS):
name = 'Netronix EB600 Device Interface'
+ gui_name = 'Netronix EB600'
description = _('Communicate with the EB600 eBook reader.')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
From bace283325092cb5757d6554fced9db27dfbbaca Mon Sep 17 00:00:00 2001
From: ldolse
Date: Sat, 29 Jan 2011 21:15:44 +0800
Subject: [PATCH 14/35] initial scene break detection
---
src/calibre/ebooks/conversion/utils.py | 40 +++++++++++++++++---------
1 file changed, 26 insertions(+), 14 deletions(-)
diff --git a/src/calibre/ebooks/conversion/utils.py b/src/calibre/ebooks/conversion/utils.py
index 786bb79bae..957950ec29 100644
--- a/src/calibre/ebooks/conversion/utils.py
+++ b/src/calibre/ebooks/conversion/utils.py
@@ -24,9 +24,10 @@ class HeuristicProcessor(object):
self.chapters_no_title = 0
self.chapters_with_title = 0
self.blanks_deleted = False
+ self.blanks_between_paragraphs = False
self.linereg = re.compile('(?<=)', re.IGNORECASE|re.DOTALL)
- self.blankreg = re.compile(r'\s*(?P]*>)\s*(?P
)', re.IGNORECASE)
- self.softbreak = re.compile(r'\s*(?P]*>)\s*(?P
)', re.IGNORECASE)
+ self.blankreg = re.compile(r'\s*(?P]*>)\s*(?P
)', re.IGNORECASE)
+ self.anyblank = re.compile(r'\s*(?P]*>)\s*(?P
)', re.IGNORECASE)
self.multi_blank = re.compile(r'(\s*]*>\s*
){2,}(?!\s*]*>\s*
){2,}(?=\s*]*>\s*
){1,}(?=\s*)(\s*]*>\s*
){1,}', re.IGNORECASE)
+
+ def markup_spacers(match):
+ blanks = match.group(0)
+ blanks = self.blankreg.sub('\n
', blanks)
+ return blanks
+ html = blanks_before_headings.sub(markup_spacers, html)
+ html = blanks_after_headings.sub(markup_spacers, html)
+ if self.html_preprocess_sections > self.min_chapters:
+ html = re.sub('(?si)^.*?(?=
', html)
+ else:
+ html = self.blankreg.sub('\n
', html)
return html
+
+
def __call__(self, html):
self.log.debug("********* Heuristic processing HTML *********")
@@ -465,25 +482,23 @@ class HeuristicProcessor(object):
#html = re.sub('
]*>', u'\u00a0
', html)
# Determine whether the document uses interleaved blank lines
- blanks_between_paragraphs = self.analyze_blanks(html)
+ self.blanks_between_paragraphs = self.analyze_blanks(html)
#self.dump(html, 'before_chapter_markup')
# detect chapters/sections to match xpath or splitting logic
if getattr(self.extra_opts, 'markup_chapter_headings', False):
- html = self.markup_chapters(html, self.totalwords, blanks_between_paragraphs)
-
- self.dump(html, 'after_chapter_markup')
+ html = self.markup_chapters(html, self.totalwords, self.blanks_between_paragraphs)
if getattr(self.extra_opts, 'italicize_common_cases', False):
html = self.markup_italicis(html)
# If more than 40% of the lines are empty paragraphs and the user has enabled delete
# blank paragraphs then delete blank lines to clean up spacing
- if blanks_between_paragraphs and getattr(self.extra_opts, 'delete_blank_paragraphs', False):
+ if self.blanks_between_paragraphs and getattr(self.extra_opts, 'delete_blank_paragraphs', False):
self.log.debug("deleting blank lines")
self.blanks_deleted = True
- html = self.multi_blank.sub('\n
', html)
+ html = self.multi_blank.sub('\n
', html)
html = self.blankreg.sub('', html)
# Determine line ending type
@@ -538,13 +553,10 @@ class HeuristicProcessor(object):
html = self.detect_blank_formatting(html)
html = self.detect_soft_breaks(html)
# Center separator lines
- html = re.sub(u'<(?Pp|div)[^>]*>\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(?P([*#•=✦]+\s*)+)\s*((?P=inner3)>)?\s*((?P=inner2)>)?\s*((?P=inner1)>)?\s*(?P=outer)>', '' + '\g' + '
', html)
- if not self.blanks_deleted:
- html = self.multi_blank.sub('\n
', html)
+ html = re.sub(u'<(?Pp|div)[^>]*>\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(?P([*#•=✦]+\s*)+)\s*((?P=inner3)>)?\s*((?P=inner2)>)?\s*((?P=inner1)>)?\s*(?P=outer)>', '' + '\g' + '
', html)
html = re.sub(']*>\s*
', '
', html)
if self.deleted_nbsps:
# put back non-breaking spaces in empty paragraphs to preserve original formatting
- html = self.blankreg.sub('\n'+r'\g'+u'\u00a0'+r'\g', html)
- html = self.softbreak.sub('\n'+r'\g'+u'\u00a0'+r'\g', html)
+ html = self.anyblank.sub('\n'+r'\g'+u'\u00a0'+r'\g', html)
return html
From 22684e7617d4012c7c533db72072b9205a4ba40a Mon Sep 17 00:00:00 2001
From: John Schember
Date: Sat, 29 Jan 2011 08:40:21 -0500
Subject: [PATCH 15/35] Heuristic: Italicize common cases, reduce false
positives.
---
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 7882d82d47..ad2214fcb5 100644
--- a/src/calibre/ebooks/conversion/utils.py
+++ b/src/calibre/ebooks/conversion/utils.py
@@ -155,7 +155,7 @@ class HeuristicProcessor(object):
]
for word in ITALICIZE_WORDS:
- html = re.sub(r'(?<=\s|>)' + word + r'(?=\s|<)', '%s' % word, html)
+ html = re.sub(r'(?<=\s|>)' + re.escape(word) + r'(?=\s|<)', '%s' % word, html)
for pat in ITALICIZE_STYLE_PATS:
html = re.sub(pat, lambda mo: '%s' % mo.group('words'), html)
From c98a122539751fc4fd56d4f32ee7bd4e06313f8b Mon Sep 17 00:00:00 2001
From: ldolse
Date: Sat, 29 Jan 2011 21:53:53 +0800
Subject: [PATCH 16/35] ...
---
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 957950ec29..5beefb5bd9 100644
--- a/src/calibre/ebooks/conversion/utils.py
+++ b/src/calibre/ebooks/conversion/utils.py
@@ -554,7 +554,7 @@ class HeuristicProcessor(object):
html = self.detect_soft_breaks(html)
# Center separator lines
html = re.sub(u'<(?Pp|div)[^>]*>\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(<(?Pfont|span|[ibu])[^>]*>)?\s*(?P([*#•=✦]+\s*)+)\s*((?P=inner3)>)?\s*((?P=inner2)>)?\s*((?P=inner1)>)?\s*(?P=outer)>', '' + '\g' + '
', html)
- html = re.sub(']*>\s*
', '
', html)
+ #html = re.sub(']*>\s*
', '
', html)
if self.deleted_nbsps:
# put back non-breaking spaces in empty paragraphs to preserve original formatting
From 6f8cdbe41ae1e3d51605707dc060961cf037e177 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 29 Jan 2011 08:30:54 -0700
Subject: [PATCH 17/35] ...
---
src/calibre/gui2/dialogs/check_library.py | 34 +++++++++++++----------
1 file changed, 20 insertions(+), 14 deletions(-)
diff --git a/src/calibre/gui2/dialogs/check_library.py b/src/calibre/gui2/dialogs/check_library.py
index bd665a7e2e..b6b15d8be8 100644
--- a/src/calibre/gui2/dialogs/check_library.py
+++ b/src/calibre/gui2/dialogs/check_library.py
@@ -74,21 +74,27 @@ class DBCheck(QDialog):
self.reject()
def start_load(self):
- self.conn.close()
- self.pb.setMaximum(self.count)
- self.pb.setValue(0)
- self.msg.setText(_('Loading database from SQL'))
- self.db.conn.close()
- self.ndbpath = PersistentTemporaryFile('.db')
- self.ndbpath.close()
- self.ndbpath = self.ndbpath.name
- t = DBThread(self.ndbpath, False)
- t.connect()
- self.conn = t.conn
- self.conn.execute('create temporary table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)')
- self.conn.commit()
+ try:
+ self.conn.close()
+ self.pb.setMaximum(self.count)
+ self.pb.setValue(0)
+ self.msg.setText(_('Loading database from SQL'))
+ self.db.conn.close()
+ self.ndbpath = PersistentTemporaryFile('.db')
+ self.ndbpath.close()
+ self.ndbpath = self.ndbpath.name
+ t = DBThread(self.ndbpath, False)
+ t.connect()
+ self.conn = t.conn
+ self.conn.execute('create temporary table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)')
+ self.conn.commit()
+
+ QTimer.singleShot(0, self.do_one_load)
+ except Exception, e:
+ import traceback
+ self.error = (as_unicode(e), traceback.format_exc())
+ self.reject()
- QTimer.singleShot(0, self.do_one_load)
def do_one_load(self):
if self.rejected:
From af6ce250b3fc3e7c636f0f8b6bf8b3b52ecae92b Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 29 Jan 2011 09:17:12 -0700
Subject: [PATCH 18/35] Fix device job error dialog not showing actual error
---
src/calibre/gui2/device.py | 2 +-
src/calibre/gui2/dialogs/message_box.py | 29 +++++++++++++++++--------
2 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py
index 1cf0fa5d67..020ba7fb32 100644
--- a/src/calibre/gui2/device.py
+++ b/src/calibre/gui2/device.py
@@ -687,7 +687,7 @@ class DeviceMixin(object): # {{{
except:
pass
if not self.device_error_dialog.isVisible():
- self.device_error_dialog.setDetailedText(job.details)
+ self.device_error_dialog.set_details(job.details)
self.device_error_dialog.show()
# Device connected {{{
diff --git a/src/calibre/gui2/dialogs/message_box.py b/src/calibre/gui2/dialogs/message_box.py
index 565fb147fc..9d586ce28d 100644
--- a/src/calibre/gui2/dialogs/message_box.py
+++ b/src/calibre/gui2/dialogs/message_box.py
@@ -45,14 +45,12 @@ class MessageBox(QDialog, Ui_Dialog):
self.ctc_button.clicked.connect(self.copy_to_clipboard)
- if det_msg:
- self.show_det_msg = _('Show &details')
- self.hide_det_msg = _('Hide &details')
- self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole)
- self.det_msg_toggle.clicked.connect(self.toggle_det_msg)
- self.det_msg_toggle.setToolTip(
- _('Show detailed information about this error'))
-
+ self.show_det_msg = _('Show &details')
+ self.hide_det_msg = _('Hide &details')
+ self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole)
+ self.det_msg_toggle.clicked.connect(self.toggle_det_msg)
+ self.det_msg_toggle.setToolTip(
+ _('Show detailed information about this error'))
self.copy_action = QAction(self)
self.addAction(self.copy_action)
@@ -66,10 +64,14 @@ class MessageBox(QDialog, Ui_Dialog):
else:
self.bb.button(self.bb.Ok).setDefault(True)
+ if not det_msg:
+ self.det_msg_toggle.setVisible(False)
+
self.do_resize()
+
def toggle_det_msg(self, *args):
- vis = self.det_msg.isVisible()
+ vis = unicode(self.det_msg_toggle.text()) == self.hide_det_msg
self.det_msg_toggle.setText(self.show_det_msg if vis else
self.hide_det_msg)
self.det_msg.setVisible(not vis)
@@ -100,6 +102,15 @@ class MessageBox(QDialog, Ui_Dialog):
self.bb.button(self.bb.Ok).setFocus(Qt.OtherFocusReason)
return ret
+ def set_details(self, msg):
+ if not msg:
+ msg = ''
+ self.det_msg.setPlainText(msg)
+ self.det_msg_toggle.setText(self.show_det_msg)
+ self.det_msg_toggle.setVisible(bool(msg))
+ self.det_msg.setVisible(False)
+ self.do_resize()
+
if __name__ == '__main__':
app = QApplication([])
from calibre.gui2 import question_dialog
From ac8ea5b16babcb9ef55025ad005e3cd86fc6343d Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 29 Jan 2011 09:28:13 -0700
Subject: [PATCH 19/35] Fix #8646 (conversion of kindle format to epub fails
ValueError: All strings must be XML compatible: Unicode or ASCII, no NULL
bytes)
---
src/calibre/ebooks/mobi/reader.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py
index 2f397006a1..0ae3c9ac9d 100644
--- a/src/calibre/ebooks/mobi/reader.py
+++ b/src/calibre/ebooks/mobi/reader.py
@@ -488,7 +488,7 @@ class MobiReader(object):
def remove_random_bytes(self, html):
- return re.sub('\x14|\x15|\x19|\x1c|\x1d|\xef|\x12|\x13|\xec|\x08',
+ return re.sub('\x14|\x15|\x19|\x1c|\x1d|\xef|\x12|\x13|\xec|\x08|\x01|\x02|\x03|\x04|\x05|\x06|\x07',
'', html)
def ensure_unit(self, raw, unit='px'):
From 7239c3af0134a9f1ea8150f0a8493e9a65acfe8d Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 29 Jan 2011 09:49:34 -0700
Subject: [PATCH 20/35] Fix #8656 (Attempting to merge two books, getting
ValueError)
---
src/calibre/library/database2.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 3c6d4016f2..3fc16e99b4 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -1549,7 +1549,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
elif mi.cover is not None:
if os.access(mi.cover, os.R_OK):
with lopen(mi.cover, 'rb') as f:
- doit(self.set_cover, id, f, commit=False)
+ raw = f.read()
+ if raw:
+ doit(self.set_cover, id, raw, commit=False)
if mi.tags:
doit(self.set_tags, id, mi.tags, notify=False, commit=False)
if mi.comments:
From 0cd6535a0396943f1722b7261232294346c79fc7 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 29 Jan 2011 09:57:21 -0700
Subject: [PATCH 21/35] Fix #8662 (Updated recipe for Vijesti)
---
resources/recipes/vijesti.recipe | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/resources/recipes/vijesti.recipe b/resources/recipes/vijesti.recipe
index c901755b78..d650bdec06 100644
--- a/resources/recipes/vijesti.recipe
+++ b/resources/recipes/vijesti.recipe
@@ -1,6 +1,6 @@
__license__ = 'GPL v3'
-__copyright__ = '2009-2010, Darko Miletic '
+__copyright__ = '2009-2011, Darko Miletic '
'''
vijesti.me
@@ -18,12 +18,16 @@ class Vijesti(BasicNewsRecipe):
oldest_article = 2
max_articles_per_feed = 150
no_stylesheets = True
- encoding = 'cp1250'
+ encoding = 'utf8'
use_embedded_content = False
language = 'sr'
publication_type = 'newspaper'
- masthead_url = 'http://www.vijesti.me/img/logo.gif'
- extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
+ extra_css = """
+ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
+ @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
+ body{font-family: Georgia,"Times New Roman",Times,serif1,serif}
+ .articledescription,.article,.chapter{font-family: sans1, sans-serif}
+ """
conversion_options = {
'comment' : description
@@ -34,11 +38,11 @@ class Vijesti(BasicNewsRecipe):
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
- keep_only_tags = [dict(name='div', attrs={'id':'mainnews'})]
+ keep_only_tags = [dict(name='div', attrs={'id':['article_intro_text','article_text']})]
remove_tags = [dict(name=['object','link','embed','form'])]
- feeds = [(u'Sve vijesti', u'http://www.vijesti.me/rss.php' )]
+ feeds = [(u'Sve vijesti', u'http://www.vijesti.me/rss/' )]
def preprocess_html(self, soup):
return self.adeify_images(soup)
From 7764b0b0074f24faec1711e090787471a5a0cd8d Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 29 Jan 2011 10:16:51 -0700
Subject: [PATCH 22/35] LIT Input: Handle LIT files that have all their text in
a single tag and use entities
---
src/calibre/ebooks/lit/input.py | 5 ++++-
src/calibre/gui2/dialogs/restore_library.py | 2 +-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/calibre/ebooks/lit/input.py b/src/calibre/ebooks/lit/input.py
index ff901c3715..9ccbba543f 100644
--- a/src/calibre/ebooks/lit/input.py
+++ b/src/calibre/ebooks/lit/input.py
@@ -38,13 +38,16 @@ class LITInput(InputFormatPlugin):
if len(body) == 1 and body[0].tag == XHTML('pre'):
pre = body[0]
from calibre.ebooks.txt.processor import convert_basic, preserve_spaces, \
- separate_paragraphs_single_line
+ separate_paragraphs_single_line
+ from calibre.ebooks.chardet import xml_to_unicode
from lxml import etree
import copy
html = separate_paragraphs_single_line(pre.text)
html = preserve_spaces(html)
html = convert_basic(html).replace('',
''%XHTML_NS)
+ html = xml_to_unicode(html, strip_encoding_pats=True,
+ resolve_entities=True)[0]
root = etree.fromstring(html)
body = XPath('//h:body')(root)
pre.tag = XHTML('div')
diff --git a/src/calibre/gui2/dialogs/restore_library.py b/src/calibre/gui2/dialogs/restore_library.py
index b5af0fcadd..a57d6c86c1 100644
--- a/src/calibre/gui2/dialogs/restore_library.py
+++ b/src/calibre/gui2/dialogs/restore_library.py
@@ -21,7 +21,7 @@ class DBRestore(QDialog):
self.l = QVBoxLayout()
self.setLayout(self.l)
self.l1 = QLabel(''+_('Restoring database from backups, do not'
- ' interrupt, this will happen in two stages')+'...')
+ ' interrupt, this will happen in three stages')+'...')
self.setWindowTitle(_('Restoring database'))
self.l.addWidget(self.l1)
self.pb = QProgressBar(self)
From b332570f2f2bfccea2140d2375a32be9c162e56c Mon Sep 17 00:00:00 2001
From: John Schember
Date: Sat, 29 Jan 2011 12:31:01 -0500
Subject: [PATCH 23/35] RTF Output: Fixes.
---
src/calibre/ebooks/rtf/rtfml.py | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/src/calibre/ebooks/rtf/rtfml.py b/src/calibre/ebooks/rtf/rtfml.py
index 1fb14eb06f..facc745a5c 100644
--- a/src/calibre/ebooks/rtf/rtfml.py
+++ b/src/calibre/ebooks/rtf/rtfml.py
@@ -30,6 +30,7 @@ TAGS = {
'h4': '\\b \\par \\pard \\hyphpar',
'h5': '\\b \\par \\pard \\hyphpar',
'h6': '\\b \\par \\pard \\hyphpar',
+ 'i': '\\i',
'li': '\\par \\pard \\hyphpar \t',
'p': '\\par \\pard \\hyphpar \t',
'sub': '\\sub',
@@ -117,6 +118,7 @@ class RTFMLizer(object):
self.log.debug('Converting %s to RTF markup...' % item.href)
content = unicode(etree.tostring(item.data, encoding=unicode))
content = self.remove_newlines(content)
+ content = self.remove_tabs(content)
content = etree.fromstring(content)
stylizer = Stylizer(content, item.href, self.oeb_book, self.opts, self.opts.output_profile)
output += self.dump_text(content.find(XHTML('body')), stylizer)
@@ -133,6 +135,12 @@ class RTFMLizer(object):
text = text.replace('\r', ' ')
return text
+
+ def remove_tabs(self, text):
+ self.log.debug('\Replace tabs with space for processing...')
+ text = text.replace('\t', ' ')
+
+ return text
def header(self):
return u'{\\rtf1{\\info{\\title %s}{\\author %s}}\\ansi\\ansicpg1252\\deff0\\deflang1033' % (self.oeb_book.metadata.title[0].value, authors_to_string([x.value for x in self.oeb_book.metadata.creator]))
@@ -175,14 +183,13 @@ class RTFMLizer(object):
text = re.sub('(?m)[ ]+$', '', text)
# Remove excessive newlines
- #text = re.sub('%s{1,1}' % os.linesep, '%s%s' % (os.linesep, os.linesep), text)
text = re.sub('%s{3,}' % os.linesep, '%s%s' % (os.linesep, os.linesep), text)
# Remove excessive spaces
text = re.sub('[ ]{2,}', ' ', text)
+ text = re.sub('\t{2,}', '\t', text)
text = re.sub(r'(\{\\line \}\s*){3,}', r'{\\line }{\\line }', text)
- #text = re.compile(r'(\{\\line \}\s*)+(?P}*)\s*\{\\par').sub(lambda mo: r'%s{\\par' % mo.group('brackets'), text)
# Remove non-breaking spaces
text = text.replace(u'\xa0', ' ')
@@ -245,7 +252,7 @@ class RTFMLizer(object):
tag_stack.append(style_tag)
# Proccess tags that contain text.
- if hasattr(elem, 'text') and elem.text != None and elem.text.strip() != '':
+ if hasattr(elem, 'text') and elem.text:
text += txt2rtf(elem.text)
for item in elem:
@@ -260,7 +267,7 @@ class RTFMLizer(object):
if single_tag_end:
text += single_tag_end
- if hasattr(elem, 'tail') and elem.tail != None and elem.tail.strip() != '':
+ if hasattr(elem, 'tail') and elem.tail:
if 'block' in tag_stack:
text += '%s' % txt2rtf(elem.tail)
else:
From 565c888b4d70e1ce069a02256a8ec4f36994def1 Mon Sep 17 00:00:00 2001
From: John Schember
Date: Sat, 29 Jan 2011 13:17:13 -0500
Subject: [PATCH 24/35] Fix bug #8640: Missing spaces arouond italic elements.
---
src/calibre/ebooks/rtf/rtfml.py | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/calibre/ebooks/rtf/rtfml.py b/src/calibre/ebooks/rtf/rtfml.py
index facc745a5c..383d1547ec 100644
--- a/src/calibre/ebooks/rtf/rtfml.py
+++ b/src/calibre/ebooks/rtf/rtfml.py
@@ -178,10 +178,6 @@ class RTFMLizer(object):
return (hex_string, width, height)
def clean_text(self, text):
- # Remove excess spaces at beginning and end of lines
- text = re.sub('(?m)^[ ]+', '', text)
- text = re.sub('(?m)[ ]+$', '', text)
-
# Remove excessive newlines
text = re.sub('%s{3,}' % os.linesep, '%s%s' % (os.linesep, os.linesep), text)
@@ -189,6 +185,7 @@ class RTFMLizer(object):
text = re.sub('[ ]{2,}', ' ', text)
text = re.sub('\t{2,}', '\t', text)
+ # Remove excessive line breaks
text = re.sub(r'(\{\\line \}\s*){3,}', r'{\\line }{\\line }', text)
# Remove non-breaking spaces
From f3ecd4f9ec4b43c72a41cfd30494f89adbf39846 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 29 Jan 2011 11:17:19 -0700
Subject: [PATCH 25/35] Improve Ledevoir
---
resources/recipes/ledevoir.recipe | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/resources/recipes/ledevoir.recipe b/resources/recipes/ledevoir.recipe
index c54f21c7ec..bc473be181 100644
--- a/resources/recipes/ledevoir.recipe
+++ b/resources/recipes/ledevoir.recipe
@@ -9,6 +9,8 @@ __description__ = 'Canadian Paper '
http://www.ledevoir.com/
'''
+import re
+
from calibre.web.feeds.news import BasicNewsRecipe
class ledevoir(BasicNewsRecipe):
@@ -32,6 +34,8 @@ class ledevoir(BasicNewsRecipe):
remove_javascript = True
no_stylesheets = True
+ preprocess_regexps = [(re.compile(r'(title|alt)=".*?>.*?"', re.DOTALL), lambda m: '')]
+
keep_only_tags = [
dict(name='div', attrs={'id':'article'}),
dict(name='ul', attrs={'id':'ariane'})
From d9e86d20a12c5fe146aaf30f9e5f8757a043bbf3 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 29 Jan 2011 17:58:17 -0700
Subject: [PATCH 26/35] BBC Mundo by Luis Hernandez
---
resources/recipes/bbc_es.recipe | 53 +++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 resources/recipes/bbc_es.recipe
diff --git a/resources/recipes/bbc_es.recipe b/resources/recipes/bbc_es.recipe
new file mode 100644
index 0000000000..b2b6192620
--- /dev/null
+++ b/resources/recipes/bbc_es.recipe
@@ -0,0 +1,53 @@
+__license__ = 'GPL v3'
+__author__ = 'Luis Hernandez'
+__copyright__ = 'Luis Hernandez'
+__version__ = 'v1.0'
+__date__ = '29 January 2011'
+
+'''
+http://www.bbc.co.uk/mundo/
+'''
+
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class AdvancedUserRecipe1294946868(BasicNewsRecipe):
+
+ title = u'BBC Mundo'
+ publisher = u'BBC'
+
+ __author__ = 'Luis Hernandez'
+ description = 'BBC World for spanish readers'
+
+ cover_url = 'http://1.bp.blogspot.com/_NHiOjk_uZwU/TEYy7IJAdAI/AAAAAAAABP8/coAE-pJ7_5E/s1600/bbcmundo_h.png'
+ oldest_article = 2
+ max_articles_per_feed = 100
+
+ remove_javascript = True
+ no_stylesheets = True
+ use_embedded_content = False
+
+ language = 'es'
+ remove_empty_feeds = True
+ encoding = 'UTF-8'
+ timefmt = '[%a, %d %b, %Y]'
+
+ remove_tags_before = dict(name='div' , attrs={'class':['g-group']})
+ remove_tags_after = dict(name='div' , attrs={'class':[' g-w8']})
+
+ remove_tags = [
+ dict(name='ul', attrs={'class':['document-tools blq-clearfix','blq-clearfix']})
+ ,dict(name='div', attrs={'class':['box bx-quote-bubble','socialmedia-links','list li-carousel','list li-plain rolling-news','list li-plain','box bx-livestats','li-tab content','list li-relatedlinks','list li-relatedinternetlinks']})
+ ]
+
+ feeds = [
+ (u'Portada' , u'http://www.bbc.co.uk/mundo/index.xml')
+ ,(u'Ultimas Noticias' , u'http://www.bbc.co.uk/mundo/ultimas_noticias/index.xml')
+ ,(u'Internacional' , u'http://www.bbc.co.uk/mundo/temas/internacional/index.xml')
+ ,(u'Economia' , u'http://www.bbc.co.uk/mundo/temas/economia/index.xml')
+ ,(u'America Latina' , u'http://www.bbc.co.uk/mundo/temas/america_latina/index.xml')
+ ,(u'Ciencia' , u'http://www.bbc.co.uk/mundo/temas/ciencia/index.xml')
+ ,(u'Salud' , u'http://www.bbc.co.uk/mundo/temas/salud/index.xml')
+ ,(u'Tecnologia' , u'http://www.bbc.co.uk/mundo/temas/tecnologia/index.xml')
+ ,(u'Cultura' , u'http://www.bbc.co.uk/mundo/temas/cultura/index.xml')
+ ]
+
From d51fc7c602165e6ba328d1cfb1fe688de42999f0 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 29 Jan 2011 18:01:18 -0700
Subject: [PATCH 27/35] ...
---
src/calibre/manual/faq.rst | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst
index 15e836940e..849ded82c9 100644
--- a/src/calibre/manual/faq.rst
+++ b/src/calibre/manual/faq.rst
@@ -295,7 +295,9 @@ e-ink screen :)
Note that in the case of the Kindle, there is a way to manipulate collections via USB,
but it requires that the Kindle be rebooted *every time* it is disconnected from the computer, for the
changes to the collections to be recognized. As such, it is unlikely that
-any |app| developers will ever feel motivated enough to support it.
+any |app| developers will ever feel motivated enough to support it. There is however, a |app| plugin
+that allows you to create collections on your Kindle from the |app| metadata. It is available
+`here `_.
Library Management
------------------
From 25129f9a7115965fb5d9a123cfdc6f9b219dba36 Mon Sep 17 00:00:00 2001
From: John Schember
Date: Sat, 29 Jan 2011 23:02:34 -0500
Subject: [PATCH 28/35] Fix bug #8641: Retain html headings as rtf headings
when converting to rtf. Rewrite and simplify large parts of markup output.
---
src/calibre/ebooks/rtf/rtfml.py | 51 ++++++++++++++++++---------------
1 file changed, 28 insertions(+), 23 deletions(-)
diff --git a/src/calibre/ebooks/rtf/rtfml.py b/src/calibre/ebooks/rtf/rtfml.py
index 383d1547ec..6b3030256d 100644
--- a/src/calibre/ebooks/rtf/rtfml.py
+++ b/src/calibre/ebooks/rtf/rtfml.py
@@ -24,15 +24,15 @@ from calibre.utils.magick.draw import save_cover_data_to, identify_data
TAGS = {
'b': '\\b',
'del': '\\deleted',
- 'h1': '\\b \\par \\pard \\hyphpar',
- 'h2': '\\b \\par \\pard \\hyphpar',
- 'h3': '\\b \\par \\pard \\hyphpar',
- 'h4': '\\b \\par \\pard \\hyphpar',
- 'h5': '\\b \\par \\pard \\hyphpar',
- 'h6': '\\b \\par \\pard \\hyphpar',
+ 'h1': '\\s1 \\afs32',
+ 'h2': '\\s2 \\afs28',
+ 'h3': '\\s3 \\afs28',
+ 'h4': '\\s4 \\afs23',
+ 'h5': '\\s5 \\afs23',
+ 'h6': '\\s6 \\afs21',
'i': '\\i',
- 'li': '\\par \\pard \\hyphpar \t',
- 'p': '\\par \\pard \\hyphpar \t',
+ 'li': '\t',
+ 'p': '\t',
'sub': '\\sub',
'sup': '\\super',
'u': '\\ul',
@@ -40,15 +40,9 @@ TAGS = {
SINGLE_TAGS = {
'br': '\n{\\line }\n',
- 'div': '\n{\\line }\n',
-}
-
-SINGLE_TAGS_END = {
- 'div': '\n{\\line }\n',
}
STYLES = [
- ('display', {'block': '\\par \\pard \\hyphpar'}),
('font-weight', {'bold': '\\b', 'bolder': '\\b'}),
('font-style', {'italic': '\\i'}),
('text-align', {'center': '\\qc', 'left': '\\ql', 'right': '\\qr'}),
@@ -56,6 +50,7 @@ STYLES = [
]
BLOCK_TAGS = [
+ 'div',
'p',
'h1',
'h2',
@@ -113,7 +108,7 @@ class RTFMLizer(object):
stylizer = Stylizer(item.data, item.href, self.oeb_book,
self.opts, self.opts.output_profile)
output += self.dump_text(item.data.find(XHTML('body')), stylizer)
- output += '{\\page } '
+ output += '{\\page }'
for item in self.oeb_book.spine:
self.log.debug('Converting %s to RTF markup...' % item.href)
content = unicode(etree.tostring(item.data, encoding=unicode))
@@ -122,6 +117,7 @@ class RTFMLizer(object):
content = etree.fromstring(content)
stylizer = Stylizer(content, item.href, self.oeb_book, self.opts, self.opts.output_profile)
output += self.dump_text(content.find(XHTML('body')), stylizer)
+ output += '{\\page }'
output += self.footer()
output = self.insert_images(output)
output = self.clean_text(output)
@@ -143,7 +139,16 @@ class RTFMLizer(object):
return text
def header(self):
- return u'{\\rtf1{\\info{\\title %s}{\\author %s}}\\ansi\\ansicpg1252\\deff0\\deflang1033' % (self.oeb_book.metadata.title[0].value, authors_to_string([x.value for x in self.oeb_book.metadata.creator]))
+ header = u'{\\rtf1{\\info{\\title %s}{\\author %s}}\\ansi\\ansicpg1252\\deff0\\deflang1033\n' % (self.oeb_book.metadata.title[0].value, authors_to_string([x.value for x in self.oeb_book.metadata.creator]))
+ return header + \
+ '{\\fonttbl{\\f0\\froman\\fprq2\\fcharset128 Times New Roman;}{\\f1\\froman\\fprq2\\fcharset128 Times New Roman;}{\\f2\\fswiss\\fprq2\\fcharset128 Arial;}{\\f3\\fnil\\fprq2\\fcharset128 Arial;}{\\f4\\fnil\\fprq2\\fcharset128 MS Mincho;}{\\f5\\fnil\\fprq2\\fcharset128 Tahoma;}{\\f6\\fnil\\fprq0\\fcharset128 Tahoma;}}\n' \
+ '{\\stylesheet{\\ql \\li0\\ri0\\nowidctlpar\\wrapdefault\\faauto\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\af25\\afs24\\alang1033 \\ltrch\\fcs0 \\fs24\\lang1033\\langfe255\\cgrid\\langnp1033\\langfenp255 \\snext0 Normal;}\n' \
+ '{\\s1\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel0\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs32\\alang1033 \\ltrch\\fcs0 \\b\\fs32\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink21 heading 1;}\n' \
+ '{\\s2\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel1\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\ai\\af0\\afs28\\alang1033 \\ltrch\\fcs0 \\b\\i\\fs28\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink22 heading 2;}\n' \
+ '{\\s3\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel2\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs28\\alang1033 \\ltrch\\fcs0 \\b\\fs28\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink23 heading 3;}\n' \
+ '{\\s4\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel3\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\ai\\af0\\afs23\\alang1033 \\ltrch\\fcs0\\b\\i\\fs23\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink24 heading 4;}\n' \
+ '{\\s5\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel4\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs23\\alang1033 \\ltrch\\fcs0 \\b\\fs23\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink25 heading 5;}\n' \
+ '{\\s6\\ql \\li0\\ri0\\sb240\\sa120\\keepn\\nowidctlpar\\wrapdefault\\faauto\\outlinelevel5\\rin0\\lin0\\itap0 \\rtlch\\fcs1 \\ab\\af0\\afs21\\alang1033 \\ltrch\\fcs0 \\b\\fs21\\lang1033\\langfe255\\loch\\f1\\hich\\af1\\dbch\\af26\\cgrid\\langnp1033\\langfenp255 \\sbasedon15 \\snext16 \\slink26 heading 6;}}\n'
def footer(self):
return ' }'
@@ -184,6 +189,7 @@ class RTFMLizer(object):
# Remove excessive spaces
text = re.sub('[ ]{2,}', ' ', text)
text = re.sub('\t{2,}', '\t', text)
+ text = re.sub('\t ', '\t', text)
# Remove excessive line breaks
text = re.sub(r'(\{\\line \}\s*){3,}', r'{\\line }{\\line }', text)
@@ -226,7 +232,7 @@ class RTFMLizer(object):
block_start = ''
block_end = ''
if 'block' not in tag_stack:
- block_start = '{\\par \\pard \\hyphpar '
+ block_start = '{\\par\\pard\\hyphpar '
block_end = '}'
text += '%s SPECIAL_IMAGE-%s-REPLACE_ME %s' % (block_start, src, block_end)
@@ -258,16 +264,15 @@ class RTFMLizer(object):
for i in range(0, tag_count):
end_tag = tag_stack.pop()
if end_tag != 'block':
- text += u'}'
-
- single_tag_end = SINGLE_TAGS_END.get(tag, None)
- if single_tag_end:
- text += single_tag_end
+ if tag in BLOCK_TAGS:
+ text += u'\\par\\pard\\plain\\hyphpar}'
+ else:
+ text += u'}'
if hasattr(elem, 'tail') and elem.tail:
if 'block' in tag_stack:
text += '%s' % txt2rtf(elem.tail)
else:
- text += '{\\par \\pard \\hyphpar %s}' % txt2rtf(elem.tail)
+ text += '{\\par\\pard\\hyphpar %s}' % txt2rtf(elem.tail)
return text
From 76837bbd7e74163668b5a1cf0c158dcd8ae375fe Mon Sep 17 00:00:00 2001
From: John Schember
Date: Sun, 30 Jan 2011 10:00:44 -0500
Subject: [PATCH 29/35] TXT Output: Handle scene breaks defined by margin-top
in the css of the OEB.
---
src/calibre/ebooks/txt/txtml.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/calibre/ebooks/txt/txtml.py b/src/calibre/ebooks/txt/txtml.py
index 00992a8612..bf33e5540a 100644
--- a/src/calibre/ebooks/txt/txtml.py
+++ b/src/calibre/ebooks/txt/txtml.py
@@ -222,6 +222,8 @@ class TXTMLizer(object):
# Scene breaks.
if tag == 'hr':
text.append('\n\n* * *\n\n')
+ elif style['margin-top']:
+ text.append('\n\n' + '\n' * round(style['margin-top']))
# Process tags that contain text.
if hasattr(elem, 'text') and elem.text:
From 8437c89d4d9e5019667806d1a2a6ed86803bf220 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sun, 30 Jan 2011 08:52:25 -0700
Subject: [PATCH 30/35] Restore functioning of CALIBRE_DEVELOP_FROM in linux
build
---
setup/installer/linux/freeze2.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/setup/installer/linux/freeze2.py b/setup/installer/linux/freeze2.py
index bd8463b1a7..4d5d1a63e7 100644
--- a/setup/installer/linux/freeze2.py
+++ b/setup/installer/linux/freeze2.py
@@ -360,6 +360,9 @@ class LinuxFreeze(Command):
def main():
try:
sys.argv[0] = sys.calibre_basename
+ dfv = os.environ.get('CALIBRE_DEVELOP_FROM', None)
+ if dfv and os.path.exists(dfv):
+ sys.path.insert(0, os.path.abspath(dfv))
set_default_encoding()
set_helper()
set_qt_plugin_path()
From 8d688ecf02921f5058f44447afa51fad485a8c5a Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sun, 30 Jan 2011 10:50:07 -0700
Subject: [PATCH 31/35] Add help to check library dialog
---
src/calibre/gui2/dialogs/check_library.py | 71 ++++++++++++++++++++---
1 file changed, 64 insertions(+), 7 deletions(-)
diff --git a/src/calibre/gui2/dialogs/check_library.py b/src/calibre/gui2/dialogs/check_library.py
index b6b15d8be8..f8eff310f6 100644
--- a/src/calibre/gui2/dialogs/check_library.py
+++ b/src/calibre/gui2/dialogs/check_library.py
@@ -7,7 +7,7 @@ import os, shutil
from PyQt4.Qt import QDialog, QVBoxLayout, QHBoxLayout, QTreeWidget, QLabel, \
QPushButton, QDialogButtonBox, QApplication, QTreeWidgetItem, \
- QLineEdit, Qt, QProgressBar, QSize, QTimer
+ QLineEdit, Qt, QProgressBar, QSize, QTimer, QIcon, QTextEdit
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.library.check_library import CheckLibrary, CHECKS
@@ -16,7 +16,7 @@ from calibre import prints, as_unicode
from calibre.ptempfile import PersistentTemporaryFile
from calibre.library.sqlite import DBThread, OperationalError
-class DBCheck(QDialog):
+class DBCheck(QDialog): # {{{
def __init__(self, parent, db):
QDialog.__init__(self, parent)
@@ -134,7 +134,7 @@ class DBCheck(QDialog):
def reject(self):
self.rejected = True
QDialog.reject(self)
-
+# }}}
class Item(QTreeWidgetItem):
pass
@@ -146,9 +146,65 @@ class CheckLibraryDialog(QDialog):
self.db = db
self.setWindowTitle(_('Check Library -- Problems Found'))
+ self.setWindowIcon(QIcon(I('debug.png')))
- self._layout = QVBoxLayout(self)
- self.setLayout(self._layout)
+ self._tl = QHBoxLayout()
+ self._layout = QVBoxLayout()
+ self.setLayout(self._tl)
+ self._tl.addLayout(self._layout)
+ self.helpw = QTextEdit(self)
+ self._tl.addWidget(self.helpw)
+ self.helpw.setReadOnly(True)
+ self.helpw.setText(_('''\
+ Help
+
+ calibre stores the list of your books and their metadata in a
+ database. The actual book files and covers are stored as normal
+ files in the calibre library folder. The database contains a list of the files
+ and cover belonging to each book entry. This tool checks that the
+ information in the database and the actual files on your computer
+ match.
+
+ The result of each type of check is shown to the left. The various
+ checks are:
+
+
+ - Invalid titles: These represent book entries in the
+ database that have no corresponding files in the calibre library.
+ - Extra titles: These represent extra files ni your calibre
+ library that have no corresponding entries in the database
+ - Invalid authors: These represent authors that exist in the
+ database, but do not have corresponding folders in the calibre
+ library
+ - Extra authors: These represent extra author folders in the
+ calibre library that do not have entries in the database
+ - Unknown files in books: These represent extra files in the
+ folder of each book that do not correspond to a know format or cover
+ for that book in the database.
+ - Missing cover files: These represent books that are marked
+ as having covers in the database but whose actual cover files are
+ missing.
+ - Cover files not in database: These represent books whose
+ cover files are present but are marked as not having covers in the
+ database.
+ - Folder raising exception: These represent folders in the
+ calibre library that could not be processed/understood by this
+ tool.
+
+
+ There are two kinds of automatic fixes possible: Delete
+ marked and Fix marked.
+ Delete marked is used to remove extra files/folders that
+ have no entries in the database. Use with caution.
+ Fix marked is more general. When applied to invalid items,
+ i.e. items that should be present according to the database but are not
+ actually present, it updates the database to remove those items. When
+ used with extra items, i.e. items that are present in the file system
+ but that the database doesn't know about, it will try to add those
+ items to the database, if possible. This is not always possible, in
+ which case, you have to use Delete marked or manually fix the
+ problem.
+ '''))
self.log = QTreeWidget(self)
self.log.itemChanged.connect(self.item_changed)
@@ -199,7 +255,7 @@ class CheckLibraryDialog(QDialog):
self._layout.addLayout(h)
self._layout.addWidget(self.bbox)
- self.resize(750, 500)
+ self.resize(950, 500)
self.bbox.setEnabled(True)
def do_exec(self):
@@ -347,5 +403,6 @@ class CheckLibraryDialog(QDialog):
if __name__ == '__main__':
app = QApplication([])
- d = CheckLibraryDialog()
+ from calibre.library import db
+ d = CheckLibraryDialog(None, db())
d.exec_()
From 6a6dbc781941b024f0f1e5c7cd20a61822861167 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 30 Jan 2011 18:44:37 +0000
Subject: [PATCH 32/35] Changes to check_library help text
---
src/calibre/gui2/dialogs/check_library.py | 63 ++++++++++++-----------
1 file changed, 34 insertions(+), 29 deletions(-)
diff --git a/src/calibre/gui2/dialogs/check_library.py b/src/calibre/gui2/dialogs/check_library.py
index f8eff310f6..f6688b0a9e 100644
--- a/src/calibre/gui2/dialogs/check_library.py
+++ b/src/calibre/gui2/dialogs/check_library.py
@@ -161,32 +161,37 @@ class CheckLibraryDialog(QDialog):
calibre stores the list of your books and their metadata in a
database. The actual book files and covers are stored as normal
files in the calibre library folder. The database contains a list of the files
- and cover belonging to each book entry. This tool checks that the
- information in the database and the actual files on your computer
- match.
+ and covers belonging to each book entry. This tool checks that the
+ actual files in the library folder on your computer match the
+ information in the database.
The result of each type of check is shown to the left. The various
checks are:
- - Invalid titles: These represent book entries in the
- database that have no corresponding files in the calibre library.
- - Extra titles: These represent extra files ni your calibre
- library that have no corresponding entries in the database
- - Invalid authors: These represent authors that exist in the
- database, but do not have corresponding folders in the calibre
- library
- - Extra authors: These represent extra author folders in the
- calibre library that do not have entries in the database
- - Unknown files in books: These represent extra files in the
- folder of each book that do not correspond to a know format or cover
- for that book in the database.
+ - Invalid titles: These are files and folders appearing
+ in the library where books titles should, but that do not have the
+ correct form to be a book title.
+ - Extra titles: These are extra files in your calibre
+ library that appear to be correctly-formed titles, but have no corresponding
+ entries in the database
+ - Invalid authors: These are files appearing
+ in the library where only author folders should be.
+ - Extra authors: These are folders in the
+ calibre library that appear to be authors but that do not have entries
+ in the database
+ - Missing book formats: These are book formats that are in
+ the database but have no corresponding format file in the book's folder.
+
- Extra book formats: These are book format files found in
+ the book's folder but not in the database.
+
- Unknown files in books: These are extra files in the
+ folder of each book that do not correspond to a known format or cover
+ file.
- Missing cover files: These represent books that are marked
- as having covers in the database but whose actual cover files are
+ in the database as having covers but the actual cover files are
missing.
- - Cover files not in database: These represent books whose
- cover files are present but are marked as not having covers in the
- database.
+ - Cover files not in database: These are books that have
+ cover files but are marked as not having covers in the database.
- Folder raising exception: These represent folders in the
calibre library that could not be processed/understood by this
tool.
@@ -194,16 +199,16 @@ class CheckLibraryDialog(QDialog):
There are two kinds of automatic fixes possible: Delete
marked and Fix marked.
- Delete marked is used to remove extra files/folders that
- have no entries in the database. Use with caution.
- Fix marked is more general. When applied to invalid items,
- i.e. items that should be present according to the database but are not
- actually present, it updates the database to remove those items. When
- used with extra items, i.e. items that are present in the file system
- but that the database doesn't know about, it will try to add those
- items to the database, if possible. This is not always possible, in
- which case, you have to use Delete marked or manually fix the
- problem.
+ Delete marked is used to remove extra files/folders/covers that
+ have no entries in the database. Check the box next to the item you want
+ to delete. Use with caution.
+ Fix marked is applicable only to covers (the two lines marked
+ 'fixable'). In the case of missing cover files, checking the fixable
+ box and pushing this button will remove the cover mark from the
+ database for all the files in that category. In the case of extra
+ cover files, checking the fixable box and pushing this button will
+ add the cover mark to the database for all the files in that
+ category.
'''))
self.log = QTreeWidget(self)
From 9ea08b5b48aab4c15508723904b07462fc80bebc Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sun, 30 Jan 2011 12:40:23 -0700
Subject: [PATCH 33/35] Categorize spanish language recipes
---
resources/recipes/180.recipe | 2 +-
resources/recipes/7dias.recipe | 2 +-
resources/recipes/ambito.recipe | 2 +-
resources/recipes/animal_politico.recipe | 2 +-
resources/recipes/axxon_magazine.recipe | 2 +-
resources/recipes/axxon_news.recipe | 2 +-
resources/recipes/bitacora.recipe | 2 +-
resources/recipes/buenosaireseconomico.recipe | 2 +-
resources/recipes/clarin.recipe | 2 +-
resources/recipes/criticadigital.recipe | 2 +-
resources/recipes/cubadebate.recipe | 10 +-
resources/recipes/deutsche_welle_es.recipe | 2 +-
resources/recipes/diagonales.recipe | 2 +-
resources/recipes/el_mercurio_chile.recipe | 6 +-
resources/recipes/el_observador.recipe | 2 +-
resources/recipes/el_pais_uy.recipe | 2 +-
resources/recipes/el_universal.recipe | 2 +-
resources/recipes/elargentino.recipe | 16 +-
resources/recipes/elcomercio.recipe | 2 +-
resources/recipes/elcronista.recipe | 8 +-
resources/recipes/eltiempo_hn.recipe | 2 +-
resources/recipes/eluniversal_ve.recipe | 2 +-
resources/recipes/eluniversalimpresa.recipe | 2 +-
resources/recipes/eluniverso_ec.recipe | 2 +-
resources/recipes/freeway.recipe | 2 +-
resources/recipes/granma.recipe | 2 +-
resources/recipes/ieco.recipe | 2 +-
resources/recipes/infobae.recipe | 6 +-
resources/recipes/juventudrebelde.recipe | 2 +-
resources/recipes/la_cuarta.recipe | 2 +-
resources/recipes/la_diaria.recipe | 2 +-
resources/recipes/la_jornada.recipe | 10 +-
resources/recipes/la_razon_bo.recipe | 2 +-
resources/recipes/la_segunda.recipe | 20 +-
resources/recipes/lamujerdemivida.recipe | 14 +-
resources/recipes/lanacion.recipe | 16 +-
resources/recipes/lanacion_chile.recipe | 2 +-
resources/recipes/laprensa.recipe | 26 +-
resources/recipes/laprensa_hn.recipe | 2 +-
resources/recipes/laprensa_ni.recipe | 2 +-
resources/recipes/latribuna.recipe | 2 +-
resources/recipes/los_tiempos_bo.recipe | 2 +-
resources/recipes/milenio.recipe | 2 +-
resources/recipes/miradasalsur.recipe | 2 +-
resources/recipes/montevideo_com.recipe | 2 +-
resources/recipes/newsweek_argentina.recipe | 2 +-
resources/recipes/observa_digital.recipe | 2 +-
resources/recipes/pagina12.recipe | 14 +-
resources/recipes/perfil.recipe | 2 +-
resources/recipes/reptantes.recipe | 2 +-
resources/recipes/revista_bla.recipe | 2 +-
resources/recipes/veintitres.recipe | 2 +-
src/calibre/gui2/device.py | 4 +-
src/calibre/translations/calibre.pot | 666 +++++++++++-------
src/calibre/utils/localization.py | 10 +
55 files changed, 537 insertions(+), 371 deletions(-)
diff --git a/resources/recipes/180.recipe b/resources/recipes/180.recipe
index 5158bb99e0..4f00314795 100644
--- a/resources/recipes/180.recipe
+++ b/resources/recipes/180.recipe
@@ -12,7 +12,7 @@ class Noticias(BasicNewsRecipe):
title = '180.com.uy'
__author__ = 'Gustavo Azambuja'
description = 'Noticias de Uruguay'
- language = 'es'
+ language = 'es_UY'
timefmt = '[%a, %d %b, %Y]'
use_embedded_content = False
recursion = 5
diff --git a/resources/recipes/7dias.recipe b/resources/recipes/7dias.recipe
index e111617b8d..3dc93295a8 100644
--- a/resources/recipes/7dias.recipe
+++ b/resources/recipes/7dias.recipe
@@ -20,7 +20,7 @@ class SieteDias(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
- language = 'es'
+ language = 'es_AR'
lang = 'es-AR'
direction = 'ltr'
diff --git a/resources/recipes/ambito.recipe b/resources/recipes/ambito.recipe
index 7074463e34..dd92ee19b3 100644
--- a/resources/recipes/ambito.recipe
+++ b/resources/recipes/ambito.recipe
@@ -58,4 +58,4 @@ class Ambito(BasicNewsRecipe):
del item['style']
return soup
- language = 'es'
+ language = 'es_AR'
diff --git a/resources/recipes/animal_politico.recipe b/resources/recipes/animal_politico.recipe
index f48587ea94..5e57e266bc 100644
--- a/resources/recipes/animal_politico.recipe
+++ b/resources/recipes/animal_politico.recipe
@@ -12,7 +12,7 @@ class AdvancedUserRecipe1290663986(BasicNewsRecipe):
masthead_url = 'http://www.animalpolitico.com/wp-content/themes/animal_mu/images/logo.png'
oldest_article = 1
max_articles_per_feed = 100
- language = 'es'
+ language = 'es_MX'
#feeds = [(u'Animal Politico', u'http://www.animalpolitico.com/feed/')]
diff --git a/resources/recipes/axxon_magazine.recipe b/resources/recipes/axxon_magazine.recipe
index 331e6fe2b5..93cb5cd03b 100644
--- a/resources/recipes/axxon_magazine.recipe
+++ b/resources/recipes/axxon_magazine.recipe
@@ -17,7 +17,7 @@ class Axxon_news(BasicNewsRecipe):
max_articles_per_feed = 100
no_stylesheets = False
use_embedded_content = False
- language = 'es'
+ language = 'es_AR'
encoding = 'utf-8'
publication_type = 'magazine'
INDEX = 'http://axxon.com.ar/rev/'
diff --git a/resources/recipes/axxon_news.recipe b/resources/recipes/axxon_news.recipe
index a9a99e1de1..3d0f8effd0 100644
--- a/resources/recipes/axxon_news.recipe
+++ b/resources/recipes/axxon_news.recipe
@@ -18,7 +18,7 @@ class Axxon_news(BasicNewsRecipe):
max_articles_per_feed = 100
no_stylesheets = False
use_embedded_content = False
- language = 'es'
+ language = 'es_AR'
lang = 'es-AR'
diff --git a/resources/recipes/bitacora.recipe b/resources/recipes/bitacora.recipe
index a36eb52988..7f41f8f32b 100644
--- a/resources/recipes/bitacora.recipe
+++ b/resources/recipes/bitacora.recipe
@@ -12,7 +12,7 @@ class General(BasicNewsRecipe):
title = 'bitacora.com.uy'
__author__ = 'Gustavo Azambuja'
description = 'Noticias de Uruguay'
- language = 'es'
+ language = 'es_UY'
timefmt = '[%a, %d %b, %Y]'
use_embedded_content = False
recursion = 5
diff --git a/resources/recipes/buenosaireseconomico.recipe b/resources/recipes/buenosaireseconomico.recipe
index c9f89269b4..782358e6d3 100644
--- a/resources/recipes/buenosaireseconomico.recipe
+++ b/resources/recipes/buenosaireseconomico.recipe
@@ -20,7 +20,7 @@ class BsAsEconomico(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
- language = 'es'
+ language = 'es_AR'
lang = 'es-AR'
direction = 'ltr'
diff --git a/resources/recipes/clarin.recipe b/resources/recipes/clarin.recipe
index cf9440ad55..7bbb663d1d 100644
--- a/resources/recipes/clarin.recipe
+++ b/resources/recipes/clarin.recipe
@@ -18,7 +18,7 @@ class Clarin(BasicNewsRecipe):
use_embedded_content = False
no_stylesheets = True
encoding = 'utf8'
- language = 'es'
+ language = 'es_AR'
publication_type = 'newspaper'
INDEX = 'http://www.clarin.com'
masthead_url = 'http://www.clarin.com/static/CLAClarin/images/logo-clarin-print.jpg'
diff --git a/resources/recipes/criticadigital.recipe b/resources/recipes/criticadigital.recipe
index d1ef97aef9..3cb72e6be4 100644
--- a/resources/recipes/criticadigital.recipe
+++ b/resources/recipes/criticadigital.recipe
@@ -14,7 +14,7 @@ class CriticaDigital(BasicNewsRecipe):
description = 'Noticias de Argentina'
oldest_article = 2
max_articles_per_feed = 100
- language = 'es'
+ language = 'es_AR'
no_stylesheets = True
use_embedded_content = False
diff --git a/resources/recipes/cubadebate.recipe b/resources/recipes/cubadebate.recipe
index f8887b2672..d563f0f86e 100644
--- a/resources/recipes/cubadebate.recipe
+++ b/resources/recipes/cubadebate.recipe
@@ -11,7 +11,7 @@ class CubaDebate(BasicNewsRecipe):
__author__ = 'Darko Miletic'
description = 'Contra el Terorismo Mediatico'
oldest_article = 15
- language = 'es'
+ language = 'es_CU'
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
@@ -20,8 +20,8 @@ class CubaDebate(BasicNewsRecipe):
encoding = 'utf-8'
masthead_url = 'http://www.cubadebate.cu/wp-content/themes/cubadebate/images/logo.gif'
publication_type = 'newsportal'
- extra_css = """
- #BlogTitle{font-size: xx-large; font-weight: bold}
+ extra_css = """
+ #BlogTitle{font-size: xx-large; font-weight: bold}
body{font-family: Verdana, Arial, Tahoma, sans-serif}
"""
@@ -41,7 +41,7 @@ class CubaDebate(BasicNewsRecipe):
feeds = [(u'Articulos', u'http://www.cubadebate.cu/feed/')]
remove_attributes=['width','height','lang']
-
+
def print_version(self, url):
return url + 'print/'
@@ -50,5 +50,5 @@ class CubaDebate(BasicNewsRecipe):
del item['style']
for item in soup.findAll('img'):
if not item.has_key('alt'):
- item['alt'] = 'image'
+ item['alt'] = 'image'
return soup
diff --git a/resources/recipes/deutsche_welle_es.recipe b/resources/recipes/deutsche_welle_es.recipe
index ab80b2f11f..c68a03b981 100644
--- a/resources/recipes/deutsche_welle_es.recipe
+++ b/resources/recipes/deutsche_welle_es.recipe
@@ -16,7 +16,7 @@ class DeutscheWelle_es(BasicNewsRecipe):
max_articles_per_feed = 100
use_embedded_content = False
no_stylesheets = True
- language = 'es'
+ language = 'de_ES'
publication_type = 'newsportal'
remove_empty_feeds = True
masthead_url = 'http://www.dw-world.de/skins/std/channel1/pics/dw_logo1024.gif'
diff --git a/resources/recipes/diagonales.recipe b/resources/recipes/diagonales.recipe
index 1604967e63..eff06efc1d 100644
--- a/resources/recipes/diagonales.recipe
+++ b/resources/recipes/diagonales.recipe
@@ -20,7 +20,7 @@ class Diagonales(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
- language = 'es'
+ language = 'es_AR'
lang = 'es-AR'
direction = 'ltr'
diff --git a/resources/recipes/el_mercurio_chile.recipe b/resources/recipes/el_mercurio_chile.recipe
index a8371f5af9..df4d027af3 100644
--- a/resources/recipes/el_mercurio_chile.recipe
+++ b/resources/recipes/el_mercurio_chile.recipe
@@ -20,8 +20,8 @@ class ElMercurio(BasicNewsRecipe):
masthead_url = 'http://www.emol.com/especiales/logo_emol/logo_emol.gif'
remove_javascript = True
use_embedded_content = False
- language = 'es'
-
+ language = 'es_CL'
+
conversion_options = {
'comment' : description
@@ -33,7 +33,7 @@ class ElMercurio(BasicNewsRecipe):
keep_only_tags = [dict(name='div', attrs={'id':['cont_iz_titulobajada','cont_iz_creditos_1_a','cont_iz_cuerpo']})]
remove_tags = [dict(name='div', attrs={'id':'cont_iz_cuerpo_relacionados'})]
remove_attributes = ['height','width']
-
+
feeds = [
(u'Noticias de ultima hora', u'http://rss.emol.com/rss.asp?canal=0')
,(u'Nacional', u'http://rss.emol.com/rss.asp?canal=1')
diff --git a/resources/recipes/el_observador.recipe b/resources/recipes/el_observador.recipe
index 6338426d59..994963671e 100644
--- a/resources/recipes/el_observador.recipe
+++ b/resources/recipes/el_observador.recipe
@@ -13,7 +13,7 @@ class ObservaDigital(BasicNewsRecipe):
title = 'Observa Digital'
__author__ = 'yrvn'
description = 'Noticias de Uruguay'
- language = 'es'
+ language = 'es_UY'
timefmt = '[%a, %d %b, %Y]'
use_embedded_content = False
recursion = 5
diff --git a/resources/recipes/el_pais_uy.recipe b/resources/recipes/el_pais_uy.recipe
index 1e9c400162..aceedf0b8c 100644
--- a/resources/recipes/el_pais_uy.recipe
+++ b/resources/recipes/el_pais_uy.recipe
@@ -14,7 +14,7 @@ class General(BasicNewsRecipe):
description = 'Noticias de Uruguay y el resto del mundo'
publisher = 'EL PAIS S.A.'
category = 'news, politics, Uruguay'
- language = 'es'
+ language = 'es_UY'
timefmt = '[%a, %d %b, %Y]'
use_embedded_content = False
recursion = 2
diff --git a/resources/recipes/el_universal.recipe b/resources/recipes/el_universal.recipe
index f053812c05..441f77cbe1 100644
--- a/resources/recipes/el_universal.recipe
+++ b/resources/recipes/el_universal.recipe
@@ -20,7 +20,7 @@ class ElUniversal(BasicNewsRecipe):
remove_javascript = True
remove_empty_feeds = True
publication_type = 'newspaper'
- language = 'es'
+ language = 'es_MX'
extra_css = '''
body{font-family:Arial,Helvetica,sans-serif}
diff --git a/resources/recipes/elargentino.recipe b/resources/recipes/elargentino.recipe
index c33b845e0a..6086ff6b59 100644
--- a/resources/recipes/elargentino.recipe
+++ b/resources/recipes/elargentino.recipe
@@ -12,7 +12,7 @@ class ElArgentino(BasicNewsRecipe):
__author__ = 'Darko Miletic'
description = 'Informacion Libre las 24 horas'
publisher = 'ElArgentino.com'
- category = 'news, politics, Argentina'
+ category = 'news, politics, Argentina'
oldest_article = 2
max_articles_per_feed = 100
remove_javascript = True
@@ -20,7 +20,7 @@ class ElArgentino(BasicNewsRecipe):
use_embedded_content = False
encoding = 'utf8'
cover_url = 'http://www.elargentino.com/TemplateWeb/MediosFooter/tapa_elargentino.png'
- language = 'es'
+ language = 'es_AR'
html2lrf_options = [
@@ -28,16 +28,16 @@ class ElArgentino(BasicNewsRecipe):
, '--category', category
, '--publisher', publisher
]
-
- html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
+
+ html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
remove_tags = [
dict(name='div', attrs={'id':'noprint' })
,dict(name='div', attrs={'class':'encabezadoImprimir'})
,dict(name='a' , attrs={'target':'_blank' })
]
-
- feeds = [
+
+ feeds = [
(u'Portada' , u'http://www.elargentino.com/Highlights.aspx?Content-Type=text/xml&ChannelDesc=Home' )
,(u'Pais' , u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=112&Content-Type=text/xml&ChannelDesc=Pa%C3%ADs' )
,(u'Economia' , u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=107&Content-Type=text/xml&ChannelDesc=Econom%C3%ADa' )
@@ -51,12 +51,12 @@ class ElArgentino(BasicNewsRecipe):
def print_version(self, url):
main, sep, article_part = url.partition('/nota-')
- article_id, rsep, rrest = article_part.partition('-')
+ article_id, rsep, rrest = article_part.partition('-')
return u'http://www.elargentino.com/Impresion.aspx?Id=' + article_id
def preprocess_html(self, soup):
mtag = '\n\n'
soup.head.insert(0,mtag)
for item in soup.findAll(style=True):
- del item['style']
+ del item['style']
return soup
diff --git a/resources/recipes/elcomercio.recipe b/resources/recipes/elcomercio.recipe
index 37733bda8b..69c513a835 100644
--- a/resources/recipes/elcomercio.recipe
+++ b/resources/recipes/elcomercio.recipe
@@ -18,7 +18,7 @@ class ElComercio(BasicNewsRecipe):
no_stylesheets = True
encoding = 'utf-8'
use_embedded_content = True
- language = 'es'
+ language = 'es_EC'
masthead_url = 'http://ww1.elcomercio.com/nv_images/headers/EC/logo_new_08.gif'
extra_css = ' body{font-family: Arial,Verdana,sans-serif} img{margin-bottom: 1em} '
diff --git a/resources/recipes/elcronista.recipe b/resources/recipes/elcronista.recipe
index d96a9746d6..93615f8f42 100644
--- a/resources/recipes/elcronista.recipe
+++ b/resources/recipes/elcronista.recipe
@@ -13,7 +13,7 @@ class ElCronista(BasicNewsRecipe):
__author__ = 'Darko Miletic'
description = 'Noticias de Argentina'
oldest_article = 2
- language = 'es'
+ language = 'es_AR'
max_articles_per_feed = 100
no_stylesheets = True
@@ -25,14 +25,14 @@ class ElCronista(BasicNewsRecipe):
, '--category' , 'news, Argentina'
, '--publisher' , title
]
-
+
keep_only_tags = [
dict(name='table', attrs={'width':'100%' })
,dict(name='h1' , attrs={'class':'Arialgris16normal'})
]
remove_tags = [dict(name='a', attrs={'class':'Arialazul12'})]
-
+
feeds = [
(u'Economia' , u'http://www.cronista.com/adjuntos/8/rss/Economia_EI.xml' )
,(u'Negocios' , u'http://www.cronista.com/adjuntos/8/rss/negocios_EI.xml' )
@@ -69,4 +69,4 @@ class ElCronista(BasicNewsRecipe):
if link_item:
cover_url = index + link_item.img['src']
return cover_url
-
+
diff --git a/resources/recipes/eltiempo_hn.recipe b/resources/recipes/eltiempo_hn.recipe
index e4d11466ee..81d1ecbb9d 100644
--- a/resources/recipes/eltiempo_hn.recipe
+++ b/resources/recipes/eltiempo_hn.recipe
@@ -21,7 +21,7 @@ class ElTiempoHn(BasicNewsRecipe):
no_stylesheets = True
remove_javascript = True
encoding = 'utf-8'
- language = 'es'
+ language = 'es_HN'
lang = 'es-HN'
direction = 'ltr'
diff --git a/resources/recipes/eluniversal_ve.recipe b/resources/recipes/eluniversal_ve.recipe
index af5d75e3e7..28667cd39b 100644
--- a/resources/recipes/eluniversal_ve.recipe
+++ b/resources/recipes/eluniversal_ve.recipe
@@ -18,7 +18,7 @@ class ElUniversal(BasicNewsRecipe):
encoding = 'cp1252'
publisher = 'El Universal'
category = 'news, Caracas, Venezuela, world'
- language = 'es'
+ language = 'es_VE'
cover_url = strftime('http://static.eluniversal.com/%Y/%m/%d/portada.jpg')
conversion_options = {
diff --git a/resources/recipes/eluniversalimpresa.recipe b/resources/recipes/eluniversalimpresa.recipe
index 7263a76e2a..a48556eedb 100644
--- a/resources/recipes/eluniversalimpresa.recipe
+++ b/resources/recipes/eluniversalimpresa.recipe
@@ -3,7 +3,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class ElUniversalImpresaRecipe(BasicNewsRecipe):
__license__ = 'GPL v3'
__author__ = 'kwetal'
- language = 'es'
+ language = 'es_MX'
version = 1
title = u'El Universal (Edici\u00F3n Impresa)'
diff --git a/resources/recipes/eluniverso_ec.recipe b/resources/recipes/eluniverso_ec.recipe
index a0e8b46474..8696e9d2f6 100644
--- a/resources/recipes/eluniverso_ec.recipe
+++ b/resources/recipes/eluniverso_ec.recipe
@@ -17,7 +17,7 @@ class ElUniverso_Ecuador(BasicNewsRecipe):
no_stylesheets = True
encoding = 'utf8'
use_embedded_content = False
- language = 'es'
+ language = 'es_EC'
remove_empty_feeds = True
publication_type = 'newspaper'
masthead_url = 'http://servicios2.eluniverso.com/versiones/v1/img/Hd/lg_ElUniverso.gif'
diff --git a/resources/recipes/freeway.recipe b/resources/recipes/freeway.recipe
index cb6d41ebb2..c50c35d8ca 100644
--- a/resources/recipes/freeway.recipe
+++ b/resources/recipes/freeway.recipe
@@ -12,7 +12,7 @@ class General(BasicNewsRecipe):
title = 'freeway.com.uy'
__author__ = 'Gustavo Azambuja'
description = 'Revista Freeway, Montevideo, Uruguay'
- language = 'es'
+ language = 'es_UY'
timefmt = '[%a, %d %b, %Y]'
use_embedded_content = False
recursion = 1
diff --git a/resources/recipes/granma.recipe b/resources/recipes/granma.recipe
index 75e89a3d03..01f383cb68 100644
--- a/resources/recipes/granma.recipe
+++ b/resources/recipes/granma.recipe
@@ -20,7 +20,7 @@ class Granma(BasicNewsRecipe):
use_embedded_content = False
encoding = 'cp1252'
cover_url = 'http://www.granma.cubaweb.cu/imagenes/granweb229d.jpg'
- language = 'es'
+ language = 'es_CU'
remove_javascript = True
diff --git a/resources/recipes/ieco.recipe b/resources/recipes/ieco.recipe
index 8c59d033a1..eb11c103d2 100644
--- a/resources/recipes/ieco.recipe
+++ b/resources/recipes/ieco.recipe
@@ -18,7 +18,7 @@ class iEco(BasicNewsRecipe):
encoding = 'utf-8'
publisher = 'Grupo Clarin'
category = 'news, economia, mercados, bolsa de valores, finanzas, empresas, negocios, empleos, emprendedores, marketinguniversidades, tecnologia, agronegocios, noticias, informacion'
- language = 'es'
+ language = 'es_AR'
cover_url = 'http://www.ieco.clarin.com/static2/images/Tapa-PDF.gif'
extra_css = ' #bd{font-family: sans-serif} '
diff --git a/resources/recipes/infobae.recipe b/resources/recipes/infobae.recipe
index b7f9cd3c6c..9553746449 100644
--- a/resources/recipes/infobae.recipe
+++ b/resources/recipes/infobae.recipe
@@ -16,7 +16,7 @@ class Infobae(BasicNewsRecipe):
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
- language = 'es'
+ language = 'es_AR'
encoding = 'cp1252'
masthead_url = 'http://www.infobae.com/imgs/header/header.gif'
remove_javascript = True
@@ -25,7 +25,7 @@ class Infobae(BasicNewsRecipe):
body{font-family:Arial,Helvetica,sans-serif;}
.popUpTitulo{color:#0D4261; font-size: xx-large}
'''
-
+
conversion_options = {
'comment' : description
, 'tags' : category
@@ -33,7 +33,7 @@ class Infobae(BasicNewsRecipe):
, 'language' : language
, 'linearize_tables' : True
}
-
+
feeds = [
(u'Noticias' , u'http://www.infobae.com/adjuntos/html/RSS/hoy.xml' )
diff --git a/resources/recipes/juventudrebelde.recipe b/resources/recipes/juventudrebelde.recipe
index dd908d57b2..93d550027a 100644
--- a/resources/recipes/juventudrebelde.recipe
+++ b/resources/recipes/juventudrebelde.recipe
@@ -20,7 +20,7 @@ class Juventudrebelde(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'
- language = 'es'
+ language = 'es_CU'
cover_url = strftime('http://www.juventudrebelde.cu/UserFiles/File/impreso/iportada-%Y-%m-%d.jpg')
remove_javascript = True
diff --git a/resources/recipes/la_cuarta.recipe b/resources/recipes/la_cuarta.recipe
index ad1a6a975e..10d6b6ce3a 100644
--- a/resources/recipes/la_cuarta.recipe
+++ b/resources/recipes/la_cuarta.recipe
@@ -50,4 +50,4 @@ class LaCuarta(BasicNewsRecipe):
feeds = [(u'Noticias', u'http://lacuarta.cl/app/rss?sc=TEFDVUFSVEE=')]
- language = 'es'
+ language = 'es_CL'
diff --git a/resources/recipes/la_diaria.recipe b/resources/recipes/la_diaria.recipe
index d89eb465dd..8aa98483c0 100644
--- a/resources/recipes/la_diaria.recipe
+++ b/resources/recipes/la_diaria.recipe
@@ -12,7 +12,7 @@ class General(BasicNewsRecipe):
title = 'La Diaria'
__author__ = 'Gustavo Azambuja'
description = 'Noticias de Uruguay'
- language = 'es'
+ language = 'es_UY'
timefmt = '[%a, %d %b, %Y]'
use_embedded_content = False
recursion = 5
diff --git a/resources/recipes/la_jornada.recipe b/resources/recipes/la_jornada.recipe
index 3ee6dc4b3f..71c526a0a0 100644
--- a/resources/recipes/la_jornada.recipe
+++ b/resources/recipes/la_jornada.recipe
@@ -19,7 +19,7 @@ class LaJornada_mx(BasicNewsRecipe):
no_stylesheets = True
encoding = 'utf8'
use_embedded_content = False
- language = 'es'
+ language = 'es_MX'
remove_empty_feeds = True
cover_url = strftime("http://www.jornada.unam.mx/%Y/%m/%d/portada.pdf")
masthead_url = 'http://www.jornada.unam.mx/v7.0/imagenes/la-jornada-trans.png'
@@ -34,8 +34,8 @@ class LaJornada_mx(BasicNewsRecipe):
.credito{font-weight: bold; margin-left: 1em}
.credito-autor{font-variant: small-caps; font-weight: bold }
.credito-titulo{text-align: right}
- .hemero{text-align: right; font-size: 0.9em; margin-bottom: 0.5em }
- .loc{font-weight: bold}
+ .hemero{text-align: right; font-size: 0.9em; margin-bottom: 0.5em }
+ .loc{font-weight: bold}
.carton{text-align: center}
.credit{font-weight: bold}
.sumario{font-weight: bold; text-align: center}
@@ -56,7 +56,7 @@ class LaJornada_mx(BasicNewsRecipe):
,re.DOTALL|re.IGNORECASE)
,lambda match: '' + match.group(1) + '
')
]
-
+
keep_only_tags = [
dict(name='div', attrs={'class':['documentContent','cabeza','sumarios','credito-articulo','text','carton']})
,dict(name='div', attrs={'id':'renderComments'})
@@ -88,4 +88,4 @@ class LaJornada_mx(BasicNewsRecipe):
def get_article_url(self, article):
rurl = article.get('link', None)
return rurl.rpartition('&partner=')[0]
-
+
diff --git a/resources/recipes/la_razon_bo.recipe b/resources/recipes/la_razon_bo.recipe
index 18a00d6763..6af899b760 100644
--- a/resources/recipes/la_razon_bo.recipe
+++ b/resources/recipes/la_razon_bo.recipe
@@ -18,7 +18,7 @@ class LaRazon_Bol(BasicNewsRecipe):
no_stylesheets = True
encoding = 'cp1252'
use_embedded_content = False
- language = 'es'
+ language = 'es_BO'
publication_type = 'newspaper'
delay = 1
remove_empty_feeds = True
diff --git a/resources/recipes/la_segunda.recipe b/resources/recipes/la_segunda.recipe
index 537bff4104..8ce25e35b3 100644
--- a/resources/recipes/la_segunda.recipe
+++ b/resources/recipes/la_segunda.recipe
@@ -9,7 +9,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class LaSegunda(BasicNewsRecipe):
title = 'La Segunda'
__author__ = 'Darko Miletic'
- description = 'El sitio de noticias online de Chile'
+ description = 'El sitio de noticias online de Chile'
publisher = 'La Segunda'
category = 'news, politics, Chile'
oldest_article = 2
@@ -19,9 +19,9 @@ class LaSegunda(BasicNewsRecipe):
encoding = 'cp1252'
masthead_url = 'http://www.lasegunda.com/imagenes/logotipo_lasegunda_Oli.gif'
remove_empty_feeds = True
- language = 'es'
- extra_css = ' .titulonegritastop{font-size: xx-large; font-weight: bold} '
-
+ language = 'es_CL'
+ extra_css = ' .titulonegritastop{font-size: xx-large; font-weight: bold} '
+
conversion_options = {
'comment' : description
, 'tags' : category
@@ -29,13 +29,13 @@ class LaSegunda(BasicNewsRecipe):
, 'language' : language
, 'linearize_tables' : True
}
-
+
remove_tags_before = dict(attrs={'class':'titulonegritastop'})
remove_tags = [dict(name='img')]
remove_attributes = ['width','height']
-
-
- feeds = [
+
+
+ feeds = [
(u'Noticias de ultima hora', u'http://www.lasegunda.com/rss20/index.asp?canal=0')
,(u'Politica' , u'http://www.lasegunda.com/rss20/index.asp?canal=21')
,(u'Cronica' , u'http://www.lasegunda.com/rss20/index.asp?canal=20')
@@ -49,6 +49,6 @@ class LaSegunda(BasicNewsRecipe):
]
def print_version(self, url):
- rest, sep, article_id = url.partition('index.asp?idnoticia=')
+ rest, sep, article_id = url.partition('index.asp?idnoticia=')
return u'http://www.lasegunda.com/edicionOnline/include/secciones/_detalle_impresion.asp?idnoticia=' + article_id
-
+
diff --git a/resources/recipes/lamujerdemivida.recipe b/resources/recipes/lamujerdemivida.recipe
index 207646902b..be7a3fad0d 100644
--- a/resources/recipes/lamujerdemivida.recipe
+++ b/resources/recipes/lamujerdemivida.recipe
@@ -11,15 +11,15 @@ from calibre.web.feeds.news import BasicNewsRecipe
class LaMujerDeMiVida(BasicNewsRecipe):
title = 'La Mujer de mi Vida'
__author__ = 'Darko Miletic'
- description = 'Cultura de otra manera'
+ description = 'Cultura de otra manera'
oldest_article = 90
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'
publisher = 'La Mujer de mi Vida'
- category = 'literatura, critica, arte, ensayos'
- language = 'es'
+ category = 'literatura, critica, arte, ensayos'
+ language = 'es_AR'
INDEX = 'http://www.lamujerdemivida.com.ar/'
html2lrf_options = [
@@ -28,8 +28,8 @@ class LaMujerDeMiVida(BasicNewsRecipe):
, '--publisher', publisher
, '--ignore-tables'
]
-
- html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
+
+ html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
keep_only_tags = [dict(name='table', attrs={'width':'570'})]
@@ -51,7 +51,7 @@ class LaMujerDeMiVida(BasicNewsRecipe):
if cover_item:
cover_url = self.INDEX + cover_item['src']
return cover_url
-
+
def parse_index(self):
totalfeeds = []
lfeeds = self.get_feeds()
@@ -74,4 +74,4 @@ class LaMujerDeMiVida(BasicNewsRecipe):
})
totalfeeds.append((feedtitle, articles))
return totalfeeds
-
+
diff --git a/resources/recipes/lanacion.recipe b/resources/recipes/lanacion.recipe
index 050cb2e79c..05e777ec67 100644
--- a/resources/recipes/lanacion.recipe
+++ b/resources/recipes/lanacion.recipe
@@ -16,17 +16,17 @@ class Lanacion(BasicNewsRecipe):
max_articles_per_feed = 100
use_embedded_content = False
no_stylesheets = True
- language = 'es'
+ language = 'es_AR'
publication_type = 'newspaper'
- remove_empty_feeds = True
+ remove_empty_feeds = True
masthead_url = 'http://www.lanacion.com.ar/imgs/layout/logos/ln341x47.gif'
extra_css = """ h1{font-family: Georgia,serif}
- h2{color: #626262}
- body{font-family: Arial,sans-serif}
+ h2{color: #626262}
+ body{font-family: Arial,sans-serif}
img{margin-top: 0.5em; margin-bottom: 0.2em; display: block}
- .notaFecha{color: #808080}
- .notaEpigrafe{font-size: x-small}
- .topNota h1{font-family: Arial,sans-serif}
+ .notaFecha{color: #808080}
+ .notaEpigrafe{font-size: x-small}
+ .topNota h1{font-family: Arial,sans-serif}
"""
@@ -45,7 +45,7 @@ class Lanacion(BasicNewsRecipe):
,dict(attrs={'class':['titulosMultimedia','derecha','techo color','encuesta','izquierda compartir','floatFix','videoCentro']})
,dict(name=['iframe','embed','object','form','base','hr','meta','link','input'])
]
- remove_tags_after = dict(attrs={'class':['tags','nota-destacado']})
+ remove_tags_after = dict(attrs={'class':['tags','nota-destacado']})
remove_attributes = ['height','width','visible','onclick','data-count','name']
feeds = [
diff --git a/resources/recipes/lanacion_chile.recipe b/resources/recipes/lanacion_chile.recipe
index f913b61855..0c0d4550fa 100644
--- a/resources/recipes/lanacion_chile.recipe
+++ b/resources/recipes/lanacion_chile.recipe
@@ -51,4 +51,4 @@ class LaNacionChile(BasicNewsRecipe):
del item['style']
return soup
- language = 'es'
+ language = 'es_CL'
diff --git a/resources/recipes/laprensa.recipe b/resources/recipes/laprensa.recipe
index 1f8c708b32..eb54ec989a 100644
--- a/resources/recipes/laprensa.recipe
+++ b/resources/recipes/laprensa.recipe
@@ -21,9 +21,9 @@ class LaPrensa(BasicNewsRecipe):
encoding = 'cp1252'
# cover_url = 'http://www.laprensa.com.ar/imgs/logo.gif'
remove_javascript = True
- language = 'es'
+ language = 'es_AR'
lang = 'es'
-
+
html2lrf_options = [
'--comment', description
, '--category', category
@@ -32,7 +32,7 @@ class LaPrensa(BasicNewsRecipe):
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
filter_regexps = [r'.*archive.aspx.*']
-
+
remove_tags = [
dict(name='td', attrs={'class':["link-registro","link-buscador"]}),
dict(name='td', attrs={'id':["TDTabItem1","TDTabItem2","TDTabItem3","TDTabItem4"]}),
@@ -58,9 +58,9 @@ class LaPrensa(BasicNewsRecipe):
dict(name='img', src = "/versions/1/imgs/separador-linea-azul.gif"),
dict(name='img', src = " /versions/1/imgs/separador-linea.gif"),
dict(name='a',text ="Powered by Civinext Groupware - V. 2.0.3567.23706"),
- dict(name='img', height ="0")
+ dict(name='img', height ="0")
]
-
+
extra_css = '''
.seccion{font-size:xx-small;}
body{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
@@ -69,7 +69,7 @@ class LaPrensa(BasicNewsRecipe):
.fecha{font-size:xx-small;}
.volanta{font-size:xx-small;}
'''
-
+
feeds = [
(u'Politica' , u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx&Rss=4' )
,(u'Economia' , u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx&Rss=5' )
@@ -80,14 +80,14 @@ class LaPrensa(BasicNewsRecipe):
,(u'Espectaculos', u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx?Rss=10')
]
-
+
def preprocess_html(self, soup):
-
+
for t in soup.findAll(['table','td','tr','span','tbody']):
t.name = 'div'
for t in soup.findAll(['hr']):
t.extract()
-
+
mtag = ''
soup.head.insert(0,mtag)
for item in soup.findAll(style=True):
@@ -95,8 +95,8 @@ class LaPrensa(BasicNewsRecipe):
for item in soup.findAll(align = "center"):
del item['align']
for item in soup.findAll(bgcolor="ffffff"):
- del item['bgcolor']
+ del item['bgcolor']
return soup
-
-
-
+
+
+
diff --git a/resources/recipes/laprensa_hn.recipe b/resources/recipes/laprensa_hn.recipe
index 356882d177..d7895bf09d 100644
--- a/resources/recipes/laprensa_hn.recipe
+++ b/resources/recipes/laprensa_hn.recipe
@@ -21,7 +21,7 @@ class LaPrensaHn(BasicNewsRecipe):
no_stylesheets = True
remove_javascript = True
encoding = 'utf-8'
- language = 'es'
+ language = 'es_HN'
lang = 'es-HN'
direction = 'ltr'
diff --git a/resources/recipes/laprensa_ni.recipe b/resources/recipes/laprensa_ni.recipe
index c7f35a6d6a..9aba2839ef 100644
--- a/resources/recipes/laprensa_ni.recipe
+++ b/resources/recipes/laprensa_ni.recipe
@@ -22,7 +22,7 @@ class LaPrensa_ni(BasicNewsRecipe):
use_embedded_content = False
encoding = 'cp1252'
remove_javascript = True
- language = 'es'
+ language = 'es_NI'
months_es = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre']
current_month = months_es[datetime.date.today().month - 1]
diff --git a/resources/recipes/latribuna.recipe b/resources/recipes/latribuna.recipe
index e7e461cd01..13c59dc4aa 100644
--- a/resources/recipes/latribuna.recipe
+++ b/resources/recipes/latribuna.recipe
@@ -21,7 +21,7 @@ class LaTribuna(BasicNewsRecipe):
no_stylesheets = True
remove_javascript = True
encoding = 'utf-8'
- language = 'es'
+ language = 'es_HN'
lang = 'es-HN'
direction = 'ltr'
diff --git a/resources/recipes/los_tiempos_bo.recipe b/resources/recipes/los_tiempos_bo.recipe
index ae2774ff59..00ddd9d7c1 100644
--- a/resources/recipes/los_tiempos_bo.recipe
+++ b/resources/recipes/los_tiempos_bo.recipe
@@ -18,7 +18,7 @@ class LosTiempos_Bol(BasicNewsRecipe):
no_stylesheets = True
encoding = 'cp1252'
use_embedded_content = False
- language = 'es'
+ language = 'es_BO'
publication_type = 'newspaper'
delay = 1
remove_empty_feeds = True
diff --git a/resources/recipes/milenio.recipe b/resources/recipes/milenio.recipe
index a279eb37dc..e6ca103952 100644
--- a/resources/recipes/milenio.recipe
+++ b/resources/recipes/milenio.recipe
@@ -12,7 +12,7 @@ import datetime
class Milenio(BasicNewsRecipe):
title = u'Milenio-diario'
__author__ = 'Bmsleight'
- language = 'es'
+ language = 'es_MX'
description = 'Milenio-diario'
oldest_article = 10
max_articles_per_feed = 100
diff --git a/resources/recipes/miradasalsur.recipe b/resources/recipes/miradasalsur.recipe
index f966a94ee9..fd306adc86 100644
--- a/resources/recipes/miradasalsur.recipe
+++ b/resources/recipes/miradasalsur.recipe
@@ -20,7 +20,7 @@ class MiradasAlSur(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
- language = 'es'
+ language = 'es_AR'
lang = 'es-AR'
direction = 'ltr'
diff --git a/resources/recipes/montevideo_com.recipe b/resources/recipes/montevideo_com.recipe
index cabd4181d6..6f1474f0c0 100644
--- a/resources/recipes/montevideo_com.recipe
+++ b/resources/recipes/montevideo_com.recipe
@@ -12,7 +12,7 @@ class Noticias(BasicNewsRecipe):
title = 'Montevideo COMM'
__author__ = 'Gustavo Azambuja'
description = 'Noticias de Uruguay'
- language = 'es'
+ language = 'es_UY'
timefmt = '[%a, %d %b, %Y]'
use_embedded_content = False
recursion = 5
diff --git a/resources/recipes/newsweek_argentina.recipe b/resources/recipes/newsweek_argentina.recipe
index 96c50bf6c3..a474f75f60 100644
--- a/resources/recipes/newsweek_argentina.recipe
+++ b/resources/recipes/newsweek_argentina.recipe
@@ -20,7 +20,7 @@ class Newsweek_Argentina(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
- language = 'es'
+ language = 'es_AR'
lang = 'es-AR'
direction = 'ltr'
diff --git a/resources/recipes/observa_digital.recipe b/resources/recipes/observa_digital.recipe
index 375d67236c..cb493bcb26 100644
--- a/resources/recipes/observa_digital.recipe
+++ b/resources/recipes/observa_digital.recipe
@@ -12,7 +12,7 @@ class Noticias(BasicNewsRecipe):
title = 'Observa Digital'
__author__ = '2010, Gustavo Azambuja '
description = 'Noticias desde Uruguay'
- language = 'es'
+ language = 'es_UY'
timefmt = '[%a, %d %b, %Y]'
use_embedded_content = False
recursion = 5
diff --git a/resources/recipes/pagina12.recipe b/resources/recipes/pagina12.recipe
index 2809e87e2a..b706acb7d7 100644
--- a/resources/recipes/pagina12.recipe
+++ b/resources/recipes/pagina12.recipe
@@ -19,15 +19,15 @@ class Pagina12(BasicNewsRecipe):
no_stylesheets = True
encoding = 'cp1252'
use_embedded_content = False
- language = 'es'
+ language = 'es_AR'
remove_empty_feeds = True
publication_type = 'newspaper'
masthead_url = 'http://www.pagina12.com.ar/commons/imgs/logo-home.gif'
- extra_css = """
- body{font-family: Arial,Helvetica,sans-serif }
+ extra_css = """
+ body{font-family: Arial,Helvetica,sans-serif }
img{margin-bottom: 0.4em; display:block}
- #autor{font-weight: bold}
- #fecha,#epigrafe{font-size: 0.9em; margin: 5px}
+ #autor{font-weight: bold}
+ #fecha,#epigrafe{font-size: 0.9em; margin: 5px}
#imagen{border: 1px solid black; margin: 0 0 1.25em 1.25em; width: 232px }
.fgprincipal{font-size: large; font-weight: bold}
"""
@@ -83,7 +83,7 @@ class Pagina12(BasicNewsRecipe):
del it['href']
del it['title']
for item in soup.findAll('p'):
- it = item.find('h3')
+ it = item.find('h3')
if it:
it.name='span'
- return soup
\ No newline at end of file
+ return soup
diff --git a/resources/recipes/perfil.recipe b/resources/recipes/perfil.recipe
index 7f51485bd0..7db86f9d4a 100644
--- a/resources/recipes/perfil.recipe
+++ b/resources/recipes/perfil.recipe
@@ -17,7 +17,7 @@ class Perfil(BasicNewsRecipe):
no_stylesheets = True
encoding = 'cp1252'
use_embedded_content = False
- language = 'es'
+ language = 'es_AR'
remove_empty_feeds = True
masthead_url = 'http://www.perfil.com/export/sites/diarioperfil/arte/10/logo_perfilcom_mm.gif'
extra_css = """
diff --git a/resources/recipes/reptantes.recipe b/resources/recipes/reptantes.recipe
index 51b65c6639..8e5ccbd192 100644
--- a/resources/recipes/reptantes.recipe
+++ b/resources/recipes/reptantes.recipe
@@ -13,7 +13,7 @@ class Reptantes(BasicNewsRecipe):
description = u"cada vez que te haces acupuntura, tu muñeco vudú sufre en algún lado"
oldest_article = 130
max_articles_per_feed = 100
- language = 'es'
+ language = 'es_AR'
encoding = 'utf-8'
no_stylesheets = True
use_embedded_content = False
diff --git a/resources/recipes/revista_bla.recipe b/resources/recipes/revista_bla.recipe
index 15c7e7fb3f..a575d01d0b 100644
--- a/resources/recipes/revista_bla.recipe
+++ b/resources/recipes/revista_bla.recipe
@@ -12,7 +12,7 @@ class Noticias(BasicNewsRecipe):
title = 'Revista Bla'
__author__ = 'Gustavo Azambuja'
description = 'Moda | Uruguay'
- language = 'es'
+ language = 'es_UY'
timefmt = '[%a, %d %b, %Y]'
use_embedded_content = False
recursion = 5
diff --git a/resources/recipes/veintitres.recipe b/resources/recipes/veintitres.recipe
index 6c8d3d1260..d36ac8568b 100644
--- a/resources/recipes/veintitres.recipe
+++ b/resources/recipes/veintitres.recipe
@@ -20,7 +20,7 @@ class Veintitres(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
- language = 'es'
+ language = 'es_AR'
lang = 'es-AR'
direction = 'ltr'
diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py
index 020ba7fb32..a5066a99ef 100644
--- a/src/calibre/gui2/device.py
+++ b/src/calibre/gui2/device.py
@@ -838,9 +838,9 @@ class DeviceMixin(object): # {{{
format_count[f] = 1
for f in self.device_manager.device.settings().format_map:
if f in format_count.keys():
- formats.append((f, _('%i of %i Books' % (format_count[f], len(rows))), True if f in aval_out_formats else False))
+ formats.append((f, _('%i of %i Books') % (format_count[f], len(rows))), True if f in aval_out_formats else False)
elif f in aval_out_formats:
- formats.append((f, _('0 of %i Books' % len(rows)), True))
+ formats.append((f, _('0 of %i Books') % len(rows)), True)
d = ChooseFormatDeviceDialog(self, _('Choose format to send to device'), formats)
if d.exec_() != QDialog.Accepted:
return
diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot
index 03016f965e..44482e118c 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.43\n"
-"POT-Creation-Date: 2011-01-28 12:03+MST\n"
-"PO-Revision-Date: 2011-01-28 12:03+MST\n"
+"POT-Creation-Date: 2011-01-30 12:40+MST\n"
+"PO-Revision-Date: 2011-01-30 12:40+MST\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@@ -113,8 +113,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:101
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:329
#: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:331
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:300
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:307
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:299
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:306
#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:100
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:331
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:334
@@ -124,8 +124,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:120
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:145
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:147
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1066
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1069
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1064
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1067
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:55
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:67
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:47
@@ -149,15 +149,15 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/database2.py:432
#: /home/kovid/work/calibre/src/calibre/library/database2.py:444
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1529
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1630
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:2470
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1632
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2472
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:2603
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:2474
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:2605
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:229
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:158
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:161
#: /home/kovid/work/calibre/src/calibre/library/server/xml.py:79
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:119
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:129
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:46
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:64
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:78
@@ -409,7 +409,7 @@ msgid "Setup the calibre Content Server which will give you access to your calib
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:889
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:163
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:269
msgid "Plugins"
msgstr ""
@@ -497,32 +497,32 @@ msgid "This profile is intended for the Cybook G3."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:149
-#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:595
+#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:596
msgid "This profile is intended for the Cybook Opus."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:161
-#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:608
+#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:609
msgid "This profile is intended for the Amazon Kindle."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:173
-#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:658
+#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:659
msgid "This profile is intended for the Irex Illiad."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:185
-#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:671
+#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:672
msgid "This profile is intended for the IRex Digital Reader 1000."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:198
-#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:685
+#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:686
msgid "This profile is intended for the IRex Digital Reader 800."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:210
-#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:699
+#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:700
msgid "This profile is intended for the B&N Nook."
msgstr ""
@@ -570,15 +570,15 @@ msgstr ""
msgid "This profile is intended for the SONY PRS line. The 500/505/700 etc, in landscape mode. Mainly useful for comics."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:634
+#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:635
msgid "This profile is intended for the Amazon Kindle DX."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:711
+#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:712
msgid "This profile is intended for the B&N Nook Color."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:722
+#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:723
msgid "This profile is intended for the Sanda Bambook."
msgstr ""
@@ -724,7 +724,7 @@ msgstr ""
#: /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:2334
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:2336
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:150
msgid "News"
msgstr ""
@@ -732,8 +732,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2554
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:65
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:625
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:2297
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:2315
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:2299
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:2317
msgid "Catalog"
msgstr ""
@@ -841,7 +841,7 @@ msgid "Communicate with the Blackberry smart phone."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/blackberry/driver.py:14
-#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:253
+#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:254
#: /home/kovid/work/calibre/src/calibre/devices/nuut2/driver.py:18
#: /home/kovid/work/calibre/src/calibre/devices/prs500/driver.py:90
msgid "Kovid Goyal"
@@ -855,23 +855,23 @@ msgstr ""
msgid "Communicate with the Cybook Orizon eBook reader."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:24
+#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:25
msgid "Communicate with the EB600 eBook reader."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:193
+#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:194
msgid "Communicate with the Astak Mentor EB600"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:216
+#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:217
msgid "Communicate with the PocketBook 301 reader."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:233
+#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:234
msgid "Communicate with the PocketBook 602/603/902/903 reader."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:252
+#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:253
msgid "Communicate with the PocketBook 701"
msgstr ""
@@ -2665,7 +2665,7 @@ msgid "%s format books are not supported"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/cover.py:98
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:172
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:173
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:220
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:691
msgid "Book %s of %s"
@@ -2720,7 +2720,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/output.py:32
#: /home/kovid/work/calibre/src/calibre/ebooks/pml/output.py:37
#: /home/kovid/work/calibre/src/calibre/ebooks/rb/output.py:21
-#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:35
+#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:34
msgid "Add Table of Contents to beginning of the book."
msgstr ""
@@ -2957,12 +2957,12 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:25
#: /home/kovid/work/calibre/src/calibre/ebooks/tcr/output.py:23
-#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:31
+#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:30
msgid "Specify the character encoding of the output document. The default is utf-8."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:29
-#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:38
+#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:37
msgid "The maximum number of characters per line. This splits on the first space before the specified value. If no space is found the line will be broken at the space after and will exceed the specified value. Also, there is a minimum of 25 characters. Use 0 to disable line splitting."
msgstr ""
@@ -3023,24 +3023,28 @@ msgstr ""
msgid "Do not insert a Table of Contents into the output text."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:25
+#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:24
msgid "Type of newline to use. Options are %s. Default is 'system'. Use 'old_mac' for compatibility with Mac OS 9 and earlier. For Mac OS X use 'unix'. 'system' will default to the newline type used by this OS."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:45
+#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:44
msgid "Force splitting on the max-line-length value when no space is present. Also allows max-line-length to be below the minimum"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:49
-msgid "Produce Markdown formatted text."
+msgid ""
+"Formatting used within the document.\n"
+"* plain: Produce plain text.\n"
+"* markdown: Produce Markdown formatted text.\n"
+"* textile: Produce Textile formatted text."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:52
-msgid "Do not remove links within the document. This is only useful when paired with the markdown-format option because links are always removed with plain text output."
+#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:55
+msgid "Do not remove links within the document. This is only useful when paired with a txt-output-formatting option that is not none because links are always removed with plain text output."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:57
-msgid "Do not remove image references within the document. This is only useful when paired with the markdown-format option because image references are always removed with plain text output."
+#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:60
+msgid "Do not remove image references within the document. This is only useful when paired with a txt-output-formatting option that is not none because links are always removed with plain text output."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:70
@@ -3119,44 +3123,44 @@ msgstr ""
msgid "Default action to perform when send to device button is clicked"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:124
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:126
msgid "Maximum number of waiting worker processes"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:126
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:128
msgid "Download social metadata (tags/rating/etc.)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:128
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:130
msgid "Overwrite author and title with new metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:130
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:132
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata_ui.py:101
msgid "Automatically download the cover, if available"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:132
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:134
msgid "Limit max simultaneous jobs to number of CPUs"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:134
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:136
msgid "tag browser categories not to display"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:136
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:138
msgid "The layout of the user interface"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:138
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:140
msgid "Show the average rating per item indication in the tag browser"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:140
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:142
msgid "Disable UI animations"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:411
+#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:410
msgid "Choose Files"
msgstr ""
@@ -3381,170 +3385,178 @@ msgstr ""
msgid "Select destination for %s.%s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:82
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:81
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/toolbar.py:54
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:167
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:126
msgid "%d books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:83
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:82
msgid "Choose calibre library to work with"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:92
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:91
msgid "Switch/create library..."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:103
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:102
#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:77
msgid "Quick switch"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:105
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:104
#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:78
msgid "Rename library"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:107
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:106
#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:79
msgid "Delete library"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:110
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:109
msgid "Pick a random book"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:129
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:128
msgid "Library Maintenance"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:130
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:129
msgid "Library metadata backup status"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:134
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:133
msgid "Start backing up metadata of all books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:138
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:137
msgid "Check library"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:211
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:141
+msgid "Restore database"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:216
msgid "Rename"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:212
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:217
msgid "Choose a new name for the library %s. "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:213
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:218
msgid "Note that the actual library folder will be renamed."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:220
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:225
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:191
msgid "Already exists"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:221
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:226
msgid "The folder %s already exists. Delete it first."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:227
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:232
msgid "Rename failed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:228
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:233
msgid "Failed to rename the library at %s. The most common cause for this is if one of the files in the library is open in another program."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:238
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:243
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/confirm_delete_ui.py:53
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:78
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:360
#: /home/kovid/work/calibre/src/calibre/gui2/jobs.py:424
#: /home/kovid/work/calibre/src/calibre/gui2/jobs.py:430
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:102
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:169
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:275
msgid "Are you sure?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:239
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:244
msgid "All files from %s will be permanently deleted. Are you sure?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:258
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:263
msgid "none"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:259
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:264
msgid "Backup status"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:260
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:265
msgid "Book metadata files remaining to be written: %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:266
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:271
msgid "Backup metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:267
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:272
msgid "Metadata will be backed up while calibre is running, at the rate of approximately 1 book every three seconds."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:289
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:180
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:240
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:304
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:106
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:111
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:286
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:340
msgid "Success"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:290
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:305
msgid "Found no errors in your calibre library database. Do you want calibre to check if the files in your library match the information in the database?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:295
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:310
#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:150
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:674
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:876
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:672
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:877
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:101
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/bulk_download.py:190
msgid "Failed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:296
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:311
msgid "Database integrity check failed, click Show details for details."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:301
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:316
msgid "No problems found"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:302
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:317
msgid "The files in your library match the information in the database."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:311
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:326
msgid "No library found"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:312
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:327
msgid "No existing calibre library was found at %s. It will be removed from the list of known libraries."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:365
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:370
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:380
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:385
#: /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
msgid "Not allowed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:366
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:381
msgid "You cannot change libraries while using the environment variable CALIBRE_OVERRIDE_DATABASE_PATH."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:371
+#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:386
msgid "You cannot change libraries while jobs are running."
msgstr ""
@@ -3565,7 +3577,7 @@ msgid "Bulk convert"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/actions/convert.py:86
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:507
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:506
msgid "Cannot convert"
msgstr ""
@@ -4191,7 +4203,7 @@ msgid "The specified directory could not be processed."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:250
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:823
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:821
msgid "No books"
msgstr ""
@@ -4442,7 +4454,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/rb_output.py:15
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output.py:15
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input.py:13
-#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output.py:17
+#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output.py:16
msgid "Options specific to"
msgstr ""
@@ -4458,7 +4470,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/pml_output.py:15
#: /home/kovid/work/calibre/src/calibre/gui2/convert/rb_output.py:15
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output.py:15
-#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output.py:17
+#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output.py:16
msgid "output"
msgstr ""
@@ -4503,7 +4515,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:139
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc_ui.py:60
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugboard_ui.py:113
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:58
+#: /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/sending_ui.py:68
@@ -5698,11 +5710,13 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder_ui.py:96
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:81
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:89
msgid "&Previous"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder_ui.py:97
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:82
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:88
msgid "&Next"
msgstr ""
@@ -5917,7 +5931,7 @@ msgstr ""
msgid "Do not insert Table of Contents into output text when using markdown"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output.py:16
+#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_output.py:15
msgid "TXT Output"
msgstr ""
@@ -6046,8 +6060,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:576
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:599
#: /home/kovid/work/calibre/src/calibre/gui2/custom_column_widgets.py:650
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:302
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:307
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:303
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:308
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:501
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:502
#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:114
@@ -6055,7 +6069,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:235
#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:268
#: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:272
-#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:975
+#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:973
msgid "Undefined"
msgstr ""
@@ -6199,7 +6213,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:594
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc.py:41
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:204
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:304
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:54
msgid "Error"
msgstr ""
@@ -6209,84 +6223,92 @@ msgid "Error communicating with device"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:611
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1116
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1114
#: /home/kovid/work/calibre/src/calibre/gui2/email.py:297
msgid "No suitable formats"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:629
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:627
msgid "Select folder to open as device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:680
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:678
msgid "Error talking to device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:681
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:679
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:724
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:722
msgid "Device: "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:726
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:724
msgid " detected."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:824
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:822
msgid "selected to send"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:846
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:841
+msgid "%i of %i Books"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:843
+msgid "0 of %i Books"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:844
msgid "Choose format to send to device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:854
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:852
msgid "No device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:855
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:853
msgid "Cannot send: No device is connected"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:858
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:862
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:856
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:860
msgid "No card"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:859
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:863
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:857
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:861
msgid "Cannot send: Device has no storage card"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:909
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:992
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1110
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:907
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:990
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1108
msgid "Auto convert the following books before uploading to the device?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:938
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:936
msgid "Sending catalogs to device."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1023
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1021
msgid "Sending news to device."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1077
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1075
msgid "Sending books to device."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1117
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1115
msgid "Could not upload the following books to the device, as no suitable formats were found. Convert the book(s) to a format supported by your device first."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1181
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1179
msgid "No space on device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1182
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1180
msgid "Cannot upload books to device there is no more free space available "
msgstr ""
@@ -6409,61 +6431,119 @@ msgstr ""
msgid "Dumping database to SQL"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:80
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:81
msgid "Loading database from SQL"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:142
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:148
msgid "Check Library -- Problems Found"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:151
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:158
+msgid ""
+"
Help
\n"
+"\n"
+" calibre stores the list of your books and their metadata in a\n"
+" database. The actual book files and covers are stored as normal\n"
+" files in the calibre library folder. The database contains a list of the files\n"
+" and covers belonging to each book entry. This tool checks that the\n"
+" actual files in the library folder on your computer match the\n"
+" information in the database.
\n"
+"\n"
+" The result of each type of check is shown to the left. The various\n"
+" checks are:\n"
+"
\n"
+" \n"
+" - Invalid titles: These are files and folders appearing\n"
+" in the library where books titles should, but that do not have the\n"
+" correct form to be a book title.
\n"
+" - Extra titles: These are extra files in your calibre\n"
+" library that appear to be correctly-formed titles, but have no corresponding\n"
+" entries in the database
\n"
+" - Invalid authors: These are files appearing\n"
+" in the library where only author folders should be.
\n"
+" - Extra authors: These are folders in the\n"
+" calibre library that appear to be authors but that do not have entries\n"
+" in the database
\n"
+" - Missing book formats: These are book formats that are in\n"
+" the database but have no corresponding format file in the book's folder.\n"
+"
- Extra book formats: These are book format files found in\n"
+" the book's folder but not in the database.\n"
+"
- Unknown files in books: These are extra files in the\n"
+" folder of each book that do not correspond to a known format or cover\n"
+" file.
\n"
+" - Missing cover files: These represent books that are marked\n"
+" in the database as having covers but the actual cover files are\n"
+" missing.
\n"
+" - Cover files not in database: These are books that have\n"
+" cover files but are marked as not having covers in the database.
\n"
+" - Folder raising exception: These represent folders in the\n"
+" calibre library that could not be processed/understood by this\n"
+" tool.
\n"
+"
\n"
+"\n"
+" There are two kinds of automatic fixes possible: Delete\n"
+" marked and Fix marked.
\n"
+" Delete marked is used to remove extra files/folders/covers that\n"
+" have no entries in the database. Check the box next to the item you want\n"
+" to delete. Use with caution.
\n"
+" Fix marked is applicable only to covers (the two lines marked\n"
+" 'fixable'). In the case of missing cover files, checking the fixable\n"
+" box and pushing this button will remove the cover mark from the\n"
+" database for all the files in that category. In the case of extra\n"
+" cover files, checking the fixable box and pushing this button will\n"
+" add the cover mark to the database for all the files in that\n"
+" category.
\n"
+" "
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:218
msgid "&Run the check again"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:154
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:221
msgid "Copy &to clipboard"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:161
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:228
msgid "Delete marked files (checked subitems)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:167
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:234
msgid "Fix marked sections (checked fixable items)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:177
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:244
msgid "Names to ignore:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:182
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:249
msgid "Enter comma-separated standard file name wildcards, such as synctoy*.dat"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:185
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:252
msgid "Extensions to ignore"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:190
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:257
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:239
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:306
msgid "(fixable)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:262
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:329
msgid "Path from library"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:262
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:329
#: /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:287
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:354
msgid "The marked files and folders will be permanently deleted. Are you sure?"
msgstr ""
@@ -6816,159 +6896,159 @@ msgstr ""
msgid "&Copy to clipboard"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:49
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:48
msgid "Show &details"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:50
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:49
msgid "Hide &details"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:54
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:53
msgid "Show detailed information about this error"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:90
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:92
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:525
msgid "Copied"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:57
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:58
msgid "Title/Author"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:58
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:59
msgid "Standard metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:59
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:60
msgid "Custom metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:60
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:61
msgid "Search/Replace"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:64
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:65
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/progress.py:76
msgid "Working"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:256
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:257
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:384
msgid "Lower Case"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:257
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:258
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:383
msgid "Upper Case"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:258
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:259
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:386
msgid "Title Case"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:259
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:260
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:387
msgid "Capitalize"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:262
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:263
msgid "Character match"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:263
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:264
msgid "Regular Expression"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:266
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:267
msgid "Replace field"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:267
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:268
msgid "Prepend to field"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:268
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:269
msgid "Append to field"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:278
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:279
msgid "Editing meta information for %d books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:319
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:320
msgid "Immediately make all changes without closing the dialog. This operation cannot be canceled or undone"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:377
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:378
msgid "Book %d:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:392
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:393
msgid "You can destroy your library using this feature. Changes are permanent. There is no undo function. You are strongly encouraged to back up your library before proceeding.Search and replace in text fields using character matching or regular expressions. "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:400
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:401
msgid "In character mode, the field is searched for the entered search text. The text is replaced by the specified replacement text everywhere it is found in the specified field. After replacement is finished, the text can be changed to upper-case, lower-case, or title-case. If the case-sensitive check box is checked, the search text must match exactly. If it is unchecked, the search text will match both upper- and lower-case letters"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:411
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:412
msgid "In regular expression mode, the search text is an arbitrary python-compatible regular expression. The replacement text can contain backreferences to parenthesized expressions in the pattern. The search is not anchored, and can match and replace multiple times on the same string. The modification functions (lower-case etc) are applied to the matched text, not to the field as a whole. The destination box specifies the field where the result after matching and replacement is to be assigned. You can replace the text in the field, or prepend or append the matched text. See this reference for more information on python's regular expressions, and in particular the 'sub' function."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:475
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:476
msgid "S/R TEMPLATE ERROR"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:595
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:596
msgid "You must specify a destination when source is a composite field"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:698
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:706
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:810
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:699
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:707
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:811
msgid "Search/replace invalid"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:699
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:700
msgid "Authors cannot be set to the empty string. Book title %s not processed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:707
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:708
msgid "Title cannot be set to the empty string. Book title %s not processed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:811
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:812
msgid "Search pattern is invalid: %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:862
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:863
msgid ""
"Applying changes to %d books.\n"
"Phase {0} {1}%%."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:891
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:892
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:555
msgid "Delete saved search/replace"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:892
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:893
msgid "The selected saved search/replace will be deleted. Are you sure?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:912
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:920
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:910
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:918
msgid "Save search/replace"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:913
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:911
msgid "Search/replace name:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:921
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:919
msgid "That saved search/replace already exists and will be overwritten. Are you sure?"
msgstr ""
@@ -7076,7 +7156,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:528
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:440
-#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:959
+#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:957
msgid "&Date:"
msgstr ""
@@ -7448,12 +7528,12 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:682
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:687
-#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:899
+#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:897
msgid "This ISBN number is valid"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:690
-#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:906
+#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:904
msgid "This ISBN number is invalid"
msgstr ""
@@ -7464,39 +7544,39 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:771
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:849
-msgid "You have changed the tags. In order to use the tags editor, you must either discard or apply these changes"
+msgid "You have changed the tags. In order to use the tags editor, you must either discard or apply these changes. Apply changes?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:807
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:805
msgid "Timed out"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:808
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:806
msgid "The download of social metadata timed out, the servers are probably busy. Try again later."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:815
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:813
msgid "There were errors"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:816
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:814
msgid "There were errors downloading social metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:850
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:848
msgid "Cannot fetch metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:851
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:849
msgid "You must specify at least one of ISBN, Title, Authors or Publisher"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:946
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:944
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:302
msgid "Permission denied"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:947
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:945
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:303
msgid "Could not open %s. Is it being used by another program?"
msgstr ""
@@ -7554,7 +7634,7 @@ msgid "Remove unused series (Series that have no books)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:439
-#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:872
+#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:870
msgid "IS&BN:"
msgstr ""
@@ -7563,7 +7643,7 @@ msgid "dd MMM yyyy"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:442
-#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1010
+#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1008
msgid "Publishe&d:"
msgstr ""
@@ -7664,6 +7744,30 @@ msgstr ""
msgid "Aborting..."
msgstr ""
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:23
+msgid "Restoring database from backups, do not interrupt, this will happen in three stages"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:25
+msgid "Restoring database"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:79
+msgid "Your list of books, with all their metadata is stored in a single file, called a database. In addition, metadata for each individual book is stored in that books' folder, as a backup.
This operation will rebuild the database from the individual book metadata. This is useful if the database has been corrupted and you get a blank list of books. Note that restoring only restores books, not any settings stored in the database, or any custom recipes.
Do you want to restore the database?"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:102
+msgid "Restoring database failed, click Show details to see details"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:107
+msgid "Restoring the database succeeded with some warnings click Show details to see the details."
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:112
+msgid "Restoring database was successful"
+msgstr ""
+
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/saved_search_editor.py:55
msgid "The current saved search will be permanently deleted. Are you sure?"
msgstr ""
@@ -8402,7 +8506,7 @@ msgid "Attached, you will find the e-book"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/email.py:247
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:117
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:186
msgid "by"
msgstr ""
@@ -8964,35 +9068,35 @@ msgstr ""
msgid "If you are sure it is not running"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/main.py:322
-msgid "Cannot Start "
-msgstr ""
-
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:323
-msgid "%s is already running."
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/main.py:326
msgid "may be running in the system tray, in the"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/main.py:328
+#: /home/kovid/work/calibre/src/calibre/gui2/main.py:325
msgid "upper right region of the screen."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/main.py:330
+#: /home/kovid/work/calibre/src/calibre/gui2/main.py:327
msgid "lower right region of the screen."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/main.py:333
+#: /home/kovid/work/calibre/src/calibre/gui2/main.py:330
msgid "try rebooting your computer."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/main.py:335
-#: /home/kovid/work/calibre/src/calibre/gui2/main.py:347
+#: /home/kovid/work/calibre/src/calibre/gui2/main.py:332
+#: /home/kovid/work/calibre/src/calibre/gui2/main.py:346
msgid "try deleting the file"
msgstr ""
+#: /home/kovid/work/calibre/src/calibre/gui2/main.py:335
+msgid "Cannot Start "
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/main.py:336
+msgid "%s is already running."
+msgstr ""
+
#: /home/kovid/work/calibre/src/calibre/gui2/main_window.py:20
msgid "Redirect console output to a dialog window (both stdout and stderr). Useful on windows where GUI apps do not have a output streams."
msgstr ""
@@ -9041,11 +9145,11 @@ msgstr ""
msgid "Tags categorize the book. This is particularly useful while searching.
They can be any wordsor phrases, separated by commas."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:913
+#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:911
msgid "&Publisher:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:978
+#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:976
msgid "Clear date"
msgstr ""
@@ -9823,97 +9927,109 @@ msgstr ""
msgid "Delete plugboard"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:110
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:179
msgid "%(plugin_type)s %(plugins)s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:111
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:180
msgid "plugins"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:120
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:189
msgid ""
"\n"
"Customization: "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:162
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:218
+msgid "Search for plugin"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:226
+msgid "No matches"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:227
+msgid "Could not find any matching plugins"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:268
msgid "Add plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:170
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:276
msgid "Installing plugins is a security risk. Plugins can contain a virus/malware. Only install it if you got it from a trusted source. Are you sure you want to proceed?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:181
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:287
msgid "Plugin {0} successfully installed under {1} plugins. You may have to restart calibre for the plugin to take effect."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:195
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:295
msgid "No valid plugin path"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:196
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:296
msgid "%s is not a valid plugin path"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:205
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:305
msgid "Select an actual plugin under %s to customize"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:211
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:311
msgid "Plugin cannot be disabled"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:212
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:312
msgid "The plugin: %s cannot be disabled"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:222
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:322
msgid "Plugin not customizable"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:223
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:323
msgid "Plugin: %s does not need customization"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:229
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:329
msgid "Must restart"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:230
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:330
msgid "You must restart calibre before you can configure the %s plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:235
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:335
msgid "Plugin {0} successfully removed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:243
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:343
msgid "Cannot remove builtin plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:244
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:344
msgid " cannot be removed. It is a builtin plugin. Try disabling it instead."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:59
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:87
msgid "Here you can customize the behavior of Calibre by controlling what plugins it uses."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:60
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:90
msgid "Enable/&Disable plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:61
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:91
msgid "&Customize plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:62
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:92
msgid "&Remove plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:63
+#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:93
msgid "&Add a new plugin"
msgstr ""
@@ -10016,7 +10132,7 @@ msgid "Here you can control how calibre will save your books when you click the
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/server.py:75
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:382
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:381
msgid "Failed to start content server"
msgstr ""
@@ -10598,62 +10714,62 @@ msgstr ""
msgid "The following books have already been converted to %s format. Do you wish to reconvert them?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:194
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:192
msgid "&Restore"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:196
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:194
msgid "&Donate to support calibre"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:200
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:198
msgid "&Eject connected device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:245
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:243
msgid "Calibre Quick Start Guide"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:307
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:305
msgid "Debug mode"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:308
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:306
msgid "You have started calibre in debug mode. After you quit calibre, the debug log will be available in the file: %s
The log will be displayed automatically."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:495
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:494
msgid "Conversion Error"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:518
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:517
msgid "Recipe Disabled"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:534
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:533
msgid "Failed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:571
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:570
msgid "is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development. Your donation helps keep calibre development going."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:597
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:596
msgid "There are active jobs. Are you sure you want to quit?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:600
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:599
msgid ""
" is communicating with the device!
\n"
" Quitting may cause corruption on the device.
\n"
" Are you sure you want to quit?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:604
-msgid "WARNING: Active jobs"
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:603
+msgid "Active jobs"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:674
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:669
msgid "will keep running in the system tray. To close it, choose Quit in the context menu of the system tray."
msgstr ""
@@ -11766,11 +11882,11 @@ msgid "Unknown files in books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/check_library.py:33
-msgid "Missing covers in books"
+msgid "Missing covers files"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/check_library.py:34
-msgid "Extra covers in books"
+msgid "Cover files not in database"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/check_library.py:35
@@ -12246,15 +12362,15 @@ msgstr ""
msgid "Main"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:2629
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:2631
msgid "
Migrating old database to ebook library in %s
"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:2658
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:2660
msgid "Copying %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:2675
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:2677
msgid "Compacting database"
msgstr ""
@@ -12933,18 +13049,58 @@ msgid "Spanish (Paraguay)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:115
-msgid "German (AT)"
+msgid "Spanish (Uruguay)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:116
-msgid "French (BE)"
+msgid "Spanish (Argentina)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:117
-msgid "Dutch (NL)"
+msgid "Spanish (Mexico)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:118
+msgid "Spanish (Cuba)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:119
+msgid "Spanish (Chile)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:120
+msgid "Spanish (Ecuador)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:121
+msgid "Spanish (Honduras)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:122
+msgid "Spanish (Venezuela)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:123
+msgid "Spanish (Bolivia)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:124
+msgid "Spanish (Nicaragua)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:125
+msgid "German (AT)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:126
+msgid "French (BE)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:127
+msgid "Dutch (NL)"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:128
msgid "Dutch (BE)"
msgstr ""
diff --git a/src/calibre/utils/localization.py b/src/calibre/utils/localization.py
index b9995db2bf..037a147e28 100644
--- a/src/calibre/utils/localization.py
+++ b/src/calibre/utils/localization.py
@@ -112,6 +112,16 @@ _extra_lang_codes = {
'en_IE' : _('English (Ireland)'),
'en_CN' : _('English (China)'),
'es_PY' : _('Spanish (Paraguay)'),
+ 'es_UY' : _('Spanish (Uruguay)'),
+ 'es_AR' : _('Spanish (Argentina)'),
+ 'es_MX' : _('Spanish (Mexico)'),
+ 'es_CU' : _('Spanish (Cuba)'),
+ 'es_CL' : _('Spanish (Chile)'),
+ 'es_EC' : _('Spanish (Ecuador)'),
+ 'es_HN' : _('Spanish (Honduras)'),
+ 'es_VE' : _('Spanish (Venezuela)'),
+ 'es_BO' : _('Spanish (Bolivia)'),
+ 'es_NI' : _('Spanish (Nicaragua)'),
'de_AT' : _('German (AT)'),
'fr_BE' : _('French (BE)'),
'nl' : _('Dutch (NL)'),
From 3ade51c73104a477a964dc89552b8dd98b2e85f2 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sun, 30 Jan 2011 14:11:29 -0700
Subject: [PATCH 34/35] ...
---
src/calibre/gui2/preferences/plugins.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/gui2/preferences/plugins.py b/src/calibre/gui2/preferences/plugins.py
index 1edd4fe5f9..8f77a03c24 100644
--- a/src/calibre/gui2/preferences/plugins.py
+++ b/src/calibre/gui2/preferences/plugins.py
@@ -266,7 +266,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def add_plugin(self):
path = choose_files(self, 'add a plugin dialog', _('Add plugin'),
- filters=[(_('Plugins'), ['zip'])], all_files=False,
+ filters=[(_('Plugins') + ' (*.zip)', ['zip'])], all_files=False,
select_only_single_file=True)
if not path:
return
From 2233dab9248fac21c6776d5b9b9fbd7e0613edd7 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sun, 30 Jan 2011 18:06:54 -0700
Subject: [PATCH 35/35] Comic input: Add an option to override the image size
in the geenrated comic. Useful if you have a device whose screen size is not
coverred by one of the available output profiles. Fixes #7837 (need new
profile added)
---
src/calibre/ebooks/comic/input.py | 16 ++++++++--
src/calibre/gui2/convert/comic_input.py | 2 +-
src/calibre/gui2/convert/comic_input.ui | 39 ++++++++++++++++---------
3 files changed, 41 insertions(+), 16 deletions(-)
diff --git a/src/calibre/ebooks/comic/input.py b/src/calibre/ebooks/comic/input.py
index 04d097ac67..c9b11e31f2 100755
--- a/src/calibre/ebooks/comic/input.py
+++ b/src/calibre/ebooks/comic/input.py
@@ -53,7 +53,7 @@ def find_pages(dir, sort_on_mtime=False, verbose=False):
prints('\t'+'\n\t'.join([os.path.basename(p) for p in pages]))
return pages
-class PageProcessor(list):
+class PageProcessor(list): # {{{
'''
Contains the actual image rendering logic. See :method:`render` and
:method:`process_pages`.
@@ -111,6 +111,13 @@ class PageProcessor(list):
SCRWIDTH, SCRHEIGHT = self.opts.output_profile.comic_screen_size
+ try:
+ if self.opts.comic_image_size:
+ SCRWIDTH, SCRHEIGHT = map(int, [x.strip() for x in
+ self.opts.comic_image_size.split('x')])
+ except:
+ pass # Ignore
+
if self.opts.keep_aspect_ratio:
# Preserve the aspect ratio by adding border
aspect = float(sizex) / float(sizey)
@@ -170,6 +177,7 @@ class PageProcessor(list):
dest = dest[:-1]
os.rename(dest+'8', dest)
self.append(dest)
+# }}}
def render_pages(tasks, dest, opts, notification=lambda x, y: x):
'''
@@ -291,7 +299,11 @@ class ComicInput(InputFormatPlugin):
OptionRecommendation(name='no_process', recommended_value=False,
help=_("Apply no processing to the image")),
OptionRecommendation(name='dont_grayscale', recommended_value=False,
- help=_('Do not convert the image to grayscale (black and white)'))
+ help=_('Do not convert the image to grayscale (black and white)')),
+ OptionRecommendation(name='comic_image_size', recommended_value=None,
+ help=_('Specify the image size as widthxheight pixels. Normally,'
+ ' an image size is automatically calculated from the output '
+ 'profile, this option overrides it.')),
])
recommendations = set([
diff --git a/src/calibre/gui2/convert/comic_input.py b/src/calibre/gui2/convert/comic_input.py
index fe86f133d1..f7f8023c0e 100644
--- a/src/calibre/gui2/convert/comic_input.py
+++ b/src/calibre/gui2/convert/comic_input.py
@@ -22,7 +22,7 @@ class PluginWidget(Widget, Ui_Form):
['colors', 'dont_normalize', 'keep_aspect_ratio', 'right2left',
'despeckle', 'no_sort', 'no_process', 'landscape',
'dont_sharpen', 'disable_trim', 'wide', 'output_format',
- 'dont_grayscale']
+ 'dont_grayscale', 'comic_image_size']
)
self.db, self.book_id = db, book_id
for x in get_option('output_format').option.choices:
diff --git a/src/calibre/gui2/convert/comic_input.ui b/src/calibre/gui2/convert/comic_input.ui
index c3b363d7e9..52c0ad2bb5 100644
--- a/src/calibre/gui2/convert/comic_input.ui
+++ b/src/calibre/gui2/convert/comic_input.ui
@@ -7,7 +7,7 @@
0
0
599
- 345
+ 398
@@ -37,70 +37,70 @@
- -
+
-
Disable &normalize
- -
+
-
Keep &aspect ratio
- -
+
-
Disable &Sharpening
- -
+
-
Disable &Trimming
- -
+
-
&Wide
- -
+
-
&Landscape
- -
+
-
&Right to left
- -
+
-
Don't so&rt
- -
+
-
De&speckle
- -
+
-
Qt::Vertical
@@ -120,7 +120,7 @@
- -
+
-
&Output format:
@@ -130,7 +130,7 @@
- -
+
-
-
@@ -140,6 +140,19 @@
+ -
+
+
+ Override image &size:
+
+
+ opt_comic_image_size
+
+
+
+ -
+
+