Sync to trunk.

This commit is contained in:
John Schember 2010-01-24 09:29:09 -05:00
commit 930f6df87e
28 changed files with 747 additions and 223 deletions

View File

@ -36,7 +36,7 @@
- title: "News downloads: When getting an article URL from a RSS feed, look first for an original article link. This speeds up the download of news services that use a syndication service like feedburner or pheedo to publish their RSS feeds."
bug fixes:
- "Windows device detection: Don't do expensive polling while waiting for device disconnect. This should fix the problems people have with their floppy drive being activated while an e-book reader is connected"
- title: "Windows device detection: Don't do expensive polling while waiting for device disconnect. This should fix the problems people have with their floppy drive being activated while an e-book reader is connected"
- title: "PML Input: Fix creation of metadata Table of Contents"
tickets: [5633]
@ -84,7 +84,7 @@
author: Darko Miletic
- title: Pajamas Media
autor: Krittika Goyal
author: Krittika Goyal
- title: Algemeen Dagbla
author: kwetal

View File

@ -1,3 +1,5 @@
body { background-color: white; }
p.title {
margin-top:0em;
margin-bottom:1em;

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -98,6 +98,9 @@ class Barrons(BasicNewsRecipe):
('Funds/Q&A', 'http://online.barrons.com/xml/rss/3_7519.xml'),
]
def get_article_url(self, article):
return article.get('link', None)
def get_cover_url(self):
cover_url = None

View File

@ -2,17 +2,37 @@
from calibre.web.feeds.news import BasicNewsRecipe
class CommonDreams(BasicNewsRecipe):
# Identify the recipe
title = u'Common Dreams'
description = u'Progressive news and views'
__author__ = u'XanthanGum'
language = 'en'
# Format the text
extra_css = '''
body{font-family:verdana,arial,helvetica,geneva,sans-serif ;}
h1{font-size: xx-large;}
h2{font-size: large;}
'''
# Pick no article older than seven days and limit the number of articles per feed to 100
oldest_article = 7
max_articles_per_feed = 100
feeds = [
(u'Common Dreams Headlines',
u'http://www.commondreams.org/feed/headlines_rss'),
(u'Common Dreams Views', u'http://www.commondreams.org/feed/views_rss'),
(u'Common Dreams Newswire', u'http://www.commondreams.org/feed/newswire_rss')
]
# Remove everything before the article
remove_tags_before = dict(name = 'div', attrs = {'id':'node-header'})
# Remove everything after the article
remove_tags_after = dict(name = 'div', attrs = {'class':'copyright-info'})
# Identify the news feeds
feeds = [(u'Headlines', u'http://www.commondreams.org/feed/headlines_rss'),
(u'Further News Articles', u'http://www.commondreams.org/feed/further_rss'),
(u'Views', u'http://www.commondreams.org/feed/views_rss'),
(u'Progressive Newswire', u'http://www.commondreams.org/feed/newswire_rss')]

View File

@ -0,0 +1,54 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Walt Anthony <workshop.northpole at gmail.com>'
'''
www.news-record.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class NewsandRecord(BasicNewsRecipe):
title = u'Greensboro News & Record'
description = "News from Greensboro, North Carolina"
__author__ = 'Walt Anthony'
publisher = 'News & Record and Landmark Media Enterprises, LLC'
category = 'news, USA'
oldest_article = 3 #days
max_articles_per_feed = 25
summary_length = 150
language = 'en'
encoding = 'utf-8'
remove_javascript = True
no_stylesheets = True
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
remove_tags_before = dict(name='h3', attrs={'class':'nrcTxt_headline'})
remove_tags_after = dict(name='div', attrs={'id':'nrcBlk_ContentBody'})
remove_tags = [
dict(name='iframe'),
dict(name=['notags','embed','object','link','img']),
]
feeds = [
('News', 'http://www.news-record.com/news/archive/feed'),
('Greensboro News', 'http://www.news-record.com/news/greensboro/feed'),
('Education', 'http://www.news-record.com/news/education/feed'),
('Government', 'http://www.news-record.com/news/government/feed'),
('College Sports', 'http://www.news-record.com/sports/college/feed'),
('Sports Extra', 'http://www.news-record.com/blog/sportsextra/feed'),
('Life', 'http://www.news-record.com/life/top/feed'),
('NASCAR', 'http://www.news-record.com/sports/nascar/top/feed'),
('Editorials', 'http://www.news-record.com/opinion/editorials/feed'),
('Letters to the Editor', 'http://www.news-record.com/opinion/letters/feed')
]

View File

@ -0,0 +1,41 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Walt Anthony <workshop.northpole at gmail.com>'
'''
www.hotair.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class hotair(BasicNewsRecipe):
title = u'Hot Air'
__author__ = 'Walt Anthony'
description = "The world's first, full-service conservative Internet broadcast network"
publisher = 'Hot Air'
category = 'news, politics, USA'
oldest_article = 3
max_articles_per_feed = 100
summary_length = 150
language = 'en'
encoding = 'utf-8'
use_embedded_content = False
remove_javascript = True
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
keep_only_tags = [dict(name='div', attrs={'id':'page-post'})]
remove_tags = [dict(name=['iframe', 'small', 'embed', 'object','link','script','form'])]
feeds = [
('Hot Air', 'http://feeds.feedburner.com/hotair/main'),
('The Greenroom', 'http://feeds2.feedburner.com/hotair/greenroom')
]

View File

