diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 3fc684e..7ea00e9 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1 @@ -github: sinkaroid -custom: https://paypal.me/sinkaroid \ No newline at end of file +github: sinkaroid \ No newline at end of file diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index fe32bc7..97f26c7 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -11,9 +11,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Python 3 - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install dependencies @@ -21,4 +21,4 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt - name: Request API status - run: make api-mock + run: python -m unittest test.test_api diff --git a/.github/workflows/asmhentai.yml b/.github/workflows/asmhentai.yml index b7d8b74..635ad03 100644 --- a/.github/workflows/asmhentai.yml +++ b/.github/workflows/asmhentai.yml @@ -11,9 +11,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Python 3 - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install dependencies @@ -21,4 +21,4 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt - name: Get book - run: make asmhentai-get + run: python -m unittest test.test_asmhentai diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f5c0897..157e530 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,14 +12,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pdoc3 - make build-docs + pdoc --html janda - name: Deploy uses: JamesIves/github-pages-deploy-action@v4.2.5 @@ -27,4 +27,4 @@ jobs: branch: gh-pages folder: html/janda - name: Check build - run: make janda \ No newline at end of file + run: python -m unittest test.test_build \ No newline at end of file diff --git a/.github/workflows/hentai2read.yml b/.github/workflows/hentai2read.yml index 2ac9a86..6fc3deb 100644 --- a/.github/workflows/hentai2read.yml +++ b/.github/workflows/hentai2read.yml @@ -11,9 +11,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Python 3 - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install dependencies @@ -21,4 +21,4 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt - name: Get book - run: make hentai2read-get + run: python -m unittest test.test_hentai2read diff --git a/.github/workflows/hentaifox.yml b/.github/workflows/hentaifox.yml index ce00cc0..39ff9de 100644 --- a/.github/workflows/hentaifox.yml +++ b/.github/workflows/hentaifox.yml @@ -11,9 +11,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Python 3 - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install dependencies @@ -21,4 +21,4 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt - name: Get book - run: make hentaifox-get + run: python -m unittest test.test_hentaifox diff --git a/.github/workflows/nhentai.yml b/.github/workflows/nhentai.yml index 997b4a2..92780b2 100644 --- a/.github/workflows/nhentai.yml +++ b/.github/workflows/nhentai.yml @@ -11,9 +11,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Python 3 - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install dependencies @@ -21,4 +21,4 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt - name: Get book - run: make nhentai-get + run: python -m unittest test.test_nhentai diff --git a/.github/workflows/pururin.yml b/.github/workflows/pururin.yml index 57d76a1..0d74ac7 100644 --- a/.github/workflows/pururin.yml +++ b/.github/workflows/pururin.yml @@ -11,9 +11,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Python 3 - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install dependencies @@ -21,4 +21,4 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt - name: Get book - run: make pururin-get + run: python -m unittest test.test_pururin diff --git a/.github/workflows/simplyh.yml b/.github/workflows/simplyh.yml index 42a0c1c..fc30810 100644 --- a/.github/workflows/simplyh.yml +++ b/.github/workflows/simplyh.yml @@ -11,9 +11,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Python 3 - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install dependencies @@ -21,4 +21,4 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt - name: Get book - run: make simplyh-get + run: python -m unittest test.test_simplyh diff --git a/.github/workflows/thentai.yml b/.github/workflows/thentai.yml new file mode 100644 index 0000000..0b29e6a --- /dev/null +++ b/.github/workflows/thentai.yml @@ -0,0 +1,24 @@ +name: 3hentai testing +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install Python 3 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Get book + run: python -m unittest test.test_thentai diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f9ac1f4..0fd4450 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,4 +7,4 @@ email, or any other method with the owners before making a change. Please note we have a code of conduct, follow it in all your interactions with the project. ## Making minor changes -@sinkaroid are not the best coder, so there are sure some problematic coding decision, every slightest of changes will helps a lot. I'm always happy to receive Pull requests to improve things. \ No newline at end of file +@sinkaroid are not the best coder, so there are sure some problematic coding decision, every slightest of changes will helps however. I always happy to receive Pull requests to improve things. \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 4a742ec..0000000 --- a/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -install: - pip install -r requirements.txt - -janda: - python -m unittest test.test_build - -nhentai-get: # testing nhentai - python -m unittest test.test_nhentai - -pururin-get: # testing pururin - python -m unittest test.test_pururin - -hentaifox-get: # testing hentaifox - python -m unittest test.test_hentaifox - -hentai2read-get: # testing hentai2read - python -m unittest test.test_hentai2read - -simplyh-get: # testing simplyh - python -m unittest test.test_simplyh - -asmhentai-get: # testing asm - python -m unittest test.test_asmhentai - -api-mock: # check api if something down - python -m unittest test.test_api - -build-docs: # build and deploy docs - pdoc --html janda - -upload: - bash build.sh \ No newline at end of file diff --git a/README.md b/README.md index a7045e0..245a613 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,43 @@
janda -

A featureful Python library covers most popular doujin API

+

Python library for Jandapress

-

