Kovid's intermediate fix for periodical Next Article

This commit is contained in:
GRiker 2009-10-23 10:40:40 -06:00
commit 376869fd7b
59 changed files with 7555 additions and 6533 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

View File

@ -21,9 +21,12 @@ class TheAtlantic(BasicNewsRecipe):
dict(name='div', id=['seealso','storybottom', 'footer', 'ad_banner_top', 'sidebar','articletoolstop','subcontent',]), dict(name='div', id=['seealso','storybottom', 'footer', 'ad_banner_top', 'sidebar','articletoolstop','subcontent',]),
dict(name='p', attrs={'id':["pagination"]}), dict(name='p', attrs={'id':["pagination"]}),
dict(name='table',attrs={'class':"tools"}), dict(name='table',attrs={'class':"tools"}),
dict(name='style'),
dict(name='a', href='/a/newsletters.mhtml') dict(name='a', href='/a/newsletters.mhtml')
] ]
remove_attributes = ['icap', 'callout', 'style']
no_stylesheets = True no_stylesheets = True
conversion_options = { 'linearize_tables':True }
extra_css = ''' extra_css = '''
#timestamp{font-family:Arial,Helvetica,sans-serif; color:#666666 ;font-size:x-small} #timestamp{font-family:Arial,Helvetica,sans-serif; color:#666666 ;font-size:x-small}
@ -50,10 +53,14 @@ class TheAtlantic(BasicNewsRecipe):
for item in soup.findAll('div', attrs={'class':'item'}): for item in soup.findAll('div', attrs={'class':'item'}):
a = item.find('a') a = item.find('a')
if a and a.has_key('href'): if a and a.has_key('href'):
url = a['href']#.replace('/doc', 'doc/print') url = a['href']
if not url.startswith('http://'): if not url.startswith('http://'):
url = 'http://www.theatlantic.com/'+url url = 'http://www.theatlantic.com/'+url
url = url.replace('/doc/', '/doc/print/')
title = self.tag_to_string(a) title = self.tag_to_string(a)
if title in ('VIDEO', 'AUDIO', 'INTERACTIVE MAP', 'SIDEBAR', 'RECIPES'):
continue
title = title.replace('&', '&')
byline = item.find(attrs={'class':'byline'}) byline = item.find(attrs={'class':'byline'})
date = self.tag_to_string(byline) if byline else '' date = self.tag_to_string(byline) if byline else ''
description = '' description = ''

View File

