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", "#": "gallery-dl default configuration file",
"#": "full documentation at", "#": "full documentation at",

View File

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

View File

@@ -58,7 +58,7 @@ bytes_to_intlist = list
def intlist_to_bytes(xs): def intlist_to_bytes(xs):
if not xs: if not xs:
return b"" return b""
return struct.pack("%dB" % len(xs), *xs) return struct.pack(f"{len(xs)}B", *xs)
def unpad_pkcs7(data): 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: if len(block_x) != BLOCK_SIZE_BYTES or len(block_y) != BLOCK_SIZE_BYTES:
raise ValueError( 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_r = [0xE1] + [0] * (BLOCK_SIZE_BYTES - 1)
block_v = block_y[:] block_v = block_y[:]
@@ -639,7 +639,7 @@ def ghash(subkey, data):
if len(data) % BLOCK_SIZE_BYTES: if len(data) % BLOCK_SIZE_BYTES:
raise ValueError( 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 last_y = [0] * BLOCK_SIZE_BYTES
for i in range(0, len(data), BLOCK_SIZE_BYTES): for i in range(0, len(data), BLOCK_SIZE_BYTES):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -86,7 +86,7 @@ class BatotoChapterExtractor(BatotoBase, ChapterExtractor):
ChapterExtractor.__init__(self, match, False) ChapterExtractor.__init__(self, match, False)
self._init_root() self._init_root()
self.chapter_id = self.groups[1] 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): def metadata(self, page):
extr = text.extract_from(page) extr = text.extract_from(page)
@@ -147,7 +147,7 @@ class BatotoMangaExtractor(BatotoBase, MangaExtractor):
MangaExtractor.__init__(self, match, False) MangaExtractor.__init__(self, match, False)
self._init_root() self._init_root()
self.manga_id = self.groups[1] or self.groups[2] 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): def chapters(self, page):
extr = text.extract_from(page) extr = text.extract_from(page)
@@ -177,6 +177,6 @@ class BatotoMangaExtractor(BatotoBase, MangaExtractor):
data["date"] = text.parse_datetime( data["date"] = text.parse_datetime(
extr('time="', '"'), "%Y-%m-%dT%H:%M:%S.%fZ") 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())) results.append((url, data.copy()))
return results return results

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -184,7 +184,7 @@ class BunkrAlbumExtractor(LolisafeAlbumExtractor):
json={"id": data_id}).json() json={"id": data_id}).json()
if data.get("encrypted"): 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()) file_url = util.decrypt_xor(data["url"], key.encode())
else: else:
file_url = data["url"] file_url = data["url"]

View File

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

View File

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

View File

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

View File

