Source code for audio_metadata.models
__all__ = [
'Format',
'Picture',
'StreamInfo',
'Tag',
'Tags',
]
import os
from attr import (
attrib,
attrs,
)
from bidict import frozenbidict
from tbm_utils import (
AttrMapping,
datareader,
humanize_filesize,
)
from .utils import (
humanize_bitrate,
humanize_duration,
humanize_sample_rate,
)
@attrs(
repr=False,
kw_only=True,
)
class Tag(AttrMapping):
name = attrib()
value = attrib()
[docs]class Tags(AttrMapping):
"""Base class for tags objects.
Attributes:
FIELD_MAP (frozenbidict): A mapping of format-specific
field names to common aliases.
"""
FIELD_MAP = frozenbidict()
def __getattr__(self, attr):
a = self.FIELD_MAP.get(attr, attr)
return super().__getattr__(a)
def __setattr__(self, attr, value):
a = self.FIELD_MAP.get(attr, attr)
super().__setattr__(a, value)
def __delattr__(self, attr):
a = self.FIELD_MAP.get(attr, attr)
super().__delattr__(a)
def __getitem__(self, key):
k = self.FIELD_MAP.get(key, key)
return super().__getitem__(k)
def __setitem__(self, key, value):
k = self.FIELD_MAP.get(key, key)
super().__setitem__(k, value)
def __delitem__(self, key):
k = self.FIELD_MAP.get(key, key)
super().__delitem__(k)
def __iter__(self):
return iter(
self.FIELD_MAP.inv.get(k, k)
for k in self.__dict__
if not k.startswith('_') and not k == 'FIELD_MAP'
)
def __repr__(self, repr_dict=None):
repr_dict = {
self.FIELD_MAP.inv.get(k, k): v
for k, v in self.__dict__.items()
if not k.startswith('_') and not k == 'FIELD_MAP'
}
return super().__repr__(repr_dict=repr_dict)
[docs]class Format(AttrMapping):
"""Base class for audio format objects.
Attributes:
filepath (str): Path to audio file, if applicable.
filesize (int): Size of audio file.
pictures (list): A list of `Picture` objects.
tags (Tags): A `Tags` object.
"""
tags_type = Tags
def __init__(self):
self.filepath = None
self.filesize = None
self.pictures = []
self.tags = self.tags_type()
def __repr__(self):
repr_dict = {}
for k, v in sorted(self.items()):
if (
k == 'filesize'
and v is not None
):
repr_dict[k] = humanize_filesize(v, precision=2)
elif not k.startswith('_'):
repr_dict[k] = v
return super().__repr__(repr_dict=repr_dict)
@datareader
@classmethod
def _load(cls, data):
self = cls()
self._obj = data
try:
self.filepath = os.path.abspath(self._obj.name)
self.filesize = os.path.getsize(self._obj.name)
except AttributeError:
self.filepath = None
self.filesize = len(self._obj.raw.getbuffer())
return self
[docs]class Picture(AttrMapping):
"""Base class for picture objects."""
def __repr__(self):
repr_dict = {}
for k, v in sorted(self.items()):
if k == 'data':
repr_dict[k] = humanize_filesize(len(v), precision=2)
elif not k.startswith('_'):
repr_dict[k] = v
return super().__repr__(repr_dict=repr_dict)
[docs]class StreamInfo(AttrMapping):
"""Base class for stream information objects."""
def __repr__(self):
repr_dict = {}
for k, v in sorted(self.items()):
if k.endswith('bitrate'):
repr_dict[k] = humanize_bitrate(v)
elif k == 'duration':
repr_dict[k] = humanize_duration(v)
elif 'sample_rate' in k:
repr_dict[k] = humanize_sample_rate(v)
elif not k.startswith('_'):
repr_dict[k] = v
return super().__repr__(repr_dict=repr_dict)