mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Content server: Initial implementation of --url-prefix option to ease use of reverse proxying. /browse and /mobile ported, OPDS remains
This commit is contained in:
parent
6681fe6ede
commit
5efad88b63
@ -8,20 +8,20 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=100" />
|
<meta http-equiv="X-UA-Compatible" content="IE=100" />
|
||||||
<link rel="icon" type="image/x-icon" href="http://calibre-ebook.com/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="http://calibre-ebook.com/favicon.ico" />
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/browse/browse.css" />
|
<link rel="stylesheet" type="text/css" href="{prefix}/static/browse/browse.css" />
|
||||||
<link type="text/css" href="/static/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css" rel="stylesheet" />
|
<link type="text/css" href="{prefix}/static/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css" rel="stylesheet" />
|
||||||
<link rel="stylesheet" type="text/css" href="/static/jquery.multiselect.css" />
|
<link rel="stylesheet" type="text/css" href="{prefix}/static/jquery.multiselect.css" />
|
||||||
|
|
||||||
<script type="text/javascript" src="/static/jquery.js"></script>
|
<script type="text/javascript" src="{prefix}/static/jquery.js"></script>
|
||||||
<script type="text/javascript" src="/static/jquery.corner.js"></script>
|
<script type="text/javascript" src="{prefix}/static/jquery.corner.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript"
|
<script type="text/javascript"
|
||||||
src="/static/jquery_ui/js/jquery-ui-1.8.5.custom.min.js"></script>
|
src="{prefix}/static/jquery_ui/js/jquery-ui-1.8.5.custom.min.js"></script>
|
||||||
<script type="text/javascript"
|
<script type="text/javascript"
|
||||||
src="/static/jquery.multiselect.min.js"></script>
|
src="{prefix}/static/jquery.multiselect.min.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript" src="/static/browse/browse.js"></script>
|
<script type="text/javascript" src="{prefix}/static/browse/browse.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var sort_cookie_name = "{sort_cookie_name}";
|
var sort_cookie_name = "{sort_cookie_name}";
|
||||||
@ -39,16 +39,16 @@
|
|||||||
<div id="header">
|
<div id="header">
|
||||||
<div class="area">
|
<div class="area">
|
||||||
<div class="bubble">
|
<div class="bubble">
|
||||||
<p><a href="/browse" title="Return to top level"
|
<p><a href="{prefix}/browse" title="Return to top level"
|
||||||
>→ home ←</a></p>
|
>→ home ←</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="nav-container">
|
<div id="nav-container">
|
||||||
<ul id="primary-nav">
|
<ul id="primary-nav">
|
||||||
<li><a id="nav-mobile" href="/mobile" title="A version of this website suited for mobile browsers">Mobile</a></li>
|
<li><a id="nav-mobile" href="{prefix}/mobile" title="A version of this website suited for mobile browsers">Mobile</a></li>
|
||||||
|
|
||||||
<li><a id="nav-demo" href="/old" title="The old version of this webiste">Old</a></li>
|
<li><a id="nav-demo" href="{prefix}/old" title="The old version of this webiste">Old</a></li>
|
||||||
<li><a id="nav-download" href="/opds" title="An OPDS feed based version of this website, used in special purpose applications">Feed</a></li>
|
<li><a id="nav-download" href="{prefix}/opds" title="An OPDS feed based version of this website, used in special purpose applications">Feed</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -58,7 +58,7 @@
|
|||||||
<input type="hidden" name="cmd" value="_s-xclick"></input>
|
<input type="hidden" name="cmd" value="_s-xclick"></input>
|
||||||
<input type="hidden" name="hosted_button_id" value="3028915"></input>
|
<input type="hidden" name="hosted_button_id" value="3028915"></input>
|
||||||
<input type="image"
|
<input type="image"
|
||||||
src="/static/button-donate.png"
|
src="{prefix}/static/button-donate.png"
|
||||||
name="submit"></input>
|
name="submit"></input>
|
||||||
<img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif"
|
<img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif"
|
||||||
width="1" height="1"></img>
|
width="1" height="1"></img>
|
||||||
@ -76,7 +76,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div id="search_box">
|
<div id="search_box">
|
||||||
<form name="search_form" action="/browse/search" method="get" accept-charset="UTF-8">
|
<form name="search_form" action="{prefix}/browse/search" method="get" accept-charset="UTF-8">
|
||||||
<input value="{initial_search}" type="text" title="Search" name="query"
|
<input value="{initial_search}" type="text" title="Search" name="query"
|
||||||
class="search_input" />
|
class="search_input" />
|
||||||
<input type="submit" value="Search" title="Search" alt="Search" />
|
<input type="submit" value="Search" title="Search" alt="Search" />
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div id="details_{id}" class="details">
|
<div id="details_{id}" class="details">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<img alt="Cover of {title}" src="/get/cover/{id}" />
|
<img alt="Cover of {title}" src="{prefix}/get/cover/{id}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<div class="field formats">{formats}</div>
|
<div class="field formats">{formats}</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<div id="summary_{id}" class="summary">
|
<div id="summary_{id}" class="summary">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<img alt="Cover of {title}" src="/get/thumb_90_120/{id}" />
|
<img alt="Cover of {title}" src="{prefix}/get/thumb_90_120/{id}" />
|
||||||
{get_button}
|
{get_button}
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
@ -8,7 +8,7 @@
|
|||||||
<span class="rating_container">{stars}</span>
|
<span class="rating_container">{stars}</span>
|
||||||
<span class="series">{series}</span>
|
<span class="series">{series}</span>
|
||||||
<a href="#" onclick="show_details(this); return false;" title="{details_tt}">{details}</a>
|
<a href="#" onclick="show_details(this); return false;" title="{details_tt}">{details}</a>
|
||||||
<a href="/browse/book/{id}" title="{permalink_tt}">{permalink}</a>
|
<a href="{prefix}/browse/book/{id}" title="{permalink_tt}">{permalink}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="title"><strong>{title}</strong></div>
|
<div class="title"><strong>{title}</strong></div>
|
||||||
<div class="authors">{authors}</div>
|
<div class="authors">{authors}</div>
|
||||||
|
@ -44,6 +44,10 @@ def server_config(defaults=None):
|
|||||||
'by first letter when there are more than this number '
|
'by first letter when there are more than this number '
|
||||||
'of items. Default: %default. Set to a large number '
|
'of items. Default: %default. Set to a large number '
|
||||||
'to disable grouping.'))
|
'to disable grouping.'))
|
||||||
|
c.add_opt('url_prefix', ['--url-prefix'], default='',
|
||||||
|
help=_('Prefix to prepend to all URLs. Useful for reverse'
|
||||||
|
'proxying to this server from Apache/nginx/etc.'))
|
||||||
|
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def custom_fields_to_display(db):
|
def custom_fields_to_display(db):
|
||||||
|
@ -28,16 +28,19 @@ from calibre.library.server.browse import BrowseServer
|
|||||||
|
|
||||||
class DispatchController(object): # {{{
|
class DispatchController(object): # {{{
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, prefix):
|
||||||
self.dispatcher = cherrypy.dispatch.RoutesDispatcher()
|
self.dispatcher = cherrypy.dispatch.RoutesDispatcher()
|
||||||
self.funcs = []
|
self.funcs = []
|
||||||
self.seen = set([])
|
self.seen = set([])
|
||||||
|
self.prefix = prefix if prefix else ''
|
||||||
|
|
||||||
def __call__(self, name, route, func, **kwargs):
|
def __call__(self, name, route, func, **kwargs):
|
||||||
if name in self.seen:
|
if name in self.seen:
|
||||||
raise NameError('Route name: '+ repr(name) + ' already used')
|
raise NameError('Route name: '+ repr(name) + ' already used')
|
||||||
self.seen.add(name)
|
self.seen.add(name)
|
||||||
kwargs['action'] = 'f_%d'%len(self.funcs)
|
kwargs['action'] = 'f_%d'%len(self.funcs)
|
||||||
|
if route != '/':
|
||||||
|
route = self.prefix + route
|
||||||
self.dispatcher.connect(name, route, self, **kwargs)
|
self.dispatcher.connect(name, route, self, **kwargs)
|
||||||
self.funcs.append(expose(func))
|
self.funcs.append(expose(func))
|
||||||
|
|
||||||
@ -55,7 +58,7 @@ class DispatchController(object): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class BonJour(SimplePlugin):
|
class BonJour(SimplePlugin): # {{{
|
||||||
|
|
||||||
def __init__(self, engine, port=8080):
|
def __init__(self, engine, port=8080):
|
||||||
SimplePlugin.__init__(self, engine)
|
SimplePlugin.__init__(self, engine)
|
||||||
@ -85,6 +88,7 @@ class BonJour(SimplePlugin):
|
|||||||
|
|
||||||
cherrypy.engine.bonjour = BonJour(cherrypy.engine)
|
cherrypy.engine.bonjour = BonJour(cherrypy.engine)
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
|
class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
|
||||||
BrowseServer):
|
BrowseServer):
|
||||||
@ -177,7 +181,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache,
|
|||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
d = DispatchController()
|
d = DispatchController(self.opts.url_prefix)
|
||||||
for x in self.__class__.__bases__:
|
for x in self.__class__.__bases__:
|
||||||
if hasattr(x, 'add_routes'):
|
if hasattr(x, 'add_routes'):
|
||||||
x.add_routes(self, d)
|
x.add_routes(self, d)
|
||||||
|
@ -22,7 +22,7 @@ from calibre.library.comments import comments_to_html
|
|||||||
from calibre.library.server import custom_fields_to_display
|
from calibre.library.server import custom_fields_to_display
|
||||||
from calibre.library.field_metadata import category_icon_map
|
from calibre.library.field_metadata import category_icon_map
|
||||||
|
|
||||||
def render_book_list(ids, suffix=''): # {{{
|
def render_book_list(ids, prefix, suffix=''): # {{{
|
||||||
pages = []
|
pages = []
|
||||||
num = len(ids)
|
num = len(ids)
|
||||||
pos = 0
|
pos = 0
|
||||||
@ -35,11 +35,11 @@ def render_book_list(ids, suffix=''): # {{{
|
|||||||
page_template = u'''\
|
page_template = u'''\
|
||||||
<div class="page" id="page{0}">
|
<div class="page" id="page{0}">
|
||||||
<div class="load_data" title="{1}">
|
<div class="load_data" title="{1}">
|
||||||
<span class="url" title="/browse/booklist_page"></span>
|
<span class="url" title="{prefix}/browse/booklist_page"></span>
|
||||||
<span class="start" title="{start}"></span>
|
<span class="start" title="{start}"></span>
|
||||||
<span class="end" title="{end}"></span>
|
<span class="end" title="{end}"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="loading"><img src="/static/loading.gif" /> {2}</div>
|
<div class="loading"><img src="{prefix}/static/loading.gif" /> {2}</div>
|
||||||
<div class="loaded"></div>
|
<div class="loaded"></div>
|
||||||
</div>
|
</div>
|
||||||
'''
|
'''
|
||||||
@ -49,7 +49,7 @@ def render_book_list(ids, suffix=''): # {{{
|
|||||||
ld = xml(json.dumps(pg), True)
|
ld = xml(json.dumps(pg), True)
|
||||||
rpages.append(page_template.format(i, ld,
|
rpages.append(page_template.format(i, ld,
|
||||||
xml(_('Loading, please wait')) + '…',
|
xml(_('Loading, please wait')) + '…',
|
||||||
start=pos+1, end=pos+len(pg)))
|
start=pos+1, end=pos+len(pg), prefix=prefix))
|
||||||
rpages = u'\n\n'.join(rpages)
|
rpages = u'\n\n'.join(rpages)
|
||||||
|
|
||||||
templ = u'''\
|
templ = u'''\
|
||||||
@ -91,7 +91,7 @@ def utf8(x): # {{{
|
|||||||
return x
|
return x
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def render_rating(rating, container='span', prefix=None): # {{{
|
def render_rating(rating, url_prefix, container='span', prefix=None): # {{{
|
||||||
if rating < 0.1:
|
if rating < 0.1:
|
||||||
return '', ''
|
return '', ''
|
||||||
added = 0
|
added = 0
|
||||||
@ -108,15 +108,15 @@ def render_rating(rating, container='span', prefix=None): # {{{
|
|||||||
elif n >= 0.9:
|
elif n >= 0.9:
|
||||||
x = 'on'
|
x = 'on'
|
||||||
ans.append(
|
ans.append(
|
||||||
u'<img alt="{0}" title="{0}" src="/static/star-{1}.png" />'.format(
|
u'<img alt="{0}" title="{0}" src="{2}/static/star-{1}.png" />'.format(
|
||||||
rstring, x))
|
rstring, x, url_prefix))
|
||||||
added += 1
|
added += 1
|
||||||
ans.append('</%s>'%container)
|
ans.append('</%s>'%container)
|
||||||
return u''.join(ans), rstring
|
return u''.join(ans), rstring
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def get_category_items(category, items, restriction, datatype): # {{{
|
def get_category_items(category, items, restriction, datatype, prefix): # {{{
|
||||||
|
|
||||||
if category == 'search':
|
if category == 'search':
|
||||||
items = [x for x in items if x.name != restriction]
|
items = [x for x in items if x.name != restriction]
|
||||||
@ -125,8 +125,8 @@ def get_category_items(category, items, restriction, datatype): # {{{
|
|||||||
templ = (u'<div title="{4}" class="category-item">'
|
templ = (u'<div title="{4}" class="category-item">'
|
||||||
'<div class="category-name">{0}</div><div>{1}</div>'
|
'<div class="category-name">{0}</div><div>{1}</div>'
|
||||||
'<div>{2}'
|
'<div>{2}'
|
||||||
'<span class="href">{3}</span></div></div>')
|
'<span class="href">{5}{3}</span></div></div>')
|
||||||
rating, rstring = render_rating(i.avg_rating)
|
rating, rstring = render_rating(i.avg_rating, prefix)
|
||||||
name = xml(i.name)
|
name = xml(i.name)
|
||||||
if datatype == 'rating':
|
if datatype == 'rating':
|
||||||
name = xml(_('%d stars')%int(i.avg_rating))
|
name = xml(_('%d stars')%int(i.avg_rating))
|
||||||
@ -142,7 +142,7 @@ def get_category_items(category, items, restriction, datatype): # {{{
|
|||||||
q = category
|
q = category
|
||||||
href = '/browse/matches/%s/%s'%(quote(q), quote(id_))
|
href = '/browse/matches/%s/%s'%(quote(q), quote(id_))
|
||||||
return templ.format(xml(name), rating,
|
return templ.format(xml(name), rating,
|
||||||
xml(desc), xml(href), rstring)
|
xml(desc), xml(href), rstring, prefix)
|
||||||
|
|
||||||
items = list(map(item, items))
|
items = list(map(item, items))
|
||||||
return '\n'.join(['<div class="category-container">'] + items + ['</div>'])
|
return '\n'.join(['<div class="category-container">'] + items + ['</div>'])
|
||||||
@ -243,6 +243,7 @@ class BrowseServer(object):
|
|||||||
|
|
||||||
ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':'))
|
ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':'))
|
||||||
ans = ans.replace('{sort_cookie_name}', scn)
|
ans = ans.replace('{sort_cookie_name}', scn)
|
||||||
|
ans = ans.replace('{prefix}', self.opts.url_prefix)
|
||||||
opts = ['<option %svalue="%s">%s</option>' % (
|
opts = ['<option %svalue="%s">%s</option>' % (
|
||||||
'selected="selected" ' if k==sort else '',
|
'selected="selected" ' if k==sort else '',
|
||||||
xml(k), xml(n), ) for k, n in
|
xml(k), xml(n), ) for k, n in
|
||||||
@ -258,15 +259,14 @@ class BrowseServer(object):
|
|||||||
ans = ans.replace('{initial_search}', initial_search)
|
ans = ans.replace('{initial_search}', initial_search)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
return self.__browse_template__
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def browse_summary_template(self):
|
def browse_summary_template(self):
|
||||||
if not hasattr(self, '__browse_summary_template__') or \
|
if not hasattr(self, '__browse_summary_template__') or \
|
||||||
self.opts.develop:
|
self.opts.develop:
|
||||||
self.__browse_summary_template__ = \
|
self.__browse_summary_template__ = \
|
||||||
P('content_server/browse/summary.html', data=True).decode('utf-8')
|
P('content_server/browse/summary.html', data=True).decode('utf-8')
|
||||||
return self.__browse_summary_template__
|
return self.__browse_summary_template__.replace('{prefix}',
|
||||||
|
self.opts.url_prefix)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def browse_details_template(self):
|
def browse_details_template(self):
|
||||||
@ -274,7 +274,8 @@ class BrowseServer(object):
|
|||||||
self.opts.develop:
|
self.opts.develop:
|
||||||
self.__browse_details_template__ = \
|
self.__browse_details_template__ = \
|
||||||
P('content_server/browse/details.html', data=True).decode('utf-8')
|
P('content_server/browse/details.html', data=True).decode('utf-8')
|
||||||
return self.__browse_details_template__
|
return self.__browse_details_template__.replace('{prefix}',
|
||||||
|
self.opts.url_prefix)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -334,11 +335,11 @@ class BrowseServer(object):
|
|||||||
icon = 'blank.png'
|
icon = 'blank.png'
|
||||||
cats.append((meta['name'], category, icon))
|
cats.append((meta['name'], category, icon))
|
||||||
|
|
||||||
cats = [('<li title="{2} {0}"><img src="{src}" alt="{0}" />'
|
cats = [('<li title="{2} {0}"><img src="{3}{src}" alt="{0}" />'
|
||||||
'<span class="label">{0}</span>'
|
'<span class="label">{0}</span>'
|
||||||
'<span class="url">/browse/category/{1}</span></li>')
|
'<span class="url">{3}/browse/category/{1}</span></li>')
|
||||||
.format(xml(x, True), xml(quote(y)), xml(_('Browse books by')),
|
.format(xml(x, True), xml(quote(y)), xml(_('Browse books by')),
|
||||||
src='/browse/icon/'+z)
|
self.opts.url_prefix, src='/browse/icon/'+z)
|
||||||
for x, y, z in cats]
|
for x, y, z in cats]
|
||||||
|
|
||||||
main = '<div class="toplevel"><h3>{0}</h3><ul>{1}</ul></div>'\
|
main = '<div class="toplevel"><h3>{0}</h3><ul>{1}</ul></div>'\
|
||||||
@ -378,7 +379,8 @@ class BrowseServer(object):
|
|||||||
if len(items) <= self.opts.max_opds_ungrouped_items:
|
if len(items) <= self.opts.max_opds_ungrouped_items:
|
||||||
script = 'false'
|
script = 'false'
|
||||||
items = get_category_items(category, items,
|
items = get_category_items(category, items,
|
||||||
self.search_restriction_name, datatype)
|
self.search_restriction_name, datatype,
|
||||||
|
self.opts.url_prefix)
|
||||||
else:
|
else:
|
||||||
getter = lambda x: unicode(getattr(x, 'sort', x.name))
|
getter = lambda x: unicode(getattr(x, 'sort', x.name))
|
||||||
starts = set([])
|
starts = set([])
|
||||||
@ -393,12 +395,13 @@ class BrowseServer(object):
|
|||||||
getter(y).upper().startswith(x)])
|
getter(y).upper().startswith(x)])
|
||||||
items = [(u'<h3 title="{0}">{0} <span>[{2}]</span></h3><div>'
|
items = [(u'<h3 title="{0}">{0} <span>[{2}]</span></h3><div>'
|
||||||
u'<div class="loaded" style="display:none"></div>'
|
u'<div class="loaded" style="display:none"></div>'
|
||||||
u'<div class="loading"><img alt="{1}" src="/static/loading.gif" /><em>{1}</em></div>'
|
u'<div class="loading"><img alt="{1}" src="{4}/static/loading.gif" /><em>{1}</em></div>'
|
||||||
u'<span class="load_href">{3}</span></div>').format(
|
u'<span class="load_href">{4}{3}</span></div>').format(
|
||||||
xml(s, True),
|
xml(s, True),
|
||||||
xml(_('Loading, please wait'))+'…',
|
xml(_('Loading, please wait'))+'…',
|
||||||
unicode(c),
|
unicode(c),
|
||||||
xml(u'/browse/category_group/%s/%s'%(category, s)))
|
xml(u'/browse/category_group/%s/%s'%(category, s)),
|
||||||
|
self.opts.url_prefix)
|
||||||
for s, c in category_groups.items()]
|
for s, c in category_groups.items()]
|
||||||
items = '\n\n'.join(items)
|
items = '\n\n'.join(items)
|
||||||
items = u'<div id="groups">\n{0}</div>'.format(items)
|
items = u'<div id="groups">\n{0}</div>'.format(items)
|
||||||
@ -410,13 +413,13 @@ class BrowseServer(object):
|
|||||||
main = u'''
|
main = u'''
|
||||||
<div class="category">
|
<div class="category">
|
||||||
<h3>{0}</h3>
|
<h3>{0}</h3>
|
||||||
<a class="navlink" href="/browse"
|
<a class="navlink" href="{3}/browse"
|
||||||
title="{2}">{2} ↑</a>
|
title="{2}">{2} ↑</a>
|
||||||
{1}
|
{1}
|
||||||
</div>
|
</div>
|
||||||
'''.format(
|
'''.format(
|
||||||
xml(_('Browsing by')+': ' + category_name), items,
|
xml(_('Browsing by')+': ' + category_name), items,
|
||||||
xml(_('Up'), True))
|
xml(_('Up'), True), self.opts.url_prefix)
|
||||||
|
|
||||||
return self.browse_template(sort).format(title=category_name,
|
return self.browse_template(sort).format(title=category_name,
|
||||||
script=script, main=main)
|
script=script, main=main)
|
||||||
@ -449,7 +452,8 @@ class BrowseServer(object):
|
|||||||
|
|
||||||
sort = self.browse_sort_categories(entries, sort)
|
sort = self.browse_sort_categories(entries, sort)
|
||||||
entries = get_category_items(category, entries,
|
entries = get_category_items(category, entries,
|
||||||
self.search_restriction_name, datatype)
|
self.search_restriction_name, datatype,
|
||||||
|
self.opts.url_prefix)
|
||||||
return json.dumps(entries, ensure_ascii=False)
|
return json.dumps(entries, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
@ -459,9 +463,11 @@ class BrowseServer(object):
|
|||||||
if category == None:
|
if category == None:
|
||||||
ans = self.browse_toplevel()
|
ans = self.browse_toplevel()
|
||||||
elif category == 'newest':
|
elif category == 'newest':
|
||||||
raise cherrypy.InternalRedirect('/browse/matches/newest/dummy')
|
raise cherrypy.InternalRedirect(self.opts.url_prefix +
|
||||||
|
'/browse/matches/newest/dummy')
|
||||||
elif category == 'allbooks':
|
elif category == 'allbooks':
|
||||||
raise cherrypy.InternalRedirect('/browse/matches/allbooks/dummy')
|
raise cherrypy.InternalRedirect(self.opts.url_prefix +
|
||||||
|
'/browse/matches/allbooks/dummy')
|
||||||
else:
|
else:
|
||||||
ans = self.browse_category(category, category_sort)
|
ans = self.browse_category(category, category_sort)
|
||||||
|
|
||||||
@ -532,7 +538,8 @@ class BrowseServer(object):
|
|||||||
list_sort = category
|
list_sort = category
|
||||||
sort = self.browse_sort_book_list(items, list_sort)
|
sort = self.browse_sort_book_list(items, list_sort)
|
||||||
ids = [x[0] for x in items]
|
ids = [x[0] for x in items]
|
||||||
html = render_book_list(ids, suffix=_('in') + ' ' + category_name)
|
html = render_book_list(ids, self.opts.url_prefix,
|
||||||
|
suffix=_('in') + ' ' + category_name)
|
||||||
|
|
||||||
return self.browse_template(sort, category=False).format(
|
return self.browse_template(sort, category=False).format(
|
||||||
title=_('Books in') + " " +category_name,
|
title=_('Books in') + " " +category_name,
|
||||||
@ -580,17 +587,18 @@ class BrowseServer(object):
|
|||||||
if fmts and fmt:
|
if fmts and fmt:
|
||||||
other_fmts = [x for x in fmts if x.lower() != fmt.lower()]
|
other_fmts = [x for x in fmts if x.lower() != fmt.lower()]
|
||||||
if other_fmts:
|
if other_fmts:
|
||||||
ofmts = [u'<a href="/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'\
|
ofmts = [u'<a href="{4}/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'\
|
||||||
.format(f, fname, id_, f.upper()) for f in
|
.format(f, fname, id_, f.upper(),
|
||||||
|
self.opts.url_prefix) for f in
|
||||||
other_fmts]
|
other_fmts]
|
||||||
ofmts = ', '.join(ofmts)
|
ofmts = ', '.join(ofmts)
|
||||||
args['other_formats'] = u'<strong>%s: </strong>' % \
|
args['other_formats'] = u'<strong>%s: </strong>' % \
|
||||||
_('Other formats') + ofmts
|
_('Other formats') + ofmts
|
||||||
|
|
||||||
args['details_href'] = '/browse/details/'+str(id_)
|
args['details_href'] = self.opts.url_prefix + '/browse/details/'+str(id_)
|
||||||
|
|
||||||
if fmt:
|
if fmt:
|
||||||
href = '/get/%s/%s_%d.%s'%(
|
href = self.opts.url_prefix + '/get/%s/%s_%d.%s'%(
|
||||||
fmt, fname, id_, fmt)
|
fmt, fname, id_, fmt)
|
||||||
rt = xml(_('Read %s in the %s format')%(args['title'],
|
rt = xml(_('Read %s in the %s format')%(args['title'],
|
||||||
fmt.upper()), True)
|
fmt.upper()), True)
|
||||||
@ -603,7 +611,8 @@ class BrowseServer(object):
|
|||||||
args['comments'] = comments_to_html(mi.comments)
|
args['comments'] = comments_to_html(mi.comments)
|
||||||
args['stars'] = ''
|
args['stars'] = ''
|
||||||
if mi.rating:
|
if mi.rating:
|
||||||
args['stars'] = render_rating(mi.rating/2.0, prefix=_('Rating'))[0]
|
args['stars'] = render_rating(mi.rating/2.0,
|
||||||
|
self.opts.url_prefix, prefix=_('Rating'))[0]
|
||||||
if args['tags']:
|
if args['tags']:
|
||||||
args['tags'] = u'<strong>%s: </strong>'%xml(_('Tags')) + \
|
args['tags'] = u'<strong>%s: </strong>'%xml(_('Tags')) + \
|
||||||
args['tags']
|
args['tags']
|
||||||
@ -628,8 +637,9 @@ class BrowseServer(object):
|
|||||||
args, fmt, fmts, fname = self.browse_get_book_args(mi, id_)
|
args, fmt, fmts, fname = self.browse_get_book_args(mi, id_)
|
||||||
args['formats'] = ''
|
args['formats'] = ''
|
||||||
if fmts:
|
if fmts:
|
||||||
ofmts = [u'<a href="/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'\
|
ofmts = [u'<a href="{4}/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'\
|
||||||
.format(fmt, fname, id_, fmt.upper()) for fmt in
|
.format(fmt, fname, id_, fmt.upper(),
|
||||||
|
self.opts.url_prefix) for fmt in
|
||||||
fmts]
|
fmts]
|
||||||
ofmts = ', '.join(ofmts)
|
ofmts = ', '.join(ofmts)
|
||||||
args['formats'] = ofmts
|
args['formats'] = ofmts
|
||||||
@ -648,7 +658,8 @@ class BrowseServer(object):
|
|||||||
continue
|
continue
|
||||||
if m['datatype'] == 'rating':
|
if m['datatype'] == 'rating':
|
||||||
r = u'<strong>%s: </strong>'%xml(m['name']) + \
|
r = u'<strong>%s: </strong>'%xml(m['name']) + \
|
||||||
render_rating(mi.rating/2.0, prefix=m['name'])[0]
|
render_rating(mi.rating/2.0, self.opts.url_prefix,
|
||||||
|
prefix=m['name'])[0]
|
||||||
else:
|
else:
|
||||||
r = u'<strong>%s: </strong>'%xml(m['name']) + \
|
r = u'<strong>%s: </strong>'%xml(m['name']) + \
|
||||||
args[field]
|
args[field]
|
||||||
@ -704,7 +715,8 @@ class BrowseServer(object):
|
|||||||
items = [self.db.data._data[x] for x in ids]
|
items = [self.db.data._data[x] for x in ids]
|
||||||
sort = self.browse_sort_book_list(items, list_sort)
|
sort = self.browse_sort_book_list(items, list_sort)
|
||||||
ids = [x[0] for x in items]
|
ids = [x[0] for x in items]
|
||||||
html = render_book_list(ids, suffix=_('in search')+': '+query)
|
html = render_book_list(ids, self.opts.url_prefix,
|
||||||
|
suffix=_('in search')+': '+query)
|
||||||
return self.browse_template(sort, category=False, initial_search=query).format(
|
return self.browse_template(sort, category=False, initial_search=query).format(
|
||||||
title=_('Matching books'),
|
title=_('Matching books'),
|
||||||
script='booklist();', main=html)
|
script='booklist();', main=html)
|
||||||
|
@ -103,7 +103,11 @@ class ContentServer(object):
|
|||||||
if self.opts.develop:
|
if self.opts.develop:
|
||||||
lm = fromtimestamp(os.stat(path).st_mtime)
|
lm = fromtimestamp(os.stat(path).st_mtime)
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(lm)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(lm)
|
||||||
return open(path, 'rb').read()
|
with open(path, 'rb') as f:
|
||||||
|
ans = f.read()
|
||||||
|
if path.endswith('.css'):
|
||||||
|
ans = ans.replace('/static/', self.opts.url_prefix + '/static/')
|
||||||
|
return ans
|
||||||
|
|
||||||
def index(self, **kwargs):
|
def index(self, **kwargs):
|
||||||
'The / URL'
|
'The / URL'
|
||||||
|
@ -26,9 +26,9 @@ def CLASS(*args, **kwargs): # class is a reserved word in Python
|
|||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
def build_search_box(num, search, sort, order): # {{{
|
def build_search_box(num, search, sort, order, prefix): # {{{
|
||||||
div = DIV(id='search_box')
|
div = DIV(id='search_box')
|
||||||
form = FORM('Show ', method='get', action='mobile')
|
form = FORM('Show ', method='get', action=prefix+'/mobile')
|
||||||
form.set('accept-charset', 'UTF-8')
|
form.set('accept-charset', 'UTF-8')
|
||||||
|
|
||||||
div.append(form)
|
div.append(form)
|
||||||
@ -89,11 +89,12 @@ def build_navigation(start, num, total, url_base): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def build_index(books, num, search, sort, order, start, total, url_base, CKEYS):
|
def build_index(books, num, search, sort, order, start, total, url_base, CKEYS,
|
||||||
logo = DIV(IMG(src='/static/calibre.png', alt=__appname__), id='logo')
|
prefix):
|
||||||
|
logo = DIV(IMG(src=prefix+'/static/calibre.png', alt=__appname__), id='logo')
|
||||||
|
|
||||||
search_box = build_search_box(num, search, sort, order)
|
search_box = build_search_box(num, search, sort, order, prefix)
|
||||||
navigation = build_navigation(start, num, total, url_base)
|
navigation = build_navigation(start, num, total, prefix+url_base)
|
||||||
bookt = TABLE(id='listing')
|
bookt = TABLE(id='listing')
|
||||||
|
|
||||||
body = BODY(
|
body = BODY(
|
||||||
@ -107,7 +108,8 @@ def build_index(books, num, search, sort, order, start, total, url_base, CKEYS):
|
|||||||
# Book list {{{
|
# Book list {{{
|
||||||
for book in books:
|
for book in books:
|
||||||
thumbnail = TD(
|
thumbnail = TD(
|
||||||
IMG(type='image/jpeg', border='0', src='/get/thumb/%s' %
|
IMG(type='image/jpeg', border='0',
|
||||||
|
src=prefix+'/get/thumb/%s' %
|
||||||
book['id']),
|
book['id']),
|
||||||
CLASS('thumbnail'))
|
CLASS('thumbnail'))
|
||||||
|
|
||||||
@ -118,7 +120,7 @@ def build_index(books, num, search, sort, order, start, total, url_base, CKEYS):
|
|||||||
s = SPAN(
|
s = SPAN(
|
||||||
A(
|
A(
|
||||||
fmt.lower(),
|
fmt.lower(),
|
||||||
href='/get/%s/%s-%s_%d.%s' % (fmt, a, t,
|
href=prefix+'/get/%s/%s-%s_%d.%s' % (fmt, a, t,
|
||||||
book['id'], fmt)
|
book['id'], fmt)
|
||||||
),
|
),
|
||||||
CLASS('button'))
|
CLASS('button'))
|
||||||
@ -154,7 +156,7 @@ def build_index(books, num, search, sort, order, start, total, url_base, CKEYS):
|
|||||||
TITLE(__appname__ + ' Library'),
|
TITLE(__appname__ + ' Library'),
|
||||||
LINK(rel='icon', href='http://calibre-ebook.com/favicon.ico',
|
LINK(rel='icon', href='http://calibre-ebook.com/favicon.ico',
|
||||||
type='image/x-icon'),
|
type='image/x-icon'),
|
||||||
LINK(rel='stylesheet', type='text/css', href='/mobile/style.css')
|
LINK(rel='stylesheet', type='text/css', href=prefix+'/mobile/style.css')
|
||||||
), # End head
|
), # End head
|
||||||
body
|
body
|
||||||
) # End html
|
) # End html
|
||||||
@ -174,7 +176,9 @@ class MobileServer(object):
|
|||||||
cherrypy.response.headers['Content-Type'] = 'text/css; charset=utf-8'
|
cherrypy.response.headers['Content-Type'] = 'text/css; charset=utf-8'
|
||||||
updated = utcfromtimestamp(os.stat(path).st_mtime)
|
updated = utcfromtimestamp(os.stat(path).st_mtime)
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
||||||
return open(path, 'rb').read()
|
with open(path, 'rb') as f:
|
||||||
|
ans = f.read()
|
||||||
|
return ans.replace('{prefix}', self.opts.url_prefix)
|
||||||
|
|
||||||
def mobile(self, start='1', num='25', sort='date', search='',
|
def mobile(self, start='1', num='25', sort='date', search='',
|
||||||
_=None, order='descending'):
|
_=None, order='descending'):
|
||||||
@ -259,7 +263,8 @@ class MobileServer(object):
|
|||||||
url_base = "/mobile?search=" + search+";order="+order+";sort="+sort+";num="+str(num)
|
url_base = "/mobile?search=" + search+";order="+order+";sort="+sort+";num="+str(num)
|
||||||
|
|
||||||
return html.tostring(build_index(books, num, search, sort, order,
|
return html.tostring(build_index(books, num, search, sort, order,
|
||||||
start, len(ids), url_base, CKEYS),
|
start, len(ids), url_base, CKEYS,
|
||||||
|
self.opts.url_prefix),
|
||||||
encoding='utf-8', include_meta_content_type=True,
|
encoding='utf-8', include_meta_content_type=True,
|
||||||
pretty_print=True)
|
pretty_print=True)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user