mirror of
https://github.com/jo1gi/grawlix.git
synced 2026-07-10 01:34:49 -06:00
Merge 3bd6310974 into 1e6e5f161c
This commit is contained in:
commit
756b67f07a
@ -84,55 +84,91 @@ class Epub(OutputFormat):
|
||||
|
||||
async def _download_epub_in_parts(self, data: EpubInParts, metadata: Metadata, location: str, update: Update) -> None:
|
||||
files = data.files
|
||||
file_count = len(files)
|
||||
progress = 1/(file_count)
|
||||
progress = 1/len(files)
|
||||
temporary_file_location = f"{location}.tmp"
|
||||
|
||||
added_files: set[str] = set()
|
||||
def get_new_files(zipfile: ZipFile):
|
||||
"""Returns files in zipfile not already added to file"""
|
||||
for filename in zipfile.namelist():
|
||||
if filename in added_files or filename.endswith(".opf") or filename.endswith(".ncx"):
|
||||
continue
|
||||
yield filename
|
||||
# First occurrence wins; parts arrive in reading order and share most
|
||||
# files, so insertion order doubles as spine order.
|
||||
collected: dict[str, bytes] = {}
|
||||
try:
|
||||
for file in files:
|
||||
await self._download_and_write_file(file, temporary_file_location)
|
||||
with ZipFile(temporary_file_location, "r") as zipfile:
|
||||
for filepath in zipfile.namelist():
|
||||
if filepath.endswith("/") or filepath in collected or self._is_part_packaging(filepath):
|
||||
continue
|
||||
collected[filepath] = zipfile.read(filepath)
|
||||
if update:
|
||||
update(progress)
|
||||
finally:
|
||||
if os.path.exists(temporary_file_location):
|
||||
os.remove(temporary_file_location)
|
||||
|
||||
self._build_epub_from_files(collected, data.files_in_toc, metadata, location)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _is_part_packaging(filename: str) -> bool:
|
||||
"""
|
||||
True for files that belong to a part's own epub packaging. Each part is
|
||||
a complete epub, so copying these into the merged book would leave
|
||||
stray mimetype/container/package files next to the generated ones.
|
||||
"""
|
||||
lowered = filename.lower()
|
||||
return (
|
||||
lowered == "mimetype"
|
||||
or lowered.startswith("meta-inf/")
|
||||
or lowered.endswith((".opf", ".ncx"))
|
||||
)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _build_epub_from_files(
|
||||
files: dict[str, bytes],
|
||||
files_in_toc: dict[str, str],
|
||||
metadata: Metadata,
|
||||
location: str,
|
||||
) -> None:
|
||||
"""
|
||||
Assemble an epub from the raw files collected from its parts. Content
|
||||
documents are stored byte-for-byte as delivered by the source, so their
|
||||
stylesheet links, language attributes and markup survive; only the
|
||||
packaging (opf, ncx, nav) is generated.
|
||||
"""
|
||||
output = epub.EpubBook()
|
||||
for file in files:
|
||||
await self._download_and_write_file(file, temporary_file_location)
|
||||
with ZipFile(temporary_file_location, "r") as zipfile:
|
||||
for filepath in get_new_files(zipfile):
|
||||
content = zipfile.read(filepath)
|
||||
if filepath.endswith("html"):
|
||||
filename = os.path.basename(filepath)
|
||||
is_in_toc = False
|
||||
title = None
|
||||
for key, value in data.files_in_toc.items():
|
||||
toc_filename = key.split("#")[0]
|
||||
if filename == toc_filename:
|
||||
title = value
|
||||
is_in_toc = True
|
||||
break
|
||||
epub_file = epub.EpubHtml(
|
||||
title = title,
|
||||
file_name = filepath,
|
||||
content = content
|
||||
)
|
||||
output.add_item(epub_file)
|
||||
output.spine.append(epub_file)
|
||||
if is_in_toc:
|
||||
output.toc.append(epub_file)
|
||||
else:
|
||||
epub_file = epub.EpubItem(
|
||||
file_name = filepath,
|
||||
content = content
|
||||
)
|
||||
output.add_item(epub_file)
|
||||
added_files.add(filepath)
|
||||
if update:
|
||||
update(progress)
|
||||
os.remove(temporary_file_location)
|
||||
output.set_title(metadata.title)
|
||||
if metadata.language:
|
||||
output.set_language(metadata.language)
|
||||
for author in metadata.authors:
|
||||
output.add_author(author)
|
||||
|
||||
# Toc keys may or may not carry directory prefixes and fragments
|
||||
# ("Text/chap01.html#nav_1"); compare by basename like the spine files.
|
||||
toc_titles = {
|
||||
os.path.basename(key.split("#")[0]): title
|
||||
for key, title in files_in_toc.items()
|
||||
}
|
||||
cover_flagged = False
|
||||
for filepath, content in files.items():
|
||||
is_content_document = filepath.lower().endswith((".html", ".xhtml", ".htm"))
|
||||
item = epub.EpubItem(
|
||||
file_name = filepath,
|
||||
# Empty media type makes ebooklib guess from the extension
|
||||
media_type = "application/xhtml+xml" if is_content_document else "",
|
||||
content = content,
|
||||
)
|
||||
output.add_item(item)
|
||||
if is_content_document:
|
||||
output.spine.append(item)
|
||||
title = toc_titles.get(os.path.basename(filepath))
|
||||
if title is not None:
|
||||
output.toc.append(epub.Link(filepath, title, uid=item.get_id()))
|
||||
elif not cover_flagged and os.path.basename(filepath).lower().startswith("cover") \
|
||||
and item.media_type.startswith("image/"):
|
||||
item.properties = ["cover-image"]
|
||||
output.add_metadata(None, "meta", "", {"name": "cover", "content": item.get_id()})
|
||||
cover_flagged = True
|
||||
|
||||
output.add_item(epub.EpubNcx())
|
||||
output.add_item(epub.EpubNav())
|
||||
epub.write_epub(location, output)
|
||||
exit()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user