Optimize code for improved cleanliness and enhanced performance.

This commit is contained in:
Choco
2023-08-08 13:14:07 +08:00
parent e8323e089d
commit e6a1d30026
11 changed files with 172 additions and 143 deletions

View File

@@ -13,42 +13,9 @@ class Settings:
self.emoji_source_raw = settings.get("emoji_source_raw", {})
self.cooldowns_settings = settings.get("cooldowns", {})
self.aliases_settings = settings.get("aliases", {})
self.controller = settings.get("default_controller",
{
"embeds": {
"active": {
"description": "**Now Playing: ```[@@track_name@@]```\nLink: [Click Me](@@track_url@@) | Requester: @@requester@@ | DJ: @@dj@@**",
"footer": {
"text": "Queue Length: @@queue_length@@ | Duration: @@duration@@ | Volume: @@volume@@% {{loop_mode!=Off ?? | Repeat: @@loop_mode@@}}",
},
"image": "@@track_thumbnail@@",
"author": {
"name": "Music Controller | @@channel_name@@",
"icon_url": "@@bot_icon@@"
},
"color": "@@default_embed_color@@"
},
"inactive": {
"title": {
"name": "There are no songs playing right now"
},
"description": "[Support](@@server_invite_link@@) | [Invite](@@invite_link@@) | [Questionnaire](https://forms.gle/Qm8vjBfg2kp13YGD7)",
"image": "https://i.imgur.com/dIFBwU7.png",
"color": "@@default_embed_color@@"
}
},
"default_buttons": [
["back", "resume", "skip", {"stop": "red"}, "add"],
["tracks"]
]
})
self.controller = settings.get("default_controller", {})
self.lyrics_platform = settings.get("lyrics_platform", "A_ZLyrics").lower()
self.ipc_server = settings.get("ipc_server", {
"host": "127.0.0.1",
"port": 8000,
"enable": False
}
)
self.ipc_server = settings.get("ipc_server", {})
self.version = settings.get("version", "")
class TOKENS:

View File

