merge from trunk

This commit is contained in:
Lee 2011-04-19 12:24:45 +08:00
commit a0cdf29379
14 changed files with 272 additions and 138 deletions

View File

@ -8,23 +8,36 @@ __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net> edited by Huan T'
from calibre.web.feeds.news import BasicNewsRecipe
class Slashdot(BasicNewsRecipe):
title = u'Slashdot.org'
description = '''Tech news. WARNING: This recipe downloads a lot
of content and may result in your IP being banned from slashdot.org'''
oldest_article = 7
simultaneous_downloads = 1
delay = 3
max_articles_per_feed = 100
language = 'en'
title = u'Slashdot.org'
description = '''Tech news. WARNING: This recipe downloads a lot
of content and may result in your IP being banned from slashdot.org'''
oldest_article = 7
simultaneous_downloads = 1
delay = 3
max_articles_per_feed = 100
language = 'en'
__author__ = 'floweros edited by Huan T'
no_stylesheets = True
# keep_only_tags = [
# dict(name='div',attrs={'class':'article'}),
# dict(name='div',attrs={'class':'commentTop'}),
# ]
__author__ = 'floweros edited by Huan T'
no_stylesheets = True
keep_only_tags = [
dict(name='div',attrs={'id':'article'}),
dict(name='div',attrs={'class':['postBody' 'details']}),
dict(name='footer',attrs={'class':['clearfix meta article-foot']}),
dict(name='article',attrs={'class':['fhitem fhitem-story article usermode thumbs grid_24']}),
dict(name='dl',attrs={'class':'relatedPosts'}),
dict(name='h2',attrs={'class':'story'}),
dict(name='span',attrs={'class':'comments'}),
]
feeds = [
remove_tags = [
dict(name='aside',attrs={'id':'slashboxes'}),
dict(name='div',attrs={'class':'paginate'}),
dict(name='section',attrs={'id':'comments'}),
dict(name='span',attrs={'class':'topic'}),
]
feeds = [
(u'Slashdot',
u'http://rss.slashdot.org/Slashdot/slashdot'),
(u'/. IT',
@ -37,5 +50,3 @@ class Slashdot(BasicNewsRecipe):
u'http://rss.slashdot.org/Slashdot/slashdotYourRightsOnline')
]
def get_article_url(self, article):
return article.get('feedburner_origlink', None)

View File

@ -48,7 +48,7 @@ authors_completer_append_separator = False
# When this tweak is changed, the author_sort values stored with each author
# must be recomputed by right-clicking on an author in the left-hand tags pane,
# selecting 'manage authors', and pressing 'Recalculate all author sort values'.
author_sort_copy_method = 'invert'
author_sort_copy_method = 'comma'
#: Use author sort in Tag Browser
# Set which author field to display in the tags pane (the list of authors,

View File

@ -108,10 +108,10 @@ class ANDROID(USBMS):
'SGH-T849', '_MB300', 'A70S', 'S_ANDROID', 'A101IT', 'A70H',
'IDEOS_TABLET', 'MYTOUCH_4G', 'UMS_COMPOSITE', 'SCH-I800_CARD',
'7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2',
'MB860', 'MULTI-CARD', 'MID7015A']
'MB860', 'MULTI-CARD', 'MID7015A', 'INCREDIBLE']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
'A70S', 'A101IT', '7']
'A70S', 'A101IT', '7', 'INCREDIBLE']
OSX_MAIN_MEM = 'Android Device Main Memory'

View File

@ -325,9 +325,8 @@ class Source(Plugin):
tokens = title.split()
for token in tokens:
token = token.strip()
if token and token.lower() not in ('a', 'and', 'the', '&') and strip_joiners:
yield token
elif token:
if token and (not strip_joiners or token.lower() not in ('a',
'and', 'the', '&')):
yield token
def split_jobs(self, jobs, num):
@ -375,7 +374,12 @@ class Source(Plugin):
def get_book_url(self, identifiers):
'''
Return the URL for the book identified by identifiers at this source.
If no URL is found, return None.
This URL must be browseable to by a human using a browser. It is meant
to provide a clickable link for the user to easily visit the books page
at this source.
If no URL is found, return None. This method must be quick, and
consistent, so only implement it if it is possible to construct the URL
from a known scheme given identifiers.
'''
return None

View File

