From 5ab738e805940ca3fe3f6c577fbed01ba4016ffe Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Aug 2009 11:39:44 -0600 Subject: [PATCH 1/7] IGN:... --- src/calibre/gui2/update.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/update.py b/src/calibre/gui2/update.py index a73b04531e..f566109bf2 100644 --- a/src/calibre/gui2/update.py +++ b/src/calibre/gui2/update.py @@ -6,7 +6,7 @@ import traceback from PyQt4.QtCore import QThread, SIGNAL import mechanize -from calibre.constants import __version__ +from calibre.constants import __version__, iswindows, isosx from calibre import browser URL = 'http://status.calibre-ebook.com/latest' @@ -18,6 +18,8 @@ class CheckForUpdates(QThread): br = browser() req = mechanize.Request(URL) req.add_header('CALIBRE_VERSION', __version__) + req.add_header('CALIBRE_OS', + 'win' if iswindows else 'osx' if isosx else 'oth') version = br.open(req).read().strip() if version and version != __version__: self.emit(SIGNAL('update_found(PyQt_PyObject)'), version) From 1ae3724038b4f6f7838fec416781ba14495c4e3f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Aug 2009 15:41:34 -0600 Subject: [PATCH 2/7] New recipe for Cuba Debate by Darko Miletic. Also improve effectiveness of the linearize tables transform. --- .../ebooks/oeb/transforms/linearize_tables.py | 12 +++-- src/calibre/gui2/images/news/cubadebate.png | Bin 0 -> 858 bytes src/calibre/web/feeds/recipes/__init__.py | 2 +- .../web/feeds/recipes/recipe_cubadebate.py | 44 ++++++++++++++++++ 4 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 src/calibre/gui2/images/news/cubadebate.png create mode 100644 src/calibre/web/feeds/recipes/recipe_cubadebate.py diff --git a/src/calibre/ebooks/oeb/transforms/linearize_tables.py b/src/calibre/ebooks/oeb/transforms/linearize_tables.py index 5842db90e5..92c3a00c7b 100644 --- a/src/calibre/ebooks/oeb/transforms/linearize_tables.py +++ b/src/calibre/ebooks/oeb/transforms/linearize_tables.py @@ -6,15 +6,19 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from calibre.ebooks.oeb.base import OEB_DOCS, XPNSMAP +from calibre.ebooks.oeb.base import OEB_DOCS, XPath class LinearizeTables(object): def linearize(self, root): - for x in root.xpath('//h:table|//h:td|//h:tr|//h:th', - namespaces=XPNSMAP): + for x in XPath('//h:table|//h:td|//h:tr|//h:th|//h:caption|' + '//h:tbody|//h:tfoot|//h:thead|//h:colgroup|//h:col')(root): x.tag = 'div' - for attr in ('valign', 'colspan', 'rowspan', 'width', 'halign'): + for attr in ('style', 'font', 'valign', + 'colspan', 'width', 'height', + 'rowspan', 'summary', 'align', + 'cellspacing', 'cellpadding', + 'frames', 'rules', 'border'): if attr in x.attrib: del x.attrib[attr] diff --git a/src/calibre/gui2/images/news/cubadebate.png b/src/calibre/gui2/images/news/cubadebate.png new file mode 100644 index 0000000000000000000000000000000000000000..4f8fc9e4fcd9e3477beced5472b93fdd72796143 GIT binary patch literal 858 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87?_$pT^vI!PEVb5-uqUe#PR#*zh~Q@(EG>k z$MH%@WFmW~goyKtOKhx(%Do*4CS5l+-r%cz^PueJf`pP7Iiu@G7ue`1x+qOzZETsQ zDB=4lJFD)V^$a~W!*2%;KmTN-fA4%Z|A+92TUHiThRe!KcxVtjlc)Kh!seT694{@O za_K{e;lvNcsjXQB6W+WGjEQ*AUoppT`DDKKW@BSxo>r&pD)EdBL0rp@eYzMZBG=!n zB*1XNq%lZ=^XrFWj|-0^8J>Rm@}>EtIKfDBMh0{9`-xSz8iP8SI6NNwlhc?U7|PX_ zxFBk+*`!~8>!jK~wx-;1In0orZvWsJo71Ugjs-!CU9KUkLq)h+*&JLL9L&tjzV!9= zZI}A~?OsCmYS&GXk8T<#WUge}v5V^y!|H%x3p8a5R+do_i?cOW*V7x{EUF7xniu zcd~5Q)g`-KdU|^Lj9IgsOr%_mX1W9i3o|U(x>ZyyF2%W_HkFD=BAgb^4p`biH)V+O>%}IXrv!?%fcf!=T`|{Ii(KY)hM( z)5n!w|5XUQ{-3l@cfNYo*&p*~&7ZH|*V9w8=6KGtZ)E|crGbpE z;yG*V Date: Sun, 16 Aug 2009 15:56:04 -0600 Subject: [PATCH 3/7] Update all Serbian and Croatin recipes to work with calibre 0.6 --- .../web/feeds/recipes/recipe_24sata.py | 25 +++---- .../web/feeds/recipes/recipe_24sata_rs.py | 66 +++++++++---------- src/calibre/web/feeds/recipes/recipe_b92.py | 38 ++++++----- src/calibre/web/feeds/recipes/recipe_blic.py | 33 ++++++---- src/calibre/web/feeds/recipes/recipe_borba.py | 42 ++++++------ src/calibre/web/feeds/recipes/recipe_danas.py | 38 +++++++---- .../web/feeds/recipes/recipe_dnevni_avaz.py | 35 ++++++---- .../web/feeds/recipes/recipe_dnevnik_cro.py | 46 ++++++++----- .../web/feeds/recipes/recipe_e_novine.py | 28 ++++---- .../web/feeds/recipes/recipe_glas_srpske.py | 20 +++--- src/calibre/web/feeds/recipes/recipe_hrt.py | 14 ++-- .../web/feeds/recipes/recipe_jutarnji.py | 57 +++++++++------- .../web/feeds/recipes/recipe_nacional_cro.py | 29 ++++---- src/calibre/web/feeds/recipes/recipe_nin.py | 32 +++++---- .../web/feeds/recipes/recipe_novosti.py | 43 +++++++----- src/calibre/web/feeds/recipes/recipe_nspm.py | 57 +++++++--------- .../web/feeds/recipes/recipe_pescanik.py | 46 +++++++------ .../web/feeds/recipes/recipe_pobjeda.py | 20 +++--- .../web/feeds/recipes/recipe_politika.py | 29 ++++---- .../web/feeds/recipes/recipe_pressonline.py | 32 ++++----- src/calibre/web/feeds/recipes/recipe_rts.py | 14 ++-- .../web/feeds/recipes/recipe_spiegel_int.py | 24 ++++--- .../web/feeds/recipes/recipe_tanjug.py | 26 ++++---- .../web/feeds/recipes/recipe_twitchfilms.py | 19 +++--- .../web/feeds/recipes/recipe_vecernji_list.py | 38 ++++++----- .../web/feeds/recipes/recipe_vijesti.py | 19 +++--- src/calibre/web/feeds/recipes/recipe_vreme.py | 35 ++++++---- 27 files changed, 501 insertions(+), 404 deletions(-) diff --git a/src/calibre/web/feeds/recipes/recipe_24sata.py b/src/calibre/web/feeds/recipes/recipe_24sata.py index 004e7ff5e6..25fe556cd4 100644 --- a/src/calibre/web/feeds/recipes/recipe_24sata.py +++ b/src/calibre/web/feeds/recipes/recipe_24sata.py @@ -9,6 +9,7 @@ __copyright__ = '2009, Darko Miletic ' import re from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class Cro24Sata(BasicNewsRecipe): title = '24 Sata - Hr' @@ -22,18 +23,18 @@ class Cro24Sata(BasicNewsRecipe): no_stylesheets = True encoding = 'utf-8' use_embedded_content = False - remove_javascript = True language = _('Croatian') + lang = 'hr-HR' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -45,9 +46,11 @@ class Cro24Sata(BasicNewsRecipe): feeds = [(u'Najnovije Vijesti', u'http://www.24sata.hr/index.php?cmd=show_rss&action=novo')] def preprocess_html(self, soup): - soup.html['lang'] = 'hr-HR' - mtag = '\n' - soup.head.insert(0,mtag) + soup.html['lang'] = self.lang + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")]) + soup.head.insert(0,mlang) + soup.head.insert(1,mcharset) for item in soup.findAll(style=True): del item['style'] return soup diff --git a/src/calibre/web/feeds/recipes/recipe_24sata_rs.py b/src/calibre/web/feeds/recipes/recipe_24sata_rs.py index 88860bf493..9ddee0be32 100644 --- a/src/calibre/web/feeds/recipes/recipe_24sata_rs.py +++ b/src/calibre/web/feeds/recipes/recipe_24sata_rs.py @@ -17,53 +17,51 @@ class Ser24Sata(BasicNewsRecipe): description = '24 sata portal vesti iz Srbije' publisher = 'Ringier d.o.o.' category = 'news, politics, entertainment, Serbia' - oldest_article = 1 + oldest_article = 7 max_articles_per_feed = 100 no_stylesheets = True encoding = 'utf-8' use_embedded_content = False - remove_javascript = True language = _('Serbian') - + lang = 'sr-Latn-RS' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] feeds = [(u'Vesti Dana', u'http://www.24sata.rs/rss.php')] - def cleanup_image_tags(self,soup): - for item in soup.findAll('img'): - for attrib in ['height','width','border','align']: - if item.has_key(attrib): - del item[attrib] - oldParent = item.parent - myIndex = oldParent.contents.index(item) - item.extract() - divtag = Tag(soup,'div') - brtag = Tag(soup,'br') - oldParent.insert(myIndex,divtag) - divtag.append(item) - divtag.append(brtag) - return soup - def preprocess_html(self, soup): - soup.html['xml:lang'] = 'sr-Latn-RS' - soup.html['lang'] = 'sr-Latn-RS' - mtag = '\n' - soup.head.insert(0,mtag) - return self.cleanup_image_tags(soup) + soup.html['xml:lang'] = self.lang + soup.html['lang'] = self.lang + + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] + + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=utf-8")]) + soup.head.insert(0,mlang) + soup.head.insert(1,mcharset) + return self.adeify_images(soup) def print_version(self, url): - article, sep, rest = url.partition('#') - article_base, sep2, article_id = article.partition('id=') - return 'http://www.24sata.co.rs/_print.php?id=' + article_id + article = url.partition('#')[0] + article_id = article.partition('id=')[2] + return 'http://www.24sata.rs/_print.php?id=' + article_id diff --git a/src/calibre/web/feeds/recipes/recipe_b92.py b/src/calibre/web/feeds/recipes/recipe_b92.py index 6f466dfaa8..98e1967bd2 100644 --- a/src/calibre/web/feeds/recipes/recipe_b92.py +++ b/src/calibre/web/feeds/recipes/recipe_b92.py @@ -14,23 +14,21 @@ class B92(BasicNewsRecipe): description = 'Dnevne vesti iz Srbije i sveta' publisher = 'B92' category = 'news, politics, Serbia' - oldest_article = 1 + oldest_article = 2 max_articles_per_feed = 100 no_stylesheets = True use_embedded_content = False - remove_javascript = True encoding = 'cp1250' language = _('Serbian') + lang = 'sr-Latn-RS' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em}"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -39,6 +37,7 @@ class B92(BasicNewsRecipe): remove_tags = [ dict(name='ul', attrs={'class':'comment-nav'}) ,dict(name=['embed','link','base'] ) + ,dict(name='div', attrs={'class':'udokum'} ) ] feeds = [ @@ -51,14 +50,19 @@ class B92(BasicNewsRecipe): def preprocess_html(self, soup): del soup.body['onload'] - mtag = '' - soup.head.insert(0,mtag) - for item in soup.findAll(style=True): - del item['style'] - for item in soup.findAll(align=True): - del item['align'] for item in soup.findAll('font'): - item.name='p' + item.name='div' if item.has_key('size'): del item['size'] + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] return soup diff --git a/src/calibre/web/feeds/recipes/recipe_blic.py b/src/calibre/web/feeds/recipes/recipe_blic.py index e212e73218..5a2c290c60 100644 --- a/src/calibre/web/feeds/recipes/recipe_blic.py +++ b/src/calibre/web/feeds/recipes/recipe_blic.py @@ -26,15 +26,13 @@ class Blic(BasicNewsRecipe): lang = 'sr-Latn-RS' 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} ' - html2lrf_options = [ - '--comment' , description - , '--category' , category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} "' - + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + } + preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] keep_only_tags = [dict(name='div', attrs={'class':'single_news'})] @@ -44,14 +42,21 @@ class Blic(BasicNewsRecipe): remove_tags = [dict(name=['object','link'])] def print_version(self, url): - start_url, question, rest_url = url.partition('?') + rest_url = url.partition('?')[2] return u'http://www.blic.rs/_print.php?' + rest_url def preprocess_html(self, soup): - mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) - soup.head.insert(0,mlang) - for item in soup.findAll(style=True): - del item['style'] + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] return self.adeify_images(soup) def get_article_url(self, article): diff --git a/src/calibre/web/feeds/recipes/recipe_borba.py b/src/calibre/web/feeds/recipes/recipe_borba.py index a7d8d9f0a4..827c94cacd 100644 --- a/src/calibre/web/feeds/recipes/recipe_borba.py +++ b/src/calibre/web/feeds/recipes/recipe_borba.py @@ -17,24 +17,23 @@ class Borba(BasicNewsRecipe): publisher = 'IP Novine Borba' category = 'news, politics, Serbia' language = _('Serbian') - oldest_article = 1 + lang = _('sr-Latn-RS') + oldest_article = 2 max_articles_per_feed = 100 no_stylesheets = True - encoding = 'utf8' - remove_javascript = True + encoding = 'utf-8' use_embedded_content = False cover_url = 'http://www.borba.rs/images/stories/novine/naslovna_v.jpg' INDEX = u'http://www.borba.rs/' - extra_css = '@font-face {font-family: "serif0";src:url(res:///Data/FONT/serif0.ttf)} @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif0, serif1, serif} .article_description{font-family: serif0, serif1, serif}' + extra_css = ' @font-face {font-family: "serif1"; src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif} .contentheading{font-size: x-large; font-weight: bold} .createdate{font-size: small; font-weight: bold} ' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -60,14 +59,17 @@ class Borba(BasicNewsRecipe): ] def preprocess_html(self, soup): - soup.html['xml:lang'] = 'sr-Latn-ME' - soup.html['lang'] = 'sr-Latn-ME' - mtag = '' - soup.head.insert(0,mtag) - for item in soup.findAll(style=True): - del item['style'] - for item in soup.findAll(font=True): - del item['font'] + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] return soup def parse_index(self): diff --git a/src/calibre/web/feeds/recipes/recipe_danas.py b/src/calibre/web/feeds/recipes/recipe_danas.py index 63a7b45738..8882b9db9d 100644 --- a/src/calibre/web/feeds/recipes/recipe_danas.py +++ b/src/calibre/web/feeds/recipes/recipe_danas.py @@ -7,9 +7,10 @@ danas.rs ''' import re from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class Danas(BasicNewsRecipe): - title = u'Danas' + title = 'Danas' __author__ = 'Darko Miletic' description = 'Vesti' publisher = 'Danas d.o.o.' @@ -17,19 +18,19 @@ class Danas(BasicNewsRecipe): oldest_article = 2 max_articles_per_feed = 100 no_stylesheets = False - remove_javascript = True use_embedded_content = False language = _('Serbian') + lang = 'sr-Latn-RS' + direction = 'ltr' 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}' - html2lrf_options = [ - '--comment' , description - , '--category' , category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -44,8 +45,17 @@ class Danas(BasicNewsRecipe): feeds = [ (u'Vesti', u'http://www.danas.rs/rss/rss.asp')] def preprocess_html(self, soup): - mtag = '' - soup.head.insert(0,mtag) - for item in soup.findAll(style=True): - del item['style'] + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.head.insert(0,mlang) + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] return soup \ No newline at end of file diff --git a/src/calibre/web/feeds/recipes/recipe_dnevni_avaz.py b/src/calibre/web/feeds/recipes/recipe_dnevni_avaz.py index 5a55db9404..e4c47c3f2e 100644 --- a/src/calibre/web/feeds/recipes/recipe_dnevni_avaz.py +++ b/src/calibre/web/feeds/recipes/recipe_dnevni_avaz.py @@ -9,6 +9,7 @@ dnevniavaz.ba import re from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class DnevniAvaz(BasicNewsRecipe): title = 'Dnevni Avaz' @@ -25,17 +26,18 @@ class DnevniAvaz(BasicNewsRecipe): cover_url = 'http://www.dnevniavaz.ba/img/logo.gif' lang = 'bs-BA' language = _('Bosnian') + direction = 'ltr' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"' - + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } + preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] keep_only_tags = [dict(name='div', attrs={'id':['fullarticle-title','fullarticle-leading','fullarticle-date','fullarticle-text','articleauthor']})] @@ -47,9 +49,20 @@ class DnevniAvaz(BasicNewsRecipe): ,(u'Najpopularnije', u'http://www.dnevniavaz.ba/rss/popularno') ] + def replace_tagname(self,soup,tagname,tagid,newtagname): + headtag = soup.find(tagname,attrs={'id':tagid}) + if headtag: + headtag.name = newtagname + return + def preprocess_html(self, soup): soup.html['xml:lang'] = self.lang soup.html['lang'] = self.lang - mtag = '\n' - soup.head.insert(0,mtag) - return soup + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")]) + soup.head.insert(0,mlang) + soup.head.insert(1,mcharset) + self.replace_tagname(soup,'div','fullarticle-title' ,'h1') + self.replace_tagname(soup,'div','fullarticle-leading','h3') + self.replace_tagname(soup,'div','fullarticle-date' ,'h5') + return self.adeify_images(soup) diff --git a/src/calibre/web/feeds/recipes/recipe_dnevnik_cro.py b/src/calibre/web/feeds/recipes/recipe_dnevnik_cro.py index ada5ea22d5..b2c3013513 100644 --- a/src/calibre/web/feeds/recipes/recipe_dnevnik_cro.py +++ b/src/calibre/web/feeds/recipes/recipe_dnevnik_cro.py @@ -9,6 +9,7 @@ dnevnik.hr import re from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class DnevnikCro(BasicNewsRecipe): title = 'Dnevnik - Hr' @@ -22,19 +23,18 @@ class DnevnikCro(BasicNewsRecipe): no_stylesheets = True encoding = 'utf-8' use_embedded_content = False - remove_javascript = True language = _('Croatian') - + lang = 'hr-HR' + direction = 'ltr' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -51,10 +51,24 @@ class DnevnikCro(BasicNewsRecipe): feeds = [(u'Vijesti', u'http://rss.dnevnik.hr/index.rss')] def preprocess_html(self, soup): - soup.html['lang'] = 'hr-HR' - mtag = '\n' - soup.head.insert(0,mtag) - for item in soup.findAll(style=True): - del item['style'] - return soup + soup.html['lang'] = self.lang + soup.html['dir' ] = self.direction + + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] + + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")]) + soup.head.insert(0,mlang) + soup.head.insert(1,mcharset) + return self.adeify_images(soup) diff --git a/src/calibre/web/feeds/recipes/recipe_e_novine.py b/src/calibre/web/feeds/recipes/recipe_e_novine.py index 83654fe4c8..166f0087d1 100644 --- a/src/calibre/web/feeds/recipes/recipe_e_novine.py +++ b/src/calibre/web/feeds/recipes/recipe_e_novine.py @@ -9,6 +9,7 @@ e-novine.com import re from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class E_novine(BasicNewsRecipe): title = 'E-Novine' @@ -16,23 +17,22 @@ class E_novine(BasicNewsRecipe): description = 'News from Serbia' publisher = 'E-novine' category = 'news, politics, Balcans' - oldest_article = 1 + oldest_article = 2 max_articles_per_feed = 100 no_stylesheets = True encoding = 'cp1250' - cover_url = 'http://www.e-novine.com/slike/slike_3/r1/g2008/m03/y3165525326702598.jpg' - remove_javascript = True use_embedded_content = False language = _('Serbian') + lang = 'sr' 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{text-align: justify; font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -43,10 +43,10 @@ class E_novine(BasicNewsRecipe): feeds = [(u'Sve vesti', u'http://www.e-novine.com/rss/e-novine.xml' )] def preprocess_html(self, soup): - soup.html['xml:lang'] = 'sr-Latn-ME' - soup.html['lang'] = 'sr-Latn-ME' - mtag = '' - soup.head.insert(0,mtag) + soup.html['xml:lang'] = self.lang + soup.html['lang'] = self.lang + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.head.insert(0,mlang) for item in soup.findAll(style=True): del item['style'] ftag = soup.find('div', attrs={'id':'css_47_0_2844H'}) diff --git a/src/calibre/web/feeds/recipes/recipe_glas_srpske.py b/src/calibre/web/feeds/recipes/recipe_glas_srpske.py index c6329387ce..8e5624792d 100644 --- a/src/calibre/web/feeds/recipes/recipe_glas_srpske.py +++ b/src/calibre/web/feeds/recipes/recipe_glas_srpske.py @@ -9,6 +9,7 @@ glassrpske.com import re from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class GlasSrpske(BasicNewsRecipe): title = 'Glas Srpske' @@ -21,7 +22,6 @@ class GlasSrpske(BasicNewsRecipe): no_stylesheets = True encoding = 'utf-8' use_embedded_content = False - remove_javascript = True cover_url = 'http://www.glassrpske.com/var/slike/glassrpske-logo.png' lang = 'sr-BA' language = _('Serbian') @@ -29,13 +29,13 @@ class GlasSrpske(BasicNewsRecipe): extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -64,8 +64,8 @@ class GlasSrpske(BasicNewsRecipe): def preprocess_html(self, soup): soup.html['xml:lang'] = self.lang soup.html['lang'] = self.lang - mtag = '\n' - soup.head.insert(0,mtag) + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.head.insert(0,mlang) return soup def parse_index(self): diff --git a/src/calibre/web/feeds/recipes/recipe_hrt.py b/src/calibre/web/feeds/recipes/recipe_hrt.py index d07b214e02..9e8e60b945 100644 --- a/src/calibre/web/feeds/recipes/recipe_hrt.py +++ b/src/calibre/web/feeds/recipes/recipe_hrt.py @@ -24,13 +24,13 @@ class HRT(BasicNewsRecipe): lang = 'hr-HR' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] diff --git a/src/calibre/web/feeds/recipes/recipe_jutarnji.py b/src/calibre/web/feeds/recipes/recipe_jutarnji.py index e8826bc4e1..89164e801e 100644 --- a/src/calibre/web/feeds/recipes/recipe_jutarnji.py +++ b/src/calibre/web/feeds/recipes/recipe_jutarnji.py @@ -8,32 +8,32 @@ jutarnji.hr import re from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class Jutarnji(BasicNewsRecipe): - title = u'Jutarnji' - __author__ = u'Darko Miletic' - description = u'Hrvatski portal' + title = 'Jutarnji' + __author__ = 'Darko Miletic' + description = 'Hrvatski portal' publisher = 'Jutarnji.hr' category = 'news, politics, Croatia' - oldest_article = 1 + oldest_article = 2 max_articles_per_feed = 100 - simultaneous_downloads = 2 delay = 1 language = _('Croatian') no_stylesheets = True use_embedded_content = False - remove_javascript = True encoding = 'cp1250' - 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{text-align: justify; font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}' + lang = 'hr-HR' + direction = 'ltr' + 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{text-align: justify; font-family: serif1, serif} .article_description{font-family: sans1, sans-serif} .vijestnaslov{font-size: x-large; font-weight: bold}' - html2lrf_options = [ - '--comment' , description - , '--category' , category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -59,11 +59,24 @@ class Jutarnji(BasicNewsRecipe): return 'http://www.jutarnji.hr/ispis_clanka.jl?artid=' + rrest def preprocess_html(self, soup): - mtag = '\n' - soup.head.insert(0,mtag) - for item in soup.findAll(style=True): - del item['style'] - for item in soup.findAll(width=True): - del item['width'] - return soup + soup.html['lang'] = self.lang + soup.html['dir' ] = self.direction + + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] + + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")]) + soup.head.insert(0,mlang) + soup.head.insert(1,mcharset) + return self.adeify_images(soup) \ No newline at end of file diff --git a/src/calibre/web/feeds/recipes/recipe_nacional_cro.py b/src/calibre/web/feeds/recipes/recipe_nacional_cro.py index bbb0112ceb..70156b8766 100644 --- a/src/calibre/web/feeds/recipes/recipe_nacional_cro.py +++ b/src/calibre/web/feeds/recipes/recipe_nacional_cro.py @@ -9,6 +9,7 @@ nacional.hr import re from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class NacionalCro(BasicNewsRecipe): title = 'Nacional - Hr' @@ -22,19 +23,20 @@ class NacionalCro(BasicNewsRecipe): no_stylesheets = True encoding = 'utf-8' use_embedded_content = False - remove_javascript = True language = _('Croatian') + lang = 'hr-HR' + direction = 'ltr' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' - + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } + preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] remove_tags = [dict(name=['object','link','embed'])] @@ -42,9 +44,12 @@ class NacionalCro(BasicNewsRecipe): feeds = [(u'Najnovije Vijesti', u'http://www.nacional.hr/rss')] def preprocess_html(self, soup): - soup.html['lang'] = 'hr-HR' - mtag = '\n' - soup.head.insert(0,mtag) + soup.html['lang'] = self.lang + soup.html['dir' ] = self.direction + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")]) + soup.head.insert(0,mlang) + soup.head.insert(1,mcharset) for item in soup.findAll(style=True): del item['style'] return soup diff --git a/src/calibre/web/feeds/recipes/recipe_nin.py b/src/calibre/web/feeds/recipes/recipe_nin.py index 4de53a1049..f7492cffce 100644 --- a/src/calibre/web/feeds/recipes/recipe_nin.py +++ b/src/calibre/web/feeds/recipes/recipe_nin.py @@ -26,21 +26,19 @@ class Nin(BasicNewsRecipe): INDEX = PREFIX + '/?change_lang=ls' LOGIN = PREFIX + '/?logout=true' FEED = PREFIX + '/misc/rss.php?feed=RSS2.0' - remove_javascript = True use_embedded_content = False language = _('Serbian') lang = 'sr-Latn-RS' direction = 'ltr' 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} .artTitle{font-size: x-large; font-weight: bold} .columnhead{font-size: small; font-weight: bold}' - html2lrf_options = [ - '--comment' , description - , '--category' , category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -74,12 +72,20 @@ class Nin(BasicNewsRecipe): mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")]) soup.head.insert(0,mlang) - soup.head.insert(1,mcharset) - for item in soup.findAll(style=True): - del item['style'] + soup.head.insert(1,mcharset) + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] return soup def get_article_url(self, article): raw = article.get('link', None) return raw.replace('.co.yu','.co.rs') - \ No newline at end of file diff --git a/src/calibre/web/feeds/recipes/recipe_novosti.py b/src/calibre/web/feeds/recipes/recipe_novosti.py index 0190307542..165a04329a 100644 --- a/src/calibre/web/feeds/recipes/recipe_novosti.py +++ b/src/calibre/web/feeds/recipes/recipe_novosti.py @@ -8,30 +8,30 @@ novosti.rs import re from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class Novosti(BasicNewsRecipe): - title = u'Vecernje Novosti' - __author__ = u'Darko Miletic' - description = u'Vesti' + title = 'Vecernje Novosti' + __author__ = 'Darko Miletic' + description = 'Vesti' publisher = 'Kompanija Novosti' category = 'news, politics, Serbia' oldest_article = 2 max_articles_per_feed = 100 no_stylesheets = True use_embedded_content = False - encoding = 'utf8' - remove_javascript = True + encoding = 'utf-8' language = _('Serbian') + lang = 'sr-Latn-RS' 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}' - html2lrf_options = [ - '--comment' , description - , '--category' , category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -41,8 +41,17 @@ class Novosti(BasicNewsRecipe): feeds = [(u'Vesti', u'http://www.novosti.rs/php/vesti/rss.php')] def preprocess_html(self, soup): - mtag = '' - soup.head.insert(0,mtag) - for item in soup.findAll(style=True): - del item['style'] + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.head.insert(0,mlang) + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] return soup diff --git a/src/calibre/web/feeds/recipes/recipe_nspm.py b/src/calibre/web/feeds/recipes/recipe_nspm.py index 360fb35c35..f90e971c37 100644 --- a/src/calibre/web/feeds/recipes/recipe_nspm.py +++ b/src/calibre/web/feeds/recipes/recipe_nspm.py @@ -21,19 +21,18 @@ class Nspm(BasicNewsRecipe): no_stylesheets = True use_embedded_content = False INDEX = 'http://www.nspm.rs/?alphabet=l' - encoding = 'utf8' - remove_javascript = True + encoding = 'utf-8' language = _('Serbian') + lang = 'sr-Latn-RS' 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{text-align: justify; font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}' - html2lrf_options = [ - '--comment' , description - , '--category' , category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] remove_tags = [ @@ -51,28 +50,18 @@ class Nspm(BasicNewsRecipe): def print_version(self, url): return url.replace('.html','/stampa.html') - def cleanup_image_tags(self,soup): - for item in soup.findAll('img'): - for attrib in ['height','width','border','align']: - if item.has_key(attrib): - del item[attrib] - oldParent = item.parent - myIndex = oldParent.contents.index(item) - item.extract() - divtag = Tag(soup,'div') - brtag = Tag(soup,'br') - oldParent.insert(myIndex,divtag) - divtag.append(item) - divtag.append(brtag) - return soup - def preprocess_html(self, soup): - lng = 'sr-Latn-RS' - soup.html['xml:lang'] = lng - soup.html['lang'] = lng - ftag = soup.find('meta',attrs={'http-equiv':'Content-Language'}) - if ftag: - ftag['content'] = lng - for item in soup.findAll(style=True): - del item['style'] - return self.cleanup_image_tags(soup) + soup.html['xml:lang'] = self.lang + soup.html['lang'] = self.lang + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] + return self.adeify_images(soup) diff --git a/src/calibre/web/feeds/recipes/recipe_pescanik.py b/src/calibre/web/feeds/recipes/recipe_pescanik.py index 278ed38183..adc4d67a8e 100644 --- a/src/calibre/web/feeds/recipes/recipe_pescanik.py +++ b/src/calibre/web/feeds/recipes/recipe_pescanik.py @@ -8,6 +8,7 @@ pescanik.net import re from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class Pescanik(BasicNewsRecipe): title = 'Pescanik' @@ -19,20 +20,18 @@ class Pescanik(BasicNewsRecipe): max_articles_per_feed = 100 no_stylesheets = True use_embedded_content = False - remove_javascript = True - encoding = 'utf8' - cover_url = "http://pescanik.net/templates/ja_teline/images/logo.png" + encoding = 'utf-8' language = _('Serbian') - 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}' + lang = 'sr-Latn-RS' + 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} .contentheading{font-size: x-large; font-weight: bold} .small{font-size: small} .createdate{font-size: x-small; font-weight: bold}' - html2lrf_options = [ - '--comment' , description - , '--category' , category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -40,18 +39,27 @@ class Pescanik(BasicNewsRecipe): remove_tags = [ dict(name='td' , attrs={'class':'buttonheading'}) ,dict(name='span', attrs={'class':'article_seperator'}) - ,dict(name=['object','link','img','h4','ul']) + ,dict(name=['object','link','h4','ul']) ] - feeds = [(u'Pescanik Online', u'http://pescanik.net/index.php?option=com_rd_rss&id=12')] + feeds = [(u'Pescanik Online', u'http://www.pescanik.net/index.php?option=com_rd_rss&id=12')] def print_version(self, url): nurl = url.replace('/index.php','/index2.php') return nurl + '&pop=1&page=0' def preprocess_html(self, soup): - mtag = '' - soup.head.insert(0,mtag) - for item in soup.findAll(style=True): - del item['style'] - return soup + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.head.insert(0,mlang) + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] + return self.adeify_images(soup) diff --git a/src/calibre/web/feeds/recipes/recipe_pobjeda.py b/src/calibre/web/feeds/recipes/recipe_pobjeda.py index 6078e6ba0a..6ecc048d2c 100644 --- a/src/calibre/web/feeds/recipes/recipe_pobjeda.py +++ b/src/calibre/web/feeds/recipes/recipe_pobjeda.py @@ -19,22 +19,20 @@ class Pobjeda(BasicNewsRecipe): publisher = 'Pobjeda a.d.' category = 'news, politics, Montenegro' no_stylesheets = True - remove_javascript = True - encoding = 'utf8' - remove_javascript = True + encoding = 'utf-8' use_embedded_content = False - language = _('Serbian') + language = _('Montenegrin') lang = 'sr-Latn-Me' INDEX = u'http://www.pobjeda.co.me' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] diff --git a/src/calibre/web/feeds/recipes/recipe_politika.py b/src/calibre/web/feeds/recipes/recipe_politika.py index 93c8f43b36..2015271ca4 100644 --- a/src/calibre/web/feeds/recipes/recipe_politika.py +++ b/src/calibre/web/feeds/recipes/recipe_politika.py @@ -1,15 +1,16 @@ #!/usr/bin/env python __license__ = 'GPL v3' -__copyright__ = '2008, Darko Miletic ' +__copyright__ = '2008-2009, Darko Miletic ' ''' politika.rs ''' import re from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class Politika(BasicNewsRecipe): - title = u'Politika Online' + title = 'Politika Online' __author__ = 'Darko Miletic' description = 'Najstariji dnevni list na Balkanu' publisher = 'Politika novine i Magazini d.o.o' @@ -21,16 +22,18 @@ class Politika(BasicNewsRecipe): remove_javascript = True encoding = 'utf8' language = _('Serbian') + lang = 'sr-Latn-RS' + direction = 'ltr' 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}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' - preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -55,11 +58,13 @@ class Politika(BasicNewsRecipe): ] def preprocess_html(self, soup): - mtag = '' - soup.head.insert(0,mtag) + soup.html['lang'] = self.lang + soup.html['dir' ] = self.direction + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.head.insert(0,mlang) for item in soup.findAll(style=True): del item['style'] ftag = soup.find('div',attrs={'class':'content_center_border'}) if ftag.has_key('align'): del ftag['align'] - return soup + return self.adeify_images(soup) diff --git a/src/calibre/web/feeds/recipes/recipe_pressonline.py b/src/calibre/web/feeds/recipes/recipe_pressonline.py index 71f69b9169..184e80a24f 100644 --- a/src/calibre/web/feeds/recipes/recipe_pressonline.py +++ b/src/calibre/web/feeds/recipes/recipe_pressonline.py @@ -9,6 +9,7 @@ pressonline.rs import re from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class PressOnline(BasicNewsRecipe): title = 'Press Online' @@ -19,20 +20,21 @@ class PressOnline(BasicNewsRecipe): oldest_article = 2 max_articles_per_feed = 100 no_stylesheets = True - encoding = 'utf8' + encoding = 'utf-8' use_embedded_content = True - cover_url = 'http://www.pressonline.rs/img/logo.gif' language = _('Serbian') + lang = 'sr-Latn-RS' + direction = 'ltr' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -57,10 +59,8 @@ class PressOnline(BasicNewsRecipe): ] def preprocess_html(self, soup): - soup.html['xml:lang'] = 'sr-Latn-RS' - soup.html['lang'] = 'sr-Latn-RS' - mtag = '\n' - soup.head.insert(0,mtag) - for img in soup.findAll('img', align=True): - del img['align'] - return soup \ No newline at end of file + soup.html['lang'] = self.lang + soup.html['dir' ] = self.direction + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.head.insert(0,mlang) + return self.adeify_images(soup) \ No newline at end of file diff --git a/src/calibre/web/feeds/recipes/recipe_rts.py b/src/calibre/web/feeds/recipes/recipe_rts.py index 57ee346d62..ebde793723 100644 --- a/src/calibre/web/feeds/recipes/recipe_rts.py +++ b/src/calibre/web/feeds/recipes/recipe_rts.py @@ -24,13 +24,13 @@ class RTS(BasicNewsRecipe): lang = 'sr-Latn-RS' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] diff --git a/src/calibre/web/feeds/recipes/recipe_spiegel_int.py b/src/calibre/web/feeds/recipes/recipe_spiegel_int.py index 3326e2b5ca..0040c76f98 100644 --- a/src/calibre/web/feeds/recipes/recipe_spiegel_int.py +++ b/src/calibre/web/feeds/recipes/recipe_spiegel_int.py @@ -1,7 +1,7 @@ #!/usr/bin/env python __license__ = 'GPL v3' -__copyright__ = '2008, Darko Miletic ' +__copyright__ = '2008-2009, Darko Miletic ' ''' spiegel.de ''' @@ -9,21 +9,25 @@ spiegel.de from calibre.web.feeds.news import BasicNewsRecipe class Spiegel_int(BasicNewsRecipe): - title = u'Spiegel Online International' + title = 'Spiegel Online International' __author__ = 'Darko Miletic' description = "News and POV from Europe's largest newsmagazine" oldest_article = 7 max_articles_per_feed = 100 - language = _('English') + language = _('English') no_stylesheets = True use_embedded_content = False - cover_url = 'http://www.spiegel.de/static/sys/v8/headlines/spiegelonline.gif' - html2lrf_options = [ - '--comment', description - , '--base-font-size', '10' - , '--category', 'news, politics, Germany' - , '--publisher', 'SPIEGEL ONLINE GmbH' - ] + publisher = 'SPIEGEL ONLINE GmbH' + category = 'news, politics, Germany' + lang = 'en' + + conversion_options = { + 'comments' : description + ,'tags' : category + ,'language' : lang + ,'publisher' : publisher + ,'pretty_print': True + } remove_tags_after = dict(name='div', attrs={'id':'spArticleBody'}) diff --git a/src/calibre/web/feeds/recipes/recipe_tanjug.py b/src/calibre/web/feeds/recipes/recipe_tanjug.py index 9a8acfaca7..c7dddb8bc1 100644 --- a/src/calibre/web/feeds/recipes/recipe_tanjug.py +++ b/src/calibre/web/feeds/recipes/recipe_tanjug.py @@ -7,6 +7,7 @@ tanjug.rs ''' import re from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class Tanjug(BasicNewsRecipe): title = 'Tanjug' @@ -14,21 +15,22 @@ class Tanjug(BasicNewsRecipe): description = 'Novinska agencija TANJUG - Dnevne vesti iz Srbije i sveta' publisher = 'Tanjug' category = 'news, politics, Serbia' - oldest_article = 1 + oldest_article = 2 max_articles_per_feed = 100 use_embedded_content = True encoding = 'utf-8' lang = 'sr-Latn-RS' language = _('Serbian') + direction = 'ltr' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em}"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -37,7 +39,7 @@ class Tanjug(BasicNewsRecipe): def preprocess_html(self, soup): soup.html['xml:lang'] = self.lang soup.html['lang' ] = self.lang - soup.html['dir' ] = "ltr" - mtag = '' - soup.head.insert(0,mtag) - return soup + soup.html['dir' ] = self.direction + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + soup.head.insert(0,mlang) + return self.adeify_images(soup) diff --git a/src/calibre/web/feeds/recipes/recipe_twitchfilms.py b/src/calibre/web/feeds/recipes/recipe_twitchfilms.py index dc0bcac88b..395920c4da 100644 --- a/src/calibre/web/feeds/recipes/recipe_twitchfilms.py +++ b/src/calibre/web/feeds/recipes/recipe_twitchfilms.py @@ -20,14 +20,15 @@ class Twitchfilm(BasicNewsRecipe): publisher = 'Twitch' category = 'twitch, twitchfilm, movie news, movie reviews, cult cinema, independent cinema, anime, foreign cinema, geek talk' language = _('English') + lang = 'en-US' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } remove_tags = [dict(name='div', attrs={'class':'feedflare'})] @@ -36,6 +37,6 @@ class Twitchfilm(BasicNewsRecipe): def preprocess_html(self, soup): mtag = Tag(soup,'meta',[('http-equiv','Content-Type'),('context','text/html; charset=utf-8')]) soup.head.insert(0,mtag) - soup.html['lang'] = 'en-US' - return soup + soup.html['lang'] = self.lang + return self.adeify_images(soup) diff --git a/src/calibre/web/feeds/recipes/recipe_vecernji_list.py b/src/calibre/web/feeds/recipes/recipe_vecernji_list.py index b34c49d9fa..0c76739b1e 100644 --- a/src/calibre/web/feeds/recipes/recipe_vecernji_list.py +++ b/src/calibre/web/feeds/recipes/recipe_vecernji_list.py @@ -9,6 +9,7 @@ www.vecernji.hr import re from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class VecernjiList(BasicNewsRecipe): title = 'Vecernji List' @@ -18,23 +19,23 @@ class VecernjiList(BasicNewsRecipe): category = 'news, politics, Croatia' oldest_article = 2 max_articles_per_feed = 100 - delay = 4 + delay = 1 no_stylesheets = True encoding = 'utf-8' use_embedded_content = False - remove_javascript = True language = _('Croatian') + lang = 'hr-HR' + direction = 'ltr' extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -46,13 +47,16 @@ class VecernjiList(BasicNewsRecipe): feeds = [(u'Vijesti', u'http://www.vecernji.hr/rss/')] def preprocess_html(self, soup): - soup.html['lang'] = 'hr-HR' - mtag = '\n' - soup.head.insert(0,mtag) - for item in soup.findAll(style=True): - del item['style'] - return soup + soup.html['lang'] = self.lang + soup.html['dir' ] = self.direction + + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) + mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")]) + soup.head.insert(0,mlang) + soup.head.insert(1,mcharset) + return self.adeify_images(soup) def print_version(self, url): - return url.replace('/index.do','/print.do') + artid = url.rpartition('-')[2] + return 'http://www.vecernji.hr/index.php?cmd=show_clanak&action=print_popup&clanak_id='+artid \ No newline at end of file diff --git a/src/calibre/web/feeds/recipes/recipe_vijesti.py b/src/calibre/web/feeds/recipes/recipe_vijesti.py index 9ef32e636c..fe7d5b0442 100644 --- a/src/calibre/web/feeds/recipes/recipe_vijesti.py +++ b/src/calibre/web/feeds/recipes/recipe_vijesti.py @@ -20,22 +20,19 @@ class Vijesti(BasicNewsRecipe): oldest_article = 2 max_articles_per_feed = 150 no_stylesheets = True - remove_javascript = True encoding = 'cp1250' - cover_url = 'http://www.vijesti.me/img/logo.gif' - remove_javascript = True use_embedded_content = False - language = _('Serbian') + language = _('Montenegrin') lang ='sr-Latn-Me' 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}' - html2lrf_options = [ - '--comment', description - , '--category', category - , '--publisher', publisher - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] diff --git a/src/calibre/web/feeds/recipes/recipe_vreme.py b/src/calibre/web/feeds/recipes/recipe_vreme.py index 1f42658d4f..02addb3f23 100644 --- a/src/calibre/web/feeds/recipes/recipe_vreme.py +++ b/src/calibre/web/feeds/recipes/recipe_vreme.py @@ -22,22 +22,20 @@ class Vreme(BasicNewsRecipe): needs_subscription = True INDEX = 'http://www.vreme.com' LOGIN = 'http://www.vreme.com/account/login.php?url=%2F' - remove_javascript = True use_embedded_content = False encoding = 'utf-8' language = _('Serbian') lang = 'sr-Latn-RS' direction = 'ltr' - extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} .heading1{font-family: sans1, sans-serif; font-size: x-large; font-weight: bold} .heading2{font-family: sans1, sans-serif; font-size: large; font-weight: bold} .toc-heading{font-family: sans1, sans-serif; font-size: small} .column-heading2{font-family: sans1, sans-serif; font-size: large} .column-heading1{font-family: sans1, sans-serif; font-size: x-large} .column-normal{font-family: sans1, sans-serif; font-size: medium} .large{font-family: sans1, sans-serif; font-size: large} ' + extra_css = ' @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} .heading1{font-family: sans1, sans-serif; font-size: x-large; font-weight: bold} .heading2{font-family: sans1, sans-serif; font-size: large; font-weight: bold} .toc-heading{font-family: sans1, sans-serif; font-size: small} .column-heading2{font-family: sans1, sans-serif; font-size: large} .column-heading1{font-family: sans1, sans-serif; font-size: x-large} .column-normal{font-family: sans1, sans-serif; font-size: medium} .large{font-family: sans1, sans-serif; font-size: large} ' - html2lrf_options = [ - '--comment' , description - , '--category' , category - , '--publisher', publisher - , '--ignore-tables' - ] - - html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True\noverride_css=" p {text-indent: 0cm; margin-top: 0em; margin-bottom: 0.5em} "' + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : lang + , 'pretty_print' : True + } preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] @@ -84,12 +82,21 @@ class Vreme(BasicNewsRecipe): del soup.body['text' ] del soup.body['bgcolor'] del soup.body['onload' ] - for item in soup.findAll(face=True): - del item['face'] - for item in soup.findAll(size=True): - del item['size'] soup.html['lang'] = self.lang soup.html['dir' ] = self.direction + + attribs = [ 'style','font','valign' + ,'colspan','width','height' + ,'rowspan','summary','align' + ,'cellspacing','cellpadding' + ,'frames','rules','border' + ] + for item in soup.body.findAll(name=['table','td','tr','th','caption','thead','tfoot','tbody','colgroup','col']): + item.name = 'div' + for attrib in attribs: + if item.has_key(attrib): + del item[attrib] + mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)]) mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")]) soup.head.insert(0,mlang) From e810b58f40921414f8c048fbba0a0fa935859a77 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 17 Aug 2009 14:11:29 -0600 Subject: [PATCH 4/7] IGN:calibredb export now supports using templates to control output directory structure/filenames. Fix pop-up menuon search box being colored. --- src/calibre/__init__.py | 2 +- src/calibre/devices/usbms/device.py | 3 +- src/calibre/ebooks/metadata/__init__.py | 9 + src/calibre/gui2/library.py | 7 +- src/calibre/library/cli.py | 36 +++- src/calibre/library/save_to_disk.py | 213 ++++++++++++++++++++++++ src/calibre/utils/config.py | 10 ++ 7 files changed, 269 insertions(+), 11 deletions(-) create mode 100644 src/calibre/library/save_to_disk.py diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index ad8f10ae06..2932d71064 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -75,7 +75,7 @@ def sanitize_file_name(name, substitute='_', as_unicode=False): ''' Sanitize the filename `name`. All invalid characters are replaced by `substitute`. The set of invalid characters is the union of the invalid characters in Windows, - OS X and Linux. Also removes leading an trailing whitespace. + OS X and Linux. Also removes leading and trailing whitespace. **WARNING:** This function also replaces path separators, so only pass file names and not full paths to it. *NOTE:* This function always returns byte strings, not unicode objects. The byte strings diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 17b02119c4..980378b928 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -520,7 +520,8 @@ class Device(DeviceConfig, DevicePlugin): main, carda, cardb = self.find_device_nodes() if main is None: - raise DeviceError(_('Unable to detect the %s disk drive.') + raise DeviceError(_('Unable to detect the %s disk drive. Your ' + ' kernel is probably exporting a deprecated version of SYSFS.') %self.__class__.__name__) self._linux_mount_map = {} diff --git a/src/calibre/ebooks/metadata/__init__.py b/src/calibre/ebooks/metadata/__init__.py index 332ae79afb..1a33bacf1a 100644 --- a/src/calibre/ebooks/metadata/__init__.py +++ b/src/calibre/ebooks/metadata/__init__.py @@ -301,6 +301,15 @@ class MetaInformation(object): def authors_from_string(self, raw): self.authors = string_to_authors(raw) + def format_authors(self): + return authors_to_string(self.authors) + + def format_tags(self): + return u', '.join([unicode(t) for t in self.tags]) + + def format_rating(self): + return unicode(self.rating) + def __unicode__(self): ans = [] def fmt(x, y): diff --git a/src/calibre/gui2/library.py b/src/calibre/gui2/library.py index fca324fe4c..f3bb4c21d0 100644 --- a/src/calibre/gui2/library.py +++ b/src/calibre/gui2/library.py @@ -1121,13 +1121,14 @@ class SearchBox(QLineEdit): def normalize_state(self): self.setText('') self.setPalette(self.default_palette) + self.setStyleSheet('QLineEdit { background-color: white; }') def clear_to_help(self): self.setPalette(self.gray) self.setText(self.help_text) self.home(False) self.initial_state = True - self.setStyleSheet("background-color: white") + self.setStyleSheet('QLineEdit { background-color: white; }') self.emit(SIGNAL('cleared()')) def clear(self): @@ -1135,8 +1136,8 @@ class SearchBox(QLineEdit): self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), '', False) def search_done(self, ok): - col = 'rgba(0,255,0,25%)' if ok else 'rgb(255,0,0,25%)' - self.setStyleSheet('background-color: '+col) + col = 'rgba(0,255,0,20%)' if ok else 'rgb(255,0,0,20%)' + self.setStyleSheet('QLineEdit { background-color: %s; }' % col) def keyPressEvent(self, event): if self.initial_state: diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index 1ac82b2edb..14896a28f4 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -11,7 +11,7 @@ import sys, os, cStringIO from textwrap import TextWrapper from urllib import quote -from calibre import terminal_controller, preferred_encoding +from calibre import terminal_controller, preferred_encoding, prints from calibre.utils.config import OptionParser, prefs try: from calibre.utils.single_qt_application import send_message @@ -488,10 +488,21 @@ show_metadata command. do_set_metadata(get_db(dbpath, opts), id, opf) return 0 -def do_export(db, ids, dir, single_dir, by_author): +def do_export(db, ids, dir, opts): if ids is None: ids = list(db.all_ids()) - db.export_to_dir(dir, ids, byauthor=by_author, single_dir=single_dir, index_is_id=True) + from calibre.library.save_to_disk import save_to_disk + failures = save_to_disk(db, ids, dir, opts=opts) + + if failures: + prints('Failed to save the following books:') + for id, title, tb in failures: + prints(str(id)+':', title) + if tb: + prints('\t'+'\n\t'.join(tb.splitlines())) + else: + prints('\tRequested formats not available') + prints(' ') def command_export(args, dbpath): parser = get_parser(_('''\ @@ -507,8 +518,21 @@ an opf file). You can get id numbers from the list command. help=(_('Export books to the specified directory. Default is')+' %default')) parser.add_option('--single-dir', default=False, action='store_true', help=_('Export all books into a single directory')) - parser.add_option('--by-author', default=False, action='store_true', - help=_('Create file names as author - title instead of title - author')) + from calibre.library.save_to_disk import config + c = config() + for pref in ['asciiize', 'update_metadata', 'write_opf', 'save_cover']: + opt = c.get_option(pref) + switch = '--dont-'+pref.replace('_', '-') + parser.add_option(switch, default=True, action='store_false', + help=opt.help+' '+_('Specifying this switch will turn ' + 'this behavior off.'), dest=pref) + + for pref in ['timefmt', 'template', 'formats']: + opt = c.get_option(pref) + switch = '--'+pref + parser.add_option(switch, default=opt.default, + help=opt.help, dest=pref) + opts, args = parser.parse_args(sys.argv[1:]+args) if (len(args) < 2 and not opts.all): parser.print_help() @@ -517,7 +541,7 @@ an opf file). You can get id numbers from the list command. return 1 ids = None if opts.all else map(int, args[1].split(',')) dir = os.path.abspath(os.path.expanduser(opts.to_dir)) - do_export(get_db(dbpath, opts), ids, dir, opts.single_dir, opts.by_author) + do_export(get_db(dbpath, opts), ids, dir, opts) return 0 def main(args=sys.argv): diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py new file mode 100644 index 0000000000..3df5bc8ab1 --- /dev/null +++ b/src/calibre/library/save_to_disk.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import os, traceback, sys, cStringIO + +from calibre.utils.config import Config, StringConfig +from calibre.utils.filenames import shorten_components_to, supports_long_names, \ + ascii_filename, sanitize_file_name +from calibre.ebooks.metadata.opf2 import metadata_to_opf +from calibre.ebooks.metadata.meta import set_metadata + +from calibre import strftime + +DEFAULT_TEMPLATE = '{author_sort}/{title} - {authors}' +FORMAT_ARGS = dict( + title='', + authors='', + author_sort='', + tags='', + series='', + series_index='', + rating='', + isbn='', + publisher='', + timestamp='', + pubdate='', + id='' + ) + + +def config(defaults=None): + if defaults is None: + c = Config('save_to_disk', _('Options to control saving to disk')) + else: + c = StringConfig(defaults) + + x = c.add_opt + x('update_metadata', default=True, + help=_('Normally, calibre will update the metadata in the saved files from what is' + ' in the calibre library. Makes saving to disk slower.')) + x('write_opf', default=True, + help=_('Normally, calibre will write the metadata into a separate OPF file along with the' + ' actual e-book files.')) + x('save_cover', default=True, + help=_('Normally, calibre will save the cover in a separate file along with the ' + 'actual e-book file(s).')) + x('formats', default='all', + help=_('Comma separated list of formats to save for each book.' + ' By default all available books are saved.')) + x('template', default=DEFAULT_TEMPLATE, + help=_('The template to control the filename and directory structure of the saved files. ' + 'Default is "%s" which will save books into a per-author ' + 'subdirectory with filenames containing title and author. ' + 'Available controls are: {%s}')%(DEFAULT_TEMPLATE, ', '.join(FORMAT_ARGS))) + x('asciiize', default=True, + help=_('Normally, calibre will convert all non English characters into English equivalents ' + 'for the file names. ' + 'WARNING: If you turn this off, you may experience errors when ' + 'saving, depending on how well the filesystem you are saving ' + 'to supports unicode.')) + x('timefmt', default='%b, %Y', + help=_('The format in which to display dates. %d - day, %b - month, ' + '%Y - year. Default is: %b, %Y')) + return c + +def preprocess_template(template): + template = template.replace('//', '/') + template = template.replace('{author}', '{authors}') + template = template.replace('{tag}', '{tags}') + return template + +def get_components(template, mi, id, timefmt='%b %Y', length=250, sanitize_func=ascii_filename): + format_args = dict(**FORMAT_ARGS) + if mi.title: + format_args['title'] = mi.title + if mi.authors: + format_args['authors'] = mi.format_authors() + if mi.author_sort: + format_args['author_sort'] = mi.author_sort + if mi.tags: + format_args['tags'] = mi.format_tags() + if mi.series: + format_args['series'] = mi.series + if mi.series_index is not None: + format_args['series_index'] = mi.format_series_index() + if mi.rating is not None: + format_args['rating'] = mi.format_rating() + if mi.isbn: + format_args['isbn'] = mi.isbn + if mi.publisher: + format_args['publisher'] = mi.publisher + if hasattr(mi.timestamp, 'timetuple'): + format_args['timestamp'] = strftime(timefmt, mi.timestamp.timetuple()) + if hasattr(mi.pubdate, 'timetuple'): + format_args['timestamp'] = strftime(timefmt, mi.pubdate.timetuple()) + format_args['id'] = str(id) + components = [x.strip() for x in template.split('/') if x.strip()] + components = [x.format(**format_args).strip() for x in components] + components = [sanitize_func(x) for x in components if x] + if not components: + components = [str(id)] + return shorten_components_to(length, components) + + +def save_book_to_disk(id, db, root, opts, length): + mi = db.get_metadata(id, index_is_id=True) + + available_formats = db.formats(id, index_is_id=True) + if not available_formats: + available_formats = [] + else: + available_formats = [x.lower().strip() for x in + available_formats.split(',')] + if opts.formats == 'all': + asked_formats = available_formats + else: + asked_formats = [x.lower().strip() for x in opts.formats.split(',')] + formats = set(available_formats).intersection(set(asked_formats)) + if not formats: + return True, id, mi.title + + components = get_components(opts.template, mi, id, opts.timefmt, length, + ascii_filename if opts.asciiize else sanitize_file_name) + base_path = os.path.join(root, *components) + base_name = os.path.basename(base_path) + dirpath = os.path.dirname(base_path) + if not os.path.exists(dirpath): + os.makedirs(dirpath) + + cdata = db.cover(id, index_is_id=True) + if opts.save_cover: + if cdata is not None: + with open(base_path+'.jpg', 'wb') as f: + f.write(cdata) + mi.cover = base_name+'.jpg' + else: + mi.cover = None + + if opts.write_opf: + opf = metadata_to_opf(mi) + with open(base_path+'.opf', 'wb') as f: + f.write(opf) + + if cdata is not None: + mi.cover_data = ('jpg', cdata) + mi.cover = None + + written = False + for fmt in formats: + data = db.format(id, fmt, index_is_id=True) + if data is None: + continue + else: + written = True + if opts.update_metadata: + stream = cStringIO.StringIO() + stream.write(data) + stream.seek(0) + try: + set_metadata(stream, mi, fmt) + except: + traceback.print_exc() + stream.seek(0) + data = stream.read() + with open(base_path+'.'+fmt, 'wb') as f: + f.write(data) + + return not written, id, mi.title + + + +def save_to_disk(db, ids, root, opts=None, callback=None): + ''' + Save books from the database ``db`` to the path specified by ``root``. + + :param:`ids` iterable of book ids to save from the database. + :param:`callback` is an optional callable that is called on after each + book is processed with the arguments: id, title and failed + :return: A list of failures. Each element of the list is a tuple + (id, title, traceback) + ''' + if opts is None: + opts = config().parse() + if isinstance(root, unicode): + root = root.encode(sys.getfilesystemencoding()) + root = os.path.abspath(root) + + opts.template = preprocess_template(opts.template) + length = 1000 if supports_long_names(root) else 250 + length -= len(root) + if length < 5: + raise ValueError('%r is too long.'%root) + failures = [] + for x in ids: + tb = '' + try: + failed, id, title = save_book_to_disk(x, db, root, opts, length) + except: + failed, id, title = True, x, db.title(x, index_is_id=True) + tb = traceback.format_exc() + if failed: + failures.append((id, title, tb)) + if callable(callback): + if not callback(int(id), title, failed): + break + return failures + + diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py index 06af68dad6..f4befc6052 100644 --- a/src/calibre/utils/config.py +++ b/src/calibre/utils/config.py @@ -216,6 +216,14 @@ class OptionSet(object): return True return False + def get_option(self, name_or_option_object): + idx = self.preferences.index(name_or_option_object) + if idx > -1: + return self.preferences[idx] + for p in self.preferences: + if p.name == name_or_option_object: + return p + def add_group(self, name, description=''): if name in self.group_list: raise ValueError('A group by the name %s already exists in this set'%name) @@ -370,6 +378,7 @@ class ConfigInterface(object): self.add_group = self.option_set.add_group self.remove_opt = self.remove = self.option_set.remove_opt self.parse_string = self.option_set.parse_string + self.get_option = self.option_set.get_option def update(self, other): self.option_set.update(other.option_set) @@ -381,6 +390,7 @@ class ConfigInterface(object): def smart_update(self, opts1, opts2): self.option_set.smart_update(opts1, opts2) + class Config(ConfigInterface): ''' A file based configuration. From 6973d806029e1e9f8eb752fc46cd876d65f58005 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 17 Aug 2009 21:12:03 -0600 Subject: [PATCH 5/7] Add an "Add/Save" category to Preferences to control the "Add books" and "Save to disk" options --- src/calibre/gui2/__init__.py | 2 + src/calibre/gui2/dialogs/add_save.py | 66 ++++++ .../dialogs/{config.py => config/__init__.py} | 17 +- src/calibre/gui2/dialogs/config/add_save.py | 100 +++++++++ src/calibre/gui2/dialogs/config/add_save.ui | 195 ++++++++++++++++++ .../gui2/dialogs/{ => config}/config.ui | 73 +++---- src/calibre/gui2/dialogs/config/history.py | 40 ++++ src/calibre/library/database2.py | 3 +- src/calibre/library/save_to_disk.py | 39 ++-- 9 files changed, 460 insertions(+), 75 deletions(-) create mode 100644 src/calibre/gui2/dialogs/add_save.py rename src/calibre/gui2/dialogs/{config.py => config/__init__.py} (98%) create mode 100644 src/calibre/gui2/dialogs/config/add_save.py create mode 100644 src/calibre/gui2/dialogs/config/add_save.ui rename src/calibre/gui2/dialogs/{ => config}/config.ui (95%) create mode 100644 src/calibre/gui2/dialogs/config/history.py diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index e09a54501c..b993b15e37 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -74,6 +74,8 @@ def _config(): c.add_opt('search_as_you_type', default=True, help='Start searching as you type. If this is disabled then search will ' 'only take place when the Enter or Return key is pressed.') + c.add_opt('save_to_disk_template_history', default=[], + help='Previously used Save to Disk templates') return ConfigProxy(c) config = _config() diff --git a/src/calibre/gui2/dialogs/add_save.py b/src/calibre/gui2/dialogs/add_save.py new file mode 100644 index 0000000000..7bd6273b4a --- /dev/null +++ b/src/calibre/gui2/dialogs/add_save.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import textwrap + +from PyQt4.Qt import QTabWidget + +from calibre.gui2.dialogs.add_save_ui import Ui_TabWidget +from calibre.library.save_to_disk import config, FORMAT_ARG_DESCS + + +class AddSave(QTabWidget, Ui_TabWidget): + + def __init__(self, parent=None): + QTabWidget.__init__(self, parent) + self.setupUi(self) + c = config() + opts = c.parse() + for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf'): + g = getattr(self, 'opt_'+x) + g.setChecked(getattr(opts, x)) + help = '\n'.join(textwrap.wrap(c.get_option(x).help, 75)) + g.setToolTip(help) + g.setWhatsThis(help) + + for x in ('formats', 'timefmt'): + g = getattr(self, 'opt_'+x) + g.setText(getattr(opts, x)) + help = '\n'.join(textwrap.wrap(c.get_option(x).help, 75)) + g.setToolTip(help) + g.setWhatsThis(help) + + help = '\n'.join(textwrap.wrap(c.get_option('template').help, 75)) + self.opt_template.initialize('save_to_disk_template_history', + opts.template, help=help) + + variables = sorted(FORMAT_ARG_DESCS.keys()) + rows = [] + for var in variables: + rows.append(u'%s%s'% + (var, FORMAT_ARG_DESCS[var])) + table = u'%s
'%(u'\n'.join(rows)) + self.template_variables.setText(table) + + def save_settings(self): + c = config() + for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf'): + c.set(x, getattr(self, 'opt_'+x).isChecked()) + for x in ('formats', 'template', 'timefmt'): + c.set(x, unicode(getattr(self, 'opt_'+x).text()).strip()) + + + +if __name__ == '__main__': + from PyQt4.Qt import QApplication + app=QApplication([]) + a = AddSave() + a.show() + app.exec_() + a.save_settings() + diff --git a/src/calibre/gui2/dialogs/config.py b/src/calibre/gui2/dialogs/config/__init__.py similarity index 98% rename from src/calibre/gui2/dialogs/config.py rename to src/calibre/gui2/dialogs/config/__init__.py index 04b5650188..feab592467 100644 --- a/src/calibre/gui2/dialogs/config.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -10,12 +10,11 @@ from PyQt4.Qt import QDialog, QMessageBox, QListWidgetItem, QIcon, \ QDialogButtonBox, QTabWidget, QBrush, QLineEdit from calibre.constants import islinux, iswindows -from calibre.gui2.dialogs.config_ui import Ui_Dialog +from calibre.gui2.dialogs.config.config_ui import Ui_Dialog from calibre.gui2 import qstring_to_unicode, choose_dir, error_dialog, config, \ ALL_COLUMNS, NONE, info_dialog, choose_files, \ warning_dialog from calibre.utils.config import prefs -from calibre.gui2.widgets import FilenamePattern from calibre.gui2.library import BooksModel from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks.oeb.iterator import is_supported @@ -193,12 +192,12 @@ class CategoryModel(QStringListModel): def __init__(self, *args): QStringListModel.__init__(self, *args) self.setStringList([_('General'), _('Interface'), _('Conversion'), - _('Email\nDelivery'), + _('Email\nDelivery'), _('Add/Save'), _('Advanced'), _('Content\nServer'), _('Plugins')]) self.icons = list(map(QVariant, map(QIcon, [':/images/dialog_information.svg', ':/images/lookfeel.svg', ':/images/convert.svg', - ':/images/mail.svg', ':/images/view.svg', + ':/images/mail.svg', ':/images/save.svg', ':/images/view.svg', ':/images/network-server.svg', ':/images/plugins.svg']))) def data(self, index, role): @@ -373,9 +372,6 @@ class ConfigDialog(QDialog, Ui_Dialog): self.connect(self.column_up, SIGNAL('clicked()'), self.up_column) self.connect(self.column_down, SIGNAL('clicked()'), self.down_column) - self.filename_pattern = FilenamePattern(self) - self.metadata_box.layout().insertWidget(0, self.filename_pattern) - icons = config['toolbar_icon_size'] self.toolbar_button_size.setCurrentIndex(0 if icons == self.ICON_SIZES[0] else 1 if icons == self.ICON_SIZES[1] else 2) self.show_toolbar_text.setChecked(config['show_text_in_toolbar']) @@ -408,7 +404,6 @@ class ConfigDialog(QDialog, Ui_Dialog): for item in items: self.language.addItem(item[1], QVariant(item[0])) - self.pdf_metadata.setChecked(prefs['read_file_metadata']) exts = set([]) for ext in BOOK_EXTENSIONS: @@ -439,7 +434,6 @@ class ConfigDialog(QDialog, Ui_Dialog): self.password.setText(opts.password if opts.password else '') self.auto_launch.setChecked(config['autolaunch_server']) self.systray_icon.setChecked(config['systray_icon']) - self.search_as_you_type.setChecked(config['search_as_you_type']) self.sync_news.setChecked(config['upload_news_to_device']) self.delete_news.setChecked(config['delete_news_from_library_on_upload']) p = {'normal':0, 'high':1, 'low':2}[prefs['worker_process_priority']] @@ -683,6 +677,8 @@ class ConfigDialog(QDialog, Ui_Dialog): return if not self.conversion_options.commit(): return + if not self.add_save.save_settings(): + return config['use_roman_numerals_for_series_number'] = bool(self.roman_numerals.isChecked()) config['new_version_notification'] = bool(self.new_version_notification.isChecked()) prefs['network_timeout'] = int(self.timeout.value()) @@ -697,11 +693,8 @@ class ConfigDialog(QDialog, Ui_Dialog): config['show_text_in_toolbar'] = bool(self.show_toolbar_text.isChecked()) config['separate_cover_flow'] = bool(self.separate_cover_flow.isChecked()) config['disable_tray_notification'] = not self.systray_notifications.isChecked() - pattern = self.filename_pattern.commit() - prefs['filename_pattern'] = pattern p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()] prefs['worker_process_priority'] = p - prefs['read_file_metadata'] = bool(self.pdf_metadata.isChecked()) prefs['output_format'] = unicode(self.output_format.currentText()).upper() config['cover_flow_queue_length'] = self.cover_browse.value() prefs['language'] = str(self.language.itemData(self.language.currentIndex()).toString()) diff --git a/src/calibre/gui2/dialogs/config/add_save.py b/src/calibre/gui2/dialogs/config/add_save.py new file mode 100644 index 0000000000..ba4289ddb9 --- /dev/null +++ b/src/calibre/gui2/dialogs/config/add_save.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import textwrap + +from PyQt4.Qt import QTabWidget + +from calibre.gui2.dialogs.config.add_save_ui import Ui_TabWidget +from calibre.library.save_to_disk import config, FORMAT_ARG_DESCS, \ + preprocess_template +from calibre.gui2 import error_dialog +from calibre.utils.config import prefs +from calibre.gui2.widgets import FilenamePattern + +class AddSave(QTabWidget, Ui_TabWidget): + + def __init__(self, parent=None): + QTabWidget.__init__(self, parent) + self.setupUi(self) + while self.count() > 2: + self.removeTab(2) + c = config() + opts = c.parse() + for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf'): + g = getattr(self, 'opt_'+x) + g.setChecked(getattr(opts, x)) + help = '\n'.join(textwrap.wrap(c.get_option(x).help, 75)) + g.setToolTip(help) + g.setWhatsThis(help) + + for x in ('formats', 'timefmt'): + g = getattr(self, 'opt_'+x) + g.setText(getattr(opts, x)) + help = '\n'.join(textwrap.wrap(c.get_option(x).help, 75)) + g.setToolTip(help) + g.setWhatsThis(help) + + help = '\n'.join(textwrap.wrap(c.get_option('template').help, 75)) + self.opt_template.initialize('save_to_disk_template_history', + opts.template, help) + + variables = sorted(FORMAT_ARG_DESCS.keys()) + rows = [] + for var in variables: + rows.append(u'%s%s'% + (var, FORMAT_ARG_DESCS[var])) + table = u'%s
'%(u'\n'.join(rows)) + self.template_variables.setText(table) + + self.opt_read_metadata_from_filename.setChecked(prefs['read_file_metadata']) + self.metadata_box.setEnabled(self.opt_read_metadata_from_filename.isChecked()) + self.filename_pattern = FilenamePattern(self) + self.metadata_box.layout().insertWidget(0, self.filename_pattern) + + + + def validate(self): + tmpl = preprocess_template(self.opt_template.text()) + fa = {} + for x in FORMAT_ARG_DESCS.keys(): + fa[x]='' + try: + tmpl.format(**fa) + except Exception, err: + error_dialog(self, _('Invalid template'), + '

