Fixed bugs and optimized code

This commit is contained in:
Choco
2025-01-07 15:13:06 +08:00
parent 5a22a930f5
commit 6dfedf0e44
4 changed files with 40 additions and 37 deletions

View File

@@ -824,9 +824,6 @@ class Player(VoiceProtocol):
if self.is_paused: if self.is_paused:
await self.set_pause(True) await self.set_pause(True)
if self.volume != 100:
await self.set_volume(self.volume)
async def get_recommendations(self, *, track: Optional[Track] = None) -> bool: async def get_recommendations(self, *, track: Optional[Track] = None) -> bool:
"""Get recommendations from Youtube or Spotify.""" """Get recommendations from Youtube or Spotify."""

View File

@@ -566,12 +566,12 @@ class NodePool:
This option is preferred if you want to choose the best node This option is preferred if you want to choose the best node
from a multi-node setup using either the node's latency from a multi-node setup using either the node's latency
or the node's voice region. or the node's voice region.
Use NodeAlgorithm.by_ping if you want to get the best node Use NodeAlgorithm.BY_PING if you want to get the best node
based on the node's latency. based on the node's latency.
Use NodeAlgorithm.by_region if you want to get the best node Use NodeAlgorithm.by_region if you want to get the best node
based on the node's voice region. This method will only work based on the node's voice region. This method will only work
if you set a voice region when you create a node. if you set a voice region when you create a node.
Use NodeAlgorithm.by_players if you want to get the best node Use NodeAlgorithm.BY_PLAYERS if you want to get the best node
based on how players it has. This method will return a node with based on how players it has. This method will return a node with
the least amount of players the least amount of players
""" """
@@ -580,11 +580,11 @@ class NodePool:
if not available_nodes: if not available_nodes:
raise NoNodesAvailable("There are no nodes available.") raise NoNodesAvailable("There are no nodes available.")
if algorithm == NodeAlgorithm.by_ping: if algorithm == NodeAlgorithm.BY_PING:
tested_nodes = {node: node.latency for node in available_nodes} tested_nodes = {node: node.latency for node in available_nodes}
return min(tested_nodes, key=tested_nodes.get) return min(tested_nodes, key=tested_nodes.get)
elif algorithm == NodeAlgorithm.by_players: elif algorithm == NodeAlgorithm.BY_PLAYERS:
tested_nodes = {node: len(node.players.keys()) for node in available_nodes} tested_nodes = {node: len(node.players.keys()) for node in available_nodes}
return min(tested_nodes, key=tested_nodes.get) return min(tested_nodes, key=tested_nodes.get)

View File

