replace old %-formatted and .format(…) strings with f-strings (#7671)

mostly using flynt
https://github.com/ikamensh/flynt
This commit is contained in:
Mike Fährmann
2025-06-28 19:36:16 +02:00
parent f77e98b57d
commit 9dbe33b6de
167 changed files with 756 additions and 891 deletions

View File

@@ -1,4 +1,4 @@
{
{
"#": "gallery-dl default configuration file",
"#": "full documentation at",

View File

@@ -139,7 +139,7 @@ def main():
extra = ""
if util.EXECUTABLE:
extra = " - Executable ({})".format(version.__variant__)
extra = f" - Executable ({version.__variant__})"
else:
git_head = util.git_head()
if git_head:

View File

@@ -58,7 +58,7 @@ bytes_to_intlist = list
def intlist_to_bytes(xs):
if not xs:
return b""
return struct.pack("%dB" % len(xs), *xs)
return struct.pack(f"{len(xs)}B", *xs)
def unpad_pkcs7(data):
@@ -615,7 +615,7 @@ def block_product(block_x, block_y):
if len(block_x) != BLOCK_SIZE_BYTES or len(block_y) != BLOCK_SIZE_BYTES:
raise ValueError(
"Length of blocks need to be %d bytes" % BLOCK_SIZE_BYTES)
f"Length of blocks need to be {BLOCK_SIZE_BYTES} bytes")
block_r = [0xE1] + [0] * (BLOCK_SIZE_BYTES - 1)
block_v = block_y[:]
@@ -639,7 +639,7 @@ def ghash(subkey, data):
if len(data) % BLOCK_SIZE_BYTES:
raise ValueError(
"Length of data should be %d bytes" % BLOCK_SIZE_BYTES)
f"Length of data should be {BLOCK_SIZE_BYTES} bytes")
last_y = [0] * BLOCK_SIZE_BYTES
for i in range(0, len(data), BLOCK_SIZE_BYTES):

View File

@@ -73,7 +73,7 @@ class DatabaseCacheDecorator():
_init = True
def __init__(self, func, keyarg, maxage):
self.key = "%s.%s" % (func.__module__, func.__name__)
self.key = f"{func.__module__}.{func.__name__}"
self.func = func
self.cache = {}
self.keyarg = keyarg
@@ -95,7 +95,7 @@ class DatabaseCacheDecorator():
pass
# database lookup
fullkey = "%s-%s" % (self.key, key)
fullkey = f"{self.key}-{key}"
with self.database() as db:
cursor = db.cursor()
try:
@@ -128,7 +128,7 @@ class DatabaseCacheDecorator():
with self.database() as db:
db.execute(
"INSERT OR REPLACE INTO data VALUES (?,?,?)",
("%s-%s" % (self.key, key), pickle.dumps(value), expires),
(f"{self.key}-{key}", pickle.dumps(value), expires),
)
def invalidate(self, key):
@@ -139,7 +139,7 @@ class DatabaseCacheDecorator():
with self.database() as db:
db.execute(
"DELETE FROM data WHERE key=?",
("%s-%s" % (self.key, key),),
(f"{self.key}-{key}",),
)
def database(self):

View File

@@ -155,8 +155,7 @@ def status():
paths.append((path, status))
fmt = "{{:<{}}} : {{}}\n".format(
max(len(p[0]) for p in paths)).format
fmt = f"{{:<{max(len(p[0]) for p in paths)}}} : {{}}\n".format
for path, status in paths:
stdout_write(fmt(path, status))

View File

@@ -43,7 +43,7 @@ def load_cookies(browser_specification):
elif browser_name in SUPPORTED_BROWSERS_CHROMIUM:
return load_cookies_chromium(browser_name, profile, keyring, domain)
else:
raise ValueError("unknown browser '{}'".format(browser_name))
raise ValueError(f"unknown browser '{browser_name}'")
def load_cookies_firefox(browser_name, profile=None,
@@ -59,7 +59,7 @@ def load_cookies_firefox(browser_name, profile=None,
if container_id is False:
conditions.append("NOT INSTR(originAttributes,'userContextId=')")
elif container_id:
uid = "%userContextId={}".format(container_id)
uid = f"%userContextId={container_id}"
conditions.append("originAttributes LIKE ? OR originAttributes LIKE ?")
parameters += (uid, uid + "&%")
@@ -72,7 +72,7 @@ def load_cookies_firefox(browser_name, profile=None,
parameters += (domain, "." + domain)
if conditions:
sql = "{} WHERE ( {} )".format(sql, " ) AND ( ".join(conditions))
sql = f"{sql} WHERE ( {' ) AND ( '.join(conditions)} )"
with DatabaseConnection(path) as db:
cookies = [
@@ -186,7 +186,7 @@ def load_cookies_chromium(browser_name, profile=None,
))
if failed_cookies > 0:
failed_message = " ({} could not be decrypted)".format(failed_cookies)
failed_message = f" ({failed_cookies} could not be decrypted)"
else:
failed_message = ""
@@ -212,9 +212,8 @@ def _firefox_cookies_database(browser_name, profile=None, container=None):
path = _find_most_recently_used_file(search_root, "cookies.sqlite")
if path is None:
raise FileNotFoundError(
"Unable to find {} cookies database in {}".format(
browser_name.capitalize(), search_root))
raise FileNotFoundError(f"Unable to find {browser_name.capitalize()} "
f"cookies database in {search_root}")
_log_debug("Extracting cookies from %s", path)
@@ -245,8 +244,7 @@ def _firefox_cookies_database(browser_name, profile=None, container=None):
container_id = context["userContextId"]
break
else:
raise ValueError("Unable to find Firefox container '{}'".format(
container))
raise ValueError(f"Unable to find Firefox container '{container}'")
_log_debug("Only loading cookies from container '%s' (ID %s)",
container, container_id)
@@ -391,8 +389,8 @@ def _chromium_cookies_database(profile, config):
path = _find_most_recently_used_file(search_root, "Cookies")
if path is None:
raise FileNotFoundError("Unable to find {} cookies database in "
"'{}'".format(config["browser"], search_root))
raise FileNotFoundError(f"Unable to find {config['browser']} cookies "
f"database in '{search_root}'")
return path
@@ -716,9 +714,9 @@ def _get_kwallet_password(browser_keyring_name):
)
if proc.returncode != 0:
_log_error("kwallet-query failed with return code {}. "
"Please consult the kwallet-query man page "
"for details".format(proc.returncode))
_log_error(f"kwallet-query failed with return code "
f"{proc.returncode}. Please consult the kwallet-query "
f"man page for details")
return b""
if stdout.lower().startswith(b"failed to read"):
@@ -847,7 +845,7 @@ class DataParser:
def read_bytes(self, num_bytes):
if num_bytes < 0:
raise ParserError("invalid read of {} bytes".format(num_bytes))
raise ParserError(f"invalid read of {num_bytes} bytes")
end = self.cursor + num_bytes
if end > len(self._data):
raise ParserError("reached end of input")
@@ -858,8 +856,8 @@ class DataParser:
def expect_bytes(self, expected_value, message):
value = self.read_bytes(len(expected_value))
if value != expected_value:
raise ParserError("unexpected value: {} != {} ({})".format(
value, expected_value, message))
raise ParserError(f"unexpected value: {value} != {expected_value} "
f"({message})")
def read_uint(self, big_endian=False):
data_format = ">I" if big_endian else "<I"
@@ -880,10 +878,10 @@ class DataParser:
def skip(self, num_bytes, description="unknown"):
if num_bytes > 0:
_log_debug("Skipping {} bytes ({}): {!r}".format(
num_bytes, description, self.read_bytes(num_bytes)))
_log_debug(f"Skipping {num_bytes} bytes ({description}): "
f"{self.read_bytes(num_bytes)!r}")
elif num_bytes < 0:
raise ParserError("Invalid skip of {} bytes".format(num_bytes))
raise ParserError(f"Invalid skip of {num_bytes} bytes")
def skip_to(self, offset, description="unknown"):
self.skip(offset - self.cursor, description)
@@ -906,7 +904,7 @@ class DatabaseConnection():
if util.WINDOWS:
path = "/" + os.path.abspath(path)
uri = "file:{}?mode=ro&immutable=1".format(path)
uri = f"file:{path}?mode=ro&immutable=1"
self.database = sqlite3.connect(
uri, uri=True, isolation_level=None, check_same_thread=False)
return self.database
@@ -1104,9 +1102,9 @@ def _parse_browser_specification(
browser, profile=None, keyring=None, container=None, domain=None):
browser = browser.lower()
if browser not in SUPPORTED_BROWSERS:
raise ValueError("Unsupported browser '{}'".format(browser))
raise ValueError(f"Unsupported browser '{browser}'")
if keyring and keyring not in SUPPORTED_KEYRINGS:
raise ValueError("Unsupported keyring '{}'".format(keyring))
raise ValueError(f"Unsupported keyring '{keyring}'")
if profile and _is_path(profile):
profile = os.path.expanduser(profile)
return browser, profile, keyring, container, domain

View File

@@ -149,7 +149,7 @@ class HttpDownloader(DownloaderBase):
# partial content
file_size = pathfmt.part_size()
if file_size:
headers["Range"] = "bytes={}-".format(file_size)
headers["Range"] = f"bytes={file_size}-"
# connect to (remote) source
try:
@@ -167,7 +167,7 @@ class HttpDownloader(DownloaderBase):
reason = exc.args[0].reason
cls = reason.__class__.__name__
pre, _, err = str(reason.args[-1]).partition(":")
msg = "{}: {}".format(cls, (err or pre).lstrip())
msg = f"{cls}: {(err or pre).lstrip()}"
except Exception:
msg = str(exc)
continue
@@ -189,7 +189,7 @@ class HttpDownloader(DownloaderBase):
elif code == 416 and file_size: # Requested Range Not Satisfiable
break
else:
msg = "'{} {}' for '{}'".format(code, response.reason, url)
msg = f"'{code} {response.reason}' for '{url}'"
challenge = util.detect_challenge(response)
if challenge is not None:
@@ -339,8 +339,7 @@ class HttpDownloader(DownloaderBase):
# check file size
if size and fp.tell() < size:
msg = "file size mismatch ({} < {})".format(
fp.tell(), size)
msg = f"file size mismatch ({fp.tell()} < {size})"
output.stderr_write("\n")
continue

View File

@@ -39,7 +39,7 @@ class GalleryDLException(Exception):
if not message:
message = self.default
elif isinstance(message, Exception):
message = "{}: {}".format(message.__class__.__name__, message)
message = f"{message.__class__.__name__}: {message}"
if self.msgfmt and fmt:
message = self.msgfmt.format(message)
Exception.__init__(self, message)
@@ -61,8 +61,8 @@ class HttpError(ExtractionError):
else:
self.status = response.status_code
if not message:
message = "'{} {}' for '{}'".format(
response.status_code, response.reason, response.url)
message = (f"'{response.status_code} {response.reason}' "
f"for '{response.url}'")
ExtractionError.__init__(self, message)

View File

@@ -26,7 +26,7 @@ class _2chThreadExtractor(Extractor):
self.board, self.thread = match.groups()
def items(self):
url = "{}/{}/res/{}.json".format(self.root, self.board, self.thread)
url = f"{self.root}/{self.board}/res/{self.thread}.json"
posts = self.request(url).json()["threads"][0]["posts"]
op = posts[0]
@@ -71,21 +71,21 @@ class _2chBoardExtractor(Extractor):
self.board = match[1]
def items(self):
base = f"{self.root}/{self.board}"
# index page
url = "{}/{}/index.json".format(self.root, self.board)
url = f"{base}/index.json"
index = self.request(url).json()
index["_extractor"] = _2chThreadExtractor
for thread in index["threads"]:
url = "{}/{}/res/{}.html".format(
self.root, self.board, thread["thread_num"])
url = f"{base}/res/{thread['thread_num']}.html"
yield Message.Queue, url, index
# pages 1..n
for n in util.advance(index["pages"], 1):
url = "{}/{}/{}.json".format(self.root, self.board, n)
url = f"{base}/{n}.json"
page = self.request(url).json()
page["_extractor"] = _2chThreadExtractor
for thread in page["threads"]:
url = "{}/{}/res/{}.html".format(
self.root, self.board, thread["thread_num"])
url = f"{base}/res/{thread['thread_num']}.html"
yield Message.Queue, url, page

View File

@@ -28,8 +28,8 @@ class _2chanThreadExtractor(Extractor):
self.server, self.board, self.thread = match.groups()
def items(self):
url = "https://{}.2chan.net/{}/res/{}.htm".format(
self.server, self.board, self.thread)
url = (f"https://{self.server}.2chan.net"
f"/{self.board}/res/{self.thread}.htm")
page = self.request(url).text
data = self.metadata(page)
yield Message.Directory, data

View File

@@ -28,7 +28,7 @@ class _2chenThreadExtractor(Extractor):
self.board, self.thread = match.groups()
def items(self):
url = "{}/{}/{}".format(self.root, self.board, self.thread)
url = f"{self.root}/{self.board}/{self.thread}"
page = self.request(url, encoding="utf-8", notfound="thread").text
data = self.metadata(page)
yield Message.Directory, data
@@ -89,7 +89,7 @@ class _2chenBoardExtractor(Extractor):
self.board = match[1]
def items(self):
url = "{}/{}/catalog".format(self.root, self.board)
url = f"{self.root}/{self.board}/catalog"
page = self.request(url, notfound="board").text
data = {"_extractor": _2chenThreadExtractor}
for thread in text.extract_iter(

View File

@@ -108,7 +108,7 @@ class _35photoUserExtractor(_35photoExtractor):
self.user_id = 0
def metadata(self):
url = "{}/{}/".format(self.root, self.user)
url = f"{self.root}/{self.user}/"
page = self.request(url).text
self.user_id = text.parse_int(text.extr(page, "/user_", ".xml"))
return {
@@ -142,7 +142,7 @@ class _35photoTagExtractor(_35photoExtractor):
num = 1
while True:
url = "{}/tags/{}/list_{}/".format(self.root, self.tag, num)
url = f"{self.root}/tags/{self.tag}/list_{num}/"
page = self.request(url).text
prev = None
@@ -170,7 +170,7 @@ class _35photoGenreExtractor(_35photoExtractor):
self.photo_ids = None
def metadata(self):
url = "{}/genre_{}{}".format(self.root, self.genre_id, self.new or "/")
url = f"{self.root}/genre_{self.genre_id}{self.new or '/'}"
page = self.request(url).text
self.photo_ids = self._photo_ids(text.extr(
page, ' class="photo', '\n'))

View File

@@ -27,8 +27,7 @@ class _4archiveThreadExtractor(Extractor):
self.board, self.thread = match.groups()
def items(self):
url = "{}/board/{}/thread/{}".format(
self.root, self.board, self.thread)
url = f"{self.root}/board/{self.board}/thread/{self.thread}"
page = self.request(url).text
data = self.metadata(page)
posts = self.posts(page)
@@ -99,12 +98,11 @@ class _4archiveBoardExtractor(Extractor):
def items(self):
data = {"_extractor": _4archiveThreadExtractor}
while True:
url = "{}/board/{}/{}".format(self.root, self.board, self.num)
url = f"{self.root}/board/{self.board}/{self.num}"
page = self.request(url).text
if 'class="thread"' not in page:
return
for thread in text.extract_iter(page, 'class="thread" id="t', '"'):
url = "{}/board/{}/thread/{}".format(
self.root, self.board, thread)
url = f"{self.root}/board/{self.board}/thread/{thread}"
yield Message.Queue, url, data
self.num += 1

View File

@@ -28,8 +28,7 @@ class _4chanThreadExtractor(Extractor):
self.board, self.thread = match.groups()
def items(self):
url = "https://a.4cdn.org/{}/thread/{}.json".format(
self.board, self.thread)
url = f"https://a.4cdn.org/{self.board}/thread/{self.thread}.json"
posts = self.request(url).json()["posts"]
title = posts[0].get("sub") or text.remove_html(posts[0]["com"])
@@ -45,8 +44,8 @@ class _4chanThreadExtractor(Extractor):
post.update(data)
post["extension"] = post["ext"][1:]
post["filename"] = text.unescape(post["filename"])
url = "https://i.4cdn.org/{}/{}{}".format(
post["board"], post["tim"], post["ext"])
url = (f"https://i.4cdn.org"
f"/{post['board']}/{post['tim']}{post['ext']}")
yield Message.Url, url, post
@@ -62,13 +61,13 @@ class _4chanBoardExtractor(Extractor):
self.board = match[1]
def items(self):
url = "https://a.4cdn.org/{}/threads.json".format(self.board)
url = f"https://a.4cdn.org/{self.board}/threads.json"
threads = self.request(url).json()
for page in threads:
for thread in page["threads"]:
url = "https://boards.4chan.org/{}/thread/{}/".format(
self.board, thread["no"])
url = (f"https://boards.4chan.org"
f"/{self.board}/thread/{thread['no']}/")
thread["page"] = page["page"]
thread["_extractor"] = _4chanThreadExtractor
yield Message.Queue, url, thread

View File

@@ -29,8 +29,7 @@ class _4chanarchivesThreadExtractor(Extractor):
self.board, self.thread = match.groups()
def items(self):
url = "{}/board/{}/thread/{}".format(
self.root, self.board, self.thread)
url = f"{self.root}/board/{self.board}/thread/{self.thread}"
page = self.request(url).text
data = self.metadata(page)
posts = self.posts(page)
@@ -104,7 +103,7 @@ class _4chanarchivesBoardExtractor(Extractor):
<span><a href="'''
while True:
url = "{}/board/{}/{}".format(self.root, self.board, pnum)
url = f"{self.root}/board/{self.board}/{pnum}"
page = self.request(url).text
thread = None

View File

@@ -78,7 +78,7 @@ class _8chanThreadExtractor(_8chanExtractor):
self.cookies.set(self.cookies_tos_name(), "1", domain=self.root[8:])
# fetch thread data
url = "{}/{}/res/{}.".format(self.root, board, thread)
url = f"{self.root}/{board}/res/{thread}."
self.session.headers["Referer"] = url + "html"
thread = self.request(url + "json").json()
thread["postId"] = thread["threadId"]
@@ -116,19 +116,18 @@ class _8chanBoardExtractor(_8chanExtractor):
self.cookies.set(self.cookies_tos_name(), "1", domain=self.root[8:])
pnum = text.parse_int(pnum, 1)
url = "{}/{}/{}.json".format(self.root, board, pnum)
url = f"{self.root}/{board}/{pnum}.json"
data = self.request(url).json()
threads = data["threads"]
while True:
for thread in threads:
thread["_extractor"] = _8chanThreadExtractor
url = "{}/{}/res/{}.html".format(
self.root, board, thread["threadId"])
url = f"{self.root}/{board}/res/{thread['threadId']}.html"
yield Message.Queue, url, thread
pnum += 1
if pnum > data["pageCount"]:
return
url = "{}/{}/{}.json".format(self.root, board, pnum)
url = f"{self.root}/{board}/{pnum}.json"
threads = self.request(url).json()["threads"]

View File

@@ -74,8 +74,7 @@ class _8musesAlbumExtractor(Extractor):
return
path, _, num = self.path.rstrip("/").rpartition("/")
path = path if num.isdecimal() else self.path
url = "{}{}/{}{}".format(
self.root, path, data["page"] + 1, self.params)
url = f"{self.root}{path}/{data['page'] + 1}{self.params}"
def _make_album(self, album):
return {

View File

@@ -60,7 +60,7 @@ class AgnphExtractor(booru.BooruExtractor):
params["page"] += 1
def _html(self, post):
url = "{}/gallery/post/show/{}/".format(self.root, post["id"])
url = f"{self.root}/gallery/post/show/{post['id']}/"
return self.request(url).text
def _tags(self, post, page):
@@ -103,7 +103,6 @@ class AgnphPostExtractor(AgnphExtractor):
example = "https://agn.ph/gallery/post/show/12345/"
def posts(self):
url = "{}/gallery/post/show/{}/?api=xml".format(
self.root, self.groups[0])
url = f"{self.root}/gallery/post/show/{self.groups[0]}/?api=xml"
post = self.request_xml(url)
return (self._xml_to_dict(post),)

View File

@@ -135,7 +135,7 @@ class Ao3WorkExtractor(Ao3Extractor):
self.login()
work_id = self.groups[0]
url = "{}/works/{}".format(self.root, work_id)
url = f"{self.root}/works/{work_id}"
response = self.request(url, notfound="work")
if response.url.endswith("/users/login?restricted=true"):
@@ -256,7 +256,7 @@ class Ao3UserExtractor(Dispatch, Ao3Extractor):
example = "https://archiveofourown.org/users/USER"
def items(self):
base = "{}/users/{}/".format(self.root, self.groups[0])
base = f"{self.root}/users/{self.groups[0]}/"
return self._dispatch_extractors((
(Ao3UserWorksExtractor , base + "works"),
(Ao3UserSeriesExtractor , base + "series"),

View File

@@ -26,7 +26,7 @@ class ArcaliveExtractor(Extractor):
for article in self.articles():
article["_extractor"] = ArcalivePostExtractor
board = self.board or article.get("boardSlug") or "breaking"
url = "{}/b/{}/{}".format(self.root, board, article["id"])
url = f"{self.root}/b/{board}/{article['id']}"
yield Message.Queue, url, article
@@ -51,8 +51,8 @@ class ArcalivePostExtractor(ArcaliveExtractor):
post["count"] = len(files)
post["date"] = text.parse_datetime(
post["createdAt"][:19], "%Y-%m-%dT%H:%M:%S")
post["post_url"] = post_url = "{}/b/{}/{}".format(
self.root, post["boardSlug"], post["id"])
post["post_url"] = post_url = \
f"{self.root}/b/{post['boardSlug']}/{post['id']}"
post["_http_headers"] = {"Referer": post_url + "?p=1"}
yield Message.Directory, post

View File

@@ -24,7 +24,7 @@ class ArchitizerProjectExtractor(GalleryExtractor):
example = "https://architizer.com/projects/NAME/"
def __init__(self, match):
url = "{}/projects/{}/".format(self.root, match[1])
url = f"{self.root}/projects/{match[1]}/"
GalleryExtractor.__init__(self, match, url)
def metadata(self, page):
@@ -71,12 +71,11 @@ class ArchitizerFirmExtractor(Extractor):
self.firm = match[1]
def items(self):
url = url = "{}/firms/{}/?requesting_merlin=pages".format(
self.root, self.firm)
url = url = f"{self.root}/firms/{self.firm}/?requesting_merlin=pages"
page = self.request(url).text
data = {"_extractor": ArchitizerProjectExtractor}
for project in text.extract_iter(page, '<a href="/projects/', '"'):
if not project.startswith("q/"):
url = "{}/projects/{}".format(self.root, project)
url = f"{self.root}/projects/{project}"
yield Message.Queue, url, data

View File

@@ -101,7 +101,7 @@ class ArtstationExtractor(Extractor):
def get_project_assets(self, project_id):
"""Return all assets associated with 'project_id'"""
url = "{}/projects/{}.json".format(self.root, project_id)
url = f"{self.root}/projects/{project_id}.json"
try:
data = self.request(url).json()
@@ -129,7 +129,7 @@ class ArtstationExtractor(Extractor):
def get_user_info(self, username):
"""Return metadata for a specific user"""
url = "{}/users/{}/quick.json".format(self.root, username.lower())
url = f"{self.root}/users/{username.lower()}/quick.json"
response = self.request(url, notfound="user")
return response.json()
@@ -197,7 +197,7 @@ class ArtstationUserExtractor(ArtstationExtractor):
example = "https://www.artstation.com/USER"
def projects(self):
url = "{}/users/{}/projects.json".format(self.root, self.user)
url = f"{self.root}/users/{self.user}/projects.json"
params = {"album_id": "all"}
return self._pagination(url, params)
@@ -233,7 +233,7 @@ class ArtstationAlbumExtractor(ArtstationExtractor):
}
def projects(self):
url = "{}/users/{}/projects.json".format(self.root, self.user)
url = f"{self.root}/users/{self.user}/projects.json"
params = {"album_id": self.album_id}
return self._pagination(url, params)
@@ -248,7 +248,7 @@ class ArtstationLikesExtractor(ArtstationExtractor):
example = "https://www.artstation.com/USER/likes"
def projects(self):
url = "{}/users/{}/likes.json".format(self.root, self.user)
url = f"{self.root}/users/{self.user}/likes.json"
return self._pagination(url)
@@ -267,16 +267,14 @@ class ArtstationCollectionExtractor(ArtstationExtractor):
self.collection_id = match[2]
def metadata(self):
url = "{}/collections/{}.json".format(
self.root, self.collection_id)
url = f"{self.root}/collections/{self.collection_id}.json"
params = {"username": self.user}
collection = self.request(
url, params=params, notfound="collection").json()
return {"collection": collection, "user": self.user}
def projects(self):
url = "{}/collections/{}/projects.json".format(
self.root, self.collection_id)
url = f"{self.root}/collections/{self.collection_id}/projects.json"
params = {"collection_id": self.collection_id}
return self._pagination(url, params)
@@ -294,8 +292,7 @@ class ArtstationCollectionsExtractor(ArtstationExtractor):
for collection in self.request(
url, params=params, notfound="collections").json():
url = "{}/{}/collections/{}".format(
self.root, self.user, collection["id"])
url = f"{self.root}/{self.user}/collections/{collection['id']}"
collection["_extractor"] = ArtstationCollectionExtractor
yield Message.Queue, url, collection
@@ -318,12 +315,10 @@ class ArtstationChallengeExtractor(ArtstationExtractor):
self.sorting = match[2] or "popular"
def items(self):
challenge_url = "{}/contests/_/challenges/{}.json".format(
self.root, self.challenge_id)
submission_url = "{}/contests/_/challenges/{}/submissions.json".format(
self.root, self.challenge_id)
update_url = "{}/contests/submission_updates.json".format(
self.root)
base = f"{self.root}/contests/_/challenges/{self.challenge_id}"
challenge_url = f"{base}.json"
submission_url = f"{base}/submissions.json"
update_url = f"{self.root}/contests/submission_updates.json"
challenge = self.request(challenge_url).json()
yield Message.Directory, {"challenge": challenge}
@@ -381,7 +376,7 @@ class ArtstationSearchExtractor(ArtstationExtractor):
"value" : value.split(","),
})
url = "{}/api/v2/search/projects.json".format(self.root)
url = f"{self.root}/api/v2/search/projects.json"
data = {
"query" : self.query,
"page" : None,
@@ -412,7 +407,7 @@ class ArtstationArtworkExtractor(ArtstationExtractor):
return {"artwork": self.query}
def projects(self):
url = "{}/projects.json".format(self.root)
url = f"{self.root}/projects.json"
return self._pagination(url, self.query.copy())
@@ -453,8 +448,8 @@ class ArtstationFollowingExtractor(ArtstationExtractor):
example = "https://www.artstation.com/USER/following"
def items(self):
url = "{}/users/{}/following.json".format(self.root, self.user)
url = f"{self.root}/users/{self.user}/following.json"
for user in self._pagination(url):
url = "{}/{}".format(self.root, user["username"])
url = f"{self.root}/{user['username']}"
user["_extractor"] = ArtstationUserExtractor
yield Message.Queue, url, user

View File

@@ -111,7 +111,7 @@ class AryionExtractor(Extractor):
url = self.root + text.rextr(page, "href='", "'", pos)
def _parse_post(self, post_id):
url = "{}/g4/data.php?id={}".format(self.root, post_id)
url = f"{self.root}/g4/data.php?id={post_id}"
with self.request(url, method="HEAD", fatal=False) as response:
if response.status_code >= 400:
@@ -141,9 +141,9 @@ class AryionExtractor(Extractor):
# fix 'Last-Modified' header
lmod = headers["last-modified"]
if lmod[22] != ":":
lmod = "{}:{} GMT".format(lmod[:22], lmod[22:24])
lmod = f"{lmod[:22]}:{lmod[22:24]} GMT"
post_url = "{}/g4/view/{}".format(self.root, post_id)
post_url = f"{self.root}/g4/view/{post_id}"
extr = text.extract_from(self.request(post_url).text)
title, _, artist = text.unescape(extr(
@@ -195,10 +195,10 @@ class AryionGalleryExtractor(AryionExtractor):
def posts(self):
if self.recursive:
url = "{}/g4/gallery/{}".format(self.root, self.user)
url = f"{self.root}/g4/gallery/{self.user}"
return self._pagination_params(url)
else:
url = "{}/g4/latest.php?name={}".format(self.root, self.user)
url = f"{self.root}/g4/latest.php?name={self.user}"
return util.advance(self._pagination_next(url), self.offset)
@@ -212,7 +212,7 @@ class AryionFavoriteExtractor(AryionExtractor):
example = "https://aryion.com/g4/favorites/USER"
def posts(self):
url = "{}/g4/favorites/{}".format(self.root, self.user)
url = f"{self.root}/g4/favorites/{self.user}"
return self._pagination_params(
url, None, "class='gallery-item favorite' id='")

View File

@@ -86,7 +86,7 @@ class BatotoChapterExtractor(BatotoBase, ChapterExtractor):
ChapterExtractor.__init__(self, match, False)
self._init_root()
self.chapter_id = self.groups[1]
self.page_url = "{}/title/0/{}".format(self.root, self.chapter_id)
self.page_url = f"{self.root}/title/0/{self.chapter_id}"
def metadata(self, page):
extr = text.extract_from(page)
@@ -147,7 +147,7 @@ class BatotoMangaExtractor(BatotoBase, MangaExtractor):
MangaExtractor.__init__(self, match, False)
self._init_root()
self.manga_id = self.groups[1] or self.groups[2]
self.page_url = "{}/title/{}".format(self.root, self.manga_id)
self.page_url = f"{self.root}/title/{self.manga_id}"
def chapters(self, page):
extr = text.extract_from(page)
@@ -177,6 +177,6 @@ class BatotoMangaExtractor(BatotoBase, MangaExtractor):
data["date"] = text.parse_datetime(
extr('time="', '"'), "%Y-%m-%dT%H:%M:%S.%fZ")
url = "{}/title/{}".format(self.root, href)
url = f"{self.root}/title/{href}"
results.append((url, data.copy()))
return results

View File

@@ -43,7 +43,7 @@ class BbcGalleryExtractor(GalleryExtractor):
def images(self, page):
width = self.config("width")
width = width - width % 16 if width else 1920
dimensions = "/{}xn/".format(width)
dimensions = f"/{width}xn/"
results = []
for img in text.extract_iter(page, 'class="gallery__thumbnail', ">"):
@@ -64,7 +64,7 @@ class BbcGalleryExtractor(GalleryExtractor):
front, _, back = src.partition("/320x180_b/")
for width in (1920, 1600, 1280, 976):
if width < max_width:
yield "{}/{}xn/{}".format(front, width, back)
yield f"{front}/{width}xn/{back}"
class BbcProgrammeExtractor(Extractor):

View File

@@ -115,7 +115,7 @@ class BehanceGalleryExtractor(BehanceExtractor):
def get_gallery_data(self):
"""Collect gallery info dict"""
url = "{}/gallery/{}/a".format(self.root, self.gallery_id)
url = f"{self.root}/gallery/{self.gallery_id}/a"
cookies = {
"gki": '{"feature_project_view":false,'
'"feature_discover_login_prompt":false,'

View File

@@ -30,7 +30,7 @@ class BilibiliUserArticlesExtractor(BilibiliExtractor):
def items(self):
for article in self.api.user_articles(self.groups[0]):
article["_extractor"] = BilibiliArticleExtractor
url = "{}/opus/{}".format(self.root, article["opus_id"])
url = f"{self.root}/opus/{article['opus_id']}"
yield Message.Queue, url, article
@@ -98,7 +98,7 @@ class BilibiliUserArticlesFavoriteExtractor(BilibiliExtractor):
def items(self):
for article in self.api.user_favlist():
article["_extractor"] = BilibiliArticleExtractor
url = "{}/opus/{}".format(self.root, article["opus_id"])
url = f"{self.root}/opus/{article['opus_id']}"
yield Message.Queue, url, article

View File

@@ -62,9 +62,8 @@ class BlueskyExtractor(Extractor):
yield Message.Directory, post
if files:
did = post["author"]["did"]
base = (
"{}/xrpc/com.atproto.sync.getBlob?did={}&cid=".format(
self.api.service_endpoint(did), did))
base = (f"{self.api.service_endpoint(did)}/xrpc"
f"/com.atproto.sync.getBlob?did={did}&cid=")
for post["num"], file in enumerate(files, 1):
post.update(file)
yield Message.Url, base + file["filename"], post
@@ -215,7 +214,7 @@ class BlueskyUserExtractor(Dispatch, BlueskyExtractor):
example = "https://bsky.app/profile/HANDLE"
def items(self):
base = "{}/profile/{}/".format(self.root, self.groups[0])
base = f"{self.root}/profile/{self.groups[0]}/"
default = ("posts" if self.config("quoted", False) or
self.config("reposts", False) else "media")
return self._dispatch_extractors((
@@ -411,11 +410,9 @@ class BlueskyAPI():
def get_feed(self, actor, feed):
endpoint = "app.bsky.feed.getFeed"
params = {
"feed" : "at://{}/app.bsky.feed.generator/{}".format(
self._did_from_actor(actor), feed),
"limit": "100",
}
uri = (f"at://{self._did_from_actor(actor)}"
f"/app.bsky.feed.generator/{feed}")
params = {"feed": uri, "limit": "100"}
return self._pagination(endpoint, params)
def get_follows(self, actor):
@@ -428,16 +425,13 @@ class BlueskyAPI():
def get_list_feed(self, actor, list):
endpoint = "app.bsky.feed.getListFeed"
params = {
"list" : "at://{}/app.bsky.graph.list/{}".format(
self._did_from_actor(actor), list),
"limit": "100",
}
uri = f"at://{self._did_from_actor(actor)}/app.bsky.graph.list/{list}"
params = {"list" : uri, "limit": "100"}
return self._pagination(endpoint, params)
def get_post_thread(self, actor, post_id):
uri = "at://{}/app.bsky.feed.post/{}".format(
self._did_from_actor(actor), post_id)
uri = (f"at://{self._did_from_actor(actor)}"
f"/app.bsky.feed.post/{post_id}")
depth = self.extractor.config("depth", "0")
return self.get_post_thread_uri(uri, depth)
@@ -547,15 +541,15 @@ class BlueskyAPI():
"password" : self.password,
}
url = "{}/xrpc/{}".format(self.root, endpoint)
url = f"{self.root}/xrpc/{endpoint}"
response = self.extractor.request(
url, method="POST", headers=headers, json=data, fatal=None)
data = response.json()
if response.status_code != 200:
self.log.debug("Server response: %s", data)
raise exception.AuthenticationError('"{}: {}"'.format(
data.get("error"), data.get("message")))
raise exception.AuthenticationError(
f"\"{data.get('error')}: {data.get('message')}\"")
_refresh_token_cache.update(self.username, data["refreshJwt"])
return "Bearer " + data["accessJwt"]
@@ -563,7 +557,7 @@ class BlueskyAPI():
def _call(self, endpoint, params, root=None):
if root is None:
root = self.root
url = "{}/xrpc/{}".format(root, endpoint)
url = f"{root}/xrpc/{endpoint}"
while True:
self.authenticate()
@@ -577,13 +571,12 @@ class BlueskyAPI():
self.extractor.wait(until=until)
continue
msg = "API request failed"
try:
data = response.json()
msg = "API request failed ('{}: {}')".format(
data["error"], data["message"])
msg = f"{msg} ('{data['error']}: {data['message']}')"
except Exception:
msg = "API request failed ({} {})".format(
response.status_code, response.reason)
msg = f"{msg} ({response.status_code} {response.reason})"
self.extractor.log.debug("Server response: %s", response.text)
raise exception.StopExtraction(msg)

View File

@@ -218,7 +218,7 @@ class BoostyFollowingExtractor(BoostyExtractor):
def items(self):
for user in self.api.user_subscriptions():
url = "{}/{}".format(self.root, user["blog"]["blogUrl"])
url = f"{self.root}/{user['blog']['blogUrl']}"
user["_extractor"] = BoostyUserExtractor
yield Message.Queue, url, user
@@ -288,7 +288,7 @@ class BoostyAPI():
self.headers["Authorization"] = "Bearer " + access_token
def blog_posts(self, username, params):
endpoint = "/v1/blog/{}/post/".format(username)
endpoint = f"/v1/blog/{username}/post/"
params = self._merge_params(params, {
"limit" : "5",
"offset" : None,
@@ -298,7 +298,7 @@ class BoostyAPI():
return self._pagination(endpoint, params)
def blog_media_album(self, username, type="all", params=()):
endpoint = "/v1/blog/{}/media_album/".format(username)
endpoint = f"/v1/blog/{username}/media_album/"
params = self._merge_params(params, {
"type" : type.rstrip("s"),
"limit" : "15",
@@ -318,7 +318,7 @@ class BoostyAPI():
return posts
def post(self, username, post_id):
endpoint = "/v1/blog/{}/post/{}".format(username, post_id)
endpoint = f"/v1/blog/{username}/post/{post_id}"
return self._call(endpoint)
def feed_posts(self, params=None):
@@ -418,11 +418,11 @@ class BoostyAPI():
params["offset"] = offset
def dialog(self, dialog_id):
endpoint = "/v1/dialog/{}".format(dialog_id)
endpoint = f"/v1/dialog/{dialog_id}"
return self._call(endpoint)
def dialog_messages(self, dialog_id, limit=300, offset=None):
endpoint = "/v1/dialog/{}/message/".format(dialog_id)
endpoint = f"/v1/dialog/{dialog_id}/message/"
params = {
"limit": limit,
"reverse": "true",

View File

@@ -184,7 +184,7 @@ class BunkrAlbumExtractor(LolisafeAlbumExtractor):
json={"id": data_id}).json()
if data.get("encrypted"):
key = "SECRET_KEY_{}".format(data["timestamp"] // 3600)
key = f"SECRET_KEY_{data['timestamp'] // 3600}"
file_url = util.decrypt_xor(data["url"], key.encode())
else:
file_url = data["url"]

View File

@@ -52,8 +52,7 @@ class CienArticleExtractor(CienExtractor):
example = "https://ci-en.net/creator/123/article/12345"
def items(self):
url = "{}/creator/{}/article/{}".format(
self.root, self.groups[0], self.groups[1])
url = f"{self.root}/creator/{self.groups[0]}/article/{self.groups[1]}"
page = self.request(url, notfound="article").text
files = self._extract_files(page)
@@ -121,7 +120,7 @@ class CienArticleExtractor(CienExtractor):
auth = text.extr(video, ' auth-key="', '"')
file = text.nameext_from_url(name)
file["url"] = "{}video-web.mp4?{}".format(path, auth)
file["url"] = f"{path}video-web.mp4?{auth}"
file["type"] = "video"
files.append(file)
@@ -163,7 +162,7 @@ class CienCreatorExtractor(CienExtractor):
example = "https://ci-en.net/creator/123"
def items(self):
url = "{}/creator/{}/article".format(self.root, self.groups[0])
url = f"{self.root}/creator/{self.groups[0]}/article"
params = text.parse_query(self.groups[1])
params["mode"] = "list"
return self._pagination_articles(url, params)

View File

@@ -75,7 +75,7 @@ class CivitaiExtractor(Extractor):
if models:
data = {"_extractor": CivitaiModelExtractor}
for model in models:
url = "{}/models/{}".format(self.root, model["id"])
url = f"{self.root}/models/{model['id']}"
yield Message.Queue, url, data
return
@@ -152,11 +152,9 @@ class CivitaiExtractor(Extractor):
name = image.get("name")
if not name:
mime = image.get("mimeType") or self._image_ext
name = "{}.{}".format(image.get("id"), mime.rpartition("/")[2])
return (
"https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/{}/{}/{}".format(
url, quality, name)
)
name = f"{image.get('id')}.{mime.rpartition('/')[2]}"
return (f"https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA"
f"/{url}/{quality}/{name}")
def _image_results(self, images):
for num, file in enumerate(images, 1):
@@ -298,16 +296,15 @@ class CivitaiModelExtractor(CivitaiExtractor):
if not sep:
name = ext
ext = "bin"
file["uuid"] = "model-{}-{}-{}".format(
model["id"], version["id"], file["id"])
file["uuid"] = f"model-{model['id']}-{version['id']}-{file['id']}"
files.append({
"num" : num,
"file" : file,
"filename" : name,
"extension": ext,
"url" : (file.get("downloadUrl") or
"{}/api/download/models/{}".format(
self.root, version["id"])),
"url" : (
file.get("downloadUrl") or
f"{self.root}/api/download/models/{version['id']}"),
"_http_headers" : {
"Authorization": self.api.headers.get("Authorization")},
"_http_validate": self._validate_file_model,
@@ -340,8 +337,7 @@ class CivitaiModelExtractor(CivitaiExtractor):
alert = text.extr(
response.text, 'mantine-Alert-message">', "</div></div></div>")
if alert:
msg = "\"{}\" - 'api-key' required".format(
text.remove_html(alert))
msg = f"\"{text.remove_html(alert)}\" - 'api-key' required"
else:
msg = "'api-key' required to download this file"
self.log.warning(msg)
@@ -436,7 +432,7 @@ class CivitaiUserExtractor(Dispatch, CivitaiExtractor):
example = "https://civitai.com/user/USER"
def items(self):
base = "{}/user/{}/".format(self.root, self.groups[0])
base = f"{self.root}/user/{self.groups[0]}/"
return self._dispatch_extractors((
(CivitaiUserModelsExtractor, base + "models"),
(CivitaiUserPostsExtractor , base + "posts"),
@@ -552,12 +548,12 @@ class CivitaiRestAPI():
})
def model(self, model_id):
endpoint = "/v1/models/{}".format(model_id)
endpoint = f"/v1/models/{model_id}"
return self._call(endpoint)
@memcache(keyarg=1)
def model_version(self, model_version_id):
endpoint = "/v1/model-versions/{}".format(model_version_id)
endpoint = f"/v1/model-versions/{model_version_id}"
return self._call(endpoint)
def models(self, params):

View File

@@ -181,7 +181,7 @@ class Extractor():
reason = exc.args[0].reason
cls = reason.__class__.__name__
pre, _, err = str(reason.args[-1]).partition(":")
msg = " {}: {}".format(cls, (err or pre).lstrip())
msg = f" {cls}: {(err or pre).lstrip()}"
except Exception:
msg = exc
code = 0
@@ -209,8 +209,7 @@ class Extractor():
if notfound and code == 404:
raise exception.NotFoundError(notfound)
msg = "'{} {}' for '{}'".format(
code, response.reason, response.url)
msg = f"'{code} {response.reason}' for '{response.url}'"
challenge = util.detect_challenge(response)
if challenge is not None:
@@ -306,7 +305,7 @@ class Extractor():
if reason:
t = datetime.fromtimestamp(until).time()
isotime = "{:02}:{:02}:{:02}".format(t.hour, t.minute, t.second)
isotime = f"{t.hour:02}:{t.minute:02}:{t.second:02}"
self.log.info("Waiting until %s (%s)", isotime, reason)
time.sleep(seconds)
@@ -696,10 +695,8 @@ class Extractor():
Extractor._dump_sanitize = util.re_compile(
r"[\\\\|/<>:\"?*&=#]+").sub
fname = "{:>02}_{}".format(
Extractor._dump_index,
Extractor._dump_sanitize('_', response.url),
)
fname = (f"{Extractor._dump_index:>02}_"
f"{Extractor._dump_sanitize('_', response.url)}")
if util.WINDOWS:
path = os.path.abspath(fname)[:255]
@@ -1041,7 +1038,7 @@ def _browser_useragent():
server.listen(1)
host, port = server.getsockname()
webbrowser.open("http://{}:{}/user-agent".format(host, port))
webbrowser.open(f"http://{host}:{port}/user-agent")
client = server.accept()[0]
server.close()

View File

@@ -32,7 +32,7 @@ class CyberdropAlbumExtractor(lolisafe.LolisafeAlbumExtractor):
yield Message.Url, file["url"], file
def fetch_album(self, album_id):
url = "{}/a/{}".format(self.root, album_id)
url = f"{self.root}/a/{album_id}"
page = self.request(url).text
extr = text.extract_from(page)
@@ -60,7 +60,7 @@ class CyberdropAlbumExtractor(lolisafe.LolisafeAlbumExtractor):
def _extract_files(self, file_ids):
for file_id in file_ids:
try:
url = "{}/api/file/info/{}".format(self.root_api, file_id)
url = f"{self.root_api}/api/file/info/{file_id}"
file = self.request(url).json()
auth = self.request(file["auth_url"]).json()
file["url"] = auth["url"]

View File

@@ -112,8 +112,7 @@ class DanbooruExtractor(BaseExtractor):
def items_artists(self):
for artist in self.artists():
artist["_extractor"] = DanbooruTagExtractor
url = "{}/posts?tags={}".format(
self.root, text.quote(artist["name"]))
url = f"{self.root}/posts?tags={text.quote(artist['name'])}"
yield Message.Queue, url, artist
def metadata(self):
@@ -156,7 +155,7 @@ class DanbooruExtractor(BaseExtractor):
return
if prefix:
params["page"] = "{}{}".format(prefix, posts[-1]["id"])
params["page"] = f"{prefix}{posts[-1]['id']}"
elif params["page"]:
params["page"] += 1
else:
@@ -164,8 +163,8 @@ class DanbooruExtractor(BaseExtractor):
first = False
def _ugoira_frames(self, post):
data = self.request_json("{}/posts/{}.json?only=media_metadata".format(
self.root, post["id"])
data = self.request_json(
f"{self.root}/posts/{post['id']}.json?only=media_metadata"
)["media_metadata"]["metadata"]
if "Ugoira:FrameMimeType" in data:
@@ -185,15 +184,15 @@ class DanbooruExtractor(BaseExtractor):
order = self.config("order-posts")
if not order or order in {"asc", "pool", "pool_asc", "asc_pool"}:
params = {"tags": "ord{}:{}".format(ctype, cid)}
params = {"tags": f"ord{ctype}:{cid}"}
elif order in {"id", "desc_id", "id_desc"}:
params = {"tags": "{}:{}".format(ctype, cid)}
params = {"tags": f"{ctype}:{cid}"}
prefix = "b"
elif order in {"desc", "desc_pool", "pool_desc"}:
params = {"tags": "ord{}:{}".format(ctype, cid)}
params = {"tags": f"ord{ctype}:{cid}"}
reverse = True
elif order in {"asc_id", "id_asc"}:
params = {"tags": "{}:{}".format(ctype, cid)}
params = {"tags": f"{ctype}:{cid}"}
reverse = True
posts = self._pagination("/posts.json", params, prefix)
@@ -204,7 +203,7 @@ class DanbooruExtractor(BaseExtractor):
return self._collection_enumerate(posts)
def _collection_metadata(self, cid, ctype, cname=None):
url = "{}/{}s/{}.json".format(self.root, cname or ctype, cid)
url = f"{self.root}/{cname or ctype}s/{cid}.json"
collection = self.request_json(url)
collection["name"] = collection["name"].replace("_", " ")
self.post_ids = collection.pop("post_ids", ())
@@ -320,7 +319,7 @@ class DanbooruPostExtractor(DanbooruExtractor):
example = "https://danbooru.donmai.us/posts/12345"
def posts(self):
url = "{}/posts/{}.json".format(self.root, self.groups[-1])
url = f"{self.root}/posts/{self.groups[-1]}.json"
post = self.request_json(url)
if self.includes:
params = {"only": self.includes}
@@ -362,7 +361,7 @@ class DanbooruArtistExtractor(DanbooruExtractor):
items = DanbooruExtractor.items_artists
def artists(self):
url = "{}/artists/{}.json".format(self.root, self.groups[-1])
url = f"{self.root}/artists/{self.groups[-1]}.json"
return (self.request_json(url),)

View File

@@ -49,7 +49,7 @@ class DesktopographyExhibitionExtractor(DesktopographyExtractor):
self.year = match[1]
def items(self):
url = "{}/exhibition-{}/".format(self.root, self.year)
url = f"{self.root}/exhibition-{self.year}/"
base_entry_url = "https://desktopography.net/portfolios/"
page = self.request(url).text
@@ -78,7 +78,7 @@ class DesktopographyEntryExtractor(DesktopographyExtractor):
self.entry = match[1]
def items(self):
url = "{}/portfolios/{}".format(self.root, self.entry)
url = f"{self.root}/portfolios/{self.entry}"
page = self.request(url).text
entry_data = {"entry": self.entry}

View File

@@ -67,7 +67,7 @@ class DeviantartExtractor(Extractor):
self.quality = "-fullview.png?"
self.quality_sub = util.re(r"-fullview\.[a-z0-9]+\?").sub
else:
self.quality = ",q_{}".format(self.quality)
self.quality = f",q_{self.quality}"
self.quality_sub = util.re(r",q_\d+").sub
if self.intermediary:
@@ -196,7 +196,7 @@ class DeviantartExtractor(Extractor):
continue
_user_details.update(name, user)
url = "{}/{}/avatar/".format(self.root, name)
url = f"{self.root}/{name}/avatar/"
comment["_extractor"] = DeviantartAvatarExtractor
yield Message.Queue, url, comment
@@ -701,10 +701,10 @@ x2="45.4107524%" y2="71.4898596%" id="app-root-3">\
raise exception.NotFoundError("folder")
def _folder_urls(self, folders, category, extractor):
base = "{}/{}/{}/".format(self.root, self.user, category)
base = f"{self.root}/{self.user}/{category}/"
for folder in folders:
folder["_extractor"] = extractor
url = "{}{}/{}".format(base, folder["folderid"], folder["name"])
url = f"{base}{folder['folderid']}/{folder['name']}"
yield url, folder
def _update_content_default(self, deviation, content):
@@ -878,7 +878,7 @@ class DeviantartUserExtractor(Dispatch, DeviantartExtractor):
example = "https://www.deviantart.com/USER"
def items(self):
base = "{}/{}/".format(self.root, self.user)
base = f"{self.root}/{self.user}/"
return self._dispatch_extractors((
(DeviantartAvatarExtractor , base + "avatar"),
(DeviantartBackgroundExtractor, base + "banner"),
@@ -943,8 +943,8 @@ class DeviantartAvatarExtractor(DeviantartExtractor):
fmt, _, ext = fmt.rpartition(".")
if fmt:
fmt = "-" + fmt
url = "https://a.deviantart.net/avatars{}/{}/{}/{}.{}?{}".format(
fmt, name[0], name[1], name, ext, index)
url = (f"https://a.deviantart.net/avatars{fmt}"
f"/{name[0]}/{name[1]}/{name}.{ext}?{index}")
results.append(self._make_deviation(url, user, index, fmt))
return results
@@ -1282,10 +1282,10 @@ class DeviantartDeviationExtractor(DeviantartExtractor):
def deviations(self):
if self.user:
url = "{}/{}/{}/{}".format(
self.root, self.user, self.type or "art", self.deviation_id)
url = (f"{self.root}/{self.user}"
f"/{self.type or 'art'}/{self.deviation_id}")
else:
url = "{}/view/{}/".format(self.root, self.deviation_id)
url = f"{self.root}/view/{self.deviation_id}/"
page = self._limited_request(url, notfound="deviation").text
uuid = text.extr(page, '"deviationUuid\\":\\"', '\\')
@@ -1431,7 +1431,7 @@ class DeviantartFollowingExtractor(DeviantartExtractor):
api = DeviantartOAuthAPI(self)
for user in api.user_friends(self.user):
url = "{}/{}".format(self.root, user["user"]["username"])
url = f"{self.root}/{user['user']['username']}"
user["_extractor"] = DeviantartUserExtractor
yield Message.Queue, url, user
@@ -1579,7 +1579,7 @@ class DeviantartOAuthAPI():
def comments(self, target_id, target_type="deviation",
comment_id=None, offset=0):
"""Fetch comments posted on a target"""
endpoint = "/comments/{}/{}".format(target_type, target_id)
endpoint = f"/comments/{target_type}/{target_id}"
params = {
"commentid" : comment_id,
"maxdepth" : "5",
@@ -1633,7 +1633,7 @@ class DeviantartOAuthAPI():
def deviation_metadata(self, deviations):
""" Fetch deviation metadata for a set of deviations"""
endpoint = "/deviation/metadata?" + "&".join(
"deviationids[{}]={}".format(num, deviation["deviationid"])
f"deviationids[{num}]={deviation['deviationid']}"
for num, deviation in enumerate(deviations)
)
return self._call(
@@ -1740,8 +1740,8 @@ class DeviantartOAuthAPI():
if response.status_code != 200:
self.log.debug("Server response: %s", data)
raise exception.AuthenticationError('"{}" ({})'.format(
data.get("error_description"), data.get("error")))
raise exception.AuthenticationError(
f"\"{data.get('error_description')}\" ({data.get('error')})")
if refresh_token_key:
_refresh_token_cache.update(
refresh_token_key, data["refresh_token"])
@@ -1784,8 +1784,7 @@ class DeviantartOAuthAPI():
raise exception.AuthorizationError()
self.log.debug(response.text)
msg = "API responded with {} {}".format(
status, response.reason)
msg = f"API responded with {status} {response.reason}"
if status == 429:
if self.delay < 30:
self.delay += 1
@@ -2068,7 +2067,7 @@ class DeviantartEclipseAPI():
params["offset"] = int(params["offset"]) + len(results)
def _ids_watching(self, user):
url = "{}/{}/about".format(self.extractor.root, user)
url = f"{self.extractor.root}/{user}/about"
page = self.request(url).text
gruser_id = text.extr(page, ' data-userid="', '"')

View File

@@ -20,7 +20,7 @@ class DynastyscansBase():
root = "https://dynasty-scans.com"
def _parse_image_page(self, image_id):
url = "{}/images/{}".format(self.root, image_id)
url = f"{self.root}/images/{image_id}"
extr = text.extract_from(self.request(url).text)
date = extr("class='create_at'>", "</span>")
@@ -140,7 +140,7 @@ class DynastyscansAnthologyExtractor(DynastyscansSearchExtractor):
example = "https://dynasty-scans.com/anthologies/TITLE"
def items(self):
url = "{}/anthologies/{}.atom".format(self.root, self.groups[0])
url = f"{self.root}/anthologies/{self.groups[0]}.atom"
root = self.request_xml(url, xmlns=False)
data = {

View File

@@ -40,8 +40,8 @@ class E621Extractor(danbooru.DanbooruExtractor):
if not file["url"]:
md5 = file["md5"]
file["url"] = "https://static1.{}/data/{}/{}/{}.{}".format(
self.root[8:], md5[0:2], md5[2:4], md5, file["ext"])
file["url"] = (f"https://static1.{self.root[8:]}/data"
f"/{md5[0:2]}/{md5[2:4]}/{md5}.{file['ext']}")
if notes and post.get("has_notes"):
post["notes"] = self._get_notes(post["id"])
@@ -61,12 +61,12 @@ class E621Extractor(danbooru.DanbooruExtractor):
def _get_notes(self, id):
return self.request(
"{}/notes.json?search[post_id]={}".format(self.root, id)).json()
f"{self.root}/notes.json?search[post_id]={id}").json()
@memcache(keyarg=1)
def _get_pools(self, ids):
pools = self.request(
"{}/pools.json?search[id]={}".format(self.root, ids)).json()
f"{self.root}/pools.json?search[id]={ids}").json()
for pool in pools:
pool["name"] = pool["name"].replace("_", " ")
return pools
@@ -126,7 +126,7 @@ class E621PostExtractor(E621Extractor, danbooru.DanbooruPostExtractor):
example = "https://e621.net/posts/12345"
def posts(self):
url = "{}/posts/{}.json".format(self.root, self.groups[-1])
url = f"{self.root}/posts/{self.groups[-1]}.json"
return (self.request(url).json()["post"],)

View File

@@ -26,7 +26,7 @@ class EromeExtractor(Extractor):
def items(self):
self.__cookies = True
for album_id in self.albums():
url = "{}/a/{}".format(self.root, album_id)
url = f"{self.root}/a/{album_id}"
try:
page = self.request(url).text
@@ -116,7 +116,7 @@ class EromeUserExtractor(EromeExtractor):
example = "https://www.erome.com/USER"
def albums(self):
url = "{}/{}".format(self.root, self.groups[0])
url = f"{self.root}/{self.groups[0]}"
return self._pagination(url, {})

View File

@@ -29,9 +29,9 @@ class EveriaExtractor(Extractor):
while True:
if pnum == 1:
url = "{}{}/".format(self.root, path)
url = f"{self.root}{path}/"
else:
url = "{}{}/page/{}/".format(self.root, path, pnum)
url = f"{self.root}{path}/page/{pnum}/"
response = self.request(url, params=params, allow_redirects=False)
if response.status_code >= 300:

View File

@@ -221,7 +221,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
data = {}
from .hitomi import HitomiGalleryExtractor
url = "https://hitomi.la/galleries/{}.html".format(self.gallery_id)
url = f"https://hitomi.la/galleries/{self.gallery_id}.html"
data["_extractor"] = HitomiGalleryExtractor
yield Message.Queue, url, data
@@ -491,8 +491,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
_limits_update = _request_home
def _gallery_page(self):
url = "{}/g/{}/{}/".format(
self.root, self.gallery_id, self.gallery_token)
url = f"{self.root}/g/{self.gallery_id}/{self.gallery_token}/"
response = self.request(url, fatal=False)
page = response.text
@@ -505,8 +504,8 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
return page
def _image_page(self):
url = "{}/s/{}/{}-{}".format(
self.root, self.image_token, self.gallery_id, self.image_num)
url = (f"{self.root}/s/{self.image_token}"
f"/{self.gallery_id}-{self.image_num}")
page = self.request(url, fatal=False).text
if page.startswith(("Invalid page", "Keep trying")):
@@ -514,7 +513,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
return page
def _fallback_original(self, nl, fullimg):
url = "{}?nl={}".format(fullimg, nl)
url = f"{fullimg}?nl={nl}"
for _ in util.repeat(self.fallback_retries):
yield url
@@ -523,8 +522,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
token = self.key_start
for _ in util.repeat(self.fallback_retries):
url = "{}/s/{}/{}-{}?nl={}".format(
self.root, token, self.gallery_id, num, nl)
url = f"{self.root}/s/{token}/{self.gallery_id}-{num}?nl={nl}"
page = self.request(url, fatal=False).text
if page.startswith(("Invalid page", "Keep trying")):
@@ -577,7 +575,7 @@ class ExhentaiSearchExtractor(ExhentaiExtractor):
if tag:
if "+" in tag:
ns, _, tag = tag.rpartition(":")
tag = '{}:"{}$"'.format(ns, tag.replace("+", " "))
tag = f"{ns}:\"{tag.replace('+', ' ')}$\""
else:
tag += "$"
self.params = {"f_search": tag, "page": 0}

View File

@@ -340,7 +340,7 @@ class FanboxExtractor(Extractor):
templ = "https://docs.google.com/forms/d/e/{}/viewform?usp=sf_link"
url = templ.format(content_id)
else:
self.log.warning("service not recognized: {}".format(provider))
self.log.warning(f"service not recognized: {provider}")
if url:
final_post["embed"] = embed

View File

@@ -184,7 +184,7 @@ class FantiaCreatorExtractor(FantiaExtractor):
self.creator_id = match[1]
def posts(self):
url = "{}/fanclubs/{}/posts".format(self.root, self.creator_id)
url = f"{self.root}/fanclubs/{self.creator_id}/posts"
return self._pagination(url)

View File

@@ -31,8 +31,7 @@ class FapachiPostExtractor(Extractor):
"user": self.user,
"id" : self.id,
}
page = self.request("{}/{}/media/{}".format(
self.root, self.user, self.id)).text
page = self.request(f"{self.root}/{self.user}/media/{self.id}").text
url = self.root + text.extract(
page, 'data-src="', '"', page.index('class="media-img'))[0]
yield Message.Directory, data
@@ -56,8 +55,8 @@ class FapachiUserExtractor(Extractor):
def items(self):
data = {"_extractor": FapachiPostExtractor}
while True:
page = self.request("{}/{}/page/{}".format(
self.root, self.user, self.num)).text
url = f"{self.root}/{self.user}/page/{self.num}"
page = self.request(url).text
for post in text.extract_iter(page, 'model-media-prew">', ">"):
path = text.extr(post, '<a href="', '"')
if path:

View File

@@ -29,7 +29,7 @@ class FapelloPostExtractor(Extractor):
self.model, self.id = match.groups()
def items(self):
url = "{}/{}/{}/".format(self.root, self.model, self.id)
url = f"{self.root}/{self.model}/{self.id}/"
page = text.extr(
self.request(url, allow_redirects=False).text,
'class="uk-align-center"', "</div>", None)
@@ -66,8 +66,7 @@ class FapelloModelExtractor(Extractor):
num = 1
data = {"_extractor": FapelloPostExtractor}
while True:
url = "{}/ajax/model/{}/page-{}/".format(
self.root, self.model, num)
url = f"{self.root}/ajax/model/{self.model}/page-{num}/"
page = self.request(url).text
if not page:
return
@@ -109,8 +108,8 @@ class FapelloPathExtractor(Extractor):
data = {"_extractor": FapelloModelExtractor}
while True:
page = self.request("{}/ajax/{}/page-{}/".format(
self.root, self.path, num)).text
url = f"{self.root}/ajax/{self.path}/page-{num}/"
page = self.request(url).text
if not page:
return

View File

@@ -134,8 +134,8 @@ class FlickrAlbumExtractor(FlickrExtractor):
for album in self.api.photosets_getList(self.user["nsid"]):
self.api._clean_info(album).update(data)
url = "https://www.flickr.com/photos/{}/albums/{}".format(
self.user["path_alias"], album["id"])
url = (f"https://www.flickr.com/photos/{self.user['path_alias']}"
f"/albums/{album['id']}")
yield Message.Queue, url, album
def metadata(self):

View File

@@ -71,7 +71,7 @@ class FuraffinityExtractor(Extractor):
return num
def _parse_post(self, post_id):
url = "{}/view/{}/".format(self.root, post_id)
url = f"{self.root}/view/{post_id}/"
extr = text.extract_from(self.request(url).text)
if self._new_layout is None:
@@ -147,9 +147,8 @@ class FuraffinityExtractor(Extractor):
data["user"] = self.user or data["artist_url"]
data["date"] = text.parse_timestamp(data["filename"].partition(".")[0])
data["description"] = self._process_description(data["_description"])
data["thumbnail"] = "https://t.furaffinity.net/{}@600-{}.jpg".format(
post_id, path.rsplit("/", 2)[1])
data["thumbnail"] = (f"https://t.furaffinity.net/{post_id}@600-"
f"{path.rsplit('/', 2)[1]}.jpg")
return data
def _process_description(self, description):
@@ -157,11 +156,10 @@ class FuraffinityExtractor(Extractor):
def _pagination(self, path, folder=None):
num = 1
folder = "" if folder is None else "/folder/{}/a".format(folder)
folder = "" if folder is None else f"/folder/{folder}/a"
while True:
url = "{}/{}/{}{}/{}/".format(
self.root, path, self.user, folder, num)
url = f"{self.root}/{path}/{self.user}{folder}/{num}/"
page = self.request(url).text
post_id = None
@@ -173,7 +171,7 @@ class FuraffinityExtractor(Extractor):
num += 1
def _pagination_favorites(self):
path = "/favorites/{}/".format(self.user)
path = f"/favorites/{self.user}/"
while path:
page = self.request(self.root + path).text
@@ -326,7 +324,7 @@ class FuraffinityUserExtractor(Dispatch, FuraffinityExtractor):
example = "https://www.furaffinity.net/user/USER/"
def items(self):
base = "{}/{{}}/{}/".format(self.root, self.user)
base = f"{self.root}/{{}}/{self.user}/"
return self._dispatch_extractors((
(FuraffinityGalleryExtractor , base.format("gallery")),
(FuraffinityScrapsExtractor , base.format("scraps")),
@@ -341,7 +339,7 @@ class FuraffinityFollowingExtractor(FuraffinityExtractor):
example = "https://www.furaffinity.net/watchlist/by/USER/"
def items(self):
url = "{}/watchlist/by/{}/".format(self.root, self.user)
url = f"{self.root}/watchlist/by/{self.user}/"
data = {"_extractor": FuraffinityUserExtractor}
while True:

View File

@@ -46,8 +46,8 @@ class Furry34Extractor(BooruExtractor):
post_id = post["id"]
root = self.root_cdn if files[fmt][0] else self.root
post["file_url"] = url = "{}/posts/{}/{}/{}.{}".format(
root, post_id // 1000, post_id, post_id, extension)
post["file_url"] = url = \
f"{root}/posts/{post_id // 1000}/{post_id}/{post_id}.{extension}"
post["format_id"] = fmt
post["format"] = extension.partition(".")[0]
@@ -73,11 +73,11 @@ class Furry34Extractor(BooruExtractor):
post["tags_" + types[type]] = values
def _fetch_post(self, post_id):
url = "{}/api/v2/post/{}".format(self.root, post_id)
url = f"{self.root}/api/v2/post/{post_id}"
return self.request(url).json()
def _pagination(self, endpoint, params=None):
url = "{}/api{}".format(self.root, endpoint)
url = f"{self.root}/api{endpoint}"
if params is None:
params = {}

View File

@@ -22,7 +22,7 @@ class FuskatorGalleryExtractor(GalleryExtractor):
def __init__(self, match):
self.gallery_hash = match[1]
url = "{}/thumbs/{}/index.html".format(self.root, self.gallery_hash)
url = f"{self.root}/thumbs/{self.gallery_hash}/index.html"
GalleryExtractor.__init__(self, match, url)
def metadata(self, page):

View File

@@ -73,7 +73,7 @@ class GelbooruBase():
if id:
tag = "id:" + op
tags = [t for t in tags if not t.startswith(tag)]
tags = "{} id:{}".format(" ".join(tags), op)
tags = f"{' '.join(tags)} id:{op}"
while True:
posts = self._api_request(params)
@@ -113,7 +113,7 @@ class GelbooruBase():
post["_fallback"] = (url,)
md5 = post["md5"]
root = text.root_from_url(post["preview_url"])
path = "/images/{}/{}/{}.webm".format(md5[0:2], md5[2:4], md5)
path = f"/images/{md5[0:2]}/{md5[2:4]}/{md5}.webm"
url = root + path
return url

View File

@@ -17,8 +17,7 @@ class GelbooruV01Extractor(booru.BooruExtractor):
per_page = 20
def _parse_post(self, post_id):
url = "{}/index.php?page=post&s=view&id={}".format(
self.root, post_id)
url = f"{self.root}/index.php?page=post&s=view&id={post_id}"
extr = text.extract_from(self.request(url).text)
post = {

View File

@@ -66,7 +66,7 @@ class GirlswithmusclePostExtractor(GirlswithmuscleExtractor):
def items(self):
self.login()
url = "{}/{}/".format(self.root, self.groups[0])
url = f"{self.root}/{self.groups[0]}/"
page = self.request(url).text
if not page:
raise exception.NotFoundError("post")
@@ -150,11 +150,10 @@ class GirlswithmuscleSearchExtractor(GirlswithmuscleExtractor):
def pages(self):
query = self.groups[0]
url = "{}/images/{}".format(self.root, query)
url = f"{self.root}/images/{query}"
response = self.request(url)
if response.history:
msg = 'Request was redirected to "{}", try logging in'.format(
response.url)
msg = f'Request was redirected to "{response.url}", try logging in'
raise exception.AuthorizationError(msg)
page = response.text
@@ -164,7 +163,7 @@ class GirlswithmuscleSearchExtractor(GirlswithmuscleExtractor):
yield page
for i in range(current + 1, total + 1):
url = "{}/images/{}/{}".format(self.root, i, query)
url = f"{self.root}/images/{i}/{query}"
yield self.request(url).text
def items(self):
@@ -175,5 +174,5 @@ class GirlswithmuscleSearchExtractor(GirlswithmuscleExtractor):
"gallery_name": text.unescape(text.extr(page, "<title>", "<")),
}
for imgid in text.extract_iter(page, 'id="imgid-', '"'):
url = "{}/{}/".format(self.root, imgid)
url = f"{self.root}/{imgid}/"
yield Message.Queue, url, data

View File

@@ -44,7 +44,7 @@ class HentaicosplaysGalleryExtractor(
def __init__(self, match):
BaseExtractor.__init__(self, match)
self.slug = self.groups[-1]
self.page_url = "{}/story/{}/".format(self.root, self.slug)
self.page_url = f"{self.root}/story/{self.slug}/"
def _init(self):
self.session.headers["Referer"] = self.page_url

View File

@@ -58,7 +58,7 @@ class HentaifoundryExtractor(Extractor):
num = self.start_page
while True:
page = self.request("{}/page/{}".format(url, num)).text
page = self.request(f"{url}/page/{num}").text
yield from text.extract_iter(page, begin, end)
if 'class="pager"' not in page or 'class="last hidden"' in page:
@@ -220,7 +220,7 @@ class HentaifoundryPicturesExtractor(HentaifoundryExtractor):
def __init__(self, match):
HentaifoundryExtractor.__init__(self, match)
self.page_url = "{}/pictures/user/{}".format(self.root, self.user)
self.page_url = f"{self.root}/pictures/user/{self.user}"
class HentaifoundryScrapsExtractor(HentaifoundryExtractor):
@@ -232,8 +232,7 @@ class HentaifoundryScrapsExtractor(HentaifoundryExtractor):
def __init__(self, match):
HentaifoundryExtractor.__init__(self, match)
self.page_url = "{}/pictures/user/{}/scraps".format(
self.root, self.user)
self.page_url = f"{self.root}/pictures/user/{self.user}/scraps"
class HentaifoundryFavoriteExtractor(HentaifoundryExtractor):
@@ -246,8 +245,7 @@ class HentaifoundryFavoriteExtractor(HentaifoundryExtractor):
def __init__(self, match):
HentaifoundryExtractor.__init__(self, match)
self.page_url = "{}/user/{}/faves/pictures".format(
self.root, self.user)
self.page_url = f"{self.root}/user/{self.user}/faves/pictures"
class HentaifoundryTagExtractor(HentaifoundryExtractor):
@@ -260,7 +258,7 @@ class HentaifoundryTagExtractor(HentaifoundryExtractor):
def __init__(self, match):
HentaifoundryExtractor.__init__(self, match)
self.page_url = "{}/pictures/tagged/{}".format(self.root, self.user)
self.page_url = f"{self.root}/pictures/tagged/{self.user}"
def metadata(self):
return {"search_tags": self.user}
@@ -276,7 +274,7 @@ class HentaifoundryRecentExtractor(HentaifoundryExtractor):
def __init__(self, match):
HentaifoundryExtractor.__init__(self, match)
self.page_url = "{}/pictures/recent/{}".format(self.root, self.user)
self.page_url = f"{self.root}/pictures/recent/{self.user}"
def metadata(self):
return {"date": self.user}
@@ -309,8 +307,8 @@ class HentaifoundryImageExtractor(HentaifoundryExtractor):
self.index = match[3]
def items(self):
post_url = "{}/pictures/user/{}/{}/?enterAgree=1".format(
self.root, self.user, self.index)
post_url = (f"{self.root}/pictures/user/{self.user}"
f"/{self.index}/?enterAgree=1")
image = self._parse_post(post_url)
image["user"] = self.user
yield Message.Directory, image
@@ -332,7 +330,7 @@ class HentaifoundryStoriesExtractor(HentaifoundryExtractor):
yield Message.Url, story["src"], story
def stories(self):
url = "{}/stories/user/{}".format(self.root, self.user)
url = f"{self.root}/stories/user/{self.user}"
return self._pagination(url, '<div class="storyRow">', '</tr></table>')
@@ -350,8 +348,8 @@ class HentaifoundryStoryExtractor(HentaifoundryExtractor):
self.index = match[3]
def items(self):
story_url = "{}/stories/user/{}/{}/x?enterAgree=1".format(
self.root, self.user, self.index)
story_url = (f"{self.root}/stories/user/{self.user}"
f"/{self.index}/x?enterAgree=1")
story = self._parse_story(self.request(story_url).text)
yield Message.Directory, story
yield Message.Url, story["src"], story

View File

@@ -21,7 +21,7 @@ class HentaihandGalleryExtractor(GalleryExtractor):
def __init__(self, match):
self.slug = match[1]
url = "{}/api/comics/{}".format(self.root, self.slug)
url = f"{self.root}/api/comics/{self.slug}"
GalleryExtractor.__init__(self, match, url)
def metadata(self, page):
@@ -68,7 +68,7 @@ class HentaihandTagExtractor(Extractor):
else:
tpl = self.type + "s"
url = "{}/api/{}/{}".format(self.root, tpl, self.key)
url = f"{self.root}/api/{tpl}/{self.key}"
tid = self.request(url, notfound=self.type).json()["id"]
url = self.root + "/api/comics"
@@ -85,7 +85,7 @@ class HentaihandTagExtractor(Extractor):
info = self.request(url, params=params).json()
for gallery in info["data"]:
gurl = "{}/en/comic/{}".format(self.root, gallery["slug"])
gurl = f"{self.root}/en/comic/{gallery['slug']}"
gallery["_extractor"] = HentaihandGalleryExtractor
yield Message.Queue, gurl, gallery

View File

@@ -26,7 +26,7 @@ class HentaihereChapterExtractor(HentaihereBase, ChapterExtractor):
def __init__(self, match):
self.manga_id, self.chapter = match.groups()
url = "{}/m/S{}/{}/1".format(self.root, self.manga_id, self.chapter)
url = f"{self.root}/m/S{self.manga_id}/{self.chapter}/1"
ChapterExtractor.__init__(self, match, url)
def metadata(self, page):

View File

@@ -23,7 +23,7 @@ class HentainexusGalleryExtractor(GalleryExtractor):
def __init__(self, match):
self.gallery_id = match[1]
url = "{}/view/{}".format(self.root, self.gallery_id)
url = f"{self.root}/view/{self.gallery_id}"
GalleryExtractor.__init__(self, match, url)
def metadata(self, page):
@@ -59,7 +59,7 @@ class HentainexusGalleryExtractor(GalleryExtractor):
return data
def images(self, _):
url = "{}/read/{}".format(self.root, self.gallery_id)
url = f"{self.root}/read/{self.gallery_id}"
page = self.request(url).text
imgs = util.json_loads(self._decode(text.extr(
page, 'initReader("', '"')))
@@ -135,18 +135,18 @@ class HentainexusGalleryExtractor(GalleryExtractor):
jt = ''
if event:
jt += '({}) '.format(event)
jt += f'({event}) '
if circle:
jt += '[{} ({})] '.format(circle, artist)
jt += f'[{circle} ({artist})] '
else:
jt += '[{}] '.format(artist)
jt += f'[{artist}] '
jt += title
if parody.lower() != 'original work':
jt += ' ({})'.format(parody)
jt += f' ({parody})'
if book:
jt += ' ({})'.format(book)
jt += f' ({book})'
if magazine:
jt += ' ({})'.format(magazine)
jt += f' ({magazine})'
return jt

View File

@@ -24,7 +24,7 @@ class HiperdexBase():
@memcache(keyarg=1)
def manga_data(self, manga, page=None):
if not page:
url = "{}/manga/{}/".format(self.root, manga)
url = f"{self.root}/manga/{manga}/"
page = self.request(url).text
extr = text.extract_from(page)

View File

@@ -32,12 +32,10 @@ class HitomiGalleryExtractor(HitomiExtractor, GalleryExtractor):
def __init__(self, match):
GalleryExtractor.__init__(self, match, False)
self.gid = gid = self.groups[0]
self.page_url = "https://ltn.{}/galleries/{}.js".format(
self.domain, gid)
self.page_url = f"https://ltn.{self.domain}/galleries/{gid}.js"
def _init(self):
self.session.headers["Referer"] = "{}/reader/{}.html".format(
self.root, self.gid)
self.session.headers["Referer"] = f"{self.root}/reader/{self.gid}.html"
def metadata(self, page):
self.info = info = util.json_loads(page.partition("=")[2])
@@ -93,10 +91,8 @@ class HitomiGalleryExtractor(HitomiExtractor, GalleryExtractor):
# https://ltn.gold-usergeneratedcontent.net/common.js
inum = int(ihash[-1] + ihash[-3:-1], 16)
url = "https://{}{}.{}/{}/{}/{}.{}".format(
ext[0], gg_m.get(inum, gg_default) + 1, self.domain,
gg_b, inum, ihash, ext,
)
url = (f"https://{ext[0]}{gg_m.get(inum, gg_default) + 1}."
f"{self.domain}/{gg_b}/{inum}/{ihash}.{ext}")
result.append((url, idata))
return result
@@ -122,8 +118,7 @@ class HitomiTagExtractor(HitomiExtractor):
"_extractor": HitomiGalleryExtractor,
"search_tags": text.unquote(self.tag.rpartition("-")[0]),
}
nozomi_url = "https://ltn.{}/{}/{}.nozomi".format(
self.domain, self.type, self.tag)
nozomi_url = f"https://ltn.{self.domain}/{self.type}/{self.tag}.nozomi"
headers = {
"Origin": self.root,
"Cache-Control": "max-age=0",
@@ -132,14 +127,13 @@ class HitomiTagExtractor(HitomiExtractor):
offset = 0
total = None
while True:
headers["Referer"] = "{}/{}/{}.html?page={}".format(
self.root, self.type, self.tag, offset // 100 + 1)
headers["Range"] = "bytes={}-{}".format(offset, offset+99)
headers["Referer"] = (f"{self.root}/{self.type}/{self.tag}.html"
f"?page={offset // 100 + 1}")
headers["Range"] = f"bytes={offset}-{offset + 99}"
response = self.request(nozomi_url, headers=headers)
for gallery_id in decode_nozomi(response.content):
gallery_url = "{}/galleries/{}.html".format(
self.root, gallery_id)
gallery_url = f"{self.root}/galleries/{gallery_id}.html"
yield Message.Queue, gallery_url, data
offset += 100
@@ -162,8 +156,8 @@ class HitomiIndexExtractor(HitomiTagExtractor):
def items(self):
data = {"_extractor": HitomiGalleryExtractor}
nozomi_url = "https://ltn.{}/{}-{}.nozomi".format(
self.domain, self.tag, self.language)
nozomi_url = (f"https://ltn.{self.domain}"
f"/{self.tag}-{self.language}.nozomi")
headers = {
"Origin": self.root,
"Cache-Control": "max-age=0",
@@ -172,14 +166,13 @@ class HitomiIndexExtractor(HitomiTagExtractor):
offset = 0
total = None
while True:
headers["Referer"] = "{}/{}-{}.html?page={}".format(
self.root, self.tag, self.language, offset // 100 + 1)
headers["Range"] = "bytes={}-{}".format(offset, offset+99)
headers["Referer"] = (f"{self.root}/{self.tag}-{self.language}"
f".html?page={offset // 100 + 1}")
headers["Range"] = f"bytes={offset}-{offset + 99}"
response = self.request(nozomi_url, headers=headers)
for gallery_id in decode_nozomi(response.content):
gallery_url = "{}/galleries/{}.html".format(
self.root, gallery_id)
gallery_url = f"{self.root}/galleries/{gallery_id}.html"
yield Message.Queue, gallery_url, data
offset += 100
@@ -210,24 +203,22 @@ class HitomiSearchExtractor(HitomiExtractor):
intersects = set.intersection(*results)
for gallery_id in sorted(intersects, reverse=True):
gallery_url = "{}/galleries/{}.html".format(
self.root, gallery_id)
gallery_url = f"{self.root}/galleries/{gallery_id}.html"
yield Message.Queue, gallery_url, data
def get_nozomi_items(self, full_tag):
area, tag, language = self.get_nozomi_args(full_tag)
base = f"https://ltn.{self.domain}/n/"
if area:
nozomi_url = "https://ltn.{}/n/{}/{}-{}.nozomi".format(
self.domain, area, tag, language)
nozomi_url = f"{base}{area}/{tag}-{language}.nozomi"
else:
nozomi_url = "https://ltn.{}/n/{}-{}.nozomi".format(
self.domain, tag, language)
nozomi_url = f"{base}{tag}-{language}.nozomi"
headers = {
"Origin": self.root,
"Cache-Control": "max-age=0",
"Referer": "{}/search.html?{}".format(self.root, self.query),
"Referer": f"{self.root}/search.html?{self.query}",
}
response = self.request(nozomi_url, headers=headers)

View File

@@ -70,8 +70,7 @@ class HotleakPostExtractor(HotleakExtractor):
self.creator, self.type, self.id = match.groups()
def posts(self):
url = "{}/{}/{}/{}".format(
self.root, self.creator, self.type, self.id)
url = f"{self.root}/{self.creator}/{self.type}/{self.id}"
page = self.request(url).text
page = text.extr(
page, '<div class="movie-image thumb">', '</article>')
@@ -106,7 +105,7 @@ class HotleakCreatorExtractor(HotleakExtractor):
self.creator = match[1]
def posts(self):
url = "{}/{}".format(self.root, self.creator)
url = f"{self.root}/{self.creator}"
return self._pagination(url)
def _pagination(self, url):
@@ -159,7 +158,7 @@ class HotleakCategoryExtractor(HotleakExtractor):
self._category, self.params = match.groups()
def items(self):
url = "{}/{}".format(self.root, self._category)
url = f"{self.root}/{self._category}"
if self._category in ("hot", "creators"):
data = {"_extractor": HotleakCreatorExtractor}

View File

@@ -49,7 +49,7 @@ class ImagefapGalleryExtractor(ImagefapExtractor):
self.image_id = ""
def items(self):
url = "{}/gallery/{}".format(self.root, self.gid)
url = f"{self.root}/gallery/{self.gid}"
page = self.request(url).text
data = self.get_job_metadata(page)
yield Message.Directory, data
@@ -81,12 +81,12 @@ class ImagefapGalleryExtractor(ImagefapExtractor):
def get_images(self):
"""Collect image-urls and -metadata"""
url = "{}/photo/{}/".format(self.root, self.image_id)
url = f"{self.root}/photo/{self.image_id}/"
params = {"gid": self.gid, "idx": 0, "partial": "true"}
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"X-Requested-With": "XMLHttpRequest",
"Referer": "{}?pgid=&gid={}&page=0".format(url, self.image_id)
"Referer": f"{url}?pgid=&gid={self.image_id}&page=0"
}
num = 0
@@ -124,7 +124,7 @@ class ImagefapImageExtractor(ImagefapExtractor):
yield Message.Url, url, data
def get_image(self):
url = "{}/photo/{}/".format(self.root, self.image_id)
url = f"{self.root}/photo/{self.image_id}/"
page = self.request(url).text
url, pos = text.extract(
@@ -161,7 +161,7 @@ class ImagefapFolderExtractor(ImagefapExtractor):
def items(self):
for gallery_id, name, folder in self.galleries(self.folder_id):
url = "{}/gallery/{}".format(self.root, gallery_id)
url = f"{self.root}/gallery/{gallery_id}"
data = {
"gallery_id": gallery_id,
"title" : text.unescape(name),
@@ -175,14 +175,13 @@ class ImagefapFolderExtractor(ImagefapExtractor):
if folder_id == "-1":
folder_name = "Uncategorized"
if self._id:
url = "{}/usergallery.php?userid={}&folderid=-1".format(
self.root, self.user)
url = (f"{self.root}/usergallery.php"
f"?userid={self.user}&folderid=-1")
else:
url = "{}/profile/{}/galleries?folderid=-1".format(
self.root, self.user)
url = f"{self.root}/profile/{self.user}/galleries?folderid=-1"
else:
folder_name = None
url = "{}/organizer/{}/".format(self.root, folder_id)
url = f"{self.root}/organizer/{folder_id}/"
params = {"page": 0}
extr = text.extract_from(self.request(url, params=params).text)
@@ -222,19 +221,17 @@ class ImagefapUserExtractor(ImagefapExtractor):
for folder_id in self.folders():
if folder_id == "-1":
url = "{}/profile/{}/galleries?folderid=-1".format(
self.root, self.user)
url = f"{self.root}/profile/{self.user}/galleries?folderid=-1"
else:
url = "{}/organizer/{}/".format(self.root, folder_id)
url = f"{self.root}/organizer/{folder_id}/"
yield Message.Queue, url, data
def folders(self):
"""Return a list of folder IDs of a user"""
if self.user:
url = "{}/profile/{}/galleries".format(self.root, self.user)
url = f"{self.root}/profile/{self.user}/galleries"
else:
url = "{}/usergallery.php?userid={}".format(
self.root, self.user_id)
url = f"{self.root}/usergallery.php?userid={self.user_id}"
response = self.request(url)
self.user = response.url.split("/")[-2]

View File

@@ -27,8 +27,7 @@ class ImagehostImageExtractor(Extractor):
def __init__(self, match):
Extractor.__init__(self, match)
self.page_url = "http{}://{}".format(
"s" if self._https else "", match[1])
self.page_url = f"http{'s' if self._https else ''}://{match[1]}"
self.token = match[2]
if self._params == "simple":

View File

@@ -164,7 +164,7 @@ class ImgbbUserExtractor(ImgbbExtractor):
ImgbbExtractor.__init__(self, match)
self.user = match[1]
self.sort = text.parse_query(match[2]).get("sort", "date_desc")
self.page_url = "https://{}.imgbb.com/".format(self.user)
self.page_url = f"https://{self.user}.imgbb.com/"
def metadata(self, page):
user = self._extract_user(page)

View File

@@ -21,7 +21,7 @@ class ImgthGalleryExtractor(GalleryExtractor):
def __init__(self, match):
self.gallery_id = gid = match[1]
url = "{}/gallery/{}/g/".format(self.root, gid)
url = f"{self.root}/gallery/{gid}/g/"
GalleryExtractor.__init__(self, match, url)
def metadata(self, page):
@@ -45,12 +45,11 @@ class ImgthGalleryExtractor(GalleryExtractor):
thumbs = text.extr(page, '<ul class="thumbnails">', '</ul>')
for url in text.extract_iter(thumbs, '<img src="', '"'):
path = url.partition("/thumbs/")[2]
yield ("{}/images/{}".format(self.root, path), None)
yield (f"{self.root}/images/{path}", None)
if '<li class="next">' not in page:
return
pnum += 1
url = "{}/gallery/{}/g/page/{}".format(
self.root, self.gallery_id, pnum)
url = f"{self.root}/gallery/{self.gallery_id}/g/page/{pnum}"
page = self.request(url).text

View File

@@ -36,8 +36,8 @@ class ImgurExtractor(Extractor):
elif image["is_animated"] and self.mp4 and image["ext"] == "gif":
image["ext"] = "mp4"
image["url"] = url = "https://i.imgur.com/{}.{}".format(
image["id"], image["ext"])
image["url"] = url = \
f"https://i.imgur.com/{image['id']}.{image['ext']}"
image["date"] = text.parse_datetime(image["created_at"])
image["_http_validate"] = self._validate
text.nameext_from_url(url, image)
@@ -131,10 +131,10 @@ class ImgurGalleryExtractor(ImgurExtractor):
def items(self):
if self.api.gallery(self.key)["is_album"]:
url = "{}/a/{}".format(self.root, self.key)
url = f"{self.root}/a/{self.key}"
extr = ImgurAlbumExtractor
else:
url = "{}/{}".format(self.root, self.key)
url = f"{self.root}/{self.key}"
extr = ImgurImageExtractor
yield Message.Queue, url, {"_extractor": extr}
@@ -234,16 +234,15 @@ class ImgurAPI():
self.headers = {"Authorization": "Client-ID " + self.client_id}
def account_submissions(self, account):
endpoint = "/3/account/{}/submissions".format(account)
endpoint = f"/3/account/{account}/submissions"
return self._pagination(endpoint)
def account_favorites(self, account):
endpoint = "/3/account/{}/gallery_favorites".format(account)
endpoint = f"/3/account/{account}/gallery_favorites"
return self._pagination(endpoint)
def account_favorites_folder(self, account, folder_id):
endpoint = "/3/account/{}/folders/{}/favorites".format(
account, folder_id)
endpoint = f"/3/account/{account}/folders/{folder_id}/favorites"
return self._pagination_v2(endpoint)
def accounts_me_allposts(self):
@@ -270,11 +269,11 @@ class ImgurAPI():
return self._pagination(endpoint, params)
def gallery_subreddit(self, subreddit):
endpoint = "/3/gallery/r/{}".format(subreddit)
endpoint = f"/3/gallery/r/{subreddit}"
return self._pagination(endpoint)
def gallery_tag(self, tag):
endpoint = "/3/gallery/t/{}".format(tag)
endpoint = f"/3/gallery/t/{tag}"
return self._pagination(endpoint, key="items")
def image(self, image_hash):
@@ -308,7 +307,7 @@ class ImgurAPI():
num = 0
while True:
data = self._call("{}/{}".format(endpoint, num), params)["data"]
data = self._call(f"{endpoint}/{num}", params)["data"]
if key:
data = data[key]
if not data:

View File

@@ -85,7 +85,7 @@ class ImhentaiGalleryExtractor(ImhentaiExtractor, GalleryExtractor):
def __init__(self, match):
ImhentaiExtractor.__init__(self, match)
self.gallery_id = self.groups[-1]
self.page_url = "{}/gallery/{}/".format(self.root, self.gallery_id)
self.page_url = f"{self.root}/gallery/{self.gallery_id}/"
def metadata(self, page):
extr = text.extract_from(page)

View File

@@ -153,8 +153,8 @@ class InkbunnyFavoriteExtractor(InkbunnyExtractor):
def metadata(self):
# Lookup fav user ID as username
url = "{}/userfavorites_process.php?favs_user_id={}".format(
self.root, self.user_id)
url = (f"{self.root}/userfavorites_process.php"
f"?favs_user_id={self.user_id}")
page = self.request(url).text
user_link = text.extr(page, '<a rel="author"', '</a>')
favs_username = text.extr(user_link, 'href="/', '"')
@@ -221,7 +221,7 @@ class InkbunnySearchExtractor(InkbunnyExtractor):
favsby = pop("favsby", None)
if favsby:
# get user_id from user profile
url = "{}/{}".format(self.root, favsby)
url = f"{self.root}/{favsby}"
page = self.request(url).text
user_id = text.extr(page, "?user_id=", "'")
params["favs_user_id"] = user_id.partition("&")[0]

View File

@@ -179,7 +179,7 @@ class InstagramExtractor(Extractor):
data = {
"post_id" : post["pk"],
"post_shortcode": post["code"],
"post_url": "{}/p/{}/".format(self.root, post["code"]),
"post_url": f"{self.root}/p/{post['code']}/",
"likes": post.get("like_count", 0),
"liked": post.get("has_liked", False),
"pinned": self._extract_pinned(post),
@@ -197,8 +197,8 @@ class InstagramExtractor(Extractor):
slug = location["short_name"].replace(" ", "-").lower()
data["location_id"] = location["pk"]
data["location_slug"] = slug
data["location_url"] = "{}/explore/locations/{}/{}/".format(
self.root, location["pk"], slug)
data["location_url"] = \
f"{self.root}/explore/locations/{location['pk']}/{slug}/"
coauthors = post.get("coauthor_producers")
if coauthors:
@@ -292,7 +292,7 @@ class InstagramExtractor(Extractor):
"fullname" : owner.get("full_name"),
"post_id" : post["id"],
"post_shortcode": post["shortcode"],
"post_url" : "{}/p/{}/".format(self.root, post["shortcode"]),
"post_url" : f"{self.root}/p/{post['shortcode']}/",
"post_date" : text.parse_timestamp(post["taken_at_timestamp"]),
"description": text.parse_unicode_escapes("\n".join(
edge["node"]["text"]
@@ -309,8 +309,8 @@ class InstagramExtractor(Extractor):
if location:
data["location_id"] = location["id"]
data["location_slug"] = location["slug"]
data["location_url"] = "{}/explore/locations/{}/{}/".format(
self.root, location["id"], location["slug"])
data["location_url"] = (f"{self.root}/explore/locations/"
f"{location['id']}/{location['slug']}/")
coauthors = post.get("coauthor_producers")
if coauthors:
@@ -434,8 +434,8 @@ class InstagramUserExtractor(Dispatch, InstagramExtractor):
example = "https://www.instagram.com/USER/"
def items(self):
base = "{}/{}/".format(self.root, self.item)
stories = "{}/stories/{}/".format(self.root, self.item)
base = f"{self.root}/{self.item}/"
stories = f"{self.root}/stories/{self.item}/"
return self._dispatch_extractors((
(InstagramInfoExtractor , base + "info/"),
(InstagramAvatarExtractor , base + "avatar/"),
@@ -623,7 +623,7 @@ class InstagramFollowersExtractor(InstagramExtractor):
uid = self.api.user_id(self.item)
for user in self.api.user_followers(uid):
user["_extractor"] = InstagramUserExtractor
url = "{}/{}".format(self.root, user["username"])
url = f"{self.root}/{user['username']}"
yield Message.Queue, url, user
@@ -637,7 +637,7 @@ class InstagramFollowingExtractor(InstagramExtractor):
uid = self.api.user_id(self.item)
for user in self.api.user_following(uid):
user["_extractor"] = InstagramUserExtractor
url = "{}/{}".format(self.root, user["username"])
url = f"{self.root}/{user['username']}"
yield Message.Queue, url, user
@@ -742,7 +742,7 @@ class InstagramRestAPI():
return self._call(endpoint, params=params)
def guide_media(self, guide_id):
endpoint = "/v1/guides/guide/{}/".format(guide_id)
endpoint = f"/v1/guides/guide/{guide_id}/"
return self._pagination_guides(endpoint)
def highlights_media(self, user_id, chunk_size=5):
@@ -764,13 +764,13 @@ class InstagramRestAPI():
reel_ids[offset : offset+chunk_size])
def highlights_tray(self, user_id):
endpoint = "/v1/highlights/{}/highlights_tray/".format(user_id)
endpoint = f"/v1/highlights/{user_id}/highlights_tray/"
return self._call(endpoint)["tray"]
def media(self, shortcode):
if len(shortcode) > 28:
shortcode = shortcode[:-28]
endpoint = "/v1/media/{}/info/".format(id_from_shortcode(shortcode))
endpoint = f"/v1/media/{id_from_shortcode(shortcode)}/info/"
return self._pagination(endpoint)
def reels_media(self, reel_ids):
@@ -787,7 +787,7 @@ class InstagramRestAPI():
yield media["media"]
def tags_sections(self, tag):
endpoint = "/v1/tags/{}/sections/".format(tag)
endpoint = f"/v1/tags/{tag}/sections/"
data = {
"include_persistent": "0",
"max_id" : None,
@@ -806,7 +806,7 @@ class InstagramRestAPI():
@memcache(keyarg=1)
def user_by_id(self, user_id):
endpoint = "/v1/users/{}/info/".format(user_id)
endpoint = f"/v1/users/{user_id}/info/"
return self._call(endpoint)["user"]
def user_id(self, screen_name, check_private=True):
@@ -838,22 +838,22 @@ class InstagramRestAPI():
return self._pagination_post(endpoint, data)
def user_collection(self, collection_id):
endpoint = "/v1/feed/collection/{}/posts/".format(collection_id)
endpoint = f"/v1/feed/collection/{collection_id}/posts/"
params = {"count": 50}
return self._pagination(endpoint, params, media=True)
def user_feed(self, user_id):
endpoint = "/v1/feed/user/{}/".format(user_id)
endpoint = f"/v1/feed/user/{user_id}/"
params = {"count": 30}
return self._pagination(endpoint, params)
def user_followers(self, user_id):
endpoint = "/v1/friendships/{}/followers/".format(user_id)
endpoint = f"/v1/friendships/{user_id}/followers/"
params = {"count": 12}
return self._pagination_following(endpoint, params)
def user_following(self, user_id):
endpoint = "/v1/friendships/{}/following/".format(user_id)
endpoint = f"/v1/friendships/{user_id}/following/"
params = {"count": 12}
return self._pagination_following(endpoint, params)
@@ -863,7 +863,7 @@ class InstagramRestAPI():
return self._pagination(endpoint, params, media=True)
def user_tagged(self, user_id):
endpoint = "/v1/usertags/{}/feed/".format(user_id)
endpoint = f"/v1/usertags/{user_id}/feed/"
params = {"count": 20}
return self._pagination(endpoint, params)

View File

@@ -164,12 +164,12 @@ class ItakuAPI():
return self._pagination(endpoint, params, self.image)
def image(self, image_id):
endpoint = "/galleries/images/{}/".format(image_id)
endpoint = f"/galleries/images/{image_id}/"
return self._call(endpoint)
@memcache(keyarg=1)
def user(self, username):
return self._call("/user_profiles/{}/".format(username))
return self._call(f"/user_profiles/{username}/")
def _call(self, endpoint, params=None):
if not endpoint.startswith("http"):

View File

@@ -28,7 +28,7 @@ class ItchioGameExtractor(Extractor):
Extractor.__init__(self, match)
def items(self):
game_url = "https://{}.itch.io/{}".format(self.user, self.slug)
game_url = f"https://{self.user}.itch.io/{self.slug}"
page = self.request(game_url).text
params = {
@@ -39,14 +39,14 @@ class ItchioGameExtractor(Extractor):
headers = {
"Referer": game_url,
"X-Requested-With": "XMLHttpRequest",
"Origin": "https://{}.itch.io".format(self.user),
"Origin": f"https://{self.user}.itch.io",
}
data = {
"csrf_token": text.unquote(self.cookies["itchio_token"]),
}
for upload_id in text.extract_iter(page, 'data-upload_id="', '"'):
file_url = "{}/file/{}".format(game_url, upload_id)
file_url = f"{game_url}/file/{upload_id}"
info = self.request(file_url, method="POST", params=params,
headers=headers, data=data).json()

View File

@@ -23,16 +23,12 @@ class KabeuchiUserExtractor(Extractor):
pattern = r"(?:https?://)?kabe-uchiroom\.com/mypage/?\?id=(\d+)"
example = "https://kabe-uchiroom.com/mypage/?id=12345"
def __init__(self, match):
Extractor.__init__(self, match)
self.user_id = match[1]
def items(self):
base = "{}/accounts/upfile/{}/{}/".format(
self.root, self.user_id[-1], self.user_id)
uid = self.groups[0]
base = f"{self.root}/accounts/upfile/{uid[-1]}/{uid}/"
keys = ("image1", "image2", "image3", "image4", "image5", "image6")
for post in self.posts():
for post in self.posts(uid):
if post.get("is_ad") or not post["image1"]:
continue
@@ -48,8 +44,8 @@ class KabeuchiUserExtractor(Extractor):
post["num"] = ord(key[-1]) - 48
yield Message.Url, url, text.nameext_from_url(name, post)
def posts(self):
url = "{}/mypage/?id={}".format(self.root, self.user_id)
def posts(self, uid):
url = f"{self.root}/mypage/?id={uid}"
response = self.request(url)
if response.history and response.url == self.root + "/":
raise exception.NotFoundError("user")
@@ -57,7 +53,7 @@ class KabeuchiUserExtractor(Extractor):
return self._pagination(target_id)
def _pagination(self, target_id):
url = "{}/get_posts.php".format(self.root)
url = f"{self.root}/get_posts.php"
data = {
"user_id" : "0",
"target_id" : target_id,

View File

@@ -32,7 +32,7 @@ class KemonoExtractor(Extractor):
tld = match[2]
self.category = domain = match[1]
self.root = text.root_from_url(match[0])
self.cookies_domain = ".{}.{}".format(domain, tld)
self.cookies_domain = f".{domain}.{tld}"
Extractor.__init__(self, match)
def _init(self):
@@ -81,8 +81,8 @@ class KemonoExtractor(Extractor):
posts = self._revisions(posts)
for post in posts:
headers["Referer"] = "{}/{}/user/{}/post/{}".format(
self.root, post["service"], post["user"], post["id"])
headers["Referer"] = (f"{self.root}/{post['service']}/user/"
f"{post['user']}/post/{post['id']}")
post["_http_headers"] = headers
post["date"] = self._parse_datetime(
post.get("published") or post.get("added") or "")
@@ -90,7 +90,7 @@ class KemonoExtractor(Extractor):
creator_id = post["user"]
if creator_info is not None:
key = "{}_{}".format(service, creator_id)
key = f"{service}_{creator_id}"
if key not in creator_info:
creator = creator_info[key] = self.api.creator_profile(
service, creator_id)
@@ -468,8 +468,8 @@ class KemonoDiscordServerExtractor(KemonoExtractor):
server_id = self.groups[2]
server, channels = discord_server_info(self, server_id)
for channel in channels.values():
url = "{}/discord/server/{}/{}#{}".format(
self.root, server_id, channel["id"], channel["name"])
url = (f"{self.root}/discord/server/{server_id}/"
f"{channel['id']}#{channel['name']}")
yield Message.Queue, url, {
"server" : server,
"channel" : channel,
@@ -513,12 +513,10 @@ class KemonoFavoriteExtractor(KemonoExtractor):
service = user["service"]
if service == "discord":
user["_extractor"] = KemonoDiscordServerExtractor
url = "{}/discord/server/{}".format(
self.root, user["id"])
url = f"{self.root}/discord/server/{user['id']}"
else:
user["_extractor"] = KemonoUserExtractor
url = "{}/{}/user/{}".format(
self.root, service, user["id"])
url = f"{self.root}/{service}/user/{user['id']}"
yield Message.Queue, url, user
elif type == "post":
@@ -531,8 +529,8 @@ class KemonoFavoriteExtractor(KemonoExtractor):
for post in posts:
post["_extractor"] = KemonoPostExtractor
url = "{}/{}/user/{}/post/{}".format(
self.root, post["service"], post["user"], post["id"])
url = (f"{self.root}/{post['service']}/user/"
f"{post['user']}/post/{post['id']}")
yield Message.Queue, url, post
@@ -565,12 +563,10 @@ class KemonoArtistsExtractor(KemonoExtractor):
service = user["service"]
if service == "discord":
user["_extractor"] = KemonoDiscordServerExtractor
url = "{}/discord/server/{}".format(
self.root, user["id"])
url = f"{self.root}/discord/server/{user['id']}"
else:
user["_extractor"] = KemonoUserExtractor
url = "{}/{}/user/{}".format(
self.root, service, user["id"])
url = f"{self.root}/{service}/user/{user['id']}"
yield Message.Queue, url, user
@@ -599,64 +595,62 @@ class KemonoAPI():
def creator_posts(self, service, creator_id,
offset=0, query=None, tags=None):
endpoint = "/{}/user/{}".format(service, creator_id)
endpoint = f"/{service}/user/{creator_id}"
params = {"q": query, "tag": tags, "o": offset}
return self._pagination(endpoint, params, 50)
def creator_posts_legacy(self, service, creator_id,
offset=0, query=None, tags=None):
endpoint = "/{}/user/{}/posts-legacy".format(service, creator_id)
endpoint = f"/{service}/user/{creator_id}/posts-legacy"
params = {"o": offset, "tag": tags, "q": query}
return self._pagination(endpoint, params, 50, "results")
def creator_announcements(self, service, creator_id):
endpoint = "/{}/user/{}/announcements".format(service, creator_id)
endpoint = f"/{service}/user/{creator_id}/announcements"
return self._call(endpoint)
def creator_dms(self, service, creator_id):
endpoint = "/{}/user/{}/dms".format(service, creator_id)
endpoint = f"/{service}/user/{creator_id}/dms"
return self._call(endpoint)
def creator_fancards(self, service, creator_id):
endpoint = "/{}/user/{}/fancards".format(service, creator_id)
endpoint = f"/{service}/user/{creator_id}/fancards"
return self._call(endpoint)
def creator_post(self, service, creator_id, post_id):
endpoint = "/{}/user/{}/post/{}".format(service, creator_id, post_id)
endpoint = f"/{service}/user/{creator_id}/post/{post_id}"
return self._call(endpoint)
def creator_post_comments(self, service, creator_id, post_id):
endpoint = "/{}/user/{}/post/{}/comments".format(
service, creator_id, post_id)
endpoint = f"/{service}/user/{creator_id}/post/{post_id}/comments"
return self._call(endpoint)
def creator_post_revisions(self, service, creator_id, post_id):
endpoint = "/{}/user/{}/post/{}/revisions".format(
service, creator_id, post_id)
endpoint = f"/{service}/user/{creator_id}/post/{post_id}/revisions"
return self._call(endpoint)
def creator_profile(self, service, creator_id):
endpoint = "/{}/user/{}/profile".format(service, creator_id)
endpoint = f"/{service}/user/{creator_id}/profile"
return self._call(endpoint)
def creator_links(self, service, creator_id):
endpoint = "/{}/user/{}/links".format(service, creator_id)
endpoint = f"/{service}/user/{creator_id}/links"
return self._call(endpoint)
def creator_tags(self, service, creator_id):
endpoint = "/{}/user/{}/tags".format(service, creator_id)
endpoint = f"/{service}/user/{creator_id}/tags"
return self._call(endpoint)
def discord_channel(self, channel_id):
endpoint = "/discord/channel/{}".format(channel_id)
endpoint = f"/discord/channel/{channel_id}"
return self._pagination(endpoint, {}, 150)
def discord_channel_lookup(self, server_id):
endpoint = "/discord/channel/lookup/{}".format(server_id)
endpoint = f"/discord/channel/lookup/{server_id}"
return self._call(endpoint)
def discord_server(self, server_id):
endpoint = "/discord/server/{}".format(server_id)
endpoint = f"/discord/server/{server_id}"
return self._call(endpoint)
def account_favorites(self, type):

View File

@@ -37,9 +37,9 @@ class LensdumpAlbumExtractor(LensdumpBase, GalleryExtractor):
def __init__(self, match):
self.gallery_id, query = match.groups()
if query:
url = "{}/a/{}/?{}".format(self.root, self.gallery_id, query)
url = f"{self.root}/a/{self.gallery_id}/?{query}"
else:
url = "{}/a/{}".format(self.root, self.gallery_id)
url = f"{self.root}/a/{self.gallery_id}"
GalleryExtractor.__init__(self, match, url)
def metadata(self, page):
@@ -81,7 +81,7 @@ class LensdumpAlbumsExtractor(LensdumpBase, Extractor):
def items(self):
user, query = self.groups
url = "{}/{}/".format(self.root, user)
url = f"{self.root}/{user}/"
if query:
params = text.parse_query(query)
else:
@@ -105,7 +105,7 @@ class LensdumpImageExtractor(LensdumpBase, Extractor):
def items(self):
key = self.groups[0]
url = "{}/i/{}".format(self.root, key)
url = f"{self.root}/i/{key}"
extr = text.extract_from(self.request(url).text)
data = {

View File

@@ -43,7 +43,7 @@ class LexicaSearchExtractor(Extractor):
url = self.root + "/api/infinite-prompts"
headers = {
"Accept" : "application/json, text/plain, */*",
"Referer": "{}/?q={}".format(self.root, self.query),
"Referer": f"{self.root}/?q={self.query}",
}
json = {
"text" : self.text,

View File

@@ -87,7 +87,7 @@ class LivedoorBlogExtractor(LivedoorExtractor):
example = "http://blog.livedoor.jp/USER/"
def posts(self):
url = "{}/{}".format(self.root, self.user)
url = f"{self.root}/{self.user}"
while url:
extr = text.extract_from(self.request(url).text)
while True:
@@ -111,8 +111,7 @@ class LivedoorPostExtractor(LivedoorExtractor):
self.post_id = match[2]
def posts(self):
url = "{}/{}/archives/{}.html".format(
self.root, self.user, self.post_id)
url = f"{self.root}/{self.user}/archives/{self.post_id}.html"
extr = text.extract_from(self.request(url).text)
data = extr('<rdf:RDF', '</rdf:RDF>')
body = extr('class="article-body-inner">', 'class="article-footer">')

View File

@@ -63,7 +63,7 @@ class LolisafeAlbumExtractor(LolisafeExtractor):
file["filename"] = file["name"] + "-" + fid
elif "id" in file:
file["name"] = file["filename"]
file["filename"] = "{}-{}".format(file["name"], file["id"])
file["filename"] = f"{file['name']}-{file['id']}"
else:
file["name"], sep, file["id"] = \
file["filename"].rpartition("-")
@@ -71,7 +71,7 @@ class LolisafeAlbumExtractor(LolisafeExtractor):
yield Message.Url, url, file
def fetch_album(self, album_id):
url = "{}/api/album/get/{}".format(self.root, album_id)
url = f"{self.root}/api/album/get/{album_id}"
data = self.request(url).json()
return data["files"], {

View File

@@ -26,7 +26,7 @@ class LusciousExtractor(Extractor):
"variables" : variables,
}
response = self.request(
"{}/graphql/nobatch/?operationName={}".format(self.root, op),
f"{self.root}/graphql/nobatch/?operationName={op}",
method="POST", json=data, fatal=False,
)

View File

@@ -44,7 +44,7 @@ class MangadexExtractor(Extractor):
def _items_manga(self):
data = {"_extractor": MangadexMangaExtractor}
for manga in self.manga():
url = "{}/title/{}".format(self.root, manga["id"])
url = f"{self.root}/title/{manga['id']}"
yield Message.Queue, url, data
def _transform(self, chapter):
@@ -121,7 +121,7 @@ class MangadexChapterExtractor(MangadexExtractor):
server = self.api.athome_server(self.uuid)
chapter = server["chapter"]
base = "{}/data/{}/".format(server["baseUrl"], chapter["hash"])
base = f"{server['baseUrl']}/data/{chapter['hash']}/"
enum = util.enumerate_reversed if self.config(
"page-reverse") else enumerate
@@ -199,7 +199,7 @@ class MangadexAuthorExtractor(MangadexExtractor):
def items(self):
for manga in self.api.manga_author(self.uuid):
manga["_extractor"] = MangadexMangaExtractor
url = "{}/title/{}".format(self.root, manga["id"])
url = f"{self.root}/title/{manga['id']}"
yield Message.Queue, url, manga

View File

@@ -54,7 +54,7 @@ class MangafoxChapterExtractor(ChapterExtractor):
yield text.ensure_http_scheme(text.unescape(url)), None
pnum += 2
page = self.request("{}/{}.html".format(self.urlbase, pnum)).text
page = self.request(f"{self.urlbase}/{pnum}.html").text
class MangafoxMangaExtractor(MangaExtractor):

View File

@@ -67,7 +67,7 @@ class MangahereChapterExtractor(MangahereBase, ChapterExtractor):
page = self.request(self.url_fmt.format(self.part, pnum)).text
def _get_title(self):
url = "{}/manga/{}/".format(self.root, self.part)
url = f"{self.root}/manga/{self.part}/"
page = self.request(url).text
try:

View File

@@ -86,7 +86,7 @@ class MangoxoAlbumExtractor(MangoxoExtractor):
def items(self):
self.login()
url = "{}/album/{}/".format(self.root, self.album_id)
url = f"{self.root}/album/{self.album_id}/"
page = self.request(url).text
data = self.metadata(page)
imgs = self.images(url, page)
@@ -152,7 +152,7 @@ class MangoxoChannelExtractor(MangoxoExtractor):
def items(self):
self.login()
num = total = 1
url = "{}/{}/album/".format(self.root, self.user)
url = f"{self.root}/{self.user}/album/"
data = {"_extractor": MangoxoAlbumExtractor}
while True:

View File

@@ -202,7 +202,7 @@ class MastodonStatusExtractor(MastodonExtractor):
def statuses(self):
if self.groups[-2] is not None:
url = "{}/objects/{}".format(self.root, self.item)
url = f"{self.root}/objects/{self.item}"
location = self.request_location(url)
self.item = location.rpartition("/")[2]
return (MastodonAPI(self).status(self.item),)
@@ -243,7 +243,7 @@ class MastodonAPI():
if "@" in username:
handle = "@" + username
else:
handle = "@{}@{}".format(username, self.extractor.instance)
handle = f"@{username}@{self.extractor.instance}"
for account in self.account_search(handle, 1):
if account["acct"] == username:
@@ -263,7 +263,7 @@ class MastodonAPI():
def account_following(self, account_id):
"""Accounts which the given account is following"""
endpoint = "/v1/accounts/{}/following".format(account_id)
endpoint = f"/v1/accounts/{account_id}/following"
return self._pagination(endpoint, None)
def account_lookup(self, username):
@@ -281,7 +281,7 @@ class MastodonAPI():
def account_statuses(self, account_id, only_media=True,
exclude_replies=False):
"""Statuses posted to the given account"""
endpoint = "/v1/accounts/{}/statuses".format(account_id)
endpoint = f"/v1/accounts/{account_id}/statuses"
params = {"only_media" : "true" if only_media else "false",
"exclude_replies": "true" if exclude_replies else "false"}
return self._pagination(endpoint, params)

View File

@@ -161,11 +161,8 @@ class MoebooruPopularExtractor(MoebooruExtractor):
self.params = params = text.parse_query(self.query)
if "year" in params:
date = "{:>04}-{:>02}-{:>02}".format(
params["year"],
params.get("month", "01"),
params.get("day", "01"),
)
date = (f"{params['year']:>04}-{params.get('month', '01'):>02}-"
f"{params.get('day', '01'):>02}")
else:
date = datetime.date.today().isoformat()

View File

@@ -113,7 +113,7 @@ class MotherlessGalleryExtractor(MotherlessExtractor):
yield Message.Queue, self.root + "/GV" + gid, data
return
url = "{}/G{}{}".format(self.root, type, gid)
url = f"{self.root}/G{type}{gid}"
page = self.request(url).text
data = self._extract_gallery_data(page)

View File

@@ -21,7 +21,7 @@ class MyhentaigalleryGalleryExtractor(GalleryExtractor):
def __init__(self, match):
self.gallery_id = match[1]
url = "{}/g/{}".format(self.root, self.gallery_id)
url = f"{self.root}/g/{self.gallery_id}"
GalleryExtractor.__init__(self, match, url)
def _init(self):

View File

@@ -41,8 +41,8 @@ class NaverPostExtractor(NaverBase, GalleryExtractor):
self.blog_id = match[3]
self.post_id = match[4]
url = "{}/PostView.nhn?blogId={}&logNo={}".format(
self.root, self.blog_id, self.post_id)
url = (f"{self.root}/PostView.nhn"
f"?blogId={self.blog_id}&logNo={self.post_id}")
GalleryExtractor.__init__(self, match, url)
def metadata(self, page):
@@ -138,13 +138,13 @@ class NaverBlogExtractor(NaverBase, Extractor):
def items(self):
# fetch first post number
url = "{}/PostList.nhn?blogId={}".format(self.root, self.blog_id)
url = f"{self.root}/PostList.nhn?blogId={self.blog_id}"
post_num = text.extr(
self.request(url).text, 'gnFirstLogNo = "', '"',
)
# setup params for API calls
url = "{}/PostViewBottomTitleListAsync.nhn".format(self.root)
url = f"{self.root}/PostViewBottomTitleListAsync.nhn"
params = {
"blogId" : self.blog_id,
"logNo" : post_num or "0",
@@ -163,8 +163,8 @@ class NaverBlogExtractor(NaverBase, Extractor):
data = self.request(url, params=params).json()
for post in data["postList"]:
post["url"] = "{}/PostView.nhn?blogId={}&logNo={}".format(
self.root, self.blog_id, post["logNo"])
post["url"] = (f"{self.root}/PostView.nhn?blogId="
f"{self.blog_id}&logNo={post['logNo']}")
post["_extractor"] = NaverPostExtractor
yield Message.Queue, post["url"], post

View File

@@ -32,7 +32,7 @@ class NaverwebtoonEpisodeExtractor(NaverwebtoonBase, GalleryExtractor):
def __init__(self, match):
path, query = match.groups()
url = "{}/{}/detail?{}".format(self.root, path, query)
url = f"{self.root}/{path}/detail?{query}"
GalleryExtractor.__init__(self, match, url)
query = text.parse_query(query)
@@ -92,8 +92,7 @@ class NaverwebtoonComicExtractor(NaverwebtoonBase, Extractor):
data = self.request(url, headers=headers, params=params).json()
path = data["webtoonLevelCode"].lower().replace("_c", "C", 1)
base = "{}/{}/detail?titleId={}&no=".format(
self.root, path, data["titleId"])
base = f"{self.root}/{path}/detail?titleId={data['titleId']}&no="
for article in data["articleList"]:
article["_extractor"] = NaverwebtoonEpisodeExtractor

View File

@@ -32,8 +32,7 @@ class NekohousePostExtractor(NekohouseExtractor):
def items(self):
service, user_id, post_id = self.groups
url = "{}/{}/user/{}/post/{}".format(
self.root, service, user_id, post_id)
url = f"{self.root}/{service}/user/{user_id}/post/{post_id}"
html = self.request(url).text
files = self._extract_files(html)
@@ -104,7 +103,7 @@ class NekohouseUserExtractor(NekohouseExtractor):
def items(self):
service, user_id, _ = self.groups
creator_url = "{}/{}/user/{}".format(self.root, service, user_id)
creator_url = f"{self.root}/{service}/user/{user_id}"
params = {"o": 0}
data = {"_extractor": NekohousePostExtractor}

View File

@@ -31,7 +31,7 @@ class NewgroundsExtractor(Extractor):
def __init__(self, match):
Extractor.__init__(self, match)
self.user = match[1]
self.user_root = "https://{}.newgrounds.com".format(self.user)
self.user_root = f"https://{self.user}.newgrounds.com"
def _init(self):
self._extract_comment_urls = util.re(
@@ -71,8 +71,7 @@ class NewgroundsExtractor(Extractor):
if "_multi" in post:
for data in post["_multi"]:
post["num"] += 1
post["_index"] = "{}_{:>02}".format(
post["index"], post["num"])
post["_index"] = f"{post['index']}_{post['num']:>02}"
post.update(data)
url = data["image"]
@@ -84,8 +83,7 @@ class NewgroundsExtractor(Extractor):
for url in self._extract_comment_urls(post["_comment"]):
post["num"] += 1
post["_index"] = "{}_{:>02}".format(
post["index"], post["num"])
post["_index"] = f"{post['index']}_{post['num']:>02}"
url = text.ensure_http_scheme(url)
text.nameext_from_url(url, post)
yield Message.Url, url, post
@@ -152,7 +150,7 @@ class NewgroundsExtractor(Extractor):
data["codehint"] = " "
elif result.get("requiresEmailMfa"):
email = result.get("obfuscatedEmail")
prompt = "Email Verification Code ({}): ".format(email)
prompt = f"Email Verification Code ({email}): "
data["code"] = self.input(prompt)
data["codehint"] = " "
@@ -346,7 +344,7 @@ class NewgroundsExtractor(Extractor):
yield fmt[1][0]["src"]
def _pagination(self, kind, pnum=1):
url = "{}/{}".format(self.user_root, kind)
url = f"{self.user_root}/{kind}"
params = {
"page": text.parse_int(pnum, 1),
"isAjaxRequest": "1",
@@ -399,8 +397,7 @@ class NewgroundsImageExtractor(NewgroundsExtractor):
NewgroundsExtractor.__init__(self, match)
if match[2]:
self.user = match[2]
self.post_url = "https://www.newgrounds.com/art/view/{}/{}".format(
self.user, match[3])
self.post_url = f"{self.root}/art/view/{self.user}/{match[3]}"
else:
self.post_url = text.ensure_http_scheme(match[0])
@@ -483,7 +480,7 @@ class NewgroundsFavoriteExtractor(NewgroundsExtractor):
)
def _pagination_favorites(self, kind, pnum=1):
url = "{}/favorites/{}".format(self.user_root, kind)
url = f"{self.user_root}/favorites/{kind}"
params = {
"page": text.parse_int(pnum, 1),
"isAjaxRequest": "1",
@@ -509,8 +506,7 @@ class NewgroundsFavoriteExtractor(NewgroundsExtractor):
def _extract_favorites(self, page):
return [
self.root + path
for path in text.extract_iter(
page, 'href="https://www.newgrounds.com', '"')
for path in text.extract_iter(page, f'href="{self.root}', '"')
]

View File

@@ -87,7 +87,7 @@ class NhentaiExtractor(Extractor):
def items(self):
data = {"_extractor": NhentaiGalleryExtractor}
for gallery_id in self._pagination():
url = "{}/g/{}/".format(self.root, gallery_id)
url = f"{self.root}/g/{gallery_id}/"
yield Message.Queue, url, data
def _pagination(self):

View File

@@ -40,7 +40,7 @@ class NijieExtractor(AsynchronousMixin, BaseExtractor):
for image_id in self.image_ids():
url = "{}/view.php?id={}".format(self.root, image_id)
url = f"{self.root}/view.php?id={image_id}"
response = self.request(url, fatal=False)
if response.status_code >= 400:
continue
@@ -109,7 +109,7 @@ class NijieExtractor(AsynchronousMixin, BaseExtractor):
def _extract_images(self, image_id, page):
if '&#diff_1" ' in page:
# multiple images
url = "{}/view_popup.php?id={}".format(self.root, image_id)
url = f"{self.root}/view_popup.php?id={image_id}"
page = self.request(url).text
return [
text.extr(media, ' src="', '"')
@@ -139,7 +139,7 @@ class NijieExtractor(AsynchronousMixin, BaseExtractor):
def _login_impl(self, username, password):
self.log.info("Logging in as %s", username)
url = "{}/login_int.php".format(self.root)
url = f"{self.root}/login_int.php"
data = {"email": username, "password": password, "save": "on"}
response = self.request(url, method="POST", data=data)
@@ -148,7 +148,7 @@ class NijieExtractor(AsynchronousMixin, BaseExtractor):
return self.cookies
def _pagination(self, path):
url = "{}/{}.php".format(self.root, path)
url = f"{self.root}/{path}.php"
params = {"id": self.user_id, "p": 1}
while True:
@@ -181,7 +181,7 @@ class NijieUserExtractor(Dispatch, NijieExtractor):
example = "https://nijie.info/members.php?id=12345"
def items(self):
fmt = "{}/{{}}.php?id={}".format(self.root, self.user_id).format
fmt = f"{self.root}/{{}}.php?id={self.user_id}".format
return self._dispatch_extractors((
(NijieIllustrationExtractor, fmt("members_illust")),
(NijieDoujinExtractor , fmt("members_dojin")),
@@ -280,7 +280,7 @@ class NijieFollowedExtractor(NijieExtractor):
for user_id in text.extract_iter(
page, '"><a href="/members.php?id=', '"'):
user_url = "{}/members.php?id={}".format(self.root, user_id)
user_url = f"{self.root}/members.php?id={user_id}"
yield Message.Queue, user_url, data
if '<a rel="next"' not in page:

View File

@@ -70,11 +70,8 @@ class NitterExtractor(BaseExtractor):
if videos and not files:
if ytdl:
append({
"url": "ytdl:{}/i/status/{}".format(
self.root, tweet["tweet_id"]),
"extension": None,
})
url = f"ytdl:{self.root}/i/status/{tweet['tweet_id']}"
append({"url": url, "extension": "mp4"})
else:
for url in text.extract_iter(
attachments, 'data-url="', '"'):
@@ -205,10 +202,10 @@ class NitterExtractor(BaseExtractor):
if self.user_id:
self.user = self.request(
"{}/i/user/{}".format(self.root, self.user_id),
f"{self.root}/i/user/{self.user_id}",
allow_redirects=False,
).headers["location"].rpartition("/")[2]
base_url = url = "{}/{}{}".format(self.root, self.user, path)
base_url = url = f"{self.root}/{self.user}{path}"
while True:
tweets_html = self.request(url).text.split(
@@ -284,7 +281,7 @@ class NitterTweetExtractor(NitterExtractor):
example = "https://nitter.net/USER/status/12345"
def tweets(self):
url = "{}/i/status/{}".format(self.root, self.user)
url = f"{self.root}/i/status/{self.user}"
html = text.extr(self.request(url).text, 'class="main-tweet', '''\
</div>
</div></div></div>''')

View File

@@ -32,8 +32,8 @@ class NozomiExtractor(Extractor):
data = self.metadata()
for post_id in map(str, self.posts()):
url = "https://j.{}/post/{}/{}/{}.json".format(
self.domain, post_id[-1], post_id[-3:-1], post_id)
url = (f"https://j.{self.domain}/post"
f"/{post_id[-1]}/{post_id[-3:-1]}/{post_id}.json")
response = self.request(url, fatal=False)
if response.status_code >= 400:
@@ -77,8 +77,8 @@ class NozomiExtractor(Extractor):
ext = "webp"
post["extension"] = ext
post["url"] = url = "https://{}.{}/{}/{}/{}.{}".format(
subdomain, self.domain, did[-1], did[-3:-1], did, ext)
post["url"] = url = (f"https://{subdomain}.{self.domain}"
f"/{did[-1]}/{did[-3:-1]}/{did}.{ext}")
yield Message.Url, url, post
def posts(self):
@@ -86,7 +86,7 @@ class NozomiExtractor(Extractor):
offset = (text.parse_int(self.pnum, 1) - 1) * 256
while True:
headers = {"Range": "bytes={}-{}".format(offset, offset+255)}
headers = {"Range": f"bytes={offset}-{offset + 255}"}
response = self.request(url, headers=headers)
yield from decode_nozomi(response.content)
@@ -126,7 +126,7 @@ class NozomiIndexExtractor(NozomiExtractor):
def __init__(self, match):
NozomiExtractor.__init__(self, match)
index, self.pnum = match.groups()
self.nozomi = "/{}.nozomi".format(index or "index")
self.nozomi = f"/{index or 'index'}.nozomi"
class NozomiTagExtractor(NozomiExtractor):
@@ -141,7 +141,7 @@ class NozomiTagExtractor(NozomiExtractor):
NozomiExtractor.__init__(self, match)
tags, self.pnum = match.groups()
self.tags = text.unquote(tags)
self.nozomi = "/nozomi/{}.nozomi".format(self.tags)
self.nozomi = f"/nozomi/{self.tags}.nozomi"
def metadata(self):
return {"search_tags": self.tags}
@@ -168,7 +168,7 @@ class NozomiSearchExtractor(NozomiExtractor):
negative = []
def nozomi(path):
url = "https://j.{}/{}.nozomi".format(self.domain, path)
url = f"https://j.{self.domain}/{path}.nozomi"
return decode_nozomi(self.request(url).content)
for tag in self.tags:

View File

@@ -66,8 +66,8 @@ class NsfwalbumAlbumExtractor(GalleryExtractor):
"width" : text.parse_int(data[1]),
"height": text.parse_int(data[2]),
"_http_validate": self._validate_response,
"_fallback": ("{}/imageProxy.php?photoId={}&spirit={}".format(
self.root, image_id, spirit),),
"_fallback": (f"{self.root}/imageProxy.php"
f"?photoId={image_id}&spirit={spirit}",),
}
def _validate_response(self, response):

View File

@@ -89,12 +89,12 @@ class OAuthBase(Extractor):
else:
self.log.info("Please open this URL in your browser:")
stdout_write("\n{}\n\n".format(url))
stdout_write(f"\n{url}\n\n")
return (recv or self.recv)()
def error(self, msg):
return self.send(
"Remote server reported an error:\n\n{}\n".format(msg))
f"Remote server reported an error:\n\n{msg}\n")
def _oauth1_authorization_flow(
self, default_key, default_secret,
@@ -151,10 +151,7 @@ class OAuthBase(Extractor):
"default" if client_id == default_id else "custom",
instance or self.subcategory, client_id)
state = "gallery-dl_{}_{}".format(
self.subcategory,
oauth.nonce(8),
)
state = f"gallery-dl_{self.subcategory}_{oauth.nonce(8)}"
auth_params = {
"client_id" : client_id,
@@ -170,8 +167,8 @@ class OAuthBase(Extractor):
# check authorization response
if state != params.get("state"):
self.send("'state' mismatch: expected {}, got {}.\n".format(
state, params.get("state")))
self.send(f"'state' mismatch: expected {state}, "
f"got {params.get('state')}.\n")
return
if "error" in params:
return self.error(params)
@@ -233,11 +230,9 @@ class OAuthBase(Extractor):
for n in names
)
if self.cache:
msg += (
"\nor set\n'extractor.{}.{}' to \"cache\""
.format(self.subcategory, names[0])
)
msg += "\nto use {}.\n".format(_it)
msg = (f"{msg}\nor set\n'extractor."
f"{self.subcategory}.{names[0]}' to \"cache\"")
msg = f"{msg}\nto use {_it}.\n"
return msg
@@ -371,8 +366,8 @@ class OAuthMastodon(OAuthBase):
application["client-secret"],
application["client-id"],
application["client-secret"],
"https://{}/oauth/authorize".format(self.instance),
"https://{}/oauth/token".format(self.instance),
f"https://{self.instance}/oauth/authorize",
f"https://{self.instance}/oauth/token",
instance=self.instance,
key="access_token",
cache=mastodon._access_token_cache,
@@ -382,7 +377,7 @@ class OAuthMastodon(OAuthBase):
def _register(self, instance):
self.log.info("Registering application for '%s'", instance)
url = "https://{}/api/v1/apps".format(instance)
url = f"https://{instance}/api/v1/apps"
data = {
"client_name": "gdl:" + oauth.nonce(8),
"redirect_uris": self.redirect_uri,
@@ -447,7 +442,7 @@ class OAuthPixiv(OAuthBase):
url, method="POST", headers=headers, data=data).json()
if "error" in data:
stdout_write("\n{}\n".format(data))
stdout_write(f"\n{data}\n")
if data["error"] in ("invalid_request", "invalid_grant"):
stdout_write("'code' expired, try again\n\n")
return

View File

@@ -42,7 +42,7 @@ class PahealExtractor(Extractor):
"""Return an iterable containing data of all relevant posts"""
def _extract_post(self, post_id):
url = "{}/post/view/{}".format(self.root, post_id)
url = f"{self.root}/post/view/{post_id}"
extr = text.extract_from(self.request(url).text)
post = {
@@ -64,7 +64,7 @@ class PahealExtractor(Extractor):
post["width"], _, height = dimensions.partition("x")
post["height"], _, duration = height.partition(", ")
post["duration"] = text.parse_float(duration[:-1])
post["filename"] = "{} - {}".format(post_id, post["tags"])
post["filename"] = f"{post_id} - {post['tags']}"
post["extension"] = ext
return post
@@ -94,7 +94,7 @@ class PahealTagExtractor(PahealExtractor):
def get_posts(self):
pnum = self.page_start
base = "{}/post/list/{}/".format(self.root, self.groups[0])
base = f"{self.root}/post/list/{self.groups[0]}/"
while True:
page = self.request(base + str(pnum)).text
@@ -129,7 +129,7 @@ class PahealTagExtractor(PahealExtractor):
"tags" : text.unescape(tags),
"size" : text.parse_bytes(size[:-1]),
"date" : text.parse_datetime(date, "%B %d, %Y; %H:%M"),
"filename" : "{} - {}".format(pid, tags),
"filename" : f"{pid} - {tags}",
"extension": ext,
}

View File

@@ -105,7 +105,7 @@ class PexelsImageExtractor(PexelsExtractor):
example = "https://www.pexels.com/photo/SLUG-12345/"
def posts(self):
url = "{}/photo/{}/".format(self.root, self.groups[0])
url = f"{self.root}/photo/{self.groups[0]}/"
page = self.request(url).text
return (self._extract_nextdata(page)["props"]["pageProps"]["medium"],)
@@ -132,7 +132,7 @@ class PexelsAPI():
}
def collections_media(self, collection_id):
endpoint = "/v3/collections/{}/media".format(collection_id)
endpoint = f"/v3/collections/{collection_id}/media"
params = {
"page" : "1",
"per_page": "24",
@@ -153,7 +153,7 @@ class PexelsAPI():
return self._pagination(endpoint, params)
def users_media_recent(self, user_id):
endpoint = "/v3/users/{}/media/recent".format(user_id)
endpoint = f"/v3/users/{user_id}/media/recent"
params = {
"page" : "1",
"per_page": "24",

View File

@@ -30,7 +30,7 @@ class PictoaImageExtractor(PictoaExtractor):
def items(self):
album_id, image_id = self.groups
url = "{}/albums/{}/{}.html".format(self.root, album_id, image_id)
url = f"{self.root}/albums/{album_id}/{image_id}.html"
page = self.request(url).text
album_title = text.extr(page, 'property="og:title" content="', '"')
image_url = text.extr(page, 'property="og:image" content="', '"')
@@ -55,7 +55,7 @@ class PictoaAlbumExtractor(PictoaExtractor):
def items(self):
album_id = self.groups[0]
url = "{}/albums/{}.html".format(self.root, album_id)
url = f"{self.root}/albums/{album_id}.html"
page = self.request(url).text
album_data = {

View File

@@ -71,7 +71,7 @@ class PiczelUserExtractor(PiczelExtractor):
example = "https://piczel.tv/gallery/USER"
def posts(self):
url = "{}/api/users/{}/gallery".format(self.root_api, self.groups[0])
url = f"{self.root_api}/api/users/{self.groups[0]}/gallery"
return self._pagination(url)
@@ -84,7 +84,7 @@ class PiczelFolderExtractor(PiczelExtractor):
example = "https://piczel.tv/gallery/USER/12345"
def posts(self):
url = "{}/api/gallery/folder/{}".format(self.root_api, self.groups[0])
url = f"{self.root_api}/api/gallery/folder/{self.groups[0]}"
return self._pagination(url)
@@ -95,5 +95,5 @@ class PiczelImageExtractor(PiczelExtractor):
example = "https://piczel.tv/gallery/image/12345"
def posts(self):
url = "{}/api/gallery/{}".format(self.root_api, self.groups[0])
url = f"{self.root_api}/api/gallery/{self.groups[0]}"
return (self.request(url).json(),)

Some files were not shown because too many files have changed in this diff Show More