Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement manga and some anime endpoints. #9

Merged
merged 27 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bf5592f
feat: manga, fullmanga, manga stats
r3tr0ananas Nov 23, 2024
a2f2e06
Merge pull request #7 from THEGOLDENPRO/main
THEGOLDENPRO Nov 24, 2024
f928efc
Merge pull request #8 from THEGOLDENPRO/main
r3tr0ananas Nov 24, 2024
f2fbd87
feat, docs: forums (topics), news, add manga example
r3tr0ananas Nov 24, 2024
8576903
feat: recommendations, pictures, reviews, moreinfo
r3tr0ananas Nov 24, 2024
3ce4f94
feat: forgor moreinfo for anime
r3tr0ananas Nov 24, 2024
92c5641
refactor: images should be image
r3tr0ananas Nov 24, 2024
4d69d7d
Merge pull request #10 from THEGOLDENPRO/main
THEGOLDENPRO Nov 24, 2024
f44dfc9
fix, style: invalid rst and manga resource type hinting not working f…
THEGOLDENPRO Nov 24, 2024
4f5f89f
Merge pull request #11 from THEGOLDENPRO/main
THEGOLDENPRO Nov 24, 2024
5be587f
feat: random manga and anime
r3tr0ananas Nov 25, 2024
5fef8d0
feat: add characters
r3tr0ananas Nov 25, 2024
7292eb4
feat: relations, external, user_updates
r3tr0ananas Nov 25, 2024
42b33b3
feat: genres
r3tr0ananas Nov 25, 2024
31f1e2f
fix: async using random endpoint for genres
r3tr0ananas Nov 25, 2024
399a2f6
refactor!: merge client.genres functionality into client.get
THEGOLDENPRO Nov 25, 2024
6c33db2
feat: add 'On Hiatus'
r3tr0ananas Nov 25, 2024
d680cd1
feat: add `JikanIterableResource` object
THEGOLDENPRO Nov 25, 2024
c879365
Merge branch 'feat/manga' of goldy.github.com:THEGOLDENPRO/anmoku int…
THEGOLDENPRO Nov 25, 2024
0699950
docs: add required params for resources
THEGOLDENPRO Nov 25, 2024
ceefd15
refactor!, docs: improve attributes and type hinting and add some docs
THEGOLDENPRO Nov 25, 2024
1ec3414
refactor: switch to JikanIterableResource
r3tr0ananas Nov 25, 2024
fb598df
chore: only run test workflows on push
THEGOLDENPRO Nov 25, 2024
50f14aa
Merge pull request #14 from THEGOLDENPRO/main
THEGOLDENPRO Nov 25, 2024
334fee8
style, chore: overload functions in async client should also be async…
THEGOLDENPRO Nov 25, 2024
b9ec517
refactor: rename `SnowflakeT`
THEGOLDENPRO Nov 25, 2024
6ece462
docs: add jikan docs link to Anime resource
THEGOLDENPRO Nov 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: Test anmoku (pytest & ruff)

on:
push:
pull_request:
workflow_dispatch:

jobs:
Expand Down
2 changes: 1 addition & 1 deletion anmoku/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
from .resources import *
from .errors import *

__version__ = "1.0.0dev3"
__version__ = "1.0.0alpha1"
39 changes: 33 additions & 6 deletions anmoku/clients/async_.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, overload

if TYPE_CHECKING:
from typing import Any, Optional, Type, Dict, Tuple

from ..typing.anmoku import SnowflakeT
from ..typing.anmoku import StrOrIntT
from ..typing.jikan import SearchResultData

from .base import ResourceGenericT, SearchResourceGenericT
from .base import (
ResourceGenericT,
SearchResourceGenericT,
RandomResourceGenericT,
NoArgsResourceGenericT
)

from aiohttp import ClientSession
from devgoldyutils import Colours
Expand Down Expand Up @@ -56,10 +61,21 @@ def __init__(
}
)

async def get(self, resource: Type[ResourceGenericT], id: SnowflakeT, **kwargs) -> ResourceGenericT:
"""Get's the exact resource by id."""
@overload
async def get(self, resource: Type[NoArgsResourceGenericT]) -> NoArgsResourceGenericT:
...

@overload
async def get(self, resource: Type[ResourceGenericT], id: StrOrIntT, **kwargs) -> ResourceGenericT:
...

async def get(self, resource: Type[ResourceGenericT], id: Optional[StrOrIntT] = None, **kwargs) -> ResourceGenericT:
"""Get's the exact resource typically by id."""
if id is not None:
kwargs["id"] = id

url = self._format_url(
resource._get_endpoint, resource, id = id, **kwargs
resource._get_endpoint, resource, **kwargs
)

