~ubuntu-branches/debian/experimental/calibre/experimental

« back to all changes in this revision

Viewing changes to src/calibre/library/database2.py

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2012-02-10 07:35:00 UTC
  • mfrom: (1.3.30)
  • Revision ID: package-import@ubuntu.com-20120210073500-9hx5hpketc9hb59i
Tags: 0.8.38+dfsg-1
* New upstream release.
* debian/control: Bump Standards-Version to 3.9.2. No changes necessary.

Show diffs side-by-side

added added

removed removed

Lines of Context:
40
40
from calibre.utils.recycle_bin import delete_file, delete_tree
41
41
from calibre.utils.formatter_functions import load_user_template_functions
42
42
from calibre.db.errors import NoSuchFormat
 
43
from calibre.db.lazy import FormatMetadata, FormatsList
43
44
from calibre.utils.localization import (canonicalize_lang,
44
45
        calibre_langcode_to_name)
45
46
 
81
82
    def __repr__(self):
82
83
        return str(self)
83
84
 
84
 
 
85
85
class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
86
86
    '''
87
87
    An ebook metadata database that stores references to ebook files on disk.
170
170
        except:
171
171
            traceback.print_exc()
172
172
        self.field_metadata = FieldMetadata()
 
173
        self.format_filename_cache = defaultdict(dict)
173
174
        self._library_id_ = None
174
175
        # Create the lock to be used to guard access to the metadata writer
175
176
        # queues. This must be an RLock, not a Lock
310
311
        if not self.is_second_db:
311
312
            load_user_template_functions(self.prefs.get('user_template_functions', []))
312
313
 
 
314
        # Load the format filename cache
 
315
        self.refresh_format_cache()
 
316
 
313
317
        self.conn.executescript('''
314
318
        DROP TRIGGER IF EXISTS author_insert_trg;
315
319
        CREATE TEMP TRIGGER author_insert_trg
502
506
        self.refresh_ondevice = functools.partial(self.data.refresh_ondevice, self)
503
507
        self.refresh()
504
508
        self.last_update_check = self.last_modified()
505
 
        self.format_metadata_cache = defaultdict(dict)
506
509
 
507
510
    def break_cycles(self):
508
511
        self.data.break_cycles()
521
524
        ''' Return last modified time as a UTC datetime object'''
522
525
        return utcfromtimestamp(os.stat(self.dbpath).st_mtime)
523
526
 
 
527
    def refresh_format_cache(self):
 
528
        self.format_filename_cache = defaultdict(dict)
 
529
        for book_id, fmt, name in self.conn.get(
 
530
                'SELECT book,format,name FROM data'):
 
531
            self.format_filename_cache[book_id][fmt.upper() if fmt else ''] = name
 
532
        self.format_metadata_cache = defaultdict(dict)
524
533
 
525
534
    def check_if_modified(self):
526
535
        if self.last_modified() > self.last_update_check:
546
555
        authors = self.authors(id, index_is_id=True)
547
556
        if not authors:
548
557
            authors = _('Unknown')
