Coverage for src/blob_dict/blob/audio.py: 0%
40 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-10 20:16 -0700
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-10 20:16 -0700
1from __future__ import annotations
3from io import BytesIO
4from pathlib import Path
5from tempfile import NamedTemporaryFile
6from typing import NamedTuple, override
8import numpy
9import soundfile
10from moviepy.audio.AudioClip import AudioClip
11from moviepy.audio.io.AudioFileClip import AudioFileClip
13from . import BytesBlob
16class AudioData(NamedTuple):
17 data: numpy.ndarray
18 sample_rate: int
21class AudioBlob(BytesBlob):
22 __IN_MEMORY_FILE_NAME: str = "file.mp3"
24 def __init__(self, blob: bytes | AudioClip | AudioData) -> None:
25 if isinstance(blob, AudioClip):
26 if (
27 isinstance(blob, AudioFileClip)
28 # Need to check whether `filename` is `str`` as it can also be sound array
29 and isinstance(blob.filename, str)
30 and blob.filename.endswith(".mp3")
31 ):
32 blob = Path(blob.filename).read_bytes()
33 else:
34 with NamedTemporaryFile(suffix=".mp3", delete_on_close=False) as f:
35 blob.write_audiofile(f.name)
36 blob.close()
38 f.close()
40 blob = Path(f.name).read_bytes()
41 elif isinstance(blob, AudioData):
42 bio = BytesIO()
43 bio.name = AudioBlob.__IN_MEMORY_FILE_NAME
44 soundfile.write(bio, AudioData.data, AudioData.sample_rate)
45 blob = bio.getvalue()
47 super().__init__(blob)
49 def as_audio(self, filename: str) -> AudioFileClip:
50 Path(filename).write_bytes(self._blob_bytes)
52 return AudioFileClip(filename)
54 def as_audio_data(self) -> AudioData:
55 bio = BytesIO(self._blob_bytes)
56 bio.name = AudioBlob.__IN_MEMORY_FILE_NAME
57 return AudioData(*soundfile.read(bio))
59 @override
60 def __repr__(self) -> str:
61 return f"{self.__class__.__name__}(...)"