mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
merge from main branch
This commit is contained in:
commit
c831770aa2
@ -259,4 +259,15 @@ h2.library_name {
|
|||||||
|
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
/* Booklist {{{ */
|
||||||
|
|
||||||
|
#booklist .page {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading img {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* }}} */
|
||||||
|
|
||||||
|
BIN
resources/images/news/orsai.png
Normal file
BIN
resources/images/news/orsai.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 684 B |
@ -26,30 +26,11 @@ class GlobeAndMail(BasicNewsRecipe):
|
|||||||
#credit {margin-top:0px;}
|
#credit {margin-top:0px;}
|
||||||
.tag {font-size: 22pt;}'''
|
.tag {font-size: 22pt;}'''
|
||||||
description = 'Canada\'s national newspaper'
|
description = 'Canada\'s national newspaper'
|
||||||
remove_tags_before = dict(id="article-top")
|
keep_only_tags = [dict(name='article')]
|
||||||
remove_tags = [
|
remove_tags = [dict(name='aside'),
|
||||||
{'id':['util', 'article-tabs', 'comments', 'article-relations',
|
dict(name='footer'),
|
||||||
'gallery-controls', 'video', 'galleryLoading','deck','header',
|
dict(name='div', attrs={'class':(lambda x: isinstance(x, (str,unicode)) and 'articlecommentcountholder' in x.split(' '))}),
|
||||||
'toolsBottom'] },
|
dict(name='ul', attrs={'class':(lambda x: isinstance(x, (str,unicode)) and 'articletoolbar' in x.split(' '))}),
|
||||||
{'class':['credit','inline-img-caption','tab-pointer'] },
|
|
||||||
dict(name='div', attrs={'id':['lead-photo', 'most-popular-story']}),
|
|
||||||
dict(name='div', attrs={'class':'right'}),
|
|
||||||
dict(name='div', attrs={'id':'footer'}),
|
|
||||||
dict(name='div', attrs={'id':'beta-msg'}),
|
|
||||||
dict(name='img', attrs={'class':'headshot'}),
|
|
||||||
dict(name='div', attrs={'class':'brand'}),
|
|
||||||
dict(name='div', attrs={'id':'nav-wrap'}),
|
|
||||||
dict(name='div', attrs={'id':'featureTopics'}),
|
|
||||||
dict(name='div', attrs={'id':'videoNav'}),
|
|
||||||
dict(name='div', attrs={'id':'blog-header'}),
|
|
||||||
dict(name='div', attrs={'id':'right-rail'}),
|
|
||||||
dict(name='div', attrs={'id':'group-footer-container'}),
|
|
||||||
dict(name=['iframe', 'style'])
|
|
||||||
]
|
|
||||||
remove_attributes = ['style']
|
|
||||||
remove_tags_after = [{'id':['article-content']},
|
|
||||||
{'class':['pull','inline-img'] },
|
|
||||||
dict(name='img', attrs={'class':'inline-media-embed'}),
|
|
||||||
]
|
]
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Latest headlines', u'http://www.theglobeandmail.com/?service=rss'),
|
(u'Latest headlines', u'http://www.theglobeandmail.com/?service=rss'),
|
||||||
|
37
resources/recipes/orsai.recipe
Normal file
37
resources/recipes/orsai.recipe
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
orsai.bitacoras.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Orsai(BasicNewsRecipe):
|
||||||
|
title = 'Orsai'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
language = 'es'
|
||||||
|
oldest_article = 35
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
encoding = 'utf-8'
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
publication_type = 'blog'
|
||||||
|
masthead_url = 'http://orsai.bitacoras.com/wp-content/themes/orsai/images/logo_orsai.png'
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : 'Blog literario de Hernán Casciari'
|
||||||
|
, 'tags' : 'blog, Argentina, España, literatura, Casciari'
|
||||||
|
, 'publisher': 'Editorial Orsai S.L.'
|
||||||
|
, 'language' : 'es'
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags=[dict(attrs={'class':['entry-title','entry-meta','entry-content','commentlist']})]
|
||||||
|
remove_tags=[dict(name='img',attrs={'class':'avatar avatar-40 photo'})]
|
||||||
|
feeds = [(u'Articulos', u'http://orsai.bitacoras.com/feed')]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return self.adeify_images(soup)
|
||||||
|
|
@ -11,6 +11,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
on 10/10/10 to include function to grab print version of articles
|
on 10/10/10 to include function to grab print version of articles
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
'''
|
'''
|
||||||
added by Tony Stegall
|
added by Tony Stegall
|
||||||
@ -27,7 +28,6 @@ class AdvancedUserRecipe1249039563(BasicNewsRecipe):
|
|||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
language = 'nl'
|
language = 'nl'
|
||||||
|
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
body{font-family:Arial,Helvetica,sans-serif; font-size:small;}
|
body{font-family:Arial,Helvetica,sans-serif; font-size:small;}
|
||||||
h1{font-size:large;}
|
h1{font-size:large;}
|
||||||
@ -43,10 +43,12 @@ class AdvancedUserRecipe1249039563(BasicNewsRecipe):
|
|||||||
|
|
||||||
def get_obfuscated_article(self, url):
|
def get_obfuscated_article(self, url):
|
||||||
br = self.get_browser()
|
br = self.get_browser()
|
||||||
|
print 'THE CURRENT URL IS: ', url
|
||||||
br.open(url)
|
br.open(url)
|
||||||
|
year = date.today().year
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = br.follow_link(url_regex='.*?(2010)(\\/)(article)(\\/)(print)(\\/)', nr = 0)
|
response = br.follow_link(url_regex='.*?(%d)(\\/)(article)(\\/)(print)(\\/)'%year, nr = 0)
|
||||||
html = response.read()
|
html = response.read()
|
||||||
except:
|
except:
|
||||||
response = br.open(url)
|
response = br.open(url)
|
||||||
@ -59,19 +61,22 @@ class AdvancedUserRecipe1249039563(BasicNewsRecipe):
|
|||||||
|
|
||||||
###############################################################################################################
|
###############################################################################################################
|
||||||
|
|
||||||
feeds = [
|
'''
|
||||||
(u'Laatste Nieuws', u'http://volkskrant.nl/rss/laatstenieuws.rss'),
|
Change Log:
|
||||||
(u'Binnenlands nieuws', u'http://volkskrant.nl/rss/nederland.rss'),
|
Date: 10/15/2010
|
||||||
(u'Buitenlands nieuws', u'http://volkskrant.nl/rss/internationaal.rss'),
|
Feeds updated by Martin Tarenskeen
|
||||||
(u'Economisch nieuws', u'http://volkskrant.nl/rss/economie.rss'),
|
'''
|
||||||
(u'Sportnieuws', u'http://volkskrant.nl/rss/sport.rss'),
|
|
||||||
(u'Kunstnieuws', u'http://volkskrant.nl/rss/kunst.rss'),
|
feeds = [
|
||||||
|
(u'Laatste Nieuws', u'http://www.volkskrant.nl/rss/laatstenieuws.rss'),
|
||||||
|
(u'Binnenland', u'http://www.volkskrant.nl/rss/nederland.rss'),
|
||||||
|
(u'Buitenland', u'http://www.volkskrant.nl/rss/internationaal.rss'),
|
||||||
|
(u'Economie', u'http://www.volkskrant.nl/rss/economie.rss'),
|
||||||
|
(u'Sport', u'http://www.volkskrant.nl/rss/sport.rss'),
|
||||||
|
(u'Cultuur', u'http://www.volkskrant.nl/rss/kunst.rss'),
|
||||||
|
(u'Gezondheid & Wetenschap', u'http://www.volkskrant.nl/rss/wetenschap.rss'),
|
||||||
|
(u'Internet & Media', u'http://www.volkskrant.nl/rss/media.rss') ]
|
||||||
|
|
||||||
#both of these rss feeds link back to the main volksrant.nl url a.k.a Broken
|
|
||||||
#If someone happens to know the correct paths then they can put them in here
|
|
||||||
#(u'Wetenschapsnieuws', u'http://feeds.feedburner.com/DeVolkskrantWetenschap'),
|
|
||||||
#(u'Technologienieuws', u'http://feeds.feedburner.com/vkmedia')
|
|
||||||
]
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
example for formating
|
example for formating
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import operator, os, json
|
import operator, os, json
|
||||||
from urllib import quote
|
from urllib import quote
|
||||||
from binascii import hexlify
|
from binascii import hexlify, unhexlify
|
||||||
|
|
||||||
import cherrypy
|
import cherrypy
|
||||||
|
|
||||||
@ -15,64 +15,33 @@ from calibre.constants import filesystem_encoding
|
|||||||
from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml
|
from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml
|
||||||
from calibre.utils.ordered_dict import OrderedDict
|
from calibre.utils.ordered_dict import OrderedDict
|
||||||
|
|
||||||
def paginate(offsets, content, base_url, up_url=None): # {{{
|
def render_book_list(ids):
|
||||||
'Create markup for pagination'
|
pages = []
|
||||||
|
while ids:
|
||||||
if '?' not in base_url:
|
page = list(ids[:25])
|
||||||
base_url += '?'
|
pages.append(page)
|
||||||
|
ids = ids[25:]
|
||||||
if base_url[-1] != '?':
|
page_template = u'''\
|
||||||
base_url += '&'
|
<div class="page" id="page{0}">
|
||||||
|
<div class="load_data" title="{1}"></div>
|
||||||
def navlink(decoration, name, cls, offset):
|
<div class="loading"><img src="/static/loading.gif" /> {2}</div>
|
||||||
label = xml(name)
|
<div class="loaded"></div>
|
||||||
if cls in ('next', 'last'):
|
|
||||||
label += ' ' + decoration
|
|
||||||
else:
|
|
||||||
label = decoration + ' ' + label
|
|
||||||
return (u'<a class="{cls}" href="{base_url}&offset={offset}" title={name}>'
|
|
||||||
u'{label}</a>').format(cls=cls, decoration=decoration,
|
|
||||||
name=xml(name, True), offset=offset,
|
|
||||||
base_url=xml(base_url, True), label=label)
|
|
||||||
left = ''
|
|
||||||
if offsets.offset > 0 and offsets.previous_offset > 0:
|
|
||||||
left += navlink(u'\u219e', _('First'), 'first', 0)
|
|
||||||
if offsets.offset > 0:
|
|
||||||
left += ' ' + navlink('←', _('Previous'), 'previous',
|
|
||||||
offsets.previous_offset)
|
|
||||||
|
|
||||||
middle = ''
|
|
||||||
if up_url:
|
|
||||||
middle = '<a href="{0}" title="{1}">[{1} ↑]</a>'.format(xml(up_url, True),
|
|
||||||
xml(_('Up')))
|
|
||||||
|
|
||||||
right = ''
|
|
||||||
if offsets.next_offset > -1:
|
|
||||||
right += navlink('&rarr', _('Next'), 'next', offsets.next_offset)
|
|
||||||
if offsets.last_offset > offsets.next_offset and offsets.last_offset > 0:
|
|
||||||
right += ' ' + navlink(u'\u21A0', _('Last'), 'last', offsets.last_offset)
|
|
||||||
|
|
||||||
navbar = u'''
|
|
||||||
<table class="navbar">
|
|
||||||
<tr>
|
|
||||||
<td class="left">{left}</td>
|
|
||||||
<td class="middle">{middle}</td>
|
|
||||||
<td class="right">{right}</td>
|
|
||||||
</tr>
|
|
||||||
<table>
|
|
||||||
'''.format(left=left, right=right, middle=middle)
|
|
||||||
|
|
||||||
templ = u'''
|
|
||||||
<div class="page">
|
|
||||||
{navbar}
|
|
||||||
<div class="page-contents">
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
{navbar}
|
|
||||||
</div>
|
</div>
|
||||||
'''
|
'''
|
||||||
return templ.format(navbar=navbar, content=content)
|
rpages = []
|
||||||
# }}}
|
for i, pg in enumerate(pages):
|
||||||
|
ld = xml(json.dumps(pg), True)
|
||||||
|
rpages.append(page_template.format(i, ld,
|
||||||
|
xml(_('Loading, please wait')) + '…'))
|
||||||
|
rpages = u'\n\n'.join(rpages)
|
||||||
|
|
||||||
|
templ = u'''\
|
||||||
|
<h3>{0}</h3>
|
||||||
|
<div id="booklist">
|
||||||
|
{pages}
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
return templ.format(_('Browsing %d books')%len(ids), pages=rpages)
|
||||||
|
|
||||||
def utf8(x): # {{{
|
def utf8(x): # {{{
|
||||||
if isinstance(x, unicode):
|
if isinstance(x, unicode):
|
||||||
@ -171,10 +140,15 @@ class BrowseServer(object):
|
|||||||
connect('browse_category_group',
|
connect('browse_category_group',
|
||||||
base_href+'/category_group/{category}/{group}',
|
base_href+'/category_group/{category}/{group}',
|
||||||
self.browse_category_group)
|
self.browse_category_group)
|
||||||
connect('browse_list', base_href+'/list/{query}', self.browse_list)
|
connect('browse_matches',
|
||||||
|
base_href+'/matches/{category}/{cid}',
|
||||||
|
self.browse_matches)
|
||||||
|
connect('browse_booklist_page',
|
||||||
|
base_href+'/booklist_page',
|
||||||
|
self.browse_booklist_page)
|
||||||
|
|
||||||
connect('browse_search', base_href+'/search/{query}',
|
connect('browse_search', base_href+'/search/{query}',
|
||||||
self.browse_search)
|
self.browse_search)
|
||||||
connect('browse_book', base_href+'/book/{uuid}', self.browse_book)
|
|
||||||
|
|
||||||
def browse_template(self, sort, category=True):
|
def browse_template(self, sort, category=True):
|
||||||
|
|
||||||
@ -267,12 +241,12 @@ class BrowseServer(object):
|
|||||||
|
|
||||||
def browse_category(self, category, sort):
|
def browse_category(self, category, sort):
|
||||||
categories = self.categories_cache()
|
categories = self.categories_cache()
|
||||||
|
if category not in categories:
|
||||||
|
raise cherrypy.HTTPError(404, 'category not found')
|
||||||
category_meta = self.db.field_metadata
|
category_meta = self.db.field_metadata
|
||||||
category_name = category_meta[category]['name']
|
category_name = category_meta[category]['name']
|
||||||
datatype = category_meta[category]['datatype']
|
datatype = category_meta[category]['datatype']
|
||||||
|
|
||||||
if category not in categories:
|
|
||||||
raise cherrypy.HTTPError(404, 'category not found')
|
|
||||||
|
|
||||||
items = categories[category]
|
items = categories[category]
|
||||||
sort = self.browse_sort_categories(items, sort)
|
sort = self.browse_sort_categories(items, sort)
|
||||||
@ -331,11 +305,12 @@ class BrowseServer(object):
|
|||||||
if sort not in ('rating', 'name', 'popularity'):
|
if sort not in ('rating', 'name', 'popularity'):
|
||||||
sort = 'name'
|
sort = 'name'
|
||||||
categories = self.categories_cache()
|
categories = self.categories_cache()
|
||||||
|
if category not in categories:
|
||||||
|
raise cherrypy.HTTPError(404, 'category not found')
|
||||||
|
|
||||||
category_meta = self.db.field_metadata
|
category_meta = self.db.field_metadata
|
||||||
datatype = category_meta[category]['datatype']
|
datatype = category_meta[category]['datatype']
|
||||||
|
|
||||||
if category not in categories:
|
|
||||||
raise cherrypy.HTTPError(404, 'category not found')
|
|
||||||
if not group:
|
if not group:
|
||||||
raise cherrypy.HTTPError(404, 'invalid group')
|
raise cherrypy.HTTPError(404, 'invalid group')
|
||||||
|
|
||||||
@ -360,6 +335,8 @@ class BrowseServer(object):
|
|||||||
'Entry point for top-level, categories and sub-categories'
|
'Entry point for top-level, categories and sub-categories'
|
||||||
if category == None:
|
if category == None:
|
||||||
ans = self.browse_toplevel()
|
ans = self.browse_toplevel()
|
||||||
|
elif category == 'newest':
|
||||||
|
raise cherrypy.InternalRedirect('/browse/matches/newest/dummy')
|
||||||
else:
|
else:
|
||||||
ans = self.browse_category(category, category_sort)
|
ans = self.browse_category(category, category_sort)
|
||||||
|
|
||||||
@ -368,8 +345,63 @@ class BrowseServer(object):
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Book Lists {{{
|
# Book Lists {{{
|
||||||
def browse_list(self, query=None, offset=0, sort=None):
|
|
||||||
raise NotImplementedError()
|
def browse_sort_book_list(self, items, sort):
|
||||||
|
fm = self.db.field_metadata
|
||||||
|
keys = frozenset(fm.sortable_field_keys())
|
||||||
|
if sort not in keys:
|
||||||
|
sort = 'title'
|
||||||
|
self.sort(items, 'title', True)
|
||||||
|
if sort != 'title':
|
||||||
|
ascending = fm[sort]['datatype'] not in ('rating', 'datetime')
|
||||||
|
self.sort(items, sort, ascending)
|
||||||
|
return sort
|
||||||
|
|
||||||
|
@Endpoint(sort_type='list')
|
||||||
|
def browse_matches(self, category=None, cid=None, list_sort=None):
|
||||||
|
if not cid:
|
||||||
|
raise cherrypy.HTTPError(404, 'invalid category id: %r'%cid)
|
||||||
|
categories = self.categories_cache()
|
||||||
|
|
||||||
|
if category not in categories and category != 'newest':
|
||||||
|
raise cherrypy.HTTPError(404, 'category not found')
|
||||||
|
try:
|
||||||
|
category_name = self.db.field_metadata[category]['name']
|
||||||
|
except:
|
||||||
|
if category != 'newest':
|
||||||
|
raise
|
||||||
|
category_name = _('Newest')
|
||||||
|
|
||||||
|
if category == 'search':
|
||||||
|
which = unhexlify(cid)
|
||||||
|
try:
|
||||||
|
ids = self.search_cache('search:"%s"'%which)
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404, 'Search: %r not understood'%which)
|
||||||
|
elif category == 'newest':
|
||||||
|
ids = list(self.db.data.iterallids())
|
||||||
|
else:
|
||||||
|
ids = self.db.get_books_for_category(category, cid)
|
||||||
|
|
||||||
|
items = [self.db.data._data[x] for x in ids]
|
||||||
|
if category == 'newest':
|
||||||
|
list_sort = 'timestamp'
|
||||||
|
sort = self.browse_sort_book_list(items, list_sort)
|
||||||
|
ids = [x[0] for x in items]
|
||||||
|
html = render_book_list(ids)
|
||||||
|
return self.browse_template(sort).format(
|
||||||
|
title=_('Books in') + " " +category_name,
|
||||||
|
script='booklist();', main=html)
|
||||||
|
|
||||||
|
@Endpoint(mimetype='application/json; charset=utf-8', sort_type='list')
|
||||||
|
def browse_booklist_page(self, ids=None, list_sort=None):
|
||||||
|
if ids is None:
|
||||||
|
ids = json.dumps('[]')
|
||||||
|
try:
|
||||||
|
ids = json.loads(ids)
|
||||||
|
except:
|
||||||
|
raise cherrypy.HTTPError(404, 'invalid ids')
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Search {{{
|
# Search {{{
|
||||||
|
@ -5,18 +5,15 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import re, os, cStringIO
|
import re, os
|
||||||
|
|
||||||
import cherrypy
|
import cherrypy
|
||||||
try:
|
|
||||||
from PIL import Image as PILImage
|
|
||||||
PILImage
|
|
||||||
except ImportError:
|
|
||||||
import Image as PILImage
|
|
||||||
|
|
||||||
from calibre import fit_image, guess_type
|
from calibre import fit_image, guess_type
|
||||||
from calibre.utils.date import fromtimestamp
|
from calibre.utils.date import fromtimestamp
|
||||||
from calibre.library.caches import SortKeyGenerator
|
from calibre.library.caches import SortKeyGenerator
|
||||||
|
from calibre.utils.magick.draw import save_cover_data_to, Image, \
|
||||||
|
thumbnail as generate_thumbnail
|
||||||
|
|
||||||
class CSSortKeyGenerator(SortKeyGenerator):
|
class CSSortKeyGenerator(SortKeyGenerator):
|
||||||
|
|
||||||
@ -77,8 +74,13 @@ class ContentServer(object):
|
|||||||
id = int(match.group())
|
id = int(match.group())
|
||||||
if not self.db.has_id(id):
|
if not self.db.has_id(id):
|
||||||
raise cherrypy.HTTPError(400, 'id:%d does not exist in database'%id)
|
raise cherrypy.HTTPError(400, 'id:%d does not exist in database'%id)
|
||||||
if what == 'thumb':
|
if what == 'thumb' or what.startswith('thumb_'):
|
||||||
return self.get_cover(id, thumbnail=True)
|
try:
|
||||||
|
width, height = map(int, what.split('_')[1:])
|
||||||
|
except:
|
||||||
|
width, height = 60, 80
|
||||||
|
return self.get_cover(id, thumbnail=True, thumb_width=width,
|
||||||
|
thumb_height=height)
|
||||||
if what == 'cover':
|
if what == 'cover':
|
||||||
return self.get_cover(id)
|
return self.get_cover(id)
|
||||||
return self.get_format(id, what)
|
return self.get_format(id, what)
|
||||||
@ -128,37 +130,39 @@ class ContentServer(object):
|
|||||||
return self.static('index.html')
|
return self.static('index.html')
|
||||||
|
|
||||||
# Actually get content from the database {{{
|
# Actually get content from the database {{{
|
||||||
def get_cover(self, id, thumbnail=False):
|
def get_cover(self, id, thumbnail=False, thumb_width=60, thumb_height=80):
|
||||||
cover = self.db.cover(id, index_is_id=True, as_file=False)
|
try:
|
||||||
if cover is None:
|
|
||||||
cover = self.default_cover
|
|
||||||
cherrypy.response.headers['Content-Type'] = 'image/jpeg'
|
cherrypy.response.headers['Content-Type'] = 'image/jpeg'
|
||||||
cherrypy.response.timeout = 3600
|
cherrypy.response.timeout = 3600
|
||||||
path = getattr(cover, 'name', False)
|
cover = self.db.cover(id, index_is_id=True, as_file=True)
|
||||||
updated = fromtimestamp(os.stat(path).st_mtime) if path and \
|
if cover is None:
|
||||||
os.access(path, os.R_OK) else self.build_time
|
cover = self.default_cover
|
||||||
|
updated = self.build_time
|
||||||
|
else:
|
||||||
|
with cover as f:
|
||||||
|
updated = fromtimestamp(os.stat(f.name).st_mtime)
|
||||||
|
cover = f.read()
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
||||||
try:
|
|
||||||
f = cStringIO.StringIO(cover)
|
if thumbnail:
|
||||||
try:
|
return generate_thumbnail(cover,
|
||||||
im = PILImage.open(f)
|
width=thumb_width, height=thumb_height)[-1]
|
||||||
except IOError:
|
|
||||||
raise cherrypy.HTTPError(404, 'No valid cover found')
|
img = Image()
|
||||||
width, height = im.size
|
img.load(cover)
|
||||||
|
width, height = img.size
|
||||||
scaled, width, height = fit_image(width, height,
|
scaled, width, height = fit_image(width, height,
|
||||||
60 if thumbnail else self.max_cover_width,
|
thumb_width if thumbnail else self.max_cover_width,
|
||||||
80 if thumbnail else self.max_cover_height)
|
thumb_height if thumbnail else self.max_cover_height)
|
||||||
if not scaled:
|
if not scaled:
|
||||||
return cover
|
return cover
|
||||||
im = im.resize((int(width), int(height)), PILImage.ANTIALIAS)
|
return save_cover_data_to(img, 'img.jpg', return_data=True,
|
||||||
of = cStringIO.StringIO()
|
resize_to=(width, height))
|
||||||
im.convert('RGB').save(of, 'JPEG')
|
|
||||||
return of.getvalue()
|
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
import traceback
|
import traceback
|
||||||
cherrypy.log.error('Failed to generate cover:')
|
cherrypy.log.error('Failed to generate cover:')
|
||||||
cherrypy.log.error(traceback.print_exc())
|
cherrypy.log.error(traceback.print_exc())
|
||||||
raise cherrypy.HTTPError(404, 'Failed to generate cover: %s'%err)
|
raise cherrypy.HTTPError(404, 'Failed to generate cover: %r'%err)
|
||||||
|
|
||||||
def get_format(self, id, format):
|
def get_format(self, id, format):
|
||||||
format = format.upper()
|
format = format.upper()
|
||||||
|
@ -387,6 +387,12 @@ solve it, look for a corrupted font file on your system, in ~/Library/Fonts or t
|
|||||||
check for corrupted fonts in OS X is to start the "Font Book" application, select all fonts and then in the File
|
check for corrupted fonts in OS X is to start the "Font Book" application, select all fonts and then in the File
|
||||||
menu, choose "Validate fonts".
|
menu, choose "Validate fonts".
|
||||||
|
|
||||||
|
|
||||||
|
I downloaded the installer, but it is not working?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Downloading from the internet can sometimes result in a corrupted download. If the |app| installer you downloaded is not opening, try downloading it again. If re-downloading it does not work, download it from `an alternate location <http://sourceforge.net/projects/calibre/files/>`_. If the installer still doesn't work, then something on your computer is preventing it from running. Best place to ask for more help is in the `forums <http://www.mobileread.com/forums/usercp.php>`_.
|
||||||
|
|
||||||
My antivirus program claims |app| is a virus/trojan?
|
My antivirus program claims |app| is a virus/trojan?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None,
|
|||||||
resize and the input and output image formats are the same, no changes are
|
resize and the input and output image formats are the same, no changes are
|
||||||
made.
|
made.
|
||||||
|
|
||||||
|
:param data: Image data as bytestring or Image object
|
||||||
:param compression_quality: The quality of the image after compression.
|
:param compression_quality: The quality of the image after compression.
|
||||||
Number between 1 and 100. 1 means highest compression, 100 means no
|
Number between 1 and 100. 1 means highest compression, 100 means no
|
||||||
compression (lossless).
|
compression (lossless).
|
||||||
@ -33,6 +34,9 @@ def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None,
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
changed = False
|
changed = False
|
||||||
|
if isinstance(data, Image):
|
||||||
|
img = data
|
||||||
|
else:
|
||||||
img = Image()
|
img = Image()
|
||||||
img.load(data)
|
img.load(data)
|
||||||
orig_fmt = normalize_format_name(img.format)
|
orig_fmt = normalize_format_name(img.format)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user