WIP: reversing videos, etc.

This commit is contained in:
Jérome Eertmans
2022-07-18 12:38:06 +02:00
parent e82ab99186
commit efbe488660
4 changed files with 82 additions and 10 deletions

View File

@ -3,7 +3,7 @@
![PyPI - Downloads](https://img.shields.io/pypi/dm/manim-slides) ![PyPI - Downloads](https://img.shields.io/pypi/dm/manim-slides)
# Manim Slides # Manim Slides
Tool for live presentations using either [manim](http://3b1b.github.io/manim/) or [manim-community](https://www.manim.community/). Tool for live presentations using either [manim-community](https://www.manim.community/). Currently, support for 3b1b's manim is not planned.
> **_NOTE:_** This project is a fork of [`manim-presentation`](https://github.com/galatolofederico/manim-presentation). Since the project seemed to be inactive, I decided to create my own fork to deploy new features more rapidly. > **_NOTE:_** This project is a fork of [`manim-presentation`](https://github.com/galatolofederico/manim-presentation). Since the project seemed to be inactive, I decided to create my own fork to deploy new features more rapidly.
@ -124,7 +124,6 @@ Here are a few things that I implemented (or that I'm planning to implement) on
- [x] Config file path can be manually set - [x] Config file path can be manually set
- [ ] Play animation in reverse [#9](https://github.com/galatolofederico/manim-presentation/issues/9) - [ ] Play animation in reverse [#9](https://github.com/galatolofederico/manim-presentation/issues/9)
- [x] Handle 3D scenes out of the box - [x] Handle 3D scenes out of the box
- [ ] Can work with both community and 3b1b versions (not tested)
- [ ] Generate docs online - [ ] Generate docs online
- [ ] Fix the quality problem on Windows platforms with `fullscreen` flag - [ ] Fix the quality problem on Windows platforms with `fullscreen` flag

View File

@ -16,6 +16,7 @@ import numpy as np
from .commons import config_path_option from .commons import config_path_option
from .config import Config from .config import Config
from .defaults import CONFIG_PATH, FOLDER_PATH from .defaults import CONFIG_PATH, FOLDER_PATH
from .slide import reverse_video_path
@unique @unique
@ -41,7 +42,7 @@ class Presentation:
def __init__(self, config, last_frame_next: bool = False): def __init__(self, config, last_frame_next: bool = False):
self.last_frame_next = last_frame_next self.last_frame_next = last_frame_next
self.slides = config["slides"] self.slides = config["slides"]
self.files = config["files"] self.files = [reverse_video_path(path) for path in config["files"]]
self.lastframe = [] self.lastframe = []
@ -79,6 +80,9 @@ class Presentation:
self.current_slide_i = max(0, self.current_slide_i - 1) self.current_slide_i = max(0, self.current_slide_i - 1)
self.rewind_slide() self.rewind_slide()
def reserve_slide(self):
pass
def rewind_slide(self): def rewind_slide(self):
self.current_animation = self.current_slide["start_animation"] self.current_animation = self.current_slide["start_animation"]
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0) self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
@ -292,8 +296,28 @@ class Display:
sys.exit() sys.exit()
"""
@click.command() @click.command()
@click.argument("scenes", nargs=-1) @click.option(
"--folder",
default=FOLDER_PATH,
type=click.Path(exists=True, file_okay=False),
help="Set slides folder.",
)
"""
@click.help_option("-h", "--help")
def list_scenes(folder):
scenes = []
for file in os.listdir(folder):
if file.endswith(".json"):
scenes.append(os.path.basename(file)[:-4])
return scenes
@click.command()
@click.option("--scenes", nargs=-1, prompt=True)
@config_path_option @config_path_option
@click.option( @click.option(
"--folder", "--folder",
@ -312,6 +336,20 @@ class Display:
def present(scenes, config_path, folder, start_paused, fullscreen, last_frame_next): def present(scenes, config_path, folder, start_paused, fullscreen, last_frame_next):
"""Present the different scenes.""" """Present the different scenes."""
if len(scenes) == 0:
print("ICI")
scene_choices = list_scenes(folder)
scene_choices = dict(enumerate(scene_choices, start=1))
choices = [str(i) for i in scene_choices.keys()]
def value_proc(value: str):
raise ValueError("Value:")
print(scene_choices)
scenes = click.prompt("Choose a scene", value_proc=value_proc)
presentations = list() presentations = list()
for scene in scenes: for scene in scenes:
config_file = os.path.join(folder, f"{scene}.json") config_file = os.path.join(folder, f"{scene}.json")

View File

@ -1,12 +1,31 @@
import json import json
import os import os
import platform
import shutil import shutil
import subprocess
from manim import Scene, ThreeDScene, config from manim import Scene, ThreeDScene, config, logger
from tqdm import tqdm
try: # For manim<v0.16.0.post0
from manim.constants import FFMPEG_BIN as ffmpeg_executable
except ImportError:
ffmpeg_executable = config.ffmpeg_executable
from .defaults import FOLDER_PATH from .defaults import FOLDER_PATH
def reverse_video_path(src: str) -> str:
file, ext = os.path.splitext(src)
return f"{file}_reversed{ext}"
def reverse_video_file(src: str, dst: str):
command = [config.ffmpeg_executable, "-i", src, "-vf", "reverse", dst]
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
process.communicate()
class Slide(Scene): class Slide(Scene):
def __init__(self, *args, output_folder=FOLDER_PATH, **kwargs): def __init__(self, *args, output_folder=FOLDER_PATH, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -79,14 +98,31 @@ class Slide(Scene):
os.mkdir(scene_files_folder) os.mkdir(scene_files_folder)
files = list() files = list()
for src_file in self.renderer.file_writer.partial_movie_files: for src_file in tqdm(
self.renderer.file_writer.partial_movie_files,
desc=f"Copying animation files to '{scene_files_folder}' and generating reversed animations",
leave=config["progress_bar"] == "leave",
ascii=True if platform.system() == "Windows" else None,
disable=config["progress_bar"] == "none",
):
dst_file = os.path.join(scene_files_folder, os.path.basename(src_file)) dst_file = os.path.join(scene_files_folder, os.path.basename(src_file))
shutil.copyfile(src_file, dst_file) shutil.copyfile(src_file, dst_file)
rev_file = reverse_video_path(dst_file)
reverse_video_file(src_file, rev_file)
files.append(dst_file) files.append(dst_file)
f = open(os.path.join(self.output_folder, "%s.json" % (scene_name,)), "w") logger.info(
f"Copied {len(files)} animations to '{os.path.abspath(scene_files_folder)}' and generated reversed animations"
)
slide_path = os.path.join(self.output_folder, "%s.json" % (scene_name,))
f = open(slide_path, "w")
json.dump(dict(slides=self.slides, files=files), f) json.dump(dict(slides=self.slides, files=files), f)
f.close() f.close()
logger.info(
f"Slide '{scene_name}' configuration written in '{os.path.abspath(slide_path)}'"
)
class ThreeDSlide(Slide, ThreeDScene): class ThreeDSlide(Slide, ThreeDScene):

View File

@ -2,9 +2,7 @@ import sys
import setuptools import setuptools
sys.path.append("manim_slides") # To avoid importing manim, which may not be installed from .__version__ import __version__ as version
from __version__ import __version__ as version
if sys.version_info < (3, 7): if sys.version_info < (3, 7):
raise RuntimeError("This package requires Python 3.7+") raise RuntimeError("This package requires Python 3.7+")
@ -31,6 +29,7 @@ setuptools.setup(
install_requires=[ install_requires=[
"click>=8.0", "click>=8.0",
"click-default-group>=1.2", "click-default-group>=1.2",
"manim",
"numpy>=1.19.3", "numpy>=1.19.3",
"pydantic>=1.9.1", "pydantic>=1.9.1",
"opencv-python>=4.6", "opencv-python>=4.6",