Remove all Spotify client code in preparation for 1.1.8
This commit is contained in:
parent
1f7f85df93
commit
ab708a1cfb
|
|
@ -18,15 +18,15 @@ if not discord.__version__.startswith("2.0"):
|
||||||
"using 'pip install discord.py'"
|
"using 'pip install discord.py'"
|
||||||
)
|
)
|
||||||
|
|
||||||
__version__ = "1.1.7"
|
__version__ = "1.1.8a"
|
||||||
__title__ = "pomice"
|
__title__ = "pomice"
|
||||||
__author__ = "cloudwithax"
|
__author__ = "cloudwithax"
|
||||||
|
|
||||||
from .enums import SearchType
|
from .enums import *
|
||||||
from .events import *
|
from .events import *
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
from .filters import *
|
from .filters import *
|
||||||
from .objects import *
|
from .objects import *
|
||||||
from .player import Player
|
from .player import *
|
||||||
from .pool import *
|
from .pool import *
|
||||||
from .queue import *
|
from .queue import *
|
||||||
|
|
|
||||||
|
|
@ -52,25 +52,6 @@ class FilterTagAlreadyInUse(PomiceException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SpotifyAlbumLoadFailed(PomiceException):
|
|
||||||
"""The pomice Spotify client was unable to load an album."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class SpotifyTrackLoadFailed(PomiceException):
|
|
||||||
"""The pomice Spotify client was unable to load a track."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class SpotifyPlaylistLoadFailed(PomiceException):
|
|
||||||
"""The pomice Spotify client was unable to load a playlist."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidSpotifyClientAuthorization(PomiceException):
|
|
||||||
"""No Spotify client authorization was provided for track searching."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class QueueException(Exception):
|
class QueueException(Exception):
|
||||||
"""Base Pomice queue exception."""
|
"""Base Pomice queue exception."""
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -21,17 +21,12 @@ class Track:
|
||||||
track_id: str,
|
track_id: str,
|
||||||
info: dict,
|
info: dict,
|
||||||
ctx: Optional[commands.Context] = None,
|
ctx: Optional[commands.Context] = None,
|
||||||
spotify: bool = False,
|
|
||||||
search_type: SearchType = SearchType.ytsearch,
|
search_type: SearchType = SearchType.ytsearch,
|
||||||
spotify_track = None,
|
|
||||||
):
|
):
|
||||||
self.track_id = track_id
|
self.track_id = track_id
|
||||||
self.info = info
|
self.info = info
|
||||||
self.spotify = spotify
|
|
||||||
|
|
||||||
self.original: Optional[Track] = None if spotify else self
|
|
||||||
self._search_type = search_type
|
self._search_type = search_type
|
||||||
self.spotify_track = spotify_track
|
|
||||||
|
|
||||||
self.title = info.get("title")
|
self.title = info.get("title")
|
||||||
self.author = info.get("author")
|
self.author = info.get("author")
|
||||||
|
|
@ -84,23 +79,15 @@ class Playlist:
|
||||||
playlist_info: dict,
|
playlist_info: dict,
|
||||||
tracks: list,
|
tracks: list,
|
||||||
ctx: Optional[commands.Context] = None,
|
ctx: Optional[commands.Context] = None,
|
||||||
spotify: bool = False,
|
|
||||||
spotify_playlist = None
|
|
||||||
):
|
):
|
||||||
self.playlist_info = playlist_info
|
self.playlist_info = playlist_info
|
||||||
self.tracks_raw = tracks
|
self.tracks_raw = tracks
|
||||||
self.spotify = spotify
|
|
||||||
self.name = playlist_info.get("name")
|
self.name = playlist_info.get("name")
|
||||||
self.spotify_playlist = spotify_playlist
|
|
||||||
|
|
||||||
self._thumbnail = None
|
self._thumbnail = None
|
||||||
self._uri = None
|
self._uri = None
|
||||||
|
|
||||||
if self.spotify:
|
|
||||||
self.tracks = tracks
|
|
||||||
self._thumbnail = self.spotify_playlist.image
|
|
||||||
self._uri = self.spotify_playlist.uri
|
|
||||||
else:
|
|
||||||
self.tracks = [
|
self.tracks = [
|
||||||
Track(track_id=track["track"], info=track["info"], ctx=ctx)
|
Track(track_id=track["track"], info=track["info"], ctx=ctx)
|
||||||
for track in self.tracks_raw
|
for track in self.tracks_raw
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ from discord.ext import commands
|
||||||
from . import events
|
from . import events
|
||||||
from .enums import SearchType
|
from .enums import SearchType
|
||||||
from .events import PomiceEvent, TrackEndEvent, TrackStartEvent
|
from .events import PomiceEvent, TrackEndEvent, TrackStartEvent
|
||||||
from .exceptions import FilterInvalidArgument, FilterTagAlreadyInUse, FilterTagInvalid, TrackInvalidPosition, TrackLoadError
|
from .exceptions import FilterInvalidArgument, FilterTagAlreadyInUse, FilterTagInvalid, TrackInvalidPosition
|
||||||
from .filters import Filter
|
from .filters import Filter
|
||||||
from .objects import Track
|
from .objects import Track
|
||||||
from .pool import Node, NodePool
|
from .pool import Node, NodePool
|
||||||
|
|
@ -290,37 +290,8 @@ class Player(VoiceProtocol):
|
||||||
end: int = 0,
|
end: int = 0,
|
||||||
ignore_if_playing: bool = False
|
ignore_if_playing: bool = False
|
||||||
) -> Track:
|
) -> Track:
|
||||||
"""Plays a track. If a Spotify track is passed in, it will be handled accordingly."""
|
"""Plays a track."""
|
||||||
# Make sure we've never searched the track before
|
|
||||||
if track.original is None:
|
|
||||||
# First lets try using the tracks ISRC, every track has one (hopefully)
|
|
||||||
try:
|
|
||||||
if not track.isrc:
|
|
||||||
# We have to bare raise here because theres no other way to skip this block feasibly
|
|
||||||
raise
|
|
||||||
search: Track = (await self._node.get_tracks(
|
|
||||||
f"{track._search_type}:{track.isrc}", ctx=track.ctx))[0]
|
|
||||||
except Exception:
|
|
||||||
# First method didn't work, lets try just searching it up
|
|
||||||
try:
|
|
||||||
search: Track = (await self._node.get_tracks(
|
|
||||||
f"{track._search_type}:{track.title} - {track.author}", ctx=track.ctx))[0]
|
|
||||||
except:
|
|
||||||
# The song wasn't able to be found, raise error
|
|
||||||
raise TrackLoadError (
|
|
||||||
"No equivalent track was able to be found."
|
|
||||||
)
|
|
||||||
data = {
|
|
||||||
"op": "play",
|
|
||||||
"guildId": str(self.guild.id),
|
|
||||||
"track": search.track_id,
|
|
||||||
"startTime": str(start),
|
|
||||||
"noReplace": ignore_if_playing
|
|
||||||
}
|
|
||||||
track.original = search
|
|
||||||
track.track_id = search.track_id
|
|
||||||
# Set track_id for later lavalink searches
|
|
||||||
else:
|
|
||||||
data = {
|
data = {
|
||||||
"op": "play",
|
"op": "play",
|
||||||
"guildId": str(self.guild.id),
|
"guildId": str(self.guild.id),
|
||||||
|
|
|
||||||
105
pomice/pool.py
105
pomice/pool.py
|
|
@ -14,15 +14,12 @@ from discord.ext import commands
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
__version__,
|
__version__,
|
||||||
spotify,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from .enums import SearchType, NodeAlgorithm
|
from .enums import SearchType, NodeAlgorithm
|
||||||
from .exceptions import (
|
from .exceptions import (
|
||||||
InvalidSpotifyClientAuthorization,
|
|
||||||
NodeConnectionFailure,
|
NodeConnectionFailure,
|
||||||
NodeCreationError,
|
NodeCreationError,
|
||||||
NodeException,
|
|
||||||
NodeNotAvailable,
|
NodeNotAvailable,
|
||||||
NoNodesAvailable,
|
NoNodesAvailable,
|
||||||
TrackLoadError
|
TrackLoadError
|
||||||
|
|
@ -51,14 +48,13 @@ URL_REGEX = re.compile(
|
||||||
class Node:
|
class Node:
|
||||||
"""The base class for a node.
|
"""The base class for a node.
|
||||||
This node object represents a Lavalink node.
|
This node object represents a Lavalink node.
|
||||||
To enable Spotify searching, pass in a proper Spotify Client ID and Spotify Client Secret
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
pool,
|
pool,
|
||||||
bot: Client,
|
bot: commands.Bot,
|
||||||
host: str,
|
host: str,
|
||||||
port: int,
|
port: int,
|
||||||
password: str,
|
password: str,
|
||||||
|
|
@ -66,18 +62,15 @@ class Node:
|
||||||
secure: bool = False,
|
secure: bool = False,
|
||||||
heartbeat: int = 30,
|
heartbeat: int = 30,
|
||||||
session: Optional[aiohttp.ClientSession] = None,
|
session: Optional[aiohttp.ClientSession] = None,
|
||||||
spotify_client_id: Optional[str] = None,
|
|
||||||
spotify_client_secret: Optional[str] = None,
|
|
||||||
|
|
||||||
):
|
):
|
||||||
self._bot = bot
|
self._bot: commands.Bot = bot
|
||||||
self._host = host
|
self._host: str = host
|
||||||
self._port = port
|
self._port: int = port
|
||||||
self._pool = pool
|
self._pool = pool
|
||||||
self._password = password
|
self._password: str = password
|
||||||
self._identifier = identifier
|
self._identifier: str = identifier
|
||||||
self._heartbeat = heartbeat
|
self._heartbeat: str = heartbeat
|
||||||
self._secure = secure
|
self._secure: bool = secure
|
||||||
|
|
||||||
|
|
||||||
self._websocket_uri = f"{'wss' if self._secure else 'ws'}://{self._host}:{self._port}"
|
self._websocket_uri = f"{'wss' if self._secure else 'ws'}://{self._host}:{self._port}"
|
||||||
|
|
@ -99,14 +92,6 @@ class Node:
|
||||||
|
|
||||||
self._players: Dict[int, Player] = {}
|
self._players: Dict[int, Player] = {}
|
||||||
|
|
||||||
self._spotify_client_id = spotify_client_id
|
|
||||||
self._spotify_client_secret = spotify_client_secret
|
|
||||||
|
|
||||||
if self._spotify_client_id and self._spotify_client_secret:
|
|
||||||
self._spotify_client = spotify.Client(
|
|
||||||
self._spotify_client_id, self._spotify_client_secret
|
|
||||||
)
|
|
||||||
|
|
||||||
self._bot.add_listener(self._update_handler, "on_socket_response")
|
self._bot.add_listener(self._update_handler, "on_socket_response")
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
@ -133,7 +118,7 @@ class Node:
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bot(self) -> Client:
|
def bot(self) -> commands.Bot:
|
||||||
"""Property which returns the discord.py client linked to this node"""
|
"""Property which returns the discord.py client linked to this node"""
|
||||||
return self._bot
|
return self._bot
|
||||||
|
|
||||||
|
|
@ -290,9 +275,6 @@ class Node:
|
||||||
):
|
):
|
||||||
"""Fetches tracks from the node's REST api to parse into Lavalink.
|
"""Fetches tracks from the node's REST api to parse into Lavalink.
|
||||||
|
|
||||||
If you passed in Spotify API credentials, you can also pass in a
|
|
||||||
Spotify URL of a playlist, album or track and it will be parsed accordingly.
|
|
||||||
|
|
||||||
You can also pass in a discord.py Context object to get a
|
You can also pass in a discord.py Context object to get a
|
||||||
Context object on any track you search.
|
Context object on any track you search.
|
||||||
"""
|
"""
|
||||||
|
|
@ -300,70 +282,8 @@ class Node:
|
||||||
if not URL_REGEX.match(query) and not re.match(r"(?:ytm?|sc)search:.", query):
|
if not URL_REGEX.match(query) and not re.match(r"(?:ytm?|sc)search:.", query):
|
||||||
query = f"{search_type}:{query}"
|
query = f"{search_type}:{query}"
|
||||||
|
|
||||||
if SPOTIFY_URL_REGEX.match(query):
|
|
||||||
if not self._spotify_client_id and not self._spotify_client_secret:
|
|
||||||
raise InvalidSpotifyClientAuthorization(
|
|
||||||
"You did not provide proper Spotify client authorization credentials. "
|
|
||||||
"If you would like to use the Spotify searching feature, "
|
|
||||||
"please obtain Spotify API credentials here: https://developer.spotify.com/"
|
|
||||||
)
|
|
||||||
|
|
||||||
spotify_results = await self._spotify_client.search(query=query)
|
if discord_url := DISCORD_MP3_URL_REGEX.match(query):
|
||||||
|
|
||||||
if isinstance(spotify_results, spotify.Track):
|
|
||||||
return [
|
|
||||||
Track(
|
|
||||||
track_id=spotify_results.id,
|
|
||||||
ctx=ctx,
|
|
||||||
search_type=search_type,
|
|
||||||
spotify=True,
|
|
||||||
spotify_track=spotify_results,
|
|
||||||
info={
|
|
||||||
"title": spotify_results.name,
|
|
||||||
"author": spotify_results.artists,
|
|
||||||
"length": spotify_results.length,
|
|
||||||
"identifier": spotify_results.id,
|
|
||||||
"uri": spotify_results.uri,
|
|
||||||
"isStream": False,
|
|
||||||
"isSeekable": True,
|
|
||||||
"position": 0,
|
|
||||||
"thumbnail": spotify_results.image,
|
|
||||||
"isrc": spotify_results.isrc
|
|
||||||
}
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
tracks = [
|
|
||||||
Track(
|
|
||||||
track_id=track.id,
|
|
||||||
ctx=ctx,
|
|
||||||
search_type=search_type,
|
|
||||||
spotify=True,
|
|
||||||
spotify_track=track,
|
|
||||||
info={
|
|
||||||
"title": track.name,
|
|
||||||
"author": track.artists,
|
|
||||||
"length": track.length,
|
|
||||||
"identifier": track.id,
|
|
||||||
"uri": track.uri,
|
|
||||||
"isStream": False,
|
|
||||||
"isSeekable": True,
|
|
||||||
"position": 0,
|
|
||||||
"thumbnail": track.image,
|
|
||||||
"isrc": track.isrc
|
|
||||||
}
|
|
||||||
) for track in spotify_results.tracks
|
|
||||||
]
|
|
||||||
|
|
||||||
return Playlist(
|
|
||||||
playlist_info={"name": spotify_results.name, "selectedTrack": 0},
|
|
||||||
tracks=tracks,
|
|
||||||
ctx=ctx,
|
|
||||||
spotify=True,
|
|
||||||
spotify_playlist=spotify_results
|
|
||||||
)
|
|
||||||
|
|
||||||
elif discord_url := DISCORD_MP3_URL_REGEX.match(query):
|
|
||||||
async with self._session.get(
|
async with self._session.get(
|
||||||
url=f"{self._rest_uri}/loadtracks?identifier={quote(query)}",
|
url=f"{self._rest_uri}/loadtracks?identifier={quote(query)}",
|
||||||
headers={"Authorization": self._password}
|
headers={"Authorization": self._password}
|
||||||
|
|
@ -508,8 +428,6 @@ class NodePool:
|
||||||
identifier: str,
|
identifier: str,
|
||||||
secure: bool = False,
|
secure: bool = False,
|
||||||
heartbeat: int = 30,
|
heartbeat: int = 30,
|
||||||
spotify_client_id: Optional[str] = None,
|
|
||||||
spotify_client_secret: Optional[str] = None,
|
|
||||||
session: Optional[aiohttp.ClientSession] = None,
|
session: Optional[aiohttp.ClientSession] = None,
|
||||||
|
|
||||||
) -> Node:
|
) -> Node:
|
||||||
|
|
@ -522,8 +440,7 @@ class NodePool:
|
||||||
node = Node(
|
node = Node(
|
||||||
pool=cls, bot=bot, host=host, port=port, password=password,
|
pool=cls, bot=bot, host=host, port=port, password=password,
|
||||||
identifier=identifier, secure=secure, heartbeat=heartbeat,
|
identifier=identifier, secure=secure, heartbeat=heartbeat,
|
||||||
spotify_client_id=spotify_client_id,
|
session=session
|
||||||
session=session, spotify_client_secret=spotify_client_secret
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await node.connect()
|
await node.connect()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
"""Spotify module for Pomice, made possible by cloudwithax 2021"""
|
|
||||||
|
|
||||||
from .exceptions import *
|
|
||||||
from .objects import *
|
|
||||||
from .client import Client
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
import re
|
|
||||||
import time
|
|
||||||
from base64 import b64encode
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import orjson as json
|
|
||||||
|
|
||||||
|
|
||||||
from .exceptions import InvalidSpotifyURL, SpotifyRequestException
|
|
||||||
from .objects import *
|
|
||||||
|
|
||||||
GRANT_URL = "https://accounts.spotify.com/api/token"
|
|
||||||
REQUEST_URL = "https://api.spotify.com/v1/{type}s/{id}"
|
|
||||||
SPOTIFY_URL_REGEX = re.compile(
|
|
||||||
r"https?://open.spotify.com/(?P<type>album|playlist|track|artist)/(?P<id>[a-zA-Z0-9]+)"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Client:
|
|
||||||
"""The base client for the Spotify module of Pomice.
|
|
||||||
This class will do all the heavy lifting of getting all the metadata
|
|
||||||
for any Spotify URL you throw at it.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, client_id: str, client_secret: str) -> None:
|
|
||||||
self._client_id = client_id
|
|
||||||
self._client_secret = client_secret
|
|
||||||
|
|
||||||
self.session = aiohttp.ClientSession()
|
|
||||||
|
|
||||||
self._bearer_token: str = None
|
|
||||||
self._expiry = 0
|
|
||||||
self._auth_token = b64encode(f"{self._client_id}:{self._client_secret}".encode())
|
|
||||||
self._grant_headers = {"Authorization": f"Basic {self._auth_token.decode()}"}
|
|
||||||
self._bearer_headers = None
|
|
||||||
|
|
||||||
async def _fetch_bearer_token(self) -> None:
|
|
||||||
_data = {"grant_type": "client_credentials"}
|
|
||||||
|
|
||||||
async with self.session.post(GRANT_URL, data=_data, headers=self._grant_headers) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
raise SpotifyRequestException(
|
|
||||||
f"Error fetching bearer token: {resp.status} {resp.reason}"
|
|
||||||
)
|
|
||||||
|
|
||||||
data: dict = await resp.json(loads=json.loads)
|
|
||||||
|
|
||||||
self._bearer_token = data["access_token"]
|
|
||||||
self._expiry = time.time() + (int(data["expires_in"]) - 10)
|
|
||||||
self._bearer_headers = {"Authorization": f"Bearer {self._bearer_token}"}
|
|
||||||
|
|
||||||
async def search(self, *, query: str):
|
|
||||||
if not self._bearer_token or time.time() >= self._expiry:
|
|
||||||
await self._fetch_bearer_token()
|
|
||||||
|
|
||||||
result = SPOTIFY_URL_REGEX.match(query)
|
|
||||||
spotify_type = result.group("type")
|
|
||||||
spotify_id = result.group("id")
|
|
||||||
|
|
||||||
if not result:
|
|
||||||
raise InvalidSpotifyURL("The Spotify link provided is not valid.")
|
|
||||||
|
|
||||||
request_url = REQUEST_URL.format(type=spotify_type, id=spotify_id)
|
|
||||||
|
|
||||||
async with self.session.get(request_url, headers=self._bearer_headers) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
raise SpotifyRequestException(
|
|
||||||
f"Error while fetching results: {resp.status} {resp.reason}"
|
|
||||||
)
|
|
||||||
|
|
||||||
data: dict = await resp.json(loads=json.loads)
|
|
||||||
|
|
||||||
if spotify_type == "track":
|
|
||||||
return Track(data)
|
|
||||||
elif spotify_type == "album":
|
|
||||||
return Album(data)
|
|
||||||
elif spotify_type == "artist":
|
|
||||||
async with self.session.get(f"{request_url}/top-tracks?market=US", headers=self._bearer_headers) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
raise SpotifyRequestException(
|
|
||||||
f"Error while fetching results: {resp.status} {resp.reason}"
|
|
||||||
)
|
|
||||||
|
|
||||||
track_data: dict = await resp.json(loads=json.loads)
|
|
||||||
tracks = track_data['tracks']
|
|
||||||
return Artist(data, tracks)
|
|
||||||
else:
|
|
||||||
tracks = [
|
|
||||||
Track(track["track"])
|
|
||||||
for track in data["tracks"]["items"] if track["track"] is not None
|
|
||||||
]
|
|
||||||
|
|
||||||
if not len(tracks):
|
|
||||||
raise SpotifyRequestException("This playlist is empty and therefore cannot be queued.")
|
|
||||||
|
|
||||||
next_page_url = data["tracks"]["next"]
|
|
||||||
|
|
||||||
while next_page_url is not None:
|
|
||||||
async with self.session.get(next_page_url, headers=self._bearer_headers) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
raise SpotifyRequestException(
|
|
||||||
f"Error while fetching results: {resp.status} {resp.reason}"
|
|
||||||
)
|
|
||||||
|
|
||||||
next_data: dict = await resp.json(loads=json.loads)
|
|
||||||
|
|
||||||
tracks += [
|
|
||||||
Track(track["track"])
|
|
||||||
for track in next_data["items"] if track["track"] is not None
|
|
||||||
]
|
|
||||||
next_page_url = next_data["next"]
|
|
||||||
|
|
||||||
return Playlist(data, tracks)
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
class SpotifyRequestException(Exception):
|
|
||||||
"""An error occurred when making a request to the Spotify API"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidSpotifyURL(Exception):
|
|
||||||
"""An invalid Spotify URL was passed"""
|
|
||||||
pass
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
from typing import List
|
|
||||||
|
|
||||||
|
|
||||||
class Track:
|
|
||||||
"""The base class for a Spotify Track"""
|
|
||||||
|
|
||||||
def __init__(self, data: dict, image = None) -> None:
|
|
||||||
self.name = data["name"]
|
|
||||||
self.artists = ", ".join(artist["name"] for artist in data["artists"])
|
|
||||||
self.length = data["duration_ms"]
|
|
||||||
self.id = data["id"]
|
|
||||||
|
|
||||||
if data.get("external_ids"):
|
|
||||||
self.isrc = data["external_ids"]["isrc"]
|
|
||||||
else:
|
|
||||||
self.isrc = None
|
|
||||||
|
|
||||||
if data.get("album") and data["album"].get("images"):
|
|
||||||
self.image = data["album"]["images"][0]["url"]
|
|
||||||
else:
|
|
||||||
self.image = image
|
|
||||||
|
|
||||||
if data["is_local"]:
|
|
||||||
self.uri = None
|
|
||||||
else:
|
|
||||||
self.uri = data["external_urls"]["spotify"]
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return (
|
|
||||||
f"<Pomice.spotify.Track name={self.name} artists={self.artists} "
|
|
||||||
f"length={self.length} id={self.id} isrc={self.isrc}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
class Playlist:
|
|
||||||
"""The base class for a Spotify playlist"""
|
|
||||||
|
|
||||||
def __init__(self, data: dict, tracks: List[Track]) -> None:
|
|
||||||
self.name = data["name"]
|
|
||||||
self.tracks = tracks
|
|
||||||
self.owner = data["owner"]["display_name"]
|
|
||||||
self.total_tracks = data["tracks"]["total"]
|
|
||||||
self.id = data["id"]
|
|
||||||
if data.get("images") and len(data["images"]):
|
|
||||||
self.image = data["images"][0]["url"]
|
|
||||||
else:
|
|
||||||
self.image = None
|
|
||||||
self.uri = data["external_urls"]["spotify"]
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return (
|
|
||||||
f"<Pomice.spotify.Playlist name={self.name} owner={self.owner} id={self.id} "
|
|
||||||
f"total_tracks={self.total_tracks} tracks={self.tracks}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
class Album:
|
|
||||||
"""The base class for a Spotify album"""
|
|
||||||
|
|
||||||
def __init__(self, data: dict) -> None:
|
|
||||||
self.name = data["name"]
|
|
||||||
self.artists = ", ".join(artist["name"] for artist in data["artists"])
|
|
||||||
self.image = data["images"][0]["url"]
|
|
||||||
self.tracks = [Track(track, image=self.image) for track in data["tracks"]["items"]]
|
|
||||||
self.total_tracks = data["total_tracks"]
|
|
||||||
self.id = data["id"]
|
|
||||||
self.uri = data["external_urls"]["spotify"]
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return (
|
|
||||||
f"<Pomice.spotify.Album name={self.name} artists={self.artists} id={self.id} "
|
|
||||||
f"total_tracks={self.total_tracks} tracks={self.tracks}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
class Artist:
|
|
||||||
"""The base class for a Spotify artist"""
|
|
||||||
|
|
||||||
def __init__(self, data: dict, tracks: dict) -> None:
|
|
||||||
self.name = f"Top tracks for {data['name']}" # Setting that because its only playing top tracks
|
|
||||||
self.genres = ", ".join(genre for genre in data["genres"])
|
|
||||||
self.followers = data["followers"]["total"]
|
|
||||||
self.image = data["images"][0]["url"]
|
|
||||||
self.tracks = [Track(track, image=self.image) for track in tracks]
|
|
||||||
self.id = data["id"]
|
|
||||||
self.uri = data["external_urls"]["spotify"]
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return (
|
|
||||||
f"<Pomice.spotify.Artist name={self.name} id={self.id} "
|
|
||||||
f"tracks={self.tracks}>"
|
|
||||||
)
|
|
||||||
Loading…
Reference in New Issue