Kovid's intermediate fix for periodical Next Article

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

View File

@ -21,9 +21,12 @@ class TheAtlantic(BasicNewsRecipe):
dict(name='div', id=['seealso','storybottom', 'footer', 'ad_banner_top', 'sidebar','articletoolstop','subcontent',]),
dict(name='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 = ''

View File

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

View File

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

View File

@ -18,7 +18,6 @@ class Economist(BasicNewsRecipe):
__author__ = "Kovid Goyal"
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'])]

View File

@ -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'])

View File

@ -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,8 +19,10 @@ 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
@ -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'

View File

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

View File

@ -17,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

View File

@ -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):
url = article.get('guid', None)
ans = article.get('guid',None)
if 'video' in url or 'quiz' in url :
try:
self.log('Looking for full story link in', ans)
soup = self.index_to_soup(ans)
x = soup.find(text="Auf einer Seite lesen")
if x is not None:
a = x.parent
if a and a.has_key('href'):
ans = a['href']
self.log('Found full story link', ans)
except:
pass
if 'video' in ans or 'quiz' in ans :
ans = None
return ans
url = None
return url
def preprocess_html(self, soup):
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', '')

View File

@ -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, \

View File

@ -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()

View File

@ -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',

View File

@ -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

View File

@ -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" />

View File

@ -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):

View File

@ -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',

View File

@ -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

View File

@ -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 += [

View File

@ -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()

View File

@ -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

View File

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

View File

@ -1316,9 +1316,9 @@ class HTMLConverter(object):
elif m.group(2) == 'pc':
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)))

View File

@ -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

View File

@ -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'

View File

@ -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:

View File

@ -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()

View File

@ -41,14 +41,14 @@ PML_HTML_RULES = [
(re.compile(r'\\q="(?P<target>#.+?)"(?P<text>.*?)\\q', re.DOTALL), lambda match: '<a href="%s">%s</a>' % (match.group('target'), match.group('text')) if match.group('text') else ''),
(re.compile(r'\\Q="(?P<target>.+?)"'), 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

View File

@ -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+)")

View File

@ -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:

View File

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

View File

@ -453,6 +453,12 @@ class ConfigDialog(QDialog, Ui_Dialog):
self.delete_news.setEnabled)
self.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

View File

@ -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>&amp;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 &amp;configuration directory</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_4">

View File

@ -14,7 +14,7 @@ from PyQt4.Qt import QDialog, QApplication, SIGNAL, Qt, QTime, QObject, QMenu, \
from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog
from calibre.gui2.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)

View File

@ -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):

View File

@ -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):
'''

View File

@ -18,7 +18,7 @@ from calibre.gui2.viewer.config_ui import Ui_Dialog
from calibre.gui2.viewer.js import bookmarks, referencing, hyphenation
from calibre.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()

View File

@ -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"?>

View File

@ -21,7 +21,7 @@ What formats does |app| support conversion to/from?
It can convert every input format in the following list, to every output format.
*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

View File

@ -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 &notifications 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

View File

@ -259,7 +259,7 @@ class DNSQuestion(DNSEntry):
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):
@ -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:

View File

@ -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

View File

@ -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):

View File

@ -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)