"""This module contains logic for audio metadata validation.""" import json from src import trafaret_schema from src.common import mediainfo_extractor from src.common.constants import errors from src.common.constants import transcoding_types from src.common.lambda_exceptions import LambdaStatusError from trafaret import DataError class InvalidAudioError(LambdaStatusError): """Exception class for audio validation errors.""" def __init__(self, error_dict): """Initialize instance.""" error_params = {'message': json.dumps(error_dict)} error_message = errors.ERROR_MESSAGES.get(errors.AUDIO_VALIDATION_ERROR_CODE, 'Unknown Error').format( **error_params) self.errors = error_dict Exception.__init__(self, error_message) class InvalidAudioChannels(InvalidAudioError): """Exception class for invalid audio channels.""" def get_lossless_flag(codec, mime_type): """Get lossless flag for m4a or flac mime types and codecs. Args: codec (str): Short codec name. mime_type (str): MIME type of input file object. Returns: bool: True if it's lossless. """ file_is_wav_pcm = ((mime_type in transcoding_types.WAV_MIME_TYPES) and ( codec == transcoding_types.PCM_CODEC_NAME)) file_is_flac = mime_type in transcoding_types.FLAC_MIME_TYPES file_is_m4a_alac = ((mime_type in transcoding_types.MP4_MIME_TYPES) and ( codec == transcoding_types.ALAC_CODEC_NAME)) return any([file_is_flac, file_is_m4a_alac, file_is_wav_pcm]) def get_channel_mode(channels): """Get channel mode for count of channels. Args: channels (int): count of channels. Returns: str: 'mono', 'stereo' or throw ValueError. Raises: ValueError: Invalid number of channels. """ if channels not in transcoding_types.AVAILABLE_CHANNELS_MODE: err_msg = errors.ERROR_MESSAGES[errors.AUDIO_CHANNEL_MODE_ERROR].format(channels=channels) # TODO: this is probably not needed and can be handled in the metadata validation raise InvalidAudioChannels(error_dict={errors.AUDIO_CHANNEL_MODE_ERROR: err_msg}) return transcoding_types.AVAILABLE_CHANNELS_MODE[channels] def validate_audio_asset(signed_url): """Validate input audio asset. Args: signed_url (str): Presigned S3 URL. Returns: dict: validated audio metadata. Raises: ValueError: audio metadata is invalid. """ try: metadata = mediainfo_extractor.get_audio_asset_media_info(signed_url) default_type = transcoding_types.CONTAINER_TO_MIME_MAPPING[metadata['container']] metadata['mime_type'] = (metadata['mime_type'] or default_type).lower() metadata['lossless'] = get_lossless_flag( codec=metadata['codec'], mime_type=metadata['mime_type']) metadata['channel_mode'] = get_channel_mode(metadata['channels']) # TODO: replace this trafaret schema validation with Marshmallow return trafaret_schema.AUDIO_METADATA_SCHEMA.check(metadata) except DataError as e: raise InvalidAudioError(e.as_dict())