@ -0,0 +1,38 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1255797795(BasicNewsRecipe):
title = u'Corren'
__author__ = 'Jonas Svensson'
simultaneous_downloads = 1
no_stylesheets = True
oldest_article = 7
max_articles_per_feed = 100
remove_attributes = ['onload']
timefmt = ''
feeds = [
(u'Toppnyheter (alla kategorier)', u'http://www.corren.se/inc/RssHandler.ashx?id=4122151&ripurl=http://www.corren.se/nyheter/'),
(u'Bostad', u'http://www.corren.se/inc/RssHandler.ashx?id=4122174&ripurl=http://www.corren.se/bostad/'),
(u'Ekonomi & Jobb', u'http://www.corren.se/inc/RssHandler.ashx?id=4122176&ripurl=http://www.corren.se/ekonomi/'),
(u'Kultur & Nöje', u'http://www.corren.se/inc/RssHandler.ashx?id=4122192&ripurl=http://www.corren.se/kultur/'),
(u'Mat & dryck', u'http://www.corren.se/inc/RssHandler.ashx?id=4122201&ripurl=http://www.corren.se/mat-dryck/'),
(u'Motor', u'http://www.corren.se/inc/RssHandler.ashx?id=4122203&ripurl=http://www.corren.se/motor/'),
(u'Sport', u'http://www.corren.se/inc/RssHandler.ashx?id=4122206&ripurl=http://www.corren.se/sport/'),
(u'Åsikter', u'http://www.corren.se/inc/RssHandler.ashx?id=4122223&ripurl=http://www.corren.se/asikter/'),
(u'Mjölby', u'http://www.corren.se/inc/RssHandler.ashx?id=4122235&ripurl=http://www.corren.se/ostergotland/mjolby/'),
(u'Motala', u'http://www.corren.se/inc/RssHandler.ashx?id=4122236&ripurl=http://www.corren.se/ostergotland/motala/')
]
def print_version(self, url):
url = url.replace("ekonomi/artikel.aspx", "Print.aspx")
url = url.replace("bostad/artikel.aspx", "Print.aspx")
url = url.replace("kultur/artikel.aspx", "Print.aspx")
url = url.replace("motor/artikel.aspx", "Print.aspx")
url = url.replace("mat-dryck/artikel.aspx", "Print.aspx")
url = url.replace("sport/artikel.aspx", "Print.aspx")
url = url.replace("asikter/artikel.aspx", "Print.aspx")
url = url.replace("mat-dryck/artikel.aspx", "Print.aspx")
url = url.replace("ostergotland/mjolby/artikel.aspx", "Print.aspx")
url = url.replace("ostergotland/motala/artikel.aspx", "Print.aspx")
return url.replace("nyheter/artikel.aspx", "Print.aspx")

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
dn.se
'''
from calibre.web.feeds.news import BasicNewsRecipe
class DN_se(BasicNewsRecipe):
title = 'Dagens Nyheter'
__author__ = 'Darko Miletic'
description = 'News from Sveden'
publisher = 'Dagens Nyheter'
category = 'news, politics, Sveden'
oldest_article = 2
delay = 1
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
language = 'sv'
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
feeds = [
(u'Nyheter', u'http://www.dn.se/m/rss/toppnyheter')
,(u'Ekonomi', u'http://www.dn.se/ekonomi-rss' )
,(u'Sport' , u'http://www.dn.se/sport-rss' )
,(u'Debatt' , u'http://www.dn.se/debatt-rss' )
,(u'Ledare' , u'http://www.dn.se/ledare-rss' )
,(u'Kultur' , u'http://www.dn.se/kultur-rss' )
]
keep_only_tags = [dict(name='div', attrs={'id':'article'})]
remove_tags_before = dict(name='h1')
remove_tags_after = dict(name='div',attrs={'id':'byline'})
remove_tags = [
dict(name=['object','link','base'])
,dict(name='div',attrs={'id':'hook'})
]

View File

@ -18,7 +18,6 @@ class Economist(BasicNewsRecipe):
__author__ = "Kovid Goyal" __author__ = "Kovid Goyal"
description = 'Global news and current affairs from a European perspective' description = 'Global news and current affairs from a European perspective'
oldest_article = 7.0 oldest_article = 7.0
needs_subscription = False # Strange but true
INDEX = 'http://www.economist.com/printedition' INDEX = 'http://www.economist.com/printedition'
cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg' cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg'
remove_tags = [dict(name=['script', 'noscript', 'title'])] remove_tags = [dict(name=['script', 'noscript', 'title'])]

View File

@ -10,7 +10,7 @@ from calibre.ebooks.BeautifulSoup import Tag
class HLN_be(BasicNewsRecipe): class HLN_be(BasicNewsRecipe):
title = 'Het Belang Van Limburg' title = 'Het Belang Van Limburg'
__author__ = 'Darko Miletic' __author__ = 'Darko Miletic and Sujata Raman'
description = 'News from Belgium in Dutch' description = 'News from Belgium in Dutch'
publisher = 'Het Belang Van Limburg' publisher = 'Het Belang Van Limburg'
category = 'news, politics, Belgium' category = 'news, politics, Belgium'
@ -32,6 +32,14 @@ class HLN_be(BasicNewsRecipe):
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0cm; margin-top: 0em; margin-bottom: 0.5em} "' html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0cm; margin-top: 0em; margin-bottom: 0.5em} "'
extra_css = '''
body{font-family:"Verdana",sans-serif; font-size:80%;}
.h1{font-family:"Verdana",sans-serif; font-size:large; font-weight:bold;}
.captionEmbeddedMasterObject{font-style:italic; font-size:80%;}
.gen_footnote3{font-size:80%; color: #666666;}
'''
keep_only_tags = [dict(name='div', attrs={'class':'art_box2'})] keep_only_tags = [dict(name='div', attrs={'class':'art_box2'})]
remove_tags = [ remove_tags = [
dict(name=['embed','object']) dict(name=['embed','object'])

View File

@ -5,13 +5,12 @@ __copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
''' '''
laprensa.com.ar laprensa.com.ar
''' '''
import urllib
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class LaPrensa(BasicNewsRecipe): class LaPrensa(BasicNewsRecipe):
title = 'La Prensa' title = 'La Prensa'
__author__ = 'Darko Miletic' __author__ = 'Darko Miletic and Sujata Raman'
description = 'Informacion Libre las 24 horas' description = 'Informacion Libre las 24 horas'
publisher = 'La Prensa' publisher = 'La Prensa'
category = 'news, politics, Argentina' category = 'news, politics, Argentina'
@ -20,8 +19,10 @@ class LaPrensa(BasicNewsRecipe):
no_stylesheets = True no_stylesheets = True
use_embedded_content = False use_embedded_content = False
encoding = 'cp1252' encoding = 'cp1252'
cover_url = 'http://www.laprensa.com.ar/imgs/logo.gif' # cover_url = 'http://www.laprensa.com.ar/imgs/logo.gif'
remove_javascript = True remove_javascript = True
language = 'es'
lang = 'es'
html2lrf_options = [ html2lrf_options = [
'--comment', description '--comment', description
@ -30,31 +31,72 @@ class LaPrensa(BasicNewsRecipe):
] ]
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
filter_regexps = [r'.*archive.aspx.*']
feeds = [ remove_tags = [
(u'Politica' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=4' ) dict(name='td', attrs={'class':["link-registro","link-buscador"]}),
,(u'Economia' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=5' ) dict(name='td', attrs={'id':["TDTabItem1","TDTabItem2","TDTabItem3","TDTabItem4"]}),
,(u'Opinion' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=6' ) dict(name='table', attrs={'class':["marco-botonera"]}),
,(u'El Mundo' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=7' ) dict(name='tr', attrs={'class':["messages","IUTabItemSelected"]}),
,(u'Actualidad' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=8' ) dict(name='input', attrs={'id':"txt_allfields"}),
,(u'Deportes' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=9' ) dict(name='div', attrs={'id':["TabItem1","TabItem2","TabItem3","TabItem4","RCPanel"]}),
,(u'Espectaculos', u'http://www.laprensa.com.ar/Rss.aspx?Rss=10') dict(name='span', attrs={'id':["GWCNavigatorControl","_ctl15"]}),
dict(name='span', attrs={'class':["ranking-titulo","IUTab"]}),
dict(name='a', attrs={'class':["link-registro",]}),
dict(name='img', src = "/versions/1/imgs/icono-comentario.gif"),
dict(name='img', src = "/versions/1/imgs/logo.gif"),
dict(name='img', src = "/versions/1/imgs/boton-ingresar-roll.gif"),
dict(name='img', src = "/versions/1/imgs/icono-recomendar.gif"),
dict(name='button'),
dict(name='img', src = "/versions/1/imgs/boton-votar-roll.gif"),
dict(name='img', src = "/versions/1/imgs/boton-ingresar.gif"),
dict(name='img', src = "/versions/1/imgs/icono-imprimir.gif"),
dict(name='img', src = "/versions/1/imgs/icono-ampliar-letra.gif"),
dict(name='img', src = "/versions/1/imgs/icono-reducir-letra.gif"),
dict(name='img', src = "/versions/1/imgs/pix-trans.gif"),
dict(name='img', src = "/versions/1/imgs/icono-buscador.gif"),
dict(name='img', src = "/versions/1/imgs/separador-linea-azul.gif"),
dict(name='img', src = " /versions/1/imgs/separador-linea.gif"),
dict(name='a',text ="Powered by Civinext Groupware - V. 2.0.3567.23706"),
dict(name='img', height ="0")
] ]
def print_version(self, url): extra_css = '''
return url.replace('.note.aspx','.NotePrint.note.aspx') .seccion{font-size:xx-small;}
body{font-family:Arial,Helvetica,sans-serif;font-size:x-small;}
.titulo-noticia-principal{font-size:large; color:#00427B; font-weight:bold;}
.texto-subtitulos{font-weight:bold;font-size:x-small;}
.fecha{font-size:xx-small;}
.volanta{font-size:xx-small;}
'''
feeds = [
(u'Politica' , u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx&Rss=4' )
,(u'Economia' , u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx&Rss=5' )
,(u'Opinion' , u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx?Rss=6' )
,(u'El Mundo' , u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx?Rss=7' )
,(u'Actualidad' , u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx?Rss=8' )
,(u'Deportes' , u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx?Rss=9' )
,(u'Espectaculos', u'http://www.laprensa.com.ar/ResourcesManager.aspx?Resource=Rss.aspx?Rss=10')
]
def get_article_url(self, article):
raw = article.get('link', None).encode('utf8')
final = urllib.quote(raw,':/')
return final
def preprocess_html(self, soup): def preprocess_html(self, soup):
del soup.body['onload']
for t in soup.findAll(['table','td','tr','span','tbody']):
t.name = 'div'
for t in soup.findAll(['hr']):
t.extract()
mtag = '<meta http-equiv="Content-Language" content="es-AR"/>' mtag = '<meta http-equiv="Content-Language" content="es-AR"/>'
soup.head.insert(0,mtag) soup.head.insert(0,mtag)
for item in soup.findAll(style=True): for item in soup.findAll(style=True):
del item['style'] del item['style']
for item in soup.findAll(align = "center"):
del item['align']
for item in soup.findAll(bgcolor="ffffff"):
del item['bgcolor']
return soup return soup
language = 'es'

View File

@ -0,0 +1,53 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
svd.se
'''
from calibre.web.feeds.news import BasicNewsRecipe
class SVD_se(BasicNewsRecipe):
title = 'Svenska Dagbladet'
__author__ = 'Darko Miletic'
description = 'News from Sveden'
publisher = 'Svenska Dagbladet'
category = 'news, politics, Sveden'
oldest_article = 2
delay = 1
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
language = 'sv'
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
feeds = [
(u'Toppnyheter' , u'http://www.svd.se/?service=rss' )
,(u'Inrikes' , u'http://www.svd.se/nyheter/inrikes/?service=rss' )
,(u'Utrikes' , u'http://www.svd.se/nyheter/utrikes/?service=rss' )
,(u'Politik' , u'http://www.svd.se/nyheter/politik/?service=rss' )
,(u'Idagsidan ' , u'http://www.svd.se/nyheter/idagsidan/?service=rss' )
,(u'Vetenskap' , u'http://www.svd.se/nyheter/vetenskap/?service=rss' )
,(u'Sport' , u'http://www.svd.se/sportspel/nyheter/?service=rss' )
,(u'Opinion' , u'http://www.svd.se/opinion/startsidan/?service=rss')
,(u'Kultur' , u'http://www.svd.se/kulturnoje/nyheter/?service=rss')
]
keep_only_tags = [dict(name='div', attrs={'id':'articlecontent'})]
remove_tags_after = dict(name='div',attrs={'class':'articlebody normal'})
remove_tags = [
dict(name=['object','link','base'])
,dict(name='div',attrs={'class':['articlead','factcolumn']})
,dict(name='ul', attrs={'class':'toolbar articletop clearfix'})
,dict(name='p', attrs={'class':'more'})
]

View File

@ -17,17 +17,35 @@ class Time(BasicNewsRecipe):
no_stylesheets = True no_stylesheets = True
language = 'en' language = 'en'
extra_css = '''.headline {font-size: large;} extra_css = ''' h1 {font-family:Arial,Sans-serif;}
.fact { padding-top: 10pt } h2 {font-family:Arial,Sans-serif;}
h1 {font-family:Arial,Sans-serif} .name{font-family:Arial,Sans-serif; font-size:x-small; }
.byline{font-family:Arial,Sans-serif; font-size:xx-small ;color:blue} .date{font-family:Arial,Sans-serif; font-size:x-small ;color:#999999;}
.timestamp{font-family:Arial,Sans-serif; font-size:x-small ;color:gray}''' .byline{font-family:Arial,Sans-serif; font-size:x-small ;}
remove_tags_before = dict(id="artHd") .photoBkt{ font-size:x-small ;}
remove_tags_after = {'class':"ltCol"} .vertPhoto{font-size:x-small ;}
remove_tags = [ .credits{font-family:Arial,Sans-serif; font-size:x-small ;color:gray;}
{'class':['articleTools', 'enlarge', 'search','socialtools','blogtools','moretools','page','nextUp','next','subnav','RSS','line2','first','ybuzz','articlePagination','chiclets','imgcont','createListLink','rlinks','tabsWrap','pagination']}, .artTxt{font-family:georgia,serif;}
{'id':['quigoArticle', 'contentTools', 'articleSideBar', 'header', 'navTop','articleTools','feedmodule','feedmodule3','promos','footer','linksFooter','timeArchive','belt','relatedStories','packages','Features']}, #article{font-family:georgia,serif;}
{'target':'_blank'}, .caption{font-family:georgia,serif; font-size:x-small;color:#333333;}
.credit{font-family:georgia,serif; font-size:x-small;color:#999999;}
a:link{color:#CC0000;}
'''
# remove_tags_before = dict(id="artHd")
# remove_tags_after = {'class':"ltCol"}
# remove_tags = [
# {'class':['articleTools', 'enlarge', 'search','socialtools','blogtools','moretools','page','nextUp','next','subnav','RSS','line2','first','ybuzz','articlePagination','chiclets','imgcont','createListLink','rlinks','tabsWrap','pagination']},
# {'id':['quigoArticle', 'contentTools', 'articleSideBar', 'header', 'navTop','articleTools','feedmodule','feedmodule3','promos','footer','linksFooter','timeArchive','belt','relatedStories','packages','Features']},
# {'target':'_blank'},
# ]
keep_only_tags = [ dict(name ="div",attrs = {"id" :["article",]}) ,
dict(name ="div",attrs = {"class" :["artHd","artTxt","photoBkt","vertPhoto","image","copy"]}) ,]
remove_tags = [ dict(name ="div",attrs = {'class':['articlePagination','nextUp',"rtCol","pagination","enlarge",]}),
dict(name ="span",attrs = {'class':['see']}),
dict(name ="div",attrs = {'id':['articleSideBar',"articleTools","articleFooter","cmBotLt","quigoPackage"]}),
dict(name ="a",attrs = {'class':['listLink']}),
] ]
recursions = 1 recursions = 1
match_regexps = [r'/[0-9,]+-(2|3|4|5|6|7|8|9)(,\d+){0,1}.html'] match_regexps = [r'/[0-9,]+-(2|3|4|5|6|7|8|9)(,\d+){0,1}.html']
@ -81,20 +99,3 @@ class Time(BasicNewsRecipe):
else: else:
ans.append(unicode(t)) ans.append(unicode(t))
return u' '.join(ans).replace(u'\xa0', u'').strip() return u' '.join(ans).replace(u'\xa0', u'').strip()
def postprocess_html(self, soup, first_page):
div = soup.find(attrs={'class':'artPag'})
if div is not None:
div.extract()
if not first_page:
for cls in ('photoBkt', 'artHd'):
div = soup.find(attrs={'class':cls})
if div is not None:
div.extract()
div = soup.find(attrs={'class':'artTxt'})
if div is not None:
p = div.find('p')
if p is not None:
p.extract()
return soup

View File

@ -15,7 +15,7 @@ class ZeitDe(BasicNewsRecipe):
language = 'de' language = 'de'
lang = 'de_DE' lang = 'de_DE'
__author__ = 'Martin Pitt and Suajta Raman' __author__ = 'Martin Pitt and Sujata Raman'
use_embedded_content = False use_embedded_content = False
max_articles_per_feed = 40 max_articles_per_feed = 40
remove_empty_feeds = True remove_empty_feeds = True
@ -41,7 +41,8 @@ class ZeitDe(BasicNewsRecipe):
.article{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small} .article{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small}
.headline iconportrait_inline{font-family:Arial,Helvetica,sans-serif;font-size:x-small} .headline iconportrait_inline{font-family:Arial,Helvetica,sans-serif;font-size:x-small}
''' '''
filter_regexps = [r'ad.de.doubleclick.net/'] #filter_regexps = [r'ad.de.doubleclick.net/']
keep_only_tags = [ keep_only_tags = [
dict(name='div', attrs={'class':["article"]}) , dict(name='div', attrs={'class':["article"]}) ,
] ]
@ -51,15 +52,32 @@ class ZeitDe(BasicNewsRecipe):
dict(name='div', attrs={'id':["place_5","place_4"]}) dict(name='div', attrs={'id':["place_5","place_4"]})
] ]
def get_article_url(self, article): def get_article_url(self, article):
url = article.get('guid', None) ans = article.get('guid',None)
if 'video' in url or 'quiz' in url : try:
self.log('Looking for full story link in', ans)
soup = self.index_to_soup(ans)
x = soup.find(text="Auf einer Seite lesen")
if x is not None:
a = x.parent
if a and a.has_key('href'):
ans = a['href']
self.log('Found full story link', ans)
except:
pass
if 'video' in ans or 'quiz' in ans :
ans = None
return ans
url = None
return url
def preprocess_html(self, soup): def preprocess_html(self, soup):
soup.html['xml:lang'] = self.lang soup.html['xml:lang'] = self.lang
@ -69,6 +87,7 @@ class ZeitDe(BasicNewsRecipe):
return soup return soup
#def print_version(self,url): #def print_version(self,url):
# return url.replace('http://www.zeit.de/', 'http://images.zeit.de/text/').replace('?from=rss', '') # return url.replace('http://www.zeit.de/', 'http://images.zeit.de/text/').replace('?from=rss', '')

