UNPKG

nhentai.js-api

Version:
232 lines (231 loc) 11 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NHentai = exports.NHSort = exports.NHSearchResults = void 0; const jsdom_1 = require("jsdom"); const https_1 = __importDefault(require("https")); const search_results_1 = __importStar(require("./search_results")); exports.NHSearchResults = search_results_1.default; Object.defineProperty(exports, "NHSort", { enumerable: true, get: function () { return search_results_1.NHSort; } }); class NHentai { /** * @throws if number is negative * @throws if url is falsey or does not match NHentai.nhentaiRegex */ hentai(url) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { if (typeof url === 'number') { if (isNaN(url) || url < 0) { throw new Error('Not a valid number'); } url = `https://nhentai.net/g/${url}`; } else if (!url) { throw new Error('URL should not be falsey'); } else if (!NHentai.nhentaiRegex.test(url)) { throw new Error('URL is not a valid nhentai url'); } const doc = (yield jsdom_1.JSDOM.fromURL(url)).window.document; url = doc.location.href; const id = +url.match(/\d+/)[0]; const thumbnails = Array.from(doc.querySelectorAll('div.thumbs img.lazyload')).map((el) => el.getAttribute('data-src')); const title = doc .querySelector('meta[itemprop=name]') .getAttribute('content'); const recommended = search_results_1.default.collectHentai(doc, this); const comments = () => { return new Promise((resolve, reject) => { https_1.default.get(`https://nhentai.net/api/gallery/${id}/comments`, (res) => { res.setEncoding('utf8'); let body = ''; res.on('data', (data) => { body += data; }); res.on('error', (error) => { reject(error); }); res.on('end', () => { const json = JSON.parse(body); if (res.statusCode === 200) { resolve(json.map((c) => { c.poster.avatar_url = `https://i.nhentai.net/${c.poster.avatar_url}`; c.poster.url = `https://nhentai.net/users/${c.poster.id}/${c.poster.slug}`; c.post_date *= 1000; c.user = () => this.user(c.poster.id, c.poster.slug); c.hentai = () => this.hentai(c.gallery_id); return c; })); } else { reject(json); } }); }); }); }; return { title, cleanTitle: title.replace(/\([^(]+\)|\[[^\[]+\]/g, '').trim() || title, id, url: url, cover: (_a = doc .querySelector('meta[itemprop=image]')) === null || _a === void 0 ? void 0 : _a.getAttribute('content'), images: thumbnails.map((src) => src.replace('t.', 'i.').replace('t.', '.')), thumbnails, tags: { tags: this.tags(doc, 'Tags', 'tag'), parodies: this.tags(doc, 'Parodies', 'parody'), characters: this.tags(doc, 'Characters', 'category'), artists: this.tags(doc, 'Artists', 'artist'), groups: this.tags(doc, 'Groups', 'group'), languages: this.tags(doc, 'Languages', 'language'), categories: this.tags(doc, 'Categories', 'category') }, uploaded: new Date((_b = doc.querySelector('span.tags > time')) === null || _b === void 0 ? void 0 : _b.getAttribute('datetime')).getTime(), pages: thumbnails.length, recommended, comments }; }); } tags(doc, type, urlPart) { return Array.from(Array.from(doc.querySelectorAll('div.tag-container')) .find((el) => { var _a; return (_a = el.textContent) === null || _a === void 0 ? void 0 : _a.includes(type); }) .querySelectorAll('span a span')) .map((span, i, arr) => { var _a, _b; return i % 2 === 0 ? { name: span.textContent, amount: this.toNumber((_a = arr[i + 1]) === null || _a === void 0 ? void 0 : _a.textContent), amountString: ((_b = arr[i + 1]) === null || _b === void 0 ? void 0 : _b.textContent) || '0', url: `https://nhentai.net/${urlPart}/` + span .textContent.toLowerCase() .replace(/ +/g, '-') } : undefined; }) .filter((tag) => !!tag); } toNumber(text) { if (!text) { return 0; } else if (text.endsWith('K')) { return Number.parseInt(text.replace('K', '000')); } else if (text.endsWith('M')) { return Number.parseInt(text.replace('M', '000000')); } else { return Number.parseInt(text); } } /** * Get a random hentai from nhentai.net * * @param english true if random hentai should be english * @returns a random hentai */ random(english) { return __awaiter(this, void 0, void 0, function* () { if (!english) { return this.hentai('https://nhentai.net/random/'); } else { const results = yield this.search('language:english'); const randomPage = Math.floor(Math.random() * results.pages) + 1; yield results.goto(randomPage); const randomHentai = Math.floor(Math.random() * results.hentai.length); return this.hentai(results.hentai[randomHentai].id); } }); } user(id, slug) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { slug = slug .toLowerCase() .replace(/[^a-z0-9_\-]/g, ' ') .replace(/ +/g, '-'); const doc = (yield jsdom_1.JSDOM.fromURL(`https://nhentai.net/users/${id}/${slug}`)).window.document; const container = doc.querySelector('#user-container'); const avatarImg = container.querySelector('div.bigavatar img'); const userInfo = container.querySelector('div.user-info'); const pEls = userInfo.querySelectorAll('p'); const pWith = (tag, has) => { var _a; for (const p of pEls) { const el = p.querySelector(tag); if ((_a = el === null || el === void 0 ? void 0 : el.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(has.toLowerCase())) { return p; } } }; return { avatar: avatarImg.getAttribute('src'), username: userInfo.querySelector('h1').textContent, joined: new Date(userInfo.querySelector('p time').getAttribute('datetime')).getTime(), about: (_a = pWith('b', 'About')) === null || _a === void 0 ? void 0 : _a.textContent, favoriteTags: (_b = pWith('b', 'Favorite tags')) === null || _b === void 0 ? void 0 : _b.textContent, recentComments: Array.from(doc.querySelectorAll('div.container div.comment')).map((el) => { const wuckyJson = el.getAttribute('data-state'); const json = wuckyJson.replace(/&#34;/g, '"'); const comment = JSON.parse(json); comment.poster.avatar_url = `https://i.nhentai.net/${comment.poster.avatar_url}`; comment.poster.url = `https://nhentai.net/users/${comment.poster.id}/${comment.poster.slug}`; comment.post_date *= 1000; comment.user = () => this.user(comment.poster.id, comment.poster.slug); comment.hentai = () => this.hentai(comment.gallery_id); return comment; }), recentFavorites: search_results_1.default.collectHentai(doc, this) }; }); } /** * Search for a hentai on nhentai.net * * @returns {Promise<NHSearchResults>} search results */ search(query, sort) { return __awaiter(this, void 0, void 0, function* () { return new search_results_1.default(this, query, sort).lookup(); }); } } exports.NHentai = NHentai; NHentai.nhentaiRegex = /^https?:\/\/nhentai.net\/(g\/\d+|random)\/?$/;