aboutsummaryrefslogtreecommitdiffstats
path: root/gallery_dl/exception.py
blob: 559fdd14e33c95fa867bff10c02fbf956739961e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# -*- coding: utf-8 -*-

# Copyright 2015-2023 Mike Fährmann
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.

"""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)


###############################################################################
# Extractor Errors ############################################################

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 resource:
                if " " not in resource:
                    resource = f"this {resource}"
                resource = f" to access {resource}"
            else:
                resource = ""

            message = f" ('{message}')" if message else ""
            message = f"{auth} needed{resource}{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)"


###############################################################################
# User Input ##################################################################

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"""


###############################################################################
# Control Flow ################################################################

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"""