Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2c66a1876 |
@@ -136,7 +136,6 @@ venv/
|
|||||||
ENV/
|
ENV/
|
||||||
env.bak/
|
env.bak/
|
||||||
venv.bak/
|
venv.bak/
|
||||||
.tmp-samples/
|
|
||||||
|
|
||||||
# Spyder project settings
|
# Spyder project settings
|
||||||
.spyderproject
|
.spyderproject
|
||||||
|
|||||||
@@ -10,9 +10,3 @@
|
|||||||
[submodule "src/vendor/epub2tts-kokoro"]
|
[submodule "src/vendor/epub2tts-kokoro"]
|
||||||
path = src/vendor/epub2tts-kokoro
|
path = src/vendor/epub2tts-kokoro
|
||||||
url = https://github.com/aedocw/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
|
|
||||||
|
|||||||
@@ -18,18 +18,6 @@ Recent Python (>=3.11), pip (>=22.3), some sort of virtula environment recommend
|
|||||||
3. Test install running `epub-tts --version`
|
3. Test install running `epub-tts --version`
|
||||||
- Make sure the venv is activated or that pip installed the package in your PATH
|
- 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
|
# Citation
|
||||||
|
|||||||
@@ -41,9 +41,6 @@ Issues = "https://git.silveirarosa.com/renatoxsr/epub-tts/issues"
|
|||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
epub-tts = "epub_tts.__main__:main"
|
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
|
# SETUPTOOLS
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
|
|||||||
@@ -1,25 +1,44 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
||||||
|
# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||||||
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||||
|
"""Command-line interface for converting EPUB to audio using TTS.
|
||||||
|
|
||||||
|
This module provides a command-line interface (CLI) for converting EPUB files to audio using text-to-speech (TTS) backends. It allows users to specify the input EPUB file, output audio file, language, voice, and backend to use for TTS conversion.
|
||||||
|
|
||||||
|
usage: epub-tts [-h] [--version] -i INPUT -o OUTPUT [-l LANGUAGE]
|
||||||
|
[-v VOICE] [-b {default,edge,epub2tts,epub2tts-edge,epub2tts-chatterbox,epub2tts-kokoro}]
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--version Show version and exit
|
||||||
|
-i INPUT, --input INPUT
|
||||||
|
Input EPUB file
|
||||||
|
-o OUTPUT, --output OUTPUT
|
||||||
|
Output audio file
|
||||||
|
-l LANGUAGE, --language LANGUAGE
|
||||||
|
Language to use for TTS
|
||||||
|
-v VOICE, --voice Voice to use for TTS
|
||||||
|
-b {default,edge,epub2tts,epub2tts-edge,epub2tts-chatterbox,epub2tts-kokoro}, --backend {default,edge,epub2tts,epub2tts-edge,epub2tts-chatterbox,epub2tts-kokoro}
|
||||||
|
Backend to use for TTS
|
||||||
|
"""
|
||||||
import sys
|
import sys
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
from epub_tts import aedocw_backend, tts_backend, tts_edge
|
from . import tts_aedocw, tts_edge, tts_generic
|
||||||
|
|
||||||
backend = {
|
backend = {
|
||||||
# keys are the backend names, values are the corresponding TTS classes
|
# keys are the backend names, values are the corresponding TTS classes
|
||||||
"default": tts_backend.GenericTTSBackend,
|
"default": tts_generic.GenericTTSBackend,
|
||||||
"edge": tts_edge.TTSEdge,
|
"edge": tts_edge.TTSEdge,
|
||||||
# aedocw-backed implementations — these expect the corresponding
|
# aedocw-backed implementations — these expect the corresponding
|
||||||
# package "console scripts"/entrypoints to be available in the
|
# package "console scripts"/entrypoints to be available in the
|
||||||
# environment (or an explicit backend_cmd to be provided).
|
# environment (or an explicit backend_cmd to be provided).
|
||||||
"epub2tts": aedocw_backend.AedocwEpub2TTS,
|
"epub2tts": tts_aedocw.AedocwEpub2TTS,
|
||||||
"epub2tts-edge": aedocw_backend.AedocwEpub2TTSEdge,
|
"epub2tts-edge": tts_aedocw.AedocwEpub2TTSEdge,
|
||||||
"epub2tts-chatterbox": aedocw_backend.AedocwChatterbox,
|
"epub2tts-chatterbox": tts_aedocw.AedocwChatterbox,
|
||||||
"epub2tts-kokoro": aedocw_backend.AedocwKokoro,
|
"epub2tts-kokoro": tts_aedocw.AedocwKokoro,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
#!/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())
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
||||||
|
# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||||||
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||||
"""Backends for the aedocw collection of EPUB->TTS repositories.
|
"""Backends for the aedocw collection of EPUB->TTS repositories.
|
||||||
|
|
||||||
@@ -14,11 +15,11 @@ corresponding console script is not available in the current environment.
|
|||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from . import epub2tts as epub2tts_adapter
|
from . import tts_epub2tts as epub2tts_adapter
|
||||||
from . import epub2tts_chatterbox as epub2tts_chatterbox_adapter
|
from . import tts_epub2tts_chatterbox as epub2tts_chatterbox_adapter
|
||||||
from . import epub2tts_edge as epub2tts_edge_adapter
|
from . import tts_epub2tts_edge as epub2tts_edge_adapter
|
||||||
from . import epub2tts_kokoro as epub2tts_kokoro_adapter
|
from . import tts_epub2tts_kokoro as epub2tts_kokoro_adapter
|
||||||
from .tts_backend import GenericTTSBackend
|
from .tts_generic import GenericTTSBackend
|
||||||
|
|
||||||
|
|
||||||
class AedocwBackend(GenericTTSBackend):
|
class AedocwBackend(GenericTTSBackend):
|
||||||
@@ -2,9 +2,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
# 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)
|
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||||
|
# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||||||
"""Compatibility shim for the edge backend in epub-tts."""
|
"""Compatibility shim for the edge backend in epub-tts."""
|
||||||
|
|
||||||
from .aedocw_backend import AedocwEpub2TTSEdge
|
from .tts_aedocw import AedocwEpub2TTSEdge
|
||||||
|
|
||||||
|
|
||||||
class TTSEdge(AedocwEpub2TTSEdge):
|
class TTSEdge(AedocwEpub2TTSEdge):
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
# 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)
|
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||||
|
# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||||||
"""Adapter for the vendored github.com/aedocw/epub2tts repository."""
|
"""Adapter for the vendored github.com/aedocw/epub2tts repository."""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
# 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)
|
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||||
|
# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||||||
"""Adapter for the vendored github.com/aedocw/epub2tts-chatterbox repository."""
|
"""Adapter for the vendored github.com/aedocw/epub2tts-chatterbox repository."""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
# 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)
|
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||||
|
# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||||||
"""Adapter for the vendored github.com/aedocw/epub2tts-edge repository."""
|
"""Adapter for the vendored github.com/aedocw/epub2tts-edge repository."""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
# 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)
|
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||||
|
# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||||||
"""Adapter for the vendored github.com/aedocw/epub2tts-kokoro repository."""
|
"""Adapter for the vendored github.com/aedocw/epub2tts-kokoro repository."""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
# 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)
|
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||||
|
# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||||||
"""Generic TTS Backend for handling text-to-speech conversion.
|
"""Generic TTS Backend for handling text-to-speech conversion.
|
||||||
|
|
||||||
This module provides a generic TTS backend that can be used to convert text to speech using various TTS engines. It defines a `GenericTTSBackend` class that can be initialized with a specific backend command, language, and voice. The class provides methods to normalize paths, determine input and output flags based on the type of input/output (file or directory), build the command for the TTS engine, and run the command.
|
This module provides a generic TTS backend that can be used to convert text to speech using various TTS engines. It defines a `GenericTTSBackend` class that can be initialized with a specific backend command, language, and voice. The class provides methods to normalize paths, determine input and output flags based on the type of input/output (file or directory), build the command for the TTS engine, and run the command.
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2025-2026 Renato Xavier da Silveira Rosa
|
# 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)
|
# See [LICENSE](./LICENSE) or [BSD-3-Clause-Clear](https://spdx.org/licenses/BSD-3-Clause-Clear.html)
|
||||||
|
# Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
||||||
"""Utilities for running vendored EPUB->TTS repositories in isolated venvs."""
|
"""Utilities for running vendored EPUB->TTS repositories in isolated venvs."""
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|||||||
Vendored
-1
Submodule src/vendor/epub3-samples deleted from 7651e2002b
Vendored
-1
Submodule src/vendor/sample-epub-minimal deleted from 8e7d6cdd91
Reference in New Issue
Block a user