4
4
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
5
5
__docformat__ = 'restructuredtext en'
7
import os, re, time, sys
9
from calibre.ebooks.metadata import MetaInformation
10
from calibre.devices.mime import mime_type_ext
11
11
from calibre.devices.interface import BookList as _BookList
15
def __init__(self, path, title, authors, mime):
17
self.authors = authors
19
self.size = os.path.getsize(path)
12
from calibre.constants import filesystem_encoding, preferred_encoding
13
from calibre import isbytestring
15
class Book(MetaInformation):
17
BOOK_ATTRS = ['lpath', 'size', 'mime', 'device_collections']
20
'lpath', 'title', 'authors', 'mime', 'size', 'tags', 'author_sort',
21
'title_sort', 'comments', 'category', 'publisher', 'series',
22
'series_index', 'rating', 'isbn', 'language', 'application_id',
23
'book_producer', 'lccn', 'lcc', 'ddc', 'rights', 'publication_type',
27
def __init__(self, prefix, lpath, size=None, other=None):
28
from calibre.ebooks.metadata.meta import path_to_ext
30
MetaInformation.__init__(self, '')
32
self.device_collections = []
33
self.path = os.path.join(prefix, lpath)
35
self.path = self.path.replace('/', '\\')
36
self.lpath = lpath.replace('\\', '/')
39
self.mime = mime_type_ext(path_to_ext(lpath))
40
self.size = size # will be set later if None
21
self.datetime = time.gmtime(os.path.getctime(path))
42
self.datetime = time.gmtime(os.path.getctime(self.path))
23
44
self.datetime = time.gmtime()
46
self.smart_update(other)
28
48
def __eq__(self, other):
29
return self.path == other.path
49
# use lpath because the prefix can change, changing path
50
return self.lpath == getattr(other, 'lpath', None)
54
doc = '''The database id in the application database that this file corresponds to'''
56
match = re.search(r'_(\d+)$', self.lpath.rpartition('.')[0])
58
return int(match.group(1))
60
return property(fget=fget, doc=doc)
32
63
def title_sorter(self):
39
70
def thumbnail(self):
44
Return a utf-8 encoded string with title author and path information
46
return self.title.encode('utf-8') + " by " + \
47
self.authors.encode('utf-8') + " at " + self.path.encode('utf-8')
73
def smart_update(self, other):
75
Merge the information in C{other} into self. In case of conflicts, the information
76
in C{other} takes precedence, unless the information in C{other} is NULL.
79
MetaInformation.smart_update(self, other)
81
for attr in self.BOOK_ATTRS:
82
if hasattr(other, attr):
83
val = getattr(other, attr, None)
84
setattr(self, attr, val)
88
for attr in self.JSON_ATTRS:
89
val = getattr(self, attr)
91
enc = filesystem_encoding if attr == 'lpath' else preferred_encoding
92
val = val.decode(enc, 'replace')
93
elif isinstance(val, (list, tuple)):
94
val = [x.decode(preferred_encoding, 'replace') if
95
isbytestring(x) else x for x in val]
50
99
class BookList(_BookList):
52
def supports_tags(self):
55
def set_tags(self, book, tags):
101
def __init__(self, oncard, prefix, settings):
102
_BookList.__init__(self, oncard, prefix, settings)
105
def supports_collections(self):
108
def add_book(self, book, replace_metadata):
111
except (ValueError, IndexError):
117
self[b].smart_update(book)
121
def remove_book(self, book):
124
def get_collections(self):
127
class CollectionsBookList(BookList):
129
def supports_collections(self):
132
def get_collections(self, collection_attributes):
134
series_categories = set([])
135
collection_attributes = list(collection_attributes)+['device_collections']
136
for attr in collection_attributes:
139
val = getattr(book, attr, None)
141
if isbytestring(val):
142
val = val.decode(preferred_encoding, 'replace')
143
if isinstance(val, (list, tuple)):
145
elif isinstance(val, unicode):
148
if attr == 'tags' and len(category) > 1 and \
149
category[0] == '[' and category[-1] == ']':
151
if category not in collections:
152
collections[category] = []
153
if book not in collections[category]:
154
collections[category].append(book)
156
series_categories.add(category)
159
for category, books in collections.items():
161
return getattr(x, 'title_sort', 'zzzz')
162
books.sort(cmp=lambda x,y:cmp(tgetter(x), tgetter(y)))
163
if category in series_categories:
164
# Ensures books are sub sorted by title
166
return getattr(x, 'series_index', sys.maxint)
167
books.sort(cmp=lambda x,y:cmp(getter(x), getter(y)))