'+_('The template %s is invalid:')%tmpl + \ + '
'+str(err), show=True) + return False + return True + + def save_settings(self): + if not self.validate(): + return False + c = config() + for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf'): + c.set(x, getattr(self, 'opt_'+x).isChecked()) + for x in ('formats', 'template', 'timefmt'): + c.set(x, unicode(getattr(self, 'opt_'+x).text()).strip()) + self.opt_template.save_history('save_to_disk_template_history') + prefs['read_file_metadata'] = bool(self.opt_read_metadata_from_filename.isChecked()) + pattern = self.filename_pattern.commit() + prefs['filename_pattern'] = pattern + + return True + + + +if __name__ == '__main__': + from PyQt4.Qt import QApplication + app=QApplication([]) + a = AddSave() + a.show() + app.exec_() + a.save_settings() + diff --git a/src/calibre/gui2/dialogs/config/add_save.ui b/src/calibre/gui2/dialogs/config/add_save.ui new file mode 100644 index 0000000000..e61dc102ee --- /dev/null +++ b/src/calibre/gui2/dialogs/config/add_save.ui @@ -0,0 +1,195 @@ + + + TabWidget + + + + 0 + 0 + 645 + 516 + + + + TabWidget + + + 0 + + + + &Adding books + + + + + + Here you can control how calibre will read metadata from the files you add to it. calibre can either read metadata from the contents of the file, or from the filename. + + + true + + + + + + + Read metadata from &file name + + + + + + + &Configure metadata from file name + + + + + + Qt::Vertical + + + + 20 + 363 + + + + + + + + + + + + &Saving books + + + + + + Here you can control how calibre will save your books when you click the Save to Disk button: + + + true + + + + + + + Save &cover separately + + + + + + + Update &metadata in saved copies + + + + + + + Save metadata in &OPF file + + + + + + + Convert non-English characters to &English equivalents + + + + + + + Format &dates as: + + + opt_timefmt + + + + + + + + + + File &formats to save: + + + opt_formats + + + + + + + + + + Save &template + + + + + + By adjusting the template below, you can control what folders the files are saved in and what filenames they are given. You can use the / character to indicate sub-folders. Available metadata variables are described below. If a particular book does not have some metadata, the variable will be replaced by the empty string. + + + true + + + + + + + Available variables: + + + + + + + + + + + + + + + + + + HistoryBox + QComboBox +

