"""Promo Player logic.""" import datetime from functools import lru_cache from flask import current_app from flask_executor_pde.executor import Executor from oto import response from owsrequest import request as requests from promo_player import config from promo_player.constants import error from promo_player.constants import promo_player as promo_player_constants from promo_player.constants import s3 as s3_constants from promo_player.constants import service_name from promo_player.logic import asset from promo_player.logic import feature from promo_player.logic import product from promo_player.logic import s3 from promo_player.logic import track @lru_cache(600) def get_for_frontend(code, correlation_id): """Get the data required to render a promo player, potentially from cache. Args: code (str): the unique code of the promo player. correlation_id (str): the correlation ID. Returns: response.Response: containing the data. """ return _get_for_frontend(code, correlation_id) def _get_for_frontend(code, correlation_id): """Get the data required to render a promo player. Args: code (str): the unique code of the promo player. correlation_id (str): the correlation ID. Returns: response.Response: containing the data. """ promo_player_response = get_and_validate(code) if not promo_player_response: return promo_player_response promo_player = promo_player_response.message['promo_player'] product_details = promo_player_response.message['product_details'] product_id = promo_player.get('product_id', None) promo_player_tracks = promo_player.get('tracks', []) product_name = product_details['product_name'] vendor_id = promo_player['vendor_id'] subaccount_id = product_details['subaccount_id'] if feature.is_promo_player_product_delivered_version_enabled_for_label( vendor_id, subaccount_id, ) and product_details['delivered_version'] is not None: product_name += ' (' + product_details['delivered_version'] + ')' request_list = [ (track.get_tracks, (product_id, promo_player_tracks, correlation_id)), (asset.get_product_assets, (product_id, vendor_id, subaccount_id, correlation_id)), (asset.get_product_cover_url, (product_id, correlation_id)), (s3.get_shared_static_assets_version, ()), (s3.get_player_static_assets_version, ()) ] with Executor(current_app) as executor: response_list = list(executor.map_multiple(request_list)) for response_item in response_list: if not response_item: return response_item tracks = response_list[0].message['items'] assets = response_list[1].message['assets'] cover_url = response_list[2].message shared_assets_version = response_list[3].message player_assets_version = response_list[4].message shared_assets_url = '{0}/{1}/{2}'.format( config.CDN_URL, s3_constants.SHARED_PREFIX, shared_assets_version) player_assets_url = '{0}/{1}/{2}'.format( config.CDN_URL, s3_constants.PLAYER_PREFIX, player_assets_version) result = { 'skin': promo_player['skin'], 'product_name': product_name, 'artist_name': product.get_artist_name(product_details), 'artwork_url': cover_url, 'shared_assets_url': shared_assets_url, 'player_assets_url': player_assets_url, 'tracks': track.map_tracks(tracks, assets) } return response.Response(result) def get_track_manifest_url(code, track_id, referrer=None): """Get the HLS manifest URL for a promo player's track. Args: code (str): the unique code of the promo player. track_id (int): the track ID. referrer (str): the request referrer Returns: response.Response: containing the HLS manifest URL. """ promo_player_response = get_and_validate(code) if not promo_player_response: return promo_player_response track_details_response = track.get_by_track_id(track_id) if not track_details_response: return track_details_response promo_player = promo_player_response.message['promo_player'] track_details = track_details_response.message if track_details['product_id'] != promo_player['product_id']: return response.create_not_found_response() return asset.get_track_manifest_url(track_id, referrer=referrer) def get_and_validate(code): """Get a promo player and check if it's active and expired. Args: code (str): the unique code of the promo player. Returns: response.Response: containing the promo player dict. """ promo_player_response = get_by_code(code) if not promo_player_response: return promo_player_response promo_player = promo_player_response.message if not promo_player['active']: return response.create_not_found_response() product_id = promo_player.get('product_id', None) product_details = {} if product_id: product_response = product.get_by_product_id(product_id) if not product_response: return product_response product_details = product_response.message release_date = product_details.get('release_date', None) if has_expired( promo_player['expiration_mode'], release_date, promo_player['expiry_time']): deactivate(promo_player['promo_player_id']) return response.create_not_found_response() return response.Response({ 'promo_player': promo_player, 'product_details': product_details }) def get_by_code(code): """Get a promo player by code from ows-promo-player. Args: code (str): the unique code of the promo player. Returns: response.Response: containing the promo player dict. """ path = '/player/code/{code}'.format(code=code) result = requests.get(service_name.OWS_PROMO_PLAYER, path) if result.status_code != 200: return response.create_error_response( status=result.status_code, code=error.ERROR_CODE_OWS_PROMO_PLAYER_REQUEST, message=result.json()) return response.Response(result.json()) def has_expired(expiration_mode, release_date, expiry_time): """Check if a promo player has expired. Args: expiration_mode (str): the expiration mode of the promo player release_date (str): the release date of the product (YYYY-MM-dd) expiry_time (float): the promo player expiry timestamp Returns: bool: Whether or not the promo player has expired """ if expiration_mode == promo_player_constants.EXPIRATION_MODE_RELEASE: if release_date is None: return False parsed_release_date = datetime.datetime.strptime( release_date, '%Y-%m-%d') return datetime.datetime.utcnow() >= parsed_release_date if expiration_mode == promo_player_constants.EXPIRATION_MODE_TIME: if expiry_time is None: return False return datetime.datetime.utcnow().timestamp() >= expiry_time return False def deactivate(promo_player_id): """Deactivate a promo player by calling ows-promo-player. Args: promo_player_id (int): the promo player ID. """ path = '/player/{promo_player_id}'.format(promo_player_id=promo_player_id) data = {'active': False} requests.put(service_name.OWS_PROMO_PLAYER, path, json=data)