View File

@ -21,7 +21,7 @@ __all__ = [
'linux32', 'linux64', 'linux', 'linux_freeze', 'linux_freeze2', 'linux32', 'linux64', 'linux', 'linux_freeze', 'linux_freeze2',
'osx32_freeze', 'osx32', 'osx', 'rsync', 'osx32_freeze', 'osx32', 'osx', 'rsync',
'win32_freeze', 'win32', 'win', 'win32_freeze', 'win32', 'win',
'stage1', 'stage2', 'stage3', 'publish' 'stage1', 'stage2', 'stage3', 'stage4', 'publish'
] ]
@ -50,13 +50,14 @@ from setup.resources import Resources
resources = Resources() resources = Resources()
from setup.publish import Manual, TagRelease, UploadRss, Stage1, Stage2, \ from setup.publish import Manual, TagRelease, UploadRss, Stage1, Stage2, \
Stage3, Publish Stage3, Stage4, Publish
manual = Manual() manual = Manual()
tag_release = TagRelease() tag_release = TagRelease()
upload_rss = UploadRss() upload_rss = UploadRss()
stage1 = Stage1() stage1 = Stage1()
stage2 = Stage2() stage2 = Stage2()
stage3 = Stage3() stage3 = Stage3()
stage4 = Stage4()
publish = Publish() publish = Publish()
from setup.upload import UploadUserManual, UploadInstallers, UploadDemo, \ from setup.upload import UploadUserManual, UploadInstallers, UploadDemo, \

View File

@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import sys, os, textwrap, subprocess, shutil, tempfile, atexit, stat import sys, os, textwrap, subprocess, shutil, tempfile, atexit, stat, shlex
from setup import Command, islinux, basenames, modules, functions, \ from setup import Command, islinux, basenames, modules, functions, \
__appname__, __version__ __appname__, __version__
@ -149,7 +149,9 @@ class Develop(Command):
src = os.path.join(self.SRC, 'calibre', 'devices', 'linux_mount_helper.c') src = os.path.join(self.SRC, 'calibre', 'devices', 'linux_mount_helper.c')
dest = os.path.join(self.staging_bindir, 'calibre-mount-helper') dest = os.path.join(self.staging_bindir, 'calibre-mount-helper')
self.info('Installing mount helper to '+ dest) self.info('Installing mount helper to '+ dest)
p = subprocess.Popen(['gcc', '-Wall', '-pedantic', src, '-o', dest]) cflags = os.environ.get('OVERRIDE_CFLAGS', '-Wall -pedantic')
cflags = shlex.split(cflags)
p = subprocess.Popen(['gcc']+cflags+[src, '-o', dest])
ret = p.wait() ret = p.wait()
if ret != 0: if ret != 0:
return warn() return warn()

View File

@ -24,7 +24,8 @@ class LinuxFreeze(Command):
is64bit = platform.architecture()[0] == '64bit' is64bit = platform.architecture()[0] == '64bit'
arch = 'x86_64' if is64bit else 'i686' arch = 'x86_64' if is64bit else 'i686'
ffi = '/usr/lib/libffi.so.5' if is64bit else '/usr/lib/gcc/i686-pc-linux-gnu/4.4.1/libffi.so.4' ffi = '/usr/lib/libffi.so.5' if is64bit else '/usr/lib/gcc/i686-pc-linux-gnu/4.4.1/libffi.so.4'
stdcpp = '/usr/lib/gcc/%s-pc-linux-gnu/%s/libstdc++.so.6'%(arch, '4.4.2'
if is64bit else '4.4.1')
QTDIR = '/usr/lib/qt4' QTDIR = '/usr/lib/qt4'
QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml', QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml',
@ -46,6 +47,7 @@ class LinuxFreeze(Command):
'/usr/lib/libmng.so.1', '/usr/lib/libmng.so.1',
'/usr/lib/libpodofo.so.0.6.99', '/usr/lib/libpodofo.so.0.6.99',
'/lib/libz.so.1', '/lib/libz.so.1',
'/lib/libuuid.so.1',
'/usr/lib/libtiff.so.3', '/usr/lib/libtiff.so.3',
'/lib/libbz2.so.1', '/lib/libbz2.so.1',
'/usr/lib/libpoppler.so.5', '/usr/lib/libpoppler.so.5',
@ -56,8 +58,7 @@ class LinuxFreeze(Command):
'/usr/lib/libjpeg.so.7', '/usr/lib/libjpeg.so.7',
'/usr/lib/libxslt.so.1', '/usr/lib/libxslt.so.1',
'/usr/lib/libgthread-2.0.so.0', '/usr/lib/libgthread-2.0.so.0',
'/usr/lib/gcc/***-pc-linux-gnu/4.4.1/libstdc++.so.6'.replace('***', stdcpp,
arch),
ffi, ffi,
'/usr/lib/libpng12.so.0', '/usr/lib/libpng12.so.0',
'/usr/lib/libexslt.so.0', '/usr/lib/libexslt.so.0',

View File

@ -6,7 +6,13 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import sys, re, os, shutil, subprocess, stat, glob, zipfile, plistlib import sys, re, os, shutil, subprocess, stat, glob, zipfile, plistlib
from setup import __version__ as VERSION, __appname__ as APPNAME, SRC, Command, \ from setup import __version__ as VERSION, __appname__ as APPNAME, SRC, Command, \
scripts, basenames, functions as main_functions, modules as main_modules scripts, basenames, functions as main_functions, modules as main_modules
try:
from setuptools import setup from setuptools import setup
except:
setup
class setup:
pass
try: try:
from py2app.build_app import py2app from py2app.build_app import py2app

View File

@ -120,7 +120,7 @@
</Condition> </Condition>
<InstallExecuteSequence> <InstallExecuteSequence>
<Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom> <Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom>
<RemoveExistingProducts Before="InstallInitialize" /> <RemoveExistingProducts After="InstallFinalize" />
</InstallExecuteSequence> </InstallExecuteSequence>
<InstallUISequence> <InstallUISequence>
<Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom> <Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom>
@ -143,7 +143,7 @@
has to install the VC90 merge module into the system winsxs folder for python has to install the VC90 merge module into the system winsxs folder for python
to work, so per user installs are impossible anyway. to work, so per user installs are impossible anyway.
--> -->
<Property Id="ApplicationFolderName" Value="Calibre - E-book Management" /> <Property Id="ApplicationFolderName" Value="Calibre2" />
<Property Id="WixAppFolder" Value="WixPerMachineFolder" /> <Property Id="WixAppFolder" Value="WixPerMachineFolder" />
<WixVariable Id="WixUISupportPerUser" Value="0" /> <WixVariable Id="WixUISupportPerUser" Value="0" />

View File

@ -48,10 +48,17 @@ class Stage3(Command):
'upload_to_server', 'upload_to_mobileread', 'upload_to_server', 'upload_to_mobileread',
] ]
class Stage4(Command):
description = 'Stage 4 of the publish process'
def run(self, opts):
subprocess.check_call('rm -rf build/* dist/*', shell=True)
class Publish(Command): class Publish(Command):
description = 'Publish a new calibre release' description = 'Publish a new calibre release'
sub_commands = ['stage1', 'stage2', 'stage3'] sub_commands = ['stage1', 'stage2', 'stage3', 'stage4']
class Manual(Command): class Manual(Command):

View File

