Merge branch 'op+': use '+' for 2-element string concatenations

This commit is contained in:
Mike Fährmann
2025-12-22 11:34:21 +01:00
177 changed files with 981 additions and 982 deletions

View File

@@ -20,12 +20,12 @@ class _2chThreadExtractor(Extractor):
directory_fmt = ("{category}", "{board}", "{thread} {title}")
filename_fmt = "{tim}{filename:? //}.{extension}"
archive_fmt = "{board}_{thread}_{tim}"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/res/(\d+)"
pattern = BASE_PATTERN + r"/([^/?#]+)/res/(\d+)"
example = "https://2ch.org/a/res/12345.html"
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):
@@ -66,19 +66,19 @@ class _2chBoardExtractor(Extractor):
category = "2ch"
subcategory = "board"
root = "https://2ch.org"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/?$"
pattern = BASE_PATTERN + r"/([^/?#]+)/?$"
example = "https://2ch.org/a/"
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"]:

View File

@@ -34,7 +34,7 @@ class _2chenThreadExtractor(_2chenExtractor):
directory_fmt = ("{category}", "{board}", "{thread} {title}")
filename_fmt = "{time} {filename}.{extension}"
archive_fmt = "{board}_{thread}_{no}_{time}"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/(\d+)"
pattern = BASE_PATTERN + r"/([^/?#]+)/(\d+)"
example = "https://sturdychan.help/a/12345/"
def items(self):
@@ -84,7 +84,7 @@ class _2chenThreadExtractor(_2chenExtractor):
class _2chenBoardExtractor(_2chenExtractor):
"""Extractor for 2chen boards"""
subcategory = "board"
pattern = rf"{BASE_PATTERN}/([^/?#]+)(?:/catalog|/?$)"
pattern = BASE_PATTERN + r"/([^/?#]+)(?:/catalog|/?$)"
example = "https://sturdychan.help/a/"
def items(self):

View File

@@ -92,7 +92,7 @@ class _500pxExtractor(Extractor):
class _500pxUserExtractor(_500pxExtractor):
"""Extractor for photos from a user's photostream on 500px.com"""
subcategory = "user"
pattern = rf"{BASE_PATTERN}/(?!photo/|liked)(?:p/)?([^/?#]+)/?(?:$|[?#])"
pattern = BASE_PATTERN + r"/(?!photo/|liked)(?:p/)?([^/?#]+)/?(?:$|[?#])"
example = "https://500px.com/USER"
def __init__(self, match):
@@ -121,8 +121,8 @@ class _500pxGalleryExtractor(_500pxExtractor):
"""Extractor for photo galleries on 500px.com"""
subcategory = "gallery"
directory_fmt = ("{category}", "{user[username]}", "{gallery[name]}")
pattern = (rf"{BASE_PATTERN}/(?!photo/)(?:p/)?"
rf"([^/?#]+)/galleries/([^/?#]+)")
pattern = (BASE_PATTERN + r"/(?!photo/)(?:p/)?"
r"([^/?#]+)/galleries/([^/?#]+)")
example = "https://500px.com/USER/galleries/GALLERY"
def __init__(self, match):
@@ -178,7 +178,7 @@ class _500pxGalleryExtractor(_500pxExtractor):
class _500pxFavoriteExtractor(_500pxExtractor):
"""Extractor for favorite 500px photos"""
subcategory = "favorite"
pattern = rf"{BASE_PATTERN}/liked/?$"
pattern = BASE_PATTERN + r"/liked/?$"
example = "https://500px.com/liked"
def photos(self):
@@ -202,7 +202,7 @@ class _500pxFavoriteExtractor(_500pxExtractor):
class _500pxImageExtractor(_500pxExtractor):
"""Extractor for individual images from 500px.com"""
subcategory = "image"
pattern = rf"{BASE_PATTERN}/photo/(\d+)"
pattern = BASE_PATTERN + r"/photo/(\d+)"
example = "https://500px.com/photo/12345/TITLE"
def __init__(self, match):

View File

@@ -69,7 +69,7 @@ class _8chanThreadExtractor(_8chanExtractor):
"{threadId} {subject[:50]}")
filename_fmt = "{postId}{num:?-//} {filename[:200]}.{extension}"
archive_fmt = "{boardUri}_{postId}_{num}"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/(?:res|last)/(\d+)"
pattern = BASE_PATTERN + r"/([^/?#]+)/(?:res|last)/(\d+)"
example = "https://8chan.moe/a/res/12345.html"
def items(self):
@@ -107,7 +107,7 @@ class _8chanThreadExtractor(_8chanExtractor):
class _8chanBoardExtractor(_8chanExtractor):
"""Extractor for 8chan boards"""
subcategory = "board"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/(?:(\d+)\.html)?$"
pattern = BASE_PATTERN + r"/([^/?#]+)/(?:(\d+)\.html)?$"
example = "https://8chan.moe/a/"
def items(self):

View File

@@ -81,7 +81,7 @@ class AgnphTagExtractor(AgnphExtractor):
subcategory = "tag"
directory_fmt = ("{category}", "{search_tags}")
archive_fmt = "t_{search_tags}_{id}"
pattern = rf"{BASE_PATTERN}/gallery/post/(?:\?([^#]+))?$"
pattern = BASE_PATTERN + r"/gallery/post/(?:\?([^#]+))?$"
example = "https://agn.ph/gallery/post/?search=TAG"
def __init__(self, match):
@@ -99,7 +99,7 @@ class AgnphTagExtractor(AgnphExtractor):
class AgnphPostExtractor(AgnphExtractor):
subcategory = "post"
archive_fmt = "{id}"
pattern = rf"{BASE_PATTERN}/gallery/post/show/(\d+)"
pattern = BASE_PATTERN + r"/gallery/post/show/(\d+)"
example = "https://agn.ph/gallery/post/show/12345/"
def posts(self):

View File

@@ -118,7 +118,7 @@ class Ao3WorkExtractor(Ao3Extractor):
directory_fmt = ("{category}", "{author}")
filename_fmt = "{id} {title}.{extension}"
archive_fmt = "{id}.{extension}"
pattern = rf"{BASE_PATTERN}/works/(\d+)"
pattern = BASE_PATTERN + r"/works/(\d+)"
example = "https://archiveofourown.org/works/12345"
def _init(self):
@@ -233,28 +233,28 @@ class Ao3WorkExtractor(Ao3Extractor):
class Ao3SeriesExtractor(Ao3Extractor):
"""Extractor for AO3 works of a series"""
subcategory = "series"
pattern = rf"{BASE_PATTERN}(/series/(\d+))"
pattern = BASE_PATTERN + r"(/series/(\d+))"
example = "https://archiveofourown.org/series/12345"
class Ao3TagExtractor(Ao3Extractor):
"""Extractor for AO3 works by tag"""
subcategory = "tag"
pattern = rf"{BASE_PATTERN}(/tags/([^/?#]+)/works(?:/?\?.+)?)"
pattern = BASE_PATTERN + r"(/tags/([^/?#]+)/works(?:/?\?.+)?)"
example = "https://archiveofourown.org/tags/TAG/works"
class Ao3SearchExtractor(Ao3Extractor):
"""Extractor for AO3 search results"""
subcategory = "search"
pattern = rf"{BASE_PATTERN}(/works/search/?\?.+)"
pattern = BASE_PATTERN + r"(/works/search/?\?.+)"
example = "https://archiveofourown.org/works/search?work_search[query]=air"
class Ao3UserExtractor(Dispatch, Ao3Extractor):
"""Extractor for an AO3 user profile"""
pattern = (rf"{BASE_PATTERN}/users/([^/?#]+(?:/pseuds/[^/?#]+)?)"
rf"(?:/profile)?/?(?:$|\?|#)")
pattern = (BASE_PATTERN + r"/users/([^/?#]+(?:/pseuds/[^/?#]+)?)"
r"(?:/profile)?/?(?:$|\?|#)")
example = "https://archiveofourown.org/users/USER"
def items(self):
@@ -269,16 +269,16 @@ class Ao3UserExtractor(Dispatch, Ao3Extractor):
class Ao3UserWorksExtractor(Ao3Extractor):
"""Extractor for works of an AO3 user"""
subcategory = "user-works"
pattern = (rf"{BASE_PATTERN}(/users/([^/?#]+)/(?:pseuds/([^/?#]+)/)?"
rf"works(?:/?\?.+)?)")
pattern = (BASE_PATTERN + r"(/users/([^/?#]+)/(?:pseuds/([^/?#]+)/)?"
r"works(?:/?\?.+)?)")
example = "https://archiveofourown.org/users/USER/works"
class Ao3UserSeriesExtractor(Ao3Extractor):
"""Extractor for series of an AO3 user"""
subcategory = "user-series"
pattern = (rf"{BASE_PATTERN}(/users/([^/?#]+)/(?:pseuds/([^/?#]+)/)?"
rf"series(?:/?\?.+)?)")
pattern = (BASE_PATTERN + r"(/users/([^/?#]+)/(?:pseuds/([^/?#]+)/)?"
r"series(?:/?\?.+)?)")
example = "https://archiveofourown.org/users/USER/series"
def items(self):
@@ -297,8 +297,8 @@ class Ao3UserSeriesExtractor(Ao3Extractor):
class Ao3UserBookmarkExtractor(Ao3Extractor):
"""Extractor for bookmarked works of an AO3 user"""
subcategory = "user-bookmark"
pattern = (rf"{BASE_PATTERN}(/users/([^/?#]+)/(?:pseuds/([^/?#]+)/)?"
rf"bookmarks(?:/?\?.+)?)")
pattern = (BASE_PATTERN + r"(/users/([^/?#]+)/(?:pseuds/([^/?#]+)/)?"
r"bookmarks(?:/?\?.+)?)")
example = "https://archiveofourown.org/users/USER/bookmarks"
def items(self):
@@ -308,7 +308,7 @@ class Ao3UserBookmarkExtractor(Ao3Extractor):
class Ao3SubscriptionsExtractor(Ao3Extractor):
"""Extractor for your AO3 account's subscriptions"""
subcategory = "subscriptions"
pattern = rf"{BASE_PATTERN}(/users/([^/?#]+)/subscriptions(?:/?\?.+)?)"
pattern = BASE_PATTERN + r"(/users/([^/?#]+)/subscriptions(?:/?\?.+)?)"
example = "https://archiveofourown.org/users/USER/subscriptions"
def items(self):

View File

@@ -36,7 +36,7 @@ class ArcalivePostExtractor(ArcaliveExtractor):
directory_fmt = ("{category}", "{boardSlug}")
filename_fmt = "{id}_{num}{title:? //[b:230]}.{extension}"
archive_fmt = "{id}_{num}"
pattern = rf"{BASE_PATTERN}/b/(?:\w+)/(\d+)"
pattern = BASE_PATTERN + r"/b/(?:\w+)/(\d+)"
example = "https://arca.live/b/breaking/123456789"
def items(self):
@@ -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:
@@ -115,7 +115,7 @@ class ArcalivePostExtractor(ArcaliveExtractor):
class ArcaliveBoardExtractor(ArcaliveExtractor):
"""Extractor for an arca.live board's posts"""
subcategory = "board"
pattern = rf"{BASE_PATTERN}/b/([^/?#]+)/?(?:\?([^#]+))?$"
pattern = BASE_PATTERN + r"/b/([^/?#]+)/?(?:\?([^#]+))?$"
example = "https://arca.live/b/breaking"
def articles(self):
@@ -127,7 +127,7 @@ class ArcaliveBoardExtractor(ArcaliveExtractor):
class ArcaliveUserExtractor(ArcaliveExtractor):
"""Extractor for an arca.live users's posts"""
subcategory = "user"
pattern = rf"{BASE_PATTERN}/u/@([^/?#]+)/?(?:\?([^#]+))?$"
pattern = BASE_PATTERN + r"/u/@([^/?#]+)/?(?:\?([^#]+))?$"
example = "https://arca.live/u/@USER"
def articles(self):
@@ -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:

View File

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

View File

@@ -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())

View File

@@ -211,7 +211,7 @@ class AryionGalleryExtractor(AryionExtractor):
"""Extractor for a user's gallery on eka's portal"""
subcategory = "gallery"
categorytransfer = True
pattern = rf"{BASE_PATTERN}/(?:gallery/|user/|latest.php\?name=)([^/?#]+)"
pattern = BASE_PATTERN + r"/(?:gallery/|user/|latest.php\?name=)([^/?#]+)"
example = "https://aryion.com/g4/gallery/USER"
def _init(self):
@@ -238,7 +238,7 @@ class AryionFavoriteExtractor(AryionExtractor):
subcategory = "favorite"
directory_fmt = ("{category}", "{user!l}", "favorites", "{folder}")
archive_fmt = "f_{user}_{id}"
pattern = rf"{BASE_PATTERN}/favorites/([^/?#]+)(?:/([^?#]+))?"
pattern = BASE_PATTERN + r"/favorites/([^/?#]+)(?:/([^?#]+))?"
example = "https://aryion.com/g4/favorites/USER"
def _init(self):
@@ -253,7 +253,7 @@ class AryionWatchExtractor(AryionExtractor):
"""Extractor for your watched users and tags"""
subcategory = "watch"
directory_fmt = ("{category}", "{user!l}",)
pattern = rf"{BASE_PATTERN}/messagepage\.php()"
pattern = BASE_PATTERN + r"/messagepage\.php()"
example = "https://aryion.com/g4/messagepage.php"
def posts(self):
@@ -271,7 +271,7 @@ class AryionTagExtractor(AryionExtractor):
subcategory = "tag"
directory_fmt = ("{category}", "tags", "{search_tags}")
archive_fmt = "t_{search_tags}_{id}"
pattern = rf"{BASE_PATTERN}/tags\.php\?([^#]+)"
pattern = BASE_PATTERN + r"/tags\.php\?([^#]+)"
example = "https://aryion.com/g4/tags.php?tag=TAG"
def _init(self):
@@ -293,7 +293,7 @@ class AryionSearchExtractor(AryionExtractor):
"{search[q]|search[tags]|search[user]}")
archive_fmt = ("s_{search[prefix]}"
"{search[q]|search[tags]|search[user]}_{id}")
pattern = rf"{BASE_PATTERN}/search\.php\?([^#]+)"
pattern = BASE_PATTERN + r"/search\.php\?([^#]+)"
example = "https://aryion.com/g4/search.php?q=TEXT&tags=TAGS&user=USER"
def metadata(self):
@@ -313,7 +313,7 @@ class AryionSearchExtractor(AryionExtractor):
class AryionPostExtractor(AryionExtractor):
"""Extractor for individual posts on eka's portal"""
subcategory = "post"
pattern = rf"{BASE_PATTERN}/view/(\d+)"
pattern = BASE_PATTERN + r"/view/(\d+)"
example = "https://aryion.com/g4/view/12345"
def posts(self):

View File

@@ -103,7 +103,7 @@ class AudiochanExtractor(Extractor):
class AudiochanAudioExtractor(AudiochanExtractor):
subcategory = "audio"
pattern = rf"{BASE_PATTERN}/a/([^/?#]+)"
pattern = BASE_PATTERN + r"/a/([^/?#]+)"
example = "https://audiochan.com/a/SLUG"
def posts(self):
@@ -114,7 +114,7 @@ class AudiochanAudioExtractor(AudiochanExtractor):
class AudiochanUserExtractor(AudiochanExtractor):
subcategory = "user"
pattern = rf"{BASE_PATTERN}/u/([^/?#]+)"
pattern = BASE_PATTERN + r"/u/([^/?#]+)"
example = "https://audiochan.com/u/USER"
def posts(self):
@@ -130,7 +130,7 @@ class AudiochanUserExtractor(AudiochanExtractor):
class AudiochanCollectionExtractor(AudiochanExtractor):
subcategory = "collection"
pattern = rf"{BASE_PATTERN}/c/([^/?#]+)"
pattern = BASE_PATTERN + r"/c/([^/?#]+)"
example = "https://audiochan.com/c/SLUG"
def posts(self):
@@ -146,7 +146,7 @@ class AudiochanCollectionExtractor(AudiochanExtractor):
class AudiochanSearchExtractor(AudiochanExtractor):
subcategory = "search"
pattern = rf"{BASE_PATTERN}/search/?\?([^#]+)"
pattern = BASE_PATTERN + r"/search/?\?([^#]+)"
example = "https://audiochan.com/search?q=QUERY"
def posts(self):

View File

@@ -80,7 +80,7 @@ class BatotoBase():
class BatotoChapterExtractor(BatotoBase, ChapterExtractor):
"""Extractor for batoto manga chapters"""
archive_fmt = "{chapter_id}_{page}"
pattern = rf"{BASE_PATTERN}/(?:title/[^/?#]+|chapter)/(\d+)"
pattern = BASE_PATTERN + r"/(?:title/[^/?#]+|chapter)/(\d+)"
example = "https://xbato.org/title/12345-MANGA/54321"
def __init__(self, match):
@@ -139,8 +139,8 @@ class BatotoMangaExtractor(BatotoBase, MangaExtractor):
"""Extractor for batoto manga"""
reverse = False
chapterclass = BatotoChapterExtractor
pattern = (rf"{BASE_PATTERN}"
rf"/(?:title/(\d+)[^/?#]*|series/(\d+)(?:/[^/?#]*)?)/?$")
pattern = (BASE_PATTERN +
r"/(?:title/(\d+)[^/?#]*|series/(\d+)(?:/[^/?#]*)?)/?$")
example = "https://xbato.org/title/12345-MANGA/"
def __init__(self, match):

View File

@@ -21,7 +21,7 @@ class BbcGalleryExtractor(GalleryExtractor):
directory_fmt = ("{category}", "{path:I}")
filename_fmt = "{num:>02}.{extension}"
archive_fmt = "{programme}_{num}"
pattern = rf"{BASE_PATTERN}[^/?#]+(?!/galleries)(?:/[^/?#]+)?)$"
pattern = BASE_PATTERN + r"[^/?#]+(?!/galleries)(?:/[^/?#]+)?)$"
example = "https://www.bbc.co.uk/programmes/PATH"
def metadata(self, page):
@@ -71,7 +71,7 @@ class BbcProgrammeExtractor(Extractor):
category = "bbc"
subcategory = "programme"
root = "https://www.bbc.co.uk"
pattern = rf"{BASE_PATTERN}[^/?#]+/galleries)(?:/?\?page=(\d+))?"
pattern = BASE_PATTERN + r"[^/?#]+/galleries)(?:/?\?page=(\d+))?"
example = "https://www.bbc.co.uk/programmes/ID/galleries"
def items(self):

View File

@@ -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
@@ -192,15 +192,15 @@ class BellazonExtractor(Extractor):
class BellazonPostExtractor(BellazonExtractor):
subcategory = "post"
pattern = (rf"{BASE_PATTERN}(/topic/\d+-[\w-]+(?:/page/\d+)?)"
rf"/?#(?:findC|c)omment-(\d+)")
pattern = (BASE_PATTERN + r"(/topic/\d+-[\w-]+(?:/page/\d+)?)"
r"/?#(?:findC|c)omment-(\d+)")
example = "https://www.bellazon.com/main/topic/123-SLUG/#findComment-12345"
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]
@@ -211,7 +211,7 @@ class BellazonPostExtractor(BellazonExtractor):
class BellazonThreadExtractor(BellazonExtractor):
subcategory = "thread"
pattern = rf"{BASE_PATTERN}(/topic/\d+-[\w-]+)(?:/page/(\d+))?"
pattern = BASE_PATTERN + r"(/topic/\d+-[\w-]+)(?:/page/(\d+))?"
example = "https://www.bellazon.com/main/topic/123-SLUG/"
def posts(self):
@@ -236,7 +236,7 @@ class BellazonThreadExtractor(BellazonExtractor):
class BellazonForumExtractor(BellazonExtractor):
subcategory = "forum"
pattern = rf"{BASE_PATTERN}(/forum/\d+-[\w-]+)(?:/page/(\d+))?"
pattern = BASE_PATTERN + r"(/forum/\d+-[\w-]+)(?:/page/(\d+))?"
example = "https://www.bellazon.com/main/forum/123-SLUG/"
def items(self):

View File

@@ -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):

View File

