Sync to trunk.

This commit is contained in:
John Schember 2011-05-24 19:40:30 -04:00
commit 5b0f764dd6
8 changed files with 89 additions and 34 deletions

View File

@ -3,10 +3,12 @@
"divide": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x / y)\n", "divide": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x / y)\n",
"uppercase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return val.upper()\n", "uppercase": "def evaluate(self, formatter, kwargs, mi, locals, val):\n return val.upper()\n",
"strcat": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n res = ''\n for i in range(0, len(args)):\n res += args[i]\n return res\n", "strcat": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n res = ''\n for i in range(0, len(args)):\n res += args[i]\n return res\n",
"in_list": "def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):\n l = [v.strip() for v in val.split(sep) if v.strip()]\n for v in l:\n if re.search(pat, v):\n return fv\n return nfv\n",
"substr": "def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_):\n return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)]\n", "substr": "def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_):\n return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)]\n",
"ifempty": "def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):\n if val:\n return val\n else:\n return value_if_empty\n", "ifempty": "def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):\n if val:\n return val\n else:\n return value_if_empty\n",
"booksize": "def evaluate(self, formatter, kwargs, mi, locals):\n if mi.book_size is not None:\n try:\n return str(mi.book_size)\n except:\n pass\n return ''\n", "booksize": "def evaluate(self, formatter, kwargs, mi, locals):\n if mi.book_size is not None:\n try:\n return str(mi.book_size)\n except:\n pass\n return ''\n",
"select": "def evaluate(self, formatter, kwargs, mi, locals, val, key):\n if not val:\n return ''\n vals = [v.strip() for v in val.split(',')]\n for v in vals:\n if v.startswith(key+':'):\n return v[len(key)+1:]\n return ''\n", "select": "def evaluate(self, formatter, kwargs, mi, locals, val, key):\n if not val:\n return ''\n vals = [v.strip() for v in val.split(',')]\n for v in vals:\n if v.startswith(key+':'):\n return v[len(key)+1:]\n return ''\n",
"first_non_empty": "def evaluate(self, formatter, kwargs, mi, locals, *args):\n i = 0\n while i < len(args):\n if args[i]:\n return args[i]\n i += 1\n return ''\n",
"field": "def evaluate(self, formatter, kwargs, mi, locals, name):\n return formatter.get_value(name, [], kwargs)\n", "field": "def evaluate(self, formatter, kwargs, mi, locals, name):\n return formatter.get_value(name, [], kwargs)\n",
"subtract": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x - y)\n", "subtract": "def evaluate(self, formatter, kwargs, mi, locals, x, y):\n x = float(x if x else 0)\n y = float(y if y else 0)\n return unicode(x - y)\n",
"list_item": "def evaluate(self, formatter, kwargs, mi, locals, val, index, sep):\n if not val:\n return ''\n index = int(index)\n val = val.split(sep)\n try:\n return val[index]\n except:\n return ''\n", "list_item": "def evaluate(self, formatter, kwargs, mi, locals, val, index, sep):\n if not val:\n return ''\n index = int(index)\n val = val.split(sep)\n try:\n return val[index]\n except:\n return ''\n",

View File

@ -413,6 +413,13 @@ class EPUBOutput(OutputFormatPlugin):
rule.style.removeProperty('margin-left') rule.style.removeProperty('margin-left')
# padding-left breaks rendering in webkit and gecko # padding-left breaks rendering in webkit and gecko
rule.style.removeProperty('padding-left') rule.style.removeProperty('padding-left')
# Change whitespace:pre to pre-line to accommodate readers that
# cannot scroll horizontally
for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
style = rule.style
ws = style.getPropertyValue('white-space')
if ws == 'pre':
style.setProperty('white-space', 'pre-wrap')
# }}} # }}}

View File

