[fansly] add 'creator-media' extractor (#4401)

This commit is contained in:
Mike Fährmann
2025-09-12 22:20:21 +02:00
parent 737a40c943
commit 9677bf6e18
4 changed files with 64 additions and 10 deletions

View File

@@ -292,7 +292,7 @@ Consider all listed sites to potentially be NSFW.
<tr id="fansly" title="fansly"> <tr id="fansly" title="fansly">
<td>Fansly</td> <td>Fansly</td>
<td>https://fansly.com/</td> <td>https://fansly.com/</td>
<td>Creator Posts, Home Feed, Lists, Account Lists, Posts</td> <td>Creator Media, Creator Posts, Home Feed, Lists, Account Lists, Posts</td>
<td></td> <td></td>
</tr> </tr>
<tr id="fantia" title="fantia"> <tr id="fantia" title="fantia">

View File

@@ -191,13 +191,20 @@ class FanslyCreatorPostsExtractor(FanslyExtractor):
example = "https://fansly.com/CREATOR/posts" example = "https://fansly.com/CREATOR/posts"
def posts(self): def posts(self):
creator = self.groups[0] account = self.api.account(self.groups[0])
if creator.startswith("id:"): return self.api.timeline_new(
account = self.api.account_by_id(creator[3:]) account["id"], account["walls"][0]["id"])
else:
account = self.api.account(creator)
wall_id = account["walls"][0]["id"] class FanslyCreatorMediaExtractor(FanslyExtractor):
return self.api.timeline_new(account["id"], wall_id) subcategory = "creator-media"
pattern = rf"{BASE_PATTERN}/([^/?#]+)/media"
example = "https://fansly.com/CREATOR/media"
def posts(self):
account = self.api.account(self.groups[0])
return self.api.mediaoffers_location(
account["id"], account["walls"][0]["id"])
class FanslyAPI(): class FanslyAPI():
@@ -217,7 +224,12 @@ class FanslyAPI():
else: else:
self.extractor.log.warning("No 'token' provided") self.extractor.log.warning("No 'token' provided")
def account(self, username): def account(self, creator):
if creator.startswith("id:"):
return self.account_by_id(creator[3:])
return self.account_by_username(creator)
def account_by_username(self, username):
endpoint = "/v1/account" endpoint = "/v1/account"
params = {"usernames": username} params = {"usernames": username}
return self._call(endpoint, params)[0] return self._call(endpoint, params)[0]
@@ -252,6 +264,20 @@ class FanslyAPI():
} }
return self._pagination(endpoint, params) return self._pagination(endpoint, params)
def mediaoffers_location(self, account_id, wall_id):
endpoint = "/v1/mediaoffers/location"
params = {
"locationId": wall_id,
"locationType": "1002",
"accountId": account_id,
"mediaType": "",
"before": "",
"after" : "0",
"limit" : "30",
"offset": "0",
}
return self._pagination_media(endpoint, params)
def post(self, post_id): def post(self, post_id):
endpoint = "/v1/post" endpoint = "/v1/post"
params = {"ids": post_id} params = {"ids": post_id}
@@ -319,6 +345,19 @@ class FanslyAPI():
return posts return posts
def _update_media(self, items, response):
posts = {
post["id"]: post
for post in response["posts"]
}
response["posts"] = [
posts[item["correlationId"]]
for item in items
]
return self._update_posts(response)
def _update_items(self, items): def _update_items(self, items):
ids = [item["id"] for item in items] ids = [item["id"] for item in items]
accounts = { accounts = {
@@ -353,3 +392,13 @@ class FanslyAPI():
posts = self._update_posts(response) posts = self._update_posts(response)
yield from posts yield from posts
params["before"] = min(p["id"] for p in posts) params["before"] = min(p["id"] for p in posts)
def _pagination_media(self, endpoint, params):
while True:
response = self._call(endpoint, params)
data = response["data"]
if not data:
return
yield from self._update_media(data, response["aggregationData"])
params["before"] = data[-1]["id"]

View File

@@ -591,7 +591,7 @@ def subcategory_text(bc, c, sc):
sc = f"{sc[:-1]}ies" sc = f"{sc[:-1]}ies"
elif sc.endswith("h"): elif sc.endswith("h"):
sc = f"{sc}es" sc = f"{sc}es"
elif not sc.endswith("s"): elif not sc.endswith("s") and not sc.endswith("edia"):
sc = f"{sc}s" sc = f"{sc}s"
return sc return sc

View File

@@ -67,6 +67,11 @@ __tests__ = (
"#class" : fansly.FanslyCreatorPostsExtractor, "#class" : fansly.FanslyCreatorPostsExtractor,
}, },
{
"#url" : "https://fansly.com/Oliviaus/media",
"#class" : fansly.FanslyCreatorMediaExtractor,
},
{ {
"#url" : "https://fansly.com/home", "#url" : "https://fansly.com/home",
"#class" : fansly.FanslyHomeExtractor, "#class" : fansly.FanslyHomeExtractor,