mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
0.8.47
This commit is contained in:
commit
fdd0a54f49
@ -19,6 +19,51 @@
|
||||
# new recipes:
|
||||
# - title:
|
||||
|
||||
- version: 0.8.47
|
||||
date: 2012-04-13
|
||||
|
||||
new features:
|
||||
- title: "Conversion pipeline: Add support for all the named entities in the HTML 5 spec."
|
||||
tickets: [976056]
|
||||
|
||||
- title: "Support for viewing and converting the Haodoo PDB ebook format"
|
||||
tickets: [976478]
|
||||
|
||||
- title: "Device driver for Laser EB720"
|
||||
|
||||
bug fixes:
|
||||
- title: "Fix regression in automatic adding in 0.8.46 that broke automatic adding if adding of duplicates is enabled and auto convert is also enabled"
|
||||
tickets: [976336]
|
||||
|
||||
- title: 'Fix "Tags" field in advanced search does not obey regex setting'
|
||||
tickets: [980221]
|
||||
|
||||
- title: "EPUB Input: Automatically extract cover image from simple HTML title page that consists of only a single <img> tag, instead of rendering the page"
|
||||
|
||||
- title: "Prevent errors when both author and author_sort are used in a template for reading metadata from filenames for files on a device"
|
||||
|
||||
- title: "Amazon metadata download: Handle books whose titles start with a bracket."
|
||||
tickets: [976365]
|
||||
|
||||
- title: "Get Books: Fix downloading of purchased books from Baen"
|
||||
tickets: [975929]
|
||||
|
||||
|
||||
improved recipes:
|
||||
- Forbes
|
||||
- Caros Amigos
|
||||
- Trouw
|
||||
- Sun UK
|
||||
- Metro
|
||||
- Daily Mirror
|
||||
|
||||
new recipes:
|
||||
- title: "Melbourne Herald Sun"
|
||||
author: Ray Hartley
|
||||
|
||||
- title: "Editoriali and Zerocalcare"
|
||||
author: faber1971
|
||||
|
||||
- version: 0.8.46
|
||||
date: 2012-04-06
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
__copyright__ = '2011, Pablo Aldama <pabloaldama at gmail.com>'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1311839910(BasicNewsRecipe):
|
||||
title = u'Caros Amigos'
|
||||
oldest_article = 20
|
||||
@ -9,9 +7,8 @@ class AdvancedUserRecipe1311839910(BasicNewsRecipe):
|
||||
language = 'pt_BR'
|
||||
__author__ = 'Pablo Aldama'
|
||||
|
||||
feeds = [(u'Caros Amigos', u'http://carosamigos.terra.com.br/index/index.php?format=feed&type=rss')]
|
||||
feeds = [(u'Caros Amigos', u'http://carosamigos.terra.com.br/index2/index.php?format=feed&type=rss')]
|
||||
keep_only_tags = [dict(name='div', attrs={'class':['blog']})
|
||||
,dict(name='div', attrs={'class':['blogcontent']})
|
||||
]
|
||||
remove_tags = [dict(name='div', attrs={'class':'addtoany'})]
|
||||
|
||||
|
@ -1,20 +1,21 @@
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
import mechanize
|
||||
class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
||||
title = u'The Daily Mirror'
|
||||
description = 'News as provide by The Daily Mirror -UK'
|
||||
|
||||
__author__ = 'Dave Asbury'
|
||||
# last updated 11/2/12
|
||||
# last updated 7/4/12
|
||||
language = 'en_GB'
|
||||
|
||||
cover_url = 'http://yookeo.com/screens/m/i/mirror.co.uk.jpg'
|
||||
#cover_url = 'http://yookeo.com/screens/m/i/mirror.co.uk.jpg'
|
||||
|
||||
masthead_url = 'http://www.nmauk.co.uk/nma/images/daily_mirror.gif'
|
||||
|
||||
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 5
|
||||
max_articles_per_feed = 10
|
||||
remove_empty_feeds = True
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
@ -75,3 +76,28 @@ class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
||||
img { display:block}
|
||||
'''
|
||||
|
||||
def get_cover_url(self):
|
||||
soup = self.index_to_soup('http://www.politicshome.com/uk/latest_frontpage.html')
|
||||
# look for the block containing the mirror button and url
|
||||
cov = soup.find(attrs={'style' : 'background-image: url(http://www.politicshome.com/images/sources/source_frontpage_button_92.gif);'})
|
||||
cov2 = str(cov)
|
||||
cov2='http://www.politicshome.com'+cov2[9:-142]
|
||||
#cov2 now contains url of the page containing pic
|
||||
soup = self.index_to_soup(cov2)
|
||||
cov = soup.find(attrs={'id' : 'large'})
|
||||
cov2 = str(cov)
|
||||
cov2=cov2[27:-18]
|
||||
#cov2 now is pic url, now go back to original function
|
||||
br = mechanize.Browser()
|
||||
br.set_handle_redirect(False)
|
||||
try:
|
||||
br.open_novisit(cov2)
|
||||
cover_url = cov2
|
||||
except:
|
||||
cover_url = 'http://yookeo.com/screens/m/i/mirror.co.uk.jpg'
|
||||
|
||||
#cover_url = cov2
|
||||
#cover_url = 'http://www.thesun.co.uk/img/global/new-masthead-logo.png'
|
||||
return cover_url
|
||||
|
||||
|
||||
|
16
recipes/editoriali.recipe
Normal file
16
recipes/editoriali.recipe
Normal file
@ -0,0 +1,16 @@
|
||||
__version__ = 'v1.0'
|
||||
__date__ = '7, April 2012'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1332847053(BasicNewsRecipe):
|
||||
title = u'Editoriali'
|
||||
__author__ = 'faber1971'
|
||||
description = 'Leading articles on Italy by the best Italian editorials'
|
||||
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
auto_cleanup = True
|
||||
conversion_options = {'linearize_tables': True}
|
||||
masthead_url = 'http://folkbulletin.folkest.com/wp-content/uploads/editoriale1.jpg'
|
||||
feeds = [(u'Micromega', u'http://temi.repubblica.it/micromega-online/feed/'), (u'Corriere della Sera', u'http://xml.corriereobjects.it/rss/editoriali.xml'), (u'La Stampa', u'http://www.lastampa.it/cmstp/rubriche/oggetti/rss.asp?ID_blog=25'), (u"Italia dall'estero", u'http://italiadallestero.info/feed')]
|
@ -1,39 +1,49 @@
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Forbes(BasicNewsRecipe):
|
||||
title = u'Forbes'
|
||||
description = 'Business and Financial News'
|
||||
__author__ = 'Darko Miletic'
|
||||
__author__ = 'Kovid Goyal'
|
||||
oldest_article = 30
|
||||
max_articles_per_feed = 100
|
||||
max_articles_per_feed = 20
|
||||
language = 'en'
|
||||
encoding = 'utf-8'
|
||||
recursions = 1
|
||||
|
||||
no_stylesheets = True
|
||||
html2lrf_options = ['--base-font-size', '10']
|
||||
|
||||
cover_url = u'http://www.forbes.com/media/current_covers/forbes_120_160.gif'
|
||||
|
||||
feeds = [(u'Latest', u'http://www.forbes.com/news/index.xml'),
|
||||
(u'Most Popular', u'http://www.forbes.com/feeds/popstories.xml'),
|
||||
(u'Most Emailed', u'http://www.forbes.com/feeds/mostemailed.xml'),
|
||||
(u'Faces', u'http://www.forbes.com/facesscan/index.xml'),
|
||||
(u'Technology', u'http://www.forbes.com/technology/index.xml'),
|
||||
(u'Personal Tech', u'http://www.forbes.com/personaltech/index.xml'),
|
||||
(u'Wireless', u'http://www.forbes.com/wireless/index.xml'),
|
||||
(u'Business', u'http://www.forbes.com/business/index.xml'),
|
||||
(u'Sports Money', u'http://www.forbes.com/sportsmoney/index.xml'),
|
||||
(u'Sports', u'http://www.forbes.com/forbeslife/sports/index.xml'),
|
||||
(u'Vehicles', u'http://www.forbes.com/forbeslife/vehicles/index.xml'),
|
||||
(u'Leadership', u'http://www.forbes.com/leadership/index.xml'),
|
||||
(u'Careers', u'http://www.forbes.com/leadership/careers/index.xml'),
|
||||
(u'Compensation', u'http://www.forbes.com/leadership/compensation/index.xml'),
|
||||
(u'Managing', u'http://www.forbes.com/leadership/managing/index.xml')]
|
||||
|
||||
def print_version(self, url):
|
||||
raw = self.browser.open(url).read()
|
||||
soup = BeautifulSoup(raw.decode('latin1', 'replace'))
|
||||
print_link = soup.find('a', {'onclick':"s_linkTrackVars='prop18';s_linkType='o';s_linkName='Print';if(typeof(globalPageName)!='undefined')s_prop18=globalPageName;s_lnk=s_co(this);s_gs(s_account);"})
|
||||
if print_link is None:
|
||||
return ''
|
||||
return 'http://www.forbes.com' + print_link['href']
|
||||
feeds = [(u'Latest', u'http://www.forbes.com/news/index.xml'),
|
||||
(u'Most Popular', u'http://www.forbes.com/feeds/popstories.xml'),
|
||||
(u'Technology', u'http://www.forbes.com/technology/index.xml'),
|
||||
(u'Business', u'http://www.forbes.com/business/index.xml'),
|
||||
(u'Sports Money', u'http://www.forbes.com/sportsmoney/index.xml'),
|
||||
(u'Leadership', u'http://www.forbes.com/leadership/index.xml'),]
|
||||
|
||||
keep_only_tags = \
|
||||
{'class':lambda x: x and (set(x.split()) & {'body', 'pagination',
|
||||
'articleHead', 'article_head'})}
|
||||
remove_tags_before = {'name':'h1'}
|
||||
remove_tags = [
|
||||
{'class':['comment_bug', 'engagement_block',
|
||||
'video_promo_block', 'article_actions']},
|
||||
{'id':'comments'}
|
||||
]
|
||||
|
||||
def is_link_wanted(self, url, tag):
|
||||
ans = re.match(r'http://.*/[2-9]/', url) is not None
|
||||
if ans:
|
||||
self.log('Following multipage link: %s'%url)
|
||||
return ans
|
||||
|
||||
def postprocess_html(self, soup, first_fetch):
|
||||
for pag in soup.findAll(True, 'pagination'):
|
||||
pag.extract()
|
||||
if not first_fetch:
|
||||
h1 = soup.find('h1')
|
||||
if h1 is not None:
|
||||
h1.extract()
|
||||
return soup
|
||||
|
||||
|
85
recipes/melbourne_herald_sun.recipe
Normal file
85
recipes/melbourne_herald_sun.recipe
Normal file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Matthew Briggs'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
http://www.herald sun.com.au/
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class DailyTelegraph(BasicNewsRecipe):
|
||||
title = u'Melbourne Herald Sun'
|
||||
__author__ = u'Ray Hartley'
|
||||
description = (u'Victorian and National News'
|
||||
'. You will need to have a subscription to '
|
||||
'http://www.heraldsun.com.au to get full articles.')
|
||||
language = 'en_AU'
|
||||
|
||||
oldest_article = 2
|
||||
needs_subscription = 'optional'
|
||||
max_articles_per_feed = 30
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
encoding = 'utf8'
|
||||
use_embedded_content = False
|
||||
language = 'en_AU'
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
masthead_url = 'http://resources2.news.com.au/cs/heraldsun/images/header-and-footer/logo.gif'
|
||||
extra_css = """
|
||||
body{font-family: Arial,Helvetica,sans-serif }
|
||||
img{margin-bottom: 0.4em; display:block}
|
||||
.caption{display: inline; font-size: x-small}
|
||||
"""
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(attrs={'id':'story'})]
|
||||
remove_tags_before=dict(attrs={'class':'story-header'})
|
||||
remove_tags_after=dict(attrs={'class':'story-footer'})
|
||||
remove_tags = [
|
||||
dict(name=['meta','link','base','iframe','embed','object','media-metadata','media-reference','media-producer'])
|
||||
,dict(attrs={'class':['story-header-tools','story-sidebar','story-footer','story-summary-list']})
|
||||
]
|
||||
remove_attributes=['lang']
|
||||
|
||||
|
||||
feeds = [(u'Breaking News' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_breakingnews_206.xml' )
|
||||
,(u'Business' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_business_207.xml' )
|
||||
,(u'Entertainment' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_entertainment_208.xml' )
|
||||
,(u'Health Science' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_health_212.xml' )
|
||||
,(u'Music' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_music_449.xml' )
|
||||
,(u'National News' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_national_209.xml' )
|
||||
,(u'Sport News' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_sport_213.xml' )
|
||||
,(u'AFL News' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_afl_205.xml' )
|
||||
,(u'State News' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_vic_214.xml' )
|
||||
,(u'Technology' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_tech_215.xml' )
|
||||
,(u'World News' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_world_216.xml' )
|
||||
,(u'Opinion', u'http://feeds.news.com.au/public/rss/2.0/heraldsun_opinion_210.xml' )
|
||||
,(u'Andrew Bolt' , u'http://blogs.news.com.au/heraldsun/andrewbolt/index.php/xml/rss_2.0/heraldsun/hs_andrewbolt/')
|
||||
,(u'Afl - St Kilda' , u'http://feeds.news.com.au/public/rss/2.0/heraldsun_afl_stkilda_565.xml')
|
||||
,(u'Terry McCrann' ,u'http://feeds.news.com.au/public/rss/2.0/heraldsun_tmccrann_224.xml' )
|
||||
,(u'The Other side' ,u'http://feeds.news.com.au/public/rss/2.0/heraldsun_otherside_211.xml')]
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser(self)
|
||||
if self.username and self.password:
|
||||
br.open('http://www.heraldsun.com.au')
|
||||
br.select_form(nr=0)
|
||||
br['username'] = self.username
|
||||
br['password'] = self.password
|
||||
raw = br.submit().read()
|
||||
if '>log out' not in raw.lower():
|
||||
raise ValueError('Failed to log in to www.heralsun'
|
||||
' are your username and password correct?')
|
||||
return br
|
||||
|
||||
def get_article_url(self, article):
|
||||
return article.id
|
||||
|
||||
|
@ -1,52 +1,30 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||
title = u'Metro UK'
|
||||
description = 'News as provide by The Metro -UK'
|
||||
|
||||
#timefmt = ''
|
||||
__author__ = 'Dave Asbury'
|
||||
#last update 3/12/11
|
||||
cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/276636_117118184990145_2132092232_n.jpg'
|
||||
no_stylesheets = True
|
||||
#no_stylesheets = True
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
max_articles_per_feed = 10
|
||||
remove_empty_feeds = True
|
||||
remove_javascript = True
|
||||
auto_cleanup = True
|
||||
|
||||
#preprocess_regexps = [(re.compile(r'Tweet'), lambda a : '')]
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'<span class="img-cap legend">', re.IGNORECASE | re.DOTALL), lambda match: '<p></p><span class="img-cap legend"> ')]
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'tweet', re.IGNORECASE | re.DOTALL), lambda match: '')]
|
||||
|
||||
language = 'en_GB'
|
||||
|
||||
|
||||
masthead_url = 'http://e-edition.metro.co.uk/images/metro_logo.gif'
|
||||
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='h1'),dict(name='h2', attrs={'class':'h2'}),
|
||||
dict(attrs={'class':['img-cnt figure']}),
|
||||
dict(attrs={'class':['art-img']}),
|
||||
dict(name='div', attrs={'class':'art-lft'}),
|
||||
dict(name='p')
|
||||
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name = 'div',attrs={'id' : ['comments-news','formSubmission']}),
|
||||
dict(name='div', attrs={'class':[ 'news m12 clrd clr-b p5t shareBtm', 'commentForm', 'metroCommentInnerWrap',
|
||||
'art-rgt','pluck-app pluck-comm','news m12 clrd clr-l p5t', 'flt-r','username','clrd' ]}),
|
||||
dict(attrs={'class':['username', 'metroCommentFormWrap','commentText','commentsNav','avatar','submDateAndTime','addYourComment','displayName']})
|
||||
,dict(name='div', attrs={'class' : 'clrd art-fd fd-gr1-b'})
|
||||
|
||||
]
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'News', u'http://www.metro.co.uk/rss/news/'), (u'Money', u'http://www.metro.co.uk/rss/money/'), (u'Sport', u'http://www.metro.co.uk/rss/sport/'), (u'Film', u'http://www.metro.co.uk/rss/metrolife/film/'), (u'Music', u'http://www.metro.co.uk/rss/metrolife/music/'), (u'TV', u'http://www.metro.co.uk/rss/tv/'), (u'Showbiz', u'http://www.metro.co.uk/rss/showbiz/'), (u'Weird News', u'http://www.metro.co.uk/rss/weird/'), (u'Travel', u'http://www.metro.co.uk/rss/travel/'), (u'Lifestyle', u'http://www.metro.co.uk/rss/lifestyle/'), (u'Books', u'http://www.metro.co.uk/rss/lifestyle/books/'), (u'Food', u'http://www.metro.co.uk/rss/lifestyle/restaurants/')]
|
||||
|
||||
extra_css = '''
|
||||
body {font: sans-serif medium;}'
|
||||
h1 {text-align : center; font-family:Arial,Helvetica,sans-serif; font-size:20px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold;}
|
||||
h2 {text-align : center;color:#4D4D4D;font-family:Arial,Helvetica,sans-serif; font-size:15px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; }
|
||||
span{ font-size:9.5px; font-weight:bold;font-style:italic}
|
||||
p { text-align: justify; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal;}
|
||||
|
||||
'''
|
||||
body{ text-align: justify; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal;}
|
||||
'''
|
||||
|
@ -1,24 +1,23 @@
|
||||
import re
|
||||
import re, mechanize
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||
|
||||
title = u'The Sun UK'
|
||||
cover_url = 'http://www.thesun.co.uk/img/global/new-masthead-logo.png'
|
||||
|
||||
description = 'A Recipe for The Sun tabloid UK - uses feed43'
|
||||
description = 'A Recipe for The Sun tabloid UK'
|
||||
__author__ = 'Dave Asbury'
|
||||
# last updated 20/2/12
|
||||
# last updated 7/4/12
|
||||
language = 'en_GB'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 15
|
||||
remove_empty_feeds = True
|
||||
no_stylesheets = True
|
||||
#auto_cleanup = True
|
||||
#articles_are_obfuscated = True
|
||||
|
||||
masthead_url = 'http://www.thesun.co.uk/sol/img/global/Sun-logo.gif'
|
||||
encoding = 'cp1251'
|
||||
encoding = 'UTF-8'
|
||||
|
||||
encoding = 'cp1252'
|
||||
remove_empty_feeds = True
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
@ -30,13 +29,14 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'<div class="foot-copyright".*?</div>', re.IGNORECASE | re.DOTALL), lambda match: '')]
|
||||
|
||||
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='h1'),dict(name='h2',attrs={'class' : 'medium centered'}),
|
||||
dict(name='div',attrs={'class' : 'text-center'}),
|
||||
dict(name='div',attrs={'id' : 'bodyText'})
|
||||
# dict(name='p')
|
||||
]
|
||||
|
||||
remove_tags=[
|
||||
#dict(name='head'),
|
||||
dict(attrs={'class' : ['mystery-meat-link','ltbx-container','ltbx-var ltbx-hbxpn','ltbx-var ltbx-nav-loop','ltbx-var ltbx-url']}),
|
||||
@ -46,12 +46,46 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||
|
||||
]
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'News','http://feed43.com/2517447382644748.xml'),
|
||||
(u'Sport', u'http://feed43.com/4283846255668687.xml'),
|
||||
(u'Bizarre', u'http://feed43.com/0233840304242011.xml'),
|
||||
(u'Film',u'http://feed43.com/1307545221226200.xml'),
|
||||
(u'Music',u'http://feed43.com/1701513435064132.xml'),
|
||||
(u'Sun Woman',u'http://feed43.com/0022626854226453.xml'),
|
||||
(u'News','http://feed43.com/2517447382644748.xml'),
|
||||
(u'Sport', u'http://feed43.com/4283846255668687.xml'),
|
||||
(u'Bizarre', u'http://feed43.com/0233840304242011.xml'),
|
||||
(u'Film',u'http://feed43.com/1307545221226200.xml'),
|
||||
(u'Music',u'http://feed43.com/1701513435064132.xml'),
|
||||
(u'Sun Woman',u'http://feed43.com/0022626854226453.xml'),
|
||||
]
|
||||
|
||||
def get_cover_url(self):
|
||||
soup = self.index_to_soup('http://www.politicshome.com/uk/latest_frontpage.html')
|
||||
# look for the block containing the sun button and url
|
||||
cov = soup.find(attrs={'style' : 'background-image: url(http://www.politicshome.com/images/sources/source_frontpage_button_84.gif);'})
|
||||
|
||||
|
||||
|
||||
#cov = soup.find(attrs={'id' : 'large'})
|
||||
cov2 = str(cov)
|
||||
|
||||
cov2='http://www.politicshome.com'+cov2[9:-133]
|
||||
#cov2 now contains url of the page containing pic
|
||||
|
||||
#cov2 now contains url of the page containing pic
|
||||
soup = self.index_to_soup(cov2)
|
||||
cov = soup.find(attrs={'id' : 'large'})
|
||||
cov2 = str(cov)
|
||||
cov2=cov2[27:-18]
|
||||
#cov2 now is pic url, now go back to original function
|
||||
|
||||
br = mechanize.Browser()
|
||||
br.set_handle_redirect(False)
|
||||
try:
|
||||
br.open_novisit(cov2)
|
||||
cover_url = cov2
|
||||
except:
|
||||
cover_url = 'http://www.thesun.co.uk/img/global/new-masthead-logo.png'
|
||||
|
||||
#cover_url = cov2
|
||||
#cover_url = 'http://www.thesun.co.uk/img/global/new-masthead-logo.png'
|
||||
return cover_url
|
||||
|
||||
|
||||
|
@ -1,71 +1,12 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Trouw(BasicNewsRecipe):
|
||||
class BasicUserRecipe1333905513(BasicNewsRecipe):
|
||||
title = u'Trouw'
|
||||
__author__ = u'JvdW'
|
||||
__author__ = 'asalet_r'
|
||||
language = 'nl'
|
||||
description = u'Trouw de Verdieping'
|
||||
oldest_article = 7
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 25
|
||||
language = u'nl'
|
||||
simultaneous_downloads = 1
|
||||
delay = 1
|
||||
# timefmt = ' [%A, %d %B, %Y]'
|
||||
timefmt = ''
|
||||
no_stylesheets = True
|
||||
cover_url = 'http://www.trouw.nl/template/ver2-0/images/trouw_logo.gif'
|
||||
auto_cleanup = True
|
||||
|
||||
# keep_only_tags = [ dict(name='div', attrs={'id':'content'}) ]
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'id' :'leaderboard' })
|
||||
,dict(name='div', attrs={'class':'banner' })
|
||||
,dict(name='div', attrs={'id' :'header' })
|
||||
,dict(name='div', attrs={'class':'options' })
|
||||
,dict(name='div', attrs={'id' :'menu_main' })
|
||||
,dict(name='div', attrs={'id' :'menu_sub' })
|
||||
,dict(name='div', attrs={'id' :'column_right' })
|
||||
,dict(name='div', attrs={'class':'meta_information'})
|
||||
,dict(name='div', attrs={'id' :'comments_form' })
|
||||
,dict(name='div', attrs={'id' :'mailfriend' })
|
||||
,dict(name='div', attrs={'id' :'footer' })
|
||||
,dict(name='img', attrs={'id' :'dot_clear' })
|
||||
]
|
||||
|
||||
keep_only_tags = [dict(id=['columns'])]
|
||||
|
||||
feeds = [
|
||||
(u'Algemen', u'http://www.trouw.nl/?service=rss'),
|
||||
(u'Nederland', u'http://www.trouw.nl/nieuws/nederland/?service=rss'),
|
||||
(u'Europa', u'http://www.trouw.nl/nieuws/europa/?service=rss'),
|
||||
(u'Wereld', u'http://www.trouw.nl/nieuws/wereld/?service=rss'),
|
||||
(u'Economie', u'http://www.trouw.nl/nieuws/economie/?service=rss'),
|
||||
(u'Wetenschap', u'http://www.trouw.nl/nieuws/Wetenschap/?service=rss'),
|
||||
(u'Groen', u'http://www.trouw.nl/groen/?service=rss'),
|
||||
(u'Religie en Filosofie', u'http://www.trouw.nl/religie-filosofie/?service=rss'),
|
||||
(u'Politiek', u'http://www.trouw.nl/nieuws/politiek/?service=rss'),
|
||||
(u'Zorg', u'http://www.trouw.nl/nieuws/zorg/?service=rss'),
|
||||
(u'Onderwijs', u'http://www.trouw.nl/onderwijs/nieuws/?service=rss'),
|
||||
(u'Sport', u'http://www.trouw.nl/nieuws/sport/?service=rss'),
|
||||
(u'Achtergrond', u'http://www.trouw.nl/achtergrond/?service=rss'),
|
||||
(u'De Verdieping', u'http://www.trouw.nl/achtergrond/deverdieping/?service=rss'),
|
||||
(u'Naschrift', u'http://www.trouw.nl/achtergrond/Naschrift/?service=rss'),
|
||||
(u'Opinie', u'http://www.trouw.nl/opinie/?service=rss'),
|
||||
(u'Podium', u'http://www.trouw.nl/opinie/podium/?service=rss'),
|
||||
(u'Commentaar', u'http://www.trouw.nl/opinie/commentaar/?service=rss'),
|
||||
(u'Cultuur', u'http://www.trouw.nl/cultuur/?service=rss'),
|
||||
(u'Boeken', u'http://www.trouw.nl/cultuur/boeken/?service=rss'),
|
||||
(u'Film', u'http://www.trouw.nl/cultuur/film/?service=rss'),
|
||||
(u'Beeldende kunst', u'http://www.trouw.nl/cultuur/beeldendekunst/?service=rss'),
|
||||
(u'Theater', u'http://www.trouw.nl/cultuur/theater/?service=rss'),
|
||||
(u'Muziek', u'http://www.trouw.nl/cultuur/muziek/?service=rss'),
|
||||
(u'Kinderen', u'http://www.trouw.nl/cultuur/kinderen/?service=rss'),
|
||||
(u'Ontspanning', u'http://www.trouw.nl/ontspanning/?service=rss'),
|
||||
(u'De Gids', u'http://www.trouw.nl/ontspanning/degids/?service=rss'),
|
||||
(u'Moderne manieren', u'http://www.trouw.nl/ontspanning/modernemanieren/?service=rss'),
|
||||
(u'Reizen', u'http://www.trouw.nl/ontspanning/reizen/?service=rss'),
|
||||
(u'Koken', u'http://www.trouw.nl/ontspanning/koken/?service=rss')
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '?all=true'
|
||||
feeds = [(u'Nederland', u'http://www.trouw.nl/nieuws/nederland/rss.xml'), (u'Buitenland', u'http://www.trouw.nl/nieuws/buitenland/rss.xml'), (u'Politiek', u'http://www.trouw.nl/nieuws/politiek/rss.xml'), (u'Economie', u'http://www.trouw.nl/nieuws/economie/rss.xml'), (u'Sport', u'http://www.trouw.nl/nieuws/sport/rss.xml'), (u'Cultuur', u'http://www.trouw.nl/nieuws/cultuur/rss.xml'), (u'Gezondheid', u'http://www.trouw.nl/nieuws/gezondheid/rss.xml'), (u'Onderwijs', u'http://www.trouw.nl/nieuws/onderwijs/rss.xml'), (u'Opinie', u'http://www.trouw.nl/opinie/rss.xml'), (u'Groen', u'http://www.trouw.nl/groen/rss.xml'), (u'Religie-Filosofie', u'http://www.trouw.nl/religie-filosofie/rss.xml'), (u'Schrijf', u'http://www.trouw.nl/schrijf/rss.xml'), (u'Moderne Manieren', u'http://www.trouw.nl/moderne-manieren/rss.xml')]
|
||||
|
18
recipes/zerocalcare.recipe
Normal file
18
recipes/zerocalcare.recipe
Normal file
@ -0,0 +1,18 @@
|
||||
__version__ = 'v1.0'
|
||||
__date__ = '7, April 2012'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1333705905(BasicNewsRecipe):
|
||||
title = u'Zerocalcare'
|
||||
__author__ = 'faber1971'
|
||||
description = 'Free Italian Comics'
|
||||
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
auto_cleanup = False
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':'main entry-content group'})
|
||||
]
|
||||
masthead_url = 'http://zerocalcare.it/wp-content/uploads/2011/11/zerocalcare-banner.jpg'
|
||||
feeds = [(u'Zerocalcare', u'http://feeds.feedburner.com/Zerocalcareit')]
|
@ -5,7 +5,6 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
import sys, os, re, time, random, __builtin__, warnings
|
||||
__builtin__.__dict__['dynamic_property'] = lambda(func): func(None)
|
||||
from htmlentitydefs import name2codepoint
|
||||
from math import floor
|
||||
from functools import partial
|
||||
|
||||
@ -551,6 +550,12 @@ def entity_to_unicode(match, exceptions=[], encoding='cp1252',
|
||||
return check(chr(num).decode(encoding))
|
||||
except UnicodeDecodeError:
|
||||
return check(my_unichr(num))
|
||||
from calibre.utils.html5_entities import entity_map
|
||||
try:
|
||||
return check(entity_map[ent])
|
||||
except KeyError:
|
||||
pass
|
||||
from htmlentitydefs import name2codepoint
|
||||
try:
|
||||
return check(my_unichr(name2codepoint[ent]))
|
||||
except KeyError:
|
||||
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = u'calibre'
|
||||
numeric_version = (0, 8, 46)
|
||||
numeric_version = (0, 8, 47)
|
||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
@ -48,6 +48,18 @@ fcntl = None if iswindows else importlib.import_module('fcntl')
|
||||
|
||||
filesystem_encoding = sys.getfilesystemencoding()
|
||||
if filesystem_encoding is None: filesystem_encoding = 'utf-8'
|
||||
else:
|
||||
try:
|
||||
if codecs.lookup(filesystem_encoding).name == 'ascii':
|
||||
filesystem_encoding = 'utf-8'
|
||||
# On linux, unicode arguments to os file functions are coerced to an ascii
|
||||
# bytestring if sys.getfilesystemencoding() == 'ascii', which is
|
||||
# just plain dumb. So issue a warning.
|
||||
print ('WARNING: You do not have the LANG environment variable set. '
|
||||
'This will cause problems with non-ascii filenames. '
|
||||
'Set it to something like en_US.UTF-8.\n')
|
||||
except:
|
||||
filesystem_encoding = 'utf-8'
|
||||
|
||||
|
||||
DEBUG = False
|
||||
|
@ -289,7 +289,7 @@ class OPFMetadataReader(MetadataReaderPlugin):
|
||||
class PDBMetadataReader(MetadataReaderPlugin):
|
||||
|
||||
name = 'Read PDB metadata'
|
||||
file_types = set(['pdb'])
|
||||
file_types = set(['pdb', 'updb'])
|
||||
description = _('Read metadata from %s files') % 'PDB'
|
||||
author = 'John Schember'
|
||||
|
||||
|
@ -121,7 +121,8 @@ class ANDROID(USBMS):
|
||||
0x61c5 : [0x100, 0x226, 0x9999],
|
||||
0x61cc : [0x100],
|
||||
0x61ce : [0x100],
|
||||
0x618e : [0x226, 0x227, 0x9999, 0x100]
|
||||
0x618e : [0x226, 0x227, 0x9999, 0x100],
|
||||
0x6205 : [0x226, 0x227, 0x9999, 0x100],
|
||||
},
|
||||
|
||||
# Archos
|
||||
@ -176,7 +177,7 @@ class ANDROID(USBMS):
|
||||
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC']
|
||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
|
||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID',
|
||||
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
|
||||
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H',
|
||||
'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD',
|
||||
@ -190,7 +191,7 @@ class ANDROID(USBMS):
|
||||
'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW',
|
||||
'KTABLET_PC', 'INGENIC', 'GT-I9001_CARD']
|
||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
||||
'__UMS_COMPOSITE', 'SGH-I997_CARD', 'MB870', 'ALPANDIGITAL',
|
||||
'ANDROID_MID', 'P990_SD_CARD', '.K080', 'LTE_CARD', 'MB853',
|
||||
|
@ -57,7 +57,8 @@ class PICO(NEWSMY):
|
||||
gui_name = 'Pico'
|
||||
description = _('Communicate with the Pico reader.')
|
||||
|
||||
WINDOWS_MAIN_MEM = 'USBDISK__USER'
|
||||
VENDOR_NAME = ['TECLAST', 'IMAGIN', 'LASER-']
|
||||
WINDOWS_MAIN_MEM = ['USBDISK__USER', 'EB720']
|
||||
EBOOK_DIR_MAIN = 'Books'
|
||||
FORMATS = ['EPUB', 'FB2', 'TXT', 'LRC', 'PDB', 'PDF', 'HTML', 'WTXT']
|
||||
|
||||
|
@ -402,19 +402,23 @@ class USBMS(CLI, Device):
|
||||
def build_template_regexp(cls):
|
||||
def replfunc(match, seen=None):
|
||||
v = match.group(1)
|
||||
if v in ['title', 'series', 'series_index', 'isbn']:
|
||||
if v in ['authors', 'author_sort']:
|
||||
v = 'author'
|
||||
if v in ('title', 'series', 'series_index', 'isbn', 'author'):
|
||||
if v not in seen:
|
||||
seen |= set([v])
|
||||
seen.add(v)
|
||||
return '(?P<' + v + '>.+?)'
|
||||
elif v in ['authors', 'author_sort']:
|
||||
if v not in seen:
|
||||
seen |= set([v])
|
||||
return '(?P<author>.+?)'
|
||||
return '(.+?)'
|
||||
s = set()
|
||||
f = functools.partial(replfunc, seen=s)
|
||||
template = cls.save_template().rpartition('/')[2]
|
||||
return re.compile(re.sub('{([^}]*)}', f, template) + '([_\d]*$)')
|
||||
template = None
|
||||
try:
|
||||
template = cls.save_template().rpartition('/')[2]
|
||||
return re.compile(re.sub('{([^}]*)}', f, template) + '([_\d]*$)')
|
||||
except:
|
||||
prints(u'Failed to parse template: %r'%template)
|
||||
template = u'{title} - {authors}'
|
||||
return re.compile(re.sub('{([^}]*)}', f, template) + '([_\d]*$)')
|
||||
|
||||
@classmethod
|
||||
def path_to_unicode(cls, path):
|
||||
|
@ -27,7 +27,7 @@ class ParserError(ValueError):
|
||||
pass
|
||||
|
||||
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'txtz', 'text', 'htm', 'xhtm',
|
||||
'html', 'htmlz', 'xhtml', 'pdf', 'pdb', 'pdr', 'prc', 'mobi', 'azw', 'doc',
|
||||
'html', 'htmlz', 'xhtml', 'pdf', 'pdb', 'updb', 'pdr', 'prc', 'mobi', 'azw', 'doc',
|
||||
'epub', 'fb2', 'djv', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip',
|
||||
'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1', 'pml', 'pmlz', 'mbp', 'tan', 'snb',
|
||||
'xps', 'oxps', 'azw4', 'book', 'zbf', 'pobi', 'docx', 'md',
|
||||
@ -93,6 +93,20 @@ def extract_calibre_cover(raw, base, log):
|
||||
if os.path.exists(img):
|
||||
return open(img, 'rb').read()
|
||||
|
||||
# Look for a simple cover, i.e. a body with no text and only one <img> tag
|
||||
if matches is None:
|
||||
body = soup.find('body')
|
||||
if body is not None:
|
||||
text = u''.join(map(unicode, body.findAll(text=True)))
|
||||
if text.strip():
|
||||
# Body has text, abort
|
||||
return
|
||||
images = body.findAll('img', src=True)
|
||||
if 0 < len(images) < 2:
|
||||
img = os.path.join(base, *images[0]['src'].split('/'))
|
||||
if os.path.exists(img):
|
||||
return open(img, 'rb').read()
|
||||
|
||||
def render_html_svg_workaround(path_to_html, log, width=590, height=750):
|
||||
from calibre.ebooks.oeb.base import SVG_NS
|
||||
raw = open(path_to_html, 'rb').read()
|
||||
@ -108,6 +122,7 @@ def render_html_svg_workaround(path_to_html, log, width=590, height=750):
|
||||
data = extract_calibre_cover(raw, os.path.dirname(path_to_html), log)
|
||||
except:
|
||||
pass
|
||||
|
||||
if data is None:
|
||||
renderer = render_html(path_to_html, width, height)
|
||||
data = getattr(renderer, 'data', None)
|
||||
|
@ -153,11 +153,22 @@ class MOBIOutput(OutputFormatPlugin):
|
||||
|
||||
def convert(self, oeb, output_path, input_plugin, opts, log):
|
||||
self.log, self.opts, self.oeb = log, opts, oeb
|
||||
|
||||
kf8 = self.create_kf8()
|
||||
self.write_mobi(input_plugin, output_path, kf8)
|
||||
|
||||
def create_kf8(self):
|
||||
from calibre.ebooks.mobi.writer8.main import KF8Writer
|
||||
return KF8Writer(self.oeb, self.opts)
|
||||
|
||||
def write_mobi(self, input_plugin, output_path, kf8):
|
||||
from calibre.ebooks.mobi.mobiml import MobiMLizer
|
||||
from calibre.ebooks.oeb.transforms.manglecase import CaseMangler
|
||||
from calibre.ebooks.oeb.transforms.rasterize import SVGRasterizer, Unavailable
|
||||
from calibre.ebooks.oeb.transforms.htmltoc import HTMLTOCAdder
|
||||
from calibre.customize.ui import plugin_for_input_format
|
||||
|
||||
opts, oeb = self.opts, self.oeb
|
||||
if not opts.no_inline_toc:
|
||||
tocadder = HTMLTOCAdder(title=opts.toc_title, position='start' if
|
||||
opts.mobi_toc_at_start else 'end')
|
||||
|
@ -13,7 +13,7 @@ class PDBInput(InputFormatPlugin):
|
||||
name = 'PDB Input'
|
||||
author = 'John Schember'
|
||||
description = 'Convert PDB to HTML'
|
||||
file_types = set(['pdb'])
|
||||
file_types = set(['pdb', 'updb'])
|
||||
|
||||
def convert(self, stream, options, file_ext, log,
|
||||
accelerators):
|
||||
|
23
src/calibre/ebooks/metadata/haodoo.py
Normal file
23
src/calibre/ebooks/metadata/haodoo.py
Normal file
@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Read meta information from Haodoo.net pdb files.
|
||||
'''
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kan-Ru Chen <kanru@kanru.info>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from calibre.ebooks.pdb.header import PdbHeaderReader
|
||||
from calibre.ebooks.pdb.haodoo.reader import Reader
|
||||
|
||||
def get_metadata(stream, extract_cover=True):
|
||||
'''
|
||||
Return metadata as a L{MetaInfo} object
|
||||
'''
|
||||
stream.seek(0)
|
||||
|
||||
pheader = PdbHeaderReader(stream)
|
||||
reader = Reader(pheader, stream, None, None)
|
||||
|
||||
return reader.get_metadata()
|
@ -14,11 +14,14 @@ from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ebooks.pdb.header import PdbHeaderReader
|
||||
from calibre.ebooks.metadata.ereader import get_metadata as get_eReader
|
||||
from calibre.ebooks.metadata.plucker import get_metadata as get_plucker
|
||||
from calibre.ebooks.metadata.haodoo import get_metadata as get_Haodoo
|
||||
|
||||
MREADER = {
|
||||
'PNPdPPrs' : get_eReader,
|
||||
'PNRdPPrs' : get_eReader,
|
||||
'DataPlkr' : get_plucker,
|
||||
'BOOKMTIT' : get_Haodoo,
|
||||
'BOOKMTIU' : get_Haodoo,
|
||||
}
|
||||
|
||||
from calibre.ebooks.metadata.ereader import set_metadata as set_eReader
|
||||
|
@ -347,7 +347,10 @@ class Worker(Thread): # Get details {{{
|
||||
method='text').strip()
|
||||
else:
|
||||
title = self.tostring(tdiv, encoding=unicode, method='text').strip()
|
||||
return re.sub(r'[(\[].*[)\]]', '', title).strip()
|
||||
ans = re.sub(r'[(\[].*[)\]]', '', title).strip()
|
||||
if not ans:
|
||||
ans = title.rpartition('[')[0].strip()
|
||||
return ans
|
||||
|
||||
def parse_authors(self, root):
|
||||
x = '//h1[contains(@class, "parseasinTitle")]/following-sibling::span/*[(name()="a" and @href) or (name()="span" and @class="contributorNameTrigger")]'
|
||||
|
@ -48,7 +48,8 @@ def merge_result(oldmi, newmi, ensure_fields=None):
|
||||
|
||||
return newmi
|
||||
|
||||
def main(do_identify, covers, metadata, ensure_fields):
|
||||
def main(do_identify, covers, metadata, ensure_fields, tdir):
|
||||
os.chdir(tdir)
|
||||
failed_ids = set()
|
||||
failed_covers = set()
|
||||
all_failed = True
|
||||
@ -103,7 +104,8 @@ def single_identify(title, authors, identifiers):
|
||||
return [metadata_to_opf(r) for r in results], [r.has_cached_cover_url for
|
||||
r in results], dump_caches(), log.dump()
|
||||
|
||||
def single_covers(title, authors, identifiers, caches):
|
||||
def single_covers(title, authors, identifiers, caches, tdir):
|
||||
os.chdir(tdir)
|
||||
load_caches(caches)
|
||||
log = GUILog()
|
||||
results = Queue()
|
||||
|
158
src/calibre/ebooks/mobi/debug/index.py
Normal file
158
src/calibre/ebooks/mobi/debug/index.py
Normal file
@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from collections import OrderedDict, namedtuple
|
||||
|
||||
from calibre.ebooks.mobi.reader.headers import NULL_INDEX
|
||||
from calibre.ebooks.mobi.reader.index import (CNCX, parse_indx_header,
|
||||
parse_tagx_section, parse_index_record, INDEX_HEADER_FIELDS)
|
||||
from calibre.ebooks.mobi.reader.ncx import (tag_fieldname_map, default_entry)
|
||||
|
||||
File = namedtuple('File',
|
||||
'file_number name divtbl_count start_position length')
|
||||
|
||||
Elem = namedtuple('Elem',
|
||||
'insert_pos toc_text file_number sequence_number start_pos '
|
||||
'length')
|
||||
|
||||
def read_index(sections, idx, codec):
|
||||
table, cncx = OrderedDict(), CNCX([], codec)
|
||||
|
||||
data = sections[idx].raw
|
||||
|
||||
indx_header = parse_indx_header(data)
|
||||
indx_count = indx_header['count']
|
||||
|
||||
if indx_header['ncncx'] > 0:
|
||||
off = idx + indx_count + 1
|
||||
cncx_records = [x.raw for x in sections[off:off+indx_header['ncncx']]]
|
||||
cncx = CNCX(cncx_records, codec)
|
||||
|
||||
tag_section_start = indx_header['tagx']
|
||||
control_byte_count, tags = parse_tagx_section(data[tag_section_start:])
|
||||
|
||||
for i in xrange(idx + 1, idx + 1 + indx_count):
|
||||
# Index record
|
||||
data = sections[i].raw
|
||||
parse_index_record(table, data, control_byte_count, tags, codec,
|
||||
indx_header['ordt_map'], strict=True)
|
||||
return table, cncx, indx_header
|
||||
|
||||
class Index(object):
|
||||
|
||||
def __init__(self, idx, records, codec):
|
||||
self.table = self.cncx = self.header = self.records = None
|
||||
if idx != NULL_INDEX:
|
||||
self.table, self.cncx, self.header = read_index(records, idx, codec)
|
||||
|
||||
def render(self):
|
||||
ans = ['*'*10 + ' Index Header ' + '*'*10]
|
||||
a = ans.append
|
||||
if self.header is not None:
|
||||
for field in INDEX_HEADER_FIELDS:
|
||||
a('%-12s: %r'%(field, self.header[field]))
|
||||
ans.extend(['', ''])
|
||||
|
||||
if self.cncx:
|
||||
a('*'*10 + ' CNCX ' + '*'*10)
|
||||
for offset, val in self.cncx.iteritems():
|
||||
a('%10s: %s'%(offset, val))
|
||||
ans.extend(['', ''])
|
||||
|
||||
if self.table is not None:
|
||||
a('*'*10 + ' %d Index Entries '%len(self.table) + '*'*10)
|
||||
for k, v in self.table.iteritems():
|
||||
a('%s: %r'%(k, v))
|
||||
|
||||
if self.records:
|
||||
ans.extend(['', '', '*'*10 + ' Parsed Entries ' + '*'*10])
|
||||
for f in self.records:
|
||||
a(repr(f))
|
||||
|
||||
return ans + ['']
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join(self.render())
|
||||
|
||||
class SKELIndex(Index):
|
||||
|
||||
def __init__(self, skelidx, records, codec):
|
||||
super(SKELIndex, self).__init__(skelidx, records, codec)
|
||||
self.records = []
|
||||
|
||||
if self.table is not None:
|
||||
for i, text in enumerate(self.table.iterkeys()):
|
||||
tag_map = self.table[text]
|
||||
if set(tag_map.iterkeys()) != {1, 6}:
|
||||
raise ValueError('SKEL Index has unknown tags: %s'%
|
||||
(set(tag_map.iterkeys())-{1,6}))
|
||||
self.records.append(File(
|
||||
i, # file_number
|
||||
text, # name
|
||||
tag_map[1][0], # divtbl_count
|
||||
tag_map[6][0], # start_pos
|
||||
tag_map[6][1]) # length
|
||||
)
|
||||
|
||||
class SECTIndex(Index):
|
||||
|
||||
def __init__(self, sectidx, records, codec):
|
||||
super(SECTIndex, self).__init__(sectidx, records, codec)
|
||||
self.records = []
|
||||
|
||||
if self.table is not None:
|
||||
for i, text in enumerate(self.table.iterkeys()):
|
||||
tag_map = self.table[text]
|
||||
if set(tag_map.iterkeys()) != {2, 3, 4, 6}:
|
||||
raise ValueError('SECT Index has unknown tags: %s'%
|
||||
(set(tag_map.iterkeys())-{2, 3, 4, 6}))
|
||||
|
||||
toc_text = self.cncx[tag_map[2][0]]
|
||||
self.records.append(Elem(
|
||||
int(text), # insert_pos
|
||||
toc_text, # toc_text
|
||||
tag_map[3][0], # file_number
|
||||
tag_map[4][0], # sequence_number
|
||||
tag_map[6][0], # start_pos
|
||||
tag_map[6][1] # length
|
||||
)
|
||||
)
|
||||
|
||||
class NCXIndex(Index):
|
||||
|
||||
def __init__(self, ncxidx, records, codec):
|
||||
super(NCXIndex, self).__init__(ncxidx, records, codec)
|
||||
self.records = []
|
||||
|
||||
if self.table is not None:
|
||||
for num, x in enumerate(self.table.iteritems()):
|
||||
text, tag_map = x
|
||||
entry = default_entry.copy()
|
||||
entry['name'] = text
|
||||
entry['num'] = num
|
||||
|
||||
for tag in tag_fieldname_map.iterkeys():
|
||||
fieldname, i = tag_fieldname_map[tag]
|
||||
if tag in tag_map:
|
||||
fieldvalue = tag_map[tag][i]
|
||||
if tag == 6:
|
||||
# Appears to be an idx into the KF8 elems table with an
|
||||
# offset
|
||||
fieldvalue = tuple(tag_map[tag])
|
||||
entry[fieldname] = fieldvalue
|
||||
for which, name in {3:'text', 5:'kind', 70:'description',
|
||||
71:'author', 72:'image_caption',
|
||||
73:'image_attribution'}.iteritems():
|
||||
if tag == which:
|
||||
entry[name] = self.cncx.get(fieldvalue,
|
||||
default_entry[name])
|
||||
self.records.append(entry)
|
||||
|
||||
|
||||
|
@ -393,7 +393,7 @@ class IndexRecord(object): # {{{
|
||||
|
||||
parse_index_record(table, record.raw,
|
||||
index_header.tagx_control_byte_count, tags,
|
||||
index_header.index_encoding, strict=True)
|
||||
index_header.index_encoding, {}, strict=True)
|
||||
|
||||
self.indices = []
|
||||
|
||||
|
@ -7,10 +7,42 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import sys, os, imghdr
|
||||
import sys, os, imghdr, struct
|
||||
from itertools import izip
|
||||
|
||||
from calibre.ebooks.mobi.debug.headers import TextRecord
|
||||
from calibre.ebooks.mobi.debug.index import (SKELIndex, SECTIndex, NCXIndex)
|
||||
from calibre.ebooks.mobi.utils import read_font_record
|
||||
from calibre.ebooks.mobi.debug import format_bytes
|
||||
from calibre.ebooks.mobi.reader.headers import NULL_INDEX
|
||||
|
||||
class FDST(object):
|
||||
|
||||
def __init__(self, raw):
|
||||
if raw[:4] != b'FDST':
|
||||
raise ValueError('KF8 does not have a valid FDST record')
|
||||
self.sec_off, self.num_sections = struct.unpack_from(b'>LL', raw, 4)
|
||||
if self.sec_off != 12:
|
||||
raise ValueError('FDST record has unknown extra fields')
|
||||
secf = b'>%dL' % (self.num_sections*2)
|
||||
secs = struct.unpack_from(secf, raw, self.sec_off)
|
||||
rest = raw[self.sec_off+struct.calcsize(secf):]
|
||||
if rest:
|
||||
raise ValueError('FDST record has trailing data: '
|
||||
'%s'%format_bytes(rest))
|
||||
self.sections = tuple(izip(secs[::2], secs[1::2]))
|
||||
|
||||
def __str__(self):
|
||||
ans = ['FDST record']
|
||||
a = lambda k, v:ans.append('%s: %s'%(k, v))
|
||||
a('Offset to sections', self.sec_off)
|
||||
a('Number of section records', self.num_sections)
|
||||
ans.append('**** %d Sections ****'% len(self.sections))
|
||||
for sec in self.sections:
|
||||
ans.append('Start: %20d End: %d'%sec)
|
||||
|
||||
return '\n'.join(ans)
|
||||
|
||||
|
||||
class MOBIFile(object):
|
||||
|
||||
@ -31,7 +63,10 @@ class MOBIFile(object):
|
||||
first_text_record+offset+h8.number_of_text_records])]
|
||||
|
||||
self.raw_text = b''.join(r.raw for r in self.text_records)
|
||||
self.header = self.mf.mobi8_header
|
||||
self.extract_resources()
|
||||
self.read_fdst()
|
||||
self.read_indices()
|
||||
|
||||
def print_header(self, f=sys.stdout):
|
||||
print (str(self.mf.palmdb).encode('utf-8'), file=f)
|
||||
@ -43,6 +78,23 @@ class MOBIFile(object):
|
||||
print (file=f)
|
||||
print (str(self.mf.mobi8_header).encode('utf-8'), file=f)
|
||||
|
||||
def read_fdst(self):
|
||||
self.fdst = None
|
||||
|
||||
if self.header.fdst_idx != NULL_INDEX:
|
||||
idx = self.header.fdst_idx
|
||||
self.fdst = FDST(self.mf.records[idx].raw)
|
||||
if self.fdst.num_sections != self.header.fdst_count:
|
||||
raise ValueError('KF8 Header contains invalid FDST count')
|
||||
|
||||
def read_indices(self):
|
||||
self.skel_index = SKELIndex(self.header.skel_idx, self.mf.records,
|
||||
self.header.encoding)
|
||||
self.sect_index = SECTIndex(self.header.sect_idx, self.mf.records,
|
||||
self.header.encoding)
|
||||
self.ncx_index = NCXIndex(self.header.primary_index_record,
|
||||
self.mf.records, self.header.encoding)
|
||||
|
||||
def extract_resources(self):
|
||||
self.resource_map = []
|
||||
known_types = {b'FLIS', b'FCIS', b'SRCS',
|
||||
@ -96,7 +148,19 @@ def inspect_mobi(mobi_file, ddir):
|
||||
rec.dump(os.path.join(ddir, 'text_records'))
|
||||
|
||||
for href, payload in f.resource_map:
|
||||
with open(os.path.join(ddir, href), 'wb') as f:
|
||||
f.write(payload)
|
||||
with open(os.path.join(ddir, href), 'wb') as fo:
|
||||
fo.write(payload)
|
||||
|
||||
if f.fdst:
|
||||
with open(os.path.join(ddir, 'fdst.record'), 'wb') as fo:
|
||||
fo.write(str(f.fdst).encode('utf-8'))
|
||||
|
||||
with open(os.path.join(ddir, 'skel.record'), 'wb') as fo:
|
||||
fo.write(str(f.skel_index).encode('utf-8'))
|
||||
|
||||
with open(os.path.join(ddir, 'sect.record'), 'wb') as fo:
|
||||
fo.write(str(f.sect_index).encode('utf-8'))
|
||||
|
||||
with open(os.path.join(ddir, 'ncx.record'), 'wb') as fo:
|
||||
fo.write(str(f.ncx_index).encode('utf-8'))
|
||||
|
||||
|
@ -15,6 +15,12 @@ from calibre.ebooks.mobi.utils import (decint, count_set_bits,
|
||||
|
||||
TagX = namedtuple('TagX', 'tag num_of_values bitmask eof')
|
||||
PTagX = namedtuple('PTagX', 'tag value_count value_bytes num_of_values')
|
||||
INDEX_HEADER_FIELDS = (
|
||||
'len', 'nul1', 'type', 'gen', 'start', 'count', 'code',
|
||||
'lng', 'total', 'ordt', 'ligt', 'nligt', 'ncncx'
|
||||
) + tuple('unknown%d'%i for i in xrange(27)) + ('ocnt', 'oentries',
|
||||
'ordt1', 'ordt2', 'tagx')
|
||||
|
||||
|
||||
class InvalidFile(ValueError):
|
||||
pass
|
||||
@ -36,11 +42,7 @@ def format_bytes(byts):
|
||||
|
||||
def parse_indx_header(data):
|
||||
check_signature(data, b'INDX')
|
||||
words = (
|
||||
'len', 'nul1', 'type', 'gen', 'start', 'count', 'code',
|
||||
'lng', 'total', 'ordt', 'ligt', 'nligt', 'ncncx'
|
||||
) + tuple('unknown%d'%i for i in xrange(27)) + ('ocnt', 'oentries',
|
||||
'ordt1', 'ordt2', 'tagx')
|
||||
words = INDEX_HEADER_FIELDS
|
||||
num = len(words)
|
||||
values = struct.unpack(bytes('>%dL' % num), data[4:4*(num+1)])
|
||||
ans = dict(zip(words, values))
|
||||
@ -109,6 +111,12 @@ class CNCX(object): # {{{
|
||||
|
||||
def get(self, offset, default=None):
|
||||
return self.records.get(offset, default)
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self.records)
|
||||
|
||||
def iteritems(self):
|
||||
return self.records.iteritems()
|
||||
# }}}
|
||||
|
||||
def parse_tagx_section(data):
|
||||
|
@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
import struct, re, os, imghdr
|
||||
from collections import namedtuple
|
||||
from itertools import repeat
|
||||
from itertools import repeat, izip
|
||||
from urlparse import urldefrag
|
||||
|
||||
from lxml import etree
|
||||
@ -71,16 +71,16 @@ class Mobi8Reader(object):
|
||||
return self.write_opf(guide, ncx, spine, resource_map)
|
||||
|
||||
def read_indices(self):
|
||||
self.flow_table = (0, NULL_INDEX)
|
||||
self.flow_table = ()
|
||||
|
||||
if self.header.fdstidx != NULL_INDEX:
|
||||
header = self.kf8_sections[self.header.fdstidx][0]
|
||||
if header[:4] != b'FDST':
|
||||
raise ValueError('KF8 does not have a valid FDST record')
|
||||
num_sections, = struct.unpack_from(b'>L', header, 0x08)
|
||||
sections = header[0x0c:]
|
||||
self.flow_table = struct.unpack_from(b'>%dL' % (num_sections*2),
|
||||
sections, 0)[::2] + (NULL_INDEX,)
|
||||
sec_start, num_sections = struct.unpack_from(b'>LL', header, 4)
|
||||
secs = struct.unpack_from(b'>%dL' % (num_sections*2),
|
||||
header, sec_start)
|
||||
self.flow_table = tuple(izip(secs[::2], secs[1::2]))
|
||||
|
||||
self.files = []
|
||||
if self.header.skelidx != NULL_INDEX:
|
||||
@ -127,13 +127,10 @@ class Mobi8Reader(object):
|
||||
raw_ml = self.mobi6_reader.mobi_html
|
||||
self.flows = []
|
||||
self.flowinfo = []
|
||||
ft = self.flow_table if self.flow_table else [(0, len(raw_ml))]
|
||||
|
||||
# now split the raw_ml into its flow pieces
|
||||
for j in xrange(0, len(self.flow_table)-1):
|
||||
start = self.flow_table[j]
|
||||
end = self.flow_table[j+1]
|
||||
if end == NULL_INDEX:
|
||||
end = len(raw_ml)
|
||||
for start, end in ft:
|
||||
self.flows.append(raw_ml[start:end])
|
||||
|
||||
# the first piece represents the xhtml text
|
||||
|
11
src/calibre/ebooks/mobi/writer8/__init__.py
Normal file
11
src/calibre/ebooks/mobi/writer8/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
|
15
src/calibre/ebooks/mobi/writer8/main.py
Normal file
15
src/calibre/ebooks/mobi/writer8/main.py
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
class KF8Writer(object):
|
||||
|
||||
def __init__(self, oeb, opts):
|
||||
self.oeb, self.opts, self.log = oeb, opts, oeb.log
|
||||
|
@ -454,7 +454,14 @@ class DirContainer(object):
|
||||
path = os.path.join(self.rootdir, self._unquote(path))
|
||||
except ValueError: #Happens if path contains quoted special chars
|
||||
return False
|
||||
return os.path.isfile(path)
|
||||
try:
|
||||
return os.path.isfile(path)
|
||||
except UnicodeEncodeError:
|
||||
# On linux, if LANG is unset, the os.stat call tries to encode the
|
||||
# unicode path using ASCII
|
||||
# To replicate try:
|
||||
# LANG=en_US.ASCII python -c "import os; os.stat(u'Espa\xf1a')"
|
||||
return os.path.isfile(path.encode(filesystem_encoding))
|
||||
|
||||
def namelist(self):
|
||||
names = []
|
||||
|
@ -16,6 +16,7 @@ def _import_readers():
|
||||
from calibre.ebooks.pdb.ztxt.reader import Reader as ztxt_reader
|
||||
from calibre.ebooks.pdb.pdf.reader import Reader as pdf_reader
|
||||
from calibre.ebooks.pdb.plucker.reader import Reader as plucker_reader
|
||||
from calibre.ebooks.pdb.haodoo.reader import Reader as haodoo_reader
|
||||
|
||||
FORMAT_READERS = {
|
||||
'PNPdPPrs': ereader_reader,
|
||||
@ -24,6 +25,8 @@ def _import_readers():
|
||||
'TEXtREAd': palmdoc_reader,
|
||||
'.pdfADBE': pdf_reader,
|
||||
'DataPlkr': plucker_reader,
|
||||
'BOOKMTIT': haodoo_reader,
|
||||
'BOOKMTIU': haodoo_reader,
|
||||
}
|
||||
|
||||
ALL_FORMAT_WRITERS = {'doc', 'ztxt', 'ereader'}
|
||||
@ -47,6 +50,8 @@ IDENTITY_TO_NAME = {
|
||||
'TEXtREAd': 'PalmDOC',
|
||||
'.pdfADBE': 'Adobe Reader',
|
||||
'DataPlkr': 'Plucker',
|
||||
'BOOKMTIT': 'Haodoo.net',
|
||||
'BOOKMTIU': 'Haodoo.net',
|
||||
|
||||
'BVokBDIC': 'BDicty',
|
||||
'DB99DBOS': 'DB (Database program)',
|
||||
|
151
src/calibre/ebooks/pdb/haodoo/reader.py
Normal file
151
src/calibre/ebooks/pdb/haodoo/reader.py
Normal file
@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Read content from Haodoo.net pdb file.
|
||||
'''
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2012, Kan-Ru Chen <kanru@kanru.info>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
import struct
|
||||
import os
|
||||
|
||||
from calibre import prepare_string_for_xml
|
||||
from calibre.ebooks.pdb.formatreader import FormatReader
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ebooks.txt.processor import opf_writer, HTML_TEMPLATE
|
||||
|
||||
BPDB_IDENT = b'BOOKMTIT'
|
||||
UPDB_IDENT = b'BOOKMTIU'
|
||||
|
||||
punct_table = {
|
||||
u"︵": u"(",
|
||||
u"︶": u")",
|
||||
u"︷": u"{",
|
||||
u"︸": u"}",
|
||||
u"︹": u"〔",
|
||||
u"︺": u"〕",
|
||||
u"︻": u"【",
|
||||
u"︼": u"】",
|
||||
u"︗": u"〖",
|
||||
u"︘": u"〗",
|
||||
u"﹇": u"[]",
|
||||
u"﹈": u"[]",
|
||||
u"︽": u"《",
|
||||
u"︾": u"》",
|
||||
u"︿": u"〈",
|
||||
u"﹀": u"〉",
|
||||
u"﹁": u"「",
|
||||
u"﹂": u"」",
|
||||
u"﹃": u"『",
|
||||
u"﹄": u"』",
|
||||
u"|": u"—",
|
||||
u"︙": u"…",
|
||||
u"ⸯ": u"~",
|
||||
u"│": u"…",
|
||||
u"¦": u"…",
|
||||
u" ": u" ",
|
||||
}
|
||||
|
||||
def fix_punct(line):
|
||||
for (key, value) in punct_table.items():
|
||||
line = line.replace(key, value)
|
||||
return line
|
||||
|
||||
class LegacyHeaderRecord(object):
|
||||
|
||||
def __init__(self, raw):
|
||||
fields = raw.lstrip().replace(b'\x1b\x1b\x1b', b'\x1b').split(b'\x1b')
|
||||
self.title = fix_punct(fields[0].decode('cp950', 'replace'))
|
||||
self.num_records = int(fields[1])
|
||||
self.chapter_titles = map(
|
||||
lambda x: fix_punct(x.decode('cp950', 'replace').rstrip(b'\x00')),
|
||||
fields[2:])
|
||||
|
||||
class UnicodeHeaderRecord(object):
|
||||
|
||||
def __init__(self, raw):
|
||||
fields = raw.lstrip().replace(b'\x1b\x00\x1b\x00\x1b\x00',
|
||||
b'\x1b\x00').split(b'\x1b\x00')
|
||||
self.title = fix_punct(fields[0].decode('utf_16_le', 'ignore'))
|
||||
self.num_records = int(fields[1])
|
||||
self.chapter_titles = map(
|
||||
lambda x: fix_punct(x.decode('utf_16_le', 'replace').rstrip(b'\x00')),
|
||||
fields[2].split(b'\r\x00\n\x00'))
|
||||
|
||||
class Reader(FormatReader):
|
||||
|
||||
def __init__(self, header, stream, log, options):
|
||||
self.stream = stream
|
||||
self.log = log
|
||||
|
||||
self.sections = []
|
||||
for i in range(header.num_sections):
|
||||
self.sections.append(header.section_data(i))
|
||||
|
||||
if header.ident == BPDB_IDENT:
|
||||
self.header_record = LegacyHeaderRecord(self.section_data(0))
|
||||
self.encoding = 'cp950'
|
||||
else:
|
||||
self.header_record = UnicodeHeaderRecord(self.section_data(0))
|
||||
self.encoding = 'utf_16_le'
|
||||
|
||||
def author(self):
|
||||
self.stream.seek(35)
|
||||
version = struct.unpack(b'>b', self.stream.read(1))[0]
|
||||
if version == 2:
|
||||
self.stream.seek(0)
|
||||
author = self.stream.read(35).rstrip(b'\x00').decode(self.encoding, 'replace')
|
||||
return author
|
||||
else:
|
||||
return u'Unknown'
|
||||
|
||||
def get_metadata(self):
|
||||
mi = MetaInformation(self.header_record.title,
|
||||
[self.author()])
|
||||
mi.language = u'zh-tw'
|
||||
|
||||
return mi
|
||||
|
||||
def section_data(self, number):
|
||||
return self.sections[number]
|
||||
|
||||
def decompress_text(self, number):
|
||||
return self.section_data(number).decode(self.encoding,
|
||||
'replace').rstrip(b'\x00')
|
||||
|
||||
def extract_content(self, output_dir):
|
||||
txt = u''
|
||||
|
||||
self.log.info(u'Decompressing text...')
|
||||
for i in range(1, self.header_record.num_records + 1):
|
||||
self.log.debug(u'\tDecompressing text section %i' % i)
|
||||
title = self.header_record.chapter_titles[i-1]
|
||||
lines = []
|
||||
title_added = False
|
||||
for line in self.decompress_text(i).splitlines():
|
||||
line = fix_punct(line)
|
||||
line = line.strip()
|
||||
if not title_added and title in line:
|
||||
line = u'<h1 class="chapter">' + line + u'</h1>\n'
|
||||
title_added = True
|
||||
else:
|
||||
line = prepare_string_for_xml(line)
|
||||
lines.append(u'<p>%s</p>' % line)
|
||||
if not title_added:
|
||||
lines.insert(0, u'<h1 class="chapter">' + title + u'</h1>\n')
|
||||
txt += u'\n'.join(lines)
|
||||
|
||||
self.log.info(u'Converting text to OEB...')
|
||||
html = HTML_TEMPLATE % (self.header_record.title, txt)
|
||||
with open(os.path.join(output_dir, u'index.html'), 'wb') as index:
|
||||
index.write(html.encode('utf-8'))
|
||||
|
||||
mi = self.get_metadata()
|
||||
manifest = [(u'index.html', None)]
|
||||
spine = [u'index.html']
|
||||
opf_writer(output_dir, u'metadata.opf', manifest, spine, mi)
|
||||
|
||||
return os.path.join(output_dir, u'metadata.opf')
|
@ -249,6 +249,7 @@ class Widget(QWidget):
|
||||
|
||||
|
||||
def set_value_handler(self, g, val):
|
||||
'Return True iff you handle setting the value for g'
|
||||
return False
|
||||
|
||||
def post_set_value(self, g, val):
|
||||
|
@ -6,7 +6,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import sys, cPickle, shutil, importlib
|
||||
import cPickle, shutil, importlib
|
||||
|
||||
from PyQt4.Qt import QString, SIGNAL, QAbstractListModel, Qt, QVariant, QFont
|
||||
|
||||
@ -36,17 +36,14 @@ class NoSupportedInputFormats(Exception):
|
||||
pass
|
||||
|
||||
def sort_formats_by_preference(formats, prefs):
|
||||
def fcmp(x, y):
|
||||
uprefs = [x.upper() for x in prefs]
|
||||
def key(x):
|
||||
try:
|
||||
x = prefs.index(x.upper())
|
||||
return uprefs.index(x.upper())
|
||||
except ValueError:
|
||||
x = sys.maxint
|
||||
try:
|
||||
y = prefs.index(y.upper())
|
||||
except ValueError:
|
||||
y = sys.maxint
|
||||
return cmp(x, y)
|
||||
return sorted(formats, cmp=fcmp)
|
||||
pass
|
||||
return len(prefs)
|
||||
return sorted(formats, key=key)
|
||||
|
||||
class GroupModel(QAbstractListModel):
|
||||
|
||||
@ -94,8 +91,19 @@ def get_supported_input_formats_for_book(db, book_id):
|
||||
|
||||
|
||||
def get_input_format_for_book(db, book_id, pref):
|
||||
'''
|
||||
Return (preferred input format, list of available formats) for the book
|
||||
identified by book_id. Raises an error if the book has no input formats.
|
||||
|
||||
:param pref: If None, the format used as input for the last conversion, if
|
||||
any, on this book is used. If not None, should be a lowercase format like
|
||||
'epub' or 'mobi'. If you do not want the last converted format to be used,
|
||||
set pref=False.
|
||||
'''
|
||||
if pref is None:
|
||||
pref = get_preferred_input_format_for_book(db, book_id)
|
||||
if hasattr(pref, 'lower'):
|
||||
pref = pref.lower()
|
||||
input_formats = get_supported_input_formats_for_book(db, book_id)
|
||||
input_format = pref if pref in input_formats else \
|
||||
sort_formats_by_preference(input_formats, prefs['input_format_order'])[0]
|
||||
|
@ -177,7 +177,7 @@ class EditAuthorsDialog(QDialog, Ui_EditAuthorsDialog):
|
||||
ca.triggered.connect(self.paste_from_clipboard)
|
||||
m.addSeparator()
|
||||
|
||||
if self.context_item.column() == 0:
|
||||
if self.context_item is not None and self.context_item.column() == 0:
|
||||
ca = m.addAction(_('Copy to author sort'))
|
||||
ca.triggered.connect(self.copy_au_to_aus)
|
||||
else:
|
||||
|
@ -173,7 +173,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
self.box_last_values['tags_box'] = tags
|
||||
tags = [t.strip() for t in tags.split(',') if t.strip()]
|
||||
if tags:
|
||||
tags = ['tags:"=' + t + '"' for t in tags]
|
||||
tags = ['tags:"' + self.mc + t + '"' for t in tags]
|
||||
ans.append('(' + ' or '.join(tags) + ')')
|
||||
general = unicode(self.general_box.text())
|
||||
self.box_last_values['general_box'] = general
|
||||
|
@ -108,6 +108,9 @@ class UserProfiles(ResizableDialog, Ui_Dialog):
|
||||
|
||||
def show_recipe_files(self, *args):
|
||||
bdir = os.path.dirname(custom_recipes.file_path)
|
||||
if not os.path.exists(bdir):
|
||||
return error_dialog(self, _('No recipes'),
|
||||
_('No custom recipes created.'), show=True)
|
||||
open_local_file(bdir)
|
||||
|
||||
def break_cycles(self):
|
||||
|
@ -232,8 +232,8 @@ def download(all_ids, tf, db, do_identify, covers, ensure_fields,
|
||||
metadata.iteritems()}
|
||||
try:
|
||||
ret = fork_job('calibre.ebooks.metadata.sources.worker', 'main',
|
||||
(do_identify, covers, metadata, ensure_fields),
|
||||
cwd=tdir, abort=abort, heartbeat=heartbeat, no_output=True)
|
||||
(do_identify, covers, metadata, ensure_fields, tdir),
|
||||
abort=abort, heartbeat=heartbeat, no_output=True)
|
||||
except WorkerError as e:
|
||||
if e.orig_tb:
|
||||
raise Exception('Failed to download metadata. Original '
|
||||
|
@ -573,8 +573,9 @@ class CoverWorker(Thread): # {{{
|
||||
try:
|
||||
res = fork_job('calibre.ebooks.metadata.sources.worker',
|
||||
'single_covers',
|
||||
(self.title, self.authors, self.identifiers, self.caches),
|
||||
cwd=tdir, no_output=True, abort=self.abort)
|
||||
(self.title, self.authors, self.identifiers, self.caches,
|
||||
tdir),
|
||||
no_output=True, abort=self.abort)
|
||||
self.log.append_dump(res['result'])
|
||||
finally:
|
||||
self.keep_going = False
|
||||
|
@ -89,6 +89,7 @@ class NPWebView(QWebView):
|
||||
os.path.join(home, filename),
|
||||
'*.*')
|
||||
if name:
|
||||
name = unicode(name)
|
||||
self.gui.download_ebook(url, cf, name, name, False)
|
||||
else:
|
||||
self.gui.download_ebook(url, cf, filename, tags=self.tags)
|
||||
|
@ -488,11 +488,11 @@ class TagsView(QTreeView): # {{{
|
||||
partial(self.context_menu_handler, action='defaults'))
|
||||
|
||||
m = self.context_menu.addMenu(_('Change sub-categorization scheme'))
|
||||
da = m.addAction('Disable',
|
||||
da = m.addAction(_('Disable'),
|
||||
partial(self.context_menu_handler, action='categorization', category='disable'))
|
||||
fla = m.addAction('By first letter',
|
||||
fla = m.addAction(_('By first letter'),
|
||||
partial(self.context_menu_handler, action='categorization', category='first letter'))
|
||||
pa = m.addAction('Partition',
|
||||
pa = m.addAction(_('Partition'),
|
||||
partial(self.context_menu_handler, action='categorization', category='partition'))
|
||||
if self.collapse_model == 'disable':
|
||||
da.setCheckable(True)
|
||||
|
@ -887,8 +887,8 @@ class Wizard(QWizard):
|
||||
for pid in self.pageIds():
|
||||
page = self.page(pid)
|
||||
page.retranslateUi(page)
|
||||
self.set_finish_text()
|
||||
self.set_button_texts()
|
||||
self.set_finish_text()
|
||||
|
||||
def accept(self):
|
||||
pages = map(self.page, self.visitedPages())
|
||||
|
@ -3289,7 +3289,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
paths = list(duplicate[0] for duplicate in duplicates)
|
||||
formats = list(duplicate[1] for duplicate in duplicates)
|
||||
metadata = list(duplicate[2] for duplicate in duplicates)
|
||||
return (paths, formats, metadata), len(ids)
|
||||
return (paths, formats, metadata), (ids if return_ids else
|
||||
len(ids))
|
||||
return None, (ids if return_ids else len(ids))
|
||||
|
||||
def import_book(self, mi, formats, notify=True, import_hooks=True,
|
||||
|
@ -276,6 +276,7 @@ Once the download is complete, you can look at the downloaded :term:`HTML` by op
|
||||
|
||||
If you're satisfied with your recipe, and you feel there is enough demand to justify its inclusion into the set of built-in recipes, post your recipe in the `calibre recipes forum <http://www.mobileread.com/forums/forumdisplay.php?f=228>`_ to share it with other calibre users.
|
||||
|
||||
.. note:: On OS X, the ebook-convert command will not be available by default. Go to Preferences->Miscellaneous and click the install command line tools button to make it available.
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,9 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: calibre 0.8.46\n"
|
||||
"POT-Creation-Date: 2012-04-06 09:06+IST\n"
|
||||
"PO-Revision-Date: 2012-04-06 09:06+IST\n"
|
||||
"Project-Id-Version: calibre 0.8.47\n"
|
||||
"POT-Creation-Date: 2012-04-13 09:24+IST\n"
|
||||
"PO-Revision-Date: 2012-04-13 09:24+IST\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: LANGUAGE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -24,8 +24,8 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/db/cache.py:106
|
||||
#: /home/kovid/work/calibre/src/calibre/db/cache.py:109
|
||||
#: /home/kovid/work/calibre/src/calibre/db/cache.py:120
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:316
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:317
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:318
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:100
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:101
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:74
|
||||
@ -38,7 +38,7 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/prs505/sony_cache.py:661
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/prst1/driver.py:337
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/prst1/driver.py:338
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:489
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:493
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/chm/metadata.py:57
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/chm_input.py:109
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/chm_input.py:112
|
||||
@ -78,7 +78,7 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:472
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1134
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1245
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdb.py:41
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdb.py:44
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pdf.py:29
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/plucker.py:25
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/pml.py:23
|
||||
@ -109,7 +109,7 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer2/indexer.py:497
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:168
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:170
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:829
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:836
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/parse_utils.py:353
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/parse_utils.py:356
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/parse_utils.py:360
|
||||
@ -183,9 +183,9 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:580
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2087
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2241
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3302
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3304
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3441
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3303
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3305
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3442
|
||||
#: /home/kovid/work/calibre/src/calibre/library/server/content.py:250
|
||||
#: /home/kovid/work/calibre/src/calibre/library/server/content.py:251
|
||||
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:245
|
||||
@ -894,15 +894,15 @@ msgstr ""
|
||||
msgid "Communicate with Android phones."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:166
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:167
|
||||
msgid "Comma separated list of directories to send e-books to on the device. The first one that exists will be used"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:256
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:257
|
||||
msgid "Communicate with S60 phones."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:275
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:276
|
||||
msgid "Communicate with WebOS tablets."
|
||||
msgstr ""
|
||||
|
||||
@ -1654,23 +1654,23 @@ msgstr ""
|
||||
msgid "Communicate with the Pico reader."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:68
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:69
|
||||
msgid "Communicate with the iPapyrus reader."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:79
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:80
|
||||
msgid "Communicate with the Sovos reader."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:89
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:90
|
||||
msgid "Communicate with the Sunstech EB700 reader."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:100
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:101
|
||||
msgid "Communicate with the Stash W950 reader."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:112
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:113
|
||||
msgid "Communicate with the Wexler reader."
|
||||
msgstr ""
|
||||
|
||||
@ -3200,53 +3200,53 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1434
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1237
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1244
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:937
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/store/search/models.py:41
|
||||
msgid "Cover"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:488
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:491
|
||||
msgid "Downloads metadata and covers from Amazon"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:498
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:501
|
||||
msgid "US"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:499
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:502
|
||||
msgid "France"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:500
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:503
|
||||
msgid "Germany"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:501
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:504
|
||||
msgid "UK"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:502
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:505
|
||||
msgid "Italy"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:503
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:506
|
||||
msgid "Japan"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:504
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:507
|
||||
msgid "Spain"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:508
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:511
|
||||
msgid "Amazon website to use:"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:509
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:512
|
||||
msgid "Metadata from Amazon will be fetched using this country's Amazon website."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:741
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:744
|
||||
msgid "Amazon timed out. Try again later."
|
||||
msgstr ""
|
||||
|
||||
@ -3310,70 +3310,70 @@ msgstr ""
|
||||
msgid "No details available"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1238
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1245
|
||||
msgid "Title Page"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1239
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1246
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/htmltoc.py:15
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:57
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:199
|
||||
msgid "Table of Contents"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1240
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1247
|
||||
msgid "Index"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1241
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1248
|
||||
msgid "Glossary"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1242
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1249
|
||||
msgid "Acknowledgements"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1243
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1250
|
||||
msgid "Bibliography"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1244
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1251
|
||||
msgid "Colophon"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1245
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1252
|
||||
msgid "Copyright"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1246
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1253
|
||||
msgid "Dedication"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1247
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1254
|
||||
msgid "Epigraph"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1248
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1255
|
||||
msgid "Foreword"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1249
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1256
|
||||
msgid "List of Illustrations"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1250
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1257
|
||||
msgid "List of Tables"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1251
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1258
|
||||
msgid "Notes"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1252
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1259
|
||||
msgid "Preface"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1253
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1260
|
||||
msgid "Main Text"
|
||||
msgstr ""
|
||||
|
||||
@ -4010,6 +4010,10 @@ msgstr ""
|
||||
msgid "Generating %s catalog..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/catalog.py:71
|
||||
msgid "Catalog generation complete, with warnings."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/catalog.py:86
|
||||
msgid "Catalog generated."
|
||||
msgstr ""
|
||||
@ -4128,7 +4132,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:283
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/plugin_updater.py:726
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:201
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:204
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:308
|
||||
msgid "Already exists"
|
||||
msgstr ""
|
||||
@ -4343,7 +4347,7 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/plugin_updater.py:674
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:93
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_list_editor.py:216
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:371
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:374
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/jobs.py:597
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/jobs.py:607
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:102
|
||||
@ -4590,7 +4594,7 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:101
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dnd.py:84
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:507
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:817
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:818
|
||||
msgid "Download failed"
|
||||
msgstr ""
|
||||
|
||||
@ -4618,7 +4622,7 @@ msgid "Download complete"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:121
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:879
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:880
|
||||
msgid "Download log"
|
||||
msgstr ""
|
||||
|
||||
@ -5835,7 +5839,7 @@ msgid "Bulk Convert"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/bulk.py:96
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:189
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:197
|
||||
msgid "Options specific to the output format."
|
||||
msgstr ""
|
||||
|
||||
@ -6741,11 +6745,11 @@ msgstr ""
|
||||
msgid "<p>Search and replace uses <i>regular expressions</i>. See the <a href=\"http://manual.calibre-ebook.com/regexp.html\">regular expressions tutorial</a> to get started with regular expressions. Also clicking the wizard buttons below will allow you to test your regular expression against the current input document."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:173
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:181
|
||||
msgid "Convert"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:200
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:208
|
||||
msgid "Options specific to the input format."
|
||||
msgstr ""
|
||||
|
||||
@ -8107,15 +8111,15 @@ msgid "Copied"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:141
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:872
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:873
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:205
|
||||
msgid "Copy to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:189
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:244
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:936
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:1042
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:937
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:1043
|
||||
msgid "View log"
|
||||
msgstr ""
|
||||
|
||||
@ -9708,90 +9712,98 @@ msgstr ""
|
||||
msgid "&Preview {0}"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:141
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:112
|
||||
msgid "No recipes"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:113
|
||||
msgid "No custom recipes created."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:144
|
||||
msgid "No recipe selected"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:146
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:149
|
||||
#, python-format
|
||||
msgid "The attached file: %(fname)s is a recipe to download %(title)s."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:149
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:152
|
||||
msgid "Recipe for "
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:166
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:177
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:169
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:180
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:265
|
||||
msgid "Switch to Advanced mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:172
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:180
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:175
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:183
|
||||
msgid "Switch to Basic mode"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:190
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:193
|
||||
msgid "Feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:191
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:194
|
||||
msgid "The feed must have a title"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:195
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:198
|
||||
msgid "Feed must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:196
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:199
|
||||
#, python-format
|
||||
msgid "The feed %s must have a URL"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:202
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:205
|
||||
msgid "This feed has already been added to the recipe"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:244
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:253
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:340
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:247
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:256
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:343
|
||||
msgid "Invalid input"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:245
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:254
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:341
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:248
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:257
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:344
|
||||
#, python-format
|
||||
msgid "<p>Could not create recipe. Error:<br>%s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:258
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:317
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:344
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:261
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:320
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:347
|
||||
msgid "Replace recipe?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:259
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:318
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:345
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:262
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:321
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:348
|
||||
#, python-format
|
||||
msgid "A custom recipe named %s already exists. Do you want to replace it?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:285
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:288
|
||||
msgid "Choose builtin recipe"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:331
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:334
|
||||
msgid "Choose a recipe file"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:332
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:335
|
||||
msgid "Recipes"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:372
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:375
|
||||
msgid "You will lose any unsaved changes. To save your changes, click the Add/Update recipe button. Continue?"
|
||||
msgstr ""
|
||||
|
||||
@ -10524,7 +10536,7 @@ msgid "Previous Page"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:133
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:933
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:934
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/store/web_store_dialog_ui.py:62
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:193
|
||||
msgid "Back"
|
||||
@ -10975,7 +10987,7 @@ msgid "Edit Metadata"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:66
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:926
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:927
|
||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:107
|
||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/templates.py:219
|
||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/templates.py:410
|
||||
@ -11143,38 +11155,38 @@ msgstr ""
|
||||
msgid "Failed to find any books that match your search. Try making the search <b>less specific</b>. For example, use only the author's last name and a single distinctive word from the title.<p>To see the full log, click Show Details."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:624
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:625
|
||||
msgid "Current cover"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:627
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:628
|
||||
msgid "Searching..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:787
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:788
|
||||
#, python-format
|
||||
msgid "Downloading covers for <b>%s</b>, please wait..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:818
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:819
|
||||
msgid "Failed to download any covers, click \"Show details\" for details."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:824
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:825
|
||||
#, python-format
|
||||
msgid "Could not find any covers for <b>%s</b>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:826
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:827
|
||||
#, python-format
|
||||
msgid "Found <b>%(num)d</b> covers of %(title)s. Pick the one you like best."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:915
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:916
|
||||
msgid "Downloading metadata..."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:1026
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:1027
|
||||
msgid "Downloading cover..."
|
||||
msgstr ""
|
||||
|
||||
@ -12114,6 +12126,7 @@ msgid "Never"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel.py:142
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/view.py:493
|
||||
msgid "By first letter"
|
||||
msgstr ""
|
||||
|
||||
@ -12925,7 +12938,7 @@ msgid ""
|
||||
"<p>If you leave the password blank, anyone will be able to\n"
|
||||
" access your book collection using the web interface.\n"
|
||||
"<br>\n"
|
||||
"<p>Some devices have browsers that do not support authentication. If you are having trouble downloading files from the content server, trying removing the password."
|
||||
"<p>Some devices have browsers that do not support authentication. If you are having trouble downloading files from the content server, try removing the password."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/server_ui.py:144
|
||||
@ -14031,6 +14044,14 @@ msgstr ""
|
||||
msgid "Change sub-categorization scheme"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/view.py:491
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/view.py:495
|
||||
msgid "Partition"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/view.py:510
|
||||
msgid "First letter is usable only when sorting by name"
|
||||
msgstr ""
|
||||
@ -15963,17 +15984,17 @@ msgstr ""
|
||||
msgid "%(tt)sAverage rating is %(rating)3.1f"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3467
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3468
|
||||
#, python-format
|
||||
msgid "<p>Migrating old database to ebook library in %s<br><center>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3496
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3497
|
||||
#, python-format
|
||||
msgid "Copying <b>%s</b>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3513
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3514
|
||||
msgid "Compacting database"
|
||||
msgstr ""
|
||||
|
||||
@ -16452,7 +16473,7 @@ msgid "When searching for text without using lookup prefixes, as for example, Re
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/config_base.py:420
|
||||
msgid "Choose columns to be searched when not using prefixes, as for example, when searching for Redd instead of title:Red. Enter a list of search/lookup names separated by commas. Only takes effect if you set the option to limit search columns above."
|
||||
msgid "Choose columns to be searched when not using prefixes, as for example, when searching for Red instead of title:Red. Enter a list of search/lookup names separated by commas. Only takes effect if you set the option to limit search columns above."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/formatter.py:31
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user