549
 
        author = ascii_filename(authors.split(',')[0]
 
558
        author = ascii_filename(authors.split(',')[0].replace('|', ',')
550
559
                    )[:self.PATH_LIMIT].decode('ascii', 'replace')
551
560
        title  = ascii_filename(self.title(id, index_is_id=True)
552
561
                    )[:self.PATH_LIMIT].decode('ascii', 'replace')
565
574
        authors = self.authors(id, index_is_id=True)
566
575
        if not authors:
567
576
            authors = _('Unknown')
568
 
        author = ascii_filename(authors.split(',')[0]
 
577
        author = ascii_filename(authors.split(',')[0].replace('|', ',')
569
578
                    )[:self.PATH_LIMIT].decode('ascii', 'replace')
570
579
        title  = ascii_filename(self.title(id, index_is_id=True)
571
580
                    )[:self.PATH_LIMIT].decode('ascii', 'replace')
599
608
        fname = self.construct_file_name(id)
600
609
        changed = False
601
610
        for format in formats:
602
 
            name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all=False)
 
611
            name = self.format_filename_cache[id].get(format.upper(), None)
603
612
            if name and name != fname:
604
613
                changed = True
605
614
                break
911
920
        aum = []
912
921
        aus = {}
913
922
        aul = {}
914
 
        for (author, author_sort, link) in aut_list:
915
 
            aut = author.replace('|', ',')
916
 
            aum.append(aut)
917
 
            aus[aut] = author_sort.replace('|', ',')
918
 
            aul[aut] = link
 
923
        try:
 
924
            for (author, author_sort, link) in aut_list:
 
925
                aut = author.replace('|', ',')
 
926
                aum.append(aut)
 
927
                aus[aut] = author_sort.replace('|', ',')
 
928
                aul[aut] = link
 
929
        except ValueError:
 
930
            # Author has either ::: or :#: in it
 
931
            for x in row[fm['authors']].split(','):
 
932
                aum.append(x.replace('|', ','))
 
933
                aul[aum[-1]] = ''
 
934
                aus[aum[-1]] = aum[-1]
919
935
        mi.title       = row[fm['title']]
920
936
        mi.authors     = aum
921
937
        mi.author_sort = row[fm['author_sort']]
937
953
            good_formats = None
938
954
        else:
939
955
            formats = sorted(formats.split(','))
940
 
            good_formats = []
941
 
            for f in formats:
942
 
                try:
943
 
                    mi.format_metadata[f] = self.format_metadata(id, f)
944
 
                except:
945
 
                    pass
946
 
                else:
947
 
                    good_formats.append(f)
 
956
            mi.format_metadata = FormatMetadata(self, id, formats)
 
957
            good_formats = FormatsList(formats, mi.format_metadata)
948
958
        mi.formats = good_formats
949
959
        tags = row[fm['tags']]
950
960
        if tags:
1138
1148
 
1139
1149
    def format_files(self, index, index_is_id=False):
1140
1150
        id = index if index_is_id else self.id(index)
1141
 
        try:
1142
 
            formats = self.conn.get('SELECT name,format FROM data WHERE book=?', (id,))
1143
 
            formats = map(lambda x:(x[0], x[1]), formats)
1144
 
            return formats
1145
 
        except:
1146
 
            return []
 
1151
        return [(v, k) for k, v in self.format_filename_cache[id].iteritems()]
1147
1152
 
1148
1153
    def formats(self, index, index_is_id=False, verify_formats=True):
1149
1154
        ''' Return available formats as a comma separated list or None if there are no available formats '''
1229
1234
        '''
1230
1235
        id = index if index_is_id else self.id(index)
1231
1236
        try:
1232
 
            name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all=False)
 
1237
            name = self.format_filename_cache[id][format.upper()]
1233
1238
        except:
1234
1239
            return None
1235
1240
        if name:
1326
1331
    def add_format(self, index, format, stream, index_is_id=False, path=None,
1327
1332
            notify=True, replace=True):
1328
1333
        id = index if index_is_id else self.id(index)
1329
 
        if format:
1330
 
            self.format_metadata_cache[id].pop(format.upper(), None)
 
1334
        if not format: format = ''
 
1335
        self.format_metadata_cache[id].pop(format.upper(), None)
 
1336
        name = self.format_filename_cache[id].get(format.upper(), None)
1331
1337
        if path is None:
1332
1338
            path = os.path.join(self.library_path, self.path(id, index_is_id=True))
1333
 
        name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all=False)
1334
1339
        if name and not replace:
1335
1340
            return False
1336
1341
        name = self.construct_file_name(id)
1348
1353
        self.conn.execute('INSERT OR REPLACE INTO data (book,format,uncompressed_size,name) VALUES (?,?,?,?)',
1349
1354
                          (id, format.upper(), size, name))
1350
1355
        self.conn.commit()
 
1356
        self.format_filename_cache[id][format.upper()] = name
1351
1357
        self.refresh_ids([id])
1352
1358
        if notify:
1353
1359
            self.notify('metadata', [id])
1395
1401
    def remove_format(self, index, format, index_is_id=False, notify=True,
1396
1402
                      commit=True, db_only=False):
1397
1403
        id = index if index_is_id else self.id(index)
1398
 
        if format:
1399
 
            self.format_metadata_cache[id].pop(format.upper(), None)
1400
 
        name = self.conn.get('SELECT name FROM data WHERE book=? AND format=?', (id, format), all=False)
 
1404
        if not format: format = ''
 
1405
        self.format_metadata_cache[id].pop(format.upper(), None)
 
1406
        name = self.format_filename_cache[id].get(format.upper(), None)
1401
1407
        if name:
1402
1408
            if not db_only:
1403
1409
                try:
1406
1412
                        delete_file(path)
1407
1413
                except:
1408
1414
                    traceback.print_exc()
 
1415
            self.format_filename_cache[id].pop(format.upper(), None)
1409
1416
            self.conn.execute('DELETE FROM data WHERE book=? AND format=?', (id, format.upper()))
1410
1417
            if commit:
1411
1418
                self.conn.commit()
1918
1925
 
1919
1926
    ############# End get_categories
1920
1927
 
1921
 
    def tags_older_than(self, tag, delta, must_have_tag=None):
 
1928
    def tags_older_than(self, tag, delta, must_have_tag=None,
 
1929
            must_have_authors=None):
1922
1930
        '''
1923
1931
        Return the ids of all books having the tag ``tag`` that are older than
1924
1932
        than the specified time. tag comparison is case insensitive.
1927
1935
        the tag are returned.
1928
1936
        :param must_have_tag: If not None the list of matches will be
1929
1937
        restricted to books that have this tag
 
1938
        :param must_have_authors: A list of authors. If not None the list of
 
1939
        matches will be restricted to books that have these authors (case
 
1940
        insensitive).
1930
1941
        '''
1931
1942
        tag = tag.lower().strip()
1932
1943
        mht = must_have_tag.lower().strip() if must_have_tag else None
1934
1945
        tindex = self.FIELD_MAP['timestamp']
1935
1946
        gindex = self.FIELD_MAP['tags']
1936
1947
        iindex = self.FIELD_MAP['id']
 
1948
        aindex = self.FIELD_MAP['authors']
 
1949
        mah = must_have_authors
 
1950
        if mah is not None:
 
1951
            mah = [x.replace(',', '|').lower() for x in mah]
 
1952
            mah = ','.join(mah)
1937
1953
        for r in self.data._data:
1938
1954
            if r is not None:
1939
1955
                if delta is None or (now - r[tindex]) > delta:
 
1956
                    if mah:
 
1957
                        authors = r[aindex] or ''
 
1958
                        if authors.lower() != mah:
 
1959
                            continue
1940
1960
                    tags = r[gindex]
1941
1961
                    if tags:
1942
1962
                        tags = [x.strip() for x in tags.lower().split(',')]
3121
3141
        stream.seek(0)
3122
3142
        mi = get_metadata(stream, format, use_libprs_metadata=False,
3123
3143
                force_read_metadata=True)
 
3144
        # Force the author to calibre as the auto delete of old news checks for
 
3145
        # both the author==calibre and the tag News
 
3146
        mi.authors = ['calibre']
3124
3147
        stream.seek(0)
3125
3148
        if mi.series_index is None:
3126
3149
            mi.series_index = self.get_next_series_num_for(mi.series)
3172
3195
 
3173
3196
    def create_book_entry(self, mi, cover=None, add_duplicates=True,
3174
3197
            force_id=None):
 
3198
        if mi.tags:
 
3199
            mi.tags = list(mi.tags)
3175
3200
        self._add_newbook_tag(mi)
3176
3201
        if not add_duplicates and self.has_book(mi):
3177
3202
            return None