mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-02-24 12:10:06 -05:00
Merge branch 'cache-get_book_path' of https://github.com/un-pogaz/calibre
This commit is contained in:
commit
86945ccc46
@ -983,6 +983,19 @@ class Cache:
|
||||
return {v:k for k, v in self.fields[field].table.id_map.items()}
|
||||
return {normalize_func(v):k for k, v in self.fields[field].table.id_map.items()}
|
||||
|
||||
@read_api
|
||||
def get_book_path(self, book_id, sep=os.sep, unsafe=False):
|
||||
'''
|
||||
Return the relative book path for the given id.
|
||||
Prefer this because you can choose the directory separator, default use the os one.
|
||||
If unsafe is True, allow to return None if the book_id is not in the library.
|
||||
'''
|
||||
rslt = self._field_for('path', book_id)
|
||||
if unsafe and not rslt:
|
||||
return rslt
|
||||
else:
|
||||
return rslt.replace('/', sep)
|
||||
|
||||
@read_api
|
||||
def author_data(self, author_ids=None):
|
||||
'''
|
||||
@ -1002,7 +1015,7 @@ class Cache:
|
||||
kind of hash is backend dependent, but is usually SHA-256. '''
|
||||
try:
|
||||
name = self.fields['formats'].format_fname(book_id, fmt)
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
raise NoSuchFormat(f'Record {book_id} has no fmt: {fmt}')
|
||||
return self.backend.format_hash(book_id, fmt, name, path)
|
||||
@ -1032,7 +1045,7 @@ class Cache:
|
||||
with self.safe_read_lock:
|
||||
try:
|
||||
name = self.fields['formats'].format_fname(book_id, fmt)
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
@ -1171,7 +1184,7 @@ class Cache:
|
||||
@read_api
|
||||
def cover_or_cache(self, book_id, timestamp, as_what='bytes'):
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except AttributeError:
|
||||
return False, None, None
|
||||
return self.backend.cover_or_cache(path, timestamp, as_what)
|
||||
@ -1179,7 +1192,7 @@ class Cache:
|
||||
@read_api
|
||||
def cover_last_modified(self, book_id):
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except AttributeError:
|
||||
return
|
||||
return self.backend.cover_last_modified(path)
|
||||
@ -1194,7 +1207,7 @@ class Cache:
|
||||
case sensitivity into account).
|
||||
'''
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
@ -1215,7 +1228,7 @@ class Cache:
|
||||
path_map = {}
|
||||
for book_id in book_ids:
|
||||
try:
|
||||
path_map[book_id] = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path_map[book_id] = self.get_book_path(book_id)
|
||||
except AttributeError:
|
||||
continue
|
||||
self.backend.compress_covers(path_map, jpeg_quality, progress_callback)
|
||||
@ -1232,7 +1245,7 @@ class Cache:
|
||||
fmt = (fmt or '').upper()
|
||||
try:
|
||||
name = self.fields['formats'].format_fname(book_id, fmt)
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except (KeyError, AttributeError):
|
||||
raise NoSuchFormat(f'Record {book_id} has no {fmt} file')
|
||||
|
||||
@ -1255,7 +1268,7 @@ class Cache:
|
||||
'''
|
||||
fmt = (fmt or '').upper()
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
return None
|
||||
if path:
|
||||
@ -1275,7 +1288,7 @@ class Cache:
|
||||
fmt = (fmt or '').upper()
|
||||
try:
|
||||
name = self.fields['formats'].format_fname(book_id, fmt)
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
return False
|
||||
return self.backend.has_format(book_id, fmt, name, path)
|
||||
@ -1302,7 +1315,7 @@ class Cache:
|
||||
fmt = original_fmt.partition('_')[2]
|
||||
try:
|
||||
ofmt_name = self.fields['formats'].format_fname(book_id, original_fmt)
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
return False
|
||||
if self.backend.is_format_accessible(book_id, original_fmt, ofmt_name, path):
|
||||
@ -1323,7 +1336,7 @@ class Cache:
|
||||
ans = self.field_for('formats', book_id)
|
||||
if verify_formats and ans:
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
return ()
|
||||
|
||||
@ -1627,7 +1640,7 @@ class Cache:
|
||||
is_series = f.metadata['datatype'] == 'series'
|
||||
update_path = name in {'title', 'authors'}
|
||||
if update_path and iswindows:
|
||||
paths = (x for x in (self._field_for('path', book_id) for book_id in book_id_to_val_map) if x)
|
||||
paths = (x for x in (self.get_book_path(book_id, sep='/', unsafe=True) for book_id in book_id_to_val_map) if x)
|
||||
self.backend.windows_check_if_files_in_use(paths)
|
||||
|
||||
if is_series:
|
||||
@ -1701,7 +1714,7 @@ class Cache:
|
||||
try:
|
||||
# While a book is being created, the path is empty. Don't bother to
|
||||
# try to write the opf, because it will go to the wrong folder.
|
||||
if self._field_for('path', book_id):
|
||||
if self.get_book_path(book_id, unsafe=True):
|
||||
mi = self._metadata_as_object_for_dump(book_id)
|
||||
except Exception:
|
||||
# This almost certainly means that the book has been deleted while
|
||||
@ -1722,7 +1735,7 @@ class Cache:
|
||||
@write_api
|
||||
def write_backup(self, book_id, raw):
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
@ -1737,7 +1750,7 @@ class Cache:
|
||||
''' Return the OPF metadata backup for the book as a bytestring or None
|
||||
if no such backup exists. '''
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
@ -1760,7 +1773,7 @@ class Cache:
|
||||
callback(len(book_ids), True, False)
|
||||
|
||||
for book_id in book_ids:
|
||||
if self._field_for('path', book_id) is None:
|
||||
if self.get_book_path(book_id, unsafe=True) is None:
|
||||
if callback is not None:
|
||||
callback(book_id, None, False)
|
||||
continue
|
||||
@ -1787,10 +1800,10 @@ class Cache:
|
||||
|
||||
for book_id, data in iteritems(book_id_data_map):
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except AttributeError:
|
||||
self._update_path((book_id,))
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
|
||||
self.backend.set_cover(book_id, path, data)
|
||||
for cc in self.cover_caches:
|
||||
@ -1934,14 +1947,13 @@ class Cache:
|
||||
return dirtied
|
||||
|
||||
def _do_add_format(self, book_id, fmt, stream, name=None, mtime=None):
|
||||
path = self._field_for('path', book_id)
|
||||
path = self.get_book_path(book_id, unsafe=True)
|
||||
if path is None:
|
||||
# Theoretically, this should never happen, but apparently it
|
||||
# does: https://www.mobileread.com/forums/showthread.php?t=233353
|
||||
self._update_path({book_id}, mark_as_dirtied=False)
|
||||
path = self._field_for('path', book_id)
|
||||
path = self.get_book_path(book_id)
|
||||
|
||||
path = path.replace('/', os.sep)
|
||||
title = self._field_for('title', book_id, default_value=_('Unknown'))
|
||||
try:
|
||||
author = self._field_for('authors', book_id, default_value=(_('Unknown'),))[0]
|
||||
@ -2034,7 +2046,7 @@ class Cache:
|
||||
metadata_map = {}
|
||||
for book_id, fmts in iteritems(formats_map):
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
continue
|
||||
for fmt in fmts:
|
||||
@ -2222,7 +2234,7 @@ class Cache:
|
||||
path_map = {}
|
||||
for book_id in book_ids:
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
path = None
|
||||
path_map[book_id] = path
|
||||
@ -2751,7 +2763,7 @@ class Cache:
|
||||
|
||||
@read_api
|
||||
def get_top_level_move_items(self):
|
||||
all_paths = {self._field_for('path', book_id).partition('/')[0] for book_id in self._all_book_ids()}
|
||||
all_paths = {self.get_book_path(book_id, sep='/').partition('/')[0] for book_id in self._all_book_ids()}
|
||||
return self.backend.get_top_level_move_items(all_paths)
|
||||
|
||||
@write_api
|
||||
@ -2763,7 +2775,7 @@ class Cache:
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
all_paths = {self._field_for('path', book_id).partition('/')[0] for book_id in self._all_book_ids()}
|
||||
all_paths = {self.get_book_path(book_id, sep='/').partition('/')[0] for book_id in self._all_book_ids()}
|
||||
self.backend.move_library_to(all_paths, newloc, progress=progress_callback, abort=abort)
|
||||
|
||||
@read_api
|
||||
@ -2913,7 +2925,7 @@ class Cache:
|
||||
mi.cover = None
|
||||
self._create_book_entry(mi, add_duplicates=True,
|
||||
force_id=book_id, apply_import_tags=False, preserve_uuid=True)
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
self.backend.move_book_from_trash(book_id, path)
|
||||
self.format_metadata_cache.pop(book_id, None)
|
||||
f = self.fields['formats'].table
|
||||
@ -3060,7 +3072,7 @@ class Cache:
|
||||
if cdata:
|
||||
mi.cover_data = ('jpeg', cdata)
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
continue
|
||||
for fmt in fmts:
|
||||
@ -3189,7 +3201,7 @@ class Cache:
|
||||
dest.discard()
|
||||
else:
|
||||
fm['.cover'] = cover_key
|
||||
bp = self.field_for('path', book_id)
|
||||
bp = self.get_book_path(book_id, sep='/', unsafe=True)
|
||||
extra_files[book_id] = ef = {}
|
||||
if bp:
|
||||
for (relpath, fobj, stat_result) in self.backend.iter_extra_files(book_id, bp, self.fields['formats']):
|
||||
@ -3332,7 +3344,7 @@ class Cache:
|
||||
@read_api
|
||||
def are_paths_inside_book_dir(self, book_id, paths, sub_path=''):
|
||||
try:
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
except Exception:
|
||||
return set()
|
||||
return {x for x in paths if self.backend.is_path_inside_book_dir(x, path, sub_path)}
|
||||
@ -3340,7 +3352,7 @@ class Cache:
|
||||
@write_api
|
||||
def add_extra_files(self, book_id, map_of_relpath_to_stream_or_path, replace=True, auto_rename=False):
|
||||
' Add extra data files '
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
added = {}
|
||||
for relpath, stream_or_path in map_of_relpath_to_stream_or_path.items():
|
||||
added[relpath] = bool(self.backend.add_extra_file(relpath, stream_or_path, path, replace, auto_rename))
|
||||
@ -3350,7 +3362,7 @@ class Cache:
|
||||
@write_api
|
||||
def rename_extra_files(self, book_id, map_of_relpath_to_new_relpath, replace=False):
|
||||
' Rename extra data files '
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
renamed = set()
|
||||
for relpath, newrelpath in map_of_relpath_to_new_relpath.items():
|
||||
if self.backend.rename_extra_file(relpath, newrelpath, path, replace):
|
||||
@ -3380,7 +3392,7 @@ class Cache:
|
||||
'''
|
||||
Delete the specified extra files, either to Recycle Bin or permanently.
|
||||
'''
|
||||
path = self._field_for('path', book_id)
|
||||
path = self.get_book_path(book_id, sep='/', unsafe=True)
|
||||
if path:
|
||||
self._clear_extra_files_cache(book_id)
|
||||
return self.backend.remove_extra_files(path, relpaths, permanent)
|
||||
@ -3403,7 +3415,7 @@ class Cache:
|
||||
ans = self.extra_files_cache.setdefault(book_id, {}).get(pattern)
|
||||
if ans is None or not use_cache:
|
||||
ans = []
|
||||
path = self._field_for('path', book_id)
|
||||
path = self.get_book_path(book_id, sep='/', unsafe=True)
|
||||
if path:
|
||||
for (relpath, file_path, stat_result) in self.backend.iter_extra_files(
|
||||
book_id, path, self.fields['formats'], yield_paths=True, pattern=pattern
|
||||
@ -3414,7 +3426,7 @@ class Cache:
|
||||
|
||||
@read_api
|
||||
def copy_extra_file_to(self, book_id, relpath, stream_or_path):
|
||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||
path = self.get_book_path(book_id)
|
||||
self.backend.copy_extra_file_to(book_id, path, relpath, stream_or_path)
|
||||
|
||||
@write_api
|
||||
@ -3590,7 +3602,7 @@ def import_library(library_key, importer, library_path, progress=None, abort=Non
|
||||
for fmt, fmtkey in fmt_key_map.items():
|
||||
if fmt == '.cover':
|
||||
with importer.start_file(fmtkey, _('Cover for %s') % title) as stream:
|
||||
path = cache._field_for('path', book_id).replace('/', os.sep)
|
||||
path = cache.get_book_path(book_id)
|
||||
cache.backend.set_cover(book_id, path, stream, no_processing=True)
|
||||
else:
|
||||
with importer.start_file(fmtkey, _('{0} format for {1}').format(fmt.upper(), title)) as stream:
|
||||
@ -3598,7 +3610,7 @@ def import_library(library_key, importer, library_path, progress=None, abort=Non
|
||||
cache.fields['formats'].table.update_fmt(book_id, fmt, fname, size, cache.backend)
|
||||
for relpath, efkey in extra_files.get(book_id, {}).items():
|
||||
with importer.start_file(efkey, _('Extra file {0} for book {1}').format(relpath, title)) as stream:
|
||||
path = cache._field_for('path', book_id).replace('/', os.sep)
|
||||
path = cache.get_book_path(book_id)
|
||||
cache.backend.add_extra_file(relpath, stream, path)
|
||||
cache.dump_metadata({book_id})
|
||||
if importer.corrupted_files:
|
||||
|
||||
@ -112,7 +112,7 @@ def copy_one_book(
|
||||
new_book_id = newdb.add_books(
|
||||
[(mi, format_map)], add_duplicates=True, apply_import_tags=tweaks['add_new_book_tags_when_importing_books'],
|
||||
preserve_uuid=preserve_uuid, run_hooks=False)[0][0]
|
||||
bp = db.field_for('path', book_id)
|
||||
bp = db.get_book_path(book_id, sep='/', unsafe=True)
|
||||
if bp:
|
||||
for (relpath, src_path, stat_result) in db.backend.iter_extra_files(book_id, bp, db.fields['formats'], yield_paths=True):
|
||||
nbp = newdb.field_for('path', new_book_id)
|
||||
|
||||
@ -325,7 +325,7 @@ class LibraryDatabase:
|
||||
def path(self, index, index_is_id=False):
|
||||
'Return the relative path to the directory containing this books files as a unicode string.'
|
||||
book_id = index if index_is_id else self.id(index)
|
||||
return self.new_api.field_for('path', book_id).replace('/', os.sep)
|
||||
return self.new_api.get_book_path(book_id)
|
||||
|
||||
def abspath(self, index, index_is_id=False, create_dirs=True):
|
||||
'Return the absolute path to the directory containing this books files as a unicode string.'
|
||||
|
||||
@ -104,7 +104,7 @@ class FilesystemTest(BaseTest):
|
||||
expected_contents.add(fname + '.' + fmt.lower())
|
||||
ae(expected_contents, bookdir_contents)
|
||||
fs_path = bookdir.split(os.sep)[-2:]
|
||||
db_path = cache.field_for('path', book_id).split('/')
|
||||
db_path = cache.get_book_path(book_id, sep='/').split('/')
|
||||
ae(db_path, fs_path)
|
||||
ae(initial_side_data, side_data(book_id))
|
||||
|
||||
|
||||
@ -379,7 +379,7 @@ class WritingTest(BaseTest):
|
||||
|
||||
def read_all_extra_files(book_id=1):
|
||||
ans = {}
|
||||
bp = cache.field_for('path', book_id)
|
||||
bp = cache.get_book_path(book_id, sep='/')
|
||||
for (relpath, fobj, stat_result) in cache.backend.iter_extra_files(book_id, bp, cache.fields['formats']):
|
||||
ans[relpath] = fobj.read()
|
||||
return ans
|
||||
|
||||
@ -306,7 +306,7 @@ class BookInfo(QDialog, DropMixin):
|
||||
dbn = db.new_api
|
||||
mi = dbn.get_metadata(book_id, get_cover=False)
|
||||
mi.cover_data = [None, dbn.cover(book_id, as_image=True)]
|
||||
mi.path = dbn._field_for('path', book_id)
|
||||
mi.path = dbn.get_book_path(book_id, sep='/')
|
||||
mi.format_files = dbn.format_files(book_id)
|
||||
mi.marked = db.data.get_marked(book_id)
|
||||
mi.field_metadata = db.field_metadata
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user