@ -345,7 +345,6 @@ class UploadToServer(Command):
shell=True) shell=True)
check_call('scp dist/calibre-*.tar.gz.asc divok:%s/signatures/'%DOWNLOADS, check_call('scp dist/calibre-*.tar.gz.asc divok:%s/signatures/'%DOWNLOADS,
shell=True) shell=True)
check_call('''rm -rf dist/* build/*''', shell=True)
check_call('ssh divok bzr update /var/www/calibre.kovidgoyal.net/calibre/', check_call('ssh divok bzr update /var/www/calibre.kovidgoyal.net/calibre/',
shell=True) shell=True)
check_call('ssh divok bzr update /usr/local/calibre', check_call('ssh divok bzr update /usr/local/calibre',

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.6.18' __version__ = '0.6.19'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re import re

View File

@ -352,6 +352,7 @@ from calibre.ebooks.pdf.output import PDFOutput
from calibre.ebooks.pml.output import PMLOutput from calibre.ebooks.pml.output import PMLOutput
from calibre.ebooks.rb.output import RBOutput from calibre.ebooks.rb.output import RBOutput
from calibre.ebooks.rtf.output import RTFOutput from calibre.ebooks.rtf.output import RTFOutput
from calibre.ebooks.tcr.output import TCROutput
from calibre.ebooks.txt.output import TXTOutput from calibre.ebooks.txt.output import TXTOutput
from calibre.customize.profiles import input_profiles, output_profiles from calibre.customize.profiles import input_profiles, output_profiles
@ -402,6 +403,7 @@ plugins += [
PMLOutput, PMLOutput,
RBOutput, RBOutput,
RTFOutput, RTFOutput,
TCROutput,
TXTOutput, TXTOutput,
] ]
plugins += [ plugins += [

View File

@ -165,7 +165,7 @@ def main(args=sys.argv):
sys.argv = args[:1] sys.argv = args[:1]
exec opts.command exec opts.command
elif opts.exec_file: elif opts.exec_file:
sys.argv = args[:1] sys.argv = args
base = os.path.dirname(os.path.abspath(opts.exec_file)) base = os.path.dirname(os.path.abspath(opts.exec_file))
sys.path.insert(0, base) sys.path.insert(0, base)
g = globals() g = globals()

View File

@ -36,4 +36,12 @@ class ESLICK(USBMS):
SUPPORTS_SUB_DIRS = True SUPPORTS_SUB_DIRS = True
def windows_sort_drives(self, drives):
main = drives.get('main', None)
card = drives.get('carda', None)
if card and main and card < main:
drives['main'] = card
drives['carda'] = main
return drives

View File

@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
import re
def decompress(stream):
txt = []
stream.seek(0)
if stream.read(9) != '!!8-Bit!!':
raise ValueError('File %s contaions an invalid TCR header.' % stream.name)
# Codes that the file contents are broken down into.
entries = []
for i in xrange(256):
entry_len = ord(stream.read(1))
entries.append(stream.read(entry_len))
# Map the values in the file to locations in the string list.
entry_loc = stream.read(1)
while entry_loc != '': # EOF
txt.append(entries[ord(entry_loc)])
entry_loc = stream.read(1)
return ''.join(txt)
def compress(txt, level=5):
'''
TCR compression takes the form header+code_list+coded_text.
The header is always "!!8-Bit!!". The code list is a list of 256 strings.
The list takes the form 1 byte length and then a string. Each position in
The list corresponds to a code found in the file. The coded text is
string of characters vaules. for instance the character Q represents the
value 81 which corresponds to the string in the code list at position 81.
'''
# Turn each unique character into a coded value.
# The code of the string at a given position are represented by the position
# they occupy in the list.
codes = list(set(re.findall('(?msu).', txt)))
for i in range(len(codes), 256):
codes.append('')
# Set the compression level.
if level <= 1:
new_length = 256
if level >= 10:
new_length = 1
else:
new_length = int(256 * (10 - level) * .1)
new_length = 1 if new_length < 1 else new_length
# Replace txt with codes.
coded_txt = ''
for c in txt:
coded_txt += chr(codes.index(c))
txt = coded_txt
# Start compressing the text.
new = True
merged = True
while new or merged:
# Merge codes that always follow another code
merge = []
merged = False
for i in xrange(256):
if codes[i] != '':
# Find all codes that are next to i.
fall = list(set(re.findall('(?msu)%s.' % re.escape(chr(i)), txt)))
# 1 if only one code comes after i.
if len(fall) == 1:
# We are searching codes and each code is always 1 character.
j = ord(fall[0][1:2])
# Only merge if the total length of the string represented by
# code is less than 256.
if len(codes[i]) + len(codes[j]) < 256:
merge.append((i, j))
if merge:
merged = True
for i, j in merge:
# Merge the string for j into the string for i.
if i == j:
# Don't use += here just in case something goes wrong. This
# will prevent out of control memory consumption. This is
# unecessary but when creating this routine it happened due
# to an error.
codes[i] = codes[i] + codes[i]
else:
codes[i] = codes[i] + codes[j]
txt = txt.replace(chr(i)+chr(j), chr(i))
if chr(j) not in txt:
codes[j] = ''
new = False
if '' in codes:
# Create a list of codes based on combinations of codes that are next
# to each other. The amount of savings for the new code is calculated.
new_codes = []
for c in list(set(re.findall('(?msu)..', txt))):
i = ord(c[0:1])
j = ord(c[1:2])
if codes[i]+codes[j] in codes:
continue
savings = txt.count(chr(i)+chr(j)) - len(codes[i]) - len(codes[j])
if savings > 2 and len(codes[i]) + len(codes[j]) < 256:
new_codes.append((savings, i, j, codes[i], codes[j]))
if new_codes:
new = True
# Sort the codes from highest savings to lowest.
new_codes.sort(lambda x, y: -1 if x[0] > y[0] else 1 if x[0] < y[0] else 0)
# The shorter new_length the more chances time merging will happen
# giving more changes for better codes to be created. However,
# the shorter new_lengh the longer it will take to compress.
new_codes = new_codes[:new_length]
for code in new_codes:
if '' not in codes:
break
c = codes.index('')
codes[c] = code[3]+code[4]
txt = txt.replace(chr(code[1])+chr(code[2]), chr(c))
# Generate the code dictionary.
header = []
for code in codes:
header.append(chr(len(code))+code)
for i in xrange(len(header), 256):
header.append(chr(0))
# Join the identifier with the dictionary and coded text.
return '!!8-Bit!!'+''.join(header)+txt

View File

@ -1316,9 +1316,9 @@ class HTMLConverter(object):
elif m.group(2) == 'pc': elif m.group(2) == 'pc':
result = unit * (dpi/72.) * 12 result = unit * (dpi/72.) * 12
elif m.group(2) == 'mm': elif m.group(2) == 'mm':
result = unit * 0.04 * (dpi/72.) result = unit * 0.04 * (dpi)
elif m.group(2) == 'cm': elif m.group(2) == 'cm':
result = unit * 0.40 * (dpi/72.) result = unit * 0.4 * (dpi)
if result is not None: if result is not None:
if pts: if pts:
result = int(round(result * (720./dpi))) result = int(round(result * (720./dpi)))

View File

@ -270,6 +270,7 @@ class Spine(ResourceCollection):
Resource.__init__(self, *args, **kwargs) Resource.__init__(self, *args, **kwargs)
self.is_linear = True self.is_linear = True
self.id = idfunc(self.path) self.id = idfunc(self.path)
self.idref = None
@staticmethod @staticmethod
def from_opf_spine_element(itemrefs, manifest): def from_opf_spine_element(itemrefs, manifest):
@ -281,6 +282,7 @@ class Spine(ResourceCollection):
if path: if path:
r = Spine.Item(s.manifest.id_for_path, path, is_path=True) r = Spine.Item(s.manifest.id_for_path, path, is_path=True)
r.is_linear = itemref.get('linear', 'yes') == 'yes' r.is_linear = itemref.get('linear', 'yes') == 'yes'
r.idref = idref
s.append(r) s.append(r)
return s return s

View File

@ -489,6 +489,9 @@ class MobiReader(object):
mobi_version = self.book_header.mobi_version mobi_version = self.book_header.mobi_version
for i, tag in enumerate(root.iter(etree.Element)): for i, tag in enumerate(root.iter(etree.Element)):
tag.attrib.pop('xmlns', '') tag.attrib.pop('xmlns', '')
for x in tag.attrib:
if ':' in x:
del tag.attrib[x]
if tag.tag in ('country-region', 'place', 'placetype', 'placename', if tag.tag in ('country-region', 'place', 'placetype', 'placename',
'state', 'city', 'street', 'address', 'content'): 'state', 'city', 'street', 'address', 'content'):
tag.tag = 'div' if tag.tag == 'content' else 'span' tag.tag = 'div' if tag.tag == 'content' else 'span'

View File

@ -19,6 +19,7 @@ from calibre.utils.zipfile import safe_replace, ZipFile
from calibre.utils.config import DynamicConfig from calibre.utils.config import DynamicConfig
from calibre.utils.logging import Log from calibre.utils.logging import Log
from calibre.ebooks.epub.output import EPUBOutput from calibre.ebooks.epub.output import EPUBOutput
from calibre import guess_type
TITLEPAGE = EPUBOutput.TITLEPAGE_COVER.decode('utf-8') TITLEPAGE = EPUBOutput.TITLEPAGE_COVER.decode('utf-8')
@ -39,20 +40,20 @@ class UnsupportedFormatError(Exception):
class SpineItem(unicode): class SpineItem(unicode):
def __new__(cls, *args): def __new__(cls, path, mime_type=None):
args = list(args)
path = args[0]
ppath = path.partition('#')[0] ppath = path.partition('#')[0]
if not os.path.exists(path) and os.path.exists(ppath): if not os.path.exists(path) and os.path.exists(ppath):
path = ppath path = ppath
args[0] = path obj = super(SpineItem, cls).__new__(cls, path)
obj = super(SpineItem, cls).__new__(cls, *args)
raw = open(path, 'rb').read() raw = open(path, 'rb').read()
raw, obj.encoding = xml_to_unicode(raw) raw, obj.encoding = xml_to_unicode(raw)
obj.character_count = character_count(raw) obj.character_count = character_count(raw)
obj.start_page = -1 obj.start_page = -1
obj.pages = -1 obj.pages = -1
obj.max_page = -1 obj.max_page = -1
if mime_type is None:
mime_type = guess_type(obj)[0]
obj.mime_type = mime_type
return obj return obj
class FakeOpts(object): class FakeOpts(object):
@ -150,8 +151,17 @@ class EbookIterator(object):
self.language = self.opf.language self.language = self.opf.language
if self.language: if self.language:
self.language = self.language.lower() self.language = self.language.lower()
self.spine = [SpineItem(i.path) for i in self.opf.spine if i.is_linear] ordered = [i for i in self.opf.spine if i.is_linear] + \
self.spine += [SpineItem(i.path) for i in self.opf.spine if not i.is_linear] [i for i in self.opf.spine if not i.is_linear]
self.spine = []
for i in ordered:
spath = i.path
mt = None
if i.idref is not None:
mt = self.opf.manifest.type_for_id(i.idref)
if mt is None:
mt = guess_type(spath)[0]
self.spine.append(SpineItem(spath, mime_type=mt))
cover = self.opf.cover cover = self.opf.cover
if self.ebook_ext in ('lit', 'mobi', 'prc', 'opf') and cover: if self.ebook_ext in ('lit', 'mobi', 'prc', 'opf') and cover:

View File

@ -188,6 +188,16 @@ class Stylizer(object):
%(text, item.href)) %(text, item.href))
for elem in matches: for elem in matches:
self.style(elem)._update_cssdict(cssdict) self.style(elem)._update_cssdict(cssdict)
for elem in xpath(tree, '//h:img[@width or @height]'):
base = elem.get('style', '').strip()
if base:
base += ';'
for prop in ('width', 'height'):
val = elem.get(prop, False)
if val:
base += '%s: %s;'%(prop, val)
del elem.attrib[prop]
elem.set('style', base)
for elem in xpath(tree, '//h:*[@style]'): for elem in xpath(tree, '//h:*[@style]'):
self.style(elem)._apply_style_attr() self.style(elem)._apply_style_attr()

View File

@ -41,14 +41,14 @@ PML_HTML_RULES = [
(re.compile(r'\\q="(?P<target>#.+?)"(?P<text>.*?)\\q', re.DOTALL), lambda match: '<a href="%s">%s</a>' % (match.group('target'), match.group('text')) if match.group('text') else ''), (re.compile(r'\\q="(?P<target>#.+?)"(?P<text>.*?)\\q', re.DOTALL), lambda match: '<a href="%s">%s</a>' % (match.group('target'), match.group('text')) if match.group('text') else ''),
(re.compile(r'\\Q="(?P<target>.+?)"'), lambda match: '<span id="%s"></span>' % match.group('target')), (re.compile(r'\\Q="(?P<target>.+?)"'), lambda match: '<span id="%s"></span>' % match.group('target')),
(re.compile(r'\\-'), lambda match: ''), (re.compile(r'\\-'), lambda match: ''),
(re.compile(r'\\Fn="(?P<target>.+?)"(?P<text>.*?)\\Fn'), lambda match: '<a href="#footnote-%s">%s</a>' % (match.group('target'), match.group('text')) if match.group('text') else ''), (re.compile(r'\\Fn="(?P<target>.+?)"(?P<text>.*?)\\Fn'), lambda match: '<a href="#fns-%s">%s</a>' % (match.group('target'), match.group('text')) if match.group('text') else ''),
(re.compile(r'\\Sd="(?P<target>.+?)"(?P<text>.*?)\\Sd'), lambda match: '<a href="#sidebar-%s">%s</a>' % (match.group('target'), match.group('text')) if match.group('text') else ''), (re.compile(r'\\Sd="(?P<target>.+?)"(?P<text>.*?)\\Sd'), lambda match: '<a href="#fns-%s">%s</a>' % (match.group('target'), match.group('text')) if match.group('text') else ''),
# Just italicize index items as that is how the eReader software renders them. # Just italicize index items as that is how the eReader software renders them.
(re.compile(r'\\I(?P<text>.*?)\\I', re.DOTALL), lambda match: '<i>%s</i>' % match.group('text') if match.group('text') else ''), (re.compile(r'\\I(?P<text>.*?)\\I', re.DOTALL), lambda match: '<i>%s</i>' % match.group('text') if match.group('text') else ''),
# Sidebar and Footnotes # Sidebar and Footnotes
(re.compile(r'<sidebar\s+id="(?P<target>.+?)">\s*(?P<text>.*?)\s*</sidebar>', re.DOTALL), lambda match: '<div id="sidebar-%s">%s</div>' % (match.group('target'), match.group('text')) if match.group('text') else ''), (re.compile(r'<sidebar\s+id="(?P<target>.+?)">\s*(?P<text>.*?)\s*</sidebar>', re.DOTALL), lambda match: '<div id="fns-%s">%s</div>' % (match.group('target'), match.group('text')) if match.group('text') else ''),
(re.compile(r'<footnote\s+id="(?P<target>.+?)">\s*(?P<text>.*?)\s*</footnote>', re.DOTALL), lambda match: '<div id="footnote-%s">%s</div>' % (match.group('target'), match.group('text')) if match.group('text') else ''), (re.compile(r'<footnote\s+id="(?P<target>.+?)">\s*(?P<text>.*?)\s*</footnote>', re.DOTALL), lambda match: '<div id="fns-%s">%s</div>' % (match.group('target'), match.group('text')) if match.group('text') else ''),
# eReader files are one paragraph per line. # eReader files are one paragraph per line.
# This forces the lines to wrap properly. # This forces the lines to wrap properly.
@ -80,5 +80,5 @@ def pml_to_html(pml):
def footnote_sidebar_to_html(id, pml): def footnote_sidebar_to_html(id, pml):
if id.startswith('\x01'): if id.startswith('\x01'):
id = id[2:] id = id[2:]
html = '<div id="sidebar-%s"><dt>%s</dt></div><dd>%s</dd>' % (id, id, pml_to_html(pml)) html = '<div id="fns-%s"><dt>%s</dt></div><dd>%s</dd>' % (id, id, pml_to_html(pml))
return html return html

View File

@ -72,7 +72,7 @@ class Tokenize:
return line return line
def __compile_expressions(self): def __compile_expressions(self):
self.__ms_hex_exp = re.compile(r"\\\'(..)") self.__ms_hex_exp = re.compile(r"\\\'(..)")
self.__utf_exp = re.compile(r"\\u(-?\d{3,6})") self.__utf_exp = re.compile(r"\\u(-?\d{3,6}) {0,1}")
self.__splitexp = re.compile(r"(\\[\\{}]|{|}|\\[^\s\\{}&]+(?:\s)?)") self.__splitexp = re.compile(r"(\\[\\{}]|{|}|\\[^\s\\{}&]+(?:\s)?)")
self.__par_exp = re.compile(r'\\$') self.__par_exp = re.compile(r'\\$')
self.__mixed_exp = re.compile(r"(\\[a-zA-Z]+\d+)(\D+)") self.__mixed_exp = re.compile(r"(\\[a-zA-Z]+\d+)(\D+)")

View File

@ -9,6 +9,7 @@ import os
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
from calibre.ebooks.txt.processor import convert_basic, opf_writer, \ from calibre.ebooks.txt.processor import convert_basic, opf_writer, \
separate_paragraphs_single_line, separate_paragraphs_print_formatted separate_paragraphs_single_line, separate_paragraphs_print_formatted
from calibre.ebooks.compression.tcr import decompress
class TCRInput(InputFormatPlugin): class TCRInput(InputFormatPlugin):
@ -31,28 +32,9 @@ class TCRInput(InputFormatPlugin):
]) ])
def convert(self, stream, options, file_ext, log, accelerators): def convert(self, stream, options, file_ext, log, accelerators):
txt = []
log.debug('Checking TCR header...')
if stream.read(9) != '!!8-Bit!!':
raise ValueError('File %s contaions an invalid TCR header.' % stream.name)
log.debug('Building string dictionary...')
# Dictionary codes that the file contents are broken down into.
entries = []
for i in xrange(256):
entry_len = ord(stream.read(1))
entries.append(stream.read(entry_len))
log.info('Decompressing text...') log.info('Decompressing text...')
# Map the values in the file to locations in the string list.
entry_loc = stream.read(1)
while entry_loc != '': # EOF
txt.append(entries[ord(entry_loc)])
entry_loc = stream.read(1)
ienc = options.input_encoding if options.input_encoding else 'utf-8' ienc = options.input_encoding if options.input_encoding else 'utf-8'
txt = ''.join(txt).decode(ienc, 'replace') txt = decompress(stream).decode(ienc, 'replace')
log.info('Converting text to OEB...') log.info('Converting text to OEB...')
if options.single_line_paras: if options.single_line_paras:

