diff --git a/src/calibre/devices/kindle/apnx.py b/src/calibre/devices/kindle/apnx.py index 19a7bfdff5..e2c5856d25 100644 --- a/src/calibre/devices/kindle/apnx.py +++ b/src/calibre/devices/kindle/apnx.py @@ -2,6 +2,8 @@ __license__ = 'GPL v3' __copyright__ = '2011, John Schember , refactored: 2022, Vaso Peras-Likodric ' __docformat__ = 'restructuredtext en' +from typing import Optional, Dict + ''' Generates and writes an APNX page mapping file. ''' @@ -17,8 +19,6 @@ from polyglot.builtins import as_unicode, as_bytes from calibre.devices.kindle.apnx_page_generator.generators.accurate_page_generator import AccuratePageGenerator from calibre.devices.kindle.apnx_page_generator.generators.pagebreak_page_generator import PagebreakPageGenerator -from calibre.devices.kindle.apnx_page_generator.generators.aria_pagebreak_page_generator import \ - AriaPagebreakPageGenerator from calibre.devices.kindle.apnx_page_generator.generators.exact_page_generator import ExactPageGenerator from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator @@ -30,14 +30,14 @@ class APNXBuilder: Create an APNX file using a pseudo page mapping. """ - generators: dict[str, IPageGenerator] = { + generators: Dict[str, IPageGenerator] = { FastPageGenerator.instance.name(): FastPageGenerator.instance, AccuratePageGenerator.instance.name(): AccuratePageGenerator.instance, PagebreakPageGenerator.instance.name(): PagebreakPageGenerator.instance, # ExactPageGenerator.instance.name(): ExactPageGenerator.instance, } - def write_apnx(self, mobi_file_path: str, apnx_path: str, method: str | None = None, page_count: int = 0): + def write_apnx(self, mobi_file_path: str, apnx_path: str, method: Optional[str] = None, page_count: int = 0): """ If you want a fixed number of pages (such as from a custom column) then pass in a value to page_count, otherwise a count will be estimated @@ -62,7 +62,7 @@ class APNXBuilder: fsync(apnxf) @staticmethod - def get_apnx_meta(mobi_file_path) -> dict[str, str]: + def get_apnx_meta(mobi_file_path) -> Dict[str, str]: import uuid apnx_meta = { 'guid': str(uuid.uuid4()).replace('-', '')[:8], diff --git a/src/calibre/devices/kindle/apnx_page_generator/generators/accurate_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/generators/accurate_page_generator.py index 24cf5eac9d..b76ac6cff9 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/generators/accurate_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/generators/accurate_page_generator.py @@ -2,20 +2,24 @@ __license__ = 'GPL v3' __copyright__ = '2022, Vaso Peras-Likodric ' __docformat__ = 'restructuredtext en' +from typing import Optional + from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator -from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator +from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html from calibre.devices.kindle.apnx_page_generator.pages import Pages class AccuratePageGenerator(IPageGenerator): + instance = None + def name(self) -> str: return "accurate" - def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: return FastPageGenerator.instance.generate(mobi_file_path, real_count) - def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: """ A more accurate but much more resource intensive and slower method to calculate the page length. @@ -35,7 +39,7 @@ class AccuratePageGenerator(IPageGenerator): """ pages = [] - html = self.mobi_html(mobi_file_path) + html = mobi_html(mobi_file_path) # States in_tag = False @@ -54,7 +58,7 @@ class AccuratePageGenerator(IPageGenerator): # and string functions will parse the text each # time they are called. # - # We can can use .lower() here because we are + # We can use .lower() here because we are # not modifying the text. In this case the case # doesn't matter just the absolute character and # the position within the stream. diff --git a/src/calibre/devices/kindle/apnx_page_generator/generators/exact_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/generators/exact_page_generator.py index a30d481ba4..864ad08eae 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/generators/exact_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/generators/exact_page_generator.py @@ -2,20 +2,24 @@ __license__ = 'GPL v3' __copyright__ = '2022, Vaso Peras-Likodric ' __docformat__ = 'restructuredtext en' +from typing import Optional + from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator -from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator +from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html_length from calibre.devices.kindle.apnx_page_generator.pages import Pages class ExactPageGenerator(IPageGenerator): + instance = None + def name(self) -> str: return "exact" - def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: return FastPageGenerator.instance.generate(mobi_file_path, real_count) - def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: """ Given a specified page count (such as from a custom column), create our array of pages for the apnx file by dividing by @@ -24,7 +28,7 @@ class ExactPageGenerator(IPageGenerator): pages = [] count = 0 - text_length = self.mobi_html_length(mobi_file_path) + text_length = mobi_html_length(mobi_file_path) chars_per_page = int(text_length // real_count) while count < text_length: diff --git a/src/calibre/devices/kindle/apnx_page_generator/generators/fast_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/generators/fast_page_generator.py index 23320dacdf..5247a17bfd 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/generators/fast_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/generators/fast_page_generator.py @@ -2,7 +2,9 @@ __license__ = 'GPL v3' __copyright__ = '2022, Vaso Peras-Likodric ' __docformat__ = 'restructuredtext en' -from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator +from typing import Optional + +from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html_length from calibre.devices.kindle.apnx_page_generator.pages import Pages @@ -11,10 +13,10 @@ class FastPageGenerator(IPageGenerator): def name(self) -> str: return "fast" - def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: raise Exception("Fast calculation impossible.") - def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: """ 2300 characters of uncompressed text per page. This is not meant to map 1 to 1 to a print book but to be a @@ -34,7 +36,7 @@ class FastPageGenerator(IPageGenerator): pages = [] count = 0 - text_length = self.mobi_html_length(mobi_file_path) + text_length = mobi_html_length(mobi_file_path) while count < text_length: pages.append(count) diff --git a/src/calibre/devices/kindle/apnx_page_generator/generators/pagebreak_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/generators/pagebreak_page_generator.py index bf591480f2..f80b131556 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/generators/pagebreak_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/generators/pagebreak_page_generator.py @@ -2,8 +2,10 @@ __license__ = 'GPL v3' __copyright__ = '2022, Vaso Peras-Likodric ' __docformat__ = 'restructuredtext en' +from typing import Optional + from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator -from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator +from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html from calibre.devices.kindle.apnx_page_generator.pages import Pages import re @@ -13,12 +15,12 @@ class PagebreakPageGenerator(IPageGenerator): def name(self) -> str: return "pagebreak" - def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: return FastPageGenerator.instance.generate(mobi_file_path, real_count) - def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: """ Determine pages based on the presence of <*pagebreak*/>. """ - html = self.mobi_html(mobi_file_path) + html = mobi_html(mobi_file_path) pages = [] for m in re.finditer(b'<[^>]*pagebreak[^>]*>', html): pages.append(m.end()) diff --git a/src/calibre/devices/kindle/apnx_page_generator/i_page_generator.py b/src/calibre/devices/kindle/apnx_page_generator/i_page_generator.py index 8de2eb05a8..62b2265c4b 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/i_page_generator.py +++ b/src/calibre/devices/kindle/apnx_page_generator/i_page_generator.py @@ -4,6 +4,8 @@ __docformat__ = 'restructuredtext en' import struct from abc import abstractmethod, ABCMeta +from typing import Optional + from calibre.devices.kindle.apnx_page_generator.pages import Pages from calibre.ebooks.mobi.reader.mobi6 import MobiReader from calibre.utils.logging import default_log @@ -14,14 +16,14 @@ from calibre.ebooks.pdb.header import PdbHeaderReader class IPageGenerator(metaclass=ABCMeta): @abstractmethod - def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: pass @abstractmethod - def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages: + def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: pass - def generate(self, mobi_file_path: str, real_count: int | None) -> Pages: + def generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: try: result = self._generate(mobi_file_path, real_count) if result.number_of_pages > 0: @@ -36,18 +38,17 @@ class IPageGenerator(metaclass=ABCMeta): def name(self) -> str: pass - @staticmethod - def mobi_html(mobi_file_path: str) -> bytes: - mr = MobiReader(mobi_file_path, default_log) - if mr.book_header.encryption_type != 0: - raise Exception("DRMed book") - mr.extract_text() - return as_bytes(mr.mobi_html.lower()) - @staticmethod - def mobi_html_length(mobi_file_path: str) -> int: - with lopen(mobi_file_path, 'rb') as mf: - pdb_header = PdbHeaderReader(mf) - r0 = pdb_header.section_data(0) - return struct.unpack('>I', r0[4:8])[0] +def mobi_html(mobi_file_path: str) -> bytes: + mr = MobiReader(mobi_file_path, default_log) + if mr.book_header.encryption_type != 0: + raise Exception("DRMed book") + mr.extract_text() + return as_bytes(mr.mobi_html.lower()) + +def mobi_html_length(mobi_file_path: str) -> int: + with lopen(mobi_file_path, 'rb') as mf: + pdb_header = PdbHeaderReader(mf) + r0 = pdb_header.section_data(0) + return struct.unpack('>I', r0[4:8])[0] diff --git a/src/calibre/devices/kindle/apnx_page_generator/page_group.py b/src/calibre/devices/kindle/apnx_page_generator/page_group.py index f8ea2488c4..b99e639023 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/page_group.py +++ b/src/calibre/devices/kindle/apnx_page_generator/page_group.py @@ -2,30 +2,32 @@ __license__ = 'GPL v3' __copyright__ = '2022, Vaso Peras-Likodric ' __docformat__ = 'restructuredtext en' +from typing import Union, List, Tuple + from calibre.devices.kindle.apnx_page_generator.page_number_type import PageNumberTypes class PageGroup: """Simulate constructor overloading""" - def __init__(self, page_locations: int | list[int], page_number_type: PageNumberTypes, first_value: int, - page_labels: str | list[str] | None = None): + def __init__(self, page_locations: Union[int, List[int]], page_number_type: PageNumberTypes, first_value: int, + page_labels: Union[str, List[str], None] = None): if page_locations.__class__ == int: - self.page_locations: list[int] = [page_locations] + self.page_locations: List[int] = [page_locations] else: - self.page_locations: list[int] = page_locations + self.page_locations: List[int] = page_locations self.__page_number_type: PageNumberTypes = page_number_type self.__first_value = first_value if page_number_type == PageNumberTypes.Custom: assert(page_labels is not None) if page_labels.__class__ == str: assert (1 == len(self.page_locations) and len(page_labels) > 0) - self.__page_number_labels: list[str] = [page_labels] + self.__page_number_labels: List[str] = [page_labels] else: assert (len(page_labels) == len(self.page_locations)) assert(all(len(label) > 0 for label in page_labels)) - self.__page_number_labels: list[str] = page_labels + self.__page_number_labels: List[str] = page_labels - def append(self, page_location: int | tuple[int, str]) -> None: + def append(self, page_location: Union[int, Tuple[int, str]]) -> None: if page_location.__class__ == int: assert (self.__page_number_type != PageNumberTypes.Custom) self.page_locations.append(page_location) diff --git a/src/calibre/devices/kindle/apnx_page_generator/page_number_type.py b/src/calibre/devices/kindle/apnx_page_generator/page_number_type.py index 93650522e9..4f468ab204 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/page_number_type.py +++ b/src/calibre/devices/kindle/apnx_page_generator/page_number_type.py @@ -5,7 +5,7 @@ __docformat__ = 'restructuredtext en' import enum -class PageNumberTypes(str, enum.Enum): +class PageNumberTypes(enum.Enum): Arabic = "a" Roman = "r" - Custom = 'c' + Custom = "c" diff --git a/src/calibre/devices/kindle/apnx_page_generator/pages.py b/src/calibre/devices/kindle/apnx_page_generator/pages.py index ff20943060..6edeeb875a 100644 --- a/src/calibre/devices/kindle/apnx_page_generator/pages.py +++ b/src/calibre/devices/kindle/apnx_page_generator/pages.py @@ -3,17 +3,18 @@ __copyright__ = '2022, Vaso Peras-Likodric ' __docformat__ = 'restructuredtext en' import itertools +from typing import Optional, List from calibre.devices.kindle.apnx_page_generator.page_group import PageGroup from calibre.devices.kindle.apnx_page_generator.page_number_type import PageNumberTypes class Pages: - def __init__(self, page_locations: list[int] | None = None): + def __init__(self, page_locations: Optional[List[int]] = None): if page_locations.__class__ == list: - self.__pages_groups: list[PageGroup] = [PageGroup(page_locations, PageNumberTypes.Arabic, 1)] + self.__pages_groups: List[PageGroup] = [PageGroup(page_locations, PageNumberTypes.Arabic, 1)] else: - self.__pages_groups: list[PageGroup] = [] + self.__pages_groups: List[PageGroup] = [] def append(self, page_location: PageGroup) -> None: self.__pages_groups.append(page_location) @@ -33,7 +34,7 @@ class Pages: return ",".join(result) @property - def page_locations(self) -> list[int]: + def page_locations(self) -> List[int]: return list(itertools.chain.from_iterable(list(map(lambda pg: pg.page_locations, self.__pages_groups)))) @property diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index 051d887cf3..647498f754 100644 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -411,7 +411,7 @@ class KINDLE2(KINDLE): OPT_APNX_CUST_COL = 2 OPT_APNX_METHOD_COL = 3 OPT_APNX_OVERWRITE = 4 - EXTRA_CUSTOMIZATION_CHOICES = {OPT_APNX_METHOD: APNXBuilder.generators.keys()} + EXTRA_CUSTOMIZATION_CHOICES = {OPT_APNX_METHOD: set(APNXBuilder.generators.keys())} # x330 on the PaperWhite # x262 on the Touch. Doesn't choke on x330, though.