379 lines
14 KiB
Python
379 lines
14 KiB
Python
import pathlib
|
|
import uuid
|
|
import time
|
|
from typing import Optional
|
|
|
|
from pydub import AudioSegment
|
|
|
|
from typing import Dict, Any, Union
|
|
|
|
from .misc import Request
|
|
from .misc import ShazamUrl
|
|
from .schemas.artists import ArtistQuery
|
|
from .signature import DecodedMessage
|
|
from .enums import GenreMusic
|
|
from .converter import Converter, Geo
|
|
from .typehints import CountryCode
|
|
from .utils import ArtistQueryGenerator
|
|
from .utils import 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,
|
|
)
|