@ -29,7 +29,7 @@ class Worker(Thread): # Get details {{{
Get book details from amazons book page in a separate thread Get book details from amazons book page in a separate thread
''' '''
def __init__(self, url, result_queue, browser, log, relevance, plugin, timeout=20): def __init__(self, url, result_queue, browser, log, relevance, domain, plugin, timeout=20):
Thread.__init__(self) Thread.__init__(self)
self.daemon = True self.daemon = True
self.url, self.result_queue = url, result_queue self.url, self.result_queue = url, result_queue
@ -37,7 +37,7 @@ class Worker(Thread): # Get details {{{
self.relevance, self.plugin = relevance, plugin self.relevance, self.plugin = relevance, plugin
self.browser = browser.clone_browser() self.browser = browser.clone_browser()
self.cover_url = self.amazon_id = self.isbn = None self.cover_url = self.amazon_id = self.isbn = None
self.domain = self.plugin.domain self.domain = domain
months = { months = {
'de': { 'de': {
@ -199,7 +199,8 @@ class Worker(Thread): # Get details {{{
return return
mi = Metadata(title, authors) mi = Metadata(title, authors)
mi.set_identifier('amazon', asin) idtype = 'amazon' if self.domain == 'com' else 'amazon_'+self.domain
mi.set_identifier(idtype, asin)
self.amazon_id = asin self.amazon_id = asin
try: try:
@ -404,12 +405,30 @@ class Amazon(Source):
'country\'s Amazon website.'), choices=AMAZON_DOMAINS), 'country\'s Amazon website.'), choices=AMAZON_DOMAINS),
) )
def get_domain_and_asin(self, identifiers):
for key, val in identifiers.iteritems():
key = key.lower()
if key in ('amazon', 'asin'):
return 'com', val
if key.startswith('amazon_'):
domain = key.split('_')[-1]
if domain and domain in self.AMAZON_DOMAINS:
return domain, val
return None, None
def get_book_url(self, identifiers): # {{{ def get_book_url(self, identifiers): # {{{
asin = identifiers.get('amazon', None) domain, asin = self.get_domain_and_asin(identifiers)
if asin is None: if domain and asin:
asin = identifiers.get('asin', None) url = None
if asin: if domain == 'com':
return ('amazon', asin, 'http://amzn.com/%s'%asin) url = 'http://amzn.com/'+asin
elif domain == 'uk':
url = 'http://www.amazon.co.uk/dp/'+asin
else:
url = 'http://www.amazon.%s/dp/%s'%(domain, asin)
if url:
idtype = 'amazon' if self.domain == 'com' else 'amazon_'+self.domain
return (idtype, asin, url)
# }}} # }}}
@property @property
@ -420,8 +439,14 @@ class Amazon(Source):
return domain return domain
def create_query(self, log, title=None, authors=None, identifiers={}): # {{{ def create_query(self, log, title=None, authors=None, identifiers={}, # {{{
domain = self.domain domain=None):
if domain is None:
domain = self.domain
idomain, asin = self.get_domain_and_asin(identifiers)
if idomain is not None:
domain = idomain
# See the amazon detailed search page to get all options # See the amazon detailed search page to get all options
q = { 'search-alias' : 'aps', q = { 'search-alias' : 'aps',
@ -433,7 +458,6 @@ class Amazon(Source):
else: else:
q['sort'] = 'relevancerank' q['sort'] = 'relevancerank'
asin = identifiers.get('amazon', None)
isbn = check_isbn(identifiers.get('isbn', None)) isbn = check_isbn(identifiers.get('isbn', None))
if asin is not None: if asin is not None:
@ -456,23 +480,22 @@ class Amazon(Source):
if not ('field-keywords' in q or 'field-isbn' in q or if not ('field-keywords' in q or 'field-isbn' in q or
('field-title' in q)): ('field-title' in q)):
# Insufficient metadata to make an identify query # Insufficient metadata to make an identify query
return None return None, None
latin1q = dict([(x.encode('latin1', 'ignore'), y.encode('latin1', latin1q = dict([(x.encode('latin1', 'ignore'), y.encode('latin1',
'ignore')) for x, y in 'ignore')) for x, y in
q.iteritems()]) q.iteritems()])
udomain = domain
if domain == 'uk': if domain == 'uk':
domain = 'co.uk' udomain = 'co.uk'
url = 'http://www.amazon.%s/s/?'%domain + urlencode(latin1q) url = 'http://www.amazon.%s/s/?'%udomain + urlencode(latin1q)
return url return url, domain
# }}} # }}}
def get_cached_cover_url(self, identifiers): # {{{ def get_cached_cover_url(self, identifiers): # {{{
url = None url = None
asin = identifiers.get('amazon', None) domain, asin = self.get_domain_and_asin(identifiers)
if asin is None:
asin = identifiers.get('asin', None)
if asin is None: if asin is None:
isbn = identifiers.get('isbn', None) isbn = identifiers.get('isbn', None)
if isbn is not None: if isbn is not None:
@ -489,7 +512,7 @@ class Amazon(Source):
Note this method will retry without identifiers automatically if no Note this method will retry without identifiers automatically if no
match is found with identifiers. match is found with identifiers.
''' '''
query = self.create_query(log, title=title, authors=authors, query, domain = self.create_query(log, title=title, authors=authors,
identifiers=identifiers) identifiers=identifiers)
if query is None: if query is None:
log.error('Insufficient metadata to construct query') log.error('Insufficient metadata to construct query')
@ -571,7 +594,7 @@ class Amazon(Source):
log.error('No matches found with query: %r'%query) log.error('No matches found with query: %r'%query)
return return
workers = [Worker(url, result_queue, br, log, i, self) for i, url in workers = [Worker(url, result_queue, br, log, i, domain, self) for i, url in
enumerate(matches)] enumerate(matches)]
for w in workers: for w in workers:

View File

@ -9,7 +9,7 @@ from PyQt4.Qt import (QLineEdit, QDialog, QGridLayout, QLabel,
QDialogButtonBox, QColor, QComboBox, QIcon) QDialogButtonBox, QColor, QComboBox, QIcon)
from calibre.gui2.dialogs.template_dialog import TemplateDialog from calibre.gui2.dialogs.template_dialog import TemplateDialog
from calibre.gui2.complete import MultiCompleteComboBox from calibre.gui2.complete import MultiCompleteLineEdit
from calibre.gui2 import error_dialog from calibre.gui2 import error_dialog
class TemplateLineEditor(QLineEdit): class TemplateLineEditor(QLineEdit):
@ -63,14 +63,16 @@ class TagWizard(QDialog):
self.tags = tags self.tags = tags
l = QGridLayout() l = QGridLayout()
self.setLayout(l) self.setLayout(l)
l.addWidget(QLabel(_('Tag Value')), 0, 0, 1, 1) l.setColumnStretch(0, 1)
l.setColumnMinimumWidth(0, 300)
l.addWidget(QLabel(_('Tags (more than one per box permitted)')), 0, 0, 1, 1)
l.addWidget(QLabel(_('Color')), 0, 1, 1, 1) l.addWidget(QLabel(_('Color')), 0, 1, 1, 1)
self.tagboxes = [] self.tagboxes = []
self.colorboxes = [] self.colorboxes = []
self.colors = [unicode(s) for s in list(QColor.colorNames())] self.colors = [unicode(s) for s in list(QColor.colorNames())]
self.colors.insert(0, '') self.colors.insert(0, '')
for i in range(0, 10): for i in range(0, 10):
tb = MultiCompleteComboBox(self) tb = MultiCompleteLineEdit(self)
tb.set_separator(', ') tb.set_separator(', ')
tb.update_items_cache(self.tags) tb.update_items_cache(self.tags)
self.tagboxes.append(tb) self.tagboxes.append(tb)
@ -101,10 +103,11 @@ class TagWizard(QDialog):
def accepted(self): def accepted(self):
res = ("program:\n#tag wizard -- do not directly edit\n" res = ("program:\n#tag wizard -- do not directly edit\n"
" t = field('tags');\n first_non_empty(\n") " t = field('tags');\n first_non_empty(\n")
lines = [] lines = []
for tb, cb in zip(self.tagboxes, self.colorboxes): for tb, cb in zip(self.tagboxes, self.colorboxes):
tags = [t.strip() for t in unicode(tb.currentText()).split(',') if t.strip()] tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()]
tags = '$|^'.join(tags)
c = unicode(cb.currentText()).strip() c = unicode(cb.currentText()).strip()
if not tags or not c: if not tags or not c:
continue continue
@ -113,14 +116,13 @@ class TagWizard(QDialog):
_('The color {0} is not valid').format(c), _('The color {0} is not valid').format(c),
show=True, show_copy_button=False) show=True, show_copy_button=False)
return False return False
for t in tags: lines.append(" in_list(t, ',', '^{0}$', '{1}', '')".format(tags, c))
lines.append(" in_list(t, ',', '^{0}$', '{1}', '')".format(t, c))
res += ',\n'.join(lines) res += ',\n'.join(lines)
res += ')\n' res += ')\n'
self.template = res self.template = res
res = '' res = ''
for tb, cb in zip(self.tagboxes, self.colorboxes): for tb, cb in zip(self.tagboxes, self.colorboxes):
t = unicode(tb.currentText()).strip() t = unicode(tb.text()).strip()
if t.endswith(','): if t.endswith(','):
t = t[:-1] t = t[:-1]
c = unicode(cb.currentText()).strip() c = unicode(cb.currentText()).strip()