@@ -32,7 +32,7 @@ class CyberdropAlbumExtractor(lolisafe.LolisafeAlbumExtractor):
yield Message.Url, file["url"], file yield Message.Url, file["url"], file
def fetch_album(self, album_id): 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 page = self.request(url).text
extr = text.extract_from(page) extr = text.extract_from(page)
@@ -60,7 +60,7 @@ class CyberdropAlbumExtractor(lolisafe.LolisafeAlbumExtractor):
def _extract_files(self, file_ids): def _extract_files(self, file_ids):
for file_id in file_ids: for file_id in file_ids:
try: 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() file = self.request(url).json()
auth = self.request(file["auth_url"]).json() auth = self.request(file["auth_url"]).json()
file["url"] = auth["url"] file["url"] = auth["url"]

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ class DynastyscansBase():
root = "https://dynasty-scans.com" root = "https://dynasty-scans.com"
def _parse_image_page(self, image_id): 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) extr = text.extract_from(self.request(url).text)
date = extr("class='create_at'>", "</span>") date = extr("class='create_at'>", "</span>")
@@ -140,7 +140,7 @@ class DynastyscansAnthologyExtractor(DynastyscansSearchExtractor):
example = "https://dynasty-scans.com/anthologies/TITLE" example = "https://dynasty-scans.com/anthologies/TITLE"
def items(self): 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) root = self.request_xml(url, xmlns=False)
data = { data = {

View File

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

View File

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

View File

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

View File

@@ -221,7 +221,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
data = {} data = {}
from .hitomi import HitomiGalleryExtractor 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 data["_extractor"] = HitomiGalleryExtractor
yield Message.Queue, url, data yield Message.Queue, url, data
@@ -491,8 +491,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
_limits_update = _request_home _limits_update = _request_home
def _gallery_page(self): def _gallery_page(self):
url = "{}/g/{}/{}/".format( url = f"{self.root}/g/{self.gallery_id}/{self.gallery_token}/"
self.root, self.gallery_id, self.gallery_token)
response = self.request(url, fatal=False) response = self.request(url, fatal=False)
page = response.text page = response.text
@@ -505,8 +504,8 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
return page return page
def _image_page(self): def _image_page(self):
url = "{}/s/{}/{}-{}".format( url = (f"{self.root}/s/{self.image_token}"
self.root, self.image_token, self.gallery_id, self.image_num) f"/{self.gallery_id}-{self.image_num}")
page = self.request(url, fatal=False).text page = self.request(url, fatal=False).text
if page.startswith(("Invalid page", "Keep trying")): if page.startswith(("Invalid page", "Keep trying")):
@@ -514,7 +513,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
return page return page
def _fallback_original(self, nl, fullimg): def _fallback_original(self, nl, fullimg):
url = "{}?nl={}".format(fullimg, nl) url = f"{fullimg}?nl={nl}"
for _ in util.repeat(self.fallback_retries): for _ in util.repeat(self.fallback_retries):
yield url yield url
@@ -523,8 +522,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
token = self.key_start token = self.key_start
for _ in util.repeat(self.fallback_retries): for _ in util.repeat(self.fallback_retries):
url = "{}/s/{}/{}-{}?nl={}".format( url = f"{self.root}/s/{token}/{self.gallery_id}-{num}?nl={nl}"
self.root, token, self.gallery_id, num, nl)
page = self.request(url, fatal=False).text page = self.request(url, fatal=False).text
if page.startswith(("Invalid page", "Keep trying")): if page.startswith(("Invalid page", "Keep trying")):
@@ -577,7 +575,7 @@ class ExhentaiSearchExtractor(ExhentaiExtractor):
if tag: if tag:
if "+" in tag: if "+" in tag:
ns, _, tag = tag.rpartition(":") ns, _, tag = tag.rpartition(":")
tag = '{}:"{}$"'.format(ns, tag.replace("+", " ")) tag = f"{ns}:\"{tag.replace('+', ' ')}$\""
else: else:
tag += "$" tag += "$"
self.params = {"f_search": tag, "page": 0} 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" templ = "https://docs.google.com/forms/d/e/{}/viewform?usp=sf_link"
url = templ.format(content_id) url = templ.format(content_id)
else: else:
self.log.warning("service not recognized: {}".format(provider)) self.log.warning(f"service not recognized: {provider}")
if url: if url:
final_post["embed"] = embed final_post["embed"] = embed

View File

@@ -184,7 +184,7 @@ class FantiaCreatorExtractor(FantiaExtractor):
self.creator_id = match[1] self.creator_id = match[1]
def posts(self): 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) return self._pagination(url)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,7 +22,7 @@ class FuskatorGalleryExtractor(GalleryExtractor):
def __init__(self, match): def __init__(self, match):
self.gallery_hash = match[1] 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) GalleryExtractor.__init__(self, match, url)
def metadata(self, page): def metadata(self, page):

View File

@@ -73,7 +73,7 @@ class GelbooruBase():
if id: if id:
tag = "id:" + op tag = "id:" + op
tags = [t for t in tags if not t.startswith(tag)] 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: while True:
posts = self._api_request(params) posts = self._api_request(params)
@@ -113,7 +113,7 @@ class GelbooruBase():
post["_fallback"] = (url,) post["_fallback"] = (url,)
md5 = post["md5"] md5 = post["md5"]
root = text.root_from_url(post["preview_url"]) 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 url = root + path
return url return url

