mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Stanza integration:Implement result pagination. Fixes #3412 (Add alphabetical sub directories to content server)
This commit is contained in:
parent
751d855af5
commit
aee3d6febb
@ -134,7 +134,7 @@ class Install(Develop):
|
|||||||
The default <prefix> is the prefix of your python installation.
|
The default <prefix> is the prefix of your python installation.
|
||||||
''')
|
''')
|
||||||
|
|
||||||
sub_commands = ['build']
|
sub_commands = ['build', 'gui']
|
||||||
|
|
||||||
def add_options(self, parser):
|
def add_options(self, parser):
|
||||||
parser.add_option('--prefix', help='Installation prefix')
|
parser.add_option('--prefix', help='Installation prefix')
|
||||||
|
@ -432,6 +432,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
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 '')
|
||||||
|
self.opt_max_opds_items.setValue(opts.max_opds_items)
|
||||||
self.auto_launch.setChecked(config['autolaunch_server'])
|
self.auto_launch.setChecked(config['autolaunch_server'])
|
||||||
self.systray_icon.setChecked(config['systray_icon'])
|
self.systray_icon.setChecked(config['systray_icon'])
|
||||||
self.sync_news.setChecked(config['upload_news_to_device'])
|
self.sync_news.setChecked(config['upload_news_to_device'])
|
||||||
@ -729,6 +730,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
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)
|
sc.set('max_cover', mcs)
|
||||||
|
sc.set('max_opds_items', self.opt_max_opds_items.value())
|
||||||
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()
|
||||||
config['search_as_you_type'] = self.search_as_you_type.isChecked()
|
config['search_as_you_type'] = self.search_as_you_type.isChecked()
|
||||||
|
@ -790,13 +790,6 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QCheckBox" name="show_server_password">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Show password</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLineEdit" name="max_cover_size">
|
<widget class="QLineEdit" name="max_cover_size">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>The maximum size (widthxheight) for displayed covers. Larger covers are resized. </string>
|
<string>The maximum size (widthxheight) for displayed covers. Larger covers are resized. </string>
|
||||||
@ -806,7 +799,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Max. &cover size:</string>
|
<string>Max. &cover size:</string>
|
||||||
@ -816,6 +809,33 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QCheckBox" name="show_server_password">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Show password</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_15">
|
||||||
|
<property name="text">
|
||||||
|
<string>Max. &OPDS items per query:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_max_opds_items</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QSpinBox" name="opt_max_opds_items">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10000</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -22,4 +22,7 @@ def server_config(defaults=None):
|
|||||||
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',
|
c.add_opt('max_cover', ['--max-cover'], default='600x800',
|
||||||
help=_('The maximum size for displayed covers. Default is %default.'))
|
help=_('The maximum size for displayed covers. Default is %default.'))
|
||||||
|
c.add_opt('max_opds_items', ['--max-opds-items'], default=30,
|
||||||
|
help=_('The maximum number of matches to return per OPDS query. '
|
||||||
|
'This affects Stanza, WordPlayer, etc. integration.'))
|
||||||
return c
|
return c
|
||||||
|
@ -118,6 +118,7 @@ class LibraryServer(object):
|
|||||||
<id>$id</id>
|
<id>$id</id>
|
||||||
<updated>${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')}</updated>
|
<updated>${updated.strftime('%Y-%m-%dT%H:%M:%S+00:00')}</updated>
|
||||||
<link rel="search" title="Search" type="application/atom+xml" href="/stanza/?search={searchTerms}"/>
|
<link rel="search" title="Search" type="application/atom+xml" href="/stanza/?search={searchTerms}"/>
|
||||||
|
${Markup(next_link)}
|
||||||
<author>
|
<author>
|
||||||
<name>calibre</name>
|
<name>calibre</name>
|
||||||
<uri>http://calibre.kovidgoyal.net</uri>
|
<uri>http://calibre.kovidgoyal.net</uri>
|
||||||
@ -192,6 +193,7 @@ class LibraryServer(object):
|
|||||||
self.opts = opts
|
self.opts = opts
|
||||||
self.max_cover_width, self.max_cover_height = \
|
self.max_cover_width, self.max_cover_height = \
|
||||||
map(int, self.opts.max_cover.split('x'))
|
map(int, self.opts.max_cover.split('x'))
|
||||||
|
self.max_stanza_items = opts.max_opds_items
|
||||||
path = P('content_server')
|
path = P('content_server')
|
||||||
self.build_time = datetime.fromtimestamp(os.stat(path).st_mtime)
|
self.build_time = datetime.fromtimestamp(os.stat(path).st_mtime)
|
||||||
self.default_cover = open(P('content_server/default_cover.jpg'), 'rb').read()
|
self.default_cover = open(P('content_server/default_cover.jpg'), 'rb').read()
|
||||||
@ -362,24 +364,35 @@ class LibraryServer(object):
|
|||||||
return base.intersection(epub.union(pdb))
|
return base.intersection(epub.union(pdb))
|
||||||
|
|
||||||
def stanza_sortby_subcategory(self, updated, sortby, offset):
|
def stanza_sortby_subcategory(self, updated, sortby, offset):
|
||||||
what = sortby[2:]
|
what, subtitle = sortby[2:], ''
|
||||||
if sortby == 'byseries':
|
if sortby == 'byseries':
|
||||||
data = self.db.all_series()
|
data = self.db.all_series()
|
||||||
data = [(x[0], x[1], len(self.get_matches('series', x[1]))) for x in data]
|
data = [(x[0], x[1], len(self.get_matches('series', x[1]))) for x in data]
|
||||||
|
subtitle = 'Books by series'
|
||||||
elif sortby == 'byauthor':
|
elif sortby == 'byauthor':
|
||||||
data = self.db.all_authors()
|
data = self.db.all_authors()
|
||||||
data = [(x[0], x[1], len(self.get_matches('authors', x[1]))) for x in data]
|
data = [(x[0], x[1], len(self.get_matches('authors', x[1]))) for x in data]
|
||||||
|
subtitle = 'Books by author'
|
||||||
elif sortby == 'bytag':
|
elif sortby == 'bytag':
|
||||||
data = self.db.all_tags2()
|
data = self.db.all_tags2()
|
||||||
data = [(x[0], x[1], len(self.get_matches('tags', x[1]))) for x in data]
|
data = [(x[0], x[1], len(self.get_matches('tags', x[1]))) for x in data]
|
||||||
|
subtitle = 'Books by tag'
|
||||||
data = [x for x in data if x[2] > 0]
|
data = [x for x in data if x[2] > 0]
|
||||||
data.sort(cmp=lambda x, y: cmp(x[1], y[1]))
|
data.sort(cmp=lambda x, y: cmp(x[1], y[1]))
|
||||||
|
next_offset = offset + self.max_stanza_items
|
||||||
|
rdata = data[offset:next_offset]
|
||||||
|
if next_offset >= len(data):
|
||||||
|
next_offset = -1
|
||||||
entries = [self.STANZA_SUBCATALOG_ENTRY.generate(title=title, id=id,
|
entries = [self.STANZA_SUBCATALOG_ENTRY.generate(title=title, id=id,
|
||||||
what=what, updated=updated, count=c).render('xml').decode('utf-8') for id,
|
what=what, updated=updated, count=c).render('xml').decode('utf-8') for id,
|
||||||
title, c in data]
|
title, c in rdata]
|
||||||
entries = entries[offset:]
|
next_link = ''
|
||||||
return self.STANZA.generate(subtitle='', data=entries, FM=FIELD_MAP,
|
if next_offset > -1:
|
||||||
updated=updated, id='urn:calibre:main').render('xml')
|
next_link = ('<link rel="next" title="Next" '
|
||||||
|
'type="application/atom+xml" href="/stanza/?sortby=%s&offset=%d"/>\n'
|
||||||
|
) % (sortby, next_offset)
|
||||||
|
return self.STANZA.generate(subtitle=subtitle, data=entries, FM=FIELD_MAP,
|
||||||
|
updated=updated, id='urn:calibre:main', next_link=next_link).render('xml')
|
||||||
|
|
||||||
def stanza_main(self, updated):
|
def stanza_main(self, updated):
|
||||||
return self.STANZA_MAIN.generate(subtitle='', data=[], FM=FIELD_MAP,
|
return self.STANZA_MAIN.generate(subtitle='', data=[], FM=FIELD_MAP,
|
||||||
@ -391,6 +404,7 @@ class LibraryServer(object):
|
|||||||
'Feeds to read calibre books on a ipod with stanza.'
|
'Feeds to read calibre books on a ipod with stanza.'
|
||||||
books = []
|
books = []
|
||||||
updated = self.db.last_modified()
|
updated = self.db.last_modified()
|
||||||
|
offset = int(offset)
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
||||||
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
||||||
# Main feed
|
# Main feed
|
||||||
@ -426,15 +440,34 @@ class LibraryServer(object):
|
|||||||
record_list = reversed(record_list)
|
record_list = reversed(record_list)
|
||||||
|
|
||||||
|
|
||||||
|
fmts = FIELD_MAP['formats']
|
||||||
|
pat = re.compile(r'EPUB|PDB', re.IGNORECASE)
|
||||||
|
record_list = [x for x in record_list if x[0] in ids and
|
||||||
|
pat.search(x[fmts] if x[fmts] else '') is not None]
|
||||||
|
next_offset = offset + self.max_stanza_items
|
||||||
|
nrecord_list = record_list[offset:next_offset]
|
||||||
|
if next_offset >= len(record_list):
|
||||||
|
next_offset = -1
|
||||||
|
|
||||||
|
next_link = ''
|
||||||
|
if next_offset > -1:
|
||||||
|
q = ['offset=%d'%next_offset]
|
||||||
|
for x in ('search', 'sortby', 'authorid', 'tagid', 'seriesid'):
|
||||||
|
val = locals()[x]
|
||||||
|
if val is not None:
|
||||||
|
val = prepare_string_for_xml(unicode(val), True)
|
||||||
|
q.append('%s=%s'%(x, val))
|
||||||
|
next_link = ('<link rel="next" title="Next" '
|
||||||
|
'type="application/atom+xml" href="/stanza/?%s"/>\n'
|
||||||
|
) % '&'.join(q)
|
||||||
|
|
||||||
author_list=[]
|
author_list=[]
|
||||||
tag_list=[]
|
tag_list=[]
|
||||||
series_list=[]
|
series_list=[]
|
||||||
for record in record_list:
|
|
||||||
if record[0] not in ids: continue
|
for record in nrecord_list:
|
||||||
r = record[FIELD_MAP['formats']]
|
r = record[FIELD_MAP['formats']]
|
||||||
r = r.upper() if r else ''
|
r = r.upper() if r else ''
|
||||||
if not ('EPUB' in r or 'PDB' in r):
|
|
||||||
continue
|
|
||||||
|
|
||||||
z = record[FIELD_MAP['authors']]
|
z = record[FIELD_MAP['authors']]
|
||||||
if not z:
|
if not z:
|
||||||
@ -479,7 +512,7 @@ class LibraryServer(object):
|
|||||||
.render('xml').decode('utf8'))
|
.render('xml').decode('utf8'))
|
||||||
|
|
||||||
return self.STANZA.generate(subtitle='', data=books, FM=FIELD_MAP,
|
return self.STANZA.generate(subtitle='', data=books, FM=FIELD_MAP,
|
||||||
updated=updated, id='urn:calibre:main').render('xml')
|
next_link=next_link, updated=updated, id='urn:calibre:main').render('xml')
|
||||||
|
|
||||||
|
|
||||||
@expose
|
@expose
|
||||||
@ -533,7 +566,9 @@ class LibraryServer(object):
|
|||||||
cherrypy.request.headers.get('Want-OPDS-Catalog', 919) != 919 or \
|
cherrypy.request.headers.get('Want-OPDS-Catalog', 919) != 919 or \
|
||||||
ua.startswith('Stanza')
|
ua.startswith('Stanza')
|
||||||
return self.stanza(search=kwargs.get('search', None), sortby=kwargs.get('sortby',None), authorid=kwargs.get('authorid',None),
|
return self.stanza(search=kwargs.get('search', None), sortby=kwargs.get('sortby',None), authorid=kwargs.get('authorid',None),
|
||||||
tagid=kwargs.get('tagid',None), seriesid=kwargs.get('seriesid',None)) if want_opds else self.static('index.html')
|
tagid=kwargs.get('tagid',None),
|
||||||
|
seriesid=kwargs.get('seriesid',None),
|
||||||
|
offset=kwargs.get('offset', 0)) if want_opds else self.static('index.html')
|
||||||
|
|
||||||
|
|
||||||
@expose
|
@expose
|
||||||
|
Loading…
x
Reference in New Issue
Block a user