Jikan4 0.3.0__tar.gz

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,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1,154 @@
1
+ *.ipynb
2
+
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ .Python
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ share/python-wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ MANIFEST
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py,cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+ cover/
55
+
56
+ # Translations
57
+ *.mo
58
+ *.pot
59
+
60
+ # Django stuff:
61
+ *.log
62
+ local_settings.py
63
+ db.sqlite3
64
+ db.sqlite3-journal
65
+
66
+ # Flask stuff:
67
+ instance/
68
+ .webassets-cache
69
+
70
+ # Scrapy stuff:
71
+ .scrapy
72
+
73
+ # Sphinx documentation
74
+ docs/_build/
75
+
76
+ # PyBuilder
77
+ .pybuilder/
78
+ target/
79
+
80
+ # Jupyter Notebook
81
+ .ipynb_checkpoints
82
+
83
+ # IPython
84
+ profile_default/
85
+ ipython_config.py
86
+
87
+ # pyenv
88
+ # For a library or package, you might want to ignore these files since the code is
89
+ # intended to run in multiple environments; otherwise, check them in:
90
+ # .python-version
91
+
92
+ # pipenv
93
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
94
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
95
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
96
+ # install all needed dependencies.
97
+ #Pipfile.lock
98
+
99
+ # poetry
100
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
101
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
102
+ # commonly ignored for libraries.
103
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
104
+ #poetry.lock
105
+
106
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
107
+ __pypackages__/
108
+
109
+ # Celery stuff
110
+ celerybeat-schedule
111
+ celerybeat.pid
112
+
113
+ # SageMath parsed files
114
+ *.sage.py
115
+
116
+ # Environments
117
+ .env
118
+ .venv
119
+ env/
120
+ venv/
121
+ ENV/
122
+ env.bak/
123
+ venv.bak/
124
+
125
+ # Spyder project settings
126
+ .spyderproject
127
+ .spyproject
128
+
129
+ # Rope project settings
130
+ .ropeproject
131
+
132
+ # mkdocs documentation
133
+ /site
134
+
135
+ # mypy
136
+ .mypy_cache/
137
+ .dmypy.json
138
+ dmypy.json
139
+
140
+ # Pyre type checker
141
+ .pyre/
142
+
143
+ # pytype static type analyzer
144
+ .pytype/
145
+
146
+ # Cython debug symbols
147
+ cython_debug/
148
+
149
+ # PyCharm
150
+ # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
151
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
152
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
153
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
154
+ #.idea/
jikan4-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Matheus Bessa
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.
jikan4-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.5
2
+ Name: Jikan4
3
+ Version: 0.3.0
4
+ Summary: A Python wrapper for an anime API, with Tenrai as the default backend
5
+ Project-URL: Documentation, https://github.com/mdabessa/jikan4#readme
6
+ Project-URL: Issues, https://github.com/mdabessa/jikan4/issues
7
+ Project-URL: Source, https://github.com/mdabessa/jikan4
8
+ Author-email: Matheus Bessa <mdabessa@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: aiohttp
17
+ Requires-Dist: pydantic
18
+ Requires-Dist: requests
19
+ Description-Content-Type: text/markdown
20
+
21
+ # jikan4
22
+ A Python wrapper for an anime API, with Tenrai as the default backend.
23
+
24
+
25
+ ## Tenrai Endpoint Support
26
+
27
+ The client currently uses these Tenrai v1 endpoints:
28
+
29
+ | Client method | Endpoint | Status |
30
+ | --- | --- | --- |
31
+ | `get_anime` | `GET /anime/{id}` | Supported |
32
+ | `get_anime_full` | `GET /anime/{id}/full` | Supported |
33
+ | `get_anime_characters` | `GET /anime/{id}/characters` | Supported |
34
+ | `get_anime_staff` | `GET /anime/{id}/staff` | Supported |
35
+ | `get_anime_episodes` | `GET /anime/{id}/episodes` | Supported |
36
+ | `get_anime_episode` | `GET /anime/{id}/episodes/{episode}` | Supported |
37
+ | `get_anime_news` | `GET /anime/{id}/news` | Supported |
38
+ | `search_anime` | `GET /anime` | Supported |
39
+ | `get_anime_forum` | `GET /anime/{id}/forum` | Supported |
40
+
41
+
42
+ ## Installation
43
+ ```bash
44
+ pip install jikan4
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ### Basic Usage
50
+ ```python
51
+ from jikan4.jikan import Jikan
52
+
53
+ jikan = Jikan()
54
+
55
+ anime = jikan.get_anime(1)
56
+ search = jikan.search_anime("tv", "naruto")
57
+ ```
58
+
59
+ ### Async Usage
60
+ ```python
61
+ import asyncio
62
+ from jikan4.aiojikan import AioJikan
63
+
64
+
65
+ async def main():
66
+ jikan = AioJikan()
67
+
68
+ anime = await jikan.get_anime(1)
69
+ search = await jikan.search_anime('tv', 'naruto')
70
+
71
+ jikan.close()
72
+
73
+
74
+ if __name__ == '__main__':
75
+ asyncio.run(main())
76
+ ```
jikan4-0.3.0/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # jikan4
2
+ A Python wrapper for an anime API, with Tenrai as the default backend.
3
+
4
+
5
+ ## Tenrai Endpoint Support
6
+
7
+ The client currently uses these Tenrai v1 endpoints:
8
+
9
+ | Client method | Endpoint | Status |
10
+ | --- | --- | --- |
11
+ | `get_anime` | `GET /anime/{id}` | Supported |
12
+ | `get_anime_full` | `GET /anime/{id}/full` | Supported |
13
+ | `get_anime_characters` | `GET /anime/{id}/characters` | Supported |
14
+ | `get_anime_staff` | `GET /anime/{id}/staff` | Supported |
15
+ | `get_anime_episodes` | `GET /anime/{id}/episodes` | Supported |
16
+ | `get_anime_episode` | `GET /anime/{id}/episodes/{episode}` | Supported |
17
+ | `get_anime_news` | `GET /anime/{id}/news` | Supported |
18
+ | `search_anime` | `GET /anime` | Supported |
19
+ | `get_anime_forum` | `GET /anime/{id}/forum` | Supported |
20
+
21
+
22
+ ## Installation
23
+ ```bash
24
+ pip install jikan4
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Basic Usage
30
+ ```python
31
+ from jikan4.jikan import Jikan
32
+
33
+ jikan = Jikan()
34
+
35
+ anime = jikan.get_anime(1)
36
+ search = jikan.search_anime("tv", "naruto")
37
+ ```
38
+
39
+ ### Async Usage
40
+ ```python
41
+ import asyncio
42
+ from jikan4.aiojikan import AioJikan
43
+
44
+
45
+ async def main():
46
+ jikan = AioJikan()
47
+
48
+ anime = await jikan.get_anime(1)
49
+ search = await jikan.search_anime('tv', 'naruto')
50
+
51
+ jikan.close()
52
+
53
+
54
+ if __name__ == '__main__':
55
+ asyncio.run(main())
56
+ ```
@@ -0,0 +1,6 @@
1
+ from .aiojikan import AioJikan
2
+ from .jikan import Jikan
3
+ from jikan4.__version__ import __version__
4
+
5
+
6
+ __all__ = ["Jikan", "AioJikan", "__version__"]
@@ -0,0 +1,3 @@
1
+ VERSION = (0, 3, 0)
2
+
3
+ __version__ = "0.3.0"
@@ -0,0 +1,262 @@
1
+ from __future__ import annotations
2
+
3
+ import aiohttp
4
+
5
+ from .models import (
6
+ Anime,
7
+ AnimeSearch,
8
+ AnimeCharacters,
9
+ AnimeStaff,
10
+ AnimeEpisodes,
11
+ Episode,
12
+ AnimeNews,
13
+ AnimeForum,
14
+ )
15
+ from .utils.async_limiter import AsyncLimiter
16
+
17
+
18
+ class AioJikan:
19
+ """Async anime API wrapper with Tenrai as the default backend."""
20
+
21
+ def __init__(
22
+ self, base_url: str = "https://api.tenrai.org/v1", rate_limit: int = 60
23
+ ) -> None:
24
+ """Construct a AioJikan object
25
+
26
+ Args:
27
+ base_url (str, optional): Base URL for the anime API. Defaults to "https://api.tenrai.org/v1".
28
+ rate_limit (int, optional): Rate limit in requests per minute. Defaults to 60.
29
+
30
+ Returns:
31
+ AioJikan: AioJikan object
32
+
33
+ Examples:
34
+ >>> aiojikan = AioJikan()
35
+ >>> aiojikan = AioJikan("https://api.tenrai.org/v1")
36
+ """
37
+
38
+ base_url = base_url.rstrip("/")
39
+ self.base_url = base_url
40
+ self.session: aiohttp.ClientSession | None = None
41
+
42
+ if rate_limit:
43
+ self.rate_limiter = AsyncLimiter(calls_limit=rate_limit / 60, period=1)
44
+ self._get = self.rate_limiter.__call__(self._get)
45
+
46
+ async def close(self) -> None:
47
+ """Close the aiohttp session"""
48
+
49
+ if self.session is not None and not self.session.closed:
50
+ await self.session.close()
51
+
52
+ async def __aenter__(self) -> AioJikan:
53
+ return self
54
+
55
+ async def __aexit__(self, exc_type, exc, tb) -> None:
56
+ await self.close()
57
+
58
+ async def _get(self, endpoint: str, params: dict = None) -> dict:
59
+ """Make a GET request to the configured anime API
60
+
61
+ Args:
62
+ endpoint (str): Endpoint to request
63
+ params (dict, optional): Parameters to send with request. Defaults to None.
64
+
65
+ Returns:
66
+ dict: JSON response from the configured anime API
67
+ """
68
+
69
+ url = f"{self.base_url}/{endpoint}"
70
+ if self.session is None or self.session.closed:
71
+ self.session = aiohttp.ClientSession()
72
+
73
+ async with self.session.get(url, params=params) as response:
74
+ response.raise_for_status()
75
+ return await response.json()
76
+
77
+ async def get_anime(self, anime_id: int) -> Anime:
78
+ """Get anime information
79
+
80
+ Args:
81
+ anime_id (int): Anime ID
82
+
83
+ Returns:
84
+ Anime: Anime object
85
+
86
+ Examples:
87
+ >>> aiojikan = AioJikan()
88
+ >>> anime = await aiojikan.get_anime(1)
89
+ """
90
+
91
+ endpoint = f"anime/{anime_id}"
92
+ response = await self._get(endpoint)
93
+
94
+ return Anime(**response["data"])
95
+
96
+ async def get_anime_full(self, anime_id: int) -> Anime:
97
+ """Get anime information with full details
98
+
99
+ Args:
100
+ anime_id (int): Anime ID
101
+
102
+ Returns:
103
+ Anime: Anime object
104
+
105
+ Examples:
106
+ >>> aiojikan = AioJikan()
107
+ >>> anime = await aiojikan.get_anime_full(1)
108
+ """
109
+
110
+ endpoint = f"anime/{anime_id}/full"
111
+ response = await self._get(endpoint)
112
+
113
+ return Anime(**response["data"])
114
+
115
+ async def get_anime_characters(self, anime_id: int) -> AnimeCharacters:
116
+ """Get anime characters
117
+
118
+ Args:
119
+ anime_id (int): Anime ID
120
+
121
+ Returns:
122
+ AnimeCharacters: AnimeCharacters object
123
+
124
+ Examples:
125
+ >>> aiojikan = AioJikan()
126
+ >>> characters = await aiojikan.get_anime_characters(1)
127
+ """
128
+
129
+ endpoint = f"anime/{anime_id}/characters"
130
+ response = await self._get(endpoint)
131
+
132
+ return AnimeCharacters(**response)
133
+
134
+ async def get_anime_staff(self, anime_id: int) -> AnimeStaff:
135
+ """Get anime staff
136
+
137
+ Args:
138
+ anime_id (int): Anime ID
139
+
140
+ Returns:
141
+ AnimeStaff: AnimeStaff object
142
+
143
+ Examples:
144
+ >>> aiojikan = AioJikan()
145
+ >>> staff = await aiojikan.get_anime_staff(1)
146
+ """
147
+
148
+ endpoint = f"anime/{anime_id}/staff"
149
+ response = await self._get(endpoint)
150
+
151
+ return AnimeStaff(**response)
152
+
153
+ async def get_anime_episodes(self, anime_id: int) -> AnimeEpisodes:
154
+ """Get anime episodes
155
+
156
+ Args:
157
+ anime_id (int): Anime ID
158
+
159
+ Returns:
160
+ AnimeEpisodes: AnimeEpisodes object
161
+
162
+ Examples:
163
+ >>> aiojikan = AioJikan()
164
+ >>> episodes = await aiojikan.get_anime_episodes(1)
165
+ """
166
+
167
+ endpoint = f"anime/{anime_id}/episodes"
168
+ response = await self._get(endpoint)
169
+
170
+ return AnimeEpisodes(**response)
171
+
172
+ async def get_anime_episode(self, anime_id: int, episode: int) -> Episode:
173
+ """Get anime episode
174
+
175
+ Args:
176
+ anime_id (int): Anime ID
177
+ episode (int): Episode number
178
+
179
+ Returns:
180
+ AnimeEpisodes: AnimeEpisodes object
181
+
182
+ Examples:
183
+ >>> aiojikan = AioJikan()
184
+ >>> episode = await aiojikan.get_anime_episode(1, 1)
185
+ """
186
+
187
+ endpoint = f"anime/{anime_id}/episodes/{episode}"
188
+ response = await self._get(endpoint)
189
+
190
+ return Episode(**response["data"])
191
+
192
+ async def get_anime_news(self, anime_id: int, page: int = None) -> AnimeNews:
193
+ """Get anime news
194
+
195
+ Args:
196
+ anime_id (int): Anime ID
197
+ page (int, optional): Page number. Defaults to None.
198
+
199
+ Returns:
200
+ AnimeNews: AnimeNews object
201
+
202
+ Examples:
203
+ >>> aiojikan = AioJikan()
204
+ >>> news = await aiojikan.get_anime_news(1)
205
+ """
206
+
207
+ params = {}
208
+ if page:
209
+ params["page"] = page
210
+
211
+ endpoint = f"anime/{anime_id}/news"
212
+ response = await self._get(endpoint, params=params)
213
+
214
+ return AnimeNews(**response)
215
+
216
+ async def search_anime(
217
+ self, search_type: str, query: str, page: int = 1
218
+ ) -> AnimeSearch:
219
+ """Search for anime
220
+
221
+ Args:
222
+ search_type (str): Type of search to perform (tv, movie, ova, special, ona, music)
223
+ query (str): Query to search for
224
+ page (int, optional): Page number. Defaults to 1.
225
+
226
+ Returns:
227
+ AnimeSearch: AnimeSearch object
228
+
229
+ Examples:
230
+ >>> aiojikan = AioJikan()
231
+ >>> result = await aiojikan.search_anime("tv", "naruto")
232
+ """
233
+
234
+ endpoint = f"anime"
235
+ params = {"q": query, "page": page, "type": search_type}
236
+ response = await self._get(endpoint, params)
237
+
238
+ return AnimeSearch(**response)
239
+
240
+ async def get_anime_forum(self, anime_id: int, filter: str = None) -> AnimeForum:
241
+ """Get anime forum
242
+
243
+ Args:
244
+ anime_id (int): Anime ID
245
+ filter (str, optional): Filter to apply. ("all", "episode", "other"). Defaults to None.
246
+
247
+ Returns:
248
+ AnimeForum: AnimeForum object
249
+
250
+ Examples:
251
+ >>> aiojikan = AioJikan()
252
+ >>> forum = await aiojikan.get_anime_forum(1)
253
+ """
254
+
255
+ params = {}
256
+ if filter:
257
+ params["filter"] = filter
258
+
259
+ endpoint = f"anime/{anime_id}/forum"
260
+ response = await self._get(endpoint, params=params)
261
+
262
+ return AnimeForum(**response)