diff options
Diffstat (limited to 'code/utils')
-rw-r--r-- | code/utils/command_tree.py | 47 | ||||
-rw-r--r-- | code/utils/config.py | 61 | ||||
-rw-r--r-- | code/utils/custom_sources.py | 72 | ||||
-rw-r--r-- | code/utils/source_helpers/apple/album.py | 74 | ||||
-rw-r--r-- | code/utils/source_helpers/apple/playlist.py | 88 | ||||
-rw-r--r-- | code/utils/source_helpers/apple/song.py | 68 | ||||
-rw-r--r-- | code/utils/source_helpers/parse.py | 91 | ||||
-rw-r--r-- | code/utils/source_helpers/spotify/album.py | 68 | ||||
-rw-r--r-- | code/utils/source_helpers/spotify/artist.py | 77 | ||||
-rw-r--r-- | code/utils/source_helpers/spotify/playlist.py | 68 | ||||
-rw-r--r-- | code/utils/source_helpers/spotify/song.py | 63 |
11 files changed, 710 insertions, 67 deletions
diff --git a/code/utils/command_tree.py b/code/utils/command_tree.py index 3d9214d..afdbd2c 100644 --- a/code/utils/command_tree.py +++ b/code/utils/command_tree.py @@ -1,9 +1,8 @@ import discord from discord import app_commands from discord.ext.commands.errors import * -import datetime -from utils.config import BOT_COLOR +from utils.config import create_embed from utils.custom_sources import LoadError @@ -38,16 +37,9 @@ class Tree(app_commands.CommandTree): # Custom Error class for the `create_player` function # Issues that arise may be user not in vc, user not in correct vc, missing perms, etc. elif isinstance(error, CheckPlayerError): - embed = discord.Embed( + embed = create_embed( title=error.info["title"], description=error.info["description"], - color=BOT_COLOR, - ) - embed.set_footer( - text=datetime.datetime.now(datetime.timezone.utc).strftime( - "%Y-%m-%d %H:%M:%S" - ) - + " UTC" ) try: await interaction.response.send_message( @@ -62,46 +54,13 @@ class Tree(app_commands.CommandTree): isinstance(error, app_commands.CheckFailure) and interaction.command.name in music_commands ): - embed = discord.Embed( + embed = create_embed( title="Player Creation Error", description=( "An error occured when trying to create a player. Please" " submit a bug report with </bug:1224840889906499626> if" " this issue persists." ), - color=BOT_COLOR, - ) - embed.set_footer( - text=datetime.datetime.now(datetime.timezone.utc).strftime( - "%Y-%m-%d %H:%M:%S" - ) - + " UTC" - ) - try: - await interaction.response.send_message( - embed=embed, ephemeral=True - ) - except discord.errors.InteractionResponded: - await interaction.followup.send(embed=embed, ephemeral=True) - - # If a Spotify song is linked but cannot be found on a provider (e.g. YouTube) - elif isinstance(error, LoadError): - embed = discord.Embed( - title="Nothing Found", - description=( - "Spotify does not allow direct play, meaning songs have to" - " be found on a supported provider. In this case, the song" - " couldn't be found. Please try again with a different" - " song, or try searching for just the name and artist" - " manually rather than sending a link." - ), - color=BOT_COLOR, - ) - embed.set_footer( - text=datetime.datetime.now(datetime.timezone.utc).strftime( - "%Y-%m-%d %H:%M:%S" - ) - + " UTC" ) try: await interaction.response.send_message( diff --git a/code/utils/config.py b/code/utils/config.py index 4569cd4..f2e0d01 100644 --- a/code/utils/config.py +++ b/code/utils/config.py @@ -8,6 +8,7 @@ import sys import discord import logging import requests +from datetime import datetime from colorlog import ColoredFormatter log_level = logging.DEBUG @@ -32,7 +33,8 @@ BOT_COLOR = None BOT_INVITE_LINK = None FEEDBACK_CHANNEL_ID = None BUG_CHANNEL_ID = None -YOUTUBE_SUPPORT = None +LOG_SONGS = False +YOUTUBE_SUPPORT = False SPOTIFY_CLIENT_ID = None SPOTIFY_CLIENT_SECRET = None GENIUS_CLIENT_ID = None @@ -53,10 +55,17 @@ schema = { "bot_invite_link": {"type": "string"}, "feedback_channel_id": {"type": "integer"}, "bug_channel_id": {"type": "integer"}, - "youtube_support": {"type": "boolean"}, + "log_songs": {"type": "boolean"}, }, "required": ["token"], }, + "youtube": { + "type": "object", + "properties": { + "enabled": {"type": "boolean"}, + }, + "required": ["enabled"], + }, "spotify": { "type": "object", "properties": { @@ -117,7 +126,10 @@ bot_info: bot_invite_link: "" feedback_channel_id: "" bug_channel_id: "" - youtube_support: false + log_songs: true + +youtube: + enabled: false spotify: spotify_client_id: "" @@ -148,7 +160,7 @@ lavalink: # Thouroughly validate all of the options in the config.yaml file def validate_config(file_contents): - global TOKEN, BOT_COLOR, BOT_INVITE_LINK, FEEDBACK_CHANNEL_ID, BUG_CHANNEL_ID, YOUTUBE_SUPPORT, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, GENIUS_CLIENT_ID, GENIUS_CLIENT_SECRET, OPENAI_API_KEY, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD + global TOKEN, BOT_COLOR, BOT_INVITE_LINK, FEEDBACK_CHANNEL_ID, BUG_CHANNEL_ID, LOG_SONGS, YOUTUBE_SUPPORT, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, GENIUS_CLIENT_ID, GENIUS_CLIENT_SECRET, OPENAI_API_KEY, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD config = yaml.safe_load(file_contents) try: @@ -208,10 +220,12 @@ def validate_config(file_contents): else: BUG_CHANNEL_ID = config["bot_info"]["bug_channel_id"] - if "youtube_support" in config["bot_info"]: - YOUTUBE_SUPPORT = bool(config["bot_info"]["youtube_support"]) - else: - YOUTUBE_SUPPORT = False + if "log_songs" in config["bot_info"]: + LOG_SONGS = bool(config["bot_info"]["log_songs"]) + + # Check for YouTube support + if "youtube" in config: + YOUTUBE_SUPPORT = bool(config["youtube"]["enabled"]) # # If the SPOTIFY section is present, make sure the client ID and secret are valid @@ -274,3 +288,34 @@ def validate_config(file_contents): LAVALINK_HOST = config["lavalink"]["host"] LAVALINK_PORT = config["lavalink"]["port"] LAVALINK_PASSWORD = config["lavalink"]["password"] + + +""" +Template for embeds +""" + + +def create_embed( + title: str = None, + description: str = None, + color=None, + footer=None, + thumbnail=None, +): + embed = discord.Embed( + title=title, + description=description, + color=color if color else BOT_COLOR, + ) + + if footer: + embed.set_footer(text=footer) + # else: + # embed.set_footer( + # text=datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") + " UTC" + # ) + + if thumbnail: + embed.set_thumbnail(url=thumbnail) + + return embed diff --git a/code/utils/custom_sources.py b/code/utils/custom_sources.py index 40544c1..5cf2295 100644 --- a/code/utils/custom_sources.py +++ b/code/utils/custom_sources.py @@ -6,6 +6,8 @@ from lavalink import ( PlaylistInfo, ) +from utils.config import YOUTUBE_SUPPORT + class LoadError( Exception @@ -32,21 +34,22 @@ class CustomAudioTrack(DeferredAudioTrack): LoadType.EMPTY, LoadType.ERROR, ): - ytmsearch = f"ytmsearch:{self.title} {self.author}" - results = await client.get_tracks(ytmsearch) - - if not results.tracks or results.load_type in ( - LoadType.EMPTY, - LoadType.ERROR, - ): - ytsearch = f"ytsearch:{self.title} {self.author} audio" - results = await client.get_tracks(ytsearch) + if YOUTUBE_SUPPORT: + ytmsearch = f"ytmsearch:{self.title} {self.author}" + results = await client.get_tracks(ytmsearch) if not results.tracks or results.load_type in ( LoadType.EMPTY, LoadType.ERROR, ): - raise LoadError + ytsearch = f"ytsearch:{self.title} {self.author} audio" + results = await client.get_tracks(ytsearch) + + if not results.tracks or results.load_type in ( + LoadType.EMPTY, + LoadType.ERROR, + ): + raise LoadError first_track = results.tracks[ 0 @@ -70,6 +73,10 @@ class SpotifySource(Source): ) # Initialising our custom source with the name 'custom'. async def load_item(self, user, metadata): + try: + artwork_url = metadata["album"]["images"][0]["url"] + except IndexError: + artwork_url = None track = CustomAudioTrack( { # Create an instance of our CustomAudioTrack. "identifier": metadata[ @@ -82,7 +89,7 @@ class SpotifySource(Source): "title": metadata["name"], "uri": metadata["external_urls"]["spotify"], "duration": metadata["duration_ms"], - "artworkUrl": metadata["album"]["images"][0]["url"], + "artworkUrl": artwork_url, }, requester=user, ) @@ -91,6 +98,11 @@ class SpotifySource(Source): ) async def load_album(self, user, metadata): + try: + artwork_url = metadata["images"][0]["url"] + except IndexError: + artwork_url = None + tracks = [] for track in metadata["tracks"][ "items" @@ -108,7 +120,7 @@ class SpotifySource(Source): "title": track["name"], "uri": track["external_urls"]["spotify"], "duration": track["duration_ms"], - "artworkUrl": metadata["images"][0]["url"], + "artworkUrl": artwork_url, }, requester=user, ) @@ -123,6 +135,10 @@ class SpotifySource(Source): for track in metadata["tracks"][ "items" ]: # Loop through each track in the playlist. + try: + artwork_url = track["track"]["album"]["images"][0]["url"] + except IndexError: + artwork_url = None tracks.append( CustomAudioTrack( { # Create an instance of our CustomAudioTrack. @@ -136,9 +152,7 @@ class SpotifySource(Source): "title": track["track"]["name"], "uri": track["track"]["external_urls"]["spotify"], "duration": track["track"]["duration_ms"], - "artworkUrl": track["track"]["album"]["images"][0][ - "url" - ], + "artworkUrl": artwork_url, }, requster=user, ) @@ -148,6 +162,34 @@ class SpotifySource(Source): LoadType.PLAYLIST, tracks, playlist_info=PlaylistInfo.none() ) + async def load_artist(self, user, metadata): + tracks = [] + for track in metadata["tracks"]: + try: + artwork_url = track["album"]["images"][0]["url"] + except IndexError: + artwork_url = None + tracks.append( + CustomAudioTrack( + { + "identifier": track["id"], + "isSeekable": True, + "author": track["artists"][0]["name"], + "length": track["duration_ms"], + "isStream": False, + "title": track["name"], + "uri": track["external_urls"]["spotify"], + "duration": track["duration_ms"], + "artworkUrl": artwork_url, + }, + requester=user, + ) + ) + + return LoadResult( + LoadType.PLAYLIST, tracks, playlist_info=PlaylistInfo.none() + ) + """ Custom Source for Apple Music links diff --git a/code/utils/source_helpers/apple/album.py b/code/utils/source_helpers/apple/album.py new file mode 100644 index 0000000..aa4ea0d --- /dev/null +++ b/code/utils/source_helpers/apple/album.py @@ -0,0 +1,74 @@ +import datetime +import discord +import requests +from typing import Tuple, Optional +from requests.exceptions import JSONDecodeError + +from utils.config import create_embed, LOG + + +async def load( + headers: dict, + query: str, + user: discord.User, +) -> Tuple[Optional[dict], Optional[discord.Embed]]: + """ + Get the album info from the Apple Music API + """ + album_id = query.split("/album/")[1].split("/")[1] + + try: + # Get the album info + response = requests.get( + f"https://api.music.apple.com/v1/catalog/us/albums/{album_id}", + headers=headers, + ) + + if response.status_code == 404: + embed = create_embed( + title="Album Not Found", + description=( + "The album could not be found as the provided link is" + " invalid. Please try again." + ), + ) + return None, embed + + if response.status_code == 401: + LOG.error( + "Could not authorize with Apple Music API. Likely need to" + " restart the bot." + ) + return None, None + + response.raise_for_status() + # Unpack the album info + album = response.json() + name = album["data"][0]["attributes"]["name"] + artist = album["data"][0]["attributes"]["artistName"] + num_tracks = len(album["data"][0]["relationships"]["tracks"]["data"]) + except IndexError: + LOG.error("Failed unpacking Apple Music album info") + return None, None + except (JSONDecodeError, requests.HTTPError): + LOG.error("Failed making request to Apple Music API") + return None, None + + # Extract artwork URL, if available + artwork_url = ( + album["data"][0]["attributes"].get("artwork", {}).get("url", None) + ) + if artwork_url: + artwork_url = artwork_url.replace("{w}x{h}", "300x300") + + embed = create_embed( + title="Album Queued", + description=( + f"**{name}** by **{artist}**\n" + f"` {num_tracks} ` tracks\n\n" + f"Queued by: {user.mention}" + ), + thumbnail=artwork_url, + ) + + return album, embed diff --git a/code/utils/source_helpers/apple/playlist.py b/code/utils/source_helpers/apple/playlist.py new file mode 100644 index 0000000..65dfbf8 --- /dev/null +++ b/code/utils/source_helpers/apple/playlist.py @@ -0,0 +1,88 @@ +import discord +import requests +from typing import Tuple, Optional +from requests.exceptions import JSONDecodeError + +from utils.config import create_embed, LOG + + +async def load( + headers: dict, + query: str, + user: discord.User, +) -> Tuple[Optional[dict], Optional[discord.Embed]]: + """ + Get the playlist info from the Apple Music API + """ + playlist_id = query.split("/playlist/")[1].split("/")[1] + try: + # Get all of the tracks in the playlist (limit at 100) + response = requests.get( + f"https://api.music.apple.com/v1/catalog/us/playlists/{playlist_id}/tracks?limit=100", + headers=headers, + ) + + if response.status_code == 404: + embed = create_embed( + title="Playlist Not Found", + description=( + "The playlist could not be found as the provided link is" + " invalid. Please try again." + ), + ) + return None, embed + + if response.status_code == 401: + LOG.error( + "Could not authorize with Apple Music API. Likely need to" + " restart the bot." + ) + return None, None + + response.raise_for_status() + playlist = response.json() + + # Get the general playlist info (name, artwork) + response = requests.get( + f"https://api.music.apple.com/v1/catalog/us/playlists/{playlist_id}", + headers=headers, + ) + + response.raise_for_status() + # Unpack the playlist info + playlist_info = response.json() + name = playlist_info["data"][0]["attributes"]["name"] + num_tracks = len(playlist["data"]) + except IndexError: + LOG.error("Failed unpacking Apple Music playlist info") + return None, None + except (JSONDecodeError, requests.HTTPError): + LOG.error("Failed making request to Apple Music API") + return None, None + + # Extract artwork URL, if available + artwork_url = ( + playlist_info["data"][0]["attributes"] + .get("artwork", {}) + .get("url", None) + ) + if artwork_url: + artwork_url = artwork_url.replace("{w}x{h}", "300x300") + + embed = create_embed( + title="Playlist Queued", + description=( + f"**{name}**\n` {num_tracks} ` tracks\n\nQueued by: {user.mention}" + ), + thumbnail=artwork_url, + ) + + # Add small alert if the playlist is the max size + if len(playlist["data"]) == 100: + embed.description += ( + "\n\n*This playlist is longer than the 100 song" + " maximum. Only the first 100 songs will be" + " queued.*" + ) + + return playlist, embed diff --git a/code/utils/source_helpers/apple/song.py b/code/utils/source_helpers/apple/song.py new file mode 100644 index 0000000..4190b63 --- /dev/null +++ b/code/utils/source_helpers/apple/song.py @@ -0,0 +1,68 @@ +import discord +import requests +from typing import Tuple, Optional +from requests.exceptions import JSONDecodeError + +from utils.config import create_embed, LOG + + +async def load( + headers: dict, + query: str, + user: discord.User, +) -> Tuple[Optional[dict], Optional[discord.Embed]]: + """ + Get the song info from the Apple Music API + """ + song_id = query.split("/album/")[1].split("?i=")[1] + + try: + # Get the song info + response = requests.get( + f"https://api.music.apple.com/v1/catalog/us/songs/{song_id}", + headers=headers, + ) + + if response.status_code == 404: + embed = create_embed( + title="Song Not Found", + description=( + "The song could not be found as the provided link is" + " invalid. Please try again." + ), + ) + return None, embed + + if response.status_code == 401: + LOG.error( + "Could not authorize with Apple Music API. Likely need to" + " restart the bot." + ) + return None, None + + response.raise_for_status() + # Unpack the song info + song = response.json() + name = song["data"][0]["attributes"]["name"] + artist = song["data"][0]["attributes"]["artistName"] + except IndexError: + LOG.error("Failed unpacking Apple Music song info") + return None, None + except (JSONDecodeError, requests.HTTPError): + LOG.error("Failed making request to Apple Music API") + return None, None + + # Extract artwork URL, if available + artwork_url = ( + song["data"][0]["attributes"].get("artwork", {}).get("url", None) + ) + if artwork_url: + artwork_url = artwork_url.replace("{w}x{h}", "300x300") + + embed = create_embed( + title="Song Queued", + description=f"**{name}** by **{artist}**\n\nQueued by {user.mention}", + thumbnail=artwork_url, + ) + + return song, embed diff --git a/code/utils/source_helpers/parse.py b/code/utils/source_helpers/parse.py new file mode 100644 index 0000000..b23a895 --- /dev/null +++ b/code/utils/source_helpers/parse.py @@ -0,0 +1,91 @@ +import discord + +from utils.source_helpers.apple import ( + album as apple_album, + playlist as apple_playlist, + song as apple_song, +) +from utils.source_helpers.spotify import ( + album as spotify_album, + artist as spotify_artist, + playlist as spotify_playlist, + song as spotify_song, +) +from utils.custom_sources import AppleSource, SpotifySource + + +async def parse_custom_source( + self, provider: str, query: str, user: discord.User +): + """ + Parse the query and run the appropriate functions to get the results/info + + Return the results and an embed or None, None + """ + load_funcs = { + "apple": { + "album": apple_album.load, + "playlist": apple_playlist.load, + "song": apple_song.load, + }, + "spotify": { + "album": spotify_album.load, + "artist": spotify_artist.load, + "playlist": spotify_playlist.load, + "song": spotify_song.load, + }, + } + + headers = { + "apple": self.bot.apple_headers, + "spotify": self.bot.spotify_headers, + } + + sources = { + "apple": AppleSource, + "spotify": SpotifySource, + } + # Catch all songs + if "?i=" in query or "/track/" in query: + song, embed = await load_funcs[provider]["song"]( + headers[provider], query, user + ) + + if song: + results = await sources[provider].load_item(self, user, song) + else: + return None, embed + # Catch all playlists + elif "/playlist/" in query: + playlist, embed = await load_funcs[provider]["playlist"]( + headers[provider], query, user + ) + + if playlist: + results = await sources[provider].load_playlist( + self, user, playlist + ) + else: + return None, embed + # Catch all albums + elif "/album/" in query: + album, embed = await load_funcs[provider]["album"]( + headers[provider], query, user + ) + + if album: + results = await sources[provider].load_album(self, user, album) + else: + return None, embed + # Catch Spotify artists + elif "/artist/" in query: + artist, embed = await load_funcs[provider]["artist"]( + headers[provider], query, user + ) + + if artist: + results = await sources[provider].load_artist(self, user, artist) + else: + return None, embed + + return results, embed diff --git a/code/utils/source_helpers/spotify/album.py b/code/utils/source_helpers/spotify/album.py new file mode 100644 index 0000000..0ebc7d5 --- /dev/null +++ b/code/utils/source_helpers/spotify/album.py @@ -0,0 +1,68 @@ +import datetime +import discord +import requests +from typing import Tuple, Optional +from requests.exceptions import JSONDecodeError + +from utils.config import create_embed, LOG + + +async def load( + headers: dict, + query: str, + user: discord.User, +) -> Tuple[Optional[dict], Optional[discord.Embed]]: + """ + Get the album info from the Spotify API + """ + album_id = query.split("/album/")[1] + + try: + # Get the album info + response = requests.get( + f"https://api.spotify.com/v1/albums/{album_id}", + headers=headers, + ) + + if response.status_code == 404: + embed = create_embed( + title="Album Not Found", + description=( + "The album could not be found as the provided link is" + " invalid. Please try again." + ), + ) + return None, embed + + if response.status_code == 401: + LOG.error( + "Could not authorize with Spotify API. Likely need to" + " restart the bot." + ) + return None, None + + response.raise_for_status() + # Unpack the album info + album = response.json() + name = album["name"] + artist = album["artists"][0]["name"] + num_tracks = len(album["tracks"]["items"]) + artwork_url = album["images"][0]["url"] + except IndexError: + LOG.error("Failed unpacking Spotify album info") + return None, None + except (JSONDecodeError, requests.HTTPError): + LOG.error("Failed making request to Spotify API") + return None, None + + embed = create_embed( + title="Album Queued", + description=( + f"**{name}** by **{artist}**\n" + f"` {num_tracks} ` tracks\n\n" + f"Queued by: {user.mention}" + ), + thumbnail=artwork_url, + ) + + return album, embed diff --git a/code/utils/source_helpers/spotify/artist.py b/code/utils/source_helpers/spotify/artist.py new file mode 100644 index 0000000..995e208 --- /dev/null +++ b/code/utils/source_helpers/spotify/artist.py @@ -0,0 +1,77 @@ +import datetime +import discord +import requests +from typing import Tuple, Optional +from requests.exceptions import JSONDecodeError + +from utils.config import create_embed, LOG + + +async def load( + headers: dict, + query: str, + user: discord.User, +) -> Tuple[Optional[dict], Optional[discord.Embed]]: + """ + Get the artists top tracks from the Spotify API + """ + artist_id = query.split("/artist/")[1] + + try: + # Get the artists songs + response = requests.get( + f"https://api.spotify.com/v1/artists/{artist_id}/top-tracks", + headers=headers, + ) + + if response.status_code == 404: + embed = create_embed( + title="Artist Not Found", + description=( + "Either the provided link is malformed, the artist does" + " not exist, or the artist does not have any songs." + ), + ) + return None, embed + + if response.status_code == 401: + LOG.error( + "Could not authorize with Spotify API. Likely need to" + " restart the bot." + ) + return None, None + + response.raise_for_status() + # Unpack the artists songs + artist = response.json() + name = artist["tracks"][0]["artists"][0]["name"] + num_tracks = len(artist["tracks"]) + + # Get the artist info (for the thumbnail) + response = requests.get( + f"https://api.spotify.com/v1/artists/{artist_id}", + headers=headers, + ) + + response.raise_for_status() + try: + artwork_url = response.json()["images"][0]["url"] + except IndexError: + artwork_url = None + + except IndexError: + LOG.error("Failed unpacking Spotify artist info") + return None, None + except (JSONDecodeError, requests.HTTPError): + LOG.error("Failed making request to Spotify API") + return None, None + + embed = create_embed( + title="Artist Queued", + description=( + f"Top `{num_tracks}` track by **{name}**\n\n" + f"Queued by {user.mention}" + ), + thumbnail=artwork_url, + ) + return artist, embed diff --git a/code/utils/source_helpers/spotify/playlist.py b/code/utils/source_helpers/spotify/playlist.py new file mode 100644 index 0000000..7ca9c6a --- /dev/null +++ b/code/utils/source_helpers/spotify/playlist.py @@ -0,0 +1,68 @@ +import datetime +import discord +import requests +from typing import Tuple, Optional +from requests.exceptions import JSONDecodeError + +from utils.config import create_embed, LOG + + +async def load( + headers: dict, + query: str, + user: discord.User, +) -> Tuple[Optional[dict], Optional[discord.Embed]]: + """ + Get the playlist info from the Spotify API + """ + playlist_id = query.split("/playlist/")[1] + + try: + # Get the playlist info + response = requests.get( + f"https://api.spotify.com/v1/playlists/{playlist_id}", + headers=headers, + ) + + if response.status_code == 404: + embed = create_embed( + title="Playlist Not Found", + description=( + "The playlist could not be found as the provided link is" + " invalid. Please try again." + ), + ) + return None, embed + + if response.status_code == 401: + LOG.error( + "Could not authorize with Spotify API. Likely need to" + " restart the bot." + ) + return None, None + + response.raise_for_status() + # Unpack the playlist info + playlist = response.json() + name = playlist["name"] + owner = playlist["owner"]["display_name"] + num_tracks = len(playlist["tracks"]["items"]) + artwork_url = playlist["images"][0]["url"] + except IndexError: + LOG.error("Failed unpacking Spotify playlist info") + return None, None + except (JSONDecodeError, requests.HTTPError): + LOG.error("Failed making request to Spotify API") + return None, None + + embed = create_embed( + title="Playlist Queued", + description=( + f"**{name}** from **{owner}**\n" + f"` {num_tracks} ` tracks\n\n" + f"Queued by {user.mention}" + ), + thumbnail=artwork_url, + ) + + return playlist, embed diff --git a/code/utils/source_helpers/spotify/song.py b/code/utils/source_helpers/spotify/song.py new file mode 100644 index 0000000..b0c7379 --- /dev/null +++ b/code/utils/source_helpers/spotify/song.py @@ -0,0 +1,63 @@ +import datetime +import discord +import requests +from typing import Tuple, Optional +from requests.exceptions import JSONDecodeError + +from utils.config import create_embed, LOG + + +async def load( + headers: dict, + query: str, + user: discord.User, +) -> Tuple[Optional[dict], Optional[discord.Embed]]: + """ + Get the song info from the Spotify API + """ + song_id = query.split("/track/")[1] + + try: + # Get the song info + response = requests.get( + f"https://api.spotify.com/v1/tracks/{song_id}", + headers=headers, + ) + + if response.status_code == 404: + embed = create_embed( + title="Song Not Found", + description=( + "The song could not be found as the provided link is" + " invalid. Please try again." + ), + ) + return None, embed + + if response.status_code == 401: + LOG.error( + "Could not authorize with Spotify API. Likely need to" + " restart the bot." + ) + return None, None + + response.raise_for_status() + # Unpack the song info + song = response.json() + name = song["name"] + artist = song["artists"][0]["name"] + artwork_url = song["album"]["images"][0]["url"] + except IndexError: + LOG.error("Failed unpacking Spotify song info") + return None, None + except (JSONDecodeError, requests.HTTPError): + LOG.error("Failed making request to Spotify API") + return None, None + + embed = create_embed( + title="Song Queued", + description=f"**{name}** by **{artist}**\n\nQueued by {user.mention}", + thumbnail=artwork_url, + ) + + return song, embed |