View File

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

View File

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

View File

@@ -44,7 +44,7 @@ class HentaicosplaysGalleryExtractor(
def __init__(self, match): def __init__(self, match):
BaseExtractor.__init__(self, match) BaseExtractor.__init__(self, match)
self.slug = self.groups[-1] 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): def _init(self):
self.session.headers["Referer"] = self.page_url self.session.headers["Referer"] = self.page_url

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ class HentaihereChapterExtractor(HentaihereBase, ChapterExtractor):
def __init__(self, match): def __init__(self, match):
self.manga_id, self.chapter = match.groups() 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) ChapterExtractor.__init__(self, match, url)
def metadata(self, page): def metadata(self, page):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -164,7 +164,7 @@ class ImgbbUserExtractor(ImgbbExtractor):
ImgbbExtractor.__init__(self, match) ImgbbExtractor.__init__(self, match)
self.user = match[1] self.user = match[1]
self.sort = text.parse_query(match[2]).get("sort", "date_desc") 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): def metadata(self, page):
user = self._extract_user(page) user = self._extract_user(page)

View File

@@ -21,7 +21,7 @@ class ImgthGalleryExtractor(GalleryExtractor):
def __init__(self, match): def __init__(self, match):
self.gallery_id = gid = match[1] 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) GalleryExtractor.__init__(self, match, url)
def metadata(self, page): def metadata(self, page):
@@ -45,12 +45,11 @@ class ImgthGalleryExtractor(GalleryExtractor):
thumbs = text.extr(page, '<ul class="thumbnails">', '</ul>') thumbs = text.extr(page, '<ul class="thumbnails">', '</ul>')
for url in text.extract_iter(thumbs, '<img src="', '"'): for url in text.extract_iter(thumbs, '<img src="', '"'):
path = url.partition("/thumbs/")[2] 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: if '<li class="next">' not in page:
return return
pnum += 1 pnum += 1
url = "{}/gallery/{}/g/page/{}".format( url = f"{self.root}/gallery/{self.gallery_id}/g/page/{pnum}"
self.root, self.gallery_id, pnum)
page = self.request(url).text 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": elif image["is_animated"] and self.mp4 and image["ext"] == "gif":
image["ext"] = "mp4" image["ext"] = "mp4"
image["url"] = url = "https://i.imgur.com/{}.{}".format( image["url"] = url = \
image["id"], image["ext"]) f"https://i.imgur.com/{image['id']}.{image['ext']}"
image["date"] = text.parse_datetime(image["created_at"]) image["date"] = text.parse_datetime(image["created_at"])
image["_http_validate"] = self._validate image["_http_validate"] = self._validate
text.nameext_from_url(url, image) text.nameext_from_url(url, image)
@@ -131,10 +131,10 @@ class ImgurGalleryExtractor(ImgurExtractor):
def items(self): def items(self):
if self.api.gallery(self.key)["is_album"]: if self.api.gallery(self.key)["is_album"]:
url = "{}/a/{}".format(self.root, self.key) url = f"{self.root}/a/{self.key}"
extr = ImgurAlbumExtractor extr = ImgurAlbumExtractor
else: else:
url = "{}/{}".format(self.root, self.key) url = f"{self.root}/{self.key}"
extr = ImgurImageExtractor extr = ImgurImageExtractor
yield Message.Queue, url, {"_extractor": extr} yield Message.Queue, url, {"_extractor": extr}
@@ -234,16 +234,15 @@ class ImgurAPI():
self.headers = {"Authorization": "Client-ID " + self.client_id} self.headers = {"Authorization": "Client-ID " + self.client_id}
def account_submissions(self, account): def account_submissions(self, account):
endpoint = "/3/account/{}/submissions".format(account) endpoint = f"/3/account/{account}/submissions"
return self._pagination(endpoint) return self._pagination(endpoint)
def account_favorites(self, account): def account_favorites(self, account):
endpoint = "/3/account/{}/gallery_favorites".format(account) endpoint = f"/3/account/{account}/gallery_favorites"
return self._pagination(endpoint) return self._pagination(endpoint)
def account_favorites_folder(self, account, folder_id): def account_favorites_folder(self, account, folder_id):
endpoint = "/3/account/{}/folders/{}/favorites".format( endpoint = f"/3/account/{account}/folders/{folder_id}/favorites"
account, folder_id)
return self._pagination_v2(endpoint) return self._pagination_v2(endpoint)
def accounts_me_allposts(self): def accounts_me_allposts(self):
@@ -270,11 +269,11 @@ class ImgurAPI():
return self._pagination(endpoint, params) return self._pagination(endpoint, params)
def gallery_subreddit(self, subreddit): def gallery_subreddit(self, subreddit):
endpoint = "/3/gallery/r/{}".format(subreddit) endpoint = f"/3/gallery/r/{subreddit}"
return self._pagination(endpoint) return self._pagination(endpoint)
def gallery_tag(self, tag): def gallery_tag(self, tag):
endpoint = "/3/gallery/t/{}".format(tag) endpoint = f"/3/gallery/t/{tag}"
return self._pagination(endpoint, key="items") return self._pagination(endpoint, key="items")
def image(self, image_hash): def image(self, image_hash):
@@ -308,7 +307,7 @@ class ImgurAPI():
num = 0 num = 0
while True: while True:
data = self._call("{}/{}".format(endpoint, num), params)["data"] data = self._call(f"{endpoint}/{num}", params)["data"]
if key: if key:
data = data[key] data = data[key]
if not data: if not data:

