import pathlib import time import uuid from typing import Any, Dict, Optional, Union from pydub import AudioSegment from .converter import Converter, Geo from .enums import GenreMusic from .misc import Request, ShazamUrl from .schemas.artists import ArtistQuery from .signature import DecodedMessage from .typehints import CountryCode from .utils import ArtistQueryGenerator, get_song class Shazam(Converter, Geo, Request): """Is asynchronous framework for reverse engineered Shazam API written in Python 3.7 with asyncio and aiohttp.""" def __init__(self, language: str = "en-US", endpoint_country: str = "GB"): super().__init__(language=language) self.language = language self.endpoint_country = endpoint_country async def top_world_tracks( self, limit: int = 200, offset: int = 0 ) -> Dict[str, Any]: """ Search top world tracks :param limit: Determines how many songs the maximum can be in the request. Example: If 5 is specified, the query will return no more than 5 songs. :param offset: A parameter that determines with which song to display the request. The default is 0. If you want to skip the first few songs, set this parameter to your own. :return: dict tracks """ return await self.request( "GET", ShazamUrl.TOP_TRACKS_WORLD.format( language=self.language, endpoint_country=self.endpoint_country, limit=limit, offset=offset, ), headers=self.headers(), ) async def artist_about( self, artist_id: int, query: Optional[ArtistQuery] = None ) -> Dict[str, Any]: """ Retrieving information from an artist profile :param artist_id: Artist number. Example (203347991) :param query: Foo https://www.shazam.com/artist/203347991/ :return: dict about artist """ if query: pg = ArtistQueryGenerator(source=query) params_dict = pg.params() else: params_dict = {} return await self.request( "GET", ShazamUrl.SEARCH_ARTIST_V2.format( endpoint_country=self.endpoint_country, artist_id=artist_id, ), params=params_dict, headers=self.headers(), ) async def track_about(self, track_id: int) -> Dict[str, Any]: """ Get track information :param track_id: Track number. Example: (549952578) https://www.shazam.com/track/549952578/ :return: dict about track """ return await self.request( "GET", ShazamUrl.ABOUT_TRACK.format( language=self.language, endpoint_country=self.endpoint_country, track_id=track_id, ), headers=self.headers(), ) async def top_country_tracks( self, country_code: Union[CountryCode, str], limit: int = 200, offset: int = 0, ) -> Dict[str, Any]: """ Get the best tracks by country code https://www.shazam.com/charts/discovery/netherlands :param country_code: ISO 3166-3 alpha-2 code. Example: RU,NL,UA :param limit: Determines how many songs the maximum can be in the request. Example: If 5 is specified, the query will return no more than 5 songs. :param offset: A parameter that determines with which song to display the request. The default is 0. If you want to skip the first few songs, set this parameter to your own. :return: dict songs """ return await self.request( "GET", ShazamUrl.TOP_TRACKS_COUNTRY.format( language=self.language, endpoint_country=self.endpoint_country, country_code=country_code, limit=limit, offset=offset, ), headers=self.headers(), ) async def top_city_tracks( self, country_code: Union[CountryCode, str], city_name: str, limit: int = 200, offset: int = 0, ) -> Dict[str, Any]: """ Retrieving information from an artist profile https://www.shazam.com/charts/top-50/russia/moscow :param country_code: ISO 3166-3 alpha-2 code. Example: RU,NL,UA :param city_name: City name from https://github.com/dotX12/dotX12/blob/main/city.json Example: Budapest, Moscow :param limit: Determines how many songs the maximum can be in the request. Example: If 5 is specified, the query will return no more than 5 songs. :param offset: A parameter that determines with which song to display the request. The default is 0. If you want to skip the first few songs, set this parameter to your own. :return: dict songs """ city_id = await self.city_id_from(country=country_code, city=city_name) return await self.request( "GET", ShazamUrl.TOP_TRACKS_CITY.format( language=self.language, endpoint_country=self.endpoint_country, limit=limit, offset=offset, city_id=city_id, ), headers=self.headers(), ) async def top_world_genre_tracks( self, genre: Union[GenreMusic, int], limit: int = 100, offset: int = 0, ) -> Dict[str, Any]: """ Get world tracks by certain genre https://www.shazam.com/charts/genre/world/rock :param genre: Genre name or ID: POP = 1, HIP_HOP_RAP = 2, DANCE = 3, ELECTRONIC = 4, RNB_SOUL = 5, ALTERNATIVE = 6, ROCK = 7 LATIN = 8, FILM_TV_STAGE = 9, COUNTRY = 10, AFRO_BEATS = 11, WORLDWIDE = 12, REGGAE_DANCE_HALL = 13 HOUSE = 14, K_POP = 15, FRENCH_POP = 16, SINGER_SONGWRITER = 17, REGIONAL_MEXICANO = 18 :param limit: Determines how many songs the maximum can be in the request. Example: If 5 is specified, the query will return no more than 5 songs. :param offset: A parameter that determines with which song to display the request. The default is 0. If you want to skip the first few songs, set this parameter to your own. :return: dict songs """ return await self.request( "GET", ShazamUrl.GENRE_WORLD.format( language=self.language, endpoint_country=self.endpoint_country, limit=limit, offset=offset, genre=genre, ), headers=self.headers(), ) async def top_country_genre_tracks( self, country_code: str, genre: Union[GenreMusic, int], limit: int = 200, offset: int = 0, ) -> Dict[str, Any]: """ The best tracks by a genre in the country https://www.shazam.com/charts/genre/spain/hip-hop-rap :param country_code: ISO 3166-3 alpha-2 code. Example: RU,NL,UA :param genre: Genre name or ID: POP = 1, HIP_HOP_RAP = 2, DANCE = 3, ELECTRONIC = 4, RNB_SOUL = 5, ALTERNATIVE = 6, ROCK = 7 LATIN = 8, FILM_TV_STAGE = 9, COUNTRY = 10, AFRO_BEATS = 11, WORLDWIDE = 12, REGGAE_DANCE_HALL = 13 HOUSE = 14, K_POP = 15, FRENCH_POP = 16, SINGER_SONGWRITER = 17, REGIONAL_MEXICANO = 18 :param limit: Determines how many songs the maximum can be in the request. Example: If 5 is specified, the query will return no more than 5 songs :param offset: A parameter that determines with which song to display the request. The default is 0. If you want to skip the first few songs, set this parameter to your own. :return: dict songs """ return await self.request( "GET", ShazamUrl.GENRE_COUNTRY.format( language=self.language, endpoint_country=self.endpoint_country, limit=limit, offset=offset, country=country_code, genre=genre, ), headers=self.headers(), ) async def related_tracks( self, track_id: int, limit: int = 20, offset: int = 0, ) -> Dict[str, Any]: """ Similar songs based song id https://www.shazam.com/track/546891609/2-phu%CC%81t-ho%CC%9Bn-kaiz-remix :param track_id: Track number. Example: (549952578) https://www.shazam.com/track/549952578/ :param limit: Determines how many songs the maximum can be in the request. Example: If 5 is specified, the query will return no more than 5 songs :param offset: A parameter that determines with which song to display the request. The default is 0. If you want to skip the first few songs, set this parameter to your own. :return: dict tracks """ return await self.request( "GET", ShazamUrl.RELATED_SONGS.format( language=self.language, endpoint_country=self.endpoint_country, limit=limit, offset=offset, track_id=track_id, ), headers=self.headers(), ) async def search_artist( self, query: str, limit: int = 10, offset: int = 0, ) -> Dict[str, Any]: """ Search all artists by prefix or fullname :param query: Artist name or search prefix :param limit: Determines how many artists the maximum can be in the request. Example: If 5 is specified, the query will return no more than 5 artists. :param offset: A parameter that determines with which song to display the request. The default is 0. If you want to skip the first few songs, set this parameter to your own. :return: dict artists """ return await self.request( "GET", ShazamUrl.SEARCH_ARTIST.format( language=self.language, endpoint_country=self.endpoint_country, limit=limit, offset=offset, query=query, ), headers=self.headers(), ) async def search_track( self, query: str, limit: int = 10, offset: int = 0 ) -> Dict[str, Any]: """ Search all tracks by prefix :param query: Track full title or prefix title :param limit: Determines how many songs the maximum can be in the request. Example: If 5 is specified, the query will return no more than 5 songs. :param offset: A parameter that determines with which song to display the request. The default is 0. If you want to skip the first few songs, set this parameter to your own. :return: dict songs """ return await self.request( "GET", ShazamUrl.SEARCH_MUSIC.format( language=self.language, endpoint_country=self.endpoint_country, limit=limit, offset=offset, query=query, ), headers=self.headers(), ) async def listening_counter(self, track_id: int) -> Dict[str, Any]: """ Returns the total track listener counter. :param track_id: Track number. Example: (559284007) https://www.shazam.com/track/559284007/rampampam :return: The data dictionary that contains the listen counter. """ return await self.request( "GET", ShazamUrl.LISTENING_COUNTER.format( track_id, language=self.language, ), headers=self.headers(), ) async def get_youtube_data(self, link: str) -> Dict[str, Any]: return await self.request("GET", link, headers=self.headers()) async def recognize_song( self, data: Union[str, pathlib.Path, bytes, bytearray, AudioSegment] ) -> Dict[str, Any]: """ Creating a song signature based on a file and searching for this signature in the shazam database. :param data: Path to song file or bytes :return: Dictionary with information about the found song """ song = await get_song(data=data) audio = self.normalize_audio_data(song) signature_generator = self.create_signature_generator(audio) signature = signature_generator.get_next_signature() if len(signature_generator.input_pending_processing) < 128: return {"matches": []} while not signature: signature = signature_generator.get_next_signature() results = await self.send_recognize_request(signature) return results async def send_recognize_request(self, sig: DecodedMessage) -> Dict[str, Any]: data = Converter.data_search( Request.TIME_ZONE, sig.encode_to_uri(), int(sig.number_samples / sig.sample_rate_hz * 1000), int(time.time() * 1000), ) return await self.request( "POST", ShazamUrl.SEARCH_FROM_FILE.format( language=self.language, endpoint_country=self.endpoint_country, uuid_1=str(uuid.uuid4()).upper(), uuid_2=str(uuid.uuid4()).upper(), ), headers=self.headers(), json=data, )