json_data = await self._request(url)
Expand All @@ -77,6 +93,17 @@ async def search(self, resource: Type[SearchResourceGenericT], query: str, sfw:

return SearchResult(json_data, resource)

async def random(self, resource: Type[RandomResourceGenericT]) -> RandomResourceGenericT:
"""Fetches a random object of the specified resource."""
url = resource._random_endpoint

if url is None:
raise ResourceNotSupportedError(resource, "random")

json_data = await self._request(url)

return resource(json_data)

async def _request(
self,
route: str,
Expand Down
27 changes: 23 additions & 4 deletions anmoku/clients/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from typing import TYPE_CHECKING, cast

if TYPE_CHECKING:
from typing import Any, Mapping, TypeVar, Type
from typing import Any, Mapping, TypeVar, Type, Optional

from .. import resources
from ..typing.anmoku import SnowflakeT
from ..typing.anmoku import StrOrIntT
from ..resources.helpers import SearchResult

ResourceGenericT = TypeVar(
Expand All @@ -15,8 +15,22 @@

SearchResourceGenericT = TypeVar(
"SearchResourceGenericT",
resources.Anime,
resources.Anime,
resources.Character,
resources.Manga
)

RandomResourceGenericT = TypeVar(
"RandomResourceGenericT",
resources.Anime,
resources.Character,
resources.Manga
)

NoArgsResourceGenericT = TypeVar(
"NoArgsResourceGenericT",
resources.AnimeGenres,
resources.MangaGenres
)

import logging
Expand Down Expand Up @@ -56,7 +70,7 @@ def __init__(self, debug: bool = False) -> None:
super().__init__()

@abstractmethod
def get(self, resource: Type[ResourceGenericT], id: SnowflakeT) -> ResourceGenericT:
def get(self, resource: Type[ResourceGenericT], id: Optional[StrOrIntT] = None, **kwargs) -> ResourceGenericT:
"""Get's the exact resource by id."""
...

Expand All @@ -65,6 +79,11 @@ def search(self, resource: Type[SearchResourceGenericT], query: str, sfw: bool =
"""Searches for the resource and returns a list of the results."""
...

@abstractmethod
def random(self, resource: Type[RandomResourceGenericT]) -> RandomResourceGenericT:
"""Fetches a random object of the specified resource."""
...

def _format_url(self, unformatted_url: str, resource: Type[ResourceGenericT], *args: str, **kwargs: str) -> str:
"""Formats the URL while also taking URL encoding into account."""
try:
Expand Down
39 changes: 33 additions & 6 deletions anmoku/clients/sync.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, overload

if TYPE_CHECKING:
from typing import Any, Optional, Type, Tuple

from ..typing.anmoku import SnowflakeT
from ..typing.anmoku import StrOrIntT
from ..typing.jikan import SearchResultData

from .base import ResourceGenericT, SearchResourceGenericT
from .base import (
ResourceGenericT,
SearchResourceGenericT,
NoArgsResourceGenericT,
RandomResourceGenericT
)

from requests import Session
from devgoldyutils import Colours
Expand Down Expand Up @@ -54,10 +59,21 @@ def __init__(
self._second_rate_limiter = TimesPerRateLimiter(second_rate_limits[0], second_rate_limits[1])
self._minute_rate_limiter = TimesPerRateLimiter(minute_rate_limits[0], minute_rate_limits[1])

def get(self, resource: Type[ResourceGenericT], id: SnowflakeT, **kwargs) -> ResourceGenericT:
"""Get's the exact resource by id."""
@overload
def get(self, resource: Type[NoArgsResourceGenericT]) -> NoArgsResourceGenericT:
...

@overload
def get(self, resource: Type[ResourceGenericT], id: StrOrIntT, **kwargs) -> ResourceGenericT:
...

def get(self, resource: Type[ResourceGenericT], id: Optional[StrOrIntT] = None, **kwargs) -> ResourceGenericT:
"""Get's the exact resource typically by id."""
if id is not None:
kwargs["id"] = id

url = self._format_url(
resource._get_endpoint, resource, id = id, **kwargs
resource._get_endpoint, resource, **kwargs
)

json_data = self._request(url)
Expand All @@ -75,6 +91,17 @@ def search(self, resource: Type[SearchResourceGenericT], query: str, sfw: bool =

return SearchResult(json_data, resource)

def random(self, resource: Type[RandomResourceGenericT]) -> RandomResourceGenericT:
"""Fetches a random object of the specified resource."""
url = resource._random_endpoint

if url is None:
raise ResourceNotSupportedError(resource, "random")

json_data = self._request(url)

return resource(json_data)

def _request(
self,
route: str,
Expand Down
1 change: 1 addition & 0 deletions anmoku/resources/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .base import *
from .anime import *
from .manga import *
from .character import *
10 changes: 9 additions & 1 deletion anmoku/resources/anime/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
from .anime import *
from .characters import *
from .staff import *
from .episodes import *
from .episodes import *
from .news import *
from .forum import *
from .pictures import *
from .recommendations import *
from .reviews import *
from .relations import *
from .external import *
from .genre import *
23 changes: 23 additions & 0 deletions anmoku/resources/anime/anime.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
AnimeData,
FullAnimeData,
JikanResponseData,
MoreInfoData
)
from ...typing.mal import MALRatings

Expand Down Expand Up @@ -49,6 +50,14 @@ class Anime(JikanResource):
"""
Get or search for anime.

[`jikan`_]

.. _jikan: https://docs.api.jikan.moe/#tag/anime/operation/getAnimeById

Required Params
-----------------
* `id` - Manga ID

------------

⭐ Example:
Expand All @@ -70,6 +79,7 @@ class Anime(JikanResource):
"""
_get_endpoint = "/anime/{id}"
_search_endpoint = "/anime"
_random_endpoint = "/random/anime"

data: JikanResponseData[AnimeData] = field(repr = False)

Expand Down Expand Up @@ -173,6 +183,19 @@ class FullAnime(Anime): # TODO: Finish this. You can use the FullAnimeData type

data: JikanResponseData[FullAnimeData] = field(repr=False)

@dataclass
class AnimeMoreInfo(JikanResource):
_get_endpoint = "/anime/{id}/moreinfo"

data: JikanResponseData[MoreInfoData] = field(repr=False)

more_info: Optional[str] = field(init = False, default = None)

def __post_init__(self):
more_info = self.data["data"]["moreinfo"]

if more_info is not None:
self.more_info = more_info

@dataclass
class Trailer():
Expand Down
27 changes: 16 additions & 11 deletions anmoku/resources/anime/characters.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,29 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import List
from ...typing.jikan import (
AnimeCharacterData,
JikanResponseData
)
from typing import List, Generator, Any
from ...typing.jikan import AnimeCharacterData, JikanResponseData

from ...typing.jikan.anime.characters import VoiceActorData

from dataclasses import dataclass, field

from ..helpers import Image
from ..base import JikanResource
from ..base import JikanIterableResource

__all__ = (
"AnimeCharacters",
)

@dataclass
class AnimeCharacters(JikanResource):
class AnimeCharacters(JikanIterableResource):
"""
Get data of the characters from a particular anime.

Required Params
-----------------
* `id` - Anime ID

------------

⭐ Example:
Expand All @@ -37,18 +38,22 @@ class AnimeCharacters(JikanResource):
anime_characters = client.get(AnimeCharacters, id = 28851) # ID for the anime film "A Silent Voice".

for character in anime_characters:
print(f"{character.name} ({character.url})")
print(f"{character.name} ({character.url})")

client.close()
"""
_get_endpoint = "/anime/{id}/characters"

data: JikanResponseData[List[AnimeCharacterData]]

def __iter__(self):
def __post_init__(self):
super().__post_init__(AnimeCharacter)

def __next__(self) -> AnimeCharacter:
return super().__next__()

for character in self.data["data"]:
yield AnimeCharacter(character)
def __iter__(self) -> Generator[AnimeCharacter, Any, None]:
return super().__iter__()

@dataclass
class AnimeCharacter():
Expand Down
26 changes: 19 additions & 7 deletions anmoku/resources/anime/episodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import List, Optional
from typing import List, Optional, Generator, Any
from ...typing.jikan import (
AnimeEpisodeData,
SingleAnimeEpisodeData,
Expand All @@ -14,24 +14,36 @@
from dataclasses import dataclass, field

from ..helpers import Title
from ..base import JikanResource
from ..base import JikanResource, JikanIterableResource

__all__ = (
"AnimeEpisodes",
"SingleAnimeEpisode"
)

@dataclass
class AnimeEpisodes(JikanResource):
"""Get an anime's episodes with anime's ID."""
class AnimeEpisodes(JikanIterableResource):
"""
Get an anime's episodes with anime's ID.

-----------------

Required Params
-----------------
* `id` - Anime ID
"""
_get_endpoint = "/anime/{id}/episodes"

data: JikanPageResponseData[List[AnimeEpisodeData]]

def __iter__(self):
def __post_init__(self):
super().__post_init__(AnimeEpisode)

def __next__(self) -> AnimeEpisode:
return super().__next__()

for episode in self.data["data"]:
yield AnimeEpisode(episode)
def __iter__(self) -> Generator[AnimeEpisode, Any, None]:
return super().__iter__()

@dataclass
class SingleAnimeEpisode(JikanResource):
Expand Down
Loading