Coverage for src/blob_dict/blob/video.py: 0%
39 statements
« prev ^ index » next coverage.py v7.8.1, created at 2025-05-29 23:07 -0700
« prev ^ index » next coverage.py v7.8.1, created at 2025-05-29 23:07 -0700
1from __future__ import annotations
3from pathlib import Path
4from tempfile import NamedTemporaryFile
5from typing import Self, override
7from moviepy.editor import VideoClip, VideoFileClip
9from . import BytesBlob
12class VideoBlob(BytesBlob):
13 def __init__(self, blob: bytes | VideoClip) -> None:
14 if isinstance(blob, VideoClip):
15 if (
16 isinstance(blob, VideoFileClip)
17 and blob.filename.lower().endswith(".mp4")
18 ):
19 blob = Path(blob.filename).read_bytes()
20 else:
21 with NamedTemporaryFile(suffix=".mp4", delete_on_close=False) as f:
22 blob.write_videofile(f.name)
24 f.close()
26 blob = Path(f.name).read_bytes()
28 super().__init__(blob)
30 def as_video(self, filename: str) -> VideoFileClip:
31 Path(filename).write_bytes(self._blob_bytes)
33 return VideoFileClip(filename)
35 @override
36 def __repr__(self) -> str:
37 return f"{self.__class__.__name__}(...)"
39 @classmethod
40 @override
41 def load(cls: type[Self], f: Path | str) -> Self:
42 f = Path(f).expanduser()
44 if f.suffix.lower() == ".mp4":
45 return cls(f.read_bytes())
47 clip = VideoFileClip(str(f))
48 blob = cls(clip)
49 clip.close()
51 return blob
53 @override
54 def dump(self, f: Path | str) -> None:
55 f = Path(f).expanduser()
56 if f.suffix.lower() != ".mp4":
57 msg = "Only MP4 file is supported."
58 raise ValueError(msg)
60 f.write_bytes(self.as_bytes())