@ -0,0 +1,58 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
'''
www.ionline.pt
'''
from calibre.web.feeds.news import BasicNewsRecipe
class IOnline_pt(BasicNewsRecipe):
title = 'ionline - Portugal'
__author__ = 'Darko Miletic'
description = 'News from Portugal'
publisher = 'ionline.pt'
category = 'ionline, noticias, portugal, jornal, actualidade, benfica, bolsa, desporto, empresas, globo, europa, futebol, internacional, investir, lisboa, jogos, musica, videos, tempo, meteorologia, pais, politica, porto, sporting, fcporto, televisao, tv, opiniao, nacional, sociedade, crise, financeira, policia, crime, artes, cinema, cultura, madeleine, blog, ciencia, tecnologia, galerias, fotografia, fotos, famosos, emprego, imagens, teatro, news, mundial, governo, ps, psd, be, pcp, cds, pp, partidos'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
encoding = 'utf-8'
use_embedded_content = False
language = 'pt'
extra_css = ' .publish{font-style: italic; line-height: 1.2em; border-bottom: 1px dotted; padding: 5px 0} .entity{line-height: 1.2em} .overview{line-height:1.2em} '
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
keep_only_tags = [
dict(name=['h5','h1'])
, dict(name='div', attrs={'class':['publish','overview','entity']})
]
remove_tags = [
dict(name=['object','embed','iframe'])
]
feeds = [
(u'Portugal' , u'http://www.ionline.pt/rss/portugal.xml' )
,(u'Mundo' , u'http://www.ionline.pt/rss/mundo.xml' )
,(u'Dinheiro' , u'http://www.ionline.pt/rss/dinheiro.xml' )
,(u'Desporto' , u'http://www.ionline.pt/rss/desporto.xml' )
,(u'Boa Vida' , u'http://www.ionline.pt/rss/boavida.xml' )
,(u'iReporter', u'http://www.ionline.pt/rss/ireporter.xml')
,(u'iBloges' , u'http://www.ionline.pt/rss/iblogues.xml' )
,(u'Desporto' , u'http://www.ionline.pt/rss/desporto.xml' )
]
def print_version(self, url):
rest = url.rpartition('/')[2]
lmain = rest.partition('-')[0]
lurl = u'http://www.ionline.pt/interior/index.php?p=news-print&idNota=' + lmain
return lurl

View File