@@ -117,7 +117,7 @@ BASE_PATTERN = BloggerExtractor.update({
class BloggerPostExtractor(BloggerExtractor):
"""Extractor for a single blog post"""
subcategory = "post"
pattern = rf"{BASE_PATTERN}(/\d\d\d\d/\d\d/[^/?#]+\.html)"
pattern = BASE_PATTERN + r"(/\d\d\d\d/\d\d/[^/?#]+\.html)"
example = "https://BLOG.blogspot.com/1970/01/TITLE.html"
def posts(self, blog):
@@ -127,7 +127,7 @@ class BloggerPostExtractor(BloggerExtractor):
class BloggerBlogExtractor(BloggerExtractor):
"""Extractor for an entire Blogger blog"""
subcategory = "blog"
pattern = rf"{BASE_PATTERN}/?$"
pattern = BASE_PATTERN + r"/?$"
example = "https://BLOG.blogspot.com/"
def posts(self, blog):
@@ -137,7 +137,7 @@ class BloggerBlogExtractor(BloggerExtractor):
class BloggerSearchExtractor(BloggerExtractor):
"""Extractor for Blogger search resuls"""
subcategory = "search"
pattern = rf"{BASE_PATTERN}/search/?\?q=([^&#]+)"
pattern = BASE_PATTERN + r"/search/?\?q=([^&#]+)"
example = "https://BLOG.blogspot.com/search?q=QUERY"
def metadata(self):
@@ -151,7 +151,7 @@ class BloggerSearchExtractor(BloggerExtractor):
class BloggerLabelExtractor(BloggerExtractor):
"""Extractor for Blogger posts by label"""
subcategory = "label"
pattern = rf"{BASE_PATTERN}/search/label/([^/?#]+)"
pattern = BASE_PATTERN + r"/search/label/([^/?#]+)"
example = "https://BLOG.blogspot.com/search/label/LABEL"
def metadata(self):

View File

@@ -14,7 +14,7 @@ from ..cache import cache, memcache
BASE_PATTERN = (r"(?:https?://)?"
r"(?:(?:www\.)?(?:c|[fv]x)?bs[ky]y[ex]?\.app|main\.bsky\.dev)")
USER_PATTERN = rf"{BASE_PATTERN}/profile/([^/?#]+)"
USER_PATTERN = BASE_PATTERN + r"/profile/([^/?#]+)"
class BlueskyExtractor(Extractor):
@@ -216,7 +216,7 @@ class BlueskyExtractor(Extractor):
class BlueskyUserExtractor(Dispatch, BlueskyExtractor):
pattern = rf"{USER_PATTERN}$"
pattern = USER_PATTERN + r"$"
example = "https://bsky.app/profile/HANDLE"
def items(self):
@@ -237,7 +237,7 @@ class BlueskyUserExtractor(Dispatch, BlueskyExtractor):
class BlueskyPostsExtractor(BlueskyExtractor):
subcategory = "posts"
pattern = rf"{USER_PATTERN}/posts"
pattern = USER_PATTERN + r"/posts"
example = "https://bsky.app/profile/HANDLE/posts"
def posts(self):
@@ -247,7 +247,7 @@ class BlueskyPostsExtractor(BlueskyExtractor):
class BlueskyRepliesExtractor(BlueskyExtractor):
subcategory = "replies"
pattern = rf"{USER_PATTERN}/replies"
pattern = USER_PATTERN + r"/replies"
example = "https://bsky.app/profile/HANDLE/replies"
def posts(self):
@@ -257,7 +257,7 @@ class BlueskyRepliesExtractor(BlueskyExtractor):
class BlueskyMediaExtractor(BlueskyExtractor):
subcategory = "media"
pattern = rf"{USER_PATTERN}/media"
pattern = USER_PATTERN + r"/media"
example = "https://bsky.app/profile/HANDLE/media"
def posts(self):
@@ -267,7 +267,7 @@ class BlueskyMediaExtractor(BlueskyExtractor):
class BlueskyVideoExtractor(BlueskyExtractor):
subcategory = "video"
pattern = rf"{USER_PATTERN}/video"
pattern = USER_PATTERN + r"/video"
example = "https://bsky.app/profile/HANDLE/video"
def posts(self):
@@ -277,7 +277,7 @@ class BlueskyVideoExtractor(BlueskyExtractor):
class BlueskyLikesExtractor(BlueskyExtractor):
subcategory = "likes"
pattern = rf"{USER_PATTERN}/likes"
pattern = USER_PATTERN + r"/likes"
example = "https://bsky.app/profile/HANDLE/likes"
def posts(self):
@@ -288,7 +288,7 @@ class BlueskyLikesExtractor(BlueskyExtractor):
class BlueskyFeedExtractor(BlueskyExtractor):
subcategory = "feed"
pattern = rf"{USER_PATTERN}/feed/([^/?#]+)"
pattern = USER_PATTERN + r"/feed/([^/?#]+)"
example = "https://bsky.app/profile/HANDLE/feed/NAME"
def posts(self):
@@ -298,7 +298,7 @@ class BlueskyFeedExtractor(BlueskyExtractor):
class BlueskyListExtractor(BlueskyExtractor):
subcategory = "list"
pattern = rf"{USER_PATTERN}/lists/([^/?#]+)"
pattern = USER_PATTERN + r"/lists/([^/?#]+)"
example = "https://bsky.app/profile/HANDLE/lists/ID"
def posts(self):
@@ -308,7 +308,7 @@ class BlueskyListExtractor(BlueskyExtractor):
class BlueskyFollowingExtractor(BlueskyExtractor):
subcategory = "following"
pattern = rf"{USER_PATTERN}/follows"
pattern = USER_PATTERN + r"/follows"
example = "https://bsky.app/profile/HANDLE/follows"
def items(self):
@@ -320,7 +320,7 @@ class BlueskyFollowingExtractor(BlueskyExtractor):
class BlueskyPostExtractor(BlueskyExtractor):
subcategory = "post"
pattern = rf"{USER_PATTERN}/post/([^/?#]+)"
pattern = USER_PATTERN + r"/post/([^/?#]+)"
example = "https://bsky.app/profile/HANDLE/post/ID"
def posts(self):
@@ -330,7 +330,7 @@ class BlueskyPostExtractor(BlueskyExtractor):
class BlueskyInfoExtractor(BlueskyExtractor):
subcategory = "info"
pattern = rf"{USER_PATTERN}/info"
pattern = USER_PATTERN + r"/info"
example = "https://bsky.app/profile/HANDLE/info"
def items(self):
@@ -342,7 +342,7 @@ class BlueskyInfoExtractor(BlueskyExtractor):
class BlueskyAvatarExtractor(BlueskyExtractor):
subcategory = "avatar"
filename_fmt = "avatar_{post_id}.{extension}"
pattern = rf"{USER_PATTERN}/avatar"
pattern = USER_PATTERN + r"/avatar"
example = "https://bsky.app/profile/HANDLE/avatar"
def posts(self):
@@ -352,7 +352,7 @@ class BlueskyAvatarExtractor(BlueskyExtractor):
class BlueskyBackgroundExtractor(BlueskyExtractor):
subcategory = "background"
filename_fmt = "background_{post_id}.{extension}"
pattern = rf"{USER_PATTERN}/ba(?:nner|ckground)"
pattern = USER_PATTERN + r"/ba(?:nner|ckground)"
example = "https://bsky.app/profile/HANDLE/banner"
def posts(self):
@@ -361,7 +361,7 @@ class BlueskyBackgroundExtractor(BlueskyExtractor):
class BlueskySearchExtractor(BlueskyExtractor):
subcategory = "search"
pattern = rf"{BASE_PATTERN}/search(?:/|\?q=)(.+)"
pattern = BASE_PATTERN + r"/search(?:/|\?q=)(.+)"
example = "https://bsky.app/search?q=QUERY"
def posts(self):
@@ -371,7 +371,7 @@ class BlueskySearchExtractor(BlueskyExtractor):
class BlueskyHashtagExtractor(BlueskyExtractor):
subcategory = "hashtag"
pattern = rf"{BASE_PATTERN}/hashtag/([^/?#]+)(?:/(top|latest))?"
pattern = BASE_PATTERN + r"/hashtag/([^/?#]+)(?:/(top|latest))?"
example = "https://bsky.app/hashtag/NAME"
def posts(self):
@@ -381,7 +381,7 @@ class BlueskyHashtagExtractor(BlueskyExtractor):
class BlueskyBookmarkExtractor(BlueskyExtractor):
subcategory = "bookmark"
pattern = rf"{BASE_PATTERN}/saved"
pattern = BASE_PATTERN + r"/saved"
example = "https://bsky.app/saved"
def posts(self):

View File

@@ -163,7 +163,7 @@ class BoostyExtractor(Extractor):
class BoostyUserExtractor(BoostyExtractor):
"""Extractor for boosty.to user profiles"""
subcategory = "user"
pattern = rf"{BASE_PATTERN}/([^/?#]+)(?:\?([^#]+))?$"
pattern = BASE_PATTERN + r"/([^/?#]+)(?:\?([^#]+))?$"
example = "https://boosty.to/USER"
def posts(self):
@@ -179,7 +179,7 @@ class BoostyMediaExtractor(BoostyExtractor):
subcategory = "media"
directory_fmt = "{category}", "{user[blogUrl]} ({user[id]})", "media"
filename_fmt = "{post[id]}_{num}.{extension}"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/media/([^/?#]+)(?:\?([^#]+))?"
pattern = BASE_PATTERN + r"/([^/?#]+)/media/([^/?#]+)(?:\?([^#]+))?"
example = "https://boosty.to/USER/media/all"
def posts(self):
@@ -192,7 +192,7 @@ class BoostyMediaExtractor(BoostyExtractor):
class BoostyFeedExtractor(BoostyExtractor):
"""Extractor for your boosty.to subscription feed"""
subcategory = "feed"
pattern = rf"{BASE_PATTERN}/(?:\?([^#]+))?(?:$|#)"
pattern = BASE_PATTERN + r"/(?:\?([^#]+))?(?:$|#)"
example = "https://boosty.to/"
def posts(self):
@@ -203,7 +203,7 @@ class BoostyFeedExtractor(BoostyExtractor):
class BoostyPostExtractor(BoostyExtractor):
"""Extractor for boosty.to posts"""
subcategory = "post"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/posts/([0-9a-f-]+)"
pattern = BASE_PATTERN + r"/([^/?#]+)/posts/([0-9a-f-]+)"
example = "https://boosty.to/USER/posts/01234567-89ab-cdef-0123-456789abcd"
def posts(self):
@@ -216,7 +216,7 @@ class BoostyPostExtractor(BoostyExtractor):
class BoostyFollowingExtractor(BoostyExtractor):
"""Extractor for your boosty.to subscribed users"""
subcategory = "following"
pattern = rf"{BASE_PATTERN}/app/settings/subscriptions"
pattern = BASE_PATTERN + r"/app/settings/subscriptions"
example = "https://boosty.to/app/settings/subscriptions"
def items(self):
@@ -231,7 +231,7 @@ class BoostyDirectMessagesExtractor(BoostyExtractor):
subcategory = "direct-messages"
directory_fmt = ("{category}", "{user[blogUrl]} ({user[id]})",
"Direct Messages")
pattern = rf"{BASE_PATTERN}/app/messages/?\?dialogId=(\d+)"
pattern = BASE_PATTERN + r"/app/messages/?\?dialogId=(\d+)"
example = "https://boosty.to/app/messages?dialogId=12345"
def items(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):

View File

@@ -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):

View File

@@ -64,7 +64,7 @@ class BunkrAlbumExtractor(LolisafeAlbumExtractor):
root_dl = "https://get.bunkrr.su"
root_api = "https://apidl.bunkr.ru"
archive_fmt = "{album_id}_{id|id_url|slug}"
pattern = rf"{BASE_PATTERN}/a/([^/?#]+)"
pattern = BASE_PATTERN + r"/a/([^/?#]+)"
example = "https://bunkr.si/a/ID"
def __init__(self, match):
@@ -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"]
@@ -216,12 +216,12 @@ class BunkrMediaExtractor(BunkrAlbumExtractor):
"""Extractor for bunkr.si media links"""
subcategory = "media"
directory_fmt = ("{category}",)
pattern = rf"{BASE_PATTERN}(/[fvid]/[^/?#]+)"
pattern = BASE_PATTERN + r"(/[fvid]/[^/?#]+)"
example = "https://bunkr.si/f/FILENAME"
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(

View File

@@ -60,7 +60,7 @@ BASE_PATTERN = CheveretoExtractor.update({
class CheveretoImageExtractor(CheveretoExtractor):
"""Extractor for chevereto images"""
subcategory = "image"
pattern = rf"{BASE_PATTERN}(/im(?:g|age)/[^/?#]+)"
pattern = BASE_PATTERN + r"(/im(?:g|age)/[^/?#]+)"
example = "https://jpg7.cr/img/TITLE.ID"
def items(self):
@@ -98,7 +98,7 @@ class CheveretoImageExtractor(CheveretoExtractor):
class CheveretoVideoExtractor(CheveretoExtractor):
"""Extractor for chevereto videos"""
subcategory = "video"
pattern = rf"{BASE_PATTERN}(/video/[^/?#]+)"
pattern = BASE_PATTERN + r"(/video/[^/?#]+)"
example = "https://imagepond.net/video/TITLE.ID"
def items(self):
@@ -145,7 +145,7 @@ class CheveretoVideoExtractor(CheveretoExtractor):
class CheveretoAlbumExtractor(CheveretoExtractor):
"""Extractor for chevereto albums"""
subcategory = "album"
pattern = rf"{BASE_PATTERN}(/a(?:lbum)?/[^/?#]+(?:/sub)?)"
pattern = BASE_PATTERN + r"(/a(?:lbum)?/[^/?#]+(?:/sub)?)"
example = "https://jpg7.cr/album/TITLE.ID"
def items(self):
@@ -182,7 +182,7 @@ class CheveretoAlbumExtractor(CheveretoExtractor):
class CheveretoCategoryExtractor(CheveretoExtractor):
"""Extractor for chevereto galleries"""
subcategory = "category"
pattern = rf"{BASE_PATTERN}(/category/[^/?#]+)"
pattern = BASE_PATTERN + r"(/category/[^/?#]+)"
example = "https://imglike.com/category/TITLE"
def items(self):
@@ -194,7 +194,7 @@ class CheveretoCategoryExtractor(CheveretoExtractor):
class CheveretoUserExtractor(CheveretoExtractor):
"""Extractor for chevereto users"""
subcategory = "user"
pattern = rf"{BASE_PATTERN}(/[^/?#]+(?:/albums)?)"
pattern = BASE_PATTERN + r"(/[^/?#]+(?:/albums)?)"
example = "https://jpg7.cr/USER"
def items(self):

View File

@@ -48,7 +48,7 @@ class CienArticleExtractor(CienExtractor):
filename_fmt = "{num:>02} {filename}.{extension}"
directory_fmt = ("{category}", "{author[name]}", "{post_id} {name}")
archive_fmt = "{post_id}_{num}"
pattern = rf"{BASE_PATTERN}/creator/(\d+)/article/(\d+)"
pattern = BASE_PATTERN + r"/creator/(\d+)/article/(\d+)"
example = "https://ci-en.net/creator/123/article/12345"
def items(self):
@@ -160,7 +160,7 @@ class CienArticleExtractor(CienExtractor):
class CienCreatorExtractor(CienExtractor):
subcategory = "creator"
pattern = rf"{BASE_PATTERN}/creator/(\d+)(?:/article(?:\?([^#]+))?)?/?$"
pattern = BASE_PATTERN + r"/creator/(\d+)(?:/article(?:\?([^#]+))?)?/?$"
example = "https://ci-en.net/creator/123"
def items(self):
@@ -172,7 +172,7 @@ class CienCreatorExtractor(CienExtractor):
class CienRecentExtractor(CienExtractor):
subcategory = "recent"
pattern = rf"{BASE_PATTERN}/mypage/recent(?:\?([^#]+))?"
pattern = BASE_PATTERN + r"/mypage/recent(?:\?([^#]+))?"
example = "https://ci-en.net/mypage/recent"
def items(self):
@@ -183,7 +183,7 @@ class CienRecentExtractor(CienExtractor):
class CienFollowingExtractor(CienExtractor):
subcategory = "following"
pattern = rf"{BASE_PATTERN}/mypage/subscription(/following)?"
pattern = BASE_PATTERN + r"/mypage/subscription(/following)?"
example = "https://ci-en.net/mypage/subscription"
def items(self):

View File

@@ -15,7 +15,7 @@ import itertools
import time
BASE_PATTERN = r"(?:https?://)?civitai\.com"
USER_PATTERN = rf"{BASE_PATTERN}/user/([^/?#]+)"
USER_PATTERN = BASE_PATTERN + r"/user/([^/?#]+)"
class CivitaiExtractor(Extractor):
@@ -258,7 +258,7 @@ class CivitaiModelExtractor(CivitaiExtractor):
directory_fmt = ("{category}", "{user[username]}",
"{model[id]}{model[name]:? //}",
"{version[id]}{version[name]:? //}")
pattern = rf"{BASE_PATTERN}/models/(\d+)(?:/?\?modelVersionId=(\d+))?"
pattern = BASE_PATTERN + r"/models/(\d+)(?:/?\?modelVersionId=(\d+))?"
example = "https://civitai.com/models/12345/TITLE"
def items(self):
@@ -375,7 +375,7 @@ class CivitaiModelExtractor(CivitaiExtractor):
class CivitaiImageExtractor(CivitaiExtractor):
subcategory = "image"
pattern = rf"{BASE_PATTERN}/images/(\d+)"
pattern = BASE_PATTERN + r"/images/(\d+)"
example = "https://civitai.com/images/12345"
def images(self):
@@ -386,7 +386,7 @@ class CivitaiCollectionExtractor(CivitaiExtractor):
subcategory = "collection"
directory_fmt = ("{category}", "{user_collection[username]}",
"collections", "{collection[id]}{collection[name]:? //}")
pattern = rf"{BASE_PATTERN}/collections/(\d+)"
pattern = BASE_PATTERN + r"/collections/(\d+)"
example = "https://civitai.com/collections/12345"
def images(self):
@@ -408,7 +408,7 @@ class CivitaiPostExtractor(CivitaiExtractor):
subcategory = "post"
directory_fmt = ("{category}", "{username|user[username]}", "posts",
"{post[id]}{post[title]:? //}")
pattern = rf"{BASE_PATTERN}/posts/(\d+)"
pattern = BASE_PATTERN + r"/posts/(\d+)"
example = "https://civitai.com/posts/12345"
def posts(self):
@@ -417,7 +417,7 @@ class CivitaiPostExtractor(CivitaiExtractor):
class CivitaiTagExtractor(CivitaiExtractor):
subcategory = "tag"
pattern = rf"{BASE_PATTERN}/tag/([^/?&#]+)"
pattern = BASE_PATTERN + r"/tag/([^/?&#]+)"
example = "https://civitai.com/tag/TAG"
def models(self):
@@ -427,7 +427,7 @@ class CivitaiTagExtractor(CivitaiExtractor):
class CivitaiSearchModelsExtractor(CivitaiExtractor):
subcategory = "search-models"
pattern = rf"{BASE_PATTERN}/search/models\?([^#]+)"
pattern = BASE_PATTERN + r"/search/models\?([^#]+)"
example = "https://civitai.com/search/models?query=QUERY"
def models(self):
@@ -438,7 +438,7 @@ class CivitaiSearchModelsExtractor(CivitaiExtractor):
class CivitaiSearchImagesExtractor(CivitaiExtractor):
subcategory = "search-images"
pattern = rf"{BASE_PATTERN}/search/images\?([^#]+)"
pattern = BASE_PATTERN + r"/search/images\?([^#]+)"
example = "https://civitai.com/search/images?query=QUERY"
def images(self):
@@ -449,7 +449,7 @@ class CivitaiSearchImagesExtractor(CivitaiExtractor):
class CivitaiModelsExtractor(CivitaiExtractor):
subcategory = "models"
pattern = rf"{BASE_PATTERN}/models(?:/?\?([^#]+))?(?:$|#)"
pattern = BASE_PATTERN + r"/models(?:/?\?([^#]+))?(?:$|#)"
example = "https://civitai.com/models"
def models(self):
@@ -459,7 +459,7 @@ class CivitaiModelsExtractor(CivitaiExtractor):
class CivitaiImagesExtractor(CivitaiExtractor):
subcategory = "images"
pattern = rf"{BASE_PATTERN}/images(?:/?\?([^#]+))?(?:$|#)"
pattern = BASE_PATTERN + r"/images(?:/?\?([^#]+))?(?:$|#)"
example = "https://civitai.com/images"
def images(self):
@@ -470,7 +470,7 @@ class CivitaiImagesExtractor(CivitaiExtractor):
class CivitaiVideosExtractor(CivitaiExtractor):
subcategory = "videos"
pattern = rf"{BASE_PATTERN}/videos(?:/?\?([^#]+))?(?:$|#)"
pattern = BASE_PATTERN + r"/videos(?:/?\?([^#]+))?(?:$|#)"
example = "https://civitai.com/videos"
def images(self):
@@ -481,7 +481,7 @@ class CivitaiVideosExtractor(CivitaiExtractor):
class CivitaiPostsExtractor(CivitaiExtractor):
subcategory = "posts"
pattern = rf"{BASE_PATTERN}/posts(?:/?\?([^#]+))?(?:$|#)"
pattern = BASE_PATTERN + r"/posts(?:/?\?([^#]+))?(?:$|#)"
example = "https://civitai.com/posts"
def posts(self):
@@ -490,7 +490,7 @@ class CivitaiPostsExtractor(CivitaiExtractor):
class CivitaiUserExtractor(Dispatch, CivitaiExtractor):
pattern = rf"{USER_PATTERN}/?(?:$|\?|#)"
pattern = USER_PATTERN + r"/?(?:$|\?|#)"
example = "https://civitai.com/user/USER"
def items(self):
@@ -506,7 +506,7 @@ class CivitaiUserExtractor(Dispatch, CivitaiExtractor):
class CivitaiUserModelsExtractor(CivitaiExtractor):
subcategory = "user-models"
pattern = rf"{USER_PATTERN}/models/?(?:\?([^#]+))?"
pattern = USER_PATTERN + r"/models/?(?:\?([^#]+))?"
example = "https://civitai.com/user/USER/models"
def models(self):
@@ -520,7 +520,7 @@ class CivitaiUserPostsExtractor(CivitaiExtractor):
subcategory = "user-posts"
directory_fmt = ("{category}", "{username|user[username]}", "posts",
"{post[id]}{post[title]:? //}")
pattern = rf"{USER_PATTERN}/posts/?(?:\?([^#]+))?"
pattern = USER_PATTERN + r"/posts/?(?:\?([^#]+))?"
example = "https://civitai.com/user/USER/posts"
def posts(self):
@@ -532,7 +532,7 @@ class CivitaiUserPostsExtractor(CivitaiExtractor):
class CivitaiUserImagesExtractor(CivitaiExtractor):
subcategory = "user-images"
pattern = rf"{USER_PATTERN}/images/?(?:\?([^#]+))?"
pattern = USER_PATTERN + r"/images/?(?:\?([^#]+))?"
example = "https://civitai.com/user/USER/images"
def __init__(self, match):
@@ -553,7 +553,7 @@ class CivitaiUserImagesExtractor(CivitaiExtractor):
class CivitaiUserVideosExtractor(CivitaiExtractor):
subcategory = "user-videos"
directory_fmt = ("{category}", "{username|user[username]}", "videos")
pattern = rf"{USER_PATTERN}/videos/?(?:\?([^#]+))?"
pattern = USER_PATTERN + r"/videos/?(?:\?([^#]+))?"
example = "https://civitai.com/user/USER/videos"
def __init__(self, match):
@@ -572,7 +572,7 @@ class CivitaiUserVideosExtractor(CivitaiExtractor):
class CivitaiUserCollectionsExtractor(CivitaiExtractor):
subcategory = "user-collections"
pattern = rf"{USER_PATTERN}/collections/?(?:\?([^#]+))?"
pattern = USER_PATTERN + r"/collections/?(?:\?([^#]+))?"
example = "https://civitai.com/user/USER/collections"
def items(self):
@@ -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 = rf"{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")

View File

@@ -27,7 +27,7 @@ class ComickCoversExtractor(ComickBase, GalleryExtractor):
directory_fmt = ("{category}", "{manga}", "Covers")
filename_fmt = "{volume:>02}_{lang}.{extension}"
archive_fmt = "c_{id}"
pattern = rf"{BASE_PATTERN}/comic/([\w-]+)/cover"
pattern = BASE_PATTERN + r"/comic/([\w-]+)/cover"
example = "https://comick.io/comic/MANGA/cover"
def metadata(self, page):
@@ -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"],
@@ -60,7 +60,7 @@ class ComickCoversExtractor(ComickBase, GalleryExtractor):
class ComickChapterExtractor(ComickBase, ChapterExtractor):
"""Extractor for comick.io manga chapters"""
archive_fmt = "{chapter_hid}_{page}"
pattern = (rf"{BASE_PATTERN}/comic/([\w-]+)"
pattern = (BASE_PATTERN + r"/comic/([\w-]+)"
r"/(\w+(?:-(?:chapter|volume)-[^/?#]+)?)")
example = "https://comick.io/comic/MANGA/ID-chapter-123-en"
@@ -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"],
@@ -140,7 +140,7 @@ class ComickChapterExtractor(ComickBase, ChapterExtractor):
class ComickMangaExtractor(ComickBase, MangaExtractor):
"""Extractor for comick.io manga"""
pattern = rf"{BASE_PATTERN}/comic/([\w-]+)/?(?:\?([^#]+))?"
pattern = BASE_PATTERN + r"/comic/([\w-]+)/?(?:\?([^#]+))?"
example = "https://comick.io/comic/MANGA"
def items(self):

View File

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

View File

@@ -18,7 +18,7 @@ class CyberdropAlbumExtractor(lolisafe.LolisafeAlbumExtractor):
category = "cyberdrop"
root = "https://cyberdrop.cr"
root_api = "https://api.cyberdrop.cr"
pattern = rf"{BASE_PATTERN}/a/([^/?#]+)"
pattern = BASE_PATTERN + r"/a/([^/?#]+)"
example = "https://cyberdrop.cr/a/ID"
def items(self):
@@ -76,7 +76,7 @@ class CyberdropMediaExtractor(CyberdropAlbumExtractor):
"""Extractor for cyberdrop media links"""
subcategory = "media"
directory_fmt = ("{category}",)
pattern = rf"{BASE_PATTERN}/f/([^/?#]+)"
pattern = BASE_PATTERN + r"/f/([^/?#]+)"
example = "https://cyberdrop.cr/f/ID"
def fetch_album(self, album_id):

View File

@@ -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(
@@ -48,7 +48,7 @@ class CyberfileExtractor(Extractor):
class CyberfileFolderExtractor(CyberfileExtractor):
subcategory = "folder"
pattern = rf"{BASE_PATTERN}/folder/([0-9a-f]+)"
pattern = BASE_PATTERN + r"/folder/([0-9a-f]+)"
example = "https://cyberfile.me/folder/0123456789abcdef/NAME"
def items(self):
@@ -97,7 +97,7 @@ class CyberfileFolderExtractor(CyberfileExtractor):
class CyberfileSharedExtractor(CyberfileExtractor):
subcategory = "shared"
pattern = rf"{BASE_PATTERN}/shared/([a-zA-Z0-9]+)"
pattern = BASE_PATTERN + r"/shared/([a-zA-Z0-9]+)"
example = "https://cyberfile.me/shared/AbCdEfGhIjK"
def items(self):
@@ -129,7 +129,7 @@ class CyberfileSharedExtractor(CyberfileExtractor):
class CyberfileFileExtractor(CyberfileExtractor):
subcategory = "file"
directory_fmt = ("{category}", "{uploader}", "{folder}")
pattern = rf"{BASE_PATTERN}/([a-zA-Z0-9]+)"
pattern = BASE_PATTERN + r"/([a-zA-Z0-9]+)"
example = "https://cyberfile.me/AbCdE"
def items(self):

View File

@@ -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):
@@ -251,7 +250,7 @@ class DanbooruTagExtractor(DanbooruExtractor):
subcategory = "tag"
directory_fmt = ("{category}", "{search_tags}")
archive_fmt = "t_{search_tags}_{id}"
pattern = rf"{BASE_PATTERN}/posts\?(?:[^&#]*&)*tags=([^&#]*)"
pattern = BASE_PATTERN + r"/posts\?(?:[^&#]*&)*tags=([^&#]*)"
example = "https://danbooru.donmai.us/posts?tags=TAG"
def metadata(self):
@@ -279,7 +278,7 @@ class DanbooruTagExtractor(DanbooruExtractor):
class DanbooruRandomExtractor(DanbooruTagExtractor):
"""Extractor for a random danbooru post"""
subcategory = "random"
pattern = rf"{BASE_PATTERN}/posts/random(?:\?(?:[^&#]*&)*tags=([^&#]*))?"
pattern = BASE_PATTERN + r"/posts/random(?:\?(?:[^&#]*&)*tags=([^&#]*))?"
example = "https://danbooru.donmai.us/posts/random?tags=TAG"
def metadata(self):
@@ -299,7 +298,7 @@ class DanbooruPoolExtractor(DanbooruExtractor):
directory_fmt = ("{category}", "pool", "{pool[id]} {pool[name]}")
filename_fmt = "{num:>04}_{id}_{filename}.{extension}"
archive_fmt = "p_{pool[id]}_{id}"
pattern = rf"{BASE_PATTERN}/pool(?:s|/show)/(\d+)"
pattern = BASE_PATTERN + r"/pool(?:s|/show)/(\d+)"
example = "https://danbooru.donmai.us/pools/12345"
def metadata(self):
@@ -317,7 +316,7 @@ class DanbooruFavgroupExtractor(DanbooruExtractor):
"{favgroup[id]} {favgroup[name]}")
filename_fmt = "{num:>04}_{id}_{filename}.{extension}"
archive_fmt = "fg_{favgroup[id]}_{id}"
pattern = rf"{BASE_PATTERN}/favorite_group(?:s|/show)/(\d+)"
pattern = BASE_PATTERN + r"/favorite_group(?:s|/show)/(\d+)"
example = "https://danbooru.donmai.us/favorite_groups/12345"
def metadata(self):
@@ -332,7 +331,7 @@ class DanbooruPostExtractor(DanbooruExtractor):
"""Extractor for single danbooru posts"""
subcategory = "post"
archive_fmt = "{id}"
pattern = rf"{BASE_PATTERN}/post(?:s|/show)/(\d+)"
pattern = BASE_PATTERN + r"/post(?:s|/show)/(\d+)"
example = "https://danbooru.donmai.us/posts/12345"
def posts(self):
@@ -349,7 +348,7 @@ class DanbooruMediaassetExtractor(DanbooruExtractor):
subcategory = "media-asset"
filename_fmt = "{category}_ma{id}_{filename}.{extension}"
archive_fmt = "m{id}"
pattern = rf"{BASE_PATTERN}/media_assets/(\d+)"
pattern = BASE_PATTERN + r"/media_assets/(\d+)"
example = "https://danbooru.donmai.us/media_assets/12345"
def posts(self):
@@ -375,7 +374,7 @@ class DanbooruPopularExtractor(DanbooruExtractor):
subcategory = "popular"
directory_fmt = ("{category}", "popular", "{scale}", "{date}")
archive_fmt = "P_{scale[0]}_{date}_{id}"
pattern = rf"{BASE_PATTERN}/(?:explore/posts/)?popular(?:\?([^#]*))?"
pattern = BASE_PATTERN + r"/(?:explore/posts/)?popular(?:\?([^#]*))?"
example = "https://danbooru.donmai.us/explore/posts/popular"
def metadata(self):
@@ -398,7 +397,7 @@ class DanbooruPopularExtractor(DanbooruExtractor):
class DanbooruArtistExtractor(DanbooruExtractor):
"""Extractor for danbooru artists"""
subcategory = "artist"
pattern = rf"{BASE_PATTERN}/artists/(\d+)"
pattern = BASE_PATTERN + r"/artists/(\d+)"
example = "https://danbooru.donmai.us/artists/12345"
items = DanbooruExtractor.items_artists
@@ -411,7 +410,7 @@ class DanbooruArtistExtractor(DanbooruExtractor):
class DanbooruArtistSearchExtractor(DanbooruExtractor):
"""Extractor for danbooru artist searches"""
subcategory = "artist-search"
pattern = rf"{BASE_PATTERN}/artists/?\?([^#]+)"
pattern = BASE_PATTERN + r"/artists/?\?([^#]+)"
example = "https://danbooru.donmai.us/artists?QUERY"
items = DanbooruExtractor.items_artists

View File

@@ -22,7 +22,7 @@ class DandadanBase():
class DandadanChapterExtractor(DandadanBase, ChapterExtractor):
"""Extractor for dandadan manga chapters"""
pattern = rf"{BASE_PATTERN}(/manga/dandadan-chapter-([^/?#]+)/?)"
pattern = BASE_PATTERN + r"(/manga/dandadan-chapter-([^/?#]+)/?)"
example = "https://dandadan.net/manga/dandadan-chapter-123/"
def metadata(self, page):
@@ -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",
}
@@ -54,7 +54,7 @@ class DandadanChapterExtractor(DandadanBase, ChapterExtractor):
class DandadanMangaExtractor(DandadanBase, MangaExtractor):
"""Extractor for dandadan manga"""
chapterclass = DandadanChapterExtractor
pattern = rf"{BASE_PATTERN}(/)"
pattern = BASE_PATTERN + r"(/)"
example = "https://dandadan.net/"
def chapters(self, page):

View File

@@ -28,13 +28,13 @@ class DankefuerslesenBase():
class DankefuerslesenChapterExtractor(DankefuerslesenBase, ChapterExtractor):
"""Extractor for Danke fürs Lesen manga chapters"""
pattern = rf"{BASE_PATTERN}/read/manga/([\w-]+)/([\w-]+)"
pattern = BASE_PATTERN + r"/read/manga/([\w-]+)/([\w-]+)"
example = "https://danke.moe/read/manga/TITLE/123/1/"
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):
@@ -95,7 +95,7 @@ class DankefuerslesenMangaExtractor(DankefuerslesenBase, MangaExtractor):
"""Extractor for Danke fürs Lesen manga"""
chapterclass = DankefuerslesenChapterExtractor
reverse = False
pattern = rf"{BASE_PATTERN}/read/manga/([^/?#]+)"
pattern = BASE_PATTERN + r"/read/manga/([^/?#]+)"
example = "https://danke.moe/read/manga/TITLE/"
def chapters(self, page):

View File

@@ -22,7 +22,7 @@ class DesktopographyExtractor(Extractor):
class DesktopographySiteExtractor(DesktopographyExtractor):
"""Extractor for all desktopography exhibitions """
subcategory = "site"
pattern = rf"{BASE_PATTERN}/$"
pattern = BASE_PATTERN + r"/$"
example = "https://desktopography.net/"
def items(self):
@@ -41,7 +41,7 @@ class DesktopographySiteExtractor(DesktopographyExtractor):
class DesktopographyExhibitionExtractor(DesktopographyExtractor):
"""Extractor for a yearly desktopography exhibition"""
subcategory = "exhibition"
pattern = rf"{BASE_PATTERN}/exhibition-([^/?#]+)/"
pattern = BASE_PATTERN + r"/exhibition-([^/?#]+)/"
example = "https://desktopography.net/exhibition-2020/"
def __init__(self, match):
@@ -70,7 +70,7 @@ class DesktopographyExhibitionExtractor(DesktopographyExtractor):
class DesktopographyEntryExtractor(DesktopographyExtractor):
"""Extractor for all resolutions of a desktopography wallpaper"""
subcategory = "entry"
pattern = rf"{BASE_PATTERN}/portfolios/([\w-]+)"
pattern = BASE_PATTERN + r"/portfolios/([\w-]+)"
example = "https://desktopography.net/portfolios/NAME/"
def __init__(self, match):

View File

@@ -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:
@@ -864,7 +864,7 @@ x2="45.4107524%" y2="71.4898596%" id="app-root-3">\
class DeviantartUserExtractor(Dispatch, DeviantartExtractor):
"""Extractor for an artist's user profile"""
pattern = rf"{BASE_PATTERN}/?$"
pattern = BASE_PATTERN + r"/?$"
example = "https://www.deviantart.com/USER"
def items(self):
@@ -887,7 +887,7 @@ class DeviantartGalleryExtractor(DeviantartExtractor):
"""Extractor for all deviations from an artist's gallery"""
subcategory = "gallery"
archive_fmt = "g_{_username}_{index}.{extension}"
pattern = (rf"{BASE_PATTERN}/gallery"
pattern = (BASE_PATTERN + r"/gallery"
r"(?:/all|/recommended-for-you)?/?(\?(?!q=).*)?$")
example = "https://www.deviantart.com/USER/gallery/"
@@ -902,7 +902,7 @@ class DeviantartAvatarExtractor(DeviantartExtractor):
"""Extractor for an artist's avatar"""
subcategory = "avatar"
archive_fmt = "a_{_username}_{index}"
pattern = rf"{BASE_PATTERN}/avatar"
pattern = BASE_PATTERN + r"/avatar"
example = "https://www.deviantart.com/USER/avatar/"
def deviations(self):
@@ -956,7 +956,7 @@ class DeviantartBackgroundExtractor(DeviantartExtractor):
"""Extractor for an artist's banner"""
subcategory = "background"
archive_fmt = "b_{index}"
pattern = rf"{BASE_PATTERN}/ba(?:nner|ckground)"
pattern = BASE_PATTERN + r"/ba(?:nner|ckground)"
example = "https://www.deviantart.com/USER/banner/"
def deviations(self):
@@ -972,7 +972,7 @@ class DeviantartFolderExtractor(DeviantartExtractor):
subcategory = "folder"
directory_fmt = ("{category}", "{username}", "{folder[title]}")
archive_fmt = "F_{folder[uuid]}_{index}.{extension}"
pattern = rf"{BASE_PATTERN}/gallery/([^/?#]+)/([^/?#]+)"
pattern = BASE_PATTERN + r"/gallery/([^/?#]+)/([^/?#]+)"
example = "https://www.deviantart.com/USER/gallery/12345/TITLE"
def __init__(self, match):
@@ -1088,7 +1088,7 @@ class DeviantartFavoriteExtractor(DeviantartExtractor):
subcategory = "favorite"
directory_fmt = ("{category}", "{username}", "Favourites")
archive_fmt = "f_{_username}_{index}.{extension}"
pattern = rf"{BASE_PATTERN}/favourites(?:/all|/?\?catpath=)?/?$"
pattern = BASE_PATTERN + r"/favourites(?:/all|/?\?catpath=)?/?$"
example = "https://www.deviantart.com/USER/favourites/"
def deviations(self):
@@ -1105,7 +1105,7 @@ class DeviantartCollectionExtractor(DeviantartExtractor):
directory_fmt = ("{category}", "{username}", "Favourites",
"{collection[title]}")
archive_fmt = "C_{collection[uuid]}_{index}.{extension}"
pattern = rf"{BASE_PATTERN}/favourites/([^/?#]+)/([^/?#]+)"
pattern = BASE_PATTERN + r"/favourites/([^/?#]+)/([^/?#]+)"
example = "https://www.deviantart.com/USER/favourites/12345/TITLE"
def __init__(self, match):
@@ -1136,7 +1136,7 @@ class DeviantartJournalExtractor(DeviantartExtractor):
subcategory = "journal"
directory_fmt = ("{category}", "{username}", "Journal")
archive_fmt = "j_{_username}_{index}.{extension}"
pattern = rf"{BASE_PATTERN}/(?:posts(?:/journals)?|journal)/?(?:\?.*)?$"
pattern = BASE_PATTERN + r"/(?:posts(?:/journals)?|journal)/?(?:\?.*)?$"
example = "https://www.deviantart.com/USER/posts/journals/"
def deviations(self):
@@ -1149,7 +1149,7 @@ class DeviantartStatusExtractor(DeviantartExtractor):
directory_fmt = ("{category}", "{username}", "Status")
filename_fmt = "{category}_{index}_{title}_{date}.{extension}"
archive_fmt = "S_{_username}_{index}.{extension}"
pattern = rf"{BASE_PATTERN}/posts/statuses"
pattern = BASE_PATTERN + r"/posts/statuses"
example = "https://www.deviantart.com/USER/posts/statuses/"
def deviations(self):
@@ -1253,7 +1253,7 @@ class DeviantartDeviationExtractor(DeviantartExtractor):
"""Extractor for single deviations"""
subcategory = "deviation"
archive_fmt = "g_{_username}_{index}.{extension}"
pattern = (rf"{BASE_PATTERN}/(art|journal)/(?:[^/?#]+-)?(\d+)"
pattern = (BASE_PATTERN + r"/(art|journal)/(?:[^/?#]+-)?(\d+)"
r"|(?:https?://)?(?:www\.)?(?:fx)?deviantart\.com/"
r"(?:view/|deviation/|view(?:-full)?\.php/*\?(?:[^#]+&)?id=)"
r"(\d+)" # bare deviation ID without slug
@@ -1315,7 +1315,7 @@ class DeviantartScrapsExtractor(DeviantartExtractor):
subcategory = "scraps"
directory_fmt = ("{category}", "{username}", "Scraps")
archive_fmt = "s_{_username}_{index}.{extension}"
pattern = rf"{BASE_PATTERN}/gallery/(?:\?catpath=)?scraps\b"
pattern = BASE_PATTERN + r"/gallery/(?:\?catpath=)?scraps\b"
example = "https://www.deviantart.com/USER/gallery/scraps"
def deviations(self):
@@ -1382,7 +1382,7 @@ class DeviantartGallerySearchExtractor(DeviantartExtractor):
"""Extractor for deviantart gallery searches"""
subcategory = "gallery-search"
archive_fmt = "g_{_username}_{index}.{extension}"
pattern = rf"{BASE_PATTERN}/gallery/?\?(q=[^#]+)"
pattern = BASE_PATTERN + r"/gallery/?\?(q=[^#]+)"
example = "https://www.deviantart.com/USER/gallery?q=QUERY"
def __init__(self, match):
@@ -1412,7 +1412,7 @@ class DeviantartGallerySearchExtractor(DeviantartExtractor):
class DeviantartFollowingExtractor(DeviantartExtractor):
"""Extractor for user's watched users"""
subcategory = "following"
pattern = rf"{BASE_PATTERN}/(?:about#)?watching"
pattern = BASE_PATTERN + "/(?:about#)?watching"
example = "https://www.deviantart.com/USER/about#watching"
def items(self):

View File

@@ -238,7 +238,7 @@ class DiscordExtractor(Extractor):
class DiscordChannelExtractor(DiscordExtractor):
subcategory = "channel"
pattern = rf"{BASE_PATTERN}/channels/(\d+)/(?:\d+/threads/)?(\d+)/?$"
pattern = BASE_PATTERN + r"/channels/(\d+)/(?:\d+/threads/)?(\d+)/?$"
example = "https://discord.com/channels/1234567890/9876543210"
def items(self):
@@ -251,7 +251,7 @@ class DiscordChannelExtractor(DiscordExtractor):
class DiscordMessageExtractor(DiscordExtractor):
subcategory = "message"
pattern = rf"{BASE_PATTERN}/channels/(\d+)/(\d+)/(\d+)/?$"
pattern = BASE_PATTERN + r"/channels/(\d+)/(\d+)/(\d+)/?$"
example = "https://discord.com/channels/1234567890/9876543210/2468013579"
def items(self):
@@ -268,7 +268,7 @@ class DiscordMessageExtractor(DiscordExtractor):
class DiscordServerExtractor(DiscordExtractor):
subcategory = "server"
pattern = rf"{BASE_PATTERN}/channels/(\d+)/?$"
pattern = BASE_PATTERN + r"/channels/(\d+)/?$"
example = "https://discord.com/channels/1234567890"
def items(self):
@@ -286,7 +286,7 @@ class DiscordDirectMessagesExtractor(DiscordExtractor):
subcategory = "direct-messages"
directory_fmt = ("{category}", "Direct Messages",
"{channel_id}_{recipients:J,}")
pattern = rf"{BASE_PATTERN}/channels/@me/(\d+)/?$"
pattern = BASE_PATTERN + r"/channels/@me/(\d+)/?$"
example = "https://discord.com/channels/@me/1234567890"
def items(self):
@@ -297,7 +297,7 @@ class DiscordDirectMessageExtractor(DiscordExtractor):
subcategory = "direct-message"
directory_fmt = ("{category}", "Direct Messages",
"{channel_id}_{recipients:J,}")
pattern = rf"{BASE_PATTERN}/channels/@me/(\d+)/(\d+)/?$"
pattern = BASE_PATTERN + r"/channels/@me/(\d+)/(\d+)/?$"
example = "https://discord.com/channels/@me/1234567890/9876543210"
def items(self):

View File

@@ -41,7 +41,7 @@ class DynastyscansBase():
class DynastyscansChapterExtractor(DynastyscansBase, ChapterExtractor):
"""Extractor for manga-chapters from dynasty-scans.com"""
pattern = rf"{BASE_PATTERN}(/chapters/[^/?#]+)"
pattern = BASE_PATTERN + r"(/chapters/[^/?#]+)"
example = "https://dynasty-scans.com/chapters/NAME"
def metadata(self, page):
@@ -81,7 +81,7 @@ class DynastyscansChapterExtractor(DynastyscansBase, ChapterExtractor):
class DynastyscansMangaExtractor(DynastyscansBase, MangaExtractor):
chapterclass = DynastyscansChapterExtractor
reverse = False
pattern = rf"{BASE_PATTERN}(/series/[^/?#]+)"
pattern = BASE_PATTERN + r"(/series/[^/?#]+)"
example = "https://dynasty-scans.com/series/NAME"
def chapters(self, page):
@@ -97,7 +97,7 @@ class DynastyscansSearchExtractor(DynastyscansBase, Extractor):
directory_fmt = ("{category}", "Images")
filename_fmt = "{image_id}.{extension}"
archive_fmt = "i_{image_id}"
pattern = rf"{BASE_PATTERN}/images/?(?:\?([^#]+))?$"
pattern = BASE_PATTERN + r"/images/?(?:\?([^#]+))?$"
example = "https://dynasty-scans.com/images?QUERY"
def __init__(self, match):
@@ -126,7 +126,7 @@ class DynastyscansSearchExtractor(DynastyscansBase, Extractor):
class DynastyscansImageExtractor(DynastyscansSearchExtractor):
"""Extractor for individual images on dynasty-scans.com"""
subcategory = "image"
pattern = rf"{BASE_PATTERN}/images/(\d+)"
pattern = BASE_PATTERN + r"/images/(\d+)"
example = "https://dynasty-scans.com/images/12345"
def images(self):
@@ -136,7 +136,7 @@ class DynastyscansImageExtractor(DynastyscansSearchExtractor):
class DynastyscansAnthologyExtractor(DynastyscansSearchExtractor):
"""Extractor for dynasty-scans anthologies"""
subcategory = "anthology"
pattern = rf"{BASE_PATTERN}/anthologies/([^/?#]+)"
pattern = BASE_PATTERN + r"/anthologies/([^/?#]+)"
example = "https://dynasty-scans.com/anthologies/TITLE"
def items(self):

View File

@@ -94,13 +94,13 @@ BASE_PATTERN = E621Extractor.update({
class E621TagExtractor(E621Extractor, danbooru.DanbooruTagExtractor):
"""Extractor for e621 posts from tag searches"""
pattern = rf"{BASE_PATTERN}/posts?(?:\?[^#]*?tags=|/index/\d+/)([^&#]*)"
pattern = BASE_PATTERN + r"/posts?(?:\?[^#]*?tags=|/index/\d+/)([^&#]*)"
example = "https://e621.net/posts?tags=TAG"
class E621PoolExtractor(E621Extractor, danbooru.DanbooruPoolExtractor):
"""Extractor for e621 pools"""
pattern = rf"{BASE_PATTERN}/pool(?:s|/show)/(\d+)"
pattern = BASE_PATTERN + r"/pool(?:s|/show)/(\d+)"
example = "https://e621.net/pools/12345"
def posts(self):
@@ -125,7 +125,7 @@ class E621PoolExtractor(E621Extractor, danbooru.DanbooruPoolExtractor):
class E621PostExtractor(E621Extractor, danbooru.DanbooruPostExtractor):
"""Extractor for single e621 posts"""
pattern = rf"{BASE_PATTERN}/post(?:s|/show)/(\d+)"
pattern = BASE_PATTERN + r"/post(?:s|/show)/(\d+)"
example = "https://e621.net/posts/12345"
def posts(self):
@@ -135,7 +135,7 @@ class E621PostExtractor(E621Extractor, danbooru.DanbooruPostExtractor):
class E621PopularExtractor(E621Extractor, danbooru.DanbooruPopularExtractor):
"""Extractor for popular images from e621"""
pattern = rf"{BASE_PATTERN}/explore/posts/popular(?:\?([^#]*))?"
pattern = BASE_PATTERN + r"/explore/posts/popular(?:\?([^#]*))?"
example = "https://e621.net/explore/posts/popular"
def posts(self):
@@ -145,7 +145,7 @@ class E621PopularExtractor(E621Extractor, danbooru.DanbooruPopularExtractor):
class E621ArtistExtractor(E621Extractor, danbooru.DanbooruArtistExtractor):
"""Extractor for e621 artists"""
subcategory = "artist"
pattern = rf"{BASE_PATTERN}/artists/(\d+)"
pattern = BASE_PATTERN + r"/artists/(\d+)"
example = "https://e621.net/artists/12345"
items = E621Extractor.items_artists
@@ -155,7 +155,7 @@ class E621ArtistSearchExtractor(E621Extractor,
danbooru.DanbooruArtistSearchExtractor):
"""Extractor for e621 artist searches"""
subcategory = "artist-search"
pattern = rf"{BASE_PATTERN}/artists/?\?([^#]+)"
pattern = BASE_PATTERN + r"/artists/?\?([^#]+)"
example = "https://e621.net/artists?QUERY"
items = E621Extractor.items_artists
@@ -166,7 +166,7 @@ class E621FavoriteExtractor(E621Extractor):
subcategory = "favorite"
directory_fmt = ("{category}", "Favorites", "{user_id}")
archive_fmt = "f_{user_id}_{id}"
pattern = rf"{BASE_PATTERN}/favorites(?:\?([^#]*))?"
pattern = BASE_PATTERN + r"/favorites(?:\?([^#]*))?"
example = "https://e621.net/favorites"
def metadata(self):

View File

@@ -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 ()
@@ -64,7 +64,7 @@ class EromeExtractor(Extractor):
class EromeAlbumExtractor(EromeExtractor):
"""Extractor for albums on erome.com"""
subcategory = "album"
pattern = rf"{BASE_PATTERN}/a/(\w+)"
pattern = BASE_PATTERN + r"/a/(\w+)"
example = "https://www.erome.com/a/ID"
def items(self):
@@ -121,7 +121,7 @@ class EromeAlbumExtractor(EromeExtractor):
class EromeUserExtractor(EromeExtractor):
subcategory = "user"
pattern = rf"{BASE_PATTERN}/(?!a/|search\?)([^/?#]+)(?:/?\?([^#]+))?"
pattern = BASE_PATTERN + r"/(?!a/|search\?)([^/?#]+)(?:/?\?([^#]+))?"
example = "https://www.erome.com/USER"
def albums(self):
@@ -137,11 +137,11 @@ class EromeUserExtractor(EromeExtractor):
class EromeSearchExtractor(EromeExtractor):
subcategory = "search"
pattern = rf"{BASE_PATTERN}/search/?\?(q=[^#]+)"
pattern = BASE_PATTERN + r"/search/?\?(q=[^#]+)"
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)

View File

@@ -45,7 +45,7 @@ class EveriaPostExtractor(EveriaExtractor):
subcategory = "post"
directory_fmt = ("{category}", "{title}")
archive_fmt = "{post_url}_{num}"
pattern = rf"{BASE_PATTERN}(/\d{{4}}/\d{{2}}/\d{{2}}/[^/?#]+)"
pattern = BASE_PATTERN + r"(/\d{4}/\d{2}/\d{2}/[^/?#]+)"
example = "https://everia.club/0000/00/00/TITLE"
def items(self):
@@ -72,26 +72,26 @@ class EveriaPostExtractor(EveriaExtractor):
class EveriaTagExtractor(EveriaExtractor):
subcategory = "tag"
pattern = rf"{BASE_PATTERN}(/tag/[^/?#]+)"
pattern = BASE_PATTERN + r"(/tag/[^/?#]+)"
example = "https://everia.club/tag/TAG"
class EveriaCategoryExtractor(EveriaExtractor):
subcategory = "category"
pattern = rf"{BASE_PATTERN}(/category/[^/?#]+)"
pattern = BASE_PATTERN + r"(/category/[^/?#]+)"
example = "https://everia.club/category/CATEGORY"
class EveriaDateExtractor(EveriaExtractor):
subcategory = "date"
pattern = (rf"{BASE_PATTERN}"
rf"(/\d{{4}}(?:/\d{{2}})?(?:/\d{{2}})?)(?:/page/\d+)?/?$")
pattern = (BASE_PATTERN +
r"(/\d{4}(?:/\d{2})?(?:/\d{2})?)(?:/page/\d+)?/?$")
example = "https://everia.club/0000/00/00"
class EveriaSearchExtractor(EveriaExtractor):
subcategory = "search"
pattern = rf"{BASE_PATTERN}/(?:page/\d+/)?\?s=([^&#]+)"
pattern = BASE_PATTERN + r"/(?:page/\d+/)?\?s=([^&#]+)"
example = "https://everia.club/?s=SEARCH"
def posts(self):

View File

@@ -115,9 +115,9 @@ class ExhentaiExtractor(Extractor):
class ExhentaiGalleryExtractor(ExhentaiExtractor):
"""Extractor for image galleries from exhentai.org"""
subcategory = "gallery"
pattern = (rf"{BASE_PATTERN}/(?:"
rf"g/(\d+)/([\da-f]{{10}})|"
rf"s/([\da-f]{{10}})/(\d+)-(\d+))")
pattern = (BASE_PATTERN +
r"(?:/g/(\d+)/([\da-f]{10})"
r"|/s/([\da-f]{10})/(\d+)-(\d+))")
example = "https://e-hentai.org/g/12345/67890abcde/"
def __init__(self, match):
@@ -562,7 +562,7 @@ class ExhentaiGalleryExtractor(ExhentaiExtractor):
class ExhentaiSearchExtractor(ExhentaiExtractor):
"""Extractor for exhentai search results"""
subcategory = "search"
pattern = rf"{BASE_PATTERN}/(?:\?([^#]*)|tag/([^/?#]+))"
pattern = BASE_PATTERN + r"/(?:\?([^#]*)|tag/([^/?#]+))"
example = "https://e-hentai.org/?f_search=QUERY"
def __init__(self, match):
@@ -619,7 +619,7 @@ class ExhentaiSearchExtractor(ExhentaiExtractor):
class ExhentaiFavoriteExtractor(ExhentaiSearchExtractor):
"""Extractor for favorited exhentai galleries"""
subcategory = "favorite"
pattern = rf"{BASE_PATTERN}/favorites\.php(?:\?([^#]*)())?"
pattern = BASE_PATTERN + r"/favorites\.php(?:\?([^#]*)())?"
example = "https://e-hentai.org/favorites.php"
def _init(self):

View File

@@ -11,9 +11,9 @@ from .. import text, util, exception
from ..cache import memcache
BASE_PATTERN = r"(?:https?://)?(?:[\w-]+\.)?facebook\.com"
USER_PATTERN = (rf"{BASE_PATTERN}/"
rf"(?!media/|photo/|photo.php|watch/|permalink.php)"
rf"(?:profile\.php\?id=|people/[^/?#]+/)?([^/?&#]+)")
USER_PATTERN = (BASE_PATTERN +
r"/(?!media/|photo/|photo.php|watch/|permalink.php)"
r"(?:profile\.php\?id=|people/[^/?#]+/)?([^/?&#]+)")
class FacebookExtractor(Extractor):
@@ -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
@@ -395,9 +393,9 @@ class FacebookExtractor(Extractor):
class FacebookPhotoExtractor(FacebookExtractor):
"""Base class for Facebook Photo extractors"""
subcategory = "photo"
pattern = (rf"{BASE_PATTERN}/"
rf"(?:[^/?#]+/photos/[^/?#]+/|photo(?:.php)?/?\?"
rf"(?:[^&#]+&)*fbid=)([^/?&#]+)[^/?#]*(?<!&setextract)$")
pattern = (BASE_PATTERN +
r"/(?:[^/?#]+/photos/[^/?#]+/|photo(?:.php)?/?\?"
r"(?:[^&#]+&)*fbid=)([^/?&#]+)[^/?#]*(?<!&setextract)$")
example = "https://www.facebook.com/photo/?fbid=PHOTO_ID"
def items(self):
@@ -433,11 +431,12 @@ class FacebookSetExtractor(FacebookExtractor):
"""Base class for Facebook Set extractors"""
subcategory = "set"
pattern = (
rf"{BASE_PATTERN}/"
rf"(?:(?:media/set|photo)/?\?(?:[^&#]+&)*set=([^&#]+)"
rf"[^/?#]*(?<!&setextract)$"
rf"|([^/?#]+/posts/[^/?#]+)"
rf"|photo/\?(?:[^&#]+&)*fbid=([^/?&#]+)&set=([^/?&#]+)&setextract)")
BASE_PATTERN +
r"/(?:(?:media/set|photo)/?\?(?:[^&#]+&)*set=([^&#]+)"
r"[^/?#]*(?<!&setextract)$"
r"|([^/?#]+/posts/[^/?#]+)"
r"|photo/\?(?:[^&#]+&)*fbid=([^/?&#]+)&set=([^/?&#]+)&setextract)"
)
example = "https://www.facebook.com/media/set/?set=SET_ID"
def items(self):
@@ -460,7 +459,7 @@ class FacebookVideoExtractor(FacebookExtractor):
"""Base class for Facebook Video extractors"""
subcategory = "video"
directory_fmt = ("{category}", "{username}", "{subcategory}")
pattern = rf"{BASE_PATTERN}/(?:[^/?#]+/videos/|watch/?\?v=)([^/?&#]+)"
pattern = BASE_PATTERN + r"/(?:[^/?#]+/videos/|watch/?\?v=)([^/?&#]+)"
example = "https://www.facebook.com/watch/?v=VIDEO_ID"
def items(self):
@@ -487,7 +486,7 @@ class FacebookInfoExtractor(FacebookExtractor):
"""Extractor for Facebook Profile data"""
subcategory = "info"
directory_fmt = ("{category}", "{username}")
pattern = rf"{USER_PATTERN}/info"
pattern = USER_PATTERN + r"/info"
example = "https://www.facebook.com/USERNAME/info"
def items(self):
@@ -498,7 +497,7 @@ class FacebookInfoExtractor(FacebookExtractor):
class FacebookAlbumsExtractor(FacebookExtractor):
"""Extractor for Facebook Profile albums"""
subcategory = "albums"
pattern = rf"{USER_PATTERN}/photos_albums(?:/([^/?#]+))?"
pattern = USER_PATTERN + r"/photos_albums(?:/([^/?#]+))?"
example = "https://www.facebook.com/USERNAME/photos_albums"
def items(self):
@@ -531,7 +530,7 @@ class FacebookAlbumsExtractor(FacebookExtractor):
class FacebookPhotosExtractor(FacebookExtractor):
"""Extractor for Facebook Profile Photos"""
subcategory = "photos"
pattern = rf"{USER_PATTERN}/photos(?:_by)?"
pattern = USER_PATTERN + r"/photos(?:_by)?"
example = "https://www.facebook.com/USERNAME/photos"
def items(self):
@@ -548,7 +547,7 @@ class FacebookPhotosExtractor(FacebookExtractor):
class FacebookAvatarExtractor(FacebookExtractor):
"""Extractor for Facebook Profile Avatars"""
subcategory = "avatar"
pattern = rf"{USER_PATTERN}/avatar"
pattern = USER_PATTERN + r"/avatar"
example = "https://www.facebook.com/USERNAME/avatar"
def items(self):
@@ -570,7 +569,7 @@ class FacebookAvatarExtractor(FacebookExtractor):
class FacebookUserExtractor(Dispatch, FacebookExtractor):
"""Extractor for Facebook Profiles"""
pattern = rf"{USER_PATTERN}/?(?:$|\?|#)"
pattern = USER_PATTERN + r"/?(?:$|\?|#)"
example = "https://www.facebook.com/USERNAME"
def items(self):

View File

@@ -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
@@ -355,7 +355,7 @@ class FanboxExtractor(Extractor):
class FanboxCreatorExtractor(FanboxExtractor):
"""Extractor for a Fanbox creator's works"""
subcategory = "creator"
pattern = rf"{USER_PATTERN}(?:/posts)?/?$"
pattern = USER_PATTERN + r"(?:/posts)?/?$"
example = "https://USER.fanbox.cc/"
def posts(self):
@@ -384,7 +384,7 @@ class FanboxCreatorExtractor(FanboxExtractor):
class FanboxPostExtractor(FanboxExtractor):
"""Extractor for media from a single Fanbox post"""
subcategory = "post"
pattern = rf"{USER_PATTERN}/posts/(\d+)"
pattern = USER_PATTERN + r"/posts/(\d+)"
example = "https://USER.fanbox.cc/posts/12345"
def posts(self):
@@ -394,7 +394,7 @@ class FanboxPostExtractor(FanboxExtractor):
class FanboxHomeExtractor(FanboxExtractor):
"""Extractor for your Fanbox home feed"""
subcategory = "home"
pattern = rf"{BASE_PATTERN}/?$"
pattern = BASE_PATTERN + r"/?$"
example = "https://fanbox.cc/"
def posts(self):
@@ -405,7 +405,7 @@ class FanboxHomeExtractor(FanboxExtractor):
class FanboxSupportingExtractor(FanboxExtractor):
"""Extractor for your supported Fanbox users feed"""
subcategory = "supporting"
pattern = rf"{BASE_PATTERN}/home/supporting"
pattern = BASE_PATTERN + r"/home/supporting"
example = "https://fanbox.cc/home/supporting"
def posts(self):

View File

@@ -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",
@@ -155,7 +155,7 @@ class FanslyExtractor(Extractor):
class FanslyPostExtractor(FanslyExtractor):
subcategory = "post"
pattern = rf"{BASE_PATTERN}/post/(\d+)"
pattern = BASE_PATTERN + r"/post/(\d+)"
example = "https://fansly.com/post/1234567890"
def posts(self):
@@ -164,7 +164,7 @@ class FanslyPostExtractor(FanslyExtractor):
class FanslyHomeExtractor(FanslyExtractor):
subcategory = "home"
pattern = rf"{BASE_PATTERN}/home(?:/(?:subscribed()|list/(\d+)))?"
pattern = BASE_PATTERN + r"/home(?:/(?:subscribed()|list/(\d+)))?"
example = "https://fansly.com/home"
def posts(self):
@@ -180,11 +180,11 @@ class FanslyHomeExtractor(FanslyExtractor):
class FanslyListExtractor(FanslyExtractor):
subcategory = "list"
pattern = rf"{BASE_PATTERN}/lists/(\d+)"
pattern = BASE_PATTERN + r"/lists/(\d+)"
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"
@@ -193,11 +193,11 @@ class FanslyListExtractor(FanslyExtractor):
class FanslyListsExtractor(FanslyExtractor):
subcategory = "lists"
pattern = rf"{BASE_PATTERN}/lists"
pattern = BASE_PATTERN + r"/lists"
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']}"
@@ -206,7 +206,7 @@ class FanslyListsExtractor(FanslyExtractor):
class FanslyCreatorPostsExtractor(FanslyExtractor):
subcategory = "creator-posts"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/posts(?:/wall/(\d+))?"
pattern = BASE_PATTERN + r"/([^/?#]+)/posts(?:/wall/(\d+))?"
example = "https://fansly.com/CREATOR/posts"
def posts_wall(self, account, wall):
@@ -215,7 +215,7 @@ class FanslyCreatorPostsExtractor(FanslyExtractor):
class FanslyCreatorMediaExtractor(FanslyExtractor):
subcategory = "creator-media"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/media(?:/wall/(\d+))?"
pattern = BASE_PATTERN + r"/([^/?#]+)/media(?:/wall/(\d+))?"
example = "https://fansly.com/CREATOR/media"
def posts_wall(self, account, wall):
@@ -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",

View File

@@ -20,7 +20,7 @@ class FapelloPostExtractor(Extractor):
directory_fmt = ("{category}", "{model}")
filename_fmt = "{model}_{id}.{extension}"
archive_fmt = "{type}_{model}_{id}"
pattern = rf"{BASE_PATTERN}/(?!search/|popular_videos/)([^/?#]+)/(\d+)"
pattern = BASE_PATTERN + r"/(?!search/|popular_videos/)([^/?#]+)/(\d+)"
example = "https://fapello.com/MODEL/12345/"
def __init__(self, match):
@@ -52,9 +52,9 @@ class FapelloModelExtractor(Extractor):
"""Extractor for all posts from a fapello model"""
category = "fapello"
subcategory = "model"
pattern = (rf"{BASE_PATTERN}/(?!top-(?:likes|followers)|popular_videos"
rf"|videos|trending|search/?$)"
rf"([^/?#]+)/?$")
pattern = (BASE_PATTERN + r"/(?!top-(?:likes|followers)|popular_videos"
r"|videos|trending|search/?$)"
r"([^/?#]+)/?$")
example = "https://fapello.com/model/"
def __init__(self, match):
@@ -85,9 +85,9 @@ class FapelloPathExtractor(Extractor):
"""Extractor for models and posts from fapello.com paths"""
category = "fapello"
subcategory = "path"
pattern = (rf"{BASE_PATTERN}/(?!search/?$)"
rf"(top-(?:likes|followers)|videos|trending"
rf"|popular_videos/[^/?#]+)/?$")
pattern = (BASE_PATTERN +
r"/(?!search/?$)(top-(?:likes|followers)|videos|trending"
r"|popular_videos/[^/?#]+)/?$")
example = "https://fapello.com/trending/"
def __init__(self, match):

View File

@@ -67,7 +67,7 @@ class FikfapExtractor(Extractor):
class FikfapPostExtractor(FikfapExtractor):
subcategory = "post"
pattern = rf"{BASE_PATTERN}/user/(\w+)/post/(\d+)"
pattern = BASE_PATTERN + r"/user/(\w+)/post/(\d+)"
example = "https://fikfap.com/user/USER/post/12345"
def posts(self):
@@ -86,7 +86,7 @@ class FikfapPostExtractor(FikfapExtractor):
class FikfapUserExtractor(FikfapExtractor):
subcategory = "user"
pattern = rf"{BASE_PATTERN}/user/(\w+)"
pattern = BASE_PATTERN + r"/user/(\w+)"
example = "https://fikfap.com/user/USER"
def posts(self):

View File

@@ -50,7 +50,7 @@ class FitnakedgirlsGalleryExtractor(GalleryExtractor, FitnakedgirlsExtractor):
directory_fmt = ("{category}", "{title}")
filename_fmt = "{filename}.{extension}"
archive_fmt = "{gallery_id}_{filename}"
pattern = rf"{BASE_PATTERN}/photos/gallery/([\w-]+)/?$"
pattern = BASE_PATTERN + r"/photos/gallery/([\w-]+)/?$"
example = "https://fitnakedgirls.com/photos/gallery/MODEL-nude/"
def __init__(self, match):
@@ -110,7 +110,7 @@ class FitnakedgirlsGalleryExtractor(GalleryExtractor, FitnakedgirlsExtractor):
class FitnakedgirlsCategoryExtractor(FitnakedgirlsExtractor):
"""Extractor for fitnakedgirls category pages"""
subcategory = "category"
pattern = rf"{BASE_PATTERN}/photos/gallery/category/([\w-]+)"
pattern = BASE_PATTERN + r"/photos/gallery/category/([\w-]+)"
example = "https://fitnakedgirls.com/photos/gallery/category/CATEGORY/"
def galleries(self):
@@ -121,7 +121,7 @@ class FitnakedgirlsCategoryExtractor(FitnakedgirlsExtractor):
class FitnakedgirlsTagExtractor(FitnakedgirlsExtractor):
"""Extractor for fitnakedgirls tag pages"""
subcategory = "tag"
pattern = rf"{BASE_PATTERN}/photos/gallery/tag/([\w-]+)"
pattern = BASE_PATTERN + r"/photos/gallery/tag/([\w-]+)"
example = "https://fitnakedgirls.com/photos/gallery/tag/TAG/"
def galleries(self):
@@ -135,7 +135,7 @@ class FitnakedgirlsVideoExtractor(FitnakedgirlsExtractor):
directory_fmt = ("{category}", "{title}")
filename_fmt = "{filename}.{extension}"
archive_fmt = "{video_id}_{filename}"
pattern = rf"{BASE_PATTERN}/videos/(\d+)/(\d+)/([\w-]+)"
pattern = BASE_PATTERN + r"/videos/(\d+)/(\d+)/([\w-]+)"
example = "https://fitnakedgirls.com/videos/2025/08/VIDEO-TITLE/"
def items(self):
@@ -168,7 +168,7 @@ class FitnakedgirlsBlogExtractor(FitnakedgirlsExtractor):
directory_fmt = ("{category}", "{title}")
filename_fmt = "{filename}.{extension}"
archive_fmt = "{post_id}_{filename}"
pattern = rf"{BASE_PATTERN}/fitblog/([\w-]+)"
pattern = BASE_PATTERN + r"/fitblog/([\w-]+)"
example = "https://fitnakedgirls.com/fitblog/MODEL-NAME/"
def items(self):

View File

@@ -124,7 +124,7 @@ class FlickrAlbumExtractor(FlickrExtractor):
directory_fmt = ("{category}", "{user[username]}",
"Albums", "{album[id]} {album[title]}")
archive_fmt = "a_{album[id]}_{id}"
pattern = rf"{BASE_PATTERN}/photos/([^/?#]+)/(?:album|set)s(?:/(\d+))?"
pattern = BASE_PATTERN + r"/photos/([^/?#]+)/(?:album|set)s(?:/(\d+))?"
example = "https://www.flickr.com/photos/USER/albums/12345"
def items(self):
@@ -166,7 +166,7 @@ class FlickrGalleryExtractor(FlickrExtractor):
directory_fmt = ("{category}", "{user[username]}",
"Galleries", "{gallery[gallery_id]} {gallery[title]}")
archive_fmt = "g_{gallery[id]}_{id}"
pattern = rf"{BASE_PATTERN}/photos/([^/?#]+)/galleries/(\d+)"
pattern = BASE_PATTERN + r"/photos/([^/?#]+)/galleries/(\d+)"
example = "https://www.flickr.com/photos/USER/galleries/12345/"
def metadata(self):
@@ -184,7 +184,7 @@ class FlickrGroupExtractor(FlickrExtractor):
subcategory = "group"
directory_fmt = ("{category}", "Groups", "{group[groupname]}")
archive_fmt = "G_{group[nsid]}_{id}"
pattern = rf"{BASE_PATTERN}/groups/([^/?#]+)"
pattern = BASE_PATTERN + r"/groups/([^/?#]+)"
example = "https://www.flickr.com/groups/NAME/"
def metadata(self):
@@ -199,7 +199,7 @@ class FlickrUserExtractor(FlickrExtractor):
"""Extractor for the photostream of a flickr user"""
subcategory = "user"
archive_fmt = "u_{user[nsid]}_{id}"
pattern = rf"{BASE_PATTERN}/photos/([^/?#]+)/?$"
pattern = BASE_PATTERN + r"/photos/([^/?#]+)/?$"
example = "https://www.flickr.com/photos/USER/"
def photos(self):
@@ -211,7 +211,7 @@ class FlickrFavoriteExtractor(FlickrExtractor):
subcategory = "favorite"
directory_fmt = ("{category}", "{user[username]}", "Favorites")
archive_fmt = "f_{user[nsid]}_{id}"
pattern = rf"{BASE_PATTERN}/photos/([^/?#]+)/favorites"
pattern = BASE_PATTERN + r"/photos/([^/?#]+)/favorites"
example = "https://www.flickr.com/photos/USER/favorites"
def photos(self):
@@ -223,7 +223,7 @@ class FlickrSearchExtractor(FlickrExtractor):
subcategory = "search"
directory_fmt = ("{category}", "Search", "{search[text]}")
archive_fmt = "s_{search}_{id}"
pattern = rf"{BASE_PATTERN}/search/?\?([^#]+)"
pattern = BASE_PATTERN + r"/search/?\?([^#]+)"
example = "https://flickr.com/search/?text=QUERY"
def metadata(self):
@@ -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"):

View File

@@ -147,7 +147,7 @@ class FoolfuukaThreadExtractor(FoolfuukaExtractor):
subcategory = "thread"
directory_fmt = ("{category}", "{board[shortname]}",
"{thread_num} {title|comment[:50]}")
pattern = rf"{BASE_PATTERN}/([^/?#]+)/thread/(\d+)"
pattern = BASE_PATTERN + r"/([^/?#]+)/thread/(\d+)"
example = "https://archived.moe/a/thread/12345/"
def __init__(self, match):
@@ -174,7 +174,7 @@ class FoolfuukaThreadExtractor(FoolfuukaExtractor):
class FoolfuukaBoardExtractor(FoolfuukaExtractor):
"""Base extractor for FoolFuuka based boards/archives"""
subcategory = "board"
pattern = rf"{BASE_PATTERN}/([^/?#]+)(?:/(?:page/)?(\d*))?$"
pattern = BASE_PATTERN + r"/([^/?#]+)(?:/(?:page/)?(\d*))?$"
example = "https://archived.moe/a/"
def __init__(self, match):
@@ -210,7 +210,7 @@ class FoolfuukaSearchExtractor(FoolfuukaExtractor):
"""Base extractor for search results on FoolFuuka based boards/archives"""
subcategory = "search"
directory_fmt = ("{category}", "search", "{search}")
pattern = rf"{BASE_PATTERN}/([^/?#]+)/search((?:/[^/?#]+/[^/?#]+)+)"
pattern = BASE_PATTERN + r"/([^/?#]+)/search((?:/[^/?#]+/[^/?#]+)+)"
example = "https://archived.moe/_/search/text/QUERY/"
request_interval = (0.5, 1.5)
@@ -265,7 +265,7 @@ class FoolfuukaGalleryExtractor(FoolfuukaExtractor):
"""Base extractor for FoolFuuka galleries"""
subcategory = "gallery"
directory_fmt = ("{category}", "{board}", "gallery")
pattern = rf"{BASE_PATTERN}/([^/?#]+)/gallery(?:/(\d+))?"
pattern = BASE_PATTERN + r"/([^/?#]+)/gallery(?:/(\d+))?"
example = "https://archived.moe/a/gallery"
def metadata(self):
@@ -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

View File

@@ -47,7 +47,7 @@ class FoolslideChapterExtractor(FoolslideExtractor):
filename_fmt = (
"{manga}_c{chapter:>03}{chapter_minor:?//}_{page:>03}.{extension}")
archive_fmt = "{id}"
pattern = rf"{BASE_PATTERN}(/read/[^/?#]+/[a-z-]+/\d+/\d+(?:/\d+)?)"
pattern = BASE_PATTERN + r"(/read/[^/?#]+/[a-z-]+/\d+/\d+(?:/\d+)?)"
example = "https://read.powermanga.org/read/MANGA/en/0/123/"
def items(self):
@@ -91,7 +91,7 @@ class FoolslideMangaExtractor(FoolslideExtractor):
"""Base class for manga extractors for FoOlSlide based sites"""
subcategory = "manga"
categorytransfer = True
pattern = rf"{BASE_PATTERN}(/series/[^/?#]+)"
pattern = BASE_PATTERN + r"(/series/[^/?#]+)"
example = "https://read.powermanga.org/series/MANGA/"
def items(self):

View File

@@ -231,7 +231,7 @@ class FuraffinityExtractor(Extractor):
class FuraffinityGalleryExtractor(FuraffinityExtractor):
"""Extractor for a furaffinity user's gallery"""
subcategory = "gallery"
pattern = rf"{BASE_PATTERN}/gallery/([^/?#]+)(?:$|/(?!folder/))"
pattern = BASE_PATTERN + r"/gallery/([^/?#]+)(?:$|/(?!folder/))"
example = "https://www.furaffinity.net/gallery/USER/"
def posts(self):
@@ -243,7 +243,7 @@ class FuraffinityFolderExtractor(FuraffinityExtractor):
subcategory = "folder"
directory_fmt = ("{category}", "{user!l}",
"Folders", "{folder_id}{folder_name:? //}")
pattern = rf"{BASE_PATTERN}/gallery/([^/?#]+)/folder/(\d+)(?:/([^/?#]+))?"
pattern = BASE_PATTERN + r"/gallery/([^/?#]+)/folder/(\d+)(?:/([^/?#]+))?"
example = "https://www.furaffinity.net/gallery/USER/folder/12345/FOLDER"
def metadata(self):
@@ -260,7 +260,7 @@ class FuraffinityScrapsExtractor(FuraffinityExtractor):
"""Extractor for a furaffinity user's scraps"""
subcategory = "scraps"
directory_fmt = ("{category}", "{user!l}", "Scraps")
pattern = rf"{BASE_PATTERN}/scraps/([^/?#]+)"
pattern = BASE_PATTERN + r"/scraps/([^/?#]+)"
example = "https://www.furaffinity.net/scraps/USER/"
def posts(self):
@@ -271,7 +271,7 @@ class FuraffinityFavoriteExtractor(FuraffinityExtractor):
"""Extractor for a furaffinity user's favorites"""
subcategory = "favorite"
directory_fmt = ("{category}", "{user!l}", "Favorites")
pattern = rf"{BASE_PATTERN}/favorites/([^/?#]+)"
pattern = BASE_PATTERN + r"/favorites/([^/?#]+)"
example = "https://www.furaffinity.net/favorites/USER/"
def posts(self):
@@ -287,7 +287,7 @@ class FuraffinitySearchExtractor(FuraffinityExtractor):
"""Extractor for furaffinity search results"""
subcategory = "search"
directory_fmt = ("{category}", "Search", "{search}")
pattern = rf"{BASE_PATTERN}/search(?:/([^/?#]+))?/?[?&]([^#]+)"
pattern = BASE_PATTERN + r"/search(?:/([^/?#]+))?/?[?&]([^#]+)"
example = "https://www.furaffinity.net/search/?q=QUERY"
def __init__(self, match):
@@ -306,7 +306,7 @@ class FuraffinitySearchExtractor(FuraffinityExtractor):
class FuraffinityPostExtractor(FuraffinityExtractor):
"""Extractor for individual posts on furaffinity"""
subcategory = "post"
pattern = rf"{BASE_PATTERN}/(?:view|full)/(\d+)"
pattern = BASE_PATTERN + r"/(?:view|full)/(\d+)"
example = "https://www.furaffinity.net/view/12345/"
def posts(self):
@@ -317,12 +317,12 @@ class FuraffinityPostExtractor(FuraffinityExtractor):
class FuraffinityUserExtractor(Dispatch, FuraffinityExtractor):
"""Extractor for furaffinity user profiles"""
pattern = rf"{BASE_PATTERN}/user/([^/?#]+)"
pattern = BASE_PATTERN + r"/user/([^/?#]+)"
example = "https://www.furaffinity.net/user/USER/"
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}"),
@@ -333,7 +333,7 @@ class FuraffinityUserExtractor(Dispatch, FuraffinityExtractor):
class FuraffinityFollowingExtractor(FuraffinityExtractor):
"""Extractor for a furaffinity user's watched users"""
subcategory = "following"
pattern = rf"{BASE_PATTERN}/watchlist/by/([^/?#]+)"
pattern = BASE_PATTERN + "/watchlist/by/([^/?#]+)"
example = "https://www.furaffinity.net/watchlist/by/USER/"
def items(self):
@@ -355,7 +355,7 @@ class FuraffinityFollowingExtractor(FuraffinityExtractor):
class FuraffinitySubmissionsExtractor(FuraffinityExtractor):
"""Extractor for new furaffinity submissions"""
subcategory = "submissions"
pattern = rf"{BASE_PATTERN}(/msg/submissions(?:/[^/?#]+)?)"
pattern = BASE_PATTERN + r"(/msg/submissions(?:/[^/?#]+)?)"
example = "https://www.furaffinity.net/msg/submissions"
def posts(self):

View File

@@ -97,7 +97,7 @@ class Furry34Extractor(BooruExtractor):
class Furry34PostExtractor(Furry34Extractor):
subcategory = "post"
archive_fmt = "{id}"
pattern = rf"{BASE_PATTERN}/post/(\d+)"
pattern = BASE_PATTERN + r"/post/(\d+)"
example = "https://furry34.com/post/12345"
def posts(self):
@@ -108,7 +108,7 @@ class Furry34PlaylistExtractor(Furry34Extractor):
subcategory = "playlist"
directory_fmt = ("{category}", "{playlist_id}")
archive_fmt = "p_{playlist_id}_{id}"
pattern = rf"{BASE_PATTERN}/playlists/view/(\d+)"
pattern = BASE_PATTERN + r"/playlists/view/(\d+)"
example = "https://furry34.com/playlists/view/12345"
def metadata(self):
@@ -123,7 +123,7 @@ class Furry34TagExtractor(Furry34Extractor):
subcategory = "tag"
directory_fmt = ("{category}", "{search_tags}")
archive_fmt = "t_{search_tags}_{id}"
pattern = rf"{BASE_PATTERN}/(?:([^/?#]+))?(?:/?\?([^#]+))?(?:$|#)"
pattern = BASE_PATTERN + r"/(?:([^/?#]+))?(?:/?\?([^#]+))?(?:$|#)"
example = "https://furry34.com/TAG"
def _init(self):

View File

@@ -148,7 +148,7 @@ class GelbooruBase():
class GelbooruTagExtractor(GelbooruBase,
gelbooru_v02.GelbooruV02TagExtractor):
"""Extractor for images from gelbooru.com based on search-tags"""
pattern = rf"{BASE_PATTERN}page=post&s=list&tags=([^&#]*)"
pattern = BASE_PATTERN + r"page=post&s=list&tags=([^&#]*)"
example = "https://gelbooru.com/index.php?page=post&s=list&tags=TAG"
@@ -156,7 +156,7 @@ class GelbooruPoolExtractor(GelbooruBase,
gelbooru_v02.GelbooruV02PoolExtractor):
"""Extractor for gelbooru pools"""
per_page = 45
pattern = rf"{BASE_PATTERN}page=pool&s=show&id=(\d+)"
pattern = BASE_PATTERN + r"page=pool&s=show&id=(\d+)"
example = "https://gelbooru.com/index.php?page=pool&s=show&id=12345"
skip = GelbooruBase._skip_offset
@@ -187,7 +187,7 @@ class GelbooruFavoriteExtractor(GelbooruBase,
gelbooru_v02.GelbooruV02FavoriteExtractor):
"""Extractor for gelbooru favorites"""
per_page = 100
pattern = rf"{BASE_PATTERN}page=favorites&s=view&id=(\d+)"
pattern = BASE_PATTERN + r"page=favorites&s=view&id=(\d+)"
example = "https://gelbooru.com/index.php?page=favorites&s=view&id=12345"
skip = GelbooruBase._skip_offset
@@ -284,10 +284,10 @@ class GelbooruFavoriteExtractor(GelbooruBase,
class GelbooruPostExtractor(GelbooruBase,
gelbooru_v02.GelbooruV02PostExtractor):
"""Extractor for single images from gelbooru.com"""
pattern = (rf"{BASE_PATTERN}"
rf"(?=(?:[^#]+&)?page=post(?:&|#|$))"
rf"(?=(?:[^#]+&)?s=view(?:&|#|$))"
rf"(?:[^#]+&)?id=(\d+)")
pattern = (BASE_PATTERN +
r"(?=(?:[^#]+&)?page=post(?:&|#|$))"
r"(?=(?:[^#]+&)?s=view(?:&|#|$))"
r"(?:[^#]+&)?id=(\d+)")
example = "https://gelbooru.com/index.php?page=post&s=view&id=12345"

View File

@@ -87,7 +87,7 @@ class GelbooruV01TagExtractor(GelbooruV01Extractor):
subcategory = "tag"
directory_fmt = ("{category}", "{search_tags}")
archive_fmt = "t_{search_tags}_{id}"
pattern = rf"{BASE_PATTERN}/index\.php\?page=post&s=list&tags=([^&#]+)"
pattern = BASE_PATTERN + r"/index\.php\?page=post&s=list&tags=([^&#]+)"
example = "https://allgirl.booru.org/index.php?page=post&s=list&tags=TAG"
def metadata(self):
@@ -104,7 +104,7 @@ class GelbooruV01FavoriteExtractor(GelbooruV01Extractor):
directory_fmt = ("{category}", "favorites", "{favorite_id}")
archive_fmt = "f_{favorite_id}_{id}"
per_page = 50
pattern = rf"{BASE_PATTERN}/index\.php\?page=favorites&s=view&id=(\d+)"
pattern = BASE_PATTERN + r"/index\.php\?page=favorites&s=view&id=(\d+)"
example = "https://allgirl.booru.org/index.php?page=favorites&s=view&id=1"
def metadata(self):
@@ -120,7 +120,7 @@ class GelbooruV01FavoriteExtractor(GelbooruV01Extractor):
class GelbooruV01PostExtractor(GelbooruV01Extractor):
subcategory = "post"
archive_fmt = "{id}"
pattern = rf"{BASE_PATTERN}/index\.php\?page=post&s=view&id=(\d+)"
pattern = BASE_PATTERN + r"/index\.php\?page=post&s=view&id=(\d+)"
example = "https://allgirl.booru.org/index.php?page=post&s=view&id=12345"
def posts(self):

View File

@@ -190,7 +190,7 @@ class GelbooruV02TagExtractor(GelbooruV02Extractor):
subcategory = "tag"
directory_fmt = ("{category}", "{search_tags}")
archive_fmt = "t_{search_tags}_{id}"
pattern = rf"{BASE_PATTERN}/index\.php\?page=post&s=list&tags=([^&#]*)"
pattern = BASE_PATTERN + r"/index\.php\?page=post&s=list&tags=([^&#]*)"
example = "https://safebooru.org/index.php?page=post&s=list&tags=TAG"
def posts(self):
@@ -206,7 +206,7 @@ class GelbooruV02PoolExtractor(GelbooruV02Extractor):
subcategory = "pool"
directory_fmt = ("{category}", "pool", "{pool}")
archive_fmt = "p_{pool}_{id}"
pattern = rf"{BASE_PATTERN}/index\.php\?page=pool&s=show&id=(\d+)"
pattern = BASE_PATTERN + r"/index\.php\?page=pool&s=show&id=(\d+)"
example = "https://safebooru.org/index.php?page=pool&s=show&id=12345"
def __init__(self, match):
@@ -257,7 +257,7 @@ class GelbooruV02FavoriteExtractor(GelbooruV02Extractor):
directory_fmt = ("{category}", "favorites", "{favorite_id}")
archive_fmt = "f_{favorite_id}_{id}"
per_page = 50
pattern = rf"{BASE_PATTERN}/index\.php\?page=favorites&s=view&id=(\d+)"
pattern = BASE_PATTERN + r"/index\.php\?page=favorites&s=view&id=(\d+)"
example = "https://safebooru.org/index.php?page=favorites&s=view&id=12345"
def metadata(self):
@@ -275,7 +275,7 @@ class GelbooruV02FavoriteExtractor(GelbooruV02Extractor):
class GelbooruV02PostExtractor(GelbooruV02Extractor):
subcategory = "post"
archive_fmt = "{id}"
pattern = rf"{BASE_PATTERN}/index\.php\?page=post&s=view&id=(\d+)"
pattern = BASE_PATTERN + r"/index\.php\?page=post&s=view&id=(\d+)"
example = "https://safebooru.org/index.php?page=post&s=view&id=12345"
def posts(self):

View File

@@ -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
@@ -41,7 +41,7 @@ class GirlsreleasedExtractor(Extractor):
class GirlsreleasedSetExtractor(GirlsreleasedExtractor):
"""Extractor for girlsreleased galleries"""
subcategory = "set"
pattern = rf"{BASE_PATTERN}/set/(\d+)"
pattern = BASE_PATTERN + r"/set/(\d+)"
example = "https://girlsreleased.com/set/12345"
def items(self):
@@ -65,12 +65,12 @@ class GirlsreleasedSetExtractor(GirlsreleasedExtractor):
class GirlsreleasedModelExtractor(GirlsreleasedExtractor):
"""Extractor for girlsreleased models"""
subcategory = _path = "model"
pattern = rf"{BASE_PATTERN}/model/(\d+(?:/.+)?)"
pattern = BASE_PATTERN + r"/model/(\d+(?:/.+)?)"
example = "https://girlsreleased.com/model/12345/MODEL"
class GirlsreleasedSiteExtractor(GirlsreleasedExtractor):
"""Extractor for girlsreleased sites"""
subcategory = _path = "site"
pattern = rf"{BASE_PATTERN}/site/([^/?#]+(?:/model/\d+/?.*)?)"
pattern = BASE_PATTERN + r"/site/([^/?#]+(?:/model/\d+/?.*)?)"
example = "https://girlsreleased.com/site/SITE"

View File

@@ -60,7 +60,7 @@ class GirlswithmuscleExtractor(Extractor):
class GirlswithmusclePostExtractor(GirlswithmuscleExtractor):
"""Extractor for individual posts on girlswithmuscle.com"""
subcategory = "post"
pattern = rf"{BASE_PATTERN}/(\d+)"
pattern = BASE_PATTERN + r"/(\d+)"
example = "https://www.girlswithmuscle.com/12345/"
def items(self):
@@ -143,7 +143,7 @@ class GirlswithmusclePostExtractor(GirlswithmuscleExtractor):
class GirlswithmuscleSearchExtractor(GirlswithmuscleExtractor):
"""Extractor for search results on girlswithmuscle.com"""
subcategory = "search"
pattern = rf"{BASE_PATTERN}/images/(.*)"
pattern = BASE_PATTERN + r"/images/(.*)"
example = "https://www.girlswithmuscle.com/images/?name=MODEL"
def pages(self):

View File

@@ -123,7 +123,7 @@ class HatenablogEntriesExtractor(HatenablogExtractor):
class HatenablogEntryExtractor(HatenablogExtractor):
"""Extractor for a single entry URL"""
subcategory = "entry"
pattern = rf"{BASE_PATTERN}/entry/([^?#]+){QUERY_RE}"
pattern = BASE_PATTERN + r"/entry/([^?#]+)" + QUERY_RE
example = "https://BLOG.hatenablog.com/entry/PATH"
def __init__(self, match):
@@ -146,21 +146,21 @@ class HatenablogEntryExtractor(HatenablogExtractor):
class HatenablogHomeExtractor(HatenablogEntriesExtractor):
"""Extractor for a blog's home page"""
subcategory = "home"
pattern = rf"{BASE_PATTERN}(/?){QUERY_RE}"
pattern = BASE_PATTERN + r"(/?)" + QUERY_RE
example = "https://BLOG.hatenablog.com"
class HatenablogArchiveExtractor(HatenablogEntriesExtractor):
"""Extractor for a blog's archive page"""
subcategory = "archive"
pattern = (rf"{BASE_PATTERN}(/archive(?:/\d+(?:/\d+(?:/\d+)?)?"
rf"|/category/[^?#]+)?){QUERY_RE}")
pattern = (BASE_PATTERN + r"(/archive(?:/\d+(?:/\d+(?:/\d+)?)?"
r"|/category/[^?#]+)?)" + QUERY_RE)
example = "https://BLOG.hatenablog.com/archive/2024"
class HatenablogSearchExtractor(HatenablogEntriesExtractor):
"""Extractor for a blog's search results"""
subcategory = "search"
pattern = rf"{BASE_PATTERN}(/search){QUERY_RE}"
pattern = BASE_PATTERN + r"(/search)" + QUERY_RE
example = "https://BLOG.hatenablog.com/search?q=QUERY"
allowed_parameters = ("q",)

View File

@@ -23,19 +23,19 @@ class HdoujinBase():
class HdoujinGalleryExtractor(
HdoujinBase, schalenetwork.SchalenetworkGalleryExtractor):
pattern = rf"{BASE_PATTERN}/(?:g|reader)/(\d+)/(\w+)"
pattern = BASE_PATTERN + r"/(?:g|reader)/(\d+)/(\w+)"
example = "https://hdoujin.org/g/12345/67890abcdef/"
class HdoujinSearchExtractor(
HdoujinBase, schalenetwork.SchalenetworkSearchExtractor):
pattern = rf"{BASE_PATTERN}/(?:tag/([^/?#]+)|browse)?(?:/?\?([^#]*))?$"
pattern = BASE_PATTERN + r"/(?:tag/([^/?#]+)|browse)?(?:/?\?([^#]*))?$"
example = "https://hdoujin.org/browse?s=QUERY"
class HdoujinFavoriteExtractor(
HdoujinBase, schalenetwork.SchalenetworkFavoriteExtractor):
pattern = rf"{BASE_PATTERN}/favorites(?:\?([^#]*))?"
pattern = BASE_PATTERN + r"/favorites(?:\?([^#]*))?"
example = "https://hdoujin.org/favorites"

View File

@@ -38,7 +38,7 @@ class HentaicosplaysGalleryExtractor(
directory_fmt = ("{site}", "{title}")
filename_fmt = "{filename}.{extension}"
archive_fmt = "{title}_{filename}"
pattern = rf"{BASE_PATTERN}/(?:image|story)/([\w-]+)"
pattern = BASE_PATTERN + r"/(?:image|story)/([\w-]+)"
example = "https://hentai-cosplay-xxx.com/image/TITLE/"
def __init__(self, match):

View File

@@ -214,7 +214,7 @@ class HentaifoundryExtractor(Extractor):
class HentaifoundryUserExtractor(Dispatch, HentaifoundryExtractor):
"""Extractor for a hentaifoundry user profile"""
pattern = rf"{BASE_PATTERN}/user/([^/?#]+)/profile"
pattern = BASE_PATTERN + r"/user/([^/?#]+)/profile"
example = "https://www.hentai-foundry.com/user/USER/profile"
def items(self):
@@ -235,7 +235,7 @@ class HentaifoundryUserExtractor(Dispatch, HentaifoundryExtractor):
class HentaifoundryPicturesExtractor(HentaifoundryExtractor):
"""Extractor for all pictures of a hentaifoundry user"""
subcategory = "pictures"
pattern = rf"{BASE_PATTERN}/pictures/user/([^/?#]+)(?:/page/(\d+))?/?$"
pattern = BASE_PATTERN + r"/pictures/user/([^/?#]+)(?:/page/(\d+))?/?$"
example = "https://www.hentai-foundry.com/pictures/user/USER"
def __init__(self, match):
@@ -247,7 +247,7 @@ class HentaifoundryScrapsExtractor(HentaifoundryExtractor):
"""Extractor for scraps of a hentaifoundry user"""
subcategory = "scraps"
directory_fmt = ("{category}", "{user}", "Scraps")
pattern = rf"{BASE_PATTERN}/pictures/user/([^/?#]+)/scraps"
pattern = BASE_PATTERN + r"/pictures/user/([^/?#]+)/scraps"
example = "https://www.hentai-foundry.com/pictures/user/USER/scraps"
def __init__(self, match):
@@ -260,7 +260,7 @@ class HentaifoundryFavoriteExtractor(HentaifoundryExtractor):
subcategory = "favorite"
directory_fmt = ("{category}", "{user}", "Favorites")
archive_fmt = "f_{user}_{index}"
pattern = rf"{BASE_PATTERN}/user/([^/?#]+)/faves/pictures"
pattern = BASE_PATTERN + r"/user/([^/?#]+)/faves/pictures"
example = "https://www.hentai-foundry.com/user/USER/faves/pictures"
def __init__(self, match):
@@ -273,7 +273,7 @@ class HentaifoundryTagExtractor(HentaifoundryExtractor):
subcategory = "tag"
directory_fmt = ("{category}", "{search_tags}")
archive_fmt = "t_{search_tags}_{index}"
pattern = rf"{BASE_PATTERN}/pictures/tagged/([^/?#]+)"
pattern = BASE_PATTERN + r"/pictures/tagged/([^/?#]+)"
example = "https://www.hentai-foundry.com/pictures/tagged/TAG"
def __init__(self, match):
@@ -289,7 +289,7 @@ class HentaifoundryRecentExtractor(HentaifoundryExtractor):
subcategory = "recent"
directory_fmt = ("{category}", "Recent Pictures", "{date}")
archive_fmt = "r_{index}"
pattern = rf"{BASE_PATTERN}/pictures/recent/(\d\d\d\d-\d\d-\d\d)"
pattern = BASE_PATTERN + r"/pictures/recent/(\d\d\d\d-\d\d-\d\d)"
example = "https://www.hentai-foundry.com/pictures/recent/1970-01-01"
def __init__(self, match):
@@ -305,7 +305,7 @@ class HentaifoundryPopularExtractor(HentaifoundryExtractor):
subcategory = "popular"
directory_fmt = ("{category}", "Popular Pictures")
archive_fmt = "p_{index}"
pattern = rf"{BASE_PATTERN}/pictures/popular()"
pattern = BASE_PATTERN + r"/pictures/popular()"
example = "https://www.hentai-foundry.com/pictures/popular"
def __init__(self, match):
@@ -339,7 +339,7 @@ class HentaifoundryStoriesExtractor(HentaifoundryExtractor):
"""Extractor for stories of a hentaifoundry user"""
subcategory = "stories"
archive_fmt = "s_{index}"
pattern = rf"{BASE_PATTERN}/stories/user/([^/?#]+)(?:/page/(\d+))?/?$"
pattern = BASE_PATTERN + r"/stories/user/([^/?#]+)(?:/page/(\d+))?/?$"
example = "https://www.hentai-foundry.com/stories/user/USER"
def items(self):
@@ -358,7 +358,7 @@ class HentaifoundryStoryExtractor(HentaifoundryExtractor):
"""Extractor for a hentaifoundry story"""
subcategory = "story"
archive_fmt = "s_{index}"
pattern = rf"{BASE_PATTERN}/stories/user/([^/?#]+)/(\d+)"
pattern = BASE_PATTERN + r"/stories/user/([^/?#]+)/(\d+)"
example = "https://www.hentai-foundry.com/stories/user/USER/12345/TITLE"
skip = Extractor.skip

View File

@@ -67,7 +67,7 @@ class HiperdexBase():
class HiperdexChapterExtractor(HiperdexBase, ChapterExtractor):
"""Extractor for hiperdex manga chapters"""
pattern = rf"{BASE_PATTERN}(/mangas?/([^/?#]+)/([^/?#]+))"
pattern = BASE_PATTERN + r"(/mangas?/([^/?#]+)/([^/?#]+))"
example = "https://hiperdex.com/manga/MANGA/CHAPTER/"
def __init__(self, match):
@@ -89,7 +89,7 @@ class HiperdexChapterExtractor(HiperdexBase, ChapterExtractor):
class HiperdexMangaExtractor(HiperdexBase, MangaExtractor):
"""Extractor for hiperdex manga"""
chapterclass = HiperdexChapterExtractor
pattern = rf"{BASE_PATTERN}(/mangas?/([^/?#]+))/?$"
pattern = BASE_PATTERN + r"(/mangas?/([^/?#]+))/?$"
example = "https://hiperdex.com/manga/MANGA/"
def __init__(self, match):
@@ -125,7 +125,7 @@ class HiperdexArtistExtractor(HiperdexBase, MangaExtractor):
categorytransfer = False
chapterclass = HiperdexMangaExtractor
reverse = False
pattern = rf"{BASE_PATTERN}(/manga-a(?:rtist|uthor)/(?:[^/?#]+))"
pattern = BASE_PATTERN + r"(/manga-a(?:rtist|uthor)/(?:[^/?#]+))"
example = "https://hiperdex.com/manga-artist/NAME/"
def __init__(self, match):

View File

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

View File

@@ -61,7 +61,7 @@ def decode_video_url(url):
class HotleakPostExtractor(HotleakExtractor):
"""Extractor for individual posts on hotleak"""
subcategory = "post"
pattern = (rf"{BASE_PATTERN}/(?!(?:hot|creators|videos|photos)(?:$|/))"
pattern = (BASE_PATTERN + r"/(?!(?:hot|creators|videos|photos)(?:$|/))"
r"([^/]+)/(photo|video)/(\d+)")
example = "https://hotleak.vip/MODEL/photo/12345"
@@ -96,7 +96,7 @@ class HotleakPostExtractor(HotleakExtractor):
class HotleakCreatorExtractor(HotleakExtractor):
"""Extractor for all posts from a hotleak creator"""
subcategory = "creator"
pattern = (rf"{BASE_PATTERN}/(?!(?:hot|creators|videos|photos)(?:$|/))"
pattern = (BASE_PATTERN + r"/(?!(?:hot|creators|videos|photos)(?:$|/))"
r"([^/?#]+)/?$")
example = "https://hotleak.vip/MODEL"
@@ -150,7 +150,7 @@ class HotleakCreatorExtractor(HotleakExtractor):
class HotleakCategoryExtractor(HotleakExtractor):
"""Extractor for hotleak categories"""
subcategory = "category"
pattern = rf"{BASE_PATTERN}/(hot|creators|videos|photos)(?:/?\?([^#]+))?"
pattern = BASE_PATTERN + r"/(hot|creators|videos|photos)(?:/?\?([^#]+))?"
example = "https://hotleak.vip/photos"
def __init__(self, match):
@@ -172,7 +172,7 @@ class HotleakCategoryExtractor(HotleakExtractor):
class HotleakSearchExtractor(HotleakExtractor):
"""Extractor for hotleak search results"""
subcategory = "search"
pattern = rf"{BASE_PATTERN}/search(?:/?\?([^#]+))"
pattern = BASE_PATTERN + r"/search(?:/?\?([^#]+))"
example = "https://hotleak.vip/search?search=QUERY"
def __init__(self, match):

View File

@@ -29,17 +29,17 @@ class IdolcomplexBase():
class IdolcomplexTagExtractor(IdolcomplexBase, sankaku.SankakuTagExtractor):
"""Extractor for idolcomplex tag searches"""
pattern = rf"{BASE_PATTERN}(?:/posts)?/?\?([^#]*)"
pattern = BASE_PATTERN + r"(?:/posts)?/?\?([^#]*)"
example = "https://www.idolcomplex.com/en/posts?tags=TAGS"
class IdolcomplexPoolExtractor(IdolcomplexBase, sankaku.SankakuPoolExtractor):
"""Extractor for idolcomplex pools"""
pattern = rf"{BASE_PATTERN}/pools?/(?:show/)?(\w+)"
pattern = BASE_PATTERN + r"/pools?/(?:show/)?(\w+)"
example = "https://www.idolcomplex.com/en/pools/0123456789abcdef"
class IdolcomplexPostExtractor(IdolcomplexBase, sankaku.SankakuPostExtractor):
"""Extractor for individual idolcomplex posts"""
pattern = rf"{BASE_PATTERN}/posts?(?:/show)?/(\w+)"
pattern = BASE_PATTERN + r"/posts?(?:/show)?/(\w+)"
example = "https://www.idolcomplex.com/en/posts/0123456789abcdef"

View File

@@ -19,7 +19,7 @@ class ImagechestGalleryExtractor(GalleryExtractor):
"""Extractor for image galleries from imgchest.com"""
category = "imagechest"
root = "https://imgchest.com"
pattern = rf"{BASE_PATTERN}/p/([A-Za-z0-9]{{11}})"
pattern = BASE_PATTERN + r"/p/([A-Za-z0-9]{11})"
example = "https://imgchest.com/p/abcdefghijk"
def __init__(self, match):
@@ -78,7 +78,7 @@ class ImagechestUserExtractor(Extractor):
category = "imagechest"
subcategory = "user"
root = "https://imgchest.com"
pattern = rf"{BASE_PATTERN}/u/([^/?#]+)"
pattern = BASE_PATTERN + r"/u/([^/?#]+)"
example = "https://imgchest.com/u/USER"
def items(self):

View File

@@ -39,7 +39,7 @@ class ImagefapExtractor(Extractor):
class ImagefapGalleryExtractor(ImagefapExtractor):
"""Extractor for image galleries from imagefap.com"""
subcategory = "gallery"
pattern = rf"{BASE_PATTERN}/(?:gallery\.php\?gid=|gallery/|pictures/)(\d+)"
pattern = BASE_PATTERN + r"/(?:gallery\.php\?gid=|gallery/|pictures/)(\d+)"
example = "https://www.imagefap.com/gallery/12345"
def __init__(self, match):
@@ -110,7 +110,7 @@ class ImagefapGalleryExtractor(ImagefapExtractor):
class ImagefapImageExtractor(ImagefapExtractor):
"""Extractor for single images from imagefap.com"""
subcategory = "image"
pattern = rf"{BASE_PATTERN}/photo/(\d+)"
pattern = BASE_PATTERN + r"/photo/(\d+)"
example = "https://www.imagefap.com/photo/12345"
def __init__(self, match):
@@ -148,9 +148,9 @@ class ImagefapImageExtractor(ImagefapExtractor):
class ImagefapFolderExtractor(ImagefapExtractor):
"""Extractor for imagefap user folders"""
subcategory = "folder"
pattern = (rf"{BASE_PATTERN}/(?:organizer/|"
rf"(?:usergallery\.php\?user(id)?=([^&#]+)&"
rf"|profile/([^/?#]+)/galleries\?)folderid=)(\d+|-1)")
pattern = (BASE_PATTERN + r"/(?:organizer/|"
r"(?:usergallery\.php\?user(id)?=([^&#]+)&"
r"|profile/([^/?#]+)/galleries\?)folderid=)(\d+|-1)")
example = "https://www.imagefap.com/organizer/12345"
def __init__(self, match):
@@ -206,9 +206,9 @@ class ImagefapFolderExtractor(ImagefapExtractor):
class ImagefapUserExtractor(ImagefapExtractor):
"""Extractor for an imagefap user profile"""
subcategory = "user"
pattern = (rf"{BASE_PATTERN}/(?:"
rf"profile(?:\.php\?user=|/)([^/?#]+)(?:/galleries)?|"
rf"usergallery\.php\?userid=(\d+))(?:$|#)")
pattern = (BASE_PATTERN +
r"/(?:profile(?:\.php\?user=|/)([^/?#]+)(?:/galleries)?"
r"|usergallery\.php\?userid=(\d+))(?:$|#)")
example = "https://www.imagefap.com/profile/USER"
def __init__(self, match):

View File

@@ -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):

View File

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

View File

@@ -22,13 +22,10 @@ class ImgpileExtractor(Extractor):
"{post[title]} ({post[id_slug]})")
archive_fmt = "{post[id_slug]}_{id}"
def items(self):
pass
class ImgpilePostExtractor(ImgpileExtractor):
subcategory = "post"
pattern = rf"{BASE_PATTERN}/p/(\w+)"
pattern = BASE_PATTERN + r"/p/(\w+)"
example = "https://imgpile.com/p/AbCdEfG"
def items(self):
@@ -71,24 +68,23 @@ 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
class ImgpileUserExtractor(ImgpileExtractor):
subcategory = "user"
pattern = rf"{BASE_PATTERN}/u/([^/?#]+)"
pattern = BASE_PATTERN + r"/u/([^/?#]+)"
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")

View File

@@ -67,7 +67,7 @@ class ImgurImageExtractor(ImgurExtractor):
subcategory = "image"
filename_fmt = "{category}_{id}{title:?_//}.{extension}"
archive_fmt = "{id}"
pattern = (rf"{BASE_PATTERN}/(?!gallery|search)"
pattern = (BASE_PATTERN + r"/(?!gallery|search)"
r"(?:r/\w+/)?(?:[^/?#]+-)?(\w{7}|\w{5})[sbtmlh]?")
example = "https://imgur.com/abcdefg"
@@ -93,7 +93,7 @@ class ImgurAlbumExtractor(ImgurExtractor):
directory_fmt = ("{category}", "{album[id]}{album[title]:? - //}")
filename_fmt = "{category}_{album[id]}_{num:>03}_{id}.{extension}"
archive_fmt = "{album[id]}_{id}"
pattern = rf"{BASE_PATTERN}/a/(?:[^/?#]+-)?(\w{{7}}|\w{{5}})"
pattern = BASE_PATTERN + r"/a/(?:[^/?#]+-)?(\w{7}|\w{5})"
example = "https://imgur.com/a/abcde"
def items(self):
@@ -126,8 +126,7 @@ class ImgurAlbumExtractor(ImgurExtractor):
class ImgurGalleryExtractor(ImgurExtractor):
"""Extractor for imgur galleries"""
subcategory = "gallery"
pattern = (rf"{BASE_PATTERN}/"
rf"(?:gallery|t/\w+)/(?:[^/?#]+-)?(\w{{7}}|\w{{5}})")
pattern = BASE_PATTERN + r"/(?:gallery|t/\w+)/(?:[^/?#]+-)?(\w{7}|\w{5})"
example = "https://imgur.com/gallery/abcde"
def items(self):
@@ -143,7 +142,7 @@ class ImgurGalleryExtractor(ImgurExtractor):
class ImgurUserExtractor(ImgurExtractor):
"""Extractor for all images posted by a user"""
subcategory = "user"
pattern = (rf"{BASE_PATTERN}/user/(?!me(?:/|$|\?|#))"
pattern = (BASE_PATTERN + r"/user/(?!me(?:/|$|\?|#))"
r"([^/?#]+)(?:/posts|/submitted)?/?$")
example = "https://imgur.com/user/USER"
@@ -154,7 +153,7 @@ class ImgurUserExtractor(ImgurExtractor):
class ImgurFavoriteExtractor(ImgurExtractor):
"""Extractor for a user's favorites"""
subcategory = "favorite"
pattern = rf"{BASE_PATTERN}/user/([^/?#]+)/favorites/?$"
pattern = BASE_PATTERN + r"/user/([^/?#]+)/favorites/?$"
example = "https://imgur.com/user/USER/favorites"
def items(self):
@@ -164,7 +163,7 @@ class ImgurFavoriteExtractor(ImgurExtractor):
class ImgurFavoriteFolderExtractor(ImgurExtractor):
"""Extractor for a user's favorites folder"""
subcategory = "favorite-folder"
pattern = rf"{BASE_PATTERN}/user/([^/?#]+)/favorites/folder/(\d+)"
pattern = BASE_PATTERN + r"/user/([^/?#]+)/favorites/folder/(\d+)"
example = "https://imgur.com/user/USER/favorites/folder/12345/TITLE"
def __init__(self, match):
@@ -179,7 +178,7 @@ class ImgurFavoriteFolderExtractor(ImgurExtractor):
class ImgurMeExtractor(ImgurExtractor):
"""Extractor for your personal uploads"""
subcategory = "me"
pattern = rf"{BASE_PATTERN}/user/me(?:/posts)?(/hidden)?"
pattern = BASE_PATTERN + r"/user/me(?:/posts)?(/hidden)?"
example = "https://imgur.com/user/me"
def items(self):
@@ -196,7 +195,7 @@ class ImgurMeExtractor(ImgurExtractor):
class ImgurSubredditExtractor(ImgurExtractor):
"""Extractor for a subreddits's imgur links"""
subcategory = "subreddit"
pattern = rf"{BASE_PATTERN}/r/([^/?#]+)/?$"
pattern = BASE_PATTERN + r"/r/([^/?#]+)/?$"
example = "https://imgur.com/r/SUBREDDIT"
def items(self):
@@ -206,7 +205,7 @@ class ImgurSubredditExtractor(ImgurExtractor):
class ImgurTagExtractor(ImgurExtractor):
"""Extractor for imgur tag searches"""
subcategory = "tag"
pattern = rf"{BASE_PATTERN}/t/([^/?#]+)$"
pattern = BASE_PATTERN + r"/t/([^/?#]+)$"
example = "https://imgur.com/t/TAG"
def items(self):
@@ -216,7 +215,7 @@ class ImgurTagExtractor(ImgurExtractor):
class ImgurSearchExtractor(ImgurExtractor):
"""Extractor for imgur search results"""
subcategory = "search"
pattern = rf"{BASE_PATTERN}/search(?:/[^?#]+)?/?\?q=([^&#]+)"
pattern = BASE_PATTERN + r"/search(?:/[^?#]+)?/?\?q=([^&#]+)"
example = "https://imgur.com/search?q=UERY"
def items(self):
@@ -270,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):

View File

@@ -79,7 +79,7 @@ BASE_PATTERN = ImhentaiExtractor.update({
class ImhentaiGalleryExtractor(ImhentaiExtractor, GalleryExtractor):
"""Extractor for imhentai galleries"""
pattern = rf"{BASE_PATTERN}/(?:gallery|view)/(\d+)"
pattern = BASE_PATTERN + r"/(?:gallery|view)/(\d+)"
example = "https://imhentai.xxx/gallery/12345/"
def __init__(self, match):
@@ -141,7 +141,7 @@ class ImhentaiGalleryExtractor(ImhentaiExtractor, GalleryExtractor):
class ImhentaiTagExtractor(ImhentaiExtractor):
"""Extractor for imhentai tag searches"""
subcategory = "tag"
pattern = (rf"{BASE_PATTERN}(/(?:"
pattern = (BASE_PATTERN + r"(/(?:"
r"artist|category|character|group|language|parody|tag"
r")/([^/?#]+))")
example = "https://imhentai.xxx/tag/TAG/"
@@ -154,7 +154,7 @@ class ImhentaiTagExtractor(ImhentaiExtractor):
class ImhentaiSearchExtractor(ImhentaiExtractor):
"""Extractor for imhentai search results"""
subcategory = "search"
pattern = rf"{BASE_PATTERN}(/(?:advanced-)?search/?\?[^#]+|/[^/?#]+/?)"
pattern = BASE_PATTERN + r"(/(?:advanced-)?search/?\?[^#]+|/[^/?#]+/?)"
example = "https://imhentai.xxx/search/?key=QUERY"
def items(self):

View File

@@ -71,7 +71,7 @@ class InkbunnyExtractor(Extractor):
class InkbunnyUserExtractor(InkbunnyExtractor):
"""Extractor for inkbunny user profiles"""
subcategory = "user"
pattern = rf"{BASE_PATTERN}/(?!s/)(gallery/|scraps/)?(\w+)(?:$|[/?#])"
pattern = BASE_PATTERN + r"/(?!s/)(gallery/|scraps/)?(\w+)(?:$|[/?#])"
example = "https://inkbunny.net/USER"
def __init__(self, match):
@@ -101,7 +101,7 @@ class InkbunnyUserExtractor(InkbunnyExtractor):
class InkbunnyPoolExtractor(InkbunnyExtractor):
"""Extractor for inkbunny pools"""
subcategory = "pool"
pattern = (rf"{BASE_PATTERN}/(?:"
pattern = (BASE_PATTERN + r"/(?:"
r"poolview_process\.php\?pool_id=(\d+)|"
r"submissionsviewall\.php"
r"\?((?:[^#]+&)?mode=pool(?:&[^#]+)?))")
@@ -132,7 +132,7 @@ class InkbunnyFavoriteExtractor(InkbunnyExtractor):
"""Extractor for inkbunny user favorites"""
subcategory = "favorite"
directory_fmt = ("{category}", "{favs_username!l}", "Favorites")
pattern = (rf"{BASE_PATTERN}/(?:"
pattern = (BASE_PATTERN + r"/(?:"
r"userfavorites_process\.php\?favs_user_id=(\d+)|"
r"submissionsviewall\.php"
r"\?((?:[^#]+&)?mode=userfavs(?:&[^#]+)?))")
@@ -175,7 +175,7 @@ class InkbunnyFavoriteExtractor(InkbunnyExtractor):
class InkbunnyUnreadExtractor(InkbunnyExtractor):
"""Extractor for unread inkbunny submissions"""
subcategory = "unread"
pattern = (rf"{BASE_PATTERN}/submissionsviewall\.php"
pattern = (BASE_PATTERN + r"/submissionsviewall\.php"
r"\?((?:[^#]+&)?mode=unreadsubs(?:&[^#]+)?)")
example = ("https://inkbunny.net/submissionsviewall.php"
"?text=&mode=unreadsubs&type=")
@@ -195,7 +195,7 @@ class InkbunnyUnreadExtractor(InkbunnyExtractor):
class InkbunnySearchExtractor(InkbunnyExtractor):
"""Extractor for inkbunny search results"""
subcategory = "search"
pattern = (rf"{BASE_PATTERN}/submissionsviewall\.php"
pattern = (BASE_PATTERN + r"/submissionsviewall\.php"
r"\?((?:[^#]+&)?mode=search(?:&[^#]+)?)")
example = ("https://inkbunny.net/submissionsviewall.php"
"?text=TAG&mode=search&type=")
@@ -229,7 +229,7 @@ class InkbunnySearchExtractor(InkbunnyExtractor):
class InkbunnyFollowingExtractor(InkbunnyExtractor):
"""Extractor for inkbunny user watches"""
subcategory = "following"
pattern = (rf"{BASE_PATTERN}/(?:"
pattern = (BASE_PATTERN + r"/(?:"
r"watchlist_process\.php\?mode=watching&user_id=(\d+)|"
r"usersviewall\.php"
r"\?((?:[^#]+&)?mode=watching(?:&[^#]+)?))")
@@ -268,7 +268,7 @@ class InkbunnyFollowingExtractor(InkbunnyExtractor):
class InkbunnyPostExtractor(InkbunnyExtractor):
"""Extractor for individual Inkbunny posts"""
subcategory = "post"
pattern = rf"{BASE_PATTERN}/s/(\d+)"
pattern = BASE_PATTERN + r"/s/(\d+)"
example = "https://inkbunny.net/s/12345"
def __init__(self, match):

View File

@@ -16,7 +16,7 @@ import itertools
import binascii
BASE_PATTERN = r"(?:https?://)?(?:www\.)?instagram\.com"
USER_PATTERN = rf"{BASE_PATTERN}/(?!(?:p|tv|reel|explore|stories)/)([^/?#]+)"
USER_PATTERN = BASE_PATTERN + r"/(?!(?:p|tv|reel|explore|stories)/)([^/?#]+)"
class InstagramExtractor(Extractor):
@@ -505,7 +505,7 @@ class InstagramPostExtractor(InstagramExtractor):
class InstagramUserExtractor(Dispatch, InstagramExtractor):
"""Extractor for an Instagram user profile"""
pattern = rf"{USER_PATTERN}/?(?:$|[?#])"
pattern = USER_PATTERN + r"/?(?:$|[?#])"
example = "https://www.instagram.com/USER/"
def items(self):
@@ -525,7 +525,7 @@ class InstagramUserExtractor(Dispatch, InstagramExtractor):
class InstagramPostsExtractor(InstagramExtractor):
"""Extractor for an Instagram user's posts"""
subcategory = "posts"
pattern = rf"{USER_PATTERN}/posts"
pattern = USER_PATTERN + r"/posts"
example = "https://www.instagram.com/USER/posts/"
def posts(self):
@@ -542,7 +542,7 @@ class InstagramPostsExtractor(InstagramExtractor):
class InstagramReelsExtractor(InstagramExtractor):
"""Extractor for an Instagram user's reels"""
subcategory = "reels"
pattern = rf"{USER_PATTERN}/reels"
pattern = USER_PATTERN + r"/reels"
example = "https://www.instagram.com/USER/reels/"
def posts(self):
@@ -559,7 +559,7 @@ class InstagramReelsExtractor(InstagramExtractor):
class InstagramTaggedExtractor(InstagramExtractor):
"""Extractor for an Instagram user's tagged posts"""
subcategory = "tagged"
pattern = rf"{USER_PATTERN}/tagged"
pattern = USER_PATTERN + r"/tagged"
example = "https://www.instagram.com/USER/tagged/"
def metadata(self):
@@ -585,7 +585,7 @@ class InstagramTaggedExtractor(InstagramExtractor):
class InstagramGuideExtractor(InstagramExtractor):
"""Extractor for an Instagram guide"""
subcategory = "guide"
pattern = rf"{USER_PATTERN}/guide/[^/?#]+/(\d+)"
pattern = USER_PATTERN + r"/guide/[^/?#]+/(\d+)"
example = "https://www.instagram.com/USER/guide/NAME/12345"
def __init__(self, match):
@@ -602,7 +602,7 @@ class InstagramGuideExtractor(InstagramExtractor):
class InstagramSavedExtractor(InstagramExtractor):
"""Extractor for an Instagram user's saved media"""
subcategory = "saved"
pattern = rf"{USER_PATTERN}/saved(?:/all-posts)?/?$"
pattern = USER_PATTERN + r"/saved(?:/all-posts)?/?$"
example = "https://www.instagram.com/USER/saved/"
def posts(self):
@@ -612,7 +612,7 @@ class InstagramSavedExtractor(InstagramExtractor):
class InstagramCollectionExtractor(InstagramExtractor):
"""Extractor for Instagram collection"""
subcategory = "collection"
pattern = rf"{USER_PATTERN}/saved/([^/?#]+)/([^/?#]+)"
pattern = USER_PATTERN + r"/saved/([^/?#]+)/([^/?#]+)"
example = "https://www.instagram.com/USER/saved/COLLECTION/12345"
def __init__(self, match):
@@ -632,11 +632,11 @@ class InstagramCollectionExtractor(InstagramExtractor):
class InstagramStoriesTrayExtractor(InstagramExtractor):
"""Extractor for your Instagram account's stories tray"""
subcategory = "stories-tray"
pattern = rf"{BASE_PATTERN}/stories/me/?$()"
pattern = BASE_PATTERN + r"/stories/me/?$()"
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
@@ -696,7 +696,7 @@ class InstagramStoriesExtractor(InstagramExtractor):
class InstagramHighlightsExtractor(InstagramExtractor):
"""Extractor for an Instagram user's story highlights"""
subcategory = "highlights"
pattern = rf"{USER_PATTERN}/highlights"
pattern = USER_PATTERN + r"/highlights"
example = "https://www.instagram.com/USER/highlights/"
def posts(self):
@@ -707,7 +707,7 @@ class InstagramHighlightsExtractor(InstagramExtractor):
class InstagramFollowersExtractor(InstagramExtractor):
"""Extractor for an Instagram user's followers"""
subcategory = "followers"
pattern = rf"{USER_PATTERN}/followers"
pattern = USER_PATTERN + r"/followers"
example = "https://www.instagram.com/USER/followers/"
def items(self):
@@ -721,7 +721,7 @@ class InstagramFollowersExtractor(InstagramExtractor):
class InstagramFollowingExtractor(InstagramExtractor):
"""Extractor for an Instagram user's followed users"""
subcategory = "following"
pattern = rf"{USER_PATTERN}/following"
pattern = USER_PATTERN + r"/following"
example = "https://www.instagram.com/USER/following/"
def items(self):
@@ -736,7 +736,7 @@ class InstagramTagExtractor(InstagramExtractor):
"""Extractor for Instagram tags"""
subcategory = "tag"
directory_fmt = ("{category}", "{subcategory}", "{tag}")
pattern = rf"{BASE_PATTERN}/explore/tags/([^/?#]+)"
pattern = BASE_PATTERN + r"/explore/tags/([^/?#]+)"
example = "https://www.instagram.com/explore/tags/TAG/"
def metadata(self):
@@ -749,7 +749,7 @@ class InstagramTagExtractor(InstagramExtractor):
class InstagramInfoExtractor(InstagramExtractor):
"""Extractor for an Instagram user's profile data"""
subcategory = "info"
pattern = rf"{USER_PATTERN}/info"
pattern = USER_PATTERN + r"/info"
example = "https://www.instagram.com/USER/info/"
def items(self):
@@ -765,7 +765,7 @@ class InstagramInfoExtractor(InstagramExtractor):
class InstagramAvatarExtractor(InstagramExtractor):
"""Extractor for an Instagram user's avatar"""
subcategory = "avatar"
pattern = rf"{USER_PATTERN}/avatar"
pattern = USER_PATTERN + r"/avatar"
example = "https://www.instagram.com/USER/avatar/"
def posts(self):

View File

@@ -13,7 +13,7 @@ from ..cache import memcache
from .. import text, util
BASE_PATTERN = r"(?:https?://)?itaku\.ee"
USER_PATTERN = rf"{BASE_PATTERN}/profile/([^/?#]+)"
USER_PATTERN = BASE_PATTERN + r"/profile/([^/?#]+)"
class ItakuExtractor(Extractor):
@@ -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
@@ -86,7 +86,7 @@ class ItakuExtractor(Extractor):
class ItakuGalleryExtractor(ItakuExtractor):
"""Extractor for an itaku user's gallery"""
subcategory = "gallery"
pattern = rf"{USER_PATTERN}/gallery(?:/(\d+))?"
pattern = USER_PATTERN + r"/gallery(?:/(\d+))?"
example = "https://itaku.ee/profile/USER/gallery"
def images(self):
@@ -104,7 +104,7 @@ class ItakuPostsExtractor(ItakuExtractor):
"{id}{title:? //}")
filename_fmt = "{file[id]}{file[title]:? //}.{extension}"
archive_fmt = "{id}_{file[id]}"
pattern = rf"{USER_PATTERN}/posts(?:/(\d+))?"
pattern = USER_PATTERN + r"/posts(?:/(\d+))?"
example = "https://itaku.ee/profile/USER/posts"
def posts(self):
@@ -118,7 +118,7 @@ class ItakuPostsExtractor(ItakuExtractor):
class ItakuStarsExtractor(ItakuExtractor):
"""Extractor for an itaku user's starred images"""
subcategory = "stars"
pattern = rf"{USER_PATTERN}/stars(?:/(\d+))?"
pattern = USER_PATTERN + r"/stars(?:/(\d+))?"
example = "https://itaku.ee/profile/USER/stars"
def images(self):
@@ -132,7 +132,7 @@ class ItakuStarsExtractor(ItakuExtractor):
class ItakuFollowingExtractor(ItakuExtractor):
subcategory = "following"
pattern = rf"{USER_PATTERN}/following"
pattern = USER_PATTERN + r"/following"
example = "https://itaku.ee/profile/USER/following"
def users(self):
@@ -143,7 +143,7 @@ class ItakuFollowingExtractor(ItakuExtractor):
class ItakuFollowersExtractor(ItakuExtractor):
subcategory = "followers"
pattern = rf"{USER_PATTERN}/followers"
pattern = USER_PATTERN + r"/followers"
example = "https://itaku.ee/profile/USER/followers"
def users(self):
@@ -155,7 +155,7 @@ class ItakuFollowersExtractor(ItakuExtractor):
class ItakuBookmarksExtractor(ItakuExtractor):
"""Extractor for an itaku bookmarks folder"""
subcategory = "bookmarks"
pattern = rf"{USER_PATTERN}/bookmarks/(image|user)/(\d+)"
pattern = USER_PATTERN + r"/bookmarks/(image|user)/(\d+)"
example = "https://itaku.ee/profile/USER/bookmarks/image/12345"
def _init(self):
@@ -176,23 +176,23 @@ class ItakuBookmarksExtractor(ItakuExtractor):
class ItakuUserExtractor(Dispatch, ItakuExtractor):
"""Extractor for itaku user profiles"""
pattern = rf"{USER_PATTERN}/?(?:$|\?|#)"
pattern = USER_PATTERN + r"/?(?:$|\?|#)"
example = "https://itaku.ee/profile/USER"
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",))
class ItakuImageExtractor(ItakuExtractor):
subcategory = "image"
pattern = rf"{BASE_PATTERN}/images/(\d+)"
pattern = BASE_PATTERN + r"/images/(\d+)"
example = "https://itaku.ee/images/12345"
def images(self):
@@ -205,7 +205,7 @@ class ItakuPostExtractor(ItakuExtractor):
"{id}{title:? //}")
filename_fmt = "{file[id]}{file[title]:? //}.{extension}"
archive_fmt = "{id}_{file[id]}"
pattern = rf"{BASE_PATTERN}/posts/(\d+)"
pattern = BASE_PATTERN + r"/posts/(\d+)"
example = "https://itaku.ee/posts/12345"
def posts(self):
@@ -214,7 +214,7 @@ class ItakuPostExtractor(ItakuExtractor):
class ItakuSearchExtractor(ItakuExtractor):
subcategory = "search"
pattern = rf"{BASE_PATTERN}/home/images/?\?([^#]+)"
pattern = BASE_PATTERN + r"/home/images/?\?([^#]+)"
example = "https://itaku.ee/home/images?tags=SEARCH"
def images(self):
@@ -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)

View File

@@ -12,7 +12,7 @@ from ..cache import cache, memcache
import hashlib
BASE_PATTERN = r"(?:https?://)?(?:www\.)?iwara\.tv"
USER_PATTERN = rf"{BASE_PATTERN}/profile/([^/?#]+)"
USER_PATTERN = BASE_PATTERN + r"/profile/([^/?#]+)"
class IwaraExtractor(Extractor):
@@ -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":
@@ -158,21 +158,21 @@ class IwaraExtractor(Extractor):
class IwaraUserExtractor(Dispatch, IwaraExtractor):
"""Extractor for iwara.tv profile pages"""
pattern = rf"{USER_PATTERN}/?$"
pattern = USER_PATTERN + r"/?$"
example = "https://www.iwara.tv/profile/USERNAME"
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"))
class IwaraUserImagesExtractor(IwaraExtractor):
subcategory = "user-images"
pattern = rf"{USER_PATTERN}/images(?:\?([^#]+))?"
pattern = USER_PATTERN + r"/images(?:\?([^#]+))?"
example = "https://www.iwara.tv/profile/USERNAME/images"
def items(self):
@@ -182,7 +182,7 @@ class IwaraUserImagesExtractor(IwaraExtractor):
class IwaraUserVideosExtractor(IwaraExtractor):
subcategory = "user-videos"
pattern = rf"{USER_PATTERN}/videos(?:\?([^#]+))?"
pattern = USER_PATTERN + r"/videos(?:\?([^#]+))?"
example = "https://www.iwara.tv/profile/USERNAME/videos"
def items(self):
@@ -192,22 +192,22 @@ class IwaraUserVideosExtractor(IwaraExtractor):
class IwaraUserPlaylistsExtractor(IwaraExtractor):
subcategory = "user-playlists"
pattern = rf"{USER_PATTERN}/playlists(?:\?([^#]+))?"
pattern = USER_PATTERN + r"/playlists(?:\?([^#]+))?"
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
class IwaraFollowingExtractor(IwaraExtractor):
subcategory = "following"
pattern = rf"{USER_PATTERN}/following"
pattern = USER_PATTERN + r"/following"
example = "https://www.iwara.tv/profile/USERNAME/following"
def items(self):
@@ -217,7 +217,7 @@ class IwaraFollowingExtractor(IwaraExtractor):
class IwaraFollowersExtractor(IwaraExtractor):
subcategory = "followers"
pattern = rf"{USER_PATTERN}/followers"
pattern = USER_PATTERN + r"/followers"
example = "https://www.iwara.tv/profile/USERNAME/followers"
def items(self):
@@ -228,7 +228,7 @@ class IwaraFollowersExtractor(IwaraExtractor):
class IwaraImageExtractor(IwaraExtractor):
"""Extractor for individual iwara.tv image pages"""
subcategory = "image"
pattern = rf"{BASE_PATTERN}/image/([^/?#]+)"
pattern = BASE_PATTERN + r"/image/([^/?#]+)"
example = "https://www.iwara.tv/image/ID"
def items(self):
@@ -238,7 +238,7 @@ class IwaraImageExtractor(IwaraExtractor):
class IwaraVideoExtractor(IwaraExtractor):
"""Extractor for individual iwara.tv videos"""
subcategory = "video"
pattern = rf"{BASE_PATTERN}/video/([^/?#]+)"
pattern = BASE_PATTERN + r"/video/([^/?#]+)"
example = "https://www.iwara.tv/video/ID"
def items(self):
@@ -248,7 +248,7 @@ class IwaraVideoExtractor(IwaraExtractor):
class IwaraPlaylistExtractor(IwaraExtractor):
"""Extractor for individual iwara.tv playlist pages"""
subcategory = "playlist"
pattern = rf"{BASE_PATTERN}/playlist/([^/?#]+)"
pattern = BASE_PATTERN + r"/playlist/([^/?#]+)"
example = "https://www.iwara.tv/playlist/ID"
def items(self):
@@ -257,7 +257,7 @@ class IwaraPlaylistExtractor(IwaraExtractor):
class IwaraFavoriteExtractor(IwaraExtractor):
subcategory = "favorite"
pattern = rf"{BASE_PATTERN}/favorites(?:/(image|video)s)?"
pattern = BASE_PATTERN + r"/favorites(?:/(image|video)s)?"
example = "https://www.iwara.tv/favorites/videos"
def items(self):
@@ -268,7 +268,7 @@ class IwaraFavoriteExtractor(IwaraExtractor):
class IwaraSearchExtractor(IwaraExtractor):
"""Extractor for iwara.tv search pages"""
subcategory = "search"
pattern = rf"{BASE_PATTERN}/search\?([^#]+)"
pattern = BASE_PATTERN + r"/search\?([^#]+)"
example = "https://www.iwara.tv/search?query=QUERY&type=TYPE"
def items(self):
@@ -281,7 +281,7 @@ class IwaraSearchExtractor(IwaraExtractor):
class IwaraTagExtractor(IwaraExtractor):
"""Extractor for iwara.tv tag search"""
subcategory = "tag"
pattern = rf"{BASE_PATTERN}/(image|video)s(?:\?([^#]+))?"
pattern = BASE_PATTERN + r"/(image|video)s(?:\?([^#]+))?"
example = "https://www.iwara.tv/videos?tags=TAGS"
def items(self):
@@ -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:

View File

@@ -30,7 +30,7 @@ class JschanThreadExtractor(JschanExtractor):
"{threadId} {subject|nomarkup[:50]}")
filename_fmt = "{postId}{num:?-//} {filename}.{extension}"
archive_fmt = "{board}_{postId}_{num}"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/thread/(\d+)\.html"
pattern = BASE_PATTERN + r"/([^/?#]+)/thread/(\d+)\.html"
example = "https://94chan.org/a/thread/12345.html"
def items(self):
@@ -56,7 +56,7 @@ class JschanThreadExtractor(JschanExtractor):
class JschanBoardExtractor(JschanExtractor):
"""Extractor for jschan boards"""
subcategory = "board"
pattern = (rf"{BASE_PATTERN}/([^/?#]+)"
pattern = (BASE_PATTERN + r"/([^/?#]+)"
r"(?:/index\.html|/catalog\.html|/\d+\.html|/?$)")
example = "https://94chan.org/a/"

View File

@@ -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,

View File

@@ -16,7 +16,7 @@ import json
BASE_PATTERN = (r"(?:https?://)?(?:www\.|beta\.)?"
r"(kemono|coomer)\.(cr|s[tu]|party)")
USER_PATTERN = rf"{BASE_PATTERN}/([^/?#]+)/user/([^/?#]+)"
USER_PATTERN = BASE_PATTERN + r"/([^/?#]+)/user/([^/?#]+)"
HASH_PATTERN = r"/[0-9a-f]{2}/[0-9a-f]{2}/([0-9a-f]{64})"
@@ -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)
@@ -322,7 +322,7 @@ def _validate(response):
class KemonoUserExtractor(KemonoExtractor):
"""Extractor for all posts from a kemono.cr user listing"""
subcategory = "user"
pattern = rf"{USER_PATTERN}/?(?:\?([^#]+))?(?:$|\?|#)"
pattern = USER_PATTERN + r"/?(?:\?([^#]+))?(?:$|\?|#)"
example = "https://kemono.cr/SERVICE/user/12345"
def __init__(self, match):
@@ -345,7 +345,7 @@ class KemonoUserExtractor(KemonoExtractor):
class KemonoPostsExtractor(KemonoExtractor):
"""Extractor for kemono.cr post listings"""
subcategory = "posts"
pattern = rf"{BASE_PATTERN}/posts()()(?:/?\?([^#]+))?"
pattern = BASE_PATTERN + r"/posts()()(?:/?\?([^#]+))?"
example = "https://kemono.cr/posts"
def posts(self):
@@ -357,7 +357,7 @@ class KemonoPostsExtractor(KemonoExtractor):
class KemonoPostExtractor(KemonoExtractor):
"""Extractor for a single kemono.cr post"""
subcategory = "post"
pattern = rf"{USER_PATTERN}/post/([^/?#]+)(/revisions?(?:/(\d*))?)?"
pattern = USER_PATTERN + r"/post/([^/?#]+)(/revisions?(?:/(\d*))?)?"
example = "https://kemono.cr/SERVICE/user/12345/post/12345"
def __init__(self, match):
@@ -390,7 +390,7 @@ class KemonoDiscordExtractor(KemonoExtractor):
"{server_id} {server}", "{channel_id} {channel}")
filename_fmt = "{id}_{num:>02}_{filename}.{extension}"
archive_fmt = "discord_{server_id}_{id}_{num}"
pattern = rf"{BASE_PATTERN}/discord/server/(\d+)[/#](?:channel/)?(\d+)"
pattern = BASE_PATTERN + r"/discord/server/(\d+)[/#](?:channel/)?(\d+)"
example = "https://kemono.cr/discord/server/12345/12345"
def items(self):
@@ -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)
@@ -460,7 +460,7 @@ class KemonoDiscordExtractor(KemonoExtractor):
class KemonoDiscordServerExtractor(KemonoExtractor):
subcategory = "discord-server"
pattern = rf"{BASE_PATTERN}/discord/server/(\d+)$"
pattern = BASE_PATTERN + r"/discord/server/(\d+)$"
example = "https://kemono.cr/discord/server/12345"
def items(self):
@@ -488,7 +488,7 @@ def discord_server_info(extr, server_id):
class KemonoFavoriteExtractor(KemonoExtractor):
"""Extractor for kemono.cr favorites"""
subcategory = "favorite"
pattern = rf"{BASE_PATTERN}/(?:account/)?favorites()()(?:/?\?([^#]+))?"
pattern = BASE_PATTERN + r"/(?:account/)?favorites()()(?:/?\?([^#]+))?"
example = "https://kemono.cr/account/favorites/artists"
def items(self):
@@ -536,7 +536,7 @@ class KemonoFavoriteExtractor(KemonoExtractor):
class KemonoArtistsExtractor(KemonoExtractor):
"""Extractor for kemono artists"""
subcategory = "artists"
pattern = rf"{BASE_PATTERN}/artists(?:\?([^#]+))?"
pattern = BASE_PATTERN + r"/artists(?:\?([^#]+))?"
example = "https://kemono.cr/artists"
def items(self):
@@ -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):

View File

@@ -44,7 +44,7 @@ class KomikcastBase():
class KomikcastChapterExtractor(KomikcastBase, ChapterExtractor):
"""Extractor for komikcast manga chapters"""
pattern = rf"{BASE_PATTERN}(/chapter/[^/?#]+/)"
pattern = BASE_PATTERN + r"(/chapter/[^/?#]+/)"
example = "https://komikcast.li/chapter/TITLE/"
def metadata(self, page):
@@ -64,7 +64,7 @@ class KomikcastChapterExtractor(KomikcastBase, ChapterExtractor):
class KomikcastMangaExtractor(KomikcastBase, MangaExtractor):
"""Extractor for komikcast manga"""
chapterclass = KomikcastChapterExtractor
pattern = rf"{BASE_PATTERN}(/(?:komik/)?[^/?#]+/?)$"
pattern = BASE_PATTERN + r"(/(?:komik/)?[^/?#]+/?)$"
example = "https://komikcast.li/komik/TITLE"
def chapters(self, page):

View File

@@ -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
@@ -81,7 +81,7 @@ class LeakgalleryUserExtractor(LeakgalleryExtractor):
class LeakgalleryTrendingExtractor(LeakgalleryExtractor):
"""Extractor for trending posts on leakgallery.com"""
subcategory = "trending"
pattern = rf"{BASE_PATTERN}/trending-medias(?:/([\w-]+))?"
pattern = BASE_PATTERN + r"/trending-medias(?:/([\w-]+))?"
example = "https://leakgallery.com/trending-medias/Week"
def items(self):
@@ -93,7 +93,7 @@ class LeakgalleryTrendingExtractor(LeakgalleryExtractor):
class LeakgalleryMostlikedExtractor(LeakgalleryExtractor):
"""Extractor for most liked posts on leakgallery.com"""
subcategory = "mostliked"
pattern = rf"{BASE_PATTERN}/most-liked"
pattern = BASE_PATTERN + r"/most-liked"
example = "https://leakgallery.com/most-liked"
def items(self):
@@ -104,7 +104,7 @@ class LeakgalleryMostlikedExtractor(LeakgalleryExtractor):
class LeakgalleryPostExtractor(LeakgalleryExtractor):
"""Extractor for individual posts on leakgallery.com"""
subcategory = "post"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/(\d+)"
pattern = BASE_PATTERN + r"/([^/?#]+)/(\d+)"
example = "https://leakgallery.com/CREATOR/12345"
def items(self):

View File

@@ -31,7 +31,7 @@ class LensdumpBase():
class LensdumpAlbumExtractor(LensdumpBase, GalleryExtractor):
subcategory = "album"
pattern = rf"{BASE_PATTERN}/a/(\w+)(?:/?\?([^#]+))?"
pattern = BASE_PATTERN + r"/a/(\w+)(?:/?\?([^#]+))?"
example = "https://lensdump.com/a/ID"
def __init__(self, match):
@@ -76,7 +76,7 @@ class LensdumpAlbumExtractor(LensdumpBase, GalleryExtractor):
class LensdumpAlbumsExtractor(LensdumpBase, Extractor):
"""Extractor for album list from lensdump.com"""
subcategory = "albums"
pattern = rf"{BASE_PATTERN}/(?![ai]/)([^/?#]+)(?:/?\?([^#]+))?"
pattern = BASE_PATTERN + r"/(?![ai]/)([^/?#]+)(?:/?\?([^#]+))?"
example = "https://lensdump.com/USER"
def items(self):

View File

@@ -25,7 +25,7 @@ BASE_PATTERN = LolisafeExtractor.update({
class LolisafeAlbumExtractor(LolisafeExtractor):
subcategory = "album"
pattern = rf"{BASE_PATTERN}/a/([^/?#]+)"
pattern = BASE_PATTERN + "/a/([^/?#]+)"
example = "https://xbunkr.com/a/ID"
def __init__(self, match):

View File

@@ -39,7 +39,7 @@ class LynxchanThreadExtractor(LynxchanExtractor):
"{threadId} {subject|message[:50]}")
filename_fmt = "{postId}{num:?-//} {filename}.{extension}"
archive_fmt = "{boardUri}_{postId}_{num}"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/res/(\d+)"
pattern = BASE_PATTERN + r"/([^/?#]+)/res/(\d+)"
example = "https://endchan.org/a/res/12345.html"
def items(self):
@@ -63,7 +63,7 @@ class LynxchanThreadExtractor(LynxchanExtractor):
class LynxchanBoardExtractor(LynxchanExtractor):
"""Extractor for LynxChan boards"""
subcategory = "board"
pattern = rf"{BASE_PATTERN}/([^/?#]+)(?:/index|/catalog|/\d+|/?$)"
pattern = BASE_PATTERN + r"/([^/?#]+)(?:/index|/catalog|/\d+|/?$)"
example = "https://endchan.org/a/"
def items(self):

View File

@@ -25,7 +25,7 @@ class MadokamiMangaExtractor(MadokamiExtractor):
subcategory = "manga"
directory_fmt = ("{category}", "{manga}")
archive_fmt = "{chapter_id}"
pattern = rf"{BASE_PATTERN}/Manga/(\w/\w{{2}}/\w{{4}}/.+)"
pattern = BASE_PATTERN + r"/Manga/(\w/\w{2}/\w{4}/.+)"
example = "https://manga.madokami.al/Manga/A/AB/ABCD/ABCDE_TITLE"
def items(self):
@@ -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

View File

@@ -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"]
@@ -83,8 +83,8 @@ class MangadexCoversExtractor(MangadexExtractor):
directory_fmt = ("{category}", "{manga}", "Covers")
filename_fmt = "{volume:>02}_{lang}.{extension}"
archive_fmt = "c_{cover_id}"
pattern = (rf"{BASE_PATTERN}/(?:title|manga)/(?!follows|feed$)([0-9a-f-]+)"
rf"(?:/[^/?#]+)?\?tab=art")
pattern = (BASE_PATTERN + r"/(?:title|manga)/(?!follows|feed$)([0-9a-f-]+)"
r"(?:/[^/?#]+)?\?tab=art")
example = ("https://mangadex.org/title"
"/01234567-89ab-cdef-0123-456789abcdef?tab=art")
@@ -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)
@@ -117,7 +117,7 @@ class MangadexCoversExtractor(MangadexExtractor):
class MangadexChapterExtractor(MangadexExtractor):
"""Extractor for manga-chapters from mangadex.org"""
subcategory = "chapter"
pattern = rf"{BASE_PATTERN}/chapter/([0-9a-f-]+)"
pattern = BASE_PATTERN + r"/chapter/([0-9a-f-]+)"
example = ("https://mangadex.org/chapter"
"/01234567-89ab-cdef-0123-456789abcdef")
@@ -148,15 +148,15 @@ 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):
"""Extractor for manga from mangadex.org"""
subcategory = "manga"
pattern = rf"{BASE_PATTERN}/(?:title|manga)/(?!follows|feed$)([0-9a-f-]+)"
pattern = BASE_PATTERN + r"/(?:title|manga)/(?!follows|feed$)([0-9a-f-]+)"
example = ("https://mangadex.org/title"
"/01234567-89ab-cdef-0123-456789abcdef")
@@ -167,7 +167,7 @@ class MangadexMangaExtractor(MangadexExtractor):
class MangadexFeedExtractor(MangadexExtractor):
"""Extractor for chapters from your Updates Feed"""
subcategory = "feed"
pattern = rf"{BASE_PATTERN}/titles?/feed$()"
pattern = BASE_PATTERN + r"/titles?/feed$()"
example = "https://mangadex.org/title/feed"
def chapters(self):
@@ -177,7 +177,7 @@ class MangadexFeedExtractor(MangadexExtractor):
class MangadexFollowingExtractor(MangadexExtractor):
"""Extractor for followed manga from your Library"""
subcategory = "following"
pattern = rf"{BASE_PATTERN}/titles?/follows(?:\?([^#]+))?$"
pattern = BASE_PATTERN + r"/titles?/follows(?:\?([^#]+))?$"
example = "https://mangadex.org/title/follows"
items = MangadexExtractor._items_manga
@@ -189,8 +189,8 @@ class MangadexFollowingExtractor(MangadexExtractor):
class MangadexListExtractor(MangadexExtractor):
"""Extractor for mangadex MDLists"""
subcategory = "list"
pattern = (rf"{BASE_PATTERN}"
rf"/list/([0-9a-f-]+)(?:/[^/?#]*)?(?:\?tab=(\w+))?")
pattern = (BASE_PATTERN +
r"/list/([0-9a-f-]+)(?:/[^/?#]*)?(?:\?tab=(\w+))?")
example = ("https://mangadex.org/list"
"/01234567-89ab-cdef-0123-456789abcdef/NAME")
@@ -215,7 +215,7 @@ class MangadexListExtractor(MangadexExtractor):
class MangadexAuthorExtractor(MangadexExtractor):
"""Extractor for mangadex authors"""
subcategory = "author"
pattern = rf"{BASE_PATTERN}/author/([0-9a-f-]+)"
pattern = BASE_PATTERN + r"/author/([0-9a-f-]+)"
example = ("https://mangadex.org/author"
"/01234567-89ab-cdef-0123-456789abcdef/NAME")
@@ -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:

View File

@@ -31,8 +31,8 @@ class MangafireChapterExtractor(MangafireBase, ChapterExtractor):
"{page:>03}.{extension}")
archive_fmt = (
"{manga_id}_{chapter_id}_{page}")
pattern = (rf"{BASE_PATTERN}/read/([\w-]+\.(\w+))/([\w-]+)"
rf"/((chapter|volume)-\d+(?:\D.*)?)")
pattern = (BASE_PATTERN + r"/read/([\w-]+\.(\w+))/([\w-]+)"
r"/((chapter|volume)-\d+(?:\D.*)?)")
example = "https://mangafire.to/read/MANGA.ID/LANG/chapter-123"
def metadata(self, _):
@@ -64,7 +64,7 @@ class MangafireChapterExtractor(MangafireBase, ChapterExtractor):
class MangafireMangaExtractor(MangafireBase, MangaExtractor):
"""Extractor for mangafire manga"""
chapterclass = MangafireChapterExtractor
pattern = rf"{BASE_PATTERN}/manga/([\w-]+)\.(\w+)"
pattern = BASE_PATTERN + r"/manga/([\w-]+)\.(\w+)"
example = "https://mangafire.to/manga/MANGA.ID"
def chapters(self, page):
@@ -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="', '"')),

View File

@@ -18,8 +18,8 @@ class MangafoxChapterExtractor(ChapterExtractor):
"""Extractor for manga chapters from fanfox.net"""
category = "mangafox"
root = "https://m.fanfox.net"
pattern = (rf"{BASE_PATTERN}"
rf"(/manga/[^/?#]+/((?:v([^/?#]+)/)?c(\d+)([^/?#]*)))")
pattern = BASE_PATTERN + \
r"(/manga/[^/?#]+/((?:v([^/?#]+)/)?c(\d+)([^/?#]*)))"
example = "https://fanfox.net/manga/TITLE/v01/c001/1.html"
def __init__(self, match):
@@ -62,7 +62,7 @@ class MangafoxMangaExtractor(MangaExtractor):
category = "mangafox"
root = "https://m.fanfox.net"
chapterclass = MangafoxChapterExtractor
pattern = rf"{BASE_PATTERN}(/manga/[^/?#]+)/?$"
pattern = BASE_PATTERN + r"(/manga/[^/?#]+)/?$"
example = "https://fanfox.net/manga/TITLE"
def chapters(self, page):

View File

@@ -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 + "/"

View File

@@ -39,7 +39,7 @@ BASE_PATTERN = ManganeloExtractor.update({
class ManganeloChapterExtractor(ManganeloExtractor, ChapterExtractor):
"""Extractor for manganelo manga chapters"""
pattern = rf"{BASE_PATTERN}(/manga/[^/?#]+/chapter-[^/?#]+)"
pattern = BASE_PATTERN + r"(/manga/[^/?#]+/chapter-[^/?#]+)"
example = "https://www.mangakakalot.gg/manga/MANGA_NAME/chapter-123"
def __init__(self, match):
@@ -86,7 +86,7 @@ class ManganeloChapterExtractor(ManganeloExtractor, ChapterExtractor):
class ManganeloMangaExtractor(ManganeloExtractor, MangaExtractor):
"""Extractor for manganelo manga"""
chapterclass = ManganeloChapterExtractor
pattern = rf"{BASE_PATTERN}(/manga/[^/?#]+)$"
pattern = BASE_PATTERN + r"(/manga/[^/?#]+)$"
example = "https://www.mangakakalot.gg/manga/MANGA_NAME"
def __init__(self, match):

View File

@@ -70,8 +70,8 @@ class MangaparkBase():
class MangaparkChapterExtractor(MangaparkBase, ChapterExtractor):
"""Extractor for manga-chapters from mangapark.net"""
pattern = (rf"{BASE_PATTERN}/"
rf"(?:title/[^/?#]+/|comic/\d+/[^/?#]+/[^/?#]+-i)(\d+)")
pattern = (BASE_PATTERN +
r"/(?:title/[^/?#]+/|comic/\d+/[^/?#]+/[^/?#]+-i)(\d+)")
example = "https://mangapark.net/title/MANGA/12345-en-ch.01"
def __init__(self, match):
@@ -111,7 +111,7 @@ class MangaparkChapterExtractor(MangaparkBase, ChapterExtractor):
class MangaparkMangaExtractor(MangaparkBase, Extractor):
"""Extractor for manga from mangapark.net"""
subcategory = "manga"
pattern = rf"{BASE_PATTERN}/(?:title|comic)/(\d+)(?:[/-][^/?#]*)?/?$"
pattern = BASE_PATTERN + r"/(?:title|comic)/(\d+)(?:[/-][^/?#]*)?/?$"
example = "https://mangapark.net/title/12345-MANGA"
def __init__(self, match):

View File

@@ -31,8 +31,8 @@ class MangareaderChapterExtractor(MangareaderBase, ChapterExtractor):
"{page:>03}.{extension}")
archive_fmt = (
"{manga_id}_{chapter_id}_{page}")
pattern = (rf"{BASE_PATTERN}/read/([\w-]+-\d+)/([^/?#]+)"
rf"/(chapter|volume)-(\d+[^/?#]*)")
pattern = (BASE_PATTERN + r"/read/([\w-]+-\d+)/([^/?#]+)"
r"/(chapter|volume)-(\d+[^/?#]*)")
example = "https://mangareader.to/read/MANGA-123/LANG/chapter-123"
def metadata(self, _):
@@ -81,7 +81,7 @@ class MangareaderChapterExtractor(MangareaderBase, ChapterExtractor):
class MangareaderMangaExtractor(MangareaderBase, MangaExtractor):
"""Extractor for mangareader manga"""
chapterclass = MangareaderChapterExtractor
pattern = rf"{BASE_PATTERN}/([\w-]+-\d+)"
pattern = BASE_PATTERN + r"/([\w-]+-\d+)"
example = "https://mangareader.to/MANGA-123"
def chapters(self, page):
@@ -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,
}

View File

@@ -23,7 +23,7 @@ class MangataroBase():
class MangataroChapterExtractor(MangataroBase, ChapterExtractor):
"""Extractor for mangataro manga chapters"""
pattern = rf"{BASE_PATTERN}(/read/([^/?#]+)/(?:[^/?#]*-)?(\d+))"
pattern = BASE_PATTERN + r"(/read/([^/?#]+)/(?:[^/?#]*-)?(\d+))"
example = "https://mangataro.org/read/MANGA/ch123-12345"
def metadata(self, page):
@@ -59,7 +59,7 @@ class MangataroChapterExtractor(MangataroBase, ChapterExtractor):
class MangataroMangaExtractor(MangataroBase, MangaExtractor):
"""Extractor for mangataro manga"""
chapterclass = MangataroChapterExtractor
pattern = rf"{BASE_PATTERN}(/manga/([^/?#]+))"
pattern = BASE_PATTERN + r"(/manga/([^/?#]+))"
example = "https://mangataro.org/manga/MANGA"
def chapters(self, page):
@@ -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

View File

@@ -118,7 +118,7 @@ BASE_PATTERN = MastodonExtractor.update({
class MastodonUserExtractor(MastodonExtractor):
"""Extractor for all images of an account/user"""
subcategory = "user"
pattern = rf"{BASE_PATTERN}/(?:@|users/)([^/?#]+)(?:/media)?/?$"
pattern = BASE_PATTERN + r"/(?:@|users/)([^/?#]+)(?:/media)?/?$"
example = "https://mastodon.social/@USER"
def statuses(self):
@@ -138,7 +138,7 @@ class MastodonUserExtractor(MastodonExtractor):
class MastodonBookmarkExtractor(MastodonExtractor):
"""Extractor for mastodon bookmarks"""
subcategory = "bookmark"
pattern = rf"{BASE_PATTERN}/bookmarks"
pattern = BASE_PATTERN + r"/bookmarks"
example = "https://mastodon.social/bookmarks"
def statuses(self):
@@ -148,7 +148,7 @@ class MastodonBookmarkExtractor(MastodonExtractor):
class MastodonFavoriteExtractor(MastodonExtractor):
"""Extractor for mastodon favorites"""
subcategory = "favorite"
pattern = rf"{BASE_PATTERN}/favourites"
pattern = BASE_PATTERN + r"/favourites"
example = "https://mastodon.social/favourites"
def statuses(self):
@@ -158,7 +158,7 @@ class MastodonFavoriteExtractor(MastodonExtractor):
class MastodonListExtractor(MastodonExtractor):
"""Extractor for mastodon lists"""
subcategory = "list"
pattern = rf"{BASE_PATTERN}/lists/(\w+)"
pattern = BASE_PATTERN + r"/lists/(\w+)"
example = "https://mastodon.social/lists/12345"
def statuses(self):
@@ -168,7 +168,7 @@ class MastodonListExtractor(MastodonExtractor):
class MastodonHashtagExtractor(MastodonExtractor):
"""Extractor for mastodon hashtags"""
subcategory = "hashtag"
pattern = rf"{BASE_PATTERN}/tags/(\w+)"
pattern = BASE_PATTERN + r"/tags/(\w+)"
example = "https://mastodon.social/tags/NAME"
def statuses(self):
@@ -178,7 +178,7 @@ class MastodonHashtagExtractor(MastodonExtractor):
class MastodonFollowingExtractor(MastodonExtractor):
"""Extractor for followed mastodon users"""
subcategory = "following"
pattern = rf"{BASE_PATTERN}/(?:@|users/)([^/?#]+)/following"
pattern = BASE_PATTERN + r"/(?:@|users/)([^/?#]+)/following"
example = "https://mastodon.social/@USER/following"
def items(self):
@@ -193,7 +193,7 @@ class MastodonFollowingExtractor(MastodonExtractor):
class MastodonStatusExtractor(MastodonExtractor):
"""Extractor for images from a status"""
subcategory = "status"
pattern = (rf"{BASE_PATTERN}/(?:@[^/?#]+|(?:users/[^/?#]+/)?"
pattern = (BASE_PATTERN + r"/(?:@[^/?#]+|(?:users/[^/?#]+/)?"
r"(?:statuses|notice|objects()))/(?!following)([^/?#]+)")
example = "https://mastodon.social/@USER/12345"

View File

@@ -102,7 +102,7 @@ BASE_PATTERN = MisskeyExtractor.update({
class MisskeyUserExtractor(Dispatch, MisskeyExtractor):
"""Extractor for all images of a Misskey user"""
subcategory = "user"
pattern = rf"{BASE_PATTERN}/@([^/?#]+)/?$"
pattern = BASE_PATTERN + r"/@([^/?#]+)/?$"
example = "https://misskey.io/@USER"
def items(self):
@@ -118,7 +118,7 @@ class MisskeyUserExtractor(Dispatch, MisskeyExtractor):
class MisskeyNotesExtractor(MisskeyExtractor):
"""Extractor for a Misskey user's notes"""
subcategory = "notes"
pattern = rf"{BASE_PATTERN}/@([^/?#]+)/notes"
pattern = BASE_PATTERN + r"/@([^/?#]+)/notes"
example = "https://misskey.io/@USER/notes"
def notes(self):
@@ -129,7 +129,7 @@ class MisskeyNotesExtractor(MisskeyExtractor):
class MisskeyInfoExtractor(MisskeyExtractor):
"""Extractor for a Misskey user's profile data"""
subcategory = "info"
pattern = rf"{BASE_PATTERN}/@([^/?#]+)/info"
pattern = BASE_PATTERN + r"/@([^/?#]+)/info"
example = "https://misskey.io/@USER/info"
def items(self):
@@ -140,7 +140,7 @@ class MisskeyInfoExtractor(MisskeyExtractor):
class MisskeyAvatarExtractor(MisskeyExtractor):
"""Extractor for a Misskey user's avatar"""
subcategory = "avatar"
pattern = rf"{BASE_PATTERN}/@([^/?#]+)/avatar"
pattern = BASE_PATTERN + r"/@([^/?#]+)/avatar"
example = "https://misskey.io/@USER/avatar"
def notes(self):
@@ -152,7 +152,7 @@ class MisskeyAvatarExtractor(MisskeyExtractor):
class MisskeyBackgroundExtractor(MisskeyExtractor):
"""Extractor for a Misskey user's banner image"""
subcategory = "background"
pattern = rf"{BASE_PATTERN}/@([^/?#]+)/ba(?:nner|ckground)"
pattern = BASE_PATTERN + r"/@([^/?#]+)/ba(?:nner|ckground)"
example = "https://misskey.io/@USER/banner"
def notes(self):
@@ -164,7 +164,7 @@ class MisskeyBackgroundExtractor(MisskeyExtractor):
class MisskeyFollowingExtractor(MisskeyExtractor):
"""Extractor for followed Misskey users"""
subcategory = "following"
pattern = rf"{BASE_PATTERN}/@([^/?#]+)/following"
pattern = BASE_PATTERN + r"/@([^/?#]+)/following"
example = "https://misskey.io/@USER/following"
def items(self):
@@ -181,7 +181,7 @@ class MisskeyFollowingExtractor(MisskeyExtractor):
class MisskeyNoteExtractor(MisskeyExtractor):
"""Extractor for images from a Note"""
subcategory = "note"
pattern = rf"{BASE_PATTERN}/notes/(\w+)"
pattern = BASE_PATTERN + r"/notes/(\w+)"
example = "https://misskey.io/notes/98765"
def notes(self):
@@ -191,7 +191,7 @@ class MisskeyNoteExtractor(MisskeyExtractor):
class MisskeyFavoriteExtractor(MisskeyExtractor):
"""Extractor for favorited notes"""
subcategory = "favorite"
pattern = rf"{BASE_PATTERN}/(?:my|api/i)/favorites"
pattern = BASE_PATTERN + r"/(?:my|api/i)/favorites"
example = "https://misskey.io/my/favorites"
def notes(self):

View File

@@ -92,7 +92,7 @@ class MoebooruTagExtractor(MoebooruExtractor):
subcategory = "tag"
directory_fmt = ("{category}", "{search_tags}")
archive_fmt = "t_{search_tags}_{id}"
pattern = rf"{BASE_PATTERN}/post\?(?:[^&#]*&)*tags=([^&#]*)"
pattern = BASE_PATTERN + r"/post\?(?:[^&#]*&)*tags=([^&#]*)"
example = "https://yande.re/post?tags=TAG"
def __init__(self, match):
@@ -104,14 +104,14 @@ 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):
subcategory = "pool"
directory_fmt = ("{category}", "pool", "{pool}")
archive_fmt = "p_{pool}_{id}"
pattern = rf"{BASE_PATTERN}/pool/show/(\d+)"
pattern = BASE_PATTERN + r"/pool/show/(\d+)"
example = "https://yande.re/pool/show/12345"
def __init__(self, match):
@@ -129,26 +129,26 @@ 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):
subcategory = "post"
archive_fmt = "{id}"
pattern = rf"{BASE_PATTERN}/post/show/(\d+)"
pattern = BASE_PATTERN + r"/post/show/(\d+)"
example = "https://yande.re/post/show/12345"
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):
subcategory = "popular"
directory_fmt = ("{category}", "popular", "{scale}", "{date}")
archive_fmt = "P_{scale[0]}_{date}_{id}"
pattern = (rf"{BASE_PATTERN}"
rf"/post/popular_(by_(?:day|week|month)|recent)(?:\?([^#]*))?")
pattern = BASE_PATTERN + \
r"/post/popular_(by_(?:day|week|month)|recent)(?:\?([^#]*))?"
example = "https://yande.re/post/popular_by_month?year=YYYY&month=MM"
def __init__(self, match):

View File

@@ -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(
@@ -152,9 +152,9 @@ class MotherlessExtractor(Extractor):
class MotherlessMediaExtractor(MotherlessExtractor):
"""Extractor for a single image/video from motherless.com"""
subcategory = "media"
pattern = (rf"{BASE_PATTERN}/("
rf"(?:g/[^/?#]+/|G[IV]?[A-Z0-9]+/)?"
rf"(?!G)[A-Z0-9]+)")
pattern = (BASE_PATTERN +
r"/((?:g/[^/?#]+/|G[IV]?[A-Z0-9]+/)?"
r"(?!G)[A-Z0-9]+)")
example = "https://motherless.com/ABC123"
def items(self):
@@ -170,7 +170,7 @@ class MotherlessGalleryExtractor(MotherlessExtractor):
directory_fmt = ("{category}", "{uploader}",
"{gallery_id} {gallery_title}")
archive_fmt = "{gallery_id}_{id}"
pattern = rf"{BASE_PATTERN}/G([IVG])?([A-Z0-9]+)/?$"
pattern = BASE_PATTERN + "/G([IVG])?([A-Z0-9]+)/?$"
example = "https://motherless.com/GABC123"
def items(self):
@@ -206,7 +206,7 @@ class MotherlessGroupExtractor(MotherlessExtractor):
directory_fmt = ("{category}", "{uploader}",
"{group_id} {group_title}")
archive_fmt = "{group_id}_{id}"
pattern = rf"{BASE_PATTERN}/g([iv]?)/?([a-z0-9_]+)/?$"
pattern = BASE_PATTERN + "/g([iv]?)/?([a-z0-9_]+)/?$"
example = "https://motherless.com/g/abc123"
def items(self):

View File

@@ -20,7 +20,7 @@ class MyhentaigalleryBase():
class MyhentaigalleryGalleryExtractor(MyhentaigalleryBase, GalleryExtractor):
"""Extractor for image galleries from myhentaigallery.com"""
directory_fmt = ("{category}", "{gallery_id} {artist:?[/] /J, }{title}")
pattern = rf"{BASE_PATTERN}/g(?:allery/(?:thumbnails|show))?/(\d+)"
pattern = BASE_PATTERN + r"/g(?:allery/(?:thumbnails|show))?/(\d+)"
example = "https://myhentaigallery.com/g/12345"
def __init__(self, match):
@@ -62,7 +62,7 @@ class MyhentaigalleryGalleryExtractor(MyhentaigalleryBase, GalleryExtractor):
class MyhentaigalleryTagExtractor(MyhentaigalleryBase, Extractor):
"""Extractor for myhentaigallery tag searches"""
subcategory = "tag"
pattern = rf"{BASE_PATTERN}(/g/(artist|category|group|parody)/(\d+).*)"
pattern = BASE_PATTERN + r"(/g/(artist|category|group|parody)/(\d+).*)"
example = "https://myhentaigallery.com/g/category/123"
def items(self):

View File

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

View File

@@ -27,7 +27,7 @@ class NaverWebtoonEpisodeExtractor(NaverWebtoonBase, GalleryExtractor):
directory_fmt = ("{category}", "{comic}")
filename_fmt = "{episode:>03}-{num:>02}.{extension}"
archive_fmt = "{title_id}_{episode}_{num}"
pattern = rf"{BASE_PATTERN}/detail(?:\.nhn)?\?([^#]+)"
pattern = BASE_PATTERN + r"/detail(?:\.nhn)?\?([^#]+)"
example = "https://comic.naver.com/webtoon/detail?titleId=12345&no=1"
def __init__(self, match):
@@ -66,7 +66,7 @@ class NaverWebtoonEpisodeExtractor(NaverWebtoonBase, GalleryExtractor):
class NaverWebtoonComicExtractor(NaverWebtoonBase, Extractor):
subcategory = "comic"
categorytransfer = True
pattern = rf"{BASE_PATTERN}/list(?:\.nhn)?\?([^#]+)"
pattern = BASE_PATTERN + r"/list(?:\.nhn)?\?([^#]+)"
example = "https://comic.naver.com/webtoon/list?titleId=12345"
def __init__(self, match):

View File

@@ -12,7 +12,7 @@ from .common import Extractor, Message
from .. import text
BASE_PATTERN = r"(?:https?://)?nekohouse\.su"
USER_PATTERN = rf"{BASE_PATTERN}/([^/?#]+)/user/([^/?#]+)"
USER_PATTERN = BASE_PATTERN + r"/([^/?#]+)/user/([^/?#]+)"
class NekohouseExtractor(Extractor):
@@ -27,7 +27,7 @@ class NekohousePostExtractor(NekohouseExtractor):
"{post_id} {date} {title[b:230]}")
filename_fmt = "{num:>02} {id|filename}.{extension}"
archive_fmt = "{service}_{user_id}_{post_id}_{hash}"
pattern = rf"{USER_PATTERN}/post/([^/?#]+)"
pattern = USER_PATTERN + r"/post/([^/?#]+)"
example = "https://nekohouse.su/SERVICE/user/12345/post/12345"
def items(self):
@@ -98,7 +98,7 @@ class NekohousePostExtractor(NekohouseExtractor):
class NekohouseUserExtractor(NekohouseExtractor):
subcategory = "user"
pattern = rf"{USER_PATTERN}/?(?:\?([^#]+))?(?:$|\?|#)"
pattern = USER_PATTERN + r"/?(?:\?([^#]+))?(?:$|\?|#)"
example = "https://nekohouse.su/SERVICE/user/12345"
def items(self):

View File

@@ -412,7 +412,7 @@ class NewgroundsImageExtractor(NewgroundsExtractor):
class NewgroundsMediaExtractor(NewgroundsExtractor):
"""Extractor for a media file from newgrounds.com"""
subcategory = "media"
pattern = rf"{BASE_PATTERN}(/(?:portal/view|audio/listen)/\d+)"
pattern = BASE_PATTERN + r"(/(?:portal/view|audio/listen)/\d+)"
example = "https://www.newgrounds.com/portal/view/12345"
def __init__(self, match):
@@ -427,34 +427,34 @@ class NewgroundsMediaExtractor(NewgroundsExtractor):
class NewgroundsArtExtractor(NewgroundsExtractor):
"""Extractor for all images of a newgrounds user"""
subcategory = _path = "art"
pattern = rf"{USER_PATTERN}/art(?:(?:/page/|/?\?page=)(\d+))?/?$"
pattern = USER_PATTERN + r"/art(?:(?:/page/|/?\?page=)(\d+))?/?$"
example = "https://USER.newgrounds.com/art"
class NewgroundsAudioExtractor(NewgroundsExtractor):
"""Extractor for all audio submissions of a newgrounds user"""
subcategory = _path = "audio"
pattern = rf"{USER_PATTERN}/audio(?:(?:/page/|/?\?page=)(\d+))?/?$"
pattern = USER_PATTERN + r"/audio(?:(?:/page/|/?\?page=)(\d+))?/?$"
example = "https://USER.newgrounds.com/audio"
class NewgroundsMoviesExtractor(NewgroundsExtractor):
"""Extractor for all movies of a newgrounds user"""
subcategory = _path = "movies"
pattern = rf"{USER_PATTERN}/movies(?:(?:/page/|/?\?page=)(\d+))?/?$"
pattern = USER_PATTERN + r"/movies(?:(?:/page/|/?\?page=)(\d+))?/?$"
example = "https://USER.newgrounds.com/movies"
class NewgroundsGamesExtractor(NewgroundsExtractor):
"""Extractor for a newgrounds user's games"""
subcategory = _path = "games"
pattern = rf"{USER_PATTERN}/games(?:(?:/page/|/?\?page=)(\d+))?/?$"
pattern = USER_PATTERN + r"/games(?:(?:/page/|/?\?page=)(\d+))?/?$"
example = "https://USER.newgrounds.com/games"
class NewgroundsUserExtractor(Dispatch, NewgroundsExtractor):
"""Extractor for a newgrounds user profile"""
pattern = rf"{USER_PATTERN}/?$"
pattern = USER_PATTERN + r"/?$"
example = "https://USER.newgrounds.com"
def items(self):
@@ -471,7 +471,7 @@ class NewgroundsFavoriteExtractor(NewgroundsExtractor):
"""Extractor for posts favorited by a newgrounds user"""
subcategory = "favorite"
directory_fmt = ("{category}", "{user}", "Favorites")
pattern = (rf"{USER_PATTERN}/favorites(?!/following)(?:/(art|audio|movies)"
pattern = (USER_PATTERN + r"/favorites(?!/following)(?:/(art|audio|movies)"
r"(?:(?:/page/|/?\?page=)(\d+))?)?")
example = "https://USER.newgrounds.com/favorites"
@@ -510,16 +510,15 @@ 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, '"')
]
class NewgroundsFollowingExtractor(NewgroundsFavoriteExtractor):
"""Extractor for a newgrounds user's favorited users"""
subcategory = "following"
pattern = (rf"{USER_PATTERN}/favorites/(following)"
pattern = (USER_PATTERN + r"/favorites/(following)"
r"(?:(?:/page/|/?\?page=)(\d+))?")
example = "https://USER.newgrounds.com/favorites/following"
def items(self):
@@ -539,7 +538,7 @@ class NewgroundsSearchExtractor(NewgroundsExtractor):
"""Extractor for newgrounds.com search reesults"""
subcategory = "search"
directory_fmt = ("{category}", "search", "{search_tags}")
pattern = rf"{BASE_PATTERN}/search/conduct/([^/?#]+)/?\?([^#]+)"
pattern = BASE_PATTERN + r"/search/conduct/([^/?#]+)/?\?([^#]+)"
example = "https://www.newgrounds.com/search/conduct/art?terms=QUERY"
def __init__(self, match):

View File

@@ -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)
@@ -178,7 +178,7 @@ BASE_PATTERN = NijieExtractor.update({
class NijieUserExtractor(Dispatch, NijieExtractor):
"""Extractor for nijie user profiles"""
pattern = rf"{BASE_PATTERN}/members\.php\?id=(\d+)"
pattern = BASE_PATTERN + r"/members\.php\?id=(\d+)"
example = "https://nijie.info/members.php?id=12345"
def items(self):
@@ -194,7 +194,7 @@ class NijieUserExtractor(Dispatch, NijieExtractor):
class NijieIllustrationExtractor(NijieExtractor):
"""Extractor for all illustrations of a nijie-user"""
subcategory = "illustration"
pattern = rf"{BASE_PATTERN}/members_illust\.php\?id=(\d+)"
pattern = BASE_PATTERN + r"/members_illust\.php\?id=(\d+)"
example = "https://nijie.info/members_illust.php?id=12345"
def image_ids(self):
@@ -204,7 +204,7 @@ class NijieIllustrationExtractor(NijieExtractor):
class NijieDoujinExtractor(NijieExtractor):
"""Extractor for doujin entries of a nijie user"""
subcategory = "doujin"
pattern = rf"{BASE_PATTERN}/members_dojin\.php\?id=(\d+)"
pattern = BASE_PATTERN + r"/members_dojin\.php\?id=(\d+)"
example = "https://nijie.info/members_dojin.php?id=12345"
def image_ids(self):
@@ -216,7 +216,7 @@ class NijieFavoriteExtractor(NijieExtractor):
subcategory = "favorite"
directory_fmt = ("{category}", "bookmarks", "{user_id}")
archive_fmt = "f_{user_id}_{image_id}_{num}"
pattern = rf"{BASE_PATTERN}/user_like_illust_view\.php\?id=(\d+)"
pattern = BASE_PATTERN + r"/user_like_illust_view\.php\?id=(\d+)"
example = "https://nijie.info/user_like_illust_view.php?id=12345"
def image_ids(self):
@@ -234,7 +234,7 @@ class NijieNuitaExtractor(NijieExtractor):
subcategory = "nuita"
directory_fmt = ("{category}", "nuita", "{user_id}")
archive_fmt = "n_{user_id}_{image_id}_{num}"
pattern = rf"{BASE_PATTERN}/history_nuita\.php\?id=(\d+)"
pattern = BASE_PATTERN + r"/history_nuita\.php\?id=(\d+)"
example = "https://nijie.info/history_nuita.php?id=12345"
def image_ids(self):
@@ -253,7 +253,7 @@ class NijieNuitaExtractor(NijieExtractor):
class NijieFeedExtractor(NijieExtractor):
"""Extractor for nijie liked user feed"""
subcategory = "feed"
pattern = rf"{BASE_PATTERN}/like_user_view\.php"
pattern = BASE_PATTERN + r"/like_user_view\.php"
example = "https://nijie.info/like_user_view.php"
def image_ids(self):
@@ -266,7 +266,7 @@ class NijieFeedExtractor(NijieExtractor):
class NijieFollowedExtractor(NijieExtractor):
"""Extractor for followed nijie users"""
subcategory = "followed"
pattern = rf"{BASE_PATTERN}/like_my\.php"
pattern = BASE_PATTERN + r"/like_my\.php"
example = "https://nijie.info/like_my.php"
def items(self):
@@ -292,7 +292,7 @@ class NijieFollowedExtractor(NijieExtractor):
class NijieImageExtractor(NijieExtractor):
"""Extractor for a nijie work/image"""
subcategory = "image"
pattern = rf"{BASE_PATTERN}/view(?:_popup)?\.php\?id=(\d+)"
pattern = BASE_PATTERN + r"/view(?:_popup)?\.php\?id=(\d+)"
example = "https://nijie.info/view.php?id=12345"
def image_ids(self):

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