Add an option for maximum size of covers transmitted via the content server

This commit is contained in:
Kovid Goyal 2008-12-05 19:01:20 -08:00
parent e76b1ab174
commit f04b6da557
5 changed files with 57 additions and 16 deletions

View File

@ -1,7 +1,7 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
from PyQt4.QtGui import QDialog, QListWidgetItem from PyQt4.Qt import QDialog, QListWidgetItem, SIGNAL
from calibre.gui2 import file_icon_provider from calibre.gui2 import file_icon_provider
from calibre.gui2.dialogs.choose_format_ui import Ui_ChooseFormatDialog from calibre.gui2.dialogs.choose_format_ui import Ui_ChooseFormatDialog
@ -12,6 +12,7 @@ class ChooseFormatDialog(QDialog, Ui_ChooseFormatDialog):
QDialog.__init__(self, window) QDialog.__init__(self, window)
Ui_ChooseFormatDialog.__init__(self) Ui_ChooseFormatDialog.__init__(self)
self.setupUi(self) self.setupUi(self)
self.connect(self.formats, SIGNAL('activated(QModelIndex)'), lambda i: self.accept())
self.msg.setText(msg) self.msg.setText(msg)
for format in formats: for format in formats:

View File

@ -126,6 +126,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
lambda s: self.password.setEchoMode(self.password.Normal if s == Qt.Checked else self.password.Password)) lambda s: self.password.setEchoMode(self.password.Normal if s == Qt.Checked else self.password.Password))
self.password.setEchoMode(self.password.Password) self.password.setEchoMode(self.password.Password)
opts = server_config().parse() opts = server_config().parse()
self.max_cover_size.setText(opts.max_cover)
self.port.setValue(opts.port) self.port.setValue(opts.port)
self.username.setText(opts.username) self.username.setText(opts.username)
self.password.setText(opts.password if opts.password else '') self.password.setText(opts.password if opts.password else '')
@ -221,6 +222,10 @@ class ConfigDialog(QDialog, Ui_Dialog):
self.directory_list.takeItem(idx) self.directory_list.takeItem(idx)
def accept(self): def accept(self):
mcs = unicode(self.max_cover_size.text()).strip()
if not re.match(r'\d+x\d+', mcs):
error_dialog(self, _('Invalid size'), _('The size %s is invalid. must be of the form widthxheight')%mcs).exec_()
return
config['use_roman_numerals_for_series_number'] = bool(self.roman_numerals.isChecked()) config['use_roman_numerals_for_series_number'] = bool(self.roman_numerals.isChecked())
config['new_version_notification'] = bool(self.new_version_notification.isChecked()) config['new_version_notification'] = bool(self.new_version_notification.isChecked())
prefs['network_timeout'] = int(self.timeout.value()) prefs['network_timeout'] = int(self.timeout.value())
@ -246,6 +251,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
sc.set('username', unicode(self.username.text()).strip()) sc.set('username', unicode(self.username.text()).strip())
sc.set('password', unicode(self.password.text()).strip()) sc.set('password', unicode(self.password.text()).strip())
sc.set('port', self.port.value()) sc.set('port', self.port.value())
sc.set('max_cover', mcs)
config['delete_news_from_library_on_upload'] = self.delete_news.isChecked() config['delete_news_from_library_on_upload'] = self.delete_news.isChecked()
config['upload_news_to_device'] = self.sync_news.isChecked() config['upload_news_to_device'] = self.sync_news.isChecked()
of = str(self.output_format.currentText()) of = str(self.output_format.currentText())

View File

@ -675,13 +675,33 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1" > <item row="4" column="1" >
<widget class="QCheckBox" name="show_server_password" > <widget class="QCheckBox" name="show_server_password" >
<property name="text" > <property name="text" >
<string>&amp;Show password</string> <string>&amp;Show password</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1" >
<widget class="QLineEdit" name="max_cover_size" >
<property name="toolTip" >
<string>The maximum size (widthxheight) for displayed covers. Larger covers are resized. </string>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>Max. &amp;cover size:</string>
</property>
<property name="buddy" >
<cstring>max_cover_size</cstring>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>

