SPB Git forge

spb/crea-ka

Public

Créa·Ka — annuaire public cross-plateforme des créateurs de contenu québécois (crea-ka.com)

52commits 1branches 0releases
11.3 MBsize
maindefault branch
19 days agolast push
Python 73.6% HTML 13.2% TypeScript 6% JavaScript 4.5% CSS 1.7% Dockerfile 0.6%
4.5 KB · 101 lines python
Raw Blame History
1# ==============================================================================2# Author: Simon-Pierre Boucher <contact@spboucher.ai>3# File:   src/main.py (ka-fansly)4# Desc:   Profils Fansly via l'API publique apiv3.fansly.com/api/v1/account5#         (?usernames=) : abonnés (followCount), bio, badge, compteurs de6#         médias (images/vidéos), avatar/bannière. Lots de 20 handles/requête.7# ==============================================================================8from __future__ import annotations910import asyncio11import json1213from apify import Actor1415from .net import Fetcher1617API_URL = ("https://apiv3.fansly.com/api/v1/account"18           "?usernames={us}&ngsw-bypass=true")192021def _loc(media: dict | None) -> str | None:22    """Objet média Fansly → URL de la première variante disponible."""23    if not isinstance(media, dict):24        return None25    for variant in ([media] + (media.get("variants") or [])):26        for loc in (variant.get("locations") or []):27            if loc.get("location"):28                return loc["location"]29    return None303132async def main() -> None:33    async with Actor:34        inp = await Actor.get_input() or {}35        usernames = [u.strip().lstrip("@").lower()36                     for u in (inp.get("usernames") or []) if u and u.strip()]37        proxy = await Actor.create_proxy_configuration(38            actor_proxy_input=inp.get("proxyConfiguration"))39        fetcher = Fetcher(proxy, delay=float(inp.get("requestDelay") or 1))4041        found: set[str] = set()42        for i in range(0, len(usernames), 20):43            batch = usernames[i:i + 20]44            try:45                resp = await fetcher.get(API_URL.format(us=",".join(batch)),46                                         session_id=f"b{i}",47                                         headers={"Accept":48                                                  "application/json"})49                data = (json.loads(resp.text)50                        if resp.status_code == 200 else {})51            except Exception as exc:52                for u in batch:53                    await Actor.push_data(54                        {"kind": "profile", "platform": "fansly",55                         "username": u, "found": False,56                         "error": str(exc)[:200]})57                continue58            for acc in (data.get("response") or []):59                u = str(acc.get("username") or "").lower()60                if not u:61                    continue62                found.add(u)63                stats = acc.get("timelineStats") or {}64                tiers = [{"name": t.get("name"), "price": t.get("price")}65                         for t in (acc.get("subscriptionTiers") or [])66                         if isinstance(t, dict) and t.get("name")]67                await Actor.push_data({68                    "kind": "profile",69                    "platform": "fansly",70                    "found": True,71                    "id": acc.get("id"),72                    "username": u,73                    "full_name": acc.get("displayName") or u,74                    "biography": (acc.get("about") or "")[:1000],75                    "followers": acc.get("followCount"),76                    "following": acc.get("followingCount"),77                    "is_verified": bool((acc.get("statusInfo") or {})78                                        .get("verified")79                                        or acc.get("verified")),80                    "posts_count": acc.get("postCount")81                                   or stats.get("postCount"),82                    "images_count": stats.get("imageCount"),83                    "videos_count": stats.get("videoCount"),84                    "likes": acc.get("accountMediaLikes"),85                    "avatar": _loc(acc.get("avatar")),86                    "banner": _loc(acc.get("banner")),87                    "location": acc.get("location"),88                    "account_created_at": acc.get("createdAt"),89                    "subscription_tiers": tiers[:5] or None,90                    "subscription_price": (tiers[0].get("price")91                                           if tiers else None),92                })93            for u in batch:94                if u not in found:95                    await Actor.push_data(96                        {"kind": "profile", "platform": "fansly",97                         "username": u, "found": False,98                         "error": f"not_found_{resp.status_code}"})99        Actor.log.info(f"terminé : {len(usernames)} profils, "100                       f"{len(found)} trouvés")101