""" Lambda function for media-data extraction You can find source code snippets and guide here: https://aws.amazon.com/blogs/compute/extracting-video-metadata-using-lambda-and-mediainfo/ """ import logging import subprocess from functools import reduce from common import audio_validator from common import audio_utils from constants import transcoding_types from cli_constants import CLI_FILTERS import boto3 SIGNED_URL_EXPIRATION = 300 # Number of seconds that the Signed URL is valid logger = logging.getLogger('boto3') logger.setLevel(logging.INFO) MEDIA_INFO = './mediainfo' NUMERAL = { 'playtime_seconds', 'channels', 'bitrate', 'sample_rate', 'resolution', 'bit_depth', 'bits_per_sample', 'file_size' } def lambda_handler(event, context): """Lambda handler""" # manual lambda call from dashboard manual_flag = event.get('manual') file_path = event.get('file_path') if manual_flag and file_path: logger.info('Manual file url input..') logger.info('Url: {}'.format(file_path)) result_dict = get_all_media_info(file_path) logger.info('result: {}'.format(result_dict)) return result_dict # Loop through records provided by S3 Event trigger for s3_record in event['Records']: logger.info('Working on new s3_record...') # Extract the Key and Bucket names for the asset uploaded to S3 key = s3_record['s3']['object']['key'] bucket = s3_record['s3']['bucket']['name'] logger.info('Bucket: {} \t Key: {}'.format(bucket, key)) # Generate a signed URL for the uploaded asset signed_url = get_signed_url(SIGNED_URL_EXPIRATION, bucket, key) logger.info('Signed URL: {}'.format(signed_url)) # Launch MediaInfo # Pass the signed URL of the uploaded asset to MediaInfo as an input # MediaInfo will extract the technical metadata from the asset logger.info('Start of metadata grabbing') result_dict = get_all_media_info(signed_url) logger.info('Output: {}'.format(result_dict)) return result_dict def get_all_media_info(file_path): """Get mediainfo about file Args: file_path (str): path to file or url. Returns: dict: dictionary with grabbed values """ info_list = [get_media_info_by_params(p, file_path) for p in CLI_FILTERS] result_dict = reduce((lambda x, y: dict(x, **y)), info_list) default_type = ( transcoding_types.CONTAINER_TO_MIME_MAPPING[result_dict['container']] ) result_dict['mime_type'] = ( result_dict['mime_type'] or default_type).lower() result_dict['lossless'] = audio_utils.get_lossless_flag( codec=result_dict['codec'], mime_type=result_dict['mime_type'] ) result_dict['channel_mode'] = audio_utils.get_channel_mode( result_dict['channels'] ) validation_info = '' try: validation_result = audio_validator.validate_audio_metadata( result_dict) except ValueError as e: validation_result = False validation_info = str(e) result = { 'valid': validation_result, 'meta_data': result_dict, 'validation_info': validation_info } return result def get_media_info_by_params(filter_pattern, file_path): """Get mediainfo output for file by filter pattern. Args: filter_pattern (str): String pattern for grabbing mediainfo results file_path (str): path to file or url. Returns: dict: dictionary with grabbed values """ media_info_binary = subprocess.check_output( [MEDIA_INFO, filter_pattern, file_path]) media_info = media_info_binary.decode('utf-8').replace('\n', '') # split info unit by key and value separated_key_value = lambda s: s.split(' ', 1) info_by_lines = map(separated_key_value, media_info.split(' * ')) # type casting unready_res = {l[0]: l[1] if len(l) > 1 else None for l in info_by_lines} result_dict = {k: prepare_output(k, v) for k, v in unready_res.items()} return result_dict def prepare_output(key, value): """Prepare output value from mediainfo. Args: key (str): result key for mediainfo cli output value (str): result value Returns: str or int: return casted value """ if key in NUMERAL: return int(value) else: return value.lower() def get_signed_url(expires_in, bucket, obj): """ Generate a signed URL :param expires_in: URL Expiration time in seconds :param bucket: :param obj: S3 Key name :return: Signed URL """ s3_cli = boto3.client('s3') presigned_url = s3_cli.generate_presigned_url( 'get_object', Params={'Bucket': bucket, 'Key': obj}, ExpiresIn=expires_in ) return presigned_url # local test if __name__ == '__main__': event = { "manual": True, "file_path": "https://s3-us-west-1.amazonaws.com/" "test-mediainfo-analyze/wav_file.wav" } lambda_handler(event, None)