diff --git a/.gitea/ISSUE_TEMPLATE/bug.yml b/.gitea/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..8f78e72 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,34 @@ +name: Bug Report +description: Report a bug in this project +labels: + - kind/bug + - triage +body: + + - type: textarea + id: bug + attributes: + label: Describe the bug + description: | + A clear and concise description of what the bug is. + What did you expect to happen? What happened instead? + Include screenshots if applicable. + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Steps to reproduce + description: | + A clear and concise description of how to reproduce the bug. + Include steps, code snippets, or screenshots if applicable. + validations: + required: true + - type: textarea + id: additional-info + attributes: + label: Additional information + description: | + Add any other context or screenshots about the bug here. + + \ No newline at end of file diff --git a/.gitea/ISSUE_TEMPLATE/feature.yml b/.gitea/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 0000000..cb6338e --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,23 @@ +name: Feature request +description: Suggest an idea for this project +labels: + - kind/feature + - triage + +body: + - type: textarea + id: feature + attributes: + label: Describe the feature + description: | + A clear and concise description of what the feature is. + What is the problem it solves? What are you trying to accomplish? + Include screenshots if applicable. + validations: + required: true + - type: textarea + id: additional-info + attributes: + label: Additional information + description: | + Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..a77bb37 --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,80 @@ +on: + workflow_dispatch: + inputs: + release_notes: + description: Release notes (use \n for newlines) + type: string + required: false + github_release: + description: 'Create Gitea Release' + default: true + type: boolean + bump: + description: 'Bump type' + required: true + default: 'patch' + type: choice + options: + - 'major' + - 'minor' + - 'patch' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@master + - name: Install uv + uses: astral-sh/setup-uv@v5 + - name: Set up Python + run: uv python install + - name: Set Git identity + run: | + git config user.name "Gitea CI" + git config user.email "ci@git.theprivateserver.de" + - name: Bump version + id: bump + run: | + uv tool install bump-my-version + uv tool run bump-my-version bump ${{ github.event.inputs.bump }} + # echo the version to github env, the version is shown by using uv tool run bump-my-version show current_version + echo "VERSION<> $GITHUB_ENV + echo "$(uv tool run bump-my-version show current_version)" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: ${{ github.ref }} + + - name: Create release notes + run: | + mkdir release_notes + echo -e "${{ inputs.release_notes }}" >> release_notes/release_notes.md + echo "Release notes:" + cat release_notes/release_notes.md + echo "" + - name: Build package + run: uv build + - name: Publish package + env: + USERNAME: ${{ github.repository_owner }} + run: uv publish --publish-url https://git.theprivateserver.de/api/packages/$USERNAME/pypi/ -t ${{ secrets.TOKEN }} + + + - name: Create release + id: create_release + if: ${{ github.event.inputs.github_release == 'true' }} + uses: softprops/action-gh-release@master + with: + tag_name: ${{ env.VERSION }} + release_name: Release ${{ env.VERSION }} + body_path: release_notes/release_notes.md + draft: false + prerelease: false + make_latest: true + files: | + dist/* + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} \ No newline at end of file diff --git a/src/anilistapi/api.py b/src/anilistapi/api.py index 3508ba9..eff2728 100644 --- a/src/anilistapi/api.py +++ b/src/anilistapi/api.py @@ -1,12 +1,19 @@ import requests -from .schemas.manga import Manga -from .queries.manga import MANGA_QUERY, MANGA_ID_QUERY +from .schemas.manga import Manga, Tag +from .queries.manga import ( + MANGA_QUERY, + MANGA_ID_QUERY, + REQUESTS_QUERY, + GENRES_QUERY, + TAGS_QUERY, +) from limit import limit from anilistapi import __version__, __contact__ import os +import time -REQUEST_LIMIT = 1 -REQUEST_PERIOD = 2 +REQUEST_LIMIT = 90 +REQUEST_PERIOD = 60 class AnilistAPI: @@ -19,14 +26,15 @@ class AnilistAPI: def request(self, query: str, variables: dict) -> dict: url = "https://graphql.anilist.co" response = requests.post(url, json={"query": query, "variables": variables}) + time.sleep(1) if response.status_code != 200: return {} # raise Exception(f"Error: {response}, response: {response.json()}, query: {query}, variables: {variables}") return response.json() def search_manga(self, search: str) -> list[Manga]: - variables = {"search": search} - response = self.request(MANGA_QUERY, variables) + variables = {"search": search, "format": "MANGA"} + response = self.request(REQUESTS_QUERY, variables) # check if reponse has data Page and media if not response.get("data", {}).get("Page", {}).get("media"): return [] @@ -42,3 +50,35 @@ class AnilistAPI: if not response.get("data", {}).get("Media"): return None return Manga(**response["data"]["Media"]) + + def kompage_search(self, search: str) -> list[Manga]: + variables = {"search": search} + response = self.request(REQUESTS_QUERY, variables) + # check if reponse has data Page and media + if not response.get("data", {}).get("Page", {}).get("media"): + return [] + res = [] + for manga in response["data"]["Page"]["media"]: + res.append(Manga(**manga)) + return res + + def get_genres(self) -> list[str]: + variables = {} + response = self.request(GENRES_QUERY, variables) + if not response.get("data", {}).get("genres"): + return [] + res = [] + for genre in response["data"]["genres"]: + res.append(genre) + return res + + def get_tags(self): + variables = {} + response = self.request(TAGS_QUERY, variables) + if not response.get("data", {}).get("tags"): + return [] + res = [] + for tag in response["data"]["tags"]: + ctag = Tag(**tag) + res.append(ctag.name) + return res diff --git a/src/anilistapi/queries/manga.py b/src/anilistapi/queries/manga.py index d557b7f..500ce2f 100644 --- a/src/anilistapi/queries/manga.py +++ b/src/anilistapi/queries/manga.py @@ -3,6 +3,10 @@ query media($search: String) { Page { pageInfo { hasNextPage + total + perPage + currentPage + lastPage } media(type: MANGA, search: $search) { id @@ -52,3 +56,87 @@ Media (type: MANGA, id:$id) { # Insert our variables into the query arguments (i } } """ + +REQUESTS_QUERY = """query query($search: String, $genres:[String], $tags:[String], $format: MediaFormat) { +Page(perPage: 100) { + pageInfo { + hasNextPage + total + perPage + currentPage + lastPage + } + media(type: MANGA, search: $search, genre_in: $genres, tag_in: $tags, sort: SEARCH_MATCH, format: $format) { + id + title { + romaji + english + native + } + synonyms + format + type + status(version:2) + genres + tags{ + name + isAdult + } + description + coverImage { + large + + } + isAdult + chapters + volumes + externalLinks { + site + url + type + } + countryOfOrigin + siteUrl + } + +} +}""" + +REQUESTED_QUERY = """query query($search: Int) { +Media(type: MANGA, id: $search, sort: SEARCH_MATCH) { + id + title { + romaji + english + native + } + synonyms + format + type + status(version:2) + genres + tags{ + name + isAdult + } + description + coverImage { + large + + } + isAdult + chapters + volumes + externalLinks { + site + url + type + } + countryOfOrigin + } + +}""" + +GENRES_QUERY = """query query{genres:GenreCollection}""" + +TAGS_QUERY = """query query{tags:MediaTagCollection{name}}""" diff --git a/src/anilistapi/schemas/manga.py b/src/anilistapi/schemas/manga.py index 2e4b56d..c1bef8d 100644 --- a/src/anilistapi/schemas/manga.py +++ b/src/anilistapi/schemas/manga.py @@ -2,7 +2,6 @@ from dataclasses import dataclass from .title import Title from .externalLinks import ExternalLinks from .tag import Tag -from typing import Union, Any @dataclass @@ -14,14 +13,15 @@ class Manga: type: str = None format: str = None genres: list[str] = None - tags: list | list[Tag] = None + tags: list[Tag] = None description: str = None - coverImage: dict = None + coverImage: dict[str, str] = None isAdult: bool = False chapters: int = None volumes: int = None - externalLinks: list | list[ExternalLinks] = None + externalLinks: list[ExternalLinks] = None countryOfOrigin: str = None + siteUrl: str = None def __post_init__(self): self.title = Title(**self.title) if self.title else None