View File

@ -28,4 +28,6 @@ def server_config(defaults=None):
help=_('Username for access. By default, it is: %default')) help=_('Username for access. By default, it is: %default'))
c.add_opt('develop', ['--develop'], default=False, c.add_opt('develop', ['--develop'], default=False,
help='Development mode. Server automatically restarts on file changes and serves code files (html, css, js) from the file system instead of calibre\'s resource system.') help='Development mode. Server automatically restarts on file changes and serves code files (html, css, js) from the file system instead of calibre\'s resource system.')
c.add_opt('max_cover', ['--max-cover'], default='600x800',
help=_('The maximum size for displayed covers'))
return c return c

View File

@ -14,7 +14,7 @@ from datetime import datetime
from threading import Thread from threading import Thread
import cherrypy import cherrypy
from PIL import Image from PyQt4.Qt import QImage, QApplication, QByteArray, Qt, QBuffer
from calibre.constants import __version__, __appname__ from calibre.constants import __version__, __appname__
from calibre.utils.genshi.template import MarkupTemplate from calibre.utils.genshi.template import MarkupTemplate
@ -112,6 +112,8 @@ class LibraryServer(object):
item item
break break
self.opts = opts self.opts = opts
self.max_cover_width, self.max_cover_height = \
map(int, self.opts.max_cover.split('x'))
cherrypy.config.update({ cherrypy.config.update({
'log.screen' : opts.develop, 'log.screen' : opts.develop,
@ -179,27 +181,37 @@ class LibraryServer(object):
cherrypy.engine.exit() cherrypy.engine.exit()
def get_cover(self, id, thumbnail=False): def get_cover(self, id, thumbnail=False):
cover = self.db.cover(id, index_is_id=True, as_file=True) cover = self.db.cover(id, index_is_id=True, as_file=False)
if cover is None: if cover is None:
cover = cStringIO.StringIO(server_resources['default_cover.jpg']) cover = server_resources['default_cover.jpg']
cherrypy.response.headers['Content-Type'] = 'image/jpeg' cherrypy.response.headers['Content-Type'] = 'image/jpeg'
path = getattr(cover, 'name', False) path = getattr(cover, 'name', False)
updated = datetime.utcfromtimestamp(os.stat(path).st_mtime) if path and os.access(path, os.R_OK) else build_time updated = datetime.utcfromtimestamp(os.stat(path).st_mtime) if path and os.access(path, os.R_OK) else build_time
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated) cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
if not thumbnail:
return cover.read()
try: try:
im = Image.open(cover) if QApplication.instance() is None:
width, height = im.size QApplication([])
scaled, width, height = fit_image(width, height, 80, 60)
im = QImage()
im.loadFromData(cover)
if im.isNull():
raise cherrypy.HTTPError(404, 'No valid cover found')
width, height = im.width(), im.height()
scaled, width, height = fit_image(width, height,
60 if thumbnail else self.max_cover_width,
80 if thumbnail else self.max_cover_height)
if not scaled: if not scaled:
return cover.read() return cover
im.thumbnail((width, height)) im = im.scaled(width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
o = cStringIO.StringIO() ba = QByteArray()
im.save(o, 'JPEG') buf = QBuffer(ba)
return o.getvalue() buf.open(QBuffer.WriteOnly)
im.save(buf, 'PNG')
return str(ba.data())
except Exception, err: except Exception, err:
raise cherrypy.HTTPError(404, 'failed to generate thumbnail: %s'%err) import traceback
traceback.print_exc()
raise cherrypy.HTTPError(404, 'Failed to generate cover: %s'%err)
def get_format(self, id, format): def get_format(self, id, format):
format = format.upper() format = format.upper()