mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
5626f4a6ab
BIN
resources/images/news/biggovernment.png
Normal file
BIN
resources/images/news/biggovernment.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/images/news/eluniversal.png
Normal file
BIN
resources/images/news/eluniversal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 878 B |
BIN
resources/images/news/propublica.png
Normal file
BIN
resources/images/news/propublica.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
27
resources/recipes/biggovernment.recipe
Normal file
27
resources/recipes/biggovernment.recipe
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class BigGovernmentRecipe(BasicNewsRecipe):
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'kwetal'
|
||||||
|
language = 'en_US'
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
title = u'Big Government'
|
||||||
|
publisher = u'Andrew Breitbart'
|
||||||
|
category = u'Political blog'
|
||||||
|
description = u'Political news from the USA'
|
||||||
|
|
||||||
|
oldest_article = 30
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
use_embedded_content = True
|
||||||
|
|
||||||
|
feeds = [(u'Big Government', u'http://feeds.feedburner.com/BigGovernment')]
|
||||||
|
|
||||||
|
conversion_options = {'comments': description, 'tags': category, 'language': 'en',
|
||||||
|
'publisher': publisher}
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
body{font-family:verdana,arial,helvetica,geneva,sans-serif;}
|
||||||
|
img {float: left; margin-right: 0.5em;}
|
||||||
|
'''
|
||||||
|
|
108
resources/recipes/cyprus_weekly.recipe
Normal file
108
resources/recipes/cyprus_weekly.recipe
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
class CyNewsLiveRecipe(BasicNewsRecipe):
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'kwetal'
|
||||||
|
language = 'en_CY'
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
title = u'Cyprus Weekly'
|
||||||
|
publisher = u'The Cyprus Weekly'
|
||||||
|
category = u'News, Newspaper'
|
||||||
|
description = u'News from Cyprus'
|
||||||
|
|
||||||
|
use_embedded_content = False
|
||||||
|
remove_empty_feeds = True
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
pubTime = None
|
||||||
|
minTime = None
|
||||||
|
articleCount = 0
|
||||||
|
|
||||||
|
INDEX = 'http://www.cyprusweekly.com.cy/main/default.aspx'
|
||||||
|
|
||||||
|
feeds = []
|
||||||
|
feeds.append(('News: Cyprus', 'http://www.cyprusweekly.com.cy/main/92,0,0,0-CYPRUS.aspx'))
|
||||||
|
feeds.append(('News: World', 'http://www.cyprusweekly.com.cy/main/78,0,0,0-UKWORLD.aspx'))
|
||||||
|
feeds.append(('Sport: Football', 'http://www.cyprusweekly.com.cy/main/82,0,0,0-FOOTBALL.aspx'))
|
||||||
|
feeds.append(('Sport: Rugby', 'http://www.cyprusweekly.com.cy/main/83,0,0,0-RUGBY.aspx'))
|
||||||
|
feeds.append(('Sport: Cricket', 'http://www.cyprusweekly.com.cy/main/85,0,0,0-CRICKET.aspx'))
|
||||||
|
feeds.append(('Sport: Tennis', 'http://www.cyprusweekly.com.cy/main/84,0,0,0-TENNIS.aspx'))
|
||||||
|
feeds.append(('Sport: Other', 'http://www.cyprusweekly.com.cy/main/86,0,0,0-OTHER.aspx'))
|
||||||
|
feeds.append(('Business: Local', 'http://www.cyprusweekly.com.cy/main/100,0,0,0-LOCAL.aspx'))
|
||||||
|
feeds.append(('Business: Foreign', 'http://www.cyprusweekly.com.cy/main/101,0,0,0-FOREIGN.aspx'))
|
||||||
|
feeds.append(('Whats On: Places of Interest', 'http://www.cyprusweekly.com.cy/main/123,0,0,0-PLACES-OF-INTEREST.aspx'))
|
||||||
|
feeds.append(('Whats On: Going Out', 'http://www.cyprusweekly.com.cy/main/153,0,0,0-GOING-OUT.aspx'))
|
||||||
|
feeds.append(('Whats On: Arts & Entertainment', 'http://www.cyprusweekly.com.cy/main/135,0,0,0-ARTS--and-ENTERTAINMENT.aspx'))
|
||||||
|
feeds.append(('Whats On: Things To Do', 'http://www.cyprusweekly.com.cy/main/136,0,0,0-THINGS-TO-DO.aspx'))
|
||||||
|
feeds.append(('Whats On: Shopping Guide', 'http://www.cyprusweekly.com.cy/main/142,0,0,0-SHOPPING-GUIDE.aspx'))
|
||||||
|
feeds.append(('Culture', 'http://www.cyprusweekly.com.cy/main/208,0,0,0-CULTURE.aspx'))
|
||||||
|
feeds.append(('Environment', 'http://www.cyprusweekly.com.cy/main/93,0,0,0-ENVIRONMENT.aspx'))
|
||||||
|
feeds.append(('Info', 'http://www.cyprusweekly.com.cy/main/91,0,0,0-INFO.aspx'))
|
||||||
|
|
||||||
|
keep_only_tags = []
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'class': 'ArticleCategories'}))
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
body{font-family:verdana,arial,helvetica,geneva,sans-serif ;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
answer = []
|
||||||
|
for feed in self.feeds:
|
||||||
|
self.articleCount = 0
|
||||||
|
articles = []
|
||||||
|
soup = self.index_to_soup(feed[1])
|
||||||
|
|
||||||
|
table = soup.find('table', attrs = {'id': 'ctl00_cp_ctl01_listp'})
|
||||||
|
if table:
|
||||||
|
self.pubTime = datetime.now()
|
||||||
|
self.minTime = self.pubTime - timedelta(days = self.oldest_article)
|
||||||
|
|
||||||
|
self.find_articles(table, articles)
|
||||||
|
|
||||||
|
answer.append((feed[0], articles))
|
||||||
|
|
||||||
|
return answer
|
||||||
|
|
||||||
|
def postprocess_html(self, soup, first):
|
||||||
|
for el in soup.findAll(attrs = {'style': True}):
|
||||||
|
del el['style']
|
||||||
|
|
||||||
|
for el in soup.findAll('font'):
|
||||||
|
el.name = 'div'
|
||||||
|
for attr, value in el:
|
||||||
|
del el[attr]
|
||||||
|
|
||||||
|
return soup
|
||||||
|
|
||||||
|
def find_articles(self, table, articles):
|
||||||
|
for div in table.findAll('div', attrs = {'class': 'ListArticle'}):
|
||||||
|
el = div.find('div', attrs = {'class': 'ListArticle_T'})
|
||||||
|
title = self.tag_to_string(el.a)
|
||||||
|
url = self.INDEX + el.a['href']
|
||||||
|
|
||||||
|
description = self.tag_to_string(div.find('div', attrs = {'class': 'ListArticle_BODY300'}))
|
||||||
|
|
||||||
|
el = div.find('div', attrs = {'class': 'ListArticle_D'})
|
||||||
|
if el:
|
||||||
|
dateParts = self.tag_to_string(el).split(' ')
|
||||||
|
monthNames = {'January': 1, 'February': 2, 'March': 3, 'April': 4, 'May': 5, 'June': 6,
|
||||||
|
'July': 7, 'August': 8, 'September': 9, 'October': 10, 'November': 11,
|
||||||
|
'December': 12}
|
||||||
|
timeParts = dateParts[3].split(':')
|
||||||
|
self.pubTime = datetime(year = int(dateParts[2]), month = int(monthNames[dateParts[1]]),
|
||||||
|
day = int(dateParts[0]), hour = int(timeParts[0]),
|
||||||
|
minute = int(timeParts[1]))
|
||||||
|
|
||||||
|
if self.pubTime >= self.minTime and self.articleCount <= self.max_articles_per_feed:
|
||||||
|
articles.append({'title': title, 'date': self.pubTime, 'url': url, 'description': description})
|
||||||
|
self.articleCount += 1
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
57
resources/recipes/denver_post.recipe
Normal file
57
resources/recipes/denver_post.recipe
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
|
|
||||||
|
class DenverPost(BasicNewsRecipe):
|
||||||
|
title = u'Denver Post'
|
||||||
|
language = 'en'
|
||||||
|
__author__ = 'Krittika Goyal'
|
||||||
|
oldest_article = 1 #days
|
||||||
|
max_articles_per_feed = 20
|
||||||
|
|
||||||
|
conversion_options = {'linearize_tables':True}
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
#remove_tags_before = dict(name='h1', attrs={'class':'heading'})
|
||||||
|
#remove_tags_after = dict(name='td', attrs={'class':'newptool1'})
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='iframe'),
|
||||||
|
dict(name='img', src=lambda x: not x or '/tracking/' in x),
|
||||||
|
dict(name='span', attrs={'fd-id':True}),
|
||||||
|
dict(name='div', attrs={'class':['articleOptions', 'articlePosition2']}),
|
||||||
|
#dict(name='div', attrs={'id':['qrformdiv', 'inSection', 'alpha-inner']}),
|
||||||
|
#dict(name='ul', attrs={'class':'article-tools'}),
|
||||||
|
#dict(name='ul', attrs={'class':'articleTools'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('Top Stories',
|
||||||
|
'http://feeds.denverpost.com/dp-news-topstories'),
|
||||||
|
('Business',
|
||||||
|
'http://feeds.denverpost.com/dp-business'),
|
||||||
|
('Sports',
|
||||||
|
'http://feeds.denverpost.com/dp-sports'),
|
||||||
|
('Lifestyles',
|
||||||
|
'http://feeds.denverpost.com/dp-lifestyles'),
|
||||||
|
('Politics',
|
||||||
|
'http://feeds.denverpost.com/dp-politics'),
|
||||||
|
('Entertainment',
|
||||||
|
'http://feeds.denverpost.com/dp-entertainment'),
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
story = soup.find(name='td', attrs={'class':'articleBox'})
|
||||||
|
#td = heading.findParent(name='td')
|
||||||
|
#td.extract()
|
||||||
|
story.extract()
|
||||||
|
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
||||||
|
body = soup.find(name='body')
|
||||||
|
body.insert(0, story)
|
||||||
|
story.name = 'div'
|
||||||
|
|
||||||
|
for img in soup.findAll(name='img', style='visibility:hidden;'):
|
||||||
|
del img['style']
|
||||||
|
|
||||||
|
for div in soup.findAll(id='caption', style=True):
|
||||||
|
del div['style']
|
||||||
|
return soup
|
@ -13,7 +13,7 @@ class Economist(BasicNewsRecipe):
|
|||||||
description = ('Global news and current affairs from a European perspective.'
|
description = ('Global news and current affairs from a European perspective.'
|
||||||
' Much slower than the subscription based version.')
|
' Much slower than the subscription based version.')
|
||||||
|
|
||||||
oldest_article = 6.5
|
oldest_article = 7.0
|
||||||
cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg'
|
cover_url = 'http://www.economist.com/images/covers/currentcovereu_large.jpg'
|
||||||
remove_tags = [dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),
|
remove_tags = [dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),
|
||||||
dict(attrs={'class':['dblClkTrk']})]
|
dict(attrs={'class':['dblClkTrk']})]
|
||||||
@ -29,8 +29,15 @@ class Economist(BasicNewsRecipe):
|
|||||||
self.feed_dict = {}
|
self.feed_dict = {}
|
||||||
requests = []
|
requests = []
|
||||||
for i, item in enumerate(entries):
|
for i, item in enumerate(entries):
|
||||||
published = time.gmtime(item.get('timestamp', time.time()))
|
|
||||||
title = item.get('title', _('Untitled article'))
|
title = item.get('title', _('Untitled article'))
|
||||||
|
published = item.date_parsed
|
||||||
|
if not published:
|
||||||
|
published = time.gmtime()
|
||||||
|
utctime = datetime(*published[:6])
|
||||||
|
delta = datetime.utcnow() - utctime
|
||||||
|
if delta.days*24*3600 + delta.seconds > 24*3600*self.oldest_article:
|
||||||
|
self.log.debug('Skipping article %s as it is too old.'%title)
|
||||||
|
continue
|
||||||
link = item.get('link', None)
|
link = item.get('link', None)
|
||||||
description = item.get('description', '')
|
description = item.get('description', '')
|
||||||
author = item.get('author', '')
|
author = item.get('author', '')
|
||||||
@ -64,11 +71,6 @@ class Economist(BasicNewsRecipe):
|
|||||||
self.log('Found print version for article:', title)
|
self.log('Found print version for article:', title)
|
||||||
|
|
||||||
a = Article(i, title, link, author, description, published, '')
|
a = Article(i, title, link, author, description, published, '')
|
||||||
delta = datetime.utcnow() - a.utctime
|
|
||||||
if delta.days*24*3600 + delta.seconds > 24*3600*self.oldest_article:
|
|
||||||
self.log.debug('Skipping article %s (%s) from feed %s as it is too old.'%(title, a.localtime.strftime('%a, %d %b, %Y %H:%M'), title))
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
article = dict(title=a.title, description=a.text_summary,
|
article = dict(title=a.title, description=a.text_summary,
|
||||||
date=time.strftime(self.timefmt, a.date), author=a.author, url=a.url)
|
date=time.strftime(self.timefmt, a.date), author=a.author, url=a.url)
|
||||||
|
82
resources/recipes/eluniversalimpresa.recipe
Normal file
82
resources/recipes/eluniversalimpresa.recipe
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class ElUniversalImpresaRecipe(BasicNewsRecipe):
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'kwetal'
|
||||||
|
language = 'es'
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
title = u'El Universal (Edici\u00F3n Impresa)'
|
||||||
|
publisher = u'El Universal'
|
||||||
|
category = u'News, Mexico'
|
||||||
|
description = u'News from Mexico'
|
||||||
|
|
||||||
|
remove_empty_feeds = True
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
INDEX = 'http://www.eluniversal.com.mx'
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
body{font-family:verdana,arial,helvetica,geneva,sans-serif;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
conversion_options = {'comments': description, 'tags': category, 'language': 'en',
|
||||||
|
'publisher': publisher, 'linearize_tables': True}
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
soup = self.index_to_soup('http://www.eluniversal.com.mx/edicion_impresa.html')
|
||||||
|
index = []
|
||||||
|
|
||||||
|
table = soup.find('table', attrs = {'width': '500'})
|
||||||
|
articles = []
|
||||||
|
for td in table.findAll(lambda tag: tag.name == 'td' and tag.has_key('class') and tag['class'] == 'arnegro12'):
|
||||||
|
a = td.a
|
||||||
|
a.extract()
|
||||||
|
title = self.tag_to_string(a)
|
||||||
|
url = self.INDEX + a['href']
|
||||||
|
description = self.tag_to_string(td)
|
||||||
|
articles.append({'title': title, 'date': None, 'url': url, 'description' : description})
|
||||||
|
|
||||||
|
index.append(('Primera Plana', articles))
|
||||||
|
|
||||||
|
for td in table.findAll(lambda tag: tag.name == 'td' and len(tag.attrs) == 0):
|
||||||
|
articles = []
|
||||||
|
feedTitle = None
|
||||||
|
for a in td.findAll('a'):
|
||||||
|
if not feedTitle:
|
||||||
|
feedTitle = self.tag_to_string(a)
|
||||||
|
continue
|
||||||
|
|
||||||
|
title = self.tag_to_string(a)
|
||||||
|
|
||||||
|
url = self.INDEX + a['href']
|
||||||
|
articles.append({'title': title, 'date': None, 'url': url, 'description': ''})
|
||||||
|
|
||||||
|
index.append((feedTitle, articles))
|
||||||
|
|
||||||
|
return index
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
if url.find('wcarton') >= 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
main, sep, id = url.rpartition('/')
|
||||||
|
|
||||||
|
return main + '/vi_' + id
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
table = soup.find('table')
|
||||||
|
table.extract()
|
||||||
|
|
||||||
|
for p in soup.findAll('p'):
|
||||||
|
if self.tag_to_string(p).strip() == '':
|
||||||
|
p.extract()
|
||||||
|
|
||||||
|
tag = soup.find('font', attrs = {'color': '#0F046A'})
|
||||||
|
if tag:
|
||||||
|
for attr in ['color', 'face', 'helvetica,', 'sans-serif', 'size']:
|
||||||
|
if tag.has_key(attr):
|
||||||
|
del tag[attr]
|
||||||
|
tag.name = 'h1'
|
||||||
|
|
||||||
|
return soup
|
@ -1,26 +1,55 @@
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
|
|
||||||
class Independent(BasicNewsRecipe):
|
class TheIndependent(BasicNewsRecipe):
|
||||||
title = u'The Independent'
|
title = u'The Independent'
|
||||||
oldest_article = 1
|
language = 'en_UK'
|
||||||
language = 'en_GB'
|
__author__ = 'Krittika Goyal'
|
||||||
__author__ = 'Jimmy Patrick'
|
oldest_article = 1 #days
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 25
|
||||||
|
encoding = 'latin1'
|
||||||
|
|
||||||
feeds = [(u'UK', u'http://www.independent.co.uk/news/uk/rss'),
|
no_stylesheets = True
|
||||||
(u'World', u'http://www.independent.co.uk/news/world/rss'),
|
#remove_tags_before = dict(name='h1', attrs={'class':'heading'})
|
||||||
(u'Sport', u'http://www.independent.co.uk/sport/rss'),
|
#remove_tags_after = dict(name='td', attrs={'class':'newptool1'})
|
||||||
(u'Arts & Entertainment', u'http://www.independent.co.uk/arts-entertainment/rss'),
|
remove_tags = [
|
||||||
(u'Life & Style',u'http://www.independent.co.uk/life-style/fashion/news/rss'),
|
dict(name='iframe'),
|
||||||
(u'Business',u'http://www.independent.co.uk/news/business/rss'),
|
dict(name='div', attrs={'class':'related-articles'}),
|
||||||
(u'Science',u'http://www.independent.co.uk/news/science/rss'),
|
dict(name='div', attrs={'id':['qrformdiv', 'inSection', 'alpha-inner']}),
|
||||||
(u'Media',u'http://www.independent.co.uk/news/media/rss')
|
|
||||||
]
|
|
||||||
|
|
||||||
keep_only_tags = [dict(id=['article'])]
|
|
||||||
remove_tags = [dict(name='div', attrs={'class':'share-links'}),
|
|
||||||
dict(name='ul', attrs={'class':'article-tools'}),
|
dict(name='ul', attrs={'class':'article-tools'}),
|
||||||
dict(name='div', attrs={'class':'related-articles'})
|
dict(name='ul', attrs={'class':'articleTools'}),
|
||||||
]
|
]
|
||||||
|
|
||||||
extra_css = "body{color:black;}"
|
feeds = [
|
||||||
|
('UK',
|
||||||
|
'http://www.independent.co.uk/news/uk/rss'),
|
||||||
|
('World',
|
||||||
|
'http://www.independent.co.uk/news/world/rss'),
|
||||||
|
('Sport',
|
||||||
|
'http://www.independent.co.uk/sport/rss'),
|
||||||
|
('Arts and Entertainment',
|
||||||
|
'http://www.independent.co.uk/arts-entertainment/rss'),
|
||||||
|
('Business',
|
||||||
|
'http://www.independent.co.uk/news/business/rss'),
|
||||||
|
('Life and Style',
|
||||||
|
'http://www.independent.co.uk/life-style/gadgets-and-tech/news/rss'),
|
||||||
|
('Science',
|
||||||
|
'http://www.independent.co.uk/news/science/rss'),
|
||||||
|
('People',
|
||||||
|
'http://www.independent.co.uk/news/people/rss'),
|
||||||
|
('Media',
|
||||||
|
'http://www.independent.co.uk/news/media/rss'),
|
||||||
|
('Health and Families',
|
||||||
|
'http://www.independent.co.uk/life-style/health-and-families/rss'),
|
||||||
|
('Obituaries',
|
||||||
|
'http://www.independent.co.uk/news/obituaries/rss'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
story = soup.find(name='div', attrs={'id':'mainColumn'})
|
||||||
|
#td = heading.findParent(name='td')
|
||||||
|
#td.extract()
|
||||||
|
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
||||||
|
body = soup.find(name='body')
|
||||||
|
body.insert(0, story)
|
||||||
|
return soup
|
||||||
|
76
resources/recipes/india_today.recipe
Normal file
76
resources/recipes/india_today.recipe
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class IndiaToday(BasicNewsRecipe):
|
||||||
|
|
||||||
|
title = 'India Today'
|
||||||
|
__author__ = 'Kovid Goyal'
|
||||||
|
language = 'en_IN'
|
||||||
|
timefmt = ' [%d %m, %Y]'
|
||||||
|
|
||||||
|
oldest_article = 700
|
||||||
|
max_articles_per_feed = 10
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
|
||||||
|
remove_tags_before = dict(id='content_story_title')
|
||||||
|
remove_tags_after = dict(id='rightblockdiv')
|
||||||
|
remove_tags = [dict(id=['rightblockdiv', 'share_links'])]
|
||||||
|
|
||||||
|
extra_css = '#content_story_title { font-size: 170%; font-weight: bold;}'
|
||||||
|
conversion_options = { 'linearize_tables': True }
|
||||||
|
|
||||||
|
def it_get_index(self):
|
||||||
|
soup = self.index_to_soup('http://indiatoday.intoday.in/site/archive')
|
||||||
|
a = soup.find('a', href=lambda x: x and 'issueId=' in x)
|
||||||
|
url = 'http://indiatoday.intoday.in/site/'+a.get('href')
|
||||||
|
img = a.find('img')
|
||||||
|
self.cover_url = img.get('src')
|
||||||
|
return self.index_to_soup(url)
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
soup = self.it_get_index()
|
||||||
|
feeds, current_section, current_articles = [], None, []
|
||||||
|
for x in soup.findAll(name=['h1', 'a']):
|
||||||
|
if x.name == 'h1':
|
||||||
|
if current_section and current_articles:
|
||||||
|
feeds.append((current_section, current_articles))
|
||||||
|
current_section = self.tag_to_string(x)
|
||||||
|
current_articles = []
|
||||||
|
self.log('\tFound section:', current_section)
|
||||||
|
elif x.name == 'a' and 'Story' in x.get('href', ''):
|
||||||
|
title = self.tag_to_string(x)
|
||||||
|
url = x.get('href')
|
||||||
|
url = url.replace(' ', '%20')
|
||||||
|
if not url.startswith('/'):
|
||||||
|
url = 'http://indiatoday.intoday.in/site/' + url
|
||||||
|
if title and url:
|
||||||
|
url += '?complete=1'
|
||||||
|
self.log('\tFound article:', title)
|
||||||
|
self.log('\t\t', url)
|
||||||
|
desc = ''
|
||||||
|
h3 = x.parent.findNextSibling('h3')
|
||||||
|
if h3 is not None:
|
||||||
|
desc = 'By ' + self.tag_to_string(h3)
|
||||||
|
h4 = h3.findNextSibling('h4')
|
||||||
|
if h4 is not None:
|
||||||
|
desc = self.tag_to_string(h4) + ' ' + desc
|
||||||
|
if desc:
|
||||||
|
self.log('\t\t', desc)
|
||||||
|
current_articles.append({'title':title, 'description':desc,
|
||||||
|
'url':url, 'date':''})
|
||||||
|
|
||||||
|
if current_section and current_articles:
|
||||||
|
feeds.append((current_section, current_articles))
|
||||||
|
|
||||||
|
return feeds
|
||||||
|
|
||||||
|
def postprocess_html(self, soup, first):
|
||||||
|
a = soup.find(text='Print')
|
||||||
|
if a is not None:
|
||||||
|
tr = a.findParent('tr')
|
||||||
|
if tr is not None:
|
||||||
|
tr.extract()
|
||||||
|
return soup
|
||||||
|
|
||||||
|
|
||||||
|
|
57
resources/recipes/indian_express.recipe
Normal file
57
resources/recipes/indian_express.recipe
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class IndianExpress(BasicNewsRecipe):
|
||||||
|
title = u'Indian Express'
|
||||||
|
language = 'en_IN'
|
||||||
|
__author__ = 'Krittika Goyal'
|
||||||
|
oldest_article = 1 #days
|
||||||
|
max_articles_per_feed = 25
|
||||||
|
encoding = 'cp1252'
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_tags_before = dict(name='div', attrs={'class':'top_head'})
|
||||||
|
#remove_tags_after = dict(name='td', attrs={'class':'newptool1'})
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='iframe'),
|
||||||
|
dict(name='div', attrs={'class':['bookmarks_div', 'comment_box', 'bookmarks_div_bot', 'box']}),
|
||||||
|
dict(name='div', attrs={'id':['footer', 'tab_innerhc', 'discussion', 'google_new']}),
|
||||||
|
dict(name='a', attrs={'class':'nobdr'}),
|
||||||
|
#dict(name='span', text=':'),
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('Front Page',
|
||||||
|
'http://syndication.indianexpress.com/rss/33/front-page.xml'),
|
||||||
|
('Markets',
|
||||||
|
'http://syndication.indianexpress.com/rss/793/markets.xml'),
|
||||||
|
('Editorials',
|
||||||
|
'http://syndication.indianexpress.com/rss/35/editorials.xml'),
|
||||||
|
('Crime',
|
||||||
|
'http://syndication.indianexpress.com/rss/801/crime-&-justice.xml'),
|
||||||
|
('Cricket',
|
||||||
|
'http://syndication.indianexpress.com/rss/777/cricket.xml'),
|
||||||
|
('Health',
|
||||||
|
'http://syndication.indianexpress.com/rss/697/health.xml'),
|
||||||
|
('Asia',
|
||||||
|
'http://syndication.indianexpress.com/rss/790/asia.xml'),
|
||||||
|
('Politics',
|
||||||
|
'http://syndication.indianexpress.com/rss/799/politics.xml'),
|
||||||
|
('Mumbai',
|
||||||
|
'http://syndication.indianexpress.com/rss/707/mumbai.xml'),
|
||||||
|
('Op-Ed',
|
||||||
|
'http://syndication.indianexpress.com/rss/36/oped.xml'),
|
||||||
|
('Economy',
|
||||||
|
'http://syndication.indianexpress.com/rss/794/economy.xml'),
|
||||||
|
('Lifestyle',
|
||||||
|
'http://syndication.indianexpress.com/rss/713/lifestyle.xml'),
|
||||||
|
('Letters to the Editor',
|
||||||
|
'http://syndication.indianexpress.com/rss/40/letters-to-editor.xml'),
|
||||||
|
('Movie Reviews',
|
||||||
|
'http://syndication.indianexpress.com/rss/665/movie-reviews.xml'),
|
||||||
|
('Bollywood',
|
||||||
|
'http://syndication.indianexpress.com/rss/887/bollywood.xml'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url+'/0'
|
||||||
|
|
45
resources/recipes/journalofaccountancy.recipe
Normal file
45
resources/recipes/journalofaccountancy.recipe
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class JournalOfAccountancyRecipe(BasicNewsRecipe):
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'kwetal'
|
||||||
|
language = 'en'
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
title = u'Journal of Accountancy'
|
||||||
|
publisher = u'AICPA'
|
||||||
|
category = u'News, Accountancy'
|
||||||
|
description = u'Publication of the American Institute of Certified Public Accountants'
|
||||||
|
|
||||||
|
use_embedded_content = False
|
||||||
|
remove_empty_feeds = True
|
||||||
|
oldest_article = 30
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
body{font-family:verdana,arial,helvetica,geneva,sans-serif;}
|
||||||
|
div#Rubricname {font-size: small; color: #666666; margin-bottom: 1em;}
|
||||||
|
div#Headline {font-size: x-large; font-weight: bold; margin-bottom: 0.6em}
|
||||||
|
div#SubHeadline {font-size: medium; font-weight: bold; margin-bottom: 1em}
|
||||||
|
div#Authorname, div#Date {font-size: x-small; color: #696969;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
conversion_options = {'comments': description, 'tags': category, 'language': 'en',
|
||||||
|
'publisher': publisher}
|
||||||
|
|
||||||
|
keep_only_tags = []
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'Rubricname'}))
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'Headline'}))
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'SubHeadline'}))
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'Authorname'}))
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'Date'}))
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'BodyContent'}))
|
||||||
|
|
||||||
|
remove_attributes = ['style']
|
||||||
|
|
||||||
|
feeds = []
|
||||||
|
feeds.append((u'Journal of Accountancy', u'http://feeds2.feedburner.com/JournalOfAccountancy'))
|
30
resources/recipes/projo.recipe
Normal file
30
resources/recipes/projo.recipe
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class ProvidenceJournal(BasicNewsRecipe):
|
||||||
|
title = u'Providence Journal'
|
||||||
|
language = 'en'
|
||||||
|
__author__ = 'Krittika Goyal'
|
||||||
|
oldest_article = 7 #days
|
||||||
|
max_articles_per_feed = 25
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_tags_before = dict(name='span', attrs={'class':'vitstorybody'})
|
||||||
|
#remove_tags_after = dict(name='td', attrs={'class':'newptool1'})
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='iframe'),
|
||||||
|
dict(name='div', attrs={'id':['storycontentright','slcgm_comments_block', 'footercontainer']}),
|
||||||
|
#dict(name='span', text=':'),
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('Red Sox',
|
||||||
|
'http://www.projo.com/newskiosk/rss/projoredsox.xml'),
|
||||||
|
('Political Scene',
|
||||||
|
'http://www.projo.com/newskiosk/rss/projopolitical.xml'),
|
||||||
|
('Rhode Island News',
|
||||||
|
'http://www.projo.com/newskiosk/rss/projolocalnews.xml'),
|
||||||
|
('Music',
|
||||||
|
'http://www.projo.com/newskiosk/rss/projomusic.xml'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
60
resources/recipes/propublica.recipe
Normal file
60
resources/recipes/propublica.recipe
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class ProPublicaRecipe(BasicNewsRecipe):
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__author__ = 'kwetal'
|
||||||
|
language = 'en_US'
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
title = u'Pro Publica'
|
||||||
|
publisher = u'ProPublica.org'
|
||||||
|
category = u'Political blog'
|
||||||
|
description = u'Independent investigative journalism in the public interest.'
|
||||||
|
|
||||||
|
oldest_article = 14
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
remove_empty_feeds = True
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
keep_only_tags = []
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'class': 'article'}))
|
||||||
|
|
||||||
|
remove_tags = []
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'id': 'rollups'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'class': 'follow_info'}))
|
||||||
|
remove_tags.append(dict(name = 'ul', attrs = {'class': 'long-tools-top'}))
|
||||||
|
remove_tags.append(dict(name = 'ul', attrs = {'id': 'share-box'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'class': 'tags'}))
|
||||||
|
remove_tags.append(dict(name = 'ul', attrs = {'class': 'long-tools'}))
|
||||||
|
remove_tags.append(dict(name = 'ul', attrs = {'id': 'share-box2'}))
|
||||||
|
remove_tags.append(dict(name = 'p', attrs = {'id': 'original-url'}))
|
||||||
|
|
||||||
|
feeds = []
|
||||||
|
feeds.append((u'Top Stories', u'http://feeds.propublica.org/propublica/main'))
|
||||||
|
feeds.append((u'Stimulus', u'http://feeds.propublica.org/propublica/watchdog/stimulus'))
|
||||||
|
feeds.append((u'Bailout', u'http://feeds.propublica.org/propublica/watchdog/bailout'))
|
||||||
|
feeds.append((u'Business', u'http://feeds.propublica.org/propublica/business-money'))
|
||||||
|
feeds.append((u'Justice', u'http://feeds.propublica.org/propublica/justice-law'))
|
||||||
|
feeds.append((u'Energy & Environment', u'http://feeds.propublica.org/propublica/energy-environment'))
|
||||||
|
feeds.append((u'Government & Politics', u'http://feeds.propublica.org/propublica/government-politics'))
|
||||||
|
feeds.append((u'Health & Science', u'http://feeds.propublica.org/propublica/health-science'))
|
||||||
|
feeds.append((u'Media & Technology', u'http://feeds.propublica.org/propublica/media-technology'))
|
||||||
|
feeds.append((u'National Security', u'http://feeds.propublica.org/propublica/national-security'))
|
||||||
|
#feeds.append((u'', u''))
|
||||||
|
|
||||||
|
conversion_options = {'comments': description, 'tags': category, 'language': 'en',
|
||||||
|
'publisher': publisher}
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
body{font-family:verdana,arial,helvetica,geneva,sans-serif;}
|
||||||
|
img {float: left; margin-right: 0.5em;}
|
||||||
|
h1 {text-align: left;}
|
||||||
|
a, a[href] {text-decoration: none; color: blue;}
|
||||||
|
div.cat {font-size: x-small; color: #666666; margin-bottom: 0.1em;}
|
||||||
|
div.info {font-size: small; color: #696969;}
|
||||||
|
'''
|
||||||
|
|
39
resources/recipes/psych.recipe
Normal file
39
resources/recipes/psych.recipe
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
|
|
||||||
|
class PsychologyToday(BasicNewsRecipe):
|
||||||
|
title = u'Psychology Today'
|
||||||
|
language = 'en'
|
||||||
|
__author__ = 'Krittika Goyal'
|
||||||
|
oldest_article = 1 #days
|
||||||
|
max_articles_per_feed = 25
|
||||||
|
#encoding = 'latin1'
|
||||||
|
|
||||||
|
remove_stylesheets = True
|
||||||
|
#remove_tags_before = dict(name='h1', attrs={'class':'heading'})
|
||||||
|
#remove_tags_after = dict(name='td', attrs={'class':'newptool1'})
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='iframe'),
|
||||||
|
dict(name='div', attrs={'class':['pt-box-title', 'pt-box-content', 'blog-entry-footer', 'item-list', 'article-sub-meta']}),
|
||||||
|
dict(name='div', attrs={'id':['block-td_search_160', 'block-cam_search_160']}),
|
||||||
|
#dict(name='ul', attrs={'class':'article-tools'}),
|
||||||
|
#dict(name='ul', attrs={'class':'articleTools'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('PSY TODAY',
|
||||||
|
'http://www.psychologytoday.com/articles/index.rss'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
story = soup.find(name='div', attrs={'id':'contentColumn'})
|
||||||
|
#td = heading.findParent(name='td')
|
||||||
|
#td.extract()
|
||||||
|
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
||||||
|
body = soup.find(name='body')
|
||||||
|
body.insert(0, story)
|
||||||
|
for x in soup.findAll(name='p', text=lambda x:x and '-->' in x):
|
||||||
|
p = x.findParent('p')
|
||||||
|
if p is not None:
|
||||||
|
p.extract()
|
||||||
|
return soup
|
15
resources/recipes/rte.recipe
Normal file
15
resources/recipes/rte.recipe
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class RTE(BasicNewsRecipe):
|
||||||
|
title = u'RTE News'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
__author__ = u'Robin Phillips'
|
||||||
|
language = 'en_GB'
|
||||||
|
|
||||||
|
remove_tags = [dict(attrs={'class':['topAd','botad','previousNextItem','headline','footerLinks','footernav']})]
|
||||||
|
|
||||||
|
feeds = [(u'News', u'http://www.rte.ie/rss/news.xml'), (u'Sport', u'http://www.rte.ie/rss/sport.xml'), (u'Soccer', u'http://www.rte.ie/rss/soccer.xml'), (u'GAA', u'http://www.rte.ie/rss/gaa.xml'), (u'Rugby', u'http://www.rte.ie/rss/rugby.xml'), (u'Racing', u'http://www.rte.ie/rss/racing.xml'), (u'Business', u'http://www.rte.ie/rss/business.xml'), (u'Entertainment', u'http://www.rte.ie/rss/entertainment.xml')]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url.replace('http://www', 'http://m')
|
54
resources/recipes/toi.recipe
Normal file
54
resources/recipes/toi.recipe
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
|
|
||||||
|
class TimesOfIndia(BasicNewsRecipe):
|
||||||
|
title = u'Times of India'
|
||||||
|
language = 'en_IN'
|
||||||
|
__author__ = 'Krittika Goyal'
|
||||||
|
oldest_article = 1 #days
|
||||||
|
max_articles_per_feed = 25
|
||||||
|
|
||||||
|
remove_stylesheets = True
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='iframe'),
|
||||||
|
dict(name='td', attrs={'class':'newptool1'}),
|
||||||
|
dict(name='div', attrs={'id':'newptool'}),
|
||||||
|
dict(name='ul', attrs={'class':'newtabcontent_tabs_new'}),
|
||||||
|
dict(name='b', text='Topics'),
|
||||||
|
dict(name='span', text=':'),
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('Top Stories',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeedstopstories.cms'),
|
||||||
|
('India',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeeds/-2128936835.cms'),
|
||||||
|
('World',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeeds/296589292.cms'),
|
||||||
|
('Mumbai',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeeds/-2128838597.cms'),
|
||||||
|
('Entertainment',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeeds/1081479906.cms'),
|
||||||
|
('Cricket',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeeds/4719161.cms'),
|
||||||
|
('Sunday TOI',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeeds/1945062111.cms'),
|
||||||
|
('Life and Style',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeeds/2886704.cms'),
|
||||||
|
('Business',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeeds/1898055.cms'),
|
||||||
|
('Mad Mad World',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeeds/2178430.cms'),
|
||||||
|
('Most Read',
|
||||||
|
'http://timesofindia.indiatimes.com/rssfeedmostread.cms')
|
||||||
|
]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
heading = soup.find(name='h1', attrs={'class':'heading'})
|
||||||
|
td = heading.findParent(name='td')
|
||||||
|
td.extract()
|
||||||
|
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
||||||
|
body = soup.find(name='body')
|
||||||
|
body.insert(0, td)
|
||||||
|
td.name = 'div'
|
||||||
|
return soup
|
@ -47,11 +47,10 @@ class CYBOOKG3(USBMS):
|
|||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
def upload_cover(self, path, filename, metadata):
|
def upload_cover(self, path, filename, metadata):
|
||||||
coverdata = metadata.get('cover', None)
|
coverdata = getattr(metadata, 'thumbnail', None)
|
||||||
if coverdata:
|
if coverdata and coverdata[2]:
|
||||||
coverdata = coverdata[2]
|
|
||||||
with open('%s_6090.t2b' % os.path.join(path, filename), 'wb') as t2bfile:
|
with open('%s_6090.t2b' % os.path.join(path, filename), 'wb') as t2bfile:
|
||||||
t2b.write_t2b(t2bfile, coverdata)
|
t2b.write_t2b(t2bfile, coverdata[2])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_handle(cls, device_info, debug=False):
|
def can_handle(cls, device_info, debug=False):
|
||||||
|
@ -267,15 +267,18 @@ class DevicePlugin(Plugin):
|
|||||||
This method should raise a L{FreeSpaceError} if there is not enough
|
This method should raise a L{FreeSpaceError} if there is not enough
|
||||||
free space on the device. The text of the FreeSpaceError must contain the
|
free space on the device. The text of the FreeSpaceError must contain the
|
||||||
word "card" if C{on_card} is not None otherwise it must contain the word "memory".
|
word "card" if C{on_card} is not None otherwise it must contain the word "memory".
|
||||||
@param files: A list of paths and/or file-like objects.
|
:files: A list of paths and/or file-like objects.
|
||||||
@param names: A list of file names that the books should have
|
:names: A list of file names that the books should have
|
||||||
once uploaded to the device. len(names) == len(files)
|
once uploaded to the device. len(names) == len(files)
|
||||||
@return: A list of 3-element tuples. The list is meant to be passed
|
:return: A list of 3-element tuples. The list is meant to be passed
|
||||||
to L{add_books_to_metadata}.
|
to L{add_books_to_metadata}.
|
||||||
@param metadata: If not None, it is a list of dictionaries. Each dictionary
|
:metadata: If not None, it is a list of :class:`MetaInformation` objects.
|
||||||
will have at least the key tags to allow the driver to choose book location
|
The idea is to use the metadata to determine where on the device to
|
||||||
based on tags. len(metadata) == len(files). If your device does not support
|
put the book. len(metadata) == len(files). Apart from the regular
|
||||||
hierarchical ebook folders, you can safely ignore this parameter.
|
cover_data, there may also be a thumbnail attribute, which should
|
||||||
|
be used in preference. The thumbnail attribute is of the form
|
||||||
|
(width, height, cover_data as jpeg). In addition the MetaInformation
|
||||||
|
objects can have a tag_order attribute.
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -285,17 +288,8 @@ class DevicePlugin(Plugin):
|
|||||||
Add locations to the booklists. This function must not communicate with
|
Add locations to the booklists. This function must not communicate with
|
||||||
the device.
|
the device.
|
||||||
@param locations: Result of a call to L{upload_books}
|
@param locations: Result of a call to L{upload_books}
|
||||||
@param metadata: List of dictionaries. Each dictionary must have the
|
@param metadata: List of MetaInformation objects, same as for
|
||||||
keys C{title}, C{authors}, C{author_sort}, C{cover}, C{tags}.
|
:method:`upload_books`.
|
||||||
The value of the C{cover}
|
|
||||||
element can be None or a three element tuple (width, height, data)
|
|
||||||
where data is the image data in JPEG format as a string. C{tags} must be
|
|
||||||
a possibly empty list of strings. C{authors} must be a string.
|
|
||||||
C{author_sort} may be None. It is upto the driver to decide whether to
|
|
||||||
use C{author_sort} or not.
|
|
||||||
The dictionary can also have an optional key "tag order" which should be
|
|
||||||
another dictionary that maps tag names to lists of book ids. The ids are
|
|
||||||
ids from the book database.
|
|
||||||
@param booklists: A tuple containing the result of calls to
|
@param booklists: A tuple containing the result of calls to
|
||||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
(L{books}(oncard=None), L{books}(oncard='carda'),
|
||||||
L{books}(oncard='cardb')).
|
L{books}(oncard='cardb')).
|
||||||
|
@ -11,10 +11,8 @@ Device driver for Ectaco Jetbook firmware >= JL04_v030e
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from itertools import cycle
|
|
||||||
|
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
from calibre.utils.filenames import ascii_filename as sanitize
|
|
||||||
from calibre.ebooks.metadata import string_to_authors
|
from calibre.ebooks.metadata import string_to_authors
|
||||||
|
|
||||||
class JETBOOK(USBMS):
|
class JETBOOK(USBMS):
|
||||||
@ -50,34 +48,14 @@ class JETBOOK(USBMS):
|
|||||||
r'(?P<authors>.+)#(?P<title>.+)'
|
r'(?P<authors>.+)#(?P<title>.+)'
|
||||||
)
|
)
|
||||||
|
|
||||||
def upload_books(self, files, names, on_card=False, end_session=True,
|
def filename_callback(self, fname, mi):
|
||||||
metadata=None):
|
|
||||||
|
|
||||||
base_path = self._sanity_check(on_card, files)
|
|
||||||
|
|
||||||
paths = []
|
|
||||||
names = iter(names)
|
|
||||||
metadata = iter(metadata)
|
|
||||||
|
|
||||||
for i, infile in enumerate(files):
|
|
||||||
mdata, fname = metadata.next(), names.next()
|
|
||||||
path = os.path.dirname(self.create_upload_path(base_path, mdata, fname))
|
|
||||||
|
|
||||||
author = sanitize(mdata.get('authors','Unknown')).replace(' ', '_')
|
|
||||||
title = sanitize(mdata.get('title', 'Unknown')).replace(' ', '_')
|
|
||||||
fileext = os.path.splitext(os.path.basename(fname))[1]
|
fileext = os.path.splitext(os.path.basename(fname))[1]
|
||||||
fname = '%s#%s%s' % (author, title, fileext)
|
title = mi.title if mi.title else 'Unknown'
|
||||||
|
title = title.replace(' ', '_')
|
||||||
filepath = os.path.join(path, fname)
|
au = mi.format_authors()
|
||||||
paths.append(filepath)
|
if not au:
|
||||||
|
au = 'Unknown'
|
||||||
self.put_file(infile, filepath, replace_file=True)
|
return '%s#%s%s' % (au, title, fileext)
|
||||||
|
|
||||||
self.report_progress((i+1) / float(len(files)), _('Transferring books to device...'))
|
|
||||||
|
|
||||||
self.report_progress(1.0, _('Transferring books to device...'))
|
|
||||||
|
|
||||||
return zip(paths, cycle([on_card]))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def metadata_from_path(cls, path):
|
def metadata_from_path(cls, path):
|
||||||
|
@ -53,8 +53,8 @@ class NOOK(USBMS):
|
|||||||
import Image, ImageDraw
|
import Image, ImageDraw
|
||||||
|
|
||||||
|
|
||||||
coverdata = metadata.get('cover', None)
|
coverdata = getattr(metadata, 'thumbnail', None)
|
||||||
if coverdata:
|
if coverdata and coverdata[2]:
|
||||||
cover = Image.open(cStringIO.StringIO(coverdata[2]))
|
cover = Image.open(cStringIO.StringIO(coverdata[2]))
|
||||||
else:
|
else:
|
||||||
coverdata = open(I('library.png'), 'rb').read()
|
coverdata = open(I('library.png'), 'rb').read()
|
||||||
|
@ -252,7 +252,7 @@ class BookList(_BookList):
|
|||||||
return child
|
return child
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def add_book(self, info, name, size, ctime):
|
def add_book(self, mi, name, size, ctime):
|
||||||
""" Add a node into DOM tree representing a book """
|
""" Add a node into DOM tree representing a book """
|
||||||
book = self.book_by_path(name)
|
book = self.book_by_path(name)
|
||||||
if book is not None:
|
if book is not None:
|
||||||
@ -262,9 +262,9 @@ class BookList(_BookList):
|
|||||||
cid = self.max_id()+1
|
cid = self.max_id()+1
|
||||||
sourceid = str(self[0].sourceid) if len(self) else "1"
|
sourceid = str(self[0].sourceid) if len(self) else "1"
|
||||||
attrs = {
|
attrs = {
|
||||||
"title" : info["title"],
|
"title" : mi.title,
|
||||||
'titleSorter' : sortable_title(info['title']),
|
'titleSorter' : sortable_title(mi.title),
|
||||||
"author" : info["authors"] if info['authors'] else 'Unknown', \
|
"author" : mi.format_authors() if mi.format_authors() else _('Unknown'),
|
||||||
"page":"0", "part":"0", "scale":"0", \
|
"page":"0", "part":"0", "scale":"0", \
|
||||||
"sourceid":sourceid, "id":str(cid), "date":"", \
|
"sourceid":sourceid, "id":str(cid), "date":"", \
|
||||||
"mime":mime, "path":name, "size":str(size)
|
"mime":mime, "path":name, "size":str(size)
|
||||||
@ -273,7 +273,7 @@ class BookList(_BookList):
|
|||||||
node.setAttributeNode(self.document.createAttribute(attr))
|
node.setAttributeNode(self.document.createAttribute(attr))
|
||||||
node.setAttribute(attr, attrs[attr])
|
node.setAttribute(attr, attrs[attr])
|
||||||
try:
|
try:
|
||||||
w, h, data = info["cover"]
|
w, h, data = mi.thumbnail
|
||||||
except TypeError:
|
except TypeError:
|
||||||
w, h, data = None, None, None
|
w, h, data = None, None, None
|
||||||
|
|
||||||
@ -290,11 +290,15 @@ class BookList(_BookList):
|
|||||||
book.datetime = ctime
|
book.datetime = ctime
|
||||||
self.append(book)
|
self.append(book)
|
||||||
self.set_next_id(cid+1)
|
self.set_next_id(cid+1)
|
||||||
if self.prefix and info.has_key('tags'): # Playlists only supportted in main memory
|
tags = []
|
||||||
if info.has_key('tag order'):
|
if mi.tags:
|
||||||
self.tag_order.update(info['tag order'])
|
tags.extend(mi.tags)
|
||||||
self.set_playlists(book.id, info['tags'])
|
if mi.series:
|
||||||
|
tags.append(mi.series)
|
||||||
|
if self.prefix and tags: # Playlists only supportted in main memory
|
||||||
|
if hasattr(mi, 'tag_order'):
|
||||||
|
self.tag_order.update(mi.tag_order)
|
||||||
|
self.set_tags(book, tags)
|
||||||
|
|
||||||
def playlist_by_title(self, title):
|
def playlist_by_title(self, title):
|
||||||
for pl in self.playlists():
|
for pl in self.playlists():
|
||||||
|
@ -180,7 +180,7 @@ class BookList(_BookList):
|
|||||||
return child
|
return child
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def add_book(self, info, name, size, ctime):
|
def add_book(self, mi, name, size, ctime):
|
||||||
""" Add a node into the DOM tree, representing a book """
|
""" Add a node into the DOM tree, representing a book """
|
||||||
book = self.book_by_path(name)
|
book = self.book_by_path(name)
|
||||||
if book is not None:
|
if book is not None:
|
||||||
@ -194,9 +194,9 @@ class BookList(_BookList):
|
|||||||
except:
|
except:
|
||||||
sourceid = '1'
|
sourceid = '1'
|
||||||
attrs = {
|
attrs = {
|
||||||
"title" : info["title"],
|
"title" : mi.title,
|
||||||
'titleSorter' : sortable_title(info['title']),
|
'titleSorter' : sortable_title(mi.title),
|
||||||
"author" : info["authors"] if info['authors'] else _('Unknown'),
|
"author" : mi.format_authors() if mi.format_authors() else _('Unknown'),
|
||||||
"page":"0", "part":"0", "scale":"0", \
|
"page":"0", "part":"0", "scale":"0", \
|
||||||
"sourceid":sourceid, "id":str(cid), "date":"", \
|
"sourceid":sourceid, "id":str(cid), "date":"", \
|
||||||
"mime":mime, "path":name, "size":str(size)
|
"mime":mime, "path":name, "size":str(size)
|
||||||
@ -205,8 +205,8 @@ class BookList(_BookList):
|
|||||||
node.setAttributeNode(self.document.createAttribute(attr))
|
node.setAttributeNode(self.document.createAttribute(attr))
|
||||||
node.setAttribute(attr, attrs[attr])
|
node.setAttribute(attr, attrs[attr])
|
||||||
try:
|
try:
|
||||||
w, h, data = info["cover"]
|
w, h, data = mi.thumbnail
|
||||||
except TypeError:
|
except:
|
||||||
w, h, data = None, None, None
|
w, h, data = None, None, None
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
@ -221,10 +221,15 @@ class BookList(_BookList):
|
|||||||
book = Book(node, self.mountpath, [], prefix=self.prefix)
|
book = Book(node, self.mountpath, [], prefix=self.prefix)
|
||||||
book.datetime = ctime
|
book.datetime = ctime
|
||||||
self.append(book)
|
self.append(book)
|
||||||
if info.has_key('tags'):
|
tags = []
|
||||||
if info.has_key('tag order'):
|
if mi.tags:
|
||||||
self.tag_order.update(info['tag order'])
|
tags.extend(mi.tags)
|
||||||
self.set_tags(book, info['tags'])
|
if mi.series:
|
||||||
|
tags.append(mi.series)
|
||||||
|
if tags:
|
||||||
|
if hasattr(mi, 'tag_order'):
|
||||||
|
self.tag_order.update(mi.tag_order)
|
||||||
|
self.set_tags(book, tags)
|
||||||
|
|
||||||
def _delete_book(self, node):
|
def _delete_book(self, node):
|
||||||
nid = node.getAttribute('id')
|
nid = node.getAttribute('id')
|
||||||
|
@ -842,49 +842,68 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
def filename_callback(self, default, mi):
|
||||||
|
'''
|
||||||
|
Callback to allow drivers to change the default file name
|
||||||
|
set by :method:`create_upload_path`.
|
||||||
|
'''
|
||||||
|
return default
|
||||||
|
|
||||||
def create_upload_path(self, path, mdata, fname):
|
def create_upload_path(self, path, mdata, fname):
|
||||||
path = os.path.abspath(path)
|
path = os.path.abspath(path)
|
||||||
newpath = path
|
|
||||||
extra_components = []
|
extra_components = []
|
||||||
|
|
||||||
if self.SUPPORTS_SUB_DIRS and self.settings().use_subdirs:
|
special_tag = None
|
||||||
if 'tags' in mdata.keys():
|
if mdata.tags:
|
||||||
for tag in mdata['tags']:
|
for t in mdata.tags:
|
||||||
if tag.startswith(_('News')):
|
if t.startswith(_('News')) or t.startswith('/'):
|
||||||
extra_components.append('news')
|
special_tag = t
|
||||||
c = sanitize(mdata.get('title', ''))
|
|
||||||
if c:
|
|
||||||
extra_components.append(c)
|
|
||||||
c = sanitize(mdata.get('timestamp', ''))
|
|
||||||
if c:
|
|
||||||
extra_components.append(c)
|
|
||||||
break
|
break
|
||||||
elif tag.startswith('/'):
|
|
||||||
|
settings = self.settings()
|
||||||
|
template = settings.save_template
|
||||||
|
use_subdirs = self.SUPPORTS_SUB_DIRS and settings.use_subdirs
|
||||||
|
|
||||||
|
fname = sanitize(fname)
|
||||||
|
ext = os.path.splitext(fname)[1]
|
||||||
|
|
||||||
|
if special_tag is None:
|
||||||
|
from calibre.library.save_to_disk import get_components
|
||||||
|
extra_components = get_components(template, mdata, fname,
|
||||||
|
replace_whitespace=True)
|
||||||
|
else:
|
||||||
|
tag = special_tag
|
||||||
|
if tag.startswith(_('News')):
|
||||||
|
extra_components.append('News')
|
||||||
|
c = sanitize(mdata.title if mdata.title else '')
|
||||||
|
c = c.split('[')[0].strip()
|
||||||
|
if c:
|
||||||
|
extra_components.append(c)
|
||||||
|
else:
|
||||||
for c in tag.split('/'):
|
for c in tag.split('/'):
|
||||||
c = sanitize(c)
|
c = sanitize(c)
|
||||||
if not c: continue
|
if not c: continue
|
||||||
extra_components.append(c)
|
extra_components.append(c)
|
||||||
break
|
|
||||||
|
|
||||||
|
if not use_subdirs:
|
||||||
|
extra_components = extra_components[:1]
|
||||||
|
|
||||||
if not extra_components:
|
if not extra_components:
|
||||||
c = sanitize(mdata.get('authors', _('Unknown')))
|
fname = sanitize(self.filename_callback(fname, mdata))
|
||||||
if c:
|
|
||||||
extra_components.append(c)
|
|
||||||
c = sanitize(mdata.get('title', _('Unknown')))
|
|
||||||
if c:
|
|
||||||
extra_components.append(c)
|
|
||||||
newpath = os.path.join(newpath, c)
|
|
||||||
|
|
||||||
fname = sanitize(fname)
|
|
||||||
extra_components.append(fname)
|
extra_components.append(fname)
|
||||||
extra_components = [str(x) for x in extra_components]
|
extra_components = [str(x) for x in extra_components]
|
||||||
|
else:
|
||||||
|
extra_components[-1] += ext
|
||||||
|
|
||||||
def remove_trailing_periods(x):
|
def remove_trailing_periods(x):
|
||||||
ans = x
|
ans = x
|
||||||
while ans.endswith('.'):
|
while ans.endswith('.'):
|
||||||
ans = ans[:-1]
|
ans = ans[:-1].strip()
|
||||||
if not ans:
|
if not ans:
|
||||||
ans = 'x'
|
ans = 'x'
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
extra_components = list(map(remove_trailing_periods, extra_components))
|
extra_components = list(map(remove_trailing_periods, extra_components))
|
||||||
components = shorten_components_to(250 - len(path), extra_components)
|
components = shorten_components_to(250 - len(path), extra_components)
|
||||||
filepath = os.path.join(path, *components)
|
filepath = os.path.join(path, *components)
|
||||||
|
@ -6,12 +6,22 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
from calibre.utils.config import Config, ConfigProxy
|
from calibre.utils.config import Config, ConfigProxy
|
||||||
|
|
||||||
|
|
||||||
class DeviceConfig(object):
|
class DeviceConfig(object):
|
||||||
|
|
||||||
HELP_MESSAGE = _('Configure Device')
|
HELP_MESSAGE = _('Configure Device')
|
||||||
EXTRA_CUSTOMIZATION_MESSAGE = None
|
EXTRA_CUSTOMIZATION_MESSAGE = None
|
||||||
EXTRA_CUSTOMIZATION_DEFAULT = None
|
EXTRA_CUSTOMIZATION_DEFAULT = None
|
||||||
|
|
||||||
|
#: If None the default is used
|
||||||
|
SAVE_TEMPLATE = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _default_save_template(cls):
|
||||||
|
from calibre.library.save_to_disk import config
|
||||||
|
return cls.SAVE_TEMPLATE if cls.SAVE_TEMPLATE else \
|
||||||
|
config().parse().send_template
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _config(cls):
|
def _config(cls):
|
||||||
klass = cls if isinstance(cls, type) else cls.__class__
|
klass = cls if isinstance(cls, type) else cls.__class__
|
||||||
@ -22,6 +32,8 @@ class DeviceConfig(object):
|
|||||||
help=_('Place files in sub directories if the device supports them'))
|
help=_('Place files in sub directories if the device supports them'))
|
||||||
c.add_opt('read_metadata', default=True,
|
c.add_opt('read_metadata', default=True,
|
||||||
help=_('Read metadata from files on device'))
|
help=_('Read metadata from files on device'))
|
||||||
|
c.add_opt('save_template', default=cls._default_save_template(),
|
||||||
|
help=_('Template to control how books are saved'))
|
||||||
c.add_opt('extra_customization',
|
c.add_opt('extra_customization',
|
||||||
default=cls.EXTRA_CUSTOMIZATION_DEFAULT,
|
default=cls.EXTRA_CUSTOMIZATION_DEFAULT,
|
||||||
help=_('Extra customization'))
|
help=_('Extra customization'))
|
||||||
@ -52,6 +64,8 @@ class DeviceConfig(object):
|
|||||||
if not ec:
|
if not ec:
|
||||||
ec = None
|
ec = None
|
||||||
proxy['extra_customization'] = ec
|
proxy['extra_customization'] = ec
|
||||||
|
st = unicode(config_widget.opt_save_template.text())
|
||||||
|
proxy['save_template'] = st
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def settings(cls):
|
def settings(cls):
|
||||||
|
@ -123,7 +123,8 @@ class USBMS(CLI, Device):
|
|||||||
'''
|
'''
|
||||||
:path: the full path were the associated book is located.
|
:path: the full path were the associated book is located.
|
||||||
:filename: the name of the book file without the extension.
|
:filename: the name of the book file without the extension.
|
||||||
:metatdata: metadata belonging to the book. metadata.cover[2] for coverdata.
|
:metatdata: metadata belonging to the book. Use metadata.thumbnail
|
||||||
|
for cover
|
||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
'''Read meta information from PDF files'''
|
'''Read meta information from PDF files'''
|
||||||
|
|
||||||
import re
|
#import re
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
@ -12,15 +12,15 @@ from calibre.ebooks.metadata import MetaInformation, string_to_authors, authors_
|
|||||||
|
|
||||||
pdfreflow, pdfreflow_error = plugins['pdfreflow']
|
pdfreflow, pdfreflow_error = plugins['pdfreflow']
|
||||||
|
|
||||||
_isbn_pat = re.compile(r'ISBN[: ]*([-0-9Xx]+)')
|
#_isbn_pat = re.compile(r'ISBN[: ]*([-0-9Xx]+)')
|
||||||
|
|
||||||
def get_metadata(stream, cover=True):
|
def get_metadata(stream, cover=True):
|
||||||
if pdfreflow is None:
|
if pdfreflow is None:
|
||||||
raise RuntimeError(pdfreflow_error)
|
raise RuntimeError(pdfreflow_error)
|
||||||
raw = stream.read()
|
raw = stream.read()
|
||||||
isbn = _isbn_pat.search(raw)
|
#isbn = _isbn_pat.search(raw)
|
||||||
if isbn is not None:
|
#if isbn is not None:
|
||||||
isbn = isbn.group(1).replace('-', '').replace(' ', '')
|
# isbn = isbn.group(1).replace('-', '').replace(' ', '')
|
||||||
info = pdfreflow.get_metadata(raw, cover)
|
info = pdfreflow.get_metadata(raw, cover)
|
||||||
title = info.get('Title', None)
|
title = info.get('Title', None)
|
||||||
au = info.get('Author', None)
|
au = info.get('Author', None)
|
||||||
@ -29,8 +29,8 @@ def get_metadata(stream, cover=True):
|
|||||||
else:
|
else:
|
||||||
au = string_to_authors(au)
|
au = string_to_authors(au)
|
||||||
mi = MetaInformation(title, au)
|
mi = MetaInformation(title, au)
|
||||||
if isbn is not None:
|
#if isbn is not None:
|
||||||
mi.isbn = isbn
|
# mi.isbn = isbn
|
||||||
|
|
||||||
creator = info.get('Creator', None)
|
creator = info.get('Creator', None)
|
||||||
if creator:
|
if creator:
|
||||||
|
@ -21,7 +21,7 @@ except ImportError:
|
|||||||
|
|
||||||
from lxml import html, etree
|
from lxml import html, etree
|
||||||
|
|
||||||
from calibre import entity_to_unicode
|
from calibre import entity_to_unicode, CurrentDir
|
||||||
from calibre.utils.filenames import ascii_filename
|
from calibre.utils.filenames import ascii_filename
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
from calibre.ebooks import DRMError
|
from calibre.ebooks import DRMError
|
||||||
@ -116,7 +116,7 @@ class BookHeader(object):
|
|||||||
if ident == 'TEXTREAD':
|
if ident == 'TEXTREAD':
|
||||||
self.codepage = 1252
|
self.codepage = 1252
|
||||||
if len(raw) <= 16:
|
if len(raw) <= 16:
|
||||||
self.codec = 'cp1251'
|
self.codec = 'cp1252'
|
||||||
self.extra_flags = 0
|
self.extra_flags = 0
|
||||||
self.title = _('Unknown')
|
self.title = _('Unknown')
|
||||||
self.language = 'ENGLISH'
|
self.language = 'ENGLISH'
|
||||||
@ -790,6 +790,7 @@ def get_metadata(stream):
|
|||||||
mi = mh.exth.mi
|
mi = mh.exth.mi
|
||||||
else:
|
else:
|
||||||
with TemporaryDirectory('_mobi_meta_reader') as tdir:
|
with TemporaryDirectory('_mobi_meta_reader') as tdir:
|
||||||
|
with CurrentDir(tdir):
|
||||||
mr = MobiReader(stream, log)
|
mr = MobiReader(stream, log)
|
||||||
parse_cache = {}
|
parse_cache = {}
|
||||||
mr.extract_content(tdir, parse_cache)
|
mr.extract_content(tdir, parse_cache)
|
||||||
|
@ -73,6 +73,8 @@ def _config():
|
|||||||
'only take place when the Enter or Return key is pressed.')
|
'only take place when the Enter or Return key is pressed.')
|
||||||
c.add_opt('save_to_disk_template_history', default=[],
|
c.add_opt('save_to_disk_template_history', default=[],
|
||||||
help='Previously used Save to Disk templates')
|
help='Previously used Save to Disk templates')
|
||||||
|
c.add_opt('send_to_device_template_history', default=[],
|
||||||
|
help='Previously used Send to Device templates')
|
||||||
c.add_opt('main_search_history', default=[],
|
c.add_opt('main_search_history', default=[],
|
||||||
help='Search history for the main GUI')
|
help='Search history for the main GUI')
|
||||||
c.add_opt('viewer_search_history', default=[],
|
c.add_opt('viewer_search_history', default=[],
|
||||||
|
@ -6,14 +6,14 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os, uuid
|
import os, uuid, re
|
||||||
|
|
||||||
from PyQt4.Qt import QPixmap, SIGNAL
|
from PyQt4.Qt import QPixmap, SIGNAL
|
||||||
|
|
||||||
from calibre.gui2 import choose_images, error_dialog
|
from calibre.gui2 import choose_images, error_dialog
|
||||||
from calibre.gui2.convert.metadata_ui import Ui_Form
|
from calibre.gui2.convert.metadata_ui import Ui_Form
|
||||||
from calibre.ebooks.metadata import authors_to_string, string_to_authors, \
|
from calibre.ebooks.metadata import authors_to_string, string_to_authors, \
|
||||||
MetaInformation
|
MetaInformation, authors_to_sort_string
|
||||||
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.gui2.convert import Widget
|
from calibre.gui2.convert import Widget
|
||||||
@ -53,8 +53,16 @@ class MetadataWidget(Widget, Ui_Form):
|
|||||||
self.initialize_options(get_option, get_help, db, book_id)
|
self.initialize_options(get_option, get_help, db, book_id)
|
||||||
self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover)
|
self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover)
|
||||||
|
|
||||||
|
def deduce_author_sort(self, *args):
|
||||||
|
au = unicode(self.author.currentText())
|
||||||
|
au = re.sub(r'\s+et al\.$', '', au)
|
||||||
|
authors = string_to_authors(au)
|
||||||
|
self.author_sort.setText(authors_to_sort_string(authors))
|
||||||
|
|
||||||
|
|
||||||
def initialize_metadata_options(self):
|
def initialize_metadata_options(self):
|
||||||
self.initialize_combos()
|
self.initialize_combos()
|
||||||
|
self.author.editTextChanged.connect(self.deduce_author_sort)
|
||||||
|
|
||||||
mi = self.db.get_metadata(self.book_id, index_is_id=True)
|
mi = self.db.get_metadata(self.book_id, index_is_id=True)
|
||||||
self.title.setText(mi.title)
|
self.title.setText(mi.title)
|
||||||
|
@ -536,8 +536,7 @@ class DeviceGUI(object):
|
|||||||
else:
|
else:
|
||||||
_auto_ids = []
|
_auto_ids = []
|
||||||
|
|
||||||
full_metadata = self.library_view.model().get_metadata(
|
full_metadata = self.library_view.model().metadata_for(ids)
|
||||||
ids, full_metadata=True, rows_are_ids=True)[-1]
|
|
||||||
files = [getattr(f, 'name', None) for f in files]
|
files = [getattr(f, 'name', None) for f in files]
|
||||||
|
|
||||||
bad, remove_ids, jobnames = [], [], []
|
bad, remove_ids, jobnames = [], [], []
|
||||||
@ -707,19 +706,17 @@ class DeviceGUI(object):
|
|||||||
if not files:
|
if not files:
|
||||||
dynamic.set('news_to_be_synced', set([]))
|
dynamic.set('news_to_be_synced', set([]))
|
||||||
return
|
return
|
||||||
metadata = self.library_view.model().get_metadata(ids,
|
metadata = self.library_view.model().metadata_for(ids)
|
||||||
rows_are_ids=True)
|
|
||||||
names = []
|
names = []
|
||||||
for mi in metadata:
|
for mi in metadata:
|
||||||
prefix = ascii_filename(mi['title'])
|
prefix = ascii_filename(mi.title)
|
||||||
if not isinstance(prefix, unicode):
|
if not isinstance(prefix, unicode):
|
||||||
prefix = prefix.decode(preferred_encoding, 'replace')
|
prefix = prefix.decode(preferred_encoding, 'replace')
|
||||||
prefix = ascii_filename(prefix)
|
prefix = ascii_filename(prefix)
|
||||||
names.append('%s_%d%s'%(prefix, id,
|
names.append('%s_%d%s'%(prefix, id,
|
||||||
os.path.splitext(f.name)[1]))
|
os.path.splitext(f.name)[1]))
|
||||||
cdata = mi['cover']
|
if mi.cover_data and mi.cover_data[1]:
|
||||||
if cdata:
|
mi.thumbnail = self.cover_to_thumbnail(mi.cover_data[1])
|
||||||
mi['cover'] = self.cover_to_thumbnail(cdata)
|
|
||||||
dynamic.set('news_to_be_synced', set([]))
|
dynamic.set('news_to_be_synced', set([]))
|
||||||
if config['upload_news_to_device'] and files:
|
if config['upload_news_to_device'] and files:
|
||||||
remove = ids if \
|
remove = ids if \
|
||||||
@ -751,29 +748,28 @@ class DeviceGUI(object):
|
|||||||
else:
|
else:
|
||||||
_auto_ids = []
|
_auto_ids = []
|
||||||
|
|
||||||
metadata = self.library_view.model().get_metadata(ids, True)
|
metadata = self.library_view.model().metadata_for(ids)
|
||||||
ids = iter(ids)
|
ids = iter(ids)
|
||||||
for mi in metadata:
|
for mi in metadata:
|
||||||
cdata = mi['cover']
|
if mi.cover_data and mi.cover_data[1]:
|
||||||
if cdata:
|
mi.thumbnail = self.cover_to_thumbnail(mi.cover_data[1])
|
||||||
mi['cover'] = self.cover_to_thumbnail(cdata)
|
imetadata = iter(metadata)
|
||||||
metadata = iter(metadata)
|
|
||||||
|
|
||||||
files = [getattr(f, 'name', None) for f in _files]
|
files = [getattr(f, 'name', None) for f in _files]
|
||||||
bad, good, gf, names, remove_ids = [], [], [], [], []
|
bad, good, gf, names, remove_ids = [], [], [], [], []
|
||||||
for f in files:
|
for f in files:
|
||||||
mi = metadata.next()
|
mi = imetadata.next()
|
||||||
id = ids.next()
|
id = ids.next()
|
||||||
if f is None:
|
if f is None:
|
||||||
bad.append(mi['title'])
|
bad.append(mi.title)
|
||||||
else:
|
else:
|
||||||
remove_ids.append(id)
|
remove_ids.append(id)
|
||||||
good.append(mi)
|
good.append(mi)
|
||||||
gf.append(f)
|
gf.append(f)
|
||||||
t = mi['title']
|
t = mi.title
|
||||||
if not t:
|
if not t:
|
||||||
t = _('Unknown')
|
t = _('Unknown')
|
||||||
a = mi['authors']
|
a = mi.format_authors()
|
||||||
if not a:
|
if not a:
|
||||||
a = _('Unknown')
|
a = _('Unknown')
|
||||||
prefix = ascii_filename(t+' - '+a)
|
prefix = ascii_filename(t+' - '+a)
|
||||||
@ -850,7 +846,7 @@ class DeviceGUI(object):
|
|||||||
Upload books to device.
|
Upload books to device.
|
||||||
:param files: List of either paths to files or file like objects
|
:param files: List of either paths to files or file like objects
|
||||||
'''
|
'''
|
||||||
titles = [i['title'] for i in metadata]
|
titles = [i.title for i in metadata]
|
||||||
job = self.device_manager.upload_books(
|
job = self.device_manager.upload_books(
|
||||||
Dispatcher(self.books_uploaded),
|
Dispatcher(self.books_uploaded),
|
||||||
files, names, on_card=on_card,
|
files, names, on_card=on_card,
|
||||||
|
@ -46,6 +46,7 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
|
|||||||
else:
|
else:
|
||||||
self.extra_customization_label.setVisible(False)
|
self.extra_customization_label.setVisible(False)
|
||||||
self.opt_extra_customization.setVisible(False)
|
self.opt_extra_customization.setVisible(False)
|
||||||
|
self.opt_save_template.setText(settings.save_template)
|
||||||
|
|
||||||
|
|
||||||
def up_column(self):
|
def up_column(self):
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="extra_customization_label">
|
<widget class="QLabel" name="extra_customization_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Extra customization</string>
|
<string>Extra customization</string>
|
||||||
@ -103,9 +103,22 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QLineEdit" name="opt_extra_customization"/>
|
<widget class="QLineEdit" name="opt_extra_customization"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save &template:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_save_template</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLineEdit" name="opt_save_template"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -11,9 +11,7 @@ import textwrap
|
|||||||
from PyQt4.Qt import QTabWidget
|
from PyQt4.Qt import QTabWidget
|
||||||
|
|
||||||
from calibre.gui2.dialogs.config.add_save_ui import Ui_TabWidget
|
from calibre.gui2.dialogs.config.add_save_ui import Ui_TabWidget
|
||||||
from calibre.library.save_to_disk import config, FORMAT_ARG_DESCS, \
|
from calibre.library.save_to_disk import config
|
||||||
preprocess_template
|
|
||||||
from calibre.gui2 import error_dialog
|
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
from calibre.gui2.widgets import FilenamePattern
|
from calibre.gui2.widgets import FilenamePattern
|
||||||
|
|
||||||
@ -22,8 +20,8 @@ class AddSave(QTabWidget, Ui_TabWidget):
|
|||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QTabWidget.__init__(self, parent)
|
QTabWidget.__init__(self, parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
while self.count() > 2:
|
while self.count() > 3:
|
||||||
self.removeTab(2)
|
self.removeTab(3)
|
||||||
c = config()
|
c = config()
|
||||||
opts = c.parse()
|
opts = c.parse()
|
||||||
for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf',
|
for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf',
|
||||||
@ -41,36 +39,17 @@ class AddSave(QTabWidget, Ui_TabWidget):
|
|||||||
g.setToolTip(help)
|
g.setToolTip(help)
|
||||||
g.setWhatsThis(help)
|
g.setWhatsThis(help)
|
||||||
|
|
||||||
help = '\n'.join(textwrap.wrap(c.get_option('template').help, 75))
|
|
||||||
self.opt_template.initialize('save_to_disk_template_history',
|
|
||||||
opts.template, help)
|
|
||||||
|
|
||||||
variables = sorted(FORMAT_ARG_DESCS.keys())
|
|
||||||
rows = []
|
|
||||||
for var in variables:
|
|
||||||
rows.append(u'<tr><td>%s</td><td>%s</td></tr>'%
|
|
||||||
(var, FORMAT_ARG_DESCS[var]))
|
|
||||||
table = u'<table>%s</table>'%(u'\n'.join(rows))
|
|
||||||
self.template_variables.setText(table)
|
|
||||||
|
|
||||||
self.opt_read_metadata_from_filename.setChecked(not prefs['read_file_metadata'])
|
self.opt_read_metadata_from_filename.setChecked(not prefs['read_file_metadata'])
|
||||||
self.filename_pattern = FilenamePattern(self)
|
self.filename_pattern = FilenamePattern(self)
|
||||||
self.metadata_box.layout().insertWidget(0, self.filename_pattern)
|
self.metadata_box.layout().insertWidget(0, self.filename_pattern)
|
||||||
self.opt_swap_author_names.setChecked(prefs['swap_author_names'])
|
self.opt_swap_author_names.setChecked(prefs['swap_author_names'])
|
||||||
|
help = '\n'.join(textwrap.wrap(c.get_option('template').help, 75))
|
||||||
|
self.save_template.initialize('save_to_disk', opts.template, help)
|
||||||
|
self.send_template.initialize('send_to_device', opts.send_template, help)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
tmpl = preprocess_template(self.opt_template.text())
|
return self.save_template.validate() and self.send_template.validate()
|
||||||
fa = {}
|
|
||||||
for x in FORMAT_ARG_DESCS.keys():
|
|
||||||
fa[x]=''
|
|
||||||
try:
|
|
||||||
tmpl.format(**fa)
|
|
||||||
except Exception, err:
|
|
||||||
error_dialog(self, _('Invalid template'),
|
|
||||||
'<p>'+_('The template %s is invalid:')%tmpl + \
|
|
||||||
'<br>'+str(err), show=True)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def save_settings(self):
|
def save_settings(self):
|
||||||
if not self.validate():
|
if not self.validate():
|
||||||
@ -79,12 +58,13 @@ class AddSave(QTabWidget, Ui_TabWidget):
|
|||||||
for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf',
|
for x in ('asciiize', 'update_metadata', 'save_cover', 'write_opf',
|
||||||
'replace_whitespace', 'to_lowercase'):
|
'replace_whitespace', 'to_lowercase'):
|
||||||
c.set(x, getattr(self, 'opt_'+x).isChecked())
|
c.set(x, getattr(self, 'opt_'+x).isChecked())
|
||||||
for x in ('formats', 'template', 'timefmt'):
|
for x in ('formats', 'timefmt'):
|
||||||
val = unicode(getattr(self, 'opt_'+x).text()).strip()
|
val = unicode(getattr(self, 'opt_'+x).text()).strip()
|
||||||
if x == 'formats' and not val:
|
if x == 'formats' and not val:
|
||||||
val = 'all'
|
val = 'all'
|
||||||
c.set(x, val)
|
c.set(x, val)
|
||||||
self.opt_template.save_history('save_to_disk_template_history')
|
self.save_template.save_settings(c, 'template')
|
||||||
|
self.send_template.save_settings(c, 'send_template')
|
||||||
prefs['read_file_metadata'] = not bool(self.opt_read_metadata_from_filename.isChecked())
|
prefs['read_file_metadata'] = not bool(self.opt_read_metadata_from_filename.isChecked())
|
||||||
pattern = self.filename_pattern.commit()
|
pattern = self.filename_pattern.commit()
|
||||||
prefs['filename_pattern'] = pattern
|
prefs['filename_pattern'] = pattern
|
||||||
|
@ -141,38 +141,6 @@
|
|||||||
<item row="6" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QLineEdit" name="opt_formats"/>
|
<widget class="QLineEdit" name="opt_formats"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0" colspan="2">
|
|
||||||
<widget class="QGroupBox" name="groupBox">
|
|
||||||
<property name="title">
|
|
||||||
<string>Save &template</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>By adjusting the template below, you can control what folders the files are saved in and what filenames they are given. You can use the / character to indicate sub-folders. Available metadata variables are described below. If a particular book does not have some metadata, the variable will be replaced by the empty string.</string>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>Available variables:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QTextBrowser" name="template_variables"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="HistoryBox" name="opt_template"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="opt_replace_whitespace">
|
<widget class="QCheckBox" name="opt_replace_whitespace">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -187,14 +155,38 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="7" column="0" colspan="2">
|
||||||
|
<widget class="SaveTemplate" name="save_template" native="true"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Sending to &device</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Here you can control how calibre will save your books when you click the Send to Device button. This setting can be overriden for individual devices by customizing the device interface plugins in Preferences->Plugins</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="SaveTemplate" name="send_template" native="true"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>HistoryBox</class>
|
<class>SaveTemplate</class>
|
||||||
<extends>QComboBox</extends>
|
<extends>QWidget</extends>
|
||||||
<header>calibre/gui2/dialogs/config/history.h</header>
|
<header>calibre/gui2/dialogs/config/save_template.h</header>
|
||||||
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
58
src/calibre/gui2/dialogs/config/save_template.py
Normal file
58
src/calibre/gui2/dialogs/config/save_template.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import QWidget
|
||||||
|
|
||||||
|
from calibre.gui2 import error_dialog
|
||||||
|
from calibre.gui2.dialogs.config.save_template_ui import Ui_Form
|
||||||
|
from calibre.library.save_to_disk import FORMAT_ARG_DESCS, \
|
||||||
|
preprocess_template
|
||||||
|
|
||||||
|
class SaveTemplate(QWidget, Ui_Form):
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
QWidget.__init__(self, *args)
|
||||||
|
Ui_Form.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
def initialize(self, name, default, help):
|
||||||
|
variables = sorted(FORMAT_ARG_DESCS.keys())
|
||||||
|
rows = []
|
||||||
|
for var in variables:
|
||||||
|
rows.append(u'<tr><td>%s</td><td>%s</td></tr>'%
|
||||||
|
(var, FORMAT_ARG_DESCS[var]))
|
||||||
|
table = u'<table>%s</table>'%(u'\n'.join(rows))
|
||||||
|
self.template_variables.setText(table)
|
||||||
|
|
||||||
|
self.opt_template.initialize(name+'_template_history',
|
||||||
|
default, help)
|
||||||
|
self.option_name = name
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
tmpl = preprocess_template(self.opt_template.text())
|
||||||
|
fa = {}
|
||||||
|
for x in FORMAT_ARG_DESCS.keys():
|
||||||
|
fa[x]=''
|
||||||
|
try:
|
||||||
|
tmpl.format(**fa)
|
||||||
|
except Exception, err:
|
||||||
|
error_dialog(self, _('Invalid template'),
|
||||||
|
'<p>'+_('The template %s is invalid:')%tmpl + \
|
||||||
|
'<br>'+str(err), show=True)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def save_settings(self, config, name):
|
||||||
|
val = unicode(self.opt_template.text())
|
||||||
|
config.set(name, val)
|
||||||
|
self.opt_template.save_history(self.option_name+'_template_history')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
60
src/calibre/gui2/dialogs/config/save_template.ui
Normal file
60
src/calibre/gui2/dialogs/config/save_template.ui
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Form</class>
|
||||||
|
<widget class="QWidget" name="Form">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Save &template</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>By adjusting the template below, you can control what folders the files are saved in and what filenames they are given. You can use the / character to indicate sub-folders. Available metadata variables are described below. If a particular book does not have some metadata, the variable will be replaced by the empty string.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Available variables:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QTextBrowser" name="template_variables"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="HistoryBox" name="opt_template"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>HistoryBox</class>
|
||||||
|
<extends>QComboBox</extends>
|
||||||
|
<header>calibre/gui2/dialogs/config/history.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -399,6 +399,14 @@ class BooksModel(QAbstractTableModel):
|
|||||||
data[_('Author(s)')] = au
|
data[_('Author(s)')] = au
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def metadata_for(self, ids):
|
||||||
|
ans = []
|
||||||
|
for id in ids:
|
||||||
|
mi = self.db.get_metadata(id, index_is_id=True)
|
||||||
|
if mi.series is not None:
|
||||||
|
mi.tag_order = self.db.books_in_series_of(id, index_is_id=True)
|
||||||
|
ans.append(mi)
|
||||||
|
return ans
|
||||||
|
|
||||||
def get_metadata(self, rows, rows_are_ids=False, full_metadata=False):
|
def get_metadata(self, rows, rows_are_ids=False, full_metadata=False):
|
||||||
metadata, _full_metadata = [], []
|
metadata, _full_metadata = [], []
|
||||||
|
@ -231,7 +231,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>3</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="library">
|
<widget class="QWidget" name="library">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
@ -239,33 +239,6 @@
|
|||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="match_any">
|
|
||||||
<property name="text">
|
|
||||||
<string>Match any</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="match_all">
|
|
||||||
<property name="text">
|
|
||||||
<string>Match all</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="popularity">
|
|
||||||
<property name="text">
|
|
||||||
<string>Sort by &popularity</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="TagsView" name="tags_view">
|
<widget class="TagsView" name="tags_view">
|
||||||
<property name="tabKeyNavigation">
|
<property name="tabKeyNavigation">
|
||||||
@ -282,6 +255,30 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="popularity">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sort by &popularity</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="tag_match">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Match any</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Match all</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -9,7 +9,7 @@ Browsing book collection by tags.
|
|||||||
|
|
||||||
from itertools import izip
|
from itertools import izip
|
||||||
|
|
||||||
from PyQt4.Qt import Qt, QTreeView, \
|
from PyQt4.Qt import Qt, QTreeView, QApplication, \
|
||||||
QFont, SIGNAL, QSize, QIcon, QPoint, \
|
QFont, SIGNAL, QSize, QIcon, QPoint, \
|
||||||
QAbstractItemModel, QVariant, QModelIndex
|
QAbstractItemModel, QVariant, QModelIndex
|
||||||
from calibre.gui2 import config, NONE
|
from calibre.gui2 import config, NONE
|
||||||
@ -21,24 +21,31 @@ class TagsView(QTreeView):
|
|||||||
self.setUniformRowHeights(True)
|
self.setUniformRowHeights(True)
|
||||||
self.setCursor(Qt.PointingHandCursor)
|
self.setCursor(Qt.PointingHandCursor)
|
||||||
self.setIconSize(QSize(30, 30))
|
self.setIconSize(QSize(30, 30))
|
||||||
|
self.tag_match = None
|
||||||
|
|
||||||
def set_database(self, db, match_all, popularity):
|
def set_database(self, db, tag_match, popularity):
|
||||||
self._model = TagsModel(db, parent=self)
|
self._model = TagsModel(db, parent=self)
|
||||||
self.popularity = popularity
|
self.popularity = popularity
|
||||||
self.match_all = match_all
|
self.tag_match = tag_match
|
||||||
self.setModel(self._model)
|
self.setModel(self._model)
|
||||||
self.connect(self, SIGNAL('clicked(QModelIndex)'), self.toggle)
|
self.connect(self, SIGNAL('clicked(QModelIndex)'), self.toggle)
|
||||||
self.popularity.setChecked(config['sort_by_popularity'])
|
self.popularity.setChecked(config['sort_by_popularity'])
|
||||||
self.connect(self.popularity, SIGNAL('stateChanged(int)'), self.sort_changed)
|
self.connect(self.popularity, SIGNAL('stateChanged(int)'), self.sort_changed)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def match_all(self):
|
||||||
|
return self.tag_match and self.tag_match.currentIndex() > 0
|
||||||
|
|
||||||
def sort_changed(self, state):
|
def sort_changed(self, state):
|
||||||
config.set('sort_by_popularity', state == Qt.Checked)
|
config.set('sort_by_popularity', state == Qt.Checked)
|
||||||
self.model().refresh()
|
self.model().refresh()
|
||||||
|
|
||||||
def toggle(self, index):
|
def toggle(self, index):
|
||||||
if self._model.toggle(index):
|
modifiers = int(QApplication.keyboardModifiers())
|
||||||
|
exclusive = modifiers not in (Qt.CTRL, Qt.SHIFT)
|
||||||
|
if self._model.toggle(index, exclusive):
|
||||||
self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
|
self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
|
||||||
self._model.tokens(), self.match_all.isChecked())
|
self._model.tokens(), self.match_all)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.model().clear_state()
|
self.model().clear_state()
|
||||||
@ -227,12 +234,14 @@ class TagsModel(QAbstractItemModel):
|
|||||||
|
|
||||||
return len(parent_item.children)
|
return len(parent_item.children)
|
||||||
|
|
||||||
def reset_all_states(self):
|
def reset_all_states(self, except_=None):
|
||||||
for i in xrange(self.rowCount(QModelIndex())):
|
for i in xrange(self.rowCount(QModelIndex())):
|
||||||
category_index = self.index(i, 0, QModelIndex())
|
category_index = self.index(i, 0, QModelIndex())
|
||||||
for j in xrange(self.rowCount(category_index)):
|
for j in xrange(self.rowCount(category_index)):
|
||||||
tag_index = self.index(j, 0, category_index)
|
tag_index = self.index(j, 0, category_index)
|
||||||
tag_item = tag_index.internalPointer()
|
tag_item = tag_index.internalPointer()
|
||||||
|
if tag_item is except_:
|
||||||
|
continue
|
||||||
tag = tag_item.tag
|
tag = tag_item.tag
|
||||||
if tag.state != 0:
|
if tag.state != 0:
|
||||||
tag.state = 0
|
tag.state = 0
|
||||||
@ -248,10 +257,12 @@ class TagsModel(QAbstractItemModel):
|
|||||||
else:
|
else:
|
||||||
self.ignore_next_search -= 1
|
self.ignore_next_search -= 1
|
||||||
|
|
||||||
def toggle(self, index):
|
def toggle(self, index, exclusive):
|
||||||
if not index.isValid(): return False
|
if not index.isValid(): return False
|
||||||
item = index.internalPointer()
|
item = index.internalPointer()
|
||||||
if item.type == TagTreeItem.TAG:
|
if item.type == TagTreeItem.TAG:
|
||||||
|
if exclusive:
|
||||||
|
self.reset_all_states(except_=item)
|
||||||
item.toggle()
|
item.toggle()
|
||||||
self.ignore_next_search = 2
|
self.ignore_next_search = 2
|
||||||
self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), index, index)
|
self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), index, index)
|
||||||
|
@ -498,10 +498,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
self.cover_cache.start()
|
self.cover_cache.start()
|
||||||
self.library_view.model().cover_cache = self.cover_cache
|
self.library_view.model().cover_cache = self.cover_cache
|
||||||
self.tags_view.setVisible(False)
|
self.tags_view.setVisible(False)
|
||||||
self.match_all.setVisible(False)
|
self.tag_match.setVisible(False)
|
||||||
self.match_any.setVisible(False)
|
|
||||||
self.popularity.setVisible(False)
|
self.popularity.setVisible(False)
|
||||||
self.tags_view.set_database(db, self.match_all, self.popularity)
|
self.tags_view.set_database(db, self.tag_match, self.popularity)
|
||||||
self.connect(self.tags_view,
|
self.connect(self.tags_view,
|
||||||
SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
|
SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
|
||||||
self.search.search_from_tags)
|
self.search.search_from_tags)
|
||||||
@ -708,14 +707,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
|||||||
def toggle_tags_view(self, show):
|
def toggle_tags_view(self, show):
|
||||||
if show:
|
if show:
|
||||||
self.tags_view.setVisible(True)
|
self.tags_view.setVisible(True)
|
||||||
self.match_all.setVisible(True)
|
self.tag_match.setVisible(True)
|
||||||
self.match_any.setVisible(True)
|
|
||||||
self.popularity.setVisible(True)
|
self.popularity.setVisible(True)
|
||||||
self.tags_view.setFocus(Qt.OtherFocusReason)
|
self.tags_view.setFocus(Qt.OtherFocusReason)
|
||||||
else:
|
else:
|
||||||
self.tags_view.setVisible(False)
|
self.tags_view.setVisible(False)
|
||||||
self.match_all.setVisible(False)
|
self.tag_match.setVisible(False)
|
||||||
self.match_any.setVisible(False)
|
|
||||||
self.popularity.setVisible(False)
|
self.popularity.setVisible(False)
|
||||||
|
|
||||||
def search_done(self, view, ok):
|
def search_done(self, view, ok):
|
||||||
|
@ -18,6 +18,8 @@ from calibre.constants import preferred_encoding, filesystem_encoding
|
|||||||
from calibre import strftime
|
from calibre import strftime
|
||||||
|
|
||||||
DEFAULT_TEMPLATE = '{author_sort}/{title}/{title} - {authors}'
|
DEFAULT_TEMPLATE = '{author_sort}/{title}/{title} - {authors}'
|
||||||
|
DEFAULT_SEND_TEMPLATE = '{author_sort}/{title} - {authors}'
|
||||||
|
|
||||||
FORMAT_ARG_DESCS = dict(
|
FORMAT_ARG_DESCS = dict(
|
||||||
title=_('The title'),
|
title=_('The title'),
|
||||||
authors=_('The authors'),
|
authors=_('The authors'),
|
||||||
@ -62,6 +64,13 @@ def config(defaults=None):
|
|||||||
'Default is "%s" which will save books into a per-author '
|
'Default is "%s" which will save books into a per-author '
|
||||||
'subdirectory with filenames containing title and author. '
|
'subdirectory with filenames containing title and author. '
|
||||||
'Available controls are: {%s}')%(DEFAULT_TEMPLATE, ', '.join(FORMAT_ARGS)))
|
'Available controls are: {%s}')%(DEFAULT_TEMPLATE, ', '.join(FORMAT_ARGS)))
|
||||||
|
x('send_template', default=DEFAULT_SEND_TEMPLATE,
|
||||||
|
help=_('The template to control the filename and directory structure of files '
|
||||||
|
'sent to the device. '
|
||||||
|
'Default is "%s" which will save books into a per-author '
|
||||||
|
'directory with filenames containing title and author. '
|
||||||
|
'Available controls are: {%s}')%(DEFAULT_SEND_TEMPLATE, ', '.join(FORMAT_ARGS)))
|
||||||
|
|
||||||
x('asciiize', default=True,
|
x('asciiize', default=True,
|
||||||
help=_('Normally, calibre will convert all non English characters into English equivalents '
|
help=_('Normally, calibre will convert all non English characters into English equivalents '
|
||||||
'for the file names. '
|
'for the file names. '
|
||||||
|
@ -853,14 +853,20 @@ def stop_threaded_server(server):
|
|||||||
server.thread = None
|
server.thread = None
|
||||||
|
|
||||||
def option_parser():
|
def option_parser():
|
||||||
return config().option_parser('%prog '+ _('[options]\n\nStart the calibre content server.'))
|
parser = config().option_parser('%prog '+ _('[options]\n\nStart the calibre content server.'))
|
||||||
|
parser.add_option('--with-library', default=None,
|
||||||
|
help=_('Path to the library folder to serve with the content server'))
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args=sys.argv):
|
||||||
parser = option_parser()
|
parser = option_parser()
|
||||||
opts, args = parser.parse_args(args)
|
opts, args = parser.parse_args(args)
|
||||||
cherrypy.log.screen = True
|
cherrypy.log.screen = True
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
db = LibraryDatabase2(prefs['library_path'])
|
if opts.with_library is None:
|
||||||
|
opts.with_library = prefs['library_path']
|
||||||
|
db = LibraryDatabase2(opts.with_library)
|
||||||
server = LibraryServer(db, opts)
|
server = LibraryServer(db, opts)
|
||||||
server.start()
|
server.start()
|
||||||
return 0
|
return 0
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: calibre 0.6.31\n"
|
"Project-Id-Version: calibre 0.6.31\n"
|
||||||
"POT-Creation-Date: 2009-12-27 16:02+MST\n"
|
"POT-Creation-Date: 2009-12-31 10:50+MST\n"
|
||||||
"PO-Revision-Date: 2009-12-27 16:02+MST\n"
|
"PO-Revision-Date: 2009-12-31 10:50+MST\n"
|
||||||
"Last-Translator: Automatically generated\n"
|
"Last-Translator: Automatically generated\n"
|
||||||
"Language-Team: LANGUAGE\n"
|
"Language-Team: LANGUAGE\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -103,9 +103,9 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:121
|
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:121
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:128
|
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:128
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/__init__.py:21
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/__init__.py:21
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:99
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:107
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:124
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:132
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:126
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:134
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:548
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:548
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:557
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:557
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:775
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:775
|
||||||
@ -132,7 +132,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/library/server.py:645
|
#: /home/kovid/work/calibre/src/calibre/library/server.py:645
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server.py:717
|
#: /home/kovid/work/calibre/src/calibre/library/server.py:717
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server.py:764
|
#: /home/kovid/work/calibre/src/calibre/library/server.py:764
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:107
|
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:108
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
|
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
|
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
|
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
|
||||||
@ -389,6 +389,10 @@ msgstr ""
|
|||||||
msgid "Comma separated list of directories to send e-books to on the device. The first one that exists will be used"
|
msgid "Comma separated list of directories to send e-books to on the device. The first one that exists will be used"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/devices/binatone/driver.py:17
|
||||||
|
msgid "Communicate with the Binatone Readme eBook reader."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/blackberry/driver.py:12
|
#: /home/kovid/work/calibre/src/calibre/devices/blackberry/driver.py:12
|
||||||
msgid "Communicate with the Blackberry smart phone."
|
msgid "Communicate with the Blackberry smart phone."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -407,7 +411,7 @@ msgstr ""
|
|||||||
msgid "Communicate with the Cybook Opus eBook reader."
|
msgid "Communicate with the Cybook Opus eBook reader."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:23
|
#: /home/kovid/work/calibre/src/calibre/devices/eb600/driver.py:24
|
||||||
msgid "Communicate with the EB600 eBook reader."
|
msgid "Communicate with the EB600 eBook reader."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -427,6 +431,10 @@ msgstr ""
|
|||||||
msgid "Communicate with the BOOX eBook reader."
|
msgid "Communicate with the BOOX eBook reader."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:17
|
||||||
|
msgid "Communicate with the Hanvon N520 eBook reader."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/iliad/driver.py:16
|
#: /home/kovid/work/calibre/src/calibre/devices/iliad/driver.py:16
|
||||||
msgid "Communicate with the IRex Iliad eBook reader."
|
msgid "Communicate with the IRex Iliad eBook reader."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -576,7 +584,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:853
|
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:853
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:232
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:232
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:125
|
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:132
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1065
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1065
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1069
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1069
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1403
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1403
|
||||||
@ -1344,7 +1352,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:1041
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:1041
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:1101
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:1101
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/status.py:96
|
#: /home/kovid/work/calibre/src/calibre/gui2/status.py:96
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:125
|
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:132
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1352,7 +1360,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:171
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:171
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:360
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:360
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/status.py:95
|
#: /home/kovid/work/calibre/src/calibre/gui2/status.py:95
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:125
|
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:132
|
||||||
msgid "Series"
|
msgid "Series"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2067,7 +2075,7 @@ msgid "Limit max simultaneous jobs to number of CPUs"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:127
|
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:127
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:410
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:439
|
||||||
msgid "Copied"
|
msgid "Copied"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2325,9 +2333,9 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:267
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:267
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:269
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:269
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:270
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:270
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:332
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:334
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:334
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:336
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:340
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:342
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:74
|
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:74
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:79
|
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:79
|
||||||
msgid "..."
|
msgid "..."
|
||||||
@ -2573,33 +2581,33 @@ msgstr ""
|
|||||||
msgid "Set the metadata. The output file will contain as much of this metadata as possible."
|
msgid "Set the metadata. The output file will contain as much of this metadata as possible."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:152
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:160
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:99
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:99
|
||||||
msgid "Choose cover for "
|
msgid "Choose cover for "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:159
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:167
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:106
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:106
|
||||||
msgid "Cannot read"
|
msgid "Cannot read"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:160
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:168
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:107
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:107
|
||||||
msgid "You do not have permission to read the file: "
|
msgid "You do not have permission to read the file: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:168
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:176
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:175
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:183
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:115
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:115
|
||||||
msgid "Error reading file"
|
msgid "Error reading file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:169
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:177
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:116
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:116
|
||||||
msgid "<p>There was an error reading from file: <br /><b>"
|
msgid "<p>There was an error reading from file: <br /><b>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:176
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:184
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:124
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:124
|
||||||
msgid " is not a valid picture"
|
msgid " is not a valid picture"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2795,7 +2803,7 @@ msgid "RB Output"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder.py:77
|
#: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder.py:77
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1513
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1510
|
||||||
msgid "Choose the format to view"
|
msgid "Choose the format to view"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3300,7 +3308,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:216
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:216
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:350
|
#: /home/kovid/work/calibre/src/calibre/gui2/library.py:350
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/status.py:93
|
#: /home/kovid/work/calibre/src/calibre/gui2/status.py:93
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:125
|
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:132
|
||||||
msgid "Formats"
|
msgid "Formats"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3403,7 +3411,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:475
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:475
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:819
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:819
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:158
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:158
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1186
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1183
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53
|
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3473,12 +3481,12 @@ msgid "Access log:"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:674
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:674
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:635
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:634
|
||||||
msgid "Failed to start content server"
|
msgid "Failed to start content server"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:698
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:698
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:520
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:549
|
||||||
msgid "Select location for books"
|
msgid "Select location for books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3612,7 +3620,7 @@ msgid "&Saving books"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:499
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:499
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:368
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:366
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:173
|
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:173
|
||||||
msgid "Preferences"
|
msgid "Preferences"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -4067,7 +4075,7 @@ msgid "Choose formats for "
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:137
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:137
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:947
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:944
|
||||||
msgid "Books"
|
msgid "Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -4967,135 +4975,135 @@ msgstr ""
|
|||||||
msgid "try deleting the file"
|
msgid "try deleting the file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:333
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:331
|
||||||
msgid "calibre"
|
msgid "calibre"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:335
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:333
|
||||||
msgid "Advanced search"
|
msgid "Advanced search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:337
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:335
|
||||||
msgid "Alt+S"
|
msgid "Alt+S"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:338
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:336
|
||||||
msgid "&Search:"
|
msgid "&Search:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:339
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:337
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:340
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:338
|
||||||
msgid "<p>Search the list of books by title, author, publisher, tags, comments, etc.<br><br>Words separated by spaces are ANDed"
|
msgid "<p>Search the list of books by title, author, publisher, tags, comments, etc.<br><br>Words separated by spaces are ANDed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:341
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:339
|
||||||
msgid "Reset Quick Search"
|
msgid "Reset Quick Search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:343
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:341
|
||||||
msgid "Match any"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:344
|
|
||||||
msgid "Match all"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:345
|
|
||||||
msgid "Sort by &popularity"
|
msgid "Sort by &popularity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:346
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:342
|
||||||
|
msgid "Match any"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:343
|
||||||
|
msgid "Match all"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:344
|
||||||
msgid "Add books"
|
msgid "Add books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:347
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:345
|
||||||
msgid "A"
|
msgid "A"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:348
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:346
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:349
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:347
|
||||||
msgid "Remove books"
|
msgid "Remove books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:350
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:348
|
||||||
msgid "Del"
|
msgid "Del"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:351
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:349
|
||||||
msgid "Edit meta information"
|
msgid "Edit meta information"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:352
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:350
|
||||||
msgid "E"
|
msgid "E"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:353
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:351
|
||||||
msgid "Send to device"
|
msgid "Send to device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:354
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:352
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:304
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:304
|
||||||
msgid "Save to disk"
|
msgid "Save to disk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:355
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:353
|
||||||
msgid "S"
|
msgid "S"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:356
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:354
|
||||||
msgid "Fetch news"
|
msgid "Fetch news"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:357
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:355
|
||||||
msgid "F"
|
msgid "F"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:358
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:356
|
||||||
msgid "Convert E-books"
|
msgid "Convert E-books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:359
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:357
|
||||||
msgid "C"
|
msgid "C"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:360
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:358
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:314
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:314
|
||||||
msgid "View"
|
msgid "View"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:361
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:359
|
||||||
msgid "V"
|
msgid "V"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:362
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:360
|
||||||
msgid "Open containing folder"
|
msgid "Open containing folder"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:363
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:361
|
||||||
msgid "Show book details"
|
msgid "Show book details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:364
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:362
|
||||||
msgid "Books by same author"
|
msgid "Books by same author"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:365
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:363
|
||||||
msgid "Books in this series"
|
msgid "Books in this series"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:366
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:364
|
||||||
msgid "Books by this publisher"
|
msgid "Books by this publisher"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:367
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:365
|
||||||
msgid "Books with the same tags"
|
msgid "Books with the same tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:369
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:367
|
||||||
msgid "Configure calibre"
|
msgid "Configure calibre"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:370
|
#: /home/kovid/work/calibre/src/calibre/gui2/main_ui.py:368
|
||||||
msgid "Ctrl+P"
|
msgid "Ctrl+P"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -5207,11 +5215,11 @@ msgstr ""
|
|||||||
msgid "Click to browse books by tags"
|
msgid "Click to browse books by tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:125
|
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:132
|
||||||
msgid "Authors"
|
msgid "Authors"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:125
|
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:132
|
||||||
msgid "Publishers"
|
msgid "Publishers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -5327,7 +5335,7 @@ msgid "Save to disk in a single directory"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:306
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:306
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1621
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1618
|
||||||
msgid "Save only %s format to disk"
|
msgid "Save only %s format to disk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -5373,36 +5381,36 @@ msgid "Bad database location"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:471
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:471
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:528
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:557
|
||||||
msgid "Calibre Library"
|
msgid "Calibre Library"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:481
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:481
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1767
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1774
|
||||||
msgid "Choose a location for your ebook library."
|
msgid "Choose a location for your ebook library."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:678
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:677
|
||||||
msgid "Browse by covers"
|
msgid "Browse by covers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:795
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:792
|
||||||
msgid "Device: "
|
msgid "Device: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:797
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:794
|
||||||
msgid " detected."
|
msgid " detected."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:821
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:818
|
||||||
msgid "Connected "
|
msgid "Connected "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:833
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:830
|
||||||
msgid "Device database corrupted"
|
msgid "Device database corrupted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:834
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:831
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" <p>The database of books on the reader is corrupted. Try the following:\n"
|
" <p>The database of books on the reader is corrupted. Try the following:\n"
|
||||||
@ -5413,277 +5421,281 @@ msgid ""
|
|||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:895
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:892
|
||||||
msgid "How many empty books?"
|
msgid "How many empty books?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:896
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:893
|
||||||
msgid "How many empty books should be added?"
|
msgid "How many empty books should be added?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:940
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:937
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:987
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:984
|
||||||
msgid "Uploading books to device."
|
msgid "Uploading books to device."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:948
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:945
|
||||||
msgid "EPUB Books"
|
msgid "EPUB Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:949
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:946
|
||||||
msgid "LRF Books"
|
msgid "LRF Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:950
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:947
|
||||||
msgid "HTML Books"
|
msgid "HTML Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:951
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:948
|
||||||
msgid "LIT Books"
|
msgid "LIT Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:952
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:949
|
||||||
msgid "MOBI Books"
|
msgid "MOBI Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:953
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:950
|
||||||
msgid "Text books"
|
msgid "Text books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:954
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:951
|
||||||
msgid "PDF Books"
|
msgid "PDF Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:955
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:952
|
||||||
msgid "Comics"
|
msgid "Comics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:956
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:953
|
||||||
msgid "Archives"
|
msgid "Archives"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:960
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:957
|
||||||
msgid "Supported books"
|
msgid "Supported books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:996
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:993
|
||||||
msgid "Failed to read metadata"
|
msgid "Failed to read metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:997
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:994
|
||||||
msgid "Failed to read metadata from the following"
|
msgid "Failed to read metadata from the following"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1016
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1013
|
||||||
msgid "Cannot delete"
|
msgid "Cannot delete"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1019
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1016
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1507
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1504
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1526
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1523
|
||||||
msgid "No book selected"
|
msgid "No book selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1029
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1026
|
||||||
msgid "Choose formats to be deleted"
|
msgid "Choose formats to be deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1045
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1042
|
||||||
msgid "Choose formats <b>not</b> to be deleted"
|
msgid "Choose formats <b>not</b> to be deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1081
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1078
|
||||||
msgid "The selected books will be <b>permanently deleted</b> and the files removed from your computer. Are you sure?"
|
msgid "The selected books will be <b>permanently deleted</b> and the files removed from your computer. Are you sure?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1108
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1105
|
||||||
msgid "Deleting books from device."
|
msgid "Deleting books from device."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1139
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1136
|
||||||
msgid "Cannot download metadata"
|
msgid "Cannot download metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1140
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1137
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1197
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1194
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1230
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1227
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1255
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1252
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1368
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1365
|
||||||
msgid "No books selected"
|
msgid "No books selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1155
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1152
|
||||||
msgid "social metadata"
|
msgid "social metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1157
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1154
|
||||||
msgid "covers"
|
msgid "covers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1157
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1154
|
||||||
msgid "metadata"
|
msgid "metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1159
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1156
|
||||||
msgid "Downloading %s for %d book(s)"
|
msgid "Downloading %s for %d book(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1181
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1178
|
||||||
msgid "Failed to download some metadata"
|
msgid "Failed to download some metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1182
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1179
|
||||||
msgid "Failed to download metadata for the following:"
|
msgid "Failed to download metadata for the following:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1185
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1182
|
||||||
msgid "Failed to download metadata:"
|
msgid "Failed to download metadata:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1196
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1193
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1229
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1226
|
||||||
msgid "Cannot edit metadata"
|
msgid "Cannot edit metadata"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1254
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1251
|
||||||
msgid "Cannot save to disk"
|
msgid "Cannot save to disk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1257
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1254
|
||||||
msgid "Choose destination directory"
|
msgid "Choose destination directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1284
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1281
|
||||||
msgid "Error while saving"
|
msgid "Error while saving"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1285
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1282
|
||||||
msgid "There was an error while saving."
|
msgid "There was an error while saving."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1292
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1289
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1293
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1290
|
||||||
msgid "Could not save some books"
|
msgid "Could not save some books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1294
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1291
|
||||||
msgid "Click the show details button to see which ones."
|
msgid "Click the show details button to see which ones."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1313
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1310
|
||||||
msgid "Fetching news from "
|
msgid "Fetching news from "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1327
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1324
|
||||||
msgid " fetched."
|
msgid " fetched."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1367
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1364
|
||||||
msgid "Cannot convert"
|
msgid "Cannot convert"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1396
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1393
|
||||||
msgid "Starting conversion of %d book(s)"
|
msgid "Starting conversion of %d book(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1507
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1504
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1563
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1560
|
||||||
msgid "Cannot view"
|
msgid "Cannot view"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1525
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1522
|
||||||
msgid "Cannot open folder"
|
msgid "Cannot open folder"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1547
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1544
|
||||||
msgid "Multiple Books Selected"
|
msgid "Multiple Books Selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1548
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1545
|
||||||
msgid "You are attempting to open %d books. Opening too many books at once can be slow and have a negative effect on the responsiveness of your computer. Once started the process cannot be stopped until complete. Do you wish to continue?"
|
msgid "You are attempting to open %d books. Opening too many books at once can be slow and have a negative effect on the responsiveness of your computer. Once started the process cannot be stopped until complete. Do you wish to continue?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1564
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1561
|
||||||
msgid "%s has no available formats."
|
msgid "%s has no available formats."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1605
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1602
|
||||||
msgid "Cannot configure"
|
msgid "Cannot configure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1606
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1603
|
||||||
msgid "Cannot configure while there are running jobs."
|
msgid "Cannot configure while there are running jobs."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1649
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1646
|
||||||
msgid "No detailed info available"
|
msgid "No detailed info available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1650
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1647
|
||||||
msgid "No detailed information is available for books on the device."
|
msgid "No detailed information is available for books on the device."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1705
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1702
|
||||||
msgid "Error talking to device"
|
msgid "Error talking to device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1706
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1703
|
||||||
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot."
|
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1729
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1726
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1747
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1754
|
||||||
msgid "Conversion Error"
|
msgid "Conversion Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1730
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1727
|
||||||
msgid "<p>Could not convert: %s<p>It is a <a href=\"%s\">DRM</a>ed book. You must first remove the DRM using third party tools."
|
msgid "<p>Could not convert: %s<p>It is a <a href=\"%s\">DRM</a>ed book. You must first remove the DRM using third party tools."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1748
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1740
|
||||||
|
msgid "Recipe Disabled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1755
|
||||||
msgid "<b>Failed</b>"
|
msgid "<b>Failed</b>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1776
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1783
|
||||||
msgid "Invalid library location"
|
msgid "Invalid library location"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1777
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1784
|
||||||
msgid "Could not access %s. Using %s as the library."
|
msgid "Could not access %s. Using %s as the library."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1825
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1832
|
||||||
msgid "is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development."
|
msgid "is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1850
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1857
|
||||||
msgid "There are active jobs. Are you sure you want to quit?"
|
msgid "There are active jobs. Are you sure you want to quit?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1853
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1860
|
||||||
msgid ""
|
msgid ""
|
||||||
" is communicating with the device!<br>\n"
|
" is communicating with the device!<br>\n"
|
||||||
" Quitting may cause corruption on the device.<br>\n"
|
" Quitting may cause corruption on the device.<br>\n"
|
||||||
" Are you sure you want to quit?"
|
" Are you sure you want to quit?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1857
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1864
|
||||||
msgid "WARNING: Active jobs"
|
msgid "WARNING: Active jobs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1909
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1916
|
||||||
msgid "will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray."
|
msgid "will keep running in the system tray. To close it, choose <b>Quit</b> in the context menu of the system tray."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1928
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1935
|
||||||
msgid "<span style=\"color:red; font-weight:bold\">Latest version: <a href=\"%s\">%s</a></span>"
|
msgid "<span style=\"color:red; font-weight:bold\">Latest version: <a href=\"%s\">%s</a></span>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1936
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1943
|
||||||
msgid "Update available"
|
msgid "Update available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1937
|
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:1944
|
||||||
msgid "%s has been updated to version %s. See the <a href=\"http://calibre-ebook.com/whats-new\">new features</a>. Visit the download page?"
|
msgid "%s has been updated to version %s. See the <a href=\"http://calibre-ebook.com/whats-new\">new features</a>. Visit the download page?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -6138,40 +6150,40 @@ msgstr ""
|
|||||||
msgid "Title Case"
|
msgid "Title Case"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:301
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:330
|
||||||
msgid "If you use the WordPlayer e-book app on your Android phone, you can access your calibre book collection directly on the device. To do this you have to turn on the content server."
|
msgid "If you use the WordPlayer e-book app on your Android phone, you can access your calibre book collection directly on the device. To do this you have to turn on the content server."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:305
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:334
|
||||||
msgid "Remember to leave calibre running as the server only runs as long as calibre is running."
|
msgid "Remember to leave calibre running as the server only runs as long as calibre is running."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:307
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:336
|
||||||
msgid "You have to add the URL http://myhostname:8080 as your calibre library in WordPlayer. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on."
|
msgid "You have to add the URL http://myhostname:8080 as your calibre library in WordPlayer. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:384
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:413
|
||||||
msgid "Moving library..."
|
msgid "Moving library..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:400
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:429
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:401
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:430
|
||||||
msgid "Failed to move library"
|
msgid "Failed to move library"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:455
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:484
|
||||||
msgid "Invalid database"
|
msgid "Invalid database"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:456
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:485
|
||||||
msgid "<p>An invalid library already exists at %s, delete it before trying to move the existing library.<br>Error: %s"
|
msgid "<p>An invalid library already exists at %s, delete it before trying to move the existing library.<br>Error: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:467
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:496
|
||||||
msgid "Could not move library"
|
msgid "Could not move library"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:595
|
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:624
|
||||||
msgid "welcome wizard"
|
msgid "welcome wizard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -6719,6 +6731,10 @@ msgid ""
|
|||||||
"Start the calibre content server."
|
"Start the calibre content server."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/library/server.py:858
|
||||||
|
msgid "Path to the library folder to serve with the content server"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/config.py:48
|
#: /home/kovid/work/calibre/src/calibre/utils/config.py:48
|
||||||
msgid ""
|
msgid ""
|
||||||
"%sUsage%s: %s\n"
|
"%sUsage%s: %s\n"
|
||||||
@ -6837,14 +6853,18 @@ msgid "English (CY)"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104
|
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104
|
||||||
msgid "German (AT)"
|
msgid "English (PK)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105
|
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105
|
||||||
msgid "Dutch (NL)"
|
msgid "German (AT)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:106
|
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:106
|
||||||
|
msgid "Dutch (NL)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:107
|
||||||
msgid "Dutch (BE)"
|
msgid "Dutch (BE)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -6878,99 +6898,99 @@ msgstr ""
|
|||||||
msgid "Untitled article"
|
msgid "Untitled article"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:18
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:21
|
||||||
msgid "Download periodical content from the internet"
|
msgid "Download periodical content from the internet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:33
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:36
|
||||||
msgid "Useful for recipe development. Forces max_articles_per_feed to 2 and downloads at most 2 feeds."
|
msgid "Useful for recipe development. Forces max_articles_per_feed to 2 and downloads at most 2 feeds."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:36
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:39
|
||||||
msgid "Username for sites that require a login to access content."
|
msgid "Username for sites that require a login to access content."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:39
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:42
|
||||||
msgid "Password for sites that require a login to access content."
|
msgid "Password for sites that require a login to access content."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:43
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/input.py:46
|
||||||
msgid "Download latest version of builtin recipes"
|
msgid "Do not download latest version of builtin recipes from the calibre server"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:40
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:40
|
||||||
msgid "Unknown News Source"
|
msgid "Unknown News Source"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:513
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:520
|
||||||
msgid "The \"%s\" recipe needs a username and password."
|
msgid "The \"%s\" recipe needs a username and password."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:599
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:606
|
||||||
msgid "Download finished"
|
msgid "Download finished"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:601
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:608
|
||||||
msgid "Failed to download the following articles:"
|
msgid "Failed to download the following articles:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:607
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:614
|
||||||
msgid "Failed to download parts of the following articles:"
|
msgid "Failed to download parts of the following articles:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:609
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:616
|
||||||
msgid " from "
|
msgid " from "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:611
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:618
|
||||||
msgid "\tFailed links:"
|
msgid "\tFailed links:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:692
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:699
|
||||||
msgid "Could not fetch article. Run with -vv to see the reason"
|
msgid "Could not fetch article. Run with -vv to see the reason"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:713
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:720
|
||||||
msgid "Fetching feeds..."
|
msgid "Fetching feeds..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:718
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:725
|
||||||
msgid "Got feeds from index page"
|
msgid "Got feeds from index page"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:724
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:731
|
||||||
msgid "Trying to download cover..."
|
msgid "Trying to download cover..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:782
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:789
|
||||||
msgid "Starting download [%d thread(s)]..."
|
msgid "Starting download [%d thread(s)]..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:798
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:805
|
||||||
msgid "Feeds downloaded to %s"
|
msgid "Feeds downloaded to %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:808
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:815
|
||||||
msgid "Could not download cover: %s"
|
msgid "Could not download cover: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:815
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:827
|
||||||
msgid "Downloading cover from %s"
|
msgid "Downloading cover from %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:941
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:962
|
||||||
msgid "Untitled Article"
|
msgid "Untitled Article"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1012
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1033
|
||||||
msgid "Article downloaded: %s"
|
msgid "Article downloaded: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1023
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1044
|
||||||
msgid "Article download failed: %s"
|
msgid "Article download failed: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1040
|
#: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1061
|
||||||
msgid "Fetching feed"
|
msgid "Fetching feed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -819,10 +819,24 @@ class BasicNewsRecipe(Recipe):
|
|||||||
if '?' in ext:
|
if '?' in ext:
|
||||||
ext = ''
|
ext = ''
|
||||||
ext = ext.lower() if ext else 'jpg'
|
ext = ext.lower() if ext else 'jpg'
|
||||||
self.report_progress(1, _('Downloading cover from %s')%cu)
|
|
||||||
cpath = os.path.join(self.output_dir, 'cover.'+ext)
|
cpath = os.path.join(self.output_dir, 'cover.'+ext)
|
||||||
|
if os.access(cu, os.R_OK):
|
||||||
|
with open(cpath, 'wb') as cfile:
|
||||||
|
cfile.write(open(cu, 'rb').read())
|
||||||
|
else:
|
||||||
|
self.report_progress(1, _('Downloading cover from %s')%cu)
|
||||||
with nested(open(cpath, 'wb'), closing(self.browser.open(cu))) as (cfile, r):
|
with nested(open(cpath, 'wb'), closing(self.browser.open(cu))) as (cfile, r):
|
||||||
cfile.write(r.read())
|
cfile.write(r.read())
|
||||||
|
if ext.lower() == 'pdf':
|
||||||
|
from calibre.ebooks.metadata.pdf import get_metadata
|
||||||
|
stream = open(cpath, 'rb')
|
||||||
|
mi = get_metadata(stream)
|
||||||
|
cpath = None
|
||||||
|
if mi.cover_data and mi.cover_data[1]:
|
||||||
|
cpath = os.path.join(self.output_dir,
|
||||||
|
'cover.'+mi.cover_data[0])
|
||||||
|
with open(cpath, 'wb') as f:
|
||||||
|
f.write(mi.cover_data[1])
|
||||||
self.cover_path = cpath
|
self.cover_path = cpath
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user