From fcb4d8884d6f7ffd689d696603abc2f3ef06e58c Mon Sep 17 00:00:00 2001 From: RenatoXSR Date: Thu, 6 Aug 2026 16:33:59 -0300 Subject: [PATCH] [epub-tts] Add EPUB sample submodules and download entrypoints --- .gitignore | 1 + .gitmodules | 6 ++ README.md | 12 +++ pyproject.toml | 3 + src/epub_tts/sample_download.py | 160 ++++++++++++++++++++++++++++++++ src/vendor/epub3-samples | 1 + src/vendor/sample-epub-minimal | 1 + 7 files changed, 184 insertions(+) create mode 100644 src/epub_tts/sample_download.py create mode 160000 src/vendor/epub3-samples create mode 160000 src/vendor/sample-epub-minimal diff --git a/.gitignore b/.gitignore index 63fc783..cb8314b 100644 --- a/.gitignore +++ b/.gitignore @@ -136,6 +136,7 @@ venv/ ENV/ env.bak/ venv.bak/ +.tmp-samples/ # Spyder project settings .spyderproject diff --git a/.gitmodules b/.gitmodules index a49cc23..f942b06 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,9 @@ [submodule "src/vendor/epub2tts-kokoro"] path = src/vendor/epub2tts-kokoro url = https://github.com/aedocw/epub2tts-kokoro +[submodule "src/vendor/epub3-samples"] + path = src/vendor/epub3-samples + url = https://github.com/IDPF/epub3-samples +[submodule "src/vendor/sample-epub-minimal"] + path = src/vendor/sample-epub-minimal + url = https://github.com/thansen0/sample-epub-minimal diff --git a/README.md b/README.md index 8703307..d4170c3 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,18 @@ Recent Python (>=3.11), pip (>=22.3), some sort of virtula environment recommend 3. Test install running `epub-tts --version` - Make sure the venv is activated or that pip installed the package in your PATH +# Sample EPUB download + +After cloning with submodules, the repository now includes the EPUB 3 sample collection under `src/vendor/epub3-samples`. + +Use the new console entrypoints to export the Moby Dick samples as EPUB files: + +- `download-moby-dick` creates `.tmp-samples/moby-dick.epub` +- `download-moby-dick-media-overlays` creates `.tmp-samples/moby-dick-mo.epub` +- `download-epub-minimal` creates `.tmp-samples/minimal.epub` + +The staging directory `.tmp-samples/` is included in `.gitignore` so generated artifacts stay out of version control. + # # Citation diff --git a/pyproject.toml b/pyproject.toml index 8f93971..f8c2055 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,6 +41,9 @@ Issues = "https://git.silveirarosa.com/renatoxsr/epub-tts/issues" [project.scripts] epub-tts = "epub_tts.__main__:main" +download-moby-dick = "epub_tts.sample_download:main" +download-moby-dick-media-overlays = "epub_tts.sample_download:main_with_media_overlay" +download-epub-minimal = "epub_tts.sample_download:main_minimal_epub" # SETUPTOOLS [tool.setuptools.packages.find] diff --git a/src/epub_tts/sample_download.py b/src/epub_tts/sample_download.py new file mode 100644 index 0000000..2641a2b --- /dev/null +++ b/src/epub_tts/sample_download.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa +# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html) + +"""Download EPUB sample files from a vendored samples repository and package them as EPUB archives.""" + +from __future__ import annotations + +import argparse +import shutil +import zipfile +from pathlib import Path +from typing import Optional + +from epub_tts.vendor_utils import get_repo_path + + +def get_project_root() -> Path: + return Path(__file__).resolve().parents[1].parent + + +def get_temp_sample_root() -> Path: + return get_project_root() / ".tmp-samples" + + +def get_sample_directory(with_media_overlay: bool) -> Path: + sample_name = "moby-dick-mo" if with_media_overlay else "moby-dick" + sample_dir = get_repo_path("epub3-samples") / "30" / sample_name + if not sample_dir.exists(): + raise FileNotFoundError( + f"Sample directory not found: {sample_dir}. Ensure the epub3-samples submodule is initialized." + ) + return sample_dir + + +def get_minimal_sample_file() -> Path: + sample_file = get_repo_path("sample-epub-minimal") / "minimal.epub" + if not sample_file.exists(): + raise FileNotFoundError( + f"Sample EPUB file not found: {sample_file}. Ensure the sample-epub-minimal submodule is initialized." + ) + return sample_file + + +def copy_sample_to_temp(sample_dir: Path) -> Path: + temp_root = get_temp_sample_root() + temp_root.mkdir(parents=True, exist_ok=True) + dest_dir = temp_root / sample_dir.name + if dest_dir.exists(): + shutil.rmtree(dest_dir) + shutil.copytree(sample_dir, dest_dir) + return dest_dir + + +def copy_file_to_temp(sample_file: Path) -> Path: + temp_root = get_temp_sample_root() + temp_root.mkdir(parents=True, exist_ok=True) + dest_file = temp_root / sample_file.name + shutil.copy2(sample_file, dest_file) + return dest_file + + +def zip_sample_directory(sample_dir: Path, output_path: Path) -> Path: + output_path.parent.mkdir(parents=True, exist_ok=True) + if output_path.exists(): + output_path.unlink() + + mimetype_file = sample_dir / "mimetype" + with zipfile.ZipFile(output_path, mode="w", compression=zipfile.ZIP_DEFLATED) as archive: + if mimetype_file.exists(): + archive.write(mimetype_file, "mimetype", compress_type=zipfile.ZIP_STORED) + for path in sorted(sample_dir.rglob("*")): + if path.is_file() and path != mimetype_file: + archive.write(path, path.relative_to(sample_dir).as_posix()) + + return output_path + + +def download_sample_moby_dick( + output: Optional[str] = None, + with_media_overlay: bool = False, +) -> Path: + sample_dir = get_sample_directory(with_media_overlay=with_media_overlay) + temp_sample_dir = copy_sample_to_temp(sample_dir) + default_output = get_temp_sample_root() / f"{sample_dir.name}.epub" + output_path = Path(output) if output else default_output + output_path = output_path.with_suffix(".epub") + return zip_sample_directory(temp_sample_dir, output_path) + + +def download_minimal_epub_sample(output: Optional[str] = None) -> Path: + sample_file = get_minimal_sample_file() + temp_sample_file = copy_file_to_temp(sample_file) + default_output = get_temp_sample_root() / sample_file.name + output_path = Path(output) if output else default_output + output_path = output_path.with_suffix(".epub") + if temp_sample_file != output_path: + output_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(temp_sample_file, output_path) + return output_path + + +def main(argv: Optional[list[str]] = None) -> int: + parser = argparse.ArgumentParser( + description="Download the Moby Dick EPUB sample and package it as an EPUB file." + ) + parser.add_argument( + "-o", + "--output", + help="Output EPUB file path. Defaults to .tmp-samples/moby-dick.epub or .tmp-samples/moby-dick-mo.epub.", + ) + args = parser.parse_args(argv) + output_path = download_sample_moby_dick( + output=args.output, + with_media_overlay=False, + ) + print(f"Created EPUB sample archive: {output_path}") + return 0 + + +def main_with_media_overlay(argv: Optional[list[str]] = None) -> int: + parser = argparse.ArgumentParser( + description=( + "Download the Moby Dick EPUB sample with media overlay and package it as an EPUB file." + ) + ) + parser.add_argument( + "-o", + "--output", + help="Output EPUB file path. Defaults to .tmp-samples/moby-dick-mo.epub.", + ) + args = parser.parse_args(argv) + output_path = download_sample_moby_dick( + output=args.output, + with_media_overlay=True, + ) + print(f"Created EPUB sample archive: {output_path}") + return 0 + + +def main_minimal_epub(argv: Optional[list[str]] = None) -> int: + parser = argparse.ArgumentParser( + description=( + "Download the minimal EPUB sample and copy it to an EPUB file." + ) + ) + parser.add_argument( + "-o", + "--output", + help="Output EPUB file path. Defaults to .tmp-samples/minimal.epub.", + ) + args = parser.parse_args(argv) + output_path = download_minimal_epub_sample(output=args.output) + print(f"Created minimal EPUB sample archive: {output_path}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/vendor/epub3-samples b/src/vendor/epub3-samples new file mode 160000 index 0000000..7651e20 --- /dev/null +++ b/src/vendor/epub3-samples @@ -0,0 +1 @@ +Subproject commit 7651e2002b631e6577fadf7e9e0692fa6efb8746 diff --git a/src/vendor/sample-epub-minimal b/src/vendor/sample-epub-minimal new file mode 160000 index 0000000..8e7d6cd --- /dev/null +++ b/src/vendor/sample-epub-minimal @@ -0,0 +1 @@ +Subproject commit 8e7d6cdd91030c991a0e637a3470f286d8735437