diff --git a/src/pyramid/connector/deezer/downloader.py b/src/pyramid/connector/deezer/downloader.py index 47ab146..e86e375 100644 --- a/src/pyramid/connector/deezer/downloader.py +++ b/src/pyramid/connector/deezer/downloader.py @@ -8,12 +8,11 @@ from pyramid.connector.deezer.downloader_progress_bar import DownloaderProgressBar from pyramid.connector.deezer.py_deezer import PyDeezer from pyramid.data.track import Track -from pyramid.tools.generate_token import DeezerTokenProvider, DeezerTokenEmptyException, DeezerTokenOverflowException +from pyramid.tools.generate_token import DeezerTokenProvider from pydeezer.constants import track_formats -from pydeezer.exceptions import LoginError from urllib3.exceptions import MaxRetryError -from pyramid.data.exceptions import CustomException +from pyramid.data.exceptions import CustomException, DeezerTokensUnavailableException, DeezerTokenInvalidException, DeezerTokenOverflowException class DeezerDownloader: @@ -28,15 +27,6 @@ def __init__(self, folder: str, arl: Optional[str] = None): os.makedirs(self.folder_path, exist_ok=True) self.__deezer_dl_api = None - async def check_credentials(self): - if not self.__deezer_dl_api: - raise Exception("deezer_dl_api not init") - try: - await self.__deezer_dl_api.get_user_data() - return self.__deezer_dl_api.user - except LoginError as err: - raise err # Arl is invalid - async def dl_track_by_id(self, track_id) -> Track | None: client = await self._get_client() # try: @@ -96,25 +86,21 @@ async def __dl_track(self, track_info, file_name: str) -> bool: return False async def _get_client(self) -> PyDeezer: - if not self.__deezer_dl_api: - self.__deezer_dl_api = await self._define_client() - return self.__deezer_dl_api + return await self._define_client() async def _define_client(self) -> PyDeezer: - last_err = None + last_err_local = None if self.__arls: for arl in self.__arls: deezer_dl_api = PyDeezer(arl) try: await deezer_dl_api.get_user_data() return deezer_dl_api - except LoginError as err: - last_err = err + except DeezerTokenInvalidException as err: + last_err_local = err continue - if last_err is not None: - raise last_err - last_err = None + last_err_remote = None already_overflow = False while self.__token_provider.count_valids_tokens() != 0: try: @@ -123,22 +109,32 @@ async def _define_client(self) -> PyDeezer: await deezer_dl_api.get_user_data() return deezer_dl_api - except DeezerTokenEmptyException as err: - last_err = err - break + except DeezerTokenInvalidException as err: + last_err_remote = err + continue except DeezerTokenOverflowException as err: - last_err = err + last_err_remote = err if already_overflow is True: break already_overflow = True self.__token_provider = DeezerTokenProvider() continue - except LoginError as err: - last_err = err - continue + except DeezerTokensUnavailableException as err: + last_err_remote = err + break + + if last_err_local is not None: + tb = traceback.TracebackException.from_exception(last_err_local) + formatted_tb = ''.join(tb.format()) + logging.warning("Failed to fetch valid Deezer client from local\n%s", formatted_tb) + + if last_err_remote is not None: + tb = traceback.TracebackException.from_exception(last_err_remote) + formatted_tb = ''.join(tb.format()) + logging.warning("Failed to fetch valid Deezer client from remote\n%s", formatted_tb) - if last_err is not None: - raise last_err - raise Exception("Unknown exit") + if last_err_remote is not None and last_err_local is not None: + raise last_err_remote + raise Exception("Unknown func exit") diff --git a/src/pyramid/connector/deezer/py_deezer.py b/src/pyramid/connector/deezer/py_deezer.py index f7087fd..bdcd880 100644 --- a/src/pyramid/connector/deezer/py_deezer.py +++ b/src/pyramid/connector/deezer/py_deezer.py @@ -9,7 +9,7 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from pyramid.data.exceptions import CustomException +from pyramid.data.exceptions import CustomException, DeezerTokenInvalidException with warnings.catch_warnings(): warnings.simplefilter("ignore") @@ -17,7 +17,7 @@ from pydeezer import Deezer, util from pydeezer.constants import api_methods, api_urls, networking_settings, track_formats -from pydeezer.exceptions import APIRequestError, DownloadLinkDecryptionError, LoginError +from pydeezer.exceptions import APIRequestError, DownloadLinkDecryptionError from pydeezer.ProgressHandler import BaseProgressHandler, DefaultProgressHandler @@ -289,7 +289,7 @@ async def get_user_data(self): self.token = data["checkForm"] if not data["USER"]["USER_ID"]: - raise LoginError(f"Arl ${self.arl} is invalid.") + raise DeezerTokenInvalidException("ARL invalid : %s", self.arl) raw_user = data["USER"] diff --git a/src/pyramid/connector/discord/guild_cmd_tools.py b/src/pyramid/connector/discord/guild_cmd_tools.py index b918338..3b12f56 100644 --- a/src/pyramid/connector/discord/guild_cmd_tools.py +++ b/src/pyramid/connector/discord/guild_cmd_tools.py @@ -1,14 +1,16 @@ +import logging +import traceback from typing import Union from discord.abc import Messageable from discord import Member, StageChannel, TextChannel, User, VoiceChannel, VoiceClient, VoiceState +from pyramid.data.exceptions import DeezerTokenException from pyramid.data.track import Track, TrackMinimal, TrackMinimalDeezer from pyramid.data.guild_data import GuildData from pyramid.data.tracklist import TrackList from pyramid.connector.discord.guild_queue import GuildQueue from pyramid.data.functional.messages.message_sender_queued import MessageSenderQueued from pyramid.data.functional.engine_source import EngineSource -from pyramid.tools.generate_token import DeezerTokenEmptyException class GuildCmdTools: @@ -127,11 +129,11 @@ async def _execute_play_multiple( for i, track in enumerate(tracks): try: track_downloaded: Track | None = await self.engine_source.download_track(track) - except DeezerTokenEmptyException: - ms.add_message("😥 There are currently no music accounts available. Try again later.") + except DeezerTokenException as err: + ms.add_message("😥 **There are currently no music accounts available**. Try again later.") return False - except Exception: - ms.add_message("😓 Unable to connect to music API. Try again later.") + except Exception as err: + ms.add_message("😓 **Unable to connect to music API**. Try again later.") return False if not track_downloaded: ms.add_message(content=f"ERROR > **{track.get_full_name()}** can't be downloaded.") @@ -168,11 +170,11 @@ async def _execute_play( try: track_downloaded: Track | None = await self.engine_source.download_track(track) - except DeezerTokenEmptyException: - ms.add_message("😥 There are currently no music accounts available. Try again later.") + except DeezerTokenException as err: + ms.add_message("😥 **There are currently no music accounts available**. Try again later.") return False - except Exception: - ms.add_message("😓 Unable to connect to music API. Try again later.") + except Exception as err: + ms.add_message("😓 **Unable to connect to music API**. Try again later.") return False if not track_downloaded: diff --git a/src/pyramid/data/exceptions.py b/src/pyramid/data/exceptions.py index 0b0e40f..76f2107 100644 --- a/src/pyramid/data/exceptions.py +++ b/src/pyramid/data/exceptions.py @@ -1,7 +1,7 @@ class CustomException(Exception): def __init__(self, *args: object): - self.msg = str(args[0]) % args[1:] - super().__init__(*args) + msg = str(args[0]) % args[1:] + super().__init__(msg) class DiscordMessageException(CustomException): @@ -14,3 +14,15 @@ class EngineSourceNotFoundException(DiscordMessageException): class TrackNotFoundException(DiscordMessageException): pass + +class DeezerTokenException(CustomException): + pass + +class DeezerTokenInvalidException(DeezerTokenException): + pass + +class DeezerTokensUnavailableException(DeezerTokenException): + pass + +class DeezerTokenOverflowException(DeezerTokenException): + pass diff --git a/src/pyramid/tools/generate_token.py b/src/pyramid/tools/generate_token.py index 53de4cf..11aa6e2 100644 --- a/src/pyramid/tools/generate_token.py +++ b/src/pyramid/tools/generate_token.py @@ -4,11 +4,7 @@ from typing import Optional, List import requests -class DeezerTokenEmptyException(Exception): - pass - -class DeezerTokenOverflowException(Exception): - pass +from pyramid.data.exceptions import DeezerTokensUnavailableException, DeezerTokenOverflowException class DeezerToken: def __init__(self, @@ -88,9 +84,9 @@ def next(self) -> DeezerToken: self.generate(True) token = self.pop_token() if not token and not refreshed: - raise DeezerTokenOverflowException("No tokens are available, you can regenerate them.") + raise DeezerTokenOverflowException("No valids tokens are available in cache, you can regenerate them.") if not token: - raise DeezerTokenEmptyException("No tokens are available") + raise DeezerTokensUnavailableException("No valids tokens are available") return token def _fetch(self) -> None: