rename 'StopExtraction' to 'AbortExtraction'

for cases where StopExtraction was used to report errors
This commit is contained in:
Mike Fährmann
2025-07-09 21:07:28 +02:00
parent f17ed0569a
commit d8ef1d693f
57 changed files with 149 additions and 156 deletions

View File

@@ -351,13 +351,11 @@ def main():
else:
input_manager.success()
except exception.StopExtraction:
pass
except exception.TerminateExtraction:
pass
except exception.RestartExtraction:
log.debug("Restarting '%s'", url)
continue
except exception.ControlException:
pass
except exception.NoExtractorError:
log.error("Unsupported URL '%s'", url)
retval |= 64

View File

@@ -47,6 +47,7 @@ class GalleryDLException(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)
@@ -151,11 +152,6 @@ class ControlException(GalleryDLException):
class StopExtraction(ControlException):
"""Stop data extraction"""
def __init__(self, message=None, *args):
ControlException.__init__(self)
self.message = message % args if args else message
self.code = 1 if message else 0
class AbortExtraction(ExtractionError, ControlException):
"""Abort data extraction due to an error"""

View File

@@ -144,7 +144,7 @@ class Ao3WorkExtractor(Ao3Extractor):
page = response.text
if len(page) < 20000 and \
'<h2 class="landmark heading">Adult Content Warning</' in page:
raise exception.StopExtraction("Adult Content")
raise exception.AbortExtraction("Adult Content")
extr = text.extract_from(page)

View File

@@ -171,9 +171,8 @@ class ArcaliveAPI():
return data
self.log.debug("Server response: %s", data)
msg = data.get("message")
raise exception.StopExtraction(
"API request failed%s", ": " + msg if msg else "")
msg = f": {msg}" if (msg := data.get("message")) else ""
raise exception.AbortExtraction(f"API request failed{msg}")
def _pagination(self, endpoint, params, key):
while True:

View File

@@ -112,7 +112,7 @@ class BilibiliAPI():
if data["code"] != 0:
self.extractor.log.debug("Server response: %s", data)
raise exception.StopExtraction("API request failed")
raise exception.AbortExtraction("API request failed")
return data
@@ -140,8 +140,8 @@ class BilibiliAPI():
page, "window.__INITIAL_STATE__=", "};") + "}")
except Exception:
if "window._riskdata_" not in page:
raise exception.StopExtraction(
"%s: Unable to extract INITIAL_STATE data", article_id)
raise exception.AbortExtraction(
f"{article_id}: Unable to extract INITIAL_STATE data")
self.extractor.wait(seconds=300)
def user_favlist(self):
@@ -163,8 +163,9 @@ class BilibiliAPI():
if data["code"] != 0:
self.extractor.log.debug("Server response: %s", data)
raise exception.StopExtraction("API request failed,Are you login?")
raise exception.AbortExtraction(
"API request failed. Are you logges in?")
try:
return data["data"]["profile"]["mid"]
except Exception:
raise exception.StopExtraction("API request failed")
raise exception.AbortExtraction("API request failed")

View File

@@ -95,7 +95,7 @@ class BlueskyExtractor(Extractor):
uri = record["value"]["subject"]["uri"]
if "/app.bsky.feed.post/" in uri:
yield from self.api.get_post_thread_uri(uri, depth)
except exception.StopExtraction:
except exception.ControlException:
pass # deleted post
except Exception as exc:
self.log.debug(record, exc_info=exc)
@@ -579,7 +579,7 @@ class BlueskyAPI():
msg = f"{msg} ({response.status_code} {response.reason})"
self.extractor.log.debug("Server response: %s", response.text)
raise exception.StopExtraction(msg)
raise exception.AbortExtraction(msg)
def _pagination(self, endpoint, params,
key="feed", root=None, check_empty=False):

View File

@@ -381,7 +381,7 @@ class BoostyAPI():
else:
self.extractor.log.debug(response.text)
raise exception.StopExtraction("API request failed")
raise exception.AbortExtraction("API request failed")
def _pagination(self, endpoint, params, transform=None, key=None):
if "is_only_allowed" not in params and self.extractor.only_allowed:

View File

@@ -124,7 +124,7 @@ class BunkrAlbumExtractor(LolisafeAlbumExtractor):
pass
else:
if not DOMAINS:
raise exception.StopExtraction(
raise exception.AbortExtraction(
"All Bunkr domains require solving a CF challenge")
# select alternative domain
@@ -169,7 +169,7 @@ class BunkrAlbumExtractor(LolisafeAlbumExtractor):
info[-1], "%H:%M:%S %d/%m/%Y")
yield file
except exception.StopExtraction:
except exception.ControlException:
raise
except Exception as exc:
self.log.error("%s: %s", exc.__class__.__name__, exc)