View File

@@ -85,7 +85,7 @@ class ImhentaiGalleryExtractor(ImhentaiExtractor, GalleryExtractor):
def __init__(self, match): def __init__(self, match):
ImhentaiExtractor.__init__(self, match) ImhentaiExtractor.__init__(self, match)
self.gallery_id = self.groups[-1] 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): def metadata(self, page):
extr = text.extract_from(page) extr = text.extract_from(page)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ class MyhentaigalleryGalleryExtractor(GalleryExtractor):
def __init__(self, match): def __init__(self, match):
self.gallery_id = match[1] 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) GalleryExtractor.__init__(self, match, url)
def _init(self): def _init(self):

View File

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

View File

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

View File

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

View File

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

View File

@@ -87,7 +87,7 @@ class NhentaiExtractor(Extractor):
def items(self): def items(self):
data = {"_extractor": NhentaiGalleryExtractor} data = {"_extractor": NhentaiGalleryExtractor}
for gallery_id in self._pagination(): 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 yield Message.Queue, url, data
def _pagination(self): def _pagination(self):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,7 +30,7 @@ class PictoaImageExtractor(PictoaExtractor):
def items(self): def items(self):
album_id, image_id = self.groups 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 page = self.request(url).text
album_title = text.extr(page, 'property="og:title" content="', '"') album_title = text.extr(page, 'property="og:title" content="', '"')
image_url = text.extr(page, 'property="og:image" content="', '"') image_url = text.extr(page, 'property="og:image" content="', '"')
@@ -55,7 +55,7 @@ class PictoaAlbumExtractor(PictoaExtractor):
def items(self): def items(self):
album_id = self.groups[0] 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 page = self.request(url).text
album_data = { album_data = {

View File

@@ -71,7 +71,7 @@ class PiczelUserExtractor(PiczelExtractor):
example = "https://piczel.tv/gallery/USER" example = "https://piczel.tv/gallery/USER"
def posts(self): 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) return self._pagination(url)
@@ -84,7 +84,7 @@ class PiczelFolderExtractor(PiczelExtractor):
example = "https://piczel.tv/gallery/USER/12345" example = "https://piczel.tv/gallery/USER/12345"
def posts(self): 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) return self._pagination(url)
@@ -95,5 +95,5 @@ class PiczelImageExtractor(PiczelExtractor):
example = "https://piczel.tv/gallery/image/12345" example = "https://piczel.tv/gallery/image/12345"
def posts(self): 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(),) return (self.request(url).json(),)

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