calibre/gui2/dialogs/config/history.h
+ + + + + + opt_read_metadata_from_filename + toggled(bool) + metadata_box + setEnabled(bool) + + + 159 + 81 + + + 178 + 122 + + + + + diff --git a/src/calibre/gui2/dialogs/config.ui b/src/calibre/gui2/dialogs/config/config.ui similarity index 95% rename from src/calibre/gui2/dialogs/config.ui rename to src/calibre/gui2/dialogs/config/config.ui index be213ba7bc..e38b16cdf5 100644 --- a/src/calibre/gui2/dialogs/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -15,7 +15,7 @@ Preferences - + :/images/config.svg:/images/config.svg @@ -115,7 +115,7 @@ ... - + :/images/mimetypes/dir.svg:/images/mimetypes/dir.svg @@ -131,19 +131,6 @@ - - - - If you disable this setting, metadata is guessed from the filename instead. This can be configured in the Advanced section. - - - Read &metadata from files - - - true - - - @@ -258,7 +245,7 @@ ... - + :/images/arrow-up.svg:/images/arrow-up.svg @@ -282,7 +269,7 @@ ... - + :/images/arrow-down.svg:/images/arrow-down.svg @@ -339,7 +326,7 @@ ... - + :/images/plus.svg:/images/plus.svg @@ -366,7 +353,7 @@ ... - + :/images/list_remove.svg:/images/list_remove.svg @@ -543,7 +530,7 @@ ... - + :/images/arrow-up.svg:/images/arrow-up.svg @@ -567,7 +554,7 @@ ... - + :/images/arrow-down.svg:/images/arrow-down.svg @@ -627,7 +614,7 @@ &Add email - + :/images/plus.svg:/images/plus.svg @@ -654,7 +641,7 @@ &Remove email - + :/images/minus.svg:/images/minus.svg @@ -687,6 +674,14 @@ + + + + + + + + @@ -729,28 +724,6 @@ - - - - &Metadata from file name - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - @@ -1020,7 +993,7 @@ ... - + :/images/document_open.svg:/images/document_open.svg @@ -1079,9 +1052,15 @@
calibre/gui2/wizard/send_email.h
1 + + AddSave + QTabWidget +
calibre/gui2/dialogs/config/add_save.h
+ 1 +
- + diff --git a/src/calibre/gui2/dialogs/config/history.py b/src/calibre/gui2/dialogs/config/history.py new file mode 100644 index 0000000000..74b88a4380 --- /dev/null +++ b/src/calibre/gui2/dialogs/config/history.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +from PyQt4.Qt import QComboBox, QStringList, Qt + +from calibre.gui2 import config as gui_conf + +class HistoryBox(QComboBox): + + def __init__(self, parent=None): + QComboBox.__init__(self, parent) + self.setEditable(True) + + def initialize(self, opt_name, default, help=None): + history = gui_conf[opt_name] + if default not in history: + history.append(default) + self.addItems(QStringList(history)) + self.setCurrentIndex(self.findText(default, Qt.MatchFixedString)) + if help is not None: + self.setToolTip(help) + self.setWhatsThis(help) + + def save_history(self, opt_name): + history = [unicode(self.itemText(i)) for i in range(self.count())] + ct = self.text() + if ct not in history: + history = [ct] + history + gui_conf[opt_name] = history[:10] + + def text(self): + return unicode(self.currentText()).strip() + + + diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 149e92ddf9..283de1c2f7 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1488,8 +1488,9 @@ class LibraryDatabase2(LibraryDatabase): yield record def all_ids(self): + x = FIELD_MAP['id'] for i in iter(self): - yield i['id'] + yield i[x] def get_data_as_dict(self, prefix=None, authors_as_string=False): ''' diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index 3df5bc8ab1..a274efe5ee 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -6,32 +6,37 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os, traceback, sys, cStringIO +import os, traceback, cStringIO from calibre.utils.config import Config, StringConfig from calibre.utils.filenames import shorten_components_to, supports_long_names, \ ascii_filename, sanitize_file_name from calibre.ebooks.metadata.opf2 import metadata_to_opf from calibre.ebooks.metadata.meta import set_metadata +from calibre.constants import preferred_encoding, filesystem_encoding from calibre import strftime DEFAULT_TEMPLATE = '{author_sort}/{title} - {authors}' -FORMAT_ARGS = dict( - title='', - authors='', - author_sort='', - tags='', - series='', - series_index='', - rating='', - isbn='', - publisher='', - timestamp='', - pubdate='', - id='' +FORMAT_ARG_DESCS = dict( + title=_('The title'), + authors=_('The authors'), + author_sort=_('The author sort string'), + tags=_('The tags'), + series=_('The series'), + series_index=_('The series number'), + rating=_('The rating'), + isbn=_('The ISBN'), + publisher=_('The publisher'), + timestamp=_('The date'), + pubdate=_('The published date'), + id=_('The calibre internal id') ) +FORMAT_ARGS = {} +for x in FORMAT_ARG_DESCS: + FORMAT_ARGS[x] = '' + def config(defaults=None): if defaults is None: @@ -72,6 +77,8 @@ def preprocess_template(template): template = template.replace('//', '/') template = template.replace('{author}', '{authors}') template = template.replace('{tag}', '{tags}') + if not isinstance(template, unicode): + template = template.decode(preferred_encoding, 'replace') return template def get_components(template, mi, id, timefmt='%b %Y', length=250, sanitize_func=ascii_filename): @@ -104,6 +111,8 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250, sanitize_func= components = [sanitize_func(x) for x in components if x] if not components: components = [str(id)] + components = [x.encode(filesystem_encoding, 'replace') if isinstance(x, + unicode) else x for x in components] return shorten_components_to(length, components) @@ -187,7 +196,7 @@ def save_to_disk(db, ids, root, opts=None, callback=None): if opts is None: opts = config().parse() if isinstance(root, unicode): - root = root.encode(sys.getfilesystemencoding()) + root = root.encode(filesystem_encoding) root = os.path.abspath(root) opts.template = preprocess_template(opts.template) From 181802da53d34423424452cf1b5da2c87bb176e2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 18 Aug 2009 15:23:11 -0600 Subject: [PATCH 6/7] Saving to disk is now fully customizable. The user can control the file and directory structure of the saved files as well as various other aspects of the save process. --- src/calibre/ebooks/metadata/worker.py | 47 +- src/calibre/gui2/add.py | 15 +- src/calibre/gui2/dialogs/config/add_save.py | 7 +- src/calibre/gui2/dialogs/config/add_save.ui | 21 +- src/calibre/gui2/main.py | 22 +- src/calibre/library/cli.py | 5 +- src/calibre/library/save_to_disk.py | 10 +- src/calibre/translations/calibre.pot | 709 +++++++++++------- src/calibre/utils/config.py | 1 + .../web/feeds/recipes/recipe_guardian.py | 50 +- 10 files changed, 534 insertions(+), 353 deletions(-) diff --git a/src/calibre/ebooks/metadata/worker.py b/src/calibre/ebooks/metadata/worker.py index 684ebd6b79..c675e92b1d 100644 --- a/src/calibre/ebooks/metadata/worker.py +++ b/src/calibre/ebooks/metadata/worker.py @@ -153,14 +153,17 @@ def read_metadata(paths, result_queue, chunk=50, spare_server=None): t.start() return t + +########################################################################### +############ Saving ##################### +########################################################################### + class SaveWorker(Thread): - def __init__(self, result_queue, db, ids, path, by_author=False, - single_dir=False, single_format=None, spare_server=None): + def __init__(self, result_queue, db, ids, path, opts, spare_server=None): Thread.__init__(self) self.daemon = True - self.path, self.by_author = path, by_author - self.single_dir, self.single_format = single_dir, single_format + self.path, self.opts = path, opts self.ids = ids self.library_path = db.library_path self.canceled = False @@ -170,17 +173,22 @@ class SaveWorker(Thread): self.start() def run(self): + from calibre.library.save_to_disk import config server = Server() if self.spare_server is None else self.spare_server ids = set(self.ids) tasks = server.split(list(ids)) jobs = set([]) + c = config() + recs = {} + for pref in c.preferences: + recs[pref.name] = getattr(self.opts, pref.name) + for i, task in enumerate(tasks): tids = [x[-1] for x in task] job = ParallelJob('save_book', 'Save books (%d of %d)'%(i, len(tasks)), lambda x,y:x, - args=[tids, self.library_path, self.path, self.single_dir, - self.single_format, self.by_author]) + args=[tids, self.library_path, self.path, recs]) jobs.add(job) server.add_job(job) @@ -192,9 +200,9 @@ class SaveWorker(Thread): job.update(consume_notifications=False) while True: try: - id, title, ok = job.notifications.get_nowait()[0] + id, title, ok, tb = job.notifications.get_nowait()[0] if id in ids: - self.result_queue.put((id, title, ok)) + self.result_queue.put((id, title, ok, tb)) ids.remove(id) except Empty: break @@ -221,23 +229,18 @@ class SaveWorker(Thread): pass -def save_book(task, library_path, path, single_dir, single_format, - by_author, notification=lambda x,y:x): +def save_book(task, library_path, path, recs, notification=lambda x,y:x): from calibre.library.database2 import LibraryDatabase2 db = LibraryDatabase2(library_path) + from calibre.library.save_to_disk import config, save_to_disk + opts = config().parse() + for name in recs: + setattr(opts, name, recs[name]) - def callback(id, title): - notification((id, title, True)) + + def callback(id, title, failed, tb): + notification((id, title, not failed, tb)) return True - if single_format is None: - failures = [] - db.export_to_dir(path, task, index_is_id=True, byauthor=by_author, - callback=callback, single_dir=single_dir) - else: - failures = db.export_single_format_to_dir(path, task, single_format, - index_is_id=True, callback=callback) - - for id, title in failures: - notification((id, title, False)) + save_to_disk(db, task, path, opts, callback) diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py index c7c3a9e62e..1f5670b0d3 100644 --- a/src/calibre/gui2/add.py +++ b/src/calibre/gui2/add.py @@ -295,13 +295,13 @@ class Adder(QObject): class Saver(QObject): - def __init__(self, parent, db, callback, rows, path, - by_author=False, single_dir=False, single_format=None, + def __init__(self, parent, db, callback, rows, path, opts, spare_server=None): QObject.__init__(self, parent) self.pd = ProgressDialog(_('Saving...'), parent=parent) self.spare_server = spare_server self.db = db + self.opts = opts self.pd.setModal(True) self.pd.show() self.pd.set_min(0) @@ -315,8 +315,8 @@ class Saver(QObject): self.failures = set([]) from calibre.ebooks.metadata.worker import SaveWorker - self.worker = SaveWorker(self.rq, db, self.ids, path, by_author, - single_dir, single_format, spare_server=self.spare_server) + self.worker = SaveWorker(self.rq, db, self.ids, path, self.opts, + spare_server=self.spare_server) self.connect(self.pd, SIGNAL('canceled()'), self.canceled) self.timer = QTimer(self) self.connect(self.timer, SIGNAL('timeout()'), self.update) @@ -344,15 +344,14 @@ class Saver(QObject): return try: - id, title, ok = self.rq.get_nowait() + id, title, ok, tb = self.rq.get_nowait() except Empty: return self.pd.value += 1 self.ids.remove(id) if not isinstance(title, unicode): - title = str(title).decode('utf-8', preferred_encoding) + title = str(title).decode(preferred_encoding, 'replace') self.pd.set_msg(_('Saved')+' '+title) if not ok: - self.failures.add(title) - + self.failures.add((title, tb)) diff --git a/src/calibre/gui2/dialogs/config/add_save.py b/src/calibre/gui2/dialogs/config/add_save.py index ba4289ddb9..6d91745044 100644 --- a/src/calibre/gui2/dialogs/config/add_save.py +++ b/src/calibre/gui2/dialogs/config/add_save.py @@ -52,13 +52,10 @@ class AddSave(QTabWidget, Ui_TabWidget): table = u'%s
'%(u'\n'.join(rows)) self.template_variables.setText(table) - self.opt_read_metadata_from_filename.setChecked(prefs['read_file_metadata']) - self.metadata_box.setEnabled(self.opt_read_metadata_from_filename.isChecked()) + self.opt_read_metadata_from_filename.setChecked(not prefs['read_file_metadata']) self.filename_pattern = FilenamePattern(self) self.metadata_box.layout().insertWidget(0, self.filename_pattern) - - def validate(self): tmpl = preprocess_template(self.opt_template.text()) fa = {} @@ -82,7 +79,7 @@ class AddSave(QTabWidget, Ui_TabWidget): for x in ('formats', 'template', 'timefmt'): c.set(x, unicode(getattr(self, 'opt_'+x).text()).strip()) self.opt_template.save_history('save_to_disk_template_history') - prefs['read_file_metadata'] = bool(self.opt_read_metadata_from_filename.isChecked()) + prefs['read_file_metadata'] = not bool(self.opt_read_metadata_from_filename.isChecked()) pattern = self.filename_pattern.commit() prefs['filename_pattern'] = pattern diff --git a/src/calibre/gui2/dialogs/config/add_save.ui b/src/calibre/gui2/dialogs/config/add_save.ui index e61dc102ee..6faec64626 100644 --- a/src/calibre/gui2/dialogs/config/add_save.ui +++ b/src/calibre/gui2/dialogs/config/add_save.ui @@ -34,7 +34,7 @@ - Read metadata from &file name + Read metadata only from &file name @@ -174,22 +174,5 @@ - - - opt_read_metadata_from_filename - toggled(bool) - metadata_box - setEnabled(bool) - - - 159 - 81 - - - 178 - 122 - - - - + diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 8ca4c9a17d..dc0cc0b2a9 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -1064,11 +1064,14 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): if self.current_view() is self.library_view: from calibre.gui2.add import Saver + from calibre.library.save_to_disk import config + opts = config().parse() + if single_format is not None: + opts.formats = single_format + if single_dir: + opts.template = '{title} - {authors}' self._saver = Saver(self, self.library_view.model().db, - Dispatcher(self._books_saved), rows, path, - by_author=self.library_view.model().by_author, - single_dir=single_dir, - single_format=single_format, + Dispatcher(self._books_saved), rows, path, opts, spare_server=self.spare_server) else: @@ -1078,19 +1081,20 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): def _books_saved(self, path, failures, error): - single_format = self._saver.worker.single_format self._saver = None if error: return error_dialog(self, _('Error while saving'), _('There was an error while saving.'), error, show=True) - if failures and single_format: - single_format = single_format.upper() + if failures: + failures = [u'%s\n\t%s'% + (title, '\n\t'.join(err.splitlines())) for title, err in + failures] + warning_dialog(self, _('Could not save some books'), _('Could not save some books') + ', ' + - (_('as the %s format is not available for them.')%single_format) + _('Click the show details button to see which ones.'), - '\n'.join(failures), show=True) + u'\n\n'.join(failures), show=True) QDesktopServices.openUrl(QUrl.fromLocalFile(path)) def books_saved(self, job): diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index 14896a28f4..81d293a1cb 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -498,10 +498,7 @@ def do_export(db, ids, dir, opts): prints('Failed to save the following books:') for id, title, tb in failures: prints(str(id)+':', title) - if tb: - prints('\t'+'\n\t'.join(tb.splitlines())) - else: - prints('\tRequested formats not available') + prints('\t'+'\n\t'.join(tb.splitlines())) prints(' ') def command_export(args, dbpath): diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index a274efe5ee..cafe8addc8 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -176,7 +176,8 @@ def save_book_to_disk(id, db, root, opts, length): traceback.print_exc() stream.seek(0) data = stream.read() - with open(base_path+'.'+fmt, 'wb') as f: + fmt_path = base_path+'.'+str(fmt) + with open(fmt_path, 'wb') as f: f.write(data) return not written, id, mi.title @@ -189,7 +190,9 @@ def save_to_disk(db, ids, root, opts=None, callback=None): :param:`ids` iterable of book ids to save from the database. :param:`callback` is an optional callable that is called on after each - book is processed with the arguments: id, title and failed + book is processed with the arguments: id, title, failed, traceback. + If the callback returns False, further processing is terminated and + the function returns. :return: A list of failures. Each element of the list is a tuple (id, title, traceback) ''' @@ -209,13 +212,14 @@ def save_to_disk(db, ids, root, opts=None, callback=None): tb = '' try: failed, id, title = save_book_to_disk(x, db, root, opts, length) + tb = _('Requested formats not available') except: failed, id, title = True, x, db.title(x, index_is_id=True) tb = traceback.format_exc() if failed: failures.append((id, title, tb)) if callable(callback): - if not callback(int(id), title, failed): + if not callback(int(id), title, failed, tb): break return failures diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 8b19b30307..8ec3ead55c 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: calibre 0.6.7\n" -"POT-Creation-Date: 2009-08-14 21:48+MDT\n" -"PO-Revision-Date: 2009-08-14 21:48+MDT\n" +"POT-Creation-Date: 2009-08-18 12:04+MDT\n" +"PO-Revision-Date: 2009-08-18 12:04+MDT\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -24,8 +24,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:52 #: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:58 #: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:199 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:701 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:705 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:698 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:702 #: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:403 #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:66 #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:68 @@ -37,7 +37,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:225 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:256 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:259 -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:350 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:359 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:23 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:45 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:46 @@ -60,8 +60,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:154 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:567 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:751 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:574 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:761 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:44 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:46 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:882 @@ -94,8 +94,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:29 #: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:139 #: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:141 -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:251 -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:258 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:253 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:260 #: /home/kovid/work/calibre/src/calibre/gui2/add.py:91 #: /home/kovid/work/calibre/src/calibre/gui2/add.py:98 #: /home/kovid/work/calibre/src/calibre/gui2/convert/__init__.py:19 @@ -126,10 +126,10 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/database2.py:1099 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1429 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1431 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1515 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1606 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1634 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1685 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1516 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1607 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1635 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1686 #: /home/kovid/work/calibre/src/calibre/library/server.py:309 #: /home/kovid/work/calibre/src/calibre/library/server.py:373 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45 @@ -486,43 +486,46 @@ msgstr "" msgid "Communicate with the Sony PRS-700 eBook reader." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:282 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:354 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:278 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:350 msgid "Unable to detect the %s disk drive. Try rebooting." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:422 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:527 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:418 msgid "Unable to detect the %s disk drive." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:515 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:511 msgid "Could not find mount helper: %s." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:534 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:523 +msgid "Unable to detect the %s disk drive. Your kernel is probably exporting a deprecated version of SYSFS." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:531 msgid "Unable to mount main memory (Error code: %d)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:639 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:641 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:636 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:638 msgid "The reader has no storage card in this slot." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:643 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:640 msgid "Selected slot: %s is not supported." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:667 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:664 msgid "There is insufficient free space in main memory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:669 -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:671 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:666 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:668 msgid "There is insufficient free space on the storage card" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:681 +#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:678 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:467 #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:83 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1006 @@ -669,27 +672,27 @@ msgstr "" msgid "Options to control the look and feel of the output" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:134 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:135 msgid "Control auto-detection of document structure." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:144 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:145 msgid "Control the automatic generation of a Table of Contents. By default, if the source file has a Table of Contents, it will be used in preference to the automatically generated one." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:154 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:155 msgid "Options to set metadata in the output" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:157 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:158 msgid "Options to help with debugging the conversion" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:181 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:182 msgid "List builtin recipes" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:250 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/cli.py:251 msgid "Output saved to" msgstr "" @@ -1226,7 +1229,7 @@ msgstr "" msgid "Comic" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:349 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:358 #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/info.py:45 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:69 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:70 @@ -1237,7 +1240,7 @@ msgstr "" msgid "Title" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:350 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:359 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:57 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:159 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:393 @@ -1245,18 +1248,18 @@ msgstr "" msgid "Author(s)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:351 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:360 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:59 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:164 msgid "Publisher" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:352 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:361 #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/info.py:49 msgid "Producer" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:353 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:362 #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:183 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:71 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info_ui.py:64 @@ -1265,7 +1268,7 @@ msgstr "" msgid "Comments" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:361 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:370 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:165 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:338 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:1010 @@ -1275,7 +1278,7 @@ msgstr "" msgid "Tags" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:363 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:372 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:166 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:354 #: /home/kovid/work/calibre/src/calibre/gui2/status.py:59 @@ -1283,22 +1286,22 @@ msgstr "" msgid "Series" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:364 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:373 msgid "Language" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:366 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:375 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:1009 msgid "Timestamp" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:368 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:377 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:61 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:162 msgid "Published" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:370 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:379 msgid "Rights" msgstr "" @@ -1817,7 +1820,7 @@ msgid "Specify the character encoding of the output document. The default is utf msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:28 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:507 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:502 msgid "Frequently used directories" msgstr "" @@ -1905,16 +1908,16 @@ msgstr "" msgid "Default action to perform when send to device button is clicked" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:113 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:115 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:397 msgid "Copied" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:142 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:144 msgid "Copy" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:142 +#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:144 msgid "Copy to Clipboard" msgstr "" @@ -1950,7 +1953,7 @@ msgstr "" msgid "Books with the same title as the following already exist in the database. Add them anyway?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/add.py:302 +#: /home/kovid/work/calibre/src/calibre/gui2/add.py:301 msgid "Saving..." msgstr "" @@ -2302,14 +2305,14 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/xpath_edit_ui.py:44 #: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:61 #: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:62 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:491 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:505 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:506 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:509 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:511 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:527 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:528 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:559 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:488 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:500 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:501 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:504 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:506 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:522 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:523 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:553 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:339 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:344 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:358 @@ -2870,7 +2873,7 @@ msgid "Attached, you will find the e-book" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/device.py:550 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:176 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:175 msgid "by" msgstr "" @@ -2970,7 +2973,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:88 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:89 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:92 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:215 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:214 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:344 #: /home/kovid/work/calibre/src/calibre/gui2/status.py:57 #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:83 @@ -3009,427 +3012,491 @@ msgstr "" msgid "&Profile:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:169 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:168 msgid "%(plugin_type)s %(plugins)s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:170 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:169 msgid "plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:195 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:194 msgid "Conversion" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:195 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:194 msgid "General" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:195 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:194 msgid "Interface" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:196 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:195 +msgid "Add/Save" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:195 msgid "" "Email\n" "Delivery" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:197 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:196 msgid "Advanced" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:197 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:196 msgid "" "Content\n" "Server" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:197 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:196 msgid "Plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:215 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:214 msgid "Auto send" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:215 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:214 msgid "Email" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:220 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:219 msgid "Formats to email. The first matching format will be sent." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:221 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:220 msgid "If checked, downloaded news will be automatically mailed
to this email address (provided it is in one of the listed formats)." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:295 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:294 msgid "new email address" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:511 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:505 msgid "No valid plugin path" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:512 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:506 msgid "%s is not a valid plugin path" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:515 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:509 msgid "Choose plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:527 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:521 msgid "Plugin cannot be disabled" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:528 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:522 msgid "The plugin: %s cannot be disabled" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:537 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:531 msgid "Plugin not customizable" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:538 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:532 msgid "Plugin: %s does not need customization" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:562 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:556 msgid "Customize %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:572 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:566 msgid "Cannot remove builtin plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:573 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:567 msgid " cannot be removed. It is a builtin plugin. Try disabling it instead." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:606 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:600 msgid "Error log:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:613 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:607 msgid "Access log:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:638 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:632 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:562 msgid "Failed to start content server" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:662 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:656 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:469 msgid "Select location for books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:679 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:673 msgid "Invalid size" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:680 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:674 msgid "The size %s is invalid. must be of the form widthxheight" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:725 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:730 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:718 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:723 msgid "Invalid database location" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:726 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:719 msgid "Invalid database location " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:727 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:720 msgid "
Must be a directory." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:731 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:724 msgid "Invalid database location.
Cannot write to " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:756 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:749 msgid "Checking..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:757 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:750 msgid "Checking database integrity. This may take a while." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:770 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:763 msgid "Some inconsistencies found" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config.py:771 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:764 msgid "The following books had formats listed in the database that are not actually available. The entries for the formats have been removed. You should check them manually. This can happen if you manipulate the files in the library folder directly." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:488 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save.py:70 +msgid "Invalid template" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save.py:71 +msgid "The template %s is invalid:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:94 +msgid "TabWidget" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:95 +msgid "Here you can control how calibre will read metadata from the files you add to it. calibre can either read metadata from the contents of the file, or from the filename." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:96 +msgid "Read metadata only from &file name" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:97 +msgid "&Configure metadata from file name" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:98 +msgid "&Adding books" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:99 +msgid "Here you can control how calibre will save your books when you click the Save to Disk button:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:100 +msgid "Save &cover separately" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:101 +msgid "Update &metadata in saved copies" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:102 +msgid "Save metadata in &OPF file" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:103 +msgid "Convert non-English characters to &English equivalents" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:104 +msgid "Format &dates as:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:105 +msgid "File &formats to save:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:106 +msgid "Save &template" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:107 +msgid "By adjusting the template below, you can control what folders the files are saved in and what filenames they are given. You can use the / character to indicate sub-folders. Available metadata variables are described below. If a particular book does not have some metadata, the variable will be replaced by the empty string." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:108 +msgid "Available variables:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/add_save_ui.py:109 +msgid "&Saving books" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:485 #: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:372 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:173 msgid "Preferences" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:489 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:486 msgid "&Location of ebooks (The ebooks are stored in folders sorted by author and metadata is stored in the file metadata.db)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:490 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:487 msgid "Browse for the new database location" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:492 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:489 msgid "Show notification when &new version is available" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:493 -msgid "If you disable this setting, metadata is guessed from the filename instead. This can be configured in the Advanced section." -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:494 -msgid "Read &metadata from files" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:495 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:490 msgid "Default network &timeout:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:496 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:491 msgid "Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:497 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:492 msgid " seconds" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:498 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:493 msgid "Choose &language (requires restart):" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:499 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:494 msgid "Normal" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:500 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:495 msgid "High" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:501 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:496 msgid "Low" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:502 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:497 msgid "Job &priority:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:503 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:498 msgid "Preferred &output format:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:504 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:499 msgid "Preferred &input format order:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:508 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:503 msgid "Add a directory to the frequently used directories list" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:510 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:505 msgid "Remove a directory from the frequently used directories list" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:512 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:507 msgid "Use &Roman numerals for series number" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:513 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:508 msgid "Enable system &tray icon (needs restart)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:514 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:509 msgid "Show ¬ifications in system tray" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:515 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:510 msgid "Show cover &browser in a separate window (needs restart)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:516 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:511 msgid "Search as you type" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:517 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:512 msgid "Automatically send downloaded &news to ebook reader" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:518 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:513 msgid "&Delete news from library when it is automatically sent to reader" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:519 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:514 msgid "&Number of covers to show in browse mode (needs restart):" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:520 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:515 msgid "Toolbar" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:521 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:516 msgid "Large" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:522 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:517 msgid "Medium" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:523 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:518 msgid "Small" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:524 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:519 msgid "&Button size in toolbar" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:525 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:520 msgid "Show &text in toolbar buttons" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:526 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:521 msgid "Select visible &columns in library view" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:529 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:524 msgid "Use internal &viewer for:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:530 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:525 msgid "Add an email address to which to send books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:531 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:526 msgid "&Add email" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:532 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:527 msgid "Make &default" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:533 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:528 msgid "&Remove email" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:534 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:529 msgid "calibre can send your books to you (or your reader) by email" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:535 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:530 msgid "Free unused diskspace from the database" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:536 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:531 msgid "&Check database integrity" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:537 -msgid "&Metadata from file name" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:538 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:532 msgid "calibre contains a network server that allows you to access your book collection using a browser from anywhere in the world. Any changes to the settings will only take effect after a server restart." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:539 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:533 msgid "Server &port:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:540 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:534 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:58 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:178 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:117 msgid "&Username:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:541 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:535 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:59 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:179 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:119 msgid "&Password:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:542 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:536 msgid "If you leave the password blank, anyone will be able to access your book collection using the web interface." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:543 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:537 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:60 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:180 msgid "&Show password" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:544 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:538 msgid "The maximum size (widthxheight) for displayed covers. Larger covers are resized. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:545 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:539 msgid "Max. &cover size:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:546 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:540 msgid "&Start Server" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:547 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:541 msgid "St&op Server" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:548 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:542 msgid "&Test Server" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:549 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:543 msgid "Run server &automatically on startup" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:550 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:544 msgid "View &server logs" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:551 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:545 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/stanza_ui.py:46 msgid "" "

