replace 2-element f-strings with simple '+' concatenations
Python's 'ast' module and its 'NodeVisitor' class were incredibly helpful in identifying these
This commit is contained in:
@@ -25,7 +25,7 @@ class _2chThreadExtractor(Extractor):
|
||||
|
||||
def __init__(self, match):
|
||||
tld = match[1]
|
||||
self.root = f"https://2ch.{'org' if tld == 'hk' else tld}"
|
||||
self.root = "https://2ch." + ("org" if tld == "hk" else tld)
|
||||
Extractor.__init__(self, match)
|
||||
|
||||
def items(self):
|
||||
@@ -71,14 +71,14 @@ class _2chBoardExtractor(Extractor):
|
||||
|
||||
def __init__(self, match):
|
||||
tld = match[1]
|
||||
self.root = f"https://2ch.{'su' if tld == 'hk' else tld}"
|
||||
self.root = "https://2ch." + ("org" if tld == "hk" else tld)
|
||||
Extractor.__init__(self, match)
|
||||
|
||||
def items(self):
|
||||
base = f"{self.root}/{self.groups[1]}"
|
||||
|
||||
# index page
|
||||
url = f"{base}/index.json"
|
||||
url = base + "/index.json"
|
||||
index = self.request_json(url)
|
||||
index["_extractor"] = _2chThreadExtractor
|
||||
for thread in index["threads"]:
|
||||
|
||||
@@ -84,7 +84,7 @@ class ArcalivePostExtractor(ArcaliveExtractor):
|
||||
url = src
|
||||
|
||||
fallback = ()
|
||||
query = f"?type=orig&{query}"
|
||||
query = "?type=orig&" + query
|
||||
if orig := text.extr(media, 'data-orig="', '"'):
|
||||
path, _, ext = url.rpartition(".")
|
||||
if ext != orig:
|
||||
@@ -169,8 +169,11 @@ class ArcaliveAPI():
|
||||
return data
|
||||
|
||||
self.log.debug("Server response: %s", data)
|
||||
msg = f": {msg}" if (msg := data.get("message")) else ""
|
||||
raise exception.AbortExtraction(f"API request failed{msg}")
|
||||
if msg := data.get("message"):
|
||||
msg = "API request failed: " + msg
|
||||
else:
|
||||
msg = "API request failed"
|
||||
raise exception.AbortExtraction(msg)
|
||||
|
||||
def _pagination(self, endpoint, params, key):
|
||||
while True:
|
||||
|
||||
@@ -24,8 +24,8 @@ class ArenaChannelExtractor(GalleryExtractor):
|
||||
example = "https://are.na/evan-collins-1522646491/cassette-futurism"
|
||||
|
||||
def metadata(self, page):
|
||||
channel = self.request_json(
|
||||
f"https://api.are.na/v2/channels/{self.groups[0]}")
|
||||
url = "https://api.are.na/v2/channels/" + self.groups[0]
|
||||
channel = self.request_json(url)
|
||||
|
||||
channel["date"] = self.parse_datetime_iso(
|
||||
channel["created_at"])
|
||||
|
||||
@@ -95,7 +95,7 @@ class ArtstationExtractor(Extractor):
|
||||
if not self.external:
|
||||
return
|
||||
asset["extension"] = "mp4"
|
||||
return f"ytdl:{url}"
|
||||
return "ytdl:" + url
|
||||
|
||||
self.log.debug(player)
|
||||
self.log.warning("Failed to extract embedded player URL (%s)",
|
||||
@@ -328,9 +328,9 @@ class ArtstationChallengeExtractor(ArtstationExtractor):
|
||||
|
||||
def items(self):
|
||||
base = f"{self.root}/contests/_/challenges/{self.challenge_id}"
|
||||
challenge_url = f"{base}.json"
|
||||
submission_url = f"{base}/submissions.json"
|
||||
update_url = f"{self.root}/contests/submission_updates.json"
|
||||
challenge_url = base + ".json"
|
||||
submission_url = base + "/submissions.json"
|
||||
update_url = self.root + "/contests/submission_updates.json"
|
||||
|
||||
challenge = self.request_json(challenge_url)
|
||||
yield Message.Directory, "", {"challenge": challenge}
|
||||
@@ -388,7 +388,7 @@ class ArtstationSearchExtractor(ArtstationExtractor):
|
||||
"value" : value.split(","),
|
||||
})
|
||||
|
||||
url = f"{self.root}/api/v2/search/projects.json"
|
||||
url = self.root + "/api/v2/search/projects.json"
|
||||
data = {
|
||||
"query" : self.query,
|
||||
"page" : None,
|
||||
@@ -419,7 +419,7 @@ class ArtstationArtworkExtractor(ArtstationExtractor):
|
||||
return {"artwork": self.query}
|
||||
|
||||
def projects(self):
|
||||
url = f"{self.root}/projects.json"
|
||||
url = self.root + "/projects.json"
|
||||
return self._pagination(url, self.query.copy())
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class BellazonExtractor(Extractor):
|
||||
archive_fmt = "{post[id]}/{id}_{filename}"
|
||||
|
||||
def items(self):
|
||||
native = (f"{self.root}/", f"{self.root[6:]}/")
|
||||
native = (self.root + "/", self.root[6:] + "/")
|
||||
extract_urls = text.re(
|
||||
r'(?s)<('
|
||||
r'(?:video .*?<source [^>]*?src|a [^>]*?href)="([^"]+).*?</a>'
|
||||
@@ -82,7 +82,7 @@ class BellazonExtractor(Extractor):
|
||||
dc["extension"] = text.ext_from_url(url)
|
||||
|
||||
if url[0] == "/":
|
||||
url = f"https:{url}"
|
||||
url = "https:" + url
|
||||
yield Message.Url, url, dc
|
||||
|
||||
else:
|
||||
@@ -91,10 +91,10 @@ class BellazonExtractor(Extractor):
|
||||
yield Message.Queue, url, data
|
||||
|
||||
def _pagination(self, base, pnum=None):
|
||||
base = f"{self.root}{base}"
|
||||
base = self.root + base
|
||||
|
||||
if pnum is None:
|
||||
url = f"{base}/"
|
||||
url = base + "/"
|
||||
pnum = 1
|
||||
else:
|
||||
url = f"{base}/page/{pnum}/"
|
||||
@@ -112,7 +112,7 @@ class BellazonExtractor(Extractor):
|
||||
url = f"{base}/page/{pnum}/"
|
||||
|
||||
def _pagination_reverse(self, base, pnum=None):
|
||||
base = f"{self.root}{base}"
|
||||
base = self.root + base
|
||||
|
||||
url = f"{base}/page/{'9999' if pnum is None else pnum}/"
|
||||
with self.request(url) as response:
|
||||
@@ -127,7 +127,7 @@ class BellazonExtractor(Extractor):
|
||||
if pnum > 1:
|
||||
url = f"{base}/page/{pnum}/"
|
||||
elif pnum == 1:
|
||||
url = f"{base}/"
|
||||
url = base + "/"
|
||||
else:
|
||||
return
|
||||
|
||||
@@ -198,9 +198,9 @@ class BellazonPostExtractor(BellazonExtractor):
|
||||
|
||||
def posts(self):
|
||||
path, post_id = self.groups
|
||||
page = self.request(f"{self.root}{path}").text
|
||||
page = self.request(self.root + path).text
|
||||
|
||||
pos = page.find(f'id="elComment_{post_id}')
|
||||
pos = page.find('id="elComment_' + post_id)
|
||||
if pos < 0:
|
||||
raise exception.NotFoundError("post")
|
||||
html = text.extract(page, "<article ", "</article>", pos-100)[0]
|
||||
|
||||
@@ -146,7 +146,7 @@ class BilibiliAPI():
|
||||
except Exception:
|
||||
if "window._riskdata_" not in page:
|
||||
raise exception.AbortExtraction(
|
||||
f"{article_id}: Unable to extract INITIAL_STATE data")
|
||||
article_id + ": Unable to extract INITIAL_STATE data")
|
||||
self.extractor.wait(seconds=300)
|
||||
|
||||
def user_favlist(self):
|
||||
|
||||
@@ -424,7 +424,7 @@ class BoostyAPI():
|
||||
params["offset"] = offset
|
||||
|
||||
def dialog(self, dialog_id):
|
||||
endpoint = f"/v1/dialog/{dialog_id}"
|
||||
endpoint = "/v1/dialog/" + dialog_id
|
||||
return self._call(endpoint)
|
||||
|
||||
def dialog_messages(self, dialog_id, limit=300, offset=None):
|
||||
|
||||
@@ -116,7 +116,7 @@ class BoothShopExtractor(BoothExtractor):
|
||||
BoothExtractor.__init__(self, match)
|
||||
|
||||
def shop_items(self):
|
||||
return self._pagination(f"{self.root}/items")
|
||||
return self._pagination(self.root + "/items")
|
||||
|
||||
|
||||
def _fallback(url):
|
||||
|
||||
@@ -189,7 +189,7 @@ class BunkrAlbumExtractor(LolisafeAlbumExtractor):
|
||||
json={"id": data_id})
|
||||
|
||||
if data.get("encrypted"):
|
||||
key = f"SECRET_KEY_{data['timestamp'] // 3600}"
|
||||
key = "SECRET_KEY_" + str(data["timestamp"] // 3600)
|
||||
file_url = util.decrypt_xor(data["url"], key.encode())
|
||||
else:
|
||||
file_url = data["url"]
|
||||
@@ -221,7 +221,7 @@ class BunkrMediaExtractor(BunkrAlbumExtractor):
|
||||
|
||||
def fetch_album(self, album_id):
|
||||
try:
|
||||
page = self.request(f"{self.root}{album_id}").text
|
||||
page = self.request(self.root + album_id).text
|
||||
data_id = text.extr(page, 'data-file-id="', '"')
|
||||
file = self._extract_file(data_id)
|
||||
file["name"] = text.unquote(text.unescape(text.extr(
|
||||
|
||||
@@ -580,10 +580,10 @@ class CivitaiUserCollectionsExtractor(CivitaiExtractor):
|
||||
params = self._parse_query(query)
|
||||
params["userId"] = self.api.user(text.unquote(user))[0]["id"]
|
||||
|
||||
base = f"{self.root}/collections/"
|
||||
base = self.root + "/collections/"
|
||||
for collection in self.api.collections(params):
|
||||
collection["_extractor"] = CivitaiCollectionExtractor
|
||||
yield Message.Queue, f"{base}{collection['id']}", collection
|
||||
yield Message.Queue, base + str(collection["id"]), collection
|
||||
|
||||
|
||||
class CivitaiGeneratedExtractor(CivitaiExtractor):
|
||||
@@ -591,7 +591,7 @@ class CivitaiGeneratedExtractor(CivitaiExtractor):
|
||||
subcategory = "generated"
|
||||
filename_fmt = "{filename}.{extension}"
|
||||
directory_fmt = ("{category}", "generated")
|
||||
pattern = f"{BASE_PATTERN}/generate"
|
||||
pattern = BASE_PATTERN + "/generate"
|
||||
example = "https://civitai.com/generate"
|
||||
|
||||
def items(self):
|
||||
@@ -647,12 +647,12 @@ class CivitaiRestAPI():
|
||||
})
|
||||
|
||||
def model(self, model_id):
|
||||
endpoint = f"/v1/models/{model_id}"
|
||||
endpoint = "/v1/models/" + str(model_id)
|
||||
return self._call(endpoint)
|
||||
|
||||
@memcache(keyarg=1)
|
||||
def model_version(self, model_version_id):
|
||||
endpoint = f"/v1/model-versions/{model_version_id}"
|
||||
endpoint = "/v1/model-versions/" + str(model_version_id)
|
||||
return self._call(endpoint)
|
||||
|
||||
def models(self, params):
|
||||
@@ -945,7 +945,7 @@ class CivitaiSearchAPI():
|
||||
|
||||
if auth := extractor.config("token"):
|
||||
if " " not in auth:
|
||||
auth = f"Bearer {auth}"
|
||||
auth = "Bearer " + auth
|
||||
else:
|
||||
auth = ("Bearer 8c46eb2508e21db1e9828a97968d"
|
||||
"91ab1ca1caa5f70a00e88a2ba1e286603b61")
|
||||
|
||||
@@ -44,7 +44,7 @@ class ComickCoversExtractor(ComickBase, GalleryExtractor):
|
||||
covers.reverse()
|
||||
|
||||
return [
|
||||
(f"https://meo.comick.pictures/{cover['b2key']}", {
|
||||
("https://meo.comick.pictures/" + cover["b2key"], {
|
||||
"id" : cover["id"],
|
||||
"width" : cover["w"],
|
||||
"height": cover["h"],
|
||||
@@ -128,7 +128,7 @@ class ComickChapterExtractor(ComickBase, ChapterExtractor):
|
||||
return ()
|
||||
|
||||
return [
|
||||
(f"https://meo.comick.pictures/{img['b2key']}", {
|
||||
("https://meo.comick.pictures/" + img["b2key"], {
|
||||
"width" : img["w"],
|
||||
"height" : img["h"],
|
||||
"size" : img["s"],
|
||||
|
||||
@@ -766,7 +766,7 @@ class GalleryExtractor(Extractor):
|
||||
Extractor.__init__(self, match)
|
||||
|
||||
if url is None and (path := self.groups[0]) and path[0] == "/":
|
||||
self.page_url = f"{self.root}{path}"
|
||||
self.page_url = self.root + path
|
||||
else:
|
||||
self.page_url = url
|
||||
|
||||
@@ -863,7 +863,7 @@ class MangaExtractor(Extractor):
|
||||
Extractor.__init__(self, match)
|
||||
|
||||
if url is None and (path := self.groups[0]) and path[0] == "/":
|
||||
self.page_url = f"{self.root}{path}"
|
||||
self.page_url = self.root + path
|
||||
else:
|
||||
self.page_url = url
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class CyberfileExtractor(Extractor):
|
||||
root = "https://cyberfile.me"
|
||||
|
||||
def request_api(self, endpoint, data):
|
||||
url = f"{self.root}{endpoint}"
|
||||
url = self.root + endpoint
|
||||
headers = {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"Origin": self.root,
|
||||
@@ -29,7 +29,7 @@ class CyberfileExtractor(Extractor):
|
||||
url, method="POST", headers=headers, data=data)
|
||||
|
||||
if "albumPasswordModel" in resp.get("javascript", ""):
|
||||
url_pw = f"{self.root}/ajax/folder_password_process"
|
||||
url_pw = self.root + "/ajax/folder_password_process"
|
||||
data_pw = {
|
||||
"folderPassword": self._get_auth_info(password=True)[1],
|
||||
"folderId": text.extr(
|
||||
|
||||
@@ -155,7 +155,7 @@ class DanbooruExtractor(BaseExtractor):
|
||||
return
|
||||
|
||||
if prefix:
|
||||
params["page"] = f"{prefix}{posts[-1]['id']}"
|
||||
params["page"] = prefix + str(posts[-1]["id"])
|
||||
elif params["page"]:
|
||||
params["page"] += 1
|
||||
else:
|
||||
@@ -174,9 +174,8 @@ class DanbooruExtractor(BaseExtractor):
|
||||
else:
|
||||
ext = data["ZIP:ZipFileName"].rpartition(".")[2]
|
||||
|
||||
fmt = ("{:>06}." + ext).format
|
||||
delays = data["Ugoira:FrameDelays"]
|
||||
return [{"file": fmt(index), "delay": delay}
|
||||
return [{"file": f"{index:>06}.{ext}", "delay": delay}
|
||||
for index, delay in enumerate(delays)]
|
||||
|
||||
def _collection_posts(self, cid, ctype):
|
||||
|
||||
@@ -31,7 +31,7 @@ class DandadanChapterExtractor(DandadanBase, ChapterExtractor):
|
||||
return {
|
||||
"manga" : "Dandadan",
|
||||
"chapter" : text.parse_int(chapter),
|
||||
"chapter_minor": f"{sep}{minor}",
|
||||
"chapter_minor": sep + minor,
|
||||
"lang" : "en",
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class DankefuerslesenChapterExtractor(DankefuerslesenBase, ChapterExtractor):
|
||||
def _init(self):
|
||||
self.zip = self.config("zip", False)
|
||||
if self.zip:
|
||||
self.filename_fmt = f"{self.directory_fmt[-1]}.{{extension}}"
|
||||
self.filename_fmt = self.directory_fmt[-1] + ".{extension}"
|
||||
self.directory_fmt = self.directory_fmt[:-1]
|
||||
|
||||
def metadata(self, page):
|
||||
|
||||
@@ -66,7 +66,7 @@ class DeviantartExtractor(Extractor):
|
||||
self.quality = "-fullview.png?"
|
||||
self.quality_sub = text.re(r"-fullview\.[a-z0-9]+\?").sub
|
||||
else:
|
||||
self.quality = f",q_{self.quality}"
|
||||
self.quality = ",q_" + str(self.quality)
|
||||
self.quality_sub = text.re(r",q_\d+").sub
|
||||
|
||||
if self.intermediary:
|
||||
|
||||
@@ -25,10 +25,10 @@ class EromeExtractor(Extractor):
|
||||
_cookies = True
|
||||
|
||||
def items(self):
|
||||
base = f"{self.root}/a/"
|
||||
base = self.root + "/a/"
|
||||
data = {"_extractor": EromeAlbumExtractor}
|
||||
for album_id in self.albums():
|
||||
yield Message.Queue, f"{base}{album_id}", data
|
||||
yield Message.Queue, base + album_id, data
|
||||
|
||||
def albums(self):
|
||||
return ()
|
||||
@@ -141,7 +141,7 @@ class EromeSearchExtractor(EromeExtractor):
|
||||
example = "https://www.erome.com/search?q=QUERY"
|
||||
|
||||
def albums(self):
|
||||
url = f"{self.root}/search"
|
||||
url = self.root + "/search"
|
||||
params = text.parse_query(self.groups[0])
|
||||
return self._pagination(url, params)
|
||||
|
||||
|
||||
@@ -237,16 +237,14 @@ class FacebookExtractor(Extractor):
|
||||
|
||||
if res.url.startswith(self.root + "/login"):
|
||||
raise exception.AuthRequired(
|
||||
message=(f"You must be logged in to continue viewing images."
|
||||
f"{LEFT_OFF_TXT}")
|
||||
)
|
||||
message=("You must be logged in to continue viewing images." +
|
||||
LEFT_OFF_TXT))
|
||||
|
||||
if b'{"__dr":"CometErrorRoot.react"}' in res.content:
|
||||
raise exception.AbortExtraction(
|
||||
f"You've been temporarily blocked from viewing images.\n"
|
||||
f"Please try using a different account, "
|
||||
f"using a VPN or waiting before you retry.{LEFT_OFF_TXT}"
|
||||
)
|
||||
"You've been temporarily blocked from viewing images.\n"
|
||||
"Please try using a different account, "
|
||||
"using a VPN or waiting before you retry." + LEFT_OFF_TXT)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
@@ -340,7 +340,7 @@ class FanboxExtractor(Extractor):
|
||||
url = (f"https://docs.google.com/forms/d/e/"
|
||||
f"{content_id}/viewform?usp=sf_link")
|
||||
else:
|
||||
self.log.warning(f"service not recognized: {provider}")
|
||||
self.log.warning("service not recognized: %s", provider)
|
||||
|
||||
if url:
|
||||
final_post["embed"] = embed
|
||||
|
||||
@@ -135,7 +135,7 @@ class FanslyExtractor(Extractor):
|
||||
|
||||
files.append({
|
||||
"file": file,
|
||||
"url": f"ytdl:{location['location']}",
|
||||
"url": "ytdl:" + location["location"],
|
||||
"_fallback": fallback,
|
||||
"_ytdl_manifest":
|
||||
"dash" if mime == "application/dash+xml" else "hls",
|
||||
@@ -184,7 +184,7 @@ class FanslyListExtractor(FanslyExtractor):
|
||||
example = "https://fansly.com/lists/1234567890"
|
||||
|
||||
def items(self):
|
||||
base = f"{self.root}/"
|
||||
base = self.root + "/"
|
||||
for account in self.api.lists_itemsnew(self.groups[0]):
|
||||
account["_extractor"] = FanslyCreatorPostsExtractor
|
||||
url = f"{base}{account['username']}/posts"
|
||||
@@ -197,7 +197,7 @@ class FanslyListsExtractor(FanslyExtractor):
|
||||
example = "https://fansly.com/lists"
|
||||
|
||||
def items(self):
|
||||
base = f"{self.root}/lists/"
|
||||
base = self.root + "/lists/"
|
||||
for list in self.api.lists_account():
|
||||
list["_extractor"] = FanslyListExtractor
|
||||
url = f"{base}{list['id']}#{list['label']}"
|
||||
@@ -308,7 +308,7 @@ class FanslyAPI():
|
||||
return self._pagination(endpoint, params)
|
||||
|
||||
def timeline_new(self, account_id, wall_id):
|
||||
endpoint = f"/v1/timelinenew/{account_id}"
|
||||
endpoint = "/v1/timelinenew/" + str(account_id)
|
||||
params = {
|
||||
"before" : "0",
|
||||
"after" : "0",
|
||||
|
||||
@@ -456,7 +456,7 @@ class FlickrAPI(oauth.OAuth1API):
|
||||
except ValueError:
|
||||
data = {"code": -1, "message": response.content}
|
||||
if "code" in data:
|
||||
msg = data.get("message")
|
||||
msg = data.get("message", "")
|
||||
self.log.debug("Server response: %s", data)
|
||||
if data["code"] == 1:
|
||||
raise exception.NotFoundError(self.extractor.subcategory)
|
||||
@@ -466,7 +466,7 @@ class FlickrAPI(oauth.OAuth1API):
|
||||
raise exception.AuthenticationError(msg)
|
||||
elif data["code"] == 99:
|
||||
raise exception.AuthorizationError(msg)
|
||||
raise exception.AbortExtraction(f"API request failed: {msg}")
|
||||
raise exception.AbortExtraction("API request failed: " + msg)
|
||||
return data
|
||||
|
||||
def _pagination(self, method, params, key="photos"):
|
||||
|
||||
@@ -278,7 +278,7 @@ class FoolfuukaGalleryExtractor(FoolfuukaExtractor):
|
||||
base = f"{self.root}/_/api/chan/gallery/?board={self.board}&page="
|
||||
|
||||
for pnum in pages:
|
||||
posts = self.request_json(f"{base}{pnum}")
|
||||
posts = self.request_json(base + str(pnum))
|
||||
if not posts:
|
||||
return
|
||||
yield from posts
|
||||
|
||||
@@ -322,7 +322,7 @@ class FuraffinityUserExtractor(Dispatch, FuraffinityExtractor):
|
||||
|
||||
def items(self):
|
||||
base = self.root
|
||||
user = f"{self.user}/"
|
||||
user = self.user + "/"
|
||||
return self._dispatch_extractors((
|
||||
(FuraffinityGalleryExtractor , f"{base}/gallery/{user}"),
|
||||
(FuraffinityScrapsExtractor , f"{base}/scraps/{user}"),
|
||||
|
||||
@@ -22,14 +22,14 @@ class GirlsreleasedExtractor(Extractor):
|
||||
|
||||
def items(self):
|
||||
data = {"_extractor": GirlsreleasedSetExtractor}
|
||||
base = f"{self.root}/set/"
|
||||
base = self.root + "/set/"
|
||||
for set in self._pagination():
|
||||
yield Message.Queue, f"{base}{set[0]}", data
|
||||
yield Message.Queue, base + set[0], data
|
||||
|
||||
def _pagination(self):
|
||||
base = f"{self.root}/api/0.2/sets/{self._path}/{self.groups[0]}/page/"
|
||||
for pnum in itertools.count():
|
||||
sets = self.request_json(f"{base}{pnum}")["sets"]
|
||||
sets = self.request_json(base + str(pnum))["sets"]
|
||||
if not sets:
|
||||
return
|
||||
|
||||
|
||||
@@ -32,14 +32,14 @@ class HitomiExtractor(Extractor):
|
||||
language = tag
|
||||
tag = "index"
|
||||
else:
|
||||
ns = f"{ns}/"
|
||||
ns += "/"
|
||||
|
||||
url = (f"https://ltn.{self.domain}/n/{ns}"
|
||||
f"/{tag.replace('_', ' ')}-{language}.nozomi")
|
||||
if headers is None:
|
||||
headers = {}
|
||||
headers["Origin"] = self.root
|
||||
headers["Referer"] = f"{self.root}/"
|
||||
headers["Referer"] = self.root + "/"
|
||||
return decode_nozomi(self.request(url, headers=headers).content)
|
||||
|
||||
|
||||
|
||||
@@ -28,10 +28,7 @@ class ImagehostImageExtractor(Extractor):
|
||||
|
||||
def __init__(self, match):
|
||||
Extractor.__init__(self, match)
|
||||
if self.root:
|
||||
self.page_url = f"{self.root}{match[1]}"
|
||||
else:
|
||||
self.page_url = f"http{'s' if self._https else ''}://{match[1]}"
|
||||
self.page_url = (self.root or "https://") + match[1]
|
||||
self.token = match[2]
|
||||
|
||||
if self._params == "simple":
|
||||
@@ -461,8 +458,8 @@ class ImgdriveImageExtractor(ImagehostImageExtractor):
|
||||
|
||||
def __init__(self, match):
|
||||
path, category, self.token = match.groups()
|
||||
self.page_url = f"https://{path}"
|
||||
self.category = f"img{category}"
|
||||
self.page_url = "https://" + path
|
||||
self.category = "img" + category
|
||||
Extractor.__init__(self, match)
|
||||
|
||||
def get_info(self, page):
|
||||
|
||||
@@ -136,8 +136,8 @@ class ImgbbAlbumExtractor(ImgbbExtractor):
|
||||
'data-text="image-count">', "<")),
|
||||
}
|
||||
|
||||
url = f"{self.root}/json"
|
||||
params["pathname"] = f"/album/{album['id']}"
|
||||
url = self.root + "/json"
|
||||
params["pathname"] = "/album/" + album["id"]
|
||||
return self._pagination(page, url, params)
|
||||
|
||||
|
||||
@@ -190,11 +190,11 @@ class ImgbbUserExtractor(ImgbbExtractor):
|
||||
|
||||
if response.status_code < 300:
|
||||
params["pathname"] = "/"
|
||||
return self._pagination(response.text, f"{url}json", params)
|
||||
return self._pagination(response.text, url + "json", params)
|
||||
|
||||
if response.status_code == 301:
|
||||
raise exception.NotFoundError("user")
|
||||
redirect = f"HTTP redirect to {response.headers.get('Location')}"
|
||||
redirect = "HTTP redirect to " + response.headers.get("Location", "")
|
||||
if response.status_code == 302:
|
||||
raise exception.AuthRequired(
|
||||
("username & password", "authenticated cookies"),
|
||||
|
||||
@@ -22,9 +22,6 @@ class ImgpileExtractor(Extractor):
|
||||
"{post[title]} ({post[id_slug]})")
|
||||
archive_fmt = "{post[id_slug]}_{id}"
|
||||
|
||||
def items(self):
|
||||
pass
|
||||
|
||||
|
||||
class ImgpilePostExtractor(ImgpileExtractor):
|
||||
subcategory = "post"
|
||||
@@ -71,7 +68,7 @@ class ImgpilePostExtractor(ImgpileExtractor):
|
||||
"id_slug": text.extr(media, 'data-id="', '"'),
|
||||
"id" : text.parse_int(text.extr(
|
||||
media, 'data-media-id="', '"')),
|
||||
"url": f"""http{text.extr(media, '<a href="http', '"')}""",
|
||||
"url": "http" + text.extr(media, '<a href="http', '"'),
|
||||
})
|
||||
return files
|
||||
|
||||
@@ -82,13 +79,12 @@ class ImgpileUserExtractor(ImgpileExtractor):
|
||||
example = "https://imgpile.com/u/USER"
|
||||
|
||||
def items(self):
|
||||
url = f"{self.root}/api/v1/posts"
|
||||
url = self.root + "/api/v1/posts"
|
||||
params = {
|
||||
"limit" : "100",
|
||||
"sort" : "latest",
|
||||
"period" : "all",
|
||||
"visibility": "public",
|
||||
# "moderation_status": "approved",
|
||||
"username" : self.groups[0],
|
||||
}
|
||||
headers = {
|
||||
@@ -101,7 +97,7 @@ class ImgpileUserExtractor(ImgpileExtractor):
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
}
|
||||
|
||||
base = f"{self.root}/p/"
|
||||
base = self.root + "/p/"
|
||||
while True:
|
||||
data = self.request_json(url, params=params, headers=headers)
|
||||
|
||||
@@ -111,7 +107,7 @@ class ImgpileUserExtractor(ImgpileExtractor):
|
||||
|
||||
for item in data["data"]:
|
||||
item["_extractor"] = ImgpilePostExtractor
|
||||
url = f"{base}{item['slug']}"
|
||||
url = base + item["slug"]
|
||||
yield Message.Queue, url, item
|
||||
|
||||
url = data["links"].get("next")
|
||||
|
||||
@@ -269,11 +269,11 @@ class ImgurAPI():
|
||||
return self._pagination(endpoint, params)
|
||||
|
||||
def gallery_subreddit(self, subreddit):
|
||||
endpoint = f"/3/gallery/r/{subreddit}"
|
||||
endpoint = "/3/gallery/r/" + subreddit
|
||||
return self._pagination(endpoint)
|
||||
|
||||
def gallery_tag(self, tag):
|
||||
endpoint = f"/3/gallery/t/{tag}"
|
||||
endpoint = "/3/gallery/t/" + tag
|
||||
return self._pagination(endpoint, key="items")
|
||||
|
||||
def image(self, image_hash):
|
||||
|
||||
@@ -636,7 +636,7 @@ class InstagramStoriesTrayExtractor(InstagramExtractor):
|
||||
example = "https://www.instagram.com/stories/me/"
|
||||
|
||||
def items(self):
|
||||
base = f"{self.root}/stories/id:"
|
||||
base = self.root + "/stories/id:"
|
||||
for story in self.api.reels_tray():
|
||||
story["date"] = self.parse_timestamp(story["latest_reel_media"])
|
||||
story["_extractor"] = InstagramStoriesExtractor
|
||||
|
||||
@@ -34,7 +34,7 @@ class ItakuExtractor(Extractor):
|
||||
for image in images:
|
||||
image["date"] = self.parse_datetime_iso(image["date_added"])
|
||||
for category, tags in image.pop("categorized_tags").items():
|
||||
image[f"tags_{category.lower()}"] = [
|
||||
image["tags_" + category.lower()] = [
|
||||
t["name"] for t in tags]
|
||||
image["tags"] = [t["name"] for t in image["tags"]]
|
||||
|
||||
@@ -73,9 +73,9 @@ class ItakuExtractor(Extractor):
|
||||
return
|
||||
|
||||
if users := self.users():
|
||||
base = f"{self.root}/profile/"
|
||||
base = self.root + "/profile/"
|
||||
for user in users:
|
||||
url = f"{base}{user['owner_username']}"
|
||||
url = base + user["owner_username"]
|
||||
user["_extractor"] = ItakuUserExtractor
|
||||
yield Message.Queue, url, user
|
||||
return
|
||||
@@ -182,11 +182,11 @@ class ItakuUserExtractor(Dispatch, ItakuExtractor):
|
||||
def items(self):
|
||||
base = f"{self.root}/profile/{self.groups[0]}/"
|
||||
return self._dispatch_extractors((
|
||||
(ItakuGalleryExtractor , f"{base}gallery"),
|
||||
(ItakuPostsExtractor , f"{base}posts"),
|
||||
(ItakuFollowersExtractor, f"{base}followers"),
|
||||
(ItakuFollowingExtractor, f"{base}following"),
|
||||
(ItakuStarsExtractor , f"{base}stars"),
|
||||
(ItakuGalleryExtractor , base + "gallery"),
|
||||
(ItakuPostsExtractor , base + "posts"),
|
||||
(ItakuFollowersExtractor, base + "followers"),
|
||||
(ItakuFollowingExtractor, base + "following"),
|
||||
(ItakuStarsExtractor , base + "stars"),
|
||||
), ("gallery",))
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ class ItakuAPI():
|
||||
|
||||
def __init__(self, extractor):
|
||||
self.extractor = extractor
|
||||
self.root = f"{extractor.root}/api"
|
||||
self.root = extractor.root + "/api"
|
||||
self.headers = {
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
}
|
||||
@@ -309,7 +309,7 @@ class ItakuAPI():
|
||||
|
||||
def _call(self, endpoint, params=None):
|
||||
if not endpoint.startswith("http"):
|
||||
endpoint = f"{self.root}{endpoint}"
|
||||
endpoint = self.root + endpoint
|
||||
return self.extractor.request_json(
|
||||
endpoint, params=params, headers=self.headers)
|
||||
|
||||
|
||||
@@ -79,10 +79,10 @@ class IwaraExtractor(Extractor):
|
||||
continue
|
||||
|
||||
yield Message.Directory, "", info
|
||||
yield Message.Url, f"https:{download_url}", info
|
||||
yield Message.Url, "https:" + download_url, info
|
||||
|
||||
def items_user(self, users, key=None):
|
||||
base = f"{self.root}/profile/"
|
||||
base = self.root + "/profile/"
|
||||
for user in users:
|
||||
if key is not None:
|
||||
user = user[key]
|
||||
@@ -90,7 +90,7 @@ class IwaraExtractor(Extractor):
|
||||
continue
|
||||
user["type"] = "user"
|
||||
user["_extractor"] = IwaraUserExtractor
|
||||
yield Message.Queue, f"{base}{username}", user
|
||||
yield Message.Queue, base + username, user
|
||||
|
||||
def items_by_type(self, type, results):
|
||||
if type == "image":
|
||||
@@ -164,9 +164,9 @@ class IwaraUserExtractor(Dispatch, IwaraExtractor):
|
||||
def items(self):
|
||||
base = f"{self.root}/profile/{self.groups[0]}/"
|
||||
return self._dispatch_extractors((
|
||||
(IwaraUserImagesExtractor , f"{base}images"),
|
||||
(IwaraUserVideosExtractor , f"{base}videos"),
|
||||
(IwaraUserPlaylistsExtractor, f"{base}playlists"),
|
||||
(IwaraUserImagesExtractor , base + "images"),
|
||||
(IwaraUserVideosExtractor , base + "videos"),
|
||||
(IwaraUserPlaylistsExtractor, base + "playlists"),
|
||||
), ("user-images", "user-videos"))
|
||||
|
||||
|
||||
@@ -196,12 +196,12 @@ class IwaraUserPlaylistsExtractor(IwaraExtractor):
|
||||
example = "https://www.iwara.tv/profile/USERNAME/playlists"
|
||||
|
||||
def items(self):
|
||||
base = f"{self.root}/playlist/"
|
||||
base = self.root + "/playlist/"
|
||||
|
||||
for playlist in self.api.playlists(self._user_params()[1]):
|
||||
playlist["type"] = "playlist"
|
||||
playlist["_extractor"] = IwaraPlaylistExtractor
|
||||
url = f"{base}{playlist['id']}"
|
||||
url = base + playlist["id"]
|
||||
yield Message.Queue, url, playlist
|
||||
|
||||
|
||||
@@ -298,7 +298,7 @@ class IwaraAPI():
|
||||
def __init__(self, extractor):
|
||||
self.extractor = extractor
|
||||
self.headers = {
|
||||
"Referer" : f"{extractor.root}/",
|
||||
"Referer" : extractor.root + "/",
|
||||
"Content-Type": "application/json",
|
||||
"Origin" : extractor.root,
|
||||
}
|
||||
@@ -308,15 +308,15 @@ class IwaraAPI():
|
||||
self.authenticate = util.noop
|
||||
|
||||
def image(self, image_id):
|
||||
endpoint = f"/image/{image_id}"
|
||||
endpoint = "/image/" + image_id
|
||||
return self._call(endpoint)
|
||||
|
||||
def video(self, video_id):
|
||||
endpoint = f"/video/{video_id}"
|
||||
endpoint = "/video/" + video_id
|
||||
return self._call(endpoint)
|
||||
|
||||
def playlist(self, playlist_id):
|
||||
endpoint = f"/playlist/{playlist_id}"
|
||||
endpoint = "/playlist/" + playlist_id
|
||||
return self._pagination(endpoint)
|
||||
|
||||
def detail(self, media):
|
||||
@@ -356,7 +356,7 @@ class IwaraAPI():
|
||||
|
||||
@memcache(keyarg=1)
|
||||
def profile(self, username):
|
||||
endpoint = f"/profile/{username}"
|
||||
endpoint = "/profile/" + username
|
||||
return self._call(endpoint)
|
||||
|
||||
def user_following(self, user_id):
|
||||
@@ -387,7 +387,7 @@ class IwaraAPI():
|
||||
if refresh_token is None:
|
||||
self.extractor.log.info("Logging in as %s", username)
|
||||
|
||||
url = f"{self.root}/user/login"
|
||||
url = self.root + "/user/login"
|
||||
json = {
|
||||
"email" : username,
|
||||
"password": self.password
|
||||
@@ -403,15 +403,15 @@ class IwaraAPI():
|
||||
|
||||
self.extractor.log.info("Refreshing access token for %s", username)
|
||||
|
||||
url = f"{self.root}/user/token"
|
||||
headers = {"Authorization": f"Bearer {refresh_token}", **self.headers}
|
||||
url = self.root + "/user/token"
|
||||
headers = {"Authorization": "Bearer " + refresh_token, **self.headers}
|
||||
data = self.extractor.request_json(
|
||||
url, method="POST", headers=headers, fatal=False)
|
||||
|
||||
if not (access_token := data.get("accessToken")):
|
||||
self.extractor.log.debug(data)
|
||||
raise exception.AuthenticationError(data.get("message"))
|
||||
return f"Bearer {access_token}"
|
||||
return "Bearer " + access_token
|
||||
|
||||
def _call(self, endpoint, params=None, headers=None):
|
||||
if headers is None:
|
||||
|
||||
@@ -52,7 +52,7 @@ class KabeuchiUserExtractor(Extractor):
|
||||
return self._pagination(target_id)
|
||||
|
||||
def _pagination(self, target_id):
|
||||
url = f"{self.root}/get_posts.php"
|
||||
url = self.root + "/get_posts.php"
|
||||
data = {
|
||||
"user_id" : "0",
|
||||
"target_id" : target_id,
|
||||
|
||||
@@ -200,7 +200,7 @@ class KemonoExtractor(Extractor):
|
||||
username = username[0]
|
||||
self.log.info("Logging in as %s", username)
|
||||
|
||||
url = f"{self.root}/api/v1/authentication/login"
|
||||
url = self.root + "/api/v1/authentication/login"
|
||||
data = {"username": username, "password": password}
|
||||
|
||||
response = self.request(url, method="POST", json=data, fatal=False)
|
||||
@@ -434,7 +434,7 @@ class KemonoDiscordExtractor(KemonoExtractor):
|
||||
attachment["type"] = "attachment"
|
||||
files.append(attachment)
|
||||
for path in find_inline(post["content"] or ""):
|
||||
files.append({"path": f"https://cdn.discordapp.com{path}",
|
||||
files.append({"path": "https://cdn.discordapp.com" + path,
|
||||
"name": path, "type": "inline", "hash": ""})
|
||||
|
||||
post.update(data)
|
||||
@@ -577,7 +577,7 @@ class KemonoAPI():
|
||||
|
||||
def __init__(self, extractor):
|
||||
self.extractor = extractor
|
||||
self.root = f"{extractor.root}/api"
|
||||
self.root = extractor.root + "/api"
|
||||
self.headers = {"Accept": "text/css"}
|
||||
|
||||
def posts(self, offset=0, query=None, tags=None):
|
||||
@@ -586,7 +586,7 @@ class KemonoAPI():
|
||||
return self._pagination(endpoint, params, 50, "posts")
|
||||
|
||||
def file(self, file_hash):
|
||||
endpoint = f"/v1/file/{file_hash}"
|
||||
endpoint = "/v1/file/" + file_hash
|
||||
return self._call(endpoint)
|
||||
|
||||
def creators(self):
|
||||
@@ -643,18 +643,18 @@ class KemonoAPI():
|
||||
return self._call(endpoint)
|
||||
|
||||
def discord_channel(self, channel_id, post_count=None):
|
||||
endpoint = f"/v1/discord/channel/{channel_id}"
|
||||
endpoint = "/v1/discord/channel/" + channel_id
|
||||
if post_count is None:
|
||||
return self._pagination(endpoint, {}, 150)
|
||||
else:
|
||||
return self._pagination_reverse(endpoint, {}, 150, post_count)
|
||||
|
||||
def discord_channel_lookup(self, server_id):
|
||||
endpoint = f"/v1/discord/channel/lookup/{server_id}"
|
||||
endpoint = "/v1/discord/channel/lookup/" + server_id
|
||||
return self._call(endpoint)
|
||||
|
||||
def discord_server(self, server_id):
|
||||
endpoint = f"/v1/discord/server/{server_id}"
|
||||
endpoint = "/v1/discord/server/" + server_id
|
||||
return self._call(endpoint)
|
||||
|
||||
def account_favorites(self, type):
|
||||
@@ -669,7 +669,7 @@ class KemonoAPI():
|
||||
headers = {**self.headers, **headers}
|
||||
|
||||
return self.extractor.request_json(
|
||||
f"{self.root}{endpoint}", params=params, headers=headers,
|
||||
self.root + endpoint, params=params, headers=headers,
|
||||
encoding="utf-8", fatal=fatal)
|
||||
|
||||
def _pagination(self, endpoint, params, batch=50, key=None):
|
||||
|
||||
@@ -35,7 +35,7 @@ class LeakgalleryExtractor(Extractor):
|
||||
else:
|
||||
media["creator"] = creator
|
||||
|
||||
media["url"] = url = f"https://cdn.leakgallery.com/{path}"
|
||||
media["url"] = url = "https://cdn.leakgallery.com/" + path
|
||||
text.nameext_from_url(url, media)
|
||||
yield Message.Directory, "", media
|
||||
yield Message.Url, url, media
|
||||
@@ -43,7 +43,7 @@ class LeakgalleryExtractor(Extractor):
|
||||
def _pagination(self, type, base, params=None, creator=None, pnum=1):
|
||||
while True:
|
||||
try:
|
||||
data = self.request_json(f"{base}{pnum}", params=params)
|
||||
data = self.request_json(base + str(pnum), params=params)
|
||||
|
||||
if not data:
|
||||
return
|
||||
|
||||
@@ -85,7 +85,7 @@ class MadokamiMangaExtractor(MadokamiExtractor):
|
||||
else:
|
||||
ch["volume"] = ch["chapter"] = ch["chapter_end"] = 0
|
||||
|
||||
url = f"{self.root}{ch['path']}"
|
||||
url = self.root + ch["path"]
|
||||
text.nameext_from_url(url, ch)
|
||||
|
||||
yield Message.Directory, "", ch
|
||||
|
||||
@@ -66,7 +66,7 @@ class MangadexExtractor(Extractor):
|
||||
"title" : cattributes["title"],
|
||||
"volume" : text.parse_int(cattributes["volume"]),
|
||||
"chapter" : text.parse_int(chnum),
|
||||
"chapter_minor": f"{sep}{minor}",
|
||||
"chapter_minor": sep + minor,
|
||||
"chapter_id": chapter["id"],
|
||||
"date" : self.parse_datetime_iso(cattributes["publishAt"]),
|
||||
"group" : [group["attributes"]["name"]
|
||||
@@ -96,7 +96,7 @@ class MangadexCoversExtractor(MangadexExtractor):
|
||||
text.nameext_from_url(name, data)
|
||||
data["cover_id"] = data["filename"]
|
||||
yield Message.Directory, "", data
|
||||
yield Message.Url, f"{base}{name}", data
|
||||
yield Message.Url, base + name, data
|
||||
|
||||
def _transform_cover(self, cover):
|
||||
relationships = defaultdict(list)
|
||||
@@ -148,9 +148,9 @@ class MangadexChapterExtractor(MangadexExtractor):
|
||||
|
||||
enum = util.enumerate_reversed if self.config(
|
||||
"page-reverse") else enumerate
|
||||
for data["page"], page in enum(chapter[key], 1):
|
||||
text.nameext_from_url(page, data)
|
||||
yield Message.Url, f"{base}{page}", data
|
||||
for data["page"], path in enum(chapter[key], 1):
|
||||
text.nameext_from_url(path, data)
|
||||
yield Message.Url, base + path, data
|
||||
|
||||
|
||||
class MangadexMangaExtractor(MangadexExtractor):
|
||||
@@ -253,22 +253,22 @@ class MangadexAPI():
|
||||
else text.ensure_http_scheme(server).rstrip("/"))
|
||||
|
||||
def athome_server(self, uuid):
|
||||
return self._call(f"/at-home/server/{uuid}")
|
||||
return self._call("/at-home/server/" + uuid)
|
||||
|
||||
def author(self, uuid, manga=False):
|
||||
params = {"includes[]": ("manga",)} if manga else None
|
||||
return self._call(f"/author/{uuid}", params)["data"]
|
||||
return self._call("/author/" + uuid, params)["data"]
|
||||
|
||||
def chapter(self, uuid):
|
||||
params = {"includes[]": ("scanlation_group",)}
|
||||
return self._call(f"/chapter/{uuid}", params)["data"]
|
||||
return self._call("/chapter/" + uuid, params)["data"]
|
||||
|
||||
def covers_manga(self, uuid):
|
||||
params = {"manga[]": uuid}
|
||||
return self._pagination_covers("/cover", params)
|
||||
|
||||
def list(self, uuid):
|
||||
return self._call(f"/list/{uuid}", None, True)["data"]
|
||||
return self._call("/list/" + uuid, None, True)["data"]
|
||||
|
||||
def list_feed(self, uuid):
|
||||
return self._pagination_chapters(f"/list/{uuid}/feed", None, True)
|
||||
@@ -276,7 +276,7 @@ class MangadexAPI():
|
||||
@memcache(keyarg=1)
|
||||
def manga(self, uuid):
|
||||
params = {"includes[]": ("artist", "author")}
|
||||
return self._call(f"/manga/{uuid}", params)["data"]
|
||||
return self._call("/manga/" + uuid, params)["data"]
|
||||
|
||||
def manga_author(self, uuid_author):
|
||||
params = {"authorOrArtist": uuid_author}
|
||||
@@ -339,17 +339,17 @@ class MangadexAPI():
|
||||
_refresh_token_cache.update(
|
||||
(username, "personal"), data["refresh_token"])
|
||||
|
||||
return f"Bearer {access_token}"
|
||||
return "Bearer " + access_token
|
||||
|
||||
@cache(maxage=900, keyarg=1)
|
||||
def _authenticate_impl_legacy(self, username, password):
|
||||
if refresh_token := _refresh_token_cache(username):
|
||||
self.extractor.log.info("Refreshing access token")
|
||||
url = f"{self.root}/auth/refresh"
|
||||
url = self.root + "/auth/refresh"
|
||||
json = {"token": refresh_token}
|
||||
else:
|
||||
self.extractor.log.info("Logging in as %s", username)
|
||||
url = f"{self.root}/auth/login"
|
||||
url = self.root + "/auth/login"
|
||||
json = {"username": username, "password": password}
|
||||
|
||||
self.extractor.log.debug("Using legacy login method")
|
||||
@@ -360,10 +360,10 @@ class MangadexAPI():
|
||||
|
||||
if refresh_token != data["token"]["refresh"]:
|
||||
_refresh_token_cache.update(username, data["token"]["refresh"])
|
||||
return f"Bearer {data['token']['session']}"
|
||||
return "Bearer " + data["token"]["session"]
|
||||
|
||||
def _call(self, endpoint, params=None, auth=False):
|
||||
url = f"{self.root}{endpoint}"
|
||||
url = self.root + endpoint
|
||||
headers = self.headers_auth if auth else self.headers
|
||||
|
||||
while True:
|
||||
|
||||
@@ -75,7 +75,7 @@ class MangafireMangaExtractor(MangafireBase, MangaExtractor):
|
||||
chapters = _manga_chapters(self, (manga_id, "chapter", lang))
|
||||
|
||||
return [
|
||||
(f"""{self.root}{text.extr(anchor, 'href="', '"')}""", {
|
||||
(self.root + text.extr(anchor, 'href="', '"'), {
|
||||
**manga,
|
||||
**_chapter_info(anchor),
|
||||
})
|
||||
@@ -160,7 +160,7 @@ def _chapter_info(info):
|
||||
chapter, sep, minor = text.extr(info, 'data-number="', '"').partition(".")
|
||||
return {
|
||||
"chapter" : text.parse_int(chapter),
|
||||
"chapter_minor" : f"{sep}{minor}",
|
||||
"chapter_minor" : sep + minor,
|
||||
"chapter_string": chapter_info,
|
||||
"chapter_id" : text.parse_int(text.extr(info, 'data-id="', '"')),
|
||||
"title" : text.unescape(text.extr(info, 'title="', '"')),
|
||||
|
||||
@@ -28,7 +28,7 @@ class MangahereChapterExtractor(MangahereBase, ChapterExtractor):
|
||||
def __init__(self, match):
|
||||
self.part, self.volume, self.chapter = match.groups()
|
||||
self.base = f"{self.root_mobile}/manga/{self.part}/"
|
||||
ChapterExtractor.__init__(self, match, f"{self.base}1.html")
|
||||
ChapterExtractor.__init__(self, match, self.base + "1.html")
|
||||
|
||||
def _init(self):
|
||||
self.session.headers["Referer"] = self.root_mobile + "/"
|
||||
|
||||
@@ -138,9 +138,9 @@ def _manga_info(self, manga_path):
|
||||
current[chap] = {
|
||||
"title" : name.partition(":")[2].strip(),
|
||||
"chapter" : text.parse_int(chapter),
|
||||
"chapter_minor" : f"{sep}{minor}",
|
||||
"chapter_minor" : sep + minor,
|
||||
"chapter_string": chap,
|
||||
"chapter_url" : f"{base}{path}",
|
||||
"chapter_url" : base + path,
|
||||
"lang" : lang,
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ def _manga_info(self, manga_path):
|
||||
"chapter" : 0,
|
||||
"chapter_minor" : "",
|
||||
"chapter_string": voln,
|
||||
"chapter_url" : f"{base}{path}",
|
||||
"chapter_url" : base + path,
|
||||
"lang" : lang,
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ class MangataroMangaExtractor(MangataroBase, MangaExtractor):
|
||||
results.append((url, {
|
||||
**manga,
|
||||
"chapter" : text.parse_int(chapter),
|
||||
"chapter_minor": f".{minor}" if sep else "",
|
||||
"chapter_minor": "." + minor if sep else "",
|
||||
"chapter_id" : text.parse_int(chapter_id),
|
||||
}))
|
||||
return results
|
||||
|
||||
@@ -104,7 +104,7 @@ class MoebooruTagExtractor(MoebooruExtractor):
|
||||
|
||||
def posts(self):
|
||||
params = {"tags": self.tags}
|
||||
return self._pagination(f"{self.root}/post.json", params)
|
||||
return self._pagination(self.root + "/post.json", params)
|
||||
|
||||
|
||||
class MoebooruPoolExtractor(MoebooruExtractor):
|
||||
@@ -129,7 +129,7 @@ class MoebooruPoolExtractor(MoebooruExtractor):
|
||||
|
||||
def posts(self):
|
||||
params = {"tags": "pool:" + self.pool_id}
|
||||
return self._pagination(f"{self.root}/post.json", params)
|
||||
return self._pagination(self.root + "/post.json", params)
|
||||
|
||||
|
||||
class MoebooruPostExtractor(MoebooruExtractor):
|
||||
@@ -140,7 +140,7 @@ class MoebooruPostExtractor(MoebooruExtractor):
|
||||
|
||||
def posts(self):
|
||||
params = {"tags": "id:" + self.groups[-1]}
|
||||
return self.request_json(f"{self.root}/post.json", params=params)
|
||||
return self.request_json(self.root + "/post.json", params=params)
|
||||
|
||||
|
||||
class MoebooruPopularExtractor(MoebooruExtractor):
|
||||
|
||||
@@ -93,8 +93,8 @@ class MotherlessExtractor(Extractor):
|
||||
title = self._extract_group_title(page, gid)
|
||||
|
||||
return {
|
||||
f"{category}_id": gid,
|
||||
f"{category}_title": title,
|
||||
category + "_id": gid,
|
||||
category + "_title": title,
|
||||
"uploader": text.remove_html(extr(
|
||||
f'class="{category}-member-username">', "</")),
|
||||
"count": text.parse_int(
|
||||
|
||||
@@ -142,7 +142,7 @@ class NaverBlogBlogExtractor(NaverBlogBase, Extractor):
|
||||
)
|
||||
|
||||
# setup params for API calls
|
||||
url = f"{self.root}/PostViewBottomTitleListAsync.nhn"
|
||||
url = self.root + "/PostViewBottomTitleListAsync.nhn"
|
||||
params = {
|
||||
"blogId" : self.blog_id,
|
||||
"logNo" : post_num or "0",
|
||||
|
||||
@@ -510,7 +510,7 @@ class NewgroundsFavoriteExtractor(NewgroundsExtractor):
|
||||
def _extract_favorites(self, page):
|
||||
return [
|
||||
self.root + path
|
||||
for path in text.extract_iter(page, f'href="{self.root}', '"')
|
||||
for path in text.extract_iter(page, 'href="' + self.root, '"')
|
||||
]
|
||||
|
||||
|
||||
@@ -519,7 +519,6 @@ class NewgroundsFollowingExtractor(NewgroundsFavoriteExtractor):
|
||||
subcategory = "following"
|
||||
pattern = (USER_PATTERN + r"/favorites/(following)"
|
||||
r"(?:(?:/page/|/?\?page=)(\d+))?")
|
||||
|
||||
example = "https://USER.newgrounds.com/favorites/following"
|
||||
|
||||
def items(self):
|
||||
|
||||
@@ -140,7 +140,7 @@ class NijieExtractor(AsynchronousMixin, BaseExtractor):
|
||||
def _login_impl(self, username, password):
|
||||
self.log.info("Logging in as %s", username)
|
||||
|
||||
url = f"{self.root}/login_int.php"
|
||||
url = self.root + "/login_int.php"
|
||||
data = {"email": username, "password": password, "save": "on"}
|
||||
|
||||
response = self.request(url, method="POST", data=data)
|
||||
|
||||
@@ -97,7 +97,7 @@ class PahealTagExtractor(PahealExtractor):
|
||||
|
||||
while True:
|
||||
try:
|
||||
page = self.request(f"{base}{pnum}").text
|
||||
page = self.request(base + str(pnum)).text
|
||||
except exception.HttpError as exc:
|
||||
if exc.status == 404:
|
||||
return
|
||||
|
||||
@@ -300,8 +300,8 @@ class PatreonExtractor(Extractor):
|
||||
order = "-published_at"
|
||||
elif order in {"a", "asc", "r", "reverse"}:
|
||||
order = "published_at"
|
||||
return f"&sort={order}"
|
||||
return f"&sort={sort}" if sort else ""
|
||||
return "&sort=" + order
|
||||
return "&sort=" + sort if sort else ""
|
||||
|
||||
def _build_file_generators(self, filetypes):
|
||||
if filetypes is None:
|
||||
@@ -382,8 +382,8 @@ class PatreonCollectionExtractor(PatreonExtractor):
|
||||
elif order in {"d", "desc", "r", "reverse"}:
|
||||
# "-collection_order" results in a '400 Bad Request' error
|
||||
order = "-published_at"
|
||||
return f"&sort={order}"
|
||||
return f"&sort={sort}" if sort else ""
|
||||
return "&sort=" + order
|
||||
return "&sort=" + sort if sort else ""
|
||||
|
||||
|
||||
class PatreonCreatorExtractor(PatreonExtractor):
|
||||
|
||||
@@ -116,7 +116,7 @@ class PhilomenaGalleryExtractor(PhilomenaExtractor):
|
||||
raise exception.NotFoundError("gallery")
|
||||
|
||||
def posts(self):
|
||||
gallery_id = f"gallery_id:{self.groups[-1]}"
|
||||
gallery_id = "gallery_id:" + self.groups[-1]
|
||||
params = {"sd": "desc", "sf": gallery_id, "q": gallery_id}
|
||||
return self.api.search(params)
|
||||
|
||||
|
||||
@@ -657,7 +657,7 @@ class PixivFavoriteExtractor(PixivExtractor):
|
||||
for preview in self.api.user_following(self.user_id, restrict):
|
||||
user = preview["user"]
|
||||
user["_extractor"] = PixivUserExtractor
|
||||
url = f"https://www.pixiv.net/users/{user['id']}"
|
||||
url = "https://www.pixiv.net/users/" + str(user["id"])
|
||||
yield Message.Queue, url, user
|
||||
|
||||
|
||||
@@ -1302,7 +1302,7 @@ class PixivAppAPI():
|
||||
msg = (f"'{msg}'" if (msg := error.get("user_message")) else
|
||||
f"'{msg}'" if (msg := error.get("message")) else
|
||||
error)
|
||||
raise exception.AbortExtraction(f"API request failed: {msg}")
|
||||
raise exception.AbortExtraction("API request failed: " + msg)
|
||||
|
||||
def _pagination(self, endpoint, params,
|
||||
key_items="illusts", key_data=None, key_user=None):
|
||||
|
||||
@@ -54,7 +54,7 @@ class PoipikuExtractor(Extractor):
|
||||
|
||||
for post_url in self.posts():
|
||||
if post_url[0] == "/":
|
||||
post_url = f"{self.root}{post_url}"
|
||||
post_url = self.root + post_url
|
||||
page = self.request(post_url).text
|
||||
extr = text.extract_from(page)
|
||||
parts = post_url.rsplit("/", 2)
|
||||
@@ -148,7 +148,7 @@ class PoipikuExtractor(Extractor):
|
||||
return files
|
||||
|
||||
def _show_illust_detail(self, post):
|
||||
url = f"{self.root}/f/ShowIllustDetailF.jsp"
|
||||
url = self.root + "/f/ShowIllustDetailF.jsp"
|
||||
data = {
|
||||
"ID" : post["user_id"],
|
||||
"TD" : post["post_id"],
|
||||
@@ -160,7 +160,7 @@ class PoipikuExtractor(Extractor):
|
||||
interval=False)
|
||||
|
||||
def _show_append_file(self, post):
|
||||
url = f"{self.root}/f/ShowAppendFileF.jsp"
|
||||
url = self.root + "/f/ShowAppendFileF.jsp"
|
||||
data = {
|
||||
"UID": post["user_id"],
|
||||
"IID": post["post_id"],
|
||||
@@ -183,7 +183,7 @@ class PoipikuUserExtractor(PoipikuExtractor):
|
||||
def posts(self):
|
||||
pnum, user_id = self.groups
|
||||
|
||||
url = f"{self.root}/IllustListPcV.jsp"
|
||||
url = self.root + "/IllustListPcV.jsp"
|
||||
params = {
|
||||
"PG" : text.parse_int(pnum, 0),
|
||||
"ID" : user_id,
|
||||
|
||||
@@ -65,7 +65,7 @@ class RawkumaMangaExtractor(RawkumaBase, MangaExtractor):
|
||||
manga = text.unescape(text.extr(page, "<title>", " – "))
|
||||
manga_id = text.parse_int(text.extr(page, "manga_id=", "&"))
|
||||
|
||||
url = f"{self.root}/wp-admin/admin-ajax.php"
|
||||
url = self.root + "/wp-admin/admin-ajax.php"
|
||||
params = {
|
||||
"manga_id": manga_id,
|
||||
"page" : "1",
|
||||
|
||||
@@ -49,7 +49,7 @@ class RealbooruExtractor(booru.BooruExtractor):
|
||||
tags.append(tag)
|
||||
tags_categories[tag_type].append(tag)
|
||||
for key, value in tags_categories.items():
|
||||
post[f"tags_{key}"] = ", ".join(value)
|
||||
post["tags_" + key] = ", ".join(value)
|
||||
tags.sort()
|
||||
|
||||
post["tags"] = ", ".join(tags)
|
||||
|
||||
@@ -223,10 +223,10 @@ class RedditExtractor(Extractor):
|
||||
self.log.debug(src)
|
||||
elif url := data.get("dashUrl"):
|
||||
submission["_ytdl_manifest"] = "dash"
|
||||
yield f"ytdl:{url}"
|
||||
yield "ytdl:" + url
|
||||
elif url := data.get("hlsUrl"):
|
||||
submission["_ytdl_manifest"] = "hls"
|
||||
yield f"ytdl:{url}"
|
||||
yield "ytdl:" + url
|
||||
|
||||
def _extract_video_ytdl(self, submission):
|
||||
return "https://www.reddit.com" + submission["permalink"]
|
||||
@@ -506,7 +506,7 @@ class RedditAPI():
|
||||
return "Bearer " + data["access_token"]
|
||||
|
||||
def _call(self, endpoint, params):
|
||||
url = f"{self.root}{endpoint}"
|
||||
url = self.root + endpoint
|
||||
params["raw_json"] = "1"
|
||||
|
||||
while True:
|
||||
|
||||
@@ -135,7 +135,7 @@ class RedgifsCollectionsExtractor(RedgifsExtractor):
|
||||
def items(self):
|
||||
base = f"{self.root}/users/{self.key}/collections/"
|
||||
for collection in self.api.collections(self.key):
|
||||
url = f"{base}{collection['folderId']}"
|
||||
url = base + collection["folderId"]
|
||||
collection["_extractor"] = RedgifsCollectionExtractor
|
||||
yield Message.Queue, url, collection
|
||||
|
||||
|
||||
@@ -120,13 +120,13 @@ class Rule34xyzExtractor(BooruExtractor):
|
||||
def _login_impl(self, username, password):
|
||||
self.log.info("Logging in as %s", username)
|
||||
|
||||
url = f"{self.root}/api/v2/auth/signin"
|
||||
url = self.root + "/api/v2/auth/signin"
|
||||
data = {"email": username, "password": password}
|
||||
response = self.request_json(
|
||||
url, method="POST", json=data, fatal=False)
|
||||
|
||||
if jwt := response.get("jwt"):
|
||||
return f"Bearer {jwt}"
|
||||
return "Bearer " + jwt
|
||||
raise exception.AuthenticationError(
|
||||
(msg := response.get("message")) and f'"{msg}"')
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class S3ndpicsExtractor(Extractor):
|
||||
"""Base class for s3ndpics extractors"""
|
||||
category = "s3ndpics"
|
||||
root = "https://s3nd.pics"
|
||||
root_api = f"{root}/api"
|
||||
root_api = root + "/api"
|
||||
directory_fmt = ("{category}", "{user[username]}",
|
||||
"{date} {title:?/ /}({id})")
|
||||
filename_fmt = "{num:>02}.{extension}"
|
||||
@@ -41,7 +41,7 @@ class S3ndpicsExtractor(Extractor):
|
||||
post["type"] = file["type"]
|
||||
path = file["url"]
|
||||
text.nameext_from_url(path, post)
|
||||
yield Message.Url, f"{base}{path}", post
|
||||
yield Message.Url, base + path, post
|
||||
|
||||
def _pagination(self, url, params):
|
||||
params["page"] = 1
|
||||
@@ -76,7 +76,7 @@ class S3ndpicsUserExtractor(S3ndpicsExtractor):
|
||||
url = f"{self.root_api}/users/username/{self.groups[0]}"
|
||||
self.kwdict["user"] = user = self.request_json(url)["user"]
|
||||
|
||||
url = f"{self.root_api}/posts"
|
||||
url = self.root_api + "/posts"
|
||||
params = {
|
||||
"userId": user["_id"],
|
||||
"limit" : "12",
|
||||
@@ -91,7 +91,7 @@ class S3ndpicsSearchExtractor(S3ndpicsExtractor):
|
||||
example = "https://s3nd.pics/search?QUERY"
|
||||
|
||||
def posts(self):
|
||||
url = f"{self.root_api}/posts"
|
||||
url = self.root_api + "/posts"
|
||||
params = text.parse_query(self.groups[0])
|
||||
params.setdefault("limit", "20")
|
||||
self.kwdict["search_tags"] = \
|
||||
|
||||
@@ -193,7 +193,7 @@ class SankakuBooksExtractor(SankakuExtractor):
|
||||
params = {"tags": self.tags, "pool_type": "0"}
|
||||
for pool in self.api.pools_keyset(params):
|
||||
pool["_extractor"] = SankakuPoolExtractor
|
||||
url = f"https://sankaku.app/books/{pool['id']}"
|
||||
url = "https://sankaku.app/books/" + pool["id"]
|
||||
yield Message.Queue, url, pool
|
||||
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class SchalenetworkExtractor(Extractor):
|
||||
|
||||
def _token(self, required=True):
|
||||
if token := self.config("token"):
|
||||
return f"Bearer {token.rpartition(' ')[2]}"
|
||||
return "Bearer " + token.rpartition(' ')[2]
|
||||
if required:
|
||||
raise exception.AuthRequired("'token'", "your favorites")
|
||||
|
||||
@@ -172,7 +172,7 @@ class SchalenetworkGalleryExtractor(SchalenetworkExtractor, GalleryExtractor):
|
||||
if self.config("cbz", False):
|
||||
headers["Authorization"] = self._token()
|
||||
dl = self.request_json(
|
||||
f"{url}&action=dl", method="POST", headers=headers)
|
||||
url + "&action=dl", method="POST", headers=headers)
|
||||
# 'crt' parameter here is necessary for 'hdoujin' downloads
|
||||
url = f"{dl['base']}?crt={self._crt()}"
|
||||
info = text.nameext_from_url(url)
|
||||
@@ -259,7 +259,7 @@ class SchalenetworkFavoriteExtractor(SchalenetworkExtractor):
|
||||
params = text.parse_query(self.groups[1])
|
||||
params["page"] = text.parse_int(params.get("page"), 1)
|
||||
self.headers["Authorization"] = self._token()
|
||||
return self._pagination(f"/books/favorites?crt={self._crt()}", params)
|
||||
return self._pagination("/books/favorites?crt=" + self._crt(), params)
|
||||
|
||||
|
||||
SchalenetworkExtractor.extr_class = SchalenetworkGalleryExtractor
|
||||
|
||||
@@ -282,7 +282,7 @@ class SexcomFeedExtractor(SexcomExtractor):
|
||||
def pins(self):
|
||||
if not self.cookies_check(("sess_sex",)):
|
||||
self.log.warning("no 'sess_sex' cookie set")
|
||||
url = f"{self.root}/feed/"
|
||||
url = self.root + "/feed/"
|
||||
return self._pagination(url)
|
||||
|
||||
|
||||
@@ -341,10 +341,10 @@ class SexcomSearchExtractor(SexcomExtractor):
|
||||
pin["type"] = "gif"
|
||||
if gifs and pin["extension"] == "webp":
|
||||
pin["extension"] = "gif"
|
||||
pin["_fallback"] = (f"{root}{path}",)
|
||||
path = f"{path[:-4]}gif"
|
||||
pin["_fallback"] = (root + path,)
|
||||
path = path[:-4] + "gif"
|
||||
|
||||
pin["url"] = f"{root}{path}"
|
||||
pin["url"] = root + path
|
||||
yield Message.Directory, "", pin
|
||||
yield Message.Url, pin["url"], pin
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class SubscribestarExtractor(Extractor):
|
||||
text.nameext_from_url(url, item)
|
||||
|
||||
if url[0] == "/":
|
||||
url = f"{self.root}{url}"
|
||||
url = self.root + url
|
||||
yield Message.Url, url, item
|
||||
|
||||
def posts(self):
|
||||
@@ -72,7 +72,7 @@ class SubscribestarExtractor(Extractor):
|
||||
"/verify_subscriber" in response.url or
|
||||
"/age_confirmation_warning" in response.url):
|
||||
raise exception.AbortExtraction(
|
||||
f"HTTP redirect to {response.url}")
|
||||
"HTTP redirect to " + response.url)
|
||||
|
||||
content = response.content
|
||||
if len(content) < 250 and b">redirected<" in content:
|
||||
|
||||
@@ -90,12 +90,12 @@ class ThehentaiworldExtractor(Extractor):
|
||||
post["tags"] = tags_list = []
|
||||
for key, value in tags.items():
|
||||
tags_list.extend(value)
|
||||
post[f"tags_{key}" if key else "tags_general"] = value
|
||||
post["tags_" + key if key else "tags_general"] = value
|
||||
|
||||
return post
|
||||
|
||||
def _pagination(self, endpoint):
|
||||
base = f"{self.root}{endpoint}"
|
||||
base = self.root + endpoint
|
||||
pnum = self.page_start
|
||||
|
||||
while True:
|
||||
|
||||
@@ -30,7 +30,7 @@ class TsuminoBase():
|
||||
@cache(maxage=14*86400, keyarg=1)
|
||||
def _login_impl(self, username, password):
|
||||
self.log.info("Logging in as %s", username)
|
||||
url = f"{self.root}/Account/Login"
|
||||
url = self.root + "/Account/Login"
|
||||
headers = {"Referer": url}
|
||||
data = {"Username": username, "Password": password}
|
||||
|
||||
@@ -119,9 +119,9 @@ class TsuminoSearchExtractor(TsuminoBase, Extractor):
|
||||
|
||||
def galleries(self):
|
||||
"""Return all gallery results matching 'self.query'"""
|
||||
url = f"{self.root}/Search/Operate?type=Book"
|
||||
url = self.root + "/Search/Operate?type=Book"
|
||||
headers = {
|
||||
"Referer": f"{self.root}/",
|
||||
"Referer": self.root + "/",
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
}
|
||||
data = {
|
||||
|
||||
@@ -32,7 +32,7 @@ class TumblrExtractor(Extractor):
|
||||
|
||||
def _init(self):
|
||||
if name := self.groups[1]:
|
||||
self.blog = f"{name}.tumblr.com"
|
||||
self.blog = name + ".tumblr.com"
|
||||
else:
|
||||
self.blog = self.groups[0] or self.groups[2]
|
||||
|
||||
|
||||
@@ -730,19 +730,19 @@ class TwitterUserExtractor(Dispatch, TwitterExtractor):
|
||||
def items(self):
|
||||
user, user_id = self.groups
|
||||
if user_id is not None:
|
||||
user = f"id:{user_id}"
|
||||
user = "id:" + user_id
|
||||
|
||||
base = f"{self.root}/{user}/"
|
||||
return self._dispatch_extractors((
|
||||
(TwitterInfoExtractor , f"{base}info"),
|
||||
(TwitterAvatarExtractor , f"{base}photo"),
|
||||
(TwitterBackgroundExtractor, f"{base}header_photo"),
|
||||
(TwitterTimelineExtractor , f"{base}timeline"),
|
||||
(TwitterTweetsExtractor , f"{base}tweets"),
|
||||
(TwitterMediaExtractor , f"{base}media"),
|
||||
(TwitterRepliesExtractor , f"{base}with_replies"),
|
||||
(TwitterHighlightsExtractor, f"{base}highlights"),
|
||||
(TwitterLikesExtractor , f"{base}likes"),
|
||||
(TwitterInfoExtractor , base + "info"),
|
||||
(TwitterAvatarExtractor , base + "photo"),
|
||||
(TwitterBackgroundExtractor, base + "header_photo"),
|
||||
(TwitterTimelineExtractor , base + "timeline"),
|
||||
(TwitterTweetsExtractor , base + "tweets"),
|
||||
(TwitterMediaExtractor , base + "media"),
|
||||
(TwitterRepliesExtractor , base + "with_replies"),
|
||||
(TwitterHighlightsExtractor, base + "highlights"),
|
||||
(TwitterLikesExtractor , base + "likes"),
|
||||
), ("timeline",))
|
||||
|
||||
|
||||
@@ -1990,10 +1990,10 @@ class TwitterAPI():
|
||||
extr.log.info("Retrying API request as guest")
|
||||
continue
|
||||
raise exception.AuthorizationError(
|
||||
f"{user['screen_name']} blocked your account")
|
||||
user["screen_name"] + " blocked your account")
|
||||
elif user.get("protected"):
|
||||
raise exception.AuthorizationError(
|
||||
f"{user['screen_name']}'s Tweets are protected")
|
||||
user["screen_name"] + "'s Tweets are protected")
|
||||
|
||||
raise exception.AbortExtraction(
|
||||
"Unable to retrieve Tweets from this timeline")
|
||||
@@ -2042,7 +2042,7 @@ class TwitterAPI():
|
||||
pinned = None
|
||||
elif pinned := extr._user_obj["legacy"].get(
|
||||
"pinned_tweet_ids_str"):
|
||||
pinned = f"-tweet-{pinned[0]}"
|
||||
pinned = "-tweet-" + pinned[0]
|
||||
for idx, entry in enumerate(tweets):
|
||||
if entry["entryId"].endswith(pinned):
|
||||
# mark as pinned / set 'pinned = True'
|
||||
@@ -2248,7 +2248,7 @@ class TwitterAPI():
|
||||
def _update_variables_search(self, variables, cursor, tweet):
|
||||
try:
|
||||
tweet_id = tweet.get("id_str") or tweet["legacy"]["id_str"]
|
||||
max_id = f"max_id:{int(tweet_id)-1}"
|
||||
max_id = "max_id:" + str(int(tweet_id)-1)
|
||||
|
||||
query, n = text.re(r"\bmax_id:\d+").subn(
|
||||
max_id, variables["rawQuery"])
|
||||
|
||||
@@ -94,7 +94,7 @@ class VipergirlsExtractor(Extractor):
|
||||
def _login_impl(self, username, password):
|
||||
self.log.info("Logging in as %s", username)
|
||||
|
||||
url = f"{self.root}/login.php?do=login"
|
||||
url = self.root + "/login.php?do=login"
|
||||
data = {
|
||||
"vb_login_username": username,
|
||||
"vb_login_password": password,
|
||||
|
||||
@@ -101,7 +101,7 @@ class VkExtractor(Extractor):
|
||||
url, method="POST", headers=headers, data=data)
|
||||
if response.history and "/challenge.html" in response.url:
|
||||
raise exception.AbortExtraction(
|
||||
f"HTTP redirect to 'challenge' page:\n{response.url}")
|
||||
"HTTP redirect to 'challenge' page:\n" + response.url)
|
||||
|
||||
payload = response.json()["payload"][1]
|
||||
if len(payload) < 4:
|
||||
@@ -236,7 +236,7 @@ class VkTaggedExtractor(VkExtractor):
|
||||
self.user_id = match[1]
|
||||
|
||||
def photos(self):
|
||||
return self._pagination(f"tag{self.user_id}")
|
||||
return self._pagination("tag" + self.user_id)
|
||||
|
||||
def metadata(self):
|
||||
return {"user": {"id": self.user_id}}
|
||||
|
||||
@@ -158,7 +158,7 @@ class VscoGalleryExtractor(VscoExtractor):
|
||||
tkn = data["users"]["currentUser"]["tkn"]
|
||||
sid = str(data["sites"]["siteByUsername"][self.user]["site"]["id"])
|
||||
|
||||
url = f"{self.root}/api/3.0/medias/profile"
|
||||
url = self.root + "/api/3.0/medias/profile"
|
||||
params = {
|
||||
"site_id" : sid,
|
||||
"limit" : "14",
|
||||
|
||||
@@ -113,7 +113,7 @@ class WallhavenCollectionsExtractor(WallhavenExtractor):
|
||||
base = f"{self.root}/user/{self.username}/favorites/"
|
||||
for collection in self.api.collections(self.username):
|
||||
collection["_extractor"] = WallhavenCollectionExtractor
|
||||
url = f"{base}{collection['id']}"
|
||||
url = base + str(collection["id"])
|
||||
yield Message.Queue, url, collection
|
||||
|
||||
|
||||
|
||||
@@ -114,13 +114,13 @@ class WeiboExtractor(Extractor):
|
||||
if not url:
|
||||
continue
|
||||
if url.startswith("http:"):
|
||||
url = f"https:{url[5:]}"
|
||||
url = "https:" + url[5:]
|
||||
if "filename" not in file:
|
||||
text.nameext_from_url(url, file)
|
||||
if file["extension"] == "json":
|
||||
file["extension"] = "mp4"
|
||||
if file["extension"] == "m3u8":
|
||||
url = f"ytdl:{url}"
|
||||
url = "ytdl:" + url
|
||||
file["_ytdl_manifest"] = "hls"
|
||||
file["extension"] = "mp4"
|
||||
num += 1
|
||||
@@ -307,11 +307,11 @@ class WeiboUserExtractor(WeiboExtractor):
|
||||
def items(self):
|
||||
base = f"{self.root}/u/{self._user_id()}?tabtype="
|
||||
return Dispatch._dispatch_extractors(self, (
|
||||
(WeiboHomeExtractor , f"{base}home"),
|
||||
(WeiboFeedExtractor , f"{base}feed"),
|
||||
(WeiboVideosExtractor , f"{base}video"),
|
||||
(WeiboNewvideoExtractor, f"{base}newVideo"),
|
||||
(WeiboAlbumExtractor , f"{base}album"),
|
||||
(WeiboHomeExtractor , base + "home"),
|
||||
(WeiboFeedExtractor , base + "feed"),
|
||||
(WeiboVideosExtractor , base + "video"),
|
||||
(WeiboNewvideoExtractor, base + "newVideo"),
|
||||
(WeiboAlbumExtractor , base + "album"),
|
||||
), ("feed",))
|
||||
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class WikimediaExtractor(BaseExtractor):
|
||||
def _init(self):
|
||||
if api_path := self.config_instance("api-path"):
|
||||
if api_path[0] == "/":
|
||||
self.api_url = f"{self.root}{api_path}"
|
||||
self.api_url = self.root + api_path
|
||||
else:
|
||||
self.api_url = api_path
|
||||
else:
|
||||
@@ -66,7 +66,7 @@ class WikimediaExtractor(BaseExtractor):
|
||||
def _search_api_path(self, root):
|
||||
self.log.debug("Probing possible API endpoints")
|
||||
for path in ("/api.php", "/w/api.php", "/wiki/api.php"):
|
||||
url = f"{root}{path}"
|
||||
url = root + path
|
||||
response = self.request(url, method="HEAD", fatal=None)
|
||||
if response.status_code < 400:
|
||||
return url
|
||||
@@ -122,10 +122,10 @@ class WikimediaExtractor(BaseExtractor):
|
||||
yield Message.Url, image["url"], image
|
||||
|
||||
if self.subcategories:
|
||||
base = f"{self.root}/wiki/"
|
||||
base = self.root + "/wiki/"
|
||||
params["gcmtype"] = "subcat"
|
||||
for subcat in self._pagination(params):
|
||||
url = f"{base}{subcat['title'].replace(' ', '_')}"
|
||||
url = base + subcat["title"].replace(" ", "_")
|
||||
subcat["_extractor"] = WikimediaArticleExtractor
|
||||
yield Message.Queue, url, subcat
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ class XenforoExtractor(BaseExtractor):
|
||||
def _login_impl(self, username, password):
|
||||
self.log.info("Logging in as %s", username)
|
||||
|
||||
url = f"{self.root}/login/login"
|
||||
url = self.root + "/login/login"
|
||||
page = self.request(url).text
|
||||
data = {
|
||||
"_xfToken": text.extr(page, 'name="_xfToken" value="', '"'),
|
||||
@@ -140,10 +140,10 @@ class XenforoExtractor(BaseExtractor):
|
||||
}
|
||||
|
||||
def _pagination(self, base, pnum=None):
|
||||
base = f"{self.root}{base}"
|
||||
base = self.root + base
|
||||
|
||||
if pnum is None:
|
||||
url = f"{base}/"
|
||||
url = base + "/"
|
||||
pnum = 1
|
||||
else:
|
||||
url = f"{base}/page-{pnum}"
|
||||
@@ -160,7 +160,7 @@ class XenforoExtractor(BaseExtractor):
|
||||
url = f"{base}/page-{pnum}"
|
||||
|
||||
def _pagination_reverse(self, base, pnum=None):
|
||||
base = f"{self.root}{base}"
|
||||
base = self.root + base
|
||||
|
||||
url = f"{base}/page-{'9999' if pnum is None else pnum}"
|
||||
with self.request_page(url) as response:
|
||||
@@ -180,7 +180,7 @@ class XenforoExtractor(BaseExtractor):
|
||||
if pnum > 1:
|
||||
url = f"{base}/page-{pnum}"
|
||||
elif pnum == 1:
|
||||
url = f"{base}/"
|
||||
url = base + "/"
|
||||
else:
|
||||
return
|
||||
|
||||
@@ -345,4 +345,4 @@ class XenforoForumExtractor(XenforoExtractor):
|
||||
pnum = self.groups[-1]
|
||||
for page in self._pagination(path, pnum):
|
||||
for path in extract_threads(page):
|
||||
yield Message.Queue, f"{self.root}{text.unquote(path)}", data
|
||||
yield Message.Queue, self.root + text.unquote(path), data
|
||||
|
||||
@@ -117,5 +117,5 @@ class XvideosUserExtractor(XvideosBase, Extractor):
|
||||
|
||||
base = f"{self.root}/profiles/{self.user}/photos/"
|
||||
for gallery in galleries:
|
||||
url = f"{base}{gallery['id']}"
|
||||
url = base + str(gallery["id"])
|
||||
yield Message.Queue, url, gallery
|
||||
|
||||
Reference in New Issue
Block a user