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

1from __future__ import annotations 

2 

3from pathlib import Path 

4from tempfile import NamedTemporaryFile 

5from typing import Self, override 

6 

7from moviepy.editor import VideoClip, VideoFileClip 

8 

9from . import BytesBlob 

10 

11 

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) 

23 

24 f.close() 

25 

26 blob = Path(f.name).read_bytes() 

27 

28 super().__init__(blob) 

29 

30 def as_video(self, filename: str) -> VideoFileClip: 

31 Path(filename).write_bytes(self._blob_bytes) 

32 

33 return VideoFileClip(filename) 

34 

35 @override 

36 def __repr__(self) -> str: 

37 return f"{self.__class__.__name__}(...)" 

38 

39 @classmethod 

40 @override 

41 def load(cls: type[Self], f: Path | str) -> Self: 

42 f = Path(f).expanduser() 

43 

44 if f.suffix.lower() == ".mp4": 

45 return cls(f.read_bytes()) 

46 

47 clip = VideoFileClip(str(f)) 

48 blob = cls(clip) 

49 clip.close() 

50 

51 return blob 

52 

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) 

59 

60 f.write_bytes(self.as_bytes())