anilibria-api-client 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.4
2
+ Name: anilibria-api-client
3
+ Version: 0.1.4
4
+ Summary: Python async API wrapper for AniLibria Swagger
5
+ Author-email: semen-bol <syoma.bolotov@bk.ru>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/semen-bol/Anilibria-Api-Client
8
+ Project-URL: Issues, https://github.com/semen-bol/Anilibria-Api-Client/issues
9
+ Requires-Python: >=3.11
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: aiohttp==3.12.15
13
+ Requires-Dist: aiofiles==24.1.0
14
+ Requires-Dist: pydantic==2.11.7
15
+ Requires-Dist: m3u8-To-MP4==0.1.11
16
+ Requires-Dist: ffmpeg-python==0.2.0
17
+ Dynamic: license-file
18
+
19
+ # Anilibria-Api-Client
20
+
21
+ [![pypi](https://img.shields.io/badge/anilibria_api_client_on_PyPi-blue)](https://pypi.org/project/anilibria-api-client)
22
+ ![version](https://img.shields.io/badge/Version-0.1.4-blue)
23
+ ![licence](https://img.shields.io/badge/License-MIT-green)
24
+ ![python](https://img.shields.io/badge/Python-3.13%2B-blue)
25
+
26
+ > [!CAUTION]
27
+ > **It is not an official wrapper.** [Official AniLibria's Swagger](https://anilibria.top/api/docs/v1)
28
+
29
+ Anilibria-API-Client - this a async client to work with Anilibria API, use a aiohttp. Full writed at python
30
+
31
+ ## Installing
32
+ Tested at python 3.13
33
+ ### pip
34
+ ```bash
35
+ $ pip install anilibria-api-client
36
+ ```
37
+ ## Usage
38
+ ```python
39
+ from anilibria_client import AsyncAnilibriaAPI # Client
40
+ from anilibria_client.exceptions import AnilibriaException, AnilibriaValidationException # Errors
41
+ from anilibria_client.types import * # Types for some methods
42
+ from anilibria_client.models import * # Models for some methods
43
+ from anilibria_client.helper import * # Download anime, save torrents files and more
44
+
45
+ async def main():
46
+ async with AsyncAnilibriaAPI() as api: # async with
47
+ await api.teams.users(include="nickname")
48
+
49
+ api = AsyncAnilibriaAPI() # like js support
50
+ await api.teams.users(include="nickname")
51
+ ```
52
+
53
+ ## Documentation πŸ“ƒ
54
+ Docs..
55
+ ## Issues/Contributing
56
+ ### Issues
57
+ Report for any issues [here](https://github.com/semen-bol/Anilibria-Api-Client/issues)
58
+ ### Contributing
59
+ We allow contributing! Read the [CODE_OF_CONDUCT.md](https://github.com/semen-bol/Anilibria-Api-Client/blob/main/CODE_OF_CONDUCT.md)
60
+
61
+ ## License πŸ“„
62
+ Anilibria-Api-Client is [MIT](https://github.com/semen-bol/Anilibria-Api-Client/blob/main/LICENSE) licenced.
@@ -0,0 +1,15 @@
1
+ anilibria_api_client-0.1.4.dist-info/licenses/LICENSE,sha256=gr_OTPhIQY61xF6N31e1tkelwhltQiSRA4t9HcdU11g,1091
2
+ base_api/api_class.py,sha256=g3tcpeZyappRG99L-0uWvcFO74Eqo5ubwvbhI9UGxQw,11486
3
+ methods/__init__.py,sha256=zZ2EpsQUYnke7AedgMe6vKYaWk9BSocO0M423UPqsyI,188
4
+ methods/_helper.py,sha256=2eQIaitEJ_DaSqxwPug0-zW7cM2he7-6KHtrDxb0dY8,9961
5
+ methods/_libria.py,sha256=R0l59PRMkfvAMF_aET-9rvjcTLdwueDdxusiTUxSqX0,206
6
+ methods/accounts.py,sha256=H3lbSrTmISyZwpREnrTlD8RhepYnIa1sKLiQMQEHEfw,13583
7
+ methods/ads.py,sha256=6h8vFwLre6_5k26GTcS6eiUL-jx0uRqrb1zm9m2Pnz0,717
8
+ methods/anime.py,sha256=Cw-dvBypJ9YV3QOTXWeBsAe1_R4Z1uQdxb79xCyq7-M,23423
9
+ methods/app.py,sha256=yw7L4Z0XTxIBSWJK_kZSRSK0a_ojNQdbGmzu_m4AIt4,1063
10
+ methods/media.py,sha256=9I20jNpDTrUDTY6kHf_SNuewZ7p9ptMhbZ0_9IDD8A0,1431
11
+ methods/teams.py,sha256=dQJiB7LdwyrDZ88zh0bA2IplK2--g7eQszKDHOPth6c,1792
12
+ anilibria_api_client-0.1.4.dist-info/METADATA,sha256=BLapOeuZt97orzeA7885KZVLmxSuNI1KabHmjCed_H0,2347
13
+ anilibria_api_client-0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ anilibria_api_client-0.1.4.dist-info/top_level.txt,sha256=YHiyWxRvAMuzBoCiZP2JLMvOc4f3hXjcV7o_OIGzZQ0,17
15
+ anilibria_api_client-0.1.4.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Semen Bolotov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ base_api
2
+ methods
base_api/api_class.py ADDED
@@ -0,0 +1,284 @@
1
+ import aiohttp
2
+
3
+ from typing import Dict, Any, Optional, Union
4
+ from urllib.parse import urlencode, urljoin, quote
5
+ from ..exceptions import AnilibriaValidationException, AnilibriaException
6
+
7
+
8
+ class AsyncBaseAPI:
9
+ """
10
+ Асинхронный Π±Π°Π·ΠΎΠ²Ρ‹ΠΉ класс для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с API.
11
+ ΠŸΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²Π»ΡΠ΅Ρ‚ основныС ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ для ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ HTTP-запросов ΠΈ Ρ€Π°Π±ΠΎΡ‚Ρ‹ с URL.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ base_url: str,
17
+ headers: Optional[Dict[str, str]] = None,
18
+ timeout: int = 10,
19
+ ):
20
+ """
21
+ Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ асинхронного API ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°.
22
+
23
+ :param base_url: Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ URL API
24
+ :param headers: Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ для всСх запросов
25
+ :param timeout: Π’Π°ΠΉΠΌΠ°ΡƒΡ‚ запросов Π² сСкундах
26
+ """
27
+ self.base_url = base_url.rstrip('/')
28
+ self.headers = headers or {}
29
+ self.timeout = aiohttp.ClientTimeout(total=timeout)
30
+ self.session: Optional[aiohttp.ClientSession] = None
31
+
32
+
33
+ async def __aenter__(self):
34
+ if self.session is None:
35
+ self.session = aiohttp.ClientSession()
36
+ self._own_session = True
37
+ return self
38
+
39
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
40
+ if self._own_session and self._session:
41
+ await self._session.close()
42
+ self.session = None
43
+ self._own_session = False
44
+
45
+ async def _get_session(self) -> aiohttp.ClientSession:
46
+ """ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ сСссию, создавая Π½ΠΎΠ²ΡƒΡŽ Ссли Π½ΡƒΠΆΠ½ΠΎ"""
47
+ if self.session is None:
48
+ session = aiohttp.ClientSession()
49
+ self.session = session
50
+ self._own_session = True
51
+ return self.session
52
+
53
+ @staticmethod
54
+ def build_query_string(params: Dict[str, Any]) -> str:
55
+ """
56
+ Π‘ΠΎΠ·Π΄Π°Π΅Ρ‚ query string ΠΈΠ· ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ².
57
+
58
+ :param params: Π‘Π»ΠΎΠ²Π°Ρ€ΡŒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ²
59
+ :return: Π‘Ρ‚Ρ€ΠΎΠΊΠ° Π²ΠΈΠ΄Π° ?key1=value1&key2=value2
60
+ """
61
+ if not params:
62
+ return ""
63
+
64
+ filtered_params = {k: v for k, v in params.items() if v is not None}
65
+ if not filtered_params:
66
+ return ""
67
+
68
+ return "?" + urlencode(filtered_params, doseq=True)
69
+
70
+ @staticmethod
71
+ def build_url(base_url: str, endpoint: str, params: Optional[Dict[str, Any]] = None) -> str:
72
+ """
73
+ Π‘Ρ‚Ρ€ΠΎΠΈΡ‚ ΠΏΠΎΠ»Π½Ρ‹ΠΉ URL с ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ.
74
+
75
+ :param base_url: Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ URL
76
+ :param endpoint: ΠšΠΎΠ½Π΅Ρ‡Π½Π°Ρ Ρ‚ΠΎΡ‡ΠΊΠ°
77
+ :param params: ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ запроса
78
+ :return: ΠŸΠΎΠ»Π½Ρ‹ΠΉ URL с query-ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ
79
+ """
80
+
81
+ url = urljoin(base_url + '/', endpoint.lstrip('/'))
82
+ if params:
83
+ url += AsyncBaseAPI.build_query_string(params)
84
+ return url
85
+
86
+ def encode_path_param(self, param: Any) -> str:
87
+ """
88
+ ΠšΠΎΠ΄ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ для использования Π² ΠΏΡƒΡ‚ΠΈ URL.
89
+
90
+ :param param: ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ для кодирования
91
+ :return: Закодированная строка
92
+ """
93
+
94
+ return quote(str(param))
95
+
96
+ def build_endpoint_with_params(self, endpoint_template: str, **path_params) -> str:
97
+ """
98
+ Π‘Ρ‚Ρ€ΠΎΠΈΡ‚ endpoint с подставлСнными ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ ΠΏΡƒΡ‚ΠΈ.
99
+
100
+ :param endpoint_template: Π¨Π°Π±Π»ΠΎΠ½ endpoint (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€: '/users/{user_id}/posts/{post_id}')
101
+ :param path_params: ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ для подстановки Π² ΠΏΡƒΡ‚ΡŒ
102
+ :return: Π“ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ endpoint с подставлСнными ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ
103
+ """
104
+
105
+ encoded_params = {k: self.encode_path_param(v) for k, v in path_params.items()}
106
+ return endpoint_template.format(**encoded_params)
107
+
108
+ async def _request(
109
+ self,
110
+ method: str,
111
+ endpoint: str,
112
+ params: Optional[Dict[str, Any]] = None,
113
+ data: Optional[Union[Dict[str, Any], str, bytes]] = None,
114
+ json_data: Optional[Dict[str, Any]] = None,
115
+ headers: Optional[Dict[str, str]] = None,
116
+ **kwargs
117
+ ) -> Union[Dict[str, Any], str, bytes]:
118
+ """
119
+ Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ HTTP-запросов.
120
+
121
+ :param method: HTTP ΠΌΠ΅Ρ‚ΠΎΠ΄ (GET, POST, PUT, DELETE ΠΈ Ρ‚.Π΄.)
122
+ :param endpoint: ΠšΠΎΠ½Π΅Ρ‡Π½Π°Ρ Ρ‚ΠΎΡ‡ΠΊΠ° API (ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΡƒΡ‚ΡŒ)
123
+ :param params: ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ запроса (для GET)
124
+ :param data: Π’Π΅Π»ΠΎ запроса (для POST, PUT)
125
+ :param json_data: JSON Ρ‚Π΅Π»ΠΎ запроса
126
+ :param headers: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ запроса
127
+ :param kwargs: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ для aiohttp
128
+ :return: ΠžΡ‚Π²Π΅Ρ‚ ΠΎΡ‚ API (дСсСриализованный JSON ΠΈΠ»ΠΈ сырыС Π΄Π°Π½Π½Ρ‹Π΅)
129
+ :raises: HTTPError Ссли статус ΠΎΡ‚Π²Π΅Ρ‚Π° Π½Π΅ 2xx
130
+ """
131
+ sees = await self._get_session()
132
+ if not self.session or not sees:
133
+ raise RuntimeError("Session not initialized. Use async with context manager.")
134
+
135
+ url = self.build_url(self.base_url, endpoint, params)
136
+ request_headers = {**self.headers, **(headers or {})}
137
+
138
+ try:
139
+ async with self.session.request(
140
+ method=method,
141
+ url=url,
142
+ data=data,
143
+ json=json_data,
144
+ headers=request_headers,
145
+ **kwargs
146
+ ) as response:
147
+ if response.status == 422:
148
+ error_data = await response.json()
149
+ if error_data.get("errors"):
150
+ raise AnilibriaValidationException(error_data)
151
+ else:
152
+ raise AnilibriaValidationException({"error": "Ошибка Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ Π²Ρ…ΠΎΠ΄Π½Ρ‹Ρ… ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ²"})
153
+
154
+ response.raise_for_status()
155
+
156
+ content_type = response.headers.get('Content-Type', '')
157
+ if 'application/json' in content_type:
158
+ return await response.json()
159
+ elif 'application/x-bittorrent' in content_type:
160
+ return await response.read()
161
+
162
+ return await response.text()
163
+
164
+ except aiohttp.ClientError as e:
165
+ raise self._handle_error(e)
166
+
167
+ finally:
168
+ if self._own_session and self.session:
169
+ await self.session.close()
170
+ self.session = None
171
+ self._own_session = False
172
+
173
+ def _handle_error(self, error: aiohttp.ClientError) -> Exception:
174
+ """
175
+ ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок запроса.
176
+
177
+ :param error: Π˜ΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ aiohttp
178
+ :return: Π˜ΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ для проброса
179
+ """
180
+
181
+ if hasattr(error, 'errors'):
182
+ return error
183
+ return AnilibriaException(error)
184
+
185
+ async def get(
186
+ self,
187
+ endpoint: str,
188
+ params: Optional[Dict[str, Any]] = None,
189
+ headers: Optional[Dict[str, str]] = None,
190
+ **kwargs
191
+ ) -> Union[Dict[str, Any], str, bytes]:
192
+ """
193
+ ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° GET запроса.
194
+
195
+ :param endpoint: ΠšΠΎΠ½Π΅Ρ‡Π½Π°Ρ Ρ‚ΠΎΡ‡ΠΊΠ° API
196
+ :param params: ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ запроса
197
+ :param headers: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ
198
+ :param kwargs: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ для aiohttp
199
+ :return: ΠžΡ‚Π²Π΅Ρ‚ ΠΎΡ‚ API
200
+ """
201
+
202
+ return await self._request('GET', endpoint, params=params, headers=headers, **kwargs)
203
+
204
+ async def post(
205
+ self,
206
+ endpoint: str,
207
+ data: Optional[Union[Dict[str, Any], str, bytes]] = None,
208
+ json_data: Optional[Dict[str, Any]] = None,
209
+ headers: Optional[Dict[str, str]] = None,
210
+ **kwargs
211
+ ) -> Union[Dict[str, Any], str, bytes]:
212
+ """
213
+ ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° POST запроса.
214
+
215
+ :param endpoint: ΠšΠΎΠ½Π΅Ρ‡Π½Π°Ρ Ρ‚ΠΎΡ‡ΠΊΠ° API
216
+ :param data: Π’Π΅Π»ΠΎ запроса
217
+ :param json_data: JSON Ρ‚Π΅Π»ΠΎ запроса
218
+ :param headers: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ
219
+ :param kwargs: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ для aiohttp
220
+ :return: ΠžΡ‚Π²Π΅Ρ‚ ΠΎΡ‚ API
221
+ """
222
+
223
+ return await self._request('POST', endpoint, data=data, json_data=json_data, headers=headers, **kwargs)
224
+
225
+ async def put(
226
+ self,
227
+ endpoint: str,
228
+ data: Optional[Union[Dict[str, Any], str, bytes]] = None,
229
+ json_data: Optional[Dict[str, Any]] = None,
230
+ headers: Optional[Dict[str, str]] = None,
231
+ **kwargs
232
+ ) -> Union[Dict[str, Any], str, bytes]:
233
+ """
234
+ ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° PUT запроса.
235
+
236
+ :param endpoint: ΠšΠΎΠ½Π΅Ρ‡Π½Π°Ρ Ρ‚ΠΎΡ‡ΠΊΠ° API
237
+ :param data: Π’Π΅Π»ΠΎ запроса
238
+ :param json_data: JSON Ρ‚Π΅Π»ΠΎ запроса
239
+ :param headers: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ
240
+ :param kwargs: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ для aiohttp
241
+ :return: ΠžΡ‚Π²Π΅Ρ‚ ΠΎΡ‚ API
242
+ """
243
+
244
+ return await self._request('PUT', endpoint, data=data, json_data=json_data, headers=headers, **kwargs)
245
+
246
+ async def delete(
247
+ self,
248
+ endpoint: str,
249
+ headers: Optional[Dict[str, str]] = None,
250
+ json_data: Optional[Dict[str, Any]] = None,
251
+ **kwargs
252
+ ) -> Union[Dict[str, Any], str, bytes]:
253
+ """
254
+ ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° DELETE запроса.
255
+
256
+ :param endpoint: ΠšΠΎΠ½Π΅Ρ‡Π½Π°Ρ Ρ‚ΠΎΡ‡ΠΊΠ° API
257
+ :param headers: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ
258
+ :param json_data: JSON Ρ‚Π΅Π»ΠΎ запроса
259
+ :param kwargs: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ для aiohttp
260
+ :return: ΠžΡ‚Π²Π΅Ρ‚ ΠΎΡ‚ API
261
+ """
262
+
263
+ return await self._request('DELETE', endpoint, json_data=json_data, headers=headers, **kwargs)
264
+
265
+ async def patch(
266
+ self,
267
+ endpoint: str,
268
+ data: Optional[Union[Dict[str, Any], str, bytes]] = None,
269
+ json_data: Optional[Dict[str, Any]] = None,
270
+ headers: Optional[Dict[str, str]] = None,
271
+ **kwargs
272
+ ) -> Union[Dict[str, Any], str, bytes]:
273
+ """
274
+ ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° PATCH запроса.
275
+
276
+ :param endpoint: ΠšΠΎΠ½Π΅Ρ‡Π½Π°Ρ Ρ‚ΠΎΡ‡ΠΊΠ° API
277
+ :param data: Π’Π΅Π»ΠΎ запроса
278
+ :param json_data: JSON Ρ‚Π΅Π»ΠΎ запроса
279
+ :param headers: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ
280
+ :param kwargs: Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ для aiohttp
281
+ :return: ΠžΡ‚Π²Π΅Ρ‚ ΠΎΡ‚ API
282
+ """
283
+
284
+ return await self._request('PATCH', endpoint, data=data, json_data=json_data, headers=headers, **kwargs)
methods/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ from .accounts import AccountsMethod
2
+ from .ads import AdsMethod
3
+ from .anime import AnimeMethod
4
+ from .app import AppMethod
5
+ from .media import MediaMethod
6
+ from .teams import TeamsMethod
methods/_helper.py ADDED
@@ -0,0 +1,218 @@
1
+ from typing import Dict, Any
2
+ from ..types import AgeRating, SortType, ContentType, Seasons, PublishStatusesType, ProductionStatusesType
3
+ from ..models import ReleaseCollection, Release
4
+
5
+ async def validate_filters(params: Release) -> Dict[str, Any]:
6
+ """
7
+ Валидация ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠ² Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ f["Π½Π°Π·Π²Π°Π½ΠΈΠ΅_ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ"]
8
+
9
+ Args:
10
+ filters: Π‘Π»ΠΎΠ²Π°Ρ€ΡŒ с ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠ²
11
+ """
12
+ filters = {
13
+ "genres": params.genres,
14
+ "types": params.types,
15
+ "seasons": params.seasons,
16
+ "from_year": params.from_year,
17
+ "to_year": params.to_year,
18
+ "search": params.search,
19
+ "sorting": params.sorting,
20
+ "age_ratings": params.age_ratings,
21
+ "publish_statuses": params.publish_statuses,
22
+ "production_statuses": params.production_statuses
23
+ }
24
+ validated_filters = {}
25
+
26
+ # Валидация genres
27
+ if "genres" in filters and filters["genres"] is not None:
28
+ if not isinstance(filters["genres"], str):
29
+ raise ValueError("genres Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ строкой")
30
+ validated_filters["f[genres]"] = filters["genres"]
31
+
32
+ # Валидация types (список ContentType)
33
+ if "types" in filters and filters["types"] is not None:
34
+ if not isinstance(filters["types"], list) or not all(isinstance(t, ContentType) for t in filters["types"]):
35
+ raise ValueError("types Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ списком ContentType")
36
+ validated_filters["f[types]"] = [t.value for t in filters["types"]]
37
+
38
+ # Валидация seasons (список Seasons)
39
+ if "seasons" in filters and filters["seasons"] is not None:
40
+ if not isinstance(filters["seasons"], list) or not all(isinstance(s, Seasons) for s in filters["seasons"]):
41
+ raise ValueError("seasons Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ списком Seasons")
42
+ validated_filters["f[seasons]"] = [s.value for s in filters["seasons"]]
43
+
44
+ # Валидация from_year
45
+ if "from_year" in filters and filters["from_year"] is not None:
46
+ if not isinstance(filters["from_year"], int) or filters["from_year"] < 1900:
47
+ raise ValueError("from_year Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ Ρ†Π΅Π»Ρ‹ΠΌ числом Π½Π΅ ΠΌΠ΅Π½Π΅Π΅ 1900")
48
+ validated_filters["f[from_year]"] = filters["from_year"]
49
+
50
+ # Валидация to_year
51
+ if "to_year" in filters and filters["to_year"] is not None:
52
+ if not isinstance(filters["to_year"], int) or filters["to_year"] < 1900:
53
+ raise ValueError("to_year Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ Ρ†Π΅Π»Ρ‹ΠΌ числом Π½Π΅ ΠΌΠ΅Π½Π΅Π΅ 1900")
54
+ validated_filters["f[to_year]"] = filters["to_year"]
55
+
56
+ # Валидация search
57
+ if "search" in filters and filters["search"] is not None:
58
+ if not isinstance(filters["search"], str):
59
+ raise ValueError("search Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ строкой")
60
+ validated_filters["f[search]"] = filters["search"]
61
+
62
+ # Валидация sorting
63
+ if "sorting" in filters and filters["sorting"] is not None:
64
+ if not isinstance(filters["sorting"], SortType):
65
+ raise ValueError("sorting Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ экзСмпляром SortType")
66
+ validated_filters["f[sorting]"] = filters["sorting"].value
67
+
68
+ # Валидация age_ratings (список AgeRating)
69
+ if "age_ratings" in filters and filters["age_ratings"] is not None:
70
+ if not isinstance(filters["age_ratings"], list) or not all(isinstance(a, AgeRating) for a in filters["age_ratings"]):
71
+ raise ValueError("age_ratings Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ списком AgeRating")
72
+ validated_filters["f[age_ratings]"] = [a.value for a in filters["age_ratings"]]
73
+
74
+ # Валидация publish_statuses (список PublishStatusesType)
75
+ if "publish_statuses" in filters and filters["publish_statuses"] is not None:
76
+ if not isinstance(filters["publish_statuses"], list) or not all(isinstance(p, PublishStatusesType) for p in filters["publish_statuses"]):
77
+ raise ValueError("publish_statuses Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ списком PublishStatusesType")
78
+ validated_filters["f[publish_statuses]"] = [p.value for p in filters["publish_statuses"]]
79
+
80
+ # Валидация production_statuses (список ProductionStatusesType)
81
+ if "production_statuses" in filters and filters["production_statuses"] is not None:
82
+ if not isinstance(filters["production_statuses"], list) or not all(isinstance(p, ProductionStatusesType) for p in filters["production_statuses"]):
83
+ raise ValueError("production_statuses Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ списком ProductionStatusesType")
84
+ validated_filters["f[production_statuses]"] = [p.value for p in filters["production_statuses"]]
85
+
86
+ return validated_filters
87
+
88
+ async def create_filters_from_release(release: Release) -> Dict[str, Any]:
89
+ """
90
+ Π‘ΠΎΠ·Π΄Π°Π΅Ρ‚ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ API ΠΈΠ· ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° Release
91
+
92
+ Args:
93
+ release: ΠžΠ±ΡŠΠ΅ΠΊΡ‚ Release с ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΠΈ
94
+ """
95
+ filters = {}
96
+
97
+ # genres
98
+ if release.genres is not None:
99
+ filters["genres"] = release.genres
100
+
101
+ # types
102
+ if release.types is not None:
103
+ filters["types"] = [t.value for t in release.types]
104
+
105
+ # seasons
106
+ if release.seasons is not None:
107
+ filters["seasons"] = [s.value for s in release.seasons]
108
+
109
+ # years (объСдиняСм from_year и to_year)
110
+ if release.from_year is not None or release.to_year is not None:
111
+ years = {}
112
+ if release.from_year is not None:
113
+ years["from_year"] = release.from_year
114
+ if release.to_year is not None:
115
+ years["to_year"] = release.to_year
116
+ filters["years"] = years
117
+
118
+ # search
119
+ if release.search is not None:
120
+ filters["search"] = release.search
121
+
122
+ # sorting
123
+ if release.sorting is not None and release.sorting:
124
+ filters["sorting"] = release.sorting.value
125
+
126
+ # age_ratings
127
+ if release.age_ratings is not None:
128
+ filters["age_ratings"] = [a.value for a in release.age_ratings]
129
+
130
+ # publish_statuses
131
+ if release.publish_statuses is not None:
132
+ filters["publish_statuses"] = [p.value for p in release.publish_statuses]
133
+
134
+ # production_statuses
135
+ if release.production_statuses is not None:
136
+ filters["production_statuses"] = [p.value for p in release.production_statuses]
137
+
138
+ return {"f": filters}
139
+
140
+ async def validate_collection(params: ReleaseCollection) -> Dict[str, Any]:
141
+ """
142
+ Валидация ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠ² для ReleaseCollection Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ f["Π½Π°Π·Π²Π°Π½ΠΈΠ΅_ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ"]
143
+
144
+ Args:
145
+ params: ΠžΠ±ΡŠΠ΅ΠΊΡ‚ ReleaseCollection с ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΠΈ
146
+ """
147
+ filters = {
148
+ "genres": params.genres,
149
+ "types": params.types,
150
+ "years": params.years,
151
+ "search": params.search,
152
+ "age_ratings": params.age_ratings
153
+ }
154
+ validated_filters = {}
155
+
156
+ # Валидация genres
157
+ if "genres" in filters and filters["genres"] is not None:
158
+ if not isinstance(filters["genres"], str):
159
+ raise ValueError("genres Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ строкой")
160
+ validated_filters["f[genres]"] = filters["genres"]
161
+
162
+ # Валидация types (список ContentType)
163
+ if "types" in filters and filters["types"] is not None:
164
+ if not isinstance(filters["types"], list) or not all(isinstance(t, ContentType) for t in filters["types"]):
165
+ raise ValueError("types Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ списком ContentType")
166
+ validated_filters["f[types]"] = [t.value for t in filters["types"]]
167
+
168
+ # Валидация years
169
+ if "years" in filters and filters["years"] is not None:
170
+ if not isinstance(filters["years"], str):
171
+ raise ValueError("years Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ строкой")
172
+ validated_filters["f[years]"] = filters["years"]
173
+
174
+ # Валидация search
175
+ if "search" in filters and filters["search"] is not None:
176
+ if not isinstance(filters["search"], str):
177
+ raise ValueError("search Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ строкой")
178
+ validated_filters["f[search]"] = filters["search"]
179
+
180
+ # Валидация age_ratings (список AgeRating)
181
+ if "age_ratings" in filters and filters["age_ratings"] is not None:
182
+ if not isinstance(filters["age_ratings"], list) or not all(isinstance(a, AgeRating) for a in filters["age_ratings"]):
183
+ raise ValueError("age_ratings Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ списком AgeRating")
184
+ validated_filters["f[age_ratings]"] = [a.value for a in filters["age_ratings"]]
185
+
186
+ return validated_filters
187
+
188
+
189
+ async def validated_json_collection(release: ReleaseCollection) -> Dict[str, Any]:
190
+ """
191
+ Π‘ΠΎΠ·Π΄Π°Π΅Ρ‚ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Ρ‹ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ API ΠΈΠ· ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° ReleaseCollection
192
+
193
+ Args:
194
+ release: ΠžΠ±ΡŠΠ΅ΠΊΡ‚ ReleaseCollection с ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΠΈ
195
+ """
196
+ filters = {}
197
+
198
+ # genres
199
+ if release.genres is not None:
200
+ filters["genres"] = release.genres
201
+
202
+ # types
203
+ if release.types is not None:
204
+ filters["types"] = [t.value for t in release.types]
205
+
206
+ # years
207
+ if release.years is not None:
208
+ filters["years"] = release.years
209
+
210
+ # search
211
+ if release.search is not None:
212
+ filters["search"] = release.search
213
+
214
+ # age_ratings
215
+ if release.age_ratings is not None:
216
+ filters["age_ratings"] = [a.value for a in release.age_ratings]
217
+
218
+ return {"f": filters}
methods/_libria.py ADDED
@@ -0,0 +1,10 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+
4
+ if TYPE_CHECKING:
5
+ from ..api_client import AsyncAnilibriaAPI
6
+
7
+ class BaseMethod:
8
+ def __init__(self, api: 'AsyncAnilibriaAPI'):
9
+ self._api = api
10
+