Spaces:
Running
Running
| import asyncio | |
| import hashlib | |
| from datetime import datetime, timedelta, timezone | |
| from itertools import cycle | |
| from httpx import URL | |
| from pydantic import BaseModel, Extra, Field | |
| from hibiapi.utils.log import logger | |
| from hibiapi.utils.net import BaseNetClient | |
| from .constants import PixivConstants | |
| class AccountDataModel(BaseModel): | |
| class Config: | |
| extra = Extra.allow | |
| class PixivUserData(AccountDataModel): | |
| account: str | |
| id: int | |
| is_premium: bool | |
| mail_address: str | |
| name: str | |
| class PixivAuthData(AccountDataModel): | |
| time: datetime = Field(default_factory=datetime.now) | |
| expires_in: int | |
| access_token: str | |
| refresh_token: str | |
| user: PixivUserData | |
| class NetRequest(BaseNetClient): | |
| def __init__(self, tokens: list[str]): | |
| super().__init__( | |
| headers=PixivConstants.DEFAULT_HEADERS.copy(), | |
| proxies=PixivConstants.CONFIG["proxy"].as_dict(), | |
| ) | |
| self.user_tokens = cycle(tokens) | |
| self.auth_lock = asyncio.Lock() | |
| self.user_tokens_dict: dict[str, PixivAuthData] = {} | |
| self.headers["accept-language"] = PixivConstants.CONFIG["language"].as_str() | |
| def get_available_user(self): | |
| token = next(self.user_tokens) | |
| if (auth_data := self.user_tokens_dict.get(token)) and ( | |
| auth_data.time + timedelta(minutes=1, seconds=auth_data.expires_in) | |
| > datetime.now() | |
| ): | |
| return auth_data, token | |
| return None, token | |
| async def auth(self, refresh_token: str): | |
| url = URL(PixivConstants.AUTH_HOST).join("/auth/token") | |
| time = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S+00:00") | |
| headers = { | |
| **self.headers, | |
| "X-Client-Time": time, | |
| "X-Client-Hash": hashlib.md5( | |
| time.encode() + PixivConstants.HASH_SECRET | |
| ).hexdigest(), | |
| } | |
| payload = { | |
| "get_secure_url": 1, | |
| "client_id": PixivConstants.CLIENT_ID, | |
| "client_secret": PixivConstants.CLIENT_SECRET, | |
| "grant_type": "refresh_token", | |
| "refresh_token": refresh_token, | |
| } | |
| async with self as client: | |
| response = await client.post(url, data=payload, headers=headers) | |
| response.raise_for_status() | |
| self.user_tokens_dict[refresh_token] = PixivAuthData.parse_obj(response.json()) | |
| user_data = self.user_tokens_dict[refresh_token].user | |
| logger.opt(colors=True).info( | |
| f"Pixiv account <m>{user_data.id}</m> info <b>Updated</b>: " | |
| f"<b><e>{user_data.name}</e>({user_data.account})</b>." | |
| ) | |
| return self.user_tokens_dict[refresh_token] | |