Remember to leave calibre running as the server only runs as long as calibre is running.\n" "

Stanza should see your calibre collection automatically. If not, try adding the URL http://myhostname:8080 as a new catalog in the Stanza reader on your iPhone. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:553 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:547 msgid "Here you can customize the behavior of Calibre by controlling what plugins it uses." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:554 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:548 msgid "Enable/&Disable plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:555 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:549 msgid "&Customize plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:556 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:550 msgid "&Remove plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:557 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:551 msgid "Add new plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:558 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:552 msgid "Plugin &file:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:560 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:554 msgid "&Add" msgstr "" @@ -4473,7 +4540,7 @@ msgid "Save to disk in a single directory" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/main.py:278 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1466 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1470 msgid "Save only %s format to disk" msgstr "" @@ -4508,7 +4575,7 @@ msgid "Bad database location" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/main.py:426 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1594 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1598 msgid "Choose a location for your ebook library." msgstr "" @@ -4612,7 +4679,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1004 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1034 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1059 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1190 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1194 msgid "No books selected" msgstr "" @@ -4653,191 +4720,187 @@ msgstr "" msgid "Choose destination directory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1084 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1086 msgid "Error while saving" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1085 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1087 msgid "There was an error while saving." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1089 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1090 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1094 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1095 msgid "Could not save some books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1091 -msgid "as the %s format is not available for them." -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1092 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1096 msgid "Click the show details button to see which ones." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1111 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1115 msgid "Fetching news from " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1124 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1128 msgid " fetched." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1189 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1193 msgid "Cannot convert" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1359 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1378 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1363 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1382 msgid "No book selected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1359 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1411 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1363 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1415 msgid "Cannot view" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1365 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1369 msgid "Choose the format to view" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1377 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1381 msgid "Cannot open folder" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1394 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1398 msgid "Multiple Books Selected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1395 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1399 msgid "You are attempting to open %d books. Opening too many books at once can be slow and have a negative effect on the responsiveness of your computer. Once started the process cannot be stopped until complete. Do you wish to continue?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1412 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1416 msgid "%s has no available formats." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1450 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1454 msgid "Cannot configure" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1451 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1455 msgid "Cannot configure while there are running jobs." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1495 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1499 msgid "No detailed info available" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1496 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1500 msgid "No detailed information is available for books on the device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1544 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1548 msgid "Error talking to device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1545 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1549 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/main.py:1562 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1577 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1566 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1581 msgid "Conversion Error" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1563 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1567 msgid "

