import logging from aiohttp import web from aiohttp_apispec import docs, response_schema, json_schema, querystring_schema from typing import Any, Dict from server.artist.decorators import Helper from server.track.helpers.discovery import TrackDiscoveryHelper from server.track.helpers.search_by_name import TrackSearchByNameHelper from server.track.constants import EXTERNAL_LINKS_TEMPLATES from server.track.repositories import TrackRepo from server.track.spotify.repositories import SpotifyRepo as TrackSpotifyRepo from server.track.soundcloud.repositories import SoundCloudRepo as TrackSoundCloudRepo from server.track.youtube.repositories import YoutubeRepo from server.track.tiktok.repositories import TiktokRepo from server.artist.schemas import ( SpotifyTracksResponseSchema, VideosTiktokResponseSchema, VideosYoutubeResponseSchema, TrackExternalLinksResponseSchemaV4, SoundCloudTracksResponseSchema, ) from server.track.schemas import ( TrackSearchByNameRequestSchema, TrackSearchByNameResponseSchema, TrackDiscoveryRequestSchema, TrackDiscoveryResponseSchema, TrackDiscoveryCountRequestSchema, TrackDiscoveryCountResponseSchema, TrackDiscoveryResultResponseSchema, ) from server.utils import ( group_external_links_v4, order_header_external_links, make_contry_obj_from_results, make_genres_obj_from_results, ) log = logging.getLogger(__name__) @docs(tags=["track", "discovery"], description="Track discovery") @json_schema(TrackDiscoveryRequestSchema) @response_schema(TrackDiscoveryResponseSchema) async def discovery(request: web.Request) -> web.Response: def form_response(results: list) -> web.Response: for result in results: result["country"] = make_contry_obj_from_results(result) result["genres"] = make_genres_obj_from_results(result) return web.json_response(TrackDiscoveryResponseSchema().dump(results, many=True)) if not request.body_exists: return form_response([]) params: dict = await request.json() params = TrackDiscoveryRequestSchema().load(params) results = await TrackDiscoveryHelper.search(params) return form_response(results) @docs(tags=["track", "search_by_name"], description="Track search by name") @querystring_schema(TrackSearchByNameRequestSchema) @response_schema(TrackSearchByNameResponseSchema) async def search_by_name(request: web.Request) -> web.Response: def form_response(results: list) -> web.Response: return web.json_response(TrackSearchByNameResponseSchema().dump(results, many=True)) params: dict = TrackSearchByNameRequestSchema().load(request.rel_url.query) if not params: return form_response([]) results = await TrackSearchByNameHelper.search(params) return form_response(results) @docs(tags=["track", "discovery", "count"], description="Track discovery total count") @json_schema(TrackDiscoveryCountRequestSchema) @response_schema(TrackDiscoveryCountResponseSchema) async def discovery_total_count(request: web.Request) -> web.Response: def form_response(total_count: int) -> web.Response: resp = {"total_count": total_count} return web.json_response(TrackDiscoveryCountResponseSchema().dump(resp)) if not request.body_exists: return form_response(total_count=0) params: dict = await request.json() params = TrackDiscoveryCountRequestSchema().load(params) result = await TrackDiscoveryHelper.count(params) return form_response(total_count=result) # TODO: cover with tests @docs(tags=["track", "discovery", "result"], description="Track discovery result") @response_schema(TrackDiscoveryResultResponseSchema) @Helper.args_decompose() async def discovery_track_result(request: web.Request, id: int) -> web.Response: def form_response(result: Dict[str, Any]) -> web.Response: result["country"] = make_contry_obj_from_results(result) result["genres"] = make_genres_obj_from_results(result) return web.json_response(TrackDiscoveryResultResponseSchema().dump(result)) result = await TrackDiscoveryHelper.get(str(id)) if not result: return web.json_response(status=web.HTTPNotFound.status_code) return form_response(result) @docs(tags=["track", "tracks", "spotify"], description="Related track's spotify tracks") @response_schema(SpotifyTracksResponseSchema) @Helper.args_decompose() async def tracks_spotify(request: web.Request, id: str) -> web.Response: results: list = await TrackSpotifyRepo.get_tracks(str(id)) # it's only chart-metric ID's are integer return web.json_response(SpotifyTracksResponseSchema().dump(results, many=True)) @docs(tags=["tiktok", "track", "videos"], description="Tiktok track videos") @response_schema(VideosTiktokResponseSchema) @Helper.args_decompose() async def track_videos_tiktok(request: web.Request, id: int) -> web.Response: results = await TiktokRepo.get_track_videos(str(id)) # it's only chart-metric ID's are integer return web.json_response(VideosTiktokResponseSchema().dump(results, many=True)) @docs(tags=["tiktok", "track", "videos"], description="Youtube track videos") @response_schema(VideosYoutubeResponseSchema) @Helper.args_decompose() async def track_videos_youtube(request: web.Request, id: int) -> web.Response: results = await YoutubeRepo.get_track_videos(str(id)) # it's only chart-metric ID's are integer return web.json_response(VideosYoutubeResponseSchema().dump(results, many=True)) @docs(tags=["track", "tracks", "soundcloud"], description="Related track's soundcloud tracks") @response_schema(SoundCloudTracksResponseSchema) @Helper.args_decompose() async def tracks_soundcloud(request: web.Request, id: str) -> web.Response: results: list = await TrackSoundCloudRepo.get_tracks(str(id)) # it's only chart-metric ID's are integer return web.json_response(SoundCloudTracksResponseSchema().dump(results, many=True)) # ================================= v4 views ================================= # TODO: cover with tests @docs(tags=["track", "external", "links"], description="Track external links") @response_schema(TrackExternalLinksResponseSchemaV4) @Helper.args_decompose() async def track_external_links_v4(request: web.Request, id: int) -> web.Response: results = await TrackRepo.get_external_links(str(id)) # it's only chart-metric ID's are integer links = [] for result in results: dsp = result["dsp"] template = EXTERNAL_LINKS_TEMPLATES.get(dsp) if not template: log.warning(f"Unknown dsp '{dsp}'") continue links.append({"name": dsp, "url": template.format(id=result["id"])}) grouped_links = group_external_links_v4({}, links, default_group="header") ordered_links = order_header_external_links(grouped_links) return web.json_response(TrackExternalLinksResponseSchemaV4().dump(ordered_links))