mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Kobo driver: Automatically unkepubify books when exporting from the device
Also, allow adding the virtual books since nowadays some of them are DRM free. DRMed books will still give an error when attempting to add them to the library.
This commit is contained in:
parent
249c2d11a1
commit
9199cb6905
@ -26,11 +26,12 @@ from calibre.devices.kobo.books import Book, ImageWrapper, KTCollectionsBookList
|
|||||||
from calibre.devices.mime import mime_type_ext
|
from calibre.devices.mime import mime_type_ext
|
||||||
from calibre.devices.usbms.books import BookList, CollectionsBookList
|
from calibre.devices.usbms.books import BookList, CollectionsBookList
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
from calibre.ebooks import DRMError
|
||||||
from calibre.ebooks.metadata import authors_to_string
|
from calibre.ebooks.metadata import authors_to_string
|
||||||
from calibre.ebooks.metadata.book.base import Metadata
|
from calibre.ebooks.metadata.book.base import Metadata
|
||||||
from calibre.ebooks.metadata.utils import normalize_languages
|
from calibre.ebooks.metadata.utils import normalize_languages
|
||||||
from calibre.prints import debug_print
|
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.config_base import prefs
|
||||||
from calibre.utils.date import parse_date
|
from calibre.utils.date import parse_date
|
||||||
from polyglot.builtins import iteritems, itervalues, string_or_bytes
|
from polyglot.builtins import iteritems, itervalues, string_or_bytes
|
||||||
@ -115,8 +116,7 @@ class KOBO(USBMS):
|
|||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
SUPPORTS_ANNOTATIONS = 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 = [
|
EXTRA_CUSTOMIZATION_MESSAGE = [
|
||||||
_('The Kobo supports several collections including ')+ 'Read, Closed, Im_Reading. ' + _(
|
_('The Kobo supports several collections including ')+ 'Read, Closed, Im_Reading. ' + _(
|
||||||
@ -773,7 +773,7 @@ class KOBO(USBMS):
|
|||||||
# Supported database version
|
# Supported database version
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_file(self, path, *args, **kwargs):
|
def get_file(self, path, outfile, end_session=True):
|
||||||
tpath = self.munge_path(path)
|
tpath = self.munge_path(path)
|
||||||
extension = os.path.splitext(tpath)[1]
|
extension = os.path.splitext(tpath)[1]
|
||||||
if extension == '.kobo':
|
if extension == '.kobo':
|
||||||
@ -783,8 +783,20 @@ class KOBO(USBMS):
|
|||||||
'instead they are rows in the sqlite database. '
|
'instead they are rows in the sqlite database. '
|
||||||
'Currently they cannot be exported or viewed.'),
|
'Currently they cannot be exported or viewed.'),
|
||||||
UserFeedback.WARN)
|
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
|
@classmethod
|
||||||
def book_from_path(cls, prefix, lpath, title, authors, mime, date, ContentType, ImageID):
|
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
|
with no file extension. I just hope that decision causes
|
||||||
them as much grief as it does me :-)
|
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
|
epub extension to allow calibre's normal processing to
|
||||||
deal with the file appropriately
|
deal with the file appropriately
|
||||||
'''
|
'''
|
||||||
for idx, path in enumerate(paths):
|
for idx, path in enumerate(paths):
|
||||||
if path.find('kepub') >= 0:
|
parts = path.replace(os.sep, '/').split('/')
|
||||||
with closing(open(path, 'rb')) as r:
|
if path.lower().endswith(KEPUB_EXT + EPUB_EXT) or ('kepub' in parts and '.' not in parts[-1]):
|
||||||
tf = PersistentTemporaryFile(suffix='.epub')
|
with PersistentTemporaryFile(suffix=EPUB_EXT) as dest:
|
||||||
shutil.copyfileobj(r, tf)
|
pass
|
||||||
# tf.write(r.read())
|
from calibre.ebooks.oeb.polish.kepubify import unkepubify_path
|
||||||
paths[idx] = tf.name
|
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
|
return paths
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -2332,7 +2350,6 @@ class KOBOTOUCH(KOBO):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _kepubify(self, path, name, mi, extra_css) -> None:
|
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
|
from calibre.ebooks.oeb.polish.kepubify import kepubify_path, make_options
|
||||||
debug_print(f'Starting conversion of {mi.title} ({name}) to kepub')
|
debug_print(f'Starting conversion of {mi.title} ({name}) to kepub')
|
||||||
opts = make_options(
|
opts = make_options(
|
||||||
|
@ -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.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.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.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.parsing import parse
|
||||||
from calibre.ebooks.oeb.polish.tts import lang_for_elem
|
from calibre.ebooks.oeb.polish.tts import lang_for_elem
|
||||||
from calibre.ebooks.oeb.polish.utils import extract, insert_self_closing
|
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
|
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'<?xml' not in raw and b'<html' not in raw and KOBO_SPAN_CLASS.encode() not in raw:
|
||||||
|
raise DRMError()
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
def unkepubify_path(path, outpath='', max_workers=0, allow_overwrite=False):
|
def unkepubify_path(path, outpath='', max_workers=0, allow_overwrite=False):
|
||||||
container = get_container(path, tweak_mode=True, ebook_cls=EpubContainer)
|
container = get_container(path, tweak_mode=True, ebook_cls=EpubContainer)
|
||||||
|
check_for_kobo_drm(container)
|
||||||
unkepubify_container(container, max_workers)
|
unkepubify_container(container, max_workers)
|
||||||
base, ext = os.path.splitext(path)
|
base, ext = os.path.splitext(path)
|
||||||
outpath = outpath or base + '.epub'
|
outpath = outpath or base + '.epub'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user