mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Kovid's intermediate fix for periodical Next Article
This commit is contained in:
commit
376869fd7b
BIN
resources/images/news/dn_se.png
Normal file
BIN
resources/images/news/dn_se.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 628 B |
BIN
resources/images/news/svd_se.png
Normal file
BIN
resources/images/news/svd_se.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
@ -21,9 +21,12 @@ class TheAtlantic(BasicNewsRecipe):
|
||||
dict(name='div', id=['seealso','storybottom', 'footer', 'ad_banner_top', 'sidebar','articletoolstop','subcontent',]),
|
||||
dict(name='p', attrs={'id':["pagination"]}),
|
||||
dict(name='table',attrs={'class':"tools"}),
|
||||
dict(name='style'),
|
||||
dict(name='a', href='/a/newsletters.mhtml')
|
||||
]
|
||||
remove_attributes = ['icap', 'callout', 'style']
|
||||
no_stylesheets = True
|
||||
conversion_options = { 'linearize_tables':True }
|
||||
|
||||
extra_css = '''
|
||||
#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'}):
|
||||
a = item.find('a')
|
||||
if a and a.has_key('href'):
|
||||
url = a['href']#.replace('/doc', 'doc/print')
|
||||
url = a['href']
|
||||
if not url.startswith('http://'):
|
||||
url = 'http://www.theatlantic.com/'+url
|
||||
url = url.replace('/doc/', '/doc/print/')
|
||||
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'})
|
||||
date = self.tag_to_string(byline) if byline else ''
|
||||
description = ''
|
||||
|
38
resources/recipes/corren2.recipe
Normal file
38
resources/recipes/corren2.recipe
Normal 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")
|
||||
|
49
resources/recipes/dn_se.recipe
Normal file
49
resources/recipes/dn_se.recipe
Normal 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'})
|
||||
]
|
||||
|
||||
|
||||
|
@ -18,7 +18,6 @@ class Economist(BasicNewsRecipe):
|
||||
__author__ = "Kovid Goyal"
|
||||
description = 'Global news and current affairs from a European perspective'
|
||||
oldest_article = 7.0
|
||||
needs_subscription = False # Strange but true
|
||||
INDEX = 'http://www.economist.com/printedition'
|
||||
cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg'
|
||||
remove_tags = [dict(name=['script', 'noscript', 'title'])]
|
||||
|
@ -10,7 +10,7 @@ from calibre.ebooks.BeautifulSoup import Tag
|
||||
|
||||
class HLN_be(BasicNewsRecipe):
|
||||
title = 'Het Belang Van Limburg'
|
||||
__author__ = 'Darko Miletic'
|
||||
__author__ = 'Darko Miletic and Sujata Raman'
|
||||
description = 'News from Belgium in Dutch'
|
||||
publisher = 'Het Belang Van Limburg'
|
||||
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} "'
|
||||
|
||||
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'})]
|
||||
remove_tags = [
|
||||
dict(name=['embed','object'])
|
||||
|
@ -5,13 +5,12 @@ __copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
laprensa.com.ar
|
||||
'''
|
||||
import urllib
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class LaPrensa(BasicNewsRecipe):
|
||||
title = 'La Prensa'
|
||||
__author__ = 'Darko Miletic'
|
||||
__author__ = 'Darko Miletic and Sujata Raman'
|
||||
description = 'Informacion Libre las 24 horas'
|
||||
publisher = 'La Prensa'
|
||||
category = 'news, politics, Argentina'
|
||||
@ -20,9 +19,11 @@ class LaPrensa(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
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
|
||||
|
||||
language = 'es'
|
||||
lang = 'es'
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--category', category
|
||||
@ -30,31 +31,72 @@ class LaPrensa(BasicNewsRecipe):
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
|
||||
filter_regexps = [r'.*archive.aspx.*']
|
||||
|
||||
remove_tags = [
|
||||
dict(name='td', attrs={'class':["link-registro","link-buscador"]}),
|
||||
dict(name='td', attrs={'id':["TDTabItem1","TDTabItem2","TDTabItem3","TDTabItem4"]}),
|
||||
dict(name='table', attrs={'class':["marco-botonera"]}),
|
||||
dict(name='tr', attrs={'class':["messages","IUTabItemSelected"]}),
|
||||
dict(name='input', attrs={'id':"txt_allfields"}),
|
||||
dict(name='div', attrs={'id':["TabItem1","TabItem2","TabItem3","TabItem4","RCPanel"]}),
|
||||
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")
|
||||
]
|
||||
|
||||
extra_css = '''
|
||||
.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/Rss.aspx?Rss=4' )
|
||||
,(u'Economia' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=5' )
|
||||
,(u'Opinion' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=6' )
|
||||
,(u'El Mundo' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=7' )
|
||||
,(u'Actualidad' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=8' )
|
||||
,(u'Deportes' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=9' )
|
||||
,(u'Espectaculos', u'http://www.laprensa.com.ar/Rss.aspx?Rss=10')
|
||||
(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 print_version(self, url):
|
||||
return url.replace('.note.aspx','.NotePrint.note.aspx')
|
||||
|
||||
def get_article_url(self, article):
|
||||
raw = article.get('link', None).encode('utf8')
|
||||
final = urllib.quote(raw,':/')
|
||||
return final
|
||||
|
||||
|
||||
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"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
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
|
||||
|
||||
language = 'es'
|
||||
|
||||
|
||||
|
||||
|
53
resources/recipes/svd_se.recipe
Normal file
53
resources/recipes/svd_se.recipe
Normal 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'})
|
||||
]
|
||||
|
||||
|
||||
|
@ -17,18 +17,36 @@ class Time(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
language = 'en'
|
||||
|
||||
extra_css = '''.headline {font-size: large;}
|
||||
.fact { padding-top: 10pt }
|
||||
h1 {font-family:Arial,Sans-serif}
|
||||
.byline{font-family:Arial,Sans-serif; font-size:xx-small ;color:blue}
|
||||
.timestamp{font-family:Arial,Sans-serif; font-size:x-small ;color:gray}'''
|
||||
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'},
|
||||
]
|
||||
extra_css = ''' h1 {font-family:Arial,Sans-serif;}
|
||||
h2 {font-family:Arial,Sans-serif;}
|
||||
.name{font-family:Arial,Sans-serif; font-size:x-small; }
|
||||
.date{font-family:Arial,Sans-serif; font-size:x-small ;color:#999999;}
|
||||
.byline{font-family:Arial,Sans-serif; font-size:x-small ;}
|
||||
.photoBkt{ font-size:x-small ;}
|
||||
.vertPhoto{font-size:x-small ;}
|
||||
.credits{font-family:Arial,Sans-serif; font-size:x-small ;color:gray;}
|
||||
.artTxt{font-family:georgia,serif;}
|
||||
#article{font-family:georgia,serif;}
|
||||
.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
|
||||
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:
|
||||
ans.append(unicode(t))
|
||||
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
|
||||
|
@ -15,7 +15,7 @@ class ZeitDe(BasicNewsRecipe):
|
||||
language = 'de'
|
||||
lang = 'de_DE'
|
||||
|
||||
__author__ = 'Martin Pitt and Suajta Raman'
|
||||
__author__ = 'Martin Pitt and Sujata Raman'
|
||||
use_embedded_content = False
|
||||
max_articles_per_feed = 40
|
||||
remove_empty_feeds = True
|
||||
@ -41,7 +41,8 @@ class ZeitDe(BasicNewsRecipe):
|
||||
.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}
|
||||
'''
|
||||
filter_regexps = [r'ad.de.doubleclick.net/']
|
||||
#filter_regexps = [r'ad.de.doubleclick.net/']
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':["article"]}) ,
|
||||
]
|
||||
@ -51,15 +52,32 @@ class ZeitDe(BasicNewsRecipe):
|
||||
dict(name='div', attrs={'id':["place_5","place_4"]})
|
||||
]
|
||||
|
||||
|
||||
|
||||
def get_article_url(self, article):
|
||||
|
||||
ans = article.get('guid',None)
|
||||
|
||||
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 :
|
||||
|
||||
url = article.get('guid', None)
|
||||
|
||||
if 'video' in url or 'quiz' in url :
|
||||
|
||||
url = None
|
||||
|
||||
return url
|
||||
ans = None
|
||||
return ans
|
||||
|
||||
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
soup.html['xml:lang'] = self.lang
|
||||
@ -69,6 +87,7 @@ class ZeitDe(BasicNewsRecipe):
|
||||
|
||||
return soup
|
||||
|
||||
|
||||
#def print_version(self,url):
|
||||
# return url.replace('http://www.zeit.de/', 'http://images.zeit.de/text/').replace('?from=rss', '')
|
||||
|
||||
|
@ -21,7 +21,7 @@ __all__ = [
|
||||
'linux32', 'linux64', 'linux', 'linux_freeze', 'linux_freeze2',
|
||||
'osx32_freeze', 'osx32', 'osx', 'rsync',
|
||||
'win32_freeze', 'win32', 'win',
|
||||
'stage1', 'stage2', 'stage3', 'publish'
|
||||
'stage1', 'stage2', 'stage3', 'stage4', 'publish'
|
||||
]
|
||||
|
||||
|
||||
@ -50,13 +50,14 @@ from setup.resources import Resources
|
||||
resources = Resources()
|
||||
|
||||
from setup.publish import Manual, TagRelease, UploadRss, Stage1, Stage2, \
|
||||
Stage3, Publish
|
||||
Stage3, Stage4, Publish
|
||||
manual = Manual()
|
||||
tag_release = TagRelease()
|
||||
upload_rss = UploadRss()
|
||||
stage1 = Stage1()
|
||||
stage2 = Stage2()
|
||||
stage3 = Stage3()
|
||||
stage4 = Stage4()
|
||||
publish = Publish()
|
||||
|
||||
from setup.upload import UploadUserManual, UploadInstallers, UploadDemo, \
|
||||
|
@ -6,7 +6,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__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, \
|
||||
__appname__, __version__
|
||||
@ -149,7 +149,9 @@ class Develop(Command):
|
||||
src = os.path.join(self.SRC, 'calibre', 'devices', 'linux_mount_helper.c')
|
||||
dest = os.path.join(self.staging_bindir, 'calibre-mount-helper')
|
||||
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()
|
||||
if ret != 0:
|
||||
return warn()
|
||||
|
@ -24,7 +24,8 @@ class LinuxFreeze(Command):
|
||||
is64bit = platform.architecture()[0] == '64bit'
|
||||
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'
|
||||
|
||||
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'
|
||||
QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml',
|
||||
@ -46,6 +47,7 @@ class LinuxFreeze(Command):
|
||||
'/usr/lib/libmng.so.1',
|
||||
'/usr/lib/libpodofo.so.0.6.99',
|
||||
'/lib/libz.so.1',
|
||||
'/lib/libuuid.so.1',
|
||||
'/usr/lib/libtiff.so.3',
|
||||
'/lib/libbz2.so.1',
|
||||
'/usr/lib/libpoppler.so.5',
|
||||
@ -56,8 +58,7 @@ class LinuxFreeze(Command):
|
||||
'/usr/lib/libjpeg.so.7',
|
||||
'/usr/lib/libxslt.so.1',
|
||||
'/usr/lib/libgthread-2.0.so.0',
|
||||
'/usr/lib/gcc/***-pc-linux-gnu/4.4.1/libstdc++.so.6'.replace('***',
|
||||
arch),
|
||||
stdcpp,
|
||||
ffi,
|
||||
'/usr/lib/libpng12.so.0',
|
||||
'/usr/lib/libexslt.so.0',
|
||||
|
@ -6,7 +6,13 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import sys, re, os, shutil, subprocess, stat, glob, zipfile, plistlib
|
||||
from setup import __version__ as VERSION, __appname__ as APPNAME, SRC, Command, \
|
||||
scripts, basenames, functions as main_functions, modules as main_modules
|
||||
from setuptools import setup
|
||||
|
||||
try:
|
||||
from setuptools import setup
|
||||
except:
|
||||
setup
|
||||
class setup:
|
||||
pass
|
||||
|
||||
try:
|
||||
from py2app.build_app import py2app
|
||||
|
@ -27,7 +27,7 @@
|
||||
<CustomAction Id="PreventDowngrading" Error="Newer version already installed."/>
|
||||
|
||||
<Directory Id='TARGETDIR' Name='SourceDir'>
|
||||
<Merge Id="VCRedist" SourceFile="{crt_msm}" DiskId="1" Language="0"/>
|
||||
<Merge Id="VCRedist" SourceFile="{crt_msm}" DiskId="1" Language="0"/>
|
||||
<Directory Id='ProgramFilesFolder' Name='PFiles'>
|
||||
<Directory Id='APPLICATIONFOLDER' Name='{app}' />
|
||||
</Directory>
|
||||
@ -120,7 +120,7 @@
|
||||
</Condition>
|
||||
<InstallExecuteSequence>
|
||||
<Custom Action="PreventDowngrading" After="FindRelatedProducts">NEWPRODUCTFOUND</Custom>
|
||||
<RemoveExistingProducts Before="InstallInitialize" />
|
||||
<RemoveExistingProducts After="InstallFinalize" />
|
||||
</InstallExecuteSequence>
|
||||
<InstallUISequence>
|
||||
<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
|
||||
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" />
|
||||
<WixVariable Id="WixUISupportPerUser" Value="0" />
|
||||
|
||||
|
@ -46,12 +46,19 @@ class Stage3(Command):
|
||||
sub_commands = ['upload_rss', 'upload_user_manual', 'upload_demo', 'sdist',
|
||||
'upload_to_sourceforge', 'upload_to_google_code', 'tag_release',
|
||||
'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):
|
||||
|
||||
description = 'Publish a new calibre release'
|
||||
sub_commands = ['stage1', 'stage2', 'stage3']
|
||||
sub_commands = ['stage1', 'stage2', 'stage3', 'stage4']
|
||||
|
||||
class Manual(Command):
|
||||
|
||||
|
@ -345,7 +345,6 @@ class UploadToServer(Command):
|
||||
shell=True)
|
||||
check_call('scp dist/calibre-*.tar.gz.asc divok:%s/signatures/'%DOWNLOADS,
|
||||
shell=True)
|
||||
check_call('''rm -rf dist/* build/*''', shell=True)
|
||||
check_call('ssh divok bzr update /var/www/calibre.kovidgoyal.net/calibre/',
|
||||
shell=True)
|
||||
check_call('ssh divok bzr update /usr/local/calibre',
|
||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.6.18'
|
||||
__version__ = '0.6.19'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
import re
|
||||
|
@ -352,6 +352,7 @@ from calibre.ebooks.pdf.output import PDFOutput
|
||||
from calibre.ebooks.pml.output import PMLOutput
|
||||
from calibre.ebooks.rb.output import RBOutput
|
||||
from calibre.ebooks.rtf.output import RTFOutput
|
||||
from calibre.ebooks.tcr.output import TCROutput
|
||||
from calibre.ebooks.txt.output import TXTOutput
|
||||
|
||||
from calibre.customize.profiles import input_profiles, output_profiles
|
||||
@ -402,6 +403,7 @@ plugins += [
|
||||
PMLOutput,
|
||||
RBOutput,
|
||||
RTFOutput,
|
||||
TCROutput,
|
||||
TXTOutput,
|
||||
]
|
||||
plugins += [
|
||||
|
@ -165,7 +165,7 @@ def main(args=sys.argv):
|
||||
sys.argv = args[:1]
|
||||
exec opts.command
|
||||
elif opts.exec_file:
|
||||
sys.argv = args[:1]
|
||||
sys.argv = args
|
||||
base = os.path.dirname(os.path.abspath(opts.exec_file))
|
||||
sys.path.insert(0, base)
|
||||
g = globals()
|
||||
|
@ -36,4 +36,12 @@ class ESLICK(USBMS):
|
||||
|
||||
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
|
||||
|
||||
|
126
src/calibre/ebooks/compression/tcr.py
Normal file
126
src/calibre/ebooks/compression/tcr.py
Normal 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
|
@ -1316,9 +1316,9 @@ class HTMLConverter(object):
|
||||
elif m.group(2) == 'pc':
|
||||
result = unit * (dpi/72.) * 12
|
||||
elif m.group(2) == 'mm':
|
||||
result = unit * 0.04 * (dpi/72.)
|
||||
result = unit * 0.04 * (dpi)
|
||||
elif m.group(2) == 'cm':
|
||||
result = unit * 0.40 * (dpi/72.)
|
||||
result = unit * 0.4 * (dpi)
|
||||
if result is not None:
|
||||
if pts:
|
||||
result = int(round(result * (720./dpi)))
|
||||
|
@ -270,6 +270,7 @@ class Spine(ResourceCollection):
|
||||
Resource.__init__(self, *args, **kwargs)
|
||||
self.is_linear = True
|
||||
self.id = idfunc(self.path)
|
||||
self.idref = None
|
||||
|
||||
@staticmethod
|
||||
def from_opf_spine_element(itemrefs, manifest):
|
||||
@ -281,6 +282,7 @@ class Spine(ResourceCollection):
|
||||
if path:
|
||||
r = Spine.Item(s.manifest.id_for_path, path, is_path=True)
|
||||
r.is_linear = itemref.get('linear', 'yes') == 'yes'
|
||||
r.idref = idref
|
||||
s.append(r)
|
||||
return s
|
||||
|
||||
|
@ -188,7 +188,7 @@ class MobiMLizer(object):
|
||||
bstate.vpadding = bstate.vmargin = 0
|
||||
if tag not in TABLE_TAGS:
|
||||
wrapper.attrib['height'] = self.mobimlize_measure(vspace)
|
||||
para.attrib['width'] = self.mobimlize_measure(indent)
|
||||
para.attrib['width'] = self.mobimlize_measure(indent)
|
||||
elif tag == 'table' and vspace > 0:
|
||||
vspace = int(round(vspace / self.profile.fbase))
|
||||
while vspace > 0:
|
||||
|
@ -489,6 +489,9 @@ class MobiReader(object):
|
||||
mobi_version = self.book_header.mobi_version
|
||||
for i, tag in enumerate(root.iter(etree.Element)):
|
||||
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',
|
||||
'state', 'city', 'street', 'address', 'content'):
|
||||
tag.tag = 'div' if tag.tag == 'content' else 'span'
|
||||
|
@ -19,6 +19,7 @@ from calibre.utils.zipfile import safe_replace, ZipFile
|
||||
from calibre.utils.config import DynamicConfig
|
||||
from calibre.utils.logging import Log
|
||||
from calibre.ebooks.epub.output import EPUBOutput
|
||||
from calibre import guess_type
|
||||
|
||||
TITLEPAGE = EPUBOutput.TITLEPAGE_COVER.decode('utf-8')
|
||||
|
||||
@ -39,20 +40,20 @@ class UnsupportedFormatError(Exception):
|
||||
|
||||
class SpineItem(unicode):
|
||||
|
||||
def __new__(cls, *args):
|
||||
args = list(args)
|
||||
path = args[0]
|
||||
def __new__(cls, path, mime_type=None):
|
||||
ppath = path.partition('#')[0]
|
||||
if not os.path.exists(path) and os.path.exists(ppath):
|
||||
path = ppath
|
||||
args[0] = path
|
||||
obj = super(SpineItem, cls).__new__(cls, *args)
|
||||
obj = super(SpineItem, cls).__new__(cls, path)
|
||||
raw = open(path, 'rb').read()
|
||||
raw, obj.encoding = xml_to_unicode(raw)
|
||||
obj.character_count = character_count(raw)
|
||||
obj.start_page = -1
|
||||
obj.pages = -1
|
||||
obj.max_page = -1
|
||||
if mime_type is None:
|
||||
mime_type = guess_type(obj)[0]
|
||||
obj.mime_type = mime_type
|
||||
return obj
|
||||
|
||||
class FakeOpts(object):
|
||||
@ -150,8 +151,17 @@ class EbookIterator(object):
|
||||
self.language = self.opf.language
|
||||
if self.language:
|
||||
self.language = self.language.lower()
|
||||
self.spine = [SpineItem(i.path) 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]
|
||||
ordered = [i for i in self.opf.spine if 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
|
||||
if self.ebook_ext in ('lit', 'mobi', 'prc', 'opf') and cover:
|
||||
|
@ -188,6 +188,16 @@ class Stylizer(object):
|
||||
%(text, item.href))
|
||||
for elem in matches:
|
||||
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]'):
|
||||
self.style(elem)._apply_style_attr()
|
||||
|
||||
|
@ -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>.+?)"'), lambda match: '<span id="%s"></span>' % match.group('target')),
|
||||
(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'\\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'\\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="#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.
|
||||
(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
|
||||
(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'<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'<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="fns-%s">%s</div>' % (match.group('target'), match.group('text')) if match.group('text') else ''),
|
||||
|
||||
# eReader files are one paragraph per line.
|
||||
# This forces the lines to wrap properly.
|
||||
@ -80,5 +80,5 @@ def pml_to_html(pml):
|
||||
def footnote_sidebar_to_html(id, pml):
|
||||
if id.startswith('\x01'):
|
||||
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
|
||||
|
@ -72,7 +72,7 @@ class Tokenize:
|
||||
return line
|
||||
def __compile_expressions(self):
|
||||
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.__par_exp = re.compile(r'\\$')
|
||||
self.__mixed_exp = re.compile(r"(\\[a-zA-Z]+\d+)(\D+)")
|
||||
|
@ -9,6 +9,7 @@ import os
|
||||
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
|
||||
from calibre.ebooks.txt.processor import convert_basic, opf_writer, \
|
||||
separate_paragraphs_single_line, separate_paragraphs_print_formatted
|
||||
from calibre.ebooks.compression.tcr import decompress
|
||||
|
||||
class TCRInput(InputFormatPlugin):
|
||||
|
||||
@ -31,28 +32,9 @@ class TCRInput(InputFormatPlugin):
|
||||
])
|
||||
|
||||
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...')
|
||||
# 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'
|
||||
txt = ''.join(txt).decode(ienc, 'replace')
|
||||
txt = decompress(stream).decode(ienc, 'replace')
|
||||
|
||||
log.info('Converting text to OEB...')
|
||||
if options.single_line_paras:
|
||||
|
58
src/calibre/ebooks/tcr/output.py
Normal file
58
src/calibre/ebooks/tcr/output.py
Normal 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()
|
@ -453,6 +453,12 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
self.delete_news.setEnabled)
|
||||
self.setup_conversion_options()
|
||||
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):
|
||||
from calibre.utils.osx_symlinks import create_symlinks
|
||||
|
@ -616,13 +616,20 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="button_osx_symlinks">
|
||||
<property name="text">
|
||||
<string>&Install command line tools</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="button_open_config_dir">
|
||||
<property name="text">
|
||||
<string>Open calibre &configuration directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_4">
|
||||
|
@ -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.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.ptempfile import PersistentTemporaryFile
|
||||
|
||||
@ -36,6 +36,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
self.connect(self.recipe_model, SIGNAL('searched(PyQt_PyObject)'),
|
||||
self.search_done)
|
||||
self.search.setFocus(Qt.OtherFocusReason)
|
||||
self.commit_on_change = True
|
||||
|
||||
self.recipes.setModel(self.recipe_model)
|
||||
self.detail_box.setVisible(False)
|
||||
@ -55,6 +56,10 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
self.old_news.setValue(gconf['oldest_news'])
|
||||
|
||||
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
|
||||
|
||||
def search_done(self, *args):
|
||||
@ -68,25 +73,32 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
self.last_downloaded.setVisible(enabled)
|
||||
|
||||
def current_changed(self, current, previous):
|
||||
if previous.isValid():
|
||||
self.commit(urn=getattr(previous.internalPointer(), 'urn', None))
|
||||
if self.commit_on_change:
|
||||
if previous.isValid():
|
||||
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
|
||||
if urn is not None:
|
||||
self.initialize_detail_box(urn)
|
||||
|
||||
def accept(self):
|
||||
self.commit()
|
||||
if not self.commit():
|
||||
return False
|
||||
return QDialog.accept(self)
|
||||
|
||||
def download_clicked(self):
|
||||
self.commit()
|
||||
if self.current_urn:
|
||||
if self.commit() and self.current_urn:
|
||||
self.emit(SIGNAL('download(PyQt_PyObject)'), self.current_urn)
|
||||
|
||||
def download_all_clicked(self):
|
||||
self.commit()
|
||||
self.emit(SIGNAL('download(PyQt_PyObject)'), None)
|
||||
if self.commit() and self.commit():
|
||||
self.emit(SIGNAL('download(PyQt_PyObject)'), None)
|
||||
|
||||
@property
|
||||
def current_urn(self):
|
||||
@ -97,10 +109,15 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
def commit(self, urn=None):
|
||||
urn = self.current_urn if urn is None else urn
|
||||
if not self.detail_box.isVisible() or urn is None:
|
||||
return
|
||||
return True
|
||||
|
||||
if self.account.isVisible():
|
||||
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())
|
||||
|
||||
if self.schedule.isChecked():
|
||||
@ -122,6 +139,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
|
||||
custom_tags = unicode(self.custom_tags.text()).strip()
|
||||
custom_tags = [x.strip() for x in custom_tags.split(',')]
|
||||
self.recipe_model.customize_recipe(urn, add_title_tag, custom_tags)
|
||||
return True
|
||||
|
||||
def initialize_detail_box(self, urn):
|
||||
self.detail_box.setVisible(True)
|
||||
|
@ -1621,12 +1621,13 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
try:
|
||||
os.makedirs(self.library_path)
|
||||
except:
|
||||
self.library_path = os.path.expanduser('~/Library')
|
||||
self.library_path = os.path.expanduser('~/CalibreLibrary')
|
||||
error_dialog(self, _('Invalid library location'),
|
||||
_('Could not access %s. Using %s as the library.')%
|
||||
(repr(self.library_path), repr(self.library_path))
|
||||
).exec_()
|
||||
os.makedirs(self.library_path)
|
||||
if not os.path.exists(self.library_path):
|
||||
os.makedirs(self.library_path)
|
||||
|
||||
|
||||
def read_settings(self):
|
||||
|
@ -6,7 +6,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__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
|
||||
|
||||
@ -25,6 +25,17 @@ class SearchLineEdit(QLineEdit):
|
||||
self.parent().normalize_state()
|
||||
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):
|
||||
|
||||
'''
|
||||
|
@ -18,7 +18,7 @@ from calibre.gui2.viewer.config_ui import Ui_Dialog
|
||||
from calibre.gui2.viewer.js import bookmarks, referencing, hyphenation
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.constants import iswindows
|
||||
from calibre import prints
|
||||
from calibre import prints, guess_type
|
||||
|
||||
def load_builtin_fonts():
|
||||
base = P('fonts/liberation/*.ttf')
|
||||
@ -352,6 +352,8 @@ class DocumentView(QWebView):
|
||||
def __init__(self, *args):
|
||||
QWidget.__init__(self, *args)
|
||||
self.debug_javascript = False
|
||||
self.self_closing_pat = re.compile(r'<([a-z]+)\s+([^>]+)/>',
|
||||
re.IGNORECASE)
|
||||
self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
|
||||
self._size_hint = QSize(510, 680)
|
||||
self.initial_pos = 0.0
|
||||
@ -447,8 +449,14 @@ class DocumentView(QWebView):
|
||||
|
||||
def load_path(self, path, pos=0.0):
|
||||
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 = 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.turn_off_internal_scrollbars()
|
||||
|
||||
|
@ -6,7 +6,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import sys, os, shutil, cPickle, textwrap, stat
|
||||
from subprocess import check_call
|
||||
|
||||
from calibre import __version__, __appname__, prints
|
||||
from calibre import __appname__, prints
|
||||
|
||||
|
||||
entry_points = {
|
||||
@ -140,8 +140,8 @@ class PostInstall:
|
||||
for f in os.listdir('.'):
|
||||
if os.stat(f).st_uid == 0:
|
||||
os.rmdir(f) if os.path.isdir(f) else os.unlink(f)
|
||||
if os.stat(config_dir).st_uid == 0:
|
||||
os.rmdir(config_dir)
|
||||
if os.stat(config_dir).st_uid == 0:
|
||||
os.rmdir(config_dir)
|
||||
|
||||
if warn is None and self.warnings:
|
||||
self.info('There were %d warnings'%len(self.warnings))
|
||||
@ -491,36 +491,36 @@ complete -o filenames -F _'''%(opts,exts) + name + ' ' + name +"\n\n"
|
||||
|
||||
VIEWER = '''\
|
||||
[Desktop Entry]
|
||||
Version=%s
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=LRF Viewer
|
||||
GenericName=Viewer for LRF files
|
||||
Comment=Viewer for LRF files (SONY ebook format files)
|
||||
TryExec=lrfviewer
|
||||
Exec=lrfviewer %%F
|
||||
Exec=lrfviewer %F
|
||||
Icon=calibre-viewer
|
||||
MimeType=application/x-sony-bbeb;
|
||||
Categories=Graphics;Viewer;
|
||||
'''%(__version__,)
|
||||
'''
|
||||
|
||||
EVIEWER = '''\
|
||||
[Desktop Entry]
|
||||
Version=%s
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=E-book Viewer
|
||||
GenericName=Viewer for E-books
|
||||
Comment=Viewer for E-books
|
||||
TryExec=ebook-viewer
|
||||
Exec=ebook-viewer %%F
|
||||
Exec=ebook-viewer %F
|
||||
Icon=calibre-viewer
|
||||
MimeType=application/epub+zip;
|
||||
Categories=Graphics;Viewer;
|
||||
'''%(__version__,)
|
||||
'''
|
||||
|
||||
|
||||
GUI = '''\
|
||||
[Desktop Entry]
|
||||
Version=%s
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=calibre
|
||||
GenericName=E-book library management
|
||||
@ -529,7 +529,7 @@ TryExec=calibre
|
||||
Exec=calibre
|
||||
Icon=calibre-gui
|
||||
Categories=Office;
|
||||
'''%(__version__,)
|
||||
'''
|
||||
|
||||
MIME = '''\
|
||||
<?xml version="1.0"?>
|
||||
|
@ -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.
|
||||
|
||||
*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
|
||||
|
||||
@ -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.
|
||||
|
||||
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'"?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,9 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: calibre 0.6.18\n"
|
||||
"POT-Creation-Date: 2009-10-17 10:21+MDT\n"
|
||||
"PO-Revision-Date: 2009-10-17 10:21+MDT\n"
|
||||
"Project-Id-Version: calibre 0.6.19\n"
|
||||
"POT-Creation-Date: 2009-10-20 18:49+MDT\n"
|
||||
"PO-Revision-Date: 2009-10-20 18:49+MDT\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: LANGUAGE\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/opf.py:329
|
||||
#: /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/pdf.py:21
|
||||
#: /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:121
|
||||
#: /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:775
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:591
|
||||
#: /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:51
|
||||
#: /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:139
|
||||
#: /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:404
|
||||
#: /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 ""
|
||||
|
||||
#: /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/library/database2.py:1006
|
||||
#: /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"
|
||||
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
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
@ -1560,7 +1560,7 @@ msgstr ""
|
||||
msgid "Usage: ebook-convert INFILE OUTFILE [OPTIONS..]"
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
@ -1601,13 +1601,13 @@ msgid "Sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: /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
|
||||
msgid "Normally calibre treats blank lines as paragraph markers. With this option it will assume that every line represents a paragraph instead."
|
||||
msgstr ""
|
||||
|
||||
#: /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
|
||||
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 ""
|
||||
@ -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."
|
||||
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
|
||||
msgid "Run the text input through the markdown pre-processor. To learn more about markdown see"
|
||||
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/device_drivers/configwidget_ui.py:61
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:62
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:471
|
||||
#: /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:484
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:500
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:501
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:533
|
||||
#: /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:486
|
||||
#: /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:503
|
||||
#: /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: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:361
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:375
|
||||
@ -3206,126 +3214,126 @@ msgstr ""
|
||||
msgid "new email address"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:461
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:791
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:467
|
||||
#: /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:1045
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53
|
||||
msgid "Error"
|
||||
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."
|
||||
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"
|
||||
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"
|
||||
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."
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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."
|
||||
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:"
|
||||
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:"
|
||||
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
|
||||
msgid "Failed to start content server"
|
||||
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
|
||||
msgid "Select location for books"
|
||||
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"
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:732
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:737
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:738
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:743
|
||||
msgid "Invalid database location"
|
||||
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 "
|
||||
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."
|
||||
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 "
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
@ -3409,256 +3417,260 @@ msgstr ""
|
||||
msgid "&Saving books"
|
||||
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/viewer/main_ui.py:173
|
||||
msgid "Preferences"
|
||||
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)"
|
||||
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"
|
||||
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"
|
||||
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:"
|
||||
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)"
|
||||
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"
|
||||
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):"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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:"
|
||||
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:"
|
||||
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:"
|
||||
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"
|
||||
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)"
|
||||
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 ¬ifications in system tray"
|
||||
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)"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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):"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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:"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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):"
|
||||
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"
|
||||
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"
|
||||
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."
|
||||
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:"
|
||||
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/scheduler_ui.py:210
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:117
|
||||
msgid "&Username:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/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/scheduler_ui.py:211
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:119
|
||||
msgid "&Password:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/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."
|
||||
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. "
|
||||
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:"
|
||||
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/scheduler_ui.py:212
|
||||
msgid "&Show password"
|
||||
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:"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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
|
||||
msgid ""
|
||||
"<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."
|
||||
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."
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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:"
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
@ -3990,28 +4002,36 @@ msgstr ""
|
||||
msgid "Aborting..."
|
||||
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: "
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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
|
||||
msgid "Schedule news download"
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
@ -4993,87 +5013,87 @@ msgstr ""
|
||||
msgid "Could not access %s. Using %s as the library."
|
||||
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."
|
||||
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?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1700
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1701
|
||||
msgid ""
|
||||
" is communicating with the device!<br>\n"
|
||||
" Quitting may cause corruption on the device.<br>\n"
|
||||
" Are you sure you want to quit?"
|
||||
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"
|
||||
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."
|
||||
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>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1782
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1783
|
||||
msgid "Update available"
|
||||
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?"
|
||||
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."
|
||||
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."
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1857
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1858
|
||||
msgid "Cannot Start "
|
||||
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."
|
||||
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"
|
||||
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."
|
||||
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."
|
||||
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."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1870
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1882
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1871
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1883
|
||||
msgid "try deleting the file"
|
||||
msgstr ""
|
||||
|
||||
@ -5223,7 +5243,7 @@ msgstr ""
|
||||
msgid "No matches found for this book"
|
||||
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"
|
||||
msgstr ""
|
||||
|
||||
@ -6421,71 +6441,75 @@ msgstr ""
|
||||
msgid "Unknown News Source"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:588
|
||||
msgid "Download finished"
|
||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:507
|
||||
msgid "The \"%s\" recipe needs a username and password."
|
||||
msgstr ""
|
||||
|
||||
#: /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:"
|
||||
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:"
|
||||
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 "
|
||||
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:"
|
||||
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"
|
||||
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..."
|
||||
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"
|
||||
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..."
|
||||
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)]..."
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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: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"
|
||||
msgstr ""
|
||||
|
||||
#: /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"
|
||||
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
@ -19,7 +19,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
'''
|
||||
|
||||
'''0.12 update - allow selection of binding interface
|
||||
@ -101,7 +101,7 @@ _LISTENER_TIME = 200
|
||||
_BROWSER_TIME = 500
|
||||
|
||||
# Some DNS constants
|
||||
|
||||
|
||||
_MDNS_ADDR = '224.0.0.251'
|
||||
_MDNS_PORT = 5353;
|
||||
_DNS_PORT = 53;
|
||||
@ -208,7 +208,7 @@ class BadTypeInNameException(Exception):
|
||||
|
||||
class DNSEntry(object):
|
||||
'''A DNS entry'''
|
||||
|
||||
|
||||
def __init__(self, name, type, clazz):
|
||||
self.key = string.lower(name)
|
||||
self.name = name
|
||||
@ -256,10 +256,10 @@ class DNSEntry(object):
|
||||
|
||||
class DNSQuestion(DNSEntry):
|
||||
'''A DNS question entry'''
|
||||
|
||||
|
||||
def __init__(self, name, type, clazz):
|
||||
if not name.endswith('.local.'):
|
||||
raise NonLocalNameException
|
||||
raise NonLocalNameException('DNSQuestion: Not a local name '+name)
|
||||
DNSEntry.__init__(self, name, type, clazz)
|
||||
|
||||
def answeredBy(self, rec):
|
||||
@ -273,7 +273,7 @@ class DNSQuestion(DNSEntry):
|
||||
|
||||
class DNSRecord(DNSEntry):
|
||||
'''A DNS record - like a DNS entry, but has a TTL'''
|
||||
|
||||
|
||||
def __init__(self, name, type, clazz, ttl):
|
||||
DNSEntry.__init__(self, name, type, clazz)
|
||||
self.ttl = ttl
|
||||
@ -334,7 +334,7 @@ class DNSRecord(DNSEntry):
|
||||
|
||||
class DNSAddress(DNSRecord):
|
||||
'''A DNS address record'''
|
||||
|
||||
|
||||
def __init__(self, name, type, clazz, ttl, address):
|
||||
DNSRecord.__init__(self, name, type, clazz, ttl)
|
||||
self.address = address
|
||||
@ -378,10 +378,10 @@ class DNSHinfo(DNSRecord):
|
||||
def __repr__(self):
|
||||
'''String representation'''
|
||||
return self.cpu + ' ' + self.os
|
||||
|
||||
|
||||
class DNSPointer(DNSRecord):
|
||||
'''A DNS pointer record'''
|
||||
|
||||
|
||||
def __init__(self, name, type, clazz, ttl, alias):
|
||||
DNSRecord.__init__(self, name, type, clazz, ttl)
|
||||
self.alias = alias
|
||||
@ -402,7 +402,7 @@ class DNSPointer(DNSRecord):
|
||||
|
||||
class DNSText(DNSRecord):
|
||||
'''A DNS text record'''
|
||||
|
||||
|
||||
def __init__(self, name, type, clazz, ttl, text):
|
||||
DNSRecord.__init__(self, name, type, clazz, ttl)
|
||||
self.text = text
|
||||
@ -426,7 +426,7 @@ class DNSText(DNSRecord):
|
||||
|
||||
class DNSService(DNSRecord):
|
||||
'''A DNS service record'''
|
||||
|
||||
|
||||
def __init__(self, name, type, clazz, ttl, priority, weight, port, server):
|
||||
DNSRecord.__init__(self, name, type, clazz, ttl)
|
||||
self.priority = priority
|
||||
@ -453,7 +453,7 @@ class DNSService(DNSRecord):
|
||||
|
||||
class DNSIncoming(object):
|
||||
'''Object representation of an incoming DNS packet'''
|
||||
|
||||
|
||||
def __init__(self, data):
|
||||
'''Constructor from string holding bytes of packet'''
|
||||
self.offset = 0
|
||||
@ -464,7 +464,7 @@ class DNSIncoming(object):
|
||||
self.numAnswers = 0
|
||||
self.numAuthorities = 0
|
||||
self.numAdditionals = 0
|
||||
|
||||
|
||||
self.readHeader()
|
||||
self.readQuestions()
|
||||
self.readOthers()
|
||||
@ -491,7 +491,7 @@ class DNSIncoming(object):
|
||||
name = self.readName()
|
||||
info = struct.unpack(format, self.data[self.offset:self.offset+length])
|
||||
self.offset += length
|
||||
|
||||
|
||||
question = DNSQuestion(name, info[0], info[1])
|
||||
self.questions.append(question)
|
||||
|
||||
@ -561,7 +561,7 @@ class DNSIncoming(object):
|
||||
|
||||
if rec is not None:
|
||||
self.answers.append(rec)
|
||||
|
||||
|
||||
def isQuery(self):
|
||||
'''Returns true if this is a query'''
|
||||
return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY
|
||||
@ -574,7 +574,7 @@ class DNSIncoming(object):
|
||||
'''Reads a UTF-8 string of a given length from the packet'''
|
||||
result = self.data[offset:offset+len].decode('utf-8')
|
||||
return result
|
||||
|
||||
|
||||
def readName(self):
|
||||
'''Reads a domain name from the packet'''
|
||||
result = ''
|
||||
@ -607,11 +607,11 @@ class DNSIncoming(object):
|
||||
self.offset = off
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
class DNSOutgoing(object):
|
||||
'''Object representation of an outgoing packet'''
|
||||
|
||||
|
||||
def __init__(self, flags, multicast = 1):
|
||||
self.finished = 0
|
||||
self.id = 0
|
||||
@ -620,7 +620,7 @@ class DNSOutgoing(object):
|
||||
self.names = {}
|
||||
self.data = []
|
||||
self.size = 12
|
||||
|
||||
|
||||
self.questions = []
|
||||
self.answers = []
|
||||
self.authorities = []
|
||||
@ -660,7 +660,7 @@ class DNSOutgoing(object):
|
||||
format = '!H'
|
||||
self.data.insert(index, struct.pack(format, value))
|
||||
self.size += 2
|
||||
|
||||
|
||||
def writeShort(self, value):
|
||||
'''Writes an unsigned short to the packet'''
|
||||
format = '!H'
|
||||
@ -739,7 +739,7 @@ class DNSOutgoing(object):
|
||||
self.size += 2
|
||||
record.write(self)
|
||||
self.size -= 2
|
||||
|
||||
|
||||
length = len(''.join(self.data[index:]))
|
||||
self.insertShort(index, length) # Here is the short we adjusted for
|
||||
|
||||
@ -758,7 +758,7 @@ class DNSOutgoing(object):
|
||||
self.writeRecord(authority, 0)
|
||||
for additional in self.additionals:
|
||||
self.writeRecord(additional, 0)
|
||||
|
||||
|
||||
self.insertShort(0, len(self.additionals))
|
||||
self.insertShort(0, len(self.authorities))
|
||||
self.insertShort(0, len(self.answers))
|
||||
@ -773,7 +773,7 @@ class DNSOutgoing(object):
|
||||
|
||||
class DNSCache(object):
|
||||
'''A cache of DNS entries'''
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.cache = {}
|
||||
|
||||
@ -856,11 +856,17 @@ class Engine(threading.Thread):
|
||||
self.condition.wait(self.timeout)
|
||||
self.condition.release()
|
||||
else:
|
||||
from calibre.constants import DEBUG
|
||||
try:
|
||||
rr, wr, er = select.select(rs, [], [], self.timeout)
|
||||
for socket in rr:
|
||||
try:
|
||||
self.readers[socket].handle_read()
|
||||
except NonLocalNameException, err:
|
||||
print err
|
||||
except UnicodeDecodeError:
|
||||
if DEBUG:
|
||||
traceback.print_exc()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
except:
|
||||
@ -872,7 +878,7 @@ class Engine(threading.Thread):
|
||||
result = self.readers.keys()
|
||||
self.condition.release()
|
||||
return result
|
||||
|
||||
|
||||
def addReader(self, reader, socket):
|
||||
self.condition.acquire()
|
||||
self.readers[socket] = reader
|
||||
@ -897,7 +903,7 @@ class Listener(object):
|
||||
|
||||
It requires registration with an Engine object in order to have
|
||||
the read() method called when a socket is availble for reading.'''
|
||||
|
||||
|
||||
def __init__(self, zeroconf):
|
||||
self.zeroconf = zeroconf
|
||||
self.zeroconf.engine.addReader(self, self.zeroconf.socket)
|
||||
@ -924,7 +930,7 @@ class Listener(object):
|
||||
class Reaper(threading.Thread):
|
||||
'''A Reaper is used by this module to remove cache entries that
|
||||
have expired.'''
|
||||
|
||||
|
||||
def __init__(self, zeroconf):
|
||||
threading.Thread.__init__(self)
|
||||
self.zeroconf = zeroconf
|
||||
@ -953,7 +959,7 @@ class ServiceBrowser(threading.Thread):
|
||||
The listener object will have its addService() and
|
||||
removeService() methods called when this browser
|
||||
discovers changes in the services availability.'''
|
||||
|
||||
|
||||
def __init__(self, zeroconf, type, listener):
|
||||
'''Creates a browser for a specific type'''
|
||||
threading.Thread.__init__(self)
|
||||
@ -964,7 +970,7 @@ class ServiceBrowser(threading.Thread):
|
||||
self.nextTime = currentTimeMillis()
|
||||
self.delay = _BROWSER_TIME
|
||||
self.list = []
|
||||
|
||||
|
||||
self.done = 0
|
||||
|
||||
self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN))
|
||||
@ -1024,12 +1030,12 @@ class ServiceBrowser(threading.Thread):
|
||||
|
||||
if event is not None:
|
||||
event(self.zeroconf)
|
||||
|
||||
|
||||
|
||||
class ServiceInfo(object):
|
||||
'''Service information'''
|
||||
|
||||
def __init__(self, type, name, address=None, port=None, weight=0,
|
||||
|
||||
def __init__(self, type, name, address=None, port=None, weight=0,
|
||||
priority=0, properties=None, server=None):
|
||||
'''Create a service description.
|
||||
|
||||
@ -1095,7 +1101,7 @@ class ServiceInfo(object):
|
||||
index += 1
|
||||
strs.append(text[index:index+length])
|
||||
index += length
|
||||
|
||||
|
||||
for s in strs:
|
||||
eindex = s.find('=')
|
||||
if eindex == -1:
|
||||
@ -1118,7 +1124,7 @@ class ServiceInfo(object):
|
||||
except:
|
||||
traceback.print_exc()
|
||||
self.properties = None
|
||||
|
||||
|
||||
def getType(self):
|
||||
'''Type accessor'''
|
||||
return self.type
|
||||
@ -1208,7 +1214,7 @@ class ServiceInfo(object):
|
||||
result = 1
|
||||
finally:
|
||||
zeroconf.removeListener(self)
|
||||
|
||||
|
||||
return result
|
||||
|
||||
def __eq__(self, other):
|
||||
@ -1233,7 +1239,7 @@ class ServiceInfo(object):
|
||||
result += self.text[:17] + '...'
|
||||
result += ']'
|
||||
return result
|
||||
|
||||
|
||||
|
||||
class Zeroconf(object):
|
||||
'''Implementation of Zeroconf Multicast DNS Service Discovery
|
||||
@ -1273,7 +1279,7 @@ class Zeroconf(object):
|
||||
# the SO_REUSE* options have been set, so ignore it
|
||||
#
|
||||
pass
|
||||
#self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.intf) + socket.inet_aton('0.0.0.0'))
|
||||
#self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.intf) + socket.inet_aton('0.0.0.0'))
|
||||
self.socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0'))
|
||||
|
||||
self.listeners = []
|
||||
@ -1284,7 +1290,7 @@ class Zeroconf(object):
|
||||
self.cache = DNSCache()
|
||||
|
||||
self.condition = threading.Condition()
|
||||
|
||||
|
||||
self.engine = Engine(self)
|
||||
self.listener = Listener(self)
|
||||
self.reaper = Reaper(self)
|
||||
@ -1479,7 +1485,7 @@ class Zeroconf(object):
|
||||
record = entry
|
||||
else:
|
||||
self.cache.add(record)
|
||||
|
||||
|
||||
self.updateRecord(now, record)
|
||||
|
||||
def handleQuery(self, msg, addr, port):
|
||||
@ -1493,14 +1499,14 @@ class Zeroconf(object):
|
||||
out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0)
|
||||
for question in msg.questions:
|
||||
out.addQuestion(question)
|
||||
|
||||
|
||||
for question in msg.questions:
|
||||
if question.type == _TYPE_PTR:
|
||||
if question.name == '_services._dns-sd._udp.local.':
|
||||
for stype in self.servicetypes.keys():
|
||||
if out is None:
|
||||
out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
|
||||
out.addAnswer(msg, DNSPointer('_services._dns-sd._udp.local.', _TYPE_PTR, _CLASS_IN, _DNS_TTL, stype))
|
||||
out.addAnswer(msg, DNSPointer('_services._dns-sd._udp.local.', _TYPE_PTR, _CLASS_IN, _DNS_TTL, stype))
|
||||
for service in self.services.values():
|
||||
if question.name == service.type:
|
||||
if out is None:
|
||||
@ -1510,16 +1516,16 @@ class Zeroconf(object):
|
||||
try:
|
||||
if out is None:
|
||||
out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA)
|
||||
|
||||
|
||||
# Answer A record queries for any service addresses we know
|
||||
if question.type == _TYPE_A or question.type == _TYPE_ANY:
|
||||
for service in self.services.values():
|
||||
if service.server == question.name.lower():
|
||||
out.addAnswer(msg, DNSAddress(question.name, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
|
||||
|
||||
|
||||
service = self.services.get(question.name.lower(), None)
|
||||
if not service: continue
|
||||
|
||||
|
||||
if question.type == _TYPE_SRV or question.type == _TYPE_ANY:
|
||||
out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server))
|
||||
if question.type == _TYPE_TXT or question.type == _TYPE_ANY:
|
||||
@ -1528,7 +1534,7 @@ class Zeroconf(object):
|
||||
out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if out is not None and out.answers:
|
||||
out.id = msg.id
|
||||
self.send(out, addr, port)
|
||||
@ -1553,11 +1559,11 @@ class Zeroconf(object):
|
||||
self.unregisterAllServices()
|
||||
self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0'))
|
||||
self.socket.close()
|
||||
|
||||
|
||||
# Test a few module features, including service registration, service
|
||||
# query (for Zoe), and service unregistration.
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == '__main__':
|
||||
print 'Multicast DNS Service Discovery for Python, version', __version__
|
||||
r = Zeroconf()
|
||||
print '1. Testing registration of a service...'
|
||||
|
@ -501,8 +501,10 @@ class BasicNewsRecipe(Recipe):
|
||||
if isinstance(self.feeds, basestring):
|
||||
self.feeds = [self.feeds]
|
||||
|
||||
if self.needs_subscription and (self.username is None or self.password is None):
|
||||
raise ValueError('The %s recipe needs a username and password.'%self.title)
|
||||
if self.needs_subscription and (\
|
||||
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.image_map, self.image_counter = {}, 1
|
||||
|
@ -193,11 +193,16 @@ class SchedulerConfig(object):
|
||||
|
||||
def write_scheduler_file(self):
|
||||
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:
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
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):
|
||||
s = E.schedule({'type':typ})
|
||||
@ -225,15 +230,21 @@ class SchedulerConfig(object):
|
||||
typ, sch, ld = self.un_serialize_schedule(recipe)
|
||||
except:
|
||||
return False
|
||||
utcnow = datetime.utcnow()
|
||||
if typ == 'interval':
|
||||
return datetime.utcnow() - ld > timedelta(sch)
|
||||
return utcnow - ld > timedelta(sch)
|
||||
elif typ == 'day/time':
|
||||
day, hour, minute = sch
|
||||
now = datetime.now()
|
||||
offset = now - utcnow
|
||||
ld_local = ld + offset
|
||||
day, hour, minute = sch
|
||||
|
||||
is_today = day < 0 or day > 6 or \
|
||||
day == calendar.weekday(now.year, now.month, now.day)
|
||||
return is_today and datetime.utcnow().date() != ld.date() and \
|
||||
now.hour >= hour and now.minute >= minute
|
||||
is_time = now.hour > hour or \
|
||||
(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
|
||||
|
||||
def set_account_info(self, urn, un, pw):
|
||||
|
@ -164,6 +164,8 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
|
||||
return cls(*args)
|
||||
|
||||
def ok(urn):
|
||||
if restrict_to_urns is None:
|
||||
return False
|
||||
return not restrict_to_urns or urn in restrict_to_urns
|
||||
|
||||
new_root = factory(NewsTreeItem, None)
|
||||
@ -230,6 +232,8 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
|
||||
def search(self, query, refinement):
|
||||
try:
|
||||
results = self.parse(unicode(query))
|
||||
if not results:
|
||||
results = None
|
||||
except ParseException:
|
||||
results = []
|
||||
self.do_refresh(restrict_to_urns=results)
|
||||
|
Loading…
x
Reference in New Issue
Block a user