@ -0,0 +1,50 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Walt Anthony <workshop.northpole at gmail.com>'
'''
www.nationalreview.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class NRO(BasicNewsRecipe):
title = u'National Review Online'
__author__ = 'Walt Anthony'
description = "National Review is America's most widely read and influential magazine and web site for Republican/conservative news, commentary, and opinion."
publisher = 'National Review, Inc.'
category = 'news, politics, USA'
oldest_article = 3
max_articles_per_feed = 100
summary_length = 150
language = 'en'
encoding = 'utf-8'
use_embedded_content = True
remove_javascript = True
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
remove_tags = [
dict(name=['embed','object','iframe']),
]
feeds = [
(u'National Review', u'http://www.nationalreview.com/index.xml'),
(u'The Corner', u'http://corner.nationalreview.com/corner.xml'),
(u'The Agenda', u'http://agenda.nationalreview.com/agenda.xml'),
(u'Bench Memos', u'http://bench.nationalreview.com/bench.xml'),
(u'Campaign Spot', u'http://campaignspot.nationalreview.com/campaignspot.xml'),
(u'Critical Care', u'http://healthcare.nationalreview.com/healthcare.xml'),
(u'Doctor, Doctor', u'http://www.nationalreview.com/doctor/doctor.xml'),
(u"Kudlow's Money Politic$", u'http://kudlow.nationalreview.com/kudlow.xml'),
(u'Media Blog', u'http://media.nationalreview.com/media.xml'),
(u'Phi Beta Cons', u'http://phibetacons.nationalreview.com/phibetacons.xml'),
(u'Planet Gore', u'http://planetgore.nationalreview.com/planetgore.xml')
]

View File

@ -0,0 +1,40 @@
from calibre.web.feeds.news import BasicNewsRecipe
class Neowin(BasicNewsRecipe):
title = u'Neowin.net'
oldest_article = 5
language = 'en'
description = 'News from IT'
publisher = 'Neowin'
category = 'news, IT, Microsoft, Apple, hardware, software, games'
__author__ = 'Darko Miletic'
max_articles_per_feed = 100
no_stylesheets = True
encoding = 'utf8'
conversion_options = {
'tags' : category
,'language' : language
,'comments' : description
,'publisher' : publisher
}
keep_only_tags = [dict(name='div', attrs={'id':'article'})]
remove_tags_after = dict(name='div', attrs={'id':'tag-bar'})
remove_tags = [
dict(name=['base','object','link','iframe'])
,dict(name='div', attrs={'id':'tag-bar'})
]
feeds = [
(u'Software' , u'http://www.neowin.net/news/rss/software' )
,(u'Gaming' , u'http://www.neowin.net/news/rss/gaming' )
,(u'Microsoft', u'http://www.neowin.net/news/rss/microsoft')
,(u'Apple' , u'http://www.neowin.net/news/rss/apple' )
,(u'Editorial', u'http://www.neowin.net/news/rss/editorial')
]
def image_url_processor(cls, baseurl, url):
return url

View File

@ -1,44 +1,105 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__docformat__ = 'restructuredtext en'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
'''
www.wired.com
'''
import re
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
class Wired(BasicNewsRecipe):
title = 'Wired Magazine'
__author__ = 'Darko Miletic'
description = 'Gaming news'
publisher = 'Conde Nast Digital'
category = 'news, games, IT, gadgets'
oldest_article = 32
max_articles_per_feed = 100
no_stylesheets = True
encoding = 'utf-8'
use_embedded_content = False
language = 'en'
extra_css = ' body{font-family: sans-serif} .entryDescription li {display: inline; list-style-type: none} '
index = 'http://www.wired.com/magazine/'
title = 'Wired.com'
__author__ = 'Kovid Goyal'
description = 'Technology news'
timefmt = ' [%Y%b%d %H%M]'
language = 'en'
preprocess_regexps = [(re.compile(r'<meta name="Title".*<title>', re.DOTALL|re.IGNORECASE),lambda match: '<title>')]
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
no_stylesheets = True
keep_only_tags = [dict(name='div', attrs={'class':'post'})]
remove_tags_after = dict(name='div', attrs={'class':'tweetmeme_button'})
remove_tags = [
dict(name=['object','embed','iframe','link'])
,dict(name='div', attrs={'class':['podcast_storyboard','tweetmeme_button']})
]
remove_tags_before = dict(name='div', id='content')
remove_tags = [dict(id=['social_tools', 'outerWrapper', 'sidebar',
'footer', 'advertisement', 'blog_subscription_unit',
'brightcove_component']),
{'class':'entryActions'},
dict(name=['noscript', 'script'])]
feeds = [
('Top News', 'http://feeds.wired.com/wired/index'),
('Culture', 'http://feeds.wired.com/wired/culture'),
('Software', 'http://feeds.wired.com/wired/software'),
('Mac', 'http://feeds.feedburner.com/cultofmac/bFow'),
('Gadgets', 'http://feeds.wired.com/wired/gadgets'),
('Cars', 'http://feeds.wired.com/wired/cars'),
('Entertainment', 'http://feeds.wired.com/wired/entertainment'),
('Gaming', 'http://feeds.wired.com/wired/gaming'),
('Science', 'http://feeds.wired.com/wired/science'),
('Med Tech', 'http://feeds.wired.com/wired/medtech'),
('Politics', 'http://feeds.wired.com/wired/politics'),
('Tech Biz', 'http://feeds.wired.com/wired/techbiz'),
('Commentary', 'http://feeds.wired.com/wired/commentary'),
]
#feeds = [(u'Articles' , u'http://www.wired.com/magazine/feed/' )]
def parse_index(self):
totalfeeds = []
soup = self.index_to_soup(self.index)
features = soup.find('div',attrs={'id':'my-glider'})
if features:
farticles = []
for item in features.findAll('div',attrs={'class':'section'}):
divurl = item.find('div',attrs={'class':'feature-header'})
divdesc = item.find('div',attrs={'class':'feature-text'})
url = 'http://www.wired.com' + divurl.a['href']
title = self.tag_to_string(divurl.a)
description = self.tag_to_string(divdesc)
date = strftime(self.timefmt)
farticles.append({
'title' :title
,'date' :date
,'url' :url
,'description':description
})
totalfeeds.append(('Featured Articles', farticles))
#department feeds
departments = ['rants','start','test','play','found']
dept = soup.find('div',attrs={'id':'magazine-departments'})
if dept:
for ditem in departments:
darticles = []
department = dept.find('div',attrs={'id':'department-'+ditem})
if department:
for item in department.findAll('div'):
description = ''
feed_link = item.find('a')
if feed_link and feed_link.has_key('href'):
url = feed_link['href']
title = self.tag_to_string(feed_link)
date = strftime(self.timefmt)
darticles.append({
'title' :title
,'date' :date
,'url' :url
,'description':description
})
totalfeeds.append((ditem.capitalize(), darticles))
return totalfeeds
def get_cover_url(self):
cover_url = None
soup = self.index_to_soup(self.index)
cover_item = soup.find('div',attrs={'class':'spread-image'})
if cover_item:
cover_url = 'http://www.wired.com' + cover_item.a.img['src']
return cover_url
def print_version(self, url):
return url.replace('http://www.wired.com/', 'http://www.wired.com/print/')
return url.rstrip('/') + '/all/1'
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return soup

View File

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

View File

@ -255,9 +255,16 @@ class CatalogPlugin(Plugin):
def search_sort_db(self, db, opts):
# If declared, --ids overrides any declared search criteria
if not opts.ids and opts.search_text:
db.search(opts.search_text)
'''
# Don't add Catalogs to the generated Catalogs
cat = _('Catalog')
if opts.search_text:
opts.search_text += ' not tag:'+cat
else:
opts.search_text = 'not tag:'+cat
'''
db.search(opts.search_text)
if opts.sort_by:
# 2nd arg = ascending

View File

@ -268,7 +268,8 @@ class EPUBOutput(OutputFormatPlugin):
# remove <img> tags with empty src elements
bad = []
for x in XPath('//h:img')(body):
if not x.get('src', '').strip():
src = x.get('src', '').strip()
if src in ('', '#'):
bad.append(x)
for img in bad:
img.getparent().remove(img)

View File

@ -619,7 +619,6 @@ class MobiWriter(object):
self._oeb.log.warning('_generate_indexed_navpoints: Failed to generate index')
# Zero out self._HTMLRecords, return False
self._HTMLRecords = []
#last_name = None
return False
previousOffset = offset

View File

@ -18,38 +18,11 @@ class Font(object):
self.color = spec.get('color')
self.family = spec.get('family')
class Column(object):
# A column contains an element is the element bulges out to
# the left or the right by at most HFUZZ*col width.
HFUZZ = 0.2
class Element(object):
def __init__(self):
self.left = self.right = self.top = self.bottom = 0
self.width = self.height = 0
self.elements = []
def add(self, elem):
if elem in self.elements: return
self.elements.append(elem)
self.elements.sort(cmp=lambda x,y:cmp(x.bottom,y.bottom))
self.top = self.elements[0].top
self.bottom = self.elements[-1].bottom
self.left, self.right = sys.maxint, 0
for x in self:
self.left = min(self.left, x.left)
self.right = max(self.right, x.right)
self.width, self.height = self.right-self.left, self.bottom-self.top
def __iter__(self):
for x in self.elements:
yield x
def contains(self, elem):
return elem.left > self.left - self.HFUZZ*self.width and \
elem.right < self.right + self.HFUZZ*self.width
class Element(object):
self.starts_block = None
self.block_style = None
def __eq__(self, other):
return self.id == other.id
@ -60,6 +33,7 @@ class Element(object):
class Image(Element):
def __init__(self, img, opts, log, idc):
Element.__init__(self)
self.opts, self.log = opts, log
self.id = idc.next()
self.top, self.left, self.width, self.height, self.iwidth, self.iheight = \
@ -71,6 +45,7 @@ class Image(Element):
class Text(Element):
def __init__(self, text, font_map, opts, log, idc):
Element.__init__(self)
self.id = idc.next()
self.opts, self.log = opts, log
self.font_map = font_map
@ -140,20 +115,78 @@ class Interval(object):
def __hash__(self):
return hash('(%f,%f)'%self.left, self.right)
class Column(object):
# A column contains an element is the element bulges out to
# the left or the right by at most HFUZZ*col width.
HFUZZ = 0.2
def __init__(self):
self.left = self.right = self.top = self.bottom = 0
self.width = self.height = 0
self.elements = []
self.average_line_separation = 0
def add(self, elem):
if elem in self.elements: return
self.elements.append(elem)
self.elements.sort(cmp=lambda x,y:cmp(x.bottom,y.bottom))
self.top = self.elements[0].top
self.bottom = self.elements[-1].bottom
self.left, self.right = sys.maxint, 0
for x in self:
self.left = min(self.left, x.left)
self.right = max(self.right, x.right)
self.width, self.height = self.right-self.left, self.bottom-self.top
def __iter__(self):
for x in self.elements:
yield x
def contains(self, elem):
return elem.left > self.left - self.HFUZZ*self.width and \
elem.right < self.right + self.HFUZZ*self.width
def collect_stats(self):
if len(self.elements) > 1:
gaps = [self.elements[i+1].top - self.elements[i].bottom for i in
range(len(0, len(self.elements)-1))]
self.average_line_separation = sum(gaps)/len(gaps)
for i, elem in enumerate(self.elements):
left_margin = elem.left - self.left
elem.indent_fraction = left_margin/self.width
elem.width_fraction = elem.width/self.width
if i == 0:
elem.top_gap = None
else:
elem.top_gap = self.elements[i-1].bottom - elem.top
def previous_element(self, idx):
if idx == 0:
return None
return self.elements[idx-1]
class Region(object):
def __init__(self):
self.columns = []
self.top = self.bottom = self.left = self.right = self.width = self.height = 0
def add_columns(self, columns):
def add(self, columns):
if not self.columns:
for x in sorted(columns, cmp=lambda x,y: cmp(x.left, y.left)):
self.columns.append(x)
else:
pass
for i in range(len(columns)):
for elem in columns[i]:
self.columns[i].add(elem)
def contains(self, columns):
# TODO: handle unbalanced columns
if not self.columns:
return True
if len(columns) != len(self.columns):
@ -168,6 +201,26 @@ class Region(object):
return False
return True
@property
def is_empty(self):
return len(self.elements) == 0
def collect_stats(self):
for column in self.columns:
column.collect_stats()
self.average_line_separation = sum([x.average_line_separation for x in
self.columns])/float(len(self.columns))
def __iter__(self):
for x in self.columns:
yield x
def linearize(self):
self.elements = []
for x in self.columns:
self.elements.extend(x)
class Page(object):
# Fraction of a character width that two strings have to be apart,
@ -178,6 +231,8 @@ class Page(object):
# for them to be considered to be part of the same text fragment
LINE_FACTOR = 0.4
# Multiplies the average line height when determining row height
# of a particular element to detect columns.
YFUZZ = 1.5
@ -242,19 +297,25 @@ class Page(object):
self.texts.remove(match)
def first_pass(self):
'Sort page into regions and columns'
self.regions = []
if not self.elements:
return
for i, x in enumerate(self.elements):
x.idx = i
self.current_region = None
current_region = Region()
processed = set([])
for x in self.elements:
if x in processed: continue
elems = set(self.find_elements_in_row_of(x))
columns = self.sort_into_columns(x, elems)
processed.update(elems)
columns
if not current_region.contains(columns):
self.regions.append(self.current_region)
current_region = Region()
current_region.add(columns)
if not self.current_region.is_empty():
self.regions.append(current_region)
def sort_into_columns(self, elem, neighbors):
columns = [Column()]
@ -274,7 +335,7 @@ class Page(object):
def find_elements_in_row_of(self, x):
interval = Interval(x.top,
x.top + self.YFUZZ*(1+self.average_text_height))
x.top + self.YFUZZ*(self.average_text_height))
h_interval = Interval(x.left, x.right)
for y in self.elements[x.idx:x.idx+15]:
if y is not x:
@ -285,6 +346,12 @@ class Page(object):
x_interval.intersection(h_interval).width <= 0:
yield y
def second_pass(self):
'Locate paragraph boundaries in each column'
for region in self.regions:
region.collect_stats()
region.linearize()
class PDFDocument(object):
@ -314,6 +381,7 @@ class PDFDocument(object):
for page in self.pages:
page.document_font_stats = self.font_size_stats
page.first_pass()
page.second_pass()
def collect_font_statistics(self):
self.font_size_stats = {}

View File

@ -14,10 +14,10 @@ from PyQt4.Qt import QWidget
class PluginWidget(QWidget,Ui_Form):
TITLE = _('EPUB/MOBI Options')
TITLE = _('E-book Options')
HELP = _('Options specific to')+' EPUB/MOBI '+_('output')
OPTION_FIELDS = [('exclude_genre','\[[\w ]*\]'),
('exclude_tags','~'),
('exclude_tags','~,'+_('Catalog')),
('read_tag','+'),
('note_tag','*')]

View File

@ -932,7 +932,7 @@ class DeviceGUI(object):
if isinstance(job.exception, FreeSpaceError):
where = 'in main memory.' if 'memory' in str(job.exception) \
else 'on the storage card.'
titles = '\n'.join(['<li>'+mi['title']+'</li>' \
titles = '\n'.join(['<li>'+mi.title+'</li>' \
for mi in metadata])
d = error_dialog(self, _('No space on device'),
_('<p>Cannot upload books to device there '

View File

@ -46,18 +46,16 @@ class Catalog(QDialog, Ui_Dialog):
name = plugin.name.lower().replace(' ', '_')
if type(plugin) in builtin_plugins:
info("Adding widget for builtin Catalog plugin %s" % plugin.name)
try:
catalog_widget = __import__('calibre.gui2.catalog.'+name,
fromlist=[1])
pw = catalog_widget.PluginWidget()
info("Initializing %s" % name)
pw.initialize(name)
pw.ICON = I('forward.svg')
self.widgets.append(pw)
[self.fmts.append([file_type.upper(), pw.sync_enabled,pw]) for file_type in plugin.file_types]
except ImportError:
info("ImportError with %s" % name)
info("ImportError initializing %s" % name)
continue
else:
# Load dynamic tab

View File

@ -522,7 +522,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
from calibre.ebooks.metadata import MetaInformation
mi = MetaInformation(_('Calibre Quick Start Guide'), ['John Schember'])
mi.author_sort = 'Schember, John'
mi.comments = "A guide to get you up an running with calibre"
mi.comments = "A guide to get you up and running with calibre"
mi.publisher = 'calibre'
self.library_view.model().add_books([P('quick_start.epub')], ['epub'],
[mi])

View File

@ -178,7 +178,7 @@ class Document(QWebPage):
def set_user_stylesheet(self):
raw = config().parse().user_css
raw = '::selection {background:#ffff00; color:#000;}\n'+raw
raw = '::selection {background:#ffff00; color:#000;}\nbody {background-color: white;}\n'+raw
data = 'data:text/css;charset=utf-8;base64,'
data += b64encode(raw.encode('utf-8'))
self.settings().setUserStyleSheetUrl(QUrl(data))

View File

@ -65,7 +65,10 @@ class CSV_XML(CatalogPlugin):
for key in keys:
log(" %s: %s" % (key, opts_dict[key]))
# Get the sorted, filtered database as a dictionary
# If a list of ids are provided, don't use search_text
if opts.ids:
opts.search_text = None
data = self.search_sort_db(db, opts)
if not len(data):
@ -238,13 +241,22 @@ class EPUB_MOBI(CatalogPlugin):
help = _('Title of generated catalog used as title in metadata.\n'
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats")),
Option('--debug-pipeline',
default=None,
dest='debug_pipeline',
help=_('Save the output from different stages of the conversion '
'pipeline to the specified '
'directory. Useful if you are unsure at which stage '
'of the conversion process a bug is occurring.\n'
'Default: None\n'
'Applies to: ePub, MOBI output formats')),
Option('--exclude-genre',
default='\[[\w ]*\]',
dest='exclude_genre',
help=_("Regex describing tags to exclude as genres.\n" "Default: '%default' excludes bracketed tags, e.g. '[<tag>]'\n"
"Applies to: ePub, MOBI output formats")),
Option('--exclude-tags',
default='~',
default=('~,'+_('Catalog')),
dest='exclude_tags',
help=_("Comma-separated list of tag words indicating book should be excluded from output. Case-insensitive.\n"
"--exclude-tags=skip will match 'skip this book' and 'Skip will like this'.\n"
@ -446,8 +458,8 @@ class EPUB_MOBI(CatalogPlugin):
'''
# Number of discrete steps to catalog creation
current_step = 0
total_steps = 13
current_step = 0.0
total_steps = 13.0
# Used to xlate pubdate to friendly format
MONTHS = ['January', 'February','March','April','May','June',
@ -498,7 +510,7 @@ class EPUB_MOBI(CatalogPlugin):
self.__playOrder = 1
self.__plugin = plugin
self.__plugin_path = opts.plugin_path
self.__progressInt = 0
self.__progressInt = 0.0
self.__progressString = ''
self.__reporter = notification
self.__stylesheet = stylesheet
@ -727,7 +739,6 @@ class EPUB_MOBI(CatalogPlugin):
shutil.copy(os.path.join(catalog_resources,file[1]),
os.path.join(self.catalogPath, file[0]))
def fetchBooksByTitle(self):
if self.verbose:
@ -751,14 +762,14 @@ class EPUB_MOBI(CatalogPlugin):
search_terms.append("tag:%s" % tag)
search_phrase = "not (%s)" % " or ".join(search_terms)
# Allow for no search_text
if self.opts.search_text:
self.opts.search_text = self.opts.search_text + " " + search_phrase
else:
# If a list of ids are provided, don't use search_text
if self.opts.ids:
self.opts.search_text = search_phrase
if self.verbose and False:
print "self.opts.search_text: %s" % self.opts.search_text
else:
if self.opts.search_text:
self.opts.search_text += " " + search_phrase
else:
self.opts.search_text = search_phrase
# Fetch the database as a dictionary
data = self.plugin.search_sort_db(self.db, self.opts)
@ -807,7 +818,7 @@ class EPUB_MOBI(CatalogPlugin):
# Re-sort based on title_sort
self.booksByTitle = sorted(titles,
key=lambda x:(x['title_sort'], x['title_sort']))
key=lambda x:(x['title_sort'].upper(), x['title_sort'].upper()))
def fetchBooksByAuthor(self):
# Generate a list of titles sorted by author from the database
@ -815,23 +826,37 @@ class EPUB_MOBI(CatalogPlugin):
if self.verbose:
print self.updateProgressFullStep("fetchBooksByAuthor()")
# Sort titles based on upper case authors
self.booksByAuthor = sorted(self.booksByTitle,
key=lambda x:(x['author_sort'], x['author_sort']))
key=lambda x:(x['author_sort'].upper(), x['author_sort'].upper()))
# Search_text already initialized
# Get the database sorted by author_sort
self.opts.sort_by = 'author_sort'
data = self.plugin.search_sort_db(self.db, self.opts)
if True:
# Build the unique_authors set from existing data
authors = [(record['author'], record['author_sort']) for record in self.booksByAuthor]
else:
# Search_text already initialized
# Get the database sorted by author_sort
self.opts.sort_by = 'author_sort'
data = self.plugin.search_sort_db(self.db, self.opts)
# GwR diagnostic: show sorted order
print "self.booksByAuthor in sort order:"
for book in self.booksByAuthor:
print "%s %s" % (book['author_sort'], book['title'])
print "data set in sort order:"
for book in data:
print "%s %s" % (book['author_sort'], book['title'])
# Build the unique_authors set
authors = []
for record in data:
# There may be multiple authors
author_list = []
for author in record['authors']:
author_list.append(author)
authors_concatenated = ", ".join(author_list)
authors.append((authors_concatenated, record['author_sort']))
# Build the unique_authors set
authors = []
for record in data:
# There may be multiple authors
author_list = []
for author in record['authors']:
author_list.append(author)
authors_concatenated = ", ".join(author_list)
authors.append((authors_concatenated, record['author_sort']))
# authors[] contains a list of all book authors, with multiple entries for multiple books by author
# unique_authors : (([0]:friendly [1]:sort [2]:book_count))
@ -860,7 +885,7 @@ class EPUB_MOBI(CatalogPlugin):
unique_authors.append((current_author[0], current_author[1],
books_by_current_author))
if self.verbose and False:
if False and self.verbose:
print "\nget_books_by_author(): %d unique authors" % len(unique_authors)
for author in unique_authors[0:3]:
print "%s" % author[0]
@ -880,7 +905,7 @@ class EPUB_MOBI(CatalogPlugin):
print "%3s: %s - %s" % (title['id'], title['title'], title['author'])
self.updateProgressMicroStep("generating book descriptions ...",
100*title_num/len(self.booksByTitle))
float(title_num*100/len(self.booksByTitle))/100)
# Generate the header
soup = self.generateHTMLDescriptionHeader("%s" % title['title'])
@ -917,7 +942,8 @@ class EPUB_MOBI(CatalogPlugin):
authorTag = body.find(attrs={'class':'author'})
aTag = Tag(soup, "a")
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(title['author']))
aTag.insert(0, escape(title['author']))
#aTag.insert(0, escape(title['author']))
aTag.insert(0, title['author'])
authorTag.insert(0, NavigableString("by "))
authorTag.insert(1, aTag)
@ -938,6 +964,8 @@ class EPUB_MOBI(CatalogPlugin):
# Insert a spacer to match the author indent
fontTag = Tag(soup,"font")
fontTag['style'] = 'color:white;font-size:large'
if self.opts.fmt == 'epub':
fontTag['style'] += ';opacity: 0.0'
fontTag.insert(0, NavigableString("by "))
tagsTag.insert(ttc, fontTag)
ttc += 1
@ -948,6 +976,8 @@ class EPUB_MOBI(CatalogPlugin):
aTag.insert(0,escape(NavigableString(tag)))
emTag = Tag(soup, "em")
emTag.insert(0, aTag)
if ttc < len(title['tags']):
emTag.insert(1, NavigableString(', '))
tagsTag.insert(ttc, emTag)
ttc += 1
@ -1054,15 +1084,15 @@ class EPUB_MOBI(CatalogPlugin):
# Loop through the books by title
for book in self.booksByTitle:
if book['title_sort'][0] != current_letter :
if book['title_sort'][0].upper() != current_letter :
# Start a new letter
current_letter = book['title_sort'][0]
current_letter = book['title_sort'][0].upper()
pIndexTag = Tag(soup, "p")
pIndexTag['class'] = "letter_index"
aTag = Tag(soup, "a")
aTag['name'] = "%stitles" % book['title_sort'][0]
aTag['name'] = "%stitles" % book['title_sort'][0].upper()
pIndexTag.insert(0,aTag)
pIndexTag.insert(1,NavigableString(book['title_sort'][0]))
pIndexTag.insert(1,NavigableString(book['title_sort'][0].upper()))
divTag.insert(dtc,pIndexTag)
dtc += 1
@ -1169,7 +1199,7 @@ class EPUB_MOBI(CatalogPlugin):
book_count = 0
for book in self.booksByAuthor:
book_count += 1
if book['author_sort'][0] != current_letter :
if book['author_sort'][0].upper() != current_letter :
'''
# Start a new letter - anchor only, hidden
current_letter = book['author_sort'][0].upper()
@ -1709,6 +1739,7 @@ class EPUB_MOBI(CatalogPlugin):
books_by_letter = []
if single_article_per_section:
# Create a single article for all books in this section
# THIS CODE NEEDS TO BE REVIEWED FOR .upper()
single_list = []
for book in self.booksByTitle:
single_list.append(book)
@ -1721,19 +1752,19 @@ class EPUB_MOBI(CatalogPlugin):
else:
# Loop over the titles, find start of each letter, add description_preview_count books
current_letter = self.booksByTitle[0]['title_sort'][0]
current_letter = self.booksByTitle[0]['title_sort'][0].upper()
title_letters = [current_letter]
current_book_list = []
current_book = ""
for book in self.booksByTitle:
if book['title_sort'][0] != current_letter:
if book['title_sort'][0].upper() != current_letter:
# Save the old list
book_list = " &bull; ".join(current_book_list)
short_description = self.generateShortDescription(self.formatNCXText(book_list))
books_by_letter.append(short_description)
# Start the new list
current_letter = book['title_sort'][0]
current_letter = book['title_sort'][0].upper()
title_letters.append(current_letter)
current_book = book['title']
current_book_list = [book['title']]
@ -1810,20 +1841,19 @@ class EPUB_MOBI(CatalogPlugin):
self.playOrder += 1
navLabelTag = Tag(soup, 'navLabel')
textTag = Tag(soup, 'text')
# textTag.insert(0, NavigableString('%s (%d)' % (section_title,len(sorted_authors))))
textTag.insert(0, NavigableString('%s' % tocTitle))
navLabelTag.insert(0, textTag)
nptc = 0
navPointTag.insert(nptc, navLabelTag)
nptc += 1
contentTag = Tag(soup,"content")
# contentTag['src'] = "%s#%s" % (HTML_file, file_ID)
contentTag['src'] = "%s#section_start" % HTML_file
navPointTag.insert(nptc, contentTag)
nptc += 1
if single_article_per_section:
# Create a single article for all authors in this section
# THIS CODE NEEDS TO BE REVIEWED FOR .upper()
# Build a formatted author range for article preview
single_list = []
@ -1841,7 +1871,7 @@ class EPUB_MOBI(CatalogPlugin):
# Loop over the sorted_authors list, find start of each letter, add description_preview_count artists
# sorted_authors[0]:friendly [1]:author_sort [2]:book_count
master_author_list = []
current_letter = self.authors[0][1][0]
current_letter = self.authors[0][1][0].upper()
current_author_list = []
for author in self.authors:
if author[1][0] != current_letter:
@ -1854,7 +1884,7 @@ class EPUB_MOBI(CatalogPlugin):
master_author_list.append((author_list, current_letter))
# Start the new list
current_letter = author[1][0]
current_letter = author[1][0].upper()
current_author_list = [author[0]]
else:
if len(current_author_list) < self.descriptionClip:
@ -1872,7 +1902,7 @@ class EPUB_MOBI(CatalogPlugin):
for authors in master_author_list:
navPointByLetterTag = Tag(soup, 'navPoint')
navPointByLetterTag['class'] = "article"
navPointByLetterTag['id'] = "%sauthors-ID" % (authors[1])
navPointByLetterTag['id'] = "%sauthors-ID" % (authors[1].upper())
navPointTag['playOrder'] = self.playOrder
self.playOrder += 1
navLabelTag = Tag(soup, 'navLabel')
@ -1880,7 +1910,7 @@ class EPUB_MOBI(CatalogPlugin):
if single_article_per_section:
textTag.insert(0, NavigableString("All books sorted by author"))
else:
textTag.insert(0, NavigableString("Authors beginning with '%s'" % (authors[1])))
textTag.insert(0, NavigableString("Authors beginning with '%s'" % (authors[1].upper())))
navLabelTag.insert(0, textTag)
navPointByLetterTag.insert(0,navLabelTag)
contentTag = Tag(soup, 'content')
@ -1888,7 +1918,7 @@ class EPUB_MOBI(CatalogPlugin):
if single_article_per_section:
contentTag['src'] = "%s#byauthor" % HTML_file
else:
contentTag['src'] = "%s#%sauthors" % (HTML_file, authors[1])
contentTag['src'] = "%s#%sauthors" % (HTML_file, authors[1].upper())
navPointByLetterTag.insert(1,contentTag)
cmTag = Tag(soup, '%s' % 'mbp:meta' if self.generateForMobigen else 'calibre:meta')
@ -2476,6 +2506,7 @@ class EPUB_MOBI(CatalogPlugin):
pw.MagickThumbnailImage(thumb, 75, 100)
pw.MagickWriteImage(thumb, os.path.join(image_dir, thumb_file))
pw.DestroyMagickWand(thumb)
pw.DestroyMagickWand(img)
except IOError:
print "generate_thumbnail() IOError with %s" % title['title']
except RuntimeError:
@ -2506,23 +2537,25 @@ class EPUB_MOBI(CatalogPlugin):
self.current_step += 1
self.progressString = description
self.progressInt = ((self.current_step-1)*100)/self.total_steps
self.reporter(self.progressInt, self.progressString)
return "%d%% %s" % (self.progressInt, self.progressString)
self.progressInt = float((self.current_step-1)/self.total_steps)
self.reporter(self.progressInt/100., self.progressString)
return "%.2f%% %s" % (self.progressInt, self.progressString)
def updateProgressMicroStep(self, description, micro_step_pct):
step_range = 100/self.total_steps
self.progressString = description
self.progressInt = ((self.current_step-1)*100)/self.total_steps + (micro_step_pct*step_range)/100
self.reporter(self.progressInt, self.progressString)
return "%d%% %s" % (self.progressInt, self.progressString)
coarse_progress = float((self.current_step-1)/self.total_steps)
fine_progress = float((micro_step_pct*step_range)/100)
self.progressInt = coarse_progress + fine_progress
self.reporter(self.progressInt/100., self.progressString)
return "%.2f%% %s" % (self.progressInt, self.progressString)
def run(self, path_to_output, opts, db, notification=DummyReporter()):
from calibre.utils.logging import Log
self.opts = opts
log = Log()
opts.fmt = self.fmt = path_to_output.rpartition('.')[2]
self.opts = opts
# Add local options
opts.creator = "calibre"
@ -2532,16 +2565,22 @@ class EPUB_MOBI(CatalogPlugin):
opts.plugin_path = self.plugin_path
if opts.verbose:
opts_dict = vars(opts)
log("%s:run" % self.name)
log(" path_to_output: %s" % path_to_output)
log(" Output format: %s" % self.fmt)
if opts_dict['ids']:
log(" Book count: %d" % len(opts_dict['ids']))
# Display opts
opts_dict = vars(opts)
keys = opts_dict.keys()
keys.sort()
log(" opts:")
for key in keys:
if key == 'ids':
if opts_dict[key]:
continue
else:
log(" %s: (all)" % key)
log(" %s: %s" % (key, opts_dict[key]))
# Launch the Catalog builder
@ -2552,6 +2591,11 @@ class EPUB_MOBI(CatalogPlugin):
recommendations = []
dp = getattr(opts, 'debug_pipeline', None)
if dp is not None:
recommendations.append(('debug_pipeline', dp,
OptionRecommendation.HIGH))
if opts.fmt == 'mobi' and opts.output_profile and opts.output_profile.startswith("kindle"):
recommendations.append(('output_profile', opts.output_profile,
OptionRecommendation.HIGH))
@ -2566,4 +2610,3 @@ class EPUB_MOBI(CatalogPlugin):
plumber.merge_ui_recommendations(recommendations)
plumber.run()

View File

@ -112,6 +112,7 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250,
format_args['title'] = mi.title
if mi.authors:
format_args['authors'] = mi.format_authors()
format_args['author'] = format_args['authors']
if mi.author_sort:
format_args['author_sort'] = mi.author_sort
if mi.tags:

View File

@ -4,9 +4,9 @@
#
msgid ""
msgstr ""
"Project-Id-Version: calibre 0.6.34\n"
"POT-Creation-Date: 2010-01-21 20:05+MST\n"
"PO-Revision-Date: 2010-01-21 20:05+MST\n"
"Project-Id-Version: calibre 0.6.35\n"
"POT-Creation-Date: 2010-01-22 16:20+MST\n"
"PO-Revision-Date: 2010-01-22 16:20+MST\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@ -216,19 +216,19 @@ msgstr ""
msgid "Set metadata from %s files"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/conversion.py:99
#: /home/kovid/work/calibre/src/calibre/customize/conversion.py:102
msgid "Conversion Input"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/conversion.py:122
#: /home/kovid/work/calibre/src/calibre/customize/conversion.py:125
msgid "Specify the character encoding of the input document. If set this option will override any encoding declared by the document itself. Particularly useful for documents that do not declare an encoding or that have erroneous encoding declarations."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/conversion.py:225
#: /home/kovid/work/calibre/src/calibre/customize/conversion.py:228
msgid "Conversion Output"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/conversion.py:239
#: /home/kovid/work/calibre/src/calibre/customize/conversion.py:242
msgid "If specified, the output plugin will try to create output that is as human readable as possible. May not have any effect for some output plugins."
msgstr ""
@ -2134,6 +2134,7 @@ msgid "CSV/XML Options"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_csv_xml.py:17
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:18
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input.py:16
#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output.py:16
#: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_input.py:13
@ -2151,6 +2152,7 @@ msgid "Options specific to"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_csv_xml.py:17
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:18
#: /home/kovid/work/calibre/src/calibre/gui2/convert/epub_output.py:16
#: /home/kovid/work/calibre/src/calibre/gui2/convert/fb2_output.py:15
#: /home/kovid/work/calibre/src/calibre/gui2/convert/lrf_output.py:20
@ -2162,7 +2164,8 @@ msgstr ""
msgid "output"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_csv_xml_ui.py:34
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_csv_xml_ui.py:36
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi_ui.py:51
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_tab_template_ui.py:27
#: /home/kovid/work/calibre/src/calibre/gui2/convert/comic_input_ui.py:84
#: /home/kovid/work/calibre/src/calibre/gui2/convert/debug_ui.py:49
@ -2193,10 +2196,34 @@ msgstr ""
msgid "Form"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_csv_xml_ui.py:35
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_csv_xml_ui.py:37
msgid "Fields to include in output:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:17
msgid "E-book Options"
msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi_ui.py:52
msgid "Tags to exclude as genres (regex):"
msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi_ui.py:53
msgid "'Don't include this book' tag:"
msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi_ui.py:54
msgid "'Mark this book as read' tag:"
msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi_ui.py:55
msgid "Additional note tag prefix:"
msgstr ""
#:
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_tab_template_ui.py:28
msgid "Tab template for catalog.ui"
@ -2799,7 +2826,7 @@ msgid "RB Output"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder.py:77
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1633
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1637
msgid "Choose the format to view"
msgstr ""
@ -3331,7 +3358,7 @@ msgstr ""
msgid "&Next"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/catalog.py:38
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/catalog.py:37
msgid "My Books"
msgstr ""
@ -3457,7 +3484,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:477
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:821
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:158
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1242
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1246
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53
msgid "Error"
msgstr ""
@ -5398,7 +5425,7 @@ msgid "Save to disk in a single directory"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:306
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1741
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1745
msgid "Save only %s format to disk"
msgstr ""
@ -5453,7 +5480,7 @@ msgid "Calibre Library"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:485
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1897
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1901
msgid "Choose a location for your ebook library."
msgstr ""
@ -5558,8 +5585,8 @@ msgid "Cannot delete"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1075
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1627
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1646
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1631
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1650
msgid "No book selected"
msgstr ""
@ -5567,219 +5594,227 @@ msgstr ""
msgid "Choose formats to be deleted"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1101
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1103
msgid "Choose formats <b>not</b> to be deleted"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1137
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1141
msgid "The selected books will be <b>permanently deleted</b> and the files removed from your computer. Are you sure?"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1164
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1168
msgid "Deleting books from device."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1195
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1199
msgid "Cannot download metadata"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1196
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1253
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1286
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1311
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1370
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1483
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1200
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1257
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1290
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1315
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1374
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1487
msgid "No books selected"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1211
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1215
msgid "social metadata"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1213
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1217
msgid "covers"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1213
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1217
msgid "metadata"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1215
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1219
msgid "Downloading %s for %d book(s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1237
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1241
msgid "Failed to download some metadata"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1238
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1242
msgid "Failed to download metadata for the following:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1241
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1245
msgid "Failed to download metadata:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1252
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1285
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1256
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1289
msgid "Cannot edit metadata"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1310
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1314
msgid "Cannot save to disk"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1313
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1317
msgid "Choose destination directory"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1340
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1344
msgid "Error while saving"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1341
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1345
msgid "There was an error while saving."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1348
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1349
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1352
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1353
msgid "Could not save some books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1350
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1354
msgid "Click the show details button to see which ones."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1371
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1375
msgid "No books selected to generate catalog for"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1388
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1392
msgid "Generating %s catalog..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1399
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1403
msgid "Catalog generated."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1417
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1406
msgid "Export Catalog Directory"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1407
msgid "Select destination for %s.%s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1421
msgid "Fetching news from "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1431
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1435
msgid " fetched."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1482
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1486
msgid "Cannot convert"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1511
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1515
msgid "Starting conversion of %d book(s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1627
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1683
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1631
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1687
msgid "Cannot view"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1645
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1649
msgid "Cannot open folder"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1667
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1671
msgid "Multiple Books Selected"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1668
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1672
msgid "You are attempting to open %d books. Opening too many books at once can be slow and have a negative effect on the responsiveness of your computer. Once started the process cannot be stopped until complete. Do you wish to continue?"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1684
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1688
msgid "%s has no available formats."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1725
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1729
msgid "Cannot configure"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1726
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1730
msgid "Cannot configure while there are running jobs."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1769
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1773
msgid "No detailed info available"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1770
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1774
msgid "No detailed information is available for books on the device."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1825
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1829
msgid "Error talking to device"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1826
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1830
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1849
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1877
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1853
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1881
msgid "Conversion Error"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1850
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1854
msgid "<p>Could not convert: %s<p>It is a <a href=\"%s\">DRM</a>ed book. You must first remove the DRM using third party tools."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1863
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1867
msgid "Recipe Disabled"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1878
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1882
msgid "<b>Failed</b>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1906
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1910
msgid "Invalid library location"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1907
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1911
msgid "Could not access %s. Using %s as the library."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1957
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1961
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/ui.py:1982
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1986
msgid "There are active jobs. Are you sure you want to quit?"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1985
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1989
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/ui.py:1989
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1993
msgid "WARNING: Active jobs"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:2041
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:2045
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/ui.py:2060
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:2064
msgid "<span style=\"color:red; font-weight:bold\">Latest version: <a href=\"%s\">%s</a></span>"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:2068
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:2072
msgid "Update available"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:2069
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:2073
msgid "%s has been updated to version %s. See the <a href=\"http://calibre-ebook.com/whats-new\">new features</a>. Visit the download page?"
msgstr ""
@ -5991,7 +6026,7 @@ msgstr ""
msgid "The standard font type"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:407
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:408
msgid "&Lookup in dictionary"
msgstr ""
@ -6524,7 +6559,7 @@ msgstr ""
msgid "The maximum number of matches to return per OPDS query. This affects Stanza, WordPlayer, etc. integration."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:28
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:34
msgid ""
"The fields to output when cataloging books in the database. Should be a comma-separated list of fields.\n"
"Available fields: %s.\n"
@ -6532,7 +6567,7 @@ msgid ""
"Applies to: CSV, XML output formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:37
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:43
msgid ""
"Output field to sort on.\n"
"Available fields: author_sort, id, rating, size, timestamp, title.\n"
@ -6540,6 +6575,49 @@ msgid ""
"Applies to: CSV, XML output formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:238
msgid ""
"Title of generated catalog used as title in metadata.\n"
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:244
msgid ""
"Regex describing tags to exclude as genres.\n"
"Default: '%default' excludes bracketed tags, e.g. '[<tag>]'\n"
"Applies to: ePub, MOBI output formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:249
msgid ""
"Comma-separated list of tag words indicating book should be excluded from output. Case-insensitive.\n"
"--exclude-tags=skip will match 'skip this book' and 'Skip will like this'.\n"
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:256
msgid ""
"Tag indicating book has been read.\n"
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:261
msgid ""
"Tag prefix for user notes, e.g. '*Jeff might enjoy reading this'.\n"
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:267
msgid ""
"Specifies the output profile. In some cases, an output profile is required to optimize the catalog for the device. For example, 'kindle' or 'kindle_dx' creates a structured Table of Contents with Sections and Articles.\n"
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/cli.py:121
msgid "Path to the calibre library. Default is to use the path stored in the settings."
msgstr ""