mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Initial implementation of remove_book(), needs testing
This commit is contained in:
parent
20920b37b2
commit
0711e03bd4
@ -29,7 +29,7 @@ from calibre.utils.magick.draw import save_cover_data_to
|
|||||||
from calibre.utils.recycle_bin import delete_tree, delete_file
|
from calibre.utils.recycle_bin import delete_tree, delete_file
|
||||||
from calibre.db.tables import (OneToOneTable, ManyToOneTable, ManyToManyTable,
|
from calibre.db.tables import (OneToOneTable, ManyToOneTable, ManyToManyTable,
|
||||||
SizeTable, FormatsTable, AuthorsTable, IdentifiersTable, PathTable,
|
SizeTable, FormatsTable, AuthorsTable, IdentifiersTable, PathTable,
|
||||||
CompositeTable, LanguagesTable, UUIDTable)
|
CompositeTable, UUIDTable)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -711,7 +711,6 @@ class DB(object):
|
|||||||
'authors':AuthorsTable,
|
'authors':AuthorsTable,
|
||||||
'formats':FormatsTable,
|
'formats':FormatsTable,
|
||||||
'identifiers':IdentifiersTable,
|
'identifiers':IdentifiersTable,
|
||||||
'languages':LanguagesTable,
|
|
||||||
}.get(col, ManyToManyTable)
|
}.get(col, ManyToManyTable)
|
||||||
tables[col] = cls(col, self.field_metadata[col].copy())
|
tables[col] = cls(col, self.field_metadata[col].copy())
|
||||||
|
|
||||||
@ -1165,5 +1164,16 @@ class DB(object):
|
|||||||
with lopen(path, 'rb') as f:
|
with lopen(path, 'rb') as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
|
def remove_books(self, path_map, permanent=False):
|
||||||
|
for book_id, path in path_map.iteritems():
|
||||||
|
if path:
|
||||||
|
path = os.path.join(self.library_path, path)
|
||||||
|
if os.path.exists(path):
|
||||||
|
self.rmtree(path, permanent=permanent)
|
||||||
|
parent = os.path.dirname(path)
|
||||||
|
if len(os.listdir(parent)) == 0:
|
||||||
|
self.rmtree(parent, permanent=permanent)
|
||||||
|
self.conn.executemany(
|
||||||
|
'DELETE FROM books WHERE id=?', [(x,) for x in path_map])
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -1129,6 +1129,19 @@ class Cache(object):
|
|||||||
self._add_format(book_id, fmt, stream_or_path, dbapi=dbapi)
|
self._add_format(book_id, fmt, stream_or_path, dbapi=dbapi)
|
||||||
return ids, duplicates
|
return ids, duplicates
|
||||||
|
|
||||||
|
@write_api
|
||||||
|
def remove_books(self, book_ids, permanent=False):
|
||||||
|
path_map = {}
|
||||||
|
for book_id in book_ids:
|
||||||
|
try:
|
||||||
|
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||||
|
except:
|
||||||
|
path = None
|
||||||
|
path_map[book_id] = path
|
||||||
|
self.backend.remove_books(path_map, permanent=permanent)
|
||||||
|
for field in self.fields.itervalues():
|
||||||
|
field.table.remove_books(book_ids, self.backend)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class SortKey(object): # {{{
|
class SortKey(object): # {{{
|
||||||
|
@ -20,6 +20,10 @@ _c_speedup = plugins['speedup'][0]
|
|||||||
|
|
||||||
ONE_ONE, MANY_ONE, MANY_MANY = xrange(3)
|
ONE_ONE, MANY_ONE, MANY_MANY = xrange(3)
|
||||||
|
|
||||||
|
class Null:
|
||||||
|
pass
|
||||||
|
null = Null()
|
||||||
|
|
||||||
def _c_convert_timestamp(val):
|
def _c_convert_timestamp(val):
|
||||||
if not val:
|
if not val:
|
||||||
return None
|
return None
|
||||||
@ -55,6 +59,9 @@ class Table(object):
|
|||||||
self.link_table = (link_table if link_table else
|
self.link_table = (link_table if link_table else
|
||||||
'books_%s_link'%self.metadata['table'])
|
'books_%s_link'%self.metadata['table'])
|
||||||
|
|
||||||
|
def remove_books(self, book_ids, db):
|
||||||
|
return set()
|
||||||
|
|
||||||
class VirtualTable(Table):
|
class VirtualTable(Table):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -83,6 +90,14 @@ class OneToOneTable(Table):
|
|||||||
self.metadata['column'], self.metadata['table'])):
|
self.metadata['column'], self.metadata['table'])):
|
||||||
self.book_col_map[row[0]] = self.unserialize(row[1])
|
self.book_col_map[row[0]] = self.unserialize(row[1])
|
||||||
|
|
||||||
|
def remove_books(self, book_ids, db):
|
||||||
|
clean = set()
|
||||||
|
for book_id in book_ids:
|
||||||
|
val = self.book_col_map.pop(book_id, null)
|
||||||
|
if val is not null:
|
||||||
|
clean.add(val)
|
||||||
|
return clean
|
||||||
|
|
||||||
class PathTable(OneToOneTable):
|
class PathTable(OneToOneTable):
|
||||||
|
|
||||||
def set_path(self, book_id, path, db):
|
def set_path(self, book_id, path, db):
|
||||||
@ -113,6 +128,15 @@ class UUIDTable(OneToOneTable):
|
|||||||
self.uuid_to_id_map.pop(self.book_col_map.get(book_id, None), None) # discard old uuid
|
self.uuid_to_id_map.pop(self.book_col_map.get(book_id, None), None) # discard old uuid
|
||||||
self.uuid_to_id_map[uuid] = book_id
|
self.uuid_to_id_map[uuid] = book_id
|
||||||
|
|
||||||
|
def remove_books(self, book_ids, db):
|
||||||
|
clean = set()
|
||||||
|
for book_id in book_ids:
|
||||||
|
val = self.book_col_map.pop(book_id, null)
|
||||||
|
if val is not null:
|
||||||
|
self.uuid_to_id_map.pop(val, None)
|
||||||
|
clean.add(val)
|
||||||
|
return clean
|
||||||
|
|
||||||
class CompositeTable(OneToOneTable):
|
class CompositeTable(OneToOneTable):
|
||||||
|
|
||||||
def read(self, db):
|
def read(self, db):
|
||||||
@ -124,6 +148,9 @@ class CompositeTable(OneToOneTable):
|
|||||||
self.composite_sort = d.get('composite_sort', False)
|
self.composite_sort = d.get('composite_sort', False)
|
||||||
self.use_decorations = d.get('use_decorations', False)
|
self.use_decorations = d.get('use_decorations', False)
|
||||||
|
|
||||||
|
def remove_books(self, book_ids, db):
|
||||||
|
return set()
|
||||||
|
|
||||||
class ManyToOneTable(Table):
|
class ManyToOneTable(Table):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -156,6 +183,27 @@ class ManyToOneTable(Table):
|
|||||||
self.col_book_map[row[1]].add(row[0])
|
self.col_book_map[row[1]].add(row[0])
|
||||||
self.book_col_map[row[0]] = row[1]
|
self.book_col_map[row[0]] = row[1]
|
||||||
|
|
||||||
|
def remove_books(self, book_ids, db):
|
||||||
|
clean = set()
|
||||||
|
for book_id in book_ids:
|
||||||
|
item_id = self.book_col_map.pop(book_id, None)
|
||||||
|
if item_id is not None:
|
||||||
|
try:
|
||||||
|
self.col_book_map[item_id].discard(book_id)
|
||||||
|
except KeyError:
|
||||||
|
if self.id_map.pop(item_id, null) is not null:
|
||||||
|
clean.add(item_id)
|
||||||
|
else:
|
||||||
|
if not self.col_book_map[item_id]:
|
||||||
|
del self.col_book_map[item_id]
|
||||||
|
if self.id_map.pop(item_id, null) is not null:
|
||||||
|
clean.add(item_id)
|
||||||
|
if clean:
|
||||||
|
db.conn.executemany(
|
||||||
|
'DELETE FROM {0} WHERE id=?'.format(self.metadata['table']),
|
||||||
|
[(x,) for x in clean])
|
||||||
|
return clean
|
||||||
|
|
||||||
class ManyToManyTable(ManyToOneTable):
|
class ManyToManyTable(ManyToOneTable):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -166,6 +214,7 @@ class ManyToManyTable(ManyToOneTable):
|
|||||||
|
|
||||||
table_type = MANY_MANY
|
table_type = MANY_MANY
|
||||||
selectq = 'SELECT book, {0} FROM {1} ORDER BY id'
|
selectq = 'SELECT book, {0} FROM {1} ORDER BY id'
|
||||||
|
do_clean_on_remove = True
|
||||||
|
|
||||||
def read_maps(self, db):
|
def read_maps(self, db):
|
||||||
for row in db.conn.execute(
|
for row in db.conn.execute(
|
||||||
@ -180,6 +229,27 @@ class ManyToManyTable(ManyToOneTable):
|
|||||||
for key in tuple(self.book_col_map.iterkeys()):
|
for key in tuple(self.book_col_map.iterkeys()):
|
||||||
self.book_col_map[key] = tuple(self.book_col_map[key])
|
self.book_col_map[key] = tuple(self.book_col_map[key])
|
||||||
|
|
||||||
|
def remove_books(self, book_ids, db):
|
||||||
|
clean = set()
|
||||||
|
for book_id in book_ids:
|
||||||
|
item_ids = self.book_col_map.pop(book_id, ())
|
||||||
|
for item_id in item_ids:
|
||||||
|
try:
|
||||||
|
self.col_book_map[item_id].discard(book_id)
|
||||||
|
except KeyError:
|
||||||
|
if self.id_map.pop(item_id, null) is not null:
|
||||||
|
clean.add(item_id)
|
||||||
|
else:
|
||||||
|
if not self.col_book_map[item_id]:
|
||||||
|
del self.col_book_map[item_id]
|
||||||
|
if self.id_map.pop(item_id, null) is not null:
|
||||||
|
clean.add(item_id)
|
||||||
|
if clean and self.do_clean_on_remove:
|
||||||
|
db.conn.executemany(
|
||||||
|
'DELETE FROM {0} WHERE id=?'.format(self.metadata['table']),
|
||||||
|
[(x,) for x in clean])
|
||||||
|
return clean
|
||||||
|
|
||||||
class AuthorsTable(ManyToManyTable):
|
class AuthorsTable(ManyToManyTable):
|
||||||
|
|
||||||
def read_id_maps(self, db):
|
def read_id_maps(self, db):
|
||||||
@ -197,8 +267,17 @@ class AuthorsTable(ManyToManyTable):
|
|||||||
db.conn.executemany('UPDATE authors SET sort=? WHERE id=?',
|
db.conn.executemany('UPDATE authors SET sort=? WHERE id=?',
|
||||||
[(v, k) for k, v in aus_map.iteritems()])
|
[(v, k) for k, v in aus_map.iteritems()])
|
||||||
|
|
||||||
|
def remove_books(self, book_ids, db):
|
||||||
|
clean = ManyToManyTable.remove_books(self, book_ids, db)
|
||||||
|
for item_id in clean:
|
||||||
|
self.alink_map.pop(item_id, None)
|
||||||
|
self.asort_map.pop(item_id, None)
|
||||||
|
return clean
|
||||||
|
|
||||||
class FormatsTable(ManyToManyTable):
|
class FormatsTable(ManyToManyTable):
|
||||||
|
|
||||||
|
do_clean_on_remove = False
|
||||||
|
|
||||||
def read_id_maps(self, db):
|
def read_id_maps(self, db):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -220,6 +299,13 @@ class FormatsTable(ManyToManyTable):
|
|||||||
for key in tuple(self.book_col_map.iterkeys()):
|
for key in tuple(self.book_col_map.iterkeys()):
|
||||||
self.book_col_map[key] = tuple(sorted(self.book_col_map[key]))
|
self.book_col_map[key] = tuple(sorted(self.book_col_map[key]))
|
||||||
|
|
||||||
|
def remove_books(self, book_ids, db):
|
||||||
|
clean = ManyToManyTable.remove_books(self, book_ids, db)
|
||||||
|
for book_id in book_ids:
|
||||||
|
self.fname_map.pop(book_id, None)
|
||||||
|
self.size_map.pop(book_id, None)
|
||||||
|
return clean
|
||||||
|
|
||||||
def set_fname(self, book_id, fmt, fname, db):
|
def set_fname(self, book_id, fmt, fname, db):
|
||||||
self.fname_map[book_id][fmt] = fname
|
self.fname_map[book_id][fmt] = fname
|
||||||
db.conn.execute('UPDATE data SET name=? WHERE book=? AND format=?',
|
db.conn.execute('UPDATE data SET name=? WHERE book=? AND format=?',
|
||||||
@ -280,8 +366,19 @@ class IdentifiersTable(ManyToManyTable):
|
|||||||
self.book_col_map[row[0]] = {}
|
self.book_col_map[row[0]] = {}
|
||||||
self.book_col_map[row[0]][row[1]] = row[2]
|
self.book_col_map[row[0]][row[1]] = row[2]
|
||||||
|
|
||||||
class LanguagesTable(ManyToManyTable):
|
def remove_books(self, book_ids, db):
|
||||||
|
clean = set()
|
||||||
|
for book_id in book_ids:
|
||||||
|
item_map = self.book_col_map.pop(book_id, {})
|
||||||
|
for item_id in item_map:
|
||||||
|
try:
|
||||||
|
self.col_book_map[item_id].discard(book_id)
|
||||||
|
except KeyError:
|
||||||
|
clean.add(item_id)
|
||||||
|
else:
|
||||||
|
if not self.col_book_map[item_id]:
|
||||||
|
del self.col_book_map[item_id]
|
||||||
|
clean.add(item_id)
|
||||||
|
return clean
|
||||||
|
|
||||||
def read_id_maps(self, db):
|
|
||||||
ManyToManyTable.read_id_maps(self, db)
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user