diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index b1bbd77680..96557722ef 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -1889,7 +1889,7 @@ class DB: os.makedirs(tpath) update_paths_in_db() - def iter_extra_files(self, book_id, book_path, formats_field): + def iter_extra_files(self, book_id, book_path, formats_field, yield_paths=False): known_files = {COVER_FILE_NAME, METADATA_FILE_NAME} for fmt in formats_field.for_book(book_id, default_value=()): fname = formats_field.format_fname(book_id, fmt) @@ -1904,24 +1904,35 @@ class DB: relpath = os.path.relpath(path, full_book_path) relpath = relpath.replace(os.sep, '/') if relpath not in known_files: - try: - src = open(path, 'rb') - except OSError: - if iswindows: - time.sleep(1) - src = open(path, 'rb') - with src: - yield relpath, src, os.path.getmtime(path) + mtime = os.path.getmtime(path) + if yield_paths: + yield relpath, path, mtime + else: + try: + src = open(path, 'rb') + except OSError: + if iswindows: + time.sleep(1) + src = open(path, 'rb') + with src: + yield relpath, src, mtime def add_extra_file(self, relpath, stream, book_path): dest = os.path.abspath(os.path.join(self.library_path, book_path, relpath)) - try: - d = open(dest, 'wb') - except OSError: - os.makedirs(os.path.dirname(dest), exist_ok=True) - d = open(dest, 'wb') - with d: - shutil.copyfileobj(stream, d) + if isinstance(stream, str): + try: + shutil.copy2(stream, dest) + except FileNotFoundError: + os.makedirs(os.path.dirname(dest), exist_ok=True) + shutil.copy2(stream, dest) + else: + try: + d = open(dest, 'wb') + except FileNotFoundError: + os.makedirs(os.path.dirname(dest), exist_ok=True) + d = open(dest, 'wb') + with d: + shutil.copyfileobj(stream, d) def write_backup(self, path, raw): path = os.path.abspath(os.path.join(self.library_path, path, METADATA_FILE_NAME)) diff --git a/src/calibre/db/copy_to_library.py b/src/calibre/db/copy_to_library.py index c9e4ee22f9..6488ec636b 100644 --- a/src/calibre/db/copy_to_library.py +++ b/src/calibre/db/copy_to_library.py @@ -102,6 +102,12 @@ 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) + if bp: + for (relpath, src_path, mtime) in db.backend.iter_extra_files(book_id, bp, db.fields['formats'], yield_paths=True): + nbp = newdb.field_for('path', book_id) + if nbp: + newdb.backend.add_extra_file(relpath, src_path, nbp) postprocess_copy(book_id, new_book_id, new_authors, db, newdb, identical_books_data, duplicate_action) return_data['new_book_id'] = new_book_id return return_data diff --git a/src/calibre/db/tests/add_remove.py b/src/calibre/db/tests/add_remove.py index 33e069e5bf..8e08f58146 100644 --- a/src/calibre/db/tests/add_remove.py +++ b/src/calibre/db/tests/add_remove.py @@ -377,6 +377,12 @@ class AddRemoveTest(BaseTest): a(type='highlight', highlighted_text='text2', uuid='2', seq=3, notes='notes2 some word changed again'), ] src_db.set_annotations_for_book(1, 'FMT1', annot_list) + bookdir = os.path.dirname(src_db.format_abspath(1, '__COVER_INTERNAL__')) + with open(os.path.join(bookdir, 'exf'), 'w') as f: + f.write('exf') + os.mkdir(os.path.join(bookdir, 'sub')) + with open(os.path.join(bookdir, 'sub', 'recurse'), 'w') as f: + f.write('recurse') def make_rdata(book_id=1, new_book_id=None, action='add'): return { @@ -416,5 +422,8 @@ class AddRemoveTest(BaseTest): for new_book_id in (1, 4, 5): self.assertEqual(dest_db.format(new_book_id, 'FMT1'), b'replaced') self.assertEqual(dest_db.format(rdata['new_book_id'], 'FMT1'), b'second-round') + bookdir = os.path.dirname(dest_db.format_abspath(1, '__COVER_INTERNAL__')) + self.assertEqual('exf', open(os.path.join(bookdir, 'exf')).read()) + self.assertEqual('recurse', open(os.path.join(bookdir, 'sub', 'recurse')).read()) # }}} diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py index 01b22b2377..627f02319b 100644 --- a/src/calibre/gui2/preferences/create_custom_column.py +++ b/src/calibre/gui2/preferences/create_custom_column.py @@ -459,7 +459,7 @@ class CreateCustomColumn(QDialog): l = QHBoxLayout() self.composite_in_comments_box = cmc = QCheckBox(_("Show with comments in Book details")) cmc.setToolTip('
' + _('If you check this box then the column contents ' - 'will show in the Comments section in Book details. ' + 'will show in the Comments section in the Book details. ' 'You can indicate whether not to have a header or ' 'to put a header above the column. If you want a ' "header beside the data, don't check this box. "