View File

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
import os
from calibre.customize.conversion import OutputFormatPlugin, \
OptionRecommendation
from calibre.ebooks.txt.txtml import TXTMLizer
from calibre.ebooks.compression.tcr import compress
class TCROutput(OutputFormatPlugin):
name = 'TCR Output'
author = 'John Schember'
file_type = 'tcr'
options = set([
OptionRecommendation(name='output_encoding', recommended_value='utf-8',
level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the output document. ' \
'The default is utf-8.')),
OptionRecommendation(name='compression_level', recommended_value=5,
level=OptionRecommendation.LOW,
help=_('Speciy the compression level to use. Scale 1 - 10. 1 ' \
'being the lowest compression but the fastest and 10 being the ' \
'highest compression but the slowest.')),
])
def convert(self, oeb_book, output_path, input_plugin, opts, log):
close = False
if not hasattr(output_path, 'write'):
close = True
if not os.path.exists(os.path.dirname(output_path)) and os.path.dirname(output_path) != '':
os.makedirs(os.path.dirname(output_path))
out_stream = open(output_path, 'wb')
else:
out_stream = output_path
setattr(opts, 'flush_paras', False)
setattr(opts, 'max_line_length', 0)
setattr(opts, 'force_max_line_length', False)
setattr(opts, 'indent_paras', False)
writer = TXTMLizer(log)
txt = writer.extract_content(oeb_book, opts).encode(opts.output_encoding, 'replace')
log.info('Compressing text...')
txt = compress(txt, opts.compression_level)
out_stream.seek(0)
out_stream.truncate()
out_stream.write(txt)
if close:
out_stream.close()

View File

@ -453,6 +453,12 @@ class ConfigDialog(QDialog, Ui_Dialog):
self.delete_news.setEnabled) self.delete_news.setEnabled)
self.setup_conversion_options() self.setup_conversion_options()
self.opt_worker_limit.setValue(config['worker_limit']) self.opt_worker_limit.setValue(config['worker_limit'])
self.connect(self.button_open_config_dir, SIGNAL('clicked()'),
self.open_config_dir)
def open_config_dir(self):
from calibre.utils.config import config_dir
QDesktopServices.openUrl(QUrl.fromLocalFile(config_dir))
def create_symlinks(self): def create_symlinks(self):
from calibre.utils.osx_symlinks import create_symlinks from calibre.utils.osx_symlinks import create_symlinks

View File

@ -616,13 +616,20 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="2"> <item row="3" column="0" colspan="2">
<widget class="QPushButton" name="button_osx_symlinks"> <widget class="QPushButton" name="button_osx_symlinks">
<property name="text"> <property name="text">
<string>&amp;Install command line tools</string> <string>&amp;Install command line tools</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="2">
<widget class="QPushButton" name="button_open_config_dir">
<property name="text">
<string>Open calibre &amp;configuration directory</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="page_4"> <widget class="QWidget" name="page_4">

View File

@ -14,7 +14,7 @@ from PyQt4.Qt import QDialog, QApplication, SIGNAL, Qt, QTime, QObject, QMenu, \
from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog
from calibre.gui2.search_box import SearchBox2 from calibre.gui2.search_box import SearchBox2
from calibre.gui2 import config as gconf from calibre.gui2 import config as gconf, error_dialog
from calibre.web.feeds.recipes.model import RecipeModel from calibre.web.feeds.recipes.model import RecipeModel
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
@ -36,6 +36,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.connect(self.recipe_model, SIGNAL('searched(PyQt_PyObject)'), self.connect(self.recipe_model, SIGNAL('searched(PyQt_PyObject)'),
self.search_done) self.search_done)
self.search.setFocus(Qt.OtherFocusReason) self.search.setFocus(Qt.OtherFocusReason)
self.commit_on_change = True
self.recipes.setModel(self.recipe_model) self.recipes.setModel(self.recipe_model)
self.detail_box.setVisible(False) self.detail_box.setVisible(False)
@ -55,6 +56,10 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.old_news.setValue(gconf['oldest_news']) self.old_news.setValue(gconf['oldest_news'])
def break_cycles(self): def break_cycles(self):
self.disconnect(self.recipe_model, SIGNAL('searched(PyQt_PyObject)'),
self.search_done)
self.disconnect(self.recipe_model, SIGNAL('searched(PyQt_PyObject)'),
self.search.search_done)
self.recipe_model = None self.recipe_model = None
def search_done(self, *args): def search_done(self, *args):
@ -68,24 +73,31 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.last_downloaded.setVisible(enabled) self.last_downloaded.setVisible(enabled)
def current_changed(self, current, previous): def current_changed(self, current, previous):
if self.commit_on_change:
if previous.isValid(): if previous.isValid():
self.commit(urn=getattr(previous.internalPointer(), 'urn', None)) if not self.commit(urn=getattr(previous.internalPointer(),
'urn', None)):
self.commit_on_change = False
self.recipes.setCurrentIndex(previous)
else:
self.commit_on_change = True
urn = self.current_urn urn = self.current_urn
if urn is not None: if urn is not None:
self.initialize_detail_box(urn) self.initialize_detail_box(urn)
def accept(self): def accept(self):
self.commit() if not self.commit():
return False
return QDialog.accept(self) return QDialog.accept(self)
def download_clicked(self): def download_clicked(self):
self.commit() self.commit()
if self.current_urn: if self.commit() and self.current_urn:
self.emit(SIGNAL('download(PyQt_PyObject)'), self.current_urn) self.emit(SIGNAL('download(PyQt_PyObject)'), self.current_urn)
def download_all_clicked(self): def download_all_clicked(self):
self.commit() if self.commit() and self.commit():
self.emit(SIGNAL('download(PyQt_PyObject)'), None) self.emit(SIGNAL('download(PyQt_PyObject)'), None)
@property @property
@ -97,10 +109,15 @@ class SchedulerDialog(QDialog, Ui_Dialog):
def commit(self, urn=None): def commit(self, urn=None):
urn = self.current_urn if urn is None else urn urn = self.current_urn if urn is None else urn
if not self.detail_box.isVisible() or urn is None: if not self.detail_box.isVisible() or urn is None:
return return True
if self.account.isVisible(): if self.account.isVisible():
un, pw = map(unicode, (self.username.text(), self.password.text())) un, pw = map(unicode, (self.username.text(), self.password.text()))
if not un and not pw and self.schedule.isChecked():
error_dialog(self, _('Need username and password'),
_('You must provide a username and/or password to '
'use this news source.'), show=True)
return False
self.recipe_model.set_account_info(urn, un.strip(), pw.strip()) self.recipe_model.set_account_info(urn, un.strip(), pw.strip())
if self.schedule.isChecked(): if self.schedule.isChecked():
@ -122,6 +139,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
custom_tags = unicode(self.custom_tags.text()).strip() custom_tags = unicode(self.custom_tags.text()).strip()
custom_tags = [x.strip() for x in custom_tags.split(',')] custom_tags = [x.strip() for x in custom_tags.split(',')]
self.recipe_model.customize_recipe(urn, add_title_tag, custom_tags) self.recipe_model.customize_recipe(urn, add_title_tag, custom_tags)
return True
def initialize_detail_box(self, urn): def initialize_detail_box(self, urn):
self.detail_box.setVisible(True) self.detail_box.setVisible(True)

