Source code for audio_metadata.formats.vorbiscomments

# https://xiph.org/vorbis/doc/v-comment.html

__all__ = [
	'VorbisComment',
	'VorbisComments',
]

import struct
from collections import defaultdict

from attr import (
	attrib,
	attrs,
)
from tbm_utils import datareader

from ..exceptions import (
	FormatError,
	TagError,
)
from ..models import (
	Tag,
	Tags,
)


[docs]@attrs( repr=False, kw_only=True, ) class VorbisComment(Tag): name = attrib(converter=lambda n: n.lower()) @staticmethod def _validate_name(name): return all( ( char >= ' ' and char <= '}' and char != '=' ) for char in name ) @datareader @classmethod def parse(cls, data): length = struct.unpack('I', data.read(4))[0] comment = data.read(length).decode('utf-8', 'replace') if '=' not in comment: raise FormatError("Vorbis comment must contain an ``=``.") name, value = comment.split('=', 1) if not cls._validate_name(name): raise TagError(f"Invalid character in Vorbis comment name: ``{name}``.") return cls( name=name, value=value, )
# TODO: Number frames.
[docs]class VorbisComments(Tags): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for key in self.keys(): if not VorbisComment._validate_name(key): raise TagError(f"Invalid character in Vorbis comment name: ``{key}``.") @datareader @classmethod def parse(cls, data): vendor_length = struct.unpack('I', data.read(4))[0] vendor = data.read(vendor_length).decode('utf-8', 'replace') num_comments = struct.unpack('I', data.read(4))[0] fields = defaultdict(list) for _ in range(num_comments): comment = VorbisComment.parse(data) fields[comment.name].append(comment.value) return cls( fields, _vendor=vendor, )