-Built on minimalist dependencies to resolves and interacts with ease. -It takes a much more dictionaries rather than just raw data, and hope will be extendable. Janda has plenty api support apart from nhentai. +Interacts from python, simplified the usage, and intelisense definitions on your IDEs ContributingDocumentationReport Issues
+- [Janda](#) + - [Jandapress](#jandapress) + - [Features](#janda-vs-the-competition) + - [Installation](#installation) + - [Prerequisites](#prerequisites) + - [Documentation](https://sinkaroid.github.io/janda/) + - [Example](#example) + - [Janda.resolve()](#jandaresolve) + - [Known issues](#known-issues) + - [Pronunciation](#pronunciation) + - [Legal](#legal) + --- -## Subprojects -- [jandapress](https://github.com/sinkaroid/jandapress) — RESTful API for janda client +## Jandapress +If you prefer with raw api and want to dealing with cloudflare stuff use jandapress +- [jandapress](https://github.com/sinkaroid/jandapress) — RESTful and experimental API for nhentai and other doujinshi ## Janda vs. the Competition -Built on minimalist dependencies, yet it covers most of the popular doujinboards. +Features availability from jandapress -| Client | Status | Get | Search | Randomizer | +| Client | Status | Get | Search | Random | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----- | ------ | ---------- | | nhentai | [![status](https://img.shields.io/badge/status-stable-green)](https://github.com/sinkaroid/janda/actions/workflows/nhentai.yml) | `Yes` | `Yes` | `Yes` | | pururin | [![status](https://img.shields.io/badge/status-stable-green)](https://github.com/sinkaroid/janda/actions/workflows/pururin.yml) | `Yes` | `Yes` | `Yes` | @@ -34,35 +45,25 @@ Built on minimalist dependencies, yet it covers most of the popular doujinboards | hentai2read | [![status](https://img.shields.io/badge/status-partial-blue)](https://github.com/sinkaroid/janda/actions/workflows/hentai2read.yml) | `Yes` | `Yes` | `No` | | simply-hentai | [![status](https://img.shields.io/badge/status-triage-red)](https://github.com/sinkaroid/janda/actions/workflows/simplyh.yml) | `Yes` | `No` | `No` | | asmhentai | [![status](https://img.shields.io/badge/status-stable-green)](https://github.com/sinkaroid/janda/actions/workflows/asmhentai.yml) | `Yes` | `Yes` | `Yes` | +| 3hentai | [![status](https://img.shields.io/badge/status-stable-green)](https://github.com/sinkaroid/janda/actions/workflows/thentai.yml) | `Yes` | `Yes` | `Yes` | -## Features - -- **Easy to use**: check your intelisense -- **Neat**: object taken is re-appended to make it actionable -- **Documented**: fully documented and tested -- **All-in-one**: plenty of site support ## Prerequisites - Python 3.7 or above -- Can parse JSON - - -
NOTE: Please always use the latest version of the module.
-Since this library covers a lot of sites, hence there is always a staged changes -
-## 🚀Installation -`pip install janda / pipenv install janda` -- or fork this repo -To use specific site apis, You could specify import too, for example: +## Installation +`pip install janda` +- Or manual build by cloning this repository and run `python setup.py install` + +To use specific site api, You could specify import for example: - `from janda import Nhentai` -then initializes the client, an [api key](https://scathach.dev/dashboard) is optional +then initializes the client, an [api key](https://scathach.id/login) is optional -## Quick example +## Example Some methods require additional parameters, check your intelisense. ### get @@ -73,94 +74,28 @@ from janda import Nhentai, resolve async def book(): nh = Nhentai() - data = await nh.get(274003) - print(data) ## unresolved - print(resolve(data)) ## resolved + data = await nh.get(177013) + print(data) ## this is + print(resolve(data)) ## this is async def main(): await asyncio.gather(book()) asyncio.run(main()) ``` -The final step you must resolve them to works with data. See [#Unresolved JSON](#unresolved-json) -Authorization is always optional! but if you fill it you should define through specific import ### search `(tags: str, page: int = 1, popular: str = 'today') -> Coroutine` ```py -await nh.search("jeanne alter", 1, "all") -``` - -## Unresolved JSON -Instead arbitrary object, This library designed to be neat and clean returns, although it must be reparsed to the string first, that's why [`janda.resolve()`](https://sinkaroid.github.io/janda/utils/parser.html#janda.utils.parser.resolve) exist. - -Let's see an example: - -```py -import asyncio -from janda import Nhentai, resolve - -async def main(): - nh = Nhentai() - data = await nh.get(274003) - print(data) ## unresolve - print(resolve(data)) ## resolved - -asyncio.run(main()) +await nh.search("jeanne alter", 1, "today") ``` -- Unresolve: meant is better and neat dictionaries returns instead arbitrary JSON structure -- Resolved: bad structure, arbitary indent, unsorting but it is resolved and ready to extends works with JSON +## janda.resolve() +You will need this for every object, this library designed to be neat and clean returns, although it must be reparsed to the string first, that's why `janda.resolve()` exist. ## Documentation The documentation can be found [https://sinkaroid.github.io/janda](https://sinkaroid.github.io/janda) -### Nhentai -- [`Nhentai.get(options)`](https://sinkaroid.github.io/janda/nhentai.html) - - Get specific doujin from nhentai -- [`Nhentai.search(options)`](https://sinkaroid.github.io/janda/nhentai.html) - - Search doujin by tags / artist / character / parody or group -- [`Nhentai.search_related(options)`](https://sinkaroid.github.io/janda/nhentai.html) - - Get related book or almost alike from Id given -- [`Nhentai.get_random()`](https://sinkaroid.github.io/janda/nhentai.html) - - Get random doujin from nhentai - -### Pururin -- [`Pururin.get(options)`](https://sinkaroid.github.io/janda/pururin.html) - - Get specific doujin from pururin -- [`Pururin.get_random()`](https://sinkaroid.github.io/janda/pururin.html) - - Get random doujin from pururin -- [`Pururin.search(options)`](https://sinkaroid.github.io/janda/pururin.html) - - Search doujin by tags / artist / character / parody or group - - -### Hentai2read -- [`Hentai2read.get(options)`](https://sinkaroid.github.io/janda/hentai2read.html) - - Get specific doujin from hentai2read -- [`Hentai2read.search(options)`](https://sinkaroid.github.io/janda/hentai2read.html) - - Search a doujin from hentai2read by latest only - -### Simplyhentai -- [`Simplyhentai.get(options)`](https://sinkaroid.github.io/janda/simply_hentai.html) - - Get specific doujin from simplyhentai - -### Hentaifox -- [`Hentaifox.get(options)`](https://sinkaroid.github.io/janda/hentaifox.html) - - Get specific doujin from hentaifox -- [`Hentaifox.get_random()`](https://sinkaroid.github.io/janda/hentaifox.html) - - Get random doujin from hentaifox -- [`Hentaifox.search(options)`](https://sinkaroid.github.io/janda/hentaifox.html) - - Search doujin by tags / artist / character / parody or group - - -### Asmhentai -- [`Asmhentai.get(options)`](https://sinkaroid.github.io/janda/asmhentai.html) - - Get specific doujin from asmhentai -- [`Asmhentai.search(options)`](https://sinkaroid.github.io/janda/asmhentai.html) - - Search a doujin from asmhentai, can providing with page number -- [`Asmhentai.get_random()`](https://sinkaroid.github.io/janda/asmhentai.html) - - Get random doujin from asmhentai - ## Returns example `get` method will represent as **Book Object** and packed with actionable image urls ```js @@ -531,12 +466,10 @@ Otherwise `search` will return 25 **List Object** of search results. #### `UnicodeEncodeError: 'charmap' codec can't encode characters` - It's raised when the title contains non-ascii characters, then your console can't parse them, use real console don't Git-bash. -## Legal -This tool can be freely copied, modified, altered, distributed without any attribution whatsoever. However, if you feel -like this tool deserves an attribution, mention it. It won't hurt anybody - ## Pronunciation -[`id_ID`](https://www.localeplanet.com/java/id-ID/index.html) • **/jan·da/** — gatel, nakal, dan menggoda; _(?)_ seperti siapa? adalah benar si janda gemer tomoe +[`id_ID`](https://www.localeplanet.com/java/id-ID/index.html) • **/jan·da/** — Dewasa dan mengikat; (?) -## EoF -All books from those doujinboards are definitely ilegal from original authors. \ No newline at end of file +## Legal +This tool can be freely copied, modified, altered, distributed without any attribution whatsoever. However, if you feel +like this tool deserves an attribution, mention it. It won't hurt anybody. +> Licence: WTF. \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index 931c4cf..c697fd9 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,8 +1,5 @@ -# Reporting Security Vulnerabilities +# Janda Security -**We urge you not to file a bug report in this GitHub repository since they are open for anyone to see** +## Reporting vulnerabilities -Instead, we encourage you to reach out to the maintainer team so we can assess the problem and later disclose it -responsibly. - -If you believe you have found a security-related bug, please contact [sindra](mailto:anakmancasan@gmail.com) \ No newline at end of file +To report sensitive vulnerabilities, alert the author by email at anakmancasan@gmail.com. \ No newline at end of file diff --git a/janda/__init__.py b/janda/__init__.py index 1488a03..22141df 100644 --- a/janda/__init__.py +++ b/janda/__init__.py @@ -1,8 +1,9 @@ -__version__ = "3.0.14" +__version__ = "3.1.1" from janda.pururin import Pururin from janda.nhentai import Nhentai from janda.hentaifox import Hentaifox from janda.hentai2read import Hentai2read from janda.simply_hentai import SimplyHentai from janda.asmhentai import Asmhentai -from janda.utils.parser import resolve +from janda.thentai import Thentai +from janda.utils.client import resolve diff --git a/janda/asmhentai.py b/janda/asmhentai.py index 9b62067..1af328c 100644 --- a/janda/asmhentai.py +++ b/janda/asmhentai.py @@ -1,23 +1,22 @@ -import requests -import json -from .utils.parser import * +from janda.utils.client import * +from janda.utils.request import request -BASE_URL = Api() +Janda = Api() class Asmhentai(object): - """Asmhentai API wrapper + """Jandapress Asmhentai API Methods ------- get : function - Gets doujin from id given + Get doujin from id given search : function Search for doujin wirh query and page number given get_random : function - Gets random doujin + Get random doujin """ def __init__(self, api_key: str = ""): @@ -26,7 +25,7 @@ def __init__(self, api_key: str = ""): Parameters ---------- api_key : str - scathach.dev API key (optional) + scathach.id API key (optional) """ if api_key == "": self.api_key = None @@ -34,50 +33,30 @@ def __init__(self, api_key: str = ""): self.api_key = api_key self.specs = {"api_key": self.api_key} - async def get(self, id: int): - """Gets doujin from id given + async def get(self, id: int) -> str: + """Get asmhentai doujin from id given - path: https://asmhentai.com/g/311851 + example: https://asmhentai.com/g/311851 Parameters ---------- id : int The id of the doujin - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The book object that represents the specific id response. + str + reparsed json as string """ - if isinstance(id, int): - id = str(id) - - path = id.strip("/") - self.specs["book"] = path - - try: - path = str(path) - - except ValueError or path.isdigit(): - raise ValueError("Path must be a str") - - data = requests.get(BASE_URL.asmhentai + "/get", params=self.specs) - - if data.status_code != 200: - raise ValueError("No results found for " + id) + self.book = str(id) + data = await request(Janda.asmhentai + Janda.endpoint_book, self.book) + return better_object(data) - return better_object(data.json()) + async def search(self, query: str, page: int = 1) -> str: + """Search asmhentai doujin with query and page number given - async def search(self, query: str, page: int = 1): - """Search for doujin with query and page number given - - path: https://asmhentai.com/search/?q= + example: https://asmhentai.com/search/?q= Parameters ---------- @@ -87,40 +66,27 @@ async def search(self, query: str, page: int = 1): page : int The page number to search for, Default is 1 - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The list object that represents the doujin response. + str + reparsed json as string """ - self.specs["key"] = query - self.specs["page"] = page - - query = auto_space(query) + self.query = query + self.page = page + self.req = str(self.query) + "&page=" + str(self.page) - if query == "": - raise ValueError("Query must be given") - data = requests.get(BASE_URL.asmhentai + "/search", params=self.specs) + data = await request(Janda.asmhentai + Janda.endpoint_search, self.req) + return better_object(data) - if len(data.json()["data"]) == 0: - raise ValueError("No results found") - - return better_object(data.json()) - - async def get_random(self): - """Gets random doujin on asmhentai + async def get_random(self) -> str: + """Get asmhentai random doujin Returns ------- - dict - The book object that represents the random doujin response. + str + reparsed json as string """ - data = requests.get(BASE_URL.asmhentai + "/random", params=self.specs) - - return better_object(data.json()) + data = await request(Janda.asmhentai + Janda.endpoint_random) + return better_object(data) diff --git a/janda/hentai2read.py b/janda/hentai2read.py index 5240f94..1ea9e05 100644 --- a/janda/hentai2read.py +++ b/janda/hentai2read.py @@ -1,17 +1,16 @@ -import requests -import json -from .utils.parser import * +from janda.utils.client import * +from janda.utils.request import request -BASE_URL = Api() +Janda = Api() class Hentai2read(object): - """Hentai2read API wrapper + """Jandapress Hentai2read API Methods ------- get : function - Gets doujin from path given + Get doujin from path given search : function Search for doujin based on the latest @@ -24,7 +23,7 @@ def __init__(self, api_key: str = ""): Parameters ---------- api_key : str - scathach.dev API key (optional) + scathach.id API key (optional) """ if api_key == "": self.api_key = None @@ -32,74 +31,44 @@ def __init__(self, api_key: str = ""): self.api_key = api_key self.specs = {"api_key": self.api_key} - async def get(self, path: str, chapter: int = 1): - """Gets doujin from path given - - path: https://hentai2read.com/a_story_of_tomoe_gozen_being_punished_by_a_shota/1 + async def get(self, path: str) -> str: + """Get hentai2read doujin from path given + + example: https://hentai2read.com/a_story_of_tomoe_gozen_being_punished_by_a_shota/1 Parameters ---------- path : str The path url - chapter : int - The chapter number. Default is 1 - - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The book object that represents the specific path response. + str + reparsed json as string """ - if "/" in path: - path = path.replace("/", "") - - self.specs["book"] = path + "/" + str(chapter) - - try: - path = str(path) - except ValueError: - raise ValueError("Path must be a str") + self.book = str(path) - data = requests.get(BASE_URL.hentai2read + "/get", params=self.specs) + data = await request(Janda.hentai2read + Janda.endpoint_book, self.book) + return better_object(data) - return better_object(data.json()) + async def search(self, query: str) -> str: + """Search hentai2read doujin based on the latest - async def search(self, query: str): - """Search for doujin based on the latest - - path: https://hentai2read.com/hentai-list/search/alter + example: https://hentai2read.com/hentai-list/search/alter Parameters ---------- query : str The query to search for. - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The list object that represents the doujin response. + str + reparsed json as string """ - self.specs["key"] = query - - query = auto_space(query) - - if query == "": - raise ValueError("Query must be given") - data = requests.get(BASE_URL.hentai2read + "/search", params=self.specs) - - if len(data.json()["data"]) == 0: - raise ValueError("No results found") + self.key = query - return better_object(data.json()) + data = await request(Janda.hentai2read + Janda.endpoint_search, self.key) + return better_object(data) diff --git a/janda/hentaifox.py b/janda/hentaifox.py index 2e53da0..10af248 100644 --- a/janda/hentaifox.py +++ b/janda/hentaifox.py @@ -1,12 +1,11 @@ -import requests -import json -from .utils.parser import * +from janda.utils.client import * +from janda.utils.request import request -BASE_URL = Api() +Janda = Api() class Hentaifox(object): - """HentaiFox API wrapper + """Jandapress Hentaifox API Methods ------- @@ -26,7 +25,7 @@ def __init__(self, api_key: str = ""): Parameters ---------- api_key : str - scathach.dev API key (optional) + scathach.id API key (optional) """ if api_key == "": self.api_key = None @@ -34,45 +33,30 @@ def __init__(self, api_key: str = ""): self.api_key = api_key self.specs = {"api_key": self.api_key} - async def get(self, book: int): - """Get doujin API from Id + async def get(self, book: int) -> str: + """Get hentaifox doujin book from Id - path: https://hentaifox.com/gallery/88027/ + example: https://hentaifox.com/gallery/88027/ Parameters ---------- book : int The id number of the doujin. - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The book object that represents the specific id response. + str + reparsed json as string """ - self.specs["book"] = book - - try: - book = int(book) - except ValueError: - raise ValueError("Book must be an int") - - data = requests.get(BASE_URL.hentaifox + "/get", params=self.specs) - - if data.status_code != 200: - raise ValueError("No results found") + self.book = str(book) + data = await request(Janda.hentaifox + Janda.endpoint_book, self.book) + return better_object(data) - return better_object(data.json()) + async def search(self, query: str, page: int = 1, sort: str = "latest") -> str: + """Search hentaifox doujin with query and page number given - async def search(self, query: str, page: int = 1, sort: str = "latest"): - """Search for doujin based on the latest - - path: https://hentaifox.com/search/?q=alter&sort=latest + example: https://hentaifox.com/search/?q=alter&sort=latest Parameters ---------- @@ -85,51 +69,29 @@ async def search(self, query: str, page: int = 1, sort: str = "latest"): sort : str The sort order to search: latest, popular - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The list object that represents the doujin response. + str + reparsed json as string """ - self.specs["key"] = query - self.specs["sort"] = sort - self.specs["page"] = page - - query = auto_space(query) + self.query = query + self.page = page + self.sort = sort + self.req = str(self.query + "&page=" + + str(self.page) + "&sort=" + self.sort) + + data = await request(Janda.hentaifox + Janda.endpoint_search, self.req) + return better_object(data) - if query == "": - raise ValueError("Query must be given") - data = requests.get(BASE_URL.hentaifox + "/search", params=self.specs) - - if len(data.json()["data"]) == 0: - raise ValueError("No results found") - - return better_object(data.json()) - - async def get_random(self): - """Get random doujin - - Raises - ------ - ValueError - If the doujin is not found. + async def get_random(self) -> str: + """Get hentaifox random doujin Returns ------- - dict - The book object that represents the random doujin response. + str + reparsed json as string """ - data = requests.get(BASE_URL.hentaifox + "/random", params=self.specs) - - if data.status_code != 200: - raise ValueError( - "Request failed with status code {}".format(data.status_code) - ) - - return better_object(data.json()) + data = await request(Janda.hentaifox + Janda.endpoint_random) + return better_object(data) diff --git a/janda/nhentai.py b/janda/nhentai.py index 61a2ad1..07dff79 100644 --- a/janda/nhentai.py +++ b/janda/nhentai.py @@ -1,12 +1,11 @@ -import requests -import json -from .utils.parser import * +from janda.utils.client import * +from janda.utils.request import request -BASE_URL = Api() +Janda = Api() class Nhentai(object): - """Nhentai API wrapper + """Jandapress Nhentai API Methods ------- @@ -26,40 +25,26 @@ class Nhentai(object): def __init__(self): self.specs = {} - async def get(self, book: int): - """Get doujin book from Id + async def get(self, book: int) -> str: + """Get nhentai doujin book from Id Parameters ---------- book : int The id number of the doujin. - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The book object that represents the specific id response. + str + reparsed json as string """ - self.specs["book"] = book - - try: - book = int(book) - except ValueError: - raise ValueError("Book must be an int") - - data = requests.get(BASE_URL.nhentai + "/get", params=self.specs) + self.book = str(book) + data = await request(Janda.nhentai + Janda.endpoint_book, self.book) + return better_object(data) - self.final = json.loads(better_object(data.json())) - - return better_object(self.final) - - async def search(self, query: str, page: int = 1, sort: str = "popular-today"): - """Search doujin by tags / artist / character / parody or group + async def search(self, query: str, page: int = 1, sort: str = "popular-today") -> str: + """Search nhentai doujin by tags / artist / character / parody or group Parameters ---------- @@ -72,15 +57,10 @@ async def search(self, query: str, page: int = 1, sort: str = "popular-today"): sort : str popular-today, popular-week, popular - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The list object that represents the doujin response + str + reparsed json as string """ if sort not in ["popular-today", "popular-week", "popular"]: @@ -88,71 +68,42 @@ async def search(self, query: str, page: int = 1, sort: str = "popular-today"): "Sort must be one of the following: popular-today, popular-week, popular" ) - self.specs["key"] = query - self.specs["page"] = page - self.specs["sort"] = sort - - data = requests.get(BASE_URL.nhentai + "/search", params=self.specs) - - if len(data.json()["data"]) == 0: - raise ValueError("No results found") - - if data.status_code != 200: - raise ValueError( - "Request failed with status code {}".format(data.status_code) - ) + self.query = query + self.page = page + self.sort = sort + self.req = str(self.query + "&page=" + + str(self.page) + "&sort=" + self.sort) - return better_object(data.json()) + data = await request(Janda.nhentai + Janda.endpoint_search, self.req) + return better_object(data) - async def search_related(self, book: int): - """Get related book API from book ID or book link + async def search_related(self, book: int) -> str: + """Get nhentai related from book ID Parameters ---------- book : int Number id of the book - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The list object that represents the doujin response + str + reparsed json as string """ - self.specs["book"] = book - - data = requests.get(BASE_URL.nhentai + "/related", params=self.specs) - - if data.status_code != 200: - raise ValueError( - "Request failed with status code {}".format(data.status_code) - ) - - return better_object(data.json()) + self.book = str(book) + data = await request(Janda.nhentai + Janda.endpoint_related + self.book) + return better_object(data) - async def get_random(self): - """Get random doujin - Raises - ------ - ValueError - If the doujin is not found. + async def get_random(self) -> str: + """Get nhentai random doujin Returns ------- - dict - The book object that represents the random doujin response. + str + reparsed json as string """ - data = requests.get(BASE_URL.nhentai + "/random", params=self.specs) - - if data.status_code != 200: - raise ValueError( - "Request failed with status code {}".format(data.status_code) - ) - - return better_object(data.json()) + data = await request(Janda.nhentai + Janda.endpoint_random) + return better_object(data) diff --git a/janda/pururin.py b/janda/pururin.py index 5794a86..af4b55d 100644 --- a/janda/pururin.py +++ b/janda/pururin.py @@ -1,55 +1,31 @@ -import requests -import json -from janda.utils.parser import * +from janda.utils.client import * +from janda.utils.request import request -BASE_URL = Api() +Janda = Api() class Pururin(object): - """Pururin API wrapper + """Jandapress Pururin API Methods ------- get: function - Gets doujin from id + Get doujin from id get_random: function - Gets random doujin + Get random doujin search: function Searches doujin by tags / artist / character / parody or group """ - @staticmethod - def better_object(parser: dict): - """Converts the json object to a more readable object. - - Parameters - ---------- - parser : dict - The json object. - """ - return json.dumps(parser, sort_keys=True, indent=4) - - @staticmethod - def auto_space(string: str): - """Automatically adds spaces for GET requests - - Parameters - ---------- - string : str - The string to be formatted. - """ - - return string.replace(" ", "+") - def __init__(self, api_key: str = ""): """Initializes the Pururin. Parameters ---------- api_key : str - scathach.dev API key (optional) + scathach.id API key (optional) """ if api_key == "": self.api_key = None @@ -57,10 +33,10 @@ def __init__(self, api_key: str = ""): self.api_key = api_key self.specs = {"api_key": self.api_key} - async def get(self, book: int): - """Get doujin from id + async def get(self, book: int) -> str: + """Get pururin doujin book from Id - path: https://pururin.to/gallery/61119 + example: https://pururin.to/gallery/61119 Parameters ---------- @@ -74,25 +50,16 @@ async def get(self, book: int): Returns ------- - dict - The book object that represents the specific id response. + str + reparsed json as string """ - self.specs["book"] = book - - try: - book = int(book) - except ValueError: - raise ValueError("Book must be an int") - - data = requests.get(BASE_URL.pururin + "/get", params=self.specs) - if data.json()["data"]["title"] == "": - raise ValueError("No results found") + self.book = str(book) + data = await request(Janda.pururin + Janda.endpoint_book, self.book) + return better_object(data) - return better_object(data.json()) - - async def search(self, query: str, page: int = 1, sort: str = "newest"): - """Search doujin by tags / artist / character / parody or group + async def search(self, query: str, page: int = 1, sort: str = "newest") -> str: + """Search pururin by tags / artist / character / parody or group Parameters ---------- @@ -112,8 +79,8 @@ async def search(self, query: str, page: int = 1, sort: str = "newest"): Returns ------- - dict - The list object that represents the doujin response + str + reparsed json as string """ if sort not in [ @@ -128,30 +95,22 @@ async def search(self, query: str, page: int = 1, sort: str = "newest"): "Sort must be one of newest, most-popular, highest-rated, most-viewed, title, random" ) - self.specs["key"] = query - self.specs["page"] = page - self.specs["sort"] = sort - - data = requests.get(BASE_URL.pururin + "/search", params=self.specs) + self.query = query + self.page = page + self.sort = sort + self.req = str(self.query + "&page=" + + str(self.page) + "&sort=" + self.sort) - if len(data.json()["data"]) == 0: - raise ValueError("No results found") + data = await request(Janda.pururin + Janda.endpoint_search, self.req) + return better_object(data) - if data.status_code != 200: - raise ValueError( - "Request failed with status code {}".format(data.status_code) - ) - - return better_object(data.json()) - - async def get_random(self): - """Gets random doujin on pururin + async def get_random(self) -> str: + """Get pururin random doujin Returns ------- - dict - The book object that represents the random doujin response. + str + reparsed json as string """ - data = requests.get(BASE_URL.pururin + "/random", params=self.specs) - - return better_object(data.json()) + data = await request(Janda.pururin + Janda.endpoint_random) + return better_object(data) diff --git a/janda/simply_hentai.py b/janda/simply_hentai.py index 0c85ddc..2bc047c 100644 --- a/janda/simply_hentai.py +++ b/janda/simply_hentai.py @@ -1,17 +1,16 @@ -import requests -import json -from .utils.parser import * +from janda.utils.client import * +from janda.utils.request import request -BASE_URL = Api() +Janda = Api() class SimplyHentai(object): - """Simply-hentai API wrapper + """Jandapress simply-hentai API Methods ------- get : function - Gets doujin from path given + Get doujin from path given """ def __init__(self, api_key: str = ""): @@ -20,7 +19,7 @@ def __init__(self, api_key: str = ""): Parameters ---------- api_key : str - scathach.dev API key (optional) + scathach.id API key (optional) """ if api_key == "": self.api_key = None @@ -28,32 +27,27 @@ def __init__(self, api_key: str = ""): self.api_key = api_key self.specs = {"api_key": self.api_key} - async def get(self, path: str): - """Gets doujin from path given + async def get(self, path: str) -> str: + """Get simply-hentai doujin from path given - path: https://www.simply-hentai.com/fate-grand-order/perros => 'fate-grand-order/perros' + example: https://www.simply-hentai.com/fate-grand-order/perros => 'fate-grand-order/perros' Parameters ---------- path : str The path url - Raises - ------ - ValueError - If the doujin is not found. - Returns ------- - dict - The book object that represents the specific path response. + str + reparsed json as string """ if str(path).isdigit(): raise ValueError("Invalid path, must be a str") path = path.strip("/") - self.specs["book"] = path + self.book = path try: path = str(path) @@ -61,6 +55,5 @@ async def get(self, path: str): except ValueError or path.isdigit(): raise ValueError("Path must be a str") - data = requests.get(BASE_URL.simply_hentai + "/get", params=self.specs) - - return better_object(data.json()) + data = await request(Janda.simply_hentai + Janda.endpoint_book, self.book) + return better_object(data) diff --git a/janda/thentai.py b/janda/thentai.py new file mode 100644 index 0000000..7fdaf07 --- /dev/null +++ b/janda/thentai.py @@ -0,0 +1,104 @@ +from janda.utils.client import * +from janda.utils.request import request + +Janda = Api() + + +class Thentai(object): + """Jandapress 3hentai API + + Methods + ------- + get: function + Get doujin from id + + get_random: function + Get random doujin + + search: function + Searches doujin by tags / artist / character / parody or group + """ + + def __init__(self, api_key: str = ""): + """Initializes the 3hentai. + + Parameters + ---------- + api_key : str + scathach.id API key (optional) + """ + if api_key == "": + self.api_key = None + else: + self.api_key = api_key + self.specs = {"api_key": self.api_key} + + async def get(self, book: int) -> str: + """Get 3hentai doujin book from Id + + Parameters + ---------- + book : int + The id number of the doujin + + Raises + ------ + ValueError + If the doujin is not found. + + Returns + ------- + str + reparsed json as string + """ + + self.book = str(book) + data = await request(Janda.thentai + Janda.endpoint_book, self.book) + return better_object(data) + + async def search(self, query: str, page: int = 1, sort: str = "recent") -> str: + """Search 3hentai by tags / artist / character / etc + + Parameters + ---------- + query : str + query to search for + + page : int + Page number. Default is 1 + + sort : str + recent, popular-24h, popular-7d, popular + + Returns + ------- + str + reparsed json as string + """ + + if sort not in [ + "recent", "popular-24h", "popular-7d", "popular" + ]: + raise ValueError( + "Sort must be one of these: recent, popular-24h, popular-7d, popular" + ) + + self.query = query + self.page = page + self.sort = sort + self.req = str(self.query + "&page=" + + str(self.page) + "&sort=" + self.sort) + + data = await request(Janda.thentai + Janda.endpoint_search, self.req) + return better_object(data) + + async def get_random(self) -> str: + """Get 3hentai random doujin + + Returns + ------- + str + reparsed json as string + """ + data = await request(Janda.thentai + Janda.endpoint_random) + return better_object(data) diff --git a/janda/utils/parser.py b/janda/utils/client.py similarity index 63% rename from janda/utils/parser.py rename to janda/utils/client.py index 76e9dc1..08fdc50 100644 --- a/janda/utils/parser.py +++ b/janda/utils/client.py @@ -1,6 +1,5 @@ import json -import re -from datetime import datetime +from janda import __version__ JANDA = "https://janda.mod.land" @@ -17,6 +16,13 @@ class Api: simply_hentai (str): The base url of simply-hentai api. qhentai (str): The base url of qhentai api. asmhentai (str): The base url of asmhentai api. + 3hentai (str): The base url of 3hentai api. + header (dict): The header for request. + + endpoint_book (str): The endpoint for get book. + endpoint_search (str): The endpoint for search book. + endpoint_random (str): The endpoint for get random book. + endpoint_related (str): The endpoint for get related book. """ def __init__( @@ -27,6 +33,16 @@ def __init__( BASE_HENTAI2READ: str = f"{JANDA}/hentai2read", BASE_SIMPLY_HENTAI: str = f"{JANDA}/simply-hentai", BASE_ASMHENTAI: str = f"{JANDA}/asmhentai", + BASE_3HENTAI: str = f"{JANDA}/3hentai", + BASE_HEADER: dict = { + "User-Agent": f"janda/v{__version__} (https://pypi.org/project/janda);", + "From": "hey@sinkaroid.org", + }, + BASE_ENDPOINT_BOOK: str = "/get?book=", + BASE_ENDPOINT_SEARCH: str = "/search?key=", + BASE_ENDPOINT_RANDOM: str = "/random", + BASE_ENDPOINT_RELATED: str = "/related?book=", + ): self.nhentai = BASE_NHENTAI self.pururin = BASE_PURURIN @@ -34,6 +50,12 @@ def __init__( self.hentai2read = BASE_HENTAI2READ self.simply_hentai = BASE_SIMPLY_HENTAI self.asmhentai = BASE_ASMHENTAI + self.thentai = BASE_3HENTAI + self.header = BASE_HEADER + self.endpoint_book = BASE_ENDPOINT_BOOK + self.endpoint_search = BASE_ENDPOINT_SEARCH + self.endpoint_random = BASE_ENDPOINT_RANDOM + self.endpoint_related = BASE_ENDPOINT_RELATED BASE_URL = Api() @@ -54,26 +76,11 @@ def list_api(): BASE_URL.hentai2read, BASE_URL.simply_hentai, BASE_URL.asmhentai, + BASE_URL.thentai, ] return api_list -def auto_space(string: str): - """Automatically adds spaces for GET requests - - Parameters - ---------- - string : str - The string to be formatted. - - Returns - ------- - str - - """ - return string.replace(" ", "+") - - def better_object(parser: dict): """Converts the json object to a more readable object. @@ -87,7 +94,7 @@ def better_object(parser: dict): deserialized json as string """ - return json.dumps(parser, sort_keys=True, indent=4, ensure_ascii=False) + return json.dumps(parser, indent=4, ensure_ascii=False) def resolve(b_object: dict) -> dict: diff --git a/janda/utils/request.py b/janda/utils/request.py new file mode 100644 index 0000000..d3dce51 --- /dev/null +++ b/janda/utils/request.py @@ -0,0 +1,32 @@ +import aiohttp +from .client import Api + +Janda = Api() + +async def request(url, params="", timeout=10) -> dict: + """Request to the api + + Parameters + ---------- + url : str + The url to be requested + params : str + The parameters to be requested + timeout : int + The timeout for the request + + Returns + ------- + dict + The response from the api + """ + + async with aiohttp.ClientSession() as session: + async with session.get(url + params, headers=Janda.header, timeout=timeout) as response: + print(response.url) + + try: + data = await response.json() + except: + data = await response.text() + return data diff --git a/requirements.txt b/requirements.txt index 663bd1f..25b7e73 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -requests \ No newline at end of file +aiohttp==3.8.3 \ No newline at end of file diff --git a/setup.py b/setup.py index 17dee20..e7edb19 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ project_urls={ "Documentation": "https://sinkaroid.github.io/janda", "Issue tracker": "https://github.com/sinkaroid/janda/issues/new/choose", - "Funding": "https://paypal.me/sinkaroid", + "Funding": "https://github.com/sponsors/sinkaroid", "Discord": "https://discord.gg/8wj4vM5hHM", }, packages=['janda', 'janda.utils'], @@ -52,9 +52,9 @@ "Topic :: Software Development :: Build Tools", ], - description='A featureful Python library covers most popular doujin API', + description='Python library for Jandapress, doujinshi api', include_package_data=True, keywords=['doujinshi', 'library', 'hentai', 'api', 'nhentai', - 'pururin', 'hentaifox', 'hentai2read', 'simply-hentai', 'qhentai', 'asmhentai'], + 'pururin', 'hentaifox', 'hentai2read', 'simply-hentai', '3hentai', 'asmhentai'], install_requires=requirements ) diff --git a/test/test_api.py b/test/test_api.py index d7bce06..0a296df 100644 --- a/test/test_api.py +++ b/test/test_api.py @@ -1,7 +1,19 @@ -import requests -from janda.utils.parser import list_api +import aiohttp +import asyncio +from janda.utils.client import list_api -for api in list_api(): - r = requests.get(api) - print(api, r.status_code) - \ No newline at end of file +async def test_api(): + async with aiohttp.ClientSession() as session: + for api in list_api(): + if "nhen" in api: + get_random = api + "/get?book=177013" + elif "hentai2read" in api: + get_random = api + "/search?key=futanari" + elif "simply-hentai" in api: + get_random = api + "/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages" + else: + get_random = api + "/random" + async with session.get(get_random) as resp: + print(get_random, resp.status) + +asyncio.run(test_api()) \ No newline at end of file diff --git a/test/test_thentai.py b/test/test_thentai.py new file mode 100644 index 0000000..ef2d77d --- /dev/null +++ b/test/test_thentai.py @@ -0,0 +1,13 @@ +import asyncio +from janda import Thentai + +thentai = Thentai() + +async def get(): + data = await thentai.get(608979) + print(data) + +async def main(): + await asyncio.gather(get()) + +asyncio.run(main())