View File

@ -1621,11 +1621,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
try: try:
os.makedirs(self.library_path) os.makedirs(self.library_path)
except: except:
self.library_path = os.path.expanduser('~/Library') self.library_path = os.path.expanduser('~/CalibreLibrary')
error_dialog(self, _('Invalid library location'), error_dialog(self, _('Invalid library location'),
_('Could not access %s. Using %s as the library.')% _('Could not access %s. Using %s as the library.')%
(repr(self.library_path), repr(self.library_path)) (repr(self.library_path), repr(self.library_path))
).exec_() ).exec_()
if not os.path.exists(self.library_path):
os.makedirs(self.library_path) os.makedirs(self.library_path)

View File

@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import QComboBox, SIGNAL, Qt, QLineEdit, QStringList from PyQt4.Qt import QComboBox, SIGNAL, Qt, QLineEdit, QStringList, pyqtSlot
from calibre.gui2 import config from calibre.gui2 import config
@ -25,6 +25,17 @@ class SearchLineEdit(QLineEdit):
self.parent().normalize_state() self.parent().normalize_state()
return QLineEdit.dropEvent(self, ev) return QLineEdit.dropEvent(self, ev)
def contextMenuEvent(self, ev):
if self.parent().help_state:
self.parent().normalize_state()
return QLineEdit.contextMenuEvent(self, ev)
@pyqtSlot()
def paste(self, *args):
if self.parent().help_state:
self.parent().normalize_state()
return QLineEdit.paste(self)
class SearchBox2(QComboBox): class SearchBox2(QComboBox):
''' '''

View File

@ -18,7 +18,7 @@ from calibre.gui2.viewer.config_ui import Ui_Dialog
from calibre.gui2.viewer.js import bookmarks, referencing, hyphenation from calibre.gui2.viewer.js import bookmarks, referencing, hyphenation
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.constants import iswindows from calibre.constants import iswindows
from calibre import prints from calibre import prints, guess_type
def load_builtin_fonts(): def load_builtin_fonts():
base = P('fonts/liberation/*.ttf') base = P('fonts/liberation/*.ttf')
@ -352,6 +352,8 @@ class DocumentView(QWebView):
def __init__(self, *args): def __init__(self, *args):
QWidget.__init__(self, *args) QWidget.__init__(self, *args)
self.debug_javascript = False self.debug_javascript = False
self.self_closing_pat = re.compile(r'<([a-z]+)\s+([^>]+)/>',
re.IGNORECASE)
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
self._size_hint = QSize(510, 680) self._size_hint = QSize(510, 680)
self.initial_pos = 0.0 self.initial_pos = 0.0
@ -447,8 +449,14 @@ class DocumentView(QWebView):
def load_path(self, path, pos=0.0): def load_path(self, path, pos=0.0):
self.initial_pos = pos self.initial_pos = pos
mt = getattr(path, 'mime_type', None)
if mt is None:
mt = guess_type(path)[0]
html = open(path, 'rb').read().decode(path.encoding, 'replace') html = open(path, 'rb').read().decode(path.encoding, 'replace')
html = EntityDeclarationProcessor(html).processed_html html = EntityDeclarationProcessor(html).processed_html
if 'xhtml' in mt:
html = self.self_closing_pat.sub(r'<\1 \2></\1>', html)
#self.setContent(QByteArray(html.encode(path.encoding)), mt, QUrl.fromLocalFile(path))
self.setHtml(html, QUrl.fromLocalFile(path)) self.setHtml(html, QUrl.fromLocalFile(path))
self.turn_off_internal_scrollbars() self.turn_off_internal_scrollbars()

View File

