~ubuntu-branches/ubuntu/karmic/calibre/karmic

« back to all changes in this revision

Viewing changes to src/calibre/ebooks/metadata/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-07-30 12:49:41 UTC
  • mfrom: (1.3.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20090730124941-qjdsmri25zt8zocn
Tags: 0.6.3+dfsg-0ubuntu1
* New upstream release. Please see http://calibre.kovidgoyal.net/new_in_6/
  for the list of new features and changes.
* remove_postinstall.patch: Update for new version.
* build_debug.patch: Does not apply any more, disable for now. Might not be
  necessary any more.
* debian/copyright: Fix reference to versionless GPL.
* debian/rules: Drop obsolete dh_desktop call.
* debian/rules: Add workaround for weird Python 2.6 setuptools behaviour of
  putting compiled .so files into src/calibre/plugins/calibre/plugins
  instead of src/calibre/plugins.
* debian/rules: Drop hal fdi moving, new upstream version does not use hal
  any more. Drop hal dependency, too.
* debian/rules: Install udev rules into /lib/udev/rules.d.
* Add debian/calibre.preinst: Remove unmodified
  /etc/udev/rules.d/95-calibre.rules on upgrade.
* debian/control: Bump Python dependencies to 2.6, since upstream needs
  it now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
"""
7
7
Provides abstraction for metadata reading.writing from a variety of ebook formats.
8
8
"""
9
 
import os, mimetypes, sys
 
9
import os, mimetypes, sys, re
10
10
from urllib import unquote, quote
11
11
from urlparse import urlparse
12
12
 
13
13
 
14
14
from calibre import relpath
15
 
from calibre.utils.config import OptionParser
16
15
 
 
16
_author_pat = re.compile(',?\s+and\s+', re.IGNORECASE)
17
17
def string_to_authors(raw):
18
18
    raw = raw.replace('&&', u'\uffff')
 
19
    raw = _author_pat.sub('&', raw)
19
20
    authors = [a.strip().replace(u'\uffff', '&') for a in raw.split('&')]
20
21
    return authors
21
22
 
35
36
def authors_to_sort_string(authors):
36
37
    return ' & '.join(map(author_to_author_sort, authors))
37
38
 
38
 
def get_parser(extension):
39
 
    ''' Return an option parser with the basic metadata options already setup'''
40
 
    parser = OptionParser(usage='%prog [options] myfile.'+extension+'\n\nRead and write metadata from an ebook file.')
41
 
    parser.add_option("-t", "--title", action="store", type="string", \
42
 
                    dest="title", help=_("Set the book title"), default=None)
43
 
    parser.add_option("-a", "--authors", action="store", type="string", \
44
 
                    dest="authors", help=_("Set the authors"), default=None)
45
 
    parser.add_option("-c", "--category", action="store", type="string", \
46
 
                    dest="category", help=_("The category this book belongs to. E.g.: History"), default=None)
47
 
    parser.add_option('--comment', dest='comment', default=None, action='store',
48
 
                      help=_('Set the comment'))
49
 
    return parser
 
39
_title_pat = re.compile('^(A|The|An)\s+', re.IGNORECASE)
 
40
def title_sort(title):
 
41
    match = _title_pat.search(title)
 
42
    if match:
 
43
        prep = match.group(1)
 
44
        title = title.replace(prep, '') + ', ' + prep
 
45
    return title.strip()
 
46
 
 
47
coding = zip(
 
48
[1000,900,500,400,100,90,50,40,10,9,5,4,1],
 
49
["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
 
50
)
 
51
 
 
52
 
 
53
 
 
54
def roman(num):
 
55
    if num <= 0 or num >= 4000 or int(num) != num:
 
56
        return str(num)
 
57
    result = []
 
58
    for d, r in coding:
 
59
        while num >= d:
 
60
            result.append(r)
 
61
            num -= d
 
62
    return ''.join(result)
 
63
 
 
64
 
 
65
def fmt_sidx(i, fmt='%.2f', use_roman=False):
 
66
    if i is None or i == '':
 
67
        i = 1
 
68
    try:
 
69
        i = float(i)
 
70
    except TypeError:
 
71
        return str(i)
 
72
    if int(i) == float(i):
 
73
        return roman(int(i)) if use_roman else '%d'%int(i)
 
74
    return fmt%i
50
75
 
51
76
class Resource(object):
52
77
    '''
192
217
                     'publisher', 'series', 'series_index', 'rating',
193
218
                     'isbn', 'tags', 'cover_data', 'application_id', 'guide',
194
219
                     'manifest', 'spine', 'toc', 'cover', 'language',
195
 
                     'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc'):
 
220
                     'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc',
 
221
                     'pubdate', 'rights', 'publication_type'):
196
222
            if hasattr(mi, attr):
197
223
                setattr(ans, attr, getattr(mi, attr))
198
224
 
199
225
    def __init__(self, title, authors=(_('Unknown'),)):
200
226
        '''
201
 
        @param title: title or "Unknown" or a MetaInformation object
 
227
        @param title: title or ``_('Unknown')`` or a MetaInformation object
202
228
        @param authors: List of strings or []
203
229
        '''
204
230
        mi = None
217
243
        for x in ('author_sort', 'title_sort', 'comments', 'category', 'publisher',
218
244
                  'series', 'series_index', 'rating', 'isbn', 'language',
219
245
                  'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover',
220
 
                  'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc'
 
246
                  'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate',
 
247
                  'rights', 'publication_type',
221
248
                  ):
222
249
            setattr(self, x, getattr(mi, x, None))
223
250
 
236
263
                     'publisher', 'series', 'series_index', 'rating',
237
264
                     'isbn', 'application_id', 'manifest', 'spine', 'toc',
238
265
                     'cover', 'language', 'guide', 'book_producer',
239
 
                     'timestamp', 'lccn', 'lcc', 'ddc'):
 
266
                     'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate', 'rights',
 
267
                     'publication_type'):
240
268
            if hasattr(mi, attr):
241
269
                val = getattr(mi, attr)
242
270
                if val is not None:
243
271
                    setattr(self, attr, val)
244
272
 
245
 
        self.tags += mi.tags
 
273
        if mi.tags:
 
274
            self.tags += mi.tags
246
275
        self.tags = list(set(self.tags))
247
276
 
248
 
        if getattr(mi, 'cover_data', None) and mi.cover_data[0] is not None:
249
 
            self.cover_data = mi.cover_data
 
277
        if getattr(mi, 'cover_data', False):
 
278
            other_cover = mi.cover_data[-1]
 
279
            self_cover = self.cover_data[-1] if self.cover_data else ''
 
280
            if not self_cover: self_cover = ''
 
281
            if not other_cover: other_cover = ''
 
282
            if len(other_cover) > len(self_cover):
 
283
                self.cover_data = mi.cover_data
250
284
 
251
285
        my_comments = getattr(self, 'comments', '')
252
286
        other_comments = getattr(mi, 'comments', '')
261
295
        try:
262
296
            x = float(self.series_index)
263
297
        except ValueError:
264
 
            x = 1.0
265
 
        return '%d'%x if int(x) == x else '%.2f'%x
 
298
            x = 1
 
299
        return fmt_sidx(x)
 
300
 
 
301
    def authors_from_string(self, raw):
 
302
        self.authors = string_to_authors(raw)
266
303
 
267
304
    def __unicode__(self):
268
 
        ans = u''
269
 
        ans += u'Title    : ' + unicode(self.title) + u'\n'
 
305
        ans = []
 
306
        def fmt(x, y):
 
307
            ans.append(u'%-20s: %s'%(unicode(x), unicode(y)))
 
308
 
 
309
        fmt('Title', self.title)
 
310
        if self.title_sort:
 
311
            fmt('Title sort', self.title_sort)
270
312
        if self.authors:
271
 
            ans += u'Author   : ' + (' & '.join(self.authors) if self.authors is not None else _('Unknown'))
272
 
            ans += ((' [' + self.author_sort + ']') if self.author_sort else '') + u'\n'
 
313
            fmt('Author(s)',  authors_to_string(self.authors) + \
 
314
               ((' [' + self.author_sort + ']') if self.author_sort else ''))
273
315
        if self.publisher:
274
 
            ans += u'Publisher: '+ unicode(self.publisher) + u'\n'
 
316
            fmt('Publisher', self.publisher)
275
317
        if getattr(self, 'book_producer', False):
276
 
            ans += u'Producer : '+ unicode(self.book_producer) + u'\n'
 
318
            fmt('Book Producer', self.book_producer)
277
319
        if self.category:
278
 
            ans += u'Category : ' + unicode(self.category) + u'\n'
 
320
            fmt('Category', self.category)
279
321
        if self.comments:
280
 
            ans += u'Comments : ' + unicode(self.comments) + u'\n'
 
322
            fmt('Comments', self.comments)
281
323
        if self.isbn:
282
 
            ans += u'ISBN     : '     + unicode(self.isbn) + u'\n'
 
324
            fmt('ISBN', self.isbn)
283
325
        if self.tags:
284
 
            ans += u'Tags     : ' + u', '.join([unicode(t) for t in self.tags]) + '\n'
 
326
            fmt('Tags', u', '.join([unicode(t) for t in self.tags]))
285
327
        if self.series:
286
 
            ans += u'Series   : '+unicode(self.series) + ' #%s\n'%self.format_series_index()
 
328
            fmt('Series', self.series + ' #%s'%self.format_series_index())
287
329
        if self.language:
288
 
            ans += u'Language : '     + unicode(self.language) + u'\n'
 
330
            fmt('Language', self.language)
 
331
        if self.rating is not None:
 
332
            fmt('Rating', self.rating)
289
333
        if self.timestamp is not None:
290
 
            ans += u'Timestamp : ' + self.timestamp.isoformat(' ') + u'\n'
 
334
            fmt('Timestamp', self.timestamp.isoformat(' '))
 
335
        if self.pubdate is not None:
 
336
            fmt('Published', self.pubdate.isoformat(' '))
 
337
        if self.rights is not None:
 
338
            fmt('Rights', unicode(self.rights))
291
339
        if self.lccn:
292
 
            ans += u'LCCN : ' + unicode(self.lccn) + u'\n'
 
340
            fmt('LCCN', unicode(self.lccn))
293
341
        if self.lcc:
294
 
            ans += u'LCC : ' + unicode(self.lcc) + u'\n'
 
342
            fmt('LCC', unicode(self.lcc))
295
343
        if self.ddc:
296
 
            ans += u'DDC : ' + unicode(self.ddc) + u'\n'
297
 
        return ans.strip()
 
344
            fmt('DDC', unicode(self.ddc))
 
345
 
 
346
        return u'\n'.join(ans)
298
347
 
299
348
    def to_html(self):
300
349
        ans = [(_('Title'), unicode(self.title))]
315
364
        ans += [(_('Language'), unicode(self.language))]
316
365
        if self.timestamp is not None:
317
366
            ans += [(_('Timestamp'), unicode(self.timestamp.isoformat(' ')))]
 
367
        if self.pubdate is not None:
 
368
            ans += [(_('Published'), unicode(self.pubdate.isoformat(' ')))]
 
369
        if self.rights is not None:
 
370
            ans += [(_('Rights'), unicode(self.rights))]
318
371
        for i, x in enumerate(ans):
319
372
            ans[i] = u'<tr><td><b>%s</b></td><td>%s</td></tr>'%x
320
373
        return u'<table>%s</table>'%u'\n'.join(ans)
323
376
        return self.__unicode__().encode('utf-8')
324
377
 
325
378
    def __nonzero__(self):
326
 
        return bool(self.title or self.author or self.comments or self.category)
 
379
        return bool(self.title or self.author or self.comments or self.tags)