@@ -67,6 +67,7 @@ class Client:
self._categories: List[Category] = [] self._categories: List[Category] = []
async def _fetch_bearer_token(self) -> None: async def _fetch_bearer_token(self) -> None:
"""Fetches and stores a bearer token for API authentication."""
if self._client_id and self._client_secret: if self._client_id and self._client_secret:
url, data = GRANT_URL, {"grant_type": "client_credentials"} url, data = GRANT_URL, {"grant_type": "client_credentials"}
else: else:
@@ -90,6 +91,7 @@ class Client:
self._bearer_headers = {"Authorization": f"Bearer {self._bearer_token}"} self._bearer_headers = {"Authorization": f"Bearer {self._bearer_token}"}
async def get_request(self, url: str) -> Dict: async def get_request(self, url: str) -> Dict:
"""Performs a GET request to the specified URL with authorization headers."""
if not self._bearer_token or time.time() >= self._expiry: if not self._bearer_token or time.time() >= self._expiry:
await self._fetch_bearer_token() await self._fetch_bearer_token()
@@ -102,68 +104,72 @@ class Client:
return await resp.json() return await resp.json()
async def track_search(self, query: str, track: str = "track", limit: int = 10) -> List[Track]: async def track_search(self, query: str, track: str = "track", limit: int = 10) -> List[Track]:
"""Searches for tracks based on the provided query and returns a list of Track objects."""
request_url = SEARCH_URL.format(query=query, type=track, limit=limit) request_url = SEARCH_URL.format(query=query, type=track, limit=limit)
data = await self.get_request(request_url) data = await self.get_request(request_url)
return [ Track(track) for track in data['tracks']['items'] ] return [ Track(track) for track in data['tracks']['items'] ]
async def similar_track(self, seed_tracks: str, *, limit: int = 10) -> List[Track]: async def similar_track(self, seed_tracks: str, *, limit: int = 10) -> List[Track]:
"""Retrieves tracks similar to the provided seed tracks and returns them as Track objects."""
request_url = SUGGESTION_URL.format(limit=limit, seed_tracks=seed_tracks) request_url = SUGGESTION_URL.format(limit=limit, seed_tracks=seed_tracks)
data = await self.get_request(request_url) data = await self.get_request(request_url)
return [ Track(track) for track in data['tracks'] ] return [ Track(track) for track in data['tracks'] ]
async def search(self, *, query: str) -> Union[Track, Album, Playlist]: async def search(self, *, query: str) -> Union[Track, Album, Playlist]:
"""Searches for an item (track, album, artist, or playlist) by query and returns the corresponding object."""
result = SPOTIFY_URL_REGEX.match(query) result = SPOTIFY_URL_REGEX.match(query)
spotify_type = result.group("type")
spotify_id = result.group("id")
if not result: if not result:
raise InvalidSpotifyURL("The Spotify link provided is not valid.") raise InvalidSpotifyURL("The Spotify link provided is not valid.")
spotify_type = result.group("type")
spotify_id = result.group("id")
request_url = REQUEST_URL.format(type=spotify_type, id=spotify_id) request_url = REQUEST_URL.format(type=spotify_type, id=spotify_id)
if isArtist := (spotify_type == "artist"): if isArtist := (spotify_type == "artist"):
request_url += "/top-tracks?market=US" request_url += "/top-tracks?market=US"
data = await self.get_request(request_url) data = await self.get_request(request_url)
if spotify_type == "track": if spotify_type == "track":
return Track(data) return Track(data)
elif spotify_type == "album": elif spotify_type == "album":
return Album(data) return Album(data)
elif isArtist: elif isArtist:
return Artist(data) return Artist(data)
else:
tracks = [ tracks = [
Track(track["track"])
for track in data["tracks"]["items"] if track.get("track") is not None
]
if not tracks:
raise SpotifyRequestException("This playlist is empty and therefore cannot be queued.")
next_page_url = data["tracks"].get("next")
while next_page_url:
next_data = await self.get_request(next_page_url)
tracks.extend([
Track(track["track"]) Track(track["track"])
for track in data["tracks"]["items"] if track["track"] is not None for track in next_data.get("items", []) if track.get("track") is not None
] ])
if not tracks: next_page_url = next_data.get("next")
raise SpotifyRequestException("This playlist is empty and therefore cannot be queued.")
next_page_url = data["tracks"]["next"]
while next_page_url is not None: return Playlist(data, tracks)
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()
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)
async def get_categories(self) -> List[Category]: async def get_categories(self) -> List[Category]:
"""Fetches and returns available music categories from the Spotify API."""
if not self._categories: if not self._categories:
request_url = f"{BASE_URL}browse/categories" request_url = f"{BASE_URL}browse/categories"
data = await self.get_request(request_url)
self._categories = [Category(item) for item in data.get("items", [])] while request_url:
data = await self.get_request(request_url)
items = data.get("categories", {}).get("items", [])
self._categories.extend(Category(item) for item in items)
request_url = data.get("categories", {}).get("next")
return self._categories return self._categories
async def close(self) -> None: async def close(self) -> None:
"""Closes the HTTP session used for making API requests."""
await self.session.close() await self.session.close()

View File

@@ -129,7 +129,7 @@ class Category:
self.href: str = data.get("href") self.href: str = data.get("href")
self.id: str = data.get("id") self.id: str = data.get("id")
self.name: str = data.get("name") self.name: str = data.get("name")
self.icon: str = data.get("icon", [])[0].get("url") self.icon: str = data.get("icons", [{}])[0].get("url")
def __repr__(self) -> str: def __repr__(self) -> str:
return (f"<Voicelink.spotify.Category name={self.name} id={self.id}") return (f"<Voicelink.spotify.Category name={self.name} id={self.id}")