@ -433,7 +433,7 @@ def urls_from_identifiers(identifiers): # {{{
pass
isbn = identifiers.get('isbn', None)
if isbn:
ans.append(('ISBN',
ans.append((isbn,
'http://www.worldcat.org/search?q=bn%%3A%s&qt=advanced'%isbn))
return ans
# }}}
@ -444,13 +444,18 @@ if __name__ == '__main__': # tests {{{
from calibre.ebooks.metadata.sources.test import (test_identify,
title_test, authors_test)
tests = [
(
{'title':'Magykal Papers',
'authors':['Sage']},
[title_test('The Magykal Papers', exact=True)],
),
( # An e-book ISBN not on Amazon, one of the authors is
# unknown to Amazon
{'identifiers':{'isbn': '9780307459671'},
'title':'Invisible Gorilla', 'authors':['Christopher Chabris']},
[title_test('The Invisible Gorilla',
exact=True), authors_test(['Christopher Chabris', 'Daniel Simons'])]
[title_test('The Invisible Gorilla', exact=True)]
),

View File

@ -1,4 +1,7 @@
#!/usr/bin/env python
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
@ -6,14 +9,13 @@ __docformat__ = 'restructuredtext en'
'''
Fetch metadata using Overdrive Content Reserve
'''
import sys, re, random, urllib, mechanize, copy
import re, random, mechanize, copy
from threading import RLock
from Queue import Queue, Empty
from lxml import html, etree
from lxml import html
from lxml.html import soupparser
from calibre import browser
from calibre.ebooks.metadata import check_isbn
from calibre.ebooks.metadata.sources.base import Source
from calibre.ebooks.metadata.book.base import Metadata
@ -33,7 +35,7 @@ class OverDrive(Source):
capabilities = frozenset(['identify', 'cover'])
touched_fields = frozenset(['title', 'authors', 'tags', 'pubdate',
'comments', 'publisher', 'identifier:isbn', 'series', 'series_num',
'comments', 'publisher', 'identifier:isbn', 'series', 'series_index',
'language', 'identifier:overdrive'])
has_html_comments = True
supports_gzip_transfer_encoding = False
@ -43,7 +45,6 @@ class OverDrive(Source):
Source.__init__(self, *args, **kwargs)
self.prefs.defaults['ignore_fields'] =['tags', 'pubdate', 'comments', 'identifier:isbn', 'language']
def identify(self, log, result_queue, abort, title=None, authors=None, # {{{
identifiers={}, timeout=30):
ovrdrv_id = identifiers.get('overdrive', None)
@ -68,19 +69,6 @@ class OverDrive(Source):
return None
# }}}
def get_book_url(self, identifiers): # {{{
ovrdrv_id = identifiers.get('overdrive', None)
if ovrdrv_id is not None:
ovrdrv_data = ovrdrv_data_cache.get(ovrdrv_id, None)
if ovrdrv_data:
return ovrdrv_data[1]
else:
br = browser()
ovrdrv_data = self.to_ovrdrv_data(br, None, None, ovrdrv_id)
return ovrdrv_data[1]
# }}}
def download_cover(self, log, result_queue, abort, # {{{
title=None, authors=None, identifiers={}, timeout=30):
cached_url = self.get_cached_cover_url(identifiers)
@ -136,28 +124,6 @@ class OverDrive(Source):
return url
# }}}
def create_query(self, title=None, authors=None, identifiers={}):
q = ''
if title or authors:
def build_term(prefix, parts):
return ' '.join('in'+prefix + ':' + x for x in parts)
title_tokens = list(self.get_title_tokens(title, False, True))
if title_tokens:
q += build_term('title', title_tokens)
author_tokens = self.get_author_tokens(authors,
only_first_author=True)
if author_tokens:
q += ('+' if q else '') + build_term('author',
author_tokens)
if isinstance(q, unicode):
q = q.encode('utf-8')
if not q:
return None
return BASE_URL+urlencode({
'q':q,
})
def get_base_referer(self): # to be used for passing referrer headers to cover download
choices = [
'http://overdrive.chipublib.org/82DC601D-7DDE-4212-B43A-09D821935B01/10/375/en/',
@ -209,7 +175,6 @@ class OverDrive(Source):
br.set_cookiejar(clean_cj)
def overdrive_search(self, br, q, title, author):
# re-initialize the cookiejar to so that it's clean
clean_cj = mechanize.CookieJar()
@ -279,7 +244,7 @@ class OverDrive(Source):
else:
creators = creators.split(', ')
# if an exact match in a preferred format occurs
if creators[0] == author[0] and od_title == title and int(formatid) in [1, 50, 410, 900]:
if (author and creators[0] == author[0]) and od_title == title and int(formatid) in [1, 50, 410, 900]:
return self.format_results(reserveid, od_title, subtitle, series, publisher,
creators, thumbimage, worldcatlink, formatid)
else:
@ -312,7 +277,6 @@ class OverDrive(Source):
else:
return ''
def overdrive_get_record(self, br, q, ovrdrv_id):
search_url = q+'SearchResults.aspx?ReserveID={'+ovrdrv_id+'}'
results_url = q+'SearchResults.svc/GetResults?sEcho=1&iColumns=18&sColumns=ReserveID%2CTitle%2CSubtitle%2CEdition%2CSeries%2CPublisher%2CFormat%2CFormatID%2CCreators%2CThumbImage%2CShortDescription%2CWorldCatLink%2CExcerptLink%2CCreatorFile%2CSortTitle%2CAvailableToLibrary%2CAvailableToRetailer%2CRelevancyRank&iDisplayStart=0&iDisplayLength=10&sSearch=&bEscapeRegex=true&iSortingCols=1&iSortCol_0=17&sSortDir_0=asc'
@ -390,7 +354,10 @@ class OverDrive(Source):
if len(ovrdrv_data[3]) > 1:
mi.series = ovrdrv_data[3]
if ovrdrv_data[4]:
mi.series_index = ovrdrv_data[4]
try:
mi.series_index = float(ovrdrv_data[4])
except:
pass
mi.publisher = ovrdrv_data[5]
mi.authors = ovrdrv_data[6]
mi.title = ovrdrv_data[8]
@ -427,12 +394,12 @@ class OverDrive(Source):
if lang:
mi.language = lang[0].strip()
#if ebook_isbn:
# print "ebook isbn is "+str(ebook_isbn[0])
# isbn = check_isbn(ebook_isbn[0].strip())
# if isbn:
# self.cache_isbn_to_identifier(isbn, ovrdrv_id)
# mi.isbn = isbn
if ebook_isbn:
#print "ebook isbn is "+str(ebook_isbn[0])
isbn = check_isbn(ebook_isbn[0].strip())
if isbn:
self.cache_isbn_to_identifier(isbn, ovrdrv_id)
mi.isbn = isbn
if subjects:
mi.tags = [tag.strip() for tag in subjects[0].split(',')]
@ -448,8 +415,25 @@ class OverDrive(Source):
return None
def main(args=sys.argv):
return 0
if __name__ == '__main__':
sys.exit(main())
# To run these test use:
# calibre-debug -e src/calibre/ebooks/metadata/sources/overdrive.py
from calibre.ebooks.metadata.sources.test import (test_identify_plugin,
title_test, authors_test)
test_identify_plugin(OverDrive.name,
[
(
{'title':'Foundation and Earth',
'authors':['Asimov']},
[title_test('Foundation and Earth', exact=True),
authors_test(['Isaac Asimov'])]
),
(
{'title': 'Elephants', 'authors':['Agatha']},
[title_test('Elephants Can Remember', exact=False),
authors_test(['Agatha Christie'])]
),
])

View File

@ -29,8 +29,7 @@ from calibre.ebooks.metadata.meta import set_metadata
from calibre.constants import DEBUG
from calibre.utils.config import prefs, tweaks
from calibre.utils.magick.draw import thumbnail
from calibre.library.save_to_disk import plugboard_any_device_value, \
plugboard_any_format_value
from calibre.library.save_to_disk import find_plugboard
# }}}
class DeviceJob(BaseJob): # {{{
@ -93,23 +92,6 @@ class DeviceJob(BaseJob): # {{{
# }}}
def find_plugboard(device_name, format, plugboards):
cpb = None
if format in plugboards:
cpb = plugboards[format]
elif plugboard_any_format_value in plugboards:
cpb = plugboards[plugboard_any_format_value]
if cpb is not None:
if device_name in cpb:
cpb = cpb[device_name]
elif plugboard_any_device_value in cpb:
cpb = cpb[plugboard_any_device_value]
else:
cpb = None
if DEBUG:
prints('Device using plugboard', format, device_name, cpb)
return cpb
def device_name_for_plugboards(device_class):
if hasattr(device_class, 'DEVICE_PLUGBOARD_NAME'):
return device_class.DEVICE_PLUGBOARD_NAME
@ -607,6 +589,16 @@ class DeviceMenu(QMenu): # {{{
class DeviceMixin(object): # {{{
#: This signal is emitted once, after metadata is downloaded from the
#: connected device.
#: The sequence: gui.device_manager.is_device_connected will become True,
#: then sometime later gui.device_metadata_available will be signaled.
#: This does not mean that there are no more jobs running. Automatic metadata
#: management might have kicked off a sync_booklists to write new metadata onto
#: the device, and that job might still be running when the signal is emitted.
device_metadata_available = pyqtSignal()
device_connection_changed = pyqtSignal(object)
def __init__(self):
self.device_error_dialog = error_dialog(self, _('Error'),
_('Error communicating with device'), ' ')
@ -753,6 +745,7 @@ class DeviceMixin(object): # {{{
self.location_manager.update_devices()
self.library_view.set_device_connected(self.device_connected)
self.refresh_ondevice()
self.device_connection_changed.emit(connected)
def info_read(self, job):
'''
@ -791,6 +784,7 @@ class DeviceMixin(object): # {{{
self.sync_news()
self.sync_catalogs()
self.refresh_ondevice()
self.device_metadata_available.emit()
def refresh_ondevice(self, reset_only = False):
'''

View File

@ -30,7 +30,6 @@ from calibre.ebooks.metadata.book.base import Metadata
from calibre.gui2 import error_dialog, NONE
from calibre.utils.date import utcnow, fromordinal, format_date
from calibre.library.comments import comments_to_html
from calibre.constants import islinux
from calibre import force_unicode
# }}}
@ -117,12 +116,10 @@ class CoverDelegate(QStyledItemDelegate): # {{{
def paint(self, painter, option, index):
QStyledItemDelegate.paint(self, painter, option, index)
if islinux:
# On linux for some reason the selected color is drawn on top of
# the decoration
style = QApplication.style()
style.drawItemPixmap(painter, option.rect, Qt.AlignTop|Qt.AlignHCenter,
QPixmap(index.data(Qt.DecorationRole)))
# Ensure the cover is rendered over any selection rect
style = QApplication.style()
style.drawItemPixmap(painter, option.rect, Qt.AlignTop|Qt.AlignHCenter,
QPixmap(index.data(Qt.DecorationRole)))
if self.timer.isActive() and index.data(Qt.UserRole).toBool():
rect = QRect(0, 0, self.spinner_width, self.spinner_width)
rect.moveCenter(option.rect.center())
@ -952,7 +949,7 @@ class CoverFetch(QDialog): # {{{
# }}}
if __name__ == '__main__':
DEBUG_DIALOG = True
#DEBUG_DIALOG = True
app = QApplication([])
d = FullFetch()
d.start(title='great gatsby', authors=['fitzgerald'])

View File

@ -337,7 +337,13 @@ def show_config_widget(category, name, gui=None, show_restart_msg=False,
bb.button(bb.RestoreDefaults).setEnabled(w.supports_restoring_to_defaults)
bb.button(bb.Apply).setEnabled(False)
bb.button(bb.Apply).clicked.connect(d.accept)
w.changed_signal.connect(lambda : bb.button(bb.Apply).setEnabled(True))
def onchange():
b = bb.button(bb.Apply)
b.setEnabled(True)
b.setDefault(True)
b.setAutoDefault(True)
w.changed_signal.connect(onchange)
bb.button(bb.Cancel).setFocus(True)
l = QVBoxLayout()
d.setLayout(l)
l.addWidget(w)

View File

@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
from PyQt4.Qt import Qt, QLineEdit, QComboBox, SIGNAL, QListWidgetItem
from calibre.customize.ui import is_disabled
from calibre.gui2 import error_dialog
from calibre.gui2.device import device_name_for_plugboards
from calibre.gui2.dialogs.template_dialog import TemplateDialog
@ -15,6 +16,8 @@ from calibre.gui2.preferences.plugboard_ui import Ui_Form
from calibre.customize.ui import metadata_writers, device_plugins
from calibre.library.save_to_disk import plugboard_any_format_value, \
plugboard_any_device_value, plugboard_save_to_disk_value
from calibre.library.server.content import plugboard_content_server_value, \
plugboard_content_server_formats
from calibre.utils.formatter import validation_formatter
@ -68,19 +71,26 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.device_label.setText(_('Device currently connected: None'))
self.devices = ['', 'APPLE', 'FOLDER_DEVICE']
self.device_to_formats_map = {}
for device in device_plugins():
n = device_name_for_plugboards(device)
self.device_to_formats_map[n] = device.FORMATS
if n not in self.devices:
self.devices.append(n)
self.devices.sort(cmp=lambda x, y: cmp(x.lower(), y.lower()))
self.devices.insert(1, plugboard_save_to_disk_value)
self.devices.insert(2, plugboard_any_device_value)
self.devices.insert(1, plugboard_content_server_value)
self.device_to_formats_map[plugboard_content_server_value] = \
plugboard_content_server_formats
self.devices.insert(1, plugboard_any_device_value)
self.new_device.addItems(self.devices)
self.formats = ['']
for w in metadata_writers():
for f in w.file_types:
self.formats.append(f)
if not is_disabled(w):
for f in w.file_types:
if not f in self.formats:
self.formats.append(f)
self.formats.append('device_db')
self.formats.sort()
self.formats.insert(1, plugboard_any_format_value)
@ -230,6 +240,15 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
show=True)
self.new_device.setCurrentIndex(0)
return
if self.current_device in self.device_to_formats_map:
allowable_formats = self.device_to_formats_map[self.current_device]
if self.current_format not in allowable_formats:
error_dialog(self, '',
_('The {0} device does not support the {1} format.').
format(self.current_device, self.current_format),
show=True)
self.new_device.setCurrentIndex(0)
return
self.set_fields()
def new_format_changed(self, txt):

View File

@ -8,6 +8,7 @@ __docformat__ = 'restructuredtext en'
import re
import time
import traceback
from contextlib import closing
from random import shuffle
from threading import Thread
@ -20,9 +21,12 @@ from calibre import browser
from calibre.gui2 import NONE
from calibre.gui2.progress_indicator import ProgressIndicator
from calibre.gui2.store.search_ui import Ui_Dialog
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
REGEXP_MATCH
from calibre.utils.config import DynamicConfig
from calibre.utils.icu import sort_key
from calibre.utils.magick.draw import thumbnail
from calibre.utils.search_query_parser import SearchQueryParser
HANG_TIME = 75000 # milliseconds seconds
TIMEOUT = 75 # seconds
@ -290,11 +294,15 @@ class SearchThread(Thread):
while self._run and not self.tasks.empty():
try:
query, store_name, store_plugin, timeout = self.tasks.get()
for res in store_plugin.search(query, timeout=timeout):
squery = query
for loc in SearchFilter.USABLE_LOCATIONS:
squery = re.sub(r'%s:"?(?P<a>[^\s"]+)"?' % loc, '\g<a>', squery)
for res in store_plugin.search(squery, timeout=timeout):
if not self._run:
return
res.store_name = store_name
self.results.put(res)
if SearchFilter(res).parse(query):
self.results.put(res)
self.tasks.task_done()
except:
pass
@ -450,3 +458,82 @@ class Matches(QAbstractItemModel):
if reset:
self.reset()
class SearchFilter(SearchQueryParser):
USABLE_LOCATIONS = [
'all',
'author',
'authors',
'cover',
'price',
'title',
'store',
]
def __init__(self, search_result):
SearchQueryParser.__init__(self, locations=self.USABLE_LOCATIONS)
self.search_result = search_result
def universal_set(self):
return set([self.search_result])
def get_matches(self, location, query):
location = location.lower().strip()
if location == 'authors':
location = 'author'
matchkind = CONTAINS_MATCH
if len(query) > 1:
if query.startswith('\\'):
query = query[1:]
elif query.startswith('='):
matchkind = EQUALS_MATCH
query = query[1:]
elif query.startswith('~'):
matchkind = REGEXP_MATCH
query = query[1:]
if matchkind != REGEXP_MATCH: ### leave case in regexps because it can be significant e.g. \S \W \D
query = query.lower()
if location not in self.USABLE_LOCATIONS:
return set([])
matches = set([])
all_locs = set(self.USABLE_LOCATIONS) - set(['all'])
locations = all_locs if location == 'all' else [location]
q = {
'author': self.search_result.author.lower(),
'cover': self.search_result.cover_url,
'format': '',
'price': self.search_result.price,
'store': self.search_result.store_name.lower(),
'title': self.search_result.title.lower(),
}
for x in ('author', 'format'):
q[x+'s'] = q[x]
for locvalue in locations:
ac_val = q[locvalue]
if query == 'true':
if ac_val is not None:
matches.add(self.search_result)
continue
if query == 'false':
if ac_val is None:
matches.add(self.search_result)
continue
try:
### Can't separate authors because comma is used for name sep and author sep
### Exact match might not get what you want. For that reason, turn author
### exactmatch searches into contains searches.
if locvalue == 'author' and matchkind == EQUALS_MATCH:
m = CONTAINS_MATCH
else:
m = matchkind
vals = [ac_val]
if _match(query, vals, m):
matches.add(self.search_result)
break
except ValueError: # Unicode errors
traceback.print_exc()
return matches

View File

@ -31,10 +31,14 @@ class NPWebView(QWebView):
proxy_parts = urlparse(http_proxy)
proxy = QNetworkProxy()
proxy.setType(QNetworkProxy.HttpProxy)
proxy.setUser(proxy_parts.username)
proxy.setPassword(proxy_parts.password)
proxy.setHostName(proxy_parts.hostname)
proxy.setPort(proxy_parts.port)
if proxy_parts.username:
proxy.setUser(proxy_parts.username)
if proxy_parts.password:
proxy.setPassword(proxy_parts.password)
if proxy_parts.hostname:
proxy.setHostName(proxy_parts.hostname)
if proxy_parts.port:
proxy.setPort(proxy_parts.port)
self.page().networkAccessManager().setProxy(proxy)
self.page().setForwardUnsupportedContent(True)

View File

@ -51,6 +51,23 @@ for x in FORMAT_ARG_DESCS:
FORMAT_ARGS[x] = ''
def find_plugboard(device_name, format, plugboards):
cpb = None
if format in plugboards:
cpb = plugboards[format]
elif plugboard_any_format_value in plugboards:
cpb = plugboards[plugboard_any_format_value]
if cpb is not None:
if device_name in cpb:
cpb = cpb[device_name]
elif plugboard_any_device_value in cpb:
cpb = cpb[plugboard_any_device_value]
else:
cpb = None
if DEBUG:
prints('Device using plugboard', format, device_name, cpb)
return cpb
def config(defaults=None):
if defaults is None:
c = Config('save_to_disk', _('Options to control saving to disk'))
@ -279,20 +296,7 @@ def do_save_book_to_disk(id_, mi, cover, plugboards,
written = False
for fmt in formats:
global plugboard_save_to_disk_value, plugboard_any_format_value
dev_name = plugboard_save_to_disk_value
cpb = None
if fmt in plugboards:
cpb = plugboards[fmt]
if dev_name in cpb:
cpb = cpb[dev_name]
else:
cpb = None
if cpb is None and plugboard_any_format_value in plugboards:
cpb = plugboards[plugboard_any_format_value]
if dev_name in cpb:
cpb = cpb[dev_name]
else:
cpb = None
cpb = find_plugboard(plugboard_save_to_disk_value, fmt, plugboards)
# Leave this here for a while, in case problems arise.
if cpb is not None:
prints('Save-to-disk using plugboard:', fmt, cpb)

View File

@ -12,9 +12,14 @@ import cherrypy
from calibre import fit_image, guess_type
from calibre.utils.date import fromtimestamp
from calibre.library.caches import SortKeyGenerator
from calibre.library.save_to_disk import find_plugboard
from calibre.utils.magick.draw import save_cover_data_to, Image, \
thumbnail as generate_thumbnail
plugboard_content_server_value = 'content_server'
plugboard_content_server_formats = ['epub']
class CSSortKeyGenerator(SortKeyGenerator):
def __init__(self, fields, fm, db_prefs):
@ -183,16 +188,30 @@ class ContentServer(object):
if fmt is None:
raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format))
if format == 'EPUB':
# Get the original metadata
mi = self.db.get_metadata(id, index_is_id=True)
# Get any EPUB plugboards for the content server
plugboards = self.db.prefs.get('plugboards', {})
cpb = find_plugboard(plugboard_content_server_value,
'epub', plugboards)
if cpb:
# Transform the metadata via the plugboard
newmi = mi.deepcopy_metadata()
newmi.template_to_attribute(mi, cpb)
else:
newmi = mi
# Write the updated file
from tempfile import TemporaryFile
from calibre.ebooks.metadata.meta import set_metadata
raw = fmt.read()
fmt = TemporaryFile()
fmt.write(raw)
fmt.seek(0)
set_metadata(fmt, self.db.get_metadata(id, index_is_id=True,
get_cover=True),
'epub')
set_metadata(fmt, newmi, 'epub')
fmt.seek(0)
mt = guess_type('dummy.'+format.lower())[0]
if mt is None:
mt = 'application/octet-stream'