View File

@@ -336,8 +336,8 @@ class Extractor():
if input is None:
input = output.TTY_STDIN
if not input:
raise exception.StopExtraction(
"User input required (%s)", prompt.strip(" :"))
raise exception.AbortExtraction(
f"User input required ({prompt.strip(' :')})")
def _get_auth_info(self):
"""Return authentication information as (username, password) tuple"""

View File

@@ -126,7 +126,7 @@ class DeviantartExtractor(Extractor):
self.group = False
elif group == "skip":
self.log.info("Skipping group '%s'", self.user)
raise exception.StopExtraction()
raise exception.AbortExtraction()
else:
self.subcategory = "group-" + self.subcategory
self.group = True
@@ -1373,7 +1373,7 @@ class DeviantartSearchExtractor(DeviantartExtractor):
response = self.request(url, params=params)
if response.history and "/users/login" in response.url:
raise exception.StopExtraction("HTTP redirect to login page")
raise exception.AbortExtraction("HTTP redirect to login page")
page = response.text
for dev in DeviantartDeviationExtractor.pattern.findall(

View File

@@ -165,7 +165,7 @@ class DiscordExtractor(Extractor):
yield from self.extract_channel(
channel["channel_id"], safe=True)
elif not safe:
raise exception.StopExtraction(
raise exception.AbortExtraction(
"This channel type is not supported."
)
except exception.HttpError as exc:

View File

@@ -59,7 +59,7 @@ class ExhentaiExtractor(Extractor):
def login(self):
"""Login and set necessary cookies"""
if self.LIMIT:
raise exception.StopExtraction("Image limit reached!")
raise exception.AbortExtraction("Image limit reached!")
if self.cookies_check(self.cookies_names):
return
@@ -178,7 +178,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
self.image_token = text.extr(gpage, 'hentai.org/s/', '"')
if not self.image_token:
self.log.debug("Page content:\n%s", gpage)
raise exception.StopExtraction(
raise exception.AbortExtraction(
"Failed to extract initial image token")
ipage = self._image_page()
else:
@@ -186,7 +186,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
part = text.extr(ipage, 'hentai.org/g/', '"')
if not part:
self.log.debug("Page content:\n%s", ipage)
raise exception.StopExtraction(
raise exception.AbortExtraction(
"Failed to extract gallery token")
self.gallery_token = part.split("/")[1]
gpage = self._gallery_page()
@@ -301,7 +301,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
data = self.request_json(self.api_url, method="POST", json=data)
if "error" in data:
raise exception.StopExtraction(data["error"])
raise exception.AbortExtraction(data["error"])
return data["gmetadata"][0]
@@ -326,8 +326,8 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
data["_fallback"] = self._fallback_1280(nl, self.image_num)
except IndexError:
self.log.debug("Page content:\n%s", page)
raise exception.StopExtraction(
"Unable to parse image info for '%s'", url)
raise exception.AbortExtraction(
f"Unable to parse image info for '{url}'")
data["num"] = self.image_num
data["image_token"] = self.key_start = extr('var startkey="', '";')
@@ -377,8 +377,8 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
nl, request["page"], imgkey)
except IndexError:
self.log.debug("Page content:\n%s", page)
raise exception.StopExtraction(
"Unable to parse image info for '%s'", url)
raise exception.AbortExtraction(
f"Unable to parse image info for '{url}'")
data["num"] = request["page"]
data["image_token"] = imgkey
@@ -401,7 +401,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
if " requires GP" in page:
gp = self.config("gp")
if gp == "stop":
raise exception.StopExtraction("Not enough GP")
raise exception.AbortExtraction("Not enough GP")
elif gp == "wait":
self.input("Press ENTER to continue.")
return response.url
@@ -463,7 +463,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
if not action or action == "stop":
ExhentaiExtractor.LIMIT = True
raise exception.StopExtraction(msg)
raise exception.AbortExtraction(msg)
self.log.warning(msg)
if action == "wait":

View File

@@ -219,17 +219,16 @@ class FacebookExtractor(Extractor):
res = self.request(url, **kwargs)
if res.url.startswith(self.root + "/login"):
raise exception.AuthenticationError(
"You must be logged in to continue viewing images." +
LEFT_OFF_TXT
raise exception.LoginRequires(
f"You must be logged in to continue viewing images."
f"{LEFT_OFF_TXT}"
)
if b'{"__dr":"CometErrorRoot.react"}' in res.content:
raise exception.StopExtraction(
"You've been temporarily blocked from viewing images. "
"\nPlease try using a different account, "
"using a VPN or waiting before you retry." +
LEFT_OFF_TXT
raise exception.AbortExtraction(
f"You've been temporarily blocked from viewing images.\n"
f"Please try using a different account, "
f"using a VPN or waiting before you retry.{LEFT_OFF_TXT}"
)
return res

View File

@@ -451,7 +451,7 @@ class FlickrAPI(oauth.OAuth1API):
raise exception.AuthenticationError(msg)
elif data["code"] == 99:
raise exception.AuthorizationError(msg)
raise exception.StopExtraction("API request failed: %s", msg)
raise exception.AbortExtraction(f"API request failed: {msg}")
return data
def _pagination(self, method, params, key="photos"):

View File

@@ -95,7 +95,7 @@ class GofileFolderExtractor(Extractor):
raise exception.NotFoundError("content")
if response["status"] == "error-passwordRequired":
raise exception.AuthorizationError("Password required")
raise exception.StopExtraction(
"%s failed (Status: %s)", endpoint, response["status"])
raise exception.AbortExtraction(
f"{endpoint} failed (Status: {response['status']})")
return response["data"]

View File

@@ -184,7 +184,7 @@ class IdolcomplexTagExtractor(IdolcomplexExtractor):
tags = self.tags.split()
if not self.logged_in and len(tags) > 4:
raise exception.StopExtraction(
raise exception.AbortExtraction(
"Non-members can only search up to 4 tags at once")
return {"search_tags": " ".join(tags)}

View File

@@ -152,4 +152,4 @@ class ImagechestAPI():
else:
self.extractor.log.debug(response.text)
raise exception.StopExtraction("API request failed")
raise exception.AbortExtraction("API request failed")

View File

@@ -31,7 +31,7 @@ class ImagefapExtractor(Extractor):
msg = text.extr(response.text, '<div class="mt-4', '<')
if msg:
msg = " ".join(msg.partition(">")[2].split())
raise exception.StopExtraction("'%s'", msg)
raise exception.AbortExtraction(f"'{msg}'")
self.log.warning("HTTP redirect to %s", response.url)
return response

View File

@@ -350,7 +350,7 @@ class InkbunnyAPI():
self.authenticate(invalidate=True)
continue
raise exception.StopExtraction(data.get("error_message"))
raise exception.AbortExtraction(data.get("error_message"))
def _pagination_search(self, params):
params["page"] = 1

View File

@@ -136,9 +136,9 @@ class InstagramExtractor(Extractor):
else:
page = None
if page:
raise exception.StopExtraction("HTTP redirect to %s page (%s)",
page, url.partition("?")[0])
if page is not None:
raise exception.AbortExtraction(
f"HTTP redirect to {page} page ({url.partition('?')[0]})")
www_claim = response.headers.get("x-ig-set-www-claim")
if www_claim is not None:
@@ -979,7 +979,7 @@ class InstagramGraphqlAPI():
self.user_id = api.user_id
def _unsupported(self, _=None):
raise exception.StopExtraction("Unsupported with GraphQL API")
raise exception.AbortExtraction("Unsupported with GraphQL API")
def highlights_tray(self, user_id):
query_hash = "d4d88dc1500312af6f937f7b804c68c3"
@@ -1065,9 +1065,10 @@ class InstagramGraphqlAPI():
if not info["has_next_page"]:
return extr._update_cursor(None)
elif not data["edges"]:
s = "" if self.extractor.item.endswith("s") else "s"
raise exception.StopExtraction(
"%s'%s posts are private", self.extractor.item, s)
user = self.extractor.item
s = "" if user.endswith("s") else "s"
raise exception.AbortExtraction(
f"{user}'{s} posts are private")
variables["after"] = extr._update_cursor(info["end_cursor"])

View File

@@ -136,7 +136,7 @@ class LofterAPI():
if info["meta"]["status"] != 200:
self.extractor.log.debug("Server response: %s", info)
raise exception.StopExtraction("API request failed")
raise exception.AbortExtraction("API request failed")
return info["response"]

View File

@@ -32,9 +32,9 @@ class LusciousExtractor(Extractor):
if response.status_code >= 400:
self.log.debug("Server response: %s", response.text)
raise exception.StopExtraction(
"GraphQL query failed ('%s %s')",
response.status_code, response.reason)
raise exception.AbortExtraction(
f"GraphQL query failed "
f"('{response.status_code} {response.reason}')")
return response.json()["data"]

View File

@@ -112,10 +112,10 @@ class MangadexChapterExtractor(MangadexExtractor):
data = self._transform(chapter)
if data.get("_external_url") and not data["count"]:
raise exception.StopExtraction(
"Chapter %s%s is not available on MangaDex and can instead be "
"read on the official publisher's website at %s.",
data["chapter"], data["chapter_minor"], data["_external_url"])
raise exception.AbortExtraction(
f"Chapter {data['chapter']}{data['chapter_minor']} is not "
f"available on MangaDex and can instead be read on the "
f"official publisher's website at {data['_external_url']}.")
yield Message.Directory, data
@@ -356,8 +356,8 @@ class MangadexAPI():
msg = ", ".join('{title}: "{detail}"'.format_map(error)
for error in response.json()["errors"])
raise exception.StopExtraction(
"%s %s (%s)", response.status_code, response.reason, msg)
raise exception.AbortExtraction(
f"{response.status_code} {response.reason} ({msg})")
def _pagination_chapters(self, endpoint, params=None, auth=False):
if params is None:

View File

@@ -176,8 +176,8 @@ class MangaparkMangaExtractor(MangaparkBase, Extractor):
not lang or data["lang"] == lang):
return data["id"]
raise exception.StopExtraction(
"'%s' does not match any available source", source)
raise exception.AbortExtraction(
f"'{source}' does not match any available source")
QUERIES = {

View File

@@ -315,10 +315,9 @@ class MastodonAPI():
if code < 400:
return response
if code == 401:
raise exception.StopExtraction(
"Invalid or missing access token.\n"
"Run 'gallery-dl oauth:mastodon:%s' to obtain one.",
self.extractor.instance)
raise exception.AbortExtraction(
f"Invalid or missing access token.\nRun 'gallery-dl oauth:"
f"mastodon:{self.extractor.instance}' to obtain one.")
if code == 404:
raise exception.NotFoundError()
if code == 429:
@@ -327,7 +326,7 @@ class MastodonAPI():
"%Y-%m-%dT%H:%M:%S.%fZ",
))
continue
raise exception.StopExtraction(response.json().get("error"))
raise exception.AbortExtraction(response.json().get("error"))
def _pagination(self, endpoint, params):
url = endpoint

View File

@@ -366,7 +366,7 @@ class NewgroundsExtractor(Extractor):
return
if "errors" in data:
msg = ", ".join(text.unescape(e) for e in data["errors"])
raise exception.StopExtraction(msg)
raise exception.AbortExtraction(msg)
items = data.get("items")
if not items:

View File

@@ -386,8 +386,8 @@ class OAuthMastodon(OAuthBase):
data = self.request_json(url, method="POST", data=data)
if "client_id" not in data or "client_secret" not in data:
raise exception.StopExtraction(
"Failed to register new application: '%s'", data)
raise exception.AbortExtraction(
f"Failed to register new application: '{data}'")
data["client-id"] = data.pop("client_id")
data["client-secret"] = data.pop("client_secret")

View File

@@ -306,7 +306,7 @@ class PatreonExtractor(Extractor):
except Exception:
pass
raise exception.StopExtraction("Unable to extract bootstrap data")
raise exception.AbortExtraction("Unable to extract bootstrap data")
class PatreonCreatorExtractor(PatreonExtractor):
@@ -354,21 +354,21 @@ class PatreonCreatorExtractor(PatreonExtractor):
data = None
data = self._extract_bootstrap(page)
return data["campaign"]["data"]["id"]
except exception.StopExtraction:
except exception.ControlException:
pass
except Exception as exc:
if data:
self.log.debug(data)
raise exception.StopExtraction(
"Unable to extract campaign ID (%s: %s)",
exc.__class__.__name__, exc)
raise exception.AbortExtraction(
f"Unable to extract campaign ID "
f"({exc.__class__.__name__}: {exc})")
# Next.js 13
if cid := text.extr(
page, r'{\"value\":{\"campaign\":{\"data\":{\"id\":\"', '\\"'):
return cid
raise exception.StopExtraction("Failed to extract campaign ID")
raise exception.AbortExtraction("Failed to extract campaign ID")
def _get_filters(self, query):
return "".join(

View File

@@ -175,7 +175,7 @@ class PexelsAPI():
else:
self.extractor.log.debug(response.text)
raise exception.StopExtraction("API request failed")
raise exception.AbortExtraction("API request failed")
def _pagination(self, endpoint, params):
while True:

View File

@@ -160,8 +160,7 @@ class PhilomenaAPI():
# error
self.extractor.log.debug(response.content)
raise exception.StopExtraction(
"%s %s", response.status_code, response.reason)
raise exception.HttpError("", response)
def _pagination(self, endpoint, params):
extr = self.extractor

View File

@@ -543,7 +543,7 @@ class PinterestAPI():
resource = self.extractor.subcategory.rpartition("-")[2]
raise exception.NotFoundError(resource)
self.extractor.log.debug("Server response: %s", response.text)
raise exception.StopExtraction("API request failed")
raise exception.AbortExtraction("API request failed")
def _pagination(self, resource, options):
while True:

View File

@@ -694,7 +694,7 @@ class PixivRankingExtractor(PixivExtractor):
try:
self.mode = mode = mode_map[mode]
except KeyError:
raise exception.StopExtraction("Invalid mode '%s'", mode)
raise exception.AbortExtraction(f"Invalid mode '{mode}'")
date = query.get("date")
if date:
@@ -747,7 +747,7 @@ class PixivSearchExtractor(PixivExtractor):
try:
self.word = query["word"]
except KeyError:
raise exception.StopExtraction("Missing search term")
raise exception.AbortExtraction("Missing search term")
sort = query.get("order", "date_d")
sort_map = {
@@ -760,7 +760,7 @@ class PixivSearchExtractor(PixivExtractor):
try:
self.sort = sort = sort_map[sort]
except KeyError:
raise exception.StopExtraction("Invalid search order '%s'", sort)
raise exception.AbortExtraction(f"Invalid search order '{sort}'")
target = query.get("s_mode", "s_tag_full")
target_map = {
@@ -771,7 +771,7 @@ class PixivSearchExtractor(PixivExtractor):
try:
self.target = target = target_map[target]
except KeyError:
raise exception.StopExtraction("Invalid search mode '%s'", target)
raise exception.AbortExtraction(f"Invalid search mode '{target}'")
self.date_start = query.get("scd")
self.date_end = query.get("ecd")
@@ -1288,7 +1288,7 @@ class PixivAppAPI():
self.extractor.wait(seconds=300)
continue
raise exception.StopExtraction("API request failed: %s", error)
raise exception.AbortExtraction(f"API request failed: {error}")
def _pagination(self, endpoint, params,
key_items="illusts", key_data=None):

View File

@@ -53,8 +53,8 @@ class PixnetExtractor(Extractor):
pnext = text.extr(page, 'class="nextBtn"', '>')
if pnext is None and 'name="albumpass">' in page:
raise exception.StopExtraction(
"Album %s is password-protected.", self.item_id)
raise exception.AbortExtraction(
f"Album {self.item_id} is password-protected.")
if "href" not in pnext:
return
url = self.root + text.extr(pnext, 'href="', '"')

View File

@@ -102,8 +102,8 @@ class PostmillSubmissionsExtractor(PostmillExtractor):
if response.history:
redirect_url = response.url
if redirect_url == self.root + "/login":
raise exception.StopExtraction(
"HTTP redirect to login page (%s)", redirect_url)
raise exception.AbortExtraction(
f"HTTP redirect to login page ({redirect_url})")
page = response.text
for nav in text.extract_iter(page,

View File

@@ -37,9 +37,9 @@ class ReadcomiconlineBase():
"the CAPTCHA, and press ENTER to continue", response.url)
self.input()
else:
raise exception.StopExtraction(
"Redirect to \n%s\nVisit this URL in your browser and "
"solve the CAPTCHA to continue", response.url)
raise exception.AbortExtraction(
f"Redirect to \n{response.url}\nVisit this URL in your "
f"browser and solve the CAPTCHA to continue")
class ReadcomiconlineIssueExtractor(ReadcomiconlineBase, ChapterExtractor):

View File

@@ -507,7 +507,8 @@ class RedditAPI():
try:
data = response.json()
except ValueError:
raise exception.StopExtraction(text.remove_html(response.text))
raise exception.AbortExtraction(
text.remove_html(response.text))
if "error" in data:
if data["error"] == 403:
@@ -515,7 +516,7 @@ class RedditAPI():
if data["error"] == 404:
raise exception.NotFoundError()
self.log.debug(data)
raise exception.StopExtraction(data.get("message"))
raise exception.AbortExtraction(data.get("message"))
return data
def _pagination(self, endpoint, params):

View File

@@ -308,7 +308,7 @@ class SankakuAPI():
("unauthorized", "invalid-token", "invalid_token")):
_authenticate_impl.invalidate(self.username)
continue
raise exception.StopExtraction(code)
raise exception.AbortExtraction(code)
return data
def _pagination(self, endpoint, params):

View File

@@ -45,8 +45,8 @@ class SeigaExtractor(Extractor):
url = f"{self.root}/image/source/{image_id}"
location = self.request_location(url, notfound="image")
if "nicovideo.jp/login" in location:
raise exception.StopExtraction(
"HTTP redirect to login page (%s)", location.partition("?")[0])
raise exception.AbortExtraction(
f"HTTP redirect to login page ({location.partition('?')[0]})")
return location.replace("/o/", "/priv/", 1)
def login(self):

View File

@@ -211,9 +211,9 @@ class SmugmugAPI(oauth.OAuth1API):
if data["Code"] == 404:
raise exception.NotFoundError()
if data["Code"] == 429:
raise exception.StopExtraction("Rate limit reached")
raise exception.AbortExtraction("Rate limit reached")
self.log.debug(data)
raise exception.StopExtraction("API request failed")
raise exception.AbortExtraction("API request failed")
def _expansion(self, endpoint, expands, params=None):
endpoint = self._extend(endpoint, expands)

View File

@@ -74,7 +74,7 @@ class SteamgriddbExtractor(Extractor):
def _call(self, endpoint, **kwargs):
data = self.request_json(self.root + endpoint, **kwargs)
if not data["success"]:
raise exception.StopExtraction(data["error"])
raise exception.AbortExtraction(data["error"])
return data["data"]
@@ -87,7 +87,7 @@ class SteamgriddbAssetsExtractor(SteamgriddbExtractor):
id = int(match[2])
self.game_id = id if list_type == "game" else None
self.collection_id = id if list_type == "collection" else None
self.page = int(match[3] or 1)
self.page = int(p) if (p := match[3]) else 1
def assets(self):
limit = 48
@@ -96,7 +96,7 @@ class SteamgriddbAssetsExtractor(SteamgriddbExtractor):
sort = self.config("sort", "score_desc")
if sort not in ("score_desc", "score_asc", "score_old_desc",
"score_old_asc", "age_desc", "age_asc"):
raise exception.StopExtractor("Invalid sort '%s'", sort)
raise exception.AbortExtraction(f"Invalid sort '{sort}'")
json = {
"static" : self.config("static", True),
@@ -149,7 +149,7 @@ class SteamgriddbAssetsExtractor(SteamgriddbExtractor):
for i in value:
if i not in valid_values:
raise exception.StopExtraction("Invalid %s '%s'", type_name, i)
raise exception.AbortExtraction(f"Invalid {type_name} '{i}'")
return value

View File

@@ -65,8 +65,8 @@ class SubscribestarExtractor(Extractor):
if response.history and (
"/verify_subscriber" in response.url or
"/age_confirmation_warning" in response.url):
raise exception.StopExtraction(
"HTTP redirect to %s", response.url)
raise exception.AbortExtraction(
f"HTTP redirect to {response.url}")
content = response.content
if len(content) < 250 and b">redirected<" in content:

View File

@@ -86,9 +86,9 @@ class TsuminoGalleryExtractor(TsuminoBase, GalleryExtractor):
response = self.request(url, headers=headers, fatal=False)
if "/Auth/" in response.url:
raise exception.StopExtraction(
"Failed to get gallery JSON data. Visit '%s' in a browser "
"and solve the CAPTCHA to continue.", response.url)
raise exception.AbortExtraction(
f"Failed to get gallery JSON data. Visit '{response.url}' "
f"in a browser and solve the CAPTCHA to continue.")
page = response.text
tpl, pos = text.extract(page, 'data-cdn="', '"')
@@ -155,8 +155,8 @@ class TsuminoSearchExtractor(TsuminoBase, Extractor):
return self._parse_simple(query)
return self._parse_jsurl(query)
except Exception as exc:
raise exception.StopExtraction(
"Invalid search query '%s' (%s)", query, exc)
raise exception.AbortExtraction(
f"Invalid search query '{query}' ({exc})")
def _parse_simple(self, query):
"""Parse search query with format '?<key>=value>'"""

View File

@@ -499,8 +499,8 @@ class TumblrAPI(oauth.OAuth1API):
continue
t = (datetime.now() + timedelta(0, float(reset))).time()
raise exception.StopExtraction(
"Aborting - Rate limit will reset at %s",
raise exception.AbortExtraction(
f"Aborting - Rate limit will reset at "
f"{t.hour:02}:{t.minute:02}:{t.second:02}")
# hourly rate limit
@@ -510,7 +510,7 @@ class TumblrAPI(oauth.OAuth1API):
self.extractor.wait(seconds=reset)
continue
raise exception.StopExtraction(data)
raise exception.AbortExtraction(data)
def _pagination(self, endpoint, params,
blog=None, key="posts", cache=False):

View File

@@ -154,8 +154,7 @@ class TwibooruAPI():
# error
self.extractor.log.debug(response.content)
raise exception.StopExtraction(
"%s %s", response.status_code, response.reason)
raise exception.HttpError("", response)
def _pagination(self, endpoint, params):
extr = self.extractor

View File

@@ -725,7 +725,7 @@ class TwitterTimelineExtractor(TwitterExtractor):
return self.api.user_media
if strategy == "with_replies":
return self.api.user_tweets_and_replies
raise exception.StopExtraction("Invalid strategy '%s'", strategy)
raise exception.AbortExtraction(f"Invalid strategy '{strategy}'")
class TwitterTweetsExtractor(TwitterExtractor):
@@ -940,8 +940,8 @@ class TwitterTweetExtractor(TwitterExtractor):
try:
self._assign_user(tweet["core"]["user_results"]["result"])
except KeyError:
raise exception.StopExtraction(
"'%s'", tweet.get("reason") or "Unavailable")
raise exception.AbortExtraction(
f"'{tweet.get('reason') or 'Unavailable'}'")
yield tweet
@@ -1253,7 +1253,7 @@ class TwitterAPI():
raise exception.AuthorizationError("NSFW Tweet")
if reason == "Protected":
raise exception.AuthorizationError("Protected Tweet")
raise exception.StopExtraction("Tweet unavailable ('%s')", reason)
raise exception.AbortExtraction(f"Tweet unavailable ('{reason}')")
return tweet
@@ -1634,8 +1634,8 @@ class TwitterAPI():
except Exception:
pass
raise exception.StopExtraction(
"%s %s (%s)", response.status_code, response.reason, errors)
raise exception.AbortExtraction(
f"{response.status_code} {response.reason} ({errors})")
def _pagination_legacy(self, endpoint, params):
extr = self.extractor
@@ -1809,7 +1809,7 @@ class TwitterAPI():
raise exception.AuthorizationError(
f"{user['screen_name']}'s Tweets are protected")
raise exception.StopExtraction(
raise exception.AbortExtraction(
"Unable to retrieve Tweets from this timeline")
tweets = []
@@ -1988,7 +1988,7 @@ class TwitterAPI():
def _handle_ratelimit(self, response):
rl = self.extractor.config("ratelimit")
if rl == "abort":
raise exception.StopExtraction("Rate limit exceeded")
raise exception.AbortExtraction("Rate limit exceeded")
elif rl and isinstance(rl, str) and rl.startswith("wait:"):
until = None
seconds = text.parse_float(rl.partition(":")[2]) or 60.0
@@ -2172,7 +2172,7 @@ def _login_impl(extr, username, password):
raise exception.AuthenticationError(
"No 'auth_token' cookie received")
else:
raise exception.StopExtraction("Unrecognized subtask %s", subtask)
raise exception.AbortExtraction(f"Unrecognized subtask {subtask}")
inputs = {"subtask_id": subtask}
inputs.update(data)

View File

@@ -40,5 +40,5 @@ class UrlshortenerLinkExtractor(UrlshortenerExtractor):
location = self.request_location(
url, headers=self.config_instance("headers"), notfound="URL")
if not location:
raise exception.StopExtraction("Unable to resolve short URL")
raise exception.AbortExtraction("Unable to resolve short URL")
yield Message.Queue, location, {}

View File

@@ -96,8 +96,8 @@ class VkExtractor(Extractor):
response = self.request(
url, method="POST", headers=headers, data=data)
if response.history and "/challenge.html" in response.url:
raise exception.StopExtraction(
"HTTP redirect to 'challenge' page<:\n%s", response.url)
raise exception.AbortExtraction(
f"HTTP redirect to 'challenge' page:\n{response.url}")
payload = response.json()["payload"][1]
if len(payload) < 4:

View File

@@ -200,9 +200,9 @@ class WallhavenAPI():
continue
self.extractor.log.debug("Server response: %s", response.text)
raise exception.StopExtraction(
"API request failed (%s %s)",
response.status_code, response.reason)
raise exception.AbortExtraction(
f"API request failed "
f"({response.status_code} {response.reason})")
def _pagination(self, endpoint, params=None, metadata=None):
if params is None:

View File

@@ -40,8 +40,8 @@ class WebtoonsBase():
def request(self, url, **kwargs):
response = Extractor.request(self, url, **kwargs)
if response.history and "/ageGate" in response.url:
raise exception.StopExtraction(
"HTTP redirect to age gate check ('%s')", response.url)
raise exception.AbortExtraction(
f"HTTP redirect to age gate check ('{response.url}')")
return response

View File

@@ -46,9 +46,9 @@ class WeiboExtractor(Extractor):
if response.history:
if "login.sina.com" in response.url:
raise exception.StopExtraction(
"HTTP redirect to login page (%s)",
response.url.partition("?")[0])
raise exception.AbortExtraction(
f"HTTP redirect to login page "
f"({response.url.partition('?')[0]})")
if "passport.weibo.com" in response.url:
self._sina_visitor_system(response)
response = Extractor.request(self, url, **kwargs)
@@ -179,8 +179,8 @@ class WeiboExtractor(Extractor):
if not data.get("ok"):
self.log.debug(response.content)
if "since_id" not in params: # first iteration
raise exception.StopExtraction(
'"%s"', data.get("msg") or "unknown error")
raise exception.AbortExtraction(
f'"{data.get("msg") or "unknown error"}"')
data = data["data"]
statuses = data["list"]

View File

@@ -51,7 +51,7 @@ class WikimediaExtractor(BaseExtractor):
response = self.request(url, method="HEAD", fatal=None)
if response.status_code < 400:
return url
raise exception.StopExtraction("Unable to find API endpoint")
raise exception.AbortExtraction("Unable to find API endpoint")
def prepare(self, image):
"""Adjust the content of an image object"""

View File

@@ -38,7 +38,7 @@ class XfolioExtractor(Extractor):
response = Extractor.request(self, url, **kwargs)
if "/system/recaptcha" in response.url:
raise exception.StopExtraction("Bot check / CAPTCHA page")
raise exception.AbortExtraction("Bot check / CAPTCHA page")
return response

View File

@@ -95,7 +95,7 @@ class YoutubeDLExtractor(Extractor):
ytdl_instance.get_info_extractor(self.ytdl_ie_key),
False, {}, True)
except ytdl_module.utils.YoutubeDLError:
raise exception.StopExtraction("Failed to extract video data")
raise exception.AbortExtraction("Failed to extract video data")
if not info_dict:
return

View File

@@ -238,7 +238,7 @@ class ZerochanTagExtractor(ZerochanExtractor):
self.log.warning("HTTP redirect to %s", url)
if self.config("redirects"):
continue
raise exception.StopExtraction()
raise exception.AbortExtraction()
data = response.json()
try:

View File

@@ -153,9 +153,10 @@ class Job():
try:
for msg in extractor:
self.dispatch(msg)
except exception.StopExtraction as exc:
if exc.message:
log.error(exc.message)
except exception.StopExtraction:
pass
except exception.AbortExtraction as exc:
log.error(exc.message)
self.status |= exc.code
except (exception.TerminateExtraction, exception.RestartExtraction):
raise

View File

@@ -189,7 +189,7 @@ class UpdateExtractor(Extractor):
try:
path_repo = REPOS[repo or "stable"]
except KeyError:
raise exception.StopExtraction("Invalid channel '%s'", repo)
raise exception.AbortExtraction(f"Invalid channel '{repo}'")
path_tag = tag if tag == "latest" else "tags/" + tag
url = f"{self.root_api}/repos/{path_repo}/releases/{path_tag}"

View File

@@ -41,7 +41,7 @@ def construct_YoutubeDL(module, obj, user_opts, system_opts=None):
try:
opts = parse_command_line(module, argv) if argv else user_opts
except SystemExit:
raise exception.StopExtraction("Invalid command-line option")
raise exception.AbortExtraction("Invalid command-line option")
if opts.get("format") is None:
opts["format"] = config("format")