Could not convert: %s

It is a DRMed book. You must first remove the DRM using 3rd party tools." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1578 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1582 msgid "Failed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1603 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1607 msgid "Invalid library location" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1604 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1608 msgid "Could not access %s. Using %s as the library." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1652 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1656 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." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1676 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1680 msgid "There are active jobs. Are you sure you want to quit?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1679 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1683 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/main.py:1683 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1687 msgid "WARNING: Active jobs" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1734 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1738 msgid "will keep running in the system tray. To close it, choose Quit in the context menu of the system tray." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1753 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1757 msgid "Latest version: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1761 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1765 msgid "Update available" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1762 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1766 msgid "%s has been updated to version %s. See the new features. Visit the download page?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1780 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1784 msgid "Use the library located at the specified path." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1782 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1786 msgid "Start minimized to system tray." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1784 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1788 msgid "Log debugging information to console" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1832 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1836 msgid "If you are sure it is not running" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1834 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1838 msgid "Cannot Start " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1835 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1839 msgid "%s is already running." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1838 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1842 msgid "may be running in the system tray, in the" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1840 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1844 msgid "upper right region of the screen." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1842 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1846 msgid "lower right region of the screen." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1845 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1849 msgid "try rebooting your computer." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1847 -#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1867 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1851 +#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1871 msgid "try deleting the file" msgstr "" @@ -5857,7 +5920,7 @@ msgstr "" msgid "You must specify an id and a metadata file" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:497 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:505 msgid "" "%prog export [options] ids\n" "\n" @@ -5866,27 +5929,27 @@ msgid "" "an opf file). You can get id numbers from the list command.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:505 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:513 msgid "Export all books in database, ignoring the list of ids." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:507 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:515 msgid "Export books to the specified directory. Default is" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:509 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:517 msgid "Export all books into a single directory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:511 -msgid "Create file names as author - title instead of title - author" +#: /home/kovid/work/calibre/src/calibre/library/cli.py:524 +msgid "Specifying this switch will turn this behavior off." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:516 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:537 msgid "You must specify some ids or the %s option" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:526 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:547 msgid "" "%%prog command [options] [arguments]\n" "\n" @@ -5898,18 +5961,102 @@ msgid "" "For help on an individual command: %%prog command --help\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1541 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1542 msgid "

