UNPKG

nhentai-api-tiffceet-patch

Version:
330 lines (292 loc) 7.46 kB
/** * @module API */ import processOptions from './options'; import http from 'http'; import https from 'https'; import { version, } from '../package.json'; import Search from './search'; import Book from './book'; import Tag from './tag'; import Image from './image'; /** * API arguments * @typedef {object} APIArgs * @property {string} host API host. * @property {Function} apiPath API endpoint URL path generator. */ /** * Class used for building URL paths to nHentai API endpoints. * This class is internal and has only static methods. * @class */ class APIPath { /** * Search by query endpoint. * @param {string} query Search query. * @param {number} [page=1] Page ID. * @returns {string} URL path. */ static search(query, page = 1) { return `/api/galleries/search?query=${query}&page=${page}`; } /** * Search by tag endpoint. * @param {number} tagID Tag ID. * @param {number} [page=1] Page ID. * @returns {string} URL path. */ static searchTagged(tagID, page = 1) { return `/api/galleries/tagged?tag_id=${tagID}&page=${page}`; } /** * Search alike endpoint. * @param {number} bookID Book ID. * @returns {string} URL path. */ static searchAlike(bookID) { return `/api/gallery/${bookID}/related`; } /** * Book content endpoint. * @param {number} bookID Book ID. * @returns {string} URL path. */ static book(bookID) { return `/api/gallery/${bookID}`; } /** * Book's cover image endpoint. * @param {number} mediaID Media ID. * @param {string} extension Image extension. * @returns {string} URL path. */ static bookCover(mediaID, extension) { return `/galleries/${mediaID}/cover.${extension}`; } /** * Book's page image endpoint. * @param {number} mediaID Media ID. * @param {number} page Page ID. * @param {string} extension Image extension. * @returns {string} URL path. */ static bookPage(mediaID, page, extension) { return `/galleries/${mediaID}/${page}.${extension}`; } /** * Book's page's thumbnail image endpoint. * @param {number} mediaID Media ID. * @param {number} page Page ID. * @param {string} extension Image extension. * @returns {string} URL path. */ static bookThumb(mediaID, page, extension) { return `/galleries/${mediaID}/${page}t.${extension}`; } } /** * Class used for interaction with nHentai API. * @class */ class API { /** * API path class * @type {APIPath} * @static * @private */ static APIPath = APIPath; /** * Applies provided options on top of defaults. * @param {nHentaiOptions} options Options to apply. */ constructor(options = {}) { let params = processOptions(options); Object.assign(this, params); } /** * Get http(s) module depending on `options.ssl`. * @type {https|http} */ get net() { return this.ssl ? https : http; } /** * JSON get request. * @param {object} options HTTP(S) request options. * @param {string} options.host Host. * @param {string} options.path Path. * @returns {Promise<object>} Parsed JSON. */ request(options) { let { net, agent, } = this; return new Promise((resolve, reject) => { Object.assign(options, { agent, headers: { 'User-Agent': `nhentai-api-client/${version} Node.js/${process.versions.node}`, }, }); net.get(options, response => { const { statusCode, } = response, contentType = response.headers['content-type']; let error; if (statusCode !== 200) error = new Error(`Request failed with status code ${statusCode}`); else if (!(/^application\/json/).test(contentType)) error = new Error(`Invalid content-type - expected application/json but received ${contentType}`); if (error) { response.resume(); reject(error); return; } response.setEncoding('utf8'); let rawData = ''; response.on('data', (chunk) => rawData += chunk); response.on('end', () => { try { resolve(JSON.parse(rawData)); } catch (error) { reject(error); } }); }).on('error', error => reject(error)); }); } /** * Get API arguments. * This is internal method. * @param {string} hostType Host type. * @param {string} api Endpoint type. * @returns {APIArgs} API arguments. * @private */ getAPIArgs(hostType, api) { let { hosts: { [hostType]: host, }, constructor: { APIPath: { [api]: apiPath, }, }, } = this; return { host, apiPath, }; } /** * Search by query. * @param {string} query Query. * @param {number} [page=1] Page ID. * @returns {Promise<Search>} Search instance. * @async */ async search(query, page = 1) { let { host, apiPath, } = this.getAPIArgs('api', 'search'), search = Search.parse( await this.request({ host, path: apiPath(query, page), }) ); search.page = page; return search; } /** * Search related books. * @param {number|Book} book Book instance or Book ID. * @returns {Promise<Search>} Search instance. * @async */ async searchAlike(book) { let { host, apiPath, } = this.getAPIArgs('api', 'searchAlike'); return Search.parse( await this.request({ host, path: apiPath( book instanceof Book ? book.id : +book ), }) ); } /** * Search by tag id. * @param {number|Tag} tag Tag or Tag ID. * @param {number} [page=1] Page ID. * @returns {Promise<Search>} Search instance. * @async */ async searchTagged(tag, page = 1) { let { host, apiPath, } = this.getAPIArgs('api', 'searchTagged'), search = Search.parse( await this.request({ host, path: apiPath( tag instanceof Tag ? tag.id : +tag, page ), }) ); search.page = page; return search; } /** * Get book by id. * @param {number} bookID Book ID. * @returns {Promise<Book>} Book instance. * @async */ async getBook(bookID) { let { host, apiPath, } = this.getAPIArgs('api', 'book'); return Book.parse( await this.request({ host, path: apiPath(bookID), }) ); } /** * Get image URL. * @param {Image} image Image. * @returns {string} Image URL. */ getImageURL(image) { if (image instanceof Image) { let { host, apiPath, } = image.isCover ? this.getAPIArgs('thumbs', 'bookCover') : this.getAPIArgs('images', 'bookPage'); return `http${this.ssl ? 's' : ''}://${host}` + (image.isCover ? apiPath(image.book.media, image.type.extension) : apiPath(image.book.media, image.id, image.type.extension)); } throw new Error('image must be Image instance.'); } /** * Get image thumbnail URL. * @param {Image} image Image. * @returns {string} Image thumbnail URL. */ getThumbURL(image) { if (image instanceof Image && !image.isCover) { let { host, apiPath, } = this.getAPIArgs('thumbs', 'bookThumb'); return `http${this.ssl ? 's' : ''}://${host}` + apiPath(image.book.media, image.id, image.type.extension); } throw new Error('image must be Image instance and not book cover.'); } } export default API;