diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 1e839940eb..3aba2d049c 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -26,11 +26,12 @@ from calibre.devices.kobo.books import Book, ImageWrapper, KTCollectionsBookList from calibre.devices.mime import mime_type_ext from calibre.devices.usbms.books import BookList, CollectionsBookList from calibre.devices.usbms.driver import USBMS +from calibre.ebooks import DRMError from calibre.ebooks.metadata import authors_to_string from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.utils import normalize_languages from calibre.prints import debug_print -from calibre.ptempfile import PersistentTemporaryFile, better_mktemp +from calibre.ptempfile import PersistentTemporaryFile, TemporaryDirectory, better_mktemp from calibre.utils.config_base import prefs from calibre.utils.date import parse_date from polyglot.builtins import iteritems, itervalues, string_or_bytes @@ -115,8 +116,7 @@ class KOBO(USBMS): SUPPORTS_SUB_DIRS = True SUPPORTS_ANNOTATIONS = True - # "kepubs" do not have an extension. The name looks like a GUID. Using an empty string seems to work. - VIRTUAL_BOOK_EXTENSIONS = frozenset(('kobo', '')) + VIRTUAL_BOOK_EXTENSIONS = frozenset(('kobo',)) EXTRA_CUSTOMIZATION_MESSAGE = [ _('The Kobo supports several collections including ')+ 'Read, Closed, Im_Reading. ' + _( @@ -773,7 +773,7 @@ class KOBO(USBMS): # Supported database version return True - def get_file(self, path, *args, **kwargs): + def get_file(self, path, outfile, end_session=True): tpath = self.munge_path(path) extension = os.path.splitext(tpath)[1] if extension == '.kobo': @@ -783,8 +783,20 @@ class KOBO(USBMS): 'instead they are rows in the sqlite database. ' 'Currently they cannot be exported or viewed.'), UserFeedback.WARN) + if tpath.lower().endswith(KEPUB_EXT + EPUB_EXT): + with TemporaryDirectory() as tdir: + outpath = os.path.join(tdir, 'file.epub') + from calibre.ebooks.oeb.polish.kepubify import unkepubify_path + try: + unkepubify_path(path, outpath, allow_overwrite=True) + except DRMError: + pass + else: + with open(outpath, 'rb') as src: + shutil.copyfile(src, outfile) + return - return USBMS.get_file(self, path, *args, **kwargs) + return USBMS.get_file(self, path, outfile, end_session=end_session) @classmethod def book_from_path(cls, prefix, lpath, title, authors, mime, date, ContentType, ImageID): @@ -1125,17 +1137,23 @@ class KOBO(USBMS): with no file extension. I just hope that decision causes them as much grief as it does me :-) - This has to make a temporary copy of the book files with a + This has to make a temporary copy of the book files with an epub extension to allow calibre's normal processing to deal with the file appropriately ''' for idx, path in enumerate(paths): - if path.find('kepub') >= 0: - with closing(open(path, 'rb')) as r: - tf = PersistentTemporaryFile(suffix='.epub') - shutil.copyfileobj(r, tf) - # tf.write(r.read()) - paths[idx] = tf.name + parts = path.replace(os.sep, '/').split('/') + if path.lower().endswith(KEPUB_EXT + EPUB_EXT) or ('kepub' in parts and '.' not in parts[-1]): + with PersistentTemporaryFile(suffix=EPUB_EXT) as dest: + pass + from calibre.ebooks.oeb.polish.kepubify import unkepubify_path + try: + unkepubify_path(path, dest.name, allow_overwrite=True) + except DRMError as e: + import traceback + paths[idx] = (path, e, traceback.format_exc()) + else: + paths[idx] = dest.name return paths @classmethod @@ -2332,7 +2350,6 @@ class KOBOTOUCH(KOBO): return result def _kepubify(self, path, name, mi, extra_css) -> None: - from calibre.ebooks.oeb.polish.errors import DRMError from calibre.ebooks.oeb.polish.kepubify import kepubify_path, make_options debug_print(f'Starting conversion of {mi.title} ({name}) to kepub') opts = make_options( diff --git a/src/calibre/ebooks/oeb/polish/kepubify.py b/src/calibre/ebooks/oeb/polish/kepubify.py index acc0569640..76b2a35a4b 100644 --- a/src/calibre/ebooks/oeb/polish/kepubify.py +++ b/src/calibre/ebooks/oeb/polish/kepubify.py @@ -28,6 +28,7 @@ from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES, XHTML, XPath, escape_c from calibre.ebooks.oeb.parse_utils import barename, merge_multiple_html_heads_and_bodies from calibre.ebooks.oeb.polish.container import Container, EpubContainer, get_container from calibre.ebooks.oeb.polish.cover import find_cover_image, find_cover_image3, find_cover_page +from calibre.ebooks.oeb.polish.errors import DRMError from calibre.ebooks.oeb.polish.parsing import parse from calibre.ebooks.oeb.polish.tts import lang_for_elem from calibre.ebooks.oeb.polish.utils import extract, insert_self_closing @@ -516,8 +517,24 @@ def kepubify_path(path, outpath='', max_workers=0, allow_overwrite=False, opts: return outpath +def check_for_kobo_drm(container: Container) -> None: + # sadly rights.xml is not definitive as various dedrm tools leave it behind + has_rights_xml = container.has_name_and_is_not_empty('rights.xml') + if not has_rights_xml: + return + for name, is_linear in container.spine_names: + mt = container.mime_map[name] + if mt in OEB_DOCS: + with container.open(name, 'rb') as f: + raw = f.read(8192) + if b'