0.7.27 updates

This commit is contained in:
GRiker 2010-11-06 05:15:08 -07:00
commit c243416d71
72 changed files with 10969 additions and 7296 deletions

View File

@ -4,6 +4,95 @@
# for important features/bug fixes.
# Also, each release can have new and improved recipes.
- version: 0.7.27
date: 2010-11-05
new features:
- title: "The book list behavior has changed"
type: major
description: >
"Now double clicking on an entry in the book list will open it in the viewer. To edit metadata single click a previously selected entry instead. This is consistent with
the usage in most operating systems, so should be most intuitive for new users. Also typing any key no longer starts an edit, instead press F2 (Enter on OS X) to start editing
the current cell. Also you now have to double click instead of single clicking the book details panel to open the detailed info dialog for the book."
- title: "Added a new HTML output format plugin, which converts the input document to a ZIP file. The zip file contains HTML pages suitable for display in a website"
- title: "Support for iRiver Cover Story and Digma Q600"
- title: "Add a search button (labelled Go!) to explicitly run a search with the text currently in the quick search box"
- title: "Add author to the calibre geenrated book jacket"
tickets: [7402]
- title: "Add the title of the destination book to the merge dialog warning message"
- title: "calibre-server: Make auto reload control separate from --devlop with a new command line option --auto-reload"
bug fixes:
- title: "Fix book details panel not being updated after a delete-merge"
tickets: [7426]
- title: "Fix clicking in the search box launches a search if you have search as you type enabled"
tickets: [7425]
- title: "Use a browser widget to display book details for more robustness and better performance when vieweing large HTML comments"
- title: "Fix cover browser not updated after copy to library and delete"
tickets: [7416]
- title: "Fix regression that broke sending non calibre EPUB files to the iPad. Also handle failure to set cover in iTunes gracefully"
tickets: [7356]
- title: "News download: Workaround lack of thread safety in python mechanize, causing corrupted network packets (degrading network performance) on Ubuntu Maverick 64bit kernels"
- title: "Convert comments to HTML for book details panel in separate thread to make scrolling through the book list faster when large comments are present"
- title: "calibre-server: Fix regression that broke --daemonize"
- title: "EPUB Input: Handle ncx files that have <navpoint> elements with no content correctly."
tickets: [7396]
- title: "SNBOutput: Fixed a bug in handling pre tag"
- title: "MOBI Output: Don't ignore hidden anchors."
tickets: [7384]
- title: "Fix switching libraries and generating a catalog could generate a catalog for the wrong library"
- title: "MOBI Output: Fix regression that broke conversion of anchors inside superscripts/subscripts."
tickets: [7368]
- title: "Content server: Fix various minor bugs"
tickets: [7379, 6768, 7354]
- title: "Amazon metadata download plugin: Make it more robust and add option to auto convert HTML to text"
- title: "Re-arrange send to device menu to make it harder to accidentally trigger the send and delete actions"
improved recipes:
- Danas
- Fudzilla
- Zeit Online
- New York Times
- Mediapart
new recipes:
- title: "Ynet and Calcalist"
author: "marbs"
- title: "El Faro de Vigo"
author: "Jefferson Frantz"
- title: "Clic_RBS"
author: "avoredo"
- title: "Correio da Manha"
author: "jmst"
- title: "Rue89"
author: "Louis Gesbert"
- version: 0.7.26
date: 2010-10-30

View File