@@ -33,7 +33,7 @@ async def nowplay(ctx: commands.Context, player: voicelink.Player):
if not track:
return await ctx.send(player.get_msg('noTrackPlaying'), ephemeral=True)
upnext = "\n".join(f"`{index}.` `[{track.formatLength}]` [{track.title[:30]}]({track.uri})" for index, track in enumerate(
upnext = "\n".join(f"`{index}.` `[{track.formatted_length}]` [{track.title[:30]}]({track.uri})" for index, track in enumerate(
player.queue.tracks()[:2], start=2))
embed = discord.Embed(description=player.get_msg(
'nowplayingDesc').format(track.title), color=settings.embed_color)
@@ -48,7 +48,7 @@ async def nowplay(ctx: commands.Context, player: voicelink.Player):
":pause_button:" if player.is_paused else ":arrow_forward:")
embed.add_field(
name="\u2800", value=f"{icon} {pbar} **[{ctime(player.position)}/{track.formatLength}]**", inline=False)
name="\u2800", value=f"{icon} {pbar} **[{ctime(player.position)}/{track.formatted_length}]**", inline=False)
return await ctx.send(embed=embed, view=LinkView(player.get_msg('nowplayingLink').format(track.source), track.emoji, track.uri))
@@ -103,7 +103,7 @@ class Basic(commands.Cog):
await ctx.send(player.get_msg('playlistLoad').format(tracks.name, index))
else:
position = await player.add_track(tracks[0])
await ctx.send((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + (player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatLength, position) if position >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatLength)), allowed_mentions=False)
await ctx.send((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + (player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length, position) if position >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length)), allowed_mentions=False)
except voicelink.QueueFull as e:
await ctx.send(e)
finally:
@@ -143,7 +143,7 @@ class Basic(commands.Cog):
await interaction.response.send_message(player.get_msg('playlistLoad').format(tracks.name, index))
else:
position = await player.add_track(tracks[0])
await interaction.response.send_message((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + (player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatLength, position) if position >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatLength)), allowed_mentions=False)
await interaction.response.send_message((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + (player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length, position) if position >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length)), allowed_mentions=False)
except voicelink.QueueFull as e:
await interaction.response.send_message(e)
@@ -190,7 +190,7 @@ class Basic(commands.Cog):
return await ctx.send(player.get_msg('noTrackFound'))
query_track = "\n".join(
f"`{index}.` `[{track.formatLength}]` **{track.title[:35]}**" for index, track in enumerate(tracks[0:10], start=1))
f"`{index}.` `[{track.formatted_length}]` **{track.title[:35]}**" for index, track in enumerate(tracks[0:10], start=1))
embed = discord.Embed(title=player.get_msg('searchTitle').format(query), description=player.get_msg(
'searchDesc').format(emoji_source(platform), platform, len(tracks[0:10]), query_track), color=settings.embed_color)
view = SearchView(tracks=tracks[0:10], lang=player.lang)
@@ -202,8 +202,8 @@ class Basic(commands.Cog):
for value in view.values:
track = tracks[int(value.split(". ")[0]) - 1]
position = await player.add_track(track)
msg += ((f"`{player.get_msg('live')}`" if track.is_stream else "") + (player.get_msg('trackLoad_pos').format(track.title, track.uri, track.author, track.formatLength,
position) if position >= 1 else player.get_msg('trackLoad').format(track.title, track.uri, track.author, track.formatLength)))
msg += ((f"`{player.get_msg('live')}`" if track.is_stream else "") + (player.get_msg('trackLoad_pos').format(track.title, track.uri, track.author, track.formatted_length,
position) if position >= 1 else player.get_msg('trackLoad').format(track.title, track.uri, track.author, track.formatted_length)))
await ctx.send(msg, allowed_mentions=False)
if not player.is_playing:
@@ -231,7 +231,7 @@ class Basic(commands.Cog):
await ctx.send(player.get_msg('playlistLoad').format(tracks.name, index))
else:
position = await player.add_track(tracks[0], at_font=True)
await ctx.send((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + (player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatLength, position) if position >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatLength)), allowed_mentions=False)
await ctx.send((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + (player.get_msg('trackLoad_pos').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length, position) if position >= 1 and player.is_playing else player.get_msg('trackLoad').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length)), allowed_mentions=False)
except voicelink.QueueFull as e:
await ctx.send(e)
@@ -262,7 +262,7 @@ class Basic(commands.Cog):
await ctx.send(player.get_msg('playlistLoad').format(tracks.name, index))
else:
await player.add_track(tracks[0], at_font=True)
await ctx.send((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + player.get_msg('trackLoad').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatLength), allowed_mentions=False)
await ctx.send((f"`{player.get_msg('live')}`" if tracks[0].is_stream else "") + player.get_msg('trackLoad').format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length), allowed_mentions=False)
except voicelink.QueueFull as e:
await ctx.send(e)

View File

@@ -17,7 +17,7 @@ if not os.path.exists(os.path.join(root_dir, "settings.json")):
raise Exception("Settings file not set!")
#-------------- API Clients --------------
tokens: TOKENS = TOKENS();
tokens: TOKENS = TOKENS()
if not (tokens.mongodb_name and tokens.mongodb_url):
raise Exception("MONGODB_NAME and MONGODB_URL can't not be empty in .env")
@@ -133,7 +133,7 @@ def formatTime(number:str) -> Optional[int]:
return (int(num.tm_hour) * 3600 + int(num.tm_min) * 60 + int(num.tm_sec)) * 1000
def emoji_source(emoji:str):
def emoji_source(emoji:str) -> str:
return settings.emoji_source_raw.get(emoji.lower(), "🔗")
def gen_report() -> Optional[discord.File]:

View File

