"""Poll video workflow status.""" import re import uuid from ddex_ingester_common.constants.ddex_providers import SME from ddex_ingester_common.constants.status import (IN_CONTENT, TRANSFER_TO_CONTENT) from ddex_ingester_common.lambda_exceptions import (PollVideoFatalException, VideoException) from ddex_ingester_common.logging import utils as logging_utils from ddex_ingester_common.schemas.state_machine_schema import \ StateMachineSchema import config from config import graphql_gateway from constants.queries import POLL_VIDEO_WORKFLOW_STATUS, SAVE_VIDEO_SINGLE from constants.thumbnail import DEFAULT_THUMBNAIL_TIME, THUMBNAIL_FORMAT from constants.workflow_status import (CANCELLED, COMPLETE, ERROR, PROCESSING, SUBMITTED) logger = logging_utils.get_logger(config.app_logger) def handler(event: dict, context: dict): """Lambda entrypoint.""" context = StateMachineSchema().load(event.get('context')) correlation_id = context.correlation_id or str(uuid.uuid4()) context.correlation_id = correlation_id logging_utils.update_logger_correlation_id(logger, correlation_id) logging_utils.update_logger_with_message_ids( logger, context.message_id, context.message_thread_id, context.execution_name ) graphql_gateway.set_headers( { 'Orchard-User-Id': config.OA_USER, 'Correlation-Id': correlation_id, } ) # If we're in an unsupported workflow, skip the checks. if context.product.status not in [IN_CONTENT, TRANSFER_TO_CONTENT]: # Call ows video for status of video ingest workflow workflow_result = get_workflow_status( context.video.workflow_id, context.ddex_provider ) # save thumbnail based on the returned thumbnails from the workflow job save_thumbnail_path(context.product.product_id, workflow_result) return StateMachineSchema().dump(context) def get_workflow_status(workflow_id: int, ddex_provider: str) -> dict: """Get video workflow status. Args: workflow_id (int): Workflow ID. Returns: dict """ result = graphql_gateway.execute( POLL_VIDEO_WORKFLOW_STATUS, {'workflow_id': workflow_id} )['data']['getVideoWorkflowJobDetails'] for item in result['items']: if item['status'] in [CANCELLED, ERROR]: error_messages = [] for output in item['outputs']: if 'error_' in output['code']: error_messages.append(output['message']) resolution_error = 'Cannot convert video to a standard resolution.' if ddex_provider != SME and resolution_error in error_messages: raise TriggerVideoResolutionFixException() raise PollVideoFatalException( f'{item["type"]} failed. Errors: {"|".join(error_messages)}') elif item['status'] in [PROCESSING, SUBMITTED]: raise VideoException('Video ingest workflow still processing.') elif item['status'] == COMPLETE: continue return result def save_thumbnail_path(product_id: int, workflow_result: dict): """Save thumbnail path onto product from workflow result. Args: product_id (int): Product ID workflow_result (dict): Workflow result from GraphQA """ logger.info( f'Getting thumbnail out of workflow result: {workflow_result}' ) thumbnails = workflow_result.get('thumbnails') if not thumbnails: msg = ('Video ingest workflow completed ' 'but no thumbnails have generated yet.') logger.warning(msg) raise VideoException(msg) try: thumbnail = re.sub( THUMBNAIL_FORMAT, '', thumbnails[DEFAULT_THUMBNAIL_TIME] ) except IndexError: logger.warning('Unable to extract thumbnail at default time') thumbnail = re.sub(THUMBNAIL_FORMAT, '', thumbnails[-1]) graphql_gateway.execute( SAVE_VIDEO_SINGLE, { 'data': { 'update': { 'productId': str(product_id), 'thumbnailPath': thumbnail } } } ) class TriggerVideoResolutionFixException(Exception): """Exception detected by terraform to trigger video resolution fix."""