"""Interface to the ows-assets microservice. Handles track file asset """ from flask import g from oto import response from oto import status as response_code from owsrequest import request from backend.constants import error from backend.constants import header from backend.constants import services from backend.constants.error import OWS_ASSETS_ERROR_CODE from backend.exceptions import RequestError from backend.utils import api as api_utils from backend.utils import model_utils def copy_assets(from_tuid, to_tuid, orchard_user_id, correlation_id=None): """Copy assets from track with to track with . Args: from_tuid (int): The source track primary key. to_tuid (int): The destination track primary key. orchard_user_id (str): Orchard user id. correlation_id (str): Override correlation_id used for logging. Returns: response.Response """ endpoint = '/track/{0}/copy/{1}'.format(from_tuid, to_tuid) request_body = {header.ORCHARD_USER_ID: orchard_user_id} options = {} if correlation_id: options['correlation_id'] = correlation_id assets_response = request.post( services.OWS_ASSETS, endpoint, json=request_body, **options) if assets_response.status_code == response_code.OK: return response.Response(message=assets_response.json()) return model_utils.process_error_response( assets_response, code=error.OWS_ASSETS_ERROR_CODE) def delete_track_assets(tuid): """Delete track assets by tuid. Args: tuid (int): The track primary key. Returns: response.Response """ endpoint = '/track/{track_id}'.format(track_id=tuid) assets_response = request.delete(services.OWS_ASSETS, endpoint) if assets_response.status_code == response_code.OK: return response.Response(message=assets_response.json()) return model_utils.process_error_response( assets_response, code=error.OWS_ASSETS_ERROR_CODE) def bulk_delete_track_assets(tuids): """Delete assets of multiple tracks. Args: tuids (list): List of track primary keys Returns: response.Response """ responses = { tuid: delete_track_assets(tuid) for tuid in tuids} if not all(responses.values()): error_msg = 'Failed to delete assets for tracks with tuids: {}' error_responses = { tuid: assets_response for tuid, assets_response in responses.items() if not assets_response} g.log.error(error_msg.format(list(error_responses))) return response.Response(responses) def get_product_assets_v2(product_id): """Get all product v2 assets from ows-assets. Currently used for validation when user clicks 'Validate track' in Track Builder. Track information is valid only if audio file was uploaded and saved successfully. Validation request from frontend gets here through ows-product-digital. Args: product_id (int): The product primary key. Returns: response.Response: items with pagination """ url = '/v2/asset/product/{}'.format(product_id) assets_response = request.get(service_name=services.OWS_ASSETS, path=url) if assets_response.status_code == response_code.NOT_FOUND: return response.create_not_found_response() elif assets_response.status_code != response_code.OK: return model_utils.process_error_response( assets_response, code=error.OWS_ASSETS_ERROR_CODE) assets_payload = assets_response.json() assets = _format_asset_dicts_v2(assets_payload) return api_utils.create_get_list_response(assets) def _format_asset_dicts_v2(assets_payload): """Convert response from ows-assets to convenient format. Only assets with type == 'audio' will get into result list. Args: assets_payload (dict): data from GET /asset/product/ Returns: list: formatted assets """ assets = assets_payload[services.ASSETS] result = [] for asset in assets: if not asset[services.TRACK_UNIQUE_ID]: continue formatted_asset = _get_formatted_asset(asset) result.append(formatted_asset) return result def _get_formatted_asset(asset): duration = None if services.STREAM in asset: stream = asset[services.STREAM] if isinstance(stream, dict): duration = stream.get(services.DURATION, None) if duration is not None: # Convert to milliseconds duration = int(duration * 1000) return { services.TRACK_UNIQUE_ID: asset[services.TRACK_UNIQUE_ID], services.UPLOAD_STATUS: asset[services.UPLOAD_STATUS], services.DURATION: duration, services.ASSET_UPLOAD_TYPE: asset.get(services.ASSET_UPLOAD_TYPE), } def get_match_audio_results(product_id): """Get Audio Matches. Retrieve results from a process that matches audio from asset_final records with publicly released audio. """ match_audio_response = request.get( service_name=services.OWS_ASSETS, path=f'/v2/asset/product/{product_id}/match-audio' ) response_status = match_audio_response.status_code if response_status == 404: return [] if response_status != 200: raise RequestError( f'Failed to get match audio results for product_id: {product_id}. ' f'Error: {match_audio_response.text}', error_code=OWS_ASSETS_ERROR_CODE, http_status=response_status, ) return match_audio_response.json()['items'] def get_ai_generated_audio_results(product_id): """Get Suspected AI Generated Audio Results. Retrieve results from a process that identifies audio as AI-generated. """ ai_audio_response = request.get( service_name=services.OWS_ASSETS, path=f'/v2/asset/product/{product_id}/ai-generated-audio' ) response_status = ai_audio_response.status_code if response_status == 404: return [] if response_status != 200: raise RequestError( f'Failed to get suspected AI generated audio results for product_id: {product_id}. ' f'Error: {ai_audio_response.text}', error_code=OWS_ASSETS_ERROR_CODE, http_status=response_status, ) return ai_audio_response.json()['items']