voice-clone-rvc / rvc_logic /core /exceptions.py
Antigravity AI
Fix Python 3.10 compatibility: add StrEnum fallback
82c4d2e
"""
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 `<entity>` 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:
"`<entity>` not found `(`in `|` at:`)` `<location>`."
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:
"`<entity>` with name '`<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:
'`<entity>` with name "`<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 '`<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 "`<name>`"
[and sample rate `<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 "`<name>`" is incompatible with
sample rate `<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 `<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 `<entity>` associated with the model with name
"`<model_name>`". [Please run `<required_step>` 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:
'`<entity>` with name '`<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:
'`<entity>` with name "`<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 '`<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 "`<name>`" and sample rate
`<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:
"`<entity>` should be located in `<location>` but found at:
`<path>`"
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: `<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`]`:
`<url>`"
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 `<limit>` `<entity>` 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 `<entity>` with the following `<type_class>` can be
uploaded `(`by themselves | together`)`: `<types>`."
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: `<path>`. Supported formats are:
`<formats>`."
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:
"`<entity>` 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)