mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
0.8.68+
This commit is contained in:
commit
1dc4dd91dc
@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
- title: "Add an option under Preferences->Look & Feel->Book Details to hide the cover in the book details panel"
|
- 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:
|
bug fixes:
|
||||||
- title: "Fix sorting by author not working in the device view in calibre when connected to iTunes"
|
- 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 = []
|
feeds = []
|
||||||
for title, url in [
|
for title, url in [
|
||||||
######## COMICS - GENERAL ########
|
######## COMICS - GENERAL ########
|
||||||
#(u"9 Chickweed Lane", u"http://www.arcamax.com/ninechickweedlane"),
|
#(u"9 Chickweed Lane", #u"http://www.arcamax.com/thefunnies/ninechickweedlane"),
|
||||||
#(u"Agnes", u"http://www.arcamax.com/agnes"),
|
#(u"Agnes", u"http://www.arcamax.com/thefunnies/agnes"),
|
||||||
#(u"Andy Capp", u"http://www.arcamax.com/andycapp"),
|
#(u"Andy Capp", #u"http://www.arcamax.com/thefunnies/andycapp"),
|
||||||
(u"BC", u"http://www.arcamax.com/thefunnies/bc"),
|
(u"BC", u"http://www.arcamax.com/thefunnies/bc"),
|
||||||
#(u"Baby Blues", u"http://www.arcamax.com/babyblues"),
|
#(u"Baby Blues", #u"http://www.arcamax.com/thefunnies/babyblues"),
|
||||||
#(u"Beetle Bailey", u"http://www.arcamax.com/beetlebailey"),
|
#(u"Beetle Bailey", #u"http://www.arcamax.com/thefunnies/beetlebailey"),
|
||||||
(u"Blondie", u"http://www.arcamax.com/thefunnies/blondie"),
|
(u"Blondie", u"http://www.arcamax.com/thefunnies/blondie"),
|
||||||
#u"Boondocks", u"http://www.arcamax.com/boondocks"),
|
#u"Boondocks", u"http://www.arcamax.com/thefunnies/boondocks"),
|
||||||
#(u"Cathy", u"http://www.arcamax.com/cathy"),
|
#(u"Cathy", u"http://www.arcamax.com/thefunnies/cathy"),
|
||||||
#(u"Daddys Home", u"http://www.arcamax.com/daddyshome"),
|
#(u"Daddys Home", #u"http://www.arcamax.com/thefunnies/daddyshome"),
|
||||||
(u"Dilbert", u"http://www.arcamax.com/thefunnies/dilbert"),
|
(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"Dog Eat Doug", u"http://www.arcamax.com/thefunnies/dogeatdoug"),
|
||||||
(u"Doonesbury", u"http://www.arcamax.com/thefunnies/doonesbury"),
|
(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"Family Circus", u"http://www.arcamax.com/thefunnies/familycircus"),
|
||||||
(u"Garfield", u"http://www.arcamax.com/thefunnies/garfield"),
|
(u"Garfield", u"http://www.arcamax.com/thefunnies/garfield"),
|
||||||
#(u"Get Fuzzy", u"http://www.arcamax.com/getfuzzy"),
|
#(u"Get Fuzzy", #u"http://www.arcamax.com/thefunnies/getfuzzy"),
|
||||||
#(u"Girls and Sports", u"http://www.arcamax.com/girlsandsports"),
|
#(u"Girls and Sports", #u"http://www.arcamax.com/thefunnies/girlsandsports"),
|
||||||
#(u"Hagar the Horrible", u"http://www.arcamax.com/hagarthehorrible"),
|
#(u"Hagar the Horrible", #u"http://www.arcamax.com/thefunnies/hagarthehorrible"),
|
||||||
#(u"Heathcliff", u"http://www.arcamax.com/heathcliff"),
|
#(u"Heathcliff", #u"http://www.arcamax.com/thefunnies/heathcliff"),
|
||||||
#(u"Jerry King Cartoons", u"http://www.arcamax.com/humorcartoon"),
|
#(u"Jerry King Cartoons", #u"http://www.arcamax.com/thefunnies/humorcartoon"),
|
||||||
#(u"Luann", u"http://www.arcamax.com/luann"),
|
#(u"Luann", u"http://www.arcamax.com/thefunnies/luann"),
|
||||||
#(u"Momma", u"http://www.arcamax.com/momma"),
|
#(u"Momma", u"http://www.arcamax.com/thefunnies/momma"),
|
||||||
#(u"Mother Goose and Grimm", u"http://www.arcamax.com/mothergooseandgrimm"),
|
#(u"Mother Goose and Grimm", #u"http://www.arcamax.com/thefunnies/mothergooseandgrimm"),
|
||||||
(u"Mutts", u"http://www.arcamax.com/thefunnies/mutts"),
|
(u"Mutts", u"http://www.arcamax.com/thefunnies/mutts"),
|
||||||
#(u"Non Sequitur", u"http://www.arcamax.com/nonsequitur"),
|
#(u"Non Sequitur", #u"http://www.arcamax.com/thefunnies/nonsequitur"),
|
||||||
#(u"Pearls Before Swine", u"http://www.arcamax.com/pearlsbeforeswine"),
|
#(u"Pearls Before Swine", #u"http://www.arcamax.com/thefunnies/pearlsbeforeswine"),
|
||||||
#(u"Pickles", u"http://www.arcamax.com/pickles"),
|
#(u"Pickles", u"http://www.arcamax.com/thefunnies/pickles"),
|
||||||
#(u"Red and Rover", u"http://www.arcamax.com/redandrover"),
|
#(u"Red and Rover", #u"http://www.arcamax.com/thefunnies/redandrover"),
|
||||||
#(u"Rubes", u"http://www.arcamax.com/rubes"),
|
#(u"Rubes", u"http://www.arcamax.com/thefunnies/rubes"),
|
||||||
#(u"Rugrats", u"http://www.arcamax.com/rugrats"),
|
#(u"Rugrats", u"http://www.arcamax.com/thefunnies/rugrats"),
|
||||||
(u"Speed Bump", u"http://www.arcamax.com/thefunnies/speedbump"),
|
(u"Speed Bump", u"http://www.arcamax.com/thefunnies/speedbump"),
|
||||||
(u"Wizard of Id", u"http://www.arcamax.com/thefunnies/wizardofid"),
|
(u"Wizard of Id", u"http://www.arcamax.com/thefunnies/wizardofid"),
|
||||||
(u"Zits", u"http://www.arcamax.com/thefunnies/zits"),
|
(u"Zits", u"http://www.arcamax.com/thefunnies/zits"),
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||||
title = u'Birmingham post'
|
title = u'Birmingham post'
|
||||||
description = 'News for Birmingham UK'
|
description = 'Author D.Asbury. News for Birmingham UK'
|
||||||
timefmt = ''
|
#timefmt = ''
|
||||||
|
# last update 8/9/12
|
||||||
__author__ = 'Dave Asbury'
|
__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
|
oldest_article = 2
|
||||||
max_articles_per_feed = 12
|
max_articles_per_feed = 12
|
||||||
|
linearize_tables = True
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
|
no_stylesheets = True
|
||||||
#auto_cleanup = True
|
#auto_cleanup = True
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
|
|
||||||
@ -17,11 +20,12 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
|||||||
|
|
||||||
|
|
||||||
keep_only_tags = [
|
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(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(name='div',attrs={'class' : 'article-image full'}),
|
||||||
dict(attrs={'clas' : 'art-o art-align-center otm-1 '}),
|
dict(attrs={'clas' : 'art-o art-align-center otm-1 '}),
|
||||||
dict(name='div',attrs={'class' : 'article main'}),
|
dict(name='div',attrs={'class' : 'article main'}),
|
||||||
#dict(name='p')
|
#dict(name='p')
|
||||||
#dict(attrs={'id' : 'three-col'})
|
#dict(attrs={'id' : 'three-col'})
|
||||||
]
|
]
|
||||||
@ -37,11 +41,9 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
|||||||
(u'Bloggs & Comments',u'http://www.birminghampost.net/comment/rss.xml')
|
(u'Bloggs & Comments',u'http://www.birminghampost.net/comment/rss.xml')
|
||||||
|
|
||||||
]
|
]
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
body {font: sans-serif medium;}'
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;text-align:center;}
|
||||||
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{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||||
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; }
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
span{ font-size:9.5px; font-weight:bold;font-style:italic}
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
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;}
|
'''
|
||||||
|
|
||||||
'''
|
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
from calibre import browser
|
from calibre import browser
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||||
title = u'Countryfile.com'
|
title = u'Countryfile.com'
|
||||||
#cover_url = 'http://www.countryfile.com/sites/default/files/imagecache/160px_wide/cover/2_1.jpg'
|
#cover_url = 'http://www.countryfile.com/sites/default/files/imagecache/160px_wide/cover/2_1.jpg'
|
||||||
__author__ = 'Dave Asbury'
|
__author__ = 'Dave Asbury'
|
||||||
description = 'The official website of Countryfile Magazine'
|
description = 'The official website of Countryfile Magazine'
|
||||||
# last updated 15/4/12
|
# last updated 9/9//12
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
oldest_article = 30
|
oldest_article = 30
|
||||||
max_articles_per_feed = 25
|
max_articles_per_feed = 25
|
||||||
@ -17,13 +16,14 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
def get_cover_url(self):
|
def get_cover_url(self):
|
||||||
soup = self.index_to_soup('http://www.countryfile.com/')
|
soup = self.index_to_soup('http://www.countryfile.com/')
|
||||||
cov = soup.find(attrs={'class' : 'imagecache imagecache-160px_wide imagecache-linked imagecache-160px_wide_linked'})
|
cov = soup.find(attrs={'class' : 'imagecache imagecache-160px_wide imagecache-linked imagecache-160px_wide_linked'})
|
||||||
#print '******** ',cov,' ***'
|
print '******** ',cov,' ***'
|
||||||
cov2 = str(cov)
|
cov2 = str(cov)
|
||||||
cov2=cov2[124:-90]
|
cov2=cov2[140:223]
|
||||||
#print '******** ',cov2,' ***'
|
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
|
# try to get cover - if can't get known cover
|
||||||
br = browser()
|
br = browser()
|
||||||
|
|
||||||
br.set_handle_redirect(False)
|
br.set_handle_redirect(False)
|
||||||
try:
|
try:
|
||||||
br.open_novisit(cov2)
|
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')
|
||||||
|
|
@ -15,11 +15,11 @@ class HoustonChronicle(BasicNewsRecipe):
|
|||||||
remove_attributes = ['style']
|
remove_attributes = ['style']
|
||||||
auto_cleanup = True
|
auto_cleanup = True
|
||||||
|
|
||||||
oldest_article = 2.0
|
oldest_article = 3.0
|
||||||
|
|
||||||
#keep_only_tags = {'class':lambda x: x and ('hst-articletitle' in x or
|
#keep_only_tags = {'class':lambda x: x and ('hst-articletitle' in x or
|
||||||
#'hst-articletext' in x or 'hst-galleryitem' in x)}
|
#'hst-articletext' in x or 'hst-galleryitem' in x)}
|
||||||
#remove_attributes = ['xmlns']
|
remove_attributes = ['xmlns']
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
('News', "http://www.chron.com/rss/feed/News-270.php"),
|
('News', "http://www.chron.com/rss/feed/News-270.php"),
|
||||||
@ -38,4 +38,4 @@ class HoustonChronicle(BasicNewsRecipe):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||||
title = u'Metro UK'
|
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 = ''
|
#timefmt = ''
|
||||||
__author__ = 'Dave Asbury'
|
__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'
|
cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/276636_117118184990145_2132092232_n.jpg'
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
oldest_article = 1
|
oldest_article = 1
|
||||||
@ -17,23 +17,24 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
|||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
masthead_url = 'http://e-edition.metro.co.uk/images/metro_logo.gif'
|
masthead_url = 'http://e-edition.metro.co.uk/images/metro_logo.gif'
|
||||||
extra_css = '''
|
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;}
|
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;}
|
p{font-family:Arial,Helvetica,sans-serif;font-size:1.0em;}
|
||||||
body{font-family:Helvetica,Arial,sans-serif;font-size:1.0em;}
|
body{font-family:Helvetica,Arial,sans-serif;font-size:1.0em;}
|
||||||
'''
|
'''
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
#dict(name='h1'),
|
#dict(name='h1'),
|
||||||
#dict(name='h2'),
|
#dict(name='h2'),
|
||||||
#dict(name='div', attrs={'class' : ['row','article','img-cnt figure','clrd']})
|
#dict(name='div', attrs={'class' : ['row','article','img-cnt figure','clrd']})
|
||||||
#dict(name='h3'),
|
#dict(name='h3'),
|
||||||
#dict(attrs={'class' : 'BText'}),
|
#dict(attrs={'class' : 'BText'}),
|
||||||
]
|
]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
|
dict(name='div',attrs={'class' : 'art-fd fd-gr1-b clrd'}),
|
||||||
dict(name='span',attrs={'class' : 'share'}),
|
dict(name='span',attrs={'class' : 'share'}),
|
||||||
dict(name='li'),
|
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={'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(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_before = dict(name='h1')
|
||||||
#remove_tags_after = dict(attrs={'id':['topic-buttons']})
|
#remove_tags_after = dict(attrs={'id':['topic-buttons']})
|
||||||
|
@ -73,14 +73,20 @@ class AdvancedUserRecipe1249039563(BasicNewsRecipe):
|
|||||||
Change Log:
|
Change Log:
|
||||||
Date: 10/15/2010
|
Date: 10/15/2010
|
||||||
Feeds updated by Martin Tarenskeen
|
Feeds updated by Martin Tarenskeen
|
||||||
|
Date: 09/09/2012
|
||||||
|
Feeds updated by Eric Lammerts
|
||||||
'''
|
'''
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Laatste Nieuws', u'http://www.volkskrant.nl/rss/laatstenieuws.rss'),
|
(u'Nieuws', u'http://www.volkskrant.nl/nieuws/rss.xml'),
|
||||||
(u'Binnenland', u'http://www.volkskrant.nl/rss/nederland.rss'),
|
(u'Binnenland', u'http://www.volkskrant.nl/nieuws/binnenland/rss.xml'),
|
||||||
(u'Buitenland', u'http://www.volkskrant.nl/rss/internationaal.rss'),
|
(u'Buitenland', u'http://www.volkskrant.nl/buitenland/rss.xml'),
|
||||||
(u'Economie', u'http://www.volkskrant.nl/rss/economie.rss'),
|
(u'Economie', u'http://www.volkskrant.nl/nieuws/economie/rss.xml'),
|
||||||
(u'Sport', u'http://www.volkskrant.nl/rss/sport.rss'),
|
(u'Politiek', u'http://www.volkskrant.nl/politiek/rss.xml'),
|
||||||
(u'Cultuur', u'http://www.volkskrant.nl/rss/kunst.rss'),
|
(u'Sport', u'http://www.volkskrant.nl/sport/rss.xml'),
|
||||||
(u'Gezondheid & Wetenschap', u'http://www.volkskrant.nl/rss/wetenschap.rss'),
|
(u'Cultuur', u'http://www.volkskrant.nl/nieuws/cultuur/rss.xml'),
|
||||||
(u'Internet & Media', u'http://www.volkskrant.nl/rss/media.rss') ]
|
(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') ]
|
||||||
|
@ -65,7 +65,7 @@ class WallStreetJournal(BasicNewsRecipe):
|
|||||||
br['password'] = self.password
|
br['password'] = self.password
|
||||||
res = br.submit()
|
res = br.submit()
|
||||||
raw = res.read()
|
raw = res.read()
|
||||||
if '>Log Out<' not in raw:
|
if 'Welcome,' not in raw and '>Logout<' not in raw and '>Log Out<' not in raw:
|
||||||
raise ValueError('Failed to log in to wsj.com, check your '
|
raise ValueError('Failed to log in to wsj.com, check your '
|
||||||
'username and password')
|
'username and password')
|
||||||
return br
|
return br
|
||||||
|
@ -118,13 +118,13 @@ class ZeitEPUBAbo(BasicNewsRecipe):
|
|||||||
|
|
||||||
def build_index(self):
|
def build_index(self):
|
||||||
domain = "https://premium.zeit.de"
|
domain = "https://premium.zeit.de"
|
||||||
url = domain + "/abo/zeit_digital"
|
url = domain + "/abo/digitalpaket"
|
||||||
browser = self.get_browser()
|
browser = self.get_browser()
|
||||||
|
|
||||||
# new login process
|
# new login process
|
||||||
response = browser.open(url)
|
response = browser.open(url)
|
||||||
# Get rid of nested form
|
# 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.set_response(response)
|
||||||
browser.select_form(nr=2)
|
browser.select_form(nr=2)
|
||||||
browser.form['name']=self.username
|
browser.form['name']=self.username
|
||||||
@ -177,13 +177,13 @@ class ZeitEPUBAbo(BasicNewsRecipe):
|
|||||||
try:
|
try:
|
||||||
self.log.warning('Trying PDF-based cover')
|
self.log.warning('Trying PDF-based cover')
|
||||||
domain = "https://premium.zeit.de"
|
domain = "https://premium.zeit.de"
|
||||||
url = domain + "/abo/zeit_digital"
|
url = domain + "/abo/digitalpaket"
|
||||||
browser = self.get_browser()
|
browser = self.get_browser()
|
||||||
|
|
||||||
# new login process
|
# new login process
|
||||||
response=browser.open(url)
|
response=browser.open(url)
|
||||||
# Get rid of nested form
|
# 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.set_response(response)
|
||||||
|
|
||||||
browser.select_form(nr=2)
|
browser.select_form(nr=2)
|
||||||
|
Binary file not shown.
@ -187,7 +187,7 @@ MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
|
|||||||
// fill it with the proper elements,
|
// fill it with the proper elements,
|
||||||
// and clean up the bbox
|
// and clean up the bbox
|
||||||
//
|
//
|
||||||
line = BBOX();
|
var line = BBOX();
|
||||||
state.first = broken; state.last = true;
|
state.first = broken; state.last = true;
|
||||||
this.SVGmoveLine(start,end,line,state,values);
|
this.SVGmoveLine(start,end,line,state,values);
|
||||||
line.Clean();
|
line.Clean();
|
||||||
|
@ -57,7 +57,7 @@ else:
|
|||||||
# On linux, unicode arguments to os file functions are coerced to an ascii
|
# On linux, unicode arguments to os file functions are coerced to an ascii
|
||||||
# bytestring if sys.getfilesystemencoding() == 'ascii', which is
|
# bytestring if sys.getfilesystemencoding() == 'ascii', which is
|
||||||
# just plain dumb. So issue a warning.
|
# 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. '
|
'This will cause problems with non-ascii filenames. '
|
||||||
'Set it to something like en_US.UTF-8.\n')
|
'Set it to something like en_US.UTF-8.\n')
|
||||||
except:
|
except:
|
||||||
|
@ -208,7 +208,7 @@ class ANDROID(USBMS):
|
|||||||
'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP',
|
'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP',
|
||||||
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C', 'PD',
|
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C', 'PD',
|
||||||
'PMP5097C', 'MASS', 'NOVO7', 'ZEKI', 'COBY', 'SXZ', 'USB_2.0',
|
'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',
|
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
||||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID',
|
'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',
|
'GT-S5830L_CARD', 'UNIVERSE', 'XT875', 'PRO', '.KOBO_VOX',
|
||||||
'THINKPAD_TABLET', 'SGH-T989', 'YP-G70', 'STORAGE_DEVICE',
|
'THINKPAD_TABLET', 'SGH-T989', 'YP-G70', 'STORAGE_DEVICE',
|
||||||
'ADVANCED', 'SGH-I727', 'USB_FLASH_DRIVER', 'ANDROID',
|
'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',
|
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||||
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
'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',
|
'USB_2.0_DRIVER', 'I9100T', 'P999DW_SD_CARD', 'KTABLET_PC',
|
||||||
'FILE-CD_GADGET', 'GT-I9001_CARD', 'USB_2.0', 'XT875',
|
'FILE-CD_GADGET', 'GT-I9001_CARD', 'USB_2.0', 'XT875',
|
||||||
'UMS_COMPOSITE', 'PRO', '.KOBO_VOX', 'SGH-T989_CARD', 'SGH-I727',
|
'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'
|
OSX_MAIN_MEM = 'Android Device Main Memory'
|
||||||
|
|
||||||
|
@ -110,3 +110,9 @@ class WrongDestinationError(PathError):
|
|||||||
trying to send books to a non existant storage card.'''
|
trying to send books to a non existant storage card.'''
|
||||||
pass
|
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
|
||||||
|
|
||||||
|
@ -288,7 +288,7 @@ class KINDLE2(KINDLE):
|
|||||||
name = 'Kindle 2/3/4/Touch Device Interface'
|
name = 'Kindle 2/3/4/Touch Device Interface'
|
||||||
description = _('Communicate with the Kindle 2/3/4/Touch eBook reader.')
|
description = _('Communicate with the Kindle 2/3/4/Touch eBook reader.')
|
||||||
|
|
||||||
FORMATS = KINDLE.FORMATS + ['pdf', 'azw4', 'pobi']
|
FORMATS = ['azw3'] + KINDLE.FORMATS + ['pdf', 'azw4', 'pobi']
|
||||||
DELETE_EXTS = KINDLE.DELETE_EXTS + ['.mbp1', '.mbs', '.sdr']
|
DELETE_EXTS = KINDLE.DELETE_EXTS + ['.mbp1', '.mbs', '.sdr']
|
||||||
|
|
||||||
PRODUCT_ID = [0x0002, 0x0004]
|
PRODUCT_ID = [0x0002, 0x0004]
|
||||||
@ -449,7 +449,7 @@ class KINDLE_DX(KINDLE2):
|
|||||||
name = 'Kindle DX Device Interface'
|
name = 'Kindle DX Device Interface'
|
||||||
description = _('Communicate with the Kindle DX eBook reader.')
|
description = _('Communicate with the Kindle DX eBook reader.')
|
||||||
|
|
||||||
|
FORMATS = KINDLE2.FORMATS[1:]
|
||||||
PRODUCT_ID = [0x0003]
|
PRODUCT_ID = [0x0003]
|
||||||
BCD = [0x0100]
|
BCD = [0x0100]
|
||||||
|
|
||||||
@ -462,7 +462,6 @@ class KINDLE_FIRE(KINDLE2):
|
|||||||
description = _('Communicate with the Kindle Fire')
|
description = _('Communicate with the Kindle Fire')
|
||||||
gui_name = 'Fire'
|
gui_name = 'Fire'
|
||||||
FORMATS = list(KINDLE2.FORMATS)
|
FORMATS = list(KINDLE2.FORMATS)
|
||||||
FORMATS.insert(0, 'azw3')
|
|
||||||
|
|
||||||
PRODUCT_ID = [0x0006]
|
PRODUCT_ID = [0x0006]
|
||||||
BCD = [0x216, 0x100]
|
BCD = [0x216, 0x100]
|
||||||
|
@ -59,4 +59,7 @@ class MTPDeviceBase(DevicePlugin):
|
|||||||
from calibre.devices.utils import build_template_regexp
|
from calibre.devices.utils import build_template_regexp
|
||||||
return build_template_regexp(self.save_template)
|
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.devices.mtp.base import debug
|
||||||
from calibre.ptempfile import SpooledTemporaryFile, PersistentTemporaryDirectory
|
from calibre.ptempfile import SpooledTemporaryFile, PersistentTemporaryDirectory
|
||||||
from calibre.utils.config import from_json, to_json, JSONConfig
|
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'%(
|
BASE = importlib.import_module('calibre.devices.mtp.%s.driver'%(
|
||||||
'windows' if iswindows else 'unix')).MTP_DEVICE
|
'windows' if iswindows else 'unix')).MTP_DEVICE
|
||||||
@ -47,10 +47,13 @@ class MTP_DEVICE(BASE):
|
|||||||
from calibre.library.save_to_disk import config
|
from calibre.library.save_to_disk import config
|
||||||
self._prefs = p = JSONConfig('mtp_devices')
|
self._prefs = p = JSONConfig('mtp_devices')
|
||||||
p.defaults['format_map'] = self.FORMATS
|
p.defaults['format_map'] = self.FORMATS
|
||||||
p.defaults['send_to'] = ['eBooks/import',
|
p.defaults['send_to'] = ['Calibre_Companion', 'Books',
|
||||||
'wordplayer/calibretransfer', 'Books', 'sdcard/ebooks',
|
'eBooks/import', 'eBooks', 'wordplayer/calibretransfer',
|
||||||
'eBooks', 'kindle']
|
'sdcard/ebooks', 'kindle']
|
||||||
p.defaults['send_template'] = config().parse().send_template
|
p.defaults['send_template'] = config().parse().send_template
|
||||||
|
p.defaults['blacklist'] = []
|
||||||
|
p.defaults['history'] = {}
|
||||||
|
p.defaults['rules'] = []
|
||||||
|
|
||||||
return self._prefs
|
return self._prefs
|
||||||
|
|
||||||
@ -74,6 +77,11 @@ class MTP_DEVICE(BASE):
|
|||||||
self.current_library_uuid = library_uuid
|
self.current_library_uuid = library_uuid
|
||||||
self.location_paths = None
|
self.location_paths = None
|
||||||
BASE.open(self, devices, library_uuid)
|
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 {{{
|
# Device information {{{
|
||||||
def _update_drive_info(self, storage, location_code, name=None):
|
def _update_drive_info(self, storage, location_code, name=None):
|
||||||
@ -99,7 +107,7 @@ class MTP_DEVICE(BASE):
|
|||||||
dinfo['mtp_prefix'] = storage.storage_prefix
|
dinfo['mtp_prefix'] = storage.storage_prefix
|
||||||
raw = json.dumps(dinfo, default=to_json)
|
raw = json.dumps(dinfo, default=to_json)
|
||||||
self.put_file(storage, self.DRIVEINFO, BytesIO(raw), len(raw))
|
self.put_file(storage, self.DRIVEINFO, BytesIO(raw), len(raw))
|
||||||
self.driveinfo = dinfo
|
self.driveinfo[location_code] = dinfo
|
||||||
|
|
||||||
def get_device_information(self, end_session=True):
|
def get_device_information(self, end_session=True):
|
||||||
self.report_progress(1.0, _('Get device information...'))
|
self.report_progress(1.0, _('Get device information...'))
|
||||||
@ -266,15 +274,18 @@ class MTP_DEVICE(BASE):
|
|||||||
self.plugboards = plugboards
|
self.plugboards = plugboards
|
||||||
self.plugboard_func = pb_func
|
self.plugboard_func = pb_func
|
||||||
|
|
||||||
def create_upload_path(self, path, mdata, fname):
|
def create_upload_path(self, path, mdata, fname, routing):
|
||||||
from calibre.devices.utils import create_upload_path
|
from calibre.devices.utils import create_upload_path
|
||||||
from calibre.utils.filenames import ascii_filename as sanitize
|
from calibre.utils.filenames import ascii_filename as sanitize
|
||||||
|
ext = fname.rpartition('.')[-1].lower()
|
||||||
|
path = routing.get(ext, path)
|
||||||
|
|
||||||
filepath = create_upload_path(mdata, fname, self.save_template, sanitize,
|
filepath = create_upload_path(mdata, fname, self.save_template, sanitize,
|
||||||
prefix_path=path,
|
prefix_path=path,
|
||||||
path_type=posixpath,
|
path_type=posixpath,
|
||||||
maxlen=self.MAX_PATH_LEN,
|
maxlen=self.MAX_PATH_LEN,
|
||||||
use_subdirs = True,
|
use_subdirs=True,
|
||||||
news_in_folder = self.NEWS_IN_FOLDER,
|
news_in_folder=self.NEWS_IN_FOLDER,
|
||||||
)
|
)
|
||||||
return tuple(x for x in filepath.split('/'))
|
return tuple(x for x in filepath.split('/'))
|
||||||
|
|
||||||
@ -293,7 +304,7 @@ class MTP_DEVICE(BASE):
|
|||||||
p = path
|
p = path
|
||||||
break
|
break
|
||||||
if p is None:
|
if p is None:
|
||||||
p = 'eBooks'
|
p = 'Books'
|
||||||
self.location_paths[loc] = p
|
self.location_paths[loc] = p
|
||||||
|
|
||||||
return self.location_paths[on_card]
|
return self.location_paths[on_card]
|
||||||
@ -322,8 +333,10 @@ class MTP_DEVICE(BASE):
|
|||||||
self.report_progress(0, _('Transferring books to device...'))
|
self.report_progress(0, _('Transferring books to device...'))
|
||||||
i, total = 0, len(files)
|
i, total = 0, len(files)
|
||||||
|
|
||||||
|
routing = {fmt:dest for fmt,dest in self.get_pref('rules')}
|
||||||
|
|
||||||
for infile, fname, mi in izip(files, names, metadata):
|
for infile, fname, mi in izip(files, names, metadata):
|
||||||
path = self.create_upload_path(prefix, mi, fname)
|
path = self.create_upload_path(prefix, mi, fname, routing)
|
||||||
parent = self.ensure_parent(storage, path)
|
parent = self.ensure_parent(storage, path)
|
||||||
if hasattr(infile, 'read'):
|
if hasattr(infile, 'read'):
|
||||||
pos = infile.tell()
|
pos = infile.tell()
|
||||||
|
@ -7,7 +7,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import operator, traceback, pprint, sys
|
import operator, traceback, pprint, sys, time
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from functools import partial
|
from functools import partial
|
||||||
@ -15,8 +15,8 @@ from functools import partial
|
|||||||
from calibre import prints, as_unicode
|
from calibre import prints, as_unicode
|
||||||
from calibre.constants import plugins
|
from calibre.constants import plugins
|
||||||
from calibre.ptempfile import SpooledTemporaryFile
|
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
|
from calibre.devices.mtp.base import MTPDeviceBase, synchronous, debug
|
||||||
|
|
||||||
MTPDevice = namedtuple('MTPDevice', 'busnum devnum vendor_id product_id '
|
MTPDevice = namedtuple('MTPDevice', 'busnum devnum vendor_id product_id '
|
||||||
'bcd serial manufacturer product')
|
'bcd serial manufacturer product')
|
||||||
@ -99,19 +99,25 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
return False
|
return False
|
||||||
p('Known MTP devices connected:')
|
p('Known MTP devices connected:')
|
||||||
for d in devs: p(d)
|
for d in devs: p(d)
|
||||||
d = devs[0]
|
|
||||||
p('\nTrying to open:', d)
|
for d in devs:
|
||||||
try:
|
p('\nTrying to open:', d)
|
||||||
self.open(d, 'debug')
|
try:
|
||||||
except:
|
self.open(d, 'debug')
|
||||||
p('Opening device failed:')
|
except BlacklistedDevice:
|
||||||
p(traceback.format_exc())
|
p('This device has been blacklisted by the user')
|
||||||
return False
|
continue
|
||||||
p('Opened', self.current_friendly_name, 'successfully')
|
except:
|
||||||
p('Storage info:')
|
p('Opening device failed:')
|
||||||
p(pprint.pformat(self.dev.storage_info))
|
p(traceback.format_exc())
|
||||||
self.eject()
|
return False
|
||||||
return True
|
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
|
@synchronous
|
||||||
def create_device(self, connected_device):
|
def create_device(self, connected_device):
|
||||||
@ -167,6 +173,12 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
if not storage:
|
if not storage:
|
||||||
self.blacklisted_devices.add(connected_device)
|
self.blacklisted_devices.add(connected_device)
|
||||||
raise OpenFailed('No storage found for device %s'%(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._main_id = storage[0]['id']
|
||||||
self._carda_id = self._cardb_id = None
|
self._carda_id = self._cardb_id = None
|
||||||
if len(storage) > 1:
|
if len(storage) > 1:
|
||||||
@ -176,11 +188,13 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
self.current_friendly_name = self.dev.friendly_name
|
self.current_friendly_name = self.dev.friendly_name
|
||||||
if not self.current_friendly_name:
|
if not self.current_friendly_name:
|
||||||
self.current_friendly_name = self.dev.model_name or _('Unknown MTP device')
|
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
|
@property
|
||||||
def filesystem_cache(self):
|
def filesystem_cache(self):
|
||||||
if self._filesystem_cache is None:
|
if self._filesystem_cache is None:
|
||||||
|
st = time.time()
|
||||||
|
debug('Loading filesystem metadata...')
|
||||||
from calibre.devices.mtp.filesystem_cache import FilesystemCache
|
from calibre.devices.mtp.filesystem_cache import FilesystemCache
|
||||||
with self.lock:
|
with self.lock:
|
||||||
storage, all_items, all_errs = [], [], []
|
storage, all_items, all_errs = [], [], []
|
||||||
@ -208,6 +222,7 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
self.current_friendly_name,
|
self.current_friendly_name,
|
||||||
self.format_errorstack(all_errs)))
|
self.format_errorstack(all_errs)))
|
||||||
self._filesystem_cache = FilesystemCache(storage, all_items)
|
self._filesystem_cache = FilesystemCache(storage, all_items)
|
||||||
|
debug('Filesystem metadata loaded in %g seconds'%(time.time()-st))
|
||||||
return self._filesystem_cache
|
return self._filesystem_cache
|
||||||
|
|
||||||
@synchronous
|
@synchronous
|
||||||
|
@ -15,8 +15,8 @@ from itertools import chain
|
|||||||
from calibre import as_unicode, prints
|
from calibre import as_unicode, prints
|
||||||
from calibre.constants import plugins, __appname__, numeric_version
|
from calibre.constants import plugins, __appname__, numeric_version
|
||||||
from calibre.ptempfile import SpooledTemporaryFile
|
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
|
from calibre.devices.mtp.base import MTPDeviceBase, debug
|
||||||
|
|
||||||
class ThreadingViolation(Exception):
|
class ThreadingViolation(Exception):
|
||||||
|
|
||||||
@ -163,6 +163,9 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
p('\nTrying to open:', pnp_id)
|
p('\nTrying to open:', pnp_id)
|
||||||
try:
|
try:
|
||||||
self.open(pnp_id, 'debug-detection')
|
self.open(pnp_id, 'debug-detection')
|
||||||
|
except BlacklistedDevice:
|
||||||
|
p('This device has been blacklisted by the user')
|
||||||
|
continue
|
||||||
except:
|
except:
|
||||||
p('Open failed:')
|
p('Open failed:')
|
||||||
p(traceback.format_exc())
|
p(traceback.format_exc())
|
||||||
@ -172,7 +175,7 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
p('Opened', self.current_friendly_name, 'successfully')
|
p('Opened', self.current_friendly_name, 'successfully')
|
||||||
p('Device info:')
|
p('Device info:')
|
||||||
p(pprint.pformat(self.dev.data))
|
p(pprint.pformat(self.dev.data))
|
||||||
self.eject()
|
self.post_yank_cleanup()
|
||||||
return True
|
return True
|
||||||
p('No suitable MTP devices found')
|
p('No suitable MTP devices found')
|
||||||
return False
|
return False
|
||||||
@ -196,6 +199,8 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
@property
|
@property
|
||||||
def filesystem_cache(self):
|
def filesystem_cache(self):
|
||||||
if self._filesystem_cache is None:
|
if self._filesystem_cache is None:
|
||||||
|
debug('Loading filesystem metadata...')
|
||||||
|
st = time.time()
|
||||||
from calibre.devices.mtp.filesystem_cache import FilesystemCache
|
from calibre.devices.mtp.filesystem_cache import FilesystemCache
|
||||||
ts = self.total_space()
|
ts = self.total_space()
|
||||||
all_storage = []
|
all_storage = []
|
||||||
@ -215,6 +220,7 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
all_storage.append(storage)
|
all_storage.append(storage)
|
||||||
items.append(id_map.itervalues())
|
items.append(id_map.itervalues())
|
||||||
self._filesystem_cache = FilesystemCache(all_storage, chain(*items))
|
self._filesystem_cache = FilesystemCache(all_storage, chain(*items))
|
||||||
|
debug('Filesystem metadata loaded in %g seconds'%(time.time()-st))
|
||||||
return self._filesystem_cache
|
return self._filesystem_cache
|
||||||
|
|
||||||
@same_thread
|
@same_thread
|
||||||
@ -225,7 +231,6 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
self._main_id = self._carda_id = self._cardb_id = None
|
self._main_id = self._carda_id = self._cardb_id = None
|
||||||
self.dev = self._filesystem_cache = None
|
self.dev = self._filesystem_cache = None
|
||||||
|
|
||||||
|
|
||||||
@same_thread
|
@same_thread
|
||||||
def post_yank_cleanup(self):
|
def post_yank_cleanup(self):
|
||||||
self.currently_connected_pnp_id = self.current_friendly_name = None
|
self.currently_connected_pnp_id = self.current_friendly_name = None
|
||||||
@ -256,6 +261,13 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
if not storage:
|
if not storage:
|
||||||
self.blacklisted_devices.add(connected_device)
|
self.blacklisted_devices.add(connected_device)
|
||||||
raise OpenFailed('No storage found for device %s'%(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']
|
self._main_id = storage[0]['id']
|
||||||
if len(storage) > 1:
|
if len(storage) > 1:
|
||||||
self._carda_id = storage[1]['id']
|
self._carda_id = storage[1]['id']
|
||||||
@ -266,7 +278,7 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
self.current_friendly_name = devdata.get('model_name',
|
self.current_friendly_name = devdata.get('model_name',
|
||||||
_('Unknown MTP device'))
|
_('Unknown MTP device'))
|
||||||
self.currently_connected_pnp_id = connected_device
|
self.currently_connected_pnp_id = connected_device
|
||||||
self.current_serial_num = devdata.get('serial_number', None)
|
self.current_serial_num = snum
|
||||||
|
|
||||||
@same_thread
|
@same_thread
|
||||||
def get_basic_device_information(self):
|
def get_basic_device_information(self):
|
||||||
|
@ -329,8 +329,48 @@ class DeviceScanner(object):
|
|||||||
return device.is_usb_connected(self.devices, debug=debug,
|
return device.is_usb_connected(self.devices, debug=debug,
|
||||||
only_presence=only_presence)
|
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):
|
def main(args=sys.argv):
|
||||||
|
test_for_mem_leak()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -84,6 +84,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
PREFIX = ''
|
PREFIX = ''
|
||||||
BACKLOADING_ERROR_MESSAGE = None
|
BACKLOADING_ERROR_MESSAGE = None
|
||||||
|
|
||||||
|
SAVE_TEMPLATE = '{title} - {authors} ({id})'
|
||||||
|
|
||||||
# Some network protocol constants
|
# Some network protocol constants
|
||||||
BASE_PACKET_LEN = 4096
|
BASE_PACKET_LEN = 4096
|
||||||
PROTOCOL_VERSION = 1
|
PROTOCOL_VERSION = 1
|
||||||
|
@ -974,6 +974,9 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
def get_carda_ebook_dir(self, for_upload=False):
|
def get_carda_ebook_dir(self, for_upload=False):
|
||||||
return self.EBOOK_DIR_CARD_A
|
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):
|
def _sanity_check(self, on_card, files):
|
||||||
from calibre.devices.utils import sanity_check
|
from calibre.devices.utils import sanity_check
|
||||||
sanity_check(on_card, files, self.card_prefix(), self.free_space())
|
sanity_check(on_card, files, self.card_prefix(), self.free_space())
|
||||||
|
@ -66,6 +66,7 @@ class RecipeInput(InputFormatPlugin):
|
|||||||
if os.access(recipe_or_file, os.R_OK):
|
if os.access(recipe_or_file, os.R_OK):
|
||||||
self.recipe_source = open(recipe_or_file, 'rb').read()
|
self.recipe_source = open(recipe_or_file, 'rb').read()
|
||||||
recipe = compile_recipe(self.recipe_source)
|
recipe = compile_recipe(self.recipe_source)
|
||||||
|
log('Using custom recipe')
|
||||||
else:
|
else:
|
||||||
from calibre.web.feeds.recipes.collection import \
|
from calibre.web.feeds.recipes.collection import \
|
||||||
get_builtin_recipe_by_title
|
get_builtin_recipe_by_title
|
||||||
@ -87,12 +88,15 @@ class RecipeInput(InputFormatPlugin):
|
|||||||
'back to builtin one')
|
'back to builtin one')
|
||||||
builtin = True
|
builtin = True
|
||||||
if builtin:
|
if builtin:
|
||||||
|
log('Using bundled builtin recipe')
|
||||||
raw = get_builtin_recipe_by_title(title, log=log,
|
raw = get_builtin_recipe_by_title(title, log=log,
|
||||||
download_recipe=False)
|
download_recipe=False)
|
||||||
if raw is None:
|
if raw is None:
|
||||||
raise ValueError('Failed to find builtin recipe: '+title)
|
raise ValueError('Failed to find builtin recipe: '+title)
|
||||||
recipe = compile_recipe(raw)
|
recipe = compile_recipe(raw)
|
||||||
self.recipe_source = raw
|
self.recipe_source = raw
|
||||||
|
else:
|
||||||
|
log('Using downloaded builtin recipe')
|
||||||
|
|
||||||
if recipe is None:
|
if recipe is None:
|
||||||
raise ValueError('%r is not a valid recipe file or builtin recipe' %
|
raise ValueError('%r is not a valid recipe file or builtin recipe' %
|
||||||
|
@ -1009,6 +1009,8 @@ OptionRecommendation(name='search_replace',
|
|||||||
|
|
||||||
pr(0., _('Running transforms on ebook...'))
|
pr(0., _('Running transforms on ebook...'))
|
||||||
|
|
||||||
|
self.oeb.plumber_output_format = self.output_fmt or ''
|
||||||
|
|
||||||
from calibre.ebooks.oeb.transforms.guide import Clean
|
from calibre.ebooks.oeb.transforms.guide import Clean
|
||||||
Clean()(self.oeb, self.opts)
|
Clean()(self.oeb, self.opts)
|
||||||
pr(0.1)
|
pr(0.1)
|
||||||
@ -1120,7 +1122,7 @@ OptionRecommendation(name='search_replace',
|
|||||||
self.log.info('Creating %s...'%self.output_plugin.name)
|
self.log.info('Creating %s...'%self.output_plugin.name)
|
||||||
our = CompositeProgressReporter(0.67, 1., self.ui_reporter)
|
our = CompositeProgressReporter(0.67, 1., self.ui_reporter)
|
||||||
self.output_plugin.report_progress = our
|
self.output_plugin.report_progress = our
|
||||||
our(0., _('Creating')+' %s'%self.output_plugin.name)
|
our(0., _('Running %s plugin')%self.output_plugin.name)
|
||||||
with self.output_plugin:
|
with self.output_plugin:
|
||||||
self.output_plugin.convert(self.oeb, self.output, self.input_plugin,
|
self.output_plugin.convert(self.oeb, self.output, self.input_plugin,
|
||||||
self.opts, self.log)
|
self.opts, self.log)
|
||||||
|
@ -39,6 +39,7 @@ class MathJax
|
|||||||
showMathMenu: false,
|
showMathMenu: false,
|
||||||
extensions: ["tex2jax.js", "asciimath2jax.js", "mml2jax.js"],
|
extensions: ["tex2jax.js", "asciimath2jax.js", "mml2jax.js"],
|
||||||
jax: ["input/TeX","input/MathML","input/AsciiMath","output/SVG"],
|
jax: ["input/TeX","input/MathML","input/AsciiMath","output/SVG"],
|
||||||
|
SVG : { linebreaks : { automatic : true } },
|
||||||
TeX: {
|
TeX: {
|
||||||
extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]
|
extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]
|
||||||
}
|
}
|
||||||
|
@ -267,34 +267,43 @@ class Stylizer(object):
|
|||||||
rules.sort()
|
rules.sort()
|
||||||
self.rules = rules
|
self.rules = rules
|
||||||
self._styles = {}
|
self._styles = {}
|
||||||
|
pseudo_pat = re.compile(ur':(first-letter|first-line|link|hover|visited|active|focus)', re.I)
|
||||||
for _, _, cssdict, text, _ in rules:
|
for _, _, cssdict, text, _ in rules:
|
||||||
fl = ':first-letter' in text
|
fl = pseudo_pat.search(text)
|
||||||
if fl:
|
if fl is not None:
|
||||||
text = text.replace(':first-letter', '')
|
text = text.replace(fl.group(), '')
|
||||||
selector = get_css_selector(text)
|
selector = get_css_selector(text)
|
||||||
matches = selector(tree, self.logger)
|
matches = selector(tree, self.logger)
|
||||||
if fl:
|
if fl is not None:
|
||||||
from lxml.builder import ElementMaker
|
fl = fl.group(1)
|
||||||
E = ElementMaker(namespace=XHTML_NS)
|
if fl == 'first-letter' and getattr(self.oeb,
|
||||||
for elem in matches:
|
'plumber_output_format', '').lower() == u'mobi':
|
||||||
for x in elem.iter():
|
# Fake first-letter
|
||||||
if x.text:
|
from lxml.builder import ElementMaker
|
||||||
punctuation_chars = []
|
E = ElementMaker(namespace=XHTML_NS)
|
||||||
text = unicode(x.text)
|
for elem in matches:
|
||||||
while text:
|
for x in elem.iter():
|
||||||
if not unicodedata.category(text[0]).startswith('P'):
|
if x.text:
|
||||||
break
|
punctuation_chars = []
|
||||||
punctuation_chars.append(text[0])
|
text = unicode(x.text)
|
||||||
text = text[1:]
|
while text:
|
||||||
|
category = unicodedata.category(text[0])
|
||||||
|
if category[0] not in {'P', 'Z'}:
|
||||||
|
break
|
||||||
|
punctuation_chars.append(text[0])
|
||||||
|
text = text[1:]
|
||||||
|
|
||||||
special_text = u''.join(punctuation_chars) + \
|
special_text = u''.join(punctuation_chars) + \
|
||||||
(text[0] if text else u'')
|
(text[0] if text else u'')
|
||||||
span = E.span(special_text)
|
span = E.span(special_text)
|
||||||
span.tail = text[1:]
|
span.tail = text[1:]
|
||||||
x.text = None
|
x.text = None
|
||||||
x.insert(0, span)
|
x.insert(0, span)
|
||||||
self.style(span)._update_cssdict(cssdict)
|
self.style(span)._update_cssdict(cssdict)
|
||||||
break
|
break
|
||||||
|
else: # Element pseudo-class
|
||||||
|
for elem in matches:
|
||||||
|
self.style(elem)._update_pseudo_class(fl, cssdict)
|
||||||
else:
|
else:
|
||||||
for elem in matches:
|
for elem in matches:
|
||||||
self.style(elem)._update_cssdict(cssdict)
|
self.style(elem)._update_cssdict(cssdict)
|
||||||
@ -495,6 +504,7 @@ class Style(object):
|
|||||||
self._height = None
|
self._height = None
|
||||||
self._lineHeight = None
|
self._lineHeight = None
|
||||||
self._bgcolor = None
|
self._bgcolor = None
|
||||||
|
self._pseudo_classes = {}
|
||||||
stylizer._styles[element] = self
|
stylizer._styles[element] = self
|
||||||
|
|
||||||
def set(self, prop, val):
|
def set(self, prop, val):
|
||||||
@ -506,6 +516,11 @@ class Style(object):
|
|||||||
def _update_cssdict(self, cssdict):
|
def _update_cssdict(self, cssdict):
|
||||||
self._style.update(cssdict)
|
self._style.update(cssdict)
|
||||||
|
|
||||||
|
def _update_pseudo_class(self, name, cssdict):
|
||||||
|
orig = self._pseudo_classes.get(name, {})
|
||||||
|
orig.update(cssdict)
|
||||||
|
self._pseudo_classes[name] = orig
|
||||||
|
|
||||||
def _apply_style_attr(self, url_replacer=None):
|
def _apply_style_attr(self, url_replacer=None):
|
||||||
attrib = self._element.attrib
|
attrib = self._element.attrib
|
||||||
if 'style' not in attrib:
|
if 'style' not in attrib:
|
||||||
@ -778,3 +793,14 @@ class Style(object):
|
|||||||
|
|
||||||
def cssdict(self):
|
def cssdict(self):
|
||||||
return dict(self._style)
|
return dict(self._style)
|
||||||
|
|
||||||
|
def pseudo_classes(self, filter_css):
|
||||||
|
if filter_css:
|
||||||
|
css = copy.deepcopy(self._pseudo_classes)
|
||||||
|
for psel, cssdict in css.iteritems():
|
||||||
|
for k in filter_css:
|
||||||
|
cssdict.pop(k, None)
|
||||||
|
else:
|
||||||
|
css = self._pseudo_classes
|
||||||
|
return {k:v for k, v in css.iteritems() if v}
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ class CSSFlattener(object):
|
|||||||
value = 0.0
|
value = 0.0
|
||||||
cssdict[property] = "%0.5fem" % (value / fsize)
|
cssdict[property] = "%0.5fem" % (value / fsize)
|
||||||
|
|
||||||
def flatten_node(self, node, stylizer, names, styles, psize, item_id):
|
def flatten_node(self, node, stylizer, names, styles, pseudo_styles, psize, item_id):
|
||||||
if not isinstance(node.tag, basestring) \
|
if not isinstance(node.tag, basestring) \
|
||||||
or namespace(node.tag) != XHTML_NS:
|
or namespace(node.tag) != XHTML_NS:
|
||||||
return
|
return
|
||||||
@ -357,25 +357,51 @@ class CSSFlattener(object):
|
|||||||
cssdict.get('text-align', None) not in ('center', 'right')):
|
cssdict.get('text-align', None) not in ('center', 'right')):
|
||||||
cssdict['text-indent'] = "%1.1fem" % indent_size
|
cssdict['text-indent'] = "%1.1fem" % indent_size
|
||||||
|
|
||||||
if cssdict:
|
pseudo_classes = style.pseudo_classes(self.filter_css)
|
||||||
items = cssdict.items()
|
if cssdict or pseudo_classes:
|
||||||
items.sort()
|
keep_classes = set()
|
||||||
css = u';\n'.join(u'%s: %s' % (key, val) for key, val in items)
|
|
||||||
classes = node.get('class', '').strip() or 'calibre'
|
if cssdict:
|
||||||
klass = STRIPNUM.sub('', classes.split()[0].replace('_', ''))
|
items = cssdict.items()
|
||||||
if css in styles:
|
items.sort()
|
||||||
match = styles[css]
|
css = u';\n'.join(u'%s: %s' % (key, val) for key, val in items)
|
||||||
else:
|
classes = node.get('class', '').strip() or 'calibre'
|
||||||
match = klass + str(names[klass] or '')
|
klass = STRIPNUM.sub('', classes.split()[0].replace('_', ''))
|
||||||
styles[css] = match
|
if css in styles:
|
||||||
names[klass] += 1
|
match = styles[css]
|
||||||
node.attrib['class'] = match
|
else:
|
||||||
|
match = klass + str(names[klass] or '')
|
||||||
|
styles[css] = match
|
||||||
|
names[klass] += 1
|
||||||
|
node.attrib['class'] = match
|
||||||
|
keep_classes.add(match)
|
||||||
|
|
||||||
|
for psel, cssdict in pseudo_classes.iteritems():
|
||||||
|
items = sorted(cssdict.iteritems())
|
||||||
|
css = u';\n'.join(u'%s: %s' % (key, val) for key, val in items)
|
||||||
|
pstyles = pseudo_styles[psel]
|
||||||
|
if css in pstyles:
|
||||||
|
match = pstyles[css]
|
||||||
|
else:
|
||||||
|
# We have to use a different class for each psel as
|
||||||
|
# otherwise you can have incorrect styles for a situation
|
||||||
|
# like: a:hover { color: red } a:link { color: blue } a.x:hover { color: green }
|
||||||
|
# If the pcalibre class for a:hover and a:link is the same,
|
||||||
|
# then the class attribute for a.x tags will contain both
|
||||||
|
# that class and the class for a.x:hover, which is wrong.
|
||||||
|
klass = 'pcalibre'
|
||||||
|
match = klass + str(names[klass] or '')
|
||||||
|
pstyles[css] = match
|
||||||
|
names[klass] += 1
|
||||||
|
keep_classes.add(match)
|
||||||
|
node.attrib['class'] = ' '.join(keep_classes)
|
||||||
|
|
||||||
elif 'class' in node.attrib:
|
elif 'class' in node.attrib:
|
||||||
del node.attrib['class']
|
del node.attrib['class']
|
||||||
if 'style' in node.attrib:
|
if 'style' in node.attrib:
|
||||||
del node.attrib['style']
|
del node.attrib['style']
|
||||||
for child in node:
|
for child in node:
|
||||||
self.flatten_node(child, stylizer, names, styles, psize, item_id)
|
self.flatten_node(child, stylizer, names, styles, pseudo_styles, psize, item_id)
|
||||||
|
|
||||||
def flatten_head(self, item, href, global_href):
|
def flatten_head(self, item, href, global_href):
|
||||||
html = item.data
|
html = item.data
|
||||||
@ -446,7 +472,7 @@ class CSSFlattener(object):
|
|||||||
|
|
||||||
def flatten_spine(self):
|
def flatten_spine(self):
|
||||||
names = defaultdict(int)
|
names = defaultdict(int)
|
||||||
styles = {}
|
styles, pseudo_styles = {}, defaultdict(dict)
|
||||||
for item in self.oeb.spine:
|
for item in self.oeb.spine:
|
||||||
html = item.data
|
html = item.data
|
||||||
stylizer = self.stylizers[item]
|
stylizer = self.stylizers[item]
|
||||||
@ -454,10 +480,20 @@ class CSSFlattener(object):
|
|||||||
self.specializer(item, stylizer)
|
self.specializer(item, stylizer)
|
||||||
body = html.find(XHTML('body'))
|
body = html.find(XHTML('body'))
|
||||||
fsize = self.context.dest.fbase
|
fsize = self.context.dest.fbase
|
||||||
self.flatten_node(body, stylizer, names, styles, fsize, item.id)
|
self.flatten_node(body, stylizer, names, styles, pseudo_styles, fsize, item.id)
|
||||||
items = [(key, val) for (val, key) in styles.items()]
|
items = [(key, val) for (val, key) in styles.items()]
|
||||||
items.sort()
|
items.sort()
|
||||||
|
# :hover must come after link and :active must come after :hover
|
||||||
|
psels = sorted(pseudo_styles.iterkeys(), key=lambda x :
|
||||||
|
{'hover':1, 'active':2}.get(x, 0))
|
||||||
|
for psel in psels:
|
||||||
|
styles = pseudo_styles[psel]
|
||||||
|
if not styles: continue
|
||||||
|
x = sorted(((k+':'+psel, v) for v, k in styles.iteritems()))
|
||||||
|
items.extend(x)
|
||||||
|
|
||||||
css = ''.join(".%s {\n%s;\n}\n\n" % (key, val) for key, val in items)
|
css = ''.join(".%s {\n%s;\n}\n\n" % (key, val) for key, val in items)
|
||||||
|
|
||||||
href = self.replace_css(css)
|
href = self.replace_css(css)
|
||||||
global_css = self.collect_global_css()
|
global_css = self.collect_global_css()
|
||||||
for item in self.oeb.spine:
|
for item in self.oeb.spine:
|
||||||
|
@ -11,6 +11,7 @@ from PyQt4.Qt import QToolButton, QMenu, pyqtSignal, QIcon, QTimer
|
|||||||
|
|
||||||
from calibre.gui2.actions import InterfaceAction
|
from calibre.gui2.actions import InterfaceAction
|
||||||
from calibre.utils.smtp import config as email_config
|
from calibre.utils.smtp import config as email_config
|
||||||
|
from calibre.utils.config import tweaks
|
||||||
from calibre.constants import iswindows, isosx
|
from calibre.constants import iswindows, isosx
|
||||||
from calibre.customize.ui import is_disabled
|
from calibre.customize.ui import is_disabled
|
||||||
from calibre.devices.bambook.driver import BAMBOOK
|
from calibre.devices.bambook.driver import BAMBOOK
|
||||||
@ -84,10 +85,12 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
action=self.toggle_server_action, group=gr)
|
action=self.toggle_server_action, group=gr)
|
||||||
|
|
||||||
def server_state_changed(self, running):
|
def server_state_changed(self, running):
|
||||||
from calibre.utils.mdns import get_external_ip
|
from calibre.utils.mdns import get_external_ip, verify_ipV4_address
|
||||||
text = _('Start Content Server')
|
text = _('Start Content Server')
|
||||||
if running:
|
if running:
|
||||||
text = _('Stop Content Server') + ' [%s]'%get_external_ip()
|
listen_on = (verify_ipV4_address(tweaks['server_listen_on']) or
|
||||||
|
get_external_ip())
|
||||||
|
text = _('Stop Content Server') + ' [%s]'%listen_on
|
||||||
self.toggle_server_action.setText(text)
|
self.toggle_server_action.setText(text)
|
||||||
|
|
||||||
def hide_smartdevice_menus(self):
|
def hide_smartdevice_menus(self):
|
||||||
|
@ -80,7 +80,7 @@ class EditMetadataAction(InterfaceAction):
|
|||||||
Dispatcher(self.metadata_downloaded),
|
Dispatcher(self.metadata_downloaded),
|
||||||
ensure_fields=ensure_fields)
|
ensure_fields=ensure_fields)
|
||||||
|
|
||||||
def cleanup_bulk_download(self, tdir):
|
def cleanup_bulk_download(self, tdir, *args):
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(tdir, ignore_errors=True)
|
shutil.rmtree(tdir, ignore_errors=True)
|
||||||
except:
|
except:
|
||||||
@ -108,22 +108,26 @@ class EditMetadataAction(InterfaceAction):
|
|||||||
'Proceed with updating the metadata in your library?')%len(id_map)
|
'Proceed with updating the metadata in your library?')%len(id_map)
|
||||||
|
|
||||||
show_copy_button = False
|
show_copy_button = False
|
||||||
|
checkbox_msg = None
|
||||||
if failed_ids or failed_covers:
|
if failed_ids or failed_covers:
|
||||||
show_copy_button = True
|
show_copy_button = True
|
||||||
num = len(failed_ids.union(failed_covers))
|
num = len(failed_ids.union(failed_covers))
|
||||||
msg += '<p>'+_('Could not download metadata and/or covers for %d of the books. Click'
|
msg += '<p>'+_('Could not download metadata and/or covers for %d of the books. Click'
|
||||||
' "Show details" to see which books.')%num
|
' "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)
|
payload = (id_map, tdir, log_file, lm_map,
|
||||||
self.gui.proceed_question(self.apply_downloaded_metadata,
|
failed_ids.union(failed_covers))
|
||||||
payload, log_file,
|
self.gui.proceed_question(self.apply_downloaded_metadata, payload,
|
||||||
_('Download log'), _('Download complete'), msg,
|
log_file, _('Download log'), _('Download complete'), msg,
|
||||||
det_msg=det_msg, show_copy_button=show_copy_button,
|
det_msg=det_msg, show_copy_button=show_copy_button,
|
||||||
cancel_callback=lambda x:self.cleanup_bulk_download(tdir),
|
cancel_callback=partial(self.cleanup_bulk_download, tdir),
|
||||||
log_is_file=True)
|
log_is_file=True, checkbox_msg=checkbox_msg,
|
||||||
|
checkbox_checked=False)
|
||||||
|
|
||||||
def apply_downloaded_metadata(self, payload):
|
def apply_downloaded_metadata(self, payload, *args):
|
||||||
good_ids, tdir, log_file, lm_map = payload
|
good_ids, tdir, log_file, lm_map, failed_ids = payload
|
||||||
if not good_ids:
|
if not good_ids:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -162,8 +166,18 @@ class EditMetadataAction(InterfaceAction):
|
|||||||
cov = None
|
cov = None
|
||||||
id_map[bid] = (opf, cov)
|
id_map[bid] = (opf, cov)
|
||||||
|
|
||||||
self.apply_metadata_changes(id_map, callback=lambda x:
|
restrict_to_failed = bool(args and args[0])
|
||||||
self.cleanup_bulk_download(tdir))
|
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)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -152,8 +152,16 @@ def render_data(mi, use_roman_numbers=True, all_fields=False):
|
|||||||
scheme = u'devpath' if isdevice else u'path'
|
scheme = u'devpath' if isdevice else u'path'
|
||||||
url = prepare_string_for_xml(path if isdevice else
|
url = prepare_string_for_xml(path if isdevice else
|
||||||
unicode(mi.id), True)
|
unicode(mi.id), True)
|
||||||
link = u'<a href="%s:%s" title="%s">%s</a>' % (scheme, url,
|
pathstr = _('Click to open')
|
||||||
prepare_string_for_xml(path, True), _('Click to open'))
|
extra = ''
|
||||||
|
if isdevice:
|
||||||
|
durl = url
|
||||||
|
if durl.startswith('mtp:::'):
|
||||||
|
durl = ':::'.join( (durl.split(':::'))[2:] )
|
||||||
|
extra = '<br><span style="font-size:smaller">%s</span>'%(
|
||||||
|
prepare_string_for_xml(durl))
|
||||||
|
link = u'<a href="%s:%s" title="%s">%s</a>%s' % (scheme, url,
|
||||||
|
prepare_string_for_xml(path, True), pathstr, extra)
|
||||||
ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name, link)))
|
ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name, link)))
|
||||||
elif field == 'formats':
|
elif field == 'formats':
|
||||||
if isdevice: continue
|
if isdevice: continue
|
||||||
|
@ -612,7 +612,7 @@ class GenericRulesTable(QTableWidget):
|
|||||||
first_rule_name = unicode(self.cellWidget(first-1,self.COLUMNS['NAME']['ordinal']).text()).strip()
|
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)
|
message = _("Are you sure you want to delete '%s'?") % (first_rule_name)
|
||||||
if len(rows) > 1:
|
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):
|
if not question_dialog(self, _('Delete Rule'), message, show_copy_button=False):
|
||||||
return
|
return
|
||||||
first_sel_row = self.currentRow()
|
first_sel_row = self.currentRow()
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>588</width>
|
<width>588</width>
|
||||||
<height>342</height>
|
<height>416</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -91,23 +91,33 @@
|
|||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QComboBox" name="opt_mobi_file_type"/>
|
<widget class="QComboBox" name="opt_mobi_file_type"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Personal Doc tag:</string>
|
<string>Personal Doc tag:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QLineEdit" name="opt_personal_doc"/>
|
<widget class="QLineEdit" name="opt_personal_doc"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="2">
|
<item row="4" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="opt_share_not_sync">
|
<widget class="QCheckBox" name="opt_share_not_sync">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable sharing of book content via Facebook, etc. WARNING: Disables last read syncing</string>
|
<string>Enable sharing of book content via Facebook, etc. WARNING: Disables last read syncing</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string><b>WARNING:</b> Various Kindle devices have trouble displaying the new or both MOBI filetypes. If you wish to use the new format on your device, convert to AZW3 instead of MOBI.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -24,7 +24,8 @@ from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic,
|
|||||||
from calibre.ebooks.metadata import authors_to_string
|
from calibre.ebooks.metadata import authors_to_string
|
||||||
from calibre import preferred_encoding, prints, force_unicode, as_unicode
|
from calibre import preferred_encoding, prints, force_unicode, as_unicode
|
||||||
from calibre.utils.filenames import ascii_filename
|
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.apple.driver import ITUNES_ASYNC
|
||||||
from calibre.devices.folder_device.driver import FOLDER_DEVICE
|
from calibre.devices.folder_device.driver import FOLDER_DEVICE
|
||||||
from calibre.devices.bambook.driver import BAMBOOK, BAMBOOKWifi
|
from calibre.devices.bambook.driver import BAMBOOK, BAMBOOKWifi
|
||||||
@ -252,6 +253,9 @@ class DeviceManager(Thread): # {{{
|
|||||||
if cd is not None:
|
if cd is not None:
|
||||||
try:
|
try:
|
||||||
dev.open(cd, self.current_library_uuid)
|
dev.open(cd, self.current_library_uuid)
|
||||||
|
except BlacklistedDevice as e:
|
||||||
|
prints('Ignoring blacklisted device: %s'%
|
||||||
|
as_unicode(e))
|
||||||
except:
|
except:
|
||||||
prints('Error while trying to open %s (Driver: %s)'%
|
prints('Error while trying to open %s (Driver: %s)'%
|
||||||
(cd, dev))
|
(cd, dev))
|
||||||
|
@ -11,11 +11,13 @@ import weakref
|
|||||||
|
|
||||||
from PyQt4.Qt import (QWidget, QListWidgetItem, Qt, QToolButton, QLabel,
|
from PyQt4.Qt import (QWidget, QListWidgetItem, Qt, QToolButton, QLabel,
|
||||||
QTabWidget, QGridLayout, QListWidget, QIcon, QLineEdit, QVBoxLayout,
|
QTabWidget, QGridLayout, QListWidget, QIcon, QLineEdit, QVBoxLayout,
|
||||||
QPushButton)
|
QPushButton, QGroupBox, QScrollArea, QHBoxLayout, QComboBox,
|
||||||
|
pyqtSignal, QSizePolicy, QDialog, QDialogButtonBox)
|
||||||
|
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog
|
||||||
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||||
|
from calibre.utils.date import parse_date
|
||||||
|
|
||||||
class FormatsConfig(QWidget): # {{{
|
class FormatsConfig(QWidget): # {{{
|
||||||
|
|
||||||
@ -85,7 +87,7 @@ class TemplateConfig(QWidget): # {{{
|
|||||||
m.setBuddy(t)
|
m.setBuddy(t)
|
||||||
l.addWidget(m, 0, 0, 1, 2)
|
l.addWidget(m, 0, 0, 1, 2)
|
||||||
l.addWidget(t, 1, 0, 1, 1)
|
l.addWidget(t, 1, 0, 1, 1)
|
||||||
b = self.b = QPushButton(_('Template editor'))
|
b = self.b = QPushButton(_('&Template editor'))
|
||||||
l.addWidget(b, 1, 1, 1, 1)
|
l.addWidget(b, 1, 1, 1, 1)
|
||||||
b.clicked.connect(self.edit_template)
|
b.clicked.connect(self.edit_template)
|
||||||
|
|
||||||
@ -136,6 +138,152 @@ 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
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Rules {{{
|
||||||
|
|
||||||
|
class Rule(QWidget):
|
||||||
|
|
||||||
|
remove = pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self, rule=None):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
|
||||||
|
self.l = l = QHBoxLayout()
|
||||||
|
self.setLayout(l)
|
||||||
|
|
||||||
|
self.l1 = l1 = QLabel(_('Send the '))
|
||||||
|
l.addWidget(l1)
|
||||||
|
self.fmt = f = QComboBox(self)
|
||||||
|
l.addWidget(f)
|
||||||
|
self.l2 = l2 = QLabel(_(' format to the folder: '))
|
||||||
|
l.addWidget(l2)
|
||||||
|
self.folder = f = QLineEdit(self)
|
||||||
|
f.setPlaceholderText(_('Folder on the device'))
|
||||||
|
l.addWidget(f)
|
||||||
|
self.rb = rb = QPushButton(QIcon(I('list_remove.png')),
|
||||||
|
_('&Remove rule'), self)
|
||||||
|
l.addWidget(rb)
|
||||||
|
rb.clicked.connect(self.removed)
|
||||||
|
|
||||||
|
for fmt in sorted(BOOK_EXTENSIONS):
|
||||||
|
self.fmt.addItem(fmt.upper(), fmt.lower())
|
||||||
|
|
||||||
|
self.fmt.setCurrentIndex(0)
|
||||||
|
|
||||||
|
if rule is not None:
|
||||||
|
fmt, folder = rule
|
||||||
|
idx = self.fmt.findText(fmt.upper())
|
||||||
|
if idx > -1:
|
||||||
|
self.fmt.setCurrentIndex(idx)
|
||||||
|
self.folder.setText(folder)
|
||||||
|
|
||||||
|
self.ignore = False
|
||||||
|
|
||||||
|
def removed(self):
|
||||||
|
self.remove.emit(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rule(self):
|
||||||
|
folder = unicode(self.folder.text()).strip()
|
||||||
|
if folder:
|
||||||
|
return (
|
||||||
|
unicode(self.fmt.itemData(self.fmt.currentIndex()).toString()),
|
||||||
|
folder
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
class FormatRules(QGroupBox):
|
||||||
|
|
||||||
|
def __init__(self, rules):
|
||||||
|
QGroupBox.__init__(self, _('Format specific sending'))
|
||||||
|
self.l = l = QVBoxLayout()
|
||||||
|
self.setLayout(l)
|
||||||
|
self.la = la = QLabel('<p>'+_(
|
||||||
|
'''You can create rules that control where ebooks of a specific
|
||||||
|
format are sent to on the device. These will take precedence over
|
||||||
|
the folders specified above.'''))
|
||||||
|
la.setWordWrap(True)
|
||||||
|
l.addWidget(la)
|
||||||
|
self.sa = sa = QScrollArea(self)
|
||||||
|
sa.setWidgetResizable(True)
|
||||||
|
self.w = w = QWidget(self)
|
||||||
|
w.l = QVBoxLayout()
|
||||||
|
w.setLayout(w.l)
|
||||||
|
sa.setWidget(w)
|
||||||
|
l.addWidget(sa)
|
||||||
|
self.widgets = []
|
||||||
|
for rule in rules:
|
||||||
|
r = Rule(rule)
|
||||||
|
self.widgets.append(r)
|
||||||
|
w.l.addWidget(r)
|
||||||
|
r.remove.connect(self.remove_rule)
|
||||||
|
|
||||||
|
if not self.widgets:
|
||||||
|
self.add_rule()
|
||||||
|
|
||||||
|
self.b = b = QPushButton(QIcon(I('plus.png')), _('Add a &new rule'))
|
||||||
|
l.addWidget(b)
|
||||||
|
b.clicked.connect(self.add_rule)
|
||||||
|
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored)
|
||||||
|
|
||||||
|
def add_rule(self):
|
||||||
|
r = Rule()
|
||||||
|
self.widgets.append(r)
|
||||||
|
self.w.l.addWidget(r)
|
||||||
|
r.remove.connect(self.remove_rule)
|
||||||
|
self.sa.verticalScrollBar().setValue(self.sa.verticalScrollBar().maximum())
|
||||||
|
|
||||||
|
def remove_rule(self, rule):
|
||||||
|
rule.setVisible(False)
|
||||||
|
rule.ignore = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rules(self):
|
||||||
|
for w in self.widgets:
|
||||||
|
if not w.ignore:
|
||||||
|
r = w.rule
|
||||||
|
if r is not None:
|
||||||
|
yield r
|
||||||
|
# }}}
|
||||||
|
|
||||||
class MTPConfig(QTabWidget):
|
class MTPConfig(QTabWidget):
|
||||||
|
|
||||||
def __init__(self, device, parent=None):
|
def __init__(self, device, parent=None):
|
||||||
@ -145,8 +293,8 @@ class MTPConfig(QTabWidget):
|
|||||||
cd = msg = None
|
cd = msg = None
|
||||||
if device.current_friendly_name is not None:
|
if device.current_friendly_name is not None:
|
||||||
if device.current_serial_num is None:
|
if device.current_serial_num is None:
|
||||||
msg = '<p>' + _('The <b>%s</b> device has no serial number, '
|
msg = '<p>' + (_('The <b>%s</b> device has no serial number, '
|
||||||
'it cannot be configured'%device.current_friendly_name)
|
'it cannot be configured')%device.current_friendly_name)
|
||||||
else:
|
else:
|
||||||
cd = 'device-'+device.current_serial_num
|
cd = 'device-'+device.current_serial_num
|
||||||
else:
|
else:
|
||||||
@ -162,6 +310,8 @@ class MTPConfig(QTabWidget):
|
|||||||
l = QLabel(msg)
|
l = QLabel(msg)
|
||||||
l.setWordWrap(True)
|
l.setWordWrap(True)
|
||||||
l.setStyleSheet('QLabel { margin-left: 2em }')
|
l.setStyleSheet('QLabel { margin-left: 2em }')
|
||||||
|
l.setMinimumWidth(500)
|
||||||
|
l.setMinimumHeight(400)
|
||||||
self.insertTab(0, l, _('Cannot configure'))
|
self.insertTab(0, l, _('Cannot configure'))
|
||||||
else:
|
else:
|
||||||
self.base = QWidget(self)
|
self.base = QWidget(self)
|
||||||
@ -169,20 +319,42 @@ class MTPConfig(QTabWidget):
|
|||||||
l = self.base.l = QGridLayout(self.base)
|
l = self.base.l = QGridLayout(self.base)
|
||||||
self.base.setLayout(l)
|
self.base.setLayout(l)
|
||||||
|
|
||||||
|
self.rules = r = FormatRules(self.get_pref('rules'))
|
||||||
self.formats = FormatsConfig(set(BOOK_EXTENSIONS),
|
self.formats = FormatsConfig(set(BOOK_EXTENSIONS),
|
||||||
self.get_pref('format_map'))
|
self.get_pref('format_map'))
|
||||||
self.send_to = SendToConfig(self.get_pref('send_to'))
|
self.send_to = SendToConfig(self.get_pref('send_to'))
|
||||||
self.template = TemplateConfig(self.get_pref('send_template'))
|
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)
|
la.setWordWrap(True)
|
||||||
l.addWidget(la, 0, 0, 1, 1)
|
self.base.b = b = QPushButton(QIcon(I('list_remove.png')),
|
||||||
l.addWidget(self.formats, 1, 0, 3, 1)
|
_('&Ignore the %s in calibre')%device.current_friendly_name,
|
||||||
l.addWidget(self.send_to, 1, 1, 1, 1)
|
self.base)
|
||||||
l.addWidget(self.template, 2, 1, 1, 1)
|
b.clicked.connect(self.ignore_device)
|
||||||
l.setRowStretch(2, 10)
|
|
||||||
|
l.addWidget(b, 0, 0, 1, 2)
|
||||||
|
l.addWidget(la, 1, 0, 1, 1)
|
||||||
|
l.addWidget(self.formats, 2, 0, 3, 1)
|
||||||
|
l.addWidget(self.send_to, 2, 1, 1, 1)
|
||||||
|
l.addWidget(self.template, 3, 1, 1, 1)
|
||||||
|
l.setRowStretch(4, 10)
|
||||||
|
l.addWidget(r, 5, 0, 1, 2)
|
||||||
|
l.setRowStretch(5, 100)
|
||||||
|
|
||||||
|
self.igntab = IgnoredDevices(self.device.prefs['history'],
|
||||||
|
self.device.prefs['blacklist'])
|
||||||
|
self.addTab(self.igntab, _('Ignored devices'))
|
||||||
|
|
||||||
self.setCurrentIndex(0)
|
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):
|
def get_pref(self, key):
|
||||||
p = self.device.prefs.get(self.current_device_key, {})
|
p = self.device.prefs.get(self.current_device_key, {})
|
||||||
if not p:
|
if not p:
|
||||||
@ -194,31 +366,40 @@ class MTPConfig(QTabWidget):
|
|||||||
return self._device()
|
return self._device()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if not self.formats.validate():
|
if hasattr(self, 'formats'):
|
||||||
return False
|
if not self.formats.validate():
|
||||||
if not self.template.validate():
|
return False
|
||||||
return False
|
if not self.template.validate():
|
||||||
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
p = self.device.prefs.get(self.current_device_key, {})
|
p = self.device.prefs.get(self.current_device_key, {})
|
||||||
|
|
||||||
p.pop('format_map', None)
|
if hasattr(self, 'formats'):
|
||||||
f = self.formats.format_map
|
p.pop('format_map', None)
|
||||||
if f and f != self.device.prefs['format_map']:
|
f = self.formats.format_map
|
||||||
p['format_map'] = f
|
if f and f != self.device.prefs['format_map']:
|
||||||
|
p['format_map'] = f
|
||||||
|
|
||||||
p.pop('send_template', None)
|
p.pop('send_template', None)
|
||||||
t = self.template.template
|
t = self.template.template
|
||||||
if t and t != self.device.prefs['send_template']:
|
if t and t != self.device.prefs['send_template']:
|
||||||
p['send_template'] = t
|
p['send_template'] = t
|
||||||
|
|
||||||
p.pop('send_to', None)
|
p.pop('send_to', None)
|
||||||
s = self.send_to.value
|
s = self.send_to.value
|
||||||
if s and s != self.device.prefs['send_to']:
|
if s and s != self.device.prefs['send_to']:
|
||||||
p['send_to'] = s
|
p['send_to'] = s
|
||||||
|
|
||||||
self.device.prefs[self.current_device_key] = p
|
p.pop('rules', None)
|
||||||
|
r = list(self.rules.rules)
|
||||||
|
if r and r != self.device.prefs['rules']:
|
||||||
|
p['rules'] = r
|
||||||
|
|
||||||
|
self.device.prefs[self.current_device_key] = p
|
||||||
|
|
||||||
|
self.device.prefs['blacklist'] = self.igntab.blacklist
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from calibre.gui2 import Application
|
from calibre.gui2 import Application
|
||||||
@ -232,8 +413,16 @@ if __name__ == '__main__':
|
|||||||
cd = dev.detect_managed_devices(s.devices)
|
cd = dev.detect_managed_devices(s.devices)
|
||||||
dev.open(cd, 'test')
|
dev.open(cd, 'test')
|
||||||
cw = dev.config_widget()
|
cw = dev.config_widget()
|
||||||
cw.show()
|
d = QDialog()
|
||||||
app.exec_()
|
d.l = QVBoxLayout()
|
||||||
|
d.setLayout(d.l)
|
||||||
|
d.l.addWidget(cw)
|
||||||
|
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
|
||||||
|
d.l.addWidget(bb)
|
||||||
|
bb.accepted.connect(d.accept)
|
||||||
|
bb.rejected.connect(d.reject)
|
||||||
|
if d.exec_() == d.Accepted:
|
||||||
|
cw.commit()
|
||||||
dev.shutdown()
|
dev.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,18 +11,18 @@ from collections import namedtuple
|
|||||||
|
|
||||||
from PyQt4.Qt import (QDialog, Qt, QLabel, QGridLayout, QPixmap,
|
from PyQt4.Qt import (QDialog, Qt, QLabel, QGridLayout, QPixmap,
|
||||||
QDialogButtonBox, QApplication, QSize, pyqtSignal, QIcon,
|
QDialogButtonBox, QApplication, QSize, pyqtSignal, QIcon,
|
||||||
QPlainTextEdit)
|
QPlainTextEdit, QCheckBox)
|
||||||
|
|
||||||
from calibre.constants import __version__
|
from calibre.constants import __version__
|
||||||
from calibre.gui2.dialogs.message_box import ViewLog
|
from calibre.gui2.dialogs.message_box import ViewLog
|
||||||
|
|
||||||
Question = namedtuple('Question', 'payload callback cancel_callback '
|
Question = namedtuple('Question', 'payload callback cancel_callback '
|
||||||
'title msg html_log log_viewer_title log_is_file det_msg '
|
'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):
|
class ProceedQuestion(QDialog):
|
||||||
|
|
||||||
ask_question = pyqtSignal(object, object)
|
ask_question = pyqtSignal(object, object, object)
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QDialog.__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.setStandardButtons(self.bb.Yes|self.bb.No)
|
||||||
self.bb.button(self.bb.Yes).setDefault(True)
|
self.bb.button(self.bb.Yes).setDefault(True)
|
||||||
|
|
||||||
|
self.checkbox = QCheckBox('', self)
|
||||||
|
|
||||||
l.addWidget(ic, 0, 0, 1, 1)
|
l.addWidget(ic, 0, 0, 1, 1)
|
||||||
l.addWidget(msg, 0, 1, 1, 1)
|
l.addWidget(msg, 0, 1, 1, 1)
|
||||||
l.addWidget(self.det_msg, 1, 0, 1, 2)
|
l.addWidget(self.checkbox, 1, 0, 1, 2)
|
||||||
l.addWidget(self.bb, 2, 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,
|
self.ask_question.connect(self.do_ask_question,
|
||||||
type=Qt.QueuedConnection)
|
type=Qt.QueuedConnection)
|
||||||
@ -82,19 +85,28 @@ class ProceedQuestion(QDialog):
|
|||||||
if self.questions:
|
if self.questions:
|
||||||
payload, callback, cancel_callback = self.questions[0][:3]
|
payload, callback, cancel_callback = self.questions[0][:3]
|
||||||
self.questions = self.questions[1:]
|
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()
|
self.hide()
|
||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
if self.questions:
|
if self.questions:
|
||||||
payload, callback, cancel_callback = self.questions[0][:3]
|
payload, callback, cancel_callback = self.questions[0][:3]
|
||||||
self.questions = self.questions[1:]
|
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()
|
self.hide()
|
||||||
|
|
||||||
def do_ask_question(self, callback, payload):
|
def do_ask_question(self, callback, payload, checkbox_checked):
|
||||||
if callable(callback):
|
if callable(callback):
|
||||||
callback(payload)
|
args = [payload]
|
||||||
|
if checkbox_checked is not None:
|
||||||
|
args.append(checkbox_checked)
|
||||||
|
callback(*args)
|
||||||
self.show_question()
|
self.show_question()
|
||||||
|
|
||||||
def toggle_det_msg(self, *args):
|
def toggle_det_msg(self, *args):
|
||||||
@ -122,6 +134,10 @@ class ProceedQuestion(QDialog):
|
|||||||
self.det_msg.setVisible(False)
|
self.det_msg.setVisible(False)
|
||||||
self.det_msg_toggle.setVisible(bool(question.det_msg))
|
self.det_msg_toggle.setVisible(bool(question.det_msg))
|
||||||
self.det_msg_toggle.setText(self.show_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.do_resize()
|
||||||
self.show()
|
self.show()
|
||||||
self.bb.button(self.bb.Yes).setDefault(True)
|
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,
|
def __call__(self, callback, payload, html_log, log_viewer_title, title,
|
||||||
msg, det_msg='', show_copy_button=False, cancel_callback=None,
|
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
|
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
|
visible at any one time. Other requests are queued and displayed after
|
||||||
the user dismisses the current popup.
|
the user dismisses the current popup.
|
||||||
|
|
||||||
@ -147,11 +163,18 @@ class ProceedQuestion(QDialog):
|
|||||||
:param msg: The msg to display
|
:param msg: The msg to display
|
||||||
:param det_msg: Detailed message
|
:param det_msg: Detailed message
|
||||||
:param log_is_file: If True the html_log parameter is interpreted as
|
: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,
|
question = Question(payload, callback, cancel_callback, title, msg,
|
||||||
html_log, log_viewer_title, log_is_file, det_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.questions.append(question)
|
||||||
self.show_question()
|
self.show_question()
|
||||||
|
|
||||||
@ -169,7 +192,8 @@ def main():
|
|||||||
from calibre.gui2 import Application
|
from calibre.gui2 import Application
|
||||||
app = Application([])
|
app = Application([])
|
||||||
p = ProceedQuestion(None)
|
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_()
|
p.exec_()
|
||||||
app
|
app
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class UpdateNotification(QDialog):
|
|||||||
self.logo.setPixmap(QPixmap(I('lt.png')).scaled(100, 100,
|
self.logo.setPixmap(QPixmap(I('lt.png')).scaled(100, 100,
|
||||||
Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
|
Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
|
||||||
self.label = QLabel(('<p>'+
|
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'
|
'See the <a href="http://calibre-ebook.com/whats-new'
|
||||||
'">new features</a>.'))%dict(
|
'">new features</a>.'))%dict(
|
||||||
app=__appname__, ver=calibre_version))
|
app=__appname__, ver=calibre_version))
|
||||||
|
@ -211,15 +211,15 @@ class CatalogBuilder(object):
|
|||||||
(str): sort key
|
(str): sort key
|
||||||
"""
|
"""
|
||||||
if not book['series']:
|
if not book['series']:
|
||||||
fs = '{:<%d}!{!s}' % longest_author_sort
|
fs = u'{:<%d}!{!s}' % longest_author_sort
|
||||||
key = fs.format(capitalize(book['author_sort']),
|
key = fs.format(capitalize(book['author_sort']),
|
||||||
capitalize(book['title_sort']))
|
capitalize(book['title_sort']))
|
||||||
else:
|
else:
|
||||||
index = book['series_index']
|
index = book['series_index']
|
||||||
integer = int(index)
|
integer = int(index)
|
||||||
fraction = index-integer
|
fraction = index-integer
|
||||||
series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0'))
|
series_index = u'%04d%s' % (integer, str(u'%0.4f' % fraction).lstrip(u'0'))
|
||||||
fs = '{:<%d}~{!s}{!s}' % longest_author_sort
|
fs = u'{:<%d}~{!s}{!s}' % longest_author_sort
|
||||||
key = fs.format(capitalize(book['author_sort']),
|
key = fs.format(capitalize(book['author_sort']),
|
||||||
self.generate_sort_title(book['series']),
|
self.generate_sort_title(book['series']),
|
||||||
series_index)
|
series_index)
|
||||||
@ -2464,7 +2464,9 @@ class CatalogBuilder(object):
|
|||||||
title_str=title_str,
|
title_str=title_str,
|
||||||
xmlns=XHTML_NS,
|
xmlns=XHTML_NS,
|
||||||
)
|
)
|
||||||
|
for k, v in args.iteritems():
|
||||||
|
if isbytestring(v):
|
||||||
|
args[k] = v.decode('utf-8')
|
||||||
generated_html = P('catalog/template.xhtml',
|
generated_html = P('catalog/template.xhtml',
|
||||||
data=True).decode('utf-8').format(**args)
|
data=True).decode('utf-8').format(**args)
|
||||||
generated_html = substitute_entites(generated_html)
|
generated_html = substitute_entites(generated_html)
|
||||||
|
@ -1432,6 +1432,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
pdir = os.path.dirname(dest)
|
pdir = os.path.dirname(dest)
|
||||||
if not os.path.exists(pdir):
|
if not os.path.exists(pdir):
|
||||||
os.makedirs(pdir)
|
os.makedirs(pdir)
|
||||||
|
size = 0
|
||||||
if copy_function is not None:
|
if copy_function is not None:
|
||||||
copy_function(dest)
|
copy_function(dest)
|
||||||
size = os.path.getsize(dest)
|
size = os.path.getsize(dest)
|
||||||
@ -1441,6 +1442,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
with lopen(dest, 'wb') as f:
|
with lopen(dest, 'wb') as f:
|
||||||
shutil.copyfileobj(stream, f)
|
shutil.copyfileobj(stream, f)
|
||||||
size = f.tell()
|
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 (?,?,?,?)',
|
self.conn.execute('INSERT OR REPLACE INTO data (book,format,uncompressed_size,name) VALUES (?,?,?,?)',
|
||||||
(id, format.upper(), size, name))
|
(id, format.upper(), size, name))
|
||||||
self.update_last_modified([id], commit=False)
|
self.update_last_modified([id], commit=False)
|
||||||
|
@ -5,7 +5,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import os
|
import os, socket
|
||||||
import logging
|
import logging
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ from calibre.utils.date import fromtimestamp
|
|||||||
from calibre.library.server import listen_on, log_access_file, log_error_file
|
from calibre.library.server import listen_on, log_access_file, log_error_file
|
||||||
from calibre.library.server.utils import expose, AuthController
|
from calibre.library.server.utils import expose, AuthController
|
||||||
from calibre.utils.mdns import publish as publish_zeroconf, \
|
from calibre.utils.mdns import publish as publish_zeroconf, \
|
||||||
unpublish as unpublish_zeroconf, get_external_ip
|
unpublish as unpublish_zeroconf, get_external_ip, verify_ipV4_address
|
||||||
from calibre.library.server.content import ContentServer
|
from calibre.library.server.content import ContentServer
|
||||||
from calibre.library.server.mobile import MobileServer
|
from calibre.library.server.mobile import MobileServer
|
||||||
from calibre.library.server.xml import XMLServer
|
from calibre.library.server.xml import XMLServer
|
||||||
@ -78,6 +78,7 @@ class BonJour(SimplePlugin): # {{{
|
|||||||
SimplePlugin.__init__(self, engine)
|
SimplePlugin.__init__(self, engine)
|
||||||
self.port = port
|
self.port = port
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
|
self.ip_address = '0.0.0.0'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mdns_services(self):
|
def mdns_services(self):
|
||||||
@ -90,9 +91,10 @@ class BonJour(SimplePlugin): # {{{
|
|||||||
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
zeroconf_ip_address = verify_ipV4_address(self.ip_address)
|
||||||
try:
|
try:
|
||||||
for s in self.mdns_services:
|
for s in self.mdns_services:
|
||||||
publish_zeroconf(*s)
|
publish_zeroconf(*s, use_ip_address=zeroconf_ip_address)
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
cherrypy.log.error('Failed to start BonJour:')
|
cherrypy.log.error('Failed to start BonJour:')
|
||||||
@ -140,6 +142,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
|
|||||||
if not opts.url_prefix:
|
if not opts.url_prefix:
|
||||||
opts.url_prefix = ''
|
opts.url_prefix = ''
|
||||||
|
|
||||||
|
cherrypy.engine.bonjour.ip_address = listen_on
|
||||||
cherrypy.engine.bonjour.port = opts.port
|
cherrypy.engine.bonjour.port = opts.port
|
||||||
cherrypy.engine.bonjour.prefix = opts.url_prefix
|
cherrypy.engine.bonjour.prefix = opts.url_prefix
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: calibre 0.8.68\n"
|
"Project-Id-Version: calibre 0.8.68\n"
|
||||||
"POT-Creation-Date: 2012-09-07 08:49+IST\n"
|
"POT-Creation-Date: 2012-09-08 17:09+IST\n"
|
||||||
"PO-Revision-Date: 2012-09-07 08:49+IST\n"
|
"PO-Revision-Date: 2012-09-08 17:09+IST\n"
|
||||||
"Last-Translator: Automatically generated\n"
|
"Last-Translator: Automatically generated\n"
|
||||||
"Language-Team: LANGUAGE\n"
|
"Language-Team: LANGUAGE\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -131,8 +131,8 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ztxt/writer.py:27
|
#: /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:108
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:109
|
#: /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:439
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:434
|
#: /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/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:397
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:400
|
#: /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:124
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:143
|
#: /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/convert/metadata.py:145
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1366
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1367
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1369
|
#: /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:55
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:60
|
#: /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
|
#: /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:585
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:593
|
#: /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:604
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2189
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2192
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2343
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2346
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2768
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:2771
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3415
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3418
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3417
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3420
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3554
|
#: /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:250
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/content.py:251
|
#: /home/kovid/work/calibre/src/calibre/library/server/content.py:251
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:247
|
#: /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/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:370
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:383
|
#: /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
|
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:187
|
||||||
msgid "News"
|
msgid "News"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2770
|
#: /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:3231
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3246
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3249
|
||||||
msgid "Catalog"
|
msgid "Catalog"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3352,8 +3352,8 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:67
|
#: /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/create_custom_column.py:70
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:163
|
#: /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:292
|
||||||
#: /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:2142
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:140
|
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:140
|
||||||
msgid "Series"
|
msgid "Series"
|
||||||
msgid_plural "Series"
|
msgid_plural "Series"
|
||||||
@ -3880,7 +3880,17 @@ msgstr ""
|
|||||||
msgid "Show this confirmation again"
|
msgid "Show this confirmation again"
|
||||||
msgstr ""
|
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"
|
msgid "Choose Files"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -4112,7 +4122,7 @@ msgid "Merging user annotations into database"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/annotate.py:63
|
#: /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)"
|
msgid "Fetch annotations (experimental)"
|
||||||
msgstr ""
|
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/choose_library.py:403
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:197
|
#: /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/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:114
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:128
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:128
|
||||||
@ -4590,14 +4600,14 @@ msgid "Main memory"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:239
|
#: /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:670
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:678
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:679
|
||||||
msgid "Storage Card A"
|
msgid "Storage Card A"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:240
|
#: /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:672
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:680
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:681
|
||||||
msgid "Storage Card B"
|
msgid "Storage Card B"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -5373,7 +5383,7 @@ msgid "The specified directory could not be processed."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:274
|
#: /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"
|
msgid "No books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -5794,6 +5804,18 @@ msgstr ""
|
|||||||
msgid "E-book options"
|
msgid "E-book options"
|
||||||
msgstr ""
|
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:133
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:769
|
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:769
|
||||||
msgid "any date"
|
msgid "any date"
|
||||||
@ -5831,7 +5853,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:615
|
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:615
|
||||||
#, python-format
|
#, 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:616
|
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:616
|
||||||
@ -7685,226 +7707,222 @@ msgstr ""
|
|||||||
msgid "tags to remove"
|
msgid "tags to remove"
|
||||||
msgstr ""
|
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
|
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:148
|
||||||
msgid "No details available."
|
msgid "No details available."
|
||||||
msgstr ""
|
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."
|
msgid "Device no longer connected."
|
||||||
msgstr ""
|
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
|
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/device_debug.py:27
|
||||||
msgid "Debug device detection"
|
msgid "Debug device detection"
|
||||||
msgstr ""
|
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"
|
msgid "Get device information"
|
||||||
msgstr ""
|
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"
|
msgid "Get list of books on device"
|
||||||
msgstr ""
|
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"
|
msgid "Prepare files for transfer from device"
|
||||||
msgstr ""
|
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"
|
msgid "Get annotations from device"
|
||||||
msgstr ""
|
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"
|
msgid "Send metadata to device"
|
||||||
msgstr ""
|
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"
|
msgid "Send collections to device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:529
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:530
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Upload %d books to device"
|
msgid "Upload %d books to device"
|
||||||
msgstr ""
|
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"
|
msgid "Delete books from device"
|
||||||
msgstr ""
|
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"
|
msgid "Download books from device"
|
||||||
msgstr ""
|
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"
|
msgid "View book on device"
|
||||||
msgstr ""
|
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"
|
msgid "Set default send to device action"
|
||||||
msgstr ""
|
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"
|
msgid "Send to main memory"
|
||||||
msgstr ""
|
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"
|
msgid "Send to storage card A"
|
||||||
msgstr ""
|
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"
|
msgid "Send to storage card B"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:667
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:668
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:676
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:677
|
||||||
msgid "Main Memory"
|
msgid "Main Memory"
|
||||||
msgstr ""
|
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"
|
msgid "Send specific format to"
|
||||||
msgstr ""
|
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"
|
msgid "Send and delete from library"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:732
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:733
|
||||||
msgid "Eject device"
|
msgid "Eject device"
|
||||||
msgstr ""
|
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/misc.py:71
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:332
|
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:332
|
||||||
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:58
|
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:58
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr ""
|
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"
|
msgid "Error communicating with device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:843
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:844
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1416
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1417
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/email.py:260
|
#: /home/kovid/work/calibre/src/calibre/gui2/email.py:260
|
||||||
msgid "No suitable formats"
|
msgid "No suitable formats"
|
||||||
msgstr ""
|
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"
|
msgid "Select folder to open as device"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:877
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:878
|
||||||
msgid "Running jobs"
|
msgid "Running jobs"
|
||||||
msgstr ""
|
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."
|
msgid "Cannot configure the device while there are running device jobs."
|
||||||
msgstr ""
|
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
|
#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/mtp_config.py:168
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Configure %s"
|
msgid "Configure %s"
|
||||||
msgstr ""
|
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
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:899
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Disconnect and re-connect the %s for your changes to be applied."
|
msgid "Restart calibre for the changes to %s to be applied."
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:939
|
|
||||||
msgid "Error talking to device"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:940
|
#: /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."
|
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device or reboot."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:984
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:985
|
||||||
msgid "Device: "
|
msgid "Device: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:986
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:987
|
||||||
msgid " detected."
|
msgid " detected."
|
||||||
msgstr ""
|
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"
|
msgid "selected to send"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1095
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1096
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1125
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1126
|
||||||
msgid "No device"
|
msgid "No device"
|
||||||
msgstr ""
|
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"
|
msgid "No device connected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1112
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1113
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(num)i of %(total)i Books"
|
msgid "%(num)i of %(total)i Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1116
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1117
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "0 of %i Books"
|
msgid "0 of %i Books"
|
||||||
msgstr ""
|
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"
|
msgid "Choose format to send to device"
|
||||||
msgstr ""
|
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"
|
msgid "Cannot send: No device is connected"
|
||||||
msgstr ""
|
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:1130
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1134
|
#: /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"
|
msgid "Cannot send: Device has no storage card"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1195
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1196
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1278
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1279
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1410
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1411
|
||||||
msgid "Auto convert the following books before uploading to the device?"
|
msgid "Auto convert the following books before uploading to the device?"
|
||||||
msgstr ""
|
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."
|
msgid "Sending catalogs to device."
|
||||||
msgstr ""
|
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."
|
msgid "Sending news to device."
|
||||||
msgstr ""
|
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."
|
msgid "Sending books to device."
|
||||||
msgstr ""
|
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."
|
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 ""
|
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"
|
msgid "No space on device"
|
||||||
msgstr ""
|
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 "
|
msgid "<p>Cannot upload books to device there is no more free space available "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1496
|
#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1497
|
||||||
msgid "Incorrect destination"
|
msgid "Incorrect destination"
|
||||||
msgstr ""
|
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."
|
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 ""
|
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
|
#: /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."
|
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 ""
|
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/quickview.py:87
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:60
|
#: /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/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:288
|
||||||
#: /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:1254
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:113
|
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:113
|
||||||
msgid "Authors"
|
msgid "Authors"
|
||||||
msgstr ""
|
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."
|
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 ""
|
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
|
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:48
|
||||||
msgid "Source"
|
msgid "Source"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -14986,7 +14994,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/update.py:78
|
#: /home/kovid/work/calibre/src/calibre/gui2/update.py:78
|
||||||
#, python-format
|
#, 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/update.py:84
|
#: /home/kovid/work/calibre/src/calibre/gui2/update.py:84
|
||||||
@ -16370,153 +16378,168 @@ msgid ""
|
|||||||
"*** Adding 'By Authors' Section required for MOBI output ***"
|
"*** Adding 'By Authors' Section required for MOBI output ***"
|
||||||
msgstr ""
|
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"
|
msgid "Symbols"
|
||||||
msgstr ""
|
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"
|
msgid "No genres to catalog.\n"
|
||||||
msgstr ""
|
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"
|
msgid "Check 'Excluded genres' regex in E-book options.\n"
|
||||||
msgstr ""
|
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"
|
msgid "No books available to catalog"
|
||||||
msgstr ""
|
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:290
|
||||||
#: /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:2313
|
||||||
msgid "Titles"
|
msgid "Titles"
|
||||||
msgstr ""
|
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"
|
msgid "Genres"
|
||||||
msgstr ""
|
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:296
|
||||||
#: /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:1615
|
||||||
msgid "Recently Added"
|
msgid "Recently Added"
|
||||||
msgstr ""
|
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:298
|
||||||
#: /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:1814
|
||||||
msgid "Recently Read"
|
msgid "Recently Read"
|
||||||
msgstr ""
|
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"
|
msgid "Descriptions"
|
||||||
msgstr ""
|
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/>"
|
msgid "<p>Inconsistent Author Sort values for Author<br/>"
|
||||||
msgstr ""
|
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"
|
msgid "Warning: Inconsistent Author Sort values for Author '{!s}':\n"
|
||||||
msgstr ""
|
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"
|
msgid "Sorting database"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:983
|
#: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:751
|
||||||
msgid "Fetching database"
|
msgid "Sorting titles"
|
||||||
msgstr ""
|
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 ""
|
msgid ""
|
||||||
"No books to catalog.\n"
|
"No books to catalog.\n"
|
||||||
"Check 'Excluded books' rules in E-book options.\n"
|
"Check 'Excluded books' rules in E-book options.\n"
|
||||||
msgstr ""
|
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"
|
msgid "No books available to include in catalog"
|
||||||
msgstr ""
|
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"
|
msgid "Genres HTML"
|
||||||
msgstr ""
|
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"
|
msgid "Titles HTML"
|
||||||
msgstr ""
|
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:2488
|
||||||
#: /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:2490
|
||||||
#: /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:2492
|
||||||
msgid "by "
|
msgid "by "
|
||||||
msgstr ""
|
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"
|
msgid "Descriptions HTML"
|
||||||
msgstr ""
|
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"
|
msgid "Description HTML"
|
||||||
msgstr ""
|
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"
|
msgid "NCX header"
|
||||||
msgstr ""
|
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"
|
msgid "NCX for Descriptions"
|
||||||
msgstr ""
|
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"
|
msgid "NCX for Series"
|
||||||
msgstr ""
|
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
|
#, python-format
|
||||||
msgid "Series beginning with %s"
|
msgid "Series beginning with %s"
|
||||||
msgstr ""
|
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"
|
msgid "NCX for Titles"
|
||||||
msgstr ""
|
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
|
#, python-format
|
||||||
msgid "Titles beginning with %s"
|
msgid "Titles beginning with %s"
|
||||||
msgstr ""
|
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"
|
msgid "NCX for Authors"
|
||||||
msgstr ""
|
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
|
#, python-format
|
||||||
msgid "Authors beginning with '%s'"
|
msgid "Authors beginning with '%s'"
|
||||||
msgstr ""
|
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"
|
msgid "NCX for Recently Added"
|
||||||
msgstr ""
|
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"
|
msgid "NCX for Recently Read"
|
||||||
msgstr ""
|
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"
|
msgid "NCX for Genres"
|
||||||
msgstr ""
|
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"
|
msgid "Generating OPF"
|
||||||
msgstr ""
|
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"
|
msgid "Thumbnails"
|
||||||
msgstr ""
|
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"
|
msgid "Thumbnail"
|
||||||
msgstr ""
|
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"
|
msgid "Saving NCX"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -17107,17 +17130,17 @@ msgstr ""
|
|||||||
msgid "creating custom column "
|
msgid "creating custom column "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3580
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3583
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<p>Migrating old database to ebook library in %s<br><center>"
|
msgid "<p>Migrating old database to ebook library in %s<br><center>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3609
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3612
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Copying <b>%s</b>"
|
msgid "Copying <b>%s</b>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3626
|
#: /home/kovid/work/calibre/src/calibre/library/database2.py:3629
|
||||||
msgid "Compacting database"
|
msgid "Compacting database"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -39,6 +39,19 @@ def _get_external_ip():
|
|||||||
#print 'ipaddr: %s' % ipaddr
|
#print 'ipaddr: %s' % ipaddr
|
||||||
return ipaddr
|
return ipaddr
|
||||||
|
|
||||||
|
def verify_ipV4_address(ip_address):
|
||||||
|
result = None
|
||||||
|
if ip_address != '0.0.0.0' and ip_address != '::':
|
||||||
|
# do some more sanity checks on the address
|
||||||
|
try:
|
||||||
|
socket.inet_aton(ip_address)
|
||||||
|
if len(ip_address.split('.')) == 4:
|
||||||
|
result = ip_address
|
||||||
|
except socket.error:
|
||||||
|
# Not legal ip address
|
||||||
|
pass
|
||||||
|
return result
|
||||||
|
|
||||||
_ext_ip = None
|
_ext_ip = None
|
||||||
def get_external_ip():
|
def get_external_ip():
|
||||||
global _ext_ip
|
global _ext_ip
|
||||||
@ -93,7 +106,8 @@ def publish(desc, type, port, properties=None, add_hostname=True, use_ip_address
|
|||||||
into the TXT record.
|
into the TXT record.
|
||||||
'''
|
'''
|
||||||
server = start_server()
|
server = start_server()
|
||||||
service = create_service(desc, type, port, properties, add_hostname)
|
service = create_service(desc, type, port, properties, add_hostname,
|
||||||
|
use_ip_address)
|
||||||
server.registerService(service)
|
server.registerService(service)
|
||||||
|
|
||||||
def unpublish(desc, type, port, properties=None, add_hostname=True):
|
def unpublish(desc, type, port, properties=None, add_hostname=True):
|
||||||
|
@ -168,9 +168,9 @@ winutil_set_debug(PyObject *self, PyObject *args) {
|
|||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LPTSTR
|
static LPWSTR
|
||||||
get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iterate) {
|
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`
|
* device enumerated by `index` in the collection `hDevInfo`. `iterate`
|
||||||
* will be set to `FALSE` if `index` points outside `hDevInfo`.
|
* will be set to `FALSE` if `index` points outside `hDevInfo`.
|
||||||
* :return: A string allocated on the heap containing the property or
|
* :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;
|
SP_DEVINFO_DATA DeviceInfoData;
|
||||||
DWORD DataT;
|
DWORD DataT;
|
||||||
LPTSTR buffer = NULL;
|
LPWSTR buffer = NULL;
|
||||||
DWORD buffersize = 0;
|
DWORD buffersize = 0;
|
||||||
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iter
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!SetupDiGetDeviceRegistryProperty(
|
while(!SetupDiGetDeviceRegistryPropertyW(
|
||||||
hDevInfo,
|
hDevInfo,
|
||||||
&DeviceInfoData,
|
&DeviceInfoData,
|
||||||
property,
|
property,
|
||||||
@ -196,11 +196,11 @@ get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iter
|
|||||||
buffersize,
|
buffersize,
|
||||||
&buffersize)) {
|
&buffersize)) {
|
||||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
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 {
|
} else {
|
||||||
PyMem_Free(buffer);
|
if (buffer != NULL) { PyMem_Free(buffer); buffer = NULL; }
|
||||||
PyErr_SetFromWindowsErr(0);
|
PyErr_SetFromWindowsErr(0);
|
||||||
buffer = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} //while
|
} //while
|
||||||
@ -209,7 +209,7 @@ get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iter
|
|||||||
}
|
}
|
||||||
|
|
||||||
static BOOL
|
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];
|
WCHAR xVid[9], dVid[9], xPid[9], dPid[9];
|
||||||
unsigned int j;
|
unsigned int j;
|
||||||
_snwprintf_s(xVid, 9, _TRUNCATE, L"vid_%4.4x", vid);
|
_snwprintf_s(xVid, 9, _TRUNCATE, L"vid_%4.4x", vid);
|
||||||
@ -607,31 +607,28 @@ winutil_get_removable_drives(PyObject *self, PyObject *args) {
|
|||||||
return NULL;
|
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();
|
volumes = PyDict_New();
|
||||||
if (volumes == NULL) return NULL;
|
if (volumes == NULL) return PyErr_NoMemory();
|
||||||
|
ddebug = PyObject_IsTrue(pdebug);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
hDevInfo = create_device_info_set((LPGUID)&GUID_DEVINTERFACE_VOLUME,
|
hDevInfo = create_device_info_set((LPGUID)&GUID_DEVINTERFACE_VOLUME,
|
||||||
NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
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
|
// Enumerate through the set
|
||||||
for (i=0; iterate; i++) {
|
for (i=0; iterate; i++) {
|
||||||
candidates = PyList_New(0);
|
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);
|
interfaceDetailData = get_device_ancestors(hDevInfo, i, candidates, &iterate, ddebug);
|
||||||
if (interfaceDetailData == NULL) {
|
if (interfaceDetailData == NULL) {
|
||||||
PyErr_Print(); continue;
|
PyErr_Print();
|
||||||
|
Py_DECREF(candidates); candidates = NULL;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
length = wcslen(interfaceDetailData->DevicePath);
|
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);
|
key = PyBytes_FromFormat("%c", (char)g_drives[j].letter);
|
||||||
if (key == NULL) return PyErr_NoMemory();
|
if (key == NULL) return PyErr_NoMemory();
|
||||||
PyDict_SetItem(volumes, key, candidates);
|
PyDict_SetItem(volumes, key, candidates);
|
||||||
Py_DECREF(candidates);
|
Py_DECREF(key); key = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Py_XDECREF(candidates); candidates = NULL;
|
||||||
PyMem_Free(interfaceDetailData);
|
PyMem_Free(interfaceDetailData);
|
||||||
} //for
|
} //for
|
||||||
|
|
||||||
@ -672,7 +670,8 @@ winutil_get_usb_devices(PyObject *self, PyObject *args) {
|
|||||||
HDEVINFO hDevInfo;
|
HDEVINFO hDevInfo;
|
||||||
DWORD i; BOOL iterate = TRUE;
|
DWORD i; BOOL iterate = TRUE;
|
||||||
PyObject *devices, *temp = (PyObject *)1;
|
PyObject *devices, *temp = (PyObject *)1;
|
||||||
LPTSTR buffer;
|
LPWSTR buffer;
|
||||||
|
BOOL ok = 1;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "")) return NULL;
|
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
|
// Create a Device information set with all USB devices
|
||||||
hDevInfo = create_device_info_set(NULL, L"USB", 0,
|
hDevInfo = create_device_info_set(NULL, L"USB", 0,
|
||||||
DIGCF_PRESENT | DIGCF_ALLCLASSES);
|
DIGCF_PRESENT | DIGCF_ALLCLASSES);
|
||||||
if (hDevInfo == INVALID_HANDLE_VALUE)
|
if (hDevInfo == INVALID_HANDLE_VALUE) {
|
||||||
|
Py_DECREF(devices);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
// Enumerate through the set
|
// Enumerate through the set
|
||||||
for (i=0; iterate; i++) {
|
for (i=0; iterate; i++) {
|
||||||
buffer = get_registry_property(hDevInfo, i, SPDRP_HARDWAREID, &iterate);
|
buffer = get_registry_property(hDevInfo, i, SPDRP_HARDWAREID, &iterate);
|
||||||
@ -691,16 +692,17 @@ winutil_get_usb_devices(PyObject *self, PyObject *args) {
|
|||||||
PyErr_Print(); continue;
|
PyErr_Print(); continue;
|
||||||
}
|
}
|
||||||
buffersize = wcslen(buffer);
|
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);
|
temp = PyUnicode_FromWideChar(buffer, buffersize);
|
||||||
PyMem_Free(buffer);
|
PyMem_Free(buffer);
|
||||||
if (temp == NULL) {
|
if (temp == NULL) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
|
ok = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
PyList_Append(devices, temp);
|
PyList_Append(devices, temp); Py_DECREF(temp); temp = NULL;
|
||||||
} //for
|
} //for
|
||||||
if (temp == NULL) { Py_DECREF(devices); devices = NULL; }
|
if (!ok) { Py_DECREF(devices); devices = NULL; }
|
||||||
SetupDiDestroyDeviceInfoList(hDevInfo);
|
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
@ -711,7 +713,7 @@ winutil_is_usb_device_connected(PyObject *self, PyObject *args) {
|
|||||||
unsigned int vid, pid;
|
unsigned int vid, pid;
|
||||||
HDEVINFO hDevInfo;
|
HDEVINFO hDevInfo;
|
||||||
DWORD i; BOOL iterate = TRUE;
|
DWORD i; BOOL iterate = TRUE;
|
||||||
LPTSTR buffer;
|
LPWSTR buffer;
|
||||||
int found = FALSE;
|
int found = FALSE;
|
||||||
PyObject *ans;
|
PyObject *ans;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user