@@ -376,7 +376,7 @@ class Tracks(discord.ui.Select):
for index, track in enumerate(self.player.queue.tracks(), start=1):
if index > 10:
break
options.append(discord.SelectOption(label=f"{index}. {track.title[:40]}", description=f"{track.author[:30]} · " + ("Live" if track.is_stream else track.formatLength), emoji=track.emoji))
options.append(discord.SelectOption(label=f"{index}. {track.title[:40]}", description=f"{track.author[:30]} · " + ("Live" if track.is_stream else track.formatted_length), emoji=track.emoji))
super().__init__(
placeholder=player.get_msg("playerDropdown"),

View File

@@ -31,7 +31,7 @@ class SearchDropdown(discord.ui.Select):
self.lang = lang
options = []
for index, track in enumerate(self.tracks, start=1):
options.append(discord.SelectOption(label=f"{index}. {track.title[:50]}", description=f"{track.author[:50]} · {track.formatLength}"))
options.append(discord.SelectOption(label=f"{index}. {track.title[:50]}", description=f"{track.author[:50]} · {track.formatted_length}"))
super().__init__(placeholder=langs[lang]['searchWait'],
min_values=1, max_values=len(tracks),

View File

@@ -41,6 +41,28 @@ class Track:
You can also pass in commands.Context to get a discord.py Context object in your track.
"""
__slots__ = (
"track_id",
"info",
"identifier",
"title",
"author",
"uri",
"source",
"spotify",
"artist_id",
"original",
"_search_type",
"spotify_track",
"thumbnail",
"emoji",
"length",
"requester",
"is_stream",
"is_seekable",
"position"
)
def __init__(
self,
*,
@@ -50,73 +72,83 @@ class Track:
search_type: SearchType = SearchType.ytsearch,
spotify_track = None,
):
self.track_id = track_id
self.info = info
self.track_id: str = track_id
self.info: dict = info
self.identifier = info.get("identifier")
self.title = info.get("title", "Unknown")
self.author = info.get("author", "Unknown")
self.uri = info.get("uri", "https://discord.com/application-directory/605618911471468554")
self.source = info.get("sourceName", extract(self.uri).domain)
self.spotify = True if self.source == "spotify" else False
self.identifier: str = info.get("identifier")
self.title: str = info.get("title", "Unknown")
self.author: str = info.get("author", "Unknown")
self.uri: str = info.get("uri", "https://discord.com/application-directory/605618911471468554")
self.source: str = info.get("sourceName", extract(self.uri).domain)
self.spotify: bool = True if self.source == "spotify" else False
if self.spotify:
self.artistId: Optional[list] = info.get("artistId")
self.artist_id: Optional[list] = info.get("artist_id")
self.original: Optional[Track] = None if self.spotify else self
self._search_type = SearchType.ytmsearch if self.spotify else search_type
self.spotify_track = spotify_track
self._search_type: SearchType = SearchType.ytmsearch if self.spotify else search_type
self.spotify_track: Track = spotify_track
self.thumbnail = None
self.emoji = emoji_source(self.source)
self.thumbnail: str = None
self.emoji: str = emoji_source(self.source)
if info.get("thumbnail"):
self.thumbnail = info.get("thumbnail")
elif YOUTUBE_REGEX.match(self.uri):
self.thumbnail = f"https://img.youtube.com/vi/{self.identifier}/hqdefault.jpg"
if self.source == "soundcloud" and "/preview/" in self.identifier:
self.length = 30000
else:
self.length = info.get("length")
self.length: float = 3000 if self.source == "soundcloud" and "/preview/" in self.identifier else info.get("length")
self.formatLength = ctime(self.length)
self.requester = requester
self.is_stream = info.get("isStream", False)
self.is_seekable = info.get("isSeekable", True)
self.position = info.get("position", 0)
self.requester: Member = requester
self.is_stream: bool = info.get("isStream", False)
self.is_seekable: bool = info.get("isSeekable", True)
self.position: int = info.get("position", 0)
if not track_id:
self.track_id = encode(self)
def toDict(self):
def __eq__(self, other) -> bool:
if not isinstance(other, Track):
return False
return other.track_id == self.track_id
def __str__(self) -> str:
return self.title
def __repr__(self) -> str:
return f"<Voicelink.track title={self.title!r} uri=<{self.uri!r}> length={self.length}>"
def toDict(self) -> dict:
return {
"track_id": self.track_id,
"info": self.info,
"thumbnail": self.thumbnail
}
def encode(self):
def encode(self) -> bytes:
return encode(self)
def __eq__(self, other):
if not isinstance(other, Track):
return False
return other.track_id == self.track_id
def __str__(self):
return self.title
def __repr__(self):
return f"<Voicelink.track title={self.title!r} uri=<{self.uri!r}> length={self.length}>"
@property
def formatted_length(self) -> str:
return ctime(self.length)
class Playlist:
"""The base playlist object.
Returns critical playlist information needed for parsing by Lavalink.
You can also pass in commands.Context to get a discord.py Context object in your tracks.
"""
__slots__ = (
"playlist_info",
"tracks_raw",
"spotify",
"name",
"spotify_playlist",
"_thumbnail",
"_uri",
"tracks"
)
def __init__(
self,
*,
@@ -147,12 +179,10 @@ class Playlist:
self._thumbnail = None
self._uri = None
self.track_count = len(self.tracks)
def __str__(self):
def __str__(self) -> str:
return self.name
def __repr__(self):
def __repr__(self) -> str:
return f"<Voicelink.playlist name={self.name!r} track_count={len(self.tracks)}>"
@property
@@ -164,3 +194,7 @@ class Playlist:
def thumbnail(self) -> Optional[str]:
"""Spotify album/playlist thumbnail, or None if not a Spotify object."""
return self._thumbnail
@property
def track_count(self) -> int:
return len(self.tracks)

View File

@@ -441,7 +441,7 @@ class Player(VoiceProtocol):
try:
tracks = await self._node._spotify_client.trackSearch(query=query)
except:
except Exception as _:
raise TrackLoadError("Not able to find the provided Spotify entity, is it private?")
return [ Track(

View File

@@ -394,7 +394,7 @@ class Node:
try:
spotify_results = await self._spotify_client.search(query=query)
except:
except Exception as _:
raise TrackLoadError("Not able to find the provided Spotify entity, is it private?")
if isinstance(spotify_results, spotify.Track):

View File

@@ -53,12 +53,12 @@ class LoopTypeCycle:
class Queue:
def __init__(self, size: int, allow_duplicate: bool, get_msg: Callable[[str], str]) -> None:
self._queue = []
self._position = 0
self._size = size
self._repeat = LoopTypeCycle()
self._repeat_position = 0
self._allow_duplicate = allow_duplicate
self._queue: List[Track] = []
self._position: int = 0
self._size: int = size
self._repeat: LoopTypeCycle = LoopTypeCycle()
self._repeat_position: int = 0
self._allow_duplicate: bool = allow_duplicate
self.get_msg = get_msg

View File

@@ -23,12 +23,12 @@ SOFTWARE.
import re
import time
from base64 import b64encode
import aiohttp
from base64 import b64encode
from typing import List, Union
from .objects import Track, Album, Artist, Playlist
from .exceptions import InvalidSpotifyURL, SpotifyRequestException
from .exceptions import InvalidSpotifyURL, SpotifyRequestException
GRANT_URL = "https://accounts.spotify.com/api/token"
REQUEST_URL = "https://api.spotify.com/v1/{type}s/{id}"
@@ -71,7 +71,7 @@ class Client:
self._expiry = time.time() + (int(data["expires_in"]) - 10)
self._bearer_headers = {"Authorization": f"Bearer {self._bearer_token}"}
async def trackSearch(self, query: str, track: str = "track", limit: int = 10) -> list:
async def trackSearch(self, query: str, track: str = "track", limit: int = 10) -> List[Track]:
if not self._bearer_token or time.time() >= self._expiry:
await self._fetch_bearer_token()
@@ -87,7 +87,7 @@ class Client:
return [ Track(track) for track in data['tracks']['items'] ]
async def similar_track(self, seed_tracks: str, *, limit: int = 5) -> list:
async def similar_track(self, seed_tracks: str, *, limit: int = 5) -> List[Track]:
if not self._bearer_token or time.time() >= self._expiry:
await self._fetch_bearer_token()
@@ -103,7 +103,7 @@ class Client:
return [ Track(track) for track in data['tracks'] ]
async def search(self, *, query: str):
async def search(self, *, query: str) -> Union(Track, Album, Playlist):
if not self._bearer_token or time.time() >= self._expiry:
await self._fetch_bearer_token()

View File

@@ -3,22 +3,24 @@ from typing import List
class Track:
"""The base class for a Spotify Track"""
__slots__ = (
"name",
"artists",
"artist_id",
"length",
"id",
"image",
"uri"
)
def __init__(self, data: dict, image=None) -> None:
self.name = data.get('name', 'Unknown')
self.artists = ", ".join(artist["name"] for artist in data.get('artists'))
self.artistId = [artist['id'] for artist in data.get('artists')]
self.length = data.get('duration_ms')
self.id = data.get('id')
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"]
self.name: str = data.get('name', 'Unknown')
self.artists: str = ", ".join(artist["name"] for artist in data.get('artists'))
self.artist_id: list = [artist['id'] for artist in data.get('artists')]
self.length: int = data.get('duration_ms')
self.id: str = data.get('id')
self.image: str = images[0]["url"] if (images := data.get("album", {}).get("images")) else image
self.uri: str = None if data["is_local"] else data["external_urls"]["spotify"]
def to_dict(self) -> dict:
return {
@@ -26,7 +28,7 @@ class Track:
"author": self.artists,
"length": self.length,
"identifier": self.id,
"artistId": self.artistId,
"artist_id": self.artist_id,
"uri": self.uri,
"isStream": False,
"isSeekable": True,
@@ -43,14 +45,24 @@ class Track:
class Album:
"""The base class for a Spotify album"""
__slots__ = (
"name",
"artists",
"image",
"tracks",
"total_tracks",
"id",
"uri"
)
def __init__(self, data: dict) -> None:
self.name = data.get('name', 'Unknown')
self.artists = ", ".join(artist["name"] for artist in data.get('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.get('id')
self.uri = data["external_urls"]["spotify"]
self.name: str = data.get('name', 'Unknown')
self.artists: str = ", ".join(artist["name"] for artist in data.get('artists'))
self.image: str = data["images"][0]["url"]
self.tracks: list = [Track(track, image=self.image) for track in data["tracks"]["items"]]
self.total_tracks: int = data["total_tracks"]
self.id: str = data.get('id')
self.uri: str = data["external_urls"]["spotify"]
def __repr__(self) -> str:
return (
@@ -61,15 +73,24 @@ class Album:
class Artist:
"""The base class for a Spotify playlist"""
__slots__ = (
"tracks",
"image",
"total_tracks",
"owner",
"id",
"uri",
"name"
)
def __init__(self, data: dict) -> None:
self.tracks = [Track(track) for track in data['tracks']]
self.tracks: List[Track] = [Track(track) for track in data['tracks']]
if self.tracks:
self.image = self.tracks[0].image
self.total_tracks = len(self.tracks)
self.owner = self.tracks[0].artists
self.id = self.tracks[0].artistId
self.uri = data['tracks'][0]['album']['artists'][0]['external_urls']['spotify']
self.name = f"Top tracks - {self.owner}"
self.image: str = self.tracks[0].image
self.total_tracks: int = len(self.tracks)
self.owner: str = self.tracks[0].artists
self.id: str = self.tracks[0].artist_id
self.uri: str = data['tracks'][0]['album']['artists'][0]['external_urls']['spotify']
self.name: str = f"Top tracks - {self.owner}"
def __repr__(self) -> str:
return (
@@ -80,17 +101,24 @@ class Artist:
class Playlist:
"""The base class for a Spotify playlist"""
__slots__ = (
"name",
"tracks",
"owner",
"total_tracks",
"id",
"image",
"uri"
)
def __init__(self, data: dict, tracks: List[Track]) -> None:
self.name = data.get('name', 'Unknown')
self.tracks = tracks
self.owner = data["owner"]["display_name"]
self.total_tracks = data["tracks"]["total"]
self.id = data.get('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"]
self.name: str = data.get('name', 'Unknown')
self.tracks: List[Track] = tracks
self.owner: str = data["owner"]["display_name"]
self.total_tracks: int = data["tracks"]["total"]
self.id: str = data.get('id')
self.image: str = data["images"][0]["url"] if len(data.get("images", [])) else None
self.uri: str = data["external_urls"]["spotify"]
def __repr__(self) -> str:
return (