Migrating old database to ebook library in %s

" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1570 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1571 msgid "Copying %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1587 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1588 msgid "Compacting database" msgstr "" +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:22 +msgid "The title" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:23 +msgid "The authors" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:24 +msgid "The author sort string" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:25 +msgid "The tags" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:26 +msgid "The series" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:27 +msgid "The series number" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:28 +msgid "The rating" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:29 +msgid "The ISBN" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:30 +msgid "The publisher" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:31 +msgid "The date" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:32 +msgid "The published date" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:33 +msgid "The calibre internal id" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:43 +msgid "Options to control saving to disk" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:49 +msgid "Normally, calibre will update the metadata in the saved files from what is in the calibre library. Makes saving to disk slower." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:52 +msgid "Normally, calibre will write the metadata into a separate OPF file along with the actual e-book files." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:55 +msgid "Normally, calibre will save the cover in a separate file along with the actual e-book file(s)." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:58 +msgid "Comma separated list of formats to save for each book. By default all available books are saved." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:61 +msgid "The template to control the filename and directory structure of the saved files. Default is \"%s\" which will save books into a per-author subdirectory with filenames containing title and author. Available controls are: {%s}" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:66 +msgid "Normally, calibre will convert all non English characters into English equivalents for the file names. WARNING: If you turn this off, you may experience errors when saving, depending on how well the filesystem you are saving to supports unicode." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:72 +msgid "The format in which to display dates. %d - day, %b - month, %Y - year. Default is: %b, %Y" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:214 +msgid "Requested formats not available" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/library/server.py:159 msgid "Password to access your calibre library. Username is " msgstr "" @@ -5934,43 +6081,43 @@ msgstr "" msgid "Whenever you pass arguments to %prog that have spaces in them, enclose the arguments in quotation marks." msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:540 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:550 msgid "Path to the database in which books are stored" msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:542 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:552 msgid "Pattern to guess metadata from filenames" msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:544 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:554 msgid "Access key for isbndb.com" msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:546 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:556 msgid "Default timeout for network operations (seconds)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:548 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:558 msgid "Path to directory in which your library of books is stored" msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:550 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:560 msgid "The language in which to display the user interface" msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:552 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:562 msgid "The default output format for ebook conversions." msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:556 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:566 msgid "Ordered list of formats to prefer for input." msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:558 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:568 msgid "Read metadata from files" msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/config.py:560 +#: /home/kovid/work/calibre/src/calibre/utils/config.py:570 msgid "The priority of worker processes" msgstr "" @@ -6040,77 +6187,77 @@ msgstr "" msgid "Unknown News Source" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:578 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:587 msgid "Download finished" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:580 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:589 msgid "Failed to download the following articles:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:586 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:595 msgid "Failed to download parts of the following articles:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:588 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:597 msgid " from " msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:590 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:599 msgid "\tFailed links:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:671 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:680 msgid "Could not fetch article. Run with --debug to see the reason" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:692 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:701 msgid "Fetching feeds..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:696 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:705 msgid "Got feeds from index page" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:702 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:711 msgid "Trying to download cover..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:753 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:762 msgid "Starting download [%d thread(s)]..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:769 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:778 msgid "Feeds downloaded to %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:779 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:788 msgid "Could not download cover: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:786 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:795 msgid "Downloading cover from %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:907 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:916 msgid "Untitled Article" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:978 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:987 msgid "Article downloaded: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:989 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:998 msgid "Article download failed: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1004 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:78 +#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1013 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:80 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:76 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_instapaper.py:59 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_lamujerdemivida.py:59 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_laprensa_ni.py:63 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:85 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:83 msgid "Fetching feed" msgstr "" @@ -6124,25 +6271,23 @@ msgid "Croatian" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_24sata_rs.py:26 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_b92.py:23 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_24sata_rs.py:25 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_b92.py:22 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_blic.py:25 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:19 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_danas.py:22 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_e_novine.py:26 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_e_novine.py:25 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_glas_srpske.py:27 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_krstarica.py:23 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_nin.py:31 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_nin.py:30 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_novosti.py:24 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_nspm.py:26 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pescanik.py:25 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:26 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_politika.py:23 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_nspm.py:25 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pescanik.py:24 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_politika.py:24 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pressonline.py:25 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_rts.py:23 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_tanjug.py:22 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vijesti.py:28 -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vreme.py:28 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_tanjug.py:23 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vreme.py:27 msgid "Serbian" msgstr "" @@ -6151,6 +6296,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_buenosaireseconomico.py:23 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_clarin.py:26 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_criticadigital.py:17 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_cubadebate.py:17 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_diagonales.py:23 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_el_mercurio_chile.py:61 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_el_pais.py:14 @@ -6320,6 +6466,10 @@ msgstr "" msgid "Vietnamese" msgstr "" +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_borba.py:20 +msgid "sr-Latn-RS" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_carta.py:24 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_der_standard.py:22 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_diepresse.py:23 @@ -6365,7 +6515,7 @@ msgid "Dutch" msgstr "" #: -#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_dnevni_avaz.py:27 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_dnevni_avaz.py:28 msgid "Bosnian" msgstr "" @@ -6400,6 +6550,11 @@ msgstr "" msgid "Skipping filtered article: %s" msgstr "" +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_pobjeda.py:24 +#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_vijesti.py:25 +msgid "Montenegrin" +msgstr "" + #: #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/recipe_usatoday.py:18 msgid "Kovid Goyal and Sujata Raman" diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py index f4befc6052..d343043c97 100644 --- a/src/calibre/utils/config.py +++ b/src/calibre/utils/config.py @@ -379,6 +379,7 @@ class ConfigInterface(object): self.remove_opt = self.remove = self.option_set.remove_opt self.parse_string = self.option_set.parse_string self.get_option = self.option_set.get_option + self.preferences = self.option_set.preferences def update(self, other): self.option_set.update(other.option_set) diff --git a/src/calibre/web/feeds/recipes/recipe_guardian.py b/src/calibre/web/feeds/recipes/recipe_guardian.py index 02be26cffd..f8543c7d59 100644 --- a/src/calibre/web/feeds/recipes/recipe_guardian.py +++ b/src/calibre/web/feeds/recipes/recipe_guardian.py @@ -8,6 +8,7 @@ www.guardian.co.uk ''' from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag class Guardian(BasicNewsRecipe): @@ -16,14 +17,33 @@ class Guardian(BasicNewsRecipe): language = _('English') oldest_article = 7 max_articles_per_feed = 20 - + remove_javascript = True + timefmt = ' [%a, %d %b %Y]' - - remove_tags_before = dict(id='main-article-info') - remove_tags_after = dict(id='article-wrapper') - remove_tags_after = dict(id='content') + keep_only_tags = [ + dict(name='div', attrs={'id':["content","article_header","main-article-info",]}), + ] + remove_tags = [ + dict(name='div', attrs={'class':["video-content","videos-third-column"]}), + dict(name='div', attrs={'id':["article-toolbox","subscribe-feeds",]}), + dict(name='ul', attrs={'class':["pagination"]}), + dict(name='ul', attrs={'id':["content-actions"]}), + ] + use_embedded_content = False + no_stylesheets = True - extra_css = 'h2 {font-size: medium;} \n h1 {text-align: left;}' + extra_css = ''' + .article-attributes{font-size: x-small; font-family:Arial,Helvetica,sans-serif;} + .h1{font-size: large ;font-family:georgia,serif; font-weight:bold;} + .stand-first-alone{color:#666666; font-size:small; font-family:Arial,Helvetica,sans-serif;} + .caption{color:#666666; font-size:x-small; font-family:Arial,Helvetica,sans-serif;} + #article-wrapper{font-size:small; font-family:Arial,Helvetica,sans-serif;} + .main-article-info{font-family:Arial,Helvetica,sans-serif;} + #full-contents{font-size:small; font-family:Arial,Helvetica,sans-serif;} + #match-stats-summary{font-size:small; font-family:Arial,Helvetica,sans-serif;} + ''' + + feeds = [ ('Front Page', 'http://www.guardian.co.uk/rss'), @@ -37,3 +57,21 @@ class Guardian(BasicNewsRecipe): ('Comment','http://www.guardian.co.uk/commentisfree/rss'), ] + + def preprocess_html(self, soup): + + for item in soup.findAll(style=True): + del item['style'] + + for item in soup.findAll(face=True): + del item['face'] + for tag in soup.findAll(name=['ul','li']): + tag.name = 'div' + + return soup + + + + + + From 059ad61377a35fdb796b5e13ef5e74d17660ee01 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 18 Aug 2009 16:39:34 -0600 Subject: [PATCH 7/7] Make calibre less likely to loose track of e-book files if the user renames them outside of calibre --- src/calibre/library/database2.py | 35 +++++++++++++++----------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 283de1c2f7..3ab4f97f83 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -850,20 +850,14 @@ class LibraryDatabase2(LibraryDatabase): return None ans = [] for format in formats: - _format = ('.' + format.lower()) if format else '' - if os.access(os.path.join(path, name+_format), os.R_OK|os.W_OK): + if self.format_abspath(id, format, index_is_id=True) is not None: ans.append(format) + if not ans: + return None return ','.join(ans) def has_format(self, index, format, index_is_id=False): - id = index if index_is_id else self.id(index) - name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all=False) - if name: - path = os.path.join(self.library_path, self.path(id, index_is_id=True)) - format = ('.' + format.lower()) if format else '' - path = os.path.join(path, name+format) - return os.access(path, os.R_OK|os.W_OK) - return False + return self.format_abspath(index, format, index_is_id) is not None def format_abspath(self, index, format, index_is_id=False): 'Return absolute path to the ebook file of format `format`' @@ -872,9 +866,13 @@ class LibraryDatabase2(LibraryDatabase): if name: path = os.path.join(self.library_path, self.path(id, index_is_id=True)) format = ('.' + format.lower()) if format else '' - path = os.path.join(path, name+format) - if os.access(path, os.R_OK|os.W_OK): - return path + fmt_path = os.path.join(path, name+format) + if os.path.exists(fmt_path): + return fmt_path + candidates = glob.glob(os.path.join(path, '*'+format)) + if format and candidates and os.path.exists(candidates[0]): + shutil.copyfile(candidates[0], fmt_path) + return fmt_path def format(self, index, format, index_is_id=False, as_file=False, mode='r+b'): ''' @@ -886,9 +884,10 @@ class LibraryDatabase2(LibraryDatabase): path = self.format_abspath(index, format, index_is_id=index_is_id) if path is not None: f = open(path, mode) - return f if as_file else f.read() - if self.has_format(index, format, index_is_id): - self.remove_format(id, format, index_is_id=True) + ret = f if as_file else f.read() + if not as_file: + f.close() + return ret def add_format_with_hooks(self, index, format, fpath, index_is_id=False, path=None, notify=True): @@ -944,11 +943,9 @@ class LibraryDatabase2(LibraryDatabase): def remove_format(self, index, format, index_is_id=False, notify=True): id = index if index_is_id else self.id(index) - path = os.path.join(self.library_path, *self.path(id, index_is_id=True).split(os.sep)) name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all=False) if name: - ext = ('.' + format.lower()) if format else '' - path = os.path.join(path, name+ext) + path = self.format_abspath(id, format, index_is_id=True) try: delete_file(path) except: