From ab708a1cfb0f05f5a5a1a80432493b98ac1b4c19 Mon Sep 17 00:00:00 2001 From: cloudwithax Date: Sat, 3 Sep 2022 10:36:25 -0400 Subject: [PATCH] Remove all Spotify client code in preparation for 1.1.8 --- pomice/__init__.py | 6 +- pomice/exceptions.py | 19 ------ pomice/objects.py | 29 +++------ pomice/player.py | 49 ++++----------- pomice/pool.py | 107 ++++----------------------------- pomice/spotify/__init__.py | 5 -- pomice/spotify/client.py | 113 ----------------------------------- pomice/spotify/exceptions.py | 8 --- pomice/spotify/objects.py | 89 --------------------------- 9 files changed, 33 insertions(+), 392 deletions(-) delete mode 100644 pomice/spotify/__init__.py delete mode 100644 pomice/spotify/client.py delete mode 100644 pomice/spotify/exceptions.py delete mode 100644 pomice/spotify/objects.py diff --git a/pomice/__init__.py b/pomice/__init__.py index 5652c75..92aa1d4 100644 --- a/pomice/__init__.py +++ b/pomice/__init__.py @@ -18,15 +18,15 @@ if not discord.__version__.startswith("2.0"): "using 'pip install discord.py'" ) -__version__ = "1.1.7" +__version__ = "1.1.8a" __title__ = "pomice" __author__ = "cloudwithax" -from .enums import SearchType +from .enums import * from .events import * from .exceptions import * from .filters import * from .objects import * -from .player import Player +from .player import * from .pool import * from .queue import * diff --git a/pomice/exceptions.py b/pomice/exceptions.py index f244191..03536af 100644 --- a/pomice/exceptions.py +++ b/pomice/exceptions.py @@ -52,25 +52,6 @@ class FilterTagAlreadyInUse(PomiceException): 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): """Base Pomice queue exception.""" pass diff --git a/pomice/objects.py b/pomice/objects.py index 5bfca07..c1abd66 100644 --- a/pomice/objects.py +++ b/pomice/objects.py @@ -21,17 +21,12 @@ class Track: track_id: str, info: dict, ctx: Optional[commands.Context] = None, - spotify: bool = False, search_type: SearchType = SearchType.ytsearch, - spotify_track = None, ): self.track_id = track_id self.info = info - self.spotify = spotify - - self.original: Optional[Track] = None if spotify else self self._search_type = search_type - self.spotify_track = spotify_track + self.title = info.get("title") self.author = info.get("author") @@ -84,29 +79,21 @@ class Playlist: playlist_info: dict, tracks: list, ctx: Optional[commands.Context] = None, - spotify: bool = False, - spotify_playlist = None ): self.playlist_info = playlist_info self.tracks_raw = tracks - self.spotify = spotify self.name = playlist_info.get("name") - self.spotify_playlist = spotify_playlist self._thumbnail = 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 = [ - Track(track_id=track["track"], info=track["info"], ctx=ctx) - for track in self.tracks_raw - ] - self._thumbnail = None - self._uri = None + + self.tracks = [ + Track(track_id=track["track"], info=track["info"], ctx=ctx) + for track in self.tracks_raw + ] + self._thumbnail = None + self._uri = None if (index := playlist_info.get("selectedTrack")) == -1: self.selected_track = None diff --git a/pomice/player.py b/pomice/player.py index e6f64d3..8006885 100644 --- a/pomice/player.py +++ b/pomice/player.py @@ -17,7 +17,7 @@ from discord.ext import commands from . import events from .enums import SearchType 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 .objects import Track from .pool import Node, NodePool @@ -290,44 +290,15 @@ class Player(VoiceProtocol): end: int = 0, ignore_if_playing: bool = False ) -> Track: - """Plays a track. If a Spotify track is passed in, it will be handled accordingly.""" - # 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 = { - "op": "play", - "guildId": str(self.guild.id), - "track": track.track_id, - "startTime": str(start), - "noReplace": ignore_if_playing - } + """Plays a track.""" + + data = { + "op": "play", + "guildId": str(self.guild.id), + "track": track.track_id, + "startTime": str(start), + "noReplace": ignore_if_playing + } if end > 0: data["endTime"] = str(end) diff --git a/pomice/pool.py b/pomice/pool.py index 5e234ff..f412b55 100644 --- a/pomice/pool.py +++ b/pomice/pool.py @@ -14,15 +14,12 @@ from discord.ext import commands from . import ( __version__, - spotify, ) from .enums import SearchType, NodeAlgorithm from .exceptions import ( - InvalidSpotifyClientAuthorization, NodeConnectionFailure, NodeCreationError, - NodeException, NodeNotAvailable, NoNodesAvailable, TrackLoadError @@ -51,14 +48,13 @@ URL_REGEX = re.compile( class Node: """The base class for a 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__( self, *, pool, - bot: Client, + bot: commands.Bot, host: str, port: int, password: str, @@ -66,18 +62,15 @@ class Node: secure: bool = False, heartbeat: int = 30, session: Optional[aiohttp.ClientSession] = None, - spotify_client_id: Optional[str] = None, - spotify_client_secret: Optional[str] = None, - ): - self._bot = bot - self._host = host - self._port = port + self._bot: commands.Bot = bot + self._host: str = host + self._port: int = port self._pool = pool - self._password = password - self._identifier = identifier - self._heartbeat = heartbeat - self._secure = secure + self._password: str = password + self._identifier: str = identifier + self._heartbeat: str = heartbeat + self._secure: bool = secure 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._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") def __repr__(self): @@ -133,7 +118,7 @@ class Node: @property - def bot(self) -> Client: + def bot(self) -> commands.Bot: """Property which returns the discord.py client linked to this node""" return self._bot @@ -290,9 +275,6 @@ class Node: ): """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 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): 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 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): + if discord_url := DISCORD_MP3_URL_REGEX.match(query): async with self._session.get( url=f"{self._rest_uri}/loadtracks?identifier={quote(query)}", headers={"Authorization": self._password} @@ -508,8 +428,6 @@ class NodePool: identifier: str, secure: bool = False, heartbeat: int = 30, - spotify_client_id: Optional[str] = None, - spotify_client_secret: Optional[str] = None, session: Optional[aiohttp.ClientSession] = None, ) -> Node: @@ -521,9 +439,8 @@ class NodePool: node = Node( pool=cls, bot=bot, host=host, port=port, password=password, - identifier=identifier, secure=secure, heartbeat=heartbeat, - spotify_client_id=spotify_client_id, - session=session, spotify_client_secret=spotify_client_secret + identifier=identifier, secure=secure, heartbeat=heartbeat, + session=session ) await node.connect() diff --git a/pomice/spotify/__init__.py b/pomice/spotify/__init__.py deleted file mode 100644 index 3f012c5..0000000 --- a/pomice/spotify/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Spotify module for Pomice, made possible by cloudwithax 2021""" - -from .exceptions import * -from .objects import * -from .client import Client diff --git a/pomice/spotify/client.py b/pomice/spotify/client.py deleted file mode 100644 index 9b4ecae..0000000 --- a/pomice/spotify/client.py +++ /dev/null @@ -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/(?Palbum|playlist|track|artist)/(?P[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) \ No newline at end of file diff --git a/pomice/spotify/exceptions.py b/pomice/spotify/exceptions.py deleted file mode 100644 index e421fbf..0000000 --- a/pomice/spotify/exceptions.py +++ /dev/null @@ -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 diff --git a/pomice/spotify/objects.py b/pomice/spotify/objects.py deleted file mode 100644 index b52b4d5..0000000 --- a/pomice/spotify/objects.py +++ /dev/null @@ -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"" - ) - -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"" - ) - -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"" - ) - -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"" - ) \ No newline at end of file