@ -39,7 +39,16 @@
.cbj_title {
font-size: x-large;
text-align: center;
}
}
/*
** Author
*/
.cbj_author {
font-size: medium;
text-align: center;
margin-bottom: 1ex;
}
/*
** Table containing Series, Publication Year, Rating and Tags

View File

@ -7,6 +7,7 @@
<body>
<div class="cbj_banner">
<div class="cbj_title">{title}</div>
<div class="cbj_author">{author}</div>
<table class="cbj_header">
<tr class="cbj_series">
<td class="cbj_label">{series_label}:</td>

View File

@ -0,0 +1,43 @@
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import re
class AdvancedUserRecipe1283848012(BasicNewsRecipe):
description = 'This is a recipe of Calcalist.co.il. The recipe downloads the article page to not hurt the sites advertising income.'
cover_url = 'http://ftp5.bizportal.co.il/web/giflib/news/calcalist.JPG'
title = u'Calcalist'
language = 'he'
__author__ = 'marbs'
extra_css='img {max-width:100%;} body{direction: rtl;},title{direction: rtl; } ,article_description{direction: rtl; }, a.article{direction: rtl; } ,calibre_feed_description{direction: rtl; }'
simultaneous_downloads = 5
remove_javascript = True
timefmt = '[%a, %d %b, %Y]'
oldest_article = 1
max_articles_per_feed = 100
remove_attributes = ['width']
simultaneous_downloads = 5
keep_only_tags =dict(name='div', attrs={'id':'articleContainer'})
remove_tags = [dict(name='p', attrs={'text':['&nbsp;']})]
max_articles_per_feed = 100
preprocess_regexps = [
(re.compile(r'<p>&nbsp;</p>', re.DOTALL|re.IGNORECASE), lambda match: '')
]
feeds = [(u'\u05d3\u05e3 \u05d4\u05d1\u05d9\u05ea', u'http://www.calcalist.co.il/integration/StoryRss8.xml'),
(u'24/7', u'http://www.calcalist.co.il/integration/StoryRss3674.xml'),
(u'\u05d1\u05d0\u05d6\u05d6', u'http://www.calcalist.co.il/integration/StoryRss3674.xml'),
(u'\u05de\u05d1\u05d6\u05e7\u05d9\u05dd', u'http://www.calcalist.co.il/integration/StoryRss184.xml'),
(u'\u05d4\u05e9\u05d5\u05e7', u'http://www.calcalist.co.il/integration/StoryRss2.xml'),
(u'\u05d1\u05d0\u05e8\u05e5', u'http://www.calcalist.co.il/integration/StoryRss14.xml'),
(u'\u05d4\u05db\u05e1\u05e3', u'http://www.calcalist.co.il/integration/StoryRss9.xml'),
(u'\u05e0\u05d3\u05dc"\u05df', u'http://www.calcalist.co.il/integration/StoryRss7.xml'),
(u'\u05e2\u05d5\u05dc\u05dd', u'http://www.calcalist.co.il/integration/StoryRss13.xml'),
(u'\u05e4\u05e8\u05e1\u05d5\u05dd \u05d5\u05e9\u05d9\u05d5\u05d5\u05e7', u'http://www.calcalist.co.il/integration/StoryRss5.xml'),
(u'\u05e4\u05e0\u05d0\u05d9', u'http://www.calcalist.co.il/integration/StoryRss3.xml'),
(u'\u05d8\u05db\u05e0\u05d5\u05dc\u05d5\u05d2\u05d9', u'http://www.calcalist.co.il/integration/StoryRss4.xml'),
(u'\u05e2\u05e1\u05e7\u05d9 \u05e1\u05e4\u05d5\u05e8\u05d8', u'http://www.calcalist.co.il/integration/StoryRss18.xml')]
def print_version(self, url):
split1 = url.split("-")
print_url = 'http://www.calcalist.co.il/Ext/Comp/ArticleLayout/CdaArticlePrintPreview/1,2506,L-' + split1[1]
return print_url

View File

@ -59,6 +59,9 @@ class Danas(BasicNewsRecipe):
,(re.compile(u'\u201d'), lambda match: '&rdquo;') # right double quotation mark
,(re.compile(u'\u201e'), lambda match: '&ldquo;') # double low-9 quotation mark
,(re.compile(u'\u201f'), lambda match: '&rdquo;') # double high-reversed-9 quotation mark
,(re.compile(u'\u00f4'), lambda match: '&ldquo;') # latin small letter o with circumflex
,(re.compile(u'\u00f6'), lambda match: '&rdquo;') # latin small letter o with dieaeresis
,(re.compile(u'\u00e1'), lambda match: '&nbsp;' ) # latin small letter a with acute
]
keep_only_tags = [dict(name='div', attrs={'id':'left'})]

View File

@ -1,38 +0,0 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
'''
Fetch elektrolese.
'''
from calibre.web.feeds.news import BasicNewsRecipe
class elektrolese(BasicNewsRecipe):
title = u'elektrolese'
description = 'News about electronic publishing'
__author__ = 'Oliver Niesner'
use_embedded_content = False
timefmt = ' [%a %d %b %Y]'
language = 'de'
oldest_article = 14
max_articles_per_feed = 50
no_stylesheets = True
conversion_options = {'linearize_tables':True}
encoding = 'utf-8'
remove_tags_after = [dict(id='comments')]
filter_regexps = [r'ad\.doubleclick\.net']
remove_tags = [dict(name='div', attrs={'class':'bannerSuperBanner'}),
dict(id='comments'),
dict(id='Navbar1')]
feeds = [ (u'elektrolese', u'http://elektrolese.blogspot.com/feeds/posts/default?alt=rss') ]

View File

@ -25,15 +25,15 @@ class Fudzilla(BasicNewsRecipe):
remove_tags_before = dict(name='div', attrs={'class':['padding']})
remove_tags = [dict(name='td', attrs={'class':['left','right']}),
dict(name='div', attrs={'id':['toolbar','buttons']}),
dict(name='div', attrs={'class':['artbannersxtd','back_button']}),
dict(name='span', attrs={'class':['pathway']}),
dict(name='th', attrs={'class':['pagenav_next','pagenav_prev']}),
dict(name='table', attrs={'class':['headlines']}),
dict(name='div', attrs={'id':['toolbar','buttons']}),
dict(name='div', attrs={'class':['artbannersxtd','back_button']}),
dict(name='span', attrs={'class':['pathway']}),
dict(name='th', attrs={'class':['pagenav_next','pagenav_prev']}),
dict(name='table', attrs={'class':['headlines']}),
]
feeds = [
(u'Posts', u'http://www.fudzilla.com/index.php?option=com_rss&feed=RSS2.0&no_html=1')
(u'Posts', u'http://www.fudzilla.com/?format=feed')
]
preprocess_regexps = [

View File

@ -38,6 +38,7 @@ class LaJornada_mx(BasicNewsRecipe):
.loc{font-weight: bold}
.carton{text-align: center}
.credit{font-weight: bold}
.sumario{font-weight: bold; text-align: center}
.text{margin-top: 1.4em}
p.inicial{display: inline; font-size: xx-large; font-weight: bold}
p.s-s{display: inline; text-indent: 0}
@ -53,7 +54,10 @@ class LaJornada_mx(BasicNewsRecipe):
preprocess_regexps = [
(re.compile( r'<div class="inicial">(.*)</div><p class="s-s">'
,re.DOTALL|re.IGNORECASE)
,lambda match: '<p class="inicial">' + match.group(1) + '</p><p class="s-s">')
,lambda match: '<p class="inicial">' + match.group(1) + '</p><p class="s-s">'),
(re.compile( r'<q>(.*?)</q>'
,re.DOTALL|re.IGNORECASE)
,lambda match: '"' + match.group(1) + '"')
]
keep_only_tags = [

View File

@ -5,65 +5,61 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
'''
nytimes.com
'''
import re
import time
from calibre import entity_to_unicode
import re, string, time
from calibre import entity_to_unicode, strftime
from calibre.web.feeds.recipes import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag, NavigableString, \
Comment, BeautifulStoneSoup
from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag, BeautifulStoneSoup
class NYTimes(BasicNewsRecipe):
title = 'New York Times Top Stories'
__author__ = 'GRiker'
language = 'en'
requires_version = (0, 7, 5)
description = 'Top Stories from the New York Times'
# set headlinesOnly to True for the headlines-only version
headlinesOnly = True
# List of sections typically included in Top Stories. Use a keyword from the
# right column in the excludeSectionKeywords[] list to skip downloading that section
sections = {
'arts' : 'Arts',
'business' : 'Business',
'diningwine' : 'Dining & Wine',
'editorials' : 'Editorials',
'health' : 'Health',
'magazine' : 'Magazine',
'mediaadvertising' : 'Media & Advertising',
'newyorkregion' : 'New York/Region',
'oped' : 'Op-Ed',
'politics' : 'Politics',
'science' : 'Science',
'sports' : 'Sports',
'technology' : 'Technology',
'topstories' : 'Top Stories',
'travel' : 'Travel',
'us' : 'U.S.',
'world' : 'World'
}
# includeSections: List of sections to include. If empty, all sections found will be included.
# Otherwise, only the sections named will be included. For example,
#
# includeSections = ['Politics','Sports']
#
# would cause only the Politics and Sports sections to be included.
# Add section keywords from the right column above to skip that section
# For example, to skip sections containing the word 'Sports' or 'Dining', use:
# excludeSectionKeywords = ['Sports', 'Dining']
# Fetch only Business and Technology
# excludeSectionKeywords = ['Arts','Dining','Editorials','Health','Magazine','Media','Region','Op-Ed','Politics','Science','Sports','Top Stories','Travel','U.S.','World']
# Fetch only Top Stories
# excludeSectionKeywords = ['Arts','Business','Dining','Editorials','Health','Magazine','Media','Region','Op-Ed','Politics','Science','Sports','Technology','Travel','U.S.','World']
# By default, no sections are skipped.
excludeSectionKeywords = []
includeSections = [] # by default, all sections included
# excludeSections: List of sections to exclude. If empty, all sections found will be included.
# Otherwise, the sections named will be excluded. For example,
#
# excludeSections = ['Politics','Sports']
#
# would cause the Politics and Sports sections to be excluded. This parameter can be used
# in conjuction with includeSections although in most cases using one or the other, but
# not both, is sufficient.
excludeSections = []
# one_picture_per_article specifies that calibre should only use the first image
# from an article (if one exists). If one_picture_per_article = True, the image
# will be moved to a location between the headline and the byline.
# If one_picture_per_article = False, all images from the article will be included
# and shown in their original location.
one_picture_per_article = True
# The maximum number of articles that will be downloaded
max_articles_per_feed = 40
max_articles_per_feed = 100
if headlinesOnly:
title='New York Times Headlines'
description = 'Headlines from the New York Times'
else:
title='New York Times'
description = 'Today\'s New York Times'
__author__ = 'GRiker/Kovid Goyal/Nick Redding'
language = 'en'
requires_version = (0, 7, 5)
timefmt = ''
needs_subscription = True
masthead_url = 'http://graphics8.nytimes.com/images/misc/nytlogo379x64.gif'
cover_margins = (18,18,'grey99')
@ -82,6 +78,7 @@ class NYTimes(BasicNewsRecipe):
'entry-response module',
'icon enlargeThis',
'leftNavTabs',
'metaFootnote',
'module box nav',
'nextArticleLink',
'nextArticleLink clearfix',
@ -89,12 +86,13 @@ class NYTimes(BasicNewsRecipe):
'relatedSearchesModule',
'side_tool',
'singleAd',
'subNavigation clearfix',
'subNavigation tabContent active',
'subNavigation tabContent active clearfix',
re.compile('^subNavigation'),
re.compile('^leaderboard'),
re.compile('^module'),
]}),
dict(id=[
'adxLeaderboard',
'adxSponLink',
'archive',
'articleExtras',
'articleInline',
@ -105,87 +103,98 @@ class NYTimes(BasicNewsRecipe):
'footer',
'header',
'header_search',
'inlineBox',
'login',
'masthead',
'masthead-nav',
'memberTools',
'navigation',
'portfolioInline',
'readerReviews',
'readerReviewsCount',
'relatedArticles',
'relatedTopics',
'respond',
'side_search',
'side_index',
'side_tool',
'toolsRight',
]),
dict(name=['script', 'noscript', 'style'])]
dict(name=['script', 'noscript', 'style','form','hr'])]
no_stylesheets = True
extra_css = '.headline {text-align: left;}\n \
.byline {font-family: monospace; \
text-align: left; \
margin-top: 0px; \
margin-bottom: 0px;}\n \
.dateline {font-size: small; \
margin-top: 0px; \
margin-bottom: 0px;}\n \
.timestamp {font-size: small; \
margin-top: 0px; \
margin-bottom: 0px;}\n \
.source {text-align: left;}\n \
.image {text-align: center;}\n \
.credit {text-align: right; \
font-size: small; \
margin-top: 0px; \
margin-bottom: 0px;}\n \
.articleBody {text-align: left;}\n \
.authorId {text-align: left; \
font-style: italic;}\n '
extra_css = '''
.articleHeadline { text-align: left; margin-top:0.5em; margin-bottom:0.25em; }
.credit { text-align: right; font-size: small; line-height:1em; margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
.byline { text-align: left; font-size: small; line-height:1em; margin-top:10px; margin-left:0; margin-right:0; margin-bottom: 0; }
.dateline { text-align: left; font-size: small; line-height:1em;margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
.kicker { font-size: small; line-height:1em;margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
.timestamp { text-align: left; font-size: small; }
.caption { font-size: small; font-style:italic; line-height:1em; margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
a:link {text-decoration: none; }
.articleBody { }
.authorId {text-align: left; }
.image {text-align: center;}
.source {text-align: left; }'''
def dump_ans(self, ans) :
def filter_ans(self, ans) :
total_article_count = 0
for section in ans :
idx = 0
idx_max = len(ans)-1
while idx <= idx_max:
if self.includeSections != []:
if ans[idx][0] not in self.includeSections:
print "SECTION NOT INCLUDED: ",ans[idx][0]
del ans[idx]
idx_max = idx_max-1
continue
if ans[idx][0] in self.excludeSections:
print "SECTION EXCLUDED: ",ans[idx][0]
del ans[idx]
idx_max = idx_max-1
continue
if self.verbose:
self.log("section %s: %d articles" % (section[0], len(section[1])) )
for article in section[1]:
self.log("Section %s: %d articles" % (ans[idx][0], len(ans[idx][1])) )
for article in ans[idx][1]:
total_article_count += 1
if self.verbose:
self.log("\t%-40.40s... \t%-60.60s..." % (article['title'].encode('cp1252','replace'),
article['url'].encode('cp1252','replace')))
idx = idx+1
self.log( "Queued %d articles" % total_article_count )
return ans
def fixChars(self,string):
# Replace lsquo (\x91)
fixed = re.sub("\x91","&#8216;",string)
fixed = re.sub("\x91","",string)
# Replace rsquo (\x92)
fixed = re.sub("\x92","&#8217;",fixed)
fixed = re.sub("\x92","",fixed)
# Replace ldquo (\x93)
fixed = re.sub("\x93","&#8220;",fixed)
fixed = re.sub("\x93","",fixed)
# Replace rdquo (\x94)
fixed = re.sub("\x94","&#8221;",fixed)
fixed = re.sub("\x94","",fixed)
# Replace ndash (\x96)
fixed = re.sub("\x96","&#8211;",fixed)
fixed = re.sub("\x96","",fixed)
# Replace mdash (\x97)
fixed = re.sub("\x97","&#8212;",fixed)
fixed = re.sub("\x97","",fixed)
return fixed
def get_browser(self):
br = BasicNewsRecipe.get_browser()
if self.username is not None and self.password is not None:
try:
br.open('http://www.nytimes.com/auth/login')
br.select_form(name='login')
br['USERID'] = self.username
br['PASSWORD'] = self.password
br.submit()
except:
self.log("\nFailed to login")
br.open('http://www.nytimes.com/auth/login')
br.select_form(name='login')
br['USERID'] = self.username
br['PASSWORD'] = self.password
raw = br.submit().read()
if 'Please try again' in raw:
raise Exception('Your username and password are incorrect')
return br
def skip_ad_pages(self, soup):
@ -213,6 +222,9 @@ class NYTimes(BasicNewsRecipe):
cover = None
return cover
def short_title(self):
return self.title
def index_to_soup(self, url_or_raw, raw=False):
'''
OVERRIDE of class method
@ -255,157 +267,184 @@ class NYTimes(BasicNewsRecipe):
# Kindle TOC descriptions won't render certain characters
if description:
massaged = unicode(BeautifulStoneSoup(description, convertEntities=BeautifulStoneSoup.HTML_ENTITIES))
# Replace '&' with '&#38;'
massaged = re.sub("&","&#38;", massaged)
# Replace '&' with '&'
massaged = re.sub("&","&", massaged)
return self.fixChars(massaged)
else:
return description
def parse_index(self):
def parse_todays_index(self):
def feed_title(div):
return ''.join(div.findAll(text=True, recursive=True)).strip()
articles = {}
key = None
ans = []
url_list = []
def handle_article(div):
a = div.find('a', href=True)
if not a:
return
url = re.sub(r'\?.*', '', a['href'])
if not url.startswith("http"):
return
if not url.endswith(".html"):
return
if 'podcast' in url:
return
if '/video/' in url:
return
url += '?pagewanted=all'
if url in url_list:
return
url_list.append(url)
title = self.tag_to_string(a, use_alt=True).strip()
description = ''
pubdate = strftime('%a, %d %b')
summary = div.find(True, attrs={'class':'summary'})
if summary:
description = self.tag_to_string(summary, use_alt=False)
author = ''
authorAttribution = div.find(True, attrs={'class':'byline'})
if authorAttribution:
author = self.tag_to_string(authorAttribution, use_alt=False)
else:
authorAttribution = div.find(True, attrs={'class':'byline'})
if authorAttribution:
author = self.tag_to_string(authorAttribution, use_alt=False)
feed = key if key is not None else 'Uncategorized'
if not articles.has_key(feed):
ans.append(feed)
articles[feed] = []
articles[feed].append(
dict(title=title, url=url, date=pubdate,
description=description, author=author,
content=''))
soup = self.index_to_soup('http://www.nytimes.com/pages/todayspaper/index.html')
# Find each article
for div in soup.findAll(True,
attrs={'class':['section-headline', 'story', 'story headline','sectionHeader','headlinesOnly multiline flush']}):
if div['class'] in ['section-headline','sectionHeader']:
key = string.capwords(feed_title(div))
key = key.replace('Op-ed','Op-Ed')
key = key.replace('U.s.','U.S.')
elif div['class'] in ['story', 'story headline'] :
handle_article(div)
elif div['class'] == 'headlinesOnly multiline flush':
for lidiv in div.findAll('li'):
handle_article(lidiv)
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
return self.filter_ans(ans)
def parse_headline_index(self):
articles = {}
ans = []
feed = key = 'All Top Stories'
articles[key] = []
ans.append(key)
self.log("Scanning 1 section ...")
url_list = []
soup = self.index_to_soup('http://www.nytimes.com/pages/todaysheadlines/')
# Fetch the outer table
table = soup.find('table')
previousTable = table
# Fetch the content table
content_table = soup.find('table',{'id':'content'})
if content_table is None:
self.log("FATAL ERROR: CANNOT FIND CONTENT TABLE")
return None
# Find the deepest table containing the stories
while True :
table = table.find('table')
if table.find(text=re.compile('top stories start')) :
previousTable = table
continue
else :
table = previousTable
break
# Within this table are <td id=".*Column.*"> entries, each containing one or more h6 tags which represent sections
# There are multiple subtables, find the one containing the stories
for block in table.findAll('table') :
if block.find(text=re.compile('top stories start')) :
table = block
break
else :
continue
for td_col in content_table.findAll('td', {'id' : re.compile('Column')}):
for div_sec in td_col.findAll('div',recursive=False):
for h6_sec_name in div_sec.findAll('h6',{'style' : re.compile('text-transform: *uppercase')}):
section_name = self.tag_to_string(h6_sec_name,use_alt=False)
section_name = re.sub(r'^ *$','',section_name)
if section_name == '':
continue
section_name=string.capwords(section_name)
if section_name == 'U.s.':
section_name = 'U.S.'
elif section_name == 'Op-ed':
section_name = 'Op-Ed'
pubdate = strftime('%a, %d %b')
# Again there are multiple subtables, find the one containing the stories
for storyblock in table.findAll('table') :
if storyblock.find(text=re.compile('top stories start')) :
break
else :
continue
skipThisSection = False
todays_article_count = 0
# Within this table are <font face="times new roman, times, san serif"> entries
self.log("Fetching feed Top Stories")
for tr in storyblock.findAllNext('tr'):
if tr.find('span') is not None :
sectionblock = tr.find(True, attrs={'face':['times new roman, times,sans serif',
'times new roman,times, sans serif',
'times new roman, times, sans serif']})
section = None
bylines = []
descriptions = []
pubdate = None
# Get the Section title
for (x,i) in enumerate(sectionblock.contents) :
skipThisSection = False
# Extract the section title
if ('Comment' in str(i.__class__)) :
if 'start(name=' in i :
section = i[i.find('=')+1:-2]
if not self.sections.has_key(section) :
skipThisSection = True
search_div = div_sec
for next_tag in h6_sec_name.findNextSiblings(True):
if next_tag.__class__.__name__ == 'Tag':
if next_tag.name == 'div':
search_div = next_tag
break
# Check for excluded section
if len(self.excludeSectionKeywords):
key = self.sections[section]
excluded = re.compile('|'.join(self.excludeSectionKeywords))
if excluded.search(key) or articles.has_key(key):
skipThisSection = True
break
# Get the bylines and descriptions
if not skipThisSection :
lines = sectionblock.contents
contentStrings = []
for line in lines:
if not isinstance(line, Comment) and line.strip and line.strip() > "":
contentStrings.append(line.strip())
# Gather the byline/description pairs
bylines = []
descriptions = []
for contentString in contentStrings:
if contentString[0:3] == 'By ' and contentString[3].isupper() :
bylines.append(contentString)
# Get the articles
for h3_item in search_div.findAll('h3'):
byline = h3_item.h6
if byline is not None:
author = self.tag_to_string(byline,usa_alt=False)
else:
descriptions.append(contentString)
# Fetch the article titles and URLs
articleCount = len(sectionblock.findAll('span'))
todays_article_count += articleCount
for (i,span) in enumerate(sectionblock.findAll(attrs={'class':'headlineWrapper'})) :
a = span.find('a', href=True)
author = ''
a = h3_item.find('a', href=True)
if not a:
continue
url = re.sub(r'\?.*', '', a['href'])
if not url.startswith("http"):
continue
if not url.endswith(".html"):
continue
if 'podcast' in url:
continue
if 'video' in url:
continue
url += '?pagewanted=all'
if url in url_list:
continue
url_list.append(url)
self.log("URL %s" % url)
title = self.tag_to_string(a, use_alt=True).strip()
desc = h3_item.find('p')
if desc is not None:
description = self.tag_to_string(desc,use_alt=False)
else:
description = ''
if not articles.has_key(section_name):
ans.append(section_name)
articles[section_name] = []
articles[section_name].append(dict(title=title, url=url, date=pubdate, description=description, author=author, content=''))
title = self.tag_to_string(a, use_alt=True)
# prepend the section name
title = self.sections[section] + " &middot; " + title
if not isinstance(title, unicode):
title = title.decode('utf-8', 'replace')
# Allow for unattributed, undescribed entries "Editor's Note"
if i >= len(descriptions) :
description = None
else :
description = descriptions[i]
if len(bylines) == articleCount :
author = bylines[i]
else :
author = None
# Check for duplicates
duplicateFound = False
if len(articles[feed]) > 1:
for article in articles[feed] :
if url == article['url'] :
duplicateFound = True
break
if duplicateFound:
# Continue fetching, don't add this article
todays_article_count -= 1
continue
if not articles.has_key(feed):
articles[feed] = []
articles[feed].append(
dict(title=title, url=url, date=pubdate,
description=description, author=author, content=''))
# self.log("Queuing %d articles from %s" % (todays_article_count, "Top Stories"))
ans = self.sort_index_by(ans, {'Top Stories':-1})
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
self.dump_ans(ans)
return ans
return self.filter_ans(ans)
def parse_index(self):
if self.headlinesOnly:
return self.parse_headline_index()
else:
return self.parse_todays_index()
def strip_anchors(self,soup):
paras = soup.findAll(True)
for para in paras:
aTags = para.findAll('a')
for a in aTags:
if a.img is None:
a.replaceWith(a.renderContents().decode('cp1252','replace'))
return soup
def preprocess_html(self, soup):
kicker_tag = soup.find(attrs={'class':'kicker'})
if kicker_tag: # remove Op_Ed author head shots
tagline = self.tag_to_string(kicker_tag)
if tagline=='Op-Ed Columnist':
img_div = soup.find('div','inlineImage module')
if img_div:
img_div.extract()
return self.strip_anchors(soup)
def postprocess_html(self,soup, True):
@ -422,8 +461,9 @@ class NYTimes(BasicNewsRecipe):
firstImg = inlineImgs[0]
for inlineImg in inlineImgs[1:]:
inlineImg.extract()
# Move firstImg after headline
cgFirst = soup.find(True, {'class':'columnGroup first'})
# Move firstImg before article body
#article_body = soup.find(True, {'id':'articleBody'})
cgFirst = soup.find(True, {'class':re.compile('columnGroup *first')})
if cgFirst:
# Strip all sibling NavigableStrings: noise
navstrings = cgFirst.findAll(text=True, recursive=False)
@ -443,30 +483,18 @@ class NYTimes(BasicNewsRecipe):
if headline_found:
cgFirst.insert(insertLoc,firstImg)
else:
self.log(">>> No class:'columnGroup first' found <<<")
# Change class="kicker" to <h3>
kicker = soup.find(True, {'class':'kicker'})
if kicker and kicker.contents[0]:
h3Tag = Tag(soup, "h3")
h3Tag.insert(0, self.fixChars(self.tag_to_string(kicker,
use_alt=False)))
kicker.replaceWith(h3Tag)
self.log(">>> No class:'columnGroup first' found <<<")
# Change captions to italic -1
# Change captions to italic
for caption in soup.findAll(True, {'class':'caption'}) :
if caption and caption.contents[0]:
emTag = Tag(soup, "em")
cTag = Tag(soup, "p", [("class", "caption")])
c = self.fixChars(self.tag_to_string(caption,use_alt=False)).strip()
mp_off = c.find("More Photos")
if mp_off >= 0:
c = c[:mp_off]
emTag.insert(0, c)
#hrTag = Tag(soup, 'hr')
#hrTag['class'] = 'caption_divider'
hrTag = Tag(soup, 'div')
hrTag['class'] = 'divider'
emTag.insert(1, hrTag)
caption.replaceWith(emTag)
cTag.insert(0, c)
caption.replaceWith(cTag)
# Change <nyt_headline> to <h2>
h1 = soup.find('h1')
@ -506,17 +534,6 @@ class NYTimes(BasicNewsRecipe):
bTag.insert(0, subhead.contents[0])
subhead.replaceWith(bTag)
# Synthesize a section header
dsk = soup.find('meta', attrs={'name':'dsk'})
if dsk and dsk.has_key('content'):
hTag = Tag(soup,'h3')
hTag['class'] = 'section'
hTag.insert(0,NavigableString(dsk['content']))
articleTag = soup.find(True, attrs={'id':'article'})
if articleTag:
articleTag.insert(0,hTag)
# Add class="articleBody" to <div> so we can format with CSS
divTag = soup.find('div',attrs={'id':'articleBody'})
if divTag:
divTag['class'] = divTag['id']
@ -532,11 +549,3 @@ class NYTimes(BasicNewsRecipe):
return soup
def strip_anchors(self,soup):
paras = soup.findAll(True)
for para in paras:
aTags = para.findAll('a')
for a in aTags:
if a.img is None:
a.replaceWith(a.renderContents().decode('cp1252','replace'))
return soup

View File

@ -5,52 +5,186 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
'''
nytimes.com
'''
import string, re, time
from calibre import strftime
import re, string, time
from calibre import entity_to_unicode, strftime
from calibre.web.feeds.recipes import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup
def decode(self, src):
enc = 'utf-8'
if 'iso-8859-1' in src:
enc = 'cp1252'
return src.decode(enc, 'ignore')
from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag, BeautifulStoneSoup
class NYTimes(BasicNewsRecipe):
title = u'New York Times'
__author__ = 'Kovid Goyal/Nick Redding'
language = 'en'
requires_version = (0, 6, 36)
# set headlinesOnly to True for the headlines-only version
headlinesOnly = False
description = 'Daily news from the New York Times (subscription version)'
timefmt = ' [%b %d]'
# includeSections: List of sections to include. If empty, all sections found will be included.
# Otherwise, only the sections named will be included. For example,
#
# includeSections = ['Politics','Sports']
#
# would cause only the Politics and Sports sections to be included.
includeSections = [] # by default, all sections included
# excludeSections: List of sections to exclude. If empty, all sections found will be included.
# Otherwise, the sections named will be excluded. For example,
#
# excludeSections = ['Politics','Sports']
#
# would cause the Politics and Sports sections to be excluded. This parameter can be used
# in conjuction with includeSections although in most cases using one or the other, but
# not both, is sufficient.
excludeSections = []
# one_picture_per_article specifies that calibre should only use the first image
# from an article (if one exists). If one_picture_per_article = True, the image
# will be moved to a location between the headline and the byline.
# If one_picture_per_article = False, all images from the article will be included
# and shown in their original location.
one_picture_per_article = True
# The maximum number of articles that will be downloaded
max_articles_per_feed = 100
if headlinesOnly:
title='New York Times Headlines'
description = 'Headlines from the New York Times'
else:
title='New York Times'
description = 'Today\'s New York Times'
__author__ = 'GRiker/Kovid Goyal/Nick Redding'
language = 'en'
requires_version = (0, 7, 5)
timefmt = ''
needs_subscription = True
masthead_url = 'http://graphics8.nytimes.com/images/misc/nytlogo379x64.gif'
cover_margins = (18,18,'grey99')
remove_tags_before = dict(id='article')
remove_tags_after = dict(id='article')
remove_tags = [dict(attrs={'class':['articleTools', 'post-tools', 'side_tool','nextArticleLink',
'nextArticleLink clearfix','columnGroup doubleRule','doubleRule','entry-meta',
'icon enlargeThis','columnGroup last','relatedSearchesModule']}),
dict({'class':re.compile('^subNavigation')}),
dict({'class':re.compile('^leaderboard')}),
dict({'class':re.compile('^module')}),
dict({'class':'metaFootnote'}),
dict(id=['inlineBox','footer', 'toolsRight', 'articleInline','login','masthead',
'navigation', 'archive', 'side_search', 'blog_sidebar','cCol','portfolioInline',
'side_tool', 'side_index','header','readerReviewsCount','readerReviews',
'relatedArticles', 'relatedTopics', 'adxSponLink']),
remove_tags = [dict(attrs={'class':[
'articleFooter',
'articleTools',
'columnGroup doubleRule',
'columnGroup singleRule',
'columnGroup last',
'columnGroup last',
'doubleRule',
'dottedLine',
'entry-meta',
'entry-response module',
'icon enlargeThis',
'leftNavTabs',
'metaFootnote',
'module box nav',
'nextArticleLink',
'nextArticleLink clearfix',
'post-tools',
'relatedSearchesModule',
'side_tool',
'singleAd',
re.compile('^subNavigation'),
re.compile('^leaderboard'),
re.compile('^module'),
]}),
dict(id=[
'adxLeaderboard',
'adxSponLink',
'archive',
'articleExtras',
'articleInline',
'blog_sidebar',
'businessSearchBar',
'cCol',
'entertainmentSearchBar',
'footer',
'header',
'header_search',
'inlineBox',
'login',
'masthead',
'masthead-nav',
'memberTools',
'navigation',
'portfolioInline',
'readerReviews',
'readerReviewsCount',
'relatedArticles',
'relatedTopics',
'respond',
'side_search',
'side_index',
'side_tool',
'toolsRight',
]),
dict(name=['script', 'noscript', 'style','form','hr'])]
encoding = decode
no_stylesheets = True
extra_css = '''
.articleHeadline { margin-top:0.5em; margin-bottom:0.25em; }
.credit { font-size: small; font-style:italic; line-height:1em; margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
.byline { font-size: small; font-style:italic; line-height:1em; margin-top:10px; margin-left:0; margin-right:0; margin-bottom: 0; }
.dateline { font-size: small; line-height:1em;margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
.articleHeadline { text-align: left; margin-top:0.5em; margin-bottom:0.25em; }
.credit { text-align: right; font-size: small; line-height:1em; margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
.byline { text-align: left; font-size: small; line-height:1em; margin-top:10px; margin-left:0; margin-right:0; margin-bottom: 0; }
.dateline { text-align: left; font-size: small; line-height:1em;margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
.kicker { font-size: small; line-height:1em;margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
.timestamp { font-size: small; }
.caption { font-size: small; line-height:1em; margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
a:link {text-decoration: none; }'''
.timestamp { text-align: left; font-size: small; }
.caption { font-size: small; font-style:italic; line-height:1em; margin-top:5px; margin-left:0; margin-right:0; margin-bottom: 0; }
a:link {text-decoration: none; }
.articleBody { }
.authorId {text-align: left; }
.image {text-align: center;}
.source {text-align: left; }'''
def filter_ans(self, ans) :
total_article_count = 0
idx = 0
idx_max = len(ans)-1
while idx <= idx_max:
if self.includeSections != []:
if ans[idx][0] not in self.includeSections:
print "SECTION NOT INCLUDED: ",ans[idx][0]
del ans[idx]
idx_max = idx_max-1
continue
if ans[idx][0] in self.excludeSections:
print "SECTION EXCLUDED: ",ans[idx][0]
del ans[idx]
idx_max = idx_max-1
continue
if self.verbose:
self.log("Section %s: %d articles" % (ans[idx][0], len(ans[idx][1])) )
for article in ans[idx][1]:
total_article_count += 1
if self.verbose:
self.log("\t%-40.40s... \t%-60.60s..." % (article['title'].encode('cp1252','replace'),
article['url'].encode('cp1252','replace')))
idx = idx+1
self.log( "Queued %d articles" % total_article_count )
return ans
def fixChars(self,string):
# Replace lsquo (\x91)
fixed = re.sub("\x91","",string)
# Replace rsquo (\x92)
fixed = re.sub("\x92","",fixed)
# Replace ldquo (\x93)
fixed = re.sub("\x93","“",fixed)
# Replace rdquo (\x94)
fixed = re.sub("\x94","”",fixed)
# Replace ndash (\x96)
fixed = re.sub("\x96","",fixed)
# Replace mdash (\x97)
fixed = re.sub("\x97","—",fixed)
return fixed
def get_browser(self):
br = BasicNewsRecipe.get_browser()
@ -60,22 +194,19 @@ class NYTimes(BasicNewsRecipe):
br['USERID'] = self.username
br['PASSWORD'] = self.password
raw = br.submit().read()
if 'Sorry, we could not find the combination you entered. Please try again.' in raw:
if 'Please try again' in raw:
raise Exception('Your username and password are incorrect')
#open('/t/log.html', 'wb').write(raw)
return br
def get_masthead_url(self):
masthead = 'http://graphics8.nytimes.com/images/misc/nytlogo379x64.gif'
#masthead = 'http://members.cox.net/nickredding/nytlogo.gif'
br = BasicNewsRecipe.get_browser()
try:
br.open(masthead)
except:
self.log("\nMasthead unavailable")
masthead = None
return masthead
def skip_ad_pages(self, soup):
# Skip ad pages served before actual article
skip_tag = soup.find(True, {'name':'skip'})
if skip_tag is not None:
self.log.warn("Found forwarding link: %s" % skip_tag.parent['href'])
url = 'http://www.nytimes.com' + re.sub(r'\?.*', '', skip_tag.parent['href'])
url += '?pagewanted=all'
self.log.warn("Skipping ad to article at '%s'" % url)
return self.index_to_soup(url, raw=True)
def get_cover_url(self):
cover = None
@ -93,12 +224,57 @@ class NYTimes(BasicNewsRecipe):
return cover
def short_title(self):
return 'New York Times'
return self.title
def parse_index(self):
self.encoding = 'cp1252'
soup = self.index_to_soup('http://www.nytimes.com/pages/todayspaper/index.html')
self.encoding = decode
def index_to_soup(self, url_or_raw, raw=False):
'''
OVERRIDE of class method
deals with various page encodings between index and articles
'''
def get_the_soup(docEncoding, url_or_raw, raw=False) :
if re.match(r'\w+://', url_or_raw):
f = self.browser.open(url_or_raw)
_raw = f.read()
f.close()
if not _raw:
raise RuntimeError('Could not fetch index from %s'%url_or_raw)
else:
_raw = url_or_raw
if raw:
return _raw
if not isinstance(_raw, unicode) and self.encoding:
_raw = _raw.decode(docEncoding, 'replace')
massage = list(BeautifulSoup.MARKUP_MASSAGE)
massage.append((re.compile(r'&(\S+?);'), lambda match: entity_to_unicode(match, encoding=self.encoding)))
return BeautifulSoup(_raw, markupMassage=massage)
# Entry point
print "index_to_soup()"
soup = get_the_soup( self.encoding, url_or_raw )
contentType = soup.find(True,attrs={'http-equiv':'Content-Type'})
docEncoding = str(contentType)[str(contentType).find('charset=') + len('charset='):str(contentType).rfind('"')]
if docEncoding == '' :
docEncoding = self.encoding
if self.verbose > 2:
self.log( " document encoding: '%s'" % docEncoding)
if docEncoding != self.encoding :
soup = get_the_soup(docEncoding, url_or_raw)
return soup
def massageNCXText(self, description):
# Kindle TOC descriptions won't render certain characters
if description:
massaged = unicode(BeautifulStoneSoup(description, convertEntities=BeautifulStoneSoup.HTML_ENTITIES))
# Replace '&' with '&'
massaged = re.sub("&","&", massaged)
return self.fixChars(massaged)
else:
return description
def parse_todays_index(self):
def feed_title(div):
return ''.join(div.findAll(text=True, recursive=True)).strip()
@ -119,12 +295,13 @@ class NYTimes(BasicNewsRecipe):
return
if 'podcast' in url:
return
if '/video/' in url:
return
url += '?pagewanted=all'
if url in url_list:
return
url_list.append(url)
title = self.tag_to_string(a, use_alt=True).strip()
#self.log("Title: %s" % title)
description = ''
pubdate = strftime('%a, %d %b')
summary = div.find(True, attrs={'class':'summary'})
@ -140,6 +317,7 @@ class NYTimes(BasicNewsRecipe):
author = self.tag_to_string(authorAttribution, use_alt=False)
feed = key if key is not None else 'Uncategorized'
if not articles.has_key(feed):
ans.append(feed)
articles[feed] = []
articles[feed].append(
dict(title=title, url=url, date=pubdate,
@ -147,46 +325,228 @@ class NYTimes(BasicNewsRecipe):
content=''))
soup = self.index_to_soup('http://www.nytimes.com/pages/todayspaper/index.html')
# Find each instance of class="section-headline", class="story", class="story headline"
# Find each article
for div in soup.findAll(True,
attrs={'class':['section-headline', 'story', 'story headline','sectionHeader','headlinesOnly multiline flush']}):
if div['class'] in ['section-headline','sectionHeader']:
key = string.capwords(feed_title(div))
articles[key] = []
ans.append(key)
#self.log('Section: %s' % key)
key = key.replace('Op-ed','Op-Ed')
key = key.replace('U.s.','U.S.')
elif div['class'] in ['story', 'story headline'] :
handle_article(div)
elif div['class'] == 'headlinesOnly multiline flush':
for lidiv in div.findAll('li'):
handle_article(lidiv)
# ans = self.sort_index_by(ans, {'The Front Page':-1,
# 'Dining In, Dining Out':1,
# 'Obituaries':2})
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
return self.filter_ans(ans)
def parse_headline_index(self):
articles = {}
ans = []
url_list = []
soup = self.index_to_soup('http://www.nytimes.com/pages/todaysheadlines/')
# Fetch the content table
content_table = soup.find('table',{'id':'content'})
if content_table is None:
self.log("FATAL ERROR: CANNOT FIND CONTENT TABLE")
return None
# Within this table are <td id=".*Column.*"> entries, each containing one or more h6 tags which represent sections
for td_col in content_table.findAll('td', {'id' : re.compile('Column')}):
for div_sec in td_col.findAll('div',recursive=False):
for h6_sec_name in div_sec.findAll('h6',{'style' : re.compile('text-transform: *uppercase')}):
section_name = self.tag_to_string(h6_sec_name,use_alt=False)
section_name = re.sub(r'^ *$','',section_name)
if section_name == '':
continue
section_name=string.capwords(section_name)
if section_name == 'U.s.':
section_name = 'U.S.'
elif section_name == 'Op-ed':
section_name = 'Op-Ed'
pubdate = strftime('%a, %d %b')
search_div = div_sec
for next_tag in h6_sec_name.findNextSiblings(True):
if next_tag.__class__.__name__ == 'Tag':
if next_tag.name == 'div':
search_div = next_tag
break
# Get the articles
for h3_item in search_div.findAll('h3'):
byline = h3_item.h6
if byline is not None:
author = self.tag_to_string(byline,usa_alt=False)
else:
author = ''
a = h3_item.find('a', href=True)
if not a:
continue
url = re.sub(r'\?.*', '', a['href'])
if not url.startswith("http"):
continue
if not url.endswith(".html"):
continue
if 'podcast' in url:
continue
if 'video' in url:
continue
url += '?pagewanted=all'
if url in url_list:
continue
url_list.append(url)
self.log("URL %s" % url)
title = self.tag_to_string(a, use_alt=True).strip()
desc = h3_item.find('p')
if desc is not None:
description = self.tag_to_string(desc,use_alt=False)
else:
description = ''
if not articles.has_key(section_name):
ans.append(section_name)
articles[section_name] = []
articles[section_name].append(dict(title=title, url=url, date=pubdate, description=description, author=author, content=''))
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
return self.filter_ans(ans)
def parse_index(self):
if self.headlinesOnly:
return self.parse_headline_index()
else:
return self.parse_todays_index()
def strip_anchors(self,soup):
paras = soup.findAll(True)
for para in paras:
aTags = para.findAll('a')
for a in aTags:
if a.img is None:
a.replaceWith(a.renderContents().decode('cp1252','replace'))
return soup
return ans
def preprocess_html(self, soup):
kicker_tag = soup.find(attrs={'class':'kicker'})
if kicker_tag:
if kicker_tag: # remove Op_Ed author head shots
tagline = self.tag_to_string(kicker_tag)
#self.log("FOUND KICKER %s" % tagline)
if tagline=='Op-Ed Columnist':
img_div = soup.find('div','inlineImage module')
#self.log("Searching for photo")
if img_div:
img_div.extract()
#self.log("Photo deleted")
refresh = soup.find('meta', {'http-equiv':'refresh'})
if refresh is None:
return soup
content = refresh.get('content').partition('=')[2]
raw = self.browser.open_novisit('http://www.nytimes.com'+content).read()
return BeautifulSoup(raw.decode('cp1252', 'replace'))
return self.strip_anchors(soup)
def postprocess_html(self,soup, True):
if self.one_picture_per_article:
# Remove all images after first
largeImg = soup.find(True, {'class':'articleSpanImage'})
inlineImgs = soup.findAll(True, {'class':'inlineImage module'})
if largeImg:
for inlineImg in inlineImgs:
inlineImg.extract()
else:
if inlineImgs:
firstImg = inlineImgs[0]
for inlineImg in inlineImgs[1:]:
inlineImg.extract()
# Move firstImg before article body
#article_body = soup.find(True, {'id':'articleBody'})
cgFirst = soup.find(True, {'class':re.compile('columnGroup *first')})
if cgFirst:
# Strip all sibling NavigableStrings: noise
navstrings = cgFirst.findAll(text=True, recursive=False)
[ns.extract() for ns in navstrings]
headline_found = False
tag = cgFirst.find(True)
insertLoc = 0
while True:
insertLoc += 1
if hasattr(tag,'class') and tag['class'] == 'articleHeadline':
headline_found = True
break
tag = tag.nextSibling
if not tag:
headline_found = False
break
if headline_found:
cgFirst.insert(insertLoc,firstImg)
else:
self.log(">>> No class:'columnGroup first' found <<<")
# Change captions to italic
for caption in soup.findAll(True, {'class':'caption'}) :
if caption and caption.contents[0]:
cTag = Tag(soup, "p", [("class", "caption")])
c = self.fixChars(self.tag_to_string(caption,use_alt=False)).strip()
mp_off = c.find("More Photos")
if mp_off >= 0:
c = c[:mp_off]
cTag.insert(0, c)
caption.replaceWith(cTag)
# Change <nyt_headline> to <h2>
h1 = soup.find('h1')
if h1:
headline = h1.find("nyt_headline")
if headline:
tag = Tag(soup, "h2")
tag['class'] = "headline"
tag.insert(0, self.fixChars(headline.contents[0]))
h1.replaceWith(tag)
else:
# Blog entry - replace headline, remove <hr> tags
headline = soup.find('title')
if headline:
tag = Tag(soup, "h2")
tag['class'] = "headline"
tag.insert(0, self.fixChars(headline.contents[0]))
soup.insert(0, tag)
hrs = soup.findAll('hr')
for hr in hrs:
hr.extract()
# Change <h1> to <h3> - used in editorial blogs
masthead = soup.find("h1")
if masthead:
# Nuke the href
if masthead.a:
del(masthead.a['href'])
tag = Tag(soup, "h3")
tag.insert(0, self.fixChars(masthead.contents[0]))
masthead.replaceWith(tag)
# Change <span class="bold"> to <b>
for subhead in soup.findAll(True, {'class':'bold'}) :
if subhead.contents:
bTag = Tag(soup, "b")
bTag.insert(0, subhead.contents[0])
subhead.replaceWith(bTag)
divTag = soup.find('div',attrs={'id':'articleBody'})
if divTag:
divTag['class'] = divTag['id']
# Add class="authorId" to <div> so we can format with CSS
divTag = soup.find('div',attrs={'id':'authorId'})
if divTag and divTag.contents[0]:
tag = Tag(soup, "p")
tag['class'] = "authorId"
tag.insert(0, self.fixChars(self.tag_to_string(divTag.contents[0],
use_alt=False)))
divTag.replaceWith(tag)
return soup

View File

@ -4,7 +4,7 @@ class Tagesschau(BasicNewsRecipe):
title = 'Tagesschau'
description = 'Nachrichten der ARD'
publisher = 'ARD'
language = 'de_DE'
language = 'de'
__author__ = 'Florian Andreas Pfaff'
oldest_article = 7

View File

@ -0,0 +1,72 @@
import re
from calibre.web.feeds.news import BasicNewsRecipe
import mechanize
class AdvancedUserRecipe1283848012(BasicNewsRecipe):
description = 'This is a recipe of Ynet.co.il. The recipe opens the article page and clicks on an advertisement to not hurt the sites advertising income.'
cover_url = 'http://www.bneiakiva.net/uploads/images/ynet%282%29.jpg'
title = u'Ynet'
__author__ = 'marbs'
language = 'he'
extra_css='img {max-width:100%;direction: rtl;} #article{direction: rtl;} div{direction: rtl;} title{direction: rtl; } article_description{direction: rtl; } a.article{direction: rtl; } calibre_feed_description{direction: rtl; } body{direction: ltr;}'
remove_attributes = ['width']
simultaneous_downloads = 5
keep_only_tags =dict(name='div', attrs={'id':'articleContainer'})
remove_javascript = True
timefmt = '[%a, %d %b, %Y]'
oldest_article = 1
remove_tags = [dict(name='p', attrs={'text':['&nbsp;']})]
max_articles_per_feed = 100
preprocess_regexps = [
(re.compile(r'<p>&nbsp;</p>', re.DOTALL|re.IGNORECASE), lambda match: '')
]
def preprocess_html(self, soup):
soup.html['dir'] = 'rtl'
soup.body['dir'] = 'rtl'
return soup
feeds =[(u'\u05d7\u05d3\u05e9\u05d5\u05ea',
u'http://www.ynet.co.il/Integration/StoryRss2.xml'),
(u'\u05db\u05dc\u05db\u05dc\u05d4',
u'http://www.ynet.co.il/Integration/StoryRss6.xml'),
(u'\u05e6\u05e8\u05db\u05e0\u05d5\u05ea',
u'http://www.ynet.co.il/Integration/StoryRss437.xml'),
(u'\u05e1\u05e4\u05d5\u05e8\u05d8',
u'http://www.ynet.co.il/Integration/StoryRss3.xml'),
(u'\u05ea\u05e8\u05d1\u05d5\u05ea',
u'http://www.ynet.co.il/Integration/StoryRss538.xml'),
(u'\u05de\u05e2\u05d5\u05e8\u05d1\u05d5\u05ea \u05d5\u05d7\u05d1\u05e8\u05d4',
u'http://www.ynet.co.il/Integration/StoryRss3262.xml'),
(u'\u05d1\u05e8\u05d9\u05d0\u05d5\u05ea',
u'http://www.ynet.co.il/Integration/StoryRss1208.xml'),
(u'\u05d9\u05e8\u05d5\u05e7',
u'http://www.ynet.co.il/Integration/StoryRss4872.xml'),
(u'\u05de\u05d7\u05e9\u05d1\u05d9\u05dd',
u'http://www.ynet.co.il/Integration/StoryRss544.xml'),
(u'\u05e8\u05db\u05d1', u'http://www.ynet.co.il/Integration/StoryRss550.xml'),
(u'\u05ea\u05d9\u05d9\u05e8\u05d5\u05ea',
u'http://www.ynet.co.il/Integration/StoryRss598.xml'),
(u'\u05d4\u05d5\u05e8\u05d9\u05dd',
u'http://www.ynet.co.il/Integration/StoryRss3052.xml'),
(u'\u05d0\u05d5\u05db\u05dc',
u'http://www.ynet.co.il/Integration/StoryRss975.xml'),
(u'\u05d9\u05d4\u05d3\u05d5\u05ea',
u'http://www.ynet.co.il/Integration/StoryRss4403.xml'),
(u'\u05de\u05d3\u05e2 \u05d5\u05d8\u05d1\u05e2',
u'http://www.ynet.co.il/Integration/StoryRss2142.xml'),
(u'\u05d9\u05d7\u05e1\u05d9\u05dd',
u'http://www.ynet.co.il/Integration/StoryRss3925.xml'),
(u'\u05d3\u05e2\u05d5\u05ea',
u'http://www.ynet.co.il/Integration/StoryRss194.xml')]
def print_version(self, url):
#remove from here
br = BasicNewsRecipe.get_browser()
br.open(url)
br.follow_link(mechanize.Link(base_url = '', url =url, text = '', tag = 'a', attrs = [{'id':'buzzerATop'}]))
#to here to stop supporting ynet...
split1 = url.split("-")
print_url = 'http://www.ynet.co.il/Ext/Comp/ArticleLayout/CdaArticlePrintPreview/1,2506,L-' + split1[1]
return print_url

View File

@ -6,22 +6,25 @@ Fetch Die Zeit.
'''
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class ZeitDe(BasicNewsRecipe):
title = 'ZEIT Online'
description = 'ZEIT Online'
title = 'Zeit Online'
description = 'Zeit Online'
language = 'de'
lang = 'de_DE'
__author__ = 'Martin Pitt, Sujata Raman and Ingo Paschke'
use_embedded_content = False
__author__ = 'Martin Pitt, Sujata Raman, Ingo Paschke and Marc Toensing'
max_articles_per_feed = 40
remove_empty_feeds = True
no_stylesheets = True
no_javascript = True
encoding = 'utf-8'
remove_tags = [
dict(name='iframe'),
dict(name='div', attrs={'class':["response","pagination block","pagenav","inline link", "copyright"] }),
dict(name='p', attrs={'class':["ressortbacklink", "copyright"] }),
dict(name='div', attrs={'id':["place_5","place_4","comments"]})
]
keep_only_tags = [dict(id=['main'])]
feeds = [
('Seite 1', 'http://newsfeed.zeit.de/index_xml'),
@ -40,43 +43,15 @@ class ZeitDe(BasicNewsRecipe):
('Sport', 'http://newsfeed.zeit.de/sport/index'),
]
extra_css = '''
.supertitle{color:#990000; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
.excerpt{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:small;}
.title{font-family:Arial,Helvetica,sans-serif;font-size:large;clear:right;}
.caption{color:#666666; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
.copyright{color:#666666; font-family:Arial,Helvetica,sans-serif;font-size:xx-small;}
.article{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small}
.quote{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small}
.quote .cite{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:xx-small}
.headline iconportrait_inline{font-family:Arial,Helvetica,sans-serif;font-size:x-small}
.inline{float:left;margin-top:0;margin-right:15px;position:relative;width:180px; }
img.inline{float:none}
.intertitle{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:x-small;font-weight:700}
.ebinfobox{font-family:Georgia,Palatino,Palatino Linotype,FreeSerif,serif;font-size:xx-small;list-style-type:none;float:right;margin-top:0;border-left-style:solid;border-left-width:1px;padding-left:10px;}
.infobox {border-style: solid; border-width: 1px;padding:8px;}
.infobox dt {font-weight:700;}
'''
extra_css = '.reaktion,.taglist,.comments,.reponse,.responsetitle,.responsebody,.reponse,.inline,.date{display:none;}li.date{display:block}'
#filter_regexps = [r'ad.de.doubleclick.net/']
keep_only_tags = [
dict(name='div', attrs={'class':["article"]}) ,
dict(name='ul', attrs={'class':["tools"]}) ,
]
remove_tags = [
dict(name='link'), dict(name='iframe'),dict(name='style'),dict(name='meta'),
dict(name='div', attrs={'class':["pagination block","pagenav","inline link", "copyright"] }),
dict(name='p', attrs={'class':["ressortbacklink", "copyright"] }),
dict(name='div', attrs={'id':["place_5","place_4","comments"]})
]
remove_attributes = ['style', 'font']
def get_article_url(self, article):
ans = article.get('link',None)
ans += "?page=all"
ans += "?page=all&print=true"
if 'video' in ans or 'quiz' in ans :
if 'video' in ans or 'quiz' in ans or 'blog' in ans :
ans = None
return ans
@ -86,25 +61,3 @@ class ZeitDe(BasicNewsRecipe):
return inhalt.find('div', attrs={'class':'singlearchive clearfix'}).img['src'].replace('icon_','')
except:
return 'http://images.zeit.de/bilder/titelseiten_zeit/1946/001_001.jpg'
def preprocess_html(self, soup):
soup.html['xml:lang'] = self.lang
soup.html['lang'] = self.lang
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
soup.head.insert(0,mtag)
title = soup.find('h2', attrs={'class':'title'})
if title is None:
print "no title"
return soup
info = Tag(soup,'ul',[('class','ebinfobox')])
tools = soup.find('ul', attrs={'class':'tools'})
#author = tools.find('li','author first')
for tag in ['author first', 'date', 'date first', 'author', 'source']:
line = tools.find('li', tag)
if line:
info.insert(0,line)
title.parent.insert(0,info)
tools.extract()
return soup

View File

@ -0,0 +1,60 @@
body{
margin:0px;
padding: 0.5em;
background-color:#F6F3E9;
font-size:12px;
font-family:Arial, Helvetica, sans-serif;
}
.calibreMeta{
background-color:#39322B;
color:white;
padding:10px;
}
.calibreMeta a, .calibreEbNav a, .calibreEbNavTop a, .calibreToc a{
color:white;
}
.calibreMeta h1{
margin:0px;
font-size:18px;
background-color:#39322B;
}
.calibreEbookContent{
padding:20px;
}
.calibreEbNav, .calibreEbNavTop{
clear:both;
background-color:#39322B;
color:white;
padding:10px;
text-align:center;
}
.calibreEbNavTop{
margin-bottom:20px;
}
.calibreEbNav a, .calibreEbNavTop a{
padding:0px 5px;
}
.calibreTocIndex{
line-height:18px;
}
.calibreToc{
float:left;
margin:20px;
width:300px;
background-color:#39322B;
color:white;
padding:10px;
}
.calibreEbookContent{
width:600px;
float:left;
}

View File

@ -0,0 +1,74 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
${head_content}$
<link href="${cssLink}$" type="text/css" rel="stylesheet" />
</head>
<body>
<div class="calibreMeta">
<div class="calibreMetaTitle">
${pos1=1}$
${for title in meta.titles():}$
${if pos1:}$
<h1>
<a href="${tocUrl}$">${print title}$</a>
</h1>
${:else:}$
<div class="calibreMetaSubtitle">${print title}$</div>
${:endif}$
${pos1=0}$
${:endfor}$
</div>
<div class="calibreMetaAuthor">
${print ', '.join(meta.creators())}$
</div>
</div>
<div class="calibreMain">
<div class="calibreEbookContent">
${if prevLink or nextLink:}$
<div class="calibreEbNavTop">
${if prevLink:}$
<a href="${prevLink}$" class="calibreAPrev">${print _('previous page'),}$</a>
${:else:}$
<a href="${tocUrl}$" class="calibreAPrev">${print _('previous page'),}$</a>
${:endif}$
${if nextLink:}$
<a href="${nextLink}$" class="calibreANext">${print _('next page'),}$</a>
${:endif}$
</div>
${:endif}$
${ebookContent}$
</div>
${if has_toc:}$
<div class="calibreToc">
<h2><a href="${tocUrl}$">${print _('Table of contents'),}$</a></h2>
${print toc()}$
</div>
${:endif}$
<div class="calibreEbNav">
${if prevLink:}$
<a href="${prevLink}$" class="calibreAPrev">${print _('previous page'),}$</a>
${:else:}$
<a href="${tocUrl}$" class="calibreAPrev">${print _('previous page'),}$</a>
${:endif}$
<a href="${tocUrl}$" class="calibreAHome">${print _('start'),}$</a>
${if nextLink:}$
<a href="${nextLink}$" class="calibreANext">${print _('next page'),}$</a>
${:endif}$
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,61 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" />
<link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" />
<title>${print ', '.join(meta.creators()),}$ - ${print meta.titles().next(); meta.titles().close()}$</title>
${for item in meta:}$
<meta ${print 'name="DC.'+item['name']+'"',}$ ${print 'content="'+item['value']+'"',}$ />
${:endfor}$
<link href="${cssLink}$" type="text/css" rel="stylesheet" />
</head>
<body>
<div class="calibreMeta">
<div class="calibreMetaTitle">
${pos1=1}$
${for title in meta.titles():}$
${if pos1:}$
<h1>
<a href="${tocUrl}$">${print title}$</a>
</h1>
${:else:}$
<div class="calibreMetaSubtitle">${print title}$</div>
${:endif}$
${pos1=0}$
${:endfor}$
</div>
<div class="calibreMetaAuthor">
${print ', '.join(meta.creators()),}$
</div>
</div>
<div class="calibreMain">
<div class="calibreEbookContent">
${if has_toc:}$
<div class="calibreTocIndex">
<h2>${print _('Table of contents'),}$</h2>
${toc}$
</div>
${:else:}$
<h2>${print _('No table of contents present'),}$</h2>
<div><strong><a href="${nextLink}$">${print _('begin to read'),}$</a></strong></div>
${:endif}$
</div>
<div class="calibreEbNav">
${if nextLink:}$
<a href="${nextLink}$" class="calibreANext">${print _('next page'),}$</a>
${:endif}$
</div>
</div>
</body>
</html>

View File

@ -11,7 +11,7 @@ import subprocess, tempfile, os, time
from setup import Command, installer_name
from setup.build_environment import HOST, PROJECT
BASE_RSYNC = 'rsync -avz --delete'.split()
BASE_RSYNC = ['rsync', '-avz', '--delete']
EXCLUDES = []
for x in [
'src/calibre/plugins', 'src/calibre/manual', 'src/calibre/trac',
@ -42,7 +42,8 @@ class Push(Command):
threads = []
for host in (
r'Owner@winxp:/cygdrive/c/Documents\ and\ Settings/Owner/calibre',
'kovid@ox:calibre'
'kovid@ox:calibre',
r'kovid@win7:/cygdrive/c/Users/kovid/calibre',
):
rcmd = BASE_RSYNC + EXCLUDES + ['.', host]
print '\n\nPushing to:', host, '\n'

View File

@ -89,7 +89,7 @@ class Server(Command):
t = telnetlib.Telnet('localhost', 4242)
t.read_until("repl>")
t.write('BrowserReload();')
print t.read_until("repl>")
t.read_until("repl>")
t.close()
except:
print 'Failed to reload browser'

View File

@ -21,8 +21,6 @@ from calibre.constants import iswindows, isosx, islinux, isfreebsd, isfrozen, \
filesystem_encoding, plugins, config_dir
from calibre.startup import winutil, winutilerror
import mechanize
uuid.uuid4() # Imported before PyQt4 to workaround PyQt4 util-linux conflict on gentoo
if False:
@ -269,7 +267,8 @@ def browser(honor_time=True, max_time=2, mobile_browser=False):
:param honor_time: If True honors pause time in refresh requests
:param max_time: Maximum time in seconds to wait during a refresh request
'''
opener = mechanize.Browser()
from calibre.utils.browser import Browser
opener = Browser()
opener.set_handle_refresh(True, max_time=max_time, honor_time=honor_time)
opener.set_handle_robots(False)
opener.addheaders = [('User-agent', ' Mozilla/5.0 (Windows; U; Windows CE 5.1; rv:1.8.1a3) Gecko/20060610 Minimo/0.016' if mobile_browser else \

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.7.26'
__version__ = '0.7.27'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re
@ -105,7 +105,9 @@ else:
os.makedirs(config_dir, mode=CONFIG_DIR_MODE)
except:
pass
if not os.access(config_dir, os.W_OK) or not os.access(config_dir, os.X_OK):
if not os.path.exists(config_dir) or \
not os.access(config_dir, os.W_OK) or not \
os.access(config_dir, os.X_OK):
print 'No write acces to', config_dir, 'using a temporary dir instead'
import tempfile, atexit
config_dir = tempfile.mkdtemp(prefix='calibre-config-')

View File

@ -446,6 +446,7 @@ 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.ebooks.html.output import HTMLOutput
from calibre.ebooks.snb.output import SNBOutput
from calibre.customize.profiles import input_profiles, output_profiles
@ -525,6 +526,7 @@ plugins += [
RTFOutput,
TCROutput,
TXTOutput,
HTMLOutput,
SNBOutput,
]
# Order here matters. The first matched device is the one used.
@ -893,4 +895,3 @@ plugins += [LookAndFeel, Behavior, Columns, Toolbar, InputOptions,
Email, Server, Plugins, Tweaks, Misc]
#}}}

View File

@ -17,15 +17,15 @@ class IRIVER_STORY(USBMS):
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats
FORMATS = ['epub', 'pdf', 'txt']
FORMATS = ['epub', 'fb2', 'pdf', 'djvu', 'txt']
VENDOR_ID = [0x1006]
PRODUCT_ID = [0x4023]
PRODUCT_ID = [0x4023, 0x4025]
BCD = [0x0323]
VENDOR_NAME = 'IRIVER'
WINDOWS_MAIN_MEM = 'STORY'
WINDOWS_CARD_A_MEM = 'STORY'
WINDOWS_MAIN_MEM = ['STORY', 'STORY_EB05']
WINDOWS_CARD_A_MEM = ['STORY', 'STORY_SD']
#OSX_MAIN_MEM = 'Kindle Internal Storage Media'
#OSX_CARD_A_MEM = 'Kindle Card Storage Media'

View File

@ -124,12 +124,15 @@ class PDNOVEL_KOBO(PDNOVEL):
BCD = [0x222]
EBOOK_DIR_MAIN = 'eBooks/Kobo'
EBOOK_DIR_MAIN = 'eBooks'
def upload_cover(self, path, filename, metadata, filepath):
coverdata = getattr(metadata, 'thumbnail', None)
if coverdata and coverdata[2]:
with open(os.path.join(path, '.thumbnail', filename+'.jpg'), 'wb') as coverfile:
dirpath = os.path.join(path, '.thumbnail')
if not os.path.exists(dirpath):
os.makedirs(dirpath)
with open(os.path.join(dirpath, filename+'.jpg'), 'wb') as coverfile:
coverfile.write(coverdata[2])

View File

@ -32,7 +32,7 @@ def detect(aBuf):
ENCODING_PATS = [
re.compile(r'<\?[^<>]+encoding\s*=\s*[\'"](.*?)[\'"][^<>]*>',
re.IGNORECASE),
re.compile(r'''<meta\s+?[^<>]+?content\s*=\s*['"][^'"]*?charset=([-a-z0-9]+)[^'"]*?['"][^<>]*>''',
re.compile(r'''<meta\s+?[^<>]*?content\s*=\s*['"][^'"]*?charset=([-a-z0-9]+)[^'"]*?['"][^<>]*>''',
re.IGNORECASE)
]
ENTITY_PATTERN = re.compile(r'&(\S+?);')

View File

@ -144,7 +144,10 @@ class DocAnalysis(object):
# Normalize the histogram into percents
totalLines = len(self.lines)
h = [ float(count)/totalLines for count in hRaw ]
if totalLines > 0:
h = [ float(count)/totalLines for count in hRaw ]
else:
h = []
#print "\nhRaw histogram lengths are: "+str(hRaw)
#print " percents are: "+str(h)+"\n"

View File

@ -0,0 +1,33 @@
from __future__ import with_statement
__license__ = 'GPL 3'
__copyright__ = '2010, Fabian Grassl <fg@jusmeum.de>'
__docformat__ = 'restructuredtext en'
from calibre.ebooks.oeb.base import namespace, barename, DC11_NS
class EasyMeta(object):
def __init__(self, meta):
self.meta = meta
def __iter__(self):
meta = self.meta
for item_name in meta.items:
for item in meta[item_name]:
if namespace(item.term) == DC11_NS:
yield { 'name': barename(item.term), 'value': item.value }
def __len__(self):
count = 0
for item in self:
count = count+1
return count
def titles(self):
for item in self.meta['title']:
yield item.value
def creators(self):
for item in self.meta['creator']:
yield item.value

View File

@ -0,0 +1,209 @@
from __future__ import with_statement
__license__ = 'GPL 3'
__copyright__ = '2010, Fabian Grassl <fg@jusmeum.de>'
__docformat__ = 'restructuredtext en'
import os, re, shutil
from calibre.utils import zipfile
from os.path import dirname, abspath, relpath, exists, basename
from lxml import etree
from templite import Templite
from calibre.ebooks.oeb.base import element
from calibre.customize.conversion import OutputFormatPlugin, OptionRecommendation
from calibre import CurrentDir
from calibre.ptempfile import PersistentTemporaryDirectory
from calibre.utils.zipfile import ZipFile
from urllib import unquote
from calibre.ebooks.html.meta import EasyMeta
class HTMLOutput(OutputFormatPlugin):
name = 'HTML Output'
author = 'Fabian Grassl'
file_type = 'zip'
options = set([
OptionRecommendation(name='template_css',
help=_('CSS file used for the output instead of the default file')),
OptionRecommendation(name='template_html_index',
help=_('Template used for generation of the html index file instead of the default file')),
OptionRecommendation(name='template_html',
help=_('Template used for the generation of the html contents of the book instead of the default file')),
OptionRecommendation(name='extract_to',
help=_('Extract the contents of the generated ZIP file to the '
'specified directory. WARNING: The contents of the directory '
'will be deleted.')
),
])
recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)])
def generate_toc(self, oeb_book, ref_url, output_dir):
'''
Generate table of contents
'''
with CurrentDir(output_dir):
def build_node(current_node, parent=None):
if parent is None:
parent = etree.Element('ul')
elif len(current_node.nodes):
parent = element(parent, ('ul'))
for node in current_node.nodes:
point = element(parent, 'li')
href = relpath(abspath(unquote(node.href)), dirname(ref_url))
link = element(point, 'a', href=href)
title = node.title
if title:
title = re.sub(r'\s+', ' ', title)
link.text=title
build_node(node, point)
return parent
wrap = etree.Element('div')
wrap.append(build_node(oeb_book.toc))
return wrap
def generate_html_toc(self, oeb_book, ref_url, output_dir):
root = self.generate_toc(oeb_book, ref_url, output_dir)
return etree.tostring(root, pretty_print=True, encoding='utf-8',
xml_declaration=False)
def convert(self, oeb_book, output_path, input_plugin, opts, log):
# read template files
if opts.template_html_index is not None:
template_html_index_data = open(opts.template_html_index, 'rb').read()
else:
template_html_index_data = P('templates/html_export_default_index.tmpl', data=True)
if opts.template_html is not None:
template_html_data = open(opts.template_html, 'rb').read()
else:
template_html_data = P('templates/html_export_default.tmpl', data=True)
if opts.template_css is not None:
template_css_data = open(opts.template_css, 'rb').read()
else:
template_css_data = P('templates/html_export_default.css', data=True)
template_html_index_data = template_html_index_data.decode('utf-8')
template_html_data = template_html_data.decode('utf-8')
template_css_data = template_css_data.decode('utf-8')
self.log = log
self.opts = opts
meta = EasyMeta(oeb_book.metadata)
tempdir = os.path.realpath(PersistentTemporaryDirectory())
output_file = os.path.join(tempdir,
basename(re.sub(r'\.zip', '', output_path)+'.html'))
output_dir = re.sub(r'\.html', '', output_file)+'_files'
if not exists(output_dir):
os.makedirs(output_dir)
css_path = output_dir+os.sep+'calibreHtmlOutBasicCss.css'
with open(css_path, 'wb') as f:
f.write(template_css_data.encode('utf-8'))
with open(output_file, 'wb') as f:
html_toc = self.generate_html_toc(oeb_book, output_file, output_dir)
templite = Templite(template_html_index_data)
nextLink = oeb_book.spine[0].href
nextLink = relpath(output_dir+os.sep+nextLink, dirname(output_file))
cssLink = relpath(abspath(css_path), dirname(output_file))
tocUrl = relpath(output_file, dirname(output_file))
t = templite.render(has_toc=bool(oeb_book.toc.count()),
toc=html_toc, meta=meta, nextLink=nextLink,
tocUrl=tocUrl, cssLink=cssLink,
firstContentPageLink=nextLink)
f.write(t)
with CurrentDir(output_dir):
for item in oeb_book.manifest:
path = abspath(unquote(item.href))
dir = dirname(path)
if not exists(dir):
os.makedirs(dir)
if item.spine_position is not None:
with open(path, 'wb') as f:
pass
else:
with open(path, 'wb') as f:
f.write(str(item))
item.unload_data_from_memory(memory=path)
for item in oeb_book.spine:
path = abspath(unquote(item.href))
dir = dirname(path)
root = item.data.getroottree()
# get & clean HTML <HEAD>-data
head = root.xpath('//h:head', namespaces={'h': 'http://www.w3.org/1999/xhtml'})[0]
head_content = etree.tostring(head, pretty_print=True, encoding='utf-8')
head_content = re.sub(r'\<\/?head.*\>', '', head_content)
head_content = re.sub(re.compile(r'\<style.*\/style\>', re.M|re.S), '', head_content)
head_content = re.sub(r'<(title)([^>]*)/>', r'<\1\2></\1>', head_content)
# get & clean HTML <BODY>-data
body = root.xpath('//h:body', namespaces={'h': 'http://www.w3.org/1999/xhtml'})[0]
ebook_content = etree.tostring(body, pretty_print=True, encoding='utf-8')
ebook_content = re.sub(r'\<\/?body.*\>', '', ebook_content)
ebook_content = re.sub(r'<(div|a|span)([^>]*)/>', r'<\1\2></\1>', ebook_content)
# generate link to next page
if item.spine_position+1 < len(oeb_book.spine):
nextLink = oeb_book.spine[item.spine_position+1].href
nextLink = relpath(abspath(nextLink), dir)
else:
nextLink = None
# generate link to previous page
if item.spine_position > 0:
prevLink = oeb_book.spine[item.spine_position-1].href
prevLink = relpath(abspath(prevLink), dir)
else:
prevLink = None
cssLink = relpath(abspath(css_path), dir)
tocUrl = relpath(output_file, dir)
firstContentPageLink = oeb_book.spine[0].href
# render template
templite = Templite(template_html_data)
toc = lambda: self.generate_html_toc(oeb_book, path, output_dir)
t = templite.render(ebookContent=ebook_content,
prevLink=prevLink, nextLink=nextLink,
has_toc=bool(oeb_book.toc.count()), toc=toc,
tocUrl=tocUrl, head_content=head_content,
meta=meta, cssLink=cssLink,
firstContentPageLink=firstContentPageLink)
# write html to file
with open(path, 'wb') as f:
f.write(t)
item.unload_data_from_memory(memory=path)
zfile = ZipFile(output_path, "w")
zfile.add_dir(output_dir, basename(output_dir))
zfile.write(output_file, basename(output_file), zipfile.ZIP_DEFLATED)
if opts.extract_to:
if os.path.exists(opts.extract_to):
shutil.rmtree(opts.extract_to)
os.makedirs(opts.extract_to)
zfile.extractall(opts.extract_to)
self.log('Zip file extracted to', opts.extract_to)
zfile.close()
# cleanup temp dir
shutil.rmtree(tempdir)

View File

@ -49,13 +49,12 @@ def get_social_metadata(title, authors, publisher, isbn):
return mi
br = browser()
asin = to_asin(br, isbn)
if asin:
if get_metadata(br, asin, mi):
return mi
if asin and get_metadata(br, asin, mi):
return mi
from calibre.ebooks.metadata.xisbn import xisbn
for i in xisbn.get_associated_isbns(isbn):
asin = to_asin(br, i)
if get_metadata(br, asin, mi):
if asin and get_metadata(br, asin, mi):
return mi
return mi
@ -113,13 +112,12 @@ def get_metadata(br, asin, mi):
def main(args=sys.argv):
# Test xisbn
#print get_social_metadata('Learning Python', None, None, '8324616489')
#print
print get_social_metadata('Learning Python', None, None, '8324616489')
print
# Test sophisticated comment formatting
print get_social_metadata('Angels & Demons', None, None, '9781416580829')
print
return
# Random tests
print get_social_metadata('Star Trek: Destiny: Mere Mortals', None, None, '9781416551720')

View File

@ -12,6 +12,7 @@ from calibre.utils.logging import default_log
from calibre.utils.titlecase import titlecase
from calibre.customize import Plugin
from calibre.ebooks.metadata.covers import check_for_cover
from calibre.utils.html2text import html2text
metadata_config = None
@ -48,6 +49,11 @@ class MetadataSource(Plugin): # {{{
#: member.
string_customization_help = None
#: Set this to true if your plugin returns HTML markup in comments.
#: Then if the user disables HTML, calibre will automagically convert
#: the HTML to Markdown.
has_html_comments = False
type = _('Metadata download')
def __call__(self, title, author, publisher, isbn, verbose, log=None,
@ -79,6 +85,13 @@ class MetadataSource(Plugin): # {{{
mi.comments = None
if not c.get('tags', True):
mi.tags = []
if self.has_html_comments and mi.comments and \
c.get('textcomments', False):
try:
mi.comments = html2text(mi.comments)
except:
traceback.print_exc()
mi.comments = None
except Exception, e:
self.exception = e
@ -132,11 +145,17 @@ class MetadataSource(Plugin): # {{{
setattr(w, '_'+x, cb)
cb.setChecked(c.get(x, True))
w._layout.addWidget(cb)
cb = QCheckBox(_('Convert comments downloaded from %s to plain text')%(self.name))
setattr(w, '_textcomments', cb)
cb.setChecked(c.get('textcomments', False))
w._layout.addWidget(cb)
return w
def save_settings(self, w):
dl_settings = {}
for x in ('rating', 'tags', 'comments'):
for x in ('rating', 'tags', 'comments', 'textcomments'):
dl_settings[x] = getattr(w, '_'+x).isChecked()
c = self.config_store()
c.set(self.name, dl_settings)
@ -210,6 +229,8 @@ class Amazon(MetadataSource): # {{{
metadata_type = 'social'
description = _('Downloads social metadata from amazon.com')
has_html_comments = True
def fetch(self):
if not self.isbn:
return

View File

@ -182,6 +182,7 @@ class TOC(list):
except:
play_order = 1
href = fragment = text = None
nd = dest
nl = nl_path(np)
if nl:
nl = nl[0]
@ -190,17 +191,14 @@ class TOC(list):
text += etree.tostring(txt, method='text',
encoding=unicode, with_tail=False)
content = content_path(np)
if not content or not text:
return
content = content[0]
src = get_attr(content, attr='src')
if src is None:
return
purl = urlparse(unquote(content.get('src')))
href, fragment = purl[2], purl[5]
nd = dest.add_item(href, fragment, text)
nd.play_order = play_order
if content and text:
content = content[0]
src = get_attr(content, attr='src')
if src:
purl = urlparse(unquote(content.get('src')))
href, fragment = purl[2], purl[5]
nd = dest.add_item(href, fragment, text)
nd.play_order = play_order
for c in np_path(np):
process_navpoint(c, nd)

View File

@ -275,7 +275,15 @@ class MobiMLizer(object):
# <mbp:frame-set/> does not exist lalalala
if style['display'] in ('none', 'oeb-page-head', 'oeb-page-foot') \
or style['visibility'] == 'hidden':
return
id_ = elem.get('id', None)
if id_:
# Keep anchors so people can use display:none
# to generate hidden TOCs
elem.clear()
elem.text = None
elem.set('id', id_)
else:
return
tag = barename(elem.tag)
istate = copy.copy(istates[-1])
istate.rendered = False
@ -378,6 +386,15 @@ class MobiMLizer(object):
for attr in ('rowspan', 'colspan','width','border','scope'):
if attr in elem.attrib:
istate.attrib[attr] = elem.attrib[attr]
if tag == 'q':
t = elem.text
if not t:
t = ''
elem.text = u'\u201c' + t
t = elem.tail
if not t:
t = ''
elem.tail = u'\u201d' + t
text = None
if elem.text:
if istate.preserve:
@ -406,6 +423,12 @@ class MobiMLizer(object):
parent = bstate.para if bstate.inline is None else bstate.inline
if parent is not None:
vtag = etree.SubElement(parent, XHTML(vtag))
# Add anchors
for child in vbstate.body:
if child is not vbstate.para:
vtag.append(child)
else:
break
for child in vbstate.para:
vtag.append(child)
return

View File

@ -49,5 +49,3 @@ class OEBOutput(OutputFormatPlugin):
with open(path, 'wb') as f:
f.write(str(item))
item.unload_data_from_memory(memory=path)

View File

@ -143,11 +143,17 @@ def render_jacket(mi, output_profile,
if comments:
comments = comments_to_html(comments)
try:
author = mi.format_authors()
except:
author = ''
def generate_html(comments):
args = dict(xmlns=XHTML_NS,
title_str=title_str,
css=css,
title=title,
author=author,
pubdate_label=_('Published'), pubdate=pubdate,
series_label=_('Series'), series=series,
rating_label=_('Rating'), rating=rating,

View File

@ -101,11 +101,12 @@ class SNBMLizer(object):
subitem = ''
bodyTree = trees[subitem].find(".//body")
for line in output.splitlines():
if not line.find(CALIBRE_SNB_PRE_TAG) == 0:
pos = line.find(CALIBRE_SNB_PRE_TAG)
if pos == -1:
line = line.strip(u' \t\n\r\u3000')
else:
etree.SubElement(bodyTree, "text").text = \
etree.CDATA(line[len(CALIBRE_SNB_PRE_TAG):])
etree.CDATA(line[pos+len(CALIBRE_SNB_PRE_TAG):])
continue
if len(line) != 0:
if line.find(CALIBRE_SNB_IMG_TAG) == 0:

View File

@ -715,13 +715,13 @@ def build_forms(srcdir, info=None):
dat = re.compile(r'QtGui.QApplication.translate\(.+?,\s+"(.+?)(?<!\\)",.+?\)', re.DOTALL).sub(r'_("\1")', dat)
dat = dat.replace('_("MMM yyyy")', '"MMM yyyy"')
dat = pat.sub(sub, dat)
dat = dat.replace('from QtWebKit.QWebView import QWebView',
'from PyQt4 import QtWebKit\nfrom PyQt4.QtWebKit import QWebView')
if form.endswith('viewer%smain.ui'%os.sep):
info('\t\tPromoting WebView')
dat = dat.replace('self.view = QtWebKit.QWebView(', 'self.view = DocumentView(')
dat = dat.replace('self.view = QWebView(', 'self.view = DocumentView(')
dat = dat.replace('from QtWebKit.QWebView import QWebView',
'from PyQt4 import QtWebKit\nfrom PyQt4.QtWebKit import QWebView')
dat += '\n\nfrom calibre.gui2.viewer.documentview import DocumentView'
open(compiled_form, 'wb').write(dat)

View File

@ -192,14 +192,15 @@ class EditMetadataAction(InterfaceAction):
_('At least two books must be selected for merging'),
show=True)
dest_id, src_books, src_ids = self.books_to_merge(rows)
title = self.gui.library_view.model().db.title(dest_id, index_is_id=True)
if safe_merge:
if not confirm('<p>'+_(
'Book formats and metadata from the selected books '
'will be added to the <b>first selected book.</b> '
'will be added to the <b>first selected book</b> (%s). '
'ISBN will <i>not</i> be merged.<br><br> '
'The second and subsequently selected books will not '
'be deleted or changed.<br><br>'
'Please confirm you want to proceed.')
'Please confirm you want to proceed.')%title
+'</p>', 'merge_books_safe', self.gui):
return
self.add_formats(dest_id, src_books)
@ -207,14 +208,14 @@ class EditMetadataAction(InterfaceAction):
else:
if not confirm('<p>'+_(
'Book formats and metadata from the selected books will be merged '
'into the <b>first selected book</b>. '
'into the <b>first selected book</b> (%s). '
'ISBN will <i>not</i> be merged.<br><br>'
'After merger the second and '
'subsequently selected books will be <b>deleted</b>. <br><br>'
'All book formats of the first selected book will be kept '
'and any duplicate formats in the second and subsequently selected books '
'will be permanently <b>deleted</b> from your computer.<br><br> '
'Are you <b>sure</b> you want to proceed?')
'Are you <b>sure</b> you want to proceed?')%title
+'</p>', 'merge_books', self.gui):
return
if len(rows)>5:
@ -233,6 +234,7 @@ class EditMetadataAction(InterfaceAction):
ci = self.gui.library_view.model().index(dest_row, 0)
if ci.isValid():
self.gui.library_view.setCurrentIndex(ci)
self.gui.library_view.model().current_changed(ci, ci)
def add_formats(self, dest_id, src_books, replace=False):
for src_book in src_books:

View File

@ -35,7 +35,6 @@ class ViewAction(InterfaceAction):
self.qaction.setMenu(self.view_menu)
ac.triggered.connect(self.view_specific_format, type=Qt.QueuedConnection)
def location_selected(self, loc):
enabled = loc == 'library'
for action in list(self.view_menu.actions())[1:]:
@ -134,6 +133,9 @@ class ViewAction(InterfaceAction):
rows = self.gui.current_view().selectionModel().selectedRows()
self._view_books(rows)
def view_triggered(self, index):
self._view_books([index])
def view_specific_book(self, index):
self._view_books([index])

View File

@ -5,11 +5,13 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, collections
import os, collections, sys
from Queue import Queue
from PyQt4.Qt import QLabel, QPixmap, QSize, QWidget, Qt, pyqtSignal, \
QVBoxLayout, QScrollArea, QPropertyAnimation, QEasingCurve, \
QSizePolicy, QPainter, QRect, pyqtProperty
from PyQt4.Qt import QPixmap, QSize, QWidget, Qt, pyqtSignal, \
QPropertyAnimation, QEasingCurve, QThread, QApplication, QFontInfo, \
QSizePolicy, QPainter, QRect, pyqtProperty, QLayout, QPalette
from PyQt4.QtWebKit import QWebView
from calibre import fit_image, prepare_string_for_xml
from calibre.gui2.widgets import IMAGE_EXTENSIONS
@ -67,10 +69,7 @@ class CoverView(QWidget): # {{{
def __init__(self, vertical, parent=None):
QWidget.__init__(self, parent)
self.setMaximumSize(QSize(120, 120))
self.setMinimumSize(QSize(120 if vertical else 20, 120 if vertical else
20))
self._current_pixmap_size = self.maximumSize()
self._current_pixmap_size = QSize(120, 120)
self.vertical = vertical
self.animation = QPropertyAnimation(self, 'current_pixmap_size', self)
@ -79,8 +78,9 @@ class CoverView(QWidget): # {{{
self.animation.setStartValue(QSize(0, 0))
self.animation.valueChanged.connect(self.value_changed)
self.setSizePolicy(QSizePolicy.Expanding if vertical else
QSizePolicy.Minimum, QSizePolicy.Expanding)
self.setSizePolicy(
QSizePolicy.Expanding if vertical else QSizePolicy.Minimum,
QSizePolicy.Expanding)
self.default_pixmap = QPixmap(I('book.png'))
self.pixmap = self.default_pixmap
@ -109,20 +109,6 @@ class CoverView(QWidget): # {{{
self.current_pixmap_size = QSize(self.pwidth, self.pheight)
self.animation.setEndValue(self.current_pixmap_size)
def relayout(self, parent_size):
if self.vertical:
self.setMaximumSize(parent_size.width(),
min(int(parent_size.height()/2.),int(4/3. * parent_size.width())+1))
else:
self.setMaximumSize(1+int(3/4. * parent_size.height()),
parent_size.height())
self.resize(self.maximumSize())
self.animation.stop()
self.do_layout()
def sizeHint(self):
return self.maximumSize()
def show_data(self, data):
self.animation.stop()
same_item = data.get('id', True) == self.data.get('id', False)
@ -165,70 +151,185 @@ class CoverView(QWidget): # {{{
# }}}
# Book Info {{{
class Label(QLabel):
mr = pyqtSignal(object)
class RenderComments(QThread):
rdone = pyqtSignal(object, object)
def __init__(self, parent):
QThread.__init__(self, parent)
self.queue = Queue()
self.start()
def run(self):
while True:
try:
rows, comments = self.queue.get()
except:
break
import time
time.sleep(0.001)
oint = sys.getcheckinterval()
sys.setcheckinterval(5)
try:
self.rdone.emit(rows, comments_to_html(comments))
except:
pass
sys.setcheckinterval(oint)
class BookInfo(QWebView):
link_clicked = pyqtSignal(object)
def __init__(self):
QLabel.__init__(self)
self.setTextFormat(Qt.RichText)
self.setText('')
self.setWordWrap(True)
self.setAlignment(Qt.AlignTop)
self.linkActivated.connect(self.link_activated)
def __init__(self, vertical, parent=None):
QWebView.__init__(self, parent)
self.vertical = vertical
self.renderer = RenderComments(self)
self.renderer.rdone.connect(self._show_data, type=Qt.QueuedConnection)
self.page().setLinkDelegationPolicy(self.page().DelegateAllLinks)
self.linkClicked.connect(self.link_activated)
self._link_clicked = False
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
def link_activated(self, link):
self._link_clicked = True
link = unicode(link)
link = unicode(link.toString())
self.link_clicked.emit(link)
def mouseReleaseEvent(self, ev):
QLabel.mouseReleaseEvent(self, ev)
if not self._link_clicked:
self.mr.emit(ev)
self._link_clicked = False
class BookInfo(QScrollArea):
def __init__(self, vertical, parent=None):
QScrollArea.__init__(self, parent)
self.vertical = vertical
self.setWidgetResizable(True)
self.label = Label()
self.setWidget(self.label)
self.link_clicked = self.label.link_clicked
self.mr = self.label.mr
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def turnoff_scrollbar(self, *args):
self.page().mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
def show_data(self, data):
self.label.setText('')
rows = render_rows(data)
rows = u'\n'.join([u'<tr><td valign="top"><b>%s:</b></td><td valign="top">%s</td></tr>'%(k,t) for
k, t in rows])
comments = ''
if data.get(_('Comments'), '') not in ('', u'None'):
comments = data[_('Comments')]
comments = comments_to_html(comments)
comments = data.get(_('Comments'), '')
if comments and comments != u'None':
self.renderer.queue.put((rows, comments))
self._show_data(rows, '')
def _show_data(self, rows, comments):
f = QFontInfo(QApplication.font(self.parent())).pixelSize()
p = unicode(QApplication.palette().color(QPalette.Normal,
QPalette.Base).name())
templ = u'''\
<html>
<head>
<style type="text/css">
body, td {background-color: %s; font-size: %dpx}
a { text-decoration: none; color: blue }
</style>
</head>
<body>
%%s
</body>
<html>
'''%(p, f)
if self.vertical:
if comments:
rows += u'<tr><td colspan="2">%s</td></tr>'%comments
self.label.setText(u'<table>%s</table>'%rows)
self.setHtml(templ%(u'<table>%s</table>'%rows))
else:
left_pane = u'<table>%s</table>'%rows
right_pane = u'<div>%s</div>'%comments
self.label.setText(u'<table><tr><td valign="top" '
self.setHtml(templ%(u'<table><tr><td valign="top" '
'style="padding-right:2em">%s</td><td valign="top">%s</td></tr></table>'
% (left_pane, right_pane))
% (left_pane, right_pane)))
def mouseDoubleClickEvent(self, ev):
ev.ignore()
# }}}
class DetailsLayout(QLayout): # {{{
def __init__(self, vertical, parent):
QLayout.__init__(self, parent)
self.vertical = vertical
self._children = []
self.min_size = QSize(190, 200) if vertical else QSize(120, 120)
self.setContentsMargins(0, 0, 0, 0)
def minimumSize(self):
return QSize(self.min_size)
def addItem(self, child):
if len(self._children) > 2:
raise ValueError('This layout can only manage two children')
self._children.append(child)
def itemAt(self, i):
try:
return self._children[i]
except:
pass
return None
def takeAt(self, i):
try:
self._children.pop(i)
except:
pass
return None
def count(self):
return len(self._children)
def sizeHint(self):
return QSize(self.min_size)
def setGeometry(self, r):
QLayout.setGeometry(self, r)
self.do_layout(r)
def cover_height(self, r):
mh = min(int(r.height()/2.), int(4/3. * r.width())+1)
try:
ph = self._children[0].widget().pixmap.height()
except:
ph = 0
if ph > 0:
mh = min(mh, ph)
return mh
def cover_width(self, r):
mw = 1 + int(3/4. * r.height())
try:
pw = self._children[0].widget().pixmap.width()
except:
pw = 0
if pw > 0:
mw = min(mw, pw)
return mw
def do_layout(self, rect):
if len(self._children) != 2:
return
left, top, right, bottom = self.getContentsMargins()
r = rect.adjusted(+left, +top, -right, -bottom)
x = r.x()
y = r.y()
cover, details = self._children
if self.vertical:
ch = self.cover_height(r)
cover.setGeometry(QRect(x, y, r.width(), ch))
cover.widget().do_layout()
y += ch + 5
details.setGeometry(QRect(x, y, r.width(), r.height()-ch-5))
else:
cw = self.cover_width(r)
cover.setGeometry(QRect(x, y, cw, r.height()))
cover.widget().do_layout()
x += cw + 5
details.setGeometry(QRect(x, y, r.width() - cw - 5, r.height()))
# }}}
class BookDetails(QWidget): # {{{
resized = pyqtSignal(object)
show_book_info = pyqtSignal()
open_containing_folder = pyqtSignal(int)
view_specific_format = pyqtSignal(int, object)
@ -269,23 +370,14 @@ class BookDetails(QWidget): # {{{
def __init__(self, vertical, parent=None):
QWidget.__init__(self, parent)
self.setAcceptDrops(True)
self._layout = QVBoxLayout()
if not vertical:
self._layout.setDirection(self._layout.LeftToRight)
self._layout = DetailsLayout(vertical, self)
self.setLayout(self._layout)
self.cover_view = CoverView(vertical, self)
self.cover_view.relayout(self.size())
self.resized.connect(self.cover_view.relayout, type=Qt.QueuedConnection)
self._layout.addWidget(self.cover_view)
self.book_info = BookInfo(vertical, self)
self._layout.addWidget(self.book_info)
self.book_info.link_clicked.connect(self._link_clicked)
self.book_info.mr.connect(self.mouseReleaseEvent)
if vertical:
self.setMinimumSize(QSize(190, 200))
else:
self.setMinimumSize(120, 120)
self.setCursor(Qt.PointingHandCursor)
def _link_clicked(self, link):
@ -299,17 +391,15 @@ class BookDetails(QWidget): # {{{
open_local_file(val)
def mouseReleaseEvent(self, ev):
def mouseDoubleClickEvent(self, ev):
ev.accept()
self.show_book_info.emit()
def resizeEvent(self, ev):
self.resized.emit(self.size())
def show_data(self, data):
self.cover_view.show_data(data)
self.book_info.show_data(data)
self.setToolTip('<p>'+_('Click to open Book Details window') +
self.cover_view.show_data(data)
self._layout.do_layout(self.rect())
self.setToolTip('<p>'+_('Double-click to open Book Details window') +
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), ''))
def reset_info(self):

View File

@ -28,6 +28,8 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options, conne
if log is None:
log = Log()
from calibre.library import db
from calibre.utils.config import prefs
prefs.refresh()
db = db()
db.catalog_plugin_on_device_temp_mapping = dbspec

View File

@ -108,7 +108,7 @@ class BookInfo(QDialog, Ui_BookInfo):
lines = comments.splitlines()
lines = [x if x.strip() else '<br><br>' for x in lines]
comments = '\n'.join(lines)
self.comments.setText('<div>%s</div>' % comments)
self.comments.setHtml('<div>%s</div>' % comments)
cdata = info.pop('cover', '')
self.cover_pixmap = QPixmap.fromImage(cdata)
self.resize_cover()

View File

@ -49,7 +49,19 @@
</property>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QTextBrowser" name="comments"/>
<widget class="QWebView" name="comments">
<property name="maximumSize">
<size>
<width>350</width>
<height>16777215</height>
</size>
</property>
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</item>
</layout>
</widget>
@ -91,6 +103,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebView</class>
<extends>QWidget</extends>
<header>QtWebKit/QWebView</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../../../resources/images.qrc"/>
</resources>

View File

@ -456,7 +456,7 @@ Using this button to create author sort will change author sort from red to gree
<property name="maximumSize">
<size>
<width>16777215</width>
<height>130</height>
<height>140</height>
</size>
</property>
<property name="dragDropMode">

View File

@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
from functools import partial
from PyQt4.Qt import QIcon, Qt, QWidget, QToolBar, QSize, \
pyqtSignal, QToolButton, \
pyqtSignal, QToolButton, QPushButton, \
QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup, \
QMenu
@ -151,6 +151,7 @@ class SearchBar(QWidget): # {{{
QWidget.__init__(self, parent)
self._layout = l = QHBoxLayout()
self.setLayout(self._layout)
self._layout.setContentsMargins(0,5,0,0)
x = ComboBoxWithHelp(self)
x.setMaximumSize(QSize(150, 16777215))
@ -170,16 +171,20 @@ class SearchBar(QWidget): # {{{
l.addWidget(x)
x.setToolTip(_("Advanced search"))
self.label = x = QLabel(_('&Search:'))
l.addWidget(self.label)
x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
x = parent.search = SearchBox2(self)
x.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
x.setObjectName("search")
x.setToolTip(_("<p>Search the list of books by title, author, publisher, tags, comments, etc.<br><br>Words separated by spaces are ANDed"))
l.addWidget(x)
self.search_button = QPushButton(_('&Go!'))
l.addWidget(self.search_button)
self.search_button.setSizePolicy(QSizePolicy.Minimum,
QSizePolicy.Minimum)
self.search_button.clicked.connect(parent.search.do_search)
self.search_button.setToolTip(
_('Do Quick Search (you can also press the Enter key)'))
x = parent.clear_button = QToolButton(self)
x.setIcon(QIcon(I('clear_left.png')))
x.setObjectName("clear_button")
@ -210,7 +215,6 @@ class SearchBar(QWidget): # {{{
l.addWidget(x)
x.setToolTip(_("Delete current saved search"))
self.label.setBuddy(parent.search)
# }}}

View File

@ -233,17 +233,10 @@ class BooksModel(QAbstractTableModel): # {{{
def delete_books_by_id(self, ids):
for id in ids:
try:
row = self.db.row(id)
except:
row = -1
if row > -1:
self.beginRemoveRows(QModelIndex(), row, row)
self.db.delete_book(id)
if row > -1:
self.endRemoveRows()
self.count_changed()
self.clear_caches()
self.reset()
def books_added(self, num):
if num > 0:
@ -335,7 +328,7 @@ class BooksModel(QAbstractTableModel): # {{{
sidx = self.db.series_index(idx)
sidx = fmt_sidx(sidx, use_roman = self.use_roman_numbers)
data[_('Series')] = \
_('Book <font face="serif">%s</font> of %s.')%\
_('Book %s of %s.')%\
(sidx, prepare_string_for_xml(series))
mi = self.db.get_metadata(idx)
for key in mi.custom_field_keys():

View File

@ -50,6 +50,8 @@ class BooksView(QTableView): # {{{
def __init__(self, parent, modelcls=BooksModel):
QTableView.__init__(self, parent)
self.setEditTriggers(self.SelectedClicked|self.EditKeyPressed)
self.drag_allowed = True
self.setDragEnabled(True)
self.setDragDropOverwriteMode(False)
@ -98,6 +100,8 @@ class BooksView(QTableView): # {{{
self._model.about_to_be_sorted.connect(self.about_to_be_sorted)
self._model.sorting_done.connect(self.sorting_done)
self.doubleClicked.connect(parent.iactions['View'].view_triggered)
# Column Header Context Menu {{{
def column_header_context_handler(self, action=None, column=None):
if not action or not column:
@ -385,7 +389,8 @@ class BooksView(QTableView): # {{{
self.save_state()
self._model.set_database(db)
self.tags_delegate.set_database(db)
self.authors_delegate.set_auto_complete_function(db.all_authors)
self.authors_delegate.set_auto_complete_function(
lambda: [(x, y.replace('|', ',')) for (x, y) in db.all_authors()])
self.series_delegate.set_auto_complete_function(db.all_series)
self.publisher_delegate.set_auto_complete_function(db.all_publishers)

View File

@ -345,6 +345,7 @@ def main(args=sys.argv):
# On windows only singleinstance can be trusted
otherinstance = True if iswindows else False
if not otherinstance:
sys.setcheckinterval(50) # Make GUI more responsive
return run_gui(opts, args, actions, listener, app)
communicate(args)

View File

@ -148,6 +148,10 @@ class SearchBox2(QComboBox):
self.line_edit.setStyleSheet('QLineEdit { color: black; background-color: %s; }' % col)
def key_pressed(self, event):
k = event.key()
if k in (Qt.Key_Left, Qt.Key_Right, Qt.Key_Up, Qt.Key_Down,
Qt.Key_Home, Qt.Key_End, Qt.Key_PageUp, Qt.Key_PageDown):
return
self.normalize_state()
if self._in_a_search:
self.emit(SIGNAL('changed()'))
@ -159,8 +163,11 @@ class SearchBox2(QComboBox):
def mouse_released(self, event):
self.normalize_state()
if self.as_you_type:
self.timer.start(1500)
# Dont trigger a search since it make
# re-positioning the cursor using the mouse
# impossible
#if self.as_you_type:
# self.timer.start(1500)
def timer_event(self):
self.do_search()
@ -176,7 +183,7 @@ class SearchBox2(QComboBox):
return ''
return text
def do_search(self):
def do_search(self, *args):
text = unicode(self.currentText()).strip()
if not text or text == self.help_text:
return self.clear()
@ -365,7 +372,7 @@ class SearchBoxMixin(object):
self.search.setMaximumWidth(self.width()-150)
self.action_focus_search = QAction(self)
shortcuts = QKeySequence.keyBindings(QKeySequence.Find)
shortcuts = list(shortcuts) + [QKeySequence('/')]
shortcuts = list(shortcuts) + [QKeySequence('/'), QKeySequence('Alt+S')]
self.action_focus_search.setShortcuts(shortcuts)
self.action_focus_search.triggered.connect(lambda x:
self.search.setFocus(Qt.OtherFocusReason))

View File

@ -861,6 +861,7 @@ class TagBrowserWidget(QWidget): # {{{
QWidget.__init__(self, parent)
self._layout = QVBoxLayout()
self.setLayout(self._layout)
self._layout.setContentsMargins(0,0,0,0)
parent.tags_view = TagsView(parent)
self._layout.addWidget(parent.tags_view)

View File

@ -358,10 +358,11 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
def toc_clicked(self, index):
item = self.toc_model.itemFromIndex(index)
url = QUrl.fromLocalFile(item.abspath)
if item.fragment:
url.setFragment(item.fragment)
self.link_clicked(url)
if item.abspath is not None:
url = QUrl.fromLocalFile(item.abspath)
if item.fragment:
url.setFragment(item.fragment)
self.link_clicked(url)
def selection_changed(self, selected_text):
self.selected_text = selected_text.strip()

View File

@ -128,7 +128,7 @@ class ContentServer(object):
if want_mobile:
return self.mobile()
return self.browse_toplevel()
return self.browse_catalog()
def old(self, **kwargs):
return self.static('index.html').replace('{prefix}',

View File

@ -5,7 +5,7 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import sys
import sys, os
from threading import Thread
from calibre.library.server import server_config as config
@ -63,15 +63,47 @@ The OPDS interface is advertised via BonJour automatically.
' work in all environments.'))
return parser
def daemonize(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
try:
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
except OSError, e:
print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
sys.exit(1)
# decouple from parent environment
os.chdir("/")
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
except OSError, e:
print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
sys.exit(1)
# Redirect standard file descriptors.
si = file(stdin, 'r')
so = file(stdout, 'a+')
se = file(stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
def main(args=sys.argv):
from calibre.library.database2 import LibraryDatabase2
parser = option_parser()
opts, args = parser.parse_args(args)
if opts.daemonize and not iswindows:
from cherrypy.process.plugins import Daemonizer
d = Daemonizer(cherrypy.engine)
d.subscribe()
daemonize()
if opts.pidfile is not None:
from cherrypy.process.plugins import PIDFile
PIDFile(cherrypy.engine, opts.pidfile).subscribe()

View File

@ -338,6 +338,8 @@ Calibre has several keyboard shortcuts to save you time and mouse movement. Thes
* - Keyboard Shortcut
- Action
* - :kbd:`F2 (Enter in OS X)`
- Edit the metadata of the currently selected field in the book list.
* - :kbd:`A`
- Add Books
* - :kbd:`C`

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

@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: calibre\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2010-10-28 00:31+0000\n"
"PO-Revision-Date: 2010-10-28 17:43+0000\n"
"POT-Creation-Date: 2010-10-29 19:59+0000\n"
"PO-Revision-Date: 2010-10-29 19:53+0000\n"
"Last-Translator: Antón Méixome <meixome@gmail.com>\n"
"Language-Team: Galician <gl@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-10-29 05:23+0000\n"
"X-Launchpad-Export-Date: 2010-10-30 05:03+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:43
@ -38,8 +38,8 @@ msgstr "Non facer nada"
#: /home/kovid/work/calibre/src/calibre/ebooks/chm/metadata.py:56
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:407
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/periodical.py:124
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:70
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:72
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:93
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:95
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:336
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:339
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1894
@ -54,7 +54,7 @@ msgstr "Non facer nada"
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:606
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:36
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:61
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:50
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fetch.py:333
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:36
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:64
@ -145,7 +145,7 @@ msgstr "Non facer nada"
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2161
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2163
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2295
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:224
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:228
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:139
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:142
#: /home/kovid/work/calibre/src/calibre/library/server/xml.py:78
@ -267,45 +267,45 @@ msgstr "Define os metadatos nos ficheiros %s"
msgid "Set metadata from %s files"
msgstr "Define os metadatos desde os ficheiros %s"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:706
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:708
msgid "Look and Feel"
msgstr "Aparencia e comportamento"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:708
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:720
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:731
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:742
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:710
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:722
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:733
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:744
msgid "Interface"
msgstr "Interface"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:712
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:714
msgid "Adjust the look and feel of the calibre interface to suit your tastes"
msgstr ""
"Axuste a aparencia e comportamento da interface do Calibre para que se "
"adapte aos seus gustos"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:718
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:720
msgid "Behavior"
msgstr "Comportamento"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:724
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:726
msgid "Change the way calibre behaves"
msgstr "Cambiar o modo en que se comporta o Calibre"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:729
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:731
#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:202
msgid "Add your own columns"
msgstr "Engadir as súas propias columnas"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:735
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:737
msgid "Add/remove your own columns to the calibre book list"
msgstr "Engadir/retirar as columnas propias da lista de libros do Calibre"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:740
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:742
msgid "Customize the toolbar"
msgstr "Personalizar a barra de ferramentas"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:746
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:748
msgid ""
"Customize the toolbars and context menus, changing which actions are "
"available in each"
@ -313,60 +313,60 @@ msgstr ""
"Personalizar as barras de ferramentas e os menús de contexto, cambiando as "
"accións que estarán dispoñíbeis en cada un"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:752
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:754
msgid "Input Options"
msgstr "Opcións de entrada"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:754
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:765
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:776
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:756
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:767
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:778
msgid "Conversion"
msgstr "Conversión"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:758
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:760
msgid "Set conversion options specific to each input format"
msgstr ""
"Estabelecer as opcións de conversión específicas para cada formato de entrada"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:763
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:765
msgid "Common Options"
msgstr "Opcións comúns"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:769
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:771
msgid "Set conversion options common to all formats"
msgstr "Estabelecer as opcións de conversión comúns para todos os formatos"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:774
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:776
msgid "Output Options"
msgstr "Opcións de saída"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:780
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:782
msgid "Set conversion options specific to each output format"
msgstr ""
"Estabelece as opcións específicas de conversión para cada formato de saída"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:785
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:787
msgid "Adding books"
msgstr "Adición de libros"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:787
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:799
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:811
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:823
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:789
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:801
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:813
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:825
msgid "Import/Export"
msgstr "Importar/exportar"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:791
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:793
msgid "Control how calibre reads metadata from files when adding books"
msgstr ""
"Controla o modo como Calibre le os metadatos dos ficheiros na proceso de "
"adición de libros"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:797
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:799
msgid "Saving books to disk"
msgstr "Gardado de libros no disco"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:803
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:805
msgid ""
"Control how calibre exports files from its database to disk when using Save "
"to disk"
@ -374,33 +374,33 @@ msgstr ""
"Controla o modo como Calibre exporta ficheiros da súa base de datos no "
"disco no proceso de Gardado no disco"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:809
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:811
msgid "Sending books to devices"
msgstr "Envío de libros a dispositivos"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:815
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:817
msgid "Control how calibre transfers files to your ebook reader"
msgstr ""
"Controla cando Calibre transfere os ficheiros ao seu lector de libros"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:821
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:823
msgid "Metadata plugboards"
msgstr "Plugboards de metadatos"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:827
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:829
msgid "Change metadata fields before saving/sending"
msgstr "Cambiar campos de metadatos antes do gardado/envío"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:832
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:834
msgid "Sharing books by email"
msgstr "Compartición de libros por correo"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:834
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:846
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:836
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:848
msgid "Sharing"
msgstr "Compartición"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:838
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:840
msgid ""
"Setup sharing of books via email. Can be used for automatic sending of "
"downloaded news to your devices"
@ -408,11 +408,11 @@ msgstr ""
"Configura a compartición de libros por correo electrónico. Pódese usar para "
"enviar automticamente as noticias descargadas aos seus dispositivos"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:844
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:846
msgid "Sharing over the net"
msgstr "Compartición na rede"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:850
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:852
msgid ""
"Setup the calibre Content Server which will give you access to your calibre "
"library from anywhere, on any device, over the internet"
@ -421,33 +421,33 @@ msgstr ""
"biblioteca do Calibre en calquera lugar, con calquera dispositivo, a través "
"da Internet."
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:857
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:859
msgid "Plugins"
msgstr "Complementos"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:859
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:871
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:882
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:861
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:873
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:884
msgid "Advanced"
msgstr "Avanzado"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:863
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:865
msgid "Add/remove/customize various bits of calibre functionality"
msgstr "Engadir/eliminar/configurar diversas funcións do Calibre"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:869
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:871
msgid "Tweaks"
msgstr "Axustes"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:875
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:877
msgid "Fine tune how calibre behaves in various contexts"
msgstr "Axuste fino de como se comporta o Calibre en diversos contextos"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:880
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:882
msgid "Miscellaneous"
msgstr "Miscelánea"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:886
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:888
msgid "Miscellaneous advanced configuration"
msgstr "Configuración miscelánea avanzada"
@ -611,7 +611,7 @@ msgstr ""
msgid "This profile is intended for the Amazon Kindle DX."
msgstr "Este perfil é o propio o Amazon Kindle DX."
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:663
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:664
msgid "This profile is intended for the Sanda Bambook."
msgstr "Este perfil está pensado para o Sandra Bambook."
@ -1024,11 +1024,11 @@ msgstr "Comunicar co Kogan"
msgid "Communicate with the Pandigital Novel"
msgstr "Conectar co Pandigital Novel"
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:124
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:130
msgid "Communicate with the VelocityMicro"
msgstr "Comunicar con VelocityMono"
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:142
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:148
msgid "Communicate with the GM2000"
msgstr "Comunicar co GM2000"
@ -1089,11 +1089,15 @@ msgstr "Comunicar co lector Teclast K3/K5."
msgid "Communicate with the Newsmy reader."
msgstr "Comunicar co lector de Newsmy"
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:48
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:47
msgid "Communicate with the Pico reader."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:57
msgid "Communicate with the iPapyrus reader."
msgstr "Comunicar co lector de iPapyrus."
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:59
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:68
msgid "Communicate with the Sovos reader."
msgstr "Comunicar co lector Sovos."
@ -2040,7 +2044,7 @@ msgstr ""
"opción, a imaxe pode distorsionarse, mais non terá bordos."
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:170
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:205
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:203
msgid "Start"
msgstr "Iniciar"
@ -2435,7 +2439,7 @@ msgstr "Si"
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:380
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:930
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:303
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:570
msgid "Title"
msgstr "Título"
@ -2479,7 +2483,7 @@ msgstr "Comentarios"
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:320
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1134
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:160
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:608
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:618
msgid "Tags"
msgstr "Etiquetas"
@ -2909,7 +2913,7 @@ msgstr "Opcións da xeración HTM TOC"
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:153
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:71
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:606
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:616
msgid "Rating"
msgstr "Avaliación"
@ -3279,7 +3283,7 @@ msgstr ""
"Calibre. Convértao primeiro a HTML e ténteo despois.\n"
"%s"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:32
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:25
#: /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."
@ -3287,7 +3291,7 @@ msgstr ""
"Especifique a codificación de caracteres do documento de saída. A "
"predeterminada é UTF-8."
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:39
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:29
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:38
msgid ""
"The maximum number of characters per line. This splits on the first space "
@ -3301,17 +3305,34 @@ msgstr ""
"de haber un mínimo de 25 caracteres. Se, pola contra, desexa desactivar a "
"quebra de liña, empregue o 0."
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:124
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:36
msgid ""
"Specify whether or not to insert an empty line between two paragraphs."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:40
msgid ""
"Specify whether or not to insert two space characters to indent the first "
"line of each paragraph."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:44
msgid ""
"Specify whether or not to hide the chapter title for each chapter. Useful "
"for image-only output (eg. comics)."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:122
msgid "Start Page"
msgstr "Páxina de inicio"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:132
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:134
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:136
msgid "Cover Pages"
msgstr "Portadas"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:149
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:152
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:147
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:150
msgid " (Preface)"
msgstr " (Prefacio)"
@ -3492,7 +3513,7 @@ msgid "Disable UI animations"
msgstr "Desactivar as animacións UI"
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:183
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:487
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:494
msgid "Copied"
msgstr "Copiado"
@ -3504,7 +3525,7 @@ msgstr "Copiar"
msgid "Copy to Clipboard"
msgstr "Copiar no Portapapeis"
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:466
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:467
msgid "Choose Files"
msgstr "Escoller os Ficheiros"
@ -4410,7 +4431,7 @@ msgid "Click the show details button to see which ones."
msgstr "Prema o botón de amosar detalles para os ver."
#: /home/kovid/work/calibre/src/calibre/gui2/actions/show_book_details.py:16
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:613
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:623
msgid "Show book details"
msgstr "Amosar os detalles do libro"
@ -4868,7 +4889,7 @@ msgstr "saída"
#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_input_ui.py:43
#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:47
#: /home/kovid/work/calibre/src/calibre/gui2/convert/rb_output_ui.py:33
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:28
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:39
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:80
#: /home/kovid/work/calibre/src/calibre/gui2/convert/toc_ui.py:67
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:51
@ -5768,6 +5789,18 @@ msgstr ""
msgid "SNB Output"
msgstr "Saída SNB"
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:40
msgid "Hide chapter name"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:41
msgid "Insert space before the first line for each paragraph"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:42
msgid "Insert empty line between paragraphs"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:17
msgid ""
"Structure\n"
@ -6166,8 +6199,8 @@ msgid "Send and delete from library"
msgstr "Enviar e borrar da biblioteca"
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:492
msgid "Send specific format"
msgstr "Enviar un formato específico"
msgid "Send specific format to"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:528
msgid "Eject device"
@ -6505,7 +6538,7 @@ msgid "No location selected"
msgstr "Non seleccionou unha localización"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_library.py:84
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:628
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:635
msgid "Bad location"
msgstr "Localización incorrecta"
@ -6586,7 +6619,7 @@ msgstr "Localización"
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:932
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:31
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:294
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:568
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
msgid "Date"
msgstr "Data"
@ -7234,76 +7267,77 @@ msgid "This ISBN number is invalid"
msgstr "O ISBN non é correcto"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:611
msgid "Cannot use tag editor"
msgstr "Non se pode empregar o editor de etiquetas"
msgid "Tags changed"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:612
msgid "The tags editor cannot be used if you have modified the tags"
msgid ""
"You have changed the tags. In order to use the tags editor, you must either "
"discard or apply these changes"
msgstr ""
"O editor de etiquetas non se pode empregar se se modificaron as etiquetas"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:632
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:639
msgid "Downloading cover..."
msgstr "A descargar a capa..."
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:644
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:649
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:655
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:660
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:651
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:656
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:662
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:667
msgid "Cannot fetch cover"
msgstr "Non se puido obter a capa"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:645
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:656
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:661
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:652
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:663
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:668
msgid "<b>Could not fetch cover.</b><br/>"
msgstr "<b>Non se puido obter a capa</b><br/>"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:646
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:653
msgid "The download timed out."
msgstr "Expirou o tempo de descarga"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:650
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:657
msgid "Could not find cover for this book. Try specifying the ISBN first."
msgstr ""
"Non se achou unha capa para este libro; tente especificar primeiro o ISBN."
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:662
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:669
msgid ""
"For the error message from each cover source, click Show details below."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:669
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:676
msgid "Bad cover"
msgstr "Capa incorrecta"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:670
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:677
msgid "The cover is not a valid picture"
msgstr "A capa non é unha imaxe válida"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:703
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:710
msgid "There were errors"
msgstr "Houbo algúns erros"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:704
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:711
msgid "There were errors downloading social metadata"
msgstr "Houbo algúns erros ao descargar os metadatos sociais"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:735
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:742
msgid "Cannot fetch metadata"
msgstr "Non é posíbel oter metadatos"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:736
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:743
msgid "You must specify at least one of ISBN, Title, Authors or Publisher"
msgstr ""
"Ten de especificar, cando menos, un dos seguintes: ISBN, título, autoría ou "
"editorial."
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:823
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:833
msgid "Permission denied"
msgstr "Permiso denegado"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:824
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:834
msgid "Could not open %s. Is it being used by another program?"
msgstr "Non se pode abri %s. Estará a se empregar cun outro programa?"
@ -8645,7 +8679,7 @@ msgid "Do not check for updates"
msgstr "Non comprobar se hai actualizacións"
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:58
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:636
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:643
msgid "Calibre Library"
msgstr "Biblioteca do Calibre"
@ -8813,7 +8847,7 @@ msgid "Successfully downloaded metadata for %d out of %d books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/metadata.py:287
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:612
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:622
msgid "Details"
msgstr ""
@ -10726,59 +10760,59 @@ msgstr ""
msgid "Toggle"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:378
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:385
msgid ""
"If you use the WordPlayer e-book app on your Android phone, you can access "
"your calibre book collection directly on the device. To do this you have to "
"turn on the content server."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:382
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:389
msgid ""
"Remember to leave calibre running as the server only runs as long as calibre "
"is running."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:384
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:391
msgid ""
"You have to add the URL http://myhostname:8080 as your calibre library in "
"WordPlayer. 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/wizard/__init__.py:461
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:468
msgid "Moving library..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:477
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:478
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:484
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:485
msgid "Failed to move library"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:532
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:539
msgid "Invalid database"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:533
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:540
msgid ""
"<p>An invalid library already exists at %s, delete it before trying to move "
"the existing library.<br>Error: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:544
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:551
msgid "Could not move library"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:615
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:622
msgid "Select location for books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:629
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:636
msgid ""
"You must choose an empty folder for the calibre library. %s is not empty."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:703
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:710
msgid "welcome wizard"
msgstr ""
@ -11956,12 +11990,18 @@ msgid ""
"disable grouping."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/base.py:131
#: /home/kovid/work/calibre/src/calibre/library/server/__init__.py:48
msgid ""
"Prefix to prepend to all URLs. Useful for reverseproxying to this server "
"from Apache/nginx/etc."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/base.py:149
msgid "Password to access your calibre library. Username is "
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:51
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:399
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:401
msgid "Loading, please wait"
msgstr ""
@ -12006,74 +12046,74 @@ msgstr ""
msgid "Sort by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:307
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:505
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:568
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:308
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:512
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
msgid "Newest"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:308
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:506
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:309
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:513
msgid "All books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:340
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:341
msgid "Browse books by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:345
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:346
msgid "Choose a category to browse by:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:418
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:421
msgid "Browsing by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:419
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:422
msgid "Up"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:535
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:543
msgid "in"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:538
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:546
msgid "Books in"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:588
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:597
msgid "Other formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:595
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:604
msgid "Read %s in the %s format"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:600
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:609
msgid "Get"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:614
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:624
msgid "Permalink"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:615
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:625
msgid "A permanent link to this book"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:626
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:636
msgid "This book has been deleted"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:707
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:720
msgid "in search"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:709
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:722
msgid "Matching books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:28
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:39
msgid ""
"[options]\n"
"\n"
@ -12086,15 +12126,15 @@ msgid ""
"The OPDS interface is advertised via BonJour automatically.\n"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:41
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:52
msgid "Path to the library folder to serve with the content server"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:43
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:54
msgid "Write process PID to the specified file"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:47
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:58
msgid ""
"Specifies a restriction to be used for this invocation. This option "
"overrides any per-library settings specified in the GUI"
@ -12424,93 +12464,93 @@ msgstr ""
msgid "Unknown News Source"
msgstr "Fonte de Novas Descoñecida"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:615
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:614
msgid "The \"%s\" recipe needs a username and password."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:714
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:713
msgid "Download finished"
msgstr "Rematou a descarga"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:716
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:715
msgid "Failed to download the following articles:"
msgstr "Produciuse un fallo na descarga dos seguintes artigos:"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:722
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:721
msgid "Failed to download parts of the following articles:"
msgstr "Produciuse un fallo na descarga de partes do seguintes artigos:"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:724
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:723
msgid " from "
msgstr " desde "
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:726
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:725
msgid "\tFailed links:"
msgstr "\tLigazóns erradas:"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:815
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:814
msgid "Could not fetch article. Run with -vv to see the reason"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:836
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:835
msgid "Fetching feeds..."
msgstr "A obter os fluxos..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:841
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:840
msgid "Got feeds from index page"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:850
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:849
msgid "Trying to download cover..."
msgstr "Tratando de descargar a capa..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:852
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:851
msgid "Generating masthead..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:933
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:932
msgid "Starting download [%d thread(s)]..."
msgstr "A comezar a descarga [%d fío(s)]..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:949
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:948
msgid "Feeds downloaded to %s"
msgstr "Fluxos descargados en %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:958
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:957
msgid "Could not download cover: %s"
msgstr "Non se puido descargar a capa: %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:965
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:964
msgid "Downloading cover from %s"
msgstr "A descargar a capa desde %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1010
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1009
msgid "Masthead image downloaded"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1178
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1177
msgid "Untitled Article"
msgstr "Artigo sen Título"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1249
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1248
msgid "Article downloaded: %s"
msgstr "Artigo descargado: %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1260
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1259
msgid "Article download failed: %s"
msgstr "Erro ao descargar o artigo: %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1277
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1276
msgid "Fetching feed"
msgstr "A obter o fluxo"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1424
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1423
msgid ""
"Failed to log in, check your username and password for the calibre "
"Periodicals service."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1439
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1438
msgid ""
"You do not have permission to download this issue. Either your subscription "
"has expired or you have exceeded the maximum allowed downloads for today."
@ -12779,6 +12819,9 @@ msgstr "Non descargar follas de estilo CSS"
#~ msgid "Force maximum line lenght"
#~ msgstr "Obrigar á lonxitude máxima da liña"
#~ msgid "Send specific format"
#~ msgstr "Enviar un formato específico"
#~ msgid "Fit &cover to view"
#~ msgstr "Axustar a &capa á xanela"
@ -12944,6 +12987,13 @@ msgstr "Non descargar follas de estilo CSS"
#~ "Así, se seleccionou o libro A e despois o Libro B,\n"
#~ "o libro A será o número 1 e o libro B o número 2."
#~ msgid "Cannot use tag editor"
#~ msgstr "Non se pode empregar o editor de etiquetas"
#~ msgid "The tags editor cannot be used if you have modified the tags"
#~ msgstr ""
#~ "O editor de etiquetas non se pode empregar se se modificaron as etiquetas"
#~ msgid ""
#~ "Automatically create the author sort entry based on the current author entry"
#~ msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,14 +6,14 @@ msgid ""
msgstr ""
"Project-Id-Version: calibre 0.4.55\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-10-28 00:31+0000\n"
"PO-Revision-Date: 2010-10-28 13:56+0000\n"
"Last-Translator: Vitaliy Starostin <Unknown>\n"
"POT-Creation-Date: 2010-10-29 19:59+0000\n"
"PO-Revision-Date: 2010-10-29 19:54+0000\n"
"Last-Translator: Kovid Goyal <Unknown>\n"
"Language-Team: American English <kde-i18n-doc@lists.kde.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-10-29 05:24+0000\n"
"X-Launchpad-Export-Date: 2010-10-30 05:03+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"X-Poedit-Country: RUSSIAN FEDERATION\n"
"X-Poedit-Language: Russian\n"
@ -42,8 +42,8 @@ msgstr "Ничего не делает"
#: /home/kovid/work/calibre/src/calibre/ebooks/chm/metadata.py:56
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:407
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/periodical.py:124
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:70
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:72
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:93
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:95
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:336
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:339
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1894
@ -58,7 +58,7 @@ msgstr "Ничего не делает"
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:606
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:36
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:61
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:50
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fetch.py:333
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:36
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:64
@ -149,7 +149,7 @@ msgstr "Ничего не делает"
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2161
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2163
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2295
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:224
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:228
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:139
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:142
#: /home/kovid/work/calibre/src/calibre/library/server/xml.py:78
@ -271,130 +271,130 @@ msgstr "Внести метаданные в файлы %s"
msgid "Set metadata from %s files"
msgstr "Внести метаданные из файлов %s"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:706
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:708
msgid "Look and Feel"
msgstr "Оформление"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:708
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:720
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:731
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:742
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:710
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:722
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:733
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:744
msgid "Interface"
msgstr "Интерфейс"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:712
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:714
msgid "Adjust the look and feel of the calibre interface to suit your tastes"
msgstr "Настройте внешний вид calibre по-своему вкусу"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:718
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:720
msgid "Behavior"
msgstr "Предпочтения"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:724
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:726
msgid "Change the way calibre behaves"
msgstr "Настройка поведения calibre"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:729
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:731
#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:202
msgid "Add your own columns"
msgstr "Добавление колонок"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:735
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:737
msgid "Add/remove your own columns to the calibre book list"
msgstr "Добавление/удаление личных колонок в списке книг"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:740
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:742
msgid "Customize the toolbar"
msgstr "Настройка панели инструментов"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:746
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:748
msgid ""
"Customize the toolbars and context menus, changing which actions are "
"available in each"
msgstr ""
"Настройка панели инструментов и контекстных меню, выбор доступных команд"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:752
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:754
msgid "Input Options"
msgstr "Параметры импорта"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:754
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:765
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:776
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:756
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:767
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:778
msgid "Conversion"
msgstr "Преобразование"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:758
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:760
msgid "Set conversion options specific to each input format"
msgstr "Установка параметров импорта, специфичных для каждого формата"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:763
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:765
msgid "Common Options"
msgstr "Общие настройки"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:769
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:771
msgid "Set conversion options common to all formats"
msgstr "Установка общих для всех форматов параметров конвертации"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:774
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:776
msgid "Output Options"
msgstr "Выходные параметры"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:780
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:782
msgid "Set conversion options specific to each output format"
msgstr "Установка параметров экспорта, специфичных для каждого формата"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:785
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:787
msgid "Adding books"
msgstr "Добавление книг"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:787
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:799
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:811
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:823
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:789
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:801
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:813
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:825
msgid "Import/Export"
msgstr "Импорт/Экспорт"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:791
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:793
msgid "Control how calibre reads metadata from files when adding books"
msgstr "Настройки чтения метаданных из добавляемых в библиотеку книг"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:797
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:799
msgid "Saving books to disk"
msgstr "Сохранить книги на диск"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:803
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:805
msgid ""
"Control how calibre exports files from its database to disk when using Save "
"to disk"
msgstr "Настройки записи книг из базы данных на диск"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:809
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:811
msgid "Sending books to devices"
msgstr "Отправить книги на устройство"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:815
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:817
msgid "Control how calibre transfers files to your ebook reader"
msgstr "Контроль передачи книжек на устройство"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:821
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:823
msgid "Metadata plugboards"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:827
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:829
msgid "Change metadata fields before saving/sending"
msgstr "Изменять поля метаданных до сохранения/отправки"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:832
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:834
msgid "Sharing books by email"
msgstr "Отправить книжку по e-mail"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:834
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:846
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:836
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:848
msgid "Sharing"
msgstr "Общий доступ к файлам"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:838
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:840
msgid ""
"Setup sharing of books via email. Can be used for automatic sending of "
"downloaded news to your devices"
@ -402,11 +402,11 @@ msgstr ""
"Настройка рассылки книг по e-mail. Может быть использована для "
"автоматической доставки загруженных новостных лент на устройство"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:844
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:846
msgid "Sharing over the net"
msgstr "Сетевой доступ"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:850
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:852
msgid ""
"Setup the calibre Content Server which will give you access to your calibre "
"library from anywhere, on any device, over the internet"
@ -414,33 +414,33 @@ msgstr ""
"Настройка http-сервера calibre, предоставляющего доступ к библиотеке с "
"любого устройства, подключенного к сети интернет"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:857
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:859
msgid "Plugins"
msgstr "Модули"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:859
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:871
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:882
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:861
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:873
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:884
msgid "Advanced"
msgstr "Расширенный"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:863
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:865
msgid "Add/remove/customize various bits of calibre functionality"
msgstr "Добавить/удалить/изменить различные части функциональности calibre"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:869
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:871
msgid "Tweaks"
msgstr "Расширенные настройки"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:875
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:877
msgid "Fine tune how calibre behaves in various contexts"
msgstr "Тонкая настройка поведения calibre при различных контекстах"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:880
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:882
msgid "Miscellaneous"
msgstr "Разное"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:886
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:888
msgid "Miscellaneous advanced configuration"
msgstr "Различная продвинутая конфигурация"
@ -602,7 +602,7 @@ msgstr ""
msgid "This profile is intended for the Amazon Kindle DX."
msgstr "Профиль для Amazon Kindle DX."
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:663
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:664
msgid "This profile is intended for the Sanda Bambook."
msgstr "Этот профиль предназначен для Sanda Bambook."
@ -1006,11 +1006,11 @@ msgstr "Соединиться с Kogan"
msgid "Communicate with the Pandigital Novel"
msgstr "Соединиться с Pandigital Novel"
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:124
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:130
msgid "Communicate with the VelocityMicro"
msgstr "Подключиться к VelocityMicro"
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:142
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:148
msgid "Communicate with the GM2000"
msgstr "Подключиться к GM2000"
@ -1071,11 +1071,15 @@ msgstr "Соединиться с Teclast K3/K5"
msgid "Communicate with the Newsmy reader."
msgstr "Соединиться с Newsmy"
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:48
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:47
msgid "Communicate with the Pico reader."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:57
msgid "Communicate with the iPapyrus reader."
msgstr "Соединиться с iPapyrus"
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:59
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:68
msgid "Communicate with the Sovos reader."
msgstr "Подключиться к Sovos"
@ -2003,7 +2007,7 @@ msgstr ""
"полей по краям."
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:170
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:205
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:203
msgid "Start"
msgstr "Начать"
@ -2396,7 +2400,7 @@ msgstr "Да"
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:380
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:930
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:303
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:570
msgid "Title"
msgstr "Заголовок"
@ -2440,7 +2444,7 @@ msgstr "Комментарии"
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:320
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1134
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:160
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:608
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:618
msgid "Tags"
msgstr "Теги"
@ -2863,7 +2867,7 @@ msgstr "Варианты создания HTML содержания"
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:153
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:71
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:606
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:616
msgid "Rating"
msgstr "Оценка"
@ -3225,13 +3229,13 @@ msgstr ""
"Преобразуйте его в HTML и попробуйте еще раз\n"
"%s"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:32
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:25
#: /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 "Укажите кодировку для создаваемого документа. По умолчанию utf-8."
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:39
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:29
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:38
msgid ""
"The maximum number of characters per line. This splits on the first space "
@ -3246,17 +3250,34 @@ msgstr ""
"строке превысит указанное. Минимально возможное значение - 25 символов. "
"Укажите 0 для запрета переноса строк."
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:124
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:36
msgid ""
"Specify whether or not to insert an empty line between two paragraphs."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:40
msgid ""
"Specify whether or not to insert two space characters to indent the first "
"line of each paragraph."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:44
msgid ""
"Specify whether or not to hide the chapter title for each chapter. Useful "
"for image-only output (eg. comics)."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:122
msgid "Start Page"
msgstr "Начальная страница"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:132
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:134
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:136
msgid "Cover Pages"
msgstr "Обложки"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:149
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:152
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:147
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:150
msgid " (Preface)"
msgstr " (Предисловие)"
@ -3424,7 +3445,7 @@ msgid "Disable UI animations"
msgstr "Отключить анимацию пользовальского интерфейса"
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:183
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:487
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:494
msgid "Copied"
msgstr "Скопирована"
@ -3436,7 +3457,7 @@ msgstr "Копировать"
msgid "Copy to Clipboard"
msgstr "Копирования в буфер обмена"
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:466
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:467
msgid "Choose Files"
msgstr "Выберите файлы"
@ -4316,7 +4337,7 @@ msgid "Click the show details button to see which ones."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/actions/show_book_details.py:16
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:613
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:623
msgid "Show book details"
msgstr "Показать подробности"
@ -4771,7 +4792,7 @@ msgstr "вывод"
#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_input_ui.py:43
#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:47
#: /home/kovid/work/calibre/src/calibre/gui2/convert/rb_output_ui.py:33
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:28
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:39
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:80
#: /home/kovid/work/calibre/src/calibre/gui2/convert/toc_ui.py:67
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:51
@ -5651,6 +5672,18 @@ msgstr "Использовать сохраненные настройки пр
msgid "SNB Output"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:40
msgid "Hide chapter name"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:41
msgid "Insert space before the first line for each paragraph"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:42
msgid "Insert empty line between paragraphs"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:17
msgid ""
"Structure\n"
@ -6040,8 +6073,8 @@ msgid "Send and delete from library"
msgstr "Отправить и удалить из библиотеки"
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:492
msgid "Send specific format"
msgstr "Отправить определенный формат"
msgid "Send specific format to"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:528
msgid "Eject device"
@ -6368,7 +6401,7 @@ msgid "No location selected"
msgstr "Путь не выбран"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_library.py:84
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:628
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:635
msgid "Bad location"
msgstr "Неправильный путь"
@ -6447,7 +6480,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:932
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:31
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:294
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:568
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
msgid "Date"
msgstr "Дата"
@ -7043,72 +7076,74 @@ msgid "This ISBN number is invalid"
msgstr "Этот номер ISBN недопустим"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:611
msgid "Cannot use tag editor"
msgstr "Не возможно использовать редактор тэгов"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:612
msgid "The tags editor cannot be used if you have modified the tags"
msgid "Tags changed"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:632
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:612
msgid ""
"You have changed the tags. In order to use the tags editor, you must either "
"discard or apply these changes"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:639
msgid "Downloading cover..."
msgstr "Загрузка обложки..."
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:644
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:649
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:655
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:660
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:651
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:656
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:662
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:667
msgid "Cannot fetch cover"
msgstr "Не могу получить обложку"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:645
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:656
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:661
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:652
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:663
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:668
msgid "<b>Could not fetch cover.</b><br/>"
msgstr "<b>Не могу получить обложку.</b><br/>"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:646
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:653
msgid "The download timed out."
msgstr "Таймаут загрузки"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:650
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:657
msgid "Could not find cover for this book. Try specifying the ISBN first."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:662
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:669
msgid ""
"For the error message from each cover source, click Show details below."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:669
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:676
msgid "Bad cover"
msgstr "Плохая обложка"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:670
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:677
msgid "The cover is not a valid picture"
msgstr "Обложка с неправильной картинкой"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:703
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:710
msgid "There were errors"
msgstr "Произошли ошибки"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:704
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:711
msgid "There were errors downloading social metadata"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:735
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:742
msgid "Cannot fetch metadata"
msgstr "Не могу доставить метаданные"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:736
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:743
msgid "You must specify at least one of ISBN, Title, Authors or Publisher"
msgstr "Нужно указаить ISBN, название, автора или издателя"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:823
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:833
msgid "Permission denied"
msgstr "Доступ запрещён"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:824
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:834
msgid "Could not open %s. Is it being used by another program?"
msgstr ""
@ -8412,7 +8447,7 @@ msgid "Do not check for updates"
msgstr "Не проверять наличие обновлений"
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:58
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:636
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:643
msgid "Calibre Library"
msgstr ""
@ -8572,7 +8607,7 @@ msgid "Successfully downloaded metadata for %d out of %d books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/metadata.py:287
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:612
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:622
msgid "Details"
msgstr ""
@ -10467,59 +10502,59 @@ msgstr "Скрыть"
msgid "Toggle"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:378
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:385
msgid ""
"If you use the WordPlayer e-book app on your Android phone, you can access "
"your calibre book collection directly on the device. To do this you have to "
"turn on the content server."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:382
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:389
msgid ""
"Remember to leave calibre running as the server only runs as long as calibre "
"is running."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:384
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:391
msgid ""
"You have to add the URL http://myhostname:8080 as your calibre library in "
"WordPlayer. 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/wizard/__init__.py:461
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:468
msgid "Moving library..."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:477
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:478
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:484
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:485
msgid "Failed to move library"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:532
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:539
msgid "Invalid database"
msgstr "Неверная база данных"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:533
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:540
msgid ""
"<p>An invalid library already exists at %s, delete it before trying to move "
"the existing library.<br>Error: %s"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:544
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:551
msgid "Could not move library"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:615
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:622
msgid "Select location for books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:629
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:636
msgid ""
"You must choose an empty folder for the calibre library. %s is not empty."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:703
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:710
msgid "welcome wizard"
msgstr ""
@ -11774,12 +11809,18 @@ msgid ""
"disable grouping."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/base.py:131
#: /home/kovid/work/calibre/src/calibre/library/server/__init__.py:48
msgid ""
"Prefix to prepend to all URLs. Useful for reverseproxying to this server "
"from Apache/nginx/etc."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/base.py:149
msgid "Password to access your calibre library. Username is "
msgstr "Пароль для доступа в библиотеку. Имя пользователя "
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:51
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:399
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:401
msgid "Loading, please wait"
msgstr ""
@ -11824,74 +11865,74 @@ msgstr ""
msgid "Sort by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:307
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:505
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:568
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:308
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:512
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
msgid "Newest"
msgstr "Самый новый"
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:308
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:506
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:309
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:513
msgid "All books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:340
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:341
msgid "Browse books by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:345
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:346
msgid "Choose a category to browse by:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:418
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:421
msgid "Browsing by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:419
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:422
msgid "Up"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:535
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:543
msgid "in"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:538
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:546
msgid "Books in"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:588
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:597
msgid "Other formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:595
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:604
msgid "Read %s in the %s format"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:600
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:609
msgid "Get"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:614
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:624
msgid "Permalink"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:615
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:625
msgid "A permanent link to this book"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:626
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:636
msgid "This book has been deleted"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:707
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:720
msgid "in search"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:709
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:722
msgid "Matching books"
msgstr "Соответствие книг"
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:28
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:39
msgid ""
"[options]\n"
"\n"
@ -11904,15 +11945,15 @@ msgid ""
"The OPDS interface is advertised via BonJour automatically.\n"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:41
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:52
msgid "Path to the library folder to serve with the content server"
msgstr "Путь к папке библиотеки, для обслуживания сервером"
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:43
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:54
msgid "Write process PID to the specified file"
msgstr "Записать PID процесса в указанный файл"
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:47
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:58
msgid ""
"Specifies a restriction to be used for this invocation. This option "
"overrides any per-library settings specified in the GUI"
@ -12249,87 +12290,87 @@ msgstr "Не загружать последнюю версию встроенн
msgid "Unknown News Source"
msgstr "Неизвестный новостной ресурс"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:615
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:614
msgid "The \"%s\" recipe needs a username and password."
msgstr "Рецепт \"%s\" требует имя пользожателя и пароль"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:714
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:713
msgid "Download finished"
msgstr "Закачка завершена"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:716
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:715
msgid "Failed to download the following articles:"
msgstr "Неудачная загрузка следующих статей:"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:722
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:721
msgid "Failed to download parts of the following articles:"
msgstr "Неудачная загрузка частей следующих статей:"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:724
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:723
msgid " from "
msgstr " из "
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:726
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:725
msgid "\tFailed links:"
msgstr "\tБитые ссылки:"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:815
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:814
msgid "Could not fetch article. Run with -vv to see the reason"
msgstr "Не может быть вызвана статья. Запустите с -w чтобы увидеть причину."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:836
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:835
msgid "Fetching feeds..."
msgstr "Вызываются заготовки..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:841
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:840
msgid "Got feeds from index page"
msgstr "Получить заготовки для оглавления"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:850
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:849
msgid "Trying to download cover..."
msgstr "Попытка скачать обложку..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:852
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:851
msgid "Generating masthead..."
msgstr "Создаем титульные данные..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:933
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:932
msgid "Starting download [%d thread(s)]..."
msgstr "Начало загрузки [%d поток(и)]..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:949
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:948
msgid "Feeds downloaded to %s"
msgstr "Заготовки закачиватся в %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:958
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:957
msgid "Could not download cover: %s"
msgstr "Невозможно скачать обложку: %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:965
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:964
msgid "Downloading cover from %s"
msgstr "Скачивается обложка из %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1010
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1009
msgid "Masthead image downloaded"
msgstr "Выходные данные изображения загрузили"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1178
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1177
msgid "Untitled Article"
msgstr "Статья без названия"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1249
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1248
msgid "Article downloaded: %s"
msgstr "Статья скачена: %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1260
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1259
msgid "Article download failed: %s"
msgstr "Ошибка загрузки статьи: %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1277
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1276
msgid "Fetching feed"
msgstr "Доставляется материал"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1424
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1423
msgid ""
"Failed to log in, check your username and password for the calibre "
"Periodicals service."
@ -12337,7 +12378,7 @@ msgstr ""
"Не удалось выполнить вход, проверьте свое имя пользователя и пароль для "
"службы Периодических изданий Calibre."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1439
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1438
msgid ""
"You do not have permission to download this issue. Either your subscription "
"has expired or you have exceeded the maximum allowed downloads for today."
@ -14711,6 +14752,9 @@ msgstr "Не скачивать файлы стилей CSS."
#~ msgid "&Saving books"
#~ msgstr "&Сохранение книг"
#~ msgid "Cannot use tag editor"
#~ msgstr "Не возможно использовать редактор тэгов"
#~ msgid ""
#~ "Automatically create the author sort entry based on the current author entry"
#~ msgstr ""
@ -14767,6 +14811,9 @@ msgstr "Не скачивать файлы стилей CSS."
#~ "навсегда <b>удалены</b> с вашего компьютера.<br><br> Вы <b>уверены</b> что "
#~ "хотите продолжить?"
#~ msgid "Send specific format"
#~ msgstr "Отправить определенный формат"
#~ msgid ""
#~ "The value <b>%d</b> you have chosen for the content server port is a system "
#~ "port. Your operating system <b>may</b> not allow the server to run on this "

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

@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: calibre\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2010-10-28 00:31+0000\n"
"PO-Revision-Date: 2010-10-29 01:45+0000\n"
"Last-Translator: Li Fanxi <Unknown>\n"
"POT-Creation-Date: 2010-10-29 19:59+0000\n"
"PO-Revision-Date: 2010-10-29 19:52+0000\n"
"Last-Translator: Kovid Goyal <Unknown>\n"
"Language-Team: Simplified Chinese <wanglihao@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-10-29 05:26+0000\n"
"X-Launchpad-Export-Date: 2010-10-30 05:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"X-Poedit-Country: CHINA\n"
"X-Poedit-Language: Chinese\n"
@ -40,8 +40,8 @@ msgstr "不做任何处理"
#: /home/kovid/work/calibre/src/calibre/ebooks/chm/metadata.py:56
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:407
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/periodical.py:124
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:70
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:72
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:93
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:95
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:336
#: /home/kovid/work/calibre/src/calibre/ebooks/html/input.py:339
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1894
@ -56,7 +56,7 @@ msgstr "不做任何处理"
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:606
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:36
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:61
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fb2.py:50
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fetch.py:333
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:36
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/meta.py:64
@ -147,7 +147,7 @@ msgstr "不做任何处理"
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2161
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2163
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2295
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:224
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:228
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:139
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:142
#: /home/kovid/work/calibre/src/calibre/library/server/xml.py:78
@ -262,171 +262,171 @@ msgstr "设置 %s 文件的元数据"
msgid "Set metadata from %s files"
msgstr "从 %s 文件设置元数据"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:706
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:708
msgid "Look and Feel"
msgstr "外表和感受"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:708
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:720
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:731
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:742
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:710
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:722
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:733
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:744
msgid "Interface"
msgstr "界面"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:712
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:714
msgid "Adjust the look and feel of the calibre interface to suit your tastes"
msgstr "调整成你喜欢的外观"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:718
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:720
msgid "Behavior"
msgstr "操作方式"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:724
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:726
msgid "Change the way calibre behaves"
msgstr "改变calibre的操作方式"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:729
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:731
#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:202
msgid "Add your own columns"
msgstr "增加栏目"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:735
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:737
msgid "Add/remove your own columns to the calibre book list"
msgstr "向calibre书籍列表中增加或删除你自定义的栏目"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:740
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:742
msgid "Customize the toolbar"
msgstr "自定义工具栏"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:746
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:748
msgid ""
"Customize the toolbars and context menus, changing which actions are "
"available in each"
msgstr "自定义工具栏和上下文菜单,设置它们所可以提供的功能"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:752
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:754
msgid "Input Options"
msgstr "输入选项"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:754
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:765
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:776
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:756
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:767
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:778
msgid "Conversion"
msgstr "转换"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:758
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:760
msgid "Set conversion options specific to each input format"
msgstr "设置针对特定输入格式的转换选项"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:763
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:765
msgid "Common Options"
msgstr "常规选项"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:769
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:771
msgid "Set conversion options common to all formats"
msgstr "设置所有输入格式共有的转换选项"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:774
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:776
msgid "Output Options"
msgstr "输出选项"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:780
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:782
msgid "Set conversion options specific to each output format"
msgstr "设置针对特定输出格式的转换选项"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:785
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:787
msgid "Adding books"
msgstr "增加图书"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:787
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:799
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:811
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:823
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:789
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:801
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:813
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:825
msgid "Import/Export"
msgstr "导入/导出"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:791
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:793
msgid "Control how calibre reads metadata from files when adding books"
msgstr "控制向calibre添加书籍时读取元数据的方式"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:797
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:799
msgid "Saving books to disk"
msgstr "保存图书到磁盘"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:803
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:805
msgid ""
"Control how calibre exports files from its database to disk when using Save "
"to disk"
msgstr "控制使用“保存到磁盘”功能时calibre从数据库导出文件的方式"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:809
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:811
msgid "Sending books to devices"
msgstr "发送图书到设备"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:815
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:817
msgid "Control how calibre transfers files to your ebook reader"
msgstr "控制calibre将文件传输到电子阅读器的方式"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:821
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:823
msgid "Metadata plugboards"
msgstr "元数据控制板"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:827
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:829
msgid "Change metadata fields before saving/sending"
msgstr "保存或发送前更改元数据域"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:832
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:834
msgid "Sharing books by email"
msgstr "通过Email分享图书"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:834
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:846
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:836
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:848
msgid "Sharing"
msgstr "分享"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:838
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:840
msgid ""
"Setup sharing of books via email. Can be used for automatic sending of "
"downloaded news to your devices"
msgstr "设置通过电子邮件分享图书。可用于在向阅读器下载新内容时自动发送通知。"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:844
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:846
msgid "Sharing over the net"
msgstr "通过网络分享"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:850
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:852
msgid ""
"Setup the calibre Content Server which will give you access to your calibre "
"library from anywhere, on any device, over the internet"
msgstr "设置calibre内容服务器以便通过网络在任何设备和地点访问到calibre书库。"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:857
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:859
msgid "Plugins"
msgstr "插件"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:859
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:871
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:882
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:861
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:873
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:884
msgid "Advanced"
msgstr "高级"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:863
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:865
msgid "Add/remove/customize various bits of calibre functionality"
msgstr "添回/删除/自定义各种calibre功能"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:869
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:871
msgid "Tweaks"
msgstr "优化调整"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:875
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:877
msgid "Fine tune how calibre behaves in various contexts"
msgstr "微调calibre在各种情况下的行为"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:880
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:882
msgid "Miscellaneous"
msgstr "杂项"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:886
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:888
msgid "Miscellaneous advanced configuration"
msgstr "其它高级选项"
@ -574,7 +574,7 @@ msgstr "该配置文件适用 SONY PRS 产品线,如 500/505/700 型号等,
msgid "This profile is intended for the Amazon Kindle DX."
msgstr "该配置文件适用 Amazon Kindle DX。"
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:663
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:664
msgid "This profile is intended for the Sanda Bambook."
msgstr "该配置文件适用于盛大锦书(Bambook)。"
@ -970,11 +970,11 @@ msgstr "与 Kogan 通信"
msgid "Communicate with the Pandigital Novel"
msgstr "与Pandigital Novel通信"
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:124
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:130
msgid "Communicate with the VelocityMicro"
msgstr "与VelocityMicro进行通讯"
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:142
#: /home/kovid/work/calibre/src/calibre/devices/misc.py:148
msgid "Communicate with the GM2000"
msgstr "与 GM2000 通信"
@ -1033,11 +1033,15 @@ msgstr "与 Teclast K3/K5 阅读器通信。"
msgid "Communicate with the Newsmy reader."
msgstr "与Newsmy阅读器通信。"
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:48
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:47
msgid "Communicate with the Pico reader."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:57
msgid "Communicate with the iPapyrus reader."
msgstr "与iPapyrus阅读器通信。"
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:59
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:68
msgid "Communicate with the Sovos reader."
msgstr "与 Sovos 阅读器通信。"
@ -1827,7 +1831,7 @@ msgstr ""
"是图像本身不会被扭曲变形。不选该选项的话,图像可能会产生轻微的扭曲变形,但优点是不会出现空白边界。"
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/output.py:170
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:205
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:203
msgid "Start"
msgstr "开始"
@ -2193,7 +2197,7 @@ msgstr "是"
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:380
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:930
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:303
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:570
msgid "Title"
msgstr "标题"
@ -2237,7 +2241,7 @@ msgstr "注释"
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:320
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1134
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:160
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:608
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:618
msgid "Tags"
msgstr "标签"
@ -2642,7 +2646,7 @@ msgstr "HTML 目录生成选项。"
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:153
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:71
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:606
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:616
msgid "Rating"
msgstr "评分"
@ -2981,13 +2985,13 @@ msgstr ""
"RTF 文件有 calibre 不支持的特性。先转换到 HTML 再试。\n"
"%s"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:32
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:25
#: /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 "指定输出文档的字符集编码。默认为 utf-8。"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:39
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:29
#: /home/kovid/work/calibre/src/calibre/ebooks/txt/output.py:38
msgid ""
"The maximum number of characters per line. This splits on the first space "
@ -2997,17 +3001,34 @@ msgid ""
msgstr ""
"每行最多容纳字符数。程序将会自动寻找行中最后一个空格换行。如果行内无空格则行宽将会超过超越此值在下个空格处换行。此外最小值为25。使用 0 禁用分行"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:124
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:36
msgid ""
"Specify whether or not to insert an empty line between two paragraphs."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:40
msgid ""
"Specify whether or not to insert two space characters to indent the first "
"line of each paragraph."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:44
msgid ""
"Specify whether or not to hide the chapter title for each chapter. Useful "
"for image-only output (eg. comics)."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:122
msgid "Start Page"
msgstr "起始页"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:132
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:134
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:136
msgid "Cover Pages"
msgstr "封面"
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:149
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:152
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:147
#: /home/kovid/work/calibre/src/calibre/ebooks/snb/output.py:150
msgid " (Preface)"
msgstr " 引言"
@ -3166,7 +3187,7 @@ msgid "Disable UI animations"
msgstr "禁用界面动画"
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:183
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:487
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:494
msgid "Copied"
msgstr "已复制"
@ -3178,7 +3199,7 @@ msgstr "复制"
msgid "Copy to Clipboard"
msgstr "复制到剪贴板"
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:466
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:467
msgid "Choose Files"
msgstr "选择文件"
@ -4047,7 +4068,7 @@ msgid "Click the show details button to see which ones."
msgstr "点击显示详情按钮查看具体哪些。"
#: /home/kovid/work/calibre/src/calibre/gui2/actions/show_book_details.py:16
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:613
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:623
msgid "Show book details"
msgstr "显示书籍详情"
@ -4482,7 +4503,7 @@ msgstr "输出"
#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_input_ui.py:43
#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:47
#: /home/kovid/work/calibre/src/calibre/gui2/convert/rb_output_ui.py:33
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:28
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:39
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection_ui.py:80
#: /home/kovid/work/calibre/src/calibre/gui2/convert/toc_ui.py:67
#: /home/kovid/work/calibre/src/calibre/gui2/convert/txt_input_ui.py:51
@ -5337,6 +5358,18 @@ msgstr "为指定书籍使用已存转换设定(&S)"
msgid "SNB Output"
msgstr "SNB 输出"
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:40
msgid "Hide chapter name"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:41
msgid "Insert space before the first line for each paragraph"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/snb_output_ui.py:42
msgid "Insert empty line between paragraphs"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/structure_detection.py:17
msgid ""
"Structure\n"
@ -5727,8 +5760,8 @@ msgid "Send and delete from library"
msgstr "发送并从书库中删除"
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:492
msgid "Send specific format"
msgstr "发送指定格式"
msgid "Send specific format to"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:528
msgid "Eject device"
@ -6049,7 +6082,7 @@ msgid "No location selected"
msgstr "没有选择位置"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_library.py:84
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:628
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:635
msgid "Bad location"
msgstr "错误的位置"
@ -6128,7 +6161,7 @@ msgstr "位置"
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:932
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:31
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:294
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:568
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
msgid "Date"
msgstr "日期"
@ -6722,72 +6755,74 @@ msgid "This ISBN number is invalid"
msgstr "ISBN 编号无效"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:611
msgid "Cannot use tag editor"
msgstr "无法使用标签编辑器"
msgid "Tags changed"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:612
msgid "The tags editor cannot be used if you have modified the tags"
msgstr "如果你已经修改了标签,那么标签编辑器就无法使用"
msgid ""
"You have changed the tags. In order to use the tags editor, you must either "
"discard or apply these changes"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:632
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:639
msgid "Downloading cover..."
msgstr "正在下载封面..."
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:644
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:649
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:655
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:660
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:651
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:656
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:662
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:667
msgid "Cannot fetch cover"
msgstr "无法抓取封面"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:645
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:656
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:661
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:652
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:663
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:668
msgid "<b>Could not fetch cover.</b><br/>"
msgstr "<b>无法抓取封面。</b><br/>"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:646
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:653
msgid "The download timed out."
msgstr "下载超时。"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:650
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:657
msgid "Could not find cover for this book. Try specifying the ISBN first."
msgstr "无法找到书籍对应的封面。请尝试首先指定 ISBN。"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:662
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:669
msgid ""
"For the error message from each cover source, click Show details below."
msgstr "每个封面源文件中的错误信息,点击在下边查看详情"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:669
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:676
msgid "Bad cover"
msgstr "非法封面"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:670
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:677
msgid "The cover is not a valid picture"
msgstr "封面不是有效图片"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:703
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:710
msgid "There were errors"
msgstr "出错"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:704
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:711
msgid "There were errors downloading social metadata"
msgstr "下载社会性元数据出错"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:735
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:742
msgid "Cannot fetch metadata"
msgstr "无法抓取元数据"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:736
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:743
msgid "You must specify at least one of ISBN, Title, Authors or Publisher"
msgstr "您必须设定 ISBN标题作者或出版商中的至少一个条件"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:823
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:833
msgid "Permission denied"
msgstr "权限拒绝"
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:824
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:834
msgid "Could not open %s. Is it being used by another program?"
msgstr "无法打开 %s。它是否被其它程序占用。"
@ -8092,7 +8127,7 @@ msgid "Do not check for updates"
msgstr "不检测更新"
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:58
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:636
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:643
msgid "Calibre Library"
msgstr "Calibre 书库"
@ -8249,7 +8284,7 @@ msgid "Successfully downloaded metadata for %d out of %d books"
msgstr "成功下载%d本书的元数据(共%d本)"
#: /home/kovid/work/calibre/src/calibre/gui2/metadata.py:287
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:612
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:622
msgid "Details"
msgstr ""
@ -10101,7 +10136,7 @@ msgstr "隐藏"
msgid "Toggle"
msgstr "触发器"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:378
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:385
msgid ""
"If you use the WordPlayer e-book app on your Android phone, you can access "
"your calibre book collection directly on the device. To do this you have to "
@ -10109,13 +10144,13 @@ msgid ""
msgstr ""
"若您使用 Android 手机上的 WordPlayer 电子书程序,您可以用设备直接访问您的 Calibre 书库。这需要您打开内容服务器。"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:382
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:389
msgid ""
"Remember to leave calibre running as the server only runs as long as calibre "
"is running."
msgstr "请记住保持 Calibre 运行,如此它才能一直提供服务。"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:384
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:391
msgid ""
"You have to add the URL http://myhostname:8080 as your calibre library in "
"WordPlayer. Here myhostname should be the fully qualified hostname or the IP "
@ -10124,39 +10159,39 @@ msgstr ""
"您需要将 URL http://myhostname:8080 添加到您 WordPlayer 中的 Calibre 书库。此处 myhostname "
"应为有效主机名或者您运行 Calibre 电脑的 IP 地址。"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:461
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:468
msgid "Moving library..."
msgstr "正在移动书库..."
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:477
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:478
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:484
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:485
msgid "Failed to move library"
msgstr "移动书库错误"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:532
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:539
msgid "Invalid database"
msgstr "无效数据库"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:533
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:540
msgid ""
"<p>An invalid library already exists at %s, delete it before trying to move "
"the existing library.<br>Error: %s"
msgstr "<p>在 %s 已有无效书库,在试图移动现有书库前删除它。<br>错误:%s"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:544
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:551
msgid "Could not move library"
msgstr "无法移动书库"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:615
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:622
msgid "Select location for books"
msgstr "选择书籍位置"
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:629
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:636
msgid ""
"You must choose an empty folder for the calibre library. %s is not empty."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:703
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:710
msgid "welcome wizard"
msgstr "欢迎向导"
@ -11397,12 +11432,18 @@ msgid ""
"disable grouping."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/base.py:131
#: /home/kovid/work/calibre/src/calibre/library/server/__init__.py:48
msgid ""
"Prefix to prepend to all URLs. Useful for reverseproxying to this server "
"from Apache/nginx/etc."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/base.py:149
msgid "Password to access your calibre library. Username is "
msgstr "密码以访问您的 Calibre 书库。用户名为 "
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:51
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:399
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:401
msgid "Loading, please wait"
msgstr ""
@ -11447,74 +11488,74 @@ msgstr ""
msgid "Sort by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:307
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:505
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:568
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:308
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:512
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
msgid "Newest"
msgstr "最新"
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:308
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:506
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:309
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:513
msgid "All books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:340
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:341
msgid "Browse books by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:345
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:346
msgid "Choose a category to browse by:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:418
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:421
msgid "Browsing by"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:419
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:422
msgid "Up"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:535
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:543
msgid "in"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:538
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:546
msgid "Books in"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:588
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:597
msgid "Other formats"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:595
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:604
msgid "Read %s in the %s format"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:600
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:609
msgid "Get"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:614
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:624
msgid "Permalink"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:615
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:625
msgid "A permanent link to this book"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:626
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:636
msgid "This book has been deleted"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:707
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:720
msgid "in search"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:709
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:722
msgid "Matching books"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:28
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:39
msgid ""
"[options]\n"
"\n"
@ -11527,15 +11568,15 @@ msgid ""
"The OPDS interface is advertised via BonJour automatically.\n"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:41
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:52
msgid "Path to the library folder to serve with the content server"
msgstr "内容服务器上书库目录路径"
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:43
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:54
msgid "Write process PID to the specified file"
msgstr "指定文件写进程 PID"
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:47
#: /home/kovid/work/calibre/src/calibre/library/server/main.py:58
msgid ""
"Specifies a restriction to be used for this invocation. This option "
"overrides any per-library settings specified in the GUI"
@ -11865,93 +11906,93 @@ msgstr "不从 calire 服务器下载最新订阅清单"
msgid "Unknown News Source"
msgstr "未知新闻来源"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:615
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:614
msgid "The \"%s\" recipe needs a username and password."
msgstr "订阅清单“%s”需要用户名与密码。"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:714
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:713
msgid "Download finished"
msgstr "下载完成"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:716
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:715
msgid "Failed to download the following articles:"
msgstr "下载下列文章失败:"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:722
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:721
msgid "Failed to download parts of the following articles:"
msgstr "下载下列文章的某些部分失败:"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:724
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:723
msgid " from "
msgstr " 自 "
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:726
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:725
msgid "\tFailed links:"
msgstr "\t失败链接"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:815
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:814
msgid "Could not fetch article. Run with -vv to see the reason"
msgstr "无法抓取文章。使用 -vv 选项运行察看原因。"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:836
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:835
msgid "Fetching feeds..."
msgstr "正在抓取源..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:841
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:840
msgid "Got feeds from index page"
msgstr "从索引页面获取了源"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:850
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:849
msgid "Trying to download cover..."
msgstr "正在尝试下载封面..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:852
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:851
msgid "Generating masthead..."
msgstr "正在生成刊头..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:933
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:932
msgid "Starting download [%d thread(s)]..."
msgstr "开始下载 [ %d 线程数]..."
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:949
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:948
msgid "Feeds downloaded to %s"
msgstr "新闻源下载到 %s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:958
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:957
msgid "Could not download cover: %s"
msgstr "无法下载封面:%s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:965
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:964
msgid "Downloading cover from %s"
msgstr "正在从 %s 下载封面"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1010
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1009
msgid "Masthead image downloaded"
msgstr "已下载刊头图像"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1178
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1177
msgid "Untitled Article"
msgstr "无标题文章"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1249
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1248
msgid "Article downloaded: %s"
msgstr "已下载文章:%s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1260
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1259
msgid "Article download failed: %s"
msgstr "下载失败文章:%s"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1277
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1276
msgid "Fetching feed"
msgstr "抓取源"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1424
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1423
msgid ""
"Failed to log in, check your username and password for the calibre "
"Periodicals service."
msgstr "登录失败,检查您 calibre 期刊服务的用户名密码。"
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1439
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1438
msgid ""
"You do not have permission to download this issue. Either your subscription "
"has expired or you have exceeded the maximum allowed downloads for today."
@ -12468,6 +12509,9 @@ msgstr "不下载 CSS 样式表。"
#~ msgid " "
#~ msgstr " "
#~ msgid "Send specific format"
#~ msgstr "发送指定格式"
#~ msgid "Library backup status..."
#~ msgstr "书库备份状态..."
@ -12479,3 +12523,9 @@ msgstr "不下载 CSS 样式表。"
#~ msgid "Run the check"
#~ msgstr "运行检查"
#~ msgid "Cannot use tag editor"
#~ msgstr "无法使用标签编辑器"
#~ msgid "The tags editor cannot be used if you have modified the tags"
#~ msgstr "如果你已经修改了标签,那么标签编辑器就无法使用"

View File

@ -0,0 +1,98 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import copy
from cookielib import CookieJar
from mechanize import Browser as B
class Browser(B):
'A cloneable mechanize browser'
def __init__(self):
self._clone_actions = {}
B.__init__(self)
self.set_cookiejar(CookieJar())
def set_handle_refresh(self, *args, **kwargs):
B.set_handle_refresh(self, *args, **kwargs)
self._clone_actions['set_handle_refresh'] = ('set_handle_refresh',
args, kwargs)
def set_cookiejar(self, *args, **kwargs):
B.set_cookiejar(self, *args, **kwargs)
self._clone_actions['set_cookiejar'] = ('set_cookiejar', args, kwargs)
def set_handle_redirect(self, *args, **kwargs):
B.set_handle_redirect(self, *args, **kwargs)
self._clone_actions['set_handle_redirect'] = ('set_handle_redirect',
args, kwargs)
def set_handle_equiv(self, *args, **kwargs):
B.set_handle_equiv(self, *args, **kwargs)
self._clone_actions['set_handle_equiv'] = ('set_handle_equiv',
args, kwargs)
def set_handle_gzip(self, *args, **kwargs):
B.set_handle_gzip(self, *args, **kwargs)
self._clone_actions['set_handle_gzip'] = ('set_handle_gzip',
args, kwargs)
def set_debug_redirect(self, *args, **kwargs):
B.set_debug_redirect(self, *args, **kwargs)
self._clone_actions['set_debug_redirect'] = ('set_debug_redirect',
args, kwargs)
def set_debug_responses(self, *args, **kwargs):
B.set_debug_responses(self, *args, **kwargs)
self._clone_actions['set_debug_responses'] = ('set_debug_responses',
args, kwargs)
def set_debug_http(self, *args, **kwargs):
B.set_debug_http(self, *args, **kwargs)
self._clone_actions['set_debug_http'] = ('set_debug_http',
args, kwargs)
def set_handle_robots(self, *args, **kwargs):
B.set_handle_robots(self, *args, **kwargs)
self._clone_actions['set_handle_robots'] = ('set_handle_robots',
args, kwargs)
def set_proxies(self, *args, **kwargs):
B.set_proxies(self, *args, **kwargs)
self._clone_actions['set_proxies'] = ('set_proxies', args, kwargs)
def add_password(self, *args, **kwargs):
B.add_password(self, *args, **kwargs)
self._clone_actions['add_password'] = ('add_password', args, kwargs)
def add_proxy_password(self, *args, **kwargs):
B.add_proxy_password(self, *args, **kwargs)
self._clone_actions['add_proxy_password'] = ('add_proxy_password', args, kwargs)
def clone_browser(self):
clone = Browser()
clone.addheaders = copy.deepcopy(self.addheaders)
for func, args, kwargs in self._clone_actions.values():
func = getattr(clone, func)
func(*args, **kwargs)
return clone
if __name__ == '__main__':
from calibre import browser
from pprint import pprint
orig = browser()
clone = orig.clone_browser()
pprint( orig._ua_handlers)
pprint(clone._ua_handlers)
assert orig._ua_handlers.keys() == clone._ua_handlers.keys()
assert orig._ua_handlers['_cookies'].cookiejar is \
clone._ua_handlers['_cookies'].cookiejar
assert orig.addheaders == clone.addheaders

View File

@ -82,9 +82,6 @@ class BasicNewsRecipe(Recipe):
#: Automatically reduced to 1 if :attr:`BasicNewsRecipe.delay` > 0
simultaneous_downloads = 5
#: If False the remote server is contacted by only one thread at a time
multithreaded_fetch = False
#: Timeout for fetching files from server in seconds
timeout = 120.0
@ -402,6 +399,23 @@ class BasicNewsRecipe(Recipe):
'''
return browser(*args, **kwargs)
def clone_browser(self, br):
'''
Clone the browser br. Cloned browsers are used for multi-threaded
downloads, since mechanize is not thread safe. The default cloning
routines should capture most browser customization, but if you do
something exotic in your recipe, you should override this method in
your recipe and clone manually.
Cloned browser instances use the same, thread-safe CookieJar by
default, unless you have customized cookie handling.
'''
if callable(getattr(br, 'clone_browser', None)):
return br.clone_browser()
# Uh-oh recipe using something exotic, call get_browser
return self.get_browser()
def get_article_url(self, article):
'''
Override in a subclass to customize extraction of the :term:`URL` that points
@ -798,17 +812,23 @@ class BasicNewsRecipe(Recipe):
extra_css=css).render(doctype='xhtml')
def _fetch_article(self, url, dir, f, a, num_of_feeds):
self.web2disk_options.browser = self.get_browser() if self.multithreaded_fetch else self.browser
def _fetch_article(self, url, dir_, f, a, num_of_feeds):
br = self.browser
if self.get_browser.im_func is BasicNewsRecipe.get_browser.im_func:
# We are using the default get_browser, which means no need to
# clone
br = BasicNewsRecipe.get_browser(self)
else:
br = self.clone_browser(self.browser)
self.web2disk_options.browser = br
fetcher = RecursiveFetcher(self.web2disk_options, self.log,
self.image_map, self.css_map,
(url, f, a, num_of_feeds))
fetcher.base_dir = dir
fetcher.current_dir = dir
fetcher.browser = br
fetcher.base_dir = dir_
fetcher.current_dir = dir_
fetcher.show_progress = False
fetcher.image_url_processor = self.image_url_processor
if self.multithreaded_fetch:
fetcher.browser_lock = fetcher.DUMMY_LOCK
res, path, failures = fetcher.start_fetch(url), fetcher.downloaded_paths, fetcher.failed_links
if not res or not os.path.exists(res):
raise Exception(_('Could not fetch article. Run with -vv to see the reason'))
@ -1387,7 +1407,7 @@ class CustomIndexRecipe(BasicNewsRecipe):
def download(self):
index = os.path.abspath(self.custom_index())
url = 'file:'+index if iswindows else 'file://'+index
self.web2disk_options.browser = self.browser
self.web2disk_options.browser = self.clone_browser(self.browser)
fetcher = RecursiveFetcher(self.web2disk_options, self.log)
fetcher.base_dir = self.output_dir
fetcher.current_dir = self.output_dir

View File

@ -86,11 +86,6 @@ class response(str):
obj.newurl = None
return obj
class DummyLock(object):
def __enter__(self, *args): return self
def __exit__(self, *args): pass
def default_is_link_wanted(url, tag):
raise NotImplementedError()
@ -104,7 +99,6 @@ class RecursiveFetcher(object):
# )
CSS_IMPORT_PATTERN = re.compile(r'\@import\s+url\((.*?)\)', re.IGNORECASE)
default_timeout = socket.getdefaulttimeout() # Needed here as it is used in __del__
DUMMY_LOCK = DummyLock()
def __init__(self, options, log, image_map={}, css_map={}, job_info=None):
self.base_dir = os.path.abspath(os.path.expanduser(options.dir))

87
src/templite/__init__.py Normal file
View File

@ -0,0 +1,87 @@
#!/usr/bin/env python
#
# Templite+
# A light-weight, fully functional, general purpose templating engine
#
# Copyright (c) 2009 joonis new media
# Author: Thimo Kraemer <thimo.kraemer@joonis.de>
#
# Based on Templite - Tomer Filiba
# http://code.activestate.com/recipes/496702/
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
import sys, re
class Templite(object):
auto_emit = re.compile('(^[\'\"])|(^[a-zA-Z0-9_\[\]\'\"]+$)')
def __init__(self, template, start='${', end='}$'):
if len(start) != 2 or len(end) != 2:
raise ValueError('each delimiter must be two characters long')
delimiter = re.compile('%s(.*?)%s' % (re.escape(start), re.escape(end)), re.DOTALL)
offset = 0
tokens = []
for i, part in enumerate(delimiter.split(template)):
part = part.replace('\\'.join(list(start)), start)
part = part.replace('\\'.join(list(end)), end)
if i % 2 == 0:
if not part: continue
part = part.replace('\\', '\\\\').replace('"', '\\"')
part = '\t' * offset + 'emit("""%s""")' % part
else:
part = part.rstrip()
if not part: continue
if part.lstrip().startswith(':'):
if not offset:
raise SyntaxError('no block statement to terminate: ${%s}$' % part)
offset -= 1
part = part.lstrip()[1:]
if not part.endswith(':'): continue
elif self.auto_emit.match(part.lstrip()):
part = 'emit(%s)' % part.lstrip()
lines = part.splitlines()
margin = min(len(l) - len(l.lstrip()) for l in lines if l.strip())
part = '\n'.join('\t' * offset + l[margin:] for l in lines)
if part.endswith(':'):
offset += 1
tokens.append(part)
if offset:
raise SyntaxError('%i block statement(s) not terminated' % offset)
self.__code = compile('\n'.join(tokens), '<templite %r>' % template[:20], 'exec')
def render(self, __namespace=None, **kw):
"""
renders the template according to the given namespace.
__namespace - a dictionary serving as a namespace for evaluation
**kw - keyword arguments which are added to the namespace
"""
namespace = {}
if __namespace: namespace.update(__namespace)
if kw: namespace.update(kw)
namespace['emit'] = self.write
__stdout = sys.stdout
sys.stdout = self
self.__output = []
eval(self.__code, namespace)
sys.stdout = __stdout
return ''.join(self.__output)
def write(self, *args):
for a in args:
self.__output.append(str(a))