View File

@ -167,8 +167,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
'<a href="http://calibre-ebook.com/user_manual/template_lang.html">' '<a href="http://calibre-ebook.com/user_manual/template_lang.html">'
'tutorial</a> on using templates.') + 'tutorial</a> on using templates.') +
'</p><p>' + '</p><p>' +
_('If you want to color a field based on tags, then right-click ' _('If you want to color a field based on tags, then click the '
'in an empty template line and choose tags wizard. ' 'button next to an empty line to open the tags wizard. '
'It will build a template for you. You can later edit that ' 'It will build a template for you. You can later edit that '
'template with the same wizard. If you edit it by hand, the ' 'template with the same wizard. If you edit it by hand, the '
'wizard might not work or might restore old values.') + 'wizard might not work or might restore old values.') +

View File

@ -442,6 +442,9 @@ then the tags will be displayed each on their own line.</string>
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset> <normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
</property> </property>
<property name="toolTip">
<string>Open the tags wizard.</string>
</property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
@ -456,6 +459,9 @@ then the tags will be displayed each on their own line.</string>
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset> <normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
</property> </property>
<property name="toolTip">
<string>Open the tags wizard.</string>
</property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="4" column="0">
@ -470,6 +476,9 @@ then the tags will be displayed each on their own line.</string>
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset> <normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
</property> </property>
<property name="toolTip">
<string>Open the tags wizard.</string>
</property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="5" column="0">
@ -484,6 +493,9 @@ then the tags will be displayed each on their own line.</string>
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset> <normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
</property> </property>
<property name="toolTip">
<string>Open the tags wizard.</string>
</property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="6" column="0">
@ -498,6 +510,9 @@ then the tags will be displayed each on their own line.</string>
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset> <normaloff>:/images/wizard.png</normaloff>:/images/wizard.png</iconset>
</property> </property>
<property name="toolTip">
<string>Open the tags wizard.</string>
</property>
</widget> </widget>
</item> </item>
<item row="20" column="0"> <item row="20" column="0">

