feat(cli/convert): add HTML zip output (#472)

Closes #437
This commit is contained in:
Jérome Eertmans
2024-09-11 15:30:38 +02:00
committed by GitHub
parent a39a9c2bb1
commit 1539b2f7e4
3 changed files with 50 additions and 13 deletions

View File

@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added support for `disable_caching` and `flush_cache` options from Manim, and
also the possibility to configure them through class options.
[#452](https://github.com/jeertmans/manim-slides/pull/452)
- Added `--to=zip` convert format to generate an archive with HTML output
and asset files.
[#470](https://github.com/jeertmans/manim-slides/pull/470)
(unreleased-chore)=
### Chore

View File

@ -1,6 +1,7 @@
import mimetypes
import os
import platform
import shutil
import subprocess
import tempfile
import webbrowser
@ -115,9 +116,9 @@ class Converter(BaseModel): # type: ignore
"""
return ""
def open(self, file: Path) -> Any:
def open(self, file: Path) -> None:
"""Open a file, generated with converter, using appropriate application."""
raise NotImplementedError
open_with_default(file)
@classmethod
def from_string(cls, s: str) -> type["Converter"]:
@ -126,6 +127,7 @@ class Converter(BaseModel): # type: ignore
"html": RevealJS,
"pdf": PDF,
"pptx": PowerPoint,
"zip": HtmlZip,
}[s]
@ -380,8 +382,8 @@ class RevealJS(Converter):
return resources.files(templates).joinpath("revealjs.html").read_text()
def open(self, file: Path) -> bool:
return webbrowser.open(file.absolute().as_uri())
def open(self, file: Path) -> None:
webbrowser.open(file.absolute().as_uri())
def convert_to(self, dest: Path) -> None:
"""
@ -452,6 +454,24 @@ class RevealJS(Converter):
f.write(content)
class HtmlZip(RevealJS):
def open(self, file: Path) -> None:
super(RevealJS, self).open(file) # Override opening with web browser
def convert_to(self, dest: Path) -> None:
"""
Convert this configuration into a zipped RevealJS HTML presentation, saved to
DEST.
"""
with tempfile.TemporaryDirectory() as directory_name:
directory = Path(directory_name)
html_file = directory / dest.with_suffix(".html").name
super().convert_to(html_file)
shutil.make_archive(str(dest.with_suffix("")), "zip", directory_name)
class FrameIndex(str, Enum):
first = "first"
last = "last"
@ -462,9 +482,6 @@ class PDF(Converter):
resolution: PositiveFloat = 100.0
model_config = ConfigDict(use_enum_values=True, extra="forbid")
def open(self, file: Path) -> None:
return open_with_default(file)
def convert_to(self, dest: Path) -> None:
"""Convert this configuration into a PDF presentation, saved to DEST."""
images = []
@ -499,9 +516,6 @@ class PowerPoint(Converter):
poster_frame_image: Optional[FilePath] = None
model_config = ConfigDict(use_enum_values=True, extra="forbid")
def open(self, file: Path) -> None:
return open_with_default(file)
def convert_to(self, dest: Path) -> None:
"""Convert this configuration into a PowerPoint presentation, saved to DEST."""
prs = pptx.Presentation()
@ -640,7 +654,7 @@ def show_template_option(function: Callable[..., Any]) -> Callable[..., Any]:
@click.argument("dest", type=click.Path(dir_okay=False, path_type=Path))
@click.option(
"--to",
type=click.Choice(["auto", "html", "pdf", "pptx"], case_sensitive=False),
type=click.Choice(["auto", "html", "zip", "pdf", "pptx"], case_sensitive=False),
metavar="FORMAT",
default="auto",
show_default=True,

View File

@ -1,3 +1,4 @@
import shutil
from enum import EnumMeta
from pathlib import Path
@ -17,6 +18,7 @@ from manim_slides.convert import (
ControlsLayout,
Converter,
Display,
HtmlZip,
JsBool,
JsFalse,
JsNull,
@ -138,7 +140,8 @@ def test_unquoted_enum(enum_type: EnumMeta) -> None:
class TestConverter:
@pytest.mark.parametrize(
("name", "converter"), [("html", RevealJS), ("pdf", PDF), ("pptx", PowerPoint)]
("name", "converter"),
[("html", RevealJS), ("pdf", PDF), ("pptx", PowerPoint), ("zip", HtmlZip)],
)
def test_from_string(self, name: str, converter: type) -> None:
assert Converter.from_string(name) == converter
@ -150,9 +153,26 @@ class TestConverter:
RevealJS(presentation_configs=[presentation_config]).convert_to(out_file)
assert out_file.exists()
assert Path(tmp_path / "slides_assets").is_dir()
file_contents = Path(out_file).read_text()
file_contents = out_file.read_text()
assert "manim" in file_contents.casefold()
def test_htmlzip_converter(
self, tmp_path: Path, presentation_config: PresentationConfig
) -> None:
archive = tmp_path / "got.zip"
expected = tmp_path / "expected.html"
got = tmp_path / "got.html"
HtmlZip(presentation_configs=[presentation_config]).convert_to(archive)
RevealJS(presentation_configs=[presentation_config]).convert_to(expected)
shutil.unpack_archive(str(archive))
assert got.exists()
assert expected.exists()
assert got.read_text() == expected.read_text()
@pytest.mark.parametrize("num_presentation_configs", (1, 2))
def test_revealjs_multiple_scenes_converter(
self,