mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Merge from trunk
This commit is contained in:
commit
f835fa2ad1
@ -35,7 +35,7 @@
|
||||
|
||||
- title: "Add an option under Preferences->Look & Feel->Book Details to hide the cover in the book details panel"
|
||||
|
||||
- title: "The Calibre Companion Android app that allows wireless connection of Android device to calibre is out of beta. See https://play.google.com/stor/apps/details?id=com.multipie.calibreandroid"
|
||||
- title: "The Calibre Companion Android app that allows wireless connection of Android device to calibre is out of beta. See https://play.google.com/store/apps/details?id=com.multipie.calibreandroid"
|
||||
|
||||
bug fixes:
|
||||
- title: "Fix sorting by author not working in the device view in calibre when connected to iTunes"
|
||||
|
@ -43,38 +43,38 @@ class Arcamax(BasicNewsRecipe):
|
||||
feeds = []
|
||||
for title, url in [
|
||||
######## COMICS - GENERAL ########
|
||||
#(u"9 Chickweed Lane", u"http://www.arcamax.com/ninechickweedlane"),
|
||||
#(u"Agnes", u"http://www.arcamax.com/agnes"),
|
||||
#(u"Andy Capp", u"http://www.arcamax.com/andycapp"),
|
||||
#(u"9 Chickweed Lane", #u"http://www.arcamax.com/thefunnies/ninechickweedlane"),
|
||||
#(u"Agnes", u"http://www.arcamax.com/thefunnies/agnes"),
|
||||
#(u"Andy Capp", #u"http://www.arcamax.com/thefunnies/andycapp"),
|
||||
(u"BC", u"http://www.arcamax.com/thefunnies/bc"),
|
||||
#(u"Baby Blues", u"http://www.arcamax.com/babyblues"),
|
||||
#(u"Beetle Bailey", u"http://www.arcamax.com/beetlebailey"),
|
||||
#(u"Baby Blues", #u"http://www.arcamax.com/thefunnies/babyblues"),
|
||||
#(u"Beetle Bailey", #u"http://www.arcamax.com/thefunnies/beetlebailey"),
|
||||
(u"Blondie", u"http://www.arcamax.com/thefunnies/blondie"),
|
||||
#u"Boondocks", u"http://www.arcamax.com/boondocks"),
|
||||
#(u"Cathy", u"http://www.arcamax.com/cathy"),
|
||||
#(u"Daddys Home", u"http://www.arcamax.com/daddyshome"),
|
||||
#u"Boondocks", u"http://www.arcamax.com/thefunnies/boondocks"),
|
||||
#(u"Cathy", u"http://www.arcamax.com/thefunnies/cathy"),
|
||||
#(u"Daddys Home", #u"http://www.arcamax.com/thefunnies/daddyshome"),
|
||||
(u"Dilbert", u"http://www.arcamax.com/thefunnies/dilbert"),
|
||||
#(u"Dinette Set", u"http://www.arcamax.com/thedinetteset"),
|
||||
#(u"Dinette Set", #u"http://www.arcamax.com/thefunnies/thedinetteset"),
|
||||
(u"Dog Eat Doug", u"http://www.arcamax.com/thefunnies/dogeatdoug"),
|
||||
(u"Doonesbury", u"http://www.arcamax.com/thefunnies/doonesbury"),
|
||||
#(u"Dustin", u"http://www.arcamax.com/dustin"),
|
||||
#(u"Dustin", u"http://www.arcamax.com/thefunnies/dustin"),
|
||||
(u"Family Circus", u"http://www.arcamax.com/thefunnies/familycircus"),
|
||||
(u"Garfield", u"http://www.arcamax.com/thefunnies/garfield"),
|
||||
#(u"Get Fuzzy", u"http://www.arcamax.com/getfuzzy"),
|
||||
#(u"Girls and Sports", u"http://www.arcamax.com/girlsandsports"),
|
||||
#(u"Hagar the Horrible", u"http://www.arcamax.com/hagarthehorrible"),
|
||||
#(u"Heathcliff", u"http://www.arcamax.com/heathcliff"),
|
||||
#(u"Jerry King Cartoons", u"http://www.arcamax.com/humorcartoon"),
|
||||
#(u"Luann", u"http://www.arcamax.com/luann"),
|
||||
#(u"Momma", u"http://www.arcamax.com/momma"),
|
||||
#(u"Mother Goose and Grimm", u"http://www.arcamax.com/mothergooseandgrimm"),
|
||||
#(u"Get Fuzzy", #u"http://www.arcamax.com/thefunnies/getfuzzy"),
|
||||
#(u"Girls and Sports", #u"http://www.arcamax.com/thefunnies/girlsandsports"),
|
||||
#(u"Hagar the Horrible", #u"http://www.arcamax.com/thefunnies/hagarthehorrible"),
|
||||
#(u"Heathcliff", #u"http://www.arcamax.com/thefunnies/heathcliff"),
|
||||
#(u"Jerry King Cartoons", #u"http://www.arcamax.com/thefunnies/humorcartoon"),
|
||||
#(u"Luann", u"http://www.arcamax.com/thefunnies/luann"),
|
||||
#(u"Momma", u"http://www.arcamax.com/thefunnies/momma"),
|
||||
#(u"Mother Goose and Grimm", #u"http://www.arcamax.com/thefunnies/mothergooseandgrimm"),
|
||||
(u"Mutts", u"http://www.arcamax.com/thefunnies/mutts"),
|
||||
#(u"Non Sequitur", u"http://www.arcamax.com/nonsequitur"),
|
||||
#(u"Pearls Before Swine", u"http://www.arcamax.com/pearlsbeforeswine"),
|
||||
#(u"Pickles", u"http://www.arcamax.com/pickles"),
|
||||
#(u"Red and Rover", u"http://www.arcamax.com/redandrover"),
|
||||
#(u"Rubes", u"http://www.arcamax.com/rubes"),
|
||||
#(u"Rugrats", u"http://www.arcamax.com/rugrats"),
|
||||
#(u"Non Sequitur", #u"http://www.arcamax.com/thefunnies/nonsequitur"),
|
||||
#(u"Pearls Before Swine", #u"http://www.arcamax.com/thefunnies/pearlsbeforeswine"),
|
||||
#(u"Pickles", u"http://www.arcamax.com/thefunnies/pickles"),
|
||||
#(u"Red and Rover", #u"http://www.arcamax.com/thefunnies/redandrover"),
|
||||
#(u"Rubes", u"http://www.arcamax.com/thefunnies/rubes"),
|
||||
#(u"Rugrats", u"http://www.arcamax.com/thefunnies/rugrats"),
|
||||
(u"Speed Bump", u"http://www.arcamax.com/thefunnies/speedbump"),
|
||||
(u"Wizard of Id", u"http://www.arcamax.com/thefunnies/wizardofid"),
|
||||
(u"Zits", u"http://www.arcamax.com/thefunnies/zits"),
|
||||
|
@ -1,14 +1,17 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||
title = u'Birmingham post'
|
||||
description = 'News for Birmingham UK'
|
||||
timefmt = ''
|
||||
description = 'Author D.Asbury. News for Birmingham UK'
|
||||
#timefmt = ''
|
||||
# last update 8/9/12
|
||||
__author__ = 'Dave Asbury'
|
||||
cover_url = 'http://1.bp.blogspot.com/_GwWyq5eGw9M/S9BHPHxW55I/AAAAAAAAB6Q/iGCWl0egGzg/s320/Birmingham+post+Lite+front.JPG'
|
||||
cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/161987_9010212100_2035706408_n.jpg'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 12
|
||||
linearize_tables = True
|
||||
remove_empty_feeds = True
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
#auto_cleanup = True
|
||||
language = 'en_GB'
|
||||
|
||||
@ -17,11 +20,12 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='h1',attrs={'id' : 'article-headline'}),
|
||||
dict(attrs={'id' : 'article-header'}),
|
||||
#dict(name='h1',attrs={'id' : 'article-header'}),
|
||||
dict(attrs={'class':['article-meta-author','article-meta-date','article main','art-o art-align-center otm-1 ']}),
|
||||
dict(name='div',attrs={'class' : 'article-image full'}),
|
||||
dict(attrs={'clas' : 'art-o art-align-center otm-1 '}),
|
||||
dict(name='div',attrs={'class' : 'article main'}),
|
||||
dict(name='div',attrs={'class' : 'article-image full'}),
|
||||
dict(attrs={'clas' : 'art-o art-align-center otm-1 '}),
|
||||
dict(name='div',attrs={'class' : 'article main'}),
|
||||
#dict(name='p')
|
||||
#dict(attrs={'id' : 'three-col'})
|
||||
]
|
||||
@ -37,11 +41,9 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||
(u'Bloggs & Comments',u'http://www.birminghampost.net/comment/rss.xml')
|
||||
|
||||
]
|
||||
extra_css = '''
|
||||
body {font: sans-serif medium;}'
|
||||
h1 {text-align : center; font-family:Arial,Helvetica,sans-serif; font-size:20px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold;}
|
||||
h2 {text-align : center;color:#4D4D4D;font-family:Arial,Helvetica,sans-serif; font-size:15px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; }
|
||||
span{ font-size:9.5px; font-weight:bold;font-style:italic}
|
||||
p { text-align: justify; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal;}
|
||||
|
||||
'''
|
||||
extra_css = '''
|
||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;text-align:center;}
|
||||
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||
'''
|
||||
|
@ -1,12 +1,11 @@
|
||||
from calibre import browser
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||
title = u'Countryfile.com'
|
||||
#cover_url = 'http://www.countryfile.com/sites/default/files/imagecache/160px_wide/cover/2_1.jpg'
|
||||
__author__ = 'Dave Asbury'
|
||||
description = 'The official website of Countryfile Magazine'
|
||||
# last updated 15/4/12
|
||||
# last updated 9/9//12
|
||||
language = 'en_GB'
|
||||
oldest_article = 30
|
||||
max_articles_per_feed = 25
|
||||
@ -17,13 +16,14 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||
def get_cover_url(self):
|
||||
soup = self.index_to_soup('http://www.countryfile.com/')
|
||||
cov = soup.find(attrs={'class' : 'imagecache imagecache-160px_wide imagecache-linked imagecache-160px_wide_linked'})
|
||||
#print '******** ',cov,' ***'
|
||||
print '******** ',cov,' ***'
|
||||
cov2 = str(cov)
|
||||
cov2=cov2[124:-90]
|
||||
#print '******** ',cov2,' ***'
|
||||
|
||||
cov2=cov2[140:223]
|
||||
print '******** ',cov2,' ***'
|
||||
#cov2='http://www.countryfile.com/sites/default/files/imagecache/160px_wide/cover/1b_0.jpg'
|
||||
# try to get cover - if can't get known cover
|
||||
br = browser()
|
||||
|
||||
br.set_handle_redirect(False)
|
||||
try:
|
||||
br.open_novisit(cov2)
|
||||
|
87
recipes/history_today.recipe
Normal file
87
recipes/history_today.recipe
Normal file
@ -0,0 +1,87 @@
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from collections import OrderedDict
|
||||
|
||||
class HistoryToday(BasicNewsRecipe):
|
||||
|
||||
title = 'History Today'
|
||||
__author__ = 'Rick Shang'
|
||||
|
||||
description = 'UK-based magazine, publishing articles and book reviews covering all types and periods of history.'
|
||||
language = 'en'
|
||||
category = 'news'
|
||||
encoding = 'UTF-8'
|
||||
|
||||
remove_tags = [dict(name='div',attrs={'class':['print-logo','print-site_name','print-breadcrumb']}),
|
||||
dict(name='div', attrs={'id':['ht-tools','ht-tools2','ht-tags']})]
|
||||
no_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
|
||||
needs_subscription = True
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
if self.username is not None and self.password is not None:
|
||||
br.open('http://www.historytoday.com/user/login')
|
||||
br.select_form(nr=1)
|
||||
br['name'] = self.username
|
||||
br['pass'] = self.password
|
||||
res = br.submit()
|
||||
raw = res.read()
|
||||
if 'Session limit exceeded' in raw:
|
||||
br.select_form(nr=1)
|
||||
control=br.find_control('sid').items[1]
|
||||
sid = []
|
||||
br['sid']=sid.join(control)
|
||||
br.submit()
|
||||
return br
|
||||
|
||||
def parse_index(self):
|
||||
|
||||
#Find date
|
||||
soup0 = self.index_to_soup('http://www.historytoday.com/')
|
||||
dates = self.tag_to_string(soup0.find('div',attrs={'id':'block-block-226'}).span)
|
||||
self.timefmt = u' [%s]'%dates
|
||||
|
||||
#Go to issue
|
||||
soup = self.index_to_soup('http://www.historytoday.com/contents')
|
||||
cover = soup.find('div',attrs={'id':'content-area'}).find('img')['src']
|
||||
self.cover_url=cover
|
||||
|
||||
#Go to the main body
|
||||
|
||||
div = soup.find ('div', attrs={'class':'region region-content-bottom'})
|
||||
|
||||
feeds = OrderedDict()
|
||||
section_title = ''
|
||||
for section in div.findAll('div', attrs={'id':re.compile("block\-views\-contents.*")}):
|
||||
section_title = self.tag_to_string(section.find('h2',attrs={'class':'title'}))
|
||||
sectionbody=section.find('div', attrs={'class':'view-content'})
|
||||
for article in sectionbody.findAll('div',attrs={'class':re.compile("views\-row.*")}):
|
||||
articles = []
|
||||
subarticle = []
|
||||
subarticle = article.findAll('div')
|
||||
if len(subarticle) < 2:
|
||||
continue
|
||||
title=self.tag_to_string(subarticle[0])
|
||||
originalurl="http://www.historytoday.com" + subarticle[0].span.a['href'].strip()
|
||||
originalpage=self.index_to_soup(originalurl)
|
||||
printurl=originalpage.find('div',attrs = {'id':'ht-tools'}).a['href'].strip()
|
||||
url="http://www.historytoday.com" + printurl
|
||||
desc=self.tag_to_string(subarticle[1])
|
||||
articles.append({'title':title, 'url':url, 'description':desc, 'date':''})
|
||||
|
||||
if articles:
|
||||
if section_title not in feeds:
|
||||
feeds[section_title] = []
|
||||
feeds[section_title] += articles
|
||||
|
||||
|
||||
ans = [(key, val) for key, val in feeds.iteritems()]
|
||||
return ans
|
||||
|
||||
|
||||
def cleanup(self):
|
||||
self.browser.open('http://www.historytoday.com/logout')
|
||||
|
@ -1,10 +1,10 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||
title = u'Metro UK'
|
||||
description = 'Author Dave Asbury : News as provide by The Metro -UK'
|
||||
description = 'Author Dave Asbury : News from The Metro - UK'
|
||||
#timefmt = ''
|
||||
__author__ = 'Dave Asbury'
|
||||
#last update 4/8/12
|
||||
#last update 9/9/12
|
||||
cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/276636_117118184990145_2132092232_n.jpg'
|
||||
no_stylesheets = True
|
||||
oldest_article = 1
|
||||
@ -17,23 +17,24 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||
language = 'en_GB'
|
||||
masthead_url = 'http://e-edition.metro.co.uk/images/metro_logo.gif'
|
||||
extra_css = '''
|
||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:1.6em;}
|
||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:900;font-size:1.6em;}
|
||||
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:1.2em;}
|
||||
p{font-family:Arial,Helvetica,sans-serif;font-size:1.0em;}
|
||||
body{font-family:Helvetica,Arial,sans-serif;font-size:1.0em;}
|
||||
'''
|
||||
'''
|
||||
keep_only_tags = [
|
||||
#dict(name='h1'),
|
||||
#dict(name='h2'),
|
||||
#dict(name='div', attrs={'class' : ['row','article','img-cnt figure','clrd']})
|
||||
#dict(name='h3'),
|
||||
#dict(attrs={'class' : 'BText'}),
|
||||
]
|
||||
#dict(name='h1'),
|
||||
#dict(name='h2'),
|
||||
#dict(name='div', attrs={'class' : ['row','article','img-cnt figure','clrd']})
|
||||
#dict(name='h3'),
|
||||
#dict(attrs={'class' : 'BText'}),
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name='div',attrs={'class' : 'art-fd fd-gr1-b clrd'}),
|
||||
dict(name='span',attrs={'class' : 'share'}),
|
||||
dict(name='li'),
|
||||
dict(attrs={'class' : ['twitter-share-button','header-forms','hdr-lnks','close','art-rgt','fd-gr1-b clrd google-article','news m12 clrd clr-b p5t shareBtm','item-ds csl-3-img news','c-1of3 c-last','c-1of1','pd','item-ds csl-3-img sport']}),
|
||||
dict(attrs={'id' : ['','sky-left','sky-right','ftr-nav','and-ftr','notificationList','logo','miniLogo','comments-news','metro_extras']})
|
||||
dict(name='li'),
|
||||
dict(attrs={'class' : ['twitter-share-button','header-forms','hdr-lnks','close','art-rgt','fd-gr1-b clrd google-article','news m12 clrd clr-b p5t shareBtm','item-ds csl-3-img news','c-1of3 c-last','c-1of1','pd','item-ds csl-3-img sport']}),
|
||||
dict(attrs={'id' : ['','sky-left','sky-right','ftr-nav','and-ftr','notificationList','logo','miniLogo','comments-news','metro_extras']})
|
||||
]
|
||||
remove_tags_before = dict(name='h1')
|
||||
#remove_tags_after = dict(attrs={'id':['topic-buttons']})
|
||||
|
@ -73,14 +73,20 @@ class AdvancedUserRecipe1249039563(BasicNewsRecipe):
|
||||
Change Log:
|
||||
Date: 10/15/2010
|
||||
Feeds updated by Martin Tarenskeen
|
||||
Date: 09/09/2012
|
||||
Feeds updated by Eric Lammerts
|
||||
'''
|
||||
|
||||
feeds = [
|
||||
(u'Laatste Nieuws', u'http://www.volkskrant.nl/rss/laatstenieuws.rss'),
|
||||
(u'Binnenland', u'http://www.volkskrant.nl/rss/nederland.rss'),
|
||||
(u'Buitenland', u'http://www.volkskrant.nl/rss/internationaal.rss'),
|
||||
(u'Economie', u'http://www.volkskrant.nl/rss/economie.rss'),
|
||||
(u'Sport', u'http://www.volkskrant.nl/rss/sport.rss'),
|
||||
(u'Cultuur', u'http://www.volkskrant.nl/rss/kunst.rss'),
|
||||
(u'Gezondheid & Wetenschap', u'http://www.volkskrant.nl/rss/wetenschap.rss'),
|
||||
(u'Internet & Media', u'http://www.volkskrant.nl/rss/media.rss') ]
|
||||
(u'Nieuws', u'http://www.volkskrant.nl/nieuws/rss.xml'),
|
||||
(u'Binnenland', u'http://www.volkskrant.nl/nieuws/binnenland/rss.xml'),
|
||||
(u'Buitenland', u'http://www.volkskrant.nl/buitenland/rss.xml'),
|
||||
(u'Economie', u'http://www.volkskrant.nl/nieuws/economie/rss.xml'),
|
||||
(u'Politiek', u'http://www.volkskrant.nl/politiek/rss.xml'),
|
||||
(u'Sport', u'http://www.volkskrant.nl/sport/rss.xml'),
|
||||
(u'Cultuur', u'http://www.volkskrant.nl/nieuws/cultuur/rss.xml'),
|
||||
(u'Gezondheid & wetenschap', u'http://www.volkskrant.nl/nieuws/gezondheid--wetenschap/rss.xml'),
|
||||
(u'Tech & Media', u'http://www.volkskrant.nl/tech-media/rss.xml'),
|
||||
(u'Reizen', u'http://www.volkskrant.nl/nieuws/reizen/rss.xml'),
|
||||
(u'Opinie', u'http://www.volkskrant.nl/opinie/rss.xml'),
|
||||
(u'Opmerkelijk', u'http://www.volkskrant.nl/nieuws/opmerkelijk/rss.xml') ]
|
||||
|
@ -118,13 +118,13 @@ class ZeitEPUBAbo(BasicNewsRecipe):
|
||||
|
||||
def build_index(self):
|
||||
domain = "https://premium.zeit.de"
|
||||
url = domain + "/abo/zeit_digital"
|
||||
url = domain + "/abo/digitalpaket"
|
||||
browser = self.get_browser()
|
||||
|
||||
# new login process
|
||||
response = browser.open(url)
|
||||
# Get rid of nested form
|
||||
response.set_data(response.get_data().replace('<div><form action="/abo/zeit_digital?destination=node%2F94" accept-charset="UTF-8" method="post" id="user-login-form" class="zol_inlinelabel">', ''))
|
||||
response.set_data(response.get_data().replace('<div><form action="/abo/digitalpaket?destination=node%2F94" accept-charset="UTF-8" method="post" id="user-login-form" class="zol_inlinelabel">', ''))
|
||||
browser.set_response(response)
|
||||
browser.select_form(nr=2)
|
||||
browser.form['name']=self.username
|
||||
@ -177,13 +177,13 @@ class ZeitEPUBAbo(BasicNewsRecipe):
|
||||
try:
|
||||
self.log.warning('Trying PDF-based cover')
|
||||
domain = "https://premium.zeit.de"
|
||||
url = domain + "/abo/zeit_digital"
|
||||
url = domain + "/abo/digitalpaket"
|
||||
browser = self.get_browser()
|
||||
|
||||
# new login process
|
||||
response=browser.open(url)
|
||||
# Get rid of nested form
|
||||
response.set_data(response.get_data().replace('<div><form action="/abo/zeit_digital?destination=node%2F94" accept-charset="UTF-8" method="post" id="user-login-form" class="zol_inlinelabel">', ''))
|
||||
response.set_data(response.get_data().replace('<div><form action="/abo/digitalpaket?destination=node%2F94" accept-charset="UTF-8" method="post" id="user-login-form" class="zol_inlinelabel">', ''))
|
||||
browser.set_response(response)
|
||||
|
||||
browser.select_form(nr=2)
|
||||
|
Binary file not shown.
@ -57,7 +57,7 @@ else:
|
||||
# On linux, unicode arguments to os file functions are coerced to an ascii
|
||||
# bytestring if sys.getfilesystemencoding() == 'ascii', which is
|
||||
# just plain dumb. So issue a warning.
|
||||
print ('WARNING: You do not have the LANG environment variable set. '
|
||||
print ('WARNING: You do not have the LANG environment variable set correctly. '
|
||||
'This will cause problems with non-ascii filenames. '
|
||||
'Set it to something like en_US.UTF-8.\n')
|
||||
except:
|
||||
|
@ -208,7 +208,7 @@ class ANDROID(USBMS):
|
||||
'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP',
|
||||
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C', 'PD',
|
||||
'PMP5097C', 'MASS', 'NOVO7', 'ZEKI', 'COBY', 'SXZ', 'USB_2.0',
|
||||
'COBY_MID']
|
||||
'COBY_MID', 'VS', 'AINOL']
|
||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID',
|
||||
@ -227,7 +227,8 @@ class ANDROID(USBMS):
|
||||
'GT-S5830L_CARD', 'UNIVERSE', 'XT875', 'PRO', '.KOBO_VOX',
|
||||
'THINKPAD_TABLET', 'SGH-T989', 'YP-G70', 'STORAGE_DEVICE',
|
||||
'ADVANCED', 'SGH-I727', 'USB_FLASH_DRIVER', 'ANDROID',
|
||||
'S5830I_CARD', 'MID7042', 'LINK-CREATE', '7035', 'VS']
|
||||
'S5830I_CARD', 'MID7042', 'LINK-CREATE', '7035', 'VIEWPAD_7E',
|
||||
'NOVO7']
|
||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
||||
@ -237,7 +238,8 @@ class ANDROID(USBMS):
|
||||
'USB_2.0_DRIVER', 'I9100T', 'P999DW_SD_CARD', 'KTABLET_PC',
|
||||
'FILE-CD_GADGET', 'GT-I9001_CARD', 'USB_2.0', 'XT875',
|
||||
'UMS_COMPOSITE', 'PRO', '.KOBO_VOX', 'SGH-T989_CARD', 'SGH-I727',
|
||||
'USB_FLASH_DRIVER', 'ANDROID', 'MID7042', '7035', 'VIEWPAD_7E']
|
||||
'USB_FLASH_DRIVER', 'ANDROID', 'MID7042', '7035', 'VIEWPAD_7E',
|
||||
'NOVO7']
|
||||
|
||||
OSX_MAIN_MEM = 'Android Device Main Memory'
|
||||
|
||||
|
@ -110,3 +110,9 @@ class WrongDestinationError(PathError):
|
||||
trying to send books to a non existant storage card.'''
|
||||
pass
|
||||
|
||||
class BlacklistedDevice(OpenFailed):
|
||||
''' Raise this error during open() when the device being opened has been
|
||||
blacklisted by the user. Only used in drivers that manage device presence,
|
||||
like the MTP driver. '''
|
||||
pass
|
||||
|
||||
|
@ -59,4 +59,7 @@ class MTPDeviceBase(DevicePlugin):
|
||||
from calibre.devices.utils import build_template_regexp
|
||||
return build_template_regexp(self.save_template)
|
||||
|
||||
def is_customizable(self):
|
||||
return True
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@ from calibre.constants import iswindows, numeric_version
|
||||
from calibre.devices.mtp.base import debug
|
||||
from calibre.ptempfile import SpooledTemporaryFile, PersistentTemporaryDirectory
|
||||
from calibre.utils.config import from_json, to_json, JSONConfig
|
||||
from calibre.utils.date import now, isoformat
|
||||
from calibre.utils.date import now, isoformat, utcnow
|
||||
|
||||
BASE = importlib.import_module('calibre.devices.mtp.%s.driver'%(
|
||||
'windows' if iswindows else 'unix')).MTP_DEVICE
|
||||
@ -51,6 +51,8 @@ class MTP_DEVICE(BASE):
|
||||
'wordplayer/calibretransfer', 'Books', 'sdcard/ebooks',
|
||||
'eBooks', 'kindle']
|
||||
p.defaults['send_template'] = config().parse().send_template
|
||||
p.defaults['blacklist'] = []
|
||||
p.defaults['history'] = {}
|
||||
|
||||
return self._prefs
|
||||
|
||||
@ -74,6 +76,11 @@ class MTP_DEVICE(BASE):
|
||||
self.current_library_uuid = library_uuid
|
||||
self.location_paths = None
|
||||
BASE.open(self, devices, library_uuid)
|
||||
h = self.prefs['history']
|
||||
if self.current_serial_num:
|
||||
h[self.current_serial_num] = (self.current_friendly_name,
|
||||
isoformat(utcnow()))
|
||||
self.prefs['history'] = h
|
||||
|
||||
# Device information {{{
|
||||
def _update_drive_info(self, storage, location_code, name=None):
|
||||
|
@ -15,7 +15,7 @@ from functools import partial
|
||||
from calibre import prints, as_unicode
|
||||
from calibre.constants import plugins
|
||||
from calibre.ptempfile import SpooledTemporaryFile
|
||||
from calibre.devices.errors import OpenFailed, DeviceError
|
||||
from calibre.devices.errors import OpenFailed, DeviceError, BlacklistedDevice
|
||||
from calibre.devices.mtp.base import MTPDeviceBase, synchronous
|
||||
|
||||
MTPDevice = namedtuple('MTPDevice', 'busnum devnum vendor_id product_id '
|
||||
@ -99,19 +99,25 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
return False
|
||||
p('Known MTP devices connected:')
|
||||
for d in devs: p(d)
|
||||
d = devs[0]
|
||||
p('\nTrying to open:', d)
|
||||
try:
|
||||
self.open(d, 'debug')
|
||||
except:
|
||||
p('Opening device failed:')
|
||||
p(traceback.format_exc())
|
||||
return False
|
||||
p('Opened', self.current_friendly_name, 'successfully')
|
||||
p('Storage info:')
|
||||
p(pprint.pformat(self.dev.storage_info))
|
||||
self.eject()
|
||||
return True
|
||||
|
||||
for d in devs:
|
||||
p('\nTrying to open:', d)
|
||||
try:
|
||||
self.open(d, 'debug')
|
||||
except BlacklistedDevice:
|
||||
p('This device has been blacklisted by the user')
|
||||
continue
|
||||
except:
|
||||
p('Opening device failed:')
|
||||
p(traceback.format_exc())
|
||||
return False
|
||||
else:
|
||||
p('Opened', self.current_friendly_name, 'successfully')
|
||||
p('Storage info:')
|
||||
p(pprint.pformat(self.dev.storage_info))
|
||||
self.post_yank_cleanup()
|
||||
return True
|
||||
return False
|
||||
|
||||
@synchronous
|
||||
def create_device(self, connected_device):
|
||||
@ -167,6 +173,12 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
if not storage:
|
||||
self.blacklisted_devices.add(connected_device)
|
||||
raise OpenFailed('No storage found for device %s'%(connected_device,))
|
||||
snum = self.dev.serial_number
|
||||
if snum in self.prefs.get('blacklist', []):
|
||||
self.blacklisted_devices.add(connected_device)
|
||||
self.dev = None
|
||||
raise BlacklistedDevice(
|
||||
'The %s device has been blacklisted by the user'%(connected_device,))
|
||||
self._main_id = storage[0]['id']
|
||||
self._carda_id = self._cardb_id = None
|
||||
if len(storage) > 1:
|
||||
@ -176,7 +188,7 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
self.current_friendly_name = self.dev.friendly_name
|
||||
if not self.current_friendly_name:
|
||||
self.current_friendly_name = self.dev.model_name or _('Unknown MTP device')
|
||||
self.current_serial_num = self.dev.serial_number
|
||||
self.current_serial_num = snum
|
||||
|
||||
@property
|
||||
def filesystem_cache(self):
|
||||
|
@ -15,7 +15,7 @@ from itertools import chain
|
||||
from calibre import as_unicode, prints
|
||||
from calibre.constants import plugins, __appname__, numeric_version
|
||||
from calibre.ptempfile import SpooledTemporaryFile
|
||||
from calibre.devices.errors import OpenFailed, DeviceError
|
||||
from calibre.devices.errors import OpenFailed, DeviceError, BlacklistedDevice
|
||||
from calibre.devices.mtp.base import MTPDeviceBase
|
||||
|
||||
class ThreadingViolation(Exception):
|
||||
@ -163,6 +163,9 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
p('\nTrying to open:', pnp_id)
|
||||
try:
|
||||
self.open(pnp_id, 'debug-detection')
|
||||
except BlacklistedDevice:
|
||||
p('This device has been blacklisted by the user')
|
||||
continue
|
||||
except:
|
||||
p('Open failed:')
|
||||
p(traceback.format_exc())
|
||||
@ -172,7 +175,7 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
p('Opened', self.current_friendly_name, 'successfully')
|
||||
p('Device info:')
|
||||
p(pprint.pformat(self.dev.data))
|
||||
self.eject()
|
||||
self.post_yank_cleanup()
|
||||
return True
|
||||
p('No suitable MTP devices found')
|
||||
return False
|
||||
@ -225,7 +228,6 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
self._main_id = self._carda_id = self._cardb_id = None
|
||||
self.dev = self._filesystem_cache = None
|
||||
|
||||
|
||||
@same_thread
|
||||
def post_yank_cleanup(self):
|
||||
self.currently_connected_pnp_id = self.current_friendly_name = None
|
||||
@ -256,6 +258,13 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
if not storage:
|
||||
self.blacklisted_devices.add(connected_device)
|
||||
raise OpenFailed('No storage found for device %s'%(connected_device,))
|
||||
snum = devdata.get('serial_number', None)
|
||||
if snum in self.prefs.get('blacklist', []):
|
||||
self.blacklisted_devices.add(connected_device)
|
||||
self.dev = None
|
||||
raise BlacklistedDevice(
|
||||
'The %s device has been blacklisted by the user'%(connected_device,))
|
||||
|
||||
self._main_id = storage[0]['id']
|
||||
if len(storage) > 1:
|
||||
self._carda_id = storage[1]['id']
|
||||
@ -266,7 +275,7 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
self.current_friendly_name = devdata.get('model_name',
|
||||
_('Unknown MTP device'))
|
||||
self.currently_connected_pnp_id = connected_device
|
||||
self.current_serial_num = devdata.get('serial_number', None)
|
||||
self.current_serial_num = snum
|
||||
|
||||
@same_thread
|
||||
def get_basic_device_information(self):
|
||||
|
@ -329,8 +329,48 @@ class DeviceScanner(object):
|
||||
return device.is_usb_connected(self.devices, debug=debug,
|
||||
only_presence=only_presence)
|
||||
|
||||
def test_for_mem_leak():
|
||||
from calibre.utils.mem import memory, gc_histogram, diff_hists
|
||||
import gc
|
||||
gc.disable()
|
||||
scanner = DeviceScanner()
|
||||
scanner.scan()
|
||||
memory() # load the psutil library
|
||||
for i in xrange(3): gc.collect()
|
||||
|
||||
for reps in (1, 10, 100, 1000):
|
||||
for i in xrange(3): gc.collect()
|
||||
h1 = gc_histogram()
|
||||
startmem = memory()
|
||||
for i in xrange(reps):
|
||||
scanner.scan()
|
||||
for i in xrange(3): gc.collect()
|
||||
usedmem = memory(startmem)
|
||||
prints('Memory used in %d repetitions of scan(): %.5f KB'%(reps,
|
||||
1024*usedmem))
|
||||
prints('Differences in python object counts:')
|
||||
diff_hists(h1, gc_histogram())
|
||||
prints()
|
||||
|
||||
if not iswindows:
|
||||
return
|
||||
|
||||
for reps in (1, 10, 100, 1000):
|
||||
for i in xrange(3): gc.collect()
|
||||
h1 = gc_histogram()
|
||||
startmem = memory()
|
||||
for i in xrange(reps):
|
||||
win_pnp_drives()
|
||||
for i in xrange(3): gc.collect()
|
||||
usedmem = memory(startmem)
|
||||
prints('Memory used in %d repetitions of pnp_scan(): %.5f KB'%(reps,
|
||||
1024*usedmem))
|
||||
prints('Differences in python object counts:')
|
||||
diff_hists(h1, gc_histogram())
|
||||
prints()
|
||||
|
||||
def main(args=sys.argv):
|
||||
test_for_mem_leak()
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -974,6 +974,9 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
def get_carda_ebook_dir(self, for_upload=False):
|
||||
return self.EBOOK_DIR_CARD_A
|
||||
|
||||
def get_cardb_ebook_dir(self, for_upload=False):
|
||||
return self.EBOOK_DIR_CARD_B
|
||||
|
||||
def _sanity_check(self, on_card, files):
|
||||
from calibre.devices.utils import sanity_check
|
||||
sanity_check(on_card, files, self.card_prefix(), self.free_space())
|
||||
@ -993,10 +996,10 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
|
||||
if on_card == 'carda':
|
||||
candidates = self.get_carda_ebook_dir(for_upload=True)
|
||||
path = get_dest_dir(self._carda_prefix, candidates)
|
||||
path = get_dest_dir(self._card_a_prefix, candidates)
|
||||
elif on_card == 'cardb':
|
||||
candidates = self.get_cardb_ebook_dir(for_upload=True)
|
||||
path = get_dest_dir(self._cardb_prefix, candidates)
|
||||
path = get_dest_dir(self._card_b_prefix, candidates)
|
||||
else:
|
||||
candidates = self.get_main_ebook_dir(for_upload=True)
|
||||
path = get_dest_dir(self._main_prefix, candidates)
|
||||
|
@ -39,6 +39,7 @@ class MathJax
|
||||
showMathMenu: false,
|
||||
extensions: ["tex2jax.js", "asciimath2jax.js", "mml2jax.js"],
|
||||
jax: ["input/TeX","input/MathML","input/AsciiMath","output/SVG"],
|
||||
// SVG : { linebreaks : { automatic : true } },
|
||||
TeX: {
|
||||
extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ class EditMetadataAction(InterfaceAction):
|
||||
Dispatcher(self.metadata_downloaded),
|
||||
ensure_fields=ensure_fields)
|
||||
|
||||
def cleanup_bulk_download(self, tdir):
|
||||
def cleanup_bulk_download(self, tdir, *args):
|
||||
try:
|
||||
shutil.rmtree(tdir, ignore_errors=True)
|
||||
except:
|
||||
@ -108,22 +108,26 @@ class EditMetadataAction(InterfaceAction):
|
||||
'Proceed with updating the metadata in your library?')%len(id_map)
|
||||
|
||||
show_copy_button = False
|
||||
checkbox_msg = None
|
||||
if failed_ids or failed_covers:
|
||||
show_copy_button = True
|
||||
num = len(failed_ids.union(failed_covers))
|
||||
msg += '<p>'+_('Could not download metadata and/or covers for %d of the books. Click'
|
||||
' "Show details" to see which books.')%num
|
||||
checkbox_msg = _('Show the &failed books in the main book list '
|
||||
'after updating metadata')
|
||||
|
||||
payload = (id_map, tdir, log_file, lm_map)
|
||||
self.gui.proceed_question(self.apply_downloaded_metadata,
|
||||
payload, log_file,
|
||||
_('Download log'), _('Download complete'), msg,
|
||||
payload = (id_map, tdir, log_file, lm_map,
|
||||
failed_ids.union(failed_covers))
|
||||
self.gui.proceed_question(self.apply_downloaded_metadata, payload,
|
||||
log_file, _('Download log'), _('Download complete'), msg,
|
||||
det_msg=det_msg, show_copy_button=show_copy_button,
|
||||
cancel_callback=lambda x:self.cleanup_bulk_download(tdir),
|
||||
log_is_file=True)
|
||||
cancel_callback=partial(self.cleanup_bulk_download, tdir),
|
||||
log_is_file=True, checkbox_msg=checkbox_msg,
|
||||
checkbox_checked=False)
|
||||
|
||||
def apply_downloaded_metadata(self, payload):
|
||||
good_ids, tdir, log_file, lm_map = payload
|
||||
def apply_downloaded_metadata(self, payload, *args):
|
||||
good_ids, tdir, log_file, lm_map, failed_ids = payload
|
||||
if not good_ids:
|
||||
return
|
||||
|
||||
@ -162,8 +166,18 @@ class EditMetadataAction(InterfaceAction):
|
||||
cov = None
|
||||
id_map[bid] = (opf, cov)
|
||||
|
||||
self.apply_metadata_changes(id_map, callback=lambda x:
|
||||
self.cleanup_bulk_download(tdir))
|
||||
restrict_to_failed = bool(args and args[0])
|
||||
if restrict_to_failed:
|
||||
db.data.set_marked_ids(failed_ids)
|
||||
|
||||
self.apply_metadata_changes(id_map,
|
||||
callback=partial(self.downloaded_metadata_applied, tdir,
|
||||
restrict_to_failed))
|
||||
|
||||
def downloaded_metadata_applied(self, tdir, restrict_to_failed, *args):
|
||||
if restrict_to_failed:
|
||||
self.gui.search.set_search_string('marked:true')
|
||||
self.cleanup_bulk_download(tdir)
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -87,7 +87,7 @@ class PluginWidget(QWidget,Ui_Form):
|
||||
option_fields += zip(['exclusion_rules_tw'],
|
||||
[{'ordinal':0,
|
||||
'enabled':True,
|
||||
'name':'Catalogs',
|
||||
'name':_('Catalogs'),
|
||||
'field':'Tags',
|
||||
'pattern':'Catalog'},],
|
||||
['table_widget'])
|
||||
@ -96,13 +96,13 @@ class PluginWidget(QWidget,Ui_Form):
|
||||
option_fields += zip(['prefix_rules_tw','prefix_rules_tw'],
|
||||
[{'ordinal':0,
|
||||
'enabled':True,
|
||||
'name':'Read book',
|
||||
'name':_('Read book'),
|
||||
'field':'Tags',
|
||||
'pattern':'+',
|
||||
'prefix':u'\u2713'},
|
||||
{'ordinal':1,
|
||||
'enabled':True,
|
||||
'name':'Wishlist item',
|
||||
'name':_('Wishlist item'),
|
||||
'field':'Tags',
|
||||
'pattern':'Wishlist',
|
||||
'prefix':u'\u00d7'},],
|
||||
@ -612,7 +612,7 @@ class GenericRulesTable(QTableWidget):
|
||||
first_rule_name = unicode(self.cellWidget(first-1,self.COLUMNS['NAME']['ordinal']).text()).strip()
|
||||
message = _("Are you sure you want to delete '%s'?") % (first_rule_name)
|
||||
if len(rows) > 1:
|
||||
message = _('Are you sure you want to delete rules #%d-%d?') % (first, last)
|
||||
message = _('Are you sure you want to delete rules #%(first)d-%(last)d?') % dict(first=first, last=last)
|
||||
if not question_dialog(self, _('Delete Rule'), message, show_copy_button=False):
|
||||
return
|
||||
first_sel_row = self.currentRow()
|
||||
|
@ -24,7 +24,8 @@ from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic,
|
||||
from calibre.ebooks.metadata import authors_to_string
|
||||
from calibre import preferred_encoding, prints, force_unicode, as_unicode
|
||||
from calibre.utils.filenames import ascii_filename
|
||||
from calibre.devices.errors import FreeSpaceError, WrongDestinationError
|
||||
from calibre.devices.errors import (FreeSpaceError, WrongDestinationError,
|
||||
BlacklistedDevice)
|
||||
from calibre.devices.apple.driver import ITUNES_ASYNC
|
||||
from calibre.devices.folder_device.driver import FOLDER_DEVICE
|
||||
from calibre.devices.bambook.driver import BAMBOOK, BAMBOOKWifi
|
||||
@ -252,6 +253,9 @@ class DeviceManager(Thread): # {{{
|
||||
if cd is not None:
|
||||
try:
|
||||
dev.open(cd, self.current_library_uuid)
|
||||
except BlacklistedDevice as e:
|
||||
prints('Ignoring blacklisted device: %s'%
|
||||
as_unicode(e))
|
||||
except:
|
||||
prints('Error while trying to open %s (Driver: %s)'%
|
||||
(cd, dev))
|
||||
|
@ -16,6 +16,7 @@ from PyQt4.Qt import (QWidget, QListWidgetItem, Qt, QToolButton, QLabel,
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||
from calibre.utils.date import parse_date
|
||||
|
||||
class FormatsConfig(QWidget): # {{{
|
||||
|
||||
@ -136,6 +137,45 @@ class SendToConfig(QWidget): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
class IgnoredDevices(QWidget): # {{{
|
||||
|
||||
def __init__(self, devs, blacklist):
|
||||
QWidget.__init__(self)
|
||||
self.l = l = QVBoxLayout()
|
||||
self.setLayout(l)
|
||||
self.la = la = QLabel('<p>'+_(
|
||||
'''Select the devices to be <b>ignored</b>. calibre will not
|
||||
connect to devices with a checkmark next to their names.'''))
|
||||
la.setWordWrap(True)
|
||||
l.addWidget(la)
|
||||
self.f = f = QListWidget(self)
|
||||
l.addWidget(f)
|
||||
|
||||
devs = [(snum, (x[0], parse_date(x[1]))) for snum, x in
|
||||
devs.iteritems()]
|
||||
for dev, x in sorted(devs, key=lambda x:x[1][1], reverse=True):
|
||||
name = x[0]
|
||||
name = '%s [%s]'%(name, dev)
|
||||
item = QListWidgetItem(name, f)
|
||||
item.setData(Qt.UserRole, dev)
|
||||
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
|
||||
item.setCheckState(Qt.Checked if dev in blacklist else Qt.Unchecked)
|
||||
|
||||
@property
|
||||
def blacklist(self):
|
||||
return [unicode(self.f.item(i).data(Qt.UserRole).toString()) for i in
|
||||
xrange(self.f.count()) if self.f.item(i).checkState()==Qt.Checked]
|
||||
|
||||
def ignore_device(self, snum):
|
||||
for i in xrange(self.f.count()):
|
||||
i = self.f.item(i)
|
||||
c = unicode(i.data(Qt.UserRole).toString())
|
||||
if c == snum:
|
||||
i.setCheckState(Qt.Checked)
|
||||
break
|
||||
|
||||
# }}}
|
||||
|
||||
class MTPConfig(QTabWidget):
|
||||
|
||||
def __init__(self, device, parent=None):
|
||||
@ -162,6 +202,8 @@ class MTPConfig(QTabWidget):
|
||||
l = QLabel(msg)
|
||||
l.setWordWrap(True)
|
||||
l.setStyleSheet('QLabel { margin-left: 2em }')
|
||||
l.setMinimumWidth(500)
|
||||
l.setMinimumHeight(400)
|
||||
self.insertTab(0, l, _('Cannot configure'))
|
||||
else:
|
||||
self.base = QWidget(self)
|
||||
@ -173,16 +215,34 @@ class MTPConfig(QTabWidget):
|
||||
self.get_pref('format_map'))
|
||||
self.send_to = SendToConfig(self.get_pref('send_to'))
|
||||
self.template = TemplateConfig(self.get_pref('send_template'))
|
||||
self.base.la = la = QLabel(_('Choose the formats to send to the %s')%self.device.current_friendly_name)
|
||||
self.base.la = la = QLabel(_(
|
||||
'Choose the formats to send to the %s')%self.device.current_friendly_name)
|
||||
la.setWordWrap(True)
|
||||
l.addWidget(la, 0, 0, 1, 1)
|
||||
l.addWidget(self.formats, 1, 0, 3, 1)
|
||||
l.addWidget(self.formats, 1, 0, 2, 1)
|
||||
l.addWidget(self.send_to, 1, 1, 1, 1)
|
||||
l.addWidget(self.template, 2, 1, 1, 1)
|
||||
l.setRowStretch(2, 10)
|
||||
self.base.b = b = QPushButton(QIcon(I('minus.png')),
|
||||
_('Ignore the %s in calibre')%device.current_friendly_name,
|
||||
self.base)
|
||||
l.addWidget(b, 3, 0, 1, 2)
|
||||
b.clicked.connect(self.ignore_device)
|
||||
|
||||
self.igntab = IgnoredDevices(self.device.prefs['history'],
|
||||
self.device.prefs['blacklist'])
|
||||
self.addTab(self.igntab, _('Ignored devices'))
|
||||
|
||||
self.setCurrentIndex(0)
|
||||
|
||||
def ignore_device(self):
|
||||
self.igntab.ignore_device(self.device.current_serial_num)
|
||||
self.base.b.setEnabled(False)
|
||||
self.base.b.setText(_('The %s will be ignored in calibre')%
|
||||
self.device.current_friendly_name)
|
||||
self.base.b.setStyleSheet('QPushButton { font-weight: bold }')
|
||||
self.base.setEnabled(False)
|
||||
|
||||
def get_pref(self, key):
|
||||
p = self.device.prefs.get(self.current_device_key, {})
|
||||
if not p:
|
||||
@ -194,31 +254,35 @@ class MTPConfig(QTabWidget):
|
||||
return self._device()
|
||||
|
||||
def validate(self):
|
||||
if not self.formats.validate():
|
||||
return False
|
||||
if not self.template.validate():
|
||||
return False
|
||||
if hasattr(self, 'formats'):
|
||||
if not self.formats.validate():
|
||||
return False
|
||||
if not self.template.validate():
|
||||
return False
|
||||
return True
|
||||
|
||||
def commit(self):
|
||||
p = self.device.prefs.get(self.current_device_key, {})
|
||||
|
||||
p.pop('format_map', None)
|
||||
f = self.formats.format_map
|
||||
if f and f != self.device.prefs['format_map']:
|
||||
p['format_map'] = f
|
||||
if hasattr(self, 'formats'):
|
||||
p.pop('format_map', None)
|
||||
f = self.formats.format_map
|
||||
if f and f != self.device.prefs['format_map']:
|
||||
p['format_map'] = f
|
||||
|
||||
p.pop('send_template', None)
|
||||
t = self.template.template
|
||||
if t and t != self.device.prefs['send_template']:
|
||||
p['send_template'] = t
|
||||
p.pop('send_template', None)
|
||||
t = self.template.template
|
||||
if t and t != self.device.prefs['send_template']:
|
||||
p['send_template'] = t
|
||||
|
||||
p.pop('send_to', None)
|
||||
s = self.send_to.value
|
||||
if s and s != self.device.prefs['send_to']:
|
||||
p['send_to'] = s
|
||||
p.pop('send_to', None)
|
||||
s = self.send_to.value
|
||||
if s and s != self.device.prefs['send_to']:
|
||||
p['send_to'] = s
|
||||
|
||||
self.device.prefs[self.current_device_key] = p
|
||||
self.device.prefs[self.current_device_key] = p
|
||||
|
||||
self.device.prefs['blacklist'] = self.igntab.blacklist
|
||||
|
||||
if __name__ == '__main__':
|
||||
from calibre.gui2 import Application
|
||||
|
@ -11,18 +11,18 @@ from collections import namedtuple
|
||||
|
||||
from PyQt4.Qt import (QDialog, Qt, QLabel, QGridLayout, QPixmap,
|
||||
QDialogButtonBox, QApplication, QSize, pyqtSignal, QIcon,
|
||||
QPlainTextEdit)
|
||||
QPlainTextEdit, QCheckBox)
|
||||
|
||||
from calibre.constants import __version__
|
||||
from calibre.gui2.dialogs.message_box import ViewLog
|
||||
|
||||
Question = namedtuple('Question', 'payload callback cancel_callback '
|
||||
'title msg html_log log_viewer_title log_is_file det_msg '
|
||||
'show_copy_button')
|
||||
'show_copy_button checkbox_msg checkbox_checked')
|
||||
|
||||
class ProceedQuestion(QDialog):
|
||||
|
||||
ask_question = pyqtSignal(object, object)
|
||||
ask_question = pyqtSignal(object, object, object)
|
||||
|
||||
def __init__(self, parent):
|
||||
QDialog.__init__(self, parent)
|
||||
@ -62,10 +62,13 @@ class ProceedQuestion(QDialog):
|
||||
self.bb.setStandardButtons(self.bb.Yes|self.bb.No)
|
||||
self.bb.button(self.bb.Yes).setDefault(True)
|
||||
|
||||
self.checkbox = QCheckBox('', self)
|
||||
|
||||
l.addWidget(ic, 0, 0, 1, 1)
|
||||
l.addWidget(msg, 0, 1, 1, 1)
|
||||
l.addWidget(self.det_msg, 1, 0, 1, 2)
|
||||
l.addWidget(self.bb, 2, 0, 1, 2)
|
||||
l.addWidget(self.checkbox, 1, 0, 1, 2)
|
||||
l.addWidget(self.det_msg, 2, 0, 1, 2)
|
||||
l.addWidget(self.bb, 3, 0, 1, 2)
|
||||
|
||||
self.ask_question.connect(self.do_ask_question,
|
||||
type=Qt.QueuedConnection)
|
||||
@ -82,19 +85,28 @@ class ProceedQuestion(QDialog):
|
||||
if self.questions:
|
||||
payload, callback, cancel_callback = self.questions[0][:3]
|
||||
self.questions = self.questions[1:]
|
||||
self.ask_question.emit(callback, payload)
|
||||
cb = None
|
||||
if self.checkbox.isVisible():
|
||||
cb = bool(self.checkbox.isChecked())
|
||||
self.ask_question.emit(callback, payload, cb)
|
||||
self.hide()
|
||||
|
||||
def reject(self):
|
||||
if self.questions:
|
||||
payload, callback, cancel_callback = self.questions[0][:3]
|
||||
self.questions = self.questions[1:]
|
||||
self.ask_question.emit(cancel_callback, payload)
|
||||
cb = None
|
||||
if self.checkbox.isVisible():
|
||||
cb = bool(self.checkbox.isChecked())
|
||||
self.ask_question.emit(cancel_callback, payload, cb)
|
||||
self.hide()
|
||||
|
||||
def do_ask_question(self, callback, payload):
|
||||
def do_ask_question(self, callback, payload, checkbox_checked):
|
||||
if callable(callback):
|
||||
callback(payload)
|
||||
args = [payload]
|
||||
if checkbox_checked is not None:
|
||||
args.append(checkbox_checked)
|
||||
callback(*args)
|
||||
self.show_question()
|
||||
|
||||
def toggle_det_msg(self, *args):
|
||||
@ -122,6 +134,10 @@ class ProceedQuestion(QDialog):
|
||||
self.det_msg.setVisible(False)
|
||||
self.det_msg_toggle.setVisible(bool(question.det_msg))
|
||||
self.det_msg_toggle.setText(self.show_det_msg)
|
||||
self.checkbox.setVisible(question.checkbox_msg is not None)
|
||||
if question.checkbox_msg is not None:
|
||||
self.checkbox.setText(question.checkbox_msg)
|
||||
self.checkbox.setChecked(question.checkbox_checked)
|
||||
self.do_resize()
|
||||
self.show()
|
||||
self.bb.button(self.bb.Yes).setDefault(True)
|
||||
@ -129,10 +145,10 @@ class ProceedQuestion(QDialog):
|
||||
|
||||
def __call__(self, callback, payload, html_log, log_viewer_title, title,
|
||||
msg, det_msg='', show_copy_button=False, cancel_callback=None,
|
||||
log_is_file=False):
|
||||
log_is_file=False, checkbox_msg=None, checkbox_checked=False):
|
||||
'''
|
||||
A non modal popup that notifies the user that a background task has
|
||||
been completed. This class guarantees that onlya single popup is
|
||||
been completed. This class guarantees that only a single popup is
|
||||
visible at any one time. Other requests are queued and displayed after
|
||||
the user dismisses the current popup.
|
||||
|
||||
@ -147,11 +163,18 @@ class ProceedQuestion(QDialog):
|
||||
:param msg: The msg to display
|
||||
:param det_msg: Detailed message
|
||||
:param log_is_file: If True the html_log parameter is interpreted as
|
||||
the path to a file on disk containing the log encoded with utf-8
|
||||
the path to a file on disk containing the log
|
||||
encoded with utf-8
|
||||
:param checkbox_msg: If not None, a checkbox is displayed in the
|
||||
dialog, showing this message. The callback is
|
||||
called with both the payload and the state of the
|
||||
checkbox as arguments.
|
||||
:param checkbox_checked: If True the checkbox is checked by default.
|
||||
|
||||
'''
|
||||
question = Question(payload, callback, cancel_callback, title, msg,
|
||||
html_log, log_viewer_title, log_is_file, det_msg,
|
||||
show_copy_button)
|
||||
show_copy_button, checkbox_msg, checkbox_checked)
|
||||
self.questions.append(question)
|
||||
self.show_question()
|
||||
|
||||
@ -169,7 +192,8 @@ def main():
|
||||
from calibre.gui2 import Application
|
||||
app = Application([])
|
||||
p = ProceedQuestion(None)
|
||||
p(lambda p:None, None, 'ass', 'ass', 'testing', 'testing')
|
||||
p(lambda p:None, None, 'ass', 'ass', 'testing', 'testing',
|
||||
checkbox_msg='testing the ruddy checkbox', det_msg='details')
|
||||
p.exec_()
|
||||
app
|
||||
|
||||
|
@ -75,7 +75,7 @@ class UpdateNotification(QDialog):
|
||||
self.logo.setPixmap(QPixmap(I('lt.png')).scaled(100, 100,
|
||||
Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
|
||||
self.label = QLabel(('<p>'+
|
||||
_('%(app)s has been updated to version <b>%(ver)s</b>. '
|
||||
_('New version <b>%(ver)s</b> of %(app)s is available for download. '
|
||||
'See the <a href="http://calibre-ebook.com/whats-new'
|
||||
'">new features</a>.'))%dict(
|
||||
app=__appname__, ver=calibre_version))
|
||||
|
@ -59,7 +59,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
"Applies to: AZW3, ePub, MOBI output formats")),
|
||||
|
||||
Option('--exclusion-rules',
|
||||
default="(('Excluded tags','Tags','Catalog'),)",
|
||||
default="(('Catalogs','Tags','Catalog'),)",
|
||||
dest='exclusion_rules',
|
||||
action=None,
|
||||
help=_("Specifies the rules used to exclude books from the generated catalog.\n"
|
||||
@ -139,7 +139,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
"Default: '%default'\n"
|
||||
"Applies to: AZW3, ePub, MOBI output formats")),
|
||||
Option('--prefix-rules',
|
||||
default="(('Read books','tags','+','\u2713'),('Wishlist items','tags','Wishlist','\u00d7'))",
|
||||
default="(('Read books','tags','+','\u2713'),('Wishlist item','tags','Wishlist','\u00d7'))",
|
||||
dest='prefix_rules',
|
||||
action=None,
|
||||
help=_("Specifies the rules used to include prefixes indicating read books, wishlist items and other user-specified prefixes.\n"
|
||||
@ -412,10 +412,15 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
pass
|
||||
|
||||
if GENERATE_DEBUG_EPUB:
|
||||
from calibre.ebooks.epub import initialize_container
|
||||
from calibre.ebooks.tweak import zip_rebuilder
|
||||
from calibre.utils.zipfile import ZipFile
|
||||
input_path = os.path.join(catalog_debug_path,'input')
|
||||
shutil.copy(P('catalog/mimetype'),input_path)
|
||||
shutil.copytree(P('catalog/META-INF'),os.path.join(input_path,'META-INF'))
|
||||
epub_shell = os.path.join(catalog_debug_path,'epub_shell.zip')
|
||||
initialize_container(epub_shell, opf_name='content.opf')
|
||||
with ZipFile(epub_shell, 'r') as zf:
|
||||
zf.extractall(path=input_path)
|
||||
os.remove(epub_shell)
|
||||
zip_rebuilder(input_path, os.path.join(catalog_debug_path,'input.epub'))
|
||||
|
||||
# returns to gui2.actions.catalog:catalog_generated()
|
||||
|
@ -5,6 +5,7 @@ __copyright__ = '2010, Greg Riker'
|
||||
|
||||
import datetime, htmlentitydefs, os, re, shutil, unicodedata, zlib
|
||||
from copy import deepcopy
|
||||
from operator import itemgetter
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
from calibre import (prepare_string_for_xml, strftime, force_unicode)
|
||||
@ -54,214 +55,95 @@ class CatalogBuilder(object):
|
||||
# title dc:title in OPF metadata, NCX periodical
|
||||
# verbosity level of diagnostic printout
|
||||
|
||||
""" property decorators for attributes """
|
||||
if True:
|
||||
''' device-specific symbol (default empty star) '''
|
||||
@property
|
||||
def SYMBOL_EMPTY_RATING(self):
|
||||
return self.output_profile.empty_ratings_char
|
||||
|
||||
''' device-specific symbol (default filled star) '''
|
||||
@property
|
||||
def SYMBOL_FULL_RATING(self):
|
||||
return self.output_profile.ratings_char
|
||||
|
||||
''' device-specific symbol for reading progress '''
|
||||
@property
|
||||
def SYMBOL_PROGRESS_READ(self):
|
||||
psr = '+'
|
||||
if self.generate_for_kindle:
|
||||
psr = '▪'
|
||||
return psr
|
||||
|
||||
''' device-specific symbol for reading progress '''
|
||||
@property
|
||||
def SYMBOL_PROGRESS_UNREAD(self):
|
||||
psu = '-'
|
||||
if self.generate_for_kindle:
|
||||
psu = '▫'
|
||||
return psu
|
||||
|
||||
''' device-specific symbol for reading progress '''
|
||||
@property
|
||||
def SYMBOL_READING(self):
|
||||
if self.generate_for_kindle:
|
||||
return self.format_prefix('▷')
|
||||
else:
|
||||
return self.format_prefix(' ')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
''' directory to store cached thumbs '''
|
||||
@property
|
||||
def cache_dir(self):
|
||||
return self.__cache_dir
|
||||
|
||||
''' temp dir to store generated catalog '''
|
||||
@property
|
||||
def catalog_path(self):
|
||||
return self.__catalog_path
|
||||
|
||||
''' content dir in generated catalog '''
|
||||
@property
|
||||
def content_dir(self):
|
||||
return self.__content_dir
|
||||
|
||||
|
||||
''' active database '''
|
||||
@property
|
||||
def db(self):
|
||||
return self.__db
|
||||
|
||||
|
||||
''' tags to exclude as genres '''
|
||||
@property
|
||||
def excluded_tags(self):
|
||||
return self.__excluded_tags
|
||||
|
||||
''' True if generating for Kindle in MOBI format '''
|
||||
@property
|
||||
def generate_for_kindle(self):
|
||||
return self.__generate_for_kindle
|
||||
|
||||
''' True if connected Kindle and generating for Kindle '''
|
||||
@property
|
||||
def generate_recently_read(self):
|
||||
return self.__generate_recently_read
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
''' additional field to include before/after comments '''
|
||||
@property
|
||||
def merge_comments_rule(self):
|
||||
return self.__merge_comments_rule
|
||||
|
||||
|
||||
''' opts passed from gui2.catalog.catalog_epub_mobi.py '''
|
||||
@property
|
||||
def opts(self):
|
||||
return self.__opts
|
||||
|
||||
''' output_profile declares special symbols '''
|
||||
@property
|
||||
def output_profile(self):
|
||||
return self.__output_profile
|
||||
|
||||
|
||||
''' catalog??? device??? '''
|
||||
@property
|
||||
def plugin(self):
|
||||
return self.__plugin
|
||||
|
||||
|
||||
|
||||
|
||||
''' Progress Reporter for Jobs '''
|
||||
@property
|
||||
def reporter(self):
|
||||
return self.__reporter
|
||||
|
||||
''' stylesheet to include with catalog '''
|
||||
@property
|
||||
def stylesheet(self):
|
||||
return self.__stylesheet
|
||||
|
||||
''' device-specific symbol (default empty star) '''
|
||||
@property
|
||||
def SYMBOL_EMPTY_RATING(self):
|
||||
return self.output_profile.empty_ratings_char
|
||||
|
||||
''' device-specific symbol (default filled star) '''
|
||||
@property
|
||||
def SYMBOL_FULL_RATING(self):
|
||||
return self.output_profile.ratings_char
|
||||
|
||||
''' device-specific symbol for reading progress '''
|
||||
@property
|
||||
def SYMBOL_PROGRESS_READ(self):
|
||||
psr = '+'
|
||||
if self.generate_for_kindle:
|
||||
psr = '▪'
|
||||
return psr
|
||||
|
||||
''' device-specific symbol for reading progress '''
|
||||
@property
|
||||
def SYMBOL_PROGRESS_UNREAD(self):
|
||||
psu = '-'
|
||||
if self.generate_for_kindle:
|
||||
psu = '▫'
|
||||
return psu
|
||||
|
||||
''' device-specific symbol for reading progress '''
|
||||
@property
|
||||
def SYMBOL_READING(self):
|
||||
if self.generate_for_kindle:
|
||||
return self.format_prefix('▷')
|
||||
else:
|
||||
return self.format_prefix(' ')
|
||||
|
||||
|
||||
''' full path to thumbs archive '''
|
||||
@property
|
||||
def thumbs_path(self):
|
||||
return self.__thumbs_path
|
||||
|
||||
|
||||
''' switch controlling format of series books in Titles section '''
|
||||
@property
|
||||
def use_series_prefix_in_titles_section(self):
|
||||
return self.__use_series_prefix_in_titles_section
|
||||
|
||||
def __init__(self, db, _opts, plugin,
|
||||
report_progress=DummyReporter(),
|
||||
stylesheet="content/stylesheet.css",
|
||||
init_resources=True):
|
||||
|
||||
self.__db = db
|
||||
self.__opts = _opts
|
||||
self.__plugin = plugin
|
||||
self.__reporter = report_progress
|
||||
self.__stylesheet = stylesheet
|
||||
self.__cache_dir = os.path.join(config_dir, 'caches', 'catalog')
|
||||
self.__catalog_path = PersistentTemporaryDirectory("_epub_mobi_catalog", prefix='')
|
||||
self.__generate_for_kindle = True if (_opts.fmt == 'mobi' and
|
||||
self.db = db
|
||||
self.opts = _opts
|
||||
self.plugin = plugin
|
||||
self.reporter = report_progress
|
||||
self.stylesheet = stylesheet
|
||||
self.cache_dir = os.path.join(config_dir, 'caches', 'catalog')
|
||||
self.catalog_path = PersistentTemporaryDirectory("_epub_mobi_catalog", prefix='')
|
||||
self.excluded_tags = self.get_excluded_tags()
|
||||
self.generate_for_kindle = True if (_opts.fmt == 'mobi' and
|
||||
_opts.output_profile and
|
||||
_opts.output_profile.startswith("kindle")) else False
|
||||
|
||||
''' list of unique authors '''
|
||||
self.authors = None
|
||||
''' dict of bookmarked books '''
|
||||
self.bookmarked_books = None
|
||||
''' list of bookmarked books, sorted by date read '''
|
||||
self.bookmarked_books_by_date_read = None
|
||||
''' list of books, sorted by author '''
|
||||
self.books_by_author = None
|
||||
''' list of books, grouped by date range (30 days) '''
|
||||
self.books_by_date_range = None
|
||||
''' list of books, by date added reverse (most recent first) '''
|
||||
self.books_by_month = None
|
||||
''' list of books in series '''
|
||||
self.books_by_series = None
|
||||
''' list of books, sorted by title '''
|
||||
self.books_by_title = None
|
||||
''' list of books in series, without series prefix '''
|
||||
self.books_by_title_no_series_prefix = None
|
||||
self.__content_dir = os.path.join(self.catalog_path, "content")
|
||||
''' track Job progress '''
|
||||
self.books_to_catalog = None
|
||||
self.content_dir = os.path.join(self.catalog_path, "content")
|
||||
self.current_step = 0.0
|
||||
''' cumulative error messages to report at conclusion '''
|
||||
self.error = []
|
||||
self.__excluded_tags = self.get_excluded_tags()
|
||||
self.__generate_recently_read = True if (_opts.generate_recently_added and
|
||||
_opts.connected_kindle and
|
||||
self.generate_for_kindle) else False
|
||||
''' list of dicts with books by genre '''
|
||||
self.generate_recently_read = True if (_opts.generate_recently_added and
|
||||
_opts.connected_kindle and
|
||||
self.generate_for_kindle) else False
|
||||
self.genres = []
|
||||
''' dict of enabled genre tags '''
|
||||
self.genre_tags_dict = None
|
||||
''' Author, Title, Series sections '''
|
||||
self.html_filelist_1 = []
|
||||
''' Date Added, Date Read '''
|
||||
self.html_filelist_2 = []
|
||||
self.__merge_comments_rule = dict(zip(['field','position','hr'],_opts.merge_comments_rule.split(':')))
|
||||
''' cumulative HTML for NCX file '''
|
||||
self.merge_comments_rule = dict(zip(['field','position','hr'],
|
||||
_opts.merge_comments_rule.split(':')))
|
||||
self.ncx_soup = None
|
||||
self.__output_profile = None
|
||||
self.__output_profile = self.get_output_profile(_opts)
|
||||
''' playOrder value for building NCX '''
|
||||
self.output_profile = None
|
||||
self.output_profile = self.get_output_profile(_opts)
|
||||
self.play_order = 1
|
||||
''' dict of prefix rules '''
|
||||
self.prefix_rules = self.get_prefix_rules()
|
||||
''' used with ProgressReporter() '''
|
||||
self.progress_int = 0.0
|
||||
''' used with ProgressReporter() '''
|
||||
self.progress_string = ''
|
||||
|
||||
self.__thumb_height = 0
|
||||
|
||||
self.__thumb_width = 0
|
||||
''' list of generated thumbs '''
|
||||
self.thumb_height = 0
|
||||
self.thumb_width = 0
|
||||
self.thumbs = None
|
||||
self.__thumbs_path = os.path.join(self.cache_dir, "thumbs.zip")
|
||||
''' used with ProgressReporter() '''
|
||||
self.thumbs_path = os.path.join(self.cache_dir, "thumbs.zip")
|
||||
self.total_steps = 6.0
|
||||
self.__use_series_prefix_in_titles_section = False
|
||||
self.use_series_prefix_in_titles_section = False
|
||||
|
||||
self.books_to_catalog = self.fetch_books_to_catalog()
|
||||
self.compute_total_steps()
|
||||
self.calculate_thumbnail_dimensions()
|
||||
self.confirm_thumbs_archive()
|
||||
@ -329,20 +211,29 @@ class CatalogBuilder(object):
|
||||
(str): sort key
|
||||
"""
|
||||
if not book['series']:
|
||||
fs = '{:<%d}!{!s}' % longest_author_sort
|
||||
fs = u'{:<%d}!{!s}' % longest_author_sort
|
||||
key = fs.format(capitalize(book['author_sort']),
|
||||
capitalize(book['title_sort']))
|
||||
else:
|
||||
index = book['series_index']
|
||||
integer = int(index)
|
||||
fraction = index-integer
|
||||
series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0'))
|
||||
fs = '{:<%d}~{!s}{!s}' % longest_author_sort
|
||||
series_index = u'%04d%s' % (integer, str(u'%0.4f' % fraction).lstrip(u'0'))
|
||||
fs = u'{:<%d}~{!s}{!s}' % longest_author_sort
|
||||
key = fs.format(capitalize(book['author_sort']),
|
||||
self.generate_sort_title(book['series']),
|
||||
series_index)
|
||||
return key
|
||||
|
||||
def _kf_books_by_series_sorter(self, book):
|
||||
index = book['series_index']
|
||||
integer = int(index)
|
||||
fraction = index-integer
|
||||
series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0'))
|
||||
key = '%s %s' % (self.generate_sort_title(book['series']),
|
||||
series_index)
|
||||
return key
|
||||
|
||||
""" Methods """
|
||||
|
||||
def build_sources(self):
|
||||
@ -614,7 +505,7 @@ class CatalogBuilder(object):
|
||||
annoyance for EPUB.
|
||||
|
||||
Inputs:
|
||||
self.books_by_title (list): list of books to catalog
|
||||
self.books_to_catalog (list): list of books to catalog
|
||||
|
||||
Output:
|
||||
self.books_by_author (list): sorted by author
|
||||
@ -623,7 +514,7 @@ class CatalogBuilder(object):
|
||||
AuthorSortMismatchException: author_sort mismatch detected
|
||||
"""
|
||||
|
||||
self.books_by_author = sorted(list(self.books_by_title), key=self._kf_books_by_author_sorter_author)
|
||||
self.books_by_author = sorted(list(self.books_to_catalog), key=self._kf_books_by_author_sorter_author)
|
||||
authors = [(record['author'], record['author_sort']) for record in self.books_by_author]
|
||||
current_author = authors[0]
|
||||
for (i,author) in enumerate(authors):
|
||||
@ -671,7 +562,7 @@ class CatalogBuilder(object):
|
||||
None: no match
|
||||
"""
|
||||
def _log_prefix_rule_match_info(rule, record):
|
||||
self.opts.log.info(" %s '%s' by %s (Prefix rule '%s')" %
|
||||
self.opts.log.info(" %s '%s' by %s (Prefix rule '%s')" %
|
||||
(rule['prefix'],record['title'],
|
||||
record['authors'][0], rule['name']))
|
||||
|
||||
@ -770,7 +661,7 @@ class CatalogBuilder(object):
|
||||
to self.authors.
|
||||
|
||||
Inputs:
|
||||
self.books_by_title (list): database, sorted by title
|
||||
self.books_to_catalog (list): database, sorted by title
|
||||
|
||||
Outputs:
|
||||
books_by_author: database, sorted by author
|
||||
@ -790,7 +681,7 @@ class CatalogBuilder(object):
|
||||
# Determine the longest author_sort length before sorting
|
||||
asl = [i['author_sort'] for i in self.books_by_author]
|
||||
las = max(asl, key=len)
|
||||
self.books_by_author = sorted(self.books_by_author,
|
||||
self.books_by_author = sorted(self.books_to_catalog,
|
||||
key=lambda x: sort_key(self._kf_books_by_author_sorter_author_sort(x, len(las))))
|
||||
|
||||
if self.DEBUG and self.opts.verbose:
|
||||
@ -843,9 +734,42 @@ class CatalogBuilder(object):
|
||||
return True
|
||||
|
||||
def fetch_books_by_title(self):
|
||||
""" Populate self.books_by_title from database
|
||||
""" Generate a list of books sorted by title.
|
||||
|
||||
Create self.books_by_title from filtered database.
|
||||
Sort the database by title.
|
||||
|
||||
Inputs:
|
||||
self.books_to_catalog (list): database
|
||||
|
||||
Outputs:
|
||||
books_by_title: database, sorted by title
|
||||
|
||||
Return:
|
||||
True: no errors
|
||||
False: author_sort mismatch detected while building MOBI
|
||||
"""
|
||||
self.update_progress_full_step(_("Sorting titles"))
|
||||
# Re-sort based on title_sort
|
||||
if len(self.books_to_catalog):
|
||||
self.books_by_title = sorted(self.books_to_catalog, key=lambda x: sort_key(x['title_sort'].upper()))
|
||||
|
||||
if self.DEBUG and self.opts.verbose:
|
||||
self.opts.log.info("fetch_books_by_title(): %d books" % len(self.books_by_title))
|
||||
self.opts.log.info(" %-40s %-40s" % ('title', 'title_sort'))
|
||||
for title in self.books_by_title:
|
||||
self.opts.log.info((u" %-40s %-40s" % (title['title'][0:40],
|
||||
title['title_sort'][0:40])).encode('utf-8'))
|
||||
else:
|
||||
error_msg = _("No books to catalog.\nCheck 'Excluded books' rules in E-book options.\n")
|
||||
self.opts.log.error('*** ' + error_msg + ' ***')
|
||||
self.error.append(_('No books available to include in catalog'))
|
||||
self.error.append(error_msg)
|
||||
raise EmptyCatalogException, error_msg
|
||||
|
||||
def fetch_books_to_catalog(self):
|
||||
""" Populate self.books_to_catalog from database
|
||||
|
||||
Create self.books_to_catalog from filtered database.
|
||||
Keys:
|
||||
authors massaged
|
||||
author_sort record['author_sort'] or computed
|
||||
@ -871,7 +795,7 @@ class CatalogBuilder(object):
|
||||
data (list): filtered list of book metadata dicts
|
||||
|
||||
Outputs:
|
||||
(list) books_by_title
|
||||
(list) books_to_catalog
|
||||
|
||||
Returns:
|
||||
True: Successful
|
||||
@ -953,9 +877,11 @@ class CatalogBuilder(object):
|
||||
|
||||
this_title['prefix'] = self.discover_prefix(record)
|
||||
|
||||
this_title['tags'] = []
|
||||
if record['tags']:
|
||||
this_title['tags'] = self.filter_excluded_genres(record['tags'],
|
||||
self.opts.exclude_genre)
|
||||
|
||||
if record['formats']:
|
||||
formats = []
|
||||
for format in record['formats']:
|
||||
@ -980,7 +906,6 @@ class CatalogBuilder(object):
|
||||
return this_title
|
||||
|
||||
# Entry point
|
||||
self.update_progress_full_step(_("Fetching database"))
|
||||
|
||||
self.opts.sort_by = 'title'
|
||||
search_phrase = ''
|
||||
@ -1003,28 +928,15 @@ class CatalogBuilder(object):
|
||||
data = self.plugin.search_sort_db(self.db, self.opts)
|
||||
data = self.process_exclusions(data)
|
||||
|
||||
if self.opts.verbose and self.prefix_rules:
|
||||
self.opts.log.info(" Added prefixes:")
|
||||
|
||||
# Populate this_title{} from data[{},{}]
|
||||
titles = []
|
||||
for record in data:
|
||||
this_title = _populate_title(record)
|
||||
titles.append(this_title)
|
||||
|
||||
# Re-sort based on title_sort
|
||||
if len(titles):
|
||||
self.books_by_title = sorted(titles, key=lambda x: sort_key(x['title_sort'].upper()))
|
||||
|
||||
if self.DEBUG and self.opts.verbose:
|
||||
self.opts.log.info("fetch_books_by_title(): %d books" % len(self.books_by_title))
|
||||
self.opts.log.info(" %-40s %-40s" % ('title', 'title_sort'))
|
||||
for title in self.books_by_title:
|
||||
self.opts.log.info((u" %-40s %-40s" % (title['title'][0:40],
|
||||
title['title_sort'][0:40])).encode('utf-8'))
|
||||
else:
|
||||
error_msg = _("No books to catalog.\nCheck 'Excluded books' rules in E-book options.\n")
|
||||
self.opts.log.error('*** ' + error_msg + ' ***')
|
||||
self.error.append(_('No books available to include in catalog'))
|
||||
self.error.append(error_msg)
|
||||
raise EmptyCatalogException, error_msg
|
||||
return titles
|
||||
|
||||
def fetch_bookmarks(self):
|
||||
""" Interrogate connected Kindle for bookmarks.
|
||||
@ -1104,7 +1016,7 @@ class CatalogBuilder(object):
|
||||
d.initialize(self.opts.connected_device['save_template'])
|
||||
|
||||
bookmarks = {}
|
||||
for book in self.books_by_title:
|
||||
for book in self.books_to_catalog:
|
||||
if 'formats' in book:
|
||||
path_map = {}
|
||||
id = book['id']
|
||||
@ -1148,7 +1060,7 @@ class CatalogBuilder(object):
|
||||
genre_tags_dict (dict): dict of filtered, normalized tags in data set
|
||||
"""
|
||||
|
||||
def _format_tag_list(tags, indent=5, line_break=70, header='Tag list'):
|
||||
def _format_tag_list(tags, indent=2, line_break=70, header='Tag list'):
|
||||
def _next_tag(sorted_tags):
|
||||
for (i, tag) in enumerate(sorted_tags):
|
||||
if i < len(tags) - 1:
|
||||
@ -1541,7 +1453,7 @@ class CatalogBuilder(object):
|
||||
def generate_html_by_date_added(self):
|
||||
""" Generate content/ByDateAdded.html.
|
||||
|
||||
Loop through self.books_by_title sorted by reverse date, generate HTML.
|
||||
Loop through self.books_to_catalog sorted by reverse date, generate HTML.
|
||||
|
||||
Input:
|
||||
books_by_title (list): books, sorted by title
|
||||
@ -1735,10 +1647,10 @@ class CatalogBuilder(object):
|
||||
|
||||
# >>> Books by date range <<<
|
||||
if self.use_series_prefix_in_titles_section:
|
||||
self.books_by_date_range = sorted(self.books_by_title,
|
||||
self.books_by_date_range = sorted(self.books_to_catalog,
|
||||
key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
|
||||
else:
|
||||
nspt = deepcopy(self.books_by_title)
|
||||
nspt = deepcopy(self.books_to_catalog)
|
||||
self.books_by_date_range = sorted(nspt, key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
|
||||
|
||||
date_range_list = []
|
||||
@ -1763,7 +1675,7 @@ class CatalogBuilder(object):
|
||||
|
||||
# >>>> Books by month <<<<
|
||||
# Sort titles case-insensitive for by month using series prefix
|
||||
self.books_by_month = sorted(self.books_by_title,
|
||||
self.books_by_month = sorted(self.books_to_catalog,
|
||||
key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
|
||||
|
||||
# Loop through books by date
|
||||
@ -2026,12 +1938,12 @@ class CatalogBuilder(object):
|
||||
|
||||
if self.opts.verbose:
|
||||
if len(genre_list):
|
||||
self.opts.log.info(" Genre summary: %d active genre tags used in generating catalog with %d titles" %
|
||||
(len(genre_list), len(self.books_by_title)))
|
||||
self.opts.log.info(" Genre summary: %d active genre tags used in generating catalog with %d titles" %
|
||||
(len(genre_list), len(self.books_to_catalog)))
|
||||
|
||||
for genre in genre_list:
|
||||
for key in genre:
|
||||
self.opts.log.info(" %s: %d %s" % (self.get_friendly_genre_tag(key),
|
||||
self.opts.log.info(" %s: %d %s" % (self.get_friendly_genre_tag(key),
|
||||
len(genre[key]),
|
||||
'titles' if len(genre[key]) > 1 else 'title'))
|
||||
|
||||
@ -2226,48 +2138,28 @@ class CatalogBuilder(object):
|
||||
Output:
|
||||
content/BySeries.html (file)
|
||||
|
||||
To do:
|
||||
self.books_by_series = [i for i in self.books_by_title if i['series']]
|
||||
"""
|
||||
friendly_name = _("Series")
|
||||
self.update_progress_full_step("%s HTML" % friendly_name)
|
||||
|
||||
self.opts.sort_by = 'series'
|
||||
|
||||
# Merge self.excluded_tags with opts.search_text
|
||||
# Updated to use exact match syntax
|
||||
|
||||
search_phrase = 'series:true '
|
||||
if self.excluded_tags:
|
||||
search_terms = []
|
||||
for tag in self.excluded_tags:
|
||||
search_terms.append("tag:=%s" % tag)
|
||||
search_phrase += "not (%s)" % " or ".join(search_terms)
|
||||
|
||||
# If a list of ids are provided, don't use search_text
|
||||
if self.opts.ids:
|
||||
self.opts.search_text = search_phrase
|
||||
else:
|
||||
if self.opts.search_text:
|
||||
self.opts.search_text += " " + search_phrase
|
||||
else:
|
||||
self.opts.search_text = search_phrase
|
||||
|
||||
# Fetch the database as a dictionary
|
||||
data = self.plugin.search_sort_db(self.db, self.opts)
|
||||
|
||||
# Remove exclusions
|
||||
self.books_by_series = self.process_exclusions(data, log_exclusion=False)
|
||||
# *** Convert the existing database, resort by series/index ***
|
||||
self.books_by_series = [i for i in self.books_to_catalog if i['series']]
|
||||
self.books_by_series = sorted(self.books_by_series, key=lambda x: sort_key(self._kf_books_by_series_sorter(x)))
|
||||
|
||||
if not self.books_by_series:
|
||||
self.opts.generate_series = False
|
||||
self.opts.log(" no series found in selected books, cancelling series generation")
|
||||
self.opts.log(" no series found in selected books, skipping Series section")
|
||||
return
|
||||
|
||||
# Generate series_sort
|
||||
for book in self.books_by_series:
|
||||
book['series_sort'] = self.generate_sort_title(book['series'])
|
||||
|
||||
# Establish initial letter equivalencies
|
||||
sort_equivalents = self.establish_equivalencies(self.books_by_series, key='series_sort')
|
||||
|
||||
soup = self.generate_html_empty_header(friendly_name)
|
||||
body = soup.find('body')
|
||||
|
||||
@ -2277,9 +2169,6 @@ class CatalogBuilder(object):
|
||||
current_letter = ""
|
||||
current_series = None
|
||||
|
||||
# Establish initial letter equivalencies
|
||||
sort_equivalents = self.establish_equivalencies(self.books_by_series, key='series_sort')
|
||||
|
||||
# Loop through books_by_series
|
||||
series_count = 0
|
||||
for idx, book in enumerate(self.books_by_series):
|
||||
@ -2335,11 +2224,6 @@ class CatalogBuilder(object):
|
||||
# Use series, series index if avail else just title
|
||||
#aTag.insert(0,'%d. %s · %s' % (book['series_index'],escape(book['title']), ' & '.join(book['authors'])))
|
||||
|
||||
if is_date_undefined(book['pubdate']):
|
||||
book['date'] = None
|
||||
else:
|
||||
book['date'] = strftime(u'%B %Y', book['pubdate'].timetuple())
|
||||
|
||||
args = self.generate_format_args(book)
|
||||
formatted_title = self.by_series_title_template.format(**args).rstrip()
|
||||
aTag.insert(0,NavigableString(escape(formatted_title)))
|
||||
@ -2438,7 +2322,7 @@ class CatalogBuilder(object):
|
||||
# Re-sort title list without leading series/series_index
|
||||
# Incoming title <series> <series_index>: <title>
|
||||
if not self.use_series_prefix_in_titles_section:
|
||||
nspt = deepcopy(self.books_by_title)
|
||||
nspt = deepcopy(self.books_to_catalog)
|
||||
nspt = sorted(nspt, key=lambda x: sort_key(x['title_sort'].upper()))
|
||||
self.books_by_title_no_series_prefix = nspt
|
||||
|
||||
@ -2580,7 +2464,9 @@ class CatalogBuilder(object):
|
||||
title_str=title_str,
|
||||
xmlns=XHTML_NS,
|
||||
)
|
||||
|
||||
for k, v in args.iteritems():
|
||||
if isbytestring(v):
|
||||
args[k] = v.decode('utf-8')
|
||||
generated_html = P('catalog/template.xhtml',
|
||||
data=True).decode('utf-8').format(**args)
|
||||
generated_html = substitute_entites(generated_html)
|
||||
@ -3153,8 +3039,12 @@ class CatalogBuilder(object):
|
||||
self.play_order += 1
|
||||
navLabelTag = Tag(soup, 'navLabel')
|
||||
textTag = Tag(soup, 'text')
|
||||
textTag.insert(0, NavigableString(_(u"Series beginning with %s") % \
|
||||
(title_letters[i] if len(title_letters[i])>1 else "'" + title_letters[i] + "'")))
|
||||
if len(title_letters[i])>1:
|
||||
fmt_string = _(u"Series beginning with %s")
|
||||
else:
|
||||
fmt_string = _(u"Series beginning with '%s'")
|
||||
textTag.insert(0, NavigableString(fmt_string %
|
||||
(title_letters[i] if len(title_letters[i])>1 else title_letters[i])))
|
||||
navLabelTag.insert(0, textTag)
|
||||
navPointByLetterTag.insert(0,navLabelTag)
|
||||
contentTag = Tag(soup, 'content')
|
||||
@ -3274,8 +3164,12 @@ class CatalogBuilder(object):
|
||||
self.play_order += 1
|
||||
navLabelTag = Tag(soup, 'navLabel')
|
||||
textTag = Tag(soup, 'text')
|
||||
textTag.insert(0, NavigableString(_(u"Titles beginning with %s") % \
|
||||
(title_letters[i] if len(title_letters[i])>1 else "'" + title_letters[i] + "'")))
|
||||
if len(title_letters[i])>1:
|
||||
fmt_string = _(u"Titles beginning with %s")
|
||||
else:
|
||||
fmt_string = _(u"Titles beginning with '%s'")
|
||||
textTag.insert(0, NavigableString(fmt_string %
|
||||
(title_letters[i] if len(title_letters[i])>1 else title_letters[i])))
|
||||
navLabelTag.insert(0, textTag)
|
||||
navPointByLetterTag.insert(0,navLabelTag)
|
||||
contentTag = Tag(soup, 'content')
|
||||
@ -3385,7 +3279,11 @@ class CatalogBuilder(object):
|
||||
self.play_order += 1
|
||||
navLabelTag = Tag(soup, 'navLabel')
|
||||
textTag = Tag(soup, 'text')
|
||||
textTag.insert(0, NavigableString(_("Authors beginning with '%s'") % (authors_by_letter[1])))
|
||||
if len(authors_by_letter[1])>1:
|
||||
fmt_string = _(u"Authors beginning with %s")
|
||||
else:
|
||||
fmt_string = _(u"Authors beginning with '%s'")
|
||||
textTag.insert(0, NavigableString(fmt_string % (authors_by_letter[1])))
|
||||
navLabelTag.insert(0, textTag)
|
||||
navPointByLetterTag.insert(0,navLabelTag)
|
||||
contentTag = Tag(soup, 'content')
|
||||
@ -4339,7 +4237,7 @@ class CatalogBuilder(object):
|
||||
|
||||
# Report excluded books
|
||||
if self.opts.verbose and excluded_tags:
|
||||
self.opts.log.info(" Excluded books by Tags:")
|
||||
self.opts.log.info(" Excluded books:")
|
||||
data = self.db.get_data_as_dict(ids=self.opts.ids)
|
||||
for record in data:
|
||||
matched = list(set(record['tags']) & set(excluded_tags))
|
||||
@ -4632,7 +4530,7 @@ class CatalogBuilder(object):
|
||||
normalized += c
|
||||
return normalized
|
||||
|
||||
def process_exclusions(self, data_set, log_exclusion=True):
|
||||
def process_exclusions(self, data_set):
|
||||
""" Filter data_set based on exclusion_rules.
|
||||
|
||||
Compare each book in data_set to each exclusion_rule. Remove
|
||||
@ -4666,16 +4564,18 @@ class CatalogBuilder(object):
|
||||
matched = re.search(pat, unicode(field_contents),
|
||||
re.IGNORECASE)
|
||||
if matched is not None:
|
||||
if self.opts.verbose and log_exclusion:
|
||||
if self.opts.verbose:
|
||||
field_md = self.db.metadata_for_field(field)
|
||||
for rule in self.opts.exclusion_rules:
|
||||
if rule[1] == '#%s' % field_md['label']:
|
||||
self.opts.log.info(" - '%s' by %s (Exclusion rule '%s')" %
|
||||
self.opts.log.info(" - '%s' by %s (Exclusion rule '%s')" %
|
||||
(record['title'], record['authors'][0], rule[0]))
|
||||
exclusion_set.append(record)
|
||||
if record in filtered_data_set:
|
||||
filtered_data_set.remove(record)
|
||||
break
|
||||
else:
|
||||
filtered_data_set.append(record)
|
||||
else:
|
||||
if (record not in filtered_data_set and
|
||||
record not in exclusion_set):
|
||||
|
@ -1432,6 +1432,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
pdir = os.path.dirname(dest)
|
||||
if not os.path.exists(pdir):
|
||||
os.makedirs(pdir)
|
||||
size = 0
|
||||
if copy_function is not None:
|
||||
copy_function(dest)
|
||||
size = os.path.getsize(dest)
|
||||
@ -1441,6 +1442,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
with lopen(dest, 'wb') as f:
|
||||
shutil.copyfileobj(stream, f)
|
||||
size = f.tell()
|
||||
elif os.path.exists(dest):
|
||||
size = os.path.getsize(dest)
|
||||
self.conn.execute('INSERT OR REPLACE INTO data (book,format,uncompressed_size,name) VALUES (?,?,?,?)',
|
||||
(id, format.upper(), size, name))
|
||||
self.update_last_modified([id], commit=False)
|
||||
|
@ -5,8 +5,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: calibre 0.8.68\n"
|
||||
"POT-Creation-Date: 2012-09-07 08:49+IST\n"
|
||||
"PO-Revision-Date: 2012-09-07 08:49+IST\n"
|
||||
"POT-Creation-Date: 2012-09-08 17:09+IST\n"
|
||||
"PO-Revision-Date: 2012-09-08 17:09+IST\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: LANGUAGE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -131,8 +131,8 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ztxt/writer.py:27
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:108
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:109
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:426
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:434
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:439
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:447
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:166
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:397
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:400
|
||||
@ -143,8 +143,8 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:124
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:143
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:145
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1366
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1369
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1367
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1370
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:55
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:60
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:128
|
||||
@ -173,12 +173,12 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:585
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:593
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:604
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2189
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2343
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2768
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3415
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3417
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3554
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2192
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2346
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2771
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3418
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3420
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3557
|
||||
#: /home/kovid/work/calibre/src/calibre/library/server/content.py:250
|
||||
#: /home/kovid/work/calibre/src/calibre/library/server/content.py:251
|
||||
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:247
|
||||
@ -1034,14 +1034,14 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/model.py:1199
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:370
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:383
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3272
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3275
|
||||
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:187
|
||||
msgid "News"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2770
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3228
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3246
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3231
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3249
|
||||
msgid "Catalog"
|
||||
msgstr ""
|
||||
|
||||
@ -3352,8 +3352,8 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:67
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:70
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:163
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:401
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2232
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:292
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2142
|
||||
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:140
|
||||
msgid "Series"
|
||||
msgid_plural "Series"
|
||||
@ -3880,7 +3880,17 @@ msgstr ""
|
||||
msgid "Show this confirmation again"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:546
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:332
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/server.py:134
|
||||
msgid "Restart needed"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:334
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/plugin_updater.py:741
|
||||
msgid "Restart calibre now"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:559
|
||||
msgid "Choose Files"
|
||||
msgstr ""
|
||||
|
||||
@ -4112,7 +4122,7 @@ msgid "Merging user annotations into database"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/annotate.py:63
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:744
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:745
|
||||
msgid "Fetch annotations (experimental)"
|
||||
msgstr ""
|
||||
|
||||
@ -4357,7 +4367,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:403
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:197
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:933
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:934
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:1004
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:114
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:128
|
||||
@ -4590,14 +4600,14 @@ msgid "Main memory"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:239
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:669
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:678
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:670
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:679
|
||||
msgid "Storage Card A"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:240
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:671
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:680
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:672
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:681
|
||||
msgid "Storage Card B"
|
||||
msgstr ""
|
||||
|
||||
@ -5373,7 +5383,7 @@ msgid "The specified directory could not be processed."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:274
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1087
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1088
|
||||
msgid "No books"
|
||||
msgstr ""
|
||||
|
||||
@ -5794,6 +5804,18 @@ msgstr ""
|
||||
msgid "E-book options"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:90
|
||||
msgid "Catalogs"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:99
|
||||
msgid "Read book"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:105
|
||||
msgid "Wishlist item"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:133
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:769
|
||||
msgid "any date"
|
||||
@ -5831,7 +5853,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:615
|
||||
#, python-format
|
||||
msgid "Are you sure you want to delete rules #%d-%d?"
|
||||
msgid "Are you sure you want to delete rules #%(first)d-%(last)d?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:616
|
||||
@ -7685,226 +7707,222 @@ msgstr ""
|
||||
msgid "tags to remove"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:50
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:51
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:148
|
||||
msgid "No details available."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:202
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:203
|
||||
msgid "Device no longer connected."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:413
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:414
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/device_debug.py:27
|
||||
msgid "Debug device detection"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:429
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:430
|
||||
msgid "Get device information"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:444
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:445
|
||||
msgid "Get list of books on device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:451
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:452
|
||||
msgid "Prepare files for transfer from device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:462
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:463
|
||||
msgid "Get annotations from device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:474
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:475
|
||||
msgid "Send metadata to device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:479
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:480
|
||||
msgid "Send collections to device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:529
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:530
|
||||
#, python-format
|
||||
msgid "Upload %d books to device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:545
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:546
|
||||
msgid "Delete books from device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:563
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:564
|
||||
msgid "Download books from device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:573
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:574
|
||||
msgid "View book on device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:652
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:653
|
||||
msgid "Set default send to device action"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:658
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:659
|
||||
msgid "Send to main memory"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:660
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:661
|
||||
msgid "Send to storage card A"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:662
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:663
|
||||
msgid "Send to storage card B"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:667
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:676
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:668
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:677
|
||||
msgid "Main Memory"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:688
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:689
|
||||
msgid "Send specific format to"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:689
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:690
|
||||
msgid "Send and delete from library"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:732
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:733
|
||||
msgid "Eject device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:813
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:814
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc.py:71
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:332
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:58
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:814
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:815
|
||||
msgid "Error communicating with device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:843
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1416
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:844
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1417
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/email.py:260
|
||||
msgid "No suitable formats"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:859
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:860
|
||||
msgid "Select folder to open as device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:877
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:878
|
||||
msgid "Running jobs"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:878
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:879
|
||||
msgid "Cannot configure the device while there are running device jobs."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:883
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:884
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/mtp_config.py:168
|
||||
#, python-format
|
||||
msgid "Configure %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:898
|
||||
msgid "Disconnect device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:899
|
||||
#, python-format
|
||||
msgid "Disconnect and re-connect the %s for your changes to be applied."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:939
|
||||
msgid "Error talking to device"
|
||||
msgid "Restart calibre for the changes to %s to be applied."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:940
|
||||
msgid "Error talking to device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:941
|
||||
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device or reboot."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:984
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:985
|
||||
msgid "Device: "
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:986
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:987
|
||||
msgid " detected."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1088
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1089
|
||||
msgid "selected to send"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1095
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1125
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1096
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1126
|
||||
msgid "No device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1096
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1097
|
||||
msgid "No device connected"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1112
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1113
|
||||
#, python-format
|
||||
msgid "%(num)i of %(total)i Books"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1116
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1117
|
||||
#, python-format
|
||||
msgid "0 of %i Books"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1117
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1118
|
||||
msgid "Choose format to send to device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1126
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1127
|
||||
msgid "Cannot send: No device is connected"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1129
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1133
|
||||
msgid "No card"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1130
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1134
|
||||
msgid "No card"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1131
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1135
|
||||
msgid "Cannot send: Device has no storage card"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1195
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1278
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1410
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1196
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1279
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1411
|
||||
msgid "Auto convert the following books before uploading to the device?"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1224
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1225
|
||||
msgid "Sending catalogs to device."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1323
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1324
|
||||
msgid "Sending news to device."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1377
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1378
|
||||
msgid "Sending books to device."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1417
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1418
|
||||
msgid "Could not upload the following books to the device, as no suitable formats were found. Convert the book(s) to a format supported by your device first."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1490
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1491
|
||||
msgid "No space on device"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1491
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1492
|
||||
msgid "<p>Cannot upload books to device there is no more free space available "
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1496
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1497
|
||||
msgid "Incorrect destination"
|
||||
msgstr ""
|
||||
|
||||
@ -9471,11 +9489,6 @@ msgstr ""
|
||||
msgid "Plugin <b>{0}</b> successfully installed under <b> {1} plugins</b>. You may have to restart calibre for the plugin to take effect."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/plugin_updater.py:741
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:352
|
||||
msgid "Restart calibre now"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/plugin_updater.py:760
|
||||
msgid "A problem occurred while installing this plugin. This plugin will now be uninstalled. Please post the error message in details below into the forum thread for this plugin and restart Calibre."
|
||||
msgstr ""
|
||||
@ -9529,8 +9542,8 @@ msgstr ""
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/quickview.py:87
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:60
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:156
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:397
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1342
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:288
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1254
|
||||
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:113
|
||||
msgid "Authors"
|
||||
msgstr ""
|
||||
@ -13072,11 +13085,6 @@ msgstr ""
|
||||
msgid "The changes you have made require calibre be restarted immediately. You will not be allowed to set any more preferences, until you restart."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:350
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/server.py:134
|
||||
msgid "Restart needed"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:48
|
||||
msgid "Source"
|
||||
msgstr ""
|
||||
@ -14986,7 +14994,7 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/update.py:78
|
||||
#, python-format
|
||||
msgid "%(app)s has been updated to version <b>%(ver)s</b>. See the <a href=\"http://calibre-ebook.com/whats-new\">new features</a>."
|
||||
msgid "New version <b>%(ver)s</b> of %(app)s is available for download. See the <a href=\"http://calibre-ebook.com/whats-new\">new features</a>."
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/update.py:84
|
||||
@ -16370,153 +16378,168 @@ msgid ""
|
||||
"*** Adding 'By Authors' Section required for MOBI output ***"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:46
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:47
|
||||
msgid "Symbols"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:382
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:273
|
||||
msgid "No genres to catalog.\n"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:384
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:275
|
||||
msgid "Check 'Excluded genres' regex in E-book options.\n"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:386
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:277
|
||||
msgid "No books available to catalog"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:399
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2429
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:290
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2313
|
||||
msgid "Titles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:403
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:294
|
||||
msgid "Genres"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:405
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1703
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:296
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1615
|
||||
msgid "Recently Added"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:407
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1902
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:298
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1814
|
||||
msgid "Recently Read"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:409
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:300
|
||||
msgid "Descriptions"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:634
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:525
|
||||
msgid "<p>Inconsistent Author Sort values for Author<br/>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:651
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:542
|
||||
msgid "Warning: Inconsistent Author Sort values for Author '{!s}':\n"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:785
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:676
|
||||
msgid "Sorting database"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:983
|
||||
msgid "Fetching database"
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:751
|
||||
msgid "Sorting titles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1023
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:763
|
||||
msgid ""
|
||||
"No books to catalog.\n"
|
||||
"Check 'Excluded books' rules in E-book options.\n"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1025
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:765
|
||||
msgid "No books available to include in catalog"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1983
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1895
|
||||
msgid "Genres HTML"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2409
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2293
|
||||
msgid "Titles HTML"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2604
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2606
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2608
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2488
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2490
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2492
|
||||
msgid "by "
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2745
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2629
|
||||
msgid "Descriptions HTML"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2749
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2633
|
||||
msgid "Description HTML"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2884
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2768
|
||||
msgid "NCX header"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2959
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2843
|
||||
msgid "NCX for Descriptions"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3080
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2964
|
||||
msgid "NCX for Series"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3156
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3041
|
||||
#, python-format
|
||||
msgid "Series beginning with %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3199
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3043
|
||||
#, python-format
|
||||
msgid "Series beginning with '%s'"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3087
|
||||
msgid "NCX for Titles"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3277
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3166
|
||||
#, python-format
|
||||
msgid "Titles beginning with %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3318
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3168
|
||||
#, python-format
|
||||
msgid "Titles beginning with '%s'"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3210
|
||||
msgid "NCX for Authors"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3388
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3281
|
||||
#, python-format
|
||||
msgid "Authors beginning with %s"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3283
|
||||
#, python-format
|
||||
msgid "Authors beginning with '%s'"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3428
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3324
|
||||
msgid "NCX for Recently Added"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3615
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3511
|
||||
msgid "NCX for Recently Read"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3752
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3648
|
||||
msgid "NCX for Genres"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3870
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3766
|
||||
msgid "Generating OPF"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:4242
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:4138
|
||||
msgid "Thumbnails"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:4248
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:4144
|
||||
msgid "Thumbnail"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:4743
|
||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:4641
|
||||
msgid "Saving NCX"
|
||||
msgstr ""
|
||||
|
||||
@ -17107,17 +17130,17 @@ msgstr ""
|
||||
msgid "creating custom column "
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3580
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3583
|
||||
#, python-format
|
||||
msgid "<p>Migrating old database to ebook library in %s<br><center>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3609
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3612
|
||||
#, python-format
|
||||
msgid "Copying <b>%s</b>"
|
||||
msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3626
|
||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3629
|
||||
msgid "Compacting database"
|
||||
msgstr ""
|
||||
|
||||
|
@ -168,9 +168,9 @@ winutil_set_debug(PyObject *self, PyObject *args) {
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static LPTSTR
|
||||
static LPWSTR
|
||||
get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iterate) {
|
||||
/* Get a the property specified by `property` from the registry for the
|
||||
/* Get the property specified by `property` from the registry for the
|
||||
* device enumerated by `index` in the collection `hDevInfo`. `iterate`
|
||||
* will be set to `FALSE` if `index` points outside `hDevInfo`.
|
||||
* :return: A string allocated on the heap containing the property or
|
||||
@ -178,7 +178,7 @@ get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iter
|
||||
*/
|
||||
SP_DEVINFO_DATA DeviceInfoData;
|
||||
DWORD DataT;
|
||||
LPTSTR buffer = NULL;
|
||||
LPWSTR buffer = NULL;
|
||||
DWORD buffersize = 0;
|
||||
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
|
||||
@ -187,7 +187,7 @@ get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iter
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while(!SetupDiGetDeviceRegistryProperty(
|
||||
while(!SetupDiGetDeviceRegistryPropertyW(
|
||||
hDevInfo,
|
||||
&DeviceInfoData,
|
||||
property,
|
||||
@ -196,11 +196,11 @@ get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iter
|
||||
buffersize,
|
||||
&buffersize)) {
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
buffer = (LPTSTR)PyMem_Malloc(2*buffersize); // Twice for bug in Win2k
|
||||
if (buffer != NULL) { PyMem_Free(buffer); buffer = NULL; }
|
||||
buffer = (LPWSTR)PyMem_Malloc(2*buffersize); // Twice for bug in Win2k
|
||||
} else {
|
||||
PyMem_Free(buffer);
|
||||
if (buffer != NULL) { PyMem_Free(buffer); buffer = NULL; }
|
||||
PyErr_SetFromWindowsErr(0);
|
||||
buffer = NULL;
|
||||
break;
|
||||
}
|
||||
} //while
|
||||
@ -209,7 +209,7 @@ get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iter
|
||||
}
|
||||
|
||||
static BOOL
|
||||
check_device_id(LPTSTR buffer, unsigned int vid, unsigned int pid) {
|
||||
check_device_id(LPWSTR buffer, unsigned int vid, unsigned int pid) {
|
||||
WCHAR xVid[9], dVid[9], xPid[9], dPid[9];
|
||||
unsigned int j;
|
||||
_snwprintf_s(xVid, 9, _TRUNCATE, L"vid_%4.4x", vid);
|
||||
@ -607,31 +607,28 @@ winutil_get_removable_drives(PyObject *self, PyObject *args) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ddebug = PyObject_IsTrue(pdebug);
|
||||
// Find all removable drives
|
||||
for (j = 0; j < MAX_DRIVES; j++) g_drives[j].letter = 0;
|
||||
if (!get_all_removable_disks(g_drives)) return NULL;
|
||||
|
||||
volumes = PyDict_New();
|
||||
if (volumes == NULL) return NULL;
|
||||
|
||||
|
||||
for (j = 0; j < MAX_DRIVES; j++) g_drives[j].letter = 0;
|
||||
|
||||
// Find all removable drives
|
||||
if (!get_all_removable_disks(g_drives)) {
|
||||
return NULL;
|
||||
}
|
||||
if (volumes == NULL) return PyErr_NoMemory();
|
||||
ddebug = PyObject_IsTrue(pdebug);
|
||||
|
||||
hDevInfo = create_device_info_set((LPGUID)&GUID_DEVINTERFACE_VOLUME,
|
||||
NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
if (hDevInfo == INVALID_HANDLE_VALUE) return NULL;
|
||||
if (hDevInfo == INVALID_HANDLE_VALUE) { Py_DECREF(volumes); return NULL; }
|
||||
|
||||
// Enumerate through the set
|
||||
for (i=0; iterate; i++) {
|
||||
candidates = PyList_New(0);
|
||||
if (candidates == NULL) return PyErr_NoMemory();
|
||||
if (candidates == NULL) { Py_DECREF(volumes); return PyErr_NoMemory();}
|
||||
|
||||
interfaceDetailData = get_device_ancestors(hDevInfo, i, candidates, &iterate, ddebug);
|
||||
if (interfaceDetailData == NULL) {
|
||||
PyErr_Print(); continue;
|
||||
PyErr_Print();
|
||||
Py_DECREF(candidates); candidates = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
length = wcslen(interfaceDetailData->DevicePath);
|
||||
@ -653,12 +650,13 @@ winutil_get_removable_drives(PyObject *self, PyObject *args) {
|
||||
key = PyBytes_FromFormat("%c", (char)g_drives[j].letter);
|
||||
if (key == NULL) return PyErr_NoMemory();
|
||||
PyDict_SetItem(volumes, key, candidates);
|
||||
Py_DECREF(candidates);
|
||||
Py_DECREF(key); key = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Py_XDECREF(candidates); candidates = NULL;
|
||||
PyMem_Free(interfaceDetailData);
|
||||
} //for
|
||||
|
||||
@ -672,7 +670,8 @@ winutil_get_usb_devices(PyObject *self, PyObject *args) {
|
||||
HDEVINFO hDevInfo;
|
||||
DWORD i; BOOL iterate = TRUE;
|
||||
PyObject *devices, *temp = (PyObject *)1;
|
||||
LPTSTR buffer;
|
||||
LPWSTR buffer;
|
||||
BOOL ok = 1;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "")) return NULL;
|
||||
|
||||
@ -682,8 +681,10 @@ winutil_get_usb_devices(PyObject *self, PyObject *args) {
|
||||
// Create a Device information set with all USB devices
|
||||
hDevInfo = create_device_info_set(NULL, L"USB", 0,
|
||||
DIGCF_PRESENT | DIGCF_ALLCLASSES);
|
||||
if (hDevInfo == INVALID_HANDLE_VALUE)
|
||||
if (hDevInfo == INVALID_HANDLE_VALUE) {
|
||||
Py_DECREF(devices);
|
||||
return NULL;
|
||||
}
|
||||
// Enumerate through the set
|
||||
for (i=0; iterate; i++) {
|
||||
buffer = get_registry_property(hDevInfo, i, SPDRP_HARDWAREID, &iterate);
|
||||
@ -691,16 +692,17 @@ winutil_get_usb_devices(PyObject *self, PyObject *args) {
|
||||
PyErr_Print(); continue;
|
||||
}
|
||||
buffersize = wcslen(buffer);
|
||||
for (j = 0; j < buffersize; j++) buffer[j] = tolower(buffer[j]);
|
||||
for (j = 0; j < buffersize; j++) buffer[j] = towlower(buffer[j]);
|
||||
temp = PyUnicode_FromWideChar(buffer, buffersize);
|
||||
PyMem_Free(buffer);
|
||||
if (temp == NULL) {
|
||||
PyErr_NoMemory();
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
PyList_Append(devices, temp);
|
||||
PyList_Append(devices, temp); Py_DECREF(temp); temp = NULL;
|
||||
} //for
|
||||
if (temp == NULL) { Py_DECREF(devices); devices = NULL; }
|
||||
if (!ok) { Py_DECREF(devices); devices = NULL; }
|
||||
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
return devices;
|
||||
}
|
||||
@ -711,7 +713,7 @@ winutil_is_usb_device_connected(PyObject *self, PyObject *args) {
|
||||
unsigned int vid, pid;
|
||||
HDEVINFO hDevInfo;
|
||||
DWORD i; BOOL iterate = TRUE;
|
||||
LPTSTR buffer;
|
||||
LPWSTR buffer;
|
||||
int found = FALSE;
|
||||
PyObject *ans;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user