mirror of
https://github.com/jeertmans/manim-slides.git
synced 2025-08-06 14:19:52 +08:00
chore(lib): remove all os.path in favor to pathlib (#225)
* chore(lib): remove all os.path in favor to pathlib * fix(lib): str to path
This commit is contained in:
@ -1,6 +1,5 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
@ -261,11 +260,11 @@ class PresentationConfig(BaseModel): # type: ignore
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
f = tempfile.NamedTemporaryFile(mode="w", delete=False)
|
f = tempfile.NamedTemporaryFile(mode="w", delete=False)
|
||||||
f.writelines(f"file '{os.path.abspath(path)}'\n" for path in files)
|
f.writelines(f"file '{path.absolute()}'\n" for path in files)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
command: List[str] = [
|
command: List[str] = [
|
||||||
FFMPEG_BIN,
|
str(FFMPEG_BIN),
|
||||||
"-f",
|
"-f",
|
||||||
"concat",
|
"concat",
|
||||||
"-safe",
|
"-safe",
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
FOLDER_PATH: str = "./slides"
|
from pathlib import Path
|
||||||
CONFIG_PATH: str = ".manim-slides.toml"
|
|
||||||
FFMPEG_BIN: str = "ffmpeg"
|
FOLDER_PATH: Path = Path("./slides")
|
||||||
|
CONFIG_PATH: Path = Path(".manim-slides.toml")
|
||||||
|
FFMPEG_BIN: Path = Path("ffmpeg")
|
||||||
|
@ -76,7 +76,7 @@ class Presentation:
|
|||||||
|
|
||||||
self.__current_slide_index: int = 0
|
self.__current_slide_index: int = 0
|
||||||
self.current_animation: int = self.current_slide.start_animation
|
self.current_animation: int = self.current_slide.start_animation
|
||||||
self.current_file: str = ""
|
self.current_file: Path = Path("")
|
||||||
|
|
||||||
self.loaded_animation_cap: int = -1
|
self.loaded_animation_cap: int = -1
|
||||||
self.cap = None # cap = cv2.VideoCapture
|
self.cap = None # cap = cv2.VideoCapture
|
||||||
@ -185,10 +185,10 @@ class Presentation:
|
|||||||
|
|
||||||
self.release_cap()
|
self.release_cap()
|
||||||
|
|
||||||
file: str = str(self.files[animation])
|
file: Path = self.files[animation]
|
||||||
|
|
||||||
if self.reverse:
|
if self.reverse:
|
||||||
file = "{}_reversed{}".format(*os.path.splitext(file))
|
file = file.parent / f"{file.stem}_reversed{file.suffix}"
|
||||||
self.reversed_animation = animation
|
self.reversed_animation = animation
|
||||||
|
|
||||||
self.current_file = file
|
self.current_file = file
|
||||||
@ -371,7 +371,7 @@ class Display(QThread): # type: ignore
|
|||||||
self.config = config
|
self.config = config
|
||||||
self.skip_all = skip_all
|
self.skip_all = skip_all
|
||||||
self.record_to = record_to
|
self.record_to = record_to
|
||||||
self.recordings: List[Tuple[str, int, int]] = []
|
self.recordings: List[Tuple[Path, int, int]] = []
|
||||||
|
|
||||||
self.state = State.PLAYING
|
self.state = State.PLAYING
|
||||||
self.lastframe: Optional[np.ndarray] = None
|
self.lastframe: Optional[np.ndarray] = None
|
||||||
@ -480,7 +480,7 @@ class Display(QThread): # type: ignore
|
|||||||
)
|
)
|
||||||
file, frame_number, fps = self.recordings[0]
|
file, frame_number, fps = self.recordings[0]
|
||||||
|
|
||||||
cap = cv2.VideoCapture(file)
|
cap = cv2.VideoCapture(str(file))
|
||||||
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number - 1)
|
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number - 1)
|
||||||
_, frame = cap.read()
|
_, frame = cap.read()
|
||||||
|
|
||||||
@ -496,7 +496,7 @@ class Display(QThread): # type: ignore
|
|||||||
if file != _file:
|
if file != _file:
|
||||||
cap.release()
|
cap.release()
|
||||||
file = _file
|
file = _file
|
||||||
cap = cv2.VideoCapture(_file)
|
cap = cv2.VideoCapture(str(_file))
|
||||||
|
|
||||||
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number - 1)
|
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number - 1)
|
||||||
_, frame = cap.read()
|
_, frame = cap.read()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import os
|
|
||||||
import platform
|
import platform
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
List,
|
List,
|
||||||
@ -9,6 +9,7 @@ from typing import (
|
|||||||
MutableMapping,
|
MutableMapping,
|
||||||
Optional,
|
Optional,
|
||||||
Sequence,
|
Sequence,
|
||||||
|
Set,
|
||||||
Tuple,
|
Tuple,
|
||||||
ValuesView,
|
ValuesView,
|
||||||
)
|
)
|
||||||
@ -34,9 +35,9 @@ from .manim import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def reverse_video_file(src: str, dst: str) -> None:
|
def reverse_video_file(src: Path, dst: Path) -> None:
|
||||||
"""Reverses a video file, writting the result to `dst`."""
|
"""Reverses a video file, writting the result to `dst`."""
|
||||||
command = [FFMPEG_BIN, "-i", src, "-vf", "reverse", dst]
|
command = [str(FFMPEG_BIN), "-i", str(src), "-vf", "reverse", str(dst)]
|
||||||
logger.debug(" ".join(command))
|
logger.debug(" ".join(command))
|
||||||
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
output, error = process.communicate()
|
output, error = process.communicate()
|
||||||
@ -54,11 +55,10 @@ class Slide(Scene): # type:ignore
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, *args: Any, output_folder: str = FOLDER_PATH, **kwargs: Any
|
self, *args: Any, output_folder: Path = FOLDER_PATH, **kwargs: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
if MANIMGL:
|
if MANIMGL:
|
||||||
if not os.path.isdir("videos"):
|
Path("videos").mkdir(exist_ok=True)
|
||||||
os.mkdir("videos")
|
|
||||||
kwargs["file_writer_config"] = {
|
kwargs["file_writer_config"] = {
|
||||||
"break_into_partial_movies": True,
|
"break_into_partial_movies": True,
|
||||||
"output_directory": "",
|
"output_directory": "",
|
||||||
@ -69,7 +69,7 @@ class Slide(Scene): # type:ignore
|
|||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.__output_folder = output_folder
|
self.__output_folder: Path = output_folder
|
||||||
self.__slides: List[SlideConfig] = []
|
self.__slides: List[SlideConfig] = []
|
||||||
self.__current_slide = 1
|
self.__current_slide = 1
|
||||||
self.__current_animation = 0
|
self.__current_animation = 0
|
||||||
@ -111,7 +111,7 @@ class Slide(Scene): # type:ignore
|
|||||||
return config["pixel_width"], config["pixel_height"]
|
return config["pixel_width"], config["pixel_height"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def __partial_movie_files(self) -> List[str]:
|
def __partial_movie_files(self) -> List[Path]:
|
||||||
"""Returns a list of partial movie files, a.k.a animations."""
|
"""Returns a list of partial movie files, a.k.a animations."""
|
||||||
if MANIMGL:
|
if MANIMGL:
|
||||||
from manimlib.utils.file_ops import get_sorted_integer_files
|
from manimlib.utils.file_ops import get_sorted_integer_files
|
||||||
@ -120,11 +120,13 @@ class Slide(Scene): # type:ignore
|
|||||||
"remove_non_integer_files": True,
|
"remove_non_integer_files": True,
|
||||||
"extension": self.file_writer.movie_file_extension,
|
"extension": self.file_writer.movie_file_extension,
|
||||||
}
|
}
|
||||||
return get_sorted_integer_files( # type: ignore
|
files = get_sorted_integer_files(
|
||||||
self.file_writer.partial_movie_directory, **kwargs
|
self.file_writer.partial_movie_directory, **kwargs
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return self.renderer.file_writer.partial_movie_files # type: ignore
|
files = self.renderer.file_writer.partial_movie_files
|
||||||
|
|
||||||
|
return [Path(file) for file in files]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def __show_progress_bar(self) -> bool:
|
def __show_progress_bar(self) -> bool:
|
||||||
@ -470,25 +472,23 @@ class Slide(Scene): # type:ignore
|
|||||||
"""
|
"""
|
||||||
self.__add_last_slide()
|
self.__add_last_slide()
|
||||||
|
|
||||||
if not os.path.exists(self.__output_folder):
|
self.__output_folder.mkdir(parents=True, exist_ok=True)
|
||||||
os.mkdir(self.__output_folder)
|
|
||||||
|
|
||||||
files_folder = os.path.join(self.__output_folder, "files")
|
files_folder = self.__output_folder / "files"
|
||||||
if not os.path.exists(files_folder):
|
files_folder.mkdir(exist_ok=True)
|
||||||
os.mkdir(files_folder)
|
|
||||||
|
|
||||||
scene_name = str(self)
|
scene_name = str(self)
|
||||||
scene_files_folder = os.path.join(files_folder, scene_name)
|
scene_files_folder = files_folder / scene_name
|
||||||
|
|
||||||
old_animation_files = set()
|
old_animation_files: Set[Path] = set()
|
||||||
|
|
||||||
if not os.path.exists(scene_files_folder):
|
if not scene_files_folder.exists():
|
||||||
os.mkdir(scene_files_folder)
|
scene_files_folder.mkdir()
|
||||||
elif not use_cache:
|
elif not use_cache:
|
||||||
shutil.rmtree(scene_files_folder)
|
shutil.rmtree(scene_files_folder)
|
||||||
os.mkdir(scene_files_folder)
|
scene_files_folder.mkdir()
|
||||||
else:
|
else:
|
||||||
old_animation_files.update(os.listdir(scene_files_folder))
|
old_animation_files.update(scene_files_folder.iterdir())
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
for src_file in tqdm(
|
for src_file in tqdm(
|
||||||
@ -504,10 +504,11 @@ class Slide(Scene): # type:ignore
|
|||||||
# but animations before a will have a None src_file
|
# but animations before a will have a None src_file
|
||||||
continue
|
continue
|
||||||
|
|
||||||
filename = os.path.basename(src_file)
|
filename = src_file.name
|
||||||
rev_filename = "{}_reversed{}".format(*os.path.splitext(filename))
|
rev_filename = (
|
||||||
|
src_file.parent / f"{src_file.stem}_reversed{src_file.suffix}"
|
||||||
dst_file = os.path.join(scene_files_folder, filename)
|
)
|
||||||
|
dst_file = scene_files_folder / filename
|
||||||
# We only copy animation if it was not present
|
# We only copy animation if it was not present
|
||||||
if filename in old_animation_files:
|
if filename in old_animation_files:
|
||||||
old_animation_files.remove(filename)
|
old_animation_files.remove(filename)
|
||||||
@ -518,7 +519,7 @@ class Slide(Scene): # type:ignore
|
|||||||
if rev_filename in old_animation_files:
|
if rev_filename in old_animation_files:
|
||||||
old_animation_files.remove(rev_filename)
|
old_animation_files.remove(rev_filename)
|
||||||
else:
|
else:
|
||||||
rev_file = os.path.join(scene_files_folder, rev_filename)
|
rev_file = scene_files_folder / rev_filename
|
||||||
reverse_video_file(src_file, rev_file)
|
reverse_video_file(src_file, rev_file)
|
||||||
|
|
||||||
files.append(dst_file)
|
files.append(dst_file)
|
||||||
@ -533,23 +534,20 @@ class Slide(Scene): # type:ignore
|
|||||||
slide.end_animation -= offset
|
slide.end_animation -= offset
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Copied {len(files)} animations to '{os.path.abspath(scene_files_folder)}' and generated reversed animations"
|
f"Copied {len(files)} animations to '{scene_files_folder.absolute()}' and generated reversed animations"
|
||||||
)
|
)
|
||||||
|
|
||||||
slide_path = os.path.join(self.__output_folder, "%s.json" % (scene_name,))
|
slide_path = self.__output_folder / f"{scene_name}.json"
|
||||||
|
|
||||||
with open(slide_path, "w") as f:
|
PresentationConfig(
|
||||||
f.write(
|
slides=self.__slides,
|
||||||
PresentationConfig(
|
files=files,
|
||||||
slides=self.__slides,
|
resolution=self.__resolution,
|
||||||
files=files,
|
background_color=self.__background_color,
|
||||||
resolution=self.__resolution,
|
).to_file(slide_path)
|
||||||
background_color=self.__background_color,
|
|
||||||
).model_dump_json(indent=2)
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Slide '{scene_name}' configuration written in '{os.path.abspath(slide_path)}'"
|
f"Slide '{scene_name}' configuration written in '{slide_path.absolute()}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self, *args: Any, **kwargs: Any) -> None:
|
def run(self, *args: Any, **kwargs: Any) -> None:
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from manim_slides.defaults import CONFIG_PATH, FFMPEG_BIN, FOLDER_PATH
|
from manim_slides.defaults import CONFIG_PATH, FFMPEG_BIN, FOLDER_PATH
|
||||||
|
|
||||||
|
|
||||||
def test_folder_path() -> None:
|
def test_folder_path() -> None:
|
||||||
assert FOLDER_PATH == "./slides"
|
assert FOLDER_PATH == Path("./slides")
|
||||||
|
|
||||||
|
|
||||||
def test_config_path() -> None:
|
def test_config_path() -> None:
|
||||||
assert CONFIG_PATH == ".manim-slides.toml"
|
assert CONFIG_PATH == Path(".manim-slides.toml")
|
||||||
|
|
||||||
|
|
||||||
def test_ffmpeg_bin() -> None:
|
def test_ffmpeg_bin() -> None:
|
||||||
assert FFMPEG_BIN == "ffmpeg"
|
assert FFMPEG_BIN == Path("ffmpeg")
|
||||||
|
Reference in New Issue
Block a user