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 @@

-
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
Contributing •
Documentation •
Report 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 | [](https://github.com/sinkaroid/janda/actions/workflows/nhentai.yml) | `Yes` | `Yes` | `Yes` |
| pururin | [](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 | [](https://github.com/sinkaroid/janda/actions/workflows/hentai2read.yml) | `Yes` | `Yes` | `No` |
| simply-hentai | [](https://github.com/sinkaroid/janda/actions/workflows/simplyh.yml) | `Yes` | `No` | `No` |
| asmhentai | [](https://github.com/sinkaroid/janda/actions/workflows/asmhentai.yml) | `Yes` | `Yes` | `Yes` |
+| 3hentai | [](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())