mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from custcol trunk
This commit is contained in:
commit
07246d842a
@ -442,7 +442,7 @@ from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
|||||||
from calibre.devices.jetbook.driver import JETBOOK
|
from calibre.devices.jetbook.driver import JETBOOK
|
||||||
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
|
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
|
||||||
from calibre.devices.nook.driver import NOOK
|
from calibre.devices.nook.driver import NOOK
|
||||||
from calibre.devices.prs505.driver import PRS505, PRS700
|
from calibre.devices.prs505.driver import PRS505
|
||||||
from calibre.devices.android.driver import ANDROID, S60
|
from calibre.devices.android.driver import ANDROID, S60
|
||||||
from calibre.devices.nokia.driver import N770, N810
|
from calibre.devices.nokia.driver import N770, N810
|
||||||
from calibre.devices.eslick.driver import ESLICK
|
from calibre.devices.eslick.driver import ESLICK
|
||||||
@ -510,7 +510,6 @@ plugins += [
|
|||||||
KINDLE_DX,
|
KINDLE_DX,
|
||||||
NOOK,
|
NOOK,
|
||||||
PRS505,
|
PRS505,
|
||||||
PRS700,
|
|
||||||
ANDROID,
|
ANDROID,
|
||||||
S60,
|
S60,
|
||||||
N770,
|
N770,
|
||||||
|
@ -293,8 +293,7 @@ class DevicePlugin(Plugin):
|
|||||||
put the book. len(metadata) == len(files). Apart from the regular
|
put the book. len(metadata) == len(files). Apart from the regular
|
||||||
cover (path to cover), there may also be a thumbnail attribute, which should
|
cover (path to cover), there may also be a thumbnail attribute, which should
|
||||||
be used in preference. The thumbnail attribute is of the form
|
be used in preference. The thumbnail attribute is of the form
|
||||||
(width, height, cover_data as jpeg). In addition the MetaInformation
|
(width, height, cover_data as jpeg).
|
||||||
objects can have a tag_order attribute.
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@ -396,14 +395,6 @@ class BookList(list):
|
|||||||
''' Return True if the the device supports tags (collections) for this book list. '''
|
''' Return True if the the device supports tags (collections) for this book list. '''
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def set_tags(self, book, tags):
|
|
||||||
'''
|
|
||||||
Set the tags for C{book} to C{tags}.
|
|
||||||
@param tags: A list of strings. Can be empty.
|
|
||||||
@param book: A book object that is in this BookList.
|
|
||||||
'''
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def add_book(self, book, replace_metadata):
|
def add_book(self, book, replace_metadata):
|
||||||
'''
|
'''
|
||||||
Add the book to the booklist. Intent is to maintain any device-internal
|
Add the book to the booklist. Intent is to maintain any device-internal
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net> ' \
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
'2009, John Schember <john at nachtimwald.com>'
|
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Device driver for the SONY PRS-505
|
Device driver for the SONY devices
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@ -20,27 +17,33 @@ from calibre import __appname__
|
|||||||
|
|
||||||
class PRS505(USBMS):
|
class PRS505(USBMS):
|
||||||
|
|
||||||
name = 'PRS-300/505 Device Interface'
|
name = 'SONY Device Interface'
|
||||||
gui_name = 'SONY Reader'
|
gui_name = 'SONY Reader'
|
||||||
description = _('Communicate with the Sony PRS-300/505/500 eBook reader.')
|
description = _('Communicate with all the Sony eBook readers.')
|
||||||
author = 'Kovid Goyal and John Schember'
|
author = 'Kovid Goyal'
|
||||||
supported_platforms = ['windows', 'osx', 'linux']
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
path_sep = '/'
|
path_sep = '/'
|
||||||
|
|
||||||
FORMATS = ['epub', 'lrf', 'lrx', 'rtf', 'pdf', 'txt']
|
FORMATS = ['epub', 'lrf', 'lrx', 'rtf', 'pdf', 'txt']
|
||||||
|
|
||||||
VENDOR_ID = [0x054c] #: SONY Vendor Id
|
VENDOR_ID = [0x054c] #: SONY Vendor Id
|
||||||
PRODUCT_ID = [0x031e] #: Product Id for the PRS 300/505/new 500
|
PRODUCT_ID = [0x031e]
|
||||||
BCD = [0x229, 0x1000, 0x22a]
|
BCD = [0x229, 0x1000, 0x22a, 0x31a]
|
||||||
|
|
||||||
VENDOR_NAME = 'SONY'
|
VENDOR_NAME = 'SONY'
|
||||||
WINDOWS_MAIN_MEM = re.compile('PRS-(505|300|500)')
|
WINDOWS_MAIN_MEM = re.compile(
|
||||||
WINDOWS_CARD_A_MEM = re.compile(r'PRS-(505|500)[#/]\S+:MS')
|
r'(PRS-(505|300|500))|'
|
||||||
WINDOWS_CARD_B_MEM = re.compile(r'PRS-(505|500)[#/]\S+:SD')
|
r'(PRS-((700[#/])|((6|9)00&)))'
|
||||||
|
)
|
||||||
|
WINDOWS_CARD_A_MEM = re.compile(
|
||||||
|
r'(PRS-(505|500)[#/]\S+:MS)|'
|
||||||
|
r'(PRS-((700[/#]\S+:)|((6|9)00[#_]))MS)'
|
||||||
|
)
|
||||||
|
WINDOWS_CARD_B_MEM = re.compile(
|
||||||
|
r'(PRS-(505|500)[#/]\S+:SD)|'
|
||||||
|
r'(PRS-((700[/#]\S+:)|((6|9)00[#_]))SD)'
|
||||||
|
)
|
||||||
|
|
||||||
OSX_MAIN_MEM = re.compile(r'Sony PRS-(((505|300|500)/[^:]+)|(300)) Media')
|
|
||||||
OSX_CARD_A_MEM = re.compile(r'Sony PRS-(505|500)/[^:]+:MS Media')
|
|
||||||
OSX_CARD_B_MEM = re.compile(r'Sony PRS-(505|500)/[^:]+:SD Media')
|
|
||||||
|
|
||||||
MAIN_MEMORY_VOLUME_LABEL = 'Sony Reader Main Memory'
|
MAIN_MEMORY_VOLUME_LABEL = 'Sony Reader Main Memory'
|
||||||
STORAGE_CARD_VOLUME_LABEL = 'Sony Reader Storage Card'
|
STORAGE_CARD_VOLUME_LABEL = 'Sony Reader Storage Card'
|
||||||
@ -101,27 +104,12 @@ class PRS505(USBMS):
|
|||||||
opts = self.settings()
|
opts = self.settings()
|
||||||
collections = ['series', 'tags']
|
collections = ['series', 'tags']
|
||||||
if opts.extra_customization:
|
if opts.extra_customization:
|
||||||
collections = opts.extra_customization.split(',')
|
collections = [x.strip() for x in
|
||||||
|
opts.extra_customization.split(',')]
|
||||||
|
|
||||||
c.update(blists, collections)
|
c.update(blists, collections)
|
||||||
c.write()
|
c.write()
|
||||||
|
|
||||||
USBMS.sync_booklists(self, booklists, end_session=end_session)
|
USBMS.sync_booklists(self, booklists, end_session=end_session)
|
||||||
|
|
||||||
class PRS700(PRS505):
|
|
||||||
|
|
||||||
name = 'PRS-600/700/900 Device Interface'
|
|
||||||
description = _('Communicate with the Sony PRS-600/700/900 eBook reader.')
|
|
||||||
author = 'Kovid Goyal and John Schember'
|
|
||||||
gui_name = 'SONY Reader'
|
|
||||||
supported_platforms = ['windows', 'osx', 'linux']
|
|
||||||
|
|
||||||
BCD = [0x31a]
|
|
||||||
|
|
||||||
WINDOWS_MAIN_MEM = re.compile('PRS-((700[#/])|((6|9)00&))')
|
|
||||||
WINDOWS_CARD_A_MEM = re.compile(r'PRS-((700[/#]\S+:)|((6|9)00[#_]))MS')
|
|
||||||
WINDOWS_CARD_B_MEM = re.compile(r'PRS-((700[/#]\S+:)|((6|9)00[#_]))SD')
|
|
||||||
|
|
||||||
OSX_MAIN_MEM = re.compile(r'Sony PRS-((700/[^:]+)|((6|9)00)) Media')
|
|
||||||
OSX_CARD_A_MEM = re.compile(r'Sony PRS-((700/[^:]+:)|((6|9)00 ))MS Media')
|
|
||||||
OSX_CARD_B_MEM = re.compile(r'Sony PRS-((700/[^:]+:)|((6|9)00 ))SD Media')
|
|
||||||
|
@ -101,16 +101,25 @@ class XMLCache(object):
|
|||||||
|
|
||||||
# Playlist management {{{
|
# Playlist management {{{
|
||||||
def purge_broken_playlist_items(self, root):
|
def purge_broken_playlist_items(self, root):
|
||||||
for item in root.xpath(
|
for pl in root.xpath('//*[local-name()="playlist"]'):
|
||||||
'//*[local-name()="playlist"]/*[local-name()="item"]'):
|
seen = set([])
|
||||||
id_ = item.get('id', None)
|
for item in list(pl):
|
||||||
if id_ is None or not root.xpath(
|
id_ = item.get('id', None)
|
||||||
'//*[local-name()!="item" and @id="%s"]'%id_):
|
if id_ is None or id_ in seen or not root.xpath(
|
||||||
if DEBUG:
|
'//*[local-name()!="item" and @id="%s"]'%id_):
|
||||||
prints('Purging broken playlist item:',
|
if DEBUG:
|
||||||
etree.tostring(item, with_tail=False))
|
if id_ is None:
|
||||||
item.getparent().remove(item)
|
cause = 'invalid id'
|
||||||
|
elif id_ in seen:
|
||||||
|
cause = 'duplicate item'
|
||||||
|
else:
|
||||||
|
cause = 'id not found'
|
||||||
|
prints('Purging broken playlist item:',
|
||||||
|
id_, 'from playlist:', pl.get('title', None),
|
||||||
|
'because:', cause)
|
||||||
|
item.getparent().remove(item)
|
||||||
|
continue
|
||||||
|
seen.add(id_)
|
||||||
|
|
||||||
def prune_empty_playlists(self):
|
def prune_empty_playlists(self):
|
||||||
for i, root in self.record_roots.items():
|
for i, root in self.record_roots.items():
|
||||||
@ -144,7 +153,7 @@ class XMLCache(object):
|
|||||||
self.ensure_unique_playlist_titles()
|
self.ensure_unique_playlist_titles()
|
||||||
self.prune_empty_playlists()
|
self.prune_empty_playlists()
|
||||||
for i, root in self.record_roots.items():
|
for i, root in self.record_roots.items():
|
||||||
ans[i] = {}
|
ans[i] = []
|
||||||
for playlist in root.xpath('//*[local-name()="playlist"]'):
|
for playlist in root.xpath('//*[local-name()="playlist"]'):
|
||||||
items = []
|
items = []
|
||||||
for item in playlist:
|
for item in playlist:
|
||||||
@ -153,7 +162,7 @@ class XMLCache(object):
|
|||||||
'//*[local-name()="text" and @id="%s"]'%id_)
|
'//*[local-name()="text" and @id="%s"]'%id_)
|
||||||
if records:
|
if records:
|
||||||
items.append(records[0])
|
items.append(records[0])
|
||||||
ans[i] = {playlist.get('title'):items}
|
ans[i].append((playlist.get('title'), items))
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def get_or_create_playlist(self, bl_idx, title):
|
def get_or_create_playlist(self, bl_idx, title):
|
||||||
@ -175,6 +184,8 @@ class XMLCache(object):
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def fix_ids(self): # {{{
|
def fix_ids(self): # {{{
|
||||||
|
if DEBUG:
|
||||||
|
prints('Running fix_ids()')
|
||||||
|
|
||||||
def ensure_numeric_ids(root):
|
def ensure_numeric_ids(root):
|
||||||
idmap = {}
|
idmap = {}
|
||||||
@ -255,7 +266,19 @@ class XMLCache(object):
|
|||||||
def update_booklist(self, bl, bl_index):
|
def update_booklist(self, bl, bl_index):
|
||||||
if bl_index not in self.record_roots:
|
if bl_index not in self.record_roots:
|
||||||
return
|
return
|
||||||
|
if DEBUG:
|
||||||
|
prints('Updating JSON cache:', bl_index)
|
||||||
root = self.record_roots[bl_index]
|
root = self.record_roots[bl_index]
|
||||||
|
pmap = self.get_playlist_map()[bl_index]
|
||||||
|
playlist_map = {}
|
||||||
|
for title, records in pmap:
|
||||||
|
for record in records:
|
||||||
|
path = record.get('path', None)
|
||||||
|
if path:
|
||||||
|
if path not in playlist_map:
|
||||||
|
playlist_map[path] = []
|
||||||
|
playlist_map[path].append(title)
|
||||||
|
|
||||||
for book in bl:
|
for book in bl:
|
||||||
record = self.book_by_lpath(book.lpath, root)
|
record = self.book_by_lpath(book.lpath, root)
|
||||||
if record is not None:
|
if record is not None:
|
||||||
@ -282,6 +305,10 @@ class XMLCache(object):
|
|||||||
book.thumbnail = raw
|
book.thumbnail = raw
|
||||||
break
|
break
|
||||||
break
|
break
|
||||||
|
if book.lpath in playlist_map:
|
||||||
|
tags = playlist_map[book.lpath]
|
||||||
|
book.device_collections = tags
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Update XML from JSON {{{
|
# Update XML from JSON {{{
|
||||||
@ -290,7 +317,7 @@ class XMLCache(object):
|
|||||||
|
|
||||||
for i, booklist in booklists.items():
|
for i, booklist in booklists.items():
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
prints('Updating booklist:', i)
|
prints('Updating XML Cache:', i)
|
||||||
root = self.record_roots[i]
|
root = self.record_roots[i]
|
||||||
for book in booklist:
|
for book in booklist:
|
||||||
path = os.path.join(self.prefixes[i], *(book.lpath.split('/')))
|
path = os.path.join(self.prefixes[i], *(book.lpath.split('/')))
|
||||||
@ -304,6 +331,10 @@ class XMLCache(object):
|
|||||||
|
|
||||||
self.fix_ids()
|
self.fix_ids()
|
||||||
|
|
||||||
|
# This is needed to update device_collections
|
||||||
|
for i, booklist in booklists.items():
|
||||||
|
self.update_booklist(booklist, i)
|
||||||
|
|
||||||
def update_playlists(self, bl_index, root, booklist, playlist_map,
|
def update_playlists(self, bl_index, root, booklist, playlist_map,
|
||||||
collections_attributes):
|
collections_attributes):
|
||||||
collections = booklist.get_collections(collections_attributes)
|
collections = booklist.get_collections(collections_attributes)
|
||||||
@ -340,7 +371,25 @@ class XMLCache(object):
|
|||||||
nsmap=playlist.nsmap, attrib={'id':id_})
|
nsmap=playlist.nsmap, attrib={'id':id_})
|
||||||
playlist.append(item)
|
playlist.append(item)
|
||||||
|
|
||||||
|
# Delete playlist entries not in collections
|
||||||
|
for playlist in root.xpath('//*[local-name()="playlist"]'):
|
||||||
|
title = playlist.get('title', None)
|
||||||
|
if title not in collections:
|
||||||
|
if DEBUG:
|
||||||
|
prints('Deleting playlist:', playlist.get('title', ''))
|
||||||
|
playlist.getparent().remove(playlist)
|
||||||
|
continue
|
||||||
|
books = collections[title]
|
||||||
|
records = [self.book_by_lpath(b.lpath, root) for b in books]
|
||||||
|
records = [x for x in records if x is not None]
|
||||||
|
ids = [x.get('id', None) for x in records]
|
||||||
|
ids = [x for x in ids if x is not None]
|
||||||
|
for item in list(playlist):
|
||||||
|
if item.get('id', None) not in ids:
|
||||||
|
if DEBUG:
|
||||||
|
prints('Deleting item:', item.get('id', ''),
|
||||||
|
'from playlist:', playlist.get('title', ''))
|
||||||
|
playlist.remove(item)
|
||||||
|
|
||||||
def create_text_record(self, root, bl_id, lpath):
|
def create_text_record(self, root, bl_id, lpath):
|
||||||
namespace = self.namespaces[bl_id]
|
namespace = self.namespaces[bl_id]
|
||||||
@ -395,8 +444,19 @@ class XMLCache(object):
|
|||||||
child.iterchildren(reversed=True).next().tail = '\n'+'\t'*level
|
child.iterchildren(reversed=True).next().tail = '\n'+'\t'*level
|
||||||
root.iterchildren(reversed=True).next().tail = '\n'+'\t'*(level-1)
|
root.iterchildren(reversed=True).next().tail = '\n'+'\t'*(level-1)
|
||||||
|
|
||||||
|
def move_playlists_to_bottom(self):
|
||||||
|
for root in self.record_roots.values():
|
||||||
|
seen = []
|
||||||
|
for pl in root.xpath('//*[local-name()="playlist"]'):
|
||||||
|
pl.getparent().remove(pl)
|
||||||
|
seen.append(pl)
|
||||||
|
for pl in seen:
|
||||||
|
root.append(pl)
|
||||||
|
|
||||||
|
|
||||||
def write(self):
|
def write(self):
|
||||||
for i, path in self.paths.items():
|
for i, path in self.paths.items():
|
||||||
|
self.move_playlists_to_bottom()
|
||||||
self.cleanup_whitespace(i)
|
self.cleanup_whitespace(i)
|
||||||
raw = etree.tostring(self.roots[i], encoding='UTF-8',
|
raw = etree.tostring(self.roots[i], encoding='UTF-8',
|
||||||
xml_declaration=True)
|
xml_declaration=True)
|
||||||
|
@ -14,14 +14,14 @@ from calibre import isbytestring
|
|||||||
|
|
||||||
class Book(MetaInformation):
|
class Book(MetaInformation):
|
||||||
|
|
||||||
BOOK_ATTRS = ['lpath', 'size', 'mime']
|
BOOK_ATTRS = ['lpath', 'size', 'mime', 'device_collections']
|
||||||
|
|
||||||
JSON_ATTRS = [
|
JSON_ATTRS = [
|
||||||
'lpath', 'title', 'authors', 'mime', 'size', 'tags', 'author_sort',
|
'lpath', 'title', 'authors', 'mime', 'size', 'tags', 'author_sort',
|
||||||
'title_sort', 'comments', 'category', 'publisher', 'series',
|
'title_sort', 'comments', 'category', 'publisher', 'series',
|
||||||
'series_index', 'rating', 'isbn', 'language', 'application_id',
|
'series_index', 'rating', 'isbn', 'language', 'application_id',
|
||||||
'book_producer', 'lccn', 'lcc', 'ddc', 'rights', 'publication_type',
|
'book_producer', 'lccn', 'lcc', 'ddc', 'rights', 'publication_type',
|
||||||
'uuid'
|
'uuid',
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, prefix, lpath, size=None, other=None):
|
def __init__(self, prefix, lpath, size=None, other=None):
|
||||||
@ -29,6 +29,7 @@ class Book(MetaInformation):
|
|||||||
|
|
||||||
MetaInformation.__init__(self, '')
|
MetaInformation.__init__(self, '')
|
||||||
|
|
||||||
|
self.device_collections = []
|
||||||
self.path = os.path.join(prefix, lpath)
|
self.path = os.path.join(prefix, lpath)
|
||||||
if os.sep == '\\':
|
if os.sep == '\\':
|
||||||
self.path = self.path.replace('/', '\\')
|
self.path = self.path.replace('/', '\\')
|
||||||
@ -45,27 +46,7 @@ class Book(MetaInformation):
|
|||||||
self.smart_update(other)
|
self.smart_update(other)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
spath = self.path
|
return self.path == getattr(other, 'path', None)
|
||||||
opath = other.path
|
|
||||||
|
|
||||||
if not isinstance(self.path, unicode):
|
|
||||||
try:
|
|
||||||
spath = unicode(self.path)
|
|
||||||
except:
|
|
||||||
try:
|
|
||||||
spath = self.path.decode(filesystem_encoding)
|
|
||||||
except:
|
|
||||||
spath = self.path
|
|
||||||
if not isinstance(other.path, unicode):
|
|
||||||
try:
|
|
||||||
opath = unicode(other.path)
|
|
||||||
except:
|
|
||||||
try:
|
|
||||||
opath = other.path.decode(filesystem_encoding)
|
|
||||||
except:
|
|
||||||
opath = other.path
|
|
||||||
|
|
||||||
return spath == opath
|
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def db_id(self):
|
def db_id(self):
|
||||||
@ -119,9 +100,6 @@ class BookList(_BookList):
|
|||||||
def supports_tags(self):
|
def supports_tags(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_tags(self, book, tags):
|
|
||||||
book.tags = tags
|
|
||||||
|
|
||||||
def add_book(self, book, replace_metadata):
|
def add_book(self, book, replace_metadata):
|
||||||
if book not in self:
|
if book not in self:
|
||||||
self.append(book)
|
self.append(book)
|
||||||
@ -134,7 +112,9 @@ class BookList(_BookList):
|
|||||||
def get_collections(self, collection_attributes):
|
def get_collections(self, collection_attributes):
|
||||||
collections = {}
|
collections = {}
|
||||||
series_categories = set([])
|
series_categories = set([])
|
||||||
|
collection_attributes = list(collection_attributes)+['device_collections']
|
||||||
for attr in collection_attributes:
|
for attr in collection_attributes:
|
||||||
|
attr = attr.strip()
|
||||||
for book in self:
|
for book in self:
|
||||||
val = getattr(book, attr, None)
|
val = getattr(book, attr, None)
|
||||||
if not val: continue
|
if not val: continue
|
||||||
@ -145,11 +125,17 @@ class BookList(_BookList):
|
|||||||
elif isinstance(val, unicode):
|
elif isinstance(val, unicode):
|
||||||
val = [val]
|
val = [val]
|
||||||
for category in val:
|
for category in val:
|
||||||
|
if attr == 'tags' and len(category) > 1 and \
|
||||||
|
category[0] == '[' and category[-1] == ']':
|
||||||
|
continue
|
||||||
if category not in collections:
|
if category not in collections:
|
||||||
collections[category] = []
|
collections[category] = []
|
||||||
collections[category].append(book)
|
if book not in collections[category]:
|
||||||
if attr == 'series':
|
collections[category].append(book)
|
||||||
series_categories.add(category)
|
if attr == 'series':
|
||||||
|
series_categories.add(category)
|
||||||
|
|
||||||
|
# Sort collections
|
||||||
for category, books in collections.items():
|
for category, books in collections.items():
|
||||||
def tgetter(x):
|
def tgetter(x):
|
||||||
return getattr(x, 'title_sort', 'zzzz')
|
return getattr(x, 'title_sort', 'zzzz')
|
||||||
|
@ -258,7 +258,7 @@ class MetaInformation(object):
|
|||||||
'series', 'series_index', 'tags', 'rating', 'isbn', 'language',
|
'series', 'series_index', 'tags', 'rating', 'isbn', 'language',
|
||||||
'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover',
|
'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover',
|
||||||
'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate',
|
'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate',
|
||||||
'rights', 'publication_type', 'uuid', 'tag_order',
|
'rights', 'publication_type', 'uuid'
|
||||||
):
|
):
|
||||||
prints(x, getattr(self, x, 'None'))
|
prints(x, getattr(self, x, 'None'))
|
||||||
|
|
||||||
@ -278,7 +278,7 @@ class MetaInformation(object):
|
|||||||
'isbn', 'application_id', 'manifest', 'spine', 'toc',
|
'isbn', 'application_id', 'manifest', 'spine', 'toc',
|
||||||
'cover', 'language', 'guide', 'book_producer',
|
'cover', 'language', 'guide', 'book_producer',
|
||||||
'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate', 'rights',
|
'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate', 'rights',
|
||||||
'publication_type', 'uuid', 'tag_order'):
|
'publication_type', 'uuid'):
|
||||||
if hasattr(mi, attr):
|
if hasattr(mi, attr):
|
||||||
val = getattr(mi, attr)
|
val = getattr(mi, attr)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
|
@ -102,6 +102,9 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
def set_device_connected(self, is_connected):
|
def set_device_connected(self, is_connected):
|
||||||
self.device_connected = is_connected
|
self.device_connected = is_connected
|
||||||
self.db.refresh_ondevice()
|
self.db.refresh_ondevice()
|
||||||
|
if is_connected and self.sorted_on[0] == 'ondevice':
|
||||||
|
self.resort()
|
||||||
|
|
||||||
|
|
||||||
def set_book_on_device_func(self, func):
|
def set_book_on_device_func(self, func):
|
||||||
self.book_on_device = func
|
self.book_on_device = func
|
||||||
@ -340,9 +343,6 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
ans = []
|
ans = []
|
||||||
for id in ids:
|
for id in ids:
|
||||||
mi = self.db.get_metadata(id, index_is_id=True, get_cover=True)
|
mi = self.db.get_metadata(id, index_is_id=True, get_cover=True)
|
||||||
if mi.series is not None:
|
|
||||||
mi.tag_order = { mi.series: self.db.books_in_series_of(id,
|
|
||||||
index_is_id=True)}
|
|
||||||
ans.append(mi)
|
ans.append(mi)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
@ -807,7 +807,7 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
'authors' : _('Author(s)'),
|
'authors' : _('Author(s)'),
|
||||||
'timestamp' : _('Date'),
|
'timestamp' : _('Date'),
|
||||||
'size' : _('Size'),
|
'size' : _('Size'),
|
||||||
'tags' : _('Tags')
|
'tags' : _('Collections')
|
||||||
}
|
}
|
||||||
self.marked_for_deletion = {}
|
self.marked_for_deletion = {}
|
||||||
self.search_engine = OnDeviceSearch(self)
|
self.search_engine = OnDeviceSearch(self)
|
||||||
@ -896,7 +896,8 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
x, y = int(self.db[x].size), int(self.db[y].size)
|
x, y = int(self.db[x].size), int(self.db[y].size)
|
||||||
return cmp(x, y)
|
return cmp(x, y)
|
||||||
def tagscmp(x, y):
|
def tagscmp(x, y):
|
||||||
x, y = ','.join(self.db[x].tags), ','.join(self.db[y].tags)
|
x = ','.join(self.db[x].device_collections)
|
||||||
|
y = ','.join(self.db[y].device_collections)
|
||||||
return cmp(x, y)
|
return cmp(x, y)
|
||||||
def libcmp(x, y):
|
def libcmp(x, y):
|
||||||
x, y = self.db[x].in_library, self.db[y].in_library
|
x, y = self.db[x].in_library, self.db[y].in_library
|
||||||
@ -966,7 +967,7 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
data[_('Path')] = item.path
|
data[_('Path')] = item.path
|
||||||
dt = dt_factory(item.datetime, assume_utc=True)
|
dt = dt_factory(item.datetime, assume_utc=True)
|
||||||
data[_('Timestamp')] = isoformat(dt, sep=' ', as_utc=False)
|
data[_('Timestamp')] = isoformat(dt, sep=' ', as_utc=False)
|
||||||
data[_('Tags')] = ', '.join(item.tags)
|
data[_('Collections')] = ', '.join(item.device_collections)
|
||||||
self.new_bookdisplay_data.emit(data)
|
self.new_bookdisplay_data.emit(data)
|
||||||
|
|
||||||
def paths(self, rows):
|
def paths(self, rows):
|
||||||
@ -1000,7 +1001,7 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
dt = dt_factory(dt, assume_utc=True, as_utc=False)
|
dt = dt_factory(dt, assume_utc=True, as_utc=False)
|
||||||
return QVariant(strftime(TIME_FMT, dt.timetuple()))
|
return QVariant(strftime(TIME_FMT, dt.timetuple()))
|
||||||
elif cname == 'tags':
|
elif cname == 'tags':
|
||||||
tags = self.db[self.map[row]].tags
|
tags = self.db[self.map[row]].device_collections
|
||||||
if tags:
|
if tags:
|
||||||
return QVariant(', '.join(tags))
|
return QVariant(', '.join(tags))
|
||||||
elif role == Qt.ToolTipRole and index.isValid():
|
elif role == Qt.ToolTipRole and index.isValid():
|
||||||
@ -1047,7 +1048,7 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
elif cname == 'tags':
|
elif cname == 'tags':
|
||||||
tags = [i.strip() for i in val.split(',')]
|
tags = [i.strip() for i in val.split(',')]
|
||||||
tags = [t for t in tags if t]
|
tags = [t for t in tags if t]
|
||||||
self.db.set_tags(self.db[idx], tags)
|
self.db[idx].device_collections = tags
|
||||||
self.dataChanged.emit(index, index)
|
self.dataChanged.emit(index, index)
|
||||||
self.booklist_dirtied.emit()
|
self.booklist_dirtied.emit()
|
||||||
done = True
|
done = True
|
||||||
|
@ -578,12 +578,14 @@ class ResultCache(SearchQueryParser):
|
|||||||
self._map_filtered = list(self._map)
|
self._map_filtered = list(self._map)
|
||||||
|
|
||||||
def seriescmp(self, x, y):
|
def seriescmp(self, x, y):
|
||||||
|
sidx = self.FIELD_MAP['series']
|
||||||
try:
|
try:
|
||||||
ans = cmp(self._data[x][9].lower(), self._data[y][9].lower())
|
ans = cmp(self._data[x][sidx].lower(), self._data[y][sidx].lower())
|
||||||
except AttributeError: # Some entries may be None
|
except AttributeError: # Some entries may be None
|
||||||
ans = cmp(self._data[x][9], self._data[y][9])
|
ans = cmp(self._data[x][sidx], self._data[y][sidx])
|
||||||
if ans != 0: return ans
|
if ans != 0: return ans
|
||||||
return cmp(self._data[x][10], self._data[y][10])
|
sidx = self.FIELD_MAP['series_index']
|
||||||
|
return cmp(self._data[x][sidx], self._data[y][sidx])
|
||||||
|
|
||||||
def cmp(self, loc, x, y, asstr=True, subsort=False):
|
def cmp(self, loc, x, y, asstr=True, subsort=False):
|
||||||
try:
|
try:
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
HTTP server for remote access to the calibre database.
|
HTTP server for remote access to the calibre database.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import sys, textwrap, operator, os, re, logging, cStringIO
|
import sys, textwrap, operator, os, re, logging, cStringIO, copy
|
||||||
import __builtin__
|
import __builtin__
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
@ -63,21 +63,21 @@ class LibraryServer(object):
|
|||||||
|
|
||||||
BOOK = textwrap.dedent('''\
|
BOOK = textwrap.dedent('''\
|
||||||
<book xmlns:py="http://genshi.edgewall.org/"
|
<book xmlns:py="http://genshi.edgewall.org/"
|
||||||
id="${r[0]}"
|
id="${r[FM['id']]}"
|
||||||
title="${r[1]}"
|
title="${r[FM['title']]}"
|
||||||
sort="${r[11]}"
|
sort="${r[FM['sort']]}"
|
||||||
author_sort="${r[12]}"
|
author_sort="${r[FM['author_sort']]}"
|
||||||
authors="${authors}"
|
authors="${authors}"
|
||||||
rating="${r[4]}"
|
rating="${r[FM['rating']]}"
|
||||||
timestamp="${timestamp}"
|
timestamp="${timestamp}"
|
||||||
pubdate="${pubdate}"
|
pubdate="${pubdate}"
|
||||||
size="${r[6]}"
|
size="${r[FM['size']]}"
|
||||||
isbn="${r[14] if r[14] else ''}"
|
isbn="${r[FM['isbn']] if r[FM['isbn']] else ''}"
|
||||||
formats="${r[13] if r[13] else ''}"
|
formats="${r[FM['formats']] if r[FM['formats']] else ''}"
|
||||||
series = "${r[9] if r[9] else ''}"
|
series = "${r[FM['series']] if r[FM['series']] else ''}"
|
||||||
series_index="${r[10]}"
|
series_index="${r[FM['series_index']]}"
|
||||||
tags="${r[7] if r[7] else ''}"
|
tags="${r[FM['tags']] if r[FM['tags']] else ''}"
|
||||||
publisher="${r[3] if r[3] else ''}">${r[8] if r[8] else ''}
|
publisher="${r[FM['publisher']] if r[FM['publisher']] else ''}">${r[FM['comments']] if r[FM['comments']] else ''}
|
||||||
</book>
|
</book>
|
||||||
''')
|
''')
|
||||||
|
|
||||||
@ -86,13 +86,13 @@ class LibraryServer(object):
|
|||||||
MOBILE_BOOK = textwrap.dedent('''\
|
MOBILE_BOOK = textwrap.dedent('''\
|
||||||
<tr xmlns:py="http://genshi.edgewall.org/">
|
<tr xmlns:py="http://genshi.edgewall.org/">
|
||||||
<td class="thumbnail">
|
<td class="thumbnail">
|
||||||
<img type="image/jpeg" src="/get/thumb/${r[0]}" border="0"/>
|
<img type="image/jpeg" src="/get/thumb/${r[FM['id']]}" border="0"/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<py:for each="format in r[13].split(',')">
|
<py:for each="format in r[FM['formats']].split(',')">
|
||||||
<span class="button"><a href="/get/${format}/${authors}-${r[1]}_${r[0]}.${format}">${format.lower()}</a></span>
|
<span class="button"><a href="/get/${format}/${authors}-${r[FM['title']]}_${r[FM['id']]}.${format}">${format.lower()}</a></span>
|
||||||
</py:for>
|
</py:for>
|
||||||
${r[1]}${(' ['+r[9]+'-'+r[10]+']') if r[9] else ''} by ${authors} - ${r[6]/1024}k - ${r[3] if r[3] else ''} ${pubdate} ${'['+r[7]+']' if r[7] else ''}
|
${r[FM['title']]}${(' ['+r[FM['series']]+'-'+r[FM['series_index']]+']') if r[FM['series']] else ''} by ${authors} - ${r[FM['size']]/1024}k - ${r[FM['publisher']] if r[FM['publisher']] else ''} ${pubdate} ${'['+r[FM['tags']]+']' if r[FM['tags']] else ''}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
''')
|
''')
|
||||||
@ -628,22 +628,23 @@ class LibraryServer(object):
|
|||||||
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
|
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
|
||||||
record_list = list(iter(self.db))
|
record_list = list(iter(self.db))
|
||||||
|
|
||||||
|
FM = self.db.FIELD_MAP
|
||||||
# Sort the record list
|
# Sort the record list
|
||||||
if sortby == "bytitle" or authorid or tagid:
|
if sortby == "bytitle" or authorid or tagid:
|
||||||
record_list.sort(lambda x, y:
|
record_list.sort(lambda x, y:
|
||||||
cmp(title_sort(x[self.db.FIELD_MAP['title']]),
|
cmp(title_sort(x[FM['title']]),
|
||||||
title_sort(y[self.db.FIELD_MAP['title']])))
|
title_sort(y[FM['title']])))
|
||||||
elif seriesid:
|
elif seriesid:
|
||||||
record_list.sort(lambda x, y:
|
record_list.sort(lambda x, y:
|
||||||
cmp(x[self.db.FIELD_MAP['series_index']],
|
cmp(x[FM['series_index']],
|
||||||
y[self.db.FIELD_MAP['series_index']]))
|
y[FM['series_index']]))
|
||||||
else: # Sort by date
|
else: # Sort by date
|
||||||
record_list = reversed(record_list)
|
record_list = reversed(record_list)
|
||||||
|
|
||||||
|
|
||||||
fmts = self.db.FIELD_MAP['formats']
|
fmts = FM['formats']
|
||||||
pat = re.compile(r'EPUB|PDB', re.IGNORECASE)
|
pat = re.compile(r'EPUB|PDB', re.IGNORECASE)
|
||||||
record_list = [x for x in record_list if x[0] in ids and
|
record_list = [x for x in record_list if x[FM['id']] in ids and
|
||||||
pat.search(x[fmts] if x[fmts] else '') is not None]
|
pat.search(x[fmts] if x[fmts] else '') is not None]
|
||||||
next_offset = offset + self.max_stanza_items
|
next_offset = offset + self.max_stanza_items
|
||||||
nrecord_list = record_list[offset:next_offset]
|
nrecord_list = record_list[offset:next_offset]
|
||||||
@ -663,10 +664,10 @@ class LibraryServer(object):
|
|||||||
) % '&'.join(q)
|
) % '&'.join(q)
|
||||||
|
|
||||||
for record in nrecord_list:
|
for record in nrecord_list:
|
||||||
r = record[self.db.FIELD_MAP['formats']]
|
r = record[FM['formats']]
|
||||||
r = r.upper() if r else ''
|
r = r.upper() if r else ''
|
||||||
|
|
||||||
z = record[self.db.FIELD_MAP['authors']]
|
z = record[FM['authors']]
|
||||||
if not z:
|
if not z:
|
||||||
z = _('Unknown')
|
z = _('Unknown')
|
||||||
authors = ' & '.join([i.replace('|', ',') for i in
|
authors = ' & '.join([i.replace('|', ',') for i in
|
||||||
@ -674,19 +675,19 @@ class LibraryServer(object):
|
|||||||
|
|
||||||
# Setup extra description
|
# Setup extra description
|
||||||
extra = []
|
extra = []
|
||||||
rating = record[self.db.FIELD_MAP['rating']]
|
rating = record[FM['rating']]
|
||||||
if rating > 0:
|
if rating > 0:
|
||||||
rating = ''.join(repeat('★', rating))
|
rating = ''.join(repeat('★', rating))
|
||||||
extra.append('RATING: %s<br />'%rating)
|
extra.append('RATING: %s<br />'%rating)
|
||||||
tags = record[self.db.FIELD_MAP['tags']]
|
tags = record[FM['tags']]
|
||||||
if tags:
|
if tags:
|
||||||
extra.append('TAGS: %s<br />'%\
|
extra.append('TAGS: %s<br />'%\
|
||||||
prepare_string_for_xml(', '.join(tags.split(','))))
|
prepare_string_for_xml(', '.join(tags.split(','))))
|
||||||
series = record[self.db.FIELD_MAP['series']]
|
series = record[FM['series']]
|
||||||
if series:
|
if series:
|
||||||
extra.append('SERIES: %s [%s]<br />'%\
|
extra.append('SERIES: %s [%s]<br />'%\
|
||||||
(prepare_string_for_xml(series),
|
(prepare_string_for_xml(series),
|
||||||
fmt_sidx(float(record[self.db.FIELD_MAP['series_index']]))))
|
fmt_sidx(float(record[FM['series_index']]))))
|
||||||
|
|
||||||
fmt = 'epub' if 'EPUB' in r else 'pdb'
|
fmt = 'epub' if 'EPUB' in r else 'pdb'
|
||||||
mimetype = guess_type('dummy.'+fmt)[0]
|
mimetype = guess_type('dummy.'+fmt)[0]
|
||||||
@ -699,17 +700,18 @@ class LibraryServer(object):
|
|||||||
authors=authors,
|
authors=authors,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
series=series,
|
series=series,
|
||||||
FM=self.db.FIELD_MAP,
|
FM=FM,
|
||||||
extra='\n'.join(extra),
|
extra='\n'.join(extra),
|
||||||
mimetype=mimetype,
|
mimetype=mimetype,
|
||||||
fmt=fmt,
|
fmt=fmt,
|
||||||
urn=record[self.db.FIELD_MAP['uuid']],
|
urn=record[FM['uuid']],
|
||||||
timestamp=strftime('%Y-%m-%dT%H:%M:%S+00:00', record[5])
|
timestamp=strftime('%Y-%m-%dT%H:%M:%S+00:00',
|
||||||
|
record[FM['timestamp']])
|
||||||
)
|
)
|
||||||
books.append(self.STANZA_ENTRY.generate(**data)\
|
books.append(self.STANZA_ENTRY.generate(**data)\
|
||||||
.render('xml').decode('utf8'))
|
.render('xml').decode('utf8'))
|
||||||
|
|
||||||
return self.STANZA.generate(subtitle='', data=books, FM=self.db.FIELD_MAP,
|
return self.STANZA.generate(subtitle='', data=books, FM=FM,
|
||||||
next_link=next_link, updated=updated, id='urn:calibre:main').render('xml')
|
next_link=next_link, updated=updated, id='urn:calibre:main').render('xml')
|
||||||
|
|
||||||
|
|
||||||
@ -734,23 +736,25 @@ class LibraryServer(object):
|
|||||||
raise cherrypy.HTTPError(400, 'num: %s is not an integer'%num)
|
raise cherrypy.HTTPError(400, 'num: %s is not an integer'%num)
|
||||||
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
|
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
|
||||||
ids = sorted(ids)
|
ids = sorted(ids)
|
||||||
items = [r for r in iter(self.db) if r[0] in ids]
|
FM = self.db.FIELD_MAP
|
||||||
|
items = copy.deepcopy([r for r in iter(self.db) if r[FM['id']] in ids])
|
||||||
if sort is not None:
|
if sort is not None:
|
||||||
self.sort(items, sort, (order.lower().strip() == 'ascending'))
|
self.sort(items, sort, (order.lower().strip() == 'ascending'))
|
||||||
|
|
||||||
book, books = MarkupTemplate(self.MOBILE_BOOK), []
|
book, books = MarkupTemplate(self.MOBILE_BOOK), []
|
||||||
for record in items[(start-1):(start-1)+num]:
|
for record in items[(start-1):(start-1)+num]:
|
||||||
if record[13] is None:
|
if record[FM['formats']] is None:
|
||||||
record[13] = ''
|
record[FM['formats']] = ''
|
||||||
if record[6] is None:
|
if record[FM['size']] is None:
|
||||||
record[6] = 0
|
record[FM['size']] = 0
|
||||||
aus = record[2] if record[2] else __builtin__._('Unknown')
|
aus = record[FM['authors']] if record[FM['authors']] else __builtin__._('Unknown')
|
||||||
authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
|
authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
|
||||||
record[10] = fmt_sidx(float(record[10]))
|
record[FM['series_index']] = \
|
||||||
ts, pd = strftime('%Y/%m/%d %H:%M:%S', record[5]), \
|
fmt_sidx(float(record[FM['series_index']]))
|
||||||
strftime('%Y/%m/%d %H:%M:%S', record[self.db.FIELD_MAP['pubdate']])
|
ts, pd = strftime('%Y/%m/%d %H:%M:%S', record[FM['timestamp']]), \
|
||||||
|
strftime('%Y/%m/%d %H:%M:%S', record[FM['pubdate']])
|
||||||
books.append(book.generate(r=record, authors=authors, timestamp=ts,
|
books.append(book.generate(r=record, authors=authors, timestamp=ts,
|
||||||
pubdate=pd).render('xml').decode('utf-8'))
|
pubdate=pd, FM=FM).render('xml').decode('utf-8'))
|
||||||
updated = self.db.last_modified()
|
updated = self.db.last_modified()
|
||||||
|
|
||||||
cherrypy.response.headers['Content-Type'] = 'text/html; charset=utf-8'
|
cherrypy.response.headers['Content-Type'] = 'text/html; charset=utf-8'
|
||||||
@ -759,8 +763,9 @@ class LibraryServer(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 self.MOBILE.generate(books=books, start=start, updated=updated, search=search, sort=sort, order=order, num=num,
|
return self.MOBILE.generate(books=books, start=start, updated=updated,
|
||||||
total=len(ids), url_base=url_base).render('html')
|
search=search, sort=sort, order=order, num=num, FM=FM,
|
||||||
|
total=len(ids), url_base=url_base).render('html')
|
||||||
|
|
||||||
|
|
||||||
@expose
|
@expose
|
||||||
@ -785,25 +790,27 @@ class LibraryServer(object):
|
|||||||
order = order.lower().strip() == 'ascending'
|
order = order.lower().strip() == 'ascending'
|
||||||
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
|
ids = self.db.data.parse(search) if search and search.strip() else self.db.data.universal_set()
|
||||||
ids = sorted(ids)
|
ids = sorted(ids)
|
||||||
items = [r for r in iter(self.db) if r[0] in ids]
|
FM = self.db.FIELD_MAP
|
||||||
|
items = copy.deepcopy([r for r in iter(self.db) if r[FM['id']] in ids])
|
||||||
if sort is not None:
|
if sort is not None:
|
||||||
self.sort(items, sort, order)
|
self.sort(items, sort, order)
|
||||||
|
|
||||||
book, books = MarkupTemplate(self.BOOK), []
|
book, books = MarkupTemplate(self.BOOK), []
|
||||||
for record in items[start:start+num]:
|
for record in items[start:start+num]:
|
||||||
aus = record[2] if record[2] else __builtin__._('Unknown')
|
aus = record[FM['authors']] if record[FM['authors']] else __builtin__._('Unknown')
|
||||||
authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
|
authors = '|'.join([i.replace('|', ',') for i in aus.split(',')])
|
||||||
record[10] = fmt_sidx(float(record[10]))
|
record[FM['series_index']] = \
|
||||||
ts, pd = strftime('%Y/%m/%d %H:%M:%S', record[5]), \
|
fmt_sidx(float(record[FM['series_index']]))
|
||||||
strftime('%Y/%m/%d %H:%M:%S', record[self.db.FIELD_MAP['pubdate']])
|
ts, pd = strftime('%Y/%m/%d %H:%M:%S', record[FM['timestamp']]), \
|
||||||
|
strftime('%Y/%m/%d %H:%M:%S', record[FM['pubdate']])
|
||||||
books.append(book.generate(r=record, authors=authors, timestamp=ts,
|
books.append(book.generate(r=record, authors=authors, timestamp=ts,
|
||||||
pubdate=pd).render('xml').decode('utf-8'))
|
pubdate=pd, FM=FM).render('xml').decode('utf-8'))
|
||||||
updated = self.db.last_modified()
|
updated = self.db.last_modified()
|
||||||
|
|
||||||
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
cherrypy.response.headers['Content-Type'] = 'text/xml'
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
|
||||||
return self.LIBRARY.generate(books=books, start=start, updated=updated,
|
return self.LIBRARY.generate(books=books, start=start, updated=updated,
|
||||||
total=len(ids)).render('xml')
|
total=len(ids), FM=FM).render('xml')
|
||||||
|
|
||||||
@expose
|
@expose
|
||||||
def index(self, **kwargs):
|
def index(self, **kwargs):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user