"""Exception classes used by gallery-dl
Class Hierarchy:
Exception
└── GalleryDLException
├── ExtractionError
│ ├── HttpError
│ │ └── ChallengeError
│ ├── AuthorizationError
│ │ └── AuthRequired
│ ├── AuthenticationError
│ └── NotFoundError
├── InputError
│ ├── FormatError
│ │ ├── FilenameFormatError
│ │ └── DirectoryFormatError
│ ├── FilterError
│ ├── InputFileError
│ └── NoExtractorError
└── ControlException
├── StopExtraction
├── AbortExtraction
├── TerminateExtraction
└── RestartExtraction
"""
class GalleryDLException(Exception):
"""Base class for GalleryDL exceptions"""
default = None
msgfmt = None
code = 1
def __init__(self, message=None, fmt=True):
if not message:
message = self.default
elif isinstance(message, Exception):
message = f"{message.__class__.__name__}: {message}"
if fmt and self.msgfmt is not None:
message = self.msgfmt.replace("{}", message)
self.message = message
Exception.__init__(self, message)
class ExtractionError(GalleryDLException):
"""Base class for exceptions during information extraction"""
code = 4
class HttpError(ExtractionError):
"""HTTP request during data extraction failed"""
default = "HTTP request failed"
def __init__(self, message="", response=None):
self.response = response
if response is None:
self.status = 0
else:
self.status = response.status_code
if not message:
message = (f"'{response.status_code} {response.reason}' "
f"for '{response.url}'")
ExtractionError.__init__(self, message)
class ChallengeError(HttpError):
code = 8
def __init__(self, challenge, response):
message = (
f"{challenge} ({response.status_code} {response.reason}) "
f"for '{response.url}'")
HttpError.__init__(self, message, response)
class AuthenticationError(ExtractionError):
"""Invalid or missing login credentials"""
default = "Invalid login credentials"
code = 16
class AuthorizationError(ExtractionError):
"""Insufficient privileges to access a resource"""
default = "Insufficient privileges to access this resource"
code = 16
class AuthRequired(AuthorizationError):
default = "Account credentials required"
def __init__(self, auth=None, resource="resource", message=None):
if auth:
if not isinstance(auth, str):
auth = " or ".join(auth)
if " " not in resource:
resource = "this " + resource
if message is None:
message = (f"{auth} needed to access {resource}")
else:
message = (f"{auth} needed to access {resource} "
f"('{message}')")
AuthorizationError.__init__(self, message)
class NotFoundError(ExtractionError):
"""Requested resource (gallery/image) could not be found"""
msgfmt = "Requested {} could not be found"
default = "resource (gallery/image)"
class InputError(GalleryDLException):
"""Error caused by user input and config options"""
code = 32
class FormatError(InputError):
"""Error while building output paths"""
class FilenameFormatError(FormatError):
"""Error while building output filenames"""
msgfmt = "Applying filename format string failed ({})"
class DirectoryFormatError(FormatError):
"""Error while building output directory paths"""
msgfmt = "Applying directory format string failed ({})"
class FilterError(InputError):
"""Error while evaluating a filter expression"""
msgfmt = "Evaluating filter expression failed ({})"
class InputFileError(InputError):
"""Error when parsing an input file"""
class NoExtractorError(InputError):
"""No extractor can handle the given URL"""
class ControlException(GalleryDLException):
code = 0
class StopExtraction(ControlException):
"""Stop data extraction"""
def __init__(self, target=None):
ControlException.__init__(self)
if target is None:
self.target = None
self.depth = 1
elif isinstance(target, int):
self.target = None
self.depth = target
elif target.isdecimal():
self.target = None
self.depth = int(target)
else:
self.target = target
self.depth = 128
class AbortExtraction(ExtractionError, ControlException):
"""Abort data extraction due to an error"""
class TerminateExtraction(ControlException):
"""Terminate data extraction"""
class RestartExtraction(ControlException):
"""Restart data extraction"""