From 1526cdb906d69ae159d944ff9a36d7bfc02dc2fe Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 28 Feb 2025 08:57:02 +0530 Subject: [PATCH] Fix #2100559 [KePubify: Restore 'template for kepubification'](https://bugs.launchpad.net/calibre/+bug/2100559) --- src/calibre/devices/kobo/driver.py | 38 +++++++++++++++----- src/calibre/devices/kobo/kobotouch_config.py | 28 ++++++++++++++- src/calibre/ebooks/oeb/polish/kepubify.py | 3 +- 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 1d8aa7c0a2..9ac76ed485 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -2294,24 +2294,43 @@ class KOBOTOUCH(KOBO): debug_print(f'KoboTouch:upload_books - {len(files)} books') debug_print('KoboTouch:upload_books - files=', files) - modify_epub = self.modifying_epub() or self.get_pref('kepubify') - def should_modify(name: str) -> bool: - ext = '.' + name.rpartition('.')[-1].lower() - return ext == EPUB_EXT and modify_epub + do_kepubify = self.get_pref('kepubify') + template = self.get_pref('template_for_kepubify') + modify_css = self.modifying_epub() + entries = tuple(zip(files, names, metadata)) + kepubifiable = set() + + def should_modify(name: str, mi) -> bool: + mi.kte_calibre_name = name + if not name.lower().endswith(EPUB_EXT): + return False + if do_kepubify: + if not template: + kepubifiable.add(mi.uuid) + return True + from calibre.ebooks.metadata.book.formatter import SafeFormat + kepubify = SafeFormat().safe_format(template, mi, 'Open With template error', mi) + debug_print(f'kepubify_template_result for {mi.title}:', kepubify) + if kepubify is not None and kepubify.startswith('PLUGBOARD TEMPLATE ERROR'): + import sys + print(f'kepubify template: {template} returned error', file=sys.stderr) + kepubifiable.add(mi.uuid) + return True + return kepubify and kepubify != 'false' + return modify_css self.extra_css, self.extra_sheet = self.get_extra_css() - modifiable = {x for x in names if should_modify(x)} + modifiable = {mi.uuid for _, name, mi in entries if should_modify(name, mi)} self.files_to_rename_to_kepub = set() if modifiable: i = 0 - for idx, (file, n, mi) in enumerate(zip(files, names, metadata)): - if n not in modifiable: + for idx, (file, n, mi) in enumerate(entries): + if mi.uuid not in modifiable: continue debug_print('KoboTouch:upload_books: Processing book: {} by {}'.format(mi.title, ' and '.join(mi.authors))) debug_print(f'KoboTouch:upload_books: file={file}, name={n}') self.report_progress(i / float(len(modifiable)), 'Processing book: {} by {}'.format(mi.title, ' and '.join(mi.authors))) - mi.kte_calibre_name = n - if self.get_pref('kepubify'): + if mi.uuid in kepubifiable: self._kepubify(file, n, mi) else: self._modify_epub(file, mi) @@ -3663,6 +3682,7 @@ class KOBOTOUCH(KOBO): c.add_opt('bookstats_timetoread_lower_template', default=None) c.add_opt('kepubify', default=True) + c.add_opt('template_for_kepubify', default=None) c.add_opt('modify_css', default=False) c.add_opt('per_device_css', default='{}') c.add_opt('override_kobo_replace_existing', default=True) # Overriding the replace behaviour is how the driver has always worked. diff --git a/src/calibre/devices/kobo/kobotouch_config.py b/src/calibre/devices/kobo/kobotouch_config.py index 605695c97a..fdb340c7cf 100644 --- a/src/calibre/devices/kobo/kobotouch_config.py +++ b/src/calibre/devices/kobo/kobotouch_config.py @@ -155,6 +155,7 @@ class KOBOTOUCHConfig(TabbedDeviceConfig): p['modify_css'] = self.modify_css p['per_device_css'] = self.per_device_css p['kepubify'] = self.kepubify + p['template_for_kepubify'] = self.template_for_kepubify p['override_kobo_replace_existing'] = self.override_kobo_replace_existing p['support_newer_firmware'] = self.support_newer_firmware @@ -359,6 +360,20 @@ class BookUploadsGroupBox(DeviceOptionsGroupBox): ' the Kobo viewer. If you would rather use the legacy viewer for EPUB, disable this option.' ), device.get_pref('kepubify')) + self.template_la = la = QLabel('\xa0\xa0' + _('Template to decide conversion:')) + self.kepubify_template_edit = TemplateConfig( + device.get_pref('template_for_kepubify'), + tooltip=_( + 'Enter a template to decide if an EPUB book is to be auto converted to KEPUB. ' + 'If the template returns false or no result, the book will not be ' + 'converted to KEPUB. For example to only kepubify books that have the tag "as_kepub", use the template: {0}' + ' or to only convert books that do not have the tag as_epub, use the template: {1}' + '\n\nIf no template is specified conversion to KEPUB is controlled only by the setting above to use the Kobo viewer. ' + 'Note that the setting above must be enabled for the template to be checked.' + ).format(r'{tags:str_in_list(\,,as_kepub,true,false)}', r'{tags:str_in_list(\,,as_epub,false,true)}') + ) + la.setBuddy(self.kepubify_template_edit) + self.override_kobo_replace_existing_checkbox = create_checkbox( _('Do not treat replacements as new books'), _('When a new book is side-loaded, the Kobo firmware imports details of the book into the internal database. ' @@ -371,7 +386,14 @@ class BookUploadsGroupBox(DeviceOptionsGroupBox): ) self.options_layout.addWidget(self.kepubify_checkbox, 0, 0, 1, 2) - self.options_layout.addWidget(self.override_kobo_replace_existing_checkbox, 1, 0, 1, 2) + self.options_layout.addWidget(self.template_la, 1, 0, 1, 1) + self.options_layout.addWidget(self.kepubify_template_edit, 1, 1, 1, 1) + self.options_layout.addWidget(self.override_kobo_replace_existing_checkbox, 2, 0, 1, 2) + self.update_template_state() + self.kepubify_checkbox.toggled.connect(self.update_template_state) + + def update_template_state(self): + self.kepubify_template_edit.setEnabled(self.kepubify) @property def override_kobo_replace_existing(self): @@ -381,6 +403,10 @@ class BookUploadsGroupBox(DeviceOptionsGroupBox): def kepubify(self): return self.kepubify_checkbox.isChecked() + @property + def template_for_kepubify(self): + return (self.kepubify_template_edit.template or '').strip() + class HyphenationGroupBox(DeviceOptionsGroupBox): diff --git a/src/calibre/ebooks/oeb/polish/kepubify.py b/src/calibre/ebooks/oeb/polish/kepubify.py index 93e586893e..b290eaee8f 100644 --- a/src/calibre/ebooks/oeb/polish/kepubify.py +++ b/src/calibre/ebooks/oeb/polish/kepubify.py @@ -553,11 +553,12 @@ def unkepubify_path(path, outpath='', max_workers=0, allow_overwrite=False): def check_if_css_needs_modification(extra_css: str) -> tuple[bool, bool]: remove_widows_and_orphans = remove_at_page_rules = False + sheet = None if extra_css: try: sheet = css_parser().parseString(extra_css) except Exception: - sheet = None + pass else: for rule in sheet.cssRules: if rule.type == CSSRule.PAGE_RULE: