mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
0.8.58+
This commit is contained in:
commit
f1e89942b3
@ -2,19 +2,19 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
|
|
||||||
class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
||||||
title = u'FHM UK'
|
title = u'FHM UK'
|
||||||
description = 'Good News for Men'
|
description = 'Good News for Men.'
|
||||||
cover_url = 'http://www.greatmagazines.co.uk/covers/large/w197/current/fhm.jpg'
|
cover_url = 'http://www.greatmagazines.co.uk/covers/large/w197/current/fhm.jpg'
|
||||||
# cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/373529_38324934806_64930243_n.jpg'
|
# cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/373529_38324934806_64930243_n.jpg'
|
||||||
masthead_url = 'http://www.fhm.com/App_Resources/Images/Site/re-design/logo.gif'
|
masthead_url = 'http://www.fhm.com/App_Resources/Images/Site/re-design/logo.gif'
|
||||||
__author__ = 'Dave Asbury'
|
__author__ = 'Dave Asbury'
|
||||||
# last updated 14/4/12
|
# last updated 1/7/12
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
oldest_article = 28
|
oldest_article = 28
|
||||||
max_articles_per_feed = 12
|
max_articles_per_feed = 8
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
#auto_cleanup = True
|
#auto_cleanup = True
|
||||||
#articles_are_obfuscated = True
|
# articles_are_obfuscated = True
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
dict(name='h1'),
|
dict(name='h1'),
|
||||||
dict(name='img',attrs={'id' : 'ctl00_Body_imgMainImage'}),
|
dict(name='img',attrs={'id' : 'ctl00_Body_imgMainImage'}),
|
||||||
@ -28,11 +28,18 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
|
|
||||||
#]
|
#]
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'From the Homepage',u'http://feed43.com/0032328550253453.xml'),
|
(u'Homepage 1',u'http://feed43.com/6655867614547036.xml'),
|
||||||
#http://feed43.com/8053226782885416.xml'),
|
(u'Homepage 2',u'http://feed43.com/4167731873103110.xml'),
|
||||||
(u'Funny - The Very Best Of The Internet',u'http://feed43.com/4538510106331565.xml'),
|
(u'Homepage 3',u'http://feed43.com/7667138788771570.xml'),
|
||||||
(u'Upgrade',u'http://feed43.com/0877305847443234.xml'),
|
(u'Homepage 4',u'http://feed43.com/6550421522527341.xml'),
|
||||||
#(u'The Final Countdown', u'http://feed43.com/3576106158530118.xml'),
|
(u'Funny - The Very Best Of The Internet',u'http://feed43.com/4538510106331565.xml'),
|
||||||
#(u'Gaming',u'http://feed43.com/0755006465351035.xml'),
|
(u'Gaming',u'http://feed43.com/6537162612465672.xml'),
|
||||||
(u'Gaming',u'http://feed43.com/6537162612465672.xml'),
|
(u'Girls',u'http://feed43.com/3674777224513254.xml'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||||
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
@ -3,8 +3,8 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '04 December 2010, desUBIKado'
|
__copyright__ = '04 December 2010, desUBIKado'
|
||||||
__author__ = 'desUBIKado'
|
__author__ = 'desUBIKado'
|
||||||
__description__ = 'Daily newspaper from Aragon'
|
__description__ = 'Daily newspaper from Aragon'
|
||||||
__version__ = 'v0.04'
|
__version__ = 'v0.05'
|
||||||
__date__ = '6, Januery 2011'
|
__date__ = '5, Februery 2012'
|
||||||
'''
|
'''
|
||||||
[url]http://www.heraldo.es/[/url]
|
[url]http://www.heraldo.es/[/url]
|
||||||
'''
|
'''
|
||||||
@ -38,7 +38,7 @@ class heraldo(BasicNewsRecipe):
|
|||||||
keep_only_tags = [dict(name='div', attrs={'id':['dts','com']})]
|
keep_only_tags = [dict(name='div', attrs={'id':['dts','com']})]
|
||||||
|
|
||||||
remove_tags = [dict(name='a', attrs={'class':['com flo-r','enl-if','enl-df']}),
|
remove_tags = [dict(name='a', attrs={'class':['com flo-r','enl-if','enl-df']}),
|
||||||
dict(name='div', attrs={'class':['brb-b-s con marg-btt','cnt-rel con']}),
|
dict(name='div', attrs={'class':['brb-b-s con marg-btt','cnt-rel con','col5-f1']}),
|
||||||
dict(name='form', attrs={'class':'form'}),
|
dict(name='form', attrs={'class':'form'}),
|
||||||
dict(name='ul', attrs={'id':['cont-tags','pag-1']})]
|
dict(name='ul', attrs={'id':['cont-tags','pag-1']})]
|
||||||
|
|
||||||
@ -72,6 +72,9 @@ class heraldo(BasicNewsRecipe):
|
|||||||
|
|
||||||
preprocess_regexps = [
|
preprocess_regexps = [
|
||||||
|
|
||||||
# To separate the comments with a blank line
|
# Para separar los comentarios con una linea en blanco
|
||||||
(re.compile(r'<div id="com"', re.DOTALL|re.IGNORECASE), lambda match: '<br><div id="com"')
|
(re.compile(r'<div id="com"', re.DOTALL|re.IGNORECASE), lambda match: '<br><div id="com"')
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ class TheAge(BasicNewsRecipe):
|
|||||||
publication_type = 'newspaper'
|
publication_type = 'newspaper'
|
||||||
__author__ = 'Matthew Briggs'
|
__author__ = 'Matthew Briggs'
|
||||||
language = 'en_AU'
|
language = 'en_AU'
|
||||||
|
|
||||||
max_articles_per_feed = 1000
|
max_articles_per_feed = 1000
|
||||||
recursions = 0
|
recursions = 0
|
||||||
remove_tags = [dict(name=['table', 'script', 'noscript', 'style']), dict(name='a', attrs={'href':'/'}), dict(name='a', attrs={'href':'/text/'})]
|
remove_tags = [dict(name=['table', 'script', 'noscript', 'style']), dict(name='a', attrs={'href':'/'}), dict(name='a', attrs={'href':'/text/'})]
|
||||||
@ -47,18 +47,19 @@ class TheAge(BasicNewsRecipe):
|
|||||||
if url.startswith('/'):
|
if url.startswith('/'):
|
||||||
url = 'http://www.theage.com.au' + url
|
url = 'http://www.theage.com.au' + url
|
||||||
title = self.tag_to_string(tag)
|
title = self.tag_to_string(tag)
|
||||||
sections[section].append({
|
if url != 'http://www.theage.com.au':
|
||||||
'title': title,
|
sections[section].append({
|
||||||
'url' : url,
|
'title': title,
|
||||||
'date' : strftime('%a, %d %b'),
|
'url' : url,
|
||||||
'description' : '',
|
'date' : strftime('%a, %d %b'),
|
||||||
'content' : '',
|
'description' : '',
|
||||||
})
|
'content' : '',
|
||||||
|
})
|
||||||
|
|
||||||
feeds = []
|
feeds = []
|
||||||
|
|
||||||
# Insert feeds in specified order, if available
|
# Insert feeds in specified order, if available
|
||||||
|
|
||||||
feedSort = [ 'National', 'World', 'Opinion', 'Columns', 'Business', 'Sport', 'Entertainment' ]
|
feedSort = [ 'National', 'World', 'Opinion', 'Columns', 'Business', 'Sport', 'Entertainment' ]
|
||||||
for i in feedSort:
|
for i in feedSort:
|
||||||
if i in sections:
|
if i in sections:
|
||||||
@ -68,12 +69,12 @@ class TheAge(BasicNewsRecipe):
|
|||||||
|
|
||||||
for i in feedSort:
|
for i in feedSort:
|
||||||
del sections[i]
|
del sections[i]
|
||||||
|
|
||||||
# Append what is left over...
|
# Append what is left over...
|
||||||
|
|
||||||
for i in sections:
|
for i in sections:
|
||||||
feeds.append((i,sections[i]))
|
feeds.append((i,sections[i]))
|
||||||
|
|
||||||
return feeds
|
return feeds
|
||||||
|
|
||||||
def get_cover_url(self):
|
def get_cover_url(self):
|
||||||
@ -88,9 +89,9 @@ class TheAge(BasicNewsRecipe):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def preprocess_html(self,soup):
|
def preprocess_html(self,soup):
|
||||||
|
|
||||||
for p in soup.findAll('p'):
|
for p in soup.findAll('p'):
|
||||||
|
|
||||||
# Collapse the paragraph by joining the non-tag contents
|
# Collapse the paragraph by joining the non-tag contents
|
||||||
|
|
||||||
contents = [i for i in p.contents if isinstance(i,unicode)]
|
contents = [i for i in p.contents if isinstance(i,unicode)]
|
||||||
@ -103,10 +104,10 @@ class TheAge(BasicNewsRecipe):
|
|||||||
p.extract()
|
p.extract()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Shrink the fine print font
|
# Shrink the fine print font
|
||||||
|
|
||||||
if contents=='This material is subject to copyright and any unauthorised use, copying or mirroring is prohibited.':
|
if contents=='This material is subject to copyright and any unauthorised use, copying or mirroring is prohibited.':
|
||||||
p['style'] = 'font-size:small'
|
p['style'] = 'font-size:small'
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return soup
|
return soup
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '4 February 2011, desUBIKado'
|
__copyright__ = '4 February 2011, desUBIKado'
|
||||||
__author__ = 'desUBIKado'
|
__author__ = 'desUBIKado'
|
||||||
__version__ = 'v0.07'
|
__version__ = 'v0.08'
|
||||||
__date__ = '13, November 2011'
|
__date__ = '30, June 2012'
|
||||||
'''
|
'''
|
||||||
http://www.weblogssl.com/
|
http://www.weblogssl.com/
|
||||||
'''
|
'''
|
||||||
@ -33,6 +33,7 @@ class weblogssl(BasicNewsRecipe):
|
|||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Xataka', u'http://feeds.weblogssl.com/xataka2')
|
(u'Xataka', u'http://feeds.weblogssl.com/xataka2')
|
||||||
|
,(u'Xataka Smart Home', u'http://feeds.weblogssl.com/Xatakahome')
|
||||||
,(u'Xataka Mexico', u'http://feeds.weblogssl.com/xatakamx')
|
,(u'Xataka Mexico', u'http://feeds.weblogssl.com/xatakamx')
|
||||||
,(u'Xataka M\xf3vil', u'http://feeds.weblogssl.com/xatakamovil')
|
,(u'Xataka M\xf3vil', u'http://feeds.weblogssl.com/xatakamovil')
|
||||||
,(u'Xataka Android', u'http://feeds.weblogssl.com/xatakandroid')
|
,(u'Xataka Android', u'http://feeds.weblogssl.com/xatakandroid')
|
||||||
@ -107,12 +108,14 @@ class weblogssl(BasicNewsRecipe):
|
|||||||
|
|
||||||
# Para obtener la url original del articulo a partir de la de "feedsportal"
|
# Para obtener la url original del articulo a partir de la de "feedsportal"
|
||||||
# El siguiente código es gracias al usuario "bosplans" de www.mobileread.com
|
# El siguiente código es gracias al usuario "bosplans" de www.mobileread.com
|
||||||
# http://www.mobileread.com/forums/showthread.php?t=130297
|
# http://www.mobileread.com/forums/sho...d.php?t=130297
|
||||||
|
|
||||||
def get_article_url(self, article):
|
def get_article_url(self, article):
|
||||||
link = article.get('link', None)
|
link = article.get('link', None)
|
||||||
if link is None:
|
if link is None:
|
||||||
return article
|
return article
|
||||||
|
if link.split('/')[-4]=="xataka2":
|
||||||
|
return article.get('feedburner_origlink', article.get('link', article.get('guid')))
|
||||||
if link.split('/')[-1]=="story01.htm":
|
if link.split('/')[-1]=="story01.htm":
|
||||||
link=link.split('/')[-2]
|
link=link.split('/')[-2]
|
||||||
a=['0B','0C','0D','0E','0F','0G','0N' ,'0L0S','0A']
|
a=['0B','0C','0D','0E','0F','0G','0N' ,'0L0S','0A']
|
||||||
@ -121,6 +124,3 @@ class weblogssl(BasicNewsRecipe):
|
|||||||
link=link.replace(a[i],b[i])
|
link=link.replace(a[i],b[i])
|
||||||
link="http://"+link
|
link="http://"+link
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Binary file not shown.
@ -21,6 +21,7 @@ defaults.
|
|||||||
# last_free - First available integer smaller than the largest existing number
|
# last_free - First available integer smaller than the largest existing number
|
||||||
# Return largest existing + 1 if no free number is found
|
# Return largest existing + 1 if no free number is found
|
||||||
# const - Assign the number 1 always
|
# const - Assign the number 1 always
|
||||||
|
# no_change - Do not change the series index
|
||||||
# a number - Assign that number always. The number is not in quotes. Note that
|
# a number - Assign that number always. The number is not in quotes. Note that
|
||||||
# 0.0 can be used here.
|
# 0.0 can be used here.
|
||||||
# Examples:
|
# Examples:
|
||||||
|
@ -97,7 +97,7 @@ Now, run configure and make::
|
|||||||
|
|
||||||
-no-plugin-manifests is needed so that loading the plugins does not fail looking for the CRT assembly
|
-no-plugin-manifests is needed so that loading the plugins does not fail looking for the CRT assembly
|
||||||
|
|
||||||
configure -opensource -release -qt-zlib -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license -nomake examples -nomake demos -nomake docs -no-plugin-manifests -openssl -I Q:\openssl\include -L Q:\openssl\lib && nmake
|
configure -ltcg -opensource -release -qt-zlib -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license -nomake examples -nomake demos -nomake docs -no-plugin-manifests -openssl -I Q:\openssl\include -L Q:\openssl\lib && nmake
|
||||||
|
|
||||||
Add the path to the bin folder inside the Qt dir to your system PATH.
|
Add the path to the bin folder inside the Qt dir to your system PATH.
|
||||||
|
|
||||||
|
@ -643,7 +643,7 @@ from calibre.devices.cybook.driver import CYBOOK, ORIZON
|
|||||||
from calibre.devices.eb600.driver import (EB600, COOL_ER, SHINEBOOK,
|
from calibre.devices.eb600.driver import (EB600, COOL_ER, SHINEBOOK,
|
||||||
POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK,
|
POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK,
|
||||||
BOOQ, ELONEX, POCKETBOOK301, MENTOR, POCKETBOOK602,
|
BOOQ, ELONEX, POCKETBOOK301, MENTOR, POCKETBOOK602,
|
||||||
POCKETBOOK701, POCKETBOOK360P, PI2)
|
POCKETBOOK701, POCKETBOOK360P, PI2, POCKETBOOK622)
|
||||||
from calibre.devices.iliad.driver import ILIAD
|
from calibre.devices.iliad.driver import ILIAD
|
||||||
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
||||||
from calibre.devices.jetbook.driver import (JETBOOK, MIBUK, JETBOOK_MINI,
|
from calibre.devices.jetbook.driver import (JETBOOK, MIBUK, JETBOOK_MINI,
|
||||||
@ -689,7 +689,7 @@ plugins += [
|
|||||||
JETBOOK, JETBOOK_MINI, MIBUK, JETBOOK_COLOR,
|
JETBOOK, JETBOOK_MINI, MIBUK, JETBOOK_COLOR,
|
||||||
SHINEBOOK,
|
SHINEBOOK,
|
||||||
POCKETBOOK360, POCKETBOOK301, POCKETBOOK602, POCKETBOOK701, POCKETBOOK360P,
|
POCKETBOOK360, POCKETBOOK301, POCKETBOOK602, POCKETBOOK701, POCKETBOOK360P,
|
||||||
PI2,
|
POCKETBOOK622, PI2,
|
||||||
KINDLE, KINDLE2, KINDLE_DX, KINDLE_FIRE,
|
KINDLE, KINDLE2, KINDLE_DX, KINDLE_FIRE,
|
||||||
NOOK, NOOK_COLOR,
|
NOOK, NOOK_COLOR,
|
||||||
PRS505, PRST1,
|
PRS505, PRST1,
|
||||||
@ -1511,15 +1511,6 @@ class StoreOpenBooksStore(StoreBase):
|
|||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
|
|
||||||
class StoreOReillyStore(StoreBase):
|
|
||||||
name = 'OReilly'
|
|
||||||
description = u'Programming and tech ebooks from OReilly.'
|
|
||||||
actual_plugin = 'calibre.gui2.store.stores.oreilly_plugin:OReillyStore'
|
|
||||||
|
|
||||||
drm_free_only = True
|
|
||||||
headquarters = 'US'
|
|
||||||
formats = ['APK', 'DAISY', 'EPUB', 'MOBI', 'PDF']
|
|
||||||
|
|
||||||
class StoreOzonRUStore(StoreBase):
|
class StoreOzonRUStore(StoreBase):
|
||||||
name = 'OZON.ru'
|
name = 'OZON.ru'
|
||||||
description = u'ebooks from OZON.ru'
|
description = u'ebooks from OZON.ru'
|
||||||
@ -1659,7 +1650,6 @@ plugins += [
|
|||||||
StoreMobileReadStore,
|
StoreMobileReadStore,
|
||||||
StoreNextoStore,
|
StoreNextoStore,
|
||||||
StoreOpenBooksStore,
|
StoreOpenBooksStore,
|
||||||
StoreOReillyStore,
|
|
||||||
StoreOzonRUStore,
|
StoreOzonRUStore,
|
||||||
StorePragmaticBookshelfStore,
|
StorePragmaticBookshelfStore,
|
||||||
StoreRW2010Store,
|
StoreRW2010Store,
|
||||||
|
@ -101,6 +101,7 @@ class ANDROID(USBMS):
|
|||||||
0x685b : [0x0400, 0x0226],
|
0x685b : [0x0400, 0x0226],
|
||||||
0x685e : [0x0400],
|
0x685e : [0x0400],
|
||||||
0x6860 : [0x0400],
|
0x6860 : [0x0400],
|
||||||
|
0x6863 : [0x226],
|
||||||
0x6877 : [0x0400],
|
0x6877 : [0x0400],
|
||||||
0x689e : [0x0400],
|
0x689e : [0x0400],
|
||||||
0xdeed : [0x0222],
|
0xdeed : [0x0222],
|
||||||
@ -114,7 +115,6 @@ class ANDROID(USBMS):
|
|||||||
0xc004 : [0x0226],
|
0xc004 : [0x0226],
|
||||||
0x8801 : [0x0226, 0x0227],
|
0x8801 : [0x0226, 0x0227],
|
||||||
0xe115 : [0x0216], # PocketBook A10
|
0xe115 : [0x0216], # PocketBook A10
|
||||||
0xe107 : [0x326], # PocketBook 622
|
|
||||||
},
|
},
|
||||||
|
|
||||||
# Acer
|
# Acer
|
||||||
|
@ -251,6 +251,19 @@ class POCKETBOOK602(USBMS):
|
|||||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB603', 'PB902',
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB603', 'PB902',
|
||||||
'PB903', 'PB']
|
'PB903', 'PB']
|
||||||
|
|
||||||
|
class POCKETBOOK622(POCKETBOOK602):
|
||||||
|
|
||||||
|
name = 'PocketBook 622 Device Interface'
|
||||||
|
description = _('Communicate with the PocketBook 622 reader.')
|
||||||
|
EBOOK_DIR_MAIN = ''
|
||||||
|
|
||||||
|
VENDOR_ID = [0x0489]
|
||||||
|
PRODUCT_ID = [0xe107]
|
||||||
|
BCD = [0x0326]
|
||||||
|
|
||||||
|
VENDOR_NAME = 'LINUX'
|
||||||
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET'
|
||||||
|
|
||||||
class POCKETBOOK360P(POCKETBOOK602):
|
class POCKETBOOK360P(POCKETBOOK602):
|
||||||
|
|
||||||
name = 'PocketBook 360+ Device Interface'
|
name = 'PocketBook 360+ Device Interface'
|
||||||
|
@ -152,27 +152,31 @@ class CHMInput(InputFormatPlugin):
|
|||||||
#print "============================="
|
#print "============================="
|
||||||
log.debug('Found %d section nodes' % len(chapters))
|
log.debug('Found %d section nodes' % len(chapters))
|
||||||
htmlpath = os.path.splitext(hhcpath)[0] + ".html"
|
htmlpath = os.path.splitext(hhcpath)[0] + ".html"
|
||||||
f = open(htmlpath, 'wb')
|
with open(htmlpath, 'wb') as f:
|
||||||
if chapters:
|
if chapters:
|
||||||
f.write('<html><head><meta http-equiv="Content-type"'
|
f.write('<html><head><meta http-equiv="Content-type"'
|
||||||
' content="text/html;charset=UTF-8" /></head><body>\n')
|
' content="text/html;charset=UTF-8" /></head><body>\n')
|
||||||
path0 = chapters[0][1]
|
path0 = chapters[0][1]
|
||||||
subpath = os.path.dirname(path0)
|
subpath = os.path.dirname(path0)
|
||||||
|
base = os.path.dirname(f.name)
|
||||||
|
|
||||||
for chapter in chapters:
|
for chapter in chapters:
|
||||||
title = chapter[0]
|
title = chapter[0]
|
||||||
rsrcname = os.path.basename(chapter[1])
|
rsrcname = os.path.basename(chapter[1])
|
||||||
rsrcpath = os.path.join(subpath, rsrcname)
|
rsrcpath = os.path.join(subpath, rsrcname)
|
||||||
# title should already be url encoded
|
if (not os.path.exists(os.path.join(base, rsrcpath)) and
|
||||||
url = "<br /><a href=" + rsrcpath + ">" + title + " </a>\n"
|
os.path.exists(os.path.join(base, chapter[1]))):
|
||||||
if isinstance(url, unicode):
|
rsrcpath = chapter[1]
|
||||||
url = url.encode('utf-8')
|
|
||||||
f.write(url)
|
|
||||||
|
|
||||||
f.write("</body></html>")
|
# title should already be url encoded
|
||||||
else:
|
url = "<br /><a href=" + rsrcpath + ">" + title + " </a>\n"
|
||||||
f.write(hhcdata)
|
if isinstance(url, unicode):
|
||||||
f.close()
|
url = url.encode('utf-8')
|
||||||
|
f.write(url)
|
||||||
|
|
||||||
|
f.write("</body></html>")
|
||||||
|
else:
|
||||||
|
f.write(hhcdata)
|
||||||
return htmlpath
|
return htmlpath
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,30 +54,35 @@ class Ozon(Source):
|
|||||||
|
|
||||||
# for ozon.ru search we have to format ISBN with '-'
|
# for ozon.ru search we have to format ISBN with '-'
|
||||||
isbn = _format_isbn(log, identifiers.get('isbn', None))
|
isbn = _format_isbn(log, identifiers.get('isbn', None))
|
||||||
# TODO: format isbn!
|
ozonid = identifiers.get('ozon', None)
|
||||||
qItems = set([isbn, title])
|
|
||||||
if authors:
|
unk = unicode(_('Unknown')).upper()
|
||||||
qItems |= frozenset(authors)
|
if (title and title != unk) or (authors and authors != [unk]) or isbn or not ozonid:
|
||||||
qItems.discard(None)
|
qItems = set([isbn, title])
|
||||||
qItems.discard('')
|
if authors:
|
||||||
qItems = map(_quoteString, qItems)
|
qItems |= frozenset(authors)
|
||||||
|
qItems.discard(None)
|
||||||
q = u' '.join(qItems).strip()
|
qItems.discard('')
|
||||||
log.info(u'search string: ' + q)
|
qItems = map(_quoteString, qItems)
|
||||||
|
|
||||||
if isinstance(q, unicode):
|
q = u' '.join(qItems).strip()
|
||||||
q = q.encode('utf-8')
|
log.info(u'search string: ' + q)
|
||||||
if not q:
|
|
||||||
return None
|
if isinstance(q, unicode):
|
||||||
|
q = q.encode('utf-8')
|
||||||
search_url += quote_plus(q)
|
if not q:
|
||||||
|
return None
|
||||||
|
|
||||||
|
search_url += quote_plus(q)
|
||||||
|
else:
|
||||||
|
search_url = self.ozon_url + '/webservices/OzonWebSvc.asmx/ItemDetail?ID=%s' % ozonid
|
||||||
|
|
||||||
log.debug(u'search url: %r'%search_url)
|
log.debug(u'search url: %r'%search_url)
|
||||||
|
|
||||||
return search_url
|
return search_url
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def identify(self, log, result_queue, abort, title=None, authors=None,
|
def identify(self, log, result_queue, abort, title=None, authors=None,
|
||||||
identifiers={}, timeout=30): # {{{
|
identifiers={}, timeout=60): # {{{
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from calibre.ebooks.chardet import xml_to_unicode
|
from calibre.ebooks.chardet import xml_to_unicode
|
||||||
|
|
||||||
@ -99,7 +104,7 @@ class Ozon(Source):
|
|||||||
try:
|
try:
|
||||||
parser = etree.XMLParser(recover=True, no_network=True)
|
parser = etree.XMLParser(recover=True, no_network=True)
|
||||||
feed = etree.fromstring(xml_to_unicode(raw, strip_encoding_pats=True, assume_utf8=True)[0], parser=parser)
|
feed = etree.fromstring(xml_to_unicode(raw, strip_encoding_pats=True, assume_utf8=True)[0], parser=parser)
|
||||||
entries = feed.xpath('//*[local-name() = "SearchItems"]')
|
entries = feed.xpath('//*[local-name()="SearchItems" or local-name()="ItemDetail"]')
|
||||||
if entries:
|
if entries:
|
||||||
metadata = self.get_metadata(log, entries, title, authors, identifiers)
|
metadata = self.get_metadata(log, entries, title, authors, identifiers)
|
||||||
self.get_all_details(log, metadata, abort, result_queue, identifiers, timeout)
|
self.get_all_details(log, metadata, abort, result_queue, identifiers, timeout)
|
||||||
@ -112,8 +117,8 @@ class Ozon(Source):
|
|||||||
def get_metadata(self, log, entries, title, authors, identifiers): # {{{
|
def get_metadata(self, log, entries, title, authors, identifiers): # {{{
|
||||||
# some book titles have extra characters like this
|
# some book titles have extra characters like this
|
||||||
# TODO: make a twick
|
# TODO: make a twick
|
||||||
reRemoveFromTitle = None
|
#reRemoveFromTitle = None
|
||||||
#reRemoveFromTitle = re.compile(r'[?!:.,;+-/&%"\'=]')
|
reRemoveFromTitle = re.compile(r'[?!:.,;+-/&%"\'=]')
|
||||||
|
|
||||||
title = unicode(title).upper() if title else ''
|
title = unicode(title).upper() if title else ''
|
||||||
if reRemoveFromTitle:
|
if reRemoveFromTitle:
|
||||||
@ -163,7 +168,7 @@ class Ozon(Source):
|
|||||||
metadata.append(mi)
|
metadata.append(mi)
|
||||||
#log.debug(u'added metadata %s %s.'%(mi.title, mi.authors))
|
#log.debug(u'added metadata %s %s.'%(mi.title, mi.authors))
|
||||||
else:
|
else:
|
||||||
log.debug(u'skipped metadata %s %s. (does not match the query)'%(mi.title, mi.authors))
|
log.debug(u'skipped metadata %s %s. (does not match the query)'%(unicode(mi.title), mi.authors))
|
||||||
return metadata
|
return metadata
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -301,7 +306,7 @@ class Ozon(Source):
|
|||||||
if series:
|
if series:
|
||||||
metadata.series = series
|
metadata.series = series
|
||||||
|
|
||||||
xpt = u'normalize-space(substring-after(//meta[@name="description"]/@content, "ISBN"))'
|
xpt = u'normalize-space(//*[@class="product-detail"]//text()[starts-with(., "ISBN")])'
|
||||||
isbn_str = doc.xpath(xpt)
|
isbn_str = doc.xpath(xpt)
|
||||||
if isbn_str:
|
if isbn_str:
|
||||||
all_isbns = [check_isbn(isbn) for isbn in self.isbnRegex.findall(isbn_str) if _verifyISBNIntegrity(log, isbn)]
|
all_isbns = [check_isbn(isbn) for isbn in self.isbnRegex.findall(isbn_str) if _verifyISBNIntegrity(log, isbn)]
|
||||||
@ -326,7 +331,7 @@ class Ozon(Source):
|
|||||||
|
|
||||||
# can be set before from xml search responce
|
# can be set before from xml search responce
|
||||||
if not metadata.pubdate:
|
if not metadata.pubdate:
|
||||||
xpt = u'normalize-space(//div[@class="product-misc"]//text()[contains(., "г.")])'
|
xpt = u'normalize-space(substring-after(//div[@class="product-detail"]//text()[contains(., "г.")],";"))'
|
||||||
yearIn = doc.xpath(xpt)
|
yearIn = doc.xpath(xpt)
|
||||||
if yearIn:
|
if yearIn:
|
||||||
matcher = re.search(r'\d{4}', yearIn)
|
matcher = re.search(r'\d{4}', yearIn)
|
||||||
@ -334,17 +339,20 @@ class Ozon(Source):
|
|||||||
metadata.pubdate = toPubdate(log, matcher.group(0))
|
metadata.pubdate = toPubdate(log, matcher.group(0))
|
||||||
|
|
||||||
# overwrite comments from HTML if any
|
# overwrite comments from HTML if any
|
||||||
xpt = u'//table[@id="detail_description"]//tr/td'
|
xpt = u'//*[@id="detail_description"]//*[contains(text(), "От производителя")]/../node()[not(self::comment())][not(self::br)][preceding::*[contains(text(), "От производителя")]]'
|
||||||
|
from lxml.etree import ElementBase
|
||||||
comment_elem = doc.xpath(xpt)
|
comment_elem = doc.xpath(xpt)
|
||||||
if comment_elem:
|
if comment_elem:
|
||||||
comments = unicode(etree.tostring(comment_elem[0], encoding=unicode))
|
comments = u''
|
||||||
if comments:
|
for node in comment_elem:
|
||||||
# cleanup root tag, TODO: remove tags like object/embeded
|
if isinstance(node, ElementBase):
|
||||||
comments = re.sub(ur'\A.*?<td.*?>|</td>.*\Z', u'', comments.strip(), re.MULTILINE).strip()
|
comments += unicode(etree.tostring(node, encoding=unicode))
|
||||||
if comments and (not metadata.comments or len(comments) > len(metadata.comments)):
|
elif isinstance(node, basestring) and node.strip():
|
||||||
metadata.comments = comments
|
comments += unicode(node) + u'\n'
|
||||||
else:
|
if comments and (not metadata.comments or len(comments) > len(metadata.comments)):
|
||||||
log.debug('HTML book description skipped in favour of search service xml responce')
|
metadata.comments = comments
|
||||||
|
else:
|
||||||
|
log.debug('HTML book description skipped in favour of search service xml responce')
|
||||||
else:
|
else:
|
||||||
log.debug('No book description found in HTML')
|
log.debug('No book description found in HTML')
|
||||||
# }}}
|
# }}}
|
||||||
@ -430,7 +438,8 @@ def _translageLanguageToCode(displayLang): # {{{
|
|||||||
u'Китайский': 'zh',
|
u'Китайский': 'zh',
|
||||||
u'Японский': 'ja',
|
u'Японский': 'ja',
|
||||||
u'Финский' : 'fi',
|
u'Финский' : 'fi',
|
||||||
u'Польский' : 'pl',}
|
u'Польский' : 'pl',
|
||||||
|
u'Украинский' : 'uk',}
|
||||||
return langTbl.get(displayLang, None)
|
return langTbl.get(displayLang, None)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -454,7 +463,7 @@ def toPubdate(log, yearAsString): # {{{
|
|||||||
res = None
|
res = None
|
||||||
if yearAsString:
|
if yearAsString:
|
||||||
try:
|
try:
|
||||||
res = parse_only_date(yearAsString)
|
res = parse_only_date(u"01.01." + yearAsString)
|
||||||
except:
|
except:
|
||||||
log.error('cannot parse to date %s'%yearAsString)
|
log.error('cannot parse to date %s'%yearAsString)
|
||||||
return res
|
return res
|
||||||
|
@ -66,6 +66,7 @@ class PagedDisplay
|
|||||||
this.in_paged_mode = false
|
this.in_paged_mode = false
|
||||||
this.current_margin_side = 0
|
this.current_margin_side = 0
|
||||||
this.is_full_screen_layout = false
|
this.is_full_screen_layout = false
|
||||||
|
this.max_col_width = -1
|
||||||
|
|
||||||
set_geometry: (cols_per_screen=1, margin_top=20, margin_side=40, margin_bottom=20) ->
|
set_geometry: (cols_per_screen=1, margin_top=20, margin_side=40, margin_bottom=20) ->
|
||||||
this.margin_top = margin_top
|
this.margin_top = margin_top
|
||||||
@ -108,6 +109,11 @@ class PagedDisplay
|
|||||||
# Minimum column width, for the cases when the window is too
|
# Minimum column width, for the cases when the window is too
|
||||||
# narrow
|
# narrow
|
||||||
col_width = Math.max(100, ((ww - adjust)/n) - 2*sm)
|
col_width = Math.max(100, ((ww - adjust)/n) - 2*sm)
|
||||||
|
if this.max_col_width > 0 and col_width > this.max_col_width
|
||||||
|
# Increase the side margin to ensure that col_width is no larger
|
||||||
|
# than max_col_width
|
||||||
|
sm += Math.ceil( (col_width - this.max_col_width) / 2*n )
|
||||||
|
col_width = Math.max(100, ((ww - adjust)/n) - 2*sm)
|
||||||
this.page_width = col_width + 2*sm
|
this.page_width = col_width + 2*sm
|
||||||
this.screen_width = this.page_width * this.cols_per_screen
|
this.screen_width = this.page_width * this.cols_per_screen
|
||||||
|
|
||||||
@ -360,5 +366,4 @@ if window?
|
|||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# Resizing of images
|
# Resizing of images
|
||||||
# Full screen mode
|
|
||||||
# Highlight on jump_to_anchor
|
# Highlight on jump_to_anchor
|
||||||
|
@ -13,7 +13,7 @@ from lxml import etree
|
|||||||
|
|
||||||
from calibre import guess_type, strftime
|
from calibre import guess_type, strftime
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
from calibre.ebooks.oeb.base import XPath, XHTML_NS, XHTML
|
from calibre.ebooks.oeb.base import XPath, XHTML_NS, XHTML, xml2text, urldefrag
|
||||||
from calibre.library.comments import comments_to_html
|
from calibre.library.comments import comments_to_html
|
||||||
from calibre.utils.date import is_date_undefined
|
from calibre.utils.date import is_date_undefined
|
||||||
from calibre.ebooks.chardet import strip_encoding_declarations
|
from calibre.ebooks.chardet import strip_encoding_declarations
|
||||||
@ -41,11 +41,25 @@ class Jacket(object):
|
|||||||
return removed
|
return removed
|
||||||
|
|
||||||
def remove_first_image(self):
|
def remove_first_image(self):
|
||||||
|
deleted_item = None
|
||||||
for item in self.oeb.spine:
|
for item in self.oeb.spine:
|
||||||
removed = self.remove_images(item)
|
removed = self.remove_images(item)
|
||||||
if removed > 0:
|
if removed > 0:
|
||||||
self.log('Removed first image')
|
self.log('Removed first image')
|
||||||
|
body = XPath('//h:body')(item.data)
|
||||||
|
if body:
|
||||||
|
raw = xml2text(body[0]).strip()
|
||||||
|
imgs = XPath('//h:img|//svg:svg')(item.data)
|
||||||
|
if not raw and not imgs:
|
||||||
|
self.log('Removing %s as it has no content'%item.href)
|
||||||
|
self.oeb.manifest.remove(item)
|
||||||
|
deleted_item = item
|
||||||
break
|
break
|
||||||
|
if deleted_item is not None:
|
||||||
|
for item in list(self.oeb.toc):
|
||||||
|
href = urldefrag(item.href)[0]
|
||||||
|
if href == deleted_item.href:
|
||||||
|
self.oeb.toc.remove(item)
|
||||||
|
|
||||||
def insert_metadata(self, mi):
|
def insert_metadata(self, mi):
|
||||||
self.log('Inserting metadata into book...')
|
self.log('Inserting metadata into book...')
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
|
|
||||||
from PyQt4.Qt import (QLineEdit, QAbstractListModel, Qt,
|
from PyQt4.Qt import (QLineEdit, QAbstractListModel, Qt,
|
||||||
QApplication, QCompleter, pyqtSignal)
|
QApplication, QCompleter)
|
||||||
|
|
||||||
from calibre.utils.icu import sort_key, lower
|
from calibre.utils.icu import sort_key, lower
|
||||||
from calibre.gui2 import NONE
|
from calibre.gui2 import NONE
|
||||||
@ -56,7 +56,7 @@ class MultiCompleteLineEdit(QLineEdit, LineEditECM):
|
|||||||
to complete non multiple fields as well.
|
to complete non multiple fields as well.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None, completer_widget=None):
|
||||||
QLineEdit.__init__(self, parent)
|
QLineEdit.__init__(self, parent)
|
||||||
|
|
||||||
self.sep = ','
|
self.sep = ','
|
||||||
@ -66,7 +66,7 @@ class MultiCompleteLineEdit(QLineEdit, LineEditECM):
|
|||||||
|
|
||||||
self._model = CompleteModel(parent=self)
|
self._model = CompleteModel(parent=self)
|
||||||
self._completer = c = QCompleter(self._model, self)
|
self._completer = c = QCompleter(self._model, self)
|
||||||
c.setWidget(self)
|
c.setWidget(self if completer_widget is None else completer_widget)
|
||||||
c.setCompletionMode(QCompleter.PopupCompletion)
|
c.setCompletionMode(QCompleter.PopupCompletion)
|
||||||
c.setCaseSensitivity(Qt.CaseInsensitive)
|
c.setCaseSensitivity(Qt.CaseInsensitive)
|
||||||
c.setModelSorting(self._model.sorting)
|
c.setModelSorting(self._model.sorting)
|
||||||
@ -158,21 +158,15 @@ class MultiCompleteLineEdit(QLineEdit, LineEditECM):
|
|||||||
|
|
||||||
class MultiCompleteComboBox(EnComboBox):
|
class MultiCompleteComboBox(EnComboBox):
|
||||||
|
|
||||||
clear_edit_text = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
EnComboBox.__init__(self, *args)
|
EnComboBox.__init__(self, *args)
|
||||||
self.setLineEdit(MultiCompleteLineEdit(self))
|
self.le = MultiCompleteLineEdit(self, completer_widget=self)
|
||||||
# Needed to allow changing the case of an existing item
|
self.setLineEdit(self.le)
|
||||||
# otherwise on focus out, the text is changed to the
|
|
||||||
# item that matches case insensitively
|
def showPopup(self):
|
||||||
c = self.lineEdit().completer()
|
c = self.le._completer
|
||||||
c.setCaseSensitivity(Qt.CaseSensitive)
|
c.setCompletionPrefix('')
|
||||||
self.dummy_model = CompleteModel(self)
|
c.complete()
|
||||||
c.setModel(self.dummy_model)
|
|
||||||
self.lineEdit()._completer.setWidget(self)
|
|
||||||
self.clear_edit_text.connect(self.clearEditText,
|
|
||||||
type=Qt.QueuedConnection)
|
|
||||||
|
|
||||||
def update_items_cache(self, complete_items):
|
def update_items_cache(self, complete_items):
|
||||||
self.lineEdit().update_items_cache(complete_items)
|
self.lineEdit().update_items_cache(complete_items)
|
||||||
@ -187,18 +181,10 @@ class MultiCompleteComboBox(EnComboBox):
|
|||||||
self.lineEdit().set_add_separator(what)
|
self.lineEdit().set_add_separator(what)
|
||||||
|
|
||||||
def show_initial_value(self, what):
|
def show_initial_value(self, what):
|
||||||
'''
|
what = unicode(what) if what else u''
|
||||||
Show an initial value. Handle the case of the initial value being blank
|
|
||||||
correctly (on Qt 4.8.0 having a blank value causes the first value from
|
|
||||||
the completer to be shown, when the event loop runs).
|
|
||||||
'''
|
|
||||||
what = unicode(what)
|
|
||||||
le = self.lineEdit()
|
le = self.lineEdit()
|
||||||
if not what.strip():
|
self.setEditText(what)
|
||||||
self.clear_edit_text.emit()
|
le.selectAll()
|
||||||
else:
|
|
||||||
self.setEditText(what)
|
|
||||||
le.selectAll()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from PyQt4.Qt import QDialog, QVBoxLayout
|
from PyQt4.Qt import QDialog, QVBoxLayout
|
||||||
@ -207,5 +193,8 @@ if __name__ == '__main__':
|
|||||||
d.setLayout(QVBoxLayout())
|
d.setLayout(QVBoxLayout())
|
||||||
le = MultiCompleteComboBox(d)
|
le = MultiCompleteComboBox(d)
|
||||||
d.layout().addWidget(le)
|
d.layout().addWidget(le)
|
||||||
le.all_items = ['one', 'otwo', 'othree', 'ooone', 'ootwo', 'oothree']
|
items = ['one', 'otwo', 'othree', 'ooone', 'ootwo',
|
||||||
|
'oothree']
|
||||||
|
le.update_items_cache(items)
|
||||||
|
le.show_initial_value('')
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
@ -12,8 +12,8 @@ from PyQt4.Qt import QPixmap, SIGNAL
|
|||||||
|
|
||||||
from calibre.gui2 import choose_images, error_dialog
|
from calibre.gui2 import choose_images, error_dialog
|
||||||
from calibre.gui2.convert.metadata_ui import Ui_Form
|
from calibre.gui2.convert.metadata_ui import Ui_Form
|
||||||
from calibre.ebooks.metadata import (authors_to_string, string_to_authors,
|
from calibre.ebooks.metadata import (string_to_authors, MetaInformation,
|
||||||
MetaInformation, title_sort)
|
title_sort)
|
||||||
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.gui2.convert import Widget
|
from calibre.gui2.convert import Widget
|
||||||
@ -74,14 +74,12 @@ class MetadataWidget(Widget, Ui_Form):
|
|||||||
|
|
||||||
mi = self.db.get_metadata(self.book_id, index_is_id=True)
|
mi = self.db.get_metadata(self.book_id, index_is_id=True)
|
||||||
self.title.setText(mi.title)
|
self.title.setText(mi.title)
|
||||||
if mi.publisher:
|
self.publisher.show_initial_value(mi.publisher if mi.publisher else '')
|
||||||
self.publisher.setCurrentIndex(self.publisher.findText(mi.publisher))
|
|
||||||
self.author_sort.setText(mi.author_sort if mi.author_sort else '')
|
self.author_sort.setText(mi.author_sort if mi.author_sort else '')
|
||||||
self.tags.setText(', '.join(mi.tags if mi.tags else []))
|
self.tags.setText(', '.join(mi.tags if mi.tags else []))
|
||||||
self.tags.update_items_cache(self.db.all_tags())
|
self.tags.update_items_cache(self.db.all_tags())
|
||||||
self.comment.html = comments_to_html(mi.comments) if mi.comments else ''
|
self.comment.html = comments_to_html(mi.comments) if mi.comments else ''
|
||||||
if mi.series:
|
self.series.show_initial_value(mi.series if mi.series else '')
|
||||||
self.series.setCurrentIndex(self.series.findText(mi.series))
|
|
||||||
if mi.series_index is not None:
|
if mi.series_index is not None:
|
||||||
try:
|
try:
|
||||||
self.series_index.setValue(mi.series_index)
|
self.series_index.setValue(mi.series_index)
|
||||||
@ -118,16 +116,11 @@ class MetadataWidget(Widget, Ui_Form):
|
|||||||
self.author.set_add_separator(tweaks['authors_completer_append_separator'])
|
self.author.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
self.author.update_items_cache(self.db.all_author_names())
|
self.author.update_items_cache(self.db.all_author_names())
|
||||||
|
|
||||||
for i in all_authors:
|
|
||||||
id, name = i
|
|
||||||
name = authors_to_string([name.strip().replace('|', ',') for n in name.split(',')])
|
|
||||||
self.author.addItem(name)
|
|
||||||
|
|
||||||
au = self.db.authors(self.book_id, True)
|
au = self.db.authors(self.book_id, True)
|
||||||
if not au:
|
if not au:
|
||||||
au = _('Unknown')
|
au = _('Unknown')
|
||||||
au = ' & '.join([a.strip().replace('|', ',') for a in au.split(',')])
|
au = ' & '.join([a.strip().replace('|', ',') for a in au.split(',')])
|
||||||
self.author.setEditText(au)
|
self.author.show_initial_value(au)
|
||||||
|
|
||||||
def initialize_series(self):
|
def initialize_series(self):
|
||||||
all_series = self.db.all_series()
|
all_series = self.db.all_series()
|
||||||
@ -135,22 +128,12 @@ class MetadataWidget(Widget, Ui_Form):
|
|||||||
self.series.set_separator(None)
|
self.series.set_separator(None)
|
||||||
self.series.update_items_cache([x[1] for x in all_series])
|
self.series.update_items_cache([x[1] for x in all_series])
|
||||||
|
|
||||||
for i in all_series:
|
|
||||||
id, name = i
|
|
||||||
self.series.addItem(name)
|
|
||||||
self.series.setCurrentIndex(-1)
|
|
||||||
|
|
||||||
def initialize_publisher(self):
|
def initialize_publisher(self):
|
||||||
all_publishers = self.db.all_publishers()
|
all_publishers = self.db.all_publishers()
|
||||||
all_publishers.sort(key=lambda x : sort_key(x[1]))
|
all_publishers.sort(key=lambda x : sort_key(x[1]))
|
||||||
self.publisher.set_separator(None)
|
self.publisher.set_separator(None)
|
||||||
self.publisher.update_items_cache([x[1] for x in all_publishers])
|
self.publisher.update_items_cache([x[1] for x in all_publishers])
|
||||||
|
|
||||||
for i in all_publishers:
|
|
||||||
id, name = i
|
|
||||||
self.publisher.addItem(name)
|
|
||||||
self.publisher.setCurrentIndex(-1)
|
|
||||||
|
|
||||||
def get_title_and_authors(self):
|
def get_title_and_authors(self):
|
||||||
title = unicode(self.title.text()).strip()
|
title = unicode(self.title.text()).strip()
|
||||||
if not title:
|
if not title:
|
||||||
|
@ -314,14 +314,7 @@ class Text(Base):
|
|||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
self.setter(val)
|
self.setter(val)
|
||||||
else:
|
else:
|
||||||
idx = None
|
self.widgets[1].show_initial_value(val)
|
||||||
for i, c in enumerate(values):
|
|
||||||
if c == val:
|
|
||||||
idx = i
|
|
||||||
self.widgets[1].addItem(c)
|
|
||||||
self.widgets[1].setEditText('')
|
|
||||||
if idx is not None:
|
|
||||||
self.widgets[1].setCurrentIndex(idx)
|
|
||||||
|
|
||||||
def setter(self, val):
|
def setter(self, val):
|
||||||
if self.col_metadata['is_multiple']:
|
if self.col_metadata['is_multiple']:
|
||||||
@ -396,16 +389,8 @@ class Series(Base):
|
|||||||
self.initial_index = s_index
|
self.initial_index = s_index
|
||||||
self.initial_val = val
|
self.initial_val = val
|
||||||
val = self.normalize_db_val(val)
|
val = self.normalize_db_val(val)
|
||||||
idx = None
|
|
||||||
self.name_widget.clear()
|
|
||||||
for i, c in enumerate(values):
|
|
||||||
if c == val:
|
|
||||||
idx = i
|
|
||||||
self.name_widget.addItem(c)
|
|
||||||
self.name_widget.update_items_cache(values)
|
self.name_widget.update_items_cache(values)
|
||||||
self.name_widget.setEditText('')
|
self.name_widget.show_initial_value(val)
|
||||||
if idx is not None:
|
|
||||||
self.widgets[1].setCurrentIndex(idx)
|
|
||||||
|
|
||||||
def getter(self):
|
def getter(self):
|
||||||
n = unicode(self.name_widget.currentText()).strip()
|
n = unicode(self.name_widget.currentText()).strip()
|
||||||
@ -860,8 +845,6 @@ class BulkSeries(BulkBase):
|
|||||||
self.idx_widget.setChecked(False)
|
self.idx_widget.setChecked(False)
|
||||||
self.main_widget.set_separator(None)
|
self.main_widget.set_separator(None)
|
||||||
self.main_widget.update_items_cache(self.all_values)
|
self.main_widget.update_items_cache(self.all_values)
|
||||||
for c in self.all_values:
|
|
||||||
self.main_widget.addItem(c)
|
|
||||||
self.main_widget.setEditText('')
|
self.main_widget.setEditText('')
|
||||||
self.a_c_checkbox.setChecked(False)
|
self.a_c_checkbox.setChecked(False)
|
||||||
|
|
||||||
@ -1005,15 +988,8 @@ class BulkText(BulkBase):
|
|||||||
if not self.col_metadata['is_multiple']:
|
if not self.col_metadata['is_multiple']:
|
||||||
val = self.get_initial_value(book_ids)
|
val = self.get_initial_value(book_ids)
|
||||||
self.initial_val = val = self.normalize_db_val(val)
|
self.initial_val = val = self.normalize_db_val(val)
|
||||||
idx = None
|
|
||||||
self.main_widget.blockSignals(True)
|
self.main_widget.blockSignals(True)
|
||||||
for i, c in enumerate(self.all_values):
|
self.main_widget.show_initial_value(val)
|
||||||
if c == val:
|
|
||||||
idx = i
|
|
||||||
self.main_widget.addItem(c)
|
|
||||||
self.main_widget.setEditText('')
|
|
||||||
if idx is not None:
|
|
||||||
self.main_widget.setCurrentIndex(idx)
|
|
||||||
self.main_widget.blockSignals(False)
|
self.main_widget.blockSignals(False)
|
||||||
|
|
||||||
def commit(self, book_ids, notify=False):
|
def commit(self, book_ids, notify=False):
|
||||||
|
@ -6,8 +6,7 @@ __license__ = 'GPL v3'
|
|||||||
|
|
||||||
from PyQt4.Qt import QDialog, QGridLayout, QLabel, QDialogButtonBox, \
|
from PyQt4.Qt import QDialog, QGridLayout, QLabel, QDialogButtonBox, \
|
||||||
QApplication, QSpinBox, QToolButton, QIcon
|
QApplication, QSpinBox, QToolButton, QIcon
|
||||||
from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
from calibre.ebooks.metadata import string_to_authors
|
||||||
from calibre.utils.icu import sort_key
|
|
||||||
from calibre.gui2.complete import MultiCompleteComboBox
|
from calibre.gui2.complete import MultiCompleteComboBox
|
||||||
from calibre.utils.config import tweaks
|
from calibre.utils.config import tweaks
|
||||||
|
|
||||||
@ -56,17 +55,10 @@ class AddEmptyBookDialog(QDialog):
|
|||||||
self.authors_combo.setEditText(_('Unknown'))
|
self.authors_combo.setEditText(_('Unknown'))
|
||||||
|
|
||||||
def initialize_authors(self, db, author):
|
def initialize_authors(self, db, author):
|
||||||
all_authors = db.all_authors()
|
|
||||||
all_authors.sort(key=lambda x : sort_key(x[1]))
|
|
||||||
for i in all_authors:
|
|
||||||
id, name = i
|
|
||||||
name = [name.strip().replace('|', ',') for n in name.split(',')]
|
|
||||||
self.authors_combo.addItem(authors_to_string(name))
|
|
||||||
|
|
||||||
au = author
|
au = author
|
||||||
if not au:
|
if not au:
|
||||||
au = _('Unknown')
|
au = _('Unknown')
|
||||||
self.authors_combo.setEditText(au.replace('|', ','))
|
self.authors_combo.show_initial_value(au.replace('|', ','))
|
||||||
|
|
||||||
self.authors_combo.set_separator('&')
|
self.authors_combo.set_separator('&')
|
||||||
self.authors_combo.set_space_before_sep(True)
|
self.authors_combo.set_space_before_sep(True)
|
||||||
|
@ -261,8 +261,12 @@ class MyBlockingBusy(QDialog): # {{{
|
|||||||
else:
|
else:
|
||||||
next = self.db.get_next_series_num_for(series)
|
next = self.db.get_next_series_num_for(series)
|
||||||
self.db.set_series(id, series, notify=False, commit=False)
|
self.db.set_series(id, series, notify=False, commit=False)
|
||||||
num = next if do_autonumber and series else 1.0
|
if not series:
|
||||||
self.db.set_series_index(id, num, notify=False, commit=False)
|
self.db.set_series_index(id, 1.0, notify=False, commit=False)
|
||||||
|
elif do_autonumber: # is True if do_series_restart is True
|
||||||
|
self.db.set_series_index(id, next, notify=False, commit=False)
|
||||||
|
elif tweaks['series_index_auto_increment'] != 'no_change':
|
||||||
|
self.db.set_series_index(id, 1.0, notify=False, commit=False)
|
||||||
|
|
||||||
if do_remove_conv:
|
if do_remove_conv:
|
||||||
self.db.delete_conversion_options(id, 'PIPE', commit=False)
|
self.db.delete_conversion_options(id, 'PIPE', commit=False)
|
||||||
@ -872,38 +876,25 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog):
|
|||||||
all_authors = self.db.all_authors()
|
all_authors = self.db.all_authors()
|
||||||
all_authors.sort(key=lambda x : sort_key(x[1]))
|
all_authors.sort(key=lambda x : sort_key(x[1]))
|
||||||
|
|
||||||
for i in all_authors:
|
|
||||||
id, name = i
|
|
||||||
name = name.strip().replace('|', ',')
|
|
||||||
self.authors.addItem(name)
|
|
||||||
self.authors.setEditText('')
|
|
||||||
|
|
||||||
self.authors.set_separator('&')
|
self.authors.set_separator('&')
|
||||||
self.authors.set_space_before_sep(True)
|
self.authors.set_space_before_sep(True)
|
||||||
self.authors.set_add_separator(tweaks['authors_completer_append_separator'])
|
self.authors.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
self.authors.update_items_cache(self.db.all_author_names())
|
self.authors.update_items_cache(self.db.all_author_names())
|
||||||
|
self.authors.show_initial_value('')
|
||||||
|
|
||||||
def initialize_series(self):
|
def initialize_series(self):
|
||||||
all_series = self.db.all_series()
|
all_series = self.db.all_series()
|
||||||
all_series.sort(key=lambda x : sort_key(x[1]))
|
all_series.sort(key=lambda x : sort_key(x[1]))
|
||||||
self.series.set_separator(None)
|
self.series.set_separator(None)
|
||||||
self.series.update_items_cache([x[1] for x in all_series])
|
self.series.update_items_cache([x[1] for x in all_series])
|
||||||
|
self.series.show_initial_value('')
|
||||||
for i in all_series:
|
|
||||||
id, name = i
|
|
||||||
self.series.addItem(name)
|
|
||||||
self.series.setEditText('')
|
|
||||||
|
|
||||||
def initialize_publisher(self):
|
def initialize_publisher(self):
|
||||||
all_publishers = self.db.all_publishers()
|
all_publishers = self.db.all_publishers()
|
||||||
all_publishers.sort(key=lambda x : sort_key(x[1]))
|
all_publishers.sort(key=lambda x : sort_key(x[1]))
|
||||||
self.publisher.set_separator(None)
|
self.publisher.set_separator(None)
|
||||||
self.publisher.update_items_cache([x[1] for x in all_publishers])
|
self.publisher.update_items_cache([x[1] for x in all_publishers])
|
||||||
|
self.publisher.show_initial_value('')
|
||||||
for i in all_publishers:
|
|
||||||
id, name = i
|
|
||||||
self.publisher.addItem(name)
|
|
||||||
self.publisher.setEditText('')
|
|
||||||
|
|
||||||
def tag_editor(self, *args):
|
def tag_editor(self, *args):
|
||||||
d = TagEditor(self, self.db, None)
|
d = TagEditor(self, self.db, None)
|
||||||
|
@ -25,10 +25,6 @@ class SearchDialog(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
all_authors = db.all_authors()
|
all_authors = db.all_authors()
|
||||||
all_authors.sort(key=lambda x : sort_key(x[1]))
|
all_authors.sort(key=lambda x : sort_key(x[1]))
|
||||||
for i in all_authors:
|
|
||||||
id, name = i
|
|
||||||
name = name.strip().replace('|', ',')
|
|
||||||
self.authors_box.addItem(name)
|
|
||||||
self.authors_box.setEditText('')
|
self.authors_box.setEditText('')
|
||||||
self.authors_box.set_separator('&')
|
self.authors_box.set_separator('&')
|
||||||
self.authors_box.set_space_before_sep(True)
|
self.authors_box.set_space_before_sep(True)
|
||||||
@ -39,10 +35,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
|||||||
all_series.sort(key=lambda x : sort_key(x[1]))
|
all_series.sort(key=lambda x : sort_key(x[1]))
|
||||||
self.series_box.set_separator(None)
|
self.series_box.set_separator(None)
|
||||||
self.series_box.update_items_cache([x[1] for x in all_series])
|
self.series_box.update_items_cache([x[1] for x in all_series])
|
||||||
for i in all_series:
|
self.series_box.show_initial_value('')
|
||||||
id, name = i
|
|
||||||
self.series_box.addItem(name)
|
|
||||||
self.series_box.setEditText('')
|
|
||||||
|
|
||||||
all_tags = db.all_tags()
|
all_tags = db.all_tags()
|
||||||
self.tags_box.update_items_cache(all_tags)
|
self.tags_box.update_items_cache(all_tags)
|
||||||
|
@ -32,8 +32,6 @@ class LanguagesEdit(MultiCompleteComboBox):
|
|||||||
all_items = sorted(self._lang_map.itervalues(),
|
all_items = sorted(self._lang_map.itervalues(),
|
||||||
key=lambda x: (-pmap.get(x, 0), sort_key(x)))
|
key=lambda x: (-pmap.get(x, 0), sort_key(x)))
|
||||||
self.update_items_cache(all_items)
|
self.update_items_cache(all_items)
|
||||||
for item in all_items:
|
|
||||||
self.addItem(item)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def vals(self):
|
def vals(self):
|
||||||
|
@ -125,8 +125,6 @@ class TextDelegate(QStyledItemDelegate): # {{{
|
|||||||
editor.set_separator(None)
|
editor.set_separator(None)
|
||||||
complete_items = [i[1] for i in self.auto_complete_function()]
|
complete_items = [i[1] for i in self.auto_complete_function()]
|
||||||
editor.update_items_cache(complete_items)
|
editor.update_items_cache(complete_items)
|
||||||
for item in sorted(complete_items, key=sort_key):
|
|
||||||
editor.addItem(item)
|
|
||||||
ct = index.data(Qt.DisplayRole).toString()
|
ct = index.data(Qt.DisplayRole).toString()
|
||||||
editor.show_initial_value(ct)
|
editor.show_initial_value(ct)
|
||||||
else:
|
else:
|
||||||
@ -166,8 +164,6 @@ class CompleteDelegate(QStyledItemDelegate): # {{{
|
|||||||
all_items = list(self.db.all_custom(
|
all_items = list(self.db.all_custom(
|
||||||
label=self.db.field_metadata.key_to_label(col)))
|
label=self.db.field_metadata.key_to_label(col)))
|
||||||
editor.update_items_cache(all_items)
|
editor.update_items_cache(all_items)
|
||||||
for item in sorted(all_items, key=sort_key):
|
|
||||||
editor.addItem(item)
|
|
||||||
ct = index.data(Qt.DisplayRole).toString()
|
ct = index.data(Qt.DisplayRole).toString()
|
||||||
editor.show_initial_value(ct)
|
editor.show_initial_value(ct)
|
||||||
else:
|
else:
|
||||||
|
@ -846,7 +846,9 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
s_index = float(match.group(1))
|
s_index = float(match.group(1))
|
||||||
val = pat.sub('', val).strip()
|
val = pat.sub('', val).strip()
|
||||||
elif val:
|
elif val:
|
||||||
if tweaks['series_index_auto_increment'] != 'const':
|
# it is OK to leave s_index == None when using 'no_change'
|
||||||
|
if tweaks['series_index_auto_increment'] != 'const' and \
|
||||||
|
tweaks['series_index_auto_increment'] != 'no_change':
|
||||||
s_index = self.db.get_next_cc_series_num_for(val,
|
s_index = self.db.get_next_cc_series_num_for(val,
|
||||||
label=label, num=None)
|
label=label, num=None)
|
||||||
elif typ == 'composite':
|
elif typ == 'composite':
|
||||||
@ -915,7 +917,8 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.db.set_series_index(id, float(match.group(1)))
|
self.db.set_series_index(id, float(match.group(1)))
|
||||||
val = pat.sub('', val).strip()
|
val = pat.sub('', val).strip()
|
||||||
elif val:
|
elif val:
|
||||||
if tweaks['series_index_auto_increment'] != 'const':
|
if tweaks['series_index_auto_increment'] != 'const' and \
|
||||||
|
tweaks['series_index_auto_increment'] != 'no_change':
|
||||||
ni = self.db.get_next_series_num_for(val)
|
ni = self.db.get_next_series_num_for(val)
|
||||||
if ni != 1:
|
if ni != 1:
|
||||||
self.db.set_series_index(id, ni)
|
self.db.set_series_index(id, ni)
|
||||||
|
@ -246,14 +246,6 @@ class AuthorsEdit(MultiCompleteComboBox):
|
|||||||
|
|
||||||
def initialize(self, db, id_):
|
def initialize(self, db, id_):
|
||||||
self.books_to_refresh = set([])
|
self.books_to_refresh = set([])
|
||||||
all_authors = db.all_authors()
|
|
||||||
all_authors.sort(key=lambda x : sort_key(x[1]))
|
|
||||||
self.clear()
|
|
||||||
for i in all_authors:
|
|
||||||
id, name = i
|
|
||||||
name = name.strip().replace('|', ',')
|
|
||||||
self.addItem(name)
|
|
||||||
|
|
||||||
self.set_separator('&')
|
self.set_separator('&')
|
||||||
self.set_space_before_sep(True)
|
self.set_space_before_sep(True)
|
||||||
self.set_add_separator(tweaks['authors_completer_append_separator'])
|
self.set_add_separator(tweaks['authors_completer_append_separator'])
|
||||||
@ -299,7 +291,6 @@ class AuthorsEdit(MultiCompleteComboBox):
|
|||||||
self.setEditText(' & '.join([x.strip() for x in val]))
|
self.setEditText(' & '.join([x.strip() for x in val]))
|
||||||
self.lineEdit().setCursorPosition(0)
|
self.lineEdit().setCursorPosition(0)
|
||||||
|
|
||||||
|
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
def break_cycles(self):
|
def break_cycles(self):
|
||||||
@ -488,19 +479,12 @@ class SeriesEdit(MultiCompleteComboBox):
|
|||||||
all_series.sort(key=lambda x : sort_key(x[1]))
|
all_series.sort(key=lambda x : sort_key(x[1]))
|
||||||
self.update_items_cache([x[1] for x in all_series])
|
self.update_items_cache([x[1] for x in all_series])
|
||||||
series_id = db.series_id(id_, index_is_id=True)
|
series_id = db.series_id(id_, index_is_id=True)
|
||||||
idx, c = None, 0
|
inval = ''
|
||||||
self.clear()
|
|
||||||
for i in all_series:
|
for i in all_series:
|
||||||
id, name = i
|
if i[0] == series_id:
|
||||||
if id == series_id:
|
inval = i[1]
|
||||||
idx = c
|
break
|
||||||
self.addItem(name)
|
self.original_val = self.current_val = inval
|
||||||
c += 1
|
|
||||||
|
|
||||||
self.lineEdit().setText('')
|
|
||||||
if idx is not None:
|
|
||||||
self.setCurrentIndex(idx)
|
|
||||||
self.original_val = self.current_val
|
|
||||||
|
|
||||||
def commit(self, db, id_):
|
def commit(self, db, id_):
|
||||||
series = self.current_val
|
series = self.current_val
|
||||||
@ -560,7 +544,7 @@ class SeriesIndexEdit(QDoubleSpinBox):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def increment(self):
|
def increment(self):
|
||||||
if self.db is not None:
|
if tweaks['series_index_auto_increment'] != 'no_change' and self.db is not None:
|
||||||
try:
|
try:
|
||||||
series = self.series_edit.current_val
|
series = self.series_edit.current_val
|
||||||
if series and series != self.original_series_name:
|
if series and series != self.original_series_name:
|
||||||
@ -1373,17 +1357,12 @@ class PublisherEdit(MultiCompleteComboBox): # {{{
|
|||||||
all_publishers.sort(key=lambda x : sort_key(x[1]))
|
all_publishers.sort(key=lambda x : sort_key(x[1]))
|
||||||
self.update_items_cache([x[1] for x in all_publishers])
|
self.update_items_cache([x[1] for x in all_publishers])
|
||||||
publisher_id = db.publisher_id(id_, index_is_id=True)
|
publisher_id = db.publisher_id(id_, index_is_id=True)
|
||||||
idx = None
|
inval = ''
|
||||||
self.clear()
|
for pid, name in all_publishers:
|
||||||
for i, x in enumerate(all_publishers):
|
if pid == publisher_id:
|
||||||
id_, name = x
|
inval = name
|
||||||
if id_ == publisher_id:
|
break
|
||||||
idx = i
|
self.original_val = self.current_val = inval
|
||||||
self.addItem(name)
|
|
||||||
|
|
||||||
self.setEditText('')
|
|
||||||
if idx is not None:
|
|
||||||
self.setCurrentIndex(idx)
|
|
||||||
|
|
||||||
def commit(self, db, id_):
|
def commit(self, db, id_):
|
||||||
self.books_to_refresh |= db.set_publisher(id_, self.current_val,
|
self.books_to_refresh |= db.set_publisher(id_, self.current_val,
|
||||||
|
@ -64,11 +64,11 @@ class EbookscomStore(BasicStoreConfig, StorePlugin):
|
|||||||
continue
|
continue
|
||||||
id = mo.group()
|
id = mo.group()
|
||||||
|
|
||||||
cover_url = ''.join(data.xpath('.//div[@class="img"]//img/@src'))
|
cover_url = ''.join(data.xpath('.//div[contains(@class, "img")]//img/@src'))
|
||||||
|
|
||||||
title = ''.join(data.xpath(
|
title = ''.join(data.xpath(
|
||||||
'descendant::span[@class="book-title"]/a/text()')).strip()
|
'descendant::span[@class="book-title"]/a/text()')).strip()
|
||||||
author = ''.join(data.xpath(
|
author = ', '.join(data.xpath(
|
||||||
'descendant::span[@class="author"]/a/text()')).strip()
|
'descendant::span[@class="author"]/a/text()')).strip()
|
||||||
if not title or not author:
|
if not title or not author:
|
||||||
continue
|
continue
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
|
||||||
|
|
||||||
import urllib
|
|
||||||
from contextlib import closing
|
|
||||||
|
|
||||||
from lxml import html
|
|
||||||
|
|
||||||
from PyQt4.Qt import QUrl
|
|
||||||
|
|
||||||
from calibre import browser, url_slash_cleaner
|
|
||||||
from calibre.gui2 import open_url
|
|
||||||
from calibre.gui2.store import StorePlugin
|
|
||||||
from calibre.gui2.store.basic_config import BasicStoreConfig
|
|
||||||
from calibre.gui2.store.search_result import SearchResult
|
|
||||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
|
||||||
|
|
||||||
class OReillyStore(BasicStoreConfig, StorePlugin):
|
|
||||||
|
|
||||||
def open(self, parent=None, detail_item=None, external=False):
|
|
||||||
url = 'http://oreilly.com/ebooks/'
|
|
||||||
|
|
||||||
if external or self.config.get('open_external', False):
|
|
||||||
open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url)))
|
|
||||||
else:
|
|
||||||
d = WebStoreDialog(self.gui, url, parent, detail_item)
|
|
||||||
d.setWindowTitle(self.name)
|
|
||||||
d.set_tags(self.config.get('tags', ''))
|
|
||||||
d.exec_()
|
|
||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
|
||||||
url = 'http://search.oreilly.com/?t1=Books&t2=Format&t3=Ebook&q=' + urllib.quote_plus(query)
|
|
||||||
|
|
||||||
br = browser()
|
|
||||||
|
|
||||||
counter = max_results
|
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
|
||||||
doc = html.fromstring(f.read())
|
|
||||||
for data in doc.xpath('//div[@class="result"]'):
|
|
||||||
if counter <= 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
ebook = ' '.join(data.xpath('.//p[@class="note"]/text()'))
|
|
||||||
if 'ebook' not in ebook.lower():
|
|
||||||
continue
|
|
||||||
|
|
||||||
id = ''.join(data.xpath('./div[@class="book_text"]//p[@class="title"]/a/@href'))
|
|
||||||
|
|
||||||
cover_url = ''.join(data.xpath('./a/img[1]/@src'))
|
|
||||||
|
|
||||||
title = ''.join(data.xpath('./div[@class="book_text"]/p[@class="title"]/a/text()'))
|
|
||||||
author = ''.join(data.xpath('./div[@class="book_text"]/p[@class="note"][1]/text()'))
|
|
||||||
author = author.split('By ')[-1].strip()
|
|
||||||
|
|
||||||
# Get the detail here because we need to get the ebook id for the detail_item.
|
|
||||||
with closing(br.open(id, timeout=timeout)) as nf:
|
|
||||||
idoc = html.fromstring(nf.read())
|
|
||||||
|
|
||||||
for td in idoc.xpath('//td[@class="optionsTd"]'):
|
|
||||||
if 'ebook' in ''.join(td.xpath('.//text()')).lower():
|
|
||||||
price = ''.join(td.xpath('.//span[@class="price"]/text()')).strip()
|
|
||||||
formats = ''.join(td.xpath('.//a[@id="availableFormats"]/text()')).strip()
|
|
||||||
break
|
|
||||||
|
|
||||||
counter -= 1
|
|
||||||
|
|
||||||
s = SearchResult()
|
|
||||||
s.cover_url = cover_url.strip()
|
|
||||||
s.title = title.strip()
|
|
||||||
s.author = author.strip()
|
|
||||||
s.detail_item = id.strip()
|
|
||||||
s.price = price.strip()
|
|
||||||
s.drm = SearchResult.DRM_UNLOCKED
|
|
||||||
s.formats = formats.upper()
|
|
||||||
|
|
||||||
yield s
|
|
@ -46,30 +46,37 @@ class OzonRUStore(BasicStoreConfig, StorePlugin):
|
|||||||
d.set_tags(self.config.get('tags', ''))
|
d.set_tags(self.config.get('tags', ''))
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
|
||||||
|
def search(self, query, max_results=15, timeout=60):
|
||||||
def search(self, query, max_results=10, timeout=60):
|
|
||||||
search_url = self.shop_url + '/webservice/webservice.asmx/SearchWebService?'\
|
search_url = self.shop_url + '/webservice/webservice.asmx/SearchWebService?'\
|
||||||
'searchText=%s&searchContext=ebook' % urllib2.quote(query)
|
'searchText=%s&searchContext=ebook' % urllib2.quote(query)
|
||||||
|
search_urls = [ search_url ]
|
||||||
|
|
||||||
|
## add this as the fist try if it looks like ozon ID
|
||||||
|
if re.match("^\d{6,9}$", query):
|
||||||
|
ozon_detail = self.shop_url + '/webservices/OzonWebSvc.asmx/ItemDetail?ID=%s' % query
|
||||||
|
search_urls.insert(0, ozon_detail)
|
||||||
|
|
||||||
xp_template = 'normalize-space(./*[local-name() = "{0}"]/text())'
|
xp_template = 'normalize-space(./*[local-name() = "{0}"]/text())'
|
||||||
|
|
||||||
counter = max_results
|
counter = max_results
|
||||||
br = browser()
|
br = browser()
|
||||||
with closing(br.open(search_url, timeout=timeout)) as f:
|
|
||||||
raw = xml_to_unicode(f.read(), strip_encoding_pats=True, assume_utf8=True)[0]
|
for url in search_urls:
|
||||||
doc = etree.fromstring(raw)
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
for data in doc.xpath('//*[local-name() = "SearchItems"]'):
|
raw = xml_to_unicode(f.read(), strip_encoding_pats=True, assume_utf8=True)[0]
|
||||||
if counter <= 0:
|
doc = etree.fromstring(raw)
|
||||||
break
|
for data in doc.xpath('//*[local-name()="SearchItems" or local-name()="ItemDetail"]'):
|
||||||
counter -= 1
|
if counter <= 0:
|
||||||
|
break
|
||||||
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
s.detail_item = data.xpath(xp_template.format('ID'))
|
s.detail_item = data.xpath(xp_template.format('ID'))
|
||||||
s.title = data.xpath(xp_template.format('Name'))
|
s.title = data.xpath(xp_template.format('Name'))
|
||||||
s.author = data.xpath(xp_template.format('Author'))
|
s.author = data.xpath(xp_template.format('Author'))
|
||||||
s.price = data.xpath(xp_template.format('Price'))
|
s.price = data.xpath(xp_template.format('Price'))
|
||||||
s.cover_url = data.xpath(xp_template.format('Picture'))
|
s.cover_url = data.xpath(xp_template.format('Picture'))
|
||||||
s.price = format_price_in_RUR(s.price)
|
s.price = format_price_in_RUR(s.price)
|
||||||
yield s
|
yield s
|
||||||
|
|
||||||
def get_details(self, search_result, timeout=60):
|
def get_details(self, search_result, timeout=60):
|
||||||
url = self.shop_url + '/context/detail/id/' + urllib2.quote(search_result.detail_item)
|
url = self.shop_url + '/context/detail/id/' + urllib2.quote(search_result.detail_item)
|
||||||
@ -97,6 +104,16 @@ class OzonRUStore(BasicStoreConfig, StorePlugin):
|
|||||||
search_result.formats = ', '.join(_parse_ebook_formats(formats))
|
search_result.formats = ', '.join(_parse_ebook_formats(formats))
|
||||||
# unfortunately no direct links to download books (only buy link)
|
# unfortunately no direct links to download books (only buy link)
|
||||||
# search_result.downloads['BF2'] = self.shop_url + '/order/digitalorder.aspx?id=' + + urllib2.quote(search_result.detail_item)
|
# search_result.downloads['BF2'] = self.shop_url + '/order/digitalorder.aspx?id=' + + urllib2.quote(search_result.detail_item)
|
||||||
|
|
||||||
|
#<p class="main-cost"><span class="main">215</span><span class="submain">00</span> руб.</p>
|
||||||
|
#<span itemprop="price" class="hidden">215.00</span>
|
||||||
|
#<meta itemprop="priceCurrency" content="RUR " />
|
||||||
|
|
||||||
|
# if the price not in the search result (the ID search case)
|
||||||
|
if not search_result.price:
|
||||||
|
price = doc.xpath(u'normalize-space(//*[@itemprop="price"]/text())')
|
||||||
|
search_result.price = format_price_in_RUR(price)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def format_price_in_RUR(price):
|
def format_price_in_RUR(price):
|
||||||
|
@ -41,7 +41,7 @@ class WeightlessBooksStore(BasicStoreConfig, StorePlugin):
|
|||||||
counter = max_results
|
counter = max_results
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
doc = html.fromstring(f.read())
|
doc = html.fromstring(f.read())
|
||||||
for data in doc.xpath('//li[@id="product"]'):
|
for data in doc.xpath('//li[@class="product"]'):
|
||||||
if counter <= 0:
|
if counter <= 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -234,21 +234,27 @@ class Document(QWebPage): # {{{
|
|||||||
|
|
||||||
def switch_to_fullscreen_mode(self):
|
def switch_to_fullscreen_mode(self):
|
||||||
self.in_fullscreen_mode = True
|
self.in_fullscreen_mode = True
|
||||||
self.javascript('''
|
if self.in_paged_mode:
|
||||||
var s = document.body.style;
|
self.javascript('paged_display.max_col_width = %d'%self.max_fs_width)
|
||||||
s.maxWidth = "%dpx";
|
else:
|
||||||
s.marginLeft = "auto";
|
self.javascript('''
|
||||||
s.marginRight = "auto";
|
var s = document.body.style;
|
||||||
'''%self.max_fs_width)
|
s.maxWidth = "%dpx";
|
||||||
|
s.marginLeft = "auto";
|
||||||
|
s.marginRight = "auto";
|
||||||
|
'''%self.max_fs_width)
|
||||||
|
|
||||||
def switch_to_window_mode(self):
|
def switch_to_window_mode(self):
|
||||||
self.in_fullscreen_mode = False
|
self.in_fullscreen_mode = False
|
||||||
self.javascript('''
|
if self.in_paged_mode:
|
||||||
var s = document.body.style;
|
self.javascript('paged_display.max_col_width = %d'%-1)
|
||||||
s.maxWidth = "none";
|
else:
|
||||||
s.marginLeft = "%s";
|
self.javascript('''
|
||||||
s.marginRight = "%s";
|
var s = document.body.style;
|
||||||
'''%(self.initial_left_margin, self.initial_right_margin))
|
s.maxWidth = "none";
|
||||||
|
s.marginLeft = "%s";
|
||||||
|
s.marginRight = "%s";
|
||||||
|
'''%(self.initial_left_margin, self.initial_right_margin))
|
||||||
|
|
||||||
@pyqtSignature("QString")
|
@pyqtSignature("QString")
|
||||||
def debug(self, msg):
|
def debug(self, msg):
|
||||||
|
@ -477,6 +477,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
else:
|
else:
|
||||||
self.view.document.switch_to_window_mode()
|
self.view.document.switch_to_window_mode()
|
||||||
self.view.document.page_position.restore()
|
self.view.document.page_position.restore()
|
||||||
|
self.scrolled(self.view.scroll_fraction)
|
||||||
|
|
||||||
def goto(self, ref):
|
def goto(self, ref):
|
||||||
if ref:
|
if ref:
|
||||||
@ -754,12 +755,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
# There hasn't been a resize event for some time
|
# There hasn't been a resize event for some time
|
||||||
# restore the current page position.
|
# restore the current page position.
|
||||||
self.resize_in_progress = False
|
self.resize_in_progress = False
|
||||||
self.view.document.after_resize()
|
|
||||||
if self.window_mode_changed:
|
if self.window_mode_changed:
|
||||||
# This resize is part of a window mode change, special case it
|
# This resize is part of a window mode change, special case it
|
||||||
self.handle_window_mode_toggle()
|
self.handle_window_mode_toggle()
|
||||||
else:
|
else:
|
||||||
self.view.document.page_position.restore()
|
self.view.document.page_position.restore()
|
||||||
|
self.view.document.after_resize()
|
||||||
|
|
||||||
def close_progress_indicator(self):
|
def close_progress_indicator(self):
|
||||||
self.pi.stop()
|
self.pi.stop()
|
||||||
|
@ -829,7 +829,9 @@ def parse_series_string(db, label, value):
|
|||||||
val = pat.sub('', val).strip()
|
val = pat.sub('', val).strip()
|
||||||
s_index = float(match.group(1))
|
s_index = float(match.group(1))
|
||||||
elif val:
|
elif val:
|
||||||
if tweaks['series_index_auto_increment'] != 'const':
|
if tweaks['series_index_auto_increment'] == 'no_change':
|
||||||
|
pass
|
||||||
|
elif tweaks['series_index_auto_increment'] != 'const':
|
||||||
s_index = db.get_next_cc_series_num_for(val, label=label)
|
s_index = db.get_next_cc_series_num_for(val, label=label)
|
||||||
else:
|
else:
|
||||||
s_index = 1.0
|
s_index = 1.0
|
||||||
|
@ -1203,7 +1203,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
if m:
|
if m:
|
||||||
return m['mtime']
|
return m['mtime']
|
||||||
|
|
||||||
def format_metadata(self, id_, fmt, allow_cache=True):
|
def format_metadata(self, id_, fmt, allow_cache=True, update_db=False,
|
||||||
|
commit=False):
|
||||||
if not fmt:
|
if not fmt:
|
||||||
return {}
|
return {}
|
||||||
fmt = fmt.upper()
|
fmt = fmt.upper()
|
||||||
@ -1218,6 +1219,12 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
ans['size'] = stat.st_size
|
ans['size'] = stat.st_size
|
||||||
ans['mtime'] = utcfromtimestamp(stat.st_mtime)
|
ans['mtime'] = utcfromtimestamp(stat.st_mtime)
|
||||||
self.format_metadata_cache[id_][fmt] = ans
|
self.format_metadata_cache[id_][fmt] = ans
|
||||||
|
if update_db:
|
||||||
|
self.conn.execute(
|
||||||
|
'UPDATE data SET uncompressed_size=? WHERE format=? AND'
|
||||||
|
' book=?', (stat.st_size, fmt, id_))
|
||||||
|
if commit:
|
||||||
|
self.conn.commit()
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def format_hash(self, id_, fmt):
|
def format_hash(self, id_, fmt):
|
||||||
@ -1448,6 +1455,24 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
if notify:
|
if notify:
|
||||||
self.notify('metadata', [id])
|
self.notify('metadata', [id])
|
||||||
|
|
||||||
|
def clean_standard_field(self, field, commit=False):
|
||||||
|
# Don't bother with validity checking. Let the exception fly out so
|
||||||
|
# we can see what happened
|
||||||
|
def doit(table, ltable_col):
|
||||||
|
st = ('DELETE FROM books_%s_link WHERE (SELECT COUNT(id) '
|
||||||
|
'FROM books WHERE id=book) < 1;')%table
|
||||||
|
self.conn.execute(st)
|
||||||
|
st = ('DELETE FROM %(table)s WHERE (SELECT COUNT(id) '
|
||||||
|
'FROM books_%(table)s_link WHERE '
|
||||||
|
'%(ltable_col)s=%(table)s.id) < 1;') % dict(
|
||||||
|
table=table, ltable_col=ltable_col)
|
||||||
|
self.conn.execute(st)
|
||||||
|
|
||||||
|
fm = self.field_metadata[field]
|
||||||
|
doit(fm['table'], fm['link_column'])
|
||||||
|
if commit:
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
'''
|
'''
|
||||||
Remove orphaned entries.
|
Remove orphaned entries.
|
||||||
@ -2550,6 +2575,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
self.set_tags(book_id, new_names, append=True, notify=False,
|
self.set_tags(book_id, new_names, append=True, notify=False,
|
||||||
commit=False)
|
commit=False)
|
||||||
self.dirtied(books, commit=False)
|
self.dirtied(books, commit=False)
|
||||||
|
self.clean_standard_field('tags', commit=False)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
def delete_tag_using_id(self, id):
|
def delete_tag_using_id(self, id):
|
||||||
@ -2564,7 +2590,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
return []
|
return []
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def rename_series(self, old_id, new_name):
|
def rename_series(self, old_id, new_name, change_index=True):
|
||||||
new_name = new_name.strip()
|
new_name = new_name.strip()
|
||||||
new_id = self.conn.get(
|
new_id = self.conn.get(
|
||||||
'''SELECT id from series
|
'''SELECT id from series
|
||||||
@ -2577,23 +2603,26 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
# New series exists. Must update the link, then assign a
|
# New series exists. Must update the link, then assign a
|
||||||
# new series index to each of the books.
|
# new series index to each of the books.
|
||||||
|
|
||||||
# Get the list of books where we must update the series index
|
if change_index:
|
||||||
books = self.conn.get('''SELECT books.id
|
# Get the list of books where we must update the series index
|
||||||
FROM books, books_series_link as lt
|
books = self.conn.get('''SELECT books.id
|
||||||
WHERE books.id = lt.book AND lt.series=?
|
FROM books, books_series_link as lt
|
||||||
ORDER BY books.series_index''', (old_id,))
|
WHERE books.id = lt.book AND lt.series=?
|
||||||
|
ORDER BY books.series_index''', (old_id,))
|
||||||
# Now update the link table
|
# Now update the link table
|
||||||
self.conn.execute('''UPDATE books_series_link
|
self.conn.execute('''UPDATE books_series_link
|
||||||
SET series=?
|
SET series=?
|
||||||
WHERE series=?''',(new_id, old_id,))
|
WHERE series=?''',(new_id, old_id,))
|
||||||
# Now set the indices
|
if change_index and tweaks['series_index_auto_increment'] != 'no_change':
|
||||||
for (book_id,) in books:
|
# Now set the indices
|
||||||
# Get the next series index
|
for (book_id,) in books:
|
||||||
index = self.get_next_series_num_for(new_name)
|
# Get the next series index
|
||||||
self.conn.execute('''UPDATE books
|
index = self.get_next_series_num_for(new_name)
|
||||||
SET series_index=?
|
self.conn.execute('''UPDATE books
|
||||||
WHERE id=?''',(index, book_id,))
|
SET series_index=?
|
||||||
|
WHERE id=?''',(index, book_id,))
|
||||||
self.dirty_books_referencing('series', new_id, commit=False)
|
self.dirty_books_referencing('series', new_id, commit=False)
|
||||||
|
self.clean_standard_field('series', commit=False)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
def delete_series_using_id(self, id):
|
def delete_series_using_id(self, id):
|
||||||
@ -2629,6 +2658,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
# Get rid of the no-longer used publisher
|
# Get rid of the no-longer used publisher
|
||||||
self.conn.execute('DELETE FROM publishers WHERE id=?', (old_id,))
|
self.conn.execute('DELETE FROM publishers WHERE id=?', (old_id,))
|
||||||
self.dirty_books_referencing('publisher', new_id, commit=False)
|
self.dirty_books_referencing('publisher', new_id, commit=False)
|
||||||
|
self.clean_standard_field('publisher', commit=False)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
def delete_publisher_using_id(self, old_id):
|
def delete_publisher_using_id(self, old_id):
|
||||||
@ -2727,7 +2757,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
# metadata. Ignore it.
|
# metadata. Ignore it.
|
||||||
pass
|
pass
|
||||||
# Now delete the old author from the DB
|
# Now delete the old author from the DB
|
||||||
bks = self.conn.get('SELECT book FROM books_authors_link WHERE author=?', (old_id,))
|
|
||||||
self.conn.execute('DELETE FROM authors WHERE id=?', (old_id,))
|
self.conn.execute('DELETE FROM authors WHERE id=?', (old_id,))
|
||||||
self.dirtied(books, commit=False)
|
self.dirtied(books, commit=False)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
@ -3684,4 +3713,12 @@ books_series_link feeds
|
|||||||
s = self.conn.get('''SELECT book FROM books_plugin_data WHERE name=?''', (name,))
|
s = self.conn.get('''SELECT book FROM books_plugin_data WHERE name=?''', (name,))
|
||||||
return [x[0] for x in s]
|
return [x[0] for x in s]
|
||||||
|
|
||||||
|
def get_usage_count_by_id(self, field):
|
||||||
|
fm = self.field_metadata[field]
|
||||||
|
if not fm.get('link_column', None):
|
||||||
|
raise ValueError('%s is not an is_multiple field')
|
||||||
|
return self.conn.get(
|
||||||
|
'SELECT {0}, count(*) FROM books_{1}_link GROUP BY {0}'.format(
|
||||||
|
fm['link_column'], fm['table']))
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user