""" Module which defines custom exception and enumerations used when instiating and re-raising those exceptions. """ from __future__ import annotations from typing import TYPE_CHECKING, Literal from enum import Enum try: from enum import StrEnum except ImportError: class StrEnum(str, Enum): def __str__(self) -> str: return str(self.value) if TYPE_CHECKING: from rvc_logic.typing_extra import StrPath, TrainingSampleRate class Entity(StrEnum): """Enumeration of entities that can be provided.""" # General entities FILE = "file" FILES = "files" DIRECTORY = "directory" DIRECTORIES = "directories" # Model entities MODEL_NAME = "model name" MODEL_NAMES = "model names" UPLOAD_NAME = "upload name" MODEL = "model" VOICE_MODEL = "voice model" TRAINING_MODEL = "training model" CUSTOM_EMBEDDER_MODEL = "custom embedder model" CUSTOM_PRETRAINED_MODEL = "custom pretrained model" GENERATOR = "generator" DISCRIMINATOR = "discriminator" MODEL_FILE = "model file" MODEL_BIN_FILE = "pytorch_model.bin file" CONFIG_JSON_FILE = "config.json file" # Audio entities AUDIO_TRACK = "audio track" AUDIO_TRACK_GAIN_PAIRS = "pairs of audio track and gain" VOICE_TRACK = "voice track" SPEECH_TRACK = "speech track" VOCALS_TRACK = "vocals track" SONG_DIR = "song directory" DATASET = "dataset" DATASETS = "datasets" DATASET_NAME = "dataset name" DATASET_FILE_LIST = "dataset file list" PREPROCESSED_AUDIO_DATASET_FILES = "preprocessed dataset audio files" # Source entities SOURCE = "source" URL = "URL" # GPU entities GPU_IDS = "GPU IDs" # Config entities CONFIG = "configuration" CONFIG_NAME = "configuration name" CONFIG_NAMES = "configuration names" EVENT = "event" COMPONENT = "component" AudioFileEntity = Literal[ Entity.AUDIO_TRACK, Entity.VOICE_TRACK, Entity.SPEECH_TRACK, Entity.VOCALS_TRACK, Entity.FILE, ] AudioDirectoryEntity = Literal[Entity.SONG_DIR, Entity.DATASET, Entity.DIRECTORY] ModelEntity = Literal[ Entity.MODEL, Entity.VOICE_MODEL, Entity.TRAINING_MODEL, Entity.CUSTOM_EMBEDDER_MODEL, Entity.CUSTOM_PRETRAINED_MODEL, ] ConfigEntity = Literal[Entity.EVENT, Entity.COMPONENT] class Location(StrEnum): """Enumeration of locations where entities can be found.""" # Audio locations AUDIO_ROOT = "the root of the audio base directory" INTERMEDIATE_AUDIO_ROOT = "the root of the intermediate audio base directory" SPEECH_AUDIO_ROOT = "the root of the speech audio directory" TRAINING_AUDIO_ROOT = "the root of the training audio directory" OUTPUT_AUDIO_ROOT = "the root of the output audio directory" # Model locations EXTRACTED_ZIP_FILE = "extracted zip file" class Step(StrEnum): """Enumeration of steps that can be run.""" DATASET_PREPROCESSING = "dataset preprocessing" FEATURE_EXTRACTION = "feature extraction" class UIMessage(StrEnum): """ Enumeration of messages that can be displayed in the UI in place of core exception messages. """ # General messages NO_UPLOADED_FILES = "No files selected." # Audio messages NO_AUDIO_TRACK = "No audio tracks provided." NO_SPEECH_AUDIO_FILES = ( "No files selected. Please select one or more speech audio files to delete." ) NO_OUTPUT_AUDIO_FILES = ( "No files selected. Please select one or more output audio files to delete." ) NO_SONG_DIR = "No song directory selected." NO_SONG_DIRS = ( "No song directories selected. Please select one or more song directories" " containing intermediate audio files to delete." ) NO_DATASETS = ( "No datasets selected. Please select one or more datasets containing audio" " files to delete." ) # Model messages NO_MODEL = "No model selected." NO_MODELS = "No models selected." NO_VOICE_MODEL = "No voice model selected." NO_VOICE_MODELS = "No voice models selected." NO_TRAINING_MODELS = "No training models selected." NO_CUSTOM_EMBEDDER_MODEL = "No custom embedder model selected." NO_CUSTOM_EMBEDDER_MODELS = "No custom embedder models selected." NO_CUSTOM_PRETRAINED_MODELS = "No custom pretrained models selected." NO_CUSTOM_PRETRAINED_MODEL = "No custom pretrained model selected." # Source messages NO_AUDIO_SOURCE = ( "No source provided. Please provide a valid Youtube URL, local audio file" " or song directory." ) NO_TEXT_SOURCE = ( "No source provided. Please provide a valid text string or path to a text file." ) # GPU messages NO_GPUS = "No GPUs selected." # Config messages NO_CONFIG = "No configuration selected." NO_CONFIGS = "No configurations selected." class NotProvidedError(ValueError): """Raised when an entity is not provided.""" def __init__(self, entity: Entity, ui_msg: UIMessage | None = None) -> None: """ Initialize a NotProvidedError instance. Exception message will be formatted as: "No `` provided." Parameters ---------- entity : Entity The entity that was not provided. ui_msg : UIMessage, default=None Message which, if provided, is displayed in the UI instead of the default exception message. """ super().__init__(f"No {entity} provided.") self.ui_msg = ui_msg class NotFoundError(OSError): """Raised when an entity is not found in a given location.""" def __init__( self, entity: Entity, location: StrPath | Location, is_path: bool = True, ) -> None: """ Initialize a NotFoundError instance. Exception message will be formatted as: "`` not found `(`in `|` at:`)` ``." Parameters ---------- entity : Entity The entity that was not found. location : StrPath | Location The location where the entity was not found. is_path : bool, default=True Whether the location is a path to the entity. """ proposition = "at:" if is_path else "in" entity_cap = entity.capitalize() if not entity.isupper() else entity super().__init__( f"{entity_cap} not found {proposition} {location}", ) class EntityNotFoundError(OSError): """Raised when an entity is not found.""" def __init__(self, entity: Entity, name: str) -> None: r""" Initialize an EntityNotFoundError instance. Exception message will be formatted as: "`` with name '``' not found." Parameters ---------- entity : Entity The entity that was not found. name : str The name of the entity that was not found. """ super().__init__(f"{entity.capitalize()} with name '{name}' not found.") class ModelNotFoundError(EntityNotFoundError): """Raised when an model with a given name is not found.""" def __init__(self, entity: ModelEntity, name: str) -> None: r""" Initialize a ModelNotFoundError instance. Exception message will be formatted as: '`` with name "``" not found.' Parameters ---------- entity : ModelEntity The model entity that was not found. name : str The name of the model that was not found. """ super().__init__(entity, name) class ConfigNotFoundError(EntityNotFoundError): """Raised when a configuration with a given name is not found.""" def __init__(self, name: str) -> None: r""" Initialize a ConfigNotFoundError instance. Exception message will be formatted as: 'Configuration with name '``' not found.' Parameters ---------- name : str The name of the configuration that was not found. """ super().__init__(Entity.CONFIG, name) class PretrainedModelNotAvailableError(OSError): """Raised when a pretrained model is not available.""" def __init__( self, name: str, sample_rate: TrainingSampleRate | None = None, download: bool = True, ) -> None: r""" Initialize a PretrainedModelNotAvailableError instance. Exception message will be formatted as: 'Pretrained model with name "``" [and sample rate ``] is not available [for download].' Parameters ---------- name : str The name of the pretrained model that is not available for download. sample_rate : TrainingSampleRate, optional The sample rate of the pretrained model that is not available. download : bool, default=True Whether the pretrained model is not available for download or not available on disk. """ suffix = f" and sample rate {sample_rate}" if sample_rate else "" second_suffix = " for download" if download else "" super().__init__( f"Pretrained model with name '{name}'{suffix} is not" f" available{second_suffix}.", ) class PretrainedModelIncompatibleError(OSError): """ Raised when a pretrained model is incompatible with a given sample rate. """ def __init__(self, name: str, sample_rate: TrainingSampleRate) -> None: r""" Initialize an IncompatiblePretrainedModelError instance. Exception message will be formatted as: 'Pretrained model with name "``" is incompatible with sample rate ``.' Parameters ---------- name : str The name of the pretrained model that is incompatible with a given sample rate. sample_rate : TrainingSampleRate The sample rate that the pretrained model is incompatible with. """ super().__init__( f"Pretrained model with name '{name}' is incompatible with sample rate" f" {sample_rate}.", ) class GPUNotFoundError(OSError): """Raised when a GPU with a given id is not found.""" def __init__(self, device_id: int | None = None) -> None: r""" Initialize a GPUNotFoundError instance. Exception message will be formatted as: 'GPU with id `` not found.' Parameters ---------- device_id : int, optional The id of a GPU that is not found. """ super().__init__( f"No GPU with id {device_id} found.", ) class ModelAsssociatedEntityNotFoundError(OSError): """Raised when an entity associated with a model is not found.""" def __init__( self, entity: Entity, model_name: str, required_step: Step | None = None, ) -> None: r""" Initialize a ModelAsssociatedEntityNotFoundError instance. Exception message will be formatted as: 'No `` associated with the model with name "``". [Please run `` first.]' Parameters ---------- entity : Entity The entity that is not associated with the model. model_name : str The name of the model that the entity is not associated with. required_step : Step | None, default=None The required step that needs to be run before will be associated with the model. """ suffix = f"Please run {required_step} first." if required_step else "" super().__init__( f"No {entity.capitalize()} associated with the model with name" f" {model_name}. {suffix}", ) class EntityExistsError(OSError): """Raised when an entity already exists.""" def __init__(self, entity: Entity, name: str) -> None: r""" Initialize an EntityExistsError instance. Exception message will be formatted as: '`` with name '``' already exists. Please provide a different name for your {entity}.' Parameters ---------- entity : Entity The entity that already exists. name : str The name of the entity that already exists. """ super().__init__( f"{entity.capitalize()} with name '{name}' already exists. Please provide a" f" different name for your {entity}.", ) class ModelExistsError(EntityExistsError): """Raised when a model already exists.""" def __init__(self, entity: ModelEntity, name: str) -> None: r""" Initialize a ModelExistsError instance. Exception message will be formatted as: '`` with name "``" already exists. Please provide a different name for your {entity}.' Parameters ---------- entity : ModelEntity The model entity that already exists. name : str The name of the model that already exists. """ super().__init__(entity, name) class ConfigExistsError(EntityExistsError): """Raised when a configuration already exists.""" def __init__(self, name: str) -> None: r""" Initialize a ConfigExistsError instance. Exception message will be formatted as: 'Configuration with name '``' already exists. Please provide a different name for your {entity}.' Parameters ---------- name : str The name of the configuration that already exists. """ super().__init__(Entity.CONFIG, name) class PretrainedModelExistsError(OSError): """ Raised when a pretrained model with a given name and sample rate already exists. """ def __init__(self, name: str, sample_rate: TrainingSampleRate) -> None: r""" Initialize a PretrainedModelExistsError instance. Exception message will be formatted as: 'Pretrained model with name "``" and sample rate `` already exists.' Parameters ---------- name : str The name of the pretrained model that already exists. sample_rate : TrainingSampleRate The sample rate of the pretrained model that already exists. """ super().__init__( f"Pretrained model with name '{name}' and sample rate {sample_rate} already" " exists.", ) class InvalidLocationError(OSError): """Raised when an entity is in a wrong location.""" def __init__(self, entity: Entity, location: Location, path: StrPath) -> None: r""" Initialize an InvalidLocationError instance. Exception message will be formatted as: "`` should be located in `` but found at: ``" Parameters ---------- entity : Entity The entity that is in a wrong location. location : Location The correct location for the entity. path : StrPath The path to the entity. """ entity_cap = entity.capitalize() if not entity.isupper() else entity super().__init__( f"{entity_cap} should be located in {location} but found at: {path}", ) class HttpUrlError(OSError): """Raised when a HTTP-based URL is invalid.""" def __init__(self, url: str) -> None: """ Initialize a HttpUrlError instance. Exception message will be formatted as: "Invalid HTTP-based URL: ``" Parameters ---------- url : str The invalid HTTP-based URL. """ super().__init__( f"Invalid HTTP-based URL: {url}", ) class YoutubeUrlError(OSError): """ Raised when an URL does not point to a YouTube video or , potentially, a Youtube playlist. """ def __init__(self, url: str, playlist: bool) -> None: """ Initialize a YoutubeURlError instance. Exception message will be formatted as: "URL does not point to a YouTube video `[`or playlist`]`: ``" Parameters ---------- url : str The URL that does not point to a YouTube video or playlist. playlist : bool Whether the URL might point to a YouTube playlist. """ suffix = " or playlist" if playlist else "" super().__init__( f"Not able to access Youtube video{suffix} at: {url}", ) class UploadLimitError(ValueError): """Raised when the upload limit for an entity is exceeded.""" def __init__(self, entity: Entity, limit: str | float) -> None: """ Initialize an UploadLimitError instance. Exception message will be formatted as: "At most `` `` can be uploaded." Parameters ---------- entity : Entity The entity for which the upload limit was exceeded. limit : str The upload limit. """ super().__init__(f"At most {limit} {entity} can be uploaded.") class UploadTypeError(ValueError): """ Raised when one or more uploaded entities have an invalid type. """ def __init__( self, entity: Entity, valid_types: list[str], type_class: Literal["formats", "names"], multiple: bool, ) -> None: """ Initialize an UploadTypeError instance. Exception message will be formatted as: "Only `` with the following `` can be uploaded `(`by themselves | together`)`: ``." Parameters ---------- entity : Entity The entity with an invalid type that was uploaded. valid_types : list[str] The valid types for the entity that was uploaded. type_class : Literal["formats", "names"] The name for the class of valid types. multiple : bool Whether multiple instances of the entity were uploaded. """ suffix = "by themselves" if not multiple else "together (at most one of each)" super().__init__( f"Only {entity} with the following {type_class} can be uploaded {suffix}:" f" {', '.join(valid_types)}.", ) class InvalidAudioFormatError(ValueError): """Raised when an audio file has an invalid format.""" def __init__(self, path: StrPath, formats: list[str]) -> None: """ Initialize an InvalidAudioFormatError instance. Exception message will be formatted as: "Invalid audio file format: ``. Supported formats are: ``." Parameters ---------- path : StrPath The path to the audio file with an invalid format. formats : list[str] Supported audio formats. """ super().__init__( f"Invalid audio file format: {path}. Supported formats are:" f" {', '.join(formats)}.", ) class NotInstantiatedError(ValueError): """Raised when an entity is not instantiated.""" def __init__(self, entity: Entity) -> None: """ Initialize a NotInstantiatedError instance. Exception message will be formatted as: "`` has not been instantiated." """ super().__init__(f"{entity} has not been instantiated.") class ComponentNotInstatiatedError(NotInstantiatedError): """Raised when a component is not instantiated.""" def __init__(self) -> None: """ Initialize a ComponentNotInstantiatedError instance. Exception message will be formatted as: "Component has not been instantiated." """ super().__init__(Entity.COMPONENT) class EventNotInstantiatedError(NotInstantiatedError): """Raised when an event is not instantiated.""" def __init__(self) -> None: """ Initialize a EventNotInstantiatedError instance. Exception message will be formatted as: "Event has not been instantiated." """ super().__init__(Entity.EVENT)