コミットを比較

...

共通のコミットはありません。 "master" と "cookie" の履歴はすべて異なっています。

79個のファイルの変更311行の追加3089行の削除

ファイルの表示

@ -1,2 +0,0 @@
node_modules
npm-debug.log

1
.env ノーマルファイル
ファイルの表示

@ -0,0 +1 @@
CF=cf_clearance=XZoRJIDGsHrMCuS3rFIH9jCLhoYmBHzUNs6dfVJwNqg-1654563641-0-150

ファイルの表示

@ -1,22 +0,0 @@
# railway, fly.dev, heroku, vercel or any free service, NHENTAI_IP_ORIGIN should be true
RAILWAY = sinkaroid
# default port
PORT = 3000
# backend storage, default is redis with this format
REDIS_URL = redis://default:somenicepassword@redis-666.c10.us-east-6-6.ec666.cloud.redislabs.com:1337
# ttl expire cache (in X hour)
EXPIRE_CACHE = 1
# nhentai strategy
# default is true which is assign to request on origin IP instead of nhentai.net with cloudflare protection
# if you have paid instance like vps you need chromium or firefox installed and set NHENTAI_IP_ORIGIN to false
NHENTAI_IP_ORIGIN = true
# you must set COOKIE if NHENTAI_IP_ORIGIN is false, read the jandapress docs
COOKIE = "cf_clearance=l7RsUjiZ3LHAZZKcM7BcCylwD2agwPDU7l9zkg8MzPo-1676044652-0-250"
# you must set USER_AGENT if NHENTAI_IP_ORIGIN is false, read the jandapress docs
USER_AGENT = "jandapress/1.0.5 Node.js/16.9.1"

ファイルの表示

@ -1,2 +0,0 @@
node_modules/
build/

1
.github/FUNDING.yml vendored
ファイルの表示

@ -1 +0,0 @@
github: sinkaroid

ファイルの表示

@ -1,25 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: bug
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Expected behavior**
A clear and concise description of what you expected to happen.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Node version [node -v]
- Typescript version [tsc -v]
**Additional context**
Add any other context about the problem here.

ファイルの表示

@ -1,23 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Describe new features you want**
A clear and concise description of what new features you want.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
**A Gif to describe how you feel when you made this request**
Add a gif here to express how you feel when you made this request.

ファイルの表示

@ -1,30 +0,0 @@
name: 3hentai test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: 3hentai test
run: npm run test:3hentai

ファイルの表示

@ -1,30 +0,0 @@
name: Asmhentai test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Asmhentai test
run: npm run test:asmhentai

ファイルの表示

@ -1,31 +0,0 @@
name: Run build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Check lint
run: npm run lint
- name: Build
run: npm run build

ファイルの表示

@ -1,27 +0,0 @@
name: Docker
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Log into GitHub Container Registry
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Get the package.json version
id: package-version
run: echo ::set-output name=version::$(jq -r .version package.json)
- name: Build the Docker image
run: docker build . --file Dockerfile --tag ghcr.io/${{ github.repository }}:${{ steps.package-version.outputs.version }}
- name: Tag the Docker image
run: docker tag ghcr.io/${{ github.repository }}:${{ steps.package-version.outputs.version }} ghcr.io/${{ github.repository }}:latest
- name: Push the Docker image
run: docker push ghcr.io/${{ github.repository }} --all-tags

ファイルの表示

@ -1,30 +0,0 @@
name: Hentai2read test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Hentai2read test
run: npm run test:hentai2read

ファイルの表示

@ -1,30 +0,0 @@
name: Hentaifox test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Hentaifox test
run: npm run test:hentaifox

ファイルの表示

@ -1,30 +0,0 @@
name: Nhentai test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Nhentai test
run: npm run test:nhentai

ファイルの表示

@ -1,30 +0,0 @@
name: Nhentaito test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Nhentaito test
run: npm run test:nhentaito

ファイルの表示

