Source code for discordSuperUtils.Spotify
import asyncio
from typing import (
Optional,
List,
Dict,
Union
)
import spotipy
from spotipy import SpotifyClientCredentials
FIELD = "items.track.name,items.track.artists(name),total"
[docs]class SpotifyClient:
def __init__(self, client_id: str, client_secret: str, loop=None):
self.sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials(client_id=client_id,
client_secret=client_secret))
self.loop = asyncio.get_event_loop() if loop is None else loop
[docs] @staticmethod
def get_type(url: str) -> Optional[str]:
"""
This function receives a url and returns the type of the URL.
Return examples: playlist, user, album, etc.
:param url:
:return:
"""
url = url.replace("https://open.spotify.com/", "")
return url.split("/")[-2]
[docs] @staticmethod
def make_title(song: Dict[str, dict]) -> str:
"""
This function receives a song and creates a title that can be used for youtube_dl searching.
Return example: Never Gonna Give You Up by Rick Astley
:param song:
:return:
"""
artists = " ".join([artist['name'] for artist in song.get('artists')])
return f"{song['name']} by {artists}"
[docs] async def fetch_playlist_data(self, url: str, offset: int) -> Dict[str, Union[int, list]]:
"""
This function receives a URL and an offset and returns 100 tracks from that offset
Example: Offset: 50, the URL has 160 tracks, returns tracks from 50-150 (limit is 100).
:param url:
:param offset:
:return:
"""
return await self.loop.run_in_executor(None, lambda: self.sp.playlist_items(playlist_id=url,
fields=FIELD,
offset=offset))
[docs] async def fetch_full_playlist(self, url: str) -> List[Dict[str, dict]]:
"""
This function receives a url and returns all the tracks in that URL.
:param url:
:return:
"""
initial_request = await self.fetch_playlist_data(url, 0)
total_tracks = initial_request.get('total')
requests = list(
await asyncio.gather(*(self.fetch_playlist_data(url, offset) for offset in range(100, total_tracks, 100)))
)
requests.insert(0, initial_request)
result_tracks = []
for request in requests:
result_tracks += request.get('items')
return result_tracks
[docs] async def get_songs(self, url: str) -> List[str]:
"""
This function receives a URL and returns all the tracks in that URL.
:param url:
:return:
"""
playlist_type = self.get_type(url)
songs = []
if playlist_type == "playlist":
return [self.make_title(song['track']) for song in await self.fetch_full_playlist(url)]
if playlist_type == "track":
songs = [await self.loop.run_in_executor(None, lambda: self.sp.track(track_id=url))]
if playlist_type == "album":
album = await self.loop.run_in_executor(None, lambda: self.sp.album_tracks(album_id=url, limit=50))
songs = album.get("items")
return [self.make_title(song) for song in songs]