Skip to content

Commit

Permalink
feat: add 3hentai to the supported sites
Browse files Browse the repository at this point in the history
  • Loading branch information
sinkaroid committed Oct 1, 2022
1 parent ac7e0db commit 58f9993
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 1 deletion.
26 changes: 26 additions & 0 deletions src/controller/3hentai/3hentaiGet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { scrapeContent } from "../../scraper/3hentai/3hentaiGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { isNumeric } from "../../utils/modifier";
import { Request, Response, NextFunction } from "express";

export async function get3hentai(req: Request, res: Response, next: NextFunction) {
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");

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: any) {
next(Error(err.message));
}
}
21 changes: 21 additions & 0 deletions src/controller/3hentai/3hentaiRandom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { scrapeContent } from "../../scraper/3hentai/3hentaiGetController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { Request, Response, NextFunction } from "express";

export async function random3hentai(req: Request, res: Response, next: NextFunction) {
try {
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: any) {
next(Error(err.message));
}
}
28 changes: 28 additions & 0 deletions src/controller/3hentai/3hentaiSearch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { scrapeContent } from "../../scraper/3hentai/3hentaiSearchController";
import c from "../../utils/options";
import { logger } from "../../utils/logger";
import { Request, Response, NextFunction } from "express";
const sorting = ["recent", "popular-24h", "popular-7d", "popular"];

export async function search3hentai(req: Request, res: Response, next: NextFunction) {
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(", "));

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: any) {
next(Error(err.message));
}
}
7 changes: 7 additions & 0 deletions src/router/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import { randomNhentai } from "../controller/nhentai/nhentaiRandom";
import { getAsmhentai } from "../controller/asmhentai/asmhentaiGet";
import { searchAsmhentai } from "../controller/asmhentai/asmhentaiSearch";
import { randomAsmhentai } from "../controller/asmhentai/asmhentaiRandom";
import { get3hentai } from "../controller/3hentai/3hentaiGet";
import { search3hentai } from "../controller/3hentai/3hentaiSearch";
import { random3hentai } from "../controller/3hentai/3hentaiRandom";

import { slow, limiter } from "../utils/limit-options";

function scrapeRoutes() {
Expand All @@ -36,6 +40,9 @@ function scrapeRoutes() {
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);

return router;
}
Expand Down
57 changes: 57 additions & 0 deletions src/scraper/3hentai/3hentaiGetController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { load } from "cheerio";
import p from "phin";
import c from "../../utils/options";


interface IGet3hentai {
title: string;
id: number;
tags: string[];
total: number;
image: string[];
upload_date: string;

}

interface IData{
data: object;
source: string;
}

export async function scrapeContent(url: string) {
try {
const res = await p({ url: url, followRedirects: true });
const $ = load(res.body);

//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();
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 = {
data: objectData,
source: `${c.THREEHENTAI}/d/${id ? id : book}`,
};
return data;
} catch (err) {
const error = err as string;
throw new Error(error);
}
}
51 changes: 51 additions & 0 deletions src/scraper/3hentai/3hentaiSearchController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { load } from "cheerio";
import p from "phin";
import { removeNonNumeric } from "../../utils/modifier";

interface I3HentaiSearch {
title: string;
id: number;
}

export async function scrapeContent(url: string) {
try {
const res = await p({ url: url, followRedirects: true });
const $ = load(res.body as Buffer);

//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);
}

const data = {
data: content,
page: parseInt(url.split("&page=")[1]),
sort: url.split("sort=")[1],
source: url
};
return data;

} catch (err) {
const error = err as string;
throw new Error(error);
}
}
3 changes: 2 additions & 1 deletion src/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export default {
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",
ASMHENTAI: "https://asmhentai.com"
ASMHENTAI: "https://asmhentai.com",
THREEHENTAI: "http://3hentai.net"
};

0 comments on commit 58f9993

Please sign in to comment.