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.
- anilibria_api_client-0.1.4.dist-info/METADATA +62 -0
- anilibria_api_client-0.1.4.dist-info/RECORD +15 -0
- anilibria_api_client-0.1.4.dist-info/WHEEL +5 -0
- anilibria_api_client-0.1.4.dist-info/licenses/LICENSE +21 -0
- anilibria_api_client-0.1.4.dist-info/top_level.txt +2 -0
- base_api/api_class.py +284 -0
- methods/__init__.py +6 -0
- methods/_helper.py +218 -0
- methods/_libria.py +10 -0
- methods/accounts.py +348 -0
- methods/ads.py +23 -0
- methods/anime.py +670 -0
- methods/app.py +34 -0
- methods/media.py +45 -0
- methods/teams.py +61 -0
@@ -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
|
+
[](https://pypi.org/project/anilibria-api-client)
|
22
|
+

|
23
|
+

|
24
|
+

|
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,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.
|
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
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}
|