2021-01-11 22:17:08 +01:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# Copyright 2021 Mike Fährmann
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License version 2 as
|
|
|
|
# published by the Free Software Foundation.
|
|
|
|
|
|
|
|
"""Extractors for https://kemono.party/"""
|
|
|
|
|
|
|
|
from .common import Extractor, Message
|
2021-09-09 01:02:59 +02:00
|
|
|
from .. import text, exception
|
|
|
|
from ..cache import cache
|
2021-07-09 18:19:02 +02:00
|
|
|
import itertools
|
2021-02-05 20:10:20 +01:00
|
|
|
import re
|
2021-01-11 22:17:08 +01:00
|
|
|
|
2021-11-26 17:03:13 +01:00
|
|
|
BASE_PATTERN = r"(?:https?://)?(?:www\.)?kemono\.party"
|
2021-10-18 04:04:58 +02:00
|
|
|
USER_PATTERN = BASE_PATTERN + r"/([^/?#]+)/user/([^/?#]+)"
|
2021-02-09 19:37:22 +01:00
|
|
|
|
2021-01-11 22:17:08 +01:00
|
|
|
|
|
|
|
class KemonopartyExtractor(Extractor):
|
|
|
|
"""Base class for kemonoparty extractors"""
|
|
|
|
category = "kemonoparty"
|
|
|
|
root = "https://kemono.party"
|
2021-02-09 20:35:36 +01:00
|
|
|
directory_fmt = ("{category}", "{service}", "{user}")
|
2021-06-18 03:29:30 +02:00
|
|
|
filename_fmt = "{id}_{title}_{num:>02}_{filename}.{extension}"
|
|
|
|
archive_fmt = "{service}_{user}_{id}_{num}"
|
2021-06-28 23:34:58 +02:00
|
|
|
cookiedomain = ".kemono.party"
|
2021-01-11 22:17:08 +01:00
|
|
|
|
|
|
|
def items(self):
|
2021-08-16 17:40:29 +02:00
|
|
|
self._prepare_ddosguard_cookies()
|
2021-06-28 23:34:58 +02:00
|
|
|
|
2021-11-17 19:59:24 +01:00
|
|
|
self._find_inline = re.compile(
|
2021-10-17 21:47:11 +02:00
|
|
|
r'src="(?:https?://kemono\.party)?(/inline/[^"]+'
|
|
|
|
r'|/[0-9a-f]{2}/[0-9a-f]{2}/[0-9a-f]{64}\.[^"]+)').findall
|
2021-11-17 22:20:18 +01:00
|
|
|
find_hash = re.compile("/[0-9a-f]{2}/[0-9a-f]{2}/([0-9a-f]{64})").match
|
2021-11-17 19:59:24 +01:00
|
|
|
generators = self._build_file_generators(self.config("files"))
|
2021-11-03 22:52:15 +01:00
|
|
|
comments = self.config("comments")
|
2021-11-20 23:36:16 +01:00
|
|
|
username = dms = None
|
2021-02-05 20:10:20 +01:00
|
|
|
|
2021-05-14 19:54:16 +02:00
|
|
|
if self.config("metadata"):
|
|
|
|
username = text.unescape(text.extract(
|
2021-08-04 16:09:22 +02:00
|
|
|
self.request(self.user_url).text,
|
|
|
|
'<meta name="artist_name" content="', '"')[0])
|
2021-11-20 23:36:16 +01:00
|
|
|
if self.config("dms"):
|
|
|
|
dms = True
|
2021-05-14 19:54:16 +02:00
|
|
|
|
2021-07-09 18:19:02 +02:00
|
|
|
posts = self.posts()
|
|
|
|
max_posts = self.config("max-posts")
|
|
|
|
if max_posts:
|
|
|
|
posts = itertools.islice(posts, max_posts)
|
|
|
|
|
|
|
|
for post in posts:
|
2021-01-13 22:36:04 +01:00
|
|
|
|
2021-01-15 01:13:59 +01:00
|
|
|
post["date"] = text.parse_datetime(
|
2021-11-11 19:38:10 +01:00
|
|
|
post["published"] or post["added"],
|
|
|
|
"%a, %d %b %Y %H:%M:%S %Z")
|
2021-05-14 19:54:16 +02:00
|
|
|
if username:
|
|
|
|
post["username"] = username
|
2021-11-03 22:52:15 +01:00
|
|
|
if comments:
|
|
|
|
post["comments"] = self._extract_comments(post)
|
2021-11-20 23:36:16 +01:00
|
|
|
if dms is not None:
|
|
|
|
if dms is True:
|
|
|
|
dms = self._extract_dms(post)
|
|
|
|
post["dms"] = dms
|
2021-01-11 22:17:08 +01:00
|
|
|
yield Message.Directory, post
|
2021-01-13 22:36:04 +01:00
|
|
|
|
2021-11-17 22:20:18 +01:00
|
|
|
hashes = set()
|
2021-11-17 19:59:24 +01:00
|
|
|
post["num"] = 0
|
|
|
|
for file in itertools.chain.from_iterable(
|
|
|
|
g(post) for g in generators):
|
2021-11-17 22:20:18 +01:00
|
|
|
url = file["path"]
|
|
|
|
|
|
|
|
match = find_hash(url)
|
|
|
|
if match:
|
|
|
|
post["hash"] = hash = match.group(1)
|
|
|
|
if hash in hashes:
|
|
|
|
self.log.debug("Skipping %s (duplicate)", url)
|
|
|
|
continue
|
|
|
|
hashes.add(hash)
|
|
|
|
else:
|
|
|
|
post["hash"] = ""
|
|
|
|
|
2021-05-27 22:11:31 +02:00
|
|
|
post["type"] = file["type"]
|
2021-11-17 19:59:24 +01:00
|
|
|
post["num"] += 1
|
2021-11-17 22:20:18 +01:00
|
|
|
|
2021-02-09 20:29:39 +01:00
|
|
|
if url[0] == "/":
|
2021-09-30 23:02:46 +02:00
|
|
|
url = self.root + "/data" + url
|
|
|
|
elif url.startswith("https://kemono.party"):
|
|
|
|
url = self.root + "/data" + url[20:]
|
2021-05-01 02:30:10 +02:00
|
|
|
|
2021-01-15 01:13:59 +01:00
|
|
|
text.nameext_from_url(file["name"], post)
|
2021-02-09 20:29:39 +01:00
|
|
|
yield Message.Url, url, post
|
2021-01-11 22:17:08 +01:00
|
|
|
|
2021-09-09 01:02:59 +02:00
|
|
|
def login(self):
|
|
|
|
username, password = self._get_auth_info()
|
|
|
|
if username:
|
|
|
|
self._update_cookies(self._login_impl(username, password))
|
|
|
|
|
|
|
|
@cache(maxage=28*24*3600, keyarg=1)
|
|
|
|
def _login_impl(self, username, password):
|
|
|
|
self.log.info("Logging in as %s", username)
|
|
|
|
|
|
|
|
url = self.root + "/account/login"
|
|
|
|
data = {"username": username, "password": password}
|
|
|
|
|
|
|
|
response = self.request(url, method="POST", data=data)
|
|
|
|
if response.url.endswith("/account/login") and \
|
|
|
|
"Username or password is incorrect" in response.text:
|
|
|
|
raise exception.AuthenticationError()
|
|
|
|
|
|
|
|
return {c.name: c.value for c in response.history[0].cookies}
|
|
|
|
|
2021-11-19 01:50:48 +01:00
|
|
|
def _file(self, post):
|
2021-11-17 19:59:24 +01:00
|
|
|
file = post["file"]
|
|
|
|
if not file:
|
|
|
|
return ()
|
|
|
|
file["type"] = "file"
|
|
|
|
return (file,)
|
|
|
|
|
|
|
|
def _attachments(self, post):
|
|
|
|
for attachment in post["attachments"]:
|
|
|
|
attachment["type"] = "attachment"
|
|
|
|
return post["attachments"]
|
|
|
|
|
|
|
|
def _inline(self, post):
|
|
|
|
for path in self._find_inline(post["content"] or ""):
|
|
|
|
yield {"path": path, "name": path, "type": "inline"}
|
|
|
|
|
|
|
|
def _build_file_generators(self, filetypes):
|
|
|
|
if filetypes is None:
|
2021-11-19 01:50:48 +01:00
|
|
|
return (self._file, self._attachments, self._inline)
|
2021-11-17 19:59:24 +01:00
|
|
|
genmap = {
|
2021-11-19 01:50:48 +01:00
|
|
|
"file" : self._file,
|
2021-11-17 19:59:24 +01:00
|
|
|
"attachments": self._attachments,
|
|
|
|
"inline" : self._inline,
|
|
|
|
}
|
|
|
|
if isinstance(filetypes, str):
|
|
|
|
filetypes = filetypes.split(",")
|
|
|
|
return [genmap[ft] for ft in filetypes]
|
|
|
|
|
2021-11-03 22:52:15 +01:00
|
|
|
def _extract_comments(self, post):
|
|
|
|
url = "{}/{}/user/{}/post/{}".format(
|
|
|
|
self.root, post["service"], post["user"], post["id"])
|
|
|
|
page = self.request(url).text
|
|
|
|
|
|
|
|
comments = []
|
|
|
|
for comment in text.extract_iter(page, "<article", "</article>"):
|
|
|
|
extr = text.extract_from(comment)
|
|
|
|
cid = extr('id="', '"')
|
|
|
|
comments.append({
|
|
|
|
"id" : cid,
|
|
|
|
"user": extr('href="#' + cid + '"', '</').strip(" \n\r>"),
|
|
|
|
"body": extr(
|
|
|
|
'<section class="comment__body">', '</section>').strip(),
|
|
|
|
"date": extr('datetime="', '"'),
|
|
|
|
})
|
|
|
|
return comments
|
|
|
|
|
2021-11-20 23:36:16 +01:00
|
|
|
def _extract_dms(self, post):
|
|
|
|
url = "{}/{}/user/{}/dms".format(
|
|
|
|
self.root, post["service"], post["user"])
|
|
|
|
page = self.request(url).text
|
|
|
|
|
|
|
|
dms = []
|
|
|
|
for dm in text.extract_iter(page, "<article", "</article>"):
|
|
|
|
dms.append({
|
|
|
|
"body": text.unescape(text.extract(
|
|
|
|
dm, '<div class="dm-card__content">', '</div>',
|
|
|
|
)[0].strip()),
|
|
|
|
"date": text.extract(dm, 'datetime="', '"')[0],
|
|
|
|
})
|
|
|
|
return dms
|
|
|
|
|
2021-01-11 22:17:08 +01:00
|
|
|
|
|
|
|
class KemonopartyUserExtractor(KemonopartyExtractor):
|
|
|
|
"""Extractor for all posts from a kemono.party user listing"""
|
|
|
|
subcategory = "user"
|
2021-10-18 04:04:58 +02:00
|
|
|
pattern = USER_PATTERN + r"/?(?:\?o=(\d+))?(?:$|[?#])"
|
2021-02-09 19:37:22 +01:00
|
|
|
test = (
|
|
|
|
("https://kemono.party/fanbox/user/6993449", {
|
|
|
|
"range": "1-25",
|
|
|
|
"count": 25,
|
|
|
|
}),
|
2021-07-09 18:29:50 +02:00
|
|
|
# 'max-posts' option, 'o' query parameter (#1674)
|
|
|
|
("https://kemono.party/patreon/user/881792?o=150", {
|
|
|
|
"options": (("max-posts", 25),),
|
|
|
|
"count": "< 100",
|
|
|
|
}),
|
2021-02-09 19:37:22 +01:00
|
|
|
("https://kemono.party/subscribestar/user/alcorart"),
|
|
|
|
)
|
2021-01-11 22:17:08 +01:00
|
|
|
|
|
|
|
def __init__(self, match):
|
|
|
|
KemonopartyExtractor.__init__(self, match)
|
2021-07-09 18:29:50 +02:00
|
|
|
service, user_id, offset = match.groups()
|
2021-01-13 22:36:04 +01:00
|
|
|
self.api_url = "{}/api/{}/user/{}".format(self.root, service, user_id)
|
2021-05-14 19:54:16 +02:00
|
|
|
self.user_url = "{}/{}/user/{}".format(self.root, service, user_id)
|
2021-07-09 18:29:50 +02:00
|
|
|
self.offset = text.parse_int(offset)
|
2021-01-11 22:17:08 +01:00
|
|
|
|
|
|
|
def posts(self):
|
2021-01-13 22:36:04 +01:00
|
|
|
url = self.api_url
|
2021-07-09 18:29:50 +02:00
|
|
|
params = {"o": self.offset}
|
2021-01-11 22:17:08 +01:00
|
|
|
|
|
|
|
while True:
|
2021-01-13 22:36:04 +01:00
|
|
|
posts = self.request(url, params=params).json()
|
|
|
|
yield from posts
|
2021-01-11 22:17:08 +01:00
|
|
|
|
2021-01-13 22:36:04 +01:00
|
|
|
if len(posts) < 25:
|
2021-01-11 22:17:08 +01:00
|
|
|
return
|
|
|
|
params["o"] += 25
|
|
|
|
|
|
|
|
|
|
|
|
class KemonopartyPostExtractor(KemonopartyExtractor):
|
|
|
|
"""Extractor for a single kemono.party post"""
|
|
|
|
subcategory = "post"
|
2021-10-18 04:04:58 +02:00
|
|
|
pattern = USER_PATTERN + r"/post/([^/?#]+)"
|
2021-02-05 20:10:20 +01:00
|
|
|
test = (
|
|
|
|
("https://kemono.party/fanbox/user/6993449/post/506575", {
|
2021-10-13 19:33:00 +02:00
|
|
|
"pattern": r"https://kemono.party/data/21/0f"
|
|
|
|
r"/210f35388e28bbcf756db18dd516e2d82ce75[0-9a-f]+\.jpg",
|
2021-02-05 20:10:20 +01:00
|
|
|
"keyword": {
|
|
|
|
"added": "Wed, 06 May 2020 20:28:02 GMT",
|
|
|
|
"content": str,
|
|
|
|
"date": "dt:2019-08-11 02:09:04",
|
|
|
|
"edited": None,
|
|
|
|
"embed": dict,
|
|
|
|
"extension": "jpeg",
|
|
|
|
"filename": "P058kDFYus7DbqAkGlfWTlOr",
|
2021-11-17 22:20:18 +01:00
|
|
|
"hash": "210f35388e28bbcf756db18dd516e2d8"
|
|
|
|
"2ce758e0d32881eeee76d43e1716d382",
|
2021-02-05 20:10:20 +01:00
|
|
|
"id": "506575",
|
|
|
|
"num": 1,
|
|
|
|
"published": "Sun, 11 Aug 2019 02:09:04 GMT",
|
|
|
|
"service": "fanbox",
|
|
|
|
"shared_file": False,
|
|
|
|
"subcategory": "post",
|
|
|
|
"title": "c96取り置き",
|
2021-05-27 22:11:31 +02:00
|
|
|
"type": "file",
|
2021-02-05 20:10:20 +01:00
|
|
|
"user": "6993449",
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
# inline image (#1286)
|
|
|
|
("https://kemono.party/fanbox/user/7356311/post/802343", {
|
2021-11-17 19:59:24 +01:00
|
|
|
"pattern": r"https://kemono\.party/data/47/b5/47b5c014ecdcfabdf2c8"
|
|
|
|
r"5eec53f1133a76336997ae8596f332e97d956a460ad2\.jpg",
|
2021-11-17 22:20:18 +01:00
|
|
|
"keyword": {"hash": "47b5c014ecdcfabdf2c85eec53f1133a"
|
|
|
|
"76336997ae8596f332e97d956a460ad2"},
|
2021-02-05 20:10:20 +01:00
|
|
|
}),
|
2021-05-01 02:30:10 +02:00
|
|
|
# kemono.party -> data.kemono.party
|
|
|
|
("https://kemono.party/gumroad/user/trylsc/post/IURjT", {
|
2021-11-17 19:59:24 +01:00
|
|
|
"pattern": r"https://kemono\.party/data/("
|
|
|
|
r"files/gumroad/trylsc/IURjT/reward8\.jpg|"
|
|
|
|
r"c6/04/c6048f5067fd9dbfa7a8be565ac194efdfb6e4.+\.zip)",
|
2021-05-01 02:30:10 +02:00
|
|
|
}),
|
2021-06-25 14:47:12 +02:00
|
|
|
# username (#1548, #1652)
|
|
|
|
("https://kemono.party/gumroad/user/3252870377455/post/aJnAH", {
|
|
|
|
"options": (("metadata", True),),
|
|
|
|
"keyword": {"username": "Kudalyn's Creations"},
|
|
|
|
}),
|
2021-11-17 22:20:18 +01:00
|
|
|
# skip patreon duplicates
|
2021-07-23 16:38:57 +02:00
|
|
|
("https://kemono.party/patreon/user/4158582/post/32099982", {
|
|
|
|
"count": 2,
|
|
|
|
}),
|
2021-11-20 23:36:16 +01:00
|
|
|
# DMs (#2008)
|
2021-11-21 22:46:34 +01:00
|
|
|
("https://kemono.party/patreon/user/34134344/post/38129255", {
|
2021-11-20 23:36:16 +01:00
|
|
|
"options": (("dms", True),),
|
|
|
|
"keyword": {"dms": [{
|
|
|
|
"body": r"re:Hi! Thank you very much for supporting the work I"
|
|
|
|
r" did in May. Here's your reward pack! I hope you fin"
|
2021-11-21 22:46:34 +01:00
|
|
|
r"d something you enjoy in it. :\)\n\nhttps://www.medi"
|
|
|
|
r"afire.com/file/\w+/Set13_tier_2.zip/file",
|
2021-11-20 23:36:16 +01:00
|
|
|
"date": "2021-07-31 02:47:51.327865",
|
|
|
|
}]},
|
|
|
|
}),
|
2021-02-09 19:37:22 +01:00
|
|
|
("https://kemono.party/subscribestar/user/alcorart/post/184330"),
|
2021-11-26 17:03:13 +01:00
|
|
|
("https://www.kemono.party/subscribestar/user/alcorart/post/184330"),
|
2021-02-05 20:10:20 +01:00
|
|
|
)
|
2021-01-11 22:17:08 +01:00
|
|
|
|
|
|
|
def __init__(self, match):
|
|
|
|
KemonopartyExtractor.__init__(self, match)
|
|
|
|
service, user_id, post_id = match.groups()
|
2021-01-13 22:36:04 +01:00
|
|
|
self.api_url = "{}/api/{}/user/{}/post/{}".format(
|
2021-01-11 22:17:08 +01:00
|
|
|
self.root, service, user_id, post_id)
|
2021-05-14 19:54:16 +02:00
|
|
|
self.user_url = "{}/{}/user/{}".format(self.root, service, user_id)
|
2021-01-11 22:17:08 +01:00
|
|
|
|
|
|
|
def posts(self):
|
2021-01-13 22:36:04 +01:00
|
|
|
posts = self.request(self.api_url).json()
|
|
|
|
return (posts[0],) if len(posts) > 1 else posts
|
2021-09-08 00:32:49 +02:00
|
|
|
|
|
|
|
|
2021-10-13 19:33:00 +02:00
|
|
|
class KemonopartyDiscordExtractor(KemonopartyExtractor):
|
|
|
|
"""Extractor for kemono.party discord servers"""
|
|
|
|
subcategory = "discord"
|
2021-10-15 18:37:08 +02:00
|
|
|
directory_fmt = ("{category}", "discord", "{server}",
|
|
|
|
"{channel_name|channel}")
|
2021-10-13 19:33:00 +02:00
|
|
|
filename_fmt = "{id}_{num:>02}_{filename}.{extension}"
|
|
|
|
archive_fmt = "discord_{server}_{id}_{num}"
|
2021-10-18 04:04:58 +02:00
|
|
|
pattern = BASE_PATTERN + r"/discord/server/(\d+)(?:/channel/(\d+))?#(.*)"
|
2021-10-15 18:50:08 +02:00
|
|
|
test = (
|
|
|
|
(("https://kemono.party/discord"
|
|
|
|
"/server/488668827274444803#finish-work"), {
|
|
|
|
"count": 4,
|
|
|
|
"keyword": {"channel_name": "finish-work"},
|
|
|
|
}),
|
2021-10-18 04:04:58 +02:00
|
|
|
(("https://kemono.party/discord"
|
|
|
|
"/server/256559665620451329/channel/462437519519383555#"), {
|
|
|
|
"pattern": r"https://kemono\.party/data/attachments/discord"
|
|
|
|
r"/256559665620451329/\d+/\d+/.+",
|
|
|
|
"count": ">= 2",
|
|
|
|
}),
|
2021-10-22 02:50:47 +02:00
|
|
|
# 'inline' files
|
|
|
|
(("https://kemono.party/discord"
|
|
|
|
"/server/315262215055736843/channel/315262215055736843#general"), {
|
|
|
|
"pattern": r"https://cdn\.discordapp\.com/attachments/\d+/\d+/.+$",
|
|
|
|
"range": "1-5",
|
|
|
|
"options": (("image-filter", "type == 'inline'"),),
|
|
|
|
}),
|
2021-10-15 18:50:08 +02:00
|
|
|
)
|
2021-10-13 19:33:00 +02:00
|
|
|
|
|
|
|
def __init__(self, match):
|
|
|
|
KemonopartyExtractor.__init__(self, match)
|
2021-10-18 04:04:58 +02:00
|
|
|
self.server, self.channel, self.channel_name = match.groups()
|
2021-10-13 19:33:00 +02:00
|
|
|
|
|
|
|
def items(self):
|
|
|
|
self._prepare_ddosguard_cookies()
|
|
|
|
|
2021-10-22 02:50:47 +02:00
|
|
|
find_inline = re.compile(
|
2021-10-24 21:15:21 +02:00
|
|
|
r"https?://(?:cdn\.discordapp.com|media\.discordapp\.net)"
|
|
|
|
r"(/[A-Za-z0-9-._~:/?#\[\]@!$&'()*+,;%=]+)").findall
|
2021-10-22 02:50:47 +02:00
|
|
|
|
|
|
|
posts = self.posts()
|
|
|
|
max_posts = self.config("max-posts")
|
|
|
|
if max_posts:
|
|
|
|
posts = itertools.islice(posts, max_posts)
|
|
|
|
|
|
|
|
for post in posts:
|
|
|
|
files = []
|
|
|
|
append = files.append
|
|
|
|
for attachment in post["attachments"]:
|
|
|
|
attachment["type"] = "attachment"
|
|
|
|
append(attachment)
|
|
|
|
for path in find_inline(post["content"] or ""):
|
2021-10-24 21:15:21 +02:00
|
|
|
append({"path": "https://cdn.discordapp.com" + path,
|
|
|
|
"name": path, "type": "inline"})
|
2021-10-22 02:50:47 +02:00
|
|
|
|
2021-10-18 04:04:58 +02:00
|
|
|
post["channel_name"] = self.channel_name
|
2021-10-13 19:33:00 +02:00
|
|
|
post["date"] = text.parse_datetime(
|
|
|
|
post["published"], "%a, %d %b %Y %H:%M:%S %Z")
|
|
|
|
yield Message.Directory, post
|
|
|
|
|
2021-10-22 02:50:47 +02:00
|
|
|
for post["num"], file in enumerate(files, 1):
|
|
|
|
post["type"] = file["type"]
|
2021-10-13 19:33:00 +02:00
|
|
|
url = file["path"]
|
|
|
|
if url[0] == "/":
|
|
|
|
url = self.root + "/data" + url
|
|
|
|
elif url.startswith("https://kemono.party"):
|
|
|
|
url = self.root + "/data" + url[20:]
|
|
|
|
|
|
|
|
text.nameext_from_url(file["name"], post)
|
|
|
|
yield Message.Url, url, post
|
|
|
|
|
|
|
|
def posts(self):
|
2021-10-18 04:04:58 +02:00
|
|
|
if self.channel is None:
|
|
|
|
url = "{}/api/discord/channels/lookup?q={}".format(
|
|
|
|
self.root, self.server)
|
|
|
|
for channel in self.request(url).json():
|
|
|
|
if channel["name"] == self.channel_name:
|
|
|
|
self.channel = channel["id"]
|
2021-10-15 18:50:08 +02:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise exception.NotFoundError("channel")
|
|
|
|
|
2021-10-18 04:04:58 +02:00
|
|
|
url = "{}/api/discord/channel/{}".format(self.root, self.channel)
|
|
|
|
params = {"skip": 0}
|
2021-10-13 19:33:00 +02:00
|
|
|
|
2021-10-18 04:04:58 +02:00
|
|
|
while True:
|
|
|
|
posts = self.request(url, params=params).json()
|
|
|
|
yield from posts
|
|
|
|
|
|
|
|
if len(posts) < 25:
|
|
|
|
break
|
|
|
|
params["skip"] += 25
|
2021-10-15 18:37:08 +02:00
|
|
|
|
2021-10-13 19:33:00 +02:00
|
|
|
|
2021-10-18 04:04:58 +02:00
|
|
|
class KemonopartyDiscordServerExtractor(KemonopartyExtractor):
|
|
|
|
subcategory = "discord-server"
|
|
|
|
pattern = BASE_PATTERN + r"/discord/server/(\d+)$"
|
|
|
|
test = ("https://kemono.party/discord/server/488668827274444803", {
|
|
|
|
"pattern": KemonopartyDiscordExtractor.pattern,
|
|
|
|
"count": 13,
|
|
|
|
})
|
|
|
|
|
|
|
|
def __init__(self, match):
|
|
|
|
KemonopartyExtractor.__init__(self, match)
|
|
|
|
self.server = match.group(1)
|
|
|
|
|
|
|
|
def items(self):
|
|
|
|
url = "{}/api/discord/channels/lookup?q={}".format(
|
|
|
|
self.root, self.server)
|
|
|
|
channels = self.request(url).json()
|
|
|
|
|
|
|
|
for channel in channels:
|
|
|
|
url = "{}/discord/server/{}/channel/{}#{}".format(
|
|
|
|
self.root, self.server, channel["id"], channel["name"])
|
|
|
|
channel["_extractor"] = KemonopartyDiscordExtractor
|
|
|
|
yield Message.Queue, url, channel
|
2021-10-13 19:33:00 +02:00
|
|
|
|
|
|
|
|
2021-09-08 00:32:49 +02:00
|
|
|
class KemonopartyFavoriteExtractor(KemonopartyExtractor):
|
|
|
|
"""Extractor for kemono.party favorites"""
|
|
|
|
subcategory = "favorite"
|
2021-10-18 04:04:58 +02:00
|
|
|
pattern = BASE_PATTERN + r"/favorites"
|
2021-09-09 01:02:59 +02:00
|
|
|
test = ("https://kemono.party/favorites", {
|
|
|
|
"pattern": KemonopartyUserExtractor.pattern,
|
|
|
|
"url": "f4b5b796979bcba824af84206578c79101c7f0e1",
|
|
|
|
"count": 3,
|
|
|
|
})
|
2021-09-08 00:32:49 +02:00
|
|
|
|
|
|
|
def items(self):
|
|
|
|
self._prepare_ddosguard_cookies()
|
2021-09-09 01:02:59 +02:00
|
|
|
self.login()
|
2021-09-08 00:32:49 +02:00
|
|
|
|
2021-09-09 01:02:59 +02:00
|
|
|
users = self.request(self.root + "/api/favorites").json()
|
2021-09-08 00:32:49 +02:00
|
|
|
for user in users:
|
|
|
|
user["_extractor"] = KemonopartyUserExtractor
|
|
|
|
url = "{}/{}/user/{}".format(
|
|
|
|
self.root, user["service"], user["id"])
|
|
|
|
yield Message.Queue, url, user
|