~ubuntu-branches/ubuntu/oneiric/calibre/oneiric

« back to all changes in this revision

Viewing changes to src/calibre/devices/usbms/books.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2010-06-21 10:18:08 UTC
  • mfrom: (1.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20100621101808-aue828f532tmo4zt
Tags: 0.7.2+dfsg-1
* New major upstream version. See http://calibre-ebook.com/new-in/seven for
  details.
* Refresh patches to apply cleanly.
* debian/control: Bump python-cssutils to >= 0.9.7~ to ensure the existence
  of the CSSRuleList.rulesOfType attribute. This makes epub conversion work
  again. (Closes: #584756)
* Add debian/local/calibre-mount-helper: Simple and safe replacement for
  upstream's calibre-mount-helper, using udisks --mount and eject.
  (Closes: #584915, LP: #561958)

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
5
5
__docformat__ = 'restructuredtext en'
6
6
 
7
 
import os
8
 
import re
9
 
import time
 
7
import os, re, time, sys
10
8
 
 
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
12
 
 
13
 
class Book(object):
14
 
 
15
 
    def __init__(self, path, title, authors, mime):
16
 
        self.title = title
17
 
        self.authors = authors
18
 
        self.mime = mime
19
 
        self.size = os.path.getsize(path)
 
12
from calibre.constants import filesystem_encoding, preferred_encoding
 
13
from calibre import isbytestring
 
14
 
 
15
class Book(MetaInformation):
 
16
 
 
17
    BOOK_ATTRS = ['lpath', 'size', 'mime', 'device_collections']
 
18
 
 
19
    JSON_ATTRS = [
 
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',
 
24
        'uuid',
 
25
    ]
 
26
 
 
27
    def __init__(self, prefix, lpath, size=None, other=None):
 
28
        from calibre.ebooks.metadata.meta import path_to_ext
 
29
 
 
30
        MetaInformation.__init__(self, '')
 
31
 
 
32
        self.device_collections = []
 
33
        self.path = os.path.join(prefix, lpath)
 
34
        if os.sep == '\\':
 
35
            self.path = self.path.replace('/', '\\')
 
36
            self.lpath = lpath.replace('\\', '/')
 
37
        else:
 
38
            self.lpath = lpath
 
39
        self.mime = mime_type_ext(path_to_ext(lpath))
 
40
        self.size = size # will be set later if None
20
41
        try:
21
 
            self.datetime = time.gmtime(os.path.getctime(path))
22
 
        except ValueError:
 
42
            self.datetime = time.gmtime(os.path.getctime(self.path))
 
43
        except:
23
44
            self.datetime = time.gmtime()
24
 
        self.path = path
25
 
        self.thumbnail = None
26
 
        self.tags = []
 
45
        if other:
 
46
            self.smart_update(other)
27
47
 
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)
 
51
 
 
52
    @dynamic_property
 
53
    def db_id(self):
 
54
        doc = '''The database id in the application database that this file corresponds to'''
 
55
        def fget(self):
 
56
            match = re.search(r'_(\d+)$', self.lpath.rpartition('.')[0])
 
57
            if match:
 
58
                return int(match.group(1))
 
59
            return None
 
60
        return property(fget=fget, doc=doc)
30
61
 
31
62
    @dynamic_property
32
63
    def title_sorter(self):
39
70
    def thumbnail(self):
40
71
        return None
41
72
 
42
 
    def __str__(self):
43
 
        '''
44
 
        Return a utf-8 encoded string with title author and path information
45
 
        '''
46
 
        return self.title.encode('utf-8') + " by " + \
47
 
               self.authors.encode('utf-8') + " at " + self.path.encode('utf-8')
48
 
 
 
73
    def smart_update(self, other):
 
74
        '''
 
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.
 
77
        '''
 
78
 
 
79
        MetaInformation.smart_update(self, other)
 
80
 
 
81
        for attr in self.BOOK_ATTRS:
 
82
            if hasattr(other, attr):
 
83
                val = getattr(other, attr, None)
 
84
                setattr(self, attr, val)
 
85
 
 
86
    def to_json(self):
 
87
        json = {}
 
88
        for attr in self.JSON_ATTRS:
 
89
            val = getattr(self, attr)
 
90
            if isbytestring(val):
 
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]
 
96
            json[attr] = val
 
97
        return json
49
98
 
50
99
class BookList(_BookList):
51
100
 
52
 
    def supports_tags(self):
53
 
        return False
54
 
 
55
 
    def set_tags(self, book, tags):
56
 
        pass
 
101
    def __init__(self, oncard, prefix, settings):
 
102
        _BookList.__init__(self, oncard, prefix, settings)
 
103
        self._bookmap = {}
 
104
 
 
105
    def supports_collections(self):
 
106
        return False
 
107
 
 
108
    def add_book(self, book, replace_metadata):
 
109
        try:
 
110
            b = self.index(book)
 
111
        except (ValueError, IndexError):
 
112
            b = None
 
113
        if b is None:
 
114
            self.append(book)
 
115
            return True
 
116
        if replace_metadata:
 
117
            self[b].smart_update(book)
 
118
            return True
 
119
        return False
 
120
 
 
121
    def remove_book(self, book):
 
122
        self.remove(book)
 
123
 
 
124
    def get_collections(self):
 
125
        return {}
 
126
 
 
127
class CollectionsBookList(BookList):
 
128
 
 
129
    def supports_collections(self):
 
130
        return True
 
131
 
 
132
    def get_collections(self, collection_attributes):
 
133
        collections = {}
 
134
        series_categories = set([])
 
135
        collection_attributes = list(collection_attributes)+['device_collections']
 
136
        for attr in collection_attributes:
 
137
            attr = attr.strip()
 
138
            for book in self:
 
139
                val = getattr(book, attr, None)
 
140
                if not val: continue
 
141
                if isbytestring(val):
 
142
                    val = val.decode(preferred_encoding, 'replace')
 
143
                if isinstance(val, (list, tuple)):
 
144
                    val = list(val)
 
145
                elif isinstance(val, unicode):
 
146
                    val = [val]
 
147
                for category in val:
 
148
                    if attr == 'tags' and len(category) > 1 and \
 
149
                            category[0] == '[' and category[-1] == ']':
 
150
                        continue
 
151
                    if category not in collections:
 
152
                        collections[category] = []
 
153
                    if book not in collections[category]:
 
154
                        collections[category].append(book)
 
155
                        if attr == 'series':
 
156
                            series_categories.add(category)
 
157
 
 
158
        # Sort collections
 
159
        for category, books in collections.items():
 
160
            def tgetter(x):
 
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
 
165
                def getter(x):
 
166
                    return getattr(x, 'series_index', sys.maxint)
 
167
                books.sort(cmp=lambda x,y:cmp(getter(x), getter(y)))
 
168
        return collections
 
169