from collections import defaultdict from functools import reduce from operator import or_ from typing import Iterable, List, Tuple from server.client import services from server.client.utils import prepare_list_arg from server.config import VENDOR_IS_SONY_DATA_TTL from server.constants import DSP from server.constants.distributors import IS_SONY_INCLUDE_DEFAULTS, IsSonyInclude @prepare_list_arg("track_id_list", required=False) async def tracks_is_sony( track_id_list: Iterable[str], country_code: str, track_list: Iterable[dict] = None, album_list: Iterable[dict] = None, include: Iterable[str] = None, # IsSonyInclude are available options ) -> Tuple[List[str], dict]: """Apply is-sony check for tracks and a specific country_code. tracks could be specified by id list (track_id_list) or by vendor-response objects list (track_list) both. album_list is an optional vendor-response objects list for albums to avid additional requests. """ include, extra_data = include or [], {} if not track_list: if not track_id_list: return [], {k: v() for k, v in IS_SONY_INCLUDE_DEFAULTS.items() if k in include} track_list = await services.vendor.get_tracks( DSP.SPOTIFY, ids=list(track_id_list), market=country_code, data_ttl=VENDOR_IS_SONY_DATA_TTL ) album_id_to_track_ids = defaultdict(set) for track in track_list: album_id = track.get("album", {}).get("id") if album_id: album_id_to_track_ids[album_id].add(track["id"]) album_includes = [*include] track_upc_mapping_included, album_upc_mapping_included = ( IsSonyInclude.TRACK_UPC_MAPPING in include, IsSonyInclude.ALBUM_UPC_MAPPING in include, ) if track_upc_mapping_included and not album_upc_mapping_included: # we map a track through an album album_includes.append(IsSonyInclude.ALBUM_UPC_MAPPING) sony_album_ids, album_extra_data = await albums_is_sony( album_id_list=list(album_id_to_track_ids.keys()), country_code=country_code, album_list=album_list, include=album_includes, ) sony_track_ids = list(reduce(or_, [album_id_to_track_ids[album_id] for album_id in sony_album_ids], set())) for k, v in ((IsSonyInclude.TRACK_LIST, track_list), (IsSonyInclude.ALBUM_LIST, album_list)): if k in include: extra_data[k] = v if album_upc_mapping_included: extra_data[IsSonyInclude.ALBUM_UPC_MAPPING] = album_extra_data[IsSonyInclude.ALBUM_UPC_MAPPING] if track_upc_mapping_included: extra_data[IsSonyInclude.TRACK_UPC_MAPPING] = { track_id: album_extra_data[IsSonyInclude.ALBUM_UPC_MAPPING][album_id] for album_id in sony_album_ids for track_id in album_id_to_track_ids[album_id] } return sony_track_ids, extra_data @prepare_list_arg("album_id_list", required=False) async def albums_is_sony( album_id_list: Iterable[str], country_code: str, album_list: Iterable[dict] = None, include: Iterable[str] = None, # IsSonyInclude.ALBUM_LIST, IsSonyInclude.ALBUM_UPC_MAPPING are available options ) -> Tuple[List[str], dict]: """Apply is-sony check for albums and a specific country_code. albums could be specified by id list (album_id_list) or by vendor-response objects list (album_list). """ include, extra_data = include or [], {} if not album_list: if not album_id_list: return [], {k: v() for k, v in IS_SONY_INCLUDE_DEFAULTS.items() if k in include} album_list = await services.vendor.get_albums( DSP.SPOTIFY, ids=list(album_id_list), market=country_code, data_ttl=VENDOR_IS_SONY_DATA_TTL ) upc_to_album_id = {a.get("external_ids", {}).get("upc"): a["id"] for a in album_list} sony_upc_list = await services.apollo.get_albums_is_sony(upc=list(upc_to_album_id.keys()), market=country_code) sony_album_ids = [upc_to_album_id[upc] for upc in sony_upc_list] if IsSonyInclude.ALBUM_LIST in include: extra_data[IsSonyInclude.ALBUM_LIST] = album_list if IsSonyInclude.ALBUM_UPC_MAPPING in include: extra_data[IsSonyInclude.ALBUM_UPC_MAPPING] = {upc_to_album_id[upc]: upc for upc in sony_upc_list} return sony_album_ids, extra_data