@ -6,7 +6,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import sys, os, shutil, cPickle, textwrap, stat import sys, os, shutil, cPickle, textwrap, stat
from subprocess import check_call from subprocess import check_call
from calibre import __version__, __appname__, prints from calibre import __appname__, prints
entry_points = { entry_points = {
@ -491,36 +491,36 @@ complete -o filenames -F _'''%(opts,exts) + name + ' ' + name +"\n\n"
VIEWER = '''\ VIEWER = '''\
[Desktop Entry] [Desktop Entry]
Version=%s Version=1.0
Type=Application Type=Application
Name=LRF Viewer Name=LRF Viewer
GenericName=Viewer for LRF files GenericName=Viewer for LRF files
Comment=Viewer for LRF files (SONY ebook format files) Comment=Viewer for LRF files (SONY ebook format files)
TryExec=lrfviewer TryExec=lrfviewer
Exec=lrfviewer %%F Exec=lrfviewer %F
Icon=calibre-viewer Icon=calibre-viewer
MimeType=application/x-sony-bbeb; MimeType=application/x-sony-bbeb;
Categories=Graphics;Viewer; Categories=Graphics;Viewer;
'''%(__version__,) '''
EVIEWER = '''\ EVIEWER = '''\
[Desktop Entry] [Desktop Entry]
Version=%s Version=1.0
Type=Application Type=Application
Name=E-book Viewer Name=E-book Viewer
GenericName=Viewer for E-books GenericName=Viewer for E-books
Comment=Viewer for E-books Comment=Viewer for E-books
TryExec=ebook-viewer TryExec=ebook-viewer
Exec=ebook-viewer %%F Exec=ebook-viewer %F
Icon=calibre-viewer Icon=calibre-viewer
MimeType=application/epub+zip; MimeType=application/epub+zip;
Categories=Graphics;Viewer; Categories=Graphics;Viewer;
'''%(__version__,) '''
GUI = '''\ GUI = '''\
[Desktop Entry] [Desktop Entry]
Version=%s Version=1.0
Type=Application Type=Application
Name=calibre Name=calibre
GenericName=E-book library management GenericName=E-book library management
@ -529,7 +529,7 @@ TryExec=calibre
Exec=calibre Exec=calibre
Icon=calibre-gui Icon=calibre-gui
Categories=Office; Categories=Office;
'''%(__version__,) '''
MIME = '''\ MIME = '''\
<?xml version="1.0"?> <?xml version="1.0"?>

View File

@ -21,7 +21,7 @@ What formats does |app| support conversion to/from?
It can convert every input format in the following list, to every output format. It can convert every input format in the following list, to every output format.
*Input Formats:* CBZ, CBR, CBC, EPUB, FB2, HTML, LIT, LRF, MOBI, ODT, PDF, PRC**, PDB, PML, RB, RTF, TCR, TXT *Input Formats:* CBZ, CBR, CBC, EPUB, FB2, HTML, LIT, LRF, MOBI, ODT, PDF, PRC**, PDB, PML, RB, RTF, TCR, TXT
*Output Formats:* EPUB, FB2, OEB, LIT, LRF, MOBI, PDB, PML, RB, PDF, TXT *Output Formats:* EPUB, FB2, OEB, LIT, LRF, MOBI, PDB, PML, RB, PDF, TCR, TXT
** PRC is a generic format, |app| supports PRC files with TextRead and MOBIBook headers ** PRC is a generic format, |app| supports PRC files with TextRead and MOBIBook headers
@ -141,6 +141,11 @@ Now you should be able to access your books on your iPhone by opening Stanza and
Replace ``192.168.1.2`` with the local IP address of the computer running |app|. If you have changed the port the |app| content server is running on, you will have to change ``8080`` as well to the new port. The local IP address is the IP address you computer is assigned on your home network. A quick Google search will tell you how to find out your local IP address. Replace ``192.168.1.2`` with the local IP address of the computer running |app|. If you have changed the port the |app| content server is running on, you will have to change ``8080`` as well to the new port. The local IP address is the IP address you computer is assigned on your home network. A quick Google search will tell you how to find out your local IP address.
How do I use |app| with my Android phone?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
First install the WordPlayer e-book reading app from the Android Marketplace onto you phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single androind device out there, so if you would like to have support for your device added, follow the instructions above for getting your device supported in |app|.
I get the error message "Failed to start content server: Port 8080 not free on '0.0.0.0'"? I get the error message "Failed to start content server: Port 8080 not free on '0.0.0.0'"?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,9 @@
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: calibre 0.6.18\n" "Project-Id-Version: calibre 0.6.19\n"
"POT-Creation-Date: 2009-10-17 10:21+MDT\n" "POT-Creation-Date: 2009-10-20 18:49+MDT\n"
"PO-Revision-Date: 2009-10-17 10:21+MDT\n" "PO-Revision-Date: 2009-10-20 18:49+MDT\n"
"Last-Translator: Automatically generated\n" "Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n" "Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -50,7 +50,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:171 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:171
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:329 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:329
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:444 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf.py:444
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:870 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:872
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdb.py:39 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdb.py:39
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:21 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:21
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/topaz.py:29 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/topaz.py:29
@ -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:79
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121 #: /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:154
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:588 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:591
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:775 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:778
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:886 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:886
@ -110,7 +110,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:106 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:106
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:139 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:139
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:431 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:431
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:152 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:170
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:391 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:391
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:404 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:404
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:876 #: /home/kovid/work/calibre/src/calibre/gui2/library.py:876
@ -521,7 +521,7 @@ msgid "There is insufficient free space on the storage card"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:713 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:713
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:214 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:232
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:122 #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:122
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1006 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1006
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1010 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1010
@ -1439,7 +1439,7 @@ msgid ""
"Fetch a cover image for the book identified by ISBN from LibraryThing.com\n" "Fetch a cover image for the book identified by ISBN from LibraryThing.com\n"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1055 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1057
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1314 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1314
msgid "Cover" msgid "Cover"
msgstr "" msgstr ""
@ -1560,7 +1560,7 @@ msgstr ""
msgid "Usage: ebook-convert INFILE OUTFILE [OPTIONS..]" msgid "Usage: ebook-convert INFILE OUTFILE [OPTIONS..]"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/iterator.py:38 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/iterator.py:39
msgid "%s format books are not supported" msgid "%s format books are not supported"
msgstr "" msgstr ""
@ -1601,13 +1601,13 @@ msgid "Sidebar"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/input.py:22 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/input.py:22
#: /home/kovid/work/calibre/src/calibre/ebooks/tcr/input.py:22 #: /home/kovid/work/calibre/src/calibre/ebooks/tcr/input.py:23
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:22 #: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:22
msgid "Normally calibre treats blank lines as paragraph markers. With this option it will assume that every line represents a paragraph instead." msgid "Normally calibre treats blank lines as paragraph markers. With this option it will assume that every line represents a paragraph instead."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/input.py:26 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/input.py:26
#: /home/kovid/work/calibre/src/calibre/ebooks/tcr/input.py:26 #: /home/kovid/work/calibre/src/calibre/ebooks/tcr/input.py:27
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:26 #: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:26
msgid "Normally calibre treats blank lines as paragraph markers. With this option it will assume that every line starting with an indent (either a tab or 2+ spaces) represents a paragraph. Paragraphs end when the next line that starts with an indent is reached." msgid "Normally calibre treats blank lines as paragraph markers. With this option it will assume that every line starting with an indent (either a tab or 2+ spaces) represents a paragraph. Paragraphs end when the next line that starts with an indent is reached."
msgstr "" msgstr ""
@ -1826,6 +1826,14 @@ msgstr ""
msgid "This RTF file has a feature calibre does not support. Convert it to HTML first and then try it." msgid "This RTF file has a feature calibre does not support. Convert it to HTML first and then try it."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/tcr/output.py:23
msgid "Specify the character encoding of the output document. The default is utf-8."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/tcr/output.py:27
msgid "Speciy the compression level to use. Scale 1 - 10. 1 being the lowest compression but the fastest and 10 being the highest compression but the slowest."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:32 #: /home/kovid/work/calibre/src/calibre/ebooks/txt/input.py:32
msgid "Run the text input through the markdown pre-processor. To learn more about markdown see" msgid "Run the text input through the markdown pre-processor. To learn more about markdown see"
msgstr "" msgstr ""
@ -2176,12 +2184,12 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/xexp_edit_ui.py:44 #: /home/kovid/work/calibre/src/calibre/gui2/convert/xexp_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:61
#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:62 #: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:62
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:471 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:474
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:483 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:486
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:484 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:487
#: /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:503
#: /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:533 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:537
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:356 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:356
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:361 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:361
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:375 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:375
@ -3206,126 +3214,126 @@ msgstr ""
msgid "new email address" msgid "new email address"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:461 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:467
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:791 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:797
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:140 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:140
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1045 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1045
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53 #: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53
msgid "Error" msgid "Error"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:462 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:468
msgid "Failed to install command line tools." msgid "Failed to install command line tools."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:465 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:471
msgid "Command line tools installed" msgid "Command line tools installed"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:466 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:472
msgid "Command line tools installed in" msgid "Command line tools installed in"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:467 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:473
msgid "If you move calibre.app, you have to re-install the command line tools." msgid "If you move calibre.app, you have to re-install the command line tools."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:518 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:524
msgid "No valid plugin path" msgid "No valid plugin path"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:519 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:525
msgid "%s is not a valid plugin path" msgid "%s is not a valid plugin path"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:522 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:528
msgid "Choose plugin" msgid "Choose plugin"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:534 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:540
msgid "Plugin cannot be disabled" msgid "Plugin cannot be disabled"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:535 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:541
msgid "The plugin: %s cannot be disabled" msgid "The plugin: %s cannot be disabled"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:544 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:550
msgid "Plugin not customizable" msgid "Plugin not customizable"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:545 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:551
msgid "Plugin: %s does not need customization" msgid "Plugin: %s does not need customization"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:569 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:575
msgid "Customize %s" msgid "Customize %s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:579 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:585
msgid "Cannot remove builtin plugin" msgid "Cannot remove builtin plugin"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:580 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:586
msgid " cannot be removed. It is a builtin plugin. Try disabling it instead." msgid " cannot be removed. It is a builtin plugin. Try disabling it instead."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:613 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:619
msgid "Error log:" msgid "Error log:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:620 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:626
msgid "Access log:" msgid "Access log:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:648 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:654
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:589 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:589
msgid "Failed to start content server" msgid "Failed to start content server"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:672 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:678
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:503 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:503
msgid "Select location for books" msgid "Select location for books"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:680 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:686
msgid "Invalid size" msgid "Invalid size"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:681 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:687
msgid "The size %s is invalid. must be of the form widthxheight" msgid "The size %s is invalid. must be of the form widthxheight"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:732 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:738
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:737 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:743
msgid "Invalid database location" msgid "Invalid database location"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:733 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:739
msgid "Invalid database location " msgid "Invalid database location "
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:734 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:740
msgid "<br>Must be a directory." msgid "<br>Must be a directory."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:738 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:744
msgid "Invalid database location.<br>Cannot write to " msgid "Invalid database location.<br>Cannot write to "
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:772 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:778
msgid "Checking database integrity" msgid "Checking database integrity"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:792 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:798
msgid "Failed to check database integrity" msgid "Failed to check database integrity"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:797 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:803
msgid "Some inconsistencies found" msgid "Some inconsistencies found"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:798 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:804
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." 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 "" msgstr ""
@ -3409,256 +3417,260 @@ msgstr ""
msgid "&Saving books" msgid "&Saving books"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:468 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:471
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:368 #: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:368
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:173 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:173
msgid "Preferences" msgid "Preferences"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:469 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:472
msgid "&Location of ebooks (The ebooks are stored in folders sorted by author and metadata is stored in the file metadata.db)" msgid "&Location of ebooks (The ebooks are stored in folders sorted by author and metadata is stored in the file metadata.db)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:470 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:473
msgid "Browse for the new database location" msgid "Browse for the new database location"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:472 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:475
msgid "Show notification when &new version is available" msgid "Show notification when &new version is available"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:473 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:476
msgid "Default network &timeout:" msgid "Default network &timeout:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:474 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:477
msgid "Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)" msgid "Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:475 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:478
msgid " seconds" msgid " seconds"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:476 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:479
msgid "Choose &language (requires restart):" msgid "Choose &language (requires restart):"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:477 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:480
msgid "Normal" msgid "Normal"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:478 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:481
msgid "High" msgid "High"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:479 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:482
msgid "Low" msgid "Low"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:480 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:483
msgid "Job &priority:" msgid "Job &priority:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:481 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:484
msgid "Preferred &output format:" msgid "Preferred &output format:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:482 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:485
msgid "Preferred &input format order:" msgid "Preferred &input format order:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:485 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:488
msgid "Use &Roman numerals for series number" msgid "Use &Roman numerals for series number"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:486 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:489
msgid "Enable system &tray icon (needs restart)" msgid "Enable system &tray icon (needs restart)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:487 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:490
msgid "Show &notifications in system tray" msgid "Show &notifications in system tray"
msgstr "" msgstr ""
#: /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:491
msgid "Show cover &browser in a separate window (needs restart)" msgid "Show cover &browser in a separate window (needs restart)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:489 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:492
msgid "Search as you type" msgid "Search as you type"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:490 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:493
msgid "Automatically send downloaded &news to ebook reader" msgid "Automatically send downloaded &news to ebook reader"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:491 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:494
msgid "&Delete news from library when it is automatically sent to reader" msgid "&Delete news from library when it is automatically sent to reader"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:492 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:495
msgid "&Number of covers to show in browse mode (needs restart):" msgid "&Number of covers to show in browse mode (needs restart):"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:493 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:496
msgid "Toolbar" msgid "Toolbar"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:494 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:497
msgid "Large" msgid "Large"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:495 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:498
msgid "Medium" msgid "Medium"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:496 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:499
msgid "Small" msgid "Small"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:497 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:500
msgid "&Button size in toolbar" msgid "&Button size in toolbar"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:498 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:501
msgid "Show &text in toolbar buttons" msgid "Show &text in toolbar buttons"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:499 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:502
msgid "Select visible &columns in library view" msgid "Select visible &columns in library view"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:502 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:505
msgid "Use internal &viewer for:" msgid "Use internal &viewer for:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:503 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:506
msgid "Add an email address to which to send books" msgid "Add an email address to which to send books"
msgstr "" msgstr ""
#: /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:507
msgid "&Add email" msgid "&Add email"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:505 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:508
msgid "Make &default" msgid "Make &default"
msgstr "" msgstr ""
#: /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:509
msgid "&Remove email" msgid "&Remove email"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:507 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:510
msgid "calibre can send your books to you (or your reader) by email" msgid "calibre can send your books to you (or your reader) by email"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:508 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:511
msgid "&Maximum number of waiting worker processes (needs restart):" msgid "&Maximum number of waiting worker processes (needs restart):"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:509 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:512
msgid "&Check database integrity" msgid "&Check database integrity"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:510 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:513
msgid "&Install command line tools" msgid "&Install command line tools"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:511 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:514
msgid "Open calibre &configuration directory"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:515
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." 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 "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:512 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:516
msgid "Server &port:" msgid "Server &port:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:513 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:517
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:58 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:58
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:210 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:210
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:117 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:117
msgid "&Username:" msgid "&Username:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:514 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:518
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:59 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:59
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:211 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:211
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:119 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:119
msgid "&Password:" msgid "&Password:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:515 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:519
msgid "If you leave the password blank, anyone will be able to access your book collection using the web interface." msgid "If you leave the password blank, anyone will be able to access your book collection using the web interface."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:516 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:520
msgid "The maximum size (widthxheight) for displayed covers. Larger covers are resized. " msgid "The maximum size (widthxheight) for displayed covers. Larger covers are resized. "
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:517 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:521
msgid "Max. &cover size:" msgid "Max. &cover size:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:518 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:522
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:60 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:60
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:212 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:212
msgid "&Show password" msgid "&Show password"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:519 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:523
msgid "Max. &OPDS items per query:" msgid "Max. &OPDS items per query:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:520 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:524
msgid "&Start Server" msgid "&Start Server"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:521 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:525
msgid "St&op Server" msgid "St&op Server"
msgstr "" msgstr ""
#: /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:526
msgid "&Test Server" msgid "&Test Server"
msgstr "" msgstr ""
#: /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:527
msgid "Run server &automatically on startup" msgid "Run server &automatically on startup"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:524 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:528
msgid "View &server logs" msgid "View &server logs"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:525 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:529
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/stanza_ui.py:46 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/stanza_ui.py:46
msgid "" msgid ""
"<p>Remember to leave calibre running as the server only runs as long as calibre is running.\n" "<p>Remember to leave calibre running as the server only runs as long as calibre is running.\n"
"<p>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." "<p>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 "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:527 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:531
msgid "Here you can customize the behavior of Calibre by controlling what plugins it uses." msgid "Here you can customize the behavior of Calibre by controlling what plugins it uses."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:528 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:532
msgid "Enable/&Disable plugin" msgid "Enable/&Disable plugin"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:529 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:533
msgid "&Customize plugin" msgid "&Customize plugin"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:530 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:534
msgid "&Remove plugin" msgid "&Remove plugin"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:531 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:535
msgid "Add new plugin" msgid "Add new plugin"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:532 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:536
msgid "Plugin &file:" msgid "Plugin &file:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:534 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:538
msgid "&Add" msgid "&Add"
msgstr "" msgstr ""
@ -3990,28 +4002,36 @@ msgstr ""
msgid "Aborting..." msgid "Aborting..."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:151 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:117
msgid "Need username and password"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:118
msgid "You must provide a username and/or password to use this news source."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:169
msgid "Created by: " msgid "Created by: "
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:158 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:176
msgid "Last downloaded: never" msgid "Last downloaded: never"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:173 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:191
msgid "%d days, %d hours and %d minutes ago" msgid "%d days, %d hours and %d minutes ago"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:175 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:193
msgid "Last downloaded" msgid "Last downloaded"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:195 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:213
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:190 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:190
msgid "Schedule news download" msgid "Schedule news download"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:198 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:216
msgid "Add a custom news source" msgid "Add a custom news source"
msgstr "" msgstr ""
@ -4993,87 +5013,87 @@ msgstr ""
msgid "Could not access %s. Using %s as the library." msgid "Could not access %s. Using %s as the library."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1673 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1674
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." 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 "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1697 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1698
msgid "There are active jobs. Are you sure you want to quit?" msgid "There are active jobs. Are you sure you want to quit?"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1700 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1701
msgid "" msgid ""
" is communicating with the device!<br>\n" " is communicating with the device!<br>\n"
" Quitting may cause corruption on the device.<br>\n" " Quitting may cause corruption on the device.<br>\n"
" Are you sure you want to quit?" " Are you sure you want to quit?"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1704 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1705
msgid "WARNING: Active jobs" msgid "WARNING: Active jobs"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1755 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1756
msgid "will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray." msgid "will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1774 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1775
msgid "<span style=\"color:red; font-weight:bold\">Latest version: <a href=\"%s\">%s</a></span>" msgid "<span style=\"color:red; font-weight:bold\">Latest version: <a href=\"%s\">%s</a></span>"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1782 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1783
msgid "Update available" msgid "Update available"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1783 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1784
msgid "%s has been updated to version %s. See the <a href=\"http://calibre.kovidgoyal.net/wiki/Changelog\">new features</a>. Visit the download page?" msgid "%s has been updated to version %s. See the <a href=\"http://calibre.kovidgoyal.net/wiki/Changelog\">new features</a>. Visit the download page?"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1801 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1802
msgid "Use the library located at the specified path." msgid "Use the library located at the specified path."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1803 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1804
msgid "Start minimized to system tray." msgid "Start minimized to system tray."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1805 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1806
msgid "Log debugging information to console" msgid "Log debugging information to console"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1807 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1808
msgid "Do not check for updates" msgid "Do not check for updates"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1855 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1856
msgid "If you are sure it is not running" msgid "If you are sure it is not running"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1857 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1858
msgid "Cannot Start " msgid "Cannot Start "
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1858 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1859
msgid "%s is already running." msgid "%s is already running."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1861 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1862
msgid "may be running in the system tray, in the" msgid "may be running in the system tray, in the"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1863 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1864
msgid "upper right region of the screen." msgid "upper right region of the screen."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1865 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1866
msgid "lower right region of the screen." msgid "lower right region of the screen."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1868 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1869
msgid "try rebooting your computer." msgid "try rebooting your computer."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1870 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1871
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1882 #: /home/kovid/work/calibre/src/calibre/gui2/main.py:1883
msgid "try deleting the file" msgid "try deleting the file"
msgstr "" msgstr ""
@ -5223,7 +5243,7 @@ msgstr ""
msgid "No matches found for this book" msgid "No matches found for this book"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:60 #: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:71
msgid "Search" msgid "Search"
msgstr "" msgstr ""
@ -6421,71 +6441,75 @@ msgstr ""
msgid "Unknown News Source" msgid "Unknown News Source"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:588 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:507
msgid "Download finished" msgid "The \"%s\" recipe needs a username and password."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:590 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:590
msgid "Download finished"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:592
msgid "Failed to download the following articles:" msgid "Failed to download the following articles:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:596 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:598
msgid "Failed to download parts of the following articles:" msgid "Failed to download parts of the following articles:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:598 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:600
msgid " from " msgid " from "
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:600 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:602
msgid "\tFailed links:" msgid "\tFailed links:"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:681 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:683
msgid "Could not fetch article. Run with -vv to see the reason" msgid "Could not fetch article. Run with -vv to see the reason"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:702 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:704
msgid "Fetching feeds..." msgid "Fetching feeds..."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:706 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:708
msgid "Got feeds from index page" msgid "Got feeds from index page"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:712 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:714
msgid "Trying to download cover..." msgid "Trying to download cover..."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:766 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:768
msgid "Starting download [%d thread(s)]..." msgid "Starting download [%d thread(s)]..."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:782 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:784
msgid "Feeds downloaded to %s" msgid "Feeds downloaded to %s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:792 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:794
msgid "Could not download cover: %s" msgid "Could not download cover: %s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:799 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:801
msgid "Downloading cover from %s" msgid "Downloading cover from %s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:925 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:927
msgid "Untitled Article" msgid "Untitled Article"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:996 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:998
msgid "Article downloaded: %s" msgid "Article downloaded: %s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1007 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1009
msgid "Article download failed: %s" msgid "Article download failed: %s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1024 #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1026
msgid "Fetching feed" msgid "Fetching feed"
msgstr "" msgstr ""
@ -6495,12 +6519,12 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:73 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:73
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:82 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:82
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:170 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:172
msgid "Scheduled" msgid "Scheduled"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:84 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:84
#: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:171 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:173
msgid "Custom" msgid "Custom"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -259,7 +259,7 @@ class DNSQuestion(DNSEntry):
def __init__(self, name, type, clazz): def __init__(self, name, type, clazz):
if not name.endswith('.local.'): if not name.endswith('.local.'):
raise NonLocalNameException raise NonLocalNameException('DNSQuestion: Not a local name '+name)
DNSEntry.__init__(self, name, type, clazz) DNSEntry.__init__(self, name, type, clazz)
def answeredBy(self, rec): def answeredBy(self, rec):
@ -856,11 +856,17 @@ class Engine(threading.Thread):
self.condition.wait(self.timeout) self.condition.wait(self.timeout)
self.condition.release() self.condition.release()
else: else:
from calibre.constants import DEBUG
try: try:
rr, wr, er = select.select(rs, [], [], self.timeout) rr, wr, er = select.select(rs, [], [], self.timeout)
for socket in rr: for socket in rr:
try: try:
self.readers[socket].handle_read() self.readers[socket].handle_read()
except NonLocalNameException, err:
print err
except UnicodeDecodeError:
if DEBUG:
traceback.print_exc()
except: except:
traceback.print_exc() traceback.print_exc()
except: except:

View File

@ -501,8 +501,10 @@ class BasicNewsRecipe(Recipe):
if isinstance(self.feeds, basestring): if isinstance(self.feeds, basestring):
self.feeds = [self.feeds] self.feeds = [self.feeds]
if self.needs_subscription and (self.username is None or self.password is None): if self.needs_subscription and (\
raise ValueError('The %s recipe needs a username and password.'%self.title) self.username is None or self.password is None or \
(not self.username and not self.password)):
raise ValueError(_('The "%s" recipe needs a username and password.')%self.title)
self.browser = self.get_browser() self.browser = self.get_browser()
self.image_map, self.image_counter = {}, 1 self.image_map, self.image_counter = {}, 1

View File

@ -193,11 +193,16 @@ class SchedulerConfig(object):
def write_scheduler_file(self): def write_scheduler_file(self):
from calibre.utils.lock import ExclusiveFile from calibre.utils.lock import ExclusiveFile
self.root.text = '\n\n\t'
for x in self.root:
x.tail = '\n\n\t'
if len(self.root) > 0:
self.root[-1].tail = '\n\n'
with ExclusiveFile(self.conf_path) as f: with ExclusiveFile(self.conf_path) as f:
f.seek(0) f.seek(0)
f.truncate() f.truncate()
f.write(etree.tostring(self.root, encoding='utf-8', f.write(etree.tostring(self.root, encoding='utf-8',
xml_declaration=True, pretty_print=True)) xml_declaration=True, pretty_print=False))
def serialize_schedule(self, typ, schedule): def serialize_schedule(self, typ, schedule):
s = E.schedule({'type':typ}) s = E.schedule({'type':typ})
@ -225,15 +230,21 @@ class SchedulerConfig(object):
typ, sch, ld = self.un_serialize_schedule(recipe) typ, sch, ld = self.un_serialize_schedule(recipe)
except: except:
return False return False
utcnow = datetime.utcnow()
if typ == 'interval': if typ == 'interval':
return datetime.utcnow() - ld > timedelta(sch) return utcnow - ld > timedelta(sch)
elif typ == 'day/time': elif typ == 'day/time':
day, hour, minute = sch
now = datetime.now() now = datetime.now()
offset = now - utcnow
ld_local = ld + offset
day, hour, minute = sch
is_today = day < 0 or day > 6 or \ is_today = day < 0 or day > 6 or \
day == calendar.weekday(now.year, now.month, now.day) day == calendar.weekday(now.year, now.month, now.day)
return is_today and datetime.utcnow().date() != ld.date() and \ is_time = now.hour > hour or \
now.hour >= hour and now.minute >= minute (now.hour == hour and now.minute >= minute)
was_downloaded_already_today = ld_local.date() == now.date()
return is_today and not was_downloaded_already_today and is_time
return False return False
def set_account_info(self, urn, un, pw): def set_account_info(self, urn, un, pw):

View File

@ -164,6 +164,8 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
return cls(*args) return cls(*args)
def ok(urn): def ok(urn):
if restrict_to_urns is None:
return False
return not restrict_to_urns or urn in restrict_to_urns return not restrict_to_urns or urn in restrict_to_urns
new_root = factory(NewsTreeItem, None) new_root = factory(NewsTreeItem, None)
@ -230,6 +232,8 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
def search(self, query, refinement): def search(self, query, refinement):
try: try:
results = self.parse(unicode(query)) results = self.parse(unicode(query))
if not results:
results = None
except ParseException: except ParseException:
results = [] results = []
self.do_refresh(restrict_to_urns=results) self.do_refresh(restrict_to_urns=results)