From 638616c94f5bf3f44f8c5ffd855fa4a1ee2a9913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= Date: Mon, 21 Aug 2023 16:50:03 +0200 Subject: [PATCH] feat(cli): rewrite presentation player (#243) * wip: rewrite player * wip(cli): new player * wip(player): allow to close * Auto stash before merge of "rewrite-player" and "origin/rewrite-player" * feat(cli): new player * chore(docs): document changes * feat(cli): add info window --- .github/workflows/tests.yml | 2 +- .pre-commit-config.yaml | 1 + CHANGELOG.md | 30 +- manim_slides/config.py | 95 +- manim_slides/convert.py | 2 +- manim_slides/present.py | 1084 ----------------- manim_slides/present/__init__.py | 303 +++++ manim_slides/present/player.py | 346 ++++++ manim_slides/slide.py | 11 +- manim_slides/utils.py | 2 +- manim_slides/wizard.py | 2 +- static/wizard_dark.png | Bin 26652 -> 28142 bytes static/wizard_light.png | Bin 26679 -> 26914 bytes tests/data/slides/BasicSlide.json | 16 +- ...83183d1ccdbc485687cee79c6ffbd4f02698da.mp4 | Bin 19494 -> 19494 bytes ...dbc485687cee79c6ffbd4f02698da_reversed.mp4 | Bin 13118 -> 13118 bytes ...bb925b8927e2b2589d31a7a578079b70076ef4.mp4 | Bin 15608 -> 15608 bytes ...7e2b2589d31a7a578079b70076ef4_reversed.mp4 | Bin 15584 -> 15584 bytes ...3b2bd6e79d093356d379d518cc929812194bd9.mp4 | Bin 16193 -> 16193 bytes ...d093356d379d518cc929812194bd9_reversed.mp4 | Bin 17174 -> 17174 bytes ...a51d66c92d0f79ac48647526817c377f843810.mp4 | Bin 8627 -> 8627 bytes ...d0f79ac48647526817c377f843810_reversed.mp4 | Bin 8547 -> 8547 bytes 22 files changed, 753 insertions(+), 1141 deletions(-) delete mode 100644 manim_slides/present.py create mode 100644 manim_slides/present/__init__.py create mode 100644 manim_slides/present/player.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5fbef8f..1eedd47 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,7 +46,7 @@ jobs: poetry install --with test - name: Run pytest - run: poetry run pytest -x + run: poetry run pytest -x -n auto build-examples: strategy: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 665dc35..32d54bf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,7 @@ repos: rev: v0.0.284 hooks: - id: ruff + args: [--fix] - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.5.0 hooks: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fd967e..25a8bce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,14 +13,42 @@ pull requests. In an effort to better document changes, this CHANGELOG document is now created. -### Chore +### Added + +- Added the following option aliases to `manim-slides present`: + `-F` and `--full-screen` for `fullscreen`, + `-H` for `--hide-mouse`, + and `-S` for `--screen-number`. + [#243](https://github.com/jeertmans/manim-slides/pull/243) +- Added a full screen key binding (defaults to F) in the + presenter. + [#243](https://github.com/jeertmans/manim-slides/pull/243) + +### Changed - Automatically concatenate all animations from a slide into one. This is a **breaking change** because the config file format is different from the previous one. For migration help, see associated PR. [#242](https://github.com/jeertmans/manim-slides/pull/242) +- Changed the player interface to only use PySide6, and not a combination of + PySide6 and OpenCV. A few features have been removed (see removed section), + but the new player should be much easier to maintain and more performant, + than its predecessor. + [#243](https://github.com/jeertmans/manim-slides/pull/243) +- Changed the slide config format to exclude unecessary information. + `StypeType` is removed in favor to one boolean `loop` field. This is + a **breaking change** and one should re-render the slides to apply changes. + [#243](https://github.com/jeertmans/manim-slides/pull/243) +- Renamed key bindings in the config. This is a **breaking change** and one + should either manually rename them (see list below) or re-init a config. + List of changes: `CONTINUE` to `NEXT`, `BACK` to `PREVIOUS`, and + `REWIND` to `REPLAY`. + [#243](https://github.com/jeertmans/manim-slides/pull/243) ### Removed - Removed `--start-at-animation-number` option from `manim-slides present`. [#242](https://github.com/jeertmans/manim-slides/pull/242) +- Removed the following options from `manim-slides present`: + `--resolution`, `--record-to`, `--resize-mode`, and `--background-color`. + [#243](https://github.com/jeertmans/manim-slides/pull/243) diff --git a/manim_slides/config.py b/manim_slides/config.py index f69c757..25f2e78 100644 --- a/manim_slides/config.py +++ b/manim_slides/config.py @@ -1,8 +1,7 @@ import json import shutil -from enum import Enum from pathlib import Path -from typing import Any, Dict, List, Optional, Set, Tuple, Union +from typing import Any, Callable, Dict, List, Optional, Set, Tuple import rtoml from pydantic import ( @@ -10,6 +9,7 @@ from pydantic import ( Field, FilePath, PositiveInt, + PrivateAttr, field_validator, model_validator, ) @@ -18,13 +18,31 @@ from PySide6.QtCore import Qt from .logger import logger +Receiver = Callable[..., Any] -class Key(BaseModel): # type: ignore + +class Signal(BaseModel): # type: ignore[misc] + __receivers: List[Receiver] = PrivateAttr(default_factory=list) + + def connect(self, receiver: Receiver) -> None: + self.__receivers.append(receiver) + + def disconnect(self, receiver: Receiver) -> None: + self.__receivers.remove(receiver) + + def emit(self, *args: Any) -> None: + for receiver in self.__receivers: + receiver(*args) + + +class Key(BaseModel): # type: ignore[misc] """Represents a list of key codes, with optionally a name.""" ids: List[PositiveInt] = Field(unique=True) name: Optional[str] = None + __signal: Signal = PrivateAttr(default_factory=Signal) + @field_validator("ids") @classmethod def ids_is_non_empty_set(cls, ids: Set[Any]) -> Set[Any]: @@ -43,14 +61,22 @@ class Key(BaseModel): # type: ignore return m + @property + def signal(self) -> Signal: + return self.__signal -class Keys(BaseModel): # type: ignore + def connect(self, function: Receiver) -> None: + self.__signal.connect(function) + + +class Keys(BaseModel): # type: ignore[misc] QUIT: Key = Key(ids=[Qt.Key_Q], name="QUIT") - CONTINUE: Key = Key(ids=[Qt.Key_Right], name="CONTINUE / NEXT") - BACK: Key = Key(ids=[Qt.Key_Left], name="BACK") - REVERSE: Key = Key(ids=[Qt.Key_V], name="REVERSE") - REWIND: Key = Key(ids=[Qt.Key_R], name="REWIND") PLAY_PAUSE: Key = Key(ids=[Qt.Key_Space], name="PLAY / PAUSE") + NEXT: Key = Key(ids=[Qt.Key_Right], name="NEXT") + PREVIOUS: Key = Key(ids=[Qt.Key_Left], name="PREVIOUS") + REVERSE: Key = Key(ids=[Qt.Key_V], name="REVERSE") + REPLAY: Key = Key(ids=[Qt.Key_R], name="REPLAY") + FULL_SCREEN: Key = Key(ids=[Qt.Key_F], name="TOGGLE FULL SCREEN") HIDE_MOUSE: Key = Key(ids=[Qt.Key_H], name="HIDE / SHOW MOUSE") @model_validator(mode="before") @@ -74,8 +100,21 @@ class Keys(BaseModel): # type: ignore return self + def dispatch_key_function(self) -> Callable[[PositiveInt], None]: + _dispatch = {} -class Config(BaseModel): # type: ignore + for _, key in self: + for _id in key.ids: + _dispatch[_id] = key.signal + + def dispatch(key: PositiveInt) -> None: + if signal := _dispatch.get(key, None): + signal.emit() + + return dispatch + + +class Config(BaseModel): # type: ignore[misc] """General Manim Slides config""" keys: Keys = Keys() @@ -94,16 +133,10 @@ class Config(BaseModel): # type: ignore return self -class SlideType(str, Enum): - slide = "slide" - loop = "loop" - last = "last" - - class PreSlideConfig(BaseModel): # type: ignore - type: SlideType start_animation: int end_animation: int + loop: bool = False @field_validator("start_animation", "end_animation") @classmethod @@ -112,12 +145,12 @@ class PreSlideConfig(BaseModel): # type: ignore raise ValueError("Animation index (start or end) cannot be negative") return v - @model_validator(mode="before") + @model_validator(mode="after") def start_animation_is_before_end( - cls, values: Dict[str, Union[SlideType, int, bool]] - ) -> Dict[str, Union[SlideType, int, bool]]: - if values["start_animation"] >= values["end_animation"]: # type: ignore - if values["start_animation"] == values["end_animation"] == 0: + cls, pre_slide_config: "PreSlideConfig" + ) -> "PreSlideConfig": + if pre_slide_config.start_animation >= pre_slide_config.end_animation: + if pre_slide_config.start_animation == pre_slide_config.end_animation == 0: raise ValueError( "You have to play at least one animation (e.g., `self.wait()`) before pausing. If you want to start paused, use the approriate command-line option when presenting. IMPORTANT: when using ManimGL, `self.wait()` is not considered to be an animation, so prefer to directly use `self.play(...)`." ) @@ -126,36 +159,26 @@ class PreSlideConfig(BaseModel): # type: ignore "Start animation index must be strictly lower than end animation index" ) - return values + return pre_slide_config @property def slides_slice(self) -> slice: return slice(self.start_animation, self.end_animation) -class SlideConfig(BaseModel): # type: ignore - type: SlideType +class SlideConfig(BaseModel): # type: ignore[misc] file: FilePath rev_file: FilePath - terminated: bool = Field(False, exclude=True) + loop: bool = False @classmethod def from_pre_slide_config_and_files( cls, pre_slide_config: PreSlideConfig, file: Path, rev_file: Path ) -> "SlideConfig": - return cls(type=pre_slide_config.type, file=file, rev_file=rev_file) - - def is_slide(self) -> bool: - return self.type == SlideType.slide - - def is_loop(self) -> bool: - return self.type == SlideType.loop - - def is_last(self) -> bool: - return self.type == SlideType.last + return cls(file=file, rev_file=rev_file, loop=pre_slide_config.loop) -class PresentationConfig(BaseModel): # type: ignore +class PresentationConfig(BaseModel): # type: ignore[misc] slides: List[SlideConfig] = Field(min_length=1) resolution: Tuple[PositiveInt, PositiveInt] = (1920, 1080) background_color: Color = "black" diff --git a/manim_slides/convert.py b/manim_slides/convert.py index de4731b..549a085 100644 --- a/manim_slides/convert.py +++ b/manim_slides/convert.py @@ -377,7 +377,7 @@ class RevealJS(Converter): # Later, this might be useful to only mute the first video, or to make it optional. # Read more about this: # https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide#autoplay_and_autoplay_blocking - if slide_config.is_loop(): + if slide_config.loop: yield f'
' else: yield f'
' diff --git a/manim_slides/present.py b/manim_slides/present.py deleted file mode 100644 index af0dbe7..0000000 --- a/manim_slides/present.py +++ /dev/null @@ -1,1084 +0,0 @@ -import os -import platform -import signal -import sys -import time -from enum import Enum, IntFlag, auto, unique -from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple, Union - -import click -import cv2 -import numpy as np -from click import Context, Parameter -from pydantic import ValidationError -from pydantic_extra_types.color import Color -from PySide6.QtCore import Qt, QThread, Signal, Slot -from PySide6.QtGui import ( - QCloseEvent, - QIcon, - QImage, - QKeyEvent, - QPixmap, - QResizeEvent, - QScreen, -) -from PySide6.QtWidgets import QApplication, QGridLayout, QLabel, QWidget -from tqdm import tqdm - -from .commons import config_path_option, verbosity_option -from .config import DEFAULT_CONFIG, Config, PresentationConfig, SlideConfig -from .defaults import FOLDER_PATH -from .logger import logger -from .resources import * # noqa: F401, F403 - -os.environ.pop( - "QT_QPA_PLATFORM_PLUGIN_PATH", None -) # See why here: https://stackoverflow.com/a/67863156 - -WINDOW_NAME = "Manim Slides" -WINDOW_INFO_NAME = f"{WINDOW_NAME}: Info" -WINDOWS = platform.system() == "Windows" - - -class AspectRatio(Enum): - ignore = Qt.IgnoreAspectRatio - keep = Qt.KeepAspectRatio - auto = "auto" - - -ASPECT_RATIO_MODES = { - "ignore": AspectRatio.ignore, - "keep": AspectRatio.keep, - "auto": AspectRatio.auto, -} - -RESIZE_MODES = { - "fast": Qt.FastTransformation, - "smooth": Qt.SmoothTransformation, -} - - -@unique -class State(IntFlag): - """Represents all possible states of a slide presentation.""" - - """A video is actively being played.""" - PLAYING = auto() - """A video was manually paused.""" - PAUSED = auto() - """Waiting for user to press next (or else).""" - WAIT = auto() - """Presentation was terminated.""" - END = auto() - - def __str__(self) -> str: - return self.name.capitalize() # type: ignore - - -def now() -> float: - """Returns time.time() in seconds.""" - return time.time() - - -class Presentation: - """Creates presentation from a configuration object.""" - - def __init__(self, config: PresentationConfig) -> None: - self.config = config - - self.__current_slide_index: int = 0 - self.current_file: Path = self.current_slide.file - - self.loaded_slide_cap: int = -1 - self.cap = None # cap = cv2.VideoCapture - - self.reverse: bool = False - self.reversed_slide: int = -1 - - self.lastframe: Optional[np.ndarray] = None - - self.reset() - - def __len__(self) -> int: - return len(self.slides) - - @property - def slides(self) -> List[SlideConfig]: - """Returns the list of slides.""" - return self.config.slides - - @property - def resolution(self) -> Tuple[int, int]: - """Returns the resolution.""" - return self.config.resolution - - @property - def background_color(self) -> Color: - """Returns the background color.""" - return self.config.background_color - - @property - def current_slide_index(self) -> int: - return self.__current_slide_index - - @current_slide_index.setter - def current_slide_index(self, value: Optional[int]) -> None: - if value is not None: - if -len(self) <= value < len(self): - self.__current_slide_index = value - logger.debug(f"Set current slide index to {value}") - else: - logger.error( - f"Could not load slide number {value}, playing first slide instead." - ) - - @property - def current_slide(self) -> SlideConfig: - """Returns currently playing slide.""" - return self.slides[self.current_slide_index] - - @property - def first_slide(self) -> SlideConfig: - """Returns first slide.""" - return self.slides[0] - - @property - def last_slide(self) -> SlideConfig: - """Returns last slide.""" - return self.slides[-1] - - def release_cap(self) -> None: - """Releases current Video Capture, if existing.""" - if self.cap is not None: - self.cap.release() - - self.loaded_slide_cap = -1 - - def load_slide_cap(self, slide: int) -> None: - """Loads video file of given slide.""" - # We must load a new VideoCapture file if: - if (self.loaded_slide_cap != slide) or ( - self.reverse and self.reversed_slide != slide - ): # cap already loaded - logger.debug(f"Loading new cap for slide #{slide}") - - self.release_cap() - - if self.reverse: - file = self.current_slide.rev_file - self.reversed_slide = slide - else: - file = self.current_slide.file - - self.current_file = file - - self.cap = cv2.VideoCapture(file.as_posix()) - self.loaded_slide_cap = slide - - @property - def current_cap(self) -> cv2.VideoCapture: - """Returns current VideoCapture object.""" - self.load_slide_cap(self.current_slide_index) - return self.cap - - def rewind_current_slide(self) -> None: - """Rewinds current slide to first frame.""" - logger.debug("Rewinding current slide") - self.current_slide.terminated = False - self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0) - - def cancel_reverse(self) -> None: - """Cancels any effet produced by a reversed slide.""" - if self.reverse: - logger.debug("Cancelling effects from previous 'reverse' action'") - self.reverse = False - self.reversed_slide = -1 - self.release_cap() - - def reverse_current_slide(self) -> None: - """Reverses current slide.""" - self.reverse = True - self.rewind_current_slide() - - def load_next_slide(self) -> None: - """Loads next slide.""" - logger.debug("Loading next slide") - if self.reverse: - self.cancel_reverse() - self.rewind_current_slide() - elif self.current_slide.is_last(): - self.current_slide.terminated = True - else: - self.current_slide_index = min( - len(self.slides) - 1, self.current_slide_index + 1 - ) - self.rewind_current_slide() - - def load_previous_slide(self) -> None: - """Loads previous slide.""" - logger.debug(f"Loading previous slide, current is {self.current_slide_index}") - self.cancel_reverse() - self.current_slide_index = max(0, self.current_slide_index - 1) - logger.debug(f"Loading slide index {self.current_slide_index}") - self.rewind_current_slide() - - @property - def fps(self) -> int: - """Returns the number of frames per second of the current video.""" - fps = self.current_cap.get(cv2.CAP_PROP_FPS) - if fps == 0: - logger.warn( - f"Something is wrong with video file {self.current_file}, as the fps returned by frame {self.current_frame_number} is 0" - ) - # TODO: understand why we sometimes get 0 fps - return max(fps, 1) # type: ignore - - def reset(self) -> None: - """Rests current presentation.""" - self.load_slide_cap(0) - self.current_slide_index = 0 - self.slides[-1].terminated = False - - def load_last_slide(self) -> None: - """Loads last slide.""" - self.current_slide_index = len(self.slides) - 1 - self.load_slide_cap(self.current_slide_index) - self.slides[-1].terminated = False - - @property - def is_last_slide(self) -> bool: - """Returns True if current slide is the last one.""" - return self.current_slide_index == len(self.slides) - 1 - - @property - def current_frame_number(self) -> int: - """Returns current frame number.""" - return int(self.current_cap.get(cv2.CAP_PROP_POS_FRAMES)) - - def update_state(self, state: State) -> Tuple[np.ndarray, State]: - """ - Updates the current state given the previous one. - - It does this by reading the video information and checking if the state is still correct. - It returns the frame to show (lastframe) and the new state. - """ - if state ^ State.PLAYING: # If not playing, we return the same - if self.lastframe is None: - _, self.lastframe = self.current_cap.read() - return self.lastframe, state - - still_playing, frame = self.current_cap.read() - - if still_playing: - self.lastframe = frame - return self.lastframe, State.PLAYING - - # Video was terminated - if self.current_slide.is_loop(): - if self.reverse: - state = State.WAIT - - else: - state = State.PLAYING - self.rewind_current_slide() - elif self.current_slide.is_last(): - state = State.END - else: - state = State.WAIT - - return self.lastframe, state - - -class Display(QThread): # type: ignore - """Displays one or more presentations one after each other.""" - - change_video_signal = Signal(np.ndarray) - change_info_signal = Signal(dict) - change_presentation_signal = Signal() - finished = Signal() - - def __init__( - self, - presentations: List[Presentation], - config: Config = DEFAULT_CONFIG, - start_paused: bool = False, - skip_all: bool = False, - record_to: Optional[str] = None, - exit_after_last_slide: bool = False, - start_at_scene_number: Optional[int] = None, - start_at_slide_number: Optional[int] = None, - ) -> None: - super().__init__() - self.presentations = presentations - self.start_paused = start_paused - self.config = config - self.skip_all = skip_all - self.record_to = record_to - self.recordings: List[Tuple[Path, int, int]] = [] - - self.state = State.PLAYING - self.lastframe: Optional[np.ndarray] = None - - self.__current_presentation_index = 0 - self.current_presentation_index = start_at_scene_number # type: ignore - self.current_presentation.current_slide_index = start_at_slide_number # type: ignore - - self.run_flag = True - - self.key = -1 - self.exit_after_last_slide = exit_after_last_slide - - def __len__(self) -> int: - return len(self.presentations) - - @property - def current_presentation_index(self) -> int: - return self.__current_presentation_index - - @current_presentation_index.setter - def current_presentation_index(self, value: Optional[int]) -> None: - if value is not None: - if -len(self) <= value < len(self): - self.__current_presentation_index = value - self.current_presentation.release_cap() - self.change_presentation_signal.emit() - else: - logger.error( - f"Could not load scene number {value}, playing first scene instead." - ) - - @property - def current_presentation(self) -> Presentation: - """Returns the current presentation.""" - return self.presentations[self.current_presentation_index] - - @property - def current_resolution(self) -> Tuple[int, int]: - """Returns the resolution of the current presentation.""" - return self.current_presentation.resolution - - @property - def current_background_color(self) -> Color: - """Returns the background color of the current presentation.""" - return self.current_presentation.background_color - - @property - def is_last_presentation(self) -> bool: - """Returns True if current presentation is the last one.""" - return self.current_presentation_index == len(self) - 1 - - def start(self) -> None: - super().start() - self.change_presentation_signal.emit() - - def run(self) -> None: - """Runs a series of presentations until end or exit.""" - while self.run_flag: - last_time = now() - self.lastframe, self.state = self.current_presentation.update_state( - self.state - ) - if self.state & (State.PLAYING | State.PAUSED): - if self.start_paused: - self.state = State.PAUSED - self.start_paused = False - if self.state & State.END: - if self.current_presentation_index == len(self.presentations) - 1: - if self.exit_after_last_slide: - self.run_flag = False - continue - - self.handle_key() - self.show_video() - self.show_info() - - lag = now() - last_time - sleep_time = 1 / self.current_presentation.fps - - logger.log( - 5, - f"Took {lag:.3f} seconds to process the current frame, that must play at a rate of one every {sleep_time:.3f} seconds.", - ) - - if sleep_time - lag < 0: - logger.warn( - "The FPS rate could not be matched. " - "This is normal when manually transitioning between slides.\n" - "If you feel that the FPS are too low, " - "consider checking this issue:\n" - "https://github.com/jeertmans/manim-slides/issues/179." - ) - - sleep_time = max(sleep_time - lag, 0) - time.sleep(sleep_time) - last_time = now() - self.current_presentation.release_cap() - - if self.record_to is not None: - self.record_movie() - - logger.debug("Closing video thread gracefully and exiting") - self.finished.emit() - - def record_movie(self) -> None: - logger.debug( - f"A total of {len(self.recordings)} frames will be saved to {self.record_to}" - ) - file, frame_number, fps = self.recordings[0] - - cap = cv2.VideoCapture(str(file)) - cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number - 1) - _, frame = cap.read() - - w, h = frame.shape[:2] - fourcc = cv2.VideoWriter_fourcc(*"XVID") - out = cv2.VideoWriter(self.record_to, fourcc, fps, (h, w)) - - out.write(frame) - - for _file, frame_number, _ in tqdm( - self.recordings[1:], desc="Creating recording file", leave=False - ): - if file != _file: - cap.release() - file = _file - cap = cv2.VideoCapture(str(_file)) - - cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number - 1) - _, frame = cap.read() - out.write(frame) - - cap.release() - out.release() - - def show_video(self) -> None: - """Shows updated video.""" - if self.record_to is not None: - pres = self.current_presentation - self.recordings.append( - (pres.current_file, pres.current_frame_number, pres.fps) - ) - - frame: np.ndarray = self.lastframe - self.change_video_signal.emit(frame) - - def show_info(self) -> None: - """Shows updated information about presentations.""" - self.change_info_signal.emit( - { - "state": self.state, - "slide_index": self.current_presentation.current_slide_index + 1, - "n_slides": len(self.current_presentation.slides), - "type": self.current_presentation.current_slide.type, - "scene_index": self.current_presentation_index + 1, - "n_scenes": len(self.presentations), - } - ) - - @Slot(int) - def set_key(self, key: int) -> None: - """Sets the next key to be handled.""" - self.key = key - - def handle_key(self) -> None: - """Handles key strokes.""" - - key = self.key - keys = self.config.keys - - if keys.QUIT.match(key): - self.run_flag = False - elif self.state == State.PLAYING and keys.PLAY_PAUSE.match(key): - self.state = State.PAUSED - elif self.state == State.PAUSED and keys.PLAY_PAUSE.match(key): - self.state = State.PLAYING - elif self.state & (State.END | State.WAIT) and ( - keys.CONTINUE.match(key) or keys.PLAY_PAUSE.match(key) or self.skip_all - ): - if (self.state & State.END) and not self.is_last_presentation: - self.current_presentation_index += 1 - self.current_presentation.rewind_current_slide() - else: - self.current_presentation.load_next_slide() - self.state = State.PLAYING - elif ( - self.state == State.PLAYING and keys.CONTINUE.match(key) - ) or self.skip_all: - self.current_presentation.load_next_slide() - elif keys.BACK.match(key): - if self.current_presentation.current_slide_index == 0: - if self.current_presentation_index == 0: - self.current_presentation.load_previous_slide() - else: - self.current_presentation.cancel_reverse() - self.current_presentation_index -= 1 - self.current_presentation.load_last_slide() - self.state = State.PLAYING - else: - self.current_presentation.load_previous_slide() - self.state = State.PLAYING - elif keys.REVERSE.match(key): - self.current_presentation.reverse_current_slide() - self.state = State.PLAYING - elif keys.REWIND.match(key): - self.current_presentation.cancel_reverse() - self.current_presentation.rewind_current_slide() - self.state = State.PLAYING - - self.key = -1 # No more key to be handled - - def stop(self) -> None: - """Stops current thread, without doing anything after.""" - self.run_flag = False - self.wait() - - -class Info(QWidget): # type: ignore - def __init__(self) -> None: - super().__init__() - self.setWindowTitle(WINDOW_INFO_NAME) - - self.layout = QGridLayout() - - self.setLayout(self.layout) - - self.stateLabel = QLabel() - self.slideLabel = QLabel() - self.typeLabel = QLabel() - self.sceneLabel = QLabel() - - self.layout.addWidget(self.stateLabel, 1, 0) - self.layout.addWidget(self.slideLabel, 1, 1) - self.layout.addWidget(self.typeLabel, 2, 0) - self.layout.addWidget(self.sceneLabel, 2, 1) - - self.update_info({}) - - @Slot(dict) - def update_info(self, info: Dict[str, Union[str, int]]) -> None: - self.stateLabel.setText("State: {}".format(info.get("state", "unknown"))) - self.slideLabel.setText( - "Slide: {}/{}".format( - info.get("slide_index", "na"), info.get("n_slides", "na") - ) - ) - self.typeLabel.setText("Slide Type: {}".format(info.get("type", "unknown"))) - self.sceneLabel.setText( - "Scene: {}/{}".format( - info.get("scene_index", "na"), info.get("n_scenes", "na") - ) - ) - - -class InfoThread(QThread): # type: ignore - def __init__(self) -> None: - super().__init__() - self.dialog = Info() - self.run_flag = True - - def start(self) -> None: - super().start() - - self.dialog.show() - - def stop(self) -> None: - self.dialog.deleteLater() - - -class App(QWidget): # type: ignore - send_key_signal = Signal(int) - - def __init__( - self, - *args: Any, - config: Config = DEFAULT_CONFIG, - fullscreen: bool = False, - hide_mouse: bool = False, - aspect_ratio: AspectRatio = AspectRatio.auto, - resize_mode: Qt.TransformationMode = Qt.SmoothTransformation, - background_color: str = "black", - screen: Optional[QScreen] = None, - **kwargs: Any, - ): - super().__init__() - - if screen: - self.setScreen(screen) - self.move(screen.geometry().topLeft()) - - self.setWindowTitle(WINDOW_NAME) - self.icon = QIcon(":/icon.png") - self.setWindowIcon(self.icon) - - # create the video capture thread - kwargs["config"] = config - self.thread = Display(*args, **kwargs) - - self.display_width, self.display_height = self.thread.current_resolution - self.aspect_ratio = aspect_ratio - self.resize_mode = resize_mode - self.hide_mouse = hide_mouse - self.config = config - if self.hide_mouse: - self.setCursor(Qt.BlankCursor) - - self.label = QLabel(self) - - if self.aspect_ratio == AspectRatio.auto: - self.label.setScaledContents(True) - self.label.setAlignment(Qt.AlignCenter) - - self.pixmap = QPixmap(self.width(), self.height()) - self.label.setPixmap(self.pixmap) - self.label.setMinimumSize(1, 1) - - # create the info dialog - self.info = Info() - self.info.show() - - # info widget will also listen to key presses - self.info.keyPressEvent = self.keyPressEvent - - if fullscreen: - self.showFullScreen() - else: - self.resize(self.display_width, self.display_height) - - # connect signals - self.thread.change_video_signal.connect(self.update_image) - self.thread.change_info_signal.connect(self.info.update_info) - self.thread.change_presentation_signal.connect(self.update_canvas) - self.thread.finished.connect(self.closeAll) - self.send_key_signal.connect(self.thread.set_key) - - # start the thread - self.thread.start() - - def keyPressEvent(self, event: QKeyEvent) -> None: - key = event.key() - if self.config.keys.HIDE_MOUSE.match(key): - if self.hide_mouse: - self.setCursor(Qt.ArrowCursor) - self.hide_mouse = False - else: - self.setCursor(Qt.BlankCursor) - self.hide_mouse = True - # We send key to be handled by video display - self.send_key_signal.emit(key) - event.accept() - - def closeAll(self) -> None: - logger.debug("Closing all QT windows") - self.thread.stop() - self.info.deleteLater() - self.deleteLater() - - def resizeEvent(self, event: QResizeEvent) -> None: - if not self.label.hasScaledContents(): - self.pixmap = self.pixmap.scaled( - self.width(), self.height(), self.aspect_ratio.value, self.resize_mode - ) - self.label.setPixmap(self.pixmap) - self.label.resize(self.width(), self.height()) - - def closeEvent(self, event: QCloseEvent) -> None: - self.closeAll() - event.accept() - - @Slot(np.ndarray) - def update_image(self, cv_img: np.ndarray) -> None: - """Updates the (image) label with a new opencv image""" - h, w, ch = cv_img.shape - bytes_per_line = ch * w - qt_img = QImage(cv_img.data, w, h, bytes_per_line, QImage.Format_BGR888) - - if not self.label.hasScaledContents() and ( - w != self.width() or h != self.height() - ): - qt_img = qt_img.scaled( - self.width(), self.height(), self.aspect_ratio.value, self.resize_mode - ) - - self.label.setPixmap(QPixmap.fromImage(qt_img)) - - @Slot() - def update_canvas(self) -> None: - """Update the canvas when a presentation has changed.""" - logger.debug("Updating canvas") - w, h = self.thread.current_resolution - - if not self.isFullScreen() and ( - self.display_width != w or self.display_height != h - ): - self.display_width, self.display_height = w, h - self.resize(self.display_width, self.display_height) - self.label.setStyleSheet( - f"background-color: {self.thread.current_background_color}" - ) - - -@click.command() -@click.option( - "--folder", - metavar="DIRECTORY", - default=FOLDER_PATH, - type=click.Path(exists=True, file_okay=False, path_type=Path), - help="Set slides folder.", - show_default=True, -) -@click.help_option("-h", "--help") -@verbosity_option -def list_scenes(folder: Path) -> None: - """List available scenes.""" - - for i, scene in enumerate(_list_scenes(folder), start=1): - click.secho(f"{i}: {scene}", fg="green") - - -def _list_scenes(folder: Path) -> List[str]: - """Lists available scenes in given directory.""" - scenes = [] - - for filepath in folder.glob("*.json"): - try: - _ = PresentationConfig.from_file(filepath) - scenes.append(filepath.stem) - except ( - Exception - ) as e: # Could not parse this file as a proper presentation config - logger.warn( - f"Something went wrong with parsing presentation config `{filepath}`: {e}" - ) - pass - - logger.debug(f"Found {len(scenes)} valid scene configuration files in `{folder}`.") - - return scenes - - -def prompt_for_scenes(folder: Path) -> List[str]: - """Prompts the user to select scenes within a given folder.""" - - scene_choices = dict(enumerate(_list_scenes(folder), start=1)) - - for i, scene in scene_choices.items(): - click.secho(f"{i}: {scene}", fg="green") - - click.echo() - - click.echo("Choose number corresponding to desired scene/arguments.") - click.echo("(Use comma separated list for multiple entries)") - - def value_proc(value: Optional[str]) -> List[str]: - indices = list(map(int, (value or "").strip().replace(" ", "").split(","))) - - if not all(0 < i <= len(scene_choices) for i in indices): - raise click.UsageError("Please only enter numbers displayed on the screen.") - - return [scene_choices[i] for i in indices] - - if len(scene_choices) == 0: - raise click.UsageError( - "No scenes were found, are you in the correct directory?" - ) - - while True: - try: - scenes = click.prompt("Choice(s)", value_proc=value_proc) - return scenes # type: ignore - except ValueError as e: - raise click.UsageError(str(e)) - - -def get_scenes_presentation_config( - scenes: List[str], folder: Path -) -> List[PresentationConfig]: - """Returns a list of presentation configurations based on the user input.""" - - if len(scenes) == 0: - scenes = prompt_for_scenes(folder) - - presentation_configs = [] - for scene in scenes: - config_file = folder / f"{scene}.json" - if not config_file.exists(): - raise click.UsageError( - f"File {config_file} does not exist, check the scene name and make sure to use Slide as your scene base class" - ) - try: - presentation_configs.append(PresentationConfig.from_file(config_file)) - except ValidationError as e: - raise click.UsageError(str(e)) - - return presentation_configs - - -def start_at_callback( - ctx: Context, param: Parameter, values: str -) -> Tuple[Optional[int], ...]: - if values == "(None, None)": - return (None, None) - - def str_to_int_or_none(value: str) -> Optional[int]: - if value.lower().strip() == "": - return None - else: - try: - return int(value) - except ValueError: - raise click.BadParameter( - f"start index can only be an integer or an empty string, not `{value}`", - ctx=ctx, - param=param, - ) - - values_tuple = values.split(",") - n_values = len(values_tuple) - if n_values == 2: - return tuple(map(str_to_int_or_none, values_tuple)) - - raise click.BadParameter( - f"exactly 2 arguments are expected but you gave {n_values}, please use commas to separate them", - ctx=ctx, - param=param, - ) - - -@click.command() -@click.argument("scenes", nargs=-1) -@config_path_option -@click.option( - "--folder", - metavar="DIRECTORY", - default=FOLDER_PATH, - type=click.Path(exists=True, file_okay=False, path_type=Path), - help="Set slides folder.", - show_default=True, -) -@click.option("--start-paused", is_flag=True, help="Start paused.") -@click.option("--fullscreen", is_flag=True, help="Fullscreen mode.") -@click.option( - "-s", - "--skip-all", - is_flag=True, - help="Skip all slides, useful the test if slides are working. Automatically sets `--exit-after-last-slide` to True.", -) -@click.option( - "-r", - "--resolution", - metavar="", - type=(int, int), - default=None, - help="Window resolution WIDTH HEIGHT used if fullscreen is not set. You may manually resize the window afterward.", -) -@click.option( - "--to", - "--record-to", - "record_to", - metavar="FILE", - type=click.Path(dir_okay=False, path_type=Path), - default=None, - help="If set, the presentation will be recorded into a AVI video file with given name.", -) -@click.option( - "--exit-after-last-slide", - is_flag=True, - help="At the end of last slide, the application will be exited.", -) -@click.option( - "--hide-mouse", - is_flag=True, - help="Hide mouse cursor.", -) -@click.option( - "--aspect-ratio", - type=click.Choice(ASPECT_RATIO_MODES.keys(), case_sensitive=False), - default="auto", - help="Set the aspect ratio mode to be used when rescaling video. `'auto'` option is equivalent to `'ignore'`, but can be much faster due to not calling `scaled()` method on every frame.", - show_default=True, -) -@click.option( - "--resize-mode", - type=click.Choice(RESIZE_MODES.keys(), case_sensitive=False), - default="smooth", - help="Set the resize (i.e., transformation) mode to be used when rescaling video.", - show_default=True, -) -@click.option( - "--background-color", - "--bgcolor", - "background_color", - metavar="COLOR", - type=str, - default=None, - help='Set the background color for borders when using "keep" resize mode. Can be any valid CSS color, e.g., "green", "#FF6500" or "rgba(255, 255, 0, .5)". If not set, it defaults to the background color configured in the Manim scene.', - show_default=True, -) -@click.option( - "--sa", - "--start-at", - "start_at", - metavar="", - type=str, - callback=start_at_callback, - default=(None, None), - help="Start presenting at (x, y), equivalent to --sacn x --sasn y, and overrides values if not None.", -) -@click.option( - "--sacn", - "--start-at-scene-number", - "start_at_scene_number", - metavar="INDEX", - type=int, - default=None, - help="Start presenting at a given scene number (0 is first, -1 is last).", -) -@click.option( - "--sasn", - "--start-at-slide-number", - "start_at_slide_number", - metavar="INDEX", - type=int, - default=None, - help="Start presenting at a given slide number (0 is first, -1 is last).", -) -@click.option( - "--screen", - "screen_number", - metavar="NUMBER", - type=int, - default=None, - help="Presents content on the given screen (a.k.a. display).", -) -@click.help_option("-h", "--help") -@verbosity_option -def present( - scenes: List[str], - config_path: Path, - folder: Path, - start_paused: bool, - fullscreen: bool, - skip_all: bool, - resolution: Optional[Tuple[int, int]], - record_to: Optional[Path], - exit_after_last_slide: bool, - hide_mouse: bool, - aspect_ratio: str, - resize_mode: str, - background_color: Optional[str], - start_at: Tuple[Optional[int], Optional[int], Optional[int]], - start_at_scene_number: Optional[int], - start_at_slide_number: Optional[int], - screen_number: Optional[int] = None, -) -> None: - """ - Present SCENE(s), one at a time, in order. - - Each SCENE parameter must be the name of a Manim scene, with existing SCENE.json config file. - - You can present the same SCENE multiple times by repeating the parameter. - - Use `manim-slide list-scenes` to list all available scenes in a given folder. - """ - - if skip_all: - exit_after_last_slide = True - - presentation_configs = get_scenes_presentation_config(scenes, folder) - - if resolution is not None: - for presentation_config in presentation_configs: - presentation_config.resolution = resolution - - if background_color is not None: - for presentation_config in presentation_configs: - presentation_config.background_color = background_color - - presentations = [ - Presentation(presentation_config) - for presentation_config in presentation_configs - ] - - # TODO: remove me in v5 - if config_path.suffix == ".json" or Path(".manim-slides.json").exists(): - logger.warn( - "Manim Slides now uses a TOML file for configuration. " - "Please create a new configuration file with `manim-slides init` " - "and move all the keys from your old config, if needed. " - "Then, delete your old JSON config file." - ) - - if config_path.exists(): - try: - config = Config.from_file(config_path) - except ValidationError as e: - raise click.UsageError(str(e)) - else: - logger.debug("No configuration file found, using default configuration.") - config = Config() - - if record_to is not None: - ext = record_to.suffix - if ext.lower() != ".avi": - raise click.UsageError( - "Recording only support '.avi' extension. " - "For other video formats, " - "please convert the resulting '.avi' file afterwards." - ) - - if start_at[0]: - start_at_scene_number = start_at[0] - - if start_at[1]: - start_at_slide_number = start_at[1] - - if not QApplication.instance(): - app = QApplication(sys.argv) - else: - app = QApplication.instance() - - app.setApplicationName("Manim Slides") - - if screen_number is not None: - try: - screen = app.screens()[screen_number] - except IndexError: - logger.error( - f"Invalid screen number {screen_number}, " - f"allowed values are from 0 to {len(app.screens())-1} (incl.)" - ) - screen = None - else: - screen = None - - a = App( - presentations, - config=config, - start_paused=start_paused, - fullscreen=fullscreen, - skip_all=skip_all, - record_to=record_to, - exit_after_last_slide=exit_after_last_slide, - hide_mouse=hide_mouse, - aspect_ratio=ASPECT_RATIO_MODES[aspect_ratio], - resize_mode=RESIZE_MODES[resize_mode], - start_at_scene_number=start_at_scene_number, - start_at_slide_number=start_at_slide_number, - screen=screen, - ) - - a.show() - - # inform about CTRL+C - def sigkill_handler(signum, frame): # type: ignore - logger.warn( - "Thie application cannot be closed with usual CTRL+C, " - "please use the appropriate key defined in your config " - "(default: q)." - ) - - raise KeyboardInterrupt - - signal.signal(signal.SIGINT, sigkill_handler) - sys.exit(app.exec_()) diff --git a/manim_slides/present/__init__.py b/manim_slides/present/__init__.py new file mode 100644 index 0000000..ea722b6 --- /dev/null +++ b/manim_slides/present/__init__.py @@ -0,0 +1,303 @@ +import signal +import sys +from pathlib import Path +from typing import List, Optional, Tuple + +import click +from click import Context, Parameter +from pydantic import ValidationError +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QApplication + +from ..commons import config_path_option, folder_path_option, verbosity_option +from ..config import Config, PresentationConfig +from ..logger import logger +from .player import Player + +ASPECT_RATIO_MODES = { + "keep": Qt.KeepAspectRatio, + "ignore": Qt.IgnoreAspectRatio, +} + + +@click.command() +@folder_path_option +@click.help_option("-h", "--help") +@verbosity_option +def list_scenes(folder: Path) -> None: + """List available scenes.""" + + for i, scene in enumerate(_list_scenes(folder), start=1): + click.secho(f"{i}: {scene}", fg="green") + + +def _list_scenes(folder: Path) -> List[str]: + """Lists available scenes in given directory.""" + scenes = [] + + for filepath in folder.glob("*.json"): + try: + _ = PresentationConfig.from_file(filepath) + scenes.append(filepath.stem) + except ( + Exception + ) as e: # Could not parse this file as a proper presentation config + logger.warn( + f"Something went wrong with parsing presentation config `{filepath}`: {e}" + ) + + logger.debug(f"Found {len(scenes)} valid scene configuration files in `{folder}`.") + + return scenes + + +def prompt_for_scenes(folder: Path) -> List[str]: + """Prompts the user to select scenes within a given folder.""" + + scene_choices = dict(enumerate(_list_scenes(folder), start=1)) + + for i, scene in scene_choices.items(): + click.secho(f"{i}: {scene}", fg="green") + + click.echo() + + click.echo("Choose number corresponding to desired scene/arguments.") + click.echo("(Use comma separated list for multiple entries)") + + def value_proc(value: Optional[str]) -> List[str]: + indices = list(map(int, (value or "").strip().replace(" ", "").split(","))) + + if not all(0 < i <= len(scene_choices) for i in indices): + raise click.UsageError("Please only enter numbers displayed on the screen.") + + return [scene_choices[i] for i in indices] + + if len(scene_choices) == 0: + raise click.UsageError( + "No scenes were found, are you in the correct directory?" + ) + + while True: + try: + scenes = click.prompt("Choice(s)", value_proc=value_proc) + return scenes # type: ignore + except ValueError as e: + raise click.UsageError(str(e)) + + +def get_scenes_presentation_config( + scenes: List[str], folder: Path +) -> List[PresentationConfig]: + """Returns a list of presentation configurations based on the user input.""" + + if len(scenes) == 0: + scenes = prompt_for_scenes(folder) + + presentation_configs = [] + for scene in scenes: + config_file = folder / f"{scene}.json" + if not config_file.exists(): + raise click.UsageError( + f"File {config_file} does not exist, check the scene name and make sure to use Slide as your scene base class" + ) + try: + presentation_configs.append(PresentationConfig.from_file(config_file)) + except ValidationError as e: + raise click.UsageError(str(e)) + + return presentation_configs + + +def start_at_callback( + ctx: Context, param: Parameter, values: str +) -> Tuple[Optional[int], ...]: + if values == "(None, None)": + return (None, None) + + def str_to_int_or_none(value: str) -> Optional[int]: + if value.lower().strip() == "": + return None + else: + try: + return int(value) + except ValueError: + raise click.BadParameter( + f"start index can only be an integer or an empty string, not `{value}`", + ctx=ctx, + param=param, + ) + + values_tuple = values.split(",") + n_values = len(values_tuple) + if n_values == 2: + return tuple(map(str_to_int_or_none, values_tuple)) + + raise click.BadParameter( + f"exactly 2 arguments are expected but you gave {n_values}, please use commas to separate them", + ctx=ctx, + param=param, + ) + + +@click.command() +@click.argument("scenes", nargs=-1) +@config_path_option +@folder_path_option +@click.option("--start-paused", is_flag=True, help="Start paused.") +@click.option( + "-F", + "--full-screen", + "--fullscreen", + "full_screen", + is_flag=True, + help="Toggle full screen mode.", +) +@click.option( + "-s", + "--skip-all", + is_flag=True, + help="Skip all slides, useful the test if slides are working. " + "Automatically sets `--exit-after-last-slide` to True.", +) +@click.option( + "--exit-after-last-slide", + is_flag=True, + help="At the end of last slide, the application will be exited.", +) +@click.option( + "-H", + "--hide-mouse", + is_flag=True, + help="Hide mouse cursor.", +) +@click.option( + "--aspect-ratio", + type=click.Choice(["keep", "ignore"], case_sensitive=False), + default="keep", + help="Set the aspect ratio mode to be used when rescaling the video.", + show_default=True, +) +@click.option( + "--sa", + "--start-at", + "start_at", + metavar="", + type=str, + callback=start_at_callback, + default=(None, None), + help="Start presenting at (x, y), equivalent to --sacn x --sasn y, " + "and overrides values if not None.", +) +@click.option( + "--sacn", + "--start-at-scene-number", + "start_at_scene_number", + metavar="INDEX", + type=int, + default=0, + help="Start presenting at a given scene number (0 is first, -1 is last).", +) +@click.option( + "--sasn", + "--start-at-slide-number", + "start_at_slide_number", + metavar="INDEX", + type=int, + default=0, + help="Start presenting at a given slide number (0 is first, -1 is last).", +) +@click.option( + "-S", + "--screen", + "screen_number", + metavar="NUMBER", + type=int, + default=None, + help="Presents content on the given screen (a.k.a. display).", +) +@click.help_option("-h", "--help") +@verbosity_option +def present( + scenes: List[str], + config_path: Path, + folder: Path, + start_paused: bool, + full_screen: bool, + skip_all: bool, + exit_after_last_slide: bool, + hide_mouse: bool, + aspect_ratio: str, + start_at: Tuple[Optional[int], Optional[int], Optional[int]], + start_at_scene_number: int, + start_at_slide_number: int, + screen_number: Optional[int] = None, +) -> None: + """ + Present SCENE(s), one at a time, in order. + + Each SCENE parameter must be the name of a Manim scene, + with existing SCENE.json config file. + + You can present the same SCENE multiple times by repeating the parameter. + + Use `manim-slide list-scenes` to list all available + scenes in a given folder. + """ + + if skip_all: + exit_after_last_slide = True + + presentation_configs = get_scenes_presentation_config(scenes, folder) + + if config_path.exists(): + try: + config = Config.from_file(config_path) + except ValidationError as e: + raise click.UsageError(str(e)) + else: + logger.debug("No configuration file found, using default configuration.") + config = Config() + + if start_at[0]: + start_at_scene_number = start_at[0] + + if start_at[1]: + start_at_scene_number = start_at[1] + + if maybe_app := QApplication.instance(): + app = maybe_app + else: + app = QApplication(sys.argv) + + app.setApplicationName("Manim Slides") + + if screen_number is not None: + try: + screen = app.screens()[screen_number] + except IndexError: + logger.error( + f"Invalid screen number {screen_number}, " + f"allowed values are from 0 to {len(app.screens())-1} (incl.)" + ) + screen = None + else: + screen = None + + player = Player( + config, + presentation_configs, + start_paused=start_paused, + full_screen=full_screen, + skip_all=skip_all, + exit_after_last_slide=exit_after_last_slide, + hide_mouse=hide_mouse, + aspect_ratio_mode=ASPECT_RATIO_MODES[aspect_ratio], + presentation_index=start_at_scene_number, + slide_index=start_at_slide_number, + screen=screen, + ) + + player.show() + + signal.signal(signal.SIGINT, signal.SIG_DFL) + sys.exit(app.exec_()) diff --git a/manim_slides/present/player.py b/manim_slides/present/player.py new file mode 100644 index 0000000..6f351c1 --- /dev/null +++ b/manim_slides/present/player.py @@ -0,0 +1,346 @@ +from pathlib import Path +from typing import Any, List, Optional + +from PySide6.QtCore import Qt, QUrl, Signal, Slot +from PySide6.QtGui import QCloseEvent, QIcon, QKeyEvent, QScreen +from PySide6.QtMultimedia import QMediaPlayer +from PySide6.QtMultimediaWidgets import QVideoWidget +from PySide6.QtWidgets import QDialog, QGridLayout, QLabel, QMainWindow + +from ..config import Config, PresentationConfig, SlideConfig +from ..logger import logger +from ..resources import * # noqa: F401, F403 + +WINDOW_NAME = "Manim Slides" + + +class Info(QDialog): # type: ignore[misc] + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + layout = QGridLayout() + self.scene_label = QLabel() + self.slide_label = QLabel() + + layout.addWidget(QLabel("Scene:"), 1, 1) + layout.addWidget(QLabel("Slide:"), 2, 1) + layout.addWidget(self.scene_label, 1, 2) + layout.addWidget(self.slide_label, 2, 2) + self.setLayout(layout) + self.setFixedWidth(150) + self.setFixedHeight(80) + + if parent := self.parent(): + self.closeEvent = parent.closeEvent + self.keyPressEvent = parent.keyPressEvent + + +class Player(QMainWindow): # type: ignore[misc] + presentation_changed: Signal = Signal() + slide_changed: Signal = Signal() + + def __init__( + self, + config: Config, + presentation_configs: List[PresentationConfig], + *, + start_paused: bool = False, + full_screen: bool = False, + skip_all: bool = False, + exit_after_last_slide: bool = False, + hide_mouse: bool = False, + aspect_ratio_mode: Qt.AspectRatioMode = Qt.KeepAspectRatio, + presentation_index: int = 0, + slide_index: int = 0, + screen: Optional[QScreen] = None, + ): + super().__init__() + + # Wizard's config + + self.config = config + + # Presentation configs + + self.presentation_configs = presentation_configs + self.__current_presentation_index = 0 + self.__current_slide_index = 0 + self.__current_file: Path = self.current_slide_config.file + + self.current_presentation_index = presentation_index + self.current_slide_index = slide_index + + self.__playing_reversed_slide = False + + # Widgets + + if screen: + self.setScreen(screen) + self.move(screen.geometry().topLeft()) + + if full_screen: + self.setWindowState(Qt.WindowFullScreen) + else: + w, h = self.current_presentation_config.resolution + geometry = self.geometry() + geometry.setWidth(w) + geometry.setHeight(h) + self.setGeometry(geometry) + + if hide_mouse: + self.setCursor(Qt.BlankCursor) + + self.setWindowTitle(WINDOW_NAME) + self.icon = QIcon(":/icon.png") + self.setWindowIcon(self.icon) + + self.video_widget = QVideoWidget() + self.video_widget.setAspectRatioMode(aspect_ratio_mode) + self.setCentralWidget(self.video_widget) + + self.media_player = QMediaPlayer(self) + self.media_player.setVideoOutput(self.video_widget) + + self.presentation_changed.connect(self.presentation_changed_callback) + self.slide_changed.connect(self.slide_changed_callback) + + self.info = Info(parent=self) + + # Connecting key callbacks + + self.config.keys.QUIT.connect(self.quit) + self.config.keys.PLAY_PAUSE.connect(self.play_pause) + self.config.keys.NEXT.connect(self.next) + self.config.keys.PREVIOUS.connect(self.previous) + self.config.keys.REVERSE.connect(self.reverse) + self.config.keys.REPLAY.connect(self.replay) + self.config.keys.FULL_SCREEN.connect(self.full_screen) + self.config.keys.HIDE_MOUSE.connect(self.hide_mouse) + + self.dispatch = self.config.keys.dispatch_key_function() + + # Misc + + self.exit_after_last_slide = exit_after_last_slide + + # Setting-up everything + + if skip_all: + + def media_status_changed(status: QMediaPlayer.MediaStatus) -> None: + self.media_player.setLoops(1) # Otherwise looping slides never end + if status == QMediaPlayer.EndOfMedia: + self.load_next_slide() + + self.media_player.mediaStatusChanged.connect(media_status_changed) + + if self.current_slide_config.loop: + self.media_player.setLoops(-1) + + self.load_current_media(start_paused=start_paused) + + self.presentation_changed.emit() + self.slide_changed.emit() + + """ + Properties + """ + + @property + def presentations_count(self) -> int: + return len(self.presentation_configs) + + @property + def current_presentation_index(self) -> int: + return self.__current_presentation_index + + @current_presentation_index.setter + def current_presentation_index(self, index: int) -> None: + if 0 <= index < self.presentations_count: + self.__current_presentation_index = index + elif -self.presentations_count <= index < 0: + self.__current_presentation_index = index + self.presentations_count + else: + logger.warn(f"Could not set presentation index to {index}") + return + + self.presentation_changed.emit() + + @property + def current_presentation_config(self) -> PresentationConfig: + return self.presentation_configs[self.current_presentation_index] + + @property + def current_slides_count(self) -> int: + return len(self.current_presentation_config.slides) + + @property + def current_slide_index(self) -> int: + return self.__current_slide_index + + @current_slide_index.setter + def current_slide_index(self, index: int) -> None: + if 0 <= index < self.current_slides_count: + self.__current_slide_index = index + elif -self.current_slides_count <= index < 0: + self.__current_slide_index = index + self.current_slides_count + else: + logger.warn(f"Could not set slide index to {index}") + return + + self.slide_changed.emit() + + @property + def current_slide_config(self) -> SlideConfig: + return self.current_presentation_config.slides[self.current_slide_index] + + @property + def current_file(self) -> Path: + return self.__current_file + + @current_file.setter + def current_file(self, file: Path) -> None: + self.__current_file = file + + @property + def playing_reversed_slide(self) -> bool: + return self.__playing_reversed_slide + + @playing_reversed_slide.setter + def playing_reversed_slide(self, playing_reversed_slide: bool) -> None: + self.__playing_reversed_slide = playing_reversed_slide + + """ + Loading slides + """ + + def load_current_media(self, start_paused: bool = False) -> None: + url = QUrl.fromLocalFile(self.current_file) + self.media_player.setSource(url) + + if start_paused: + self.media_player.pause() + else: + self.media_player.play() + + def load_current_slide(self) -> None: + slide_config = self.current_slide_config + self.current_file = slide_config.file + + if slide_config.loop: + self.media_player.setLoops(-1) + else: + self.media_player.setLoops(1) + + self.load_current_media() + + def load_previous_slide(self) -> None: + self.playing_reversed_slide = False + + if self.current_slide_index > 0: + self.current_slide_index -= 1 + elif self.current_presentation_index > 0: + self.current_presentation_index -= 1 + self.current_slide_index = self.current_slides_count - 1 + else: + logger.info("No previous slide.") + return + + self.load_current_slide() + + def load_next_slide(self) -> None: + if self.playing_reversed_slide: + self.playing_reversed_slide = False + elif self.current_slide_index < self.current_slides_count - 1: + self.current_slide_index += 1 + elif self.current_presentation_index < self.presentations_count - 1: + self.current_presentation_index += 1 + self.current_slide_index = 0 + elif self.exit_after_last_slide: + self.quit() + else: + logger.info("No more slide to play.") + return + + self.load_current_slide() + + def load_reversed_slide(self) -> None: + self.playing_reversed_slide = True + self.current_file = self.current_slide_config.rev_file + self.load_current_media() + + """ + Key callbacks and slots + """ + + @Slot() + def presentation_changed_callback(self) -> None: + index = self.current_presentation_index + count = self.presentations_count + self.info.scene_label.setText(f"{index+1:4d}/{count:4 None: + index = self.current_slide_index + count = self.current_slides_count + self.info.slide_label.setText(f"{index+1:4d}/{count:4 None: + super().show() + self.info.show() + + @Slot() + def quit(self) -> None: + logger.info("Closing gracefully...") + self.info.deleteLater() + self.deleteLater() + + @Slot() + def next(self) -> None: + if self.media_player.playbackState() == QMediaPlayer.PausedState: + self.media_player.play() + else: + self.load_next_slide() + + @Slot() + def previous(self) -> None: + self.load_previous_slide() + + @Slot() + def reverse(self) -> None: + self.load_reversed_slide() + + @Slot() + def replay(self) -> None: + self.media_player.setPosition(0) + self.media_player.play() + + @Slot() + def play_pause(self) -> None: + state = self.media_player.playbackState() + if state == QMediaPlayer.PausedState: + self.media_player.play() + elif state == QMediaPlayer.PlayingState: + self.media_player.pause() + + @Slot() + def full_screen(self) -> None: + if self.windowState() == Qt.WindowFullScreen: + self.setWindowState(Qt.WindowNoState) + else: + self.setWindowState(Qt.WindowFullScreen) + + @Slot() + def hide_mouse(self) -> None: + if self.cursor().shape() == Qt.BlankCursor: + self.setCursor(Qt.ArrowCursor) + else: + self.setCursor(Qt.BlankCursor) + + def closeEvent(self, event: QCloseEvent) -> None: + self.quit() + + def keyPressEvent(self, event: QKeyEvent) -> None: + key = event.key() + self.dispatch(key) + event.accept() diff --git a/manim_slides/slide.py b/manim_slides/slide.py index 3b6f940..5faedaa 100644 --- a/manim_slides/slide.py +++ b/manim_slides/slide.py @@ -15,7 +15,7 @@ from warnings import warn import numpy as np from tqdm import tqdm -from .config import PresentationConfig, PreSlideConfig, SlideConfig, SlideType +from .config import PresentationConfig, PreSlideConfig, SlideConfig from .defaults import FOLDER_PATH from .manim import ( LEFT, @@ -354,10 +354,8 @@ class Slide(Scene): # type:ignore self.__slides.append( PreSlideConfig( - type=SlideType.slide, start_animation=self.__pause_start_animation, end_animation=self.__current_animation, - number=self.__current_slide, ) ) self.__current_slide += 1 @@ -384,15 +382,13 @@ class Slide(Scene): # type:ignore len(self.__slides) > 0 and self.__current_animation == self.__slides[-1].end_animation ): - self.__slides[-1].type = SlideType.last return self.__slides.append( PreSlideConfig( - type=SlideType.last, start_animation=self.__pause_start_animation, end_animation=self.__current_animation, - number=self.__current_slide, + loop=self.__loop_start_animation is not None, ) ) @@ -443,10 +439,9 @@ class Slide(Scene): # type:ignore ), "You have to start a loop before ending it" self.__slides.append( PreSlideConfig( - type=SlideType.loop, start_animation=self.__loop_start_animation, end_animation=self.__current_animation, - number=self.__current_slide, + loop=True, ) ) self.__current_slide += 1 diff --git a/manim_slides/utils.py b/manim_slides/utils.py index 457e6cb..56d1a2e 100644 --- a/manim_slides/utils.py +++ b/manim_slides/utils.py @@ -61,7 +61,7 @@ def merge_basenames(files: List[Path]) -> Path: # https://github.com/jeertmans/manim-slides/issues/123 basename = hashlib.sha256(basenames_str.encode()).hexdigest() - logger.info(f"Generated a new basename for basenames: {basenames} -> '{basename}'") + logger.debug(f"Generated a new basename for basenames: {basenames} -> '{basename}'") return dirname.joinpath(basename + ext) diff --git a/manim_slides/wizard.py b/manim_slides/wizard.py index 4f725f7..1cebdae 100644 --- a/manim_slides/wizard.py +++ b/manim_slides/wizard.py @@ -72,7 +72,7 @@ class Wizard(QWidget): # type: ignore # Create label for key name information label = QLabel() key_info = value["name"] or key - label.setText(key_info) + label.setText(key_info.title()) self.layout.addWidget(label, i, 0) # Create button that will pop-up a dialog and ask to input a new key diff --git a/static/wizard_dark.png b/static/wizard_dark.png index 41cb2a9a6838c0e2653ceb515565eab8299d55f9..d90e4bb8bdec447eede2f4b2394797a055c3480b 100644 GIT binary patch literal 28142 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 Ys>5!LV;~Cb#r8>DL`Jw$P%q&B09CcRjsO4v literal 26652 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 Sd+_;15HgaA5_Mw6A^!!FMZ-J* literal 26679 zcmb5W1yEdD*DVSmXwcy9?(P!Y-8DdPcX#*T+E^pO-7UDgySux?>+_xe)_qs&J*p^b zL(j?H-MiPALsmy9%1a=?;lP1`fgwmqiYkMFft!QA1!15-zp=8_$^iZN>Le_s3IqD` zhWQl^I>vSu({xs`Gj(<|bTk1ov$eA^p?5NNG%>MtGPiTSgy<3gJ&69F2Z=bE7&=?n z*%GN**qDH+n!6A&u@K1_Iufxkv9J;`G4n99^RTiJDasLvh^Yz)YKMS<5rIjG{!n$# zI$L-1M!hE-yj~<!oWyVFv8OCF@AZ<-$JWu0?C+kyZA4Fh?9aY4;pSkJ|qIU*wvI++@l7qk$tAf3M6pVYG+l?QTn zdy5?rFWU7VEE^|L?eJFXo4z>6>hJ-s|`&2FBlhC ztbg_$LC>{f5W@ZN6p-to)rK5mrr2 zNex|Y^h{BZIQZ#O5e*U`A0}ViJ28Ri<>ghW*XG(LlgdzsNGR||I{d?if;*g5yS`rV zo-a+zj57^CV8-cS%)|KO?HS_i>`ZrOXXhfU?rR-#KG6&tf|?q0x-1zP+1AO)37h#; z?tnOO`t$X&OAuN#@i|PDAP-Fb7v9thTk5?~dWBBYgzZwbVM}u}7wj+M;35Bfs^;eA zt62p>faQAYJ=PZdFssGrvhzy%y$Gwh7fUwx>FgWb^L7|v=S+*!;fjNUL(3yo2r@~I zw6rt{6Vue->guZ9>%;k=lrJi?j@=BkKdV+)9f5tBeuwAcdYfAp?KWL7a@=5le+ZAu zNxRG0QjLocJkn&rY=r_X86iokK36fq(eH1V4Bq<}7Z;=^2*knee3AL~pq{5ce*Ez9 zsGo~(Jj?rH=^3_M`2IHn(Y^xobm22HGUoL4^{x4+3Ww_&v!qM%lZ+~##KXF}T1-|^ zybm$R&0ak`JX9zF#m|GFk@UfhJnZpYEH{eu)xpT`|3pWR;FL&sJdFW(-DIcp#e6>J zf#||ZWQoIlpp%Y>i}O3OU!Ehs9||rgAiMMA=K(OBiWd^bF=dXU(QD5&d)}WAdLU1g z%=jXQ-U*xl^!ZniF)-A3Hu-)M2cJ)~#ekmR@87@IHgpdEBH`h|k&uwk)zx)ZCX}R= zeE*|fp$(fL=;9LtU1vJF|3O(%$Ahb9Da6ao%iAU}u!|wEd0go za>IutY#F28my4Ub<;H%Wq!(Vk1BE3=jQ8l?^WFGS8^*8>bXQ19OOIT?CnhAgf0uAZ zulHRd$E>7>;OWSPit?X0v39jkkf8~#Hnbc=kNs?oqUTj`pak3&6fP)!Rs;tJOKb>v z;X&3Drle;~FAH8y<`VDz?94fHG+*~q?a4WN_Wllhc`TB6%)Y~|3r2mcyxxt@y`^2YwY5q>YGQ;HtPekgNyx$&@G#a%?_E`b0oW|0|ZmLCfmpTjMed|_qGEMjv~Xj11rL+P*d#_A-W6- zHA0APYC8+p*T?GvZlA{ShFI&0XZk<8XO&|3#%sP!_oXX1+6u|B& zIRDH=J0F=__JJ8Q7aBX%5;$P*?D#^So!x!c+A`fW2)f%o=B-(Q4+M`>O=BM9A zd2aScVd``cmX@-3Jy=^VluLDZ-V?OiTyAvWaoPJ%10@6HN(;U;kbiCbhcvw*@kV&nktRtRIl;~q|KM%h-JU9VdNAiN#UyjB4f0DG zVLHoC0tp!D^!?H-UA`n(osT@lI!-ONdbo#^%`=_WESoL~`(tf12U9P2a-G|oq)M_1 zkGM%UgPj3@yX?aSpIQ+I;6*?LxD$&sFo4!wyS=UnwUmncR|0i)ry|?*HNoYq^Ds)e z5aWGhi9fHVreF(46Uy)1kRgN7X*)7~VE-(f*CkD5x~30hBF|W<{^3^m{ggx_fep=v zS~|5B;)?U!MSfGnJL8knwa}AgHwbY}KY5tz7niROj8yc`*HnYr>n&~))Ne`! z)sg9U1}_=o2b6ce-rZ~t!h8!R{&`)#kogIbvuCM5@P@q))jwplCg^3*vheQ7?|nIc zsCu(!!clX`^{&#uo*Sbyp>zT&?u#dVo!a4gp8rbW>jsJH%GI)j>UEE`D#+j4pGGZv zp$^A;4rPXzz2)5=*b&7WlC{(9bpLhJj|HLM3^9n7x6m3{@yvntET|2FN*oDq_M@Qz zE0)eTvqVt0x7@9_Nz>u(Q}^JcKT$OM4OeB=Iz?zsko&IJSAHOiAiuKJ)hMRHO#q?G)son<2Mf<`day9wRwl!EW_z z^}0j?i+ZPLziFI>PyE6Q9Qyf|BRW(te3vtq$#b$;~7mH^? z!r0R3+106F4*4V{iWg;umz%H22d;tR*`-QWtTL?ZxQvejPy4++0*?)tz;%t338B#Q zjJ$Ol`s9Ayp_KLZ*!Hg5bTVAw4!{`ibC+pw_hcLAT2NIkjJZH}gciUyu6i;W?cwty zYB#QMH@%q{hj%j+9(M(c5rAk+v5Wjm*6;0(v(xxuy6w+PvHAPKYrEodOf%tT@n`#{ z^$vo5eB~Gm$0)Y5CqnpI?k^v4yL+Rrhly;?7kO+Y`!V=es=Y74-p{;U{n>|SIi{A7 z1ucmiiCbGeN3ow`&Z^Euo39^34-A0A1Sye2{qz^LH6?dtMmhAe^K+q?y`#vuC+AyO zTk<~`IZhpe9qtK}n%V1}+BVi%R4T+UZSR8-f^MPVg0grKGfM3yj4PXOgp{t?TU#}^ zPdRa7YPwW9%vaODyA*aG0{X|+ozuwcRqf(jAg@h^WhjZVL!^8csZZ;^yHN9T9FCcMd*M)eegN5gaiV+4GcGXYNtg)(uD^2 zifkLP@LRD)`VCR{6E9Bb6Oo0Xn`GepfOuGq5xMkH9IC@9*D0qcY6+iMZ)qVFp%!El zK=AtYfqkvnNZf%+B-s)BQ$xw_!l=6^rzOur@iauqnIT!%Y|;^rCFv$tjzV_X{Vm<+ zfR0i^;n%#W{7bxwLMvg)wl(%p$$o6KFc0qgkorau;D^ozw+$%?<`G|+a^0P>JV1JD zE;8NwU@p$pu%CG;J^bU9Z(Av(M299#=ye@0j(eoiz)m@@Gags)oOrD~uQ`ci=yO=$ z+C@x8aZY~Q`cBF9yh}V?TYjz8=W?6K>FV!Re$dfr7B_B)_6%ueMcG=5E5IGy89e4_ zp_N!XF(6T6S#o z7rMhw#1d_B_edkET3GOUHv*XTqr1`>od#{>XR1=oT@0{OXtvHd; zcN+c3?=LwpU0&gYzOCYgMZW2kc6N3O-rnA=O(tl6wQ8CspYDa_U%<6K>#AJ^Rn_*l zx`X28vQ!ueL8uOefK%W_Cc*I%zj5{)zsj2NBj~Hs#?)`wGj5q7H&WF)uST9%m5P^y{pA^^ksUp;Q-2lp`bQpkv$cozLSlFvw?O|`; zSxxh~@7i)|kvcnRo2$6v+3fb6feH*g1r08%3%)*h;nfs?_@?i>zLyK(M`ev}L|t>S zK)5vV#2Nqh@r`8>u6;v=Ko!!BtY}3 z6}9!J<3Zj(k@x}gWy)OZz$&)5V2BjOg~!f3Raev4b99R`(i`nZ5WFmWvU3dpCzsf{uv>#*ez9C z!F8+j`A_Cb5-)7xmgAyFMuvT=n~Bc>Ggd--U&0db-EV`g`Bq7ilm%?LR*tKksDd@F zZ{{o&5s}8zrQa#8JTG68sLv#4_*vyU_swtKR@>)EVZzEeuic%q=Wv><*DNLk)Bl>(Z4VjQP zpD}yyX_&UJ?FB_Ok55bw9Ds=c9h0k~$-L$QyA~nc(?3t3;@NN1-ydmgEFlzA>V6E? zA;@l3Oh9@e+TS|QbOh!DTdF2gOb6-j>X#CoB04tS^>kX5w13HKzF&tG7ENUvjF2^S z+JU3Eun2p*?ibF?y>m<`+8;?yl{BFTq6Q!Hl6)nN)*XcD0C6_pYVC3y{3cfYn;tk^ zqV9=g+ma4l9;e-g?r%Uv z@&FYWIozZK`-x)E`6#Nf59ZL-*VXkdFKfMA|9XkHMKn{VivNun#mRJ~bXW1&Ja`-={#*2ax5PIkgiEaqzuKS_4ZEhA<3nvYOB+oi+?{Ipu8h z_`@~nU>xD+qaKPdOuWr9;!$-q<(=G&Fbr|6nB{*OkmZ6T4{<4>Z^Xed3I6{yAP1)p zM&^+I0_tI%^tB#Yc!>(u{J;Ole+%~C-9}*IAy80IK(rMZ8#{eb4+SzUH5C&oD8Szz ztgN09oc;z79u9*hmS@-->amVa%;mTTb2ynp$;5OO(L_&<7=QUsDGpA?;yUsdNr8A> zXXkpgl@#pyAvY3I(rGsOsy5<2p9Pu*DWNEDsV~}SU%yY0Y$p81XcST^f9zLL{?Fx- zvzo^2wzm7t z++DrPQMy;fGIo=?CBXcCf?$MvQTAJ*mOaVwo>{;IPN#h+H-E1>m#IDJMfIR$&o%KF zBmW>-HA5)WyBB)nTf4rZ7MouPO6%kfY;M2M5zsI1w`4UxzaTbsz3iS-*{JvQ<(k!} zkCj<7L9~5}PWQ>Eby>6Q^eE)9<)+^}YCmRj!Qa39<3F-L&&cX%;eFq7ir-otn11M@gaTG_uWsN3C3gC-S~J~)euV)XK%L$_{DKKYKlR(<*%nn zT*}#CkqO-fT1tw}a7WIPEi!w-U+;FORT0pIloAFn;@Qy@(G;DBU#s9N@ks#@y?_{gr`U;@!#ZN5xD%(8}v~ULO_$Q{9YmS^i7K2z1A@B2ZX*SD#;IvL5P#6ta-sO0`15`X_^l|C% zPw|jM-7`Zgm!2oZWnvG9<}nqbnPpt&a4x@3+DZ$$nE{??F%EUua>Zvbq!lAFn=@q9 zf&-M$;bKId!errO`Nmx={*RoM4|^BFQNg#QIx(+XTQh^_A)tHkST120Yl8t`d@IN6 zT0aaU3!5cvf<5tZ7~&Lz<#YA+E0RvXl}nb1E@2|O;E7EGL0Ka&iF&A*PHQe(RJ0=*cbL0%3tocMzyO`)@MB5-&+ zhRQob;Bn%z%k9aIgtEJ{^Wg@TXK$yIt&jO zQTWI>=;-W%hndsOo%xvT1G;&yn6&9@tPN)~b7!v~x?;NdJ-6k6|IfD<`CpRyvb< z)tqIAtLyEKwri6*BCh?GQgkEUXXar4Wtb+*n|8Whf;{8kA++BwvcZf2G6JX|mf>6_ zi3(QoL$L940A4$svd^3?zmz62UlLz?j~b+*mX>10i2u8G;sn35UBQrTqN-n^xX@FjkxxEO@BEz`*jE&ad@DsewU3 zx2@lVH38m23jepId{_yO#2_*}oQQ%VbZBU({UVa;Z+m+?6Bhi}uU|C_ePGSwFGWQ~ zbE)7Ea5=_KD`C3hNhP@aKKKFx0u9J{mrL6Pw19*PZI&P+i3&EzD8q|ho^ zOlGFp)j<1ZIQviz6~D(jy2P`Q)LTphd!*t^|K*d!_=ZnH&myx~x9LP*$#E5MV}B-~ z!rPDTJ9`yj4g&px(R#<{#^(~V8JXTma&bWplE6oIF#Bmgt--p!yvcmT+5OABkH}F6SzsSqrqq%^uyW^kx?8vGP8^VW*QC!}*6;fC zg`BgVxBO&3%x-Y%lfD=*OrSV*4oy)rZT}<25*zI3w8N+PER(5y9qs!4(41zJcz`UBd?+EL z>GUG6IefiJy1q(E%_dvN> zM)i3zyEkhkk8KHdp}yH_vwrmL@>sdL!10G$4HEvqOpKn?oPWXl*bzUVj9<`5T*J@V ze*!C-IVeGCEh6rCaWXZ=015b(_!4P6_!4dK4t0-5pj*g=k>^c8TBW}}*6I_>rZ>g* z-f3!#SG&=PAM)ZeQ~;2}=J)(k;X0mQqV%JqF-5~*HCSYlT=sk`s27kGSIn;EHCXLo z$<|aks8l~m>CHlfWn|~D-^YRB%j91q_~1EjJu$oMcg&lmJBiU&;3uuTp9^1f_J z{4TF>MI0faMV*Vv#d1nsM8MD!WyQf-(h2?exE%H+4+s8$V z{|2^WAh5jyfvt>`3;>D!w7J@KiAcE7dpybnk8rtm2Sqfdm6~!=zIi;=6W>iHSgA>$ z38@q5R=%~Pk#R9x8YS`gQ5?H*+&Xj-6_$W8&}t!n0jM|bKa#UqvFmT;guywyrGrz; zjJUkg*@@L*Drcgxi;4ZNF5{b}5|3|eU{<#3RGu+M@_cP(#ph%YfMkv!)ZO5YZn^O1 zcRMMktqAL`+6dxnpxR(4bIpjDDSJt`L~_^u-5LcK80h70e6pb!+Z+n`*2K({Ky(IJNTo<&lE zlS1{M0A{(R$y#(X2itcR<5R8RTanW<R#+-4Q0Idko{Y_t7ZpS3o> zAX+tp=SVGtN#^V4h_ja4Uv7%)DuDFceVDaHc#|QG!{Ktccps2^1)E6i!VENBu(|)9 zxq#o(A%=<5Vp2b|8!d9My7=s3x>9~oZ3-U*jbQkO$Otvz6DAJzOjGhE(LFyK4sqQ# zQ23Z7xz{(wP>=fv5v#Xh{5){^^e`Kc)c(Zk)pN;5wM?BI%+DFY*r_*JJ4`RN!jz(K zceZ!&?s}I*P@e!1hUjoK<=g4rm=;`@tZAYbiXFx7Mq#Uuiqi4XU4eaasDl|y*=AX;fYs7udJgdUrO!iy`p#Rh8qjL8#cRUkeihwgyy7}7vsq0YrSseq6D5=s$!|Q;6q1H*M zH4&jQtRH=?8QSgFE?3!6Ow0kf}W4H@WU&)vzU1y*W-vhMvJ3 zy`r!_a+*y29-doXx!hzwTncyN=#&$AouhSjPa662*M`EV*HhlTVLQf?L5l-W;V?At zFpn!(<9jK{h3UAnvQTJExcHmFw~?Q+_952rwti2!H-eg-Syrk=-l6E%EU|aZ6T(TG zrMW3VDJCU=zwdHd{Di$M(#i6#&c__v1=8{Rbp!C%Pgv*AHKLjFykmFwcU_+;$T+nx z+K2c;)NaWYgP%kxU^8BYmA8l_L%m}~K+5pKKH%vZfA{6#n7cDf4=G!tF-1d(&5ZdfX{f21e2_Tmyv(oWWnW`??~QJhgU#5d$+!LChQ^ZaTuuph z!W`Bd0fGMqMCaZu_>ZGXn}dcALi_$Eo(5@dintipPyDXEYZgjF<+JKpooIloU=JbO z8uV^iUQ-i99rmq=F#|XNx4iBf&wQTMYjOAnOBiI=U}0CRF}|lDzR=jg@))o4yh@W1WxlY>>~#4`vv+R66_?8RZe#h$E^O#z+EIgp zal*A@UaK$TfjX=v53J4S?wP5n2HUV2aeb)8TW7ek{gLP;Yu zygtLof#g(RF9)( z4})N>E%*X$9$ED1i{a*dW00UP3wqmUk)WgvLN|J~Yu%StolpG|Q(J>~lHCLJa>h-M zV;rM#X5O|lT3w@NUmtc%>>L0){szw{Up+MC8xpxXeIYR4d6}BD8&hXkIV1w)Au&nWd{QlK?XIH(m=%(&(eN)o_ij-*{Qq2 zKf0aBb&I}#$5u#7&b@;K&#k8G9J+mgQyGN!jVlgQkqR=V)|=AzEly94>#xyiuSkzc z!cR%1lIykz5o+D@9TZhIOJYYoT6CJMLDRQ(@mpI`!y_?RAM-4s4G1J<(&d$!3PqhQ zu?7Y*pHnS!)Bfd{+yAZb@!U0+c}F)Dqjd4J{@4Kw#@LO zittKbF|o^#f1SF_L>;rWcCdGxxh&swPv61kQSQ$kOxfo8RsYWJW!_=SvbpMVaLb9a zll_9H!e@Rn@O8xwd#HeVbM^+P?%|l_q+ZY$kJF)w%*qakrCb_Dh|r| z0OGJ12DjZ`J|=P}3v>4;MENtHDwthD(28SCljfQK)So|pp4Cr7hzvJR=gw*~ZC4+! zOij4M)J$E^mVS(;&?~7?_qOvh_|2!YdF zXk2h<{$x@8zgi@}F9mzyX+GjF2EI&D*TrE)zQVo1^GJ+kt=?dFWqg749zhLj{;V;( zCTm>gG?XWOwDwz3D()?07HV^iKT!0idGwLouY+rt>3M>3d_})@?zF86J-E zFRe(wx!1Y7qW^>bYn8&`=eH~z*3&pEkM$^TG1DpTZE;`|N9|>Wvu(n%Q|vIjHq1A1 zAPOkkfhKk`MWO&YYgM&=b2jaNDFBFD^z`<^!ot>^5X%ey5>rzH9WrCFaMdh#k^Lj~ zXfea#z2o;Un|jS7iEl<68W}Q1n;8ZpHt+F!1MiX4 zyB=x3VGBye)E=v`-7VSqIi2-@B-lA0VTP%n@4ouc&R0j%|Bc*%bqB_4ViWwG z6yHDCi4J!zg!ALi_*nztY(;Q~3qk1+>cR%|4I#aWQ6dgboQK;z&a1D0Oddxf)?x#A zNn6e{{P6jR`yp&u3YRkhIc$YK0wH8B^Z01LL-R1+17X`>_0Pqm1~yyUw8=%cRaPf0 zNDSw+$v{GVBSW3u_bUImW%h{poVU2hUC!(CqmkydO<}Waxt3qGYgZkt-p|4s`hr@C z%cmGqtbbX~`~3pG-T{@vNP+&b2nPLqoQJgzpV+oX?ofYmMqq#xph?UnMdpm z{>|_>&auL%v8)iGj@FEB8-pEpz_qN73exVTPCAb(*?MyR8%GniE!Yv&nu|cDepLWJ zoa?~Ok^~_*0BIf5dT;*3HJ3}Vxggldw&p~V@`EeIal`nB8nYEnk!Jt3NVg0*(EwZ-Y0j$S$NbbYm~lACmjx!fd@h z0L?fFJj+*_yGde1e`ieamzQb#Ja_?uDD3KPc z=~q0bilW_Y>nw$6L*8H*bT|s3^s=Y4*{cQhvQcO`?Omt1Mm}!wLH1lX;D|Ws8t-Px z$AvTHX7{wgLhTP$0Qng+KwyFpw&PFYGM%HoiCL z!-|Y)MjB!N&UA_<1I{%$w9Zs1CuznIt{+m+SQ^=iFD;G?I_Xw(;K9lP^@Dm6^vI&!gN7 zFHX3OR^#$M|L$1pUw(wcG zH#DL8Fpug)o^_Plc~^ds!6zbbG3W?Y@uXgmZ%0Qfm^HS@P;u4j6HwsjW3DE?u_|OLy-!d@ikc zCZd|hBOuw^sgn>GC^O}BEidiO9@R3M%3^qKuqIVIs7Mw*f{@+q&G?Q#Ph5>e$jBo% zT4~4XZi=O2fYeL9&MxLlN*-IEAi{q9B%FQOJhkJ=4^Gi*29$ifh771zaC)0mve_tUX9WK_m zV+r`BRaI5>-6YU{g2n`pJgKPoTWuv3grUmz_V)U2KVZrdfn34=H#Z5dpZKN_WC|G> z8HGnjgS14@j4(R-4b)3W$p7D7@}i<&BlD-K4SPXCBr+biWAN?aBq|oxQ{QUJ_pl*1 zP$>)o!uY8z47iY{Cf4-yw7P|kDaaU#h>lJ|C5O#4s!-1SNB#t1`VS(|TtW|PVfd}w zqlcm7$A3I7kiV3=7yO=2R6PU#!k`nHI=H!ts-U0%_xggW+Cr=sHfgjP@|5JF%HTWq z#Q=z6oKa|>e2pjB-Wv)I?w(568}~@C9_VUJz5ed=hx4ASsE^Q{dg?UZHCkM-mA)e+ zbi`ovMX^ZIkPkEq(49%t%owtrbEiNG8jv6RmOXNHS$v;luaFf&A@q(+D)0s%wm%IM z8-drZ#SYPzfYZxxV6sQriT47IihjkIeF~ZBRb!#l*Xu2%KY&5vf7~K zA6Lh`c}d(fy#}8ynWxTXA9(rqgGWvbU)bzDb=>e3HGO~GGsZdgVkj4 zA|=4otW=A`5&x_Pb2Q+$s9LeeaOf8uR_{<-d7$XlVKm^)TI3pXuDDkj?q&eiES3Xa zH??y04QDyKgzqDq5vM5qesIGvh|wz5sXsdtsE#a>P=`>f)IrVTgP+XSyFiZJYu_Fx z;LBPr$5?fp?MGHp6ViKT64+9^*(`IP14n-Gk~B#Q33dv{cuq~2p`Uhr1guu@YMm*h zYIb=F&SeRwf&rq-5QgVDVbOz7g5*gBKC=7$%n|CAgNKt^)09-~oGT?RZOOwwwsC!* z4!ilsFgdQyQJ`@iE<~BvgIw9>qnGb;>Fcf zrm?oFAU2W))7xA8t>c&e-f?c#sCA)mxLEV@8zLtASlEHv`ft(RNREhH`)G`l*YnAF z+;UkG&}Q^6^nO4v0{qqEz$&lH_2IKiiHjw4&&yqYPluj&$FsQfpG@wy3!T`uhJ$5S z0SpsJ*5V@0zRA}gI==VCjy-qger&h=`JW#_fNvDn{SIa)^>(s`V%@wDevAV?i zRBPSRzpLO4T1@fj?KcZm8gWiTFYBS6aiNi0XZs~-6QyR{-+5xLK7JAy_e?@7j(G6R zN$NzEHo+7~kW`8%ZSKi#4z{l1j6Id|2wlE)dKD1eToY)=kh~U-ImGtdJc88f(#-m@@!MncEV)Fvu`wEvOhxVjU zm$j{gCg7_xBDKP_%}?HO-FIr8yf5g& ztG5?ss3lG|#3CC@6BUD=7*tppU4Li$Cu+~?5XwJ{_?u4o6mouN^#KER&}o$Oj9fg& zHOJC&cV+SUk|Aoa%PVj5<#%bCDfGy8q(j9lh;L9JMWO=kXk0@Jqd+%cU4!WS`6R1} zNvmO$<9U@4;aU!NqF=%AkcNAc55hs?on+^aw*F%uuj|?-O)ZDNRo*Uve9A?QvGYft z!t4T>T#?rY!p?7{v>$OGjvU_Mon-JJu7&pj#UnzbM*AYLLVU<1?)Szc*pN1MgtSs^ zEUZ?^=oRufk(BQcQ~QK_*Jr>#l7YnOv{;6Je<>Kix+;&t;z>-D&6ob;@V5v%6nyDK zJ}jyG_?dWd;rf+EL3_N)!rLD=hL6=brMu&jXoFRMjg71yRWFIuUw${ImgqecY0ZcE zZf5;-H4b}yq+fP|IX8PW0X#D4TOUjS$8>d>v*c+BIv4ULR1xiP(rn}KgrS&?@1Qc| z`~V^R9MjWVu!HI>CdZJKgJ}*-7=_+|06O<~^c7E<-)Zx%P%5e$kdx@jJ5QWw(sy{m zVd;}K3$Ns%Y)*+aZnA z4w|zeLdGV%QOEu(9c)YO$$(gDV^7AUh{q3xe=-{&9Q=0LlcYV zsUK3|0U(0*R^Y^_TNr!+(O^(cghqrk4sR)k%)v~xf=BnInZUve4olE`VAUMQiAKN zAs6@zQ|JDZZvlxLdoqqazDmd-GVGH|-#fZ{c^*+*Oc~~C9f>W zpfZGZ%AnSm?sF|}KfkWMqhrr-KcGi!RJrLE=HTOrGQwk{>#a>QD%o_(G~Z4vLDd#U zrpTpd6|Z;v6W_2m5Xqx)p7`KQSNzh5x{PPR?Cpf}&*9b9Mx-&y$m(%TnI?hTtpc8) zO}-oc>mKgBRXM>zP~1JK7Hbu)0QG)O!Ikk_-pg!90_L$sa6)snBTR?Sx4Mst6)YAS zTno2|cI-I6s<^aAS?c;`st=xy966bd!ds0|_v<<}S<92eZVpk~nuply&4PGjmmn%9 zy^XaS208ri%})G*2I{^;tLIG)j{A&s=9$wUq&I>8-$0(Qqf5Jz#l*#m%V7t{5@2)$ zc7E_!zG0t$I%4VCJ{z*J0r&G@a+mUHFz<`n$dcI=*{$%rE zGe(*0xb}V`^dBkQY~M~w!h7q%;Jhl8^VEK48ajpDb~11PDozH}5ge*=83j8YIL=hN z9HcCJM$J`}P5WjmC&A5JBX-LRMV=9p(Ue8s;O1_3WuYNQCR4gHCadL03{Q??Ph?3J zk8`va^{M`5W4s74;CRo^A7OJ-)`Bv7S7K7A9S+h#b~Umf+*ad%C>)AQB%fo9))mF24Y zjNK3d;;rNQL0<38!&grlu+oXg-UEl?cE+?#A=EK)&;Sj4!4+$WA6b35I{sGiMXH$F zlP`c|zD$X2R$@=+7!%{l@U-js=N9i-=es-esUmYUoNI(Gmlt(QBmIr6{vzJ(xe>E( zDRnllA7Jk_Zo}}J+3dvCT-MM!H}2WK1_LD4eD@=e;jXHo?mD)oT~A3(RWMN%^CH?` zO~c2%o)~DdaPMNt>jfsMjF^~}E>s5E_JaGq>J&=P@Te0~%M31etH#rD_O?U=I+xQCRWXGI*SYKZn`!Q zgQK^U9I<~srxu+?k+`1!(w8HiXAy&CFXnvBGnz7>Q&j{fseSOn=G&9ey~)YLi6}FE zE~7|Owi?&x{UP1c2bfecj?yIU>!mGMSj@q04fztg2Lp6MtwUHdsyB-VcPhWmY+FN9 z`z-wpW4}-YT=E~y5-1;2pYEkC<91GaxUPW%mqAhO77mnpq>IU{VZoVSd$jGVols6X zo@AT3>pORy9qw8S6ll;02 z8|LwNYs&|cEqR12t~yQ|{J$SKlG{>auSPfgh_(?mdLL^nhFLnh2!^&MH&aQutp7p(;=DSuYF)b%c`1Rl+*V0SfwSG98o>EBbT zd?j*VRB1btt1Cr%@8s@28$k5uPhM@v>l?g{xSYy7z<_3=6*xa$H~PZMiC%1z=`V|z zIqEN>6*`93TXZl>s9W5;qqdaZ)kz>#tOkcTLGYABClRB5S?g`SGYW6H2Tm5tVFz<_dBHmm|R7bxNbg;OX*m#pR zW3W3suj^T^$!N--(hQK)`~@IC_;9{zu~4qncxlXr6ZS;{9QF6Fnlw60k1Cn}j>z1Z zfPWUmlN!hX)GKAorZ?Q6c`%dCmVJ;3)x$>`;@C1g527fs&TDt+e@Iw>LLEpkBzx zKmYB8ih>f5PsL`v#8lNr@>L=hZ~N`}_A3;Wz;$C`AMp(UR4ObYa@&e43c0 zH}^%QdWtm5<|shyn;h0$30V>pSdtOeUixB{U0wEbF?YUexx*YP%JnlQt6mw#&Fjr6Yu~K%r1(& z?_crN?{9jWcFo=Z^LJ`({VRt-S2tD@YfcAAr?$86;gRmCXWcVpNSvV6#7xM7PJY$3qc-2>h ztw@WHTELywf>Q^%0Lr;rNA&_vXJiz=40Y=?wk$KAq?_G>_l{Pc{J>=s?&5#p_Jr&y zprF-6ioyX??mhvQtnD^C_k`_CouV8% z5hjm4liUWc>$)>ni;2kNYNU*?jS0?~P2Q<@^WVWZd1Z)lOtJ ze@Ns+181Bq@lPkv^VXr+?PZ&8AD)t{e%zl7Uu#rZ$c&ILZhnH$*m?gYkuz)S%^j5O^6#ZLO-eSJNr?#5Il^r1p3s(TW}*%Hi7elHUoyNzX+O_dd0r8ECngBByF5RwkWB;+|&!N-!G`s-Qo*A@X4Ydrw2Iud|KuP zvw6#kDc%0JxQw&fw_o-p70yh*N*!LukN^2OzgeZR*W8r;H5OmcyBGSLouhLm=Qu0@ zY|7SluaIN%>exUHyWrfgeXWYW5hF@Ju9*9(n#v0r}?R1#DT+vTC?Ni`{0u?jsTapv-y9B!1igCvh986PPTXH4e zPT;kvKlliJ98*4kVM=9dVQl9gbUv?wBAIa1ljw2~Xju@Jv$Ynac>V+9r`zaiG0cSC z!%MEaFw>`0^VPJ4$}8pS%Y7Sa3QkvBYGdiUtoiP^uZ#jokw|N?Q6!N~ziR1D< z%hl#+_W&^A=c^G0%Q5X<#@UP+7P%J1H(vGiI@HH#V15;6rvjv6p0C1&;K}6etp;;m zr_+KPlqBCsExMoLb^3;CEp#=|`qFnUT6ggOY3nSb+6udEO>rm`io3fz#jRLy*W&K( zf#UA&P~6?!JwS1HD^}d$rr$Z|-uvVH%uYu3dXqi&c%M1fnmd6IMNs=h7s$X4=jH71 zrq>w?GWdSr5{dOdNuWJ3+yi;M+@?JIkw4$_)am@S6ZlG_Ud&q+QbkeSr^E13$V};C zAlQ>Z=?y%&vVqctaQU#m?A){F{rL7S_(1g)*Um14!iY+VG>n9cRg|U)uuvSaS}vv( zfz_muOh;&D9eZ6M9y(jD#x7yh>=qO>={^1Q7Qb_NXckZ3cHOC8bL1)ydK8@Ybh`d03;93yR%*#QD+Hy zh7X=jSZI*(eCl0UiPlDsjemT^;x0IVoDq6bnGDtvusHn#2D}Esp~&TY$D96&R$w)q z&-Do6qWGYrw~w6e2!3_EgJxYcUzPVZ)2C&X1~udL)OXo}L$%RoGNS3NOy6C%C!)rO z>0g`)_~%EJhTmOvYzVWiXimaa;#KX*!V@brZWPujV?r>@_1fQTG~bM&1te1>ZEXXi z0i^eil}a?CRQHYq5z&m|NQ4+V?vVQU-?y#baO)jOrnYd$f?%T`n~m$?Wxh~e)nL&W z&0y%E@J5qeKzDPc^)A7(xV)`#;Ikw--r?8%5{nQmiGYJZOaLH9L@}H`+24`0%OX*m z>>@2frw)<238{2M9|&P!h)dKcY*M$yBGV@;nbel^M&0SVQ*T6PnuLa`^YbSyrMbt= z~NgJ1VWALP|Bq&6K!jMeJ?PeD{FB+xF$5=cXp2o5DmiAi_TP(?uw>QwI??w zGb`cWzZ5f9@_AOND~*w%mPbEYOH{gS{8f$_cN_Lwobny2_sa*JAdQUQk-LN zs+(&0E|g!^VS53y9HODcxp?9wN;!>E>f_DBmIId*VFNv;lw2rbjgSo@B8FZ3@{ZNaw zOtH;c?`n@AMXe!BJ2HalN*scV*C~-0AM9>|M>=rgsQ%iHQsRqM3{;%UwM+6^urtcv}-s5GAe3w|DJ< zzvWXOb^2heWb=bYrHQnln|LJ5t2vHrM!E!RuH=0878A=e`XE(fX7*y`%ZM^=qlJ$c!DEX&EXUnd!#A1W^EOjVnWrX12WzFVZ;gYG9-!r6^$4{}+j;tKo?o=NuKt^nHeNU5-KYN`v>WrxXol zS9?8p&{~Ch%6vBLZarLON41{qoVuyNS*}C$>3cQLv>1W|xmGE1WRpl8BqZ3x?B>v+ z*TZ04J0bq|GxPFe=-z!CV?6Q1)L=SC04(K^P3$7m;m(Az3pENxR=kgv@kD<<5R+xb zO9l*GY;=IzjK;16wX57%93W>tNyCbpYhG^z`a06^uQ#N3)rmyessO?of>ksHj%}oI zaa4aJqajX)^Gpx)7pMR*ytyyQ^X1{RQ)&1^8ByUGQv3HrWI&zWyH+71B`_)BfAiQy z4hH__+Orb;?zHsxtXt-{@b4wCXwo*(l1QNV8j7_~PnV3Sl`?D9WLC!vt!?0F!GYJ` z-~adKhTS?MQ8*%30_!hKLX!vYj|C4M02zU5@xQ$cWBB4Pjz&cEuagPf zv-GrqAwoJlGz1eZ5!lkwqILG!A4`*9XJ%Z{y=2PZJroexvK!ul8mJ+ZC_U1EVb<5{;e#Tn0P7){ATV|#~ zN|Vkex)ResSzlv3N|}9`cQr3o+S*7p^hQto5afy{c>r|w2tC=qwLLXnU-b@YMK;Jd z0+Mb@OxXXs(of5-K(&wlpL$LVg{tOI276jKDda5Hwwu~K@u(0?U`qp zd_UDWC-x35LL@DPteVayCiO4#3l~pjQ>zdpxX?egyCN>;Gh7I=cdj2R&a_w(V)7Qi8(95B!`DQ)nM($3!z}SqhE8tWOBz#7N3n% z_VXyFF3Sze;zj3@*RU>%59+<}<#*by44Cb6t*qS~)2o`L6!&i4jP;8_eVjjg2|Ioz z3tlsp2X8h#&6kV<(V5O&Ic;Jx>l&RfrFm#5Z}z%L&8Pkm389|Z{bE9q z_vtyy!H}nGHF_tO4Egx!z9i~)AB=ro0)S}1ZQn7rt$$~AxGjEp{N0DEJVHAO0HeTkl64lg$d%n*(?>udS${yRt zdt>^*{gl??{4qT|fWnXPTcoZrLEL^ldv!!Hm*=fJ>D9~#Ui}p3vO{5x zo^6StI4(S|y!H{t=Z^09_e!J-NF}4tVj{aRPrqdBn=6#tv+hIdO`hP5q8uac7hW_l za#JDZzIc`)7G`ZluXE$o@&($(ZfO5&B0NuH99BjapfRTc4#h!iA&R*>rx!nlHd2fa z$Clbqqn@tm^zrEVI|gL7cajTPmR?@+d>QI7WUwbxZGBA8pvUfBZpz^IH}-4@F4L{; zJ^xRG#M%Rg@B{t=FkO0~@~PUlqgpGe;1q%zrHH0uDPze3S7lZopY^zb=7vAp*-1LC zi4sx(DL>t-aOsl+z8%yGKlqWW1$SIS5>VOQJkb+V)fu^F#~;)y^@B34DjWprSSKz@ zj)K_bzWa$gkFMkl0iB1EAa)Egg|~qo^-s`hbzHNRaO;uKv0TLL<$bOx)0?2Kr0%$y zN~6Vg=+$WNT(6kA^&ad<$Yap!=$}1Av zb9o+|nR3hQl=7vt=r}(c2>OC2V6Ucws%~$b2BsLBJZ9aUb&J(+Q(Ni|h^a;SI0gj= zv~}=eUP2V`HjEu#gsmD0dXv=P+6--&4DsJ43Wb=am9rl&?&lhGyRktUBO3=>n$@jSE5_Y zFQ&Cu_wDV1+uqksuP$iMfAajal{5FrkwCXI2)zbre?z0oae?mpW>+i{#dqC(ydrpb z2Tok|+A3`*zY353X?^me4=x1T4oe?_dNgzDmOSnAd!*d1cw_e{W=}5=2!43r{N!W0 zVCN`nF`XkL<~4>#+e)Nu?7I2l8y_D&hQj75-n0sec9QNxMLLUh*Z{A?H~Qk9kbL9f zSk1m|L%y5p1hVKSlQrmoIRXHt`~jPUxr!CF&ncFCN9Xu5tQ%EJTvh;1`!!1!*_T@2 z0I#~j^+ac?|YXG4nGL0uy6AN?tmOIsCV0jys_V=Re01cI0#$P(F6M*Z6|nrH3d?@xAZx) zKZxDO2dQYG>U(qMv@%|vCBfDdd zJ6H>(x2IcvgRqKtf}R7quxy0f?%mv1J=qMBWxNXV4&g8EsuWOhBA?1e4NU<6=oZPD z%-!-{&AH#E(8mo3`9zce9?E$({tdIh5n|P!@nCZ-{05wN%~+sbAXSvW{F>LQL>@OgxIacNg`p_ z!R2R~>HV)qHuk_NAMqL7+f!G z$MqS2gN`(=@MH@WVpMG!#rEKYb$aJMMe|+zY~M)IOPWACyUw!HSRfanRr=^pmgs%O zV5+9|_?i%BM(+G{vb*EOq0BTwXDyFg$7fdnlH}pE@sY9p=CPho8{?vy<+`HG{`)zD zZe4WH47X)|@=vmPuLg8Vlp3RzhnH`bYN1q3kr|cB;l5Hx&C4s)GKIjuB%9S92~GHR zX#AzjYL%bHzIb<(K-#Jmeg09=_s(p7ZCyCa%>K^51=1d=+O<8|)D48zOnxW{cow?< zaAW-uCAhP$_ug7oELwDp3&;FPm1*8u8TqA@(Nu)3u9IhA@QpXVi37wq5Jb%TGcsR>c4d2qIH)EupE-4~j8Ug;cOeCalijf; z`NihLgySU}PpQSpGRroh)z1x1zxoY3w4EHGHiRHZQl*yi_$@f`{p^#B_%xelAmUW!!e32qnPlMd977taNs+HX2Lxwd!ZpGbSu3cl7-W z@f`U+9uTfu1WKPe!7j(($X#Z|V>3{1OTN$)pkL0v%HBEw**<*&-sugT=q-J=BQm<{ zEYf9IiIyf@SIR#cFGor9$|sPsQX7;hpt?9jSe#z#Gfw5=s7&)(rR8t)4G4t$^*g*B zuqPHxmt*3tLcxE$9>4el_d*aOry7mO#g6r;U=v0mFVf1BSdQO#8*i3oC7$KAwZ3S{!BItB=;tjcLh5so*0&-Slw@Ej+eNmz0lc7X-E^3X!b1KiKOJzHuQoK2_3W# zuQx6JLnv)N8H$cAuj)!%i5Je3*n#5BBZ4)-xCE{Bt#DMy3+Z#F;3&xK%9#k%WAomu zqkQPrc!v3qq18OiPyR|uO8Oa9n9M(e#EeTfuDE&6H>XEF!CbaejPiKjpOJEKU}#~r z83w#c7Ae<(BO$>JC{CTR>J@}YStL)z{9}2?NHU~K{6hbgfdKkXV>#*E$X)( zaZw=|Yrpvbfo(1)Xw>pqB;X#Ho=kF@K`gTh#yFmm_*0-v$dTfcyL_|m{bpicL8iWb-25~0!T zrZbYII_m@@$s?jl78!5Sa{8nND8KslMY+7W zR(=IyAy;Nm3z><73Lb1S>+@-7t|zEx4plAwv!V*@`>nKZ^g51Q6wkeecCtBQWn;vL zYBzdF*T5Ltwb5<~dus1)PXlG8B>pQWIW5KQqT9TwbW76uZ_rrM<-+)>%Qva5Jyv4v zw%Hl;)3rTU^)K{`U)@Rh3#P!SpBZ;wY8G#Yf9(AXY{16EcQF~fHvT?Qy!KaAhU?8x zH>XM8d8<0gmDZ#sSN66nIqE|5a}S#QH|rlXI(1*pLxIz6gOD>z)3Y_x5#Y-kOWI5* zYP2a+=~lrQBrk`*-QtGOl2X0D`gj77^yI>k1Lft^eagqb?oP6jqj&he%IG17b2q=d zXFPfK7cWuX0^x)&D%}|9p!}=VYZqdKh{`a~=vw|gIG?7h!0Tl2*)uM8ATTC2*8I7# zjDPZsa04+4hYN$cDrMGq*vQ&C)7N#LOQMN*P<|-}DVHncpP06!aCKyhEKe7l%F3HS z*dGX16w*D}SYPHsxri?KdZ%C}{u0b2vgO7U-##jNd^sMgDx#ko9|bkL9k~A1j*W01GnAn9|(rz66iYNuHKuKa7rbPC8FyALPD`KF~8O-Y(4g%#YH} zIg&J)1D_RMGom{l>i06gb~-tJdfR;S=R+hB+U@TXYSk?!)~{I`Z+88BqJNKc^L%S= z86>#!(DR7H5(VYv)XVdu*tx}d+f9g$!bpbm z&73hwnOS!bRdZ#w>_F)y?mQ5ddN=niO(n7ol@zRsC8SHHohS@_8;AYp<1G%xy(;>);Pq?qlH+F8HbWUda*h z@#YpQvUzd*t~*%?6hG;{fGcix;9#lTo9tXUt397utdaaZgGCbO$$hV64f6UTZ!b~m zstwJHB1SOH9jDP_NRGwCFFa*{X+`cjd%Au0HQjIk4-8}F;)TFO~ zcGDsu-&C}LXGiN3K++>L0Lc&kr!~4+A-ZKI8j&RzW>)}`6vbyxvjj|ULnsw$CZmxn z_zV8#;W7&b=_}LlL`;liC3wi*F*suE$I}boZ3O)Xq0qSBBBzD&blGdlC0b2x z$&YaR9Htakit-3Yc5Mkq`MR<=b4WR=w+t2CjddEG;aqmfe)!T%L6qm)Ew+Pk_2Gpn z#zfvqnhiHtJ~7Fw>FLbn$2~d;u5^(yqR&rmUVH#)cb?k?io?o{=_fzpyax9mi{N@k z$I*z4i1XFG4hI;%kEkMP2gsfe7l^W1myC|_^;L(Aj&Sp|jlo17%aL-)RFjD*9bOD^ z+g}1a-;vM|p-;wS^9b=5<*8@DH^{-`cLwd>8pR5DEx9OLoRa9N z8gBM+iRcd;2B8r`(|?;n&`0(R9O+L{^~&uIXY9DVq?Cx7^M{p`)~N?pE|m;QCwAGe zd7Q9CAgQZ`*1exzc;Ld4Q}XdQDs%xU6$Flt6dJ6FRugJ^1Z56LTr;q%mip?1Zg2hg zIJ{rDE>vyV!^&-+*&X&Ev{8sagI;&(1l&oRxgm2VuP!Ov43T1IzGipNX*#sF>MyQ& zKZ@FXIu1~CRXuxCw$wOs&?C?hXX_!k?w%};7_^u1=PAn^{xd`L=V8{j8e_mC zlC3h?hU?ZbF(&TqbVFtNIg_CH38XboHC@elHeL$1n^GA+(ajX2=U0`xTLzJp-^ou$GdxxtfP2{wp3(xFvT+Zu zl+j=yClPYsm5S}rpM#H9k0T7nO#z%$W(+@ik`q$O$bLpjN-Tz_Yp<4ehP1kryBhxe zpFo|IAPZsG*a%F=*-Mf$eeO+9&b_q$8-g2WJ-mi_tsRli3|J~ejnVczjT^kz1_yJx z7Gk6zoAhQ$Y8#(%yyeD%t2C|K)vZj+8qU4fUf;$tEv%|P2rbM+2Q@*(viN^ObG^~< zQxwd*baeD)OT#_6ZpTLUTv4*)4I#^sM@oQfW8u{9Nj;$1jWa zY-Qc8>k@&QTdt6G}d)yZ84G4}mIRwj+jn6Pw$eC~F1d0U>?c&_)_w5a-Y|gLk1wS~~NUD+}t%Y-i4?O&|dtdj6 z4Lqr^T=ww+dfj=o68km4G-c4!fyifQn3T%j!|NkR3p`XRze_#NfCwHTODTHN~Zqt#G74&zfoO?@ELQsk| zcAs^1e>5}_mi+z&?2N`tOm*DzWr(O^FRI`$I=*H_s#ZxN<1}MG`Xf1+{eo4q75mTP zYVa1v^Tw2u*MN4glAeyaIMY=Nu-kxAEMaOX?_HzkGUh&K@6P56N{$c5aAXoouAcKO zkP|1!HO^9CGtLu%T$jSK;xx!+vrlHr)&3uY3pzlz)_j@>+zA)o(AuN{-i7i3zbaL5 z7kfJlp^gEfRa#gpK8+>j+qww|r;)HwwFqQCuGAZqWg2fIZWvv^zjhD&TRx>B^sx4Y6rr9KLfFca=O^;Mk?lUIwXHtMwD%&xSC%5^a!%E+cO5YBaWXlTKaA=`5G;AD1DtrCS)6`{?Te)sh-6;fJ+nSOEum{4An<=o+} zuAO<`0^ORB)n{m`T8UizA8Zvkb%clZoGOw@4U38KrQPd26s}nKHEZ$rZt597>c&rS zpevp7r=l#}^+_R(7t5wqfBvc6nRvlS@}^^CF#-UWI0+5Qf3e|T zWwu}92d{RbhNO~S6=cp=n2=yk68eyo`nkN6ZACMzK^TmWsWz@l%Yj&A|9Oj=T065Z zkgKU6OBou{UpS7t@J~6dBWPO$407NPcWTxzY*rf0)GK|3hlUi{G^ng;ix8P8#J1cK z|NQTOVP#DSS$4CWI_VeVc(BI^J`|V!t%P2^5Zh#|bQD$v>^GwBBM*~)NhC!XSc*0~ z6bHMBU@x+hvg5G;gbdW2duXEf$>(6{^+>k2R14N_)V9lM0 z4zP{sODZ89s-zN2Kdsdku1P==urSUHi)OEEh0L7=;T3oyr67RMF4F&vhpgjBTo$3> zYF;x;4#7D?SSP)7kfqQNARCQeA)kIY7zvtm#`^cmAmSB6^SWl>U#>U^2EyYfAwh)< zLuR*&&<*!nF88WV^SBDCr8(1qM3Z13{fpuf|Av&!^Qe&7T5X!}^HK)}4I^`GZYCYs zF#I-Q#RRV@Kwf@#W?0X`3>R;>KkI}f{|Z~pDWNJpJ+8t7kBk+=LUt0Ld{CPN6@x=o zAkUm(GUTQ{{ORoX37cHq0Lh@CZXInt2$1HC?*5;M1& diff --git a/tests/data/slides/files/BasicSlide/05b94f634a049cd83daa9b47e483183d1ccdbc485687cee79c6ffbd4f02698da_reversed.mp4 b/tests/data/slides/files/BasicSlide/05b94f634a049cd83daa9b47e483183d1ccdbc485687cee79c6ffbd4f02698da_reversed.mp4 index cbf709ffb0ee2afec09534dd4817a922c898b625..e8fcfca8df923a697a6cfb7e3f27a6e15526cb4a 100644 GIT binary patch delta 14 Vcmdm&wl8f%A0wmb=6=TYh5#{Q1>pby delta 14 Vcmdm&wl8f%A0wmD=6=TYh5#{81>OJv diff --git a/tests/data/slides/files/BasicSlide/275756d906c706ca0125660866bb925b8927e2b2589d31a7a578079b70076ef4.mp4 b/tests/data/slides/files/BasicSlide/275756d906c706ca0125660866bb925b8927e2b2589d31a7a578079b70076ef4.mp4 index d468763a0bc536dcaf7cf9d4b4dace4e0b2b932a..a7d24439728bc669f53ea53f60bb97045e899bb7 100644 GIT binary patch delta 14 VcmexS`J-|}A0wmb=6=RZYXCTm20s7* delta 14 VcmexS`J-|}A0wmD=6=RZYXCTU20Q=& diff --git a/tests/data/slides/files/BasicSlide/275756d906c706ca0125660866bb925b8927e2b2589d31a7a578079b70076ef4_reversed.mp4 b/tests/data/slides/files/BasicSlide/275756d906c706ca0125660866bb925b8927e2b2589d31a7a578079b70076ef4_reversed.mp4 index 073050e625d7f40e49c53e59cb64b9dd40694598..b85c3380dd410d87123cf4c7b43d8d56a6bb1242 100644 GIT binary patch delta 14 VcmaD*`Ji$`A0wmb=6=RNYXCLu1@`~| delta 14 VcmaD*`Ji$`A0wmD=6=RNYXCLc1@r&_ diff --git a/tests/data/slides/files/BasicSlide/c10cc5deb3630a8259712288913b2bd6e79d093356d379d518cc929812194bd9.mp4 b/tests/data/slides/files/BasicSlide/c10cc5deb3630a8259712288913b2bd6e79d093356d379d518cc929812194bd9.mp4 index e0cfd2271213c41cff268c28ef59cc490b1482d1..878f86f4809d242ccdb6c9dda9d0cdbc0bd90ec4 100644 GIT binary patch delta 14 VcmX?Dcd%|lA0wmb=6=Rawg5B&1||Ri delta 14 VcmX?Dcd%|lA0wmD=6=Rawg5Bm1|t9f diff --git a/tests/data/slides/files/BasicSlide/c10cc5deb3630a8259712288913b2bd6e79d093356d379d518cc929812194bd9_reversed.mp4 b/tests/data/slides/files/BasicSlide/c10cc5deb3630a8259712288913b2bd6e79d093356d379d518cc929812194bd9_reversed.mp4 index a0ffe05797df6332895d91a6bf65859633cea480..f80d681fb699959513f8890eb60297888998561e 100644 GIT binary patch delta 16 XcmbQ%#yG8waYG*?qv_^;#&$;lGl2!W delta 16 XcmbQ%#yG8waYG*?qtWJm#&$;lGj9dB diff --git a/tests/data/slides/files/BasicSlide/d09707faa0d68c55e98c628c5da51d66c92d0f79ac48647526817c377f843810.mp4 b/tests/data/slides/files/BasicSlide/d09707faa0d68c55e98c628c5da51d66c92d0f79ac48647526817c377f843810.mp4 index 212137ddd5ad9644ed586259597f059b70f717de..2f972364d77da3ca9fbfc902baa38a6579ff1234 100644 GIT binary patch delta 14 Vcmdn&yxDm}A0wmb=6*&M1pq761nB?( delta 14 Vcmdn&yxDm}A0wmD=6*&M1pq6<1m*w$ diff --git a/tests/data/slides/files/BasicSlide/d09707faa0d68c55e98c628c5da51d66c92d0f79ac48647526817c377f843810_reversed.mp4 b/tests/data/slides/files/BasicSlide/d09707faa0d68c55e98c628c5da51d66c92d0f79ac48647526817c377f843810_reversed.mp4 index 783f06e357c96187d6cc5892086d43f937f0fa77..1f557f9bad1d011cc786b2fcd1f99177713c5cfa 100644 GIT binary patch delta 14 VcmaFt^w?=bA0wmb=6=R2@&GaX1@-^{ delta 14 VcmaFt^w?=bA0wmD=6=R2@&GaF1@iy^