View File

@ -221,7 +221,12 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
if not ip or ip.startswith('127.'): if not ip or ip.startswith('127.'):
raise raise
cherrypy.log('Trying to bind to single interface: '+ip) cherrypy.log('Trying to bind to single interface: '+ip)
# Change the host we listen on
cherrypy.config.update({'server.socket_host' : ip}) cherrypy.config.update({'server.socket_host' : ip})
# This ensures that the change is actually applied
cherrypy.server.socket_host = ip
cherrypy.server.httpserver = cherrypy.server.instance = None
cherrypy.engine.start() cherrypy.engine.start()
self.is_running = True self.is_running = True
@ -231,6 +236,8 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
cherrypy.engine.block() cherrypy.engine.block()
except Exception as e: except Exception as e:
self.exception = e self.exception = e
import traceback
traceback.print_exc()
finally: finally:
self.is_running = False self.is_running = False
try: try:

View File

@ -356,7 +356,7 @@ class PostInstall:
mimetypes = set([]) mimetypes = set([])
for x in all_input_formats(): for x in all_input_formats():
mt = guess_type('dummy.'+x)[0] mt = guess_type('dummy.'+x)[0]
if mt and 'chemical' not in mt: if mt and 'chemical' not in mt and 'ctc-posml' not in mt:
mimetypes.add(mt) mimetypes.add(mt)
def write_mimetypes(f): def write_mimetypes(f):
@ -376,11 +376,10 @@ class PostInstall:
des = ('calibre-gui.desktop', 'calibre-lrfviewer.desktop', des = ('calibre-gui.desktop', 'calibre-lrfviewer.desktop',
'calibre-ebook-viewer.desktop') 'calibre-ebook-viewer.desktop')
for x in des: for x in des:
cmd = ['xdg-desktop-menu', 'install', './'+x] cmd = ['xdg-desktop-menu', 'install', '--noupdate', './'+x]
if x != des[-1]:
cmd.insert(2, '--noupdate')
check_call(' '.join(cmd), shell=True) check_call(' '.join(cmd), shell=True)
self.menu_resources.append(x) self.menu_resources.append(x)
check_call(['xdg-desktop-menu', 'forceupdate'])
f = open('calibre-mimetypes', 'wb') f = open('calibre-mimetypes', 'wb')
f.write(MIME) f.write(MIME)
f.close() f.close()