@ -1,26 +0,0 @@
name: Playground
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build-and-deploy:
concurrency: ci-${{ github.ref }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Installing dependencies
run: npm install
- name: Generating docs
run: npm run build:apidoc
- name: Deploy
uses: JamesIves/github-pages-deploy-action@v4.4.1
with:
branch: gh-pages
folder: docs

ファイルの表示

@ -1,30 +0,0 @@
name: Pururin test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Pururin test
run: npm run test:pururin

ファイルの表示

@ -1,30 +0,0 @@
name: Simply-hentai test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Simply-hentai test
run: npm run test:simply-hentai

ファイルの表示

@ -1,29 +0,0 @@
name: Check status
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Check status code
run: npm run test

8
.gitignore vendored
ファイルの表示

@ -2,10 +2,4 @@
yarn.lock
/build
/playground
/template
/docs
p.ts
.env
.idea
CHANGELOG.md
theme.zip
p.ts

ファイルの表示

@ -1,3 +0,0 @@
{
"deepscan.enable": true
}

ファイルの表示

@ -1,24 +0,0 @@
# Closing remarks
I hope you have found this project useful. All the major credit really goes to the all the doujin sites, which
allows this services to operate.
- [nhentai](https://nhentai.net)
- [pururin](https://pururin.to)
- [hentaifox](https://hentaifox.com)
- [hentai2read](https://hentai2read.com)
- [asmhentai](https://asmhentai.com)
- [simply-hentai](https://simply-hentai.com)
- [3hentai](http://3hentai.net)
- [nhentai.to](https://nhentai.to)
Major dependencies:
- [express](https://github.com/expressjs/express)
- [cheerio](https://cheerio.js.org/)
- [keyv](https://github.com/jaredwray/keyv)
# Alternative-links
Just in case if https://janda.mod.land down, here some alternative deployment
- https://janda.sinkaroid.org
- https://janda.fly.dev

ファイルの表示

@ -1,76 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at anakmancasan@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

ファイルの表示

@ -1,10 +0,0 @@
# Contributing
## Making major changes
Please first discuss the change you wish to make via issue,
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 however. I always happy to receive Pull requests to improve things.

ファイルの表示

@ -1,9 +0,0 @@
FROM node:latest
WORKDIR /srv/app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "build/src/index.js"]

1
Procfile ノーマルファイル
ファイルの表示

@ -0,0 +1 @@
web: npm start

323
README.md
ファイルの表示

@ -1,323 +0,0 @@
<div align="center">
<a href="https://janda.sinkaroid.org"><img width="500" src="https://cdn.discordapp.com/attachments/1046495201176334467/1055678255866318898/tomoe-janda.png" alt="jandapress"></a>
<h4 align="center">RESTful and experimental API for the doujinboards</h4>
<p align="center">
<a href="https://github.com/sinkaroid/jandapress/actions/workflows/playground.yml"><img src="https://github.com/sinkaroid/jandapress/workflows/Playground/badge.svg"></a>
<a href="https://codeclimate.com/github/sinkaroid/jandapress/maintainability"><img src="https://api.codeclimate.com/v1/badges/829b8fe63ab78a425f0b/maintainability" /></a>
</p>
Jandapress was named **JCE** (Janda Cheerio Express) and definitely depends on them.
The motivation of this project is to bring you an actionable data related doujin with gather in mind.
<a href="https://sinkaroid.github.io/jandapress">Playground</a>
<a href="https://github.com/sinkaroid/jandapress/blob/master/CONTRIBUTING.md">Contributing</a>
<a href="https://github.com/sinkaroid/jandapress/issues/new/choose">Report Issues</a>
</div>
---
<a href="https://janda.sinkaroid.org"><img align="right" src="https://cdn.discordapp.com/attachments/952117487166705747/986315079802814524/tomoe.png" width="300"></a>
- [Jandapress](#)
- [The problem](#the-problem)
- [The solution](#the-solution)
- [Features](#features)
- [Jandapress vs. the doujinboards](#jandapress-vs-the-whole-doujin-sites)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Docker](#docker)
- [Manual](#manual)
- [Running tests](#running-tests)
- [Nhentai Guide](#nhentai-guide)
- [Playground](https://sinkaroid.github.io/jandapress)
- [Routing](#playground)
- [Status response](#status-response)
- [CLosing remarks](https://github.com/sinkaroid/jandapress/blob/master/CLOSING_REMARKS.md)
- [Alternative links](https://github.com/sinkaroid/jandapress/blob/master/CLOSING_REMARKS.md#alternative-links)
- [Pronunciation](#Pronunciation)
- [Client libraries / Wrappers](#client-libraries--wrappers)
- [Legal](#legal)
## The problem
You enjoy consume doujin sites to build web applications. There are a lot sites that have effort especially pururin, simply-hentai and etc, not official api available nor public resource that can be used for everyone. Instead making lot of abstraction and enumerating them manually, You can rely on jandapress to make less of pain. The current state is FREE to use, meant all anonymous usage is allowed no aunthentication required and CORS was enabled.
## The solution
<a href="https://github.com/sinkaroid/jandapress/wiki/Routing"><img src="https://cdn.discordapp.com/attachments/1082449595033997434/1107863120275320852/jandapressflow_1.png" width="800"></a>
## Features
- Gather the most doujin sites
- Objects taken that are consistent structure, almost
- Objects taken is re-appended to make extendable
- All in one: get, search, and random methods
- In the future we may implement JWT authentication
- Pure scraping, except nh sigh..
## Jandapress vs. the whole doujin sites
**Features availability** that Jandapress has
| Site | Status | Get | Search | Random |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | ------ | ------ |
| `nhentai` | [![Nhentai](https://github.com/sinkaroid/jandapress/workflows/Nhentai%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/nhentai.yml) | ✅ | ✅ | ✅ |
| `pururin` | [![Pururin](https://github.com/sinkaroid/jandapress/workflows/Pururin%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/pururin.yml) | ✅ | ✅ | ✅ |
| `hentaifox` | [![Hentaifox](https://github.com/sinkaroid/jandapress/workflows/Hentaifox%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/hentaifox.yml) | ✅ | ✅ | ✅ |
| `hentai2read` | [![Hentai2read](https://github.com/sinkaroid/jandapress/workflows/Hentai2read%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/hentai2read.yml) | ✅ | ✅ | ❌ |
| `simply-hentai` | [![Simply-hentai](https://github.com/sinkaroid/jandapress/workflows/Simply-hentai%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/simply-hentai.yml) | ✅ | ❌ | ❌ |
| `asmhentai` | [![Asmhentai](https://github.com/sinkaroid/jandapress/workflows/Asmhentai%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/asmhentai.yml) | ✅ | ✅ | ✅ |
| `3hentai` | [![Asmhentai](https://github.com/sinkaroid/jandapress/workflows/3hentai%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/3hentai.yml) | ✅ | ✅ | ✅ |
| `nhentai.to` | [![Nhentaito](https://github.com/sinkaroid/jandapress/workflows/Nhentaito%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/nhentaito.yml) | ✅ | ✅ | ✅ |
## Prerequisites
<table>
<td><b>NOTE:</b> NodeJS 16.x or higher</td>
</table>
To handle several requests from each web, You will also need [Redis](https://redis.io/) for persistent caching, free tier is available on [Redis Labs](https://redislabs.com/), You can also choose another provider as we using [keyv](https://github.com/jaredwray/keyv) Key-value storage with support for multiple backends. All data must be stored in `<Buffer>` here.
## Installation
Rename `.env.schema` to `.env` and fill the value with your own
```bash
# railway, fly.dev, heroku, vercel or any free service, NHENTAI_IP_ORIGIN should be true
RAILWAY = sinkaroid
# default port
PORT = 3000
# backend storage, default is redis, if not set it will consume memory storage
REDIS_URL = redis://default:somenicepassword@redis-666.c10.us-east-6-6.ec666.cloud.redislabs.com:1337
# ttl expire cache (in X hour)
EXPIRE_CACHE = 1
# nhentai strategy
# default is true which is assign to request on IP instead of nhentai.net with cloudflare
# if you have instance like vps you need chromium or firefox installed and set it to false
NHENTAI_IP_ORIGIN = true
# you must set COOKIE if NHENTAI_IP_ORIGIN is false, read the jandapress docs
COOKIE = "cf_clearance=l7RsUjiZ3LHAZZKcM7BcCylwD2agwPDU7l9zkg8MzPo-1676044652-0-250"
# you must set USER_AGENT if NHENTAI_IP_ORIGIN is false, read the jandapress docs
USER_AGENT = "jandapress/1.0.5 Node.js/16.9.1"
```
### Docker
docker pull ghcr.io/sinkaroid/jandapress:latest
docker run -p 3000:3000 -d ghcr.io/sinkaroid/jandapress:latest
### Docker (your own)
```bash
docker run -d \
--name=jandapress \
-p 3000:3000 \
-e REDIS_URL='redis://default:somenicepassword@redis-666.c10.us-east-6-6.ec666.cloud.redislabs.com:1337' \
-e EXPIRE_CACHE='1' \
-e NHENTAI_IP_ORIGIN='false' \
-e COOKIE='cf_clearance=AbcDefGhijY7RYSKv3YeJUjrI5xQ2Uc-666-0-250' \
-e USER_AGENT='jandapress/1.0.5 Node.js/16.9.1' \
ghcr.io/sinkaroid/jandapress:latest
```
### Manual
git clone https://github.com/sinkaroid/jandapress.git
- Install dependencies
- `npm install / yarn install`
- Jandapress production
- `npm run start:prod`
- Jandapress testing and hot reload
- `npm run start:dev`
## Nhentai Guide
### The problem
https://nhentai.net was Clouflare protection enabled, for default jandapress use [real IP address to bypass the protection](https://github.com/sinkaroid/jandapress/blob/master/src/utils/options.ts#L7..L10), but **sometimes** even it's from IP address the `/api` path return error that means admins or their maintainer don't allow us to request from the IP address.
![image](https://cdn.discordapp.com/attachments/952117487166705747/1073694957111627906/Screenshot_265.png)
### The solution
You will need instance such as VPS and install Chrome or Chromium or Firefox, You have to set `NHENTAI_IP_ORIGIN` to `false`, set `COOKIE` and `USER_AGENT`. We'll simulate the request with [tough-cookie](https://github.com/salesforce/tough-cookie) and [http-cookie-agent](https://www.npmjs.com/package/http-cookie-agent)
![image](https://cdn.discordapp.com/attachments/952117487166705747/1073699069643468902/Screenshot_267_copy.jpg)
- set `NHENTAI_IP_ORIGIN` to `false` in `.env` file
- open browser and go to https://nhentai.net
- verify you are human
- open devtools and set custom user agent
- reload the page and wait cloudflare again
- open devtools and go to network tab and request
- get the `cf_clearance` value and set it to `COOKIE` in `.env` file
- set the user agent to `USER_AGENT` in `.env` file
- test that your cookie is working `npm run test:cf`
- it should return 200 status code otherwise watch your step
[The documentation](https://developers.cloudflare.com/fundamentals/get-started/reference/cloudflare-cookies/#:~:text=This%20cookie%20expires%20after%2030,Bot%20Management%2C%20a%20session%20identifier.) said and correct me if I'm wrong:
> This cookie expires after 30 minutes of continuous inactivity by the end user. The cookie contains information related to the calculation of Cloudflares proprietary bot score and, when Anomaly Detection is enabled on Bot Management, a session identifier.
└── https://developers.cloudflare.com/fundamentals
You will need to make your cookie is not expired otherwise manual update is required, it can be with set interval or cron job to automate your request.
## Running tests
Jandapress testing
### Start the production server
`npm run start:prod`
### Running development server
`npm run start:dev`
### Check the whole sites, It's available for scraping or not
`npm run test`
### Check nhentai It's under cloudflare protection or not
`npm run test:cf`
### Generating playground like swagger from apidoc definition
`npm run build:apidoc`
> To running other tests, you can see object scripts in file `package.json`
## Playground
https://sinkaroid.github.io/jandapress
- These `parameter?`: means is optional
- `/` : index page
### Nhentai
The missing piece of nhentai.net - https://sinkaroid.github.io/jandapress/#api-nhentai
- `/nhentai` : nhentai api
- **get**, takes parameters : `book`
- **search**, takes parameters : `key`, `?page`, `?sort`
- **related**, takes parameters : `book`
- **random**
- <u>sort parameters on search</u>
- "popular-today", "popular-week", "popular"
- Example
- https://janda.sinkaroid.org/nhentai/get?book=177013
- https://janda.sinkaroid.org/nhentai/search?key=futanari
- https://janda.sinkaroid.org/nhentai/search?key=futanari&page=2&sort=popular-today
- https://janda.sinkaroid.org/nhentai/related?book=177013
- https://janda.sinkaroid.org/nhentai/random
### Pururin
The missing piece of pururin.to - https://sinkaroid.github.io/jandapress/#api-pururin
- `/pururin` : pururin api
- **get**, takes parameters : `book`
- **search**, takes parameters : `key`, `?page`
- **random**
- Example
- https://janda.sinkaroid.org/pururin/get?book=63373
- https://janda.sinkaroid.org/pururin/search?key=futanari
- https://janda.sinkaroid.org/pururin/random
### Hentaifox
The missing piece of hentaifox.com - https://sinkaroid.github.io/jandapress/#api-hentaifox
- `/hentaifox`: hentaifox api
- **get**, takes parameters : `book`
- **search**, takes parameters : `key`, `?page`, `?sort`
- **random**
- <u>sort parameters on search</u>
- "latest", "popular"
- Example
- https://janda.sinkaroid.org/hentaifox/get?book=97527
- https://janda.sinkaroid.org/hentaifox/search?key=milf
- https://janda.sinkaroid.org/hentaifox/search?key=milf&page=2&sort=latest
- https://janda.sinkaroid.org/hentaifox/random
### Asmhentai
The missing piece of asmhentai.com - https://sinkaroid.github.io/jandapress/#api-asmhentai
- `/asmhentai`: asmhentai api
- **get**, takes parameters : `book`
- **search**, takes parameters : `key`, `?page`
- **random**
- <u>sort parameters on search</u>
- None
- Example
- https://janda.sinkaroid.org/asmhentai/get?book=416773
- https://janda.sinkaroid.org/asmhentai/search?key=futanari
- https://janda.sinkaroid.org/asmhentai/search?key=futanari&page=2
- https://janda.sinkaroid.org/asmhentai/random
### Hentai2read
The missing piece of hentai2read.com - https://sinkaroid.github.io/jandapress/#api-hentai2read
- `/hentai2read`: hentai2read api
- **get**, takes parameters : `book`
- **search**, takes parameters : `key`
- <u>sort parameters on search</u>
- TBA
- Example
- https://janda.sinkaroid.org/hentai2read/get?book=butabako_shotaone_matome_fgo_hen/1
- https://janda.sinkaroid.org/hentai2read/search?key=futanari
### Simply-hentai
The missing piece of simply-hentai.com - https://sinkaroid.github.io/jandapress/#api-simply-hentai
- `/simply-hentai`: simply-hentai api
- **get**, takes parameters : `book`
- <u>sort parameters on search</u>
- TBA
- Example
- https://janda.sinkaroid.org/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages
### 3hentai
The missing piece of 3hentai.net - https://sinkaroid.github.io/jandapress/#api-3hentai
- `/3hentai`: 3hentai api
- **get**, takes parameters : `book`
- **search**, takes parameters : `key`, `?page`, `?sort`
- **random**
- <u>sort parameters on search</u>
- "recent", "popular-24h", "popular-7d", "popular"
- Example
- https://janda.sinkaroid.org/3hentai/get?book=608979
- https://janda.sinkaroid.org/3hentai/search?key=futanari
- https://janda.sinkaroid.org/3hentai/search?key=futanari&page=2&sort=popular-7d
- https://janda.sinkaroid.org/3hentai/random
### Nhentai.to
The missing piece of nhentai.to - https://sinkaroid.github.io/jandapress/#api-nhentaito
- `/nhentaito`: nhentaito api
- **get**, takes parameters : `book`
- **search**, takes parameters : `key`, `?page`
- **related**, takes parameters : `book`
- **random**
- <u>sort parameters on search</u>
- None
- Example
- https://janda.sinkaroid.org/nhentaito/get?book=272
- https://janda.sinkaroid.org/nhentaito/search?key=futanari
- https://janda.sinkaroid.org/nhentaito/search?key=futanari&page=2
- https://janda.sinkaroid.org/nhentaito/related?book=272
- https://janda.sinkaroid.org/nhentaito/random
## Status response
`"success": true,` or `"success": false,`
HTTP/1.1 200 OK
HTTP/1.1 400 Bad Request
HTTP/1.1 500 Fail to get data
## Frequently asked questions
**Q: The website response is slow**
> That's unfortunate, This repository was opensource already, You can host and deploy Jandapress with your own instance. Any fixes and improvements will updating to this repo.
**Q: I dont want to host my own instance**
> That's unfortunate, Hit the "Sponsor this project" button, any kind of donations will helps me to funding the development.
## Pronunciation
[`id_ID`](https://www.localeplanet.com/java/id-ID/index.html) • **/jan·da/** — Dewasa dan mengikat; _(?)_
## Client libraries / Wrappers
Seamlessly integrate with the languages you love, simplified the usage, and intelisense definitions on your IDEs
- [janda](https://github.com/sinkaroid/janda) Python wrapper by [sinkaroid](https://github.com/sinkaroid)
- Or [create your own](https://github.com/sinkaroid/jandapress/edit/master/README.md)
## 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.

ファイルの表示

@ -1,5 +0,0 @@
# Jandapress Security
## Reporting vulnerabilities
To report sensitive vulnerabilities, alert the author by email at anakmancasan@gmail.com.

ファイルの表示

@ -1,76 +1,49 @@
{
"name": "jandapress",
"version": "3.8.2-alpha",
"description": "RESTful and experimental API for the Doujinshi, Pressing the whole nhentai, pururin, hentaifox, and more.. where the official one is lack.",
"name": "jandapress-cookie",
"version": "0.0.1",
"description": "Experimental doujin API with gather in mind",
"main": "build/src/index.js",
"scripts": {
"build": "rimraf build && tsc",
"build": "rm -rf build && tsc",
"start": "node build/src/index.js",
"test": "ts-node test/test.ts",
"test:cf": "ts-node test/nhentaiCookietest.ts",
"start:prod": "npm run build && node build/src/index.js",
"start:dev": "ts-node-dev src/index.ts",
"start:dev": "nodemon src/index.ts",
"lint": "npx eslint . --ext .ts",
"lint:fix": "npx eslint . --fix",
"test:mock": "ts-node test/mock.ts",
"build:freshdoc": "rimraf docs && rimraf template && rimraf theme.zip",
"build:template": " npm run build:freshdoc && curl https://codeload.github.com/ScathachGrip/apidocjs-theme/zip/refs/tags/v9 -o theme.zip && unzip theme.zip && mv apidocjs-theme-9 template",
"build:apidoc": "npm run build:template && npx apidoc -i src -o ./docs -t ./template/template-marine",
"test:nhentai": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/nhentai/get?book=177013 | jq '.'\"",
"test:pururin": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/pururin/get?book=47226 | jq '.'\"",
"test:hentaifox": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/hentaifox/get?book=59026 | jq '.'\"",
"test:asmhentai": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/asmhentai/get?book=308830 | jq '.'\"",
"test:hentai2read": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/hentai2read/get?book=butabako_shotaone_matome_fgo_hen/1 | jq '.'\"",
"test:simply-hentai": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages | jq '.'\"",
"test:3hentai": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/3hentai/get?book=608979 | jq '.'\"",
"test:nhentaito": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/nhentaito/get?book=272 | jq '.'\""
},
"apidoc": {
"title": "Jandapress API Documentation",
"url": "https://janda.sinkaroid.org",
"sampleUrl": "https://janda.sinkaroid.org",
"name": "Jandapress"
"postinstall": "npm run build"
},
"keywords": [],
"author": "sinkaroid",
"repository": {
"type": "git",
"url": "git+https://github.com/sinkaroid/jandapress.git"
},
"license": "MIT",
"dependencies": {
"@keyv/redis": "^2.5.4",
"cheerio": "^1.0.0-rc.11",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"express-rate-limit": "^6.4.0",
"express-slow-down": "^1.4.0",
"http-cookie-agent": "^5.0.2",
"keyv": "^4.5.2",
"http-cookie-agent": "^4.0.1",
"phin": "^3.6.1",
"pino": "^8.7.0",
"pino-pretty": "^9.2.0",
"tough-cookie": "^4.1.2"
"pino": "^7.11.0",
"pino-pretty": "^8.0.0",
"tough-cookie": "^4.0.0"
},
"devDependencies": {
"@types/cors": "^2.8.12",
"@types/express": "^4.17.14",
"@types/express-rate-limit": "^6.0.0",
"@types/express-slow-down": "^1.3.2",
"@types/node": "^18.11.13",
"@types/node": "^14.14.37",
"@types/tough-cookie": "^4.0.2",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"apidoc": "^0.29.0",
"eslint": "^8.29.0",
"dotenv": "^16.0.1",
"eslint": "^7.32.0",
"nhentai-api": "^3.4.3",
"nodemon": "^2.0.15",
"npx": "^10.2.2",
"rimraf": "^4.1.2",
"start-server-and-test": "^1.14.0",
"ts-node": "^10.8.1",
"ts-node-dev": "^2.0.0",
"typescript": "4.9.4"
"typescript": "4.6.3"
},
"engines": {
"node": ">=14"
"node": ">=16.9.1"
}
}

ファイルの表示

@ -1,157 +0,0 @@
import p, { IResponse } from "phin";
import Keyv from "keyv";
import { CookieJar } from "tough-cookie";
import { HttpsCookieAgent } from "http-cookie-agent/http";
const keyv = new Keyv(process.env.REDIS_URL);
const strategy = process.env.NHENTAI_IP_ORIGIN || "true";
keyv.on("error", err => console.log("Connection Error", err));
const ttl = 1000 * 60 * 60 * Number(process.env.EXPIRE_CACHE);
class JandaPress {
url: string;
useragent: string;
constructor() {
this.url = "";
this.useragent = process.env.USER_AGENT || "jandapress/1.0.5 Node.js/16.9.1";
}
async simulateCookie(target: string, parseJson = false): Promise<p.IResponse | unknown> {
const jar = new CookieJar();
jar.setCookie(process.env.COOKIE || "", "https://nhentai.net/").catch(err => console.log(err.message));
if (!parseJson) {
const res = await p({
url: target,
followRedirects: true,
core: {
agent: new HttpsCookieAgent({ cookies: { jar, }, }),
},
headers: {
"User-Agent": this.useragent,
},
});
return res;
} else {
const res = await p({
url: target,
parse: "json",
core: {
agent: new HttpsCookieAgent({ cookies: { jar, }, }),
},
headers: {
"User-Agent": this.useragent,
},
});
return res.body;
}
}
/**
* Simulating nhentai request if origin api is not available
* You'll need [tough-cookie](https://www.npmjs.com/package/tough-cookie) and [http-cookie-agent](https://www.npmjs.com/package/http-cookie-agent) to make this work
* @param target url to fetch
* @returns Promise<unknown>
* @throws Error
*/
async simulateNhentaiRequest(target: string): Promise<unknown> {
if (strategy === "true") {
const res = await p({
url: target,
parse: "json"
});
return res.body;
} else {
try {
const res = await this.simulateCookie(target, true);
return res;
} catch (err) {
const e = err as Error;
throw new Error(e.message);
}
}
}
/**
* Fetch body from url and check if it's cached
* @param url url to fetch
* @returns Buffer
*/
async fetchBody(url: string): Promise<Buffer> {
const cached = await keyv.get(url);
if (cached) {
console.log("Fetching from cache");
return cached;
} else if (url.includes("/random")) {
console.log("Random should not be cached");
const res = await p({
url: url,
headers: {
"User-Agent": this.useragent,
},
followRedirects: true });
return res.body;
} else {
console.log("Fetching from source");
const res = await p({
url: url,
headers: {
"User-Agent": this.useragent,
},
followRedirects: true
});
await keyv.set(url, res.body, ttl);
return res.body;
}
}
/**
* Fetch json from url and check if it's cached
* @param url url to fetch
* @returns Buffer
*/
async fetchJson(url: string): Promise<unknown> {
const cached = await keyv.get(url);
if (cached) {
console.log("Fetching from cache");
return cached;
} else {
console.log("Fetching from source");
const res = await this.simulateNhentaiRequest(url);
await keyv.set(url, res, ttl);
return res;
}
}
currentProccess() {
const arr = [1, 2, 3, 4, 5, 6, 9, 7, 8, 9, 10];
arr.reverse();
const rss = process.memoryUsage().rss / 1024 / 1024;
const heap = process.memoryUsage().heapUsed / 1024 / 1024;
const heaptotal = process.memoryUsage().heapTotal / 1024 / 1024;
return {
rss: `${Math.round(rss * 100) / 100} MB`,
heap: `${Math.round(heap * 100) / 100}/${Math.round(heaptotal * 100) / 100} MB`
};
}
async getServer(): Promise<string> {
const raw = await p({
"url": "http://ip-api.com/json",
"parse": "json"
}) as IResponse;
const data = raw.body as unknown as { country: string, regionName: string };
return `${data.country}, ${data.regionName}`;
}
}
export default JandaPress;

ファイルの表示

@ -1,56 +0,0 @@
import { scrapeContent } from "../../scraper/3hentai/3hentaiGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { isNumeric, maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function get3hentai(req: Request, res: Response) {
try {
const book = req.query.book as string;
if (!book) throw Error("Parameter book is required");
if (!isNumeric(book)) throw Error("Value must be number");
/**
* @api {get} /3hentai/get?book=:book Get 3hentai
* @apiName Get 3hentai
* @apiGroup 3hentai
* @apiDescription Get a doujinshi on 3hentai based on id
*
* @apiParam {Number} book Book ID
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/3hentai/get?book=123
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/3hentai/get?book=123")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/3hentai/get?book=123") as resp:
* print(await resp.json())
*/
const url = `${c.THREEHENTAI}/d/${book}`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,51 +0,0 @@
import { scrapeContent } from "../../scraper/3hentai/3hentaiGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { Request, Response } from "express";
import { maybeError } from "../../utils/modifier";
export async function random3hentai(req: Request, res: Response) {
try {
/**
* @api {get} /3hentai/random Random 3hentai
* @apiName Random 3hentai
* @apiGroup 3hentai
* @apiDescription Gets random doujinshi on 3hentai
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/3hentai/random
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/3hentai/random")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/3hentai/random") as resp:
* print(await resp.json())
*
*/
const url = `${c.THREEHENTAI}/random`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,61 +0,0 @@
import { scrapeContent } from "../../scraper/3hentai/3hentaiSearchController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
const sorting = ["recent", "popular-24h", "popular-7d", "popular"];
export async function search3hentai(req: Request, res: Response) {
try {
const key = req.query.key || "";
const page = req.query.page || 1;
const sort = req.query.sort as string || sorting[0] as string;
if (!key) throw Error("Parameter key is required");
if (!sorting.includes(sort)) throw Error("Invalid sort: " + sorting.join(", "));
/**
* @api {get} /3hentai/search Search 3hentai
* @apiName Search 3hentai
* @apiGroup 3hentai
* @apiDescription Search doujinshi on 3hentai
* @apiParam {String} key Keyword to search
* @apiParam {Number} [page=1] Page number
* @apiParam {String} [sort=recent]
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/3hentai/search?key=yuri
* curl -i https://janda.sinkaroid.org/3hentai/search?key=yuri&page=2&sort=recent
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/3hentai/search?key=yuri")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/3hentai/search?key=yuri") as resp:
* print(await resp.json())
*/
const url = `${c.THREEHENTAI}/search?q=${key}&page=${page}&sort=${sort}`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,56 +0,0 @@
import { scrapeContent } from "../../scraper/asmhentai/asmhentaiGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { isNumeric, maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function getAsmhentai(req: Request, res: Response) {
try {
const book = req.query.book as string;
if (!book) throw Error("Parameter book is required");
if (!isNumeric(book)) throw Error("Value must be number");
/**
* @api {get} /asmhentai/get?book=:book Get asmhentai
* @apiName Get asmhentai
* @apiGroup asmhentai
* @apiDescription Get a doujinshi on asmhentai based on id
*
* @apiParam {Number} book Book ID
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/asmhentai/get?book=123
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/asmhentai/get?book=123")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/asmhentai/get?book=123") as resp:
* print(await resp.json())
*/
const url = `${c.ASMHENTAI}/g/${book}/`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,51 +0,0 @@
import { scrapeContent } from "../../scraper/asmhentai/asmhentaiGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function randomAsmhentai(req: Request, res: Response) {
try {
/**
* @api {get} /asmhentai/random Random asmhentai
* @apiName Random asmhentai
* @apiGroup asmhentai
* @apiDescription Gets random doujinshi on asmhentai
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/asmhentai/random
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/asmhentai/random")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/asmhentai/random") as resp:
* print(await resp.json())
*
*/
const url = `${c.ASMHENTAI}/random/`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,57 +0,0 @@
import { scrapeContent } from "../../scraper/asmhentai/asmhentaiSearchController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function searchAsmhentai(req: Request, res: Response) {
try {
const key = req.query.key || "";
const page = req.query.page || 1;
if (!key) throw Error("Parameter key is required");
/**
* @api {get} /asmhentai/search Search asmhentai
* @apiName Search asmhentai
* @apiGroup asmhentai
* @apiDescription Search doujinshi on asmhentai
* @apiParam {String} key Keyword to search
* @apiParam {Number} [page=1] Page number
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/asmhentai/search?key=yuri
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/asmhentai/search?key=yuri")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/asmhentai/search?key=yuri") as resp:
* print(await resp.json())
*/
const url = `${c.ASMHENTAI}/search/?q=${key}&page=${page}`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,44 +1,12 @@
import { scrapeContent } from "../../scraper/hentai2read/hentai2readGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function getHentai2read(req: Request, res: Response) {
export async function getHentai2read(req: any, res: any) {
try {
const book = req.query.book as string;
const book = req.query.book || "";
if (!book) throw Error("Parameter book is required");
if (book.split("/").length !== 2) throw Error("Book must be in format 'book_example/chapter'. Example: 'fate_lewd_summoning/1'");
/**
* @api {get} /hentai2read/get?book=:book Get hentai2read
* @apiName Get hentai2read
* @apiGroup hentai2read
* @apiDescription Get a doujinshi on hentai2read
*
* @apiParam {String} book Book path
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/hentai2read/get?book=butabako_shotaone_matome_fgo_hen/1
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/hentai2read/get?book=butabako_shotaone_matome_fgo_hen/1")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/hentai2read/get?book=butabako_shotaone_matome_fgo_hen/1") as resp:
* print(await resp.json())
*/
const url = `${c.HENTAI2READ}/${book}/`;
const data = await scrapeContent(url);
logger.info({
@ -49,8 +17,11 @@ export async function getHentai2read(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
const example = {
"success": false,
"message": err.message
};
res.json(example);
}
}

ファイルの表示

@ -1,42 +1,11 @@
import { scrapeContent } from "../../scraper/hentai2read/hentai2readSearchController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function searchHentai2read(req: Request, res: Response) {
export async function searchHentai2read(req: any, res: any, next: any) {
try {
const key = req.query.key || "";
if (!key) throw Error("Parameter book is required");
/**
* @api {get} /hentai2read/search Search hentai2read
* @apiName Search hentai2read
* @apiGroup hentai2read
* @apiDescription Search doujinshi on hentai2read
* @apiParam {String} key Keyword to search
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/hentai2read/search?key=yuri
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/hentai2read/search?key=yuri")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/hentai2read/search?key=yuri") as resp:
* print(await resp.json())
*/
const url = `${c.HENTAI2READ}/hentai-list/search/${key}`;
const data = await scrapeContent(url);
logger.info({
@ -47,8 +16,7 @@ export async function searchHentai2read(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
next(Error(err.message));
}
}

ファイルの表示

@ -1,44 +1,12 @@
import { scrapeContent } from "../../scraper/hentaifox/hentaifoxGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { isNumeric, maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function getHentaifox(req: Request, res: Response) {
export async function getHentaifox(req: any, res: any, next: any) {
try {
const book = req.query.book as string;
const book = req.query.book || "";
if (!book) throw Error("Parameter book is required");
if (!isNumeric(book)) throw Error("Parameter book must be number");
/**
* @api {get} /hentaifox/get?book=:book Get hentaifox
* @apiName Get hentaifox
* @apiGroup hentaifox
* @apiDescription Get a doujinshi on hentaifox based on id
*
* @apiParam {Number} book Book ID
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/hentaifox/get?book=123
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/hentaifox/get?book=123")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/hentaifox/get?book=123") as resp:
* print(await resp.json())
*/
if (isNaN(book)) throw Error("Value must be number");
const url = `${c.HENTAIFOX}/gallery/${book}/`;
const data = await scrapeContent(url);
logger.info({
@ -49,8 +17,7 @@ export async function getHentaifox(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
next(Error(err.message));
}
}

ファイルの表示

@ -1,50 +0,0 @@
import { Request, Response } from "express";
import { scrapeContent } from "../../scraper/hentaifox/hentaifoxGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
export async function randomHentaifox(req: Request, res: Response) {
try {
/**
* @api {get} /hentaifox/random Random hentaifox
* @apiName Random hentaifox
* @apiGroup hentaifox
* @apiDescription Gets random doujinshi on hentaifox
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/hentaifox/random
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/hentaifox/random")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/hentaifox/random") as resp:
* print(await resp.json())
*
*/
const url = `${c.HENTAIFOX}/random`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,48 +1,15 @@
import { scrapeContent } from "../../scraper/hentaifox/hentaifoxSearchController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
const sorting = ["latest", "popular"];
import { Request, Response } from "express";
export async function searchHentaifox(req: Request, res: Response) {
export async function searchHentaifox(req: any, res:any, next: any) {
try {
/**
* @api {get} /hentaifox/search Search hentaifox
* @apiName Search hentaifox
* @apiGroup hentaifox
* @apiDescription Search doujinshi on hentaifox
* @apiParam {String} key Keyword to search
* @apiParam {Number} [page=1] Page number
* @apiParam {String} [sort=latest]
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/hentaifox/search?key=yuri
* curl -i https://janda.sinkaroid.org/hentaifox/search?key=yuri&page=2&sort=latest
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/hentaifox/search?key=yuri")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/hentaifox/search?key=yuri") as resp:
* print(await resp.json())
*/
const key = req.query.key as string;
const key = req.query.key || "";
const page = req.query.page || 1;
const sort = req.query.sort as string || sorting[0] as string;
const sort = req.query.sort || sorting[0];
if (!key) throw Error("Parameter key is required");
if (!sorting.includes(sort)) throw Error("Invalid sort: " + sorting.join(", "));
if (!sorting.includes(sort)) throw Error("Invalid short: " + sorting.join(", "));
const url = `${c.HENTAIFOX}/search/?q=${key}&sort=${sort}&page=${page}`;
const data = await scrapeContent(url);
logger.info({
@ -53,8 +20,7 @@ export async function searchHentaifox(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
next(Error(err.message));
}
}

ファイルの表示

@ -1,44 +1,15 @@
import { scrapeContent } from "../../scraper/nhentai/nhentaiGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { nhentaiStrategy, isNumeric, maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
//import { mock } from "../../utils/modifier";
export async function getNhentai(req: Request, res: Response) {
export async function getNhentai(req: any, res: any) {
try {
const book = req.query.book as string;
const book = req.query.book || "";
if (!book) throw Error("Parameter book is required");
if (!isNumeric(book)) throw Error("Parameter book must be number");
/**
* @api {get} /nhentai/get?book=:book Get nhentai
* @apiName Get nhentai
* @apiGroup nhentai
* @apiDescription Get a doujinshi on nhentai based on id
*
* @apiParam {Number} book Book ID
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/nhentai/get?book=123
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/nhentai/get?book=123")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/nhentai/get?book=123") as resp:
* print(await resp.json())
*/
if (isNaN(book)) throw Error("Value must be number");
const url = `${nhentaiStrategy()}/api/gallery/${book}`;
const url = `${c.NHENTAI}/api/gallery/${book}`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
@ -48,8 +19,11 @@ export async function getNhentai(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
const e = {
"success": false,
"message": err.message
};
res.json(e);
}
}

ファイルの表示

@ -1,52 +0,0 @@
import { scrapeContent } from "../../scraper/nhentai/nhentaiGetController";
import { logger } from "../../utils/logger";
import { nhentaiStrategy, getIdRandomNhentai, maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function randomNhentai(req: Request, res: Response) {
try {
const id = await getIdRandomNhentai();
/**
* @api {get} /nhentai/random Random nhentai
* @apiName Random nhentai
* @apiGroup nhentai
* @apiDescription Gets random doujinshi on nhentai
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/nhentai/random
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/nhentai/random")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/nhentai/random") as resp:
* print(await resp.json())
*
*/
const url = `${nhentaiStrategy()}/api/gallery/${id}`;
const data = await scrapeContent(url, true);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, `Error Try again: ${e.message}`));
}
}

ファイルの表示

@ -1,44 +1,18 @@
import { scrapeContent } from "../../scraper/nhentai/nhentaiRelatedController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { nhentaiStrategy, isNumeric, maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
import { mock } from "../../utils/modifier";
export async function relatedNhentai(req: Request, res: Response) {
export async function relatedNhentai(req: any, res: any) {
try {
const book = req.query.book as string;
const book = req.query.book || "";
if (!book) throw Error("Parameter book is required");
if (!isNumeric(book)) throw Error("Value must be number");
if (isNaN(book)) throw Error("Value must be number");
/**
* @api {get} /nhentai/related?book=:book Get related nhentai
* @apiName Get related nhentai
* @apiGroup nhentai
* @apiDescription Get related or similar doujinshi on nhentai based on id
*
* @apiParam {Number} book Book ID
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/nhentai/related?book=123
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/nhentai/related?book=123")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/nhentai/related?book=123") as resp:
* print(await resp.json())
*/
let actualAPI;
if (!await mock(c.NHENTAI)) actualAPI = c.NHENTAI_IP_2;
const url = `${nhentaiStrategy()}/api/gallery/${book}/related`;
const url = `${actualAPI}/api/gallery/${book}/related`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
@ -48,8 +22,11 @@ export async function relatedNhentai(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
const e = {
"success": false,
"message": err.message
};
res.json(e);
}
}

ファイルの表示

@ -1,49 +1,21 @@
import { scrapeContent } from "../../scraper/nhentai/nhentaiSearchController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { nhentaiStrategy, maybeError } from "../../utils/modifier";
import { mock } from "../../utils/modifier";
const sorting = ["popular-today", "popular-week", "popular"];
import { Request, Response } from "express";
export async function searchNhentai(req: Request, res: Response) {
export async function searchNhentai(req: any, res: any) {
try {
const key = req.query.key || "";
const page = req.query.page || 1;
const sort = req.query.sort as string || sorting[0] as string;
const sort = req.query.sort || sorting[0];
if (!key) throw Error("Parameter key is required");
if (!sorting.includes(sort)) throw Error("Invalid sort: " + sorting.join(", "));
if (!sorting.includes(sort)) throw Error("Invalid short: " + sorting.join(", "));
/**
* @api {get} /nhentai/search Search nhentai
* @apiName Search nhentai
* @apiGroup nhentai
* @apiDescription Search doujinshi on nhentai
* @apiParam {String} key Keyword to search
* @apiParam {Number} [page=1] Page number
* @apiParam {String} [sort=popular-today]
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/nhentai/search?key=yuri
* curl -i https://janda.sinkaroid.org/nhentai/search?key=yuri&page=2&sort=popular-today
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/nhentai/search?key=yuri")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/nhentai/search?key=yuri") as resp:
* print(await resp.json())
*/
let actualAPI;
if (!await mock(c.NHENTAI)) actualAPI = c.NHENTAI_IP_2;
const url = `${nhentaiStrategy()}/api/galleries/search?query=${key}&sort=${sort}&page=${page}`;
const url = `${actualAPI}/api/galleries/search?query=${key}&sort=${sort}&page=${page}`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
@ -53,8 +25,11 @@ export async function searchNhentai(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
const e = {
"success": false,
"message": err.message
};
res.json(e);
}
}

ファイルの表示

@ -1,56 +0,0 @@
import { scrapeContent } from "../../scraper/nhentaito/nhentaiToGetController";
import { logger } from "../../utils/logger";
import { isNumeric, maybeError } from "../../utils/modifier";
import c from "../../utils/options";
import { Request, Response } from "express";
export async function getNhentaiTo(req: Request, res: Response) {
try {
const book = req.query.book as string;
if (!book) throw Error("Parameter book is required");
if (!isNumeric(book)) throw Error("Parameter book must be number");
/**
* @api {get} /nhentaito/get?book=:book Get nhentai.to
* @apiName Get nhentai.to
* @apiGroup nhentai.to
* @apiDescription Get a doujinshi on nhentai.to based on id
*
* @apiParam {Number} book Book ID
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/nhentaito/get?book=272
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/nhentaito/get?book=272")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/nhentaito/get?book=272") as resp:
* print(await resp.json())
*/
const url = `${c.NHENTAI_TO}/g/${book}`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,52 +0,0 @@
import { scrapeContent } from "../../scraper/nhentaito/nhentaiToGetController";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
import c from "../../utils/options";
import { Request, Response } from "express";
export async function randomNhentaiTo(req: Request, res: Response) {
try {
/**
* @api {get} /nhentaito/random Random nhentai.to
* @apiName Random nhentai.to
* @apiGroup nhentai.to
* @apiDescription Gets random doujinshi on nhentai.to
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/nhentaito/random
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/nhentaito/random")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/nhentaito/random") as resp:
* print(await resp.json())
*
*/
const url = `${c.NHENTAI_TO}/random/`;
const data = await scrapeContent(url, true);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, `Error Try again: ${e.message}`));
}
}

ファイルの表示

@ -1,56 +0,0 @@
import { scrapeContent } from "../../scraper/nhentaito/nhentaiToSearchController";
import { logger } from "../../utils/logger";
import { isNumeric, maybeError } from "../../utils/modifier";
import c from "../../utils/options";
import { Request, Response } from "express";
export async function relatedNhentaiTo(req: Request, res: Response) {
try {
const book = req.query.book as string;
if (!book) throw Error("Parameter book is required");
if (!isNumeric(book)) throw Error("Parameter book must be number");
/**
* @api {get} /nhentaito/related?book=:book Get related nhentai.to
* @apiName Get related nhentai.to
* @apiGroup nhentai.to
* @apiDescription Get a related doujinshi on nhentai.to based on id
*
* @apiParam {Number} book Book ID
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/nhentaito/related?book=272
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/nhentaito/related?book=272")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/nhentaito/related?book=272") as resp:
* print(await resp.json())
*/
const url = `${c.NHENTAI_TO}/g/${book}`;
const data = await scrapeContent(url, true);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,57 +0,0 @@
import { scrapeContent } from "../../scraper/nhentaito/nhentaiToSearchController";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
import c from "../../utils/options";
import { Request, Response } from "express";
export async function searchNhentaiTo(req: Request, res: Response) {
try {
const key = req.query.key || "";
const page = req.query.page || 1;
if (!key) throw Error("Parameter key is required");
/**
* @api {get} /nhentaito/search Search nhentai.to
* @apiName Search nhentai.to
* @apiGroup nhentai.to
* @apiDescription Search doujinshi on nhentai.to
* @apiParam {String} key Keyword to search
* @apiParam {Number} [page=1] Page number
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/nhentaito/search?key=yuri
* curl -i https://janda.sinkaroid.org/nhentaito/search?key=yuri&page=2
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/nhentaito/search?key=yuri")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/nhentaito/search?key=yuri") as resp:
* print(await resp.json())
*/
const url = `${c.NHENTAI_TO}/search?q=${key}&page=${page}`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,44 +1,12 @@
import { scrapeContent } from "../../scraper/pururin/pururinGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { isNumeric, maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function getPururin(req: Request, res: Response) {
export async function getPururin(req: any, res: any, next: any) {
try {
const book = req.query.book as string;
const book = req.query.book || "";
if (!book) throw Error("Parameter book is required");
if (!isNumeric(book)) throw Error("Parameter book must be number");
/**
* @api {get} /pururin/get?book=:book Get pururin
* @apiName Get pururin
* @apiGroup pururin
* @apiDescription Get a doujinshi on pururin based on id
*
* @apiParam {Number} book Book ID
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/pururin/get?book=123
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/pururin/get?book=123")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/pururin/get?book=123") as resp:
* print(await resp.json())
*/
if (isNaN(book)) throw Error("Value must be number");
const url = `${c.PURURIN}/gallery/${book}/janda`;
const data = await scrapeContent(url);
logger.info({
@ -49,8 +17,7 @@ export async function getPururin(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
next(Error(err.message));
}
}

ファイルの表示

@ -1,53 +0,0 @@
import { scrapeContent } from "../../scraper/pururin/pururinGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { getIdRandomPururin, maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
export async function randomPururin(req: Request, res: Response) {
try {
const id = await getIdRandomPururin();
/**
* @api {get} /pururin/random Random pururin
* @apiName Random pururin
* @apiGroup pururin
* @apiDescription Gets random doujinshi on pururin
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/pururin/random
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/pururin/random")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/pururin/random") as resp:
* print(await resp.json())
*
*/
const url = `${c.PURURIN}/gallery/${id}/janda`;
const data = await scrapeContent(url, true);
logger.info({
path: req.path,
query: req.query,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
}
}

ファイルの表示

@ -1,48 +1,16 @@
import { scrapeContent } from "../../scraper/pururin/pururinSearchController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { maybeError } from "../../utils/modifier";
// const sorting = ["newest", "most-popular", "highest-rated", "most-viewed", "title", "random"];
import { Request, Response } from "express";
const sorting = ["newest", "most-popular", "highest-rated", "most-viewed", "title", "random"];
export async function searchPururin(req: Request, res: Response) {
export async function searchPururin(req: any, res: any, next: any) {
try {
const key = req.query.key as string;
const key = req.query.key || "";
const page = req.query.page || 1;
// const sort = req.query.sort as string || sorting[0] as string;
const sort = req.query.sort || sorting[0];
if (!key) throw Error("Parameter key is required");
// if (!sorting.includes(sort)) throw Error("Invalid sort: " + sorting.join(", "));
/**
* @api {get} /pururin/search Search pururin
* @apiName Search pururin
* @apiGroup pururin
* @apiDescription Search doujinshi on pururin
* @apiParam {String} key Keyword to search
* @apiParam {Number} [page=1] Page number
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/pururin/search?key=yuri
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/pururin/search?key=yuri")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/pururin/search?key=yuri") as resp:
* print(await resp.json())
*/
const url = `${c.PURURIN}/search?q=${key}&page=${page}`;
if (!sorting.includes(sort)) throw Error("Invalid short: " + sorting.join(", "));
const url = `${c.PURURIN}/search/${sort}?q=${key}&page=${page}`;
const data = await scrapeContent(url);
logger.info({
path: req.path,
@ -52,8 +20,7 @@ export async function searchPururin(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
next(Error(err.message));
}
}

ファイルの表示

@ -1,45 +1,15 @@
import { scrapeContent } from "../../scraper/simply-hentai/simply-hentaiGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { mock, maybeError } from "../../utils/modifier";
import { Request, Response } from "express";
import { mock } from "../../utils/modifier";
export async function getSimplyhentai(req: Request, res: Response) {
export async function getSimplyhentai(req: any, res: any) {
try {
const book = req.query.book as string;
if (!book) throw Error("Parameter book is required, Example: idolmaster/from-fumika-fc8496c/all-pages");
const book = req.query.book || "";
if (!book) throw Error("Parameter book is required");
let actualAPI;
if (!await mock(c.SIMPLY_HENTAI)) actualAPI = c.SIMPLY_HENTAI_PROXIFIED;
/**
* @api {get} /simply-hentai/get?book=:book Get simply-hentai
* @apiName Get simply-hentai
* @apiGroup simply-hentai
* @apiDescription Get a doujinshi on simply-hentai
*
* @apiParam {String} book Book path
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* HTTP/1.1 400 Bad Request
*
* @apiExample {curl} curl
* curl -i https://janda.sinkaroid.org/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages
*
* @apiExample {js} JS/TS
* import axios from "axios"
*
* axios.get("https://janda.sinkaroid.org/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages")
* .then(res => console.log(res.data))
* .catch(err => console.error(err))
*
* @apiExample {python} Python
* import aiohttp
* async with aiohttp.ClientSession() as session:
* async with session.get("https://janda.sinkaroid.org/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages") as resp:
* print(await resp.json())
*/
const url = `${actualAPI}/${book}`;
const data = await scrapeContent(url);
@ -51,8 +21,11 @@ export async function getSimplyhentai(req: Request, res: Response) {
useragent: req.get("User-Agent")
});
return res.json(data);
} catch (err) {
const e = err as Error;
res.status(400).json(maybeError(false, e.message));
} catch (err: any) {
const example = {
"success": false,
"message": err.message
};
res.json(example);
}
}

ファイルの表示

@ -1,5 +1,3 @@
import "dotenv/config";
import JandaPress from "./JandaPress";
import express from "express";
import { Request, Response, NextFunction } from "express";
import scrapeRoutes from "./router/endpoint";
@ -7,65 +5,40 @@ import { slow, limiter } from "./utils/limit-options";
import { logger } from "./utils/logger";
import { isNumeric } from "./utils/modifier";
import * as pkg from "../package.json";
const janda = new JandaPress();
const app = express();
app.get("/", slow, limiter, async (req, res) => {
app.get("/", slow, limiter, (req, res) => {
res.send({
success: true,
message: "Hi, I'm alive!",
endpoint: "https://github.com/sinkaroid/jandapress/blob/master/README.md#routing",
date: new Date().toLocaleString(),
rss: janda.currentProccess().rss,
heap: janda.currentProccess().heap,
server: await janda.getServer(),
version: `${pkg.version}`,
});
logger.info({
path: req.path,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
logger.info({
path: req.path,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
});
app.use(scrapeRoutes());
app.get("/g/:id", slow, limiter, (req, res) => {
if (!isNumeric(req.params.id)) throw Error("This path need required number to work");
res.redirect(301, `https://nhentai.net/g/${req.params.id}`);
});
app.get("/p/:id", slow, limiter, (req, res) => {
if (!isNumeric(req.params.id)) throw Error("This path need required number to work");
res.redirect(301, `https://pururin.to/gallery/${req.params.id}/re=janda`);
});
app.get("/h/:id", slow, limiter, (req, res) => {
if (!isNumeric(req.params.id)) throw Error("This path need required number to work");
res.redirect(301, `https://hentaifox.com/gallery/${req.params.id}`);
});
app.get("/a/:id", slow, limiter, (req, res) => {
if (!isNumeric(req.params.id)) throw Error("This path need required number to work");
res.redirect(301, `https://asmhentai.com/g/${req.params.id}`);
});
app.get("/to/:id", slow, limiter, (req, res) => {
if (!isNumeric(req.params.id)) throw Error("This path need required number to work");
res.redirect(301, `https://nhentai.to/g/${req.params.id}`);
});
app.use((req: Request, res: Response, next: NextFunction) => {
res.status(404);
next(Error(`The page not found in path ${req.url} and method ${req.method}`));
logger.error({
path: req.url,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
logger.error({
path: req.url,
method: req.method,
ip: req.ip,
useragent: req.get("User-Agent")
});
});
@ -76,5 +49,4 @@ app.use((error: any, res: Response) => {
});
});
app.listen(process.env.PORT || 3000, () => console.log(`${pkg.name} is running on port ${process.env.PORT || 3000}`));
app.listen(process.env.PORT || 3000, () => console.log(`${pkg.name} running in port 3000`));

ファイルの表示

@ -1,40 +0,0 @@
export interface Nhentai {
id: number;
media_id: string;
title: Title;
images: { pages: P[]}
scanlator: string | "";
upload_date: number;
tags: T[];
num_pages: number;
num_favorites: number;
}
export interface NhentaiSearch {
result: Nhentai[];
}
interface Title {
english: string;
japanese: string;
pretty: string;
}
interface P {
t: string;
w: number;
h: number;
}
interface T {
id: number;
type: string;
name: string;
url: string;
count: number;
}
export interface MaybeError {
message: string;
}

ファイルの表示

@ -1,72 +1,28 @@
import { Router } from "express";
import cors from "cors";
import { slow, limiter } from "../utils/limit-options";
// hentaifox
import { searchHentaifox } from "../controller/hentaifox/hentaifoxSearch";
import { getHentaifox } from "../controller/hentaifox/hentaifoxGet";
import { randomHentaifox } from "../controller/hentaifox/hentaifoxRandom";
// pururin
import { getPururin } from "../controller/pururin/pururinGet";
import { searchPururin } from "../controller/pururin/pururinSearch";
import { randomPururin } from "../controller/pururin/pururinRandom";
// hentai2read
import { searchHentai2read } from "../controller/hentai2read/hentai2readSearch";
import { getHentai2read } from "../controller/hentai2read/hentai2readGet";
// simply-hentai
import { getSimplyhentai } from "../controller/simply-hentai/simply-hentaiGet";
// nhentai
import { getNhentai } from "../controller/nhentai/nhentaiGet";
import { searchNhentai } from "../controller/nhentai/nhentaiSearch";
import { relatedNhentai } from "../controller/nhentai/nhentaiRelated";
import { randomNhentai } from "../controller/nhentai/nhentaiRandom";
// asmhentai
import { getAsmhentai } from "../controller/asmhentai/asmhentaiGet";
import { searchAsmhentai } from "../controller/asmhentai/asmhentaiSearch";
import { randomAsmhentai } from "../controller/asmhentai/asmhentaiRandom";
// 3hentai
import { get3hentai } from "../controller/3hentai/3hentaiGet";
import { search3hentai } from "../controller/3hentai/3hentaiSearch";
import { random3hentai } from "../controller/3hentai/3hentaiRandom";
// nhentaito
import { getNhentaiTo } from "../controller/nhentaito/nhentaiToGet";
import { randomNhentaiTo } from "../controller/nhentaito/nhentaiToRandom";
import { searchNhentaiTo } from "../controller/nhentaito/nhentaiToSearch";
import { relatedNhentaiTo } from "../controller/nhentaito/nhentaiToRelated";
import { slow, limiter } from "../utils/limit-options";
function scrapeRoutes() {
const router = Router();
router.get("/hentaifox/search", cors(), slow, limiter, searchHentaifox);
router.get("/hentaifox/get", cors(), slow, limiter, getHentaifox);
router.get("/hentaifox/random", cors(), slow, limiter, randomHentaifox);
router.get("/pururin/get", cors(), slow, limiter, getPururin);
router.get("/pururin/search", cors(), slow, limiter, searchPururin);
router.get("/pururin/random", cors(), slow, limiter, randomPururin);
router.get("/hentai2read/search", cors(), slow, limiter, searchHentai2read);
router.get("/hentai2read/get", cors(), slow, limiter, getHentai2read);
router.get("/simply-hentai/get", cors(), slow, limiter, getSimplyhentai);
router.get("/asmhentai/get", cors(), slow, limiter, getAsmhentai);
router.get("/asmhentai/search", cors(), slow, limiter, searchAsmhentai);
router.get("/asmhentai/random", cors(), slow, limiter, randomAsmhentai);
router.get("/nhentai/get", cors(), slow, limiter, getNhentai);
router.get("/nhentai/search", cors(), slow, limiter, searchNhentai);
router.get("/nhentai/related", cors(), slow, limiter, relatedNhentai);
router.get("/nhentai/random", cors(), slow, limiter, randomNhentai);
router.get("/3hentai/get", cors(), slow, limiter, get3hentai);
router.get("/3hentai/search", cors(), slow, limiter, search3hentai);
router.get("/3hentai/random", cors(), slow, limiter, random3hentai);
router.get("/nhentaito/get", cors(), slow, limiter, getNhentaiTo);
router.get("/nhentaito/random", cors(), slow, limiter, randomNhentaiTo);
router.get("/nhentaito/search", cors(), slow, limiter, searchNhentaiTo);
router.get("/nhentaito/related", cors(), slow, limiter, relatedNhentaiTo);
router.get("/hentaifox/search", slow, limiter, searchHentaifox);
router.get("/hentaifox/get", slow, limiter, getHentaifox);
router.get("/pururin/get", slow, limiter, getPururin);
router.get("/pururin/search", slow, limiter, searchPururin);
router.get("/hentai2read/search", slow, limiter, searchHentai2read);
router.get("/hentai2read/get", slow, limiter, getHentai2read);
router.get("/simply-hentai/get", slow, limiter, getSimplyhentai);
router.get("/nhentai/get", slow, limiter, getNhentai);
router.get("/nhentai/search", slow, limiter, searchNhentai);
router.get("/nhentai/related", slow, limiter, relatedNhentai);
return router;
}

ファイルの表示

@ -1,62 +0,0 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import c from "../../utils/options";
interface IGet3hentai {
title: string;
id: number;
tags: string[];
total: number;
image: string[];
upload_date: string;
}
interface IData {
success?: boolean;
data: object;
source: string;
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
//get href in <div id="main-cover"> first
const actualId = $("#main-cover").find("a").attr("href");
//get after last second '/' in asu
const book = actualId?.split("/")[4];
const title = $("h1").text();
const id = parseInt(url.split("/").pop() as string) || parseInt(book as string);
const tags = $("span.filter-elem")?.map((i, el) => $(el).text()).get();
const tagsClean = tags.map((tag: string) => tag.replace(/<[^>]*>/g, "").replace(/\n/g, "").trim());
const image = $("div.single-thumb-col")?.map((i, el) => $(el).find("img").attr("data-src")).get();
if (image.length === 0) throw Error("No result found");
const imageClean = image.map((img: string) => img.replace("t.", "."));
const upload_date = $("time").text();
const objectData: IGet3hentai = {
title: title,
id: id,
tags: tagsClean.slice(0, tagsClean.length - 1),
total: image.length,
image: imageClean,
upload_date: upload_date,
};
const data: IData = {
success: true,
data: objectData,
source: `${c.THREEHENTAI}/d/${id ? id : book}`,
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -1,56 +0,0 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import { removeNonNumeric } from "../../utils/modifier";
interface I3HentaiSearch {
title: string;
id: number;
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
//in <div class="listing-container bg-container container-xl"> there are <div class="doujin-col">
const doujinCol = $("div.listing-container.bg-container.container-xl");
//in <div class="doujin-col"> there are <div class="doujin">
const doujin = doujinCol.find("div.doujin");
//map all href in <div class="doujin">
const href = doujin.map((i, el) => $(el).find("a").attr("href")).get();
// const book = href.map((id: string) => id.split("/").pop());
//There is two <div class="title flag flag-eng"> and <div class="title flag flag-jpn">, get all text
const title = doujin.map((i, el) => $(el).find("div.title").text()).get();
const titleClean = title.map((title: string) => title.replace(/<[^>]*>/g, "").replace(/\n/g, "").trim());
const content = [];
for (const [i, val] of href.entries()) {
const id = removeNonNumeric(val);
const objectData: I3HentaiSearch = {
title: titleClean[i],
id: parseInt(id),
};
content.push(objectData);
}
if (content.length === 0) throw Error("No result found");
const data = {
success: true,
data: content,
page: parseInt(url.split("&page=")[1]),
sort: url.split("sort=")[1],
source: url
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -1,68 +0,0 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import c from "../../utils/options";
interface IGetAsmhentai {
title: string;
id: number;
tags: string[];
total: number;
image: string[];
upload_date: string;
}
interface IData {
success?: boolean;
data: object;
source: string;
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
const actualId = $(".cover").find("a").attr("href");
const book = actualId?.replace("/gallery/", "");
const actualBook = parseInt(book as string);
const title = $("h1").text();
const tags = $("span.badge.tag")?.map((i, el) => $(el).text()).get();
const tagsClean = tags.map((tag: string) => tag.replace(/[0-9]|[.,()]/g, "").trim());
const totalIfBroken = $("div.pages").children().first().text();
const actualTotal = totalIfBroken.replace(/[^\d]/g, "");
const total = parseInt($("input[id='t_pages']")?.attr("value") || actualTotal);
const img = $("img[data-src]")?.attr("data-src") || "";
const imageUrl = img.replace("//", "https://");
const date = $("div.pages h3").map((i, el) => $(el).text()).get();
const image = [];
for (let i = 0; i < total; i++) {
image.push(`${imageUrl.replace("cover", `${i + 1}`)}`);
}
if (image.length === 0) throw Error("Not found");
const objectData: IGetAsmhentai = {
title: title,
id: actualBook,
tags: tagsClean,
total: total,
image: image,
upload_date: date[1] ? date[1] : "Unknown"
};
const data: IData = {
success: true,
data: objectData,
source: `${c.ASMHENTAI}/g/${actualBook}/`
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -1,56 +0,0 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import { removeNonNumeric } from "../../utils/modifier";
interface IAsmHentaiSearch {
title: string;
id: number;
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
//get all <img alt= inside <div class="image">
const title = $("div.image").map((i, el) => {
const img = $(el).find("img");
return img.attr("alt");
}
).get();
//get all href inside <div class="image">, get only number
const id = $("div.image").map((i, el) => {
const href = $(el).find("a");
return href.attr("href");
}).get();
const content = [];
for (const abc of title) {
const objectData: IAsmHentaiSearch = {
title: abc.split("\n")[0],
id: parseInt(removeNonNumeric(id[title.indexOf(abc)])),
};
content.push(objectData);
}
if (content.length === 0) throw Error("No result found");
const data = {
success: true,
data: content,
page: parseInt(url.split("&page=")[1]),
sort: url.split("/search/")[1].split("?")[0],
source: url
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -1,5 +1,5 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import p from "phin";
import c from "../../utils/options";
interface IHentai2readGet {
@ -17,19 +17,17 @@ interface IHentai2readGetPush {
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
const res = await p(url);
const $ = load(res.body as Buffer);
const script = $("script").map((i, el) => $(el).text()).get();
//find 'var gData = {}' inside script
const gData = script.find(el => el.includes("var gData"));
const gDataClean: string = gData?.replace(/[\s\S]*var gData = /, "").replace(/;/g, "").replace(/'/g, "\"") || "";
const gDataJson = JSON.parse(gDataClean);
const images = gDataJson.images.map((el: string) => `https://cdn-ngocok-static.sinxdr.workers.dev/hentai${el}`);
const images = gDataJson.images.map((el: any) => `https://cdn-ngocok-static.sinxdr.workers.dev/hentai${el}`);
const objectData: IHentai2readGet = {
title: gDataJson.title,
@ -45,8 +43,7 @@ export async function scrapeContent(url: string) {
previus_url: gDataJson.previousURL
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
} catch (err: any) {
throw Error(err.message);
}
}

ファイルの表示

@ -1,5 +1,5 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import p from "phin";
import c from "../../utils/options";
import { getId } from "../../utils/modifier";
@ -11,12 +11,10 @@ interface IHentai2readSearch {
message: string;
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
const res = await p(url);
const $ = load(res.body as Buffer);
const title = $(".title-text").map((i, el) => $(el).text()).get();
const imgSrc = $("img").map((i, el) => $(el).attr("data-src")).get();
const id = $(".overlay-title").map((i, el) => $(el).children("a").attr("href")).get();
@ -35,15 +33,12 @@ export async function scrapeContent(url: string) {
}
if (content.length === 0) throw Error("No result found");
const data = {
data: content,
source: url,
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
} catch (err: any) {
throw Error(err.message);
}
}

ファイルの表示

@ -1,5 +1,5 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import p from "phin";
import c from "../../utils/options";
interface IHentaiFoxGet {
@ -11,12 +11,10 @@ interface IHentaiFoxGet {
image: string[];
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
const res = await p(url);
const $ = load(res.body as Buffer);
const id = parseInt($("a.g_button")?.attr("href")?.split("/")[2] || "");
const category = $("a.tag_btn").map((i, abc) => {
@ -24,8 +22,9 @@ export async function scrapeContent(url: string) {
}).get();
const imgSrc = $("img").map((i, el) => $(el).attr("data-src")).get();
const parameterImg2 = imgSrc[0].split("/").slice(0, 5).join("/");
const parameterImg = imgSrc[0].split("/").slice(0, imgSrc[0].split("/").length - 1).join("/");
const extensionImg = `.${imgSrc[0].split(".").slice(-1)[0]}`;
const info = $("span.i_text.pages").map((i, abc) => {
return $(abc).text();
}).get();
@ -33,7 +32,7 @@ export async function scrapeContent(url: string) {
const pageCount = parseInt(info[0].replace(/[^0-9]/g, ""));
const image = [];
for (let i = 0; i < Number(pageCount); i++) {
image.push(`${parameterImg2}/${i + 1}${extensionImg}`);
image.push(`${parameterImg}/${i + 1}${extensionImg}`);
}
const titleInfo = $("div.info").children("h1").text();
@ -47,13 +46,11 @@ export async function scrapeContent(url: string) {
};
const data = {
success: true,
data: objectData,
source: `${c.HENTAIFOX}/gallery/${id}/`,
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
} catch (err: any) {
throw Error(err.message);
}
}

ファイルの表示

@ -1,22 +1,19 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import p from "phin";
import c from "../../utils/options";
interface IHentaiFoxSearch {
title: string;
cover: string;
id: number;
language: string;
category: string;
link: string;
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
const res = await p(url);
const $ = load(res.body as Buffer);
const title = $("h2.g_title").map((i, abc) => {
return $(abc).text();
@ -40,7 +37,6 @@ export async function scrapeContent(url: string) {
title: title[title.indexOf(abc)],
cover: imgSrcClean[title.indexOf(abc)],
id: parseInt(link[title.indexOf(abc)]),
language: "Translated",
category: category[title.indexOf(abc)],
link: `${c.HENTAIFOX}/gallery/${link[title.indexOf(abc)]}`,
};
@ -48,19 +44,14 @@ export async function scrapeContent(url: string) {
}
if (content.length === 0) throw Error("No result found");
const data = {
success: true,
data: content.filter(con => con.category !== ""),
page: Number(url.split("&page=")[1]),
sort: url.split("&sort=")[1].split("&")[0],
source: url,
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
} catch (err: any) {
throw Error(err.message);
}
}

ファイルの表示

@ -1,13 +1,11 @@
import JandaPress from "../../JandaPress";
import p from "phin";
import c from "../../utils/options";
import { getDate, timeAgo } from "../../utils/modifier";
import { Nhentai } from "../../interfaces";
const extension = {
j: "jpg",
p: "png",
g: "gif",
};
import * as pkg from "../../../package.json";
import { CookieJar } from "tough-cookie";
import { HttpsCookieAgent } from "http-cookie-agent/http";
import { config } from "dotenv";
config();
interface INhentaiGet {
title: string;
@ -21,71 +19,83 @@ interface INhentaiGet {
num_favorites: number;
artist: string[];
group: string;
parodies: string[];
parodies: string;
characters: string[];
upload_date: string;
}
const janda = new JandaPress();
const jar = new CookieJar();
jar.setCookie(process.env.CF as string, "https://nhentai.net/");
export async function scrapeContent(url: string, random = false) {
export async function scrapeContent(url: string) {
try {
let res, raw;
if (random) res = await janda.simulateNhentaiRequest(url), raw = res as Nhentai;
else res = await janda.fetchJson(url), raw = res as Nhentai;
const res = await p({
url: url,
core: {
agent: new HttpsCookieAgent({ cookies: { jar, }, }),
},
"headers": {
"User-Agent": `${pkg.name}/${pkg.version} Node.js/16.9.1`
},
parse: "json",
});
console.log(res.statusCode);
const GALLERY = "https://i.nhentai.net/galleries";
const imagesRaw = raw.images.pages;
const TYPE: any = {
j: "jpg",
p: "png",
g: "gif",
};
const images = Object.keys(imagesRaw)
.map((key) => imagesRaw[parseInt(key)].t);
const dataRaw: any = res.body;
const imagesRaw = dataRaw.images.pages;
const images: string[] = Object.keys(imagesRaw)
.map((key: string) => imagesRaw[key].t);
const imageList = [];
for (let i = 0; i < images.length; i++) {
imageList.push(`${GALLERY}/${raw.media_id}/${i + 1}.${(extension as any)[images[i]]}`);
imageList.push(`${GALLERY}/${dataRaw.media_id}/${i + 1}.${TYPE[images[i]]}`);
}
//get all tags.name
const tagsRaw = raw.tags;
// all tags without filter
// const tags = Object.keys(tagsRaw).map((key) => tagsRaw[parseInt(key)].name);
const tagsRaw = dataRaw.tags;
const tags: string[] = Object.keys(tagsRaw).map((key: string) => tagsRaw[key].name);
const tagsFilter = tagsRaw.filter((tag) => tag.type === "tag");
const tags = tagsFilter.map((tag) => tag.name).sort() || [];
const artistRaw = tagsRaw.filter((tag) => tag.type === "artist");
const artist = artistRaw.map((tag) => tag.name) || [];
const artistRaw = tagsRaw.filter((tag: any) => tag.type === "artist");
const artist: string[] = artistRaw.map((tag: any) => tag.name) || [];
//find "type": "language" in tagsRaw
const languageRaw = tagsRaw.find((tag) => tag.type === "language");
const language = languageRaw ? languageRaw.name : "";
const languageRaw = tagsRaw.find((tag: any) => tag.type === "language");
const language = languageRaw ? languageRaw.name : null;
const parodiesRaw = tagsRaw.filter((tag) => tag.type === "parody");
const parodies = parodiesRaw.map((tag) => tag.name) || [];
const parodiesRaw = tagsRaw.find((tag: any) => tag.type === "parody");
const parodies = parodiesRaw ? parodiesRaw.name : null;
const groupRaw = tagsRaw.find((tag) => tag.type === "group");
const group = groupRaw ? groupRaw.name : "None";
const groupRaw = tagsRaw.find((tag: any) => tag.type === "group");
const group = groupRaw ? groupRaw.name : null;
//get all "type": "character" in tagsRaw
const charactersRaw = tagsRaw.filter((tag) => tag.type === "character");
const characters = charactersRaw.map((tag) => tag.name) || [];
const charactersRaw = tagsRaw.filter((tag: any) => tag.type === "character");
const characters: string[] = charactersRaw.map((tag: any) => tag.name) || [];
const time = new Date(raw.upload_date * 1000);
const time = new Date(dataRaw.upload_date * 1000);
const objectData: INhentaiGet = {
title: raw.title.pretty,
title: dataRaw.title.pretty,
optional_title: {
english: raw.title.english,
japanese: raw.title.japanese,
pretty: raw.title.pretty,
english: dataRaw.title.english,
japanese: dataRaw.title.japanese,
pretty: dataRaw.title.pretty,
},
id: raw.id,
id: dataRaw.id,
language: language,
tags: tags,
total: imageList.length,
image: imageList,
num_pages: raw.num_pages,
num_favorites: raw.num_favorites,
num_pages: dataRaw.num_pages,
num_favorites: dataRaw.num_favorites,
artist: artist,
group: group,
parodies: parodies,
@ -94,13 +104,11 @@ export async function scrapeContent(url: string, random = false) {
};
const data = {
success: true,
data: objectData,
source: `${c.NHENTAI}/g/${raw.id}`,
source: `${c.NHENTAI}/g/${dataRaw.id}`,
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
} catch (err: any) {
throw Error(err.message);
}
}

ファイルの表示

@ -1,53 +1,39 @@
import JandaPress from "../../JandaPress";
import p from "phin";
import c from "../../utils/options";
import { getDate, timeAgo } from "../../utils/modifier";
import { NhentaiSearch } from "../../interfaces";
interface INhentaiRelated {
title: {
english: string;
japanese: string;
pretty: string
};
title: string;
id: number;
language: string;
upload_date: string;
total: number;
tags: string[];
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchJson(url);
const rawData = res as NhentaiSearch;
const res = await p({ url: url, parse: "json" });
const rawData: any = res.body;
const content = [];
for (let i = 0; i < rawData.result.length; i++) {
const time = new Date(rawData.result[i].upload_date * 1000);
const objectData: INhentaiRelated = {
title: {
english: rawData.result[i].title.english,
japanese: rawData.result[i].title.japanese,
pretty: rawData.result[i].title.pretty,
},
title: rawData.result[i].title,
id: rawData.result[i].id,
language: rawData.result[i].tags.find((tag) => tag.type === "language")?.name || "",
upload_date: `${getDate(time)} (${timeAgo(time)})`,
total: rawData.result[i].num_pages,
tags: rawData.result[i].tags.map((tag) => tag.name),
tags: rawData.result[i].tags.map((tag: any) => tag.name),
};
content.push(objectData);
}
const data = {
success: true,
data: content,
source: url.replace(c.NHENTAI_IP, c.NHENTAI),
};
return data;
} catch (err: any) {
throw Error(err.message);
}

ファイルの表示

@ -1,20 +1,9 @@
import JandaPress from "../../JandaPress";
import p from "phin";
import c from "../../utils/options";
import { getDate, timeAgo } from "../../utils/modifier";
import { NhentaiSearch } from "../../interfaces";
const extension = {
j: "jpg",
p: "png",
g: "gif",
};
interface INhentaiSearch {
title: {
english: string;
japanese: string;
pretty: string
};
title: string;
id: number;
language: string;
upload_date: string;
@ -23,40 +12,33 @@ interface INhentaiSearch {
tags: string[];
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchJson(url);
const rawData = res as NhentaiSearch;
const res = await p({ url: url, parse: "json" });
const rawData: any = res.body;
const content = [];
const GALLERY = "https://i.nhentai.net/galleries";
const TYPE: any = {
j: "jpg",
p: "png",
g: "gif",
};
for (let i = 0; i < rawData.result.length; i++) {
const GALLERY = "https://i.nhentai.net/galleries";
const imagesRaw = rawData.result[i].images.pages;
const images = Object.keys(imagesRaw)
.map((key) => imagesRaw[parseInt(key)].t);
const time = new Date(rawData.result[i].upload_date * 1000);
const objectData: INhentaiSearch = {
title: {
english: rawData.result[i].title.english,
japanese: rawData.result[i].title.japanese,
pretty: rawData.result[i].title.pretty,
},
title: rawData.result[i].title,
id: rawData.result[i].id,
language: rawData.result[i].tags.find((tag) => tag.type === "language")?.name || "",
language: rawData.result[i].tags.find((tag: any) => tag.type === "language") ? rawData.result[i].tags.find((tag: any) => tag.type === "language").name : null,
upload_date: `${getDate(time)} (${timeAgo(time)})`,
total: rawData.result[i].num_pages,
cover: `${GALLERY}/${rawData.result[i].media_id}/1.${(extension as any)[images[i]]}`,
tags: rawData.result[i].tags.map((tag) => tag.name),
cover: `${GALLERY}/${rawData.result[i].media_id}/1.${TYPE[rawData.result[i].images.cover.t]}`,
tags: rawData.result[i].tags.map((tag: any) => tag.name),
};
content.push(objectData);
}
const data = {
success: true,
data: content,
page: Number(url.split("&page=")[1]),
sort: url.split("&sort=")[1].split("&")[0],
@ -64,8 +46,7 @@ export async function scrapeContent(url: string) {
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
} catch (err: any) {
throw Error(err.message);
}
}

ファイルの表示

@ -1,75 +0,0 @@
import { load } from "cheerio";
import p from "phin";
import JandaPress from "../../JandaPress";
import c from "../../utils/options";
import prox from "../../utils/reverseprox";
interface IGetNhentaiTo {
title: string;
id: number;
tags: string[];
total: number;
image: string[];
}
interface IData {
success: boolean;
data: object;
source: string;
}
const janda = new JandaPress();
export async function scrapeContent(url: string, random = false) {
try {
let res, raw;
if (random) res = await p({
url: url,
headers: {
"User-Agent": process.env.USER_AGENT || "jandapress/1.0.5 Node.js/16.9.1"
},
followRedirects: true
}), raw = res.body;
else res = await janda.fetchBody(url), raw = res;
const $ = load(raw);
const title: string = $("div#info-block div#info h1").text();
if (!title) throw Error("Not found");
const tags: string[] = $("span.tags span.name").map((i, abc) => {
return $(abc).text();
}).get();
// const cover = $("div#cover img").attr("src") || "";
const total: number = parseInt(tags.pop()?.split(" ")[0] || "0");
const id: number = parseInt($("div#cover a").attr("href")?.split("/g/")[1] || "0");
const thumbnail = $("a.gallerythumb img").map((i, abc) => {
return $(abc).attr("data-src");
}).get();
const proxy = thumbnail
.map((img: string) => img?.replace(prox.NHENTAI_TO, prox.NHENTAI_TO_SOLVER));
const image = [];
for (let i = 0; i < total; i++) {
image.push(`${proxy[i]?.replace("t.", ".")}`);
}
const objectData: IGetNhentaiTo = {
title,
id,
tags,
total,
image
};
const data: IData = {
success: true,
data: objectData,
source: `${c.NHENTAI_TO}/g/${id}?re=janda`
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -1,71 +0,0 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import prox from "../../utils/reverseprox";
interface ISearchNhentaiTo {
title: string;
id: number;
cover: string;
}
interface IData {
success: boolean;
data: object;
page: number;
source: string;
}
const janda = new JandaPress();
export async function scrapeContent(url: string, isRelated = false) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
let mode;
if (!isRelated) mode = "div.gallery";
//in in container index-container
else mode = "div.container.index-container";
const dataRaw = $(mode);
const title = dataRaw.find("div.caption").map((i, el) => {
return $(el).text();
}).get();
const id = dataRaw.find("a").map((i, el) => {
return $(el).attr("href")?.split("/")[2];
}).get();
const cover = dataRaw.find("img").map((i, el) => {
return $(el).attr("src")?.replace(prox.NHENTAI_TO, prox.NHENTAI_TO_SOLVER) || $(el).attr("data-src")?.replace(prox.NHENTAI_TO, prox.NHENTAI_TO_SOLVER);
}).get();
let looping;
if (!isRelated) looping = dataRaw;
else looping = title;
const objectData = [];
for (let i = 0; i < looping.length; i++) {
const searchResults: ISearchNhentaiTo = {
title: title[i],
id: Number(id[i]),
cover: cover[i],
};
objectData.push(searchResults);
}
if (objectData.length === 0) throw Error("No result found");
const data: IData = {
success: true,
data: objectData,
page: Number(url.split("page=")[1]),
source: url
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
}
}

ファイルの表示

@ -1,6 +1,5 @@
import { load } from "cheerio";
import p from "phin";
import JandaPress from "../../JandaPress";
import c from "../../utils/options";
import { getPururinInfo, getUrl } from "../../utils/modifier";
@ -13,24 +12,16 @@ interface IGetPururin {
image: string[];
}
interface IData {
success: boolean;
interface IData{
data: object;
source: string;
}
const janda = new JandaPress();
export async function scrapeContent(url: string, random = false) {
export async function scrapeContent(url: string) {
try {
let res, raw;
if (random) res = await p({ url: url }), raw = res.body;
else res = await janda.fetchBody(url), raw = res;
const $ = load(raw);
const title: string = $("meta[property='og:title']").attr("content") || "";
if (!title) throw Error("Not found");
const res = await p(url);
const $ = load(res.body);
const title: string = $("div.content-wrapper h1").html() || "";
const tags: string[] = $("div.content-wrapper ul.list-inline li").map((i, abc) => {
return getPururinInfo($(abc).text());
@ -38,8 +29,8 @@ export async function scrapeContent(url: string, random = false) {
const cover = $("meta[property='og:image']").attr("content");
const extension = `.${cover?.split(".").pop()}`;
const total: number = parseInt($("span[itemprop='numberOfPages']").text()) || 0;
const id: number = parseInt($("meta[property='og:url']").attr("content")?.split("/")[4] || "0");
const total: number = parseInt($("gallery-thumbnails").attr(":total") || "0");
const id: number = parseInt($("gallery-thumbnails").attr(":id") || "0");
const image = [];
for (let i = 0; i < total; i++) {
@ -56,13 +47,11 @@ export async function scrapeContent(url: string, random = false) {
};
const data: IData = {
success: true,
data: objectData,
source: `${c.PURURIN}/gallery/${id}/janda`
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
} catch (err: any) {
throw Error(err.message);
}
}

ファイルの表示

@ -1,37 +1,31 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import p from "phin";
import c from "../../utils/options";
import { isText } from "domhandler";
import { getPururinInfo, getPururinPageCount, getPururinLanguage } from "../../utils/modifier";
import { getPururinInfo, getPururinPageCount } from "../../utils/modifier";
interface ISearchPururin {
title: string;
cover: string | null;
cover: string;
id: number;
language: string;
info: string;
link: string;
total: number;
}
interface IData {
success: boolean;
data: object;
page: number;
sort: string | null;
sort: string;
source: string;
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res);
const dataRaw = $(".card.card-gallery");
const res = await p(url);
const $ = load(res.body);
const dataRaw = $("img.card-img-top");
const info = $("div.info");
const card = $("img.card-img-top").map((i, abc) => {
return abc.attribs["src"];
}).get();
const infoBook = [];
for (let i = 0; i < info.length; i++) {
@ -45,29 +39,25 @@ export async function scrapeContent(url: string) {
for (const abc of dataRaw) {
const objectData: ISearchPururin = {
title: abc.attribs["title"],
cover: card ? card[dataRaw.index(abc)] : null,
id: parseInt(abc.attribs["data-gid"]),
language: getPururinLanguage(infoBook[dataRaw.index(abc)]) || "Unknown",
title: abc.attribs["alt"],
cover: abc.attribs["data-src"].replace(/^\/\//, "https://"),
id: parseInt(abc.attribs["data-src"].split("data/")[1].split("/cover")[0]),
info: infoBook[dataRaw.index(abc)],
link: abc.attribs["data-href"],
link: `${c.PURURIN}/gallery/${abc.attribs["data-src"].split("data/")[1].split("/cover")[0]}/janda`,
total: getPururinPageCount(infoBook[dataRaw.index(abc)])
};
content.push(objectData);
}
if (content.length === 0) throw Error("No result found");
const data: IData = {
success: true,
data: content,
page: parseInt(url.split("&page=")[1]),
sort: null,
source: url
sort: url.split("/search/")[1].split("?")[0],
source: c.PURURIN
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
} catch (err: any) {
throw Error(err.message);
}
}

ファイルの表示

@ -1,5 +1,5 @@
import { load } from "cheerio";
import JandaPress from "../../JandaPress";
import p from "phin";
import c from "../../utils/options";
interface ISimplyHentaiGet {
@ -11,18 +11,16 @@ interface ISimplyHentaiGet {
language: string;
}
const janda = new JandaPress();
export async function scrapeContent(url: string) {
try {
const res = await janda.fetchBody(url);
const $ = load(res as Buffer);
const res = await p(url);
const $ = load(res.body as Buffer);
const script = $("script#__NEXT_DATA__");
const json = JSON.parse(script.html() as string);
const dataScrape = json.props.pageProps.data.pages;
const dataScrape: any = json.props.pageProps.data.pages;
const images: string[] = Object.keys(dataScrape)
.map((key: string) => dataScrape[key].sizes.full);
const tagsRaw = json.props.pageProps.data.tags;
const tagsRaw: any = json.props.pageProps.data.tags;
const tags: string[] = Object.keys(tagsRaw).map((key: string) => tagsRaw[key].slug);
const language = json.props.pageProps.data.language;
const metaRaw= json.props.pageProps.meta;
@ -37,13 +35,11 @@ export async function scrapeContent(url: string) {
};
const data = {
success: true,
data: objectData,
source: url,
};
return data;
} catch (err) {
const e = err as Error;
throw Error(e.message);
} catch (err: any) {
throw Error(err.message);
}
}

ファイルの表示

@ -1,70 +1,47 @@
import JandaPress from "../JandaPress";
import p from "phin";
import { load } from "cheerio";
import c from "./options";
import { CookieJar } from "tough-cookie";
import { HttpsCookieAgent } from "http-cookie-agent/http";
import { config } from "dotenv";
import * as pkg from "../../package.json";
config();
const janda = new JandaPress();
const jar = new CookieJar();
jar.setCookie(process.env.CF as string, "https://nhentai.net/");
async function nhentaiStatus(): Promise<boolean> {
const res = await p({
url: "https://nhentai.net/api/galleries/search?query=futanari",
core: {
agent: new HttpsCookieAgent({ cookies: { jar, }, }),
},
"headers": {
"User-Agent": `${pkg.name}/${pkg.version} Node.js/16.9.1`
},
});
if (res.statusCode === 200) {
return true;
} else {
return false;
}
}
/**
* Get Pururin info and replace
* @param value
* @returns string
*/
function getPururinInfo(value: string) {
return value.replace(/\n/g, " ").replace(/\s\s+/g, " ").trim();
}
/**
* Get Pururin page count
* @param value
* @returns number
*/
function getPururinPageCount(value: string) {
const data = value.replace(/\n/g, " ").replace(/\s\s+/g, " ").trim().split(", ").pop();
return Number(data?.split(" ")[0]);
}
/**
* Get Pururin language
* @param value
* @returns string
*/
function getPururinLanguage(value: string) {
return value.split(",").reverse()[1].trim();
}
/**
* Parse url
* @param url
* @returns string
*/
function getUrl(url: string) {
return url.replace(/^\/\//, "https://");
}
/**
* Parse id
* @param url
* @returns string
*/
function getId(url: string) {
return url.replace(/^https?:\/\/[^\\/]+/, "").replace(/\/$/, "");
}
/**
* Parse alphabet only
* @param input
* @returns string
*/
function removeNonNumeric(input: string) {
return input.replace(/[^0-9]/g, "");
}
/**
* Parse date format on nhentai
* @param date
* @returns string
*/
function getDate(date: Date) {
return date.toLocaleDateString("en-US", {
year: "numeric",
@ -73,11 +50,6 @@ function getDate(date: Date) {
});
}
/**
* Fancy time ago format
* @param input
* @returns string
*/
function timeAgo(input: Date) {
const date = new Date(input);
const formatter: any = new Intl.RelativeTimeFormat("en");
@ -99,11 +71,6 @@ function timeAgo(input: Date) {
}
}
/**
* Check nhentai status
* @param url
* @returns boolean
*/
async function mock(url: string) {
const site = await p({ url: url });
if (site.statusCode === 200) {
@ -115,68 +82,9 @@ async function mock(url: string) {
}
}
/**
* Check if string is numeric
* @param val
* @returns boolean
*/
export const isNumeric = (val: string): boolean => {
export const isNumeric = (val: string) : boolean => {
return !isNaN(Number(val));
};
/**
* Simulate random on pururin
* @returns Promise<number>
*/
export async function getIdRandomPururin(): Promise<number> {
const randomNumber = Math.floor(Math.random() * 500) + 1;
const raw = await p(`${c.PURURIN}/browse?sort=newest&page=${randomNumber}`);
const $ = load(raw.body);
const gallery = $(".card.card-gallery").map((i, el) => $(el).attr("href")).get();
const galleryNumber = gallery.map(el => removeNonNumeric(el));
const randomgallery = galleryNumber[Math.floor(Math.random() * galleryNumber.length)];
return parseInt(randomgallery);
}
/**
* Simulate random on nhentai
* @returns Promise<number>
*/
export async function getIdRandomNhentai(): Promise<number> {
if (process.env.NHENTAI_IP_ORIGIN === "false") {
const res: any = await janda.simulateCookie(`${c.NHENTAI}/random`);
const getId = res.socket._httpMessage.path;
return parseInt(getId.replace(/^\/g\/([0-9]+)\/?$/, "$1"));
} else {
const end = 1234;
const start = 567890;
return Math.floor(Math.random() * (end - start + 1)) + start;
}
}
/**
* Error handler
* @param success
* @param message
* @returns object
*/
export function maybeError(success: boolean, message: string) {
return { success, message };
}
/**
* Get nhentai strategy from origin api or simulating the request cookie
* @returns string
*/
export function nhentaiStrategy() {
let strategy: string;
if (process.env.NHENTAI_IP_ORIGIN === "true" || process.env.NHENTAI_IP_ORIGIN === undefined) strategy = c.NHENTAI_IP_4;
else strategy = c.NHENTAI;
return strategy;
}
export {
getPururinInfo, getPururinPageCount, getUrl, getId, getDate, timeAgo,
mock, getPururinLanguage, removeNonNumeric
};
export { getPururinInfo, getPururinPageCount, getUrl, getId, getDate, timeAgo, mock, nhentaiStatus };

ファイルの表示

@ -7,9 +7,4 @@ export default {
NHENTAI: "https://nhentai.net",
NHENTAI_IP: "http://35.186.156.165",
NHENTAI_IP_2: "http://173.82.30.99:3002",
NHENTAI_IP_3: "http://138.2.77.198:3002",
NHENTAI_IP_4: "http://129.150.63.211:3002",
ASMHENTAI: "https://asmhentai.com",
THREEHENTAI: "http://3hentai.net",
NHENTAI_TO: "https://nhentai.to"
};
};

ファイルの表示

@ -1,5 +0,0 @@
export default {
NHENTAI_TO: "cdn.dogehls.xyz",
NHENTAI_TO_SOLVER: "amber.merahputih.moe"
};

ファイルの表示

@ -1,21 +0,0 @@
import p from "phin";
import { load } from "cheerio";
import { name, version } from "../package.json";
const url = "https://nhentai.to/g/272";
async function test() {
const res = await p({
url: url,
"headers": {
"User-Agent": `${name}/${version} Node.js/16.9.1`,
},
});
const $ = load(res.body);
const title = $("title").text();
console.log(title);
console.log(res.statusCode);
}
test().catch(console.error);

ファイルの表示

@ -1,24 +1,24 @@
import p from "phin";
import { CookieJar } from "tough-cookie";
import { HttpsCookieAgent } from "http-cookie-agent/http";
import * as dotenv from "dotenv";
import { config } from "dotenv";
import * as pkg from "../package.json";
config();
dotenv.config();
const jar = new CookieJar();
jar.setCookie(process.env.COOKIE || "", "https://nhentai.net/");
jar.setCookie(process.env.CF as string, "https://nhentai.net/");
async function test() {
async function test(): Promise<void> {
const res = await p({
url: "https://nhentai.net/api/gallery/1",
url: "https://nhentai.net/api/galleries/search?query=futanari",
core: {
agent: new HttpsCookieAgent({ cookies: { jar, }, }),
},
"headers": {
"User-Agent": process.env.USER_AGENT || "jandapress/1.0.5 Node.js/16.9.1",
"User-Agent": `${pkg.name}/${pkg.version} Node.js/16.9.1`
},
});
console.log(res.statusCode);
}
test().catch(console.error);
test();

ファイルの表示

@ -1,32 +1,15 @@
import c from "../src/utils/options";
import p from "phin";
import { name, version } from "../package.json";
function getKeyByValue(data: any, value: string) {
return Object.keys(data).find(key => data[key] === value);
}
for (const url of
[
c.NHENTAI_IP_4,
c.HENTAIFOX,
c.PURURIN,
c.ASMHENTAI,
c.SIMPLY_HENTAI_PROXIFIED,
c.HENTAI2READ,
c.THREEHENTAI
]) {
p({
url: url,
headers: {
"User-Agent": `${name}/${version} Node.js/16.9.1`,
}
}).then(res => {
if (res.statusCode !== 200 && res.statusCode !== 308 && res.statusCode !== 301) {
throw new Error(`${url} of ${getKeyByValue(c, url)} is not available, status: ${res.statusCode}, couldn't be scrape`);
for (const url of
[c.HENTAIFOX, c.PURURIN, c.HENTAI2READ, c.SIMPLY_HENTAI,
c.SIMPLY_HENTAI_PROXIFIED, c.NHENTAI, c.NHENTAI_IP, c.NHENTAI_IP_2]) {
p({ url }).then(res => {
if (res.statusCode !== 200) {
console.log(`${url} is not available, status code: ${res.statusCode}`);
}
else {
console.log(`${url} is available status: ${res.statusCode}, could be scrape`);
console.log(`${url} is available, can be scraped`);
}
});
}

ファイルの表示

@ -13,6 +13,7 @@
"paths": {},
"typeRoots": ["./node_modules/@types"],
"inlineSourceMap": true,
"charset": "UTF-8",
"downlevelIteration": true,
"newLine": "lf",
"strict": true,