mirror of
https://github.com/SantaSpeen/anixart.git
synced 2026-02-16 10:31:05 +00:00
Compare commits
37 Commits
v0.0.1-pla
...
v0.3.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
| aee5c542b4 | |||
| c740242c3f | |||
| 2f18c5ce85 | |||
| 9c94b68d55 | |||
| bb852d2b3a | |||
| d9a577ee14 | |||
| 52ebb42aa2 | |||
| d726088002 | |||
| 577e8dbf2a | |||
| c68735df97 | |||
| dd550855c0 | |||
| 733da877fa | |||
| 14fb575548 | |||
| 8762cca288 | |||
| d288837bb4 | |||
| 3bd27840e1 | |||
| 87fa813700 | |||
| 29205efe4f | |||
| 68e45336ad | |||
| ae1f97a07c | |||
| 85c7605b2c | |||
| 82874814e0 | |||
| 44e0485f99 | |||
| daec2199b6 | |||
| fbb738fd1c | |||
| c4d758f65a | |||
| 04be5ed2a9 | |||
| 87ff4463c4 | |||
| 403e7c8b79 | |||
| 2f0e4e3942 | |||
| 1c8d56dca6 | |||
| 326034fda9 | |||
| 0cf61563be | |||
| a6f1655449 | |||
| c6bfda72df | |||
| 8e9716cd06 | |||
| 9f50168205 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -130,3 +130,6 @@ dmypy.json
|
||||
|
||||
# PyCharm
|
||||
.idea/
|
||||
|
||||
poetry.lock
|
||||
secrets.txt
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2022 Maxim Khomutov
|
||||
Copyright (c) 2025 Maxim Khomutov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without limitation in the rights to use, copy, modify, merge, publish, and/ or distribute copies of the Software in an educational or personal context, subject to the following conditions:
|
||||
|
||||
|
||||
29
README.md
29
README.md
@@ -4,16 +4,25 @@
|
||||
Враппер для использования Anixart API.\
|
||||
Библиотека создана только для ознакомления c API.
|
||||
|
||||
**Автор презирает и не поддерживает создание авторегов / ботов для накрутки / спам ботов.**
|
||||
**Автор не поддерживает и презирает создание авторегов / ботов для накрутки / спам ботов.**
|
||||
|
||||
### Вся документация в папке [docs](./docs)
|
||||
### Установка
|
||||
|
||||
```shell
|
||||
pip install anixart
|
||||
```
|
||||
|
||||
### Как работать с библиотекой
|
||||
#### Вся документация в папке [docs](https://github.com/SantaSpeen/anixart/tree/master/docs)
|
||||
_(Пока что не дошли руки написать доки, гляньте в [examples](https://github.com/SantaSpeen/anixart/tree/master/examples) как работать с библиотекой)_
|
||||
|
||||
|
||||
## Обратная связь
|
||||
|
||||
Если у вас возникли вопросы, или вы хотите каким-либо образом помочь автору вы можете написать ему в телеграм: [@SantaSpeen](https://t.me/SantaSpeen)
|
||||
|
||||
<br/>
|
||||
|
||||
## License
|
||||
```text
|
||||
Copyright (c) 2022 Maxim Khomutov
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without limitation in the rights to use, copy, modify, merge, publish, and/ or distribute copies of the Software in an educational or personal context, subject to the following conditions:
|
||||
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
Permission is granted to sell and/ or distribute copies of the Software in a commercial context, subject to the following conditions:
|
||||
- Substantial changes: adding, removing, or modifying large parts, shall be developed in the Software. Reorganizing logic in the software does not warrant a substantial change.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
```
|
||||
|
||||
Проект распространяется под лицензией `FPA License`. Подробнее в [LICENSE](LICENSE)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .__meta__ import *
|
||||
|
||||
from .api import AnixartAPI
|
||||
from .auth import AnixartAccount, AnixartAccountGuest, AnixartAccountSaved
|
||||
|
||||
from .endpoints import *
|
||||
|
||||
from . import enums
|
||||
from . import exceptions
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
__title__ = 'anixart'
|
||||
__description__ = 'Wrapper for using the Anixart API.'
|
||||
__url__ = 'https://github.com/SantaSpeen/anixart'
|
||||
__version__ = '0.0.1'
|
||||
__build__ = 1
|
||||
__author__ = 'Maxim Khomutov'
|
||||
__version__ = '0.2.1'
|
||||
__build__ = 3
|
||||
__author__ = 'SantaSpeen'
|
||||
__author_email__ = 'SantaSpeen@gmail.com'
|
||||
__license__ = "FPA"
|
||||
__copyright__ = 'Copyright 2022 Maxim Khomutov'
|
||||
__copyright__ = 'Copyright 2022 © SantaSpeen'
|
||||
129
anixart/api.py
Normal file
129
anixart/api.py
Normal file
@@ -0,0 +1,129 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import requests
|
||||
|
||||
from .__meta__ import __version__, __build__
|
||||
from .auth import AnixartAccount, AnixartAccountGuest
|
||||
from .enums import AnixartApiErrors
|
||||
from .endpoints import API_URL
|
||||
from .exceptions import AnixartAPIRequestError, AnixartAPIError
|
||||
from .exceptions import AnixartInitError
|
||||
|
||||
|
||||
class AnixartAPI:
|
||||
|
||||
def __init__(self, account: AnixartAccount = None):
|
||||
if account is None:
|
||||
account = AnixartAccountGuest()
|
||||
if not isinstance(account, AnixartAccount):
|
||||
raise AnixartInitError(f'Use class "AnixartAccount" for user. But {type(account)} given.')
|
||||
|
||||
self.use_account(account)
|
||||
|
||||
def use_account(self, account: AnixartAccount):
|
||||
if not isinstance(account, AnixartAccount):
|
||||
raise AnixartInitError(f'Use class "AnixartAccount" for user. But {type(account)} given.')
|
||||
self.__account = account
|
||||
self.__account._set_api(self)
|
||||
self.__account.login()
|
||||
|
||||
self.__token = account.token
|
||||
self._session = account.session
|
||||
self._session.headers = {
|
||||
'User-Agent': f'AnixartPyAPI/{__version__}-{__build__} (Linux; Android 15; AnixartPyAPI Build/{__build__})'
|
||||
}
|
||||
|
||||
@property
|
||||
def account(self):
|
||||
return self.__account
|
||||
|
||||
def __parse_response(self, res: requests.Response):
|
||||
if res.status_code != 200:
|
||||
e = AnixartAPIRequestError("Bad Request: Invalid request parameters.")
|
||||
e.message = (
|
||||
f"Bad Request: Invalid request parameters.\n"
|
||||
f"Request: {res.request.method} {res.url}\n"
|
||||
f"Status code: {res.status_code}\n"
|
||||
f"Response: {res.text}\n"
|
||||
f"Client headers: {self._session.headers}\n"
|
||||
f"Client payload: {res.request.body}"
|
||||
)
|
||||
e.code = 400
|
||||
raise e
|
||||
if not res.text:
|
||||
raise AnixartAPIError("AnixartAPI send unknown error: Empty response. Is provided data correct?")
|
||||
try:
|
||||
response = res.json()
|
||||
except ValueError as e:
|
||||
raise AnixartAPIError("Failed to parse JSON response")
|
||||
|
||||
# print(response)
|
||||
if response['code'] != 0:
|
||||
code = response['code']
|
||||
if code in AnixartApiErrors:
|
||||
e = AnixartAPIError(f"AnixartAPI send error: {AnixartApiErrors(code).name}")
|
||||
e.message = AnixartApiErrors(code).name
|
||||
else:
|
||||
e = AnixartAPIError(f"AnixartAPI send unknown error, code: {response['code']}")
|
||||
e.code = response['code']
|
||||
raise e
|
||||
|
||||
return response
|
||||
|
||||
def _post(self, method: str, payload: dict = None, is_json: bool = False, **kwargs):
|
||||
if payload is None:
|
||||
payload = {}
|
||||
url = API_URL + method
|
||||
if payload.get("token") is None:
|
||||
if self.__token is not None:
|
||||
payload.update({"token": self.__token})
|
||||
url += "?token=" + self.__token
|
||||
else:
|
||||
token = kwargs.get("token")
|
||||
if token is not None:
|
||||
payload.update({"token": token})
|
||||
url += "?token=" + token
|
||||
kwargs = {"url": url}
|
||||
if is_json:
|
||||
self._session.headers["Content-Type"] = "application/json; charset=UTF-8"
|
||||
self._session.headers["Content-Length"] = str(len(payload))
|
||||
kwargs.update({"json": payload})
|
||||
else:
|
||||
kwargs.update({"data": payload})
|
||||
res = self._session.post(**kwargs)
|
||||
self._session.headers["Content-Type"] = ""
|
||||
self._session.headers["Content-Length"] = ""
|
||||
return self.__parse_response(res)
|
||||
|
||||
def _get(self, method: str, payload: dict = None, **kwargs):
|
||||
if payload is None:
|
||||
payload = {}
|
||||
if payload.get("token") is None:
|
||||
if self.__token is not None:
|
||||
payload.update({"token": self.__token})
|
||||
else:
|
||||
token = kwargs.get("token")
|
||||
if token is not None:
|
||||
payload.update({"token": token})
|
||||
res = self._session.get(API_URL + method, params=payload)
|
||||
return self.__parse_response(res)
|
||||
|
||||
def execute(self, http_method, endpoint, **kwargs):
|
||||
http_method = http_method.upper()
|
||||
if http_method == "GET":
|
||||
return self._get(endpoint, **kwargs)
|
||||
elif http_method == "POST":
|
||||
return self._post(endpoint, **kwargs)
|
||||
else:
|
||||
raise AnixartAPIRequestError("Allow only GET and POST requests.")
|
||||
|
||||
def get(self, endpoint, *args, **kwargs):
|
||||
return self.execute("GET", endpoint.format(*args), **kwargs)
|
||||
|
||||
def post(self, endpoint, *args, **kwargs):
|
||||
return self.execute("POST", endpoint.format(*args), **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return f'AnixartAPI(account={self.__account!r})'
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self}>"
|
||||
1
anixart/auth/__init__.py
Normal file
1
anixart/auth/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .account import AnixartAccount, AnixartAccountSaved, AnixartAccountGuest
|
||||
126
anixart/auth/account.py
Normal file
126
anixart/auth/account.py
Normal file
@@ -0,0 +1,126 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
from anixart import endpoints
|
||||
from anixart.exceptions import AnixartInitError
|
||||
|
||||
|
||||
class AnixartAccount:
|
||||
guest = False
|
||||
def __init__(self, username: str, password: str):
|
||||
self._username = username
|
||||
self._password = password
|
||||
if not isinstance(username, str) or not isinstance(password, str):
|
||||
raise AnixartInitError("Auth data must be strings.")
|
||||
|
||||
self._id = None
|
||||
self._token = None
|
||||
self._session = requests.Session()
|
||||
|
||||
self._api = None
|
||||
|
||||
def _set_api(self, api):
|
||||
self._api = api
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
return self._username
|
||||
|
||||
@property
|
||||
def session(self):
|
||||
return self._session
|
||||
|
||||
@property
|
||||
def token(self):
|
||||
return self._token
|
||||
|
||||
def to_file(self, filename: str | Path) -> "AnixartAccountSaved":
|
||||
"""Save the account information to a file."""
|
||||
acc = AnixartAccountSaved.from_account(filename, self)
|
||||
acc.save()
|
||||
return acc
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, filename: str | Path) -> "AnixartAccountSaved":
|
||||
"""Load the account information from a file."""
|
||||
acc = AnixartAccountSaved(filename)
|
||||
acc.load()
|
||||
return acc
|
||||
|
||||
def login(self):
|
||||
"""Login to Anixart and return the token."""
|
||||
payload = {"login": self.username, "password": self._password}
|
||||
res = self._api.post(endpoints.SING_IN, payload)
|
||||
uid = res["profile"]["id"]
|
||||
token = res["profileToken"]["token"]
|
||||
self._id = uid
|
||||
self._token = token
|
||||
|
||||
def __str__(self):
|
||||
return f'AnixartAccount(login={self._username!r}, password={"*" * len(self._password)!r})'
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self}>"
|
||||
|
||||
class AnixartAccountSaved(AnixartAccount):
|
||||
|
||||
def __init__(self, account_file: str | Path = "anixart_account.json"):
|
||||
super().__init__("", "")
|
||||
self._file = Path(account_file)
|
||||
|
||||
def save(self):
|
||||
"""Save the account information to a file."""
|
||||
data = {
|
||||
"id": self._id,
|
||||
"token": self._token
|
||||
}
|
||||
with open(self._file, 'w') as f:
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
def load(self):
|
||||
"""Load the account information from a file."""
|
||||
if not self._file.exists():
|
||||
raise AnixartInitError(f"Account file {self._file} does not exist.")
|
||||
with open(self._file, 'r') as f:
|
||||
data = json.load(f)
|
||||
self._id = data.get("id")
|
||||
self._username = data.get("username")
|
||||
self._token = data.get("token")
|
||||
if not self._id or not self._token:
|
||||
raise AnixartInitError("id and token must be provided in the account file.")
|
||||
|
||||
@classmethod
|
||||
def from_account(cls, account_file: str | Path, account: AnixartAccount):
|
||||
c = cls(account_file)
|
||||
c._username = account.username
|
||||
c._password = account._password
|
||||
c._token = account.token
|
||||
return c
|
||||
|
||||
def login(self):
|
||||
"""Login to Anixart using the saved credentials."""
|
||||
# Проверяем токен, если просрочен, то логинимся
|
||||
# Если токен валиден, то просто дальше работаем
|
||||
# TODO: Implement login logic here
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return f'AnixartAccountSaved(account_file={self._file!r}")'
|
||||
|
||||
class AnixartAccountGuest(AnixartAccount):
|
||||
guest = True
|
||||
def __init__(self):
|
||||
super().__init__("", "")
|
||||
self._token = ""
|
||||
|
||||
def login(self): ...
|
||||
|
||||
def __str__(self):
|
||||
return f'AnixartAccountGuest(token={self._token!r}")'
|
||||
|
||||
11
anixart/auth/error_handler.py
Normal file
11
anixart/auth/error_handler.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class AnixartAuthErrors(IntEnum):
|
||||
INCORRECT_LOGIN = 2
|
||||
INCORRECT_PASSWORD = 3
|
||||
|
||||
|
||||
def errors_handler(error):
|
||||
"""Handle errors and return a JSON response."""
|
||||
pass
|
||||
7
anixart/auth/objects.py
Normal file
7
anixart/auth/objects.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class ProfileToken:
|
||||
id: int
|
||||
token: str
|
||||
272
anixart/endpoints.py
Normal file
272
anixart/endpoints.py
Normal file
@@ -0,0 +1,272 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
API_URL = "https://api.anixart.tv"
|
||||
|
||||
# ----------- # AUTH # ----------- #
|
||||
|
||||
# POST
|
||||
SING_UP = None # Удалено дабы исключить автореги
|
||||
SING_IN = "/auth/signIn"
|
||||
|
||||
# Not Checked
|
||||
# POST
|
||||
_AUTH_SING_IN_WITH_GOOGLE = "/auth/google" # {googleIdToken} or {login, email, googleIdToken}
|
||||
_AUTH_SING_IN_WITH_VK = "/auth/vk" # {vkAccessToken}
|
||||
|
||||
# ----------- # PROFILE # ----------- #
|
||||
# TODO PROFILE: SETTINGS, SETTINGS_RELEASE, SETTINGS_RELEASE_FIRST,
|
||||
# SETTINGS_COMMENTS, SETTINGS_COLLECTION, EDIT_AVATAR, SETTINGS_RELEASE_LIST,
|
||||
# SETTINGS_RELEASE_TYPE
|
||||
|
||||
# GET
|
||||
PROFILE = "/profile/{}" # + profile id (Токен нужен только что бы был is_my_profile)
|
||||
PROFILE_NICK_HISTORY = "/profile/login/history/all/{}/{}" # profile id / page (Токен не нужен)
|
||||
|
||||
PROFILE_BLACKLIST = "/profile/blocklist/all/{}" # page
|
||||
PROFILE_BLACKLIST_ADD = "/profile/blocklist/add/{}" # profile id
|
||||
PROFILE_BLACKLIST_REMOVE = "/profile/blocklist/remove/{}" # profile id
|
||||
|
||||
FRIENDS = "/profile/friend/all/{}/{}" # profile id / page
|
||||
FRIENDS_RQ_IN = "/profile/friend/requests/in/{}" # page
|
||||
FRIENDS_RQ_OUT = "/profile/friend/requests/out/{}" # page
|
||||
FRIENDS_RQ_IN_LAST = "/profile/friend/requests/in/last"
|
||||
FRIENDS_RQ_OUT_LAST = "/profile/friend/requests/out/last"
|
||||
FRIENDS_SEND = "/profile/friend/request/send/{}" # profile id
|
||||
FRIENDS_REMOVE = "/profile/friend/request/remove/{}" # profile id
|
||||
|
||||
VOTE_VOTED = "/profile/vote/release/voted/{}/{}" # profile id / page
|
||||
# Да, ребята из аниксарта не знают английский; ↓
|
||||
# noinspection SpellCheckingInspection
|
||||
VOTE_UNVENTED = "/profile/vote/release/unvoted/{}" # page
|
||||
|
||||
LISTS = "/profile/list/all/{}/{}/{}" # profile id / list id / page
|
||||
|
||||
SETTINGS_NOTIFICATION = "/profile/preference/notification/my"
|
||||
SETTINGS_NOTIFICATION_RELEASE = "/profile/preference/notification/episode/edit"
|
||||
SETTINGS_NOTIFICATION_RELEASE_FIRST = "/profile/preference/notification/episode/first/edit"
|
||||
SETTINGS_NOTIFICATION_COMMENTS = "/profile/preference/notification/comment/edit"
|
||||
SETTINGS_NOTIFICATION_COLLECTION = "/profile/preference/notification/my/collection/comment/edit"
|
||||
|
||||
CHANGE_PASSWORD = "/profile/preference/password/change"
|
||||
|
||||
# POST
|
||||
EDIT_STATUS = "/profile/preference/status/edit"
|
||||
EDIT_SOCIAL = "/profile/preference/social/edit"
|
||||
EDIT_AVATAR = "/profile/preference/avatar/edit"
|
||||
|
||||
# {"profileStatusNotificationPreferences":[0 - favorite, + all in AnixList]}
|
||||
SETTINGS_NOTIFICATION_RELEASE_LIST = "/profile/preference/notification/status/edit"
|
||||
# {"profileTypeNotificationPreferences":[type ids]}
|
||||
SETTINGS_NOTIFICATION_RELEASE_TYPE = "/profile/preference/notification/type/edit"
|
||||
|
||||
# Not Checked
|
||||
# GET
|
||||
PROFILE_SOCIAL = "/profile/social/{}" # profile id
|
||||
|
||||
FRIENDS_RECOMMENDATION = "/profile/friend/recommendations"
|
||||
FRIENDS_RQ_HIDE = "profile/friend/request/hide/{}" # profile id
|
||||
|
||||
SETTINGS_PROFILE = "/profile/preference/my"
|
||||
SETTINGS_PROFILE_CHANGE_EMAIL = "/profile/preference/email/change" # {current_password, current, new}
|
||||
SETTINGS_PROFILE_CHANGE_EMAIL_CONFIRM = "/profile/preference/email/change/confirm" # {current}
|
||||
|
||||
# /profile/preference/social
|
||||
SETTINGS_PROFILE_STATUS_DELETE = "/profile/preference/status/delete"
|
||||
|
||||
# POST
|
||||
PROFILE_PROCESS = "/profile/process/{}" # profile id
|
||||
|
||||
SETTINGS_PROFILE_CHANGE_LOGIN = "/profile/preference/email/login/confirm" # {login}
|
||||
SETTINGS_PROFILE_CHANGE_LOGIN_INFO = "/profile/preference/email/login/info" # {login}
|
||||
|
||||
SETTINGS_PROFILE_BIND_GOOGLE = "/profile/preference/google/bind" # {idToken, }
|
||||
SETTINGS_PROFILE_UNBIND_GOOGLE = "/profile/preference/google/unbind"
|
||||
SETTINGS_PROFILE_BIND_VK = "/profile/preference/google/bind" # {accessToken, }
|
||||
SETTINGS_PROFILE_UNBIND_VK = "/profile/preference/google/unbind"
|
||||
|
||||
SETTINGS_PROFILE_PRIVACY_COUNTS = "/profile/preference/privacy/counts/edit"
|
||||
SETTINGS_PROFILE_PRIVACY_FRIENDS_REQUESTS = "/profile/preference/privacy/friendRequests/edit"
|
||||
SETTINGS_PROFILE_PRIVACY_SOCIAL = "/profile/preference/privacy/social/edit"
|
||||
SETTINGS_PROFILE_PRIVACY_STATS = "/profile/preference/privacy/stats/edit"
|
||||
|
||||
# ----------- # COLLECTION # ----------- #
|
||||
|
||||
# GET
|
||||
COLLECTION = "/collection/{}" # collection id
|
||||
COLLECTION_RELEASES = "/collection/{}/releases/{}" # collection id / page
|
||||
COLLECTION_LIST = "/collection/all/{}" # page
|
||||
|
||||
COLLECTION_COMMENTS = "/collection/comment/all/{}/{}" # collection id / page
|
||||
COLLECTION_COMMENTS_VOTE = "/collection/comment/vote/{}/{}" # collection comment id / mark (1, 2)
|
||||
COLLECTION_COMMENTS_VOTES = "/collection/comment/votes/{}/{}" # collection comment id / page
|
||||
COLLECTION_COMMENTS_DELETE = "/collection/comment/delete/{}" # collection comment id
|
||||
|
||||
COLLECTION_FAVORITE = "/collectionFavorite/all/{}" # page
|
||||
COLLECTION_FAVORITE_ADD = "/collectionFavorite/add/{}" # collection id
|
||||
COLLECTION_FAVORITE_DELETE = "/collectionFavorite/delete/{}" # collection id
|
||||
|
||||
# POST
|
||||
COLLECTION_COMMENTS_ADD = "/collection/comment/add/{}" # collection id
|
||||
COLLECTION_COMMENTS_EDIT = "/collection/comment/edit/{}" # collection comment id
|
||||
COLLECTION_COMMENTS_REPLIES = "/collection/comment/replies/{}/{}" # collection comment id / page
|
||||
|
||||
# Not Checked
|
||||
# GET
|
||||
COLLECTION_PROFILE = "/collection/all/profile/{}/{}" # p_id/page
|
||||
COLLECTION_RELEASE_IN = "/collection/all/release/{}/{}" # r_id/page
|
||||
|
||||
COLLECTION_MY = "/collectionMy/{id}/releases"
|
||||
COLLECTION_MY_DELETE = "/collectionMy/delete/{}" # collectionId
|
||||
COLLECTION_MY_ADD_RELEASE = "/collectionMy/release/add/{}" # collectionId
|
||||
|
||||
# POST
|
||||
COLLECTION_COMMENTS_PROCESS = "/collection/comment/process/{}" # commentId
|
||||
COLLECTION_COMMENTS_REPORT = "/collection/comment/report/{}" # commentId
|
||||
|
||||
COLLECTION_REPORT = "/collection/report/{}" # collectionId
|
||||
|
||||
COLLECTION_MY_CREATE = "/collectionMy/create"
|
||||
COLLECTION_MY_EDIT = "/collectionMy/edit/{}" # collectionId
|
||||
COLLECTION_MY_EDIT_IMAGE = "/collectionMy/editImage/{}" # collectionId
|
||||
|
||||
# ----------- # RELEASE # ----------- #
|
||||
|
||||
# GET
|
||||
RELEASE = "/release/{}" # release id
|
||||
RELEASE_VOTE_ADD = "/release/vote/add/{}/{}" # release id / mark 1-5
|
||||
RELEASE_VOTE_DELETE = "/release/vote/delete/{}" # release id
|
||||
RELEASE_RANDOM = "/release/random"
|
||||
|
||||
RELEASE_COMMENTS = "/release/comment/all/{}/{}" # release id / page
|
||||
RELEASE_COMMENTS_VOTE = "/release/comment/vote/{}/{}" # release comment id / mark (1, 2)
|
||||
RELEASE_COMMENTS_VOTES = "/release/comment/votes/{}/{}" # release comment id / page
|
||||
RELEASE_COMMENTS_REPLIES = "/release/comment/replies/{}/{}" # release comment id / page
|
||||
RELEASE_COMMENTS_DELETE = "/release/comment/delete/{}" # release comment id
|
||||
|
||||
# POST
|
||||
RELEASE_COMMENTS_ADD = "/release/comment/add/{}" # release id
|
||||
RELEASE_COMMENTS_EDIT = "/release/comment/edit/{}" # release comment id
|
||||
|
||||
# Not Checked
|
||||
# GET
|
||||
RELEASE_COMMENTS_REPORT = "/release/comment/report/{}" # commentId
|
||||
RELEASE_COMMENTS_PROCESS = "/release/comment/process/{}" # commentId
|
||||
|
||||
RELEASE_PROFILE_COMMENTS = "/release/comment/all/profile/{}/{}" # p_id/page
|
||||
|
||||
RELEASE_STREAMING_PLATFORM = "/release/streaming/platform/{}/" # releaseId
|
||||
|
||||
# POST
|
||||
RELEASE_REPORT = "/release/report/{}" # r_id
|
||||
|
||||
# ----------- # OTHER # ----------- #
|
||||
# TODO OTHER: EXPORT_BOOKMARKS, IMPORT_BOOKMARKS, CAN_IMPORT_BOOKMARKS
|
||||
|
||||
# GET
|
||||
TYPE = "/type/all"
|
||||
TYPE_RELEASE = "/type/{}" # r_id
|
||||
TOGGLES = "/config/toggles?version_code={}&is_beta={}" # version_code: int, is_beta: bool
|
||||
SCHEDULE = "/schedule"
|
||||
|
||||
# POST
|
||||
# {"bookmarksExportProfileLists":[0 - favorite, + all in AnixList]}
|
||||
EXPORT_BOOKMARKS = "/export/bookmarks"
|
||||
# {"completed":[],"dropped":[],"holdOn":[],"plans":[],"watching":[],"selected_importer_name":"Shikimori"}
|
||||
IMPORT_BOOKMARKS = "/import/bookmarks"
|
||||
CAN_IMPORT_BOOKMARKS = "/import/status" # code: 0 - Yes, code: 2 - no
|
||||
|
||||
# Not Checked
|
||||
# GET
|
||||
# page {token, genres: [], studio, category, status, year, episodes, sort,
|
||||
# country, season, duration, ratings: [], extended_mode: bool}
|
||||
FILTER = "/filter/{}"
|
||||
RELATED = "related/{}/{}" # relatedId/page
|
||||
|
||||
# POST
|
||||
VIDEO_PARSE = "/video/parse"
|
||||
|
||||
# ----------- # SEARCH # ----------- #
|
||||
# TODO SEARCH: *
|
||||
|
||||
# { "query": text, "searchBy": 0}
|
||||
# POST
|
||||
SEARCH_COLLECTION = "/search/collections/{}" # page
|
||||
SEARCH_RELEASE = "/search/releases/{}" # page
|
||||
SEARCH_FAVORITE = "/search/favorites/{}" # page
|
||||
SEARCH_COLLECTION_FAVORITE = "/search/favoriteCollections/{}" # page
|
||||
SEARCH_LIST = "/search/profile/list/{}/{}" # list id / page
|
||||
SEARCH_PROFILE = "/search/profiles/{}" # page
|
||||
SEARCH_HISTORY = "/search/history/{}" # page
|
||||
|
||||
#
|
||||
SEARCH_COLLECTION_PROFILE = "/search/profileCollections/{}/{}" # p_id/page
|
||||
|
||||
# ----------- # NOTIFICATIONS # ----------- #
|
||||
# TODO NOTIFICATIONS: *
|
||||
|
||||
# GET
|
||||
NOTIFICATION_READ = "/notification/read"
|
||||
NOTIFICATION_COUNT = "/notification/count"
|
||||
NOTIFICATION_COLLECTION_COMMENTS = "/notification/collectionComments/{}" # page
|
||||
NOTIFICATION_MY_COLLECTION_COMMENTS = "/notification/my/collection/comments/{}" # page
|
||||
NOTIFICATION_RELEASE_COMMENTS = "/notification/releaseComments/{}" # page
|
||||
NOTIFICATION_EPISODES = "/notification/episodes/{}" # page
|
||||
NOTIFICATION_FRIEND = "/notification/friends/{}" # page
|
||||
|
||||
NOTIFICATION_COLLECTION_COMMENTS_DELETE = "/notification/collectionComment/delete/{}" # n_id
|
||||
NOTIFICATION_MY_COLLECTION_COMMENTS_DELETE = "/notification/my/collection/comment/delete/{}" # page
|
||||
NOTIFICATION_RELEASE_COMMENTS_DELETE = "/notification/releaseComment/delete/{}" # page
|
||||
NOTIFICATION_EPISODES_DELETE = "/notification/episode/delete/{}" # page
|
||||
NOTIFICATION_FRIEND_DELETE = "/notification/friends/delete/{}" # page
|
||||
|
||||
# ----------- # DISCOVER # ----------- #
|
||||
# TODO DISCOVER: *
|
||||
|
||||
# Not Checked
|
||||
# POST
|
||||
DISCOVER_COMMENTS = "/discover/comments"
|
||||
DISCOVER_DISCUSSING = "/discover/discussing" # {token}
|
||||
DISCOVER_INTERESTING = "/discover/interesting"
|
||||
DISCOVER_RECOMMENDATION = "/discover/recommendations/{}" # page
|
||||
DISCOVER_WATCHING = "/discover/watching/{}" # page
|
||||
|
||||
# ----------- # EpisodeApi.kt # ----------- #
|
||||
# TODO EpisodeApi.kt: *
|
||||
|
||||
# Здесь методы из com.swiftsoft.anixartd.network.api.EpisodeApi.kt
|
||||
# Если надо, переделай так как тебе надо
|
||||
|
||||
# Not Checked
|
||||
# GET
|
||||
# @GET("episode/target/{releaseId}/{sourceId}/{position}")
|
||||
# @GET("episode/{releaseId}/{typeId}/{sourceId}")
|
||||
# @GET("episode/{releaseId}")
|
||||
# @GET("episode/updates/{releaseId}/{page}")
|
||||
|
||||
# POST
|
||||
# @POST("episode/report/{releaseId}/{sourceId}/{position}")
|
||||
# @POST("episode/unwatch/{releaseId}/{sourceId}/{position}")
|
||||
# @POST("episode/unwatch/{releaseId}/{sourceId}")
|
||||
# @POST("episode/watch/{releaseId}/{sourceId}/{position}")
|
||||
# @POST("episode/watch/{releaseId}/{sourceId}")
|
||||
|
||||
# ----------- # FAVORITE # ----------- #
|
||||
# TODO FAVORITE: *
|
||||
|
||||
# Not Checked
|
||||
# GET
|
||||
FAVORITE = "/favorite/all/{}" # r_id
|
||||
FAVORITE_ADD = "/favorite/add/{}" # r_id
|
||||
FAVORITE_DELETE = "/favorite/delete/{}" # r_id {sort}
|
||||
|
||||
# ----------- # HISTORY # ----------- #
|
||||
# TODO HISTORY: *
|
||||
|
||||
# GET
|
||||
HISTORY = "/history/{}" # page
|
||||
|
||||
# Not Checked
|
||||
# GET
|
||||
HISTORY_ADD = "/history/add/{}/{}/{}" # r_id/s_id/position
|
||||
HISTORY_DELETE = "/history/delete/{}" # r_id
|
||||
28
anixart/enums.py
Normal file
28
anixart/enums.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from enum import IntEnum
|
||||
|
||||
class AnixartApiErrors(IntEnum):
|
||||
""" Error codes for AnixartApi authentication."""
|
||||
INCORRECT_LOGIN = 2
|
||||
INCORRECT_PASSWORD = 3
|
||||
|
||||
class AnixartComment(IntEnum):
|
||||
DISLIKE = 1
|
||||
LIKE = 2
|
||||
|
||||
|
||||
class AnixartProfileVotedSort(IntEnum):
|
||||
LAST_FIRST = 1
|
||||
OLD_FIRST = 2
|
||||
STAR_5 = 3
|
||||
STAR_4 = 4
|
||||
STAR_3 = 5
|
||||
STAR_2 = 6
|
||||
STAR_1 = 7
|
||||
|
||||
|
||||
class AnixartLists(IntEnum):
|
||||
WATCHING = 1
|
||||
IN_PLANS = 2
|
||||
WATCHED = 3
|
||||
POSTPONED = 4
|
||||
DROPPED = 5
|
||||
17
anixart/exceptions.py
Normal file
17
anixart/exceptions.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class AnixartBasError(Exception): ...
|
||||
|
||||
# Init errors
|
||||
|
||||
class AnixartInitError(AnixartBasError, TypeError): ...
|
||||
|
||||
# API errors
|
||||
class AnixartAPIError(AnixartBasError):
|
||||
message = "unknown error"
|
||||
code = 0
|
||||
|
||||
class AnixartAuthError(AnixartAPIError): ...
|
||||
|
||||
class AnixartAPIRequestError(AnixartAPIError): ...
|
||||
|
||||
1
anixart/profile/__init__.py
Normal file
1
anixart/profile/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .objects import Profile, ProfileVote, ProfileRoles, ProfileHistory, ProfileFriendsPreview
|
||||
11
anixart/profile/error_handler.py
Normal file
11
anixart/profile/error_handler.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class AnixartProfileErrors(IntEnum):
|
||||
""" Error codes for AnixartApi authentication."""
|
||||
PROFILE_NOT_FOUND = 2
|
||||
|
||||
|
||||
def errors_handler(error):
|
||||
"""Handle errors and return a JSON response."""
|
||||
pass
|
||||
118
anixart/profile/objects.py
Normal file
118
anixart/profile/objects.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class ProfileHistory:
|
||||
# TODO: Надо ещё изучить релизы
|
||||
pass
|
||||
|
||||
@dataclass
|
||||
class ProfileVote:
|
||||
# TODO: Надо ещё изучить релизы
|
||||
pass
|
||||
|
||||
@dataclass
|
||||
class ProfileRoles:
|
||||
# TODO: Надо ещё изучить роли (У меня их нет(()
|
||||
pass
|
||||
|
||||
@dataclass
|
||||
class ProfileFriendsPreview:
|
||||
id: int
|
||||
avatar: str
|
||||
login: str
|
||||
friend_count: int
|
||||
friend_status: int
|
||||
is_sponsor: bool
|
||||
is_online: bool
|
||||
is_verified: bool
|
||||
is_social: bool
|
||||
badge_id: None
|
||||
badge_name: None
|
||||
badge_type: None
|
||||
badge_url: None
|
||||
|
||||
@dataclass
|
||||
class Profile:
|
||||
id: int
|
||||
login: str
|
||||
avatar: str
|
||||
status: str
|
||||
rating_score: int
|
||||
history: list[ProfileHistory]
|
||||
votes: list[ProfileVote]
|
||||
roles: list[ProfileRoles]
|
||||
friends_preview: list[ProfileFriendsPreview]
|
||||
collections_preview: list # TODO: Коллекции изучить
|
||||
release_comments_preview: list # Тут вообще не понял
|
||||
comments_preview: list # Тут типа превью к коментам
|
||||
release_videos_preview: list # Тут вообще не понял
|
||||
watch_dynamics: list # Тут вообще не понял
|
||||
last_activity_time: int
|
||||
register_date: int
|
||||
vk_page: str
|
||||
tg_page: str
|
||||
inst_page: str
|
||||
tt_page: str
|
||||
discord_page: str
|
||||
ban_expires: int
|
||||
ban_reason: str
|
||||
privilege_level: int
|
||||
watching_count: int
|
||||
plan_count: int
|
||||
completed_count: int
|
||||
hold_on_count: int
|
||||
dropped_count: int
|
||||
favorite_count: int
|
||||
comment_count: int
|
||||
collection_count: int
|
||||
video_count: int
|
||||
friend_count: int
|
||||
subscription_count: int
|
||||
watched_episode_count: int
|
||||
watched_time: int
|
||||
sponsorshipExpires: int
|
||||
is_private: bool
|
||||
is_sponsor: bool
|
||||
is_banned: bool
|
||||
is_perm_banned: bool
|
||||
is_bookmarks_transferred: bool
|
||||
is_sponsor_transferred: bool
|
||||
is_vk_bound: bool
|
||||
is_google_bound: bool
|
||||
is_release_type_notifications_enabled: bool
|
||||
is_episode_notifications_enabled: bool
|
||||
is_first_episode_notification_enabled: bool
|
||||
is_related_release_notifications_enabled: bool
|
||||
is_report_process_notifications_enabled: bool
|
||||
is_comment_notifications_enabled: bool
|
||||
is_my_collection_comment_notifications_enabled: bool
|
||||
is_my_article_comment_notifications_enabled: bool
|
||||
is_verified: bool
|
||||
is_blocked: bool
|
||||
is_me_blocked: bool
|
||||
is_stats_hidden: bool
|
||||
is_counts_hidden: bool
|
||||
is_social_hidden: bool
|
||||
is_friend_requests_disallowed: bool
|
||||
is_online: bool
|
||||
badge: None
|
||||
friend_status: None
|
||||
|
||||
is_my_profile: bool = False
|
||||
|
||||
@classmethod
|
||||
def from_response(cls, response: dict) -> "Profile":
|
||||
profile = {"is_my_profile": response['is_my_profile'], **response['profile']}
|
||||
return cls(**profile)
|
||||
|
||||
class _login:
|
||||
pos_id: int
|
||||
id: int
|
||||
newLogin: str
|
||||
timestamp: int
|
||||
|
||||
class ProfileLoginsHistory:
|
||||
content: list[_login]
|
||||
total_count: int
|
||||
# total_page_count: int
|
||||
# current_page: int
|
||||
@@ -2,8 +2,73 @@
|
||||
|
||||
## Anixart API Wrapper
|
||||
|
||||
### 02.04.2025
|
||||
#### Version: 0.3.0.1
|
||||
|
||||
##### Changes:
|
||||
|
||||
* Глобальная переборка всего проекта по `SDK-like` принципу
|
||||
* Переработан блок `API`:
|
||||
* `AnixartAPI` - Теперь является основным классом для работы с API.
|
||||
* Добавлен блок `auth`:
|
||||
* Работа с аккаунтом:
|
||||
* `AnixartAccount` - Основной класс взаимодействия с аккаунтом (логин, пароль)
|
||||
* `AnixartAccountSaved` - Работа с сохранённым аккаунтом
|
||||
* `AnixartAccountGuest` - Авторизация без логина и пароля (Как гость)
|
||||
* Добавлены ошибки
|
||||
* `INCORRECT_LOGIN` - Неверный логин
|
||||
* `INCORRECT_PASSWORD` - Неверный пароль
|
||||
* Добавлен блок `profile`:
|
||||
* Добавлены объекты для работы с `/profile/`:
|
||||
* `Profile` - ДатаКласс для работы с профилем (`endpoints.PROFILE`) __не закончены подклассы__
|
||||
* `ProfileLoginsHistory` - ДатаКласс для работы с историей логинов (`endpoints.PROFILE_NICK_HISTORY`) __не закончен__
|
||||
* Добавлены ошибки
|
||||
* `PROFILE_NOT_FOUND` - Профиль не найден (или невалидный)
|
||||
|
||||
### 28.09.2022
|
||||
#### Version: 0.2.1
|
||||
|
||||
##### Changes:
|
||||
|
||||
* Перетащил файлы из прошлого проекта
|
||||
- Поменял нейминиги
|
||||
- Оптимизировал `request_handler.py`
|
||||
- Оптимизировал `errors.py`
|
||||
- Оптимизировал `auth.py`
|
||||
- Оптимизировал `api/api.py`
|
||||
- Добавил `api/api.pyi`
|
||||
* Добавил пример: `/examples/auth.py`
|
||||
* Обновил зависимости
|
||||
|
||||
##### TODOs:
|
||||
|
||||
* Метод `AnixartAPI.execute()` пересобрать и сделать нормальный хандлер запроса.
|
||||
|
||||
_Из прошлых версий_
|
||||
|
||||
* Проверить эндпоинты через чарлес
|
||||
- Задокументировать методы
|
||||
- Выявить и удалить не рабочие
|
||||
- Выявить и удалить не используемые
|
||||
|
||||
### 27.09.2022
|
||||
#### Version: 0.0.1, Build: 1
|
||||
#### Version: 0.1.0
|
||||
|
||||
##### Changes:
|
||||
|
||||
* Изменил информацию в `__version__.py`
|
||||
* Добавил эндпоинты из прошлого проекта
|
||||
|
||||
##### TODOs:
|
||||
|
||||
* Проверить эндпоинты через чарлес
|
||||
- Задокументировать методы
|
||||
- Выявить и удалить не рабочие
|
||||
- Выявить и удалить не используемые
|
||||
|
||||
|
||||
### 27.09.2022
|
||||
#### Version: 0.0.1
|
||||
|
||||
##### Changes:
|
||||
|
||||
|
||||
@@ -17,4 +17,4 @@
|
||||
* 2
|
||||
* ...
|
||||
3. [CHANGELOG](./CHANGELOG.md)
|
||||
4. [LICENSE](./License.md)
|
||||
4. [LICENSE](./LICENSE.md)
|
||||
@@ -0,0 +1,18 @@
|
||||
from anixart import AnixartAPI, AnixartAccount
|
||||
from anixart import endpoints
|
||||
from anixart.exceptions import AnixartAPIRequestError
|
||||
from anixart.profile import Profile
|
||||
|
||||
anix = AnixartAPI() # По умолчанию используется гость
|
||||
|
||||
# acc = AnixartAccount("SantaSpeen", "I_H@ve_Very_Secret_P@ssw0rd!")
|
||||
# # id у аккаунта появляется только после
|
||||
# anix.use_account(acc)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
raw = anix.get(endpoints.PROFILE, 1)
|
||||
print(Profile.from_response(raw))
|
||||
except AnixartAPIRequestError as e:
|
||||
print(e.message)
|
||||
print(e.code)
|
||||
|
||||
3
examples/readme.md
Normal file
3
examples/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## Директория с примерами
|
||||
|
||||
* Пример авторизации и вывода информации о себе: [auth.py](./auth.py)
|
||||
3
poetry.toml
Normal file
3
poetry.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[virtualenvs]
|
||||
create = true
|
||||
in-project = true
|
||||
25
pyproject.toml
Normal file
25
pyproject.toml
Normal file
@@ -0,0 +1,25 @@
|
||||
[project]
|
||||
name = "anixart"
|
||||
version = "0.3.0.1"
|
||||
description = "Wrapper for using the Anixart API."
|
||||
authors = [
|
||||
{name = "SantaSpeen",email = "santaspeen@gmail.com"}
|
||||
]
|
||||
license = {text = "FPA License"}
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10,<4.0"
|
||||
dependencies = ["requests (>=2.32.3,<3.0.0)"]
|
||||
dynamic = [ "classifiers" ]
|
||||
|
||||
[project.urls]
|
||||
repository = "https://github.com/SantaSpeen/anixart"
|
||||
|
||||
[tool.poetry]
|
||||
classifiers = [
|
||||
"Development Status :: 2 - Pre-Alpha",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules"
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
58
setup.py
58
setup.py
@@ -1,58 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
packages = ['anixart']
|
||||
|
||||
requires = ['requests']
|
||||
|
||||
# 'setup.py publish' shortcut.
|
||||
if sys.argv[-1] == 'publish':
|
||||
os.system('py -m build')
|
||||
os.system('py -m twine upload --repository testpypi dist/*')
|
||||
os.system('py -m twine upload --repository pypi dist/*')
|
||||
sys.exit()
|
||||
|
||||
about = {}
|
||||
with open(os.path.join(here, 'anixart', '__version__.py'), 'r', encoding='utf-8') as f:
|
||||
exec(f.read(), about)
|
||||
|
||||
with open('README.md', 'r', encoding='utf-8') as f:
|
||||
readme = f.read()
|
||||
|
||||
setup(
|
||||
name=about['__title__'],
|
||||
version=about['__version__'],
|
||||
description=about['__description__'],
|
||||
long_description=readme,
|
||||
long_description_content_type='text/markdown',
|
||||
author=about['__author__'],
|
||||
author_email=about['__author_email__'],
|
||||
url=about['__url__'],
|
||||
packages=packages,
|
||||
package_data={'': ['LICENSE']},
|
||||
package_dir={'anixart': 'anixart'},
|
||||
include_package_data=True,
|
||||
install_requires=requires,
|
||||
license=about['__license__'],
|
||||
classifiers=[
|
||||
"Development Status :: 1 - Planning",
|
||||
"Intended Audience :: Developers",
|
||||
"Natural Language :: Russian",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"License :: Other/Proprietary License",
|
||||
"Operating System :: OS Independent",
|
||||
],
|
||||
project_urls={
|
||||
'Documentation': 'https://anixart.readthedocs.io/',
|
||||
'Source': 'https://github.com/SantaSpeen/anixart',
|
||||
},
|
||||
python_requires=">=3.7",
|
||||
)
|
||||
Reference in New Issue
Block a user