Source code for wed.chaptors
# coding: utf-8
from typing import Dict, List, Optional, Tuple, Union
import cv2
import numpy as np
import numpy.typing as npt
from tqdm import tqdm
from ..utils._colorings import toGREEN
from ..utils._path import OPENING_TEMPLATE_PATH
from ..utils.audio_utils import synthesize_audio
from ..utils.generic_utils import handleKeyError
from ..utils.image_utils import arr2pil, pil2arr
from ..utils.video_utils import capture2writor
from . import (
base_editor,
marquee,
rotating_rectangle,
smartphone,
spread_tile,
title_call,
)
from .base_editor import BaseVideoHandler, BaseWedOPEditor
from .marquee import MarqueeEditor
from .rotating_face import RotatingFaceEditor
from .rotating_rectangle import RotatingRectangleEditor
from .smartphone import SmartphoneEditor
from .spread_tile import SpreadTileEditor
from .title_call import TitleCallEditor
WED_EDITORS: Dict[str, BaseWedOPEditor] = {
"marquee": MarqueeEditor,
"rotating_rectangle": RotatingRectangleEditor,
"spread_tile": SpreadTileEditor,
"smartphone": SmartphoneEditor,
"title_call": TitleCallEditor,
}
[docs]def get(identifier: Union[str, BaseWedOPEditor], **kwargs) -> BaseWedOPEditor:
"""Get an instance of :class:`BaseWedOPEditor <wed.chaptors.base_editor.BaseWedOPEditor>`
Args:
identifier (Union[str, BaseWedOPEditor]) : An identifier for :class:`BaseWedOPEditor <wed.chaptors.base_editor.BaseWedOPEditor>`.
Raises:
TypeError: When ``identifier`` is not a ``str`` or an instance of :class:`BaseWedOPEditor <wed.chaptors.base_editor.BaseWedOPEditor>`
Returns:
BaseWedOPEditor: An instance of :class:`BaseWedOPEditor <wed.chaptors.base_editor.BaseWedOPEditor>`
"""
if isinstance(identifier, str):
handleKeyError(lst=list(WED_EDITORS.keys()), identifier=identifier)
instance = WED_EDITORS[identifier](**kwargs)
elif isinstance(identifier, BaseWedOPEditor):
instance = identifier
else:
raise TypeError(
f"{toGREEN('identifier')} must be a string or an instance of {toGREEN('BaseWedOPEditor')}"
)
return instance
[docs]class Administorator(BaseVideoHandler):
"""Administrator in charge of Editors.
Attributes:
editors (List[BaseWedOPEditor]) : List containing each editor
"""
def __init__(self, video_path: str):
self.video_path = video_path
self.editors: List[BaseWedOPEditor] = []
[docs] def set_editor(self, identifier: str, **kwargs) -> None:
"""Set an editor.
Args:
identifier (str) : An identifier for :class:`BaseWedOPEditor <wed.chaptors.base_editor.BaseWedOPEditor>`.
"""
self.editors.append(get(identifier=identifier, **kwargs))
[docs] def append(self, editor: BaseWedOPEditor) -> None:
"""Append an editor.
Args:
editor (BaseWedOPEditor) : An instance of :class:`BaseWedOPEditor <wed.chaptors.base_editor.BaseWedOPEditor>`.
"""
self.editors.append(editor)
[docs] def edit(self, frame: npt.NDArray[np.uint8], pos: int) -> npt.NDArray[np.uint8]:
"""Edit the image if it is an assigned chapter (``pos``)
Args:
frame (npt.NDArray[np.uint8]) : Current frame (BGR image) in the video.
pos (int) : Current position in the video.
Returns:
npt.NDArray[np.uint8]: Edited frame.
"""
for editor in self.editors:
if editor.start_pos <= pos <= editor.end_pos:
frame = editor.edit(frame=frame, pos=pos)
return frame
[docs] def export(self, out_path: Optional[str] = None, codec: str = "H264"):
"""Create a video with each editor in ``editors``.
Args:
out_path (Optional[str], optional) : The path to the created silence video file. Defaults to ``None``.
codec (str, optional) : Video codec for the created video file. Defaults to ``"H264"``.
Returns:
str: The path to the created video file.
"""
cap = cv2.VideoCapture(self.video_path)
out, out_path = capture2writor(cap=cap, out_path=out_path, codec=codec)
for i in tqdm(
range(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))), desc="Wed DOWNTOWN OP"
):
ret, frame = cap.read()
if (not ret) or (frame is None):
break
frame = self.edit(frame, i)
out.write(frame)
out.release()
cap.release()
synthesized_video_path = self.synthesize_audio(out_path)
return synthesized_video_path
[docs] def synthesize_audio(self, out_path: str) -> str:
"""Create audio with each editor in ``editors`` and attach it to the video at ``out_path``.
Args:
out_path (str) : The path to the output video.
Returns:
str: The path to the created video file.
"""
audio_path = self.video_path
for editor in self.editors:
audio_path = editor.overlayed_audio_create(video_path=audio_path)
synthesized_video_path = synthesize_audio(
video_path=out_path,
audio_path=audio_path,
)
return synthesize_audio