nhentai-api
Version:
nHentai Node.JS API client.
1 lines • 51 kB
Source Map (JSON)
{"version":3,"file":"bundle.cjs","sources":["../../src/tag.js","../../src/image.js","../../src/tagsArray.js","../../src/book.js","../../src/search.js","../../src/error.js","../../src/api.js","../../src/options.js","../../src/index.js"],"sourcesContent":["/**\r\n * @module Tag\r\n */\r\n\r\n/**\r\n * Tag object from API.\r\n * @global\r\n * @typedef {object} APITag\r\n * @property {number|string} id Tag id.\r\n * @property {string} type Tag type.\r\n * @property {string} name Tag name.\r\n * @property {number|string} count Tagged books count.\r\n * @property {string} url Tag URL.\r\n */\r\n\r\n/**\r\n * @typedef {object} TagTypes\r\n * @property {UnknownTagType} Unknown Unknown tag type.\r\n * @property {TagType} Tag Tag tag type.\r\n * @property {TagType} Category Category tag type.\r\n * @property {TagType} Artist Artist tag type.\r\n * @property {TagType} Parody Parody tag type.\r\n * @property {TagType} Character Character tag type.\r\n * @property {TagType} Group Group tag type.\r\n * @property {TagType} Language Language tag type.\r\n */\r\n\r\n/**\r\n * Class representing tag type.\r\n * @class\r\n */\r\nclass TagType {\r\n\t/**\r\n\t * @type {TagTypes}\r\n\t * @static\r\n\t * @default {}\r\n\t */\r\n\tstatic knownTypes = {};\r\n\r\n\t/**\r\n\t * Tag type name.\r\n\t * @type {?string}\r\n\t * @default null\r\n\t */\r\n\ttype = null;\r\n\r\n\t/**\r\n\t * Create tag type.\r\n\t * @param {string} type Tag type.\r\n\t */\r\n\tconstructor(type) {\r\n\t\tif (type) {\r\n\t\t\tthis.type = type;\r\n\t\t\tthis.constructor.knownTypes[type] = this;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Check if this tag type is unknown.\r\n\t * @type {boolean}\r\n\t */\r\n\tget isKnown() {\r\n\t\treturn !(this instanceof UnknownTagType);\r\n\t}\r\n\r\n\t/**\r\n\t * Tag type name.\r\n\t * @returns {string}\r\n\t */\r\n\ttoString() {\r\n\t\treturn this.type;\r\n\t}\r\n}\r\n\r\n/**\r\n * Class representing unknown tag type.\r\n * @class\r\n * @extends TagType\r\n */\r\nclass UnknownTagType extends TagType {\r\n\t/**\r\n\t * Create unknown tag type.\r\n\t * @param {string} [type=\"unknown\"] Unknown tag type name.\r\n\t */\r\n\tconstructor(type = 'unknown') {\r\n\t\tsuper(null);\r\n\t\tthis.type = type;\r\n\t}\r\n}\r\n\r\n/**\r\n * Class representing tag.\r\n * @class\r\n */\r\nclass Tag {\r\n\t/**\r\n\t * Tag types.\r\n\t * @type {TagTypes}\r\n\t * @static\r\n\t */\r\n\tstatic types = {\r\n\t\tUnknown : new UnknownTagType(), // Symbol('unknown')\r\n\t\tTag : new TagType('tag'),\r\n\t\tCategory : new TagType('category'),\r\n\t\tArtist : new TagType('artist'),\r\n\t\tParody : new TagType('parody'),\r\n\t\tCharacter: new TagType('character'),\r\n\t\tGroup : new TagType('group'),\r\n\t\tLanguage : new TagType('language'),\r\n\r\n\t\t/**\r\n\t\t * Known tag types.\r\n\t\t * @type {TagTypes}\r\n\t\t */\r\n\t\tknown: TagType.knownTypes,\r\n\r\n\t\t/**\r\n\t\t * Get tag type class instance by name.\r\n\t\t * @param {string} type Tag type.\r\n\t\t * @returns {TagType|UnknownTagType} Tag type class instance.\r\n\t\t */\r\n\t\tget(type) {\r\n\t\t\tlet known;\r\n\t\t\tif ('string' === typeof type)\r\n\t\t\t\ttype = type.toLowerCase();\r\n\t\t\treturn ((known = this.known[type]))\r\n\t\t\t\t? known\r\n\t\t\t\t: new UnknownTagType(type);\r\n\t\t},\r\n\t};\r\n\r\n\t/**\r\n\t * Warp tag object with Tag class instance.\r\n\t * @param {APITag|Tag} tag Tag to wrap.\r\n\t * @returns {Tag} Tag.\r\n\t * @static\r\n\t */\r\n\tstatic get(tag) {\r\n\t\tif (!(tag instanceof this))\r\n\t\t\ttag = new this({\r\n\t\t\t\tid : +tag.id,\r\n\t\t\t\ttype : tag.type,\r\n\t\t\t\tname : tag.name,\r\n\t\t\t\tcount: +tag.count,\r\n\t\t\t\turl : tag.url,\r\n\t\t\t});\r\n\t\treturn tag;\r\n\t}\r\n\r\n\t/**\r\n\t * Tag ID.\r\n\t * @type {number}\r\n\t * @default 0\r\n\t */\r\n\tid = 0;\r\n\r\n\t/**\r\n\t * Tag type.\r\n\t * @type {TagType|UnknownTagType}\r\n\t * @default TagTypes.Unknown\r\n\t */\r\n\ttype = this.constructor.types.Unknown;\r\n\r\n\t/**\r\n\t * Tag name.\r\n\t * @type {string}\r\n\t * @default \"\"\r\n\t */\r\n\tname = '';\r\n\r\n\t/**\r\n\t * Count of books tagged with this tag.\r\n\t * @type {number}\r\n\t * @default 0\r\n\t */\r\n\tcount = 0;\r\n\r\n\t/**\r\n\t * Tag URL.\r\n\t * @type {string}\r\n\t * @default \"\"\r\n\t */\r\n\turl = '';\r\n\r\n\t/**\r\n\t * Create tag.\r\n\t * @param {object} [params] Tag parameters.\r\n\t * @param {number} [params.id=0] Tag id.\r\n\t * @param {string|TagType} [params.type=TagTypes.Unknown] Tag type.\r\n\t * @param {string} [params.name=\"\"] Tag name.\r\n\t * @param {number} [params.count=0] Tagged books count.\r\n\t * @param {string} [params.url=\"\"] Tag URL.\r\n\t */\r\n\tconstructor({\r\n\t\tid = 0,\r\n\t\ttype = this.constructor.types.Unknown,\r\n\t\tname = '',\r\n\t\tcount = 0,\r\n\t\turl = '',\r\n\t} = {}) {\r\n\t\tObject.assign(this, {\r\n\t\t\tid,\r\n\t\t\ttype: type instanceof TagType\r\n\t\t\t\t? type\r\n\t\t\t\t: this.constructor.types.get(type),\r\n\t\t\tname,\r\n\t\t\tcount,\r\n\t\t\turl,\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Compare this to given one.\r\n\t * By default tags with different id will return false.\r\n\t * If you want to check whatever tag has any of properties from another tag pass `'any'` to `strict` parameter.\r\n\t * @param {string|Tag} tag Tag to compare with.\r\n\t * @param {boolean|string} [strict=false] Whatever all parameters must be the same.\r\n\t * @returns {boolean} Whatever tags are equal.\r\n\t */\r\n\tcompare(tag, strict = false) {\r\n\t\ttag = this.constructor.get(tag);\r\n\t\tif (strict === 'any')\r\n\t\t\tstrict = false;\r\n\t\telse if (this.id !== tag.id)\r\n\t\t\treturn false;\r\n\r\n\t\treturn !![\r\n\t\t\t'id',\r\n\t\t\t'type',\r\n\t\t\t'name',\r\n\t\t\t'count',\r\n\t\t\t'url',\r\n\t\t].map(\r\n\t\t\tprop => tag[prop] === this[prop]\r\n\t\t).reduce(\r\n\t\t\t(accum, current) => strict\r\n\t\t\t\t? accum * current\r\n\t\t\t\t: accum + current\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * Get tag name or tag name with count of tagged books.\r\n\t * @param {?boolean} [includeCount=false] Include count.\r\n\t * @returns {string}\r\n\t */\r\n\ttoString(includeCount = false) {\r\n\t\treturn this.name + (includeCount\r\n\t\t\t? ` (${this.count})`\r\n\t\t\t: '');\r\n\t}\r\n}\r\n\r\nexport {\r\n\tTag,\r\n\tTagType,\r\n\tUnknownTagType,\r\n};\r\n","/**\r\n * @module Image\r\n */\r\n\r\nimport Book from './book';\r\n\r\n/**\r\n * Image object from API.\r\n * @global\r\n * @typedef {object} APIImage\r\n * @property {string} t Image type.\r\n * @property {number|string} w Image width.\r\n * @property {number|string} h Image height.\r\n */\r\n\r\n/**\r\n * @typedef {object} ImageTypes\r\n * @property {TagType} JPEG JPEG image type.\r\n * @property {TagType} PNG PNG image type.\r\n * @property {TagType} GIF GIF image type.\r\n */\r\n\r\n/**\r\n * Class representing image type.\r\n * @class\r\n */\r\nclass ImageType {\r\n\t/**\r\n\t * @type {ImageTypes}\r\n\t * @static\r\n\t * @default {}\r\n\t */\r\n\tstatic knownTypes = {};\r\n\r\n\t/**\r\n\t * Image type name.\r\n\t * @type {?string}\r\n\t * @default null\r\n\t */\r\n\ttype = null;\r\n\r\n\t/**\r\n\t * Image type extension.\r\n\t * @type {?string}\r\n\t * @default null\r\n\t */\r\n\textension = null;\r\n\r\n\t/**\r\n\t * Create image type.\r\n\t * @param {string} type Image type name.\r\n\t * @param {string} extension Image type extension.\r\n\t */\r\n\tconstructor(type, extension) {\r\n\t\tif (type) {\r\n\t\t\tthis.type = type;\r\n\t\t\tthis.constructor.knownTypes[type] = this;\r\n\t\t}\r\n\t\tthis.extension = extension;\r\n\t}\r\n\r\n\t/**\r\n\t * Whatever this tag type is unknown.\r\n\t * @type {boolean}\r\n\t */\r\n\tget isKnown() {\r\n\t\treturn !(this instanceof UnknownImageType);\r\n\t}\r\n\r\n\t/**\r\n\t * Alias for type.\r\n\t * @type {?string}\r\n\t */\r\n\tget name() {\r\n\t\treturn this.type;\r\n\t}\r\n}\r\n\r\n/**\r\n * Class representing unknown image type.\r\n * @class\r\n * @extends ImageType\r\n */\r\nclass UnknownImageType extends ImageType {\r\n\t/**\r\n\t * Create unknown image type.\r\n\t * @param {string} type Unknown image type name.\r\n\t * @param {string} extension Unknown image type extension.\r\n\t */\r\n\tconstructor(type, extension) {\r\n\t\tsuper(null, extension);\r\n\t\tthis.type = type;\r\n\t}\r\n}\r\n\r\n/**\r\n * Class representing image.\r\n * @class\r\n */\r\nclass Image {\r\n\t/**\r\n\t * Image types.\r\n\t * @type {ImageTypes}\r\n\t * @static\r\n\t */\r\n\tstatic types = {\r\n\t\tJPEG: new ImageType('jpeg', 'jpg'),\r\n\t\tPNG : new ImageType('png', 'png'),\r\n\t\tGIF : new ImageType('gif', 'gif'),\r\n\r\n\t\tUnknown: new UnknownImageType('unknown', 'unknownExt'),\r\n\r\n\t\t/**\r\n\t\t * Known image types.\r\n\t\t * @type {ImageType}\r\n\t\t */\r\n\t\tknown: ImageType.knownTypes,\r\n\r\n\t\t/**\r\n\t\t * Get image type class instance by name.\r\n\t\t * @param {string} type Image type.\r\n\t\t * @returns {ImageType|UnknownImageType} Image type class instance.\r\n\t\t */\r\n\t\tget(type) {\r\n\t\t\tlet known;\r\n\t\t\tif ('string' === typeof type) {\r\n\t\t\t\ttype = type.toLowerCase();\r\n\t\t\t\tswitch (type) {\r\n\t\t\t\t\tcase 'j':\r\n\t\t\t\t\tcase 'jpg':\r\n\t\t\t\t\tcase 'jpeg':\r\n\t\t\t\t\t\ttype = 'jpeg';\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase 'p':\r\n\t\t\t\t\tcase 'png':\r\n\t\t\t\t\t\ttype = 'png';\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tcase 'g':\r\n\t\t\t\t\tcase 'gif':\r\n\t\t\t\t\t\ttype = 'gif';\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn ((known = this.known[type]))\r\n\t\t\t\t? known\r\n\t\t\t\t: new UnknownImageType(type);\r\n\t\t},\r\n\t};\r\n\r\n\t/**\r\n\t * Parse pure image object from API into class instance.\r\n\t * @param {APIImage} image Image object\r\n\t * @param {number} [id=0] Image id (a.k.a. page number).\r\n\t * @returns {Image} Image instance.\r\n\t * @static\r\n\t */\r\n\tstatic parse(image, id = 0) {\r\n\t\tlet {\r\n\t\t\tt: type,\r\n\t\t\tw: width,\r\n\t\t\th: height,\r\n\t\t} = image;\r\n\r\n\t\treturn new this({\r\n\t\t\ttype,\r\n\t\t\twidth : +width,\r\n\t\t\theight: +height,\r\n\t\t\tid,\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Image ID.\r\n\t * @type {number}\r\n\t * @default 0\r\n\t */\r\n\tid = 0;\r\n\r\n\t/**\r\n\t * Image width.\r\n\t * @type {number}\r\n\t * @default 0\r\n\t */\r\n\twidth = 0;\r\n\r\n\t/**\r\n\t * Image height.\r\n\t * @type {number}\r\n\t * @default 0\r\n\t */\r\n\theight = 0;\r\n\r\n\t/**\r\n\t * Image type.\r\n\t * @type {ImageType}\r\n\t * @default ImageTypes.JPEG\r\n\t */\r\n\ttype = this.constructor.types.JPEG;\r\n\r\n\t/**\r\n\t * Image parent book.\r\n\t * @type {Book}\r\n\t * @default Book.Unknown\r\n\t */\r\n\tbook = Book.Unknown;\r\n\r\n\t/**\r\n\t * Create image.\r\n\t * @param {object} [params] Image parameters.\r\n\t * @param {number} [params.id=0] Image ID.\r\n\t * @param {number} [params.width=0] Image width.\r\n\t * @param {number} [params.height=0] Image height.\r\n\t * @param {string|ImageType} [params.type=ImageTypes.JPEG] Image type.\r\n\t * @param {Book} [params.book=Book.Unknown] Image's Book.\r\n\t */\r\n\tconstructor({\r\n\t\tid = 0,\r\n\t\twidth = 0,\r\n\t\theight = 0,\r\n\t\ttype = this.constructor.types.JPEG,\r\n\t\tbook = Book.Unknown,\r\n\t} = {}) {\r\n\t\tObject.assign(this, {\r\n\t\t\tid: 'number' === typeof id\r\n\t\t\t\t? id < 1 ? 0 : id\r\n\t\t\t\t: 0,\r\n\t\t\twidth,\r\n\t\t\theight,\r\n\t\t\ttype: type instanceof ImageType\r\n\t\t\t\t? type\r\n\t\t\t\t: this.constructor.types.get(type),\r\n\t\t\tbook: book instanceof Book\r\n\t\t\t\t? book\r\n\t\t\t\t: Book.Unknown,\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Whatever this image is book cover.\r\n\t * @type {boolean}\r\n\t */\r\n\tget isCover() {\r\n\t\treturn this.id < 1;\r\n\t}\r\n\r\n\t/**\r\n\t * Image filename.\r\n\t * @type {string}\r\n\t */\r\n\tget filename() {\r\n\t\treturn `${this.isCover ? 'cover' : this.id}.${this.type.extension}`;\r\n\t}\r\n}\r\n\r\nexport default Image;\r\n","// eslint-disable-next-line no-unused-vars\r\nimport { Tag, } from './tag';\r\n\r\n\r\n/**\r\n * Array of Tags with helper methods.\r\n * @class\r\n * @extends Array<Tag>\r\n */\r\nclass TagsArray extends Array {\r\n\tconstructor(...args) {\r\n\t\tsuper(...args);\r\n\t}\r\n\r\n\t/**\r\n\t * Get array of tags names.\r\n\t * @param {?boolean} [includeCount=false] Include count.\r\n\t * @returns {String[]}\r\n\t */\r\n\ttoNames(includeCount = false) {\r\n\t\treturn Array.from(this, tag => tag.toString(includeCount));\r\n\t}\r\n}\r\n\r\nexport default TagsArray;\r\n","/**\r\n * @module Book\r\n */\r\n\r\nimport Image from './image';\r\nimport { Tag, } from './tag';\r\nimport TagsArray from './tagsArray';\r\n\r\nimport { TagTypes, } from '.';\r\n\r\n\r\n/**\r\n * Book object from API.\r\n * @global\r\n * @typedef {object} APIBook\r\n * @property {object} title Book title.\r\n * @property {string} title.english Book english title.\r\n * @property {string} title.japanese Book japanese title.\r\n * @property {string} title.pretty Book short title.\r\n * @property {number|string} id Book ID.\r\n * @property {number|string} media_id Book Media ID.\r\n * @property {number|string} num_favorites Book favours count.\r\n * @property {number|string} num_pages Book pages count.\r\n * @property {string} scanlator Book scanlator.\r\n * @property {number|string} uploaded Upload UNIX timestamp.\r\n * @property {APIImage} cover Book cover image.\r\n * @property {APIImage[]} images Book pages' images.\r\n * @property {APITag[]} tags Book tags.\r\n */\r\n\r\n/**\r\n * Book title.\r\n * @typedef {object} BookTitle\r\n * @property {string} english Book english title.\r\n * @property {string} japanese Book japanese title.\r\n * @property {string} pretty Book short title.\r\n */\r\n\r\n/**\r\n * Class representing Book.\r\n * @class\r\n */\r\nclass Book {\r\n\t/**\r\n\t * Unknown book instance.\r\n\t * @type {UnknownBook}\r\n\t * @static\r\n\t */\r\n\tstatic Unknown;\r\n\r\n\t/**\r\n\t * UnknownBook class.\r\n\t * @type {UnknownBook}\r\n\t * @static\r\n\t */\r\n\tstatic UnknownBook;\r\n\r\n\t/**\r\n\t * Parse book object into class instance.\r\n\t * @param {APIBook} book Book.\r\n\t * @returns {Book} Book instance.\r\n\t * @static\r\n\t */\r\n\tstatic parse(book) {\r\n\t\treturn new this({\r\n\t\t\ttitle : book.title,\r\n\t\t\tid : +book.id,\r\n\t\t\tmedia : +book.media_id,\r\n\t\t\tfavorites: +book.num_favorites,\r\n\t\t\tscanlator: book.scanlator,\r\n\t\t\tuploaded : new Date(+book.upload_date * 1000),\r\n\t\t\ttags : TagsArray.from(book.tags, tag => Tag.get(tag)),\r\n\t\t\tcover : Image.parse(book.images.cover),\r\n\t\t\tpages : book.images.pages.map(\r\n\t\t\t\t(image, id) => Image.parse(image, ++id)\r\n\t\t\t),\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Book title.\r\n\t * @type {BookTitle}\r\n\t */\r\n\ttitle = {\r\n\t\tenglish : '',\r\n\t\tjapanese: '',\r\n\t\tpretty : '',\r\n\t};\r\n\r\n\t/**\r\n\t * Book ID.\r\n\t * @type {number}\r\n\t * @default 0\r\n\t */\r\n\tid = 0;\r\n\r\n\t/**\r\n\t * Book Media ID.\r\n\t * @type {number}\r\n\t * @default 0\r\n\t */\r\n\tmedia = 0;\r\n\r\n\t/**\r\n\t * Book favours count.\r\n\t * @type {number}\r\n\t * @default 0\r\n\t */\r\n\tfavorites = 0;\r\n\r\n\t/**\r\n\t * Book scanlator.\r\n\t * @type {string}\r\n\t * @default ''\r\n\t */\r\n\tscanlator = '';\r\n\r\n\t/**\r\n\t * Book upload date.\r\n\t * @type {Date}\r\n\t * @default new Date(0)\r\n\t */\r\n\tuploaded = new Date(0);\r\n\r\n\t/**\r\n\t * Book tags.\r\n\t * @type {TagsArray}\r\n\t * @default []\r\n\t */\r\n\ttags = new TagsArray();\r\n\r\n\t/**\r\n\t * Book cover.\r\n\t * @type {Image}\r\n\t */\r\n\tcover = new Image({ id: 0, book: this, });\r\n\r\n\t/**\r\n\t * Book pages.\r\n\t * @type {Image[]}\r\n\t * @default []\r\n\t */\r\n\tpages = [];\r\n\r\n\t/**\r\n\t * Create book.\r\n\t * @param {object} [params] Book parameters.\r\n\t * @param {BookTitle} [params.title] Book title.\r\n\t * @param {number} [params.id=0] Book ID.\r\n\t * @param {number} [params.media=0] Book Media ID.\r\n\t * @param {number} [params.favorites=0] Book favours count.\r\n\t * @param {string} [params.scanlator=''] Book scanlator.\r\n\t * @param {Date} [params.uploaded] Book upload date.\r\n\t * @param {Tag[]|TagsArray} [params.tags=[]] Book tags.\r\n\t * @param {Image} [params.cover] Book cover.\r\n\t * @param {Image[]} [params.pages=[]] Book pages.\r\n\t */\r\n\tconstructor({\r\n\t\ttitle = {\r\n\t\t\tenglish : '',\r\n\t\t\tjapanese: '',\r\n\t\t\tpretty : '',\r\n\t\t},\r\n\t\tid = 0,\r\n\t\tmedia = 0,\r\n\t\tfavorites = 0,\r\n\t\tscanlator = '',\r\n\t\tuploaded = new Date(0),\r\n\t\ttags = new TagsArray(),\r\n\t\tcover = new Image({ id: 0, book: this, }),\r\n\t\tpages = [],\r\n\t} = {}) {\r\n\t\tthis.setCover(cover);\r\n\r\n\t\tif (Array.isArray(pages))\r\n\t\t\tpages.forEach(this.pushPage.bind(this));\r\n\r\n\t\tif (Array.isArray(tags))\r\n\t\t\ttags.forEach(this.pushTag.bind(this));\r\n\r\n\t\tObject.assign(this, {\r\n\t\t\ttitle,\r\n\t\t\tid,\r\n\t\t\tmedia,\r\n\t\t\tfavorites,\r\n\t\t\tscanlator,\r\n\t\t\tuploaded,\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Check whatever book is known.\r\n\t * @type {boolean}\r\n\t */\r\n\tget isKnown() {\r\n\t\treturn !(this instanceof UnknownBook);\r\n\t}\r\n\r\n\t/**\r\n\t * Set book cover image.\r\n\t * @param {Image} cover Image.\r\n\t * @returns {boolean} Whatever cover was set.\r\n\t * @private\r\n\t */\r\n\tsetCover(cover) {\r\n\t\tif (cover instanceof Image) {\r\n\t\t\tcover.book = this;\r\n\t\t\tthis.cover = cover;\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * Push image to book pages.\r\n\t * @param {Image} page Image.\r\n\t * @returns {boolean} Whatever page was added.\r\n\t * @private\r\n\t */\r\n\tpushPage(page) {\r\n\t\tif (page instanceof Image) {\r\n\t\t\tpage.book = this;\r\n\t\t\tthis.pages.push(page);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * Push tag to book tags.\r\n\t * @param {Tag} tag Tag.\r\n\t * @returns {boolean} Whatever tag was added.\r\n\t * @private\r\n\t */\r\n\tpushTag(tag) {\r\n\t\ttag = Tag.get(tag);\r\n\r\n\t\tif (!this.hasTag(tag)) {\r\n\t\t\tthis.tags.push(tag);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * Check if book has certain tag.\r\n\t * @param {Tag} tag Tag\r\n\t * @param {boolean} [strict=false] Strict comparison.\r\n\t */\r\n\thasTag(tag, strict = true) {\r\n\t\ttag = Tag.get(tag);\r\n\r\n\t\treturn this.tags.some(elem => elem.compare(tag, strict));\r\n\t}\r\n\r\n\t/**\r\n\t * Check if book has any tags with certain properties.\r\n\t * @param {object|Tag} tag Tag.\r\n\t */\r\n\thasTagWith(tag) {\r\n\t\treturn this.hasTag(tag, 'any');\r\n\t}\r\n\r\n\t/**\r\n\t * Get any tags with certain properties.\r\n\t * @param {object|Tag} tag Tag.\r\n\t * @returns {TagsArray}\r\n\t */\r\n\tgetTagsWith(tag) {\r\n\t\ttag = Tag.get(tag);\r\n\r\n\t\treturn this.tags.filter(elem => elem.compare(tag, 'any'));\r\n\t}\r\n\r\n\t/**\r\n\t * Pure tags (with type {TagType.Tag}).\r\n\t * @type {Tag[]}\r\n\t */\r\n\tget pureTags() {\r\n\t\treturn this.getTagsWith({ type: TagTypes.Tag, });\r\n\t}\r\n\r\n\t/**\r\n\t * Category tags.\r\n\t * @type {Tag[]}\r\n\t */\r\n\tget categories() {\r\n\t\treturn this.getTagsWith({ type: TagTypes.Category, });\r\n\t}\r\n\r\n\t/**\r\n\t * Artist tags.\r\n\t * @type {Tag[]}\r\n\t */\r\n\tget artists() {\r\n\t\treturn this.getTagsWith({ type: TagTypes.Artist, });\r\n\t}\r\n\r\n\t/**\r\n\t * Parody tags.\r\n\t * @type {Tag[]}\r\n\t */\r\n\tget parodies() {\r\n\t\treturn this.getTagsWith({ type: TagTypes.Parody, });\r\n\t}\r\n\r\n\t/**\r\n\t * Character tags.\r\n\t * @type {Tag[]}\r\n\t */\r\n\tget characters() {\r\n\t\treturn this.getTagsWith({ type: TagTypes.Character, });\r\n\t}\r\n\r\n\t/**\r\n\t * Group tags.\r\n\t * @type {Tag[]}\r\n\t */\r\n\tget groups() {\r\n\t\treturn this.getTagsWith({ type: TagTypes.Group, });\r\n\t}\r\n\r\n\t/**\r\n\t * Language tags.\r\n\t * @type {Tag[]}\r\n\t */\r\n\tget languages() {\r\n\t\treturn this.getTagsWith({ type: TagTypes.Language, });\r\n\t}\r\n}\r\n\r\n/**\r\n * Class representing unknown book.\r\n * @class\r\n * @extends Book\r\n */\r\nclass UnknownBook extends Book {\r\n\t/**\r\n\t * Create unknown book.\r\n\t */\r\n\tconstructor() {\r\n\t\tsuper({});\r\n\t}\r\n}\r\n\r\nBook.UnknownBook = UnknownBook;\r\nBook.Unknown = new UnknownBook();\r\n\r\nexport default Book;\r\n","/**\r\n * @module Search\r\n */\r\n\r\nimport API from './api';\r\nimport Book from './book';\r\nimport { Tag, } from './tag';\r\n\r\n\r\n/**\r\n * Search object from API.\r\n * @global\r\n * @typedef {object} APISearch\r\n * @property {APIBook[]} result Search results.\r\n * @property {number|string} num_pages Number of search pages available.\r\n * @property {number|string} per_page Number of books per page.\r\n */\r\n\r\n\r\n/**\r\n * @typedef {''|'popular'|'popular-week'|'popular-today'|'popular-month'} SearchSortMode\r\n */\r\n\r\nclass SearchSort {\r\n\t/**\r\n\t * @type {SearchSortMode}\r\n\t */\r\n\tstatic Recent = '';\r\n\t/**\r\n\t * @type {SearchSortMode}\r\n\t */\r\n\tstatic Popular = 'popular';\r\n\t/**\r\n\t * @type {SearchSortMode}\r\n\t */\r\n\tstatic PopularMonth = 'popular-month';\r\n\t/**\r\n\t * @type {SearchSortMode}\r\n\t */\r\n\tstatic PopularWeek = 'popular-week';\r\n\t/**\r\n\t * @type {SearchSortMode}\r\n\t */\r\n\tstatic PopularToday = 'poplar-today';\r\n}\r\n\r\n/**\r\n * Class representing search request results.\r\n * @class\r\n */\r\nclass Search {\r\n\t/**\r\n\t * Parse search object into class instance.\r\n\t * @param {APISearch} search Search object.\r\n\t */\r\n\tstatic parse(search) {\r\n\t\treturn new this({\r\n\t\t\tpages: search.num_pages\r\n\t\t\t\t? +search.num_pages\r\n\t\t\t\t: 1,\r\n\t\t\tperPage: search.per_page\r\n\t\t\t\t? +search.per_page\r\n\t\t\t\t: search.result.length,\r\n\t\t\tbooks: search.result.map(Book.parse.bind(Book)),\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * API instance.\r\n\t * @type {?API}\r\n\t * @default null\r\n\t */\r\n\tapi = null;\r\n\r\n\t/**\r\n\t * Query string.\r\n\t * @type {?string}\r\n\t * @default null\r\n\t */\r\n\tquery = null;\r\n\r\n\t/**\r\n\t * Search sort mode.\r\n\t * @type {SearchSortMode}\r\n\t * @default ''\r\n\t */\r\n\tsort = '';\r\n\r\n\t/**\r\n\t * Page ID.\r\n\t * @type {number}\r\n\t * @default 1\r\n\t */\r\n\tpage = 1;\r\n\r\n\t/**\r\n\t * Books per page.\r\n\t * @type {number}\r\n\t * @default 0\r\n\t */\r\n\tperPage = 0;\r\n\r\n\t/**\r\n\t * Books array.\r\n\t * @type {Book[]}\r\n\t * @default []\r\n\t */\r\n\tbooks = [];\r\n\r\n\t/**\r\n\t * Pages count.\r\n\t * @type {number}\r\n\t * @default 1\r\n\t */\r\n\tpages = 1;\r\n\r\n\t/**\r\n\t * Create search.\r\n\t * @param {?object} [params] Search parameters.\r\n\t * @param {?string} [params.query=''] Query string.\r\n\t * @param {?SearchSortMode} [params.sort=''] Search sort mode.\r\n\t * @param {?number} [params.page=1] Search page ID.\r\n\t * @param {?number} [params.pages=1] Search pages count.\r\n\t * @param {?number} [params.perPage=0] Search books per page.\r\n\t * @param {?Book[]} [params.books=[]] Books array.\r\n\t */\r\n\tconstructor({\r\n\t\tquery = null,\r\n\t\tsort = '',\r\n\t\tpage = 1,\r\n\t\tpages = 1,\r\n\t\tperPage = 0,\r\n\t\tbooks = [],\r\n\t} = {}) {\r\n\t\tif (Array.isArray(books))\r\n\t\t\tbooks.forEach(this.pushBook.bind(this));\r\n\r\n\t\tObject.assign(this, {\r\n\t\t\tquery,\r\n\t\t\tsort,\r\n\t\t\tpage,\r\n\t\t\tpages,\r\n\t\t\tperPage,\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Push book to books array.\r\n\t * @param {Book} book Book.\r\n\t * @returns {boolean} Whatever was book added or not.\r\n\t * @private\r\n\t */\r\n\tpushBook(book) {\r\n\t\tif (book instanceof Book) {\r\n\t\t\tthis.books.push(book);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * Request next page.\r\n\t * @throws Error if search request can't be paginated.\r\n\t * @throws Error if `api` is missing as instance property or function argument.\r\n\t * @param {API} [api=this.api] API instance.\r\n\t * @returns {Promise<Search>} Next page search.\r\n\t */\r\n\tgetNextPage(api = this.api) {\r\n\t\tlet { query, page, sort, } = this;\r\n\t\tif (query === null)\r\n\t\t\tthrow Error('pagination impossible.');\r\n\t\tif (!(api instanceof API))\r\n\t\t\tthrow Error('api must exists.');\r\n\t\treturn query instanceof Tag\r\n\t\t\t? api.searchTagged(query, page + 1, sort)\r\n\t\t\t: api.search(query, page + 1, sort);\r\n\t}\r\n}\r\n\r\nexport {\r\n\tSearch,\r\n\tSearchSort,\r\n};\r\n","// eslint-disable-next-line no-unused-vars\r\nimport { IncomingMessage, } from 'http';\r\n\r\n\r\nclass APIError extends Error {\r\n\t/**\r\n\t * Original error.\r\n\t * @type {?Error}\r\n\t * @default null\r\n\t */\r\n\toriginalError = null;\r\n\r\n\t/**\r\n\t * HTTP response.\r\n\t * @type {IncomingMessage}\r\n\t * @default null\r\n\t */\r\n\thttpResponse = null;\r\n\r\n\t/**\r\n\t * Error message.\r\n\t * @param {string} message Message.\r\n\t */\r\n\tconstructor(message = 'Unknown error') {\r\n\t\tsuper(message);\r\n\t}\r\n\r\n\t/**\r\n\t * Absorb error.\r\n\t * @param {Error} [error=null] Original error.\r\n\t * @param {?IncomingMessage} [httpResponse=null] HTTP response.\r\n\t * @returns {APIError}\r\n\t */\r\n\tstatic absorb(error, httpResponse = null) {\r\n\t\treturn Object.assign(new APIError(error.message), {\r\n\t\t\toriginalError: error,\r\n\t\t\thttpResponse,\r\n\t\t});\r\n\t}\r\n}\r\n\r\nexport default APIError;\r\n","/**\r\n * @module API\r\n */\r\n\r\n/**\r\n * @typedef { import(\"./options\").nHentaiOptions } nHentaiOptions\r\n */\r\n\r\n/**\r\n * @typedef { import(\"./options\").nHentaiHosts } nHentaiHosts\r\n */\r\n\r\n/**\r\n * @typedef { import(\"./options\").httpAgent } httpAgent\r\n */\r\n\r\n/**\r\n * @typedef { import(\"./search\").SearchSortMode } SearchSortMode\r\n */\r\n\r\n// eslint-disable-next-line no-unused-vars\r\nimport http, { IncomingMessage, } from 'http';\r\nimport https from 'https';\r\n\r\nimport { version, } from '../package.json';\r\n\r\nimport Book from './book';\r\nimport APIError from './error';\r\nimport Image from './image';\r\nimport processOptions from './options';\r\nimport { Search, } from './search';\r\nimport { Tag, } from './tag';\r\n\r\n\r\n/**\r\n * API arguments\r\n * @typedef {object} APIArgs\r\n * @property {string} host API host.\r\n * @property {Function} apiPath API endpoint URL path generator.\r\n */\r\n\r\n/**\r\n * Class used for building URL paths to nHentai API endpoints.\r\n * This class is internal and has only static methods.\r\n * @class\r\n */\r\nclass APIPath {\r\n\t/**\r\n\t * Search by query endpoint.\r\n\t * @param {string} query Search query.\r\n\t * @param {?number} [page=1] Page ID.\r\n\t * @param {?SearchSortMode} [sort=''] Search sort mode.\r\n\t * @returns {string} URL path.\r\n\t */\r\n\tstatic search(query, page = 1, sort = '') {\r\n\t\treturn `/api/galleries/search?query=${query}&page=${page}${sort ? '&sort=' + sort : ''}`;\r\n\t}\r\n\r\n\t/**\r\n\t * Search by tag endpoint.\r\n\t * @param {number} tagID Tag ID.\r\n\t * @param {?number} [page=1] Page ID.\r\n\t * @returns {string} URL path.\r\n\t */\r\n\tstatic searchTagged(tagID, page = 1) {\r\n\t\treturn `/api/galleries/tagged?tag_id=${tagID}&page=${page}`;\r\n\t}\r\n\r\n\t/**\r\n\t * Search alike endpoint.\r\n\t * @param {number} bookID Book ID.\r\n\t * @returns {string} URL path.\r\n\t */\r\n\tstatic searchAlike(bookID) {\r\n\t\treturn `/api/gallery/${bookID}/related`;\r\n\t}\r\n\r\n\t/**\r\n\t * Book content endpoint.\r\n\t * @param {number} bookID Book ID.\r\n\t * @returns {string} URL path.\r\n\t */\r\n\tstatic book(bookID) {\r\n\t\treturn `/api/gallery/${bookID}`;\r\n\t}\r\n\r\n\t/**\r\n\t * Book's cover image endpoint.\r\n\t * @param {number} mediaID Media ID.\r\n\t * @param {string} extension Image extension.\r\n\t * @returns {string} URL path.\r\n\t */\r\n\tstatic bookCover(mediaID, extension) {\r\n\t\treturn `/galleries/${mediaID}/cover.${extension}`;\r\n\t}\r\n\r\n\t/**\r\n\t * Book's page image endpoint.\r\n\t * @param {number} mediaID Media ID.\r\n\t * @param {number} page Page ID.\r\n\t * @param {string} extension Image extension.\r\n\t * @returns {string} URL path.\r\n\t */\r\n\tstatic bookPage(mediaID, page, extension) {\r\n\t\treturn `/galleries/${mediaID}/${page}.${extension}`;\r\n\t}\r\n\r\n\t/**\r\n\t * Book's page's thumbnail image endpoint.\r\n\t * @param {number} mediaID Media ID.\r\n\t * @param {number} page Page ID.\r\n\t * @param {string} extension Image extension.\r\n\t * @returns {string} URL path.\r\n\t */\r\n\tstatic bookThumb(mediaID, page, extension) {\r\n\t\treturn `/galleries/${mediaID}/${page}t.${extension}`;\r\n\t}\r\n\r\n\t/**\r\n\t * Redirect to random book at website.\r\n\t * @returns {string} URL path.\r\n\t */\r\n\tstatic randomBookRedirect() {\r\n\t\treturn '/random/';\r\n\t}\r\n}\r\n\r\n/**\r\n * Class used for interaction with nHentai API.\r\n * @class\r\n */\r\nclass API {\r\n\t/**\r\n\t * API path class\r\n\t * @type {APIPath}\r\n\t * @static\r\n\t * @private\r\n\t */\r\n\tstatic APIPath = APIPath;\r\n\r\n\t/**\r\n\t * Hosts\r\n\t * @type {?nHentaiHosts}\r\n\t */\r\n\thosts;\r\n\r\n\t/**\r\n\t * Prefer HTTPS over HTTP.\r\n\t * @type {?boolean}\r\n\t */\r\n\tssl;\r\n\r\n\t/**\r\n\t * HTTP(S) agent.\r\n\t * @property {?httpAgent}\r\n\t */\r\n\tagent;\r\n\r\n\t/**\r\n\t * Applies provided options on top of defaults.\r\n\t * @param {?nHentaiOptions} [options={}] Options to apply.\r\n\t */\r\n\tconstructor(options = {}) {\r\n\t\tlet params = processOptions(options);\r\n\r\n\t\tObject.assign(this, params);\r\n\t}\r\n\r\n\t/**\r\n\t * Get http(s) module depending on `options.ssl`.\r\n\t * @type {https|http}\r\n\t */\r\n\tget net() {\r\n\t\treturn this.ssl\r\n\t\t\t? https\r\n\t\t\t: http;\r\n\t}\r\n\r\n\t/**\r\n\t * JSON get request.\r\n\t * @param {object} options HTTP(S) request options.\r\n\t * @param {string} options.host Host.\r\n\t * @param {string} options.path Path.\r\n\t * @returns {Promise<object>} Parsed JSON.\r\n\t */\r\n\trequest(options) {\r\n\t\tlet {\r\n\t\t\tnet,\r\n\t\t\tagent,\r\n\t\t} = this;\r\n\t\treturn new Promise((resolve, reject) => {\r\n\t\t\tObject.assign(options, {\r\n\t\t\t\tagent,\r\n\t\t\t\theaders: {\r\n\t\t\t\t\t'User-Agent': `nhentai-api-client/${version} Node.js/${process.versions.node}`,\r\n\t\t\t\t},\r\n\t\t\t});\r\n\r\n\t\t\tnet.get(options, _response => {\r\n\t\t\t\tconst\r\n\t\t\t\t\t/** @type {IncomingMessage}*/\r\n\t\t\t\t\tresponse = _response,\r\n\t\t\t\t\t{ statusCode, } = response,\r\n\t\t\t\t\tcontentType = response.headers['content-type'];\r\n\r\n\t\t\t\tlet error;\r\n\t\t\t\tif (statusCode !== 200)\r\n\t\t\t\t\terror = new Error(`Request failed with status code ${statusCode}`);\r\n\t\t\t\telse if (!(/^application\\/json/).test(contentType))\r\n\t\t\t\t\terror = new Error(`Invalid content-type - expected application/json but received ${contentType}`);\r\n\r\n\t\t\t\tif (error) {\r\n\t\t\t\t\tresponse.resume();\r\n\t\t\t\t\treject(APIError.absorb(error, response));\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tresponse.setEncoding('utf8');\r\n\t\t\t\tlet rawData = '';\r\n\t\t\t\tresponse.on('data', (chunk) => rawData += chunk);\r\n\t\t\t\tresponse.on('end', () => {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tresolve(JSON.parse(rawData));\r\n\t\t\t\t\t} catch (error) {\r\n\t\t\t\t\t\treject(APIError.absorb(error, response));\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t}).on('error', error => reject(APIError.absorb(error)));\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Get API arguments.\r\n\t * This is internal method.\r\n\t * @param {string} hostType Host type.\r\n\t * @param {string} api Endpoint type.\r\n\t * @returns {APIArgs} API arguments.\r\n\t * @private\r\n\t */\r\n\tgetAPIArgs(hostType, api) {\r\n\t\tlet {\r\n\t\t\thosts: {\r\n\t\t\t\t[hostType]: host,\r\n\t\t\t},\r\n\t\t\tconstructor: {\r\n\t\t\t\tAPIPath: {\r\n\t\t\t\t\t[api]: apiPath,\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t} = this;\r\n\r\n\t\treturn {\r\n\t\t\thost,\r\n\t\t\tapiPath,\r\n\t\t};\r\n\t}\r\n\r\n\t/**\r\n\t * Search by query.\r\n\t * @param {string} query Query.\r\n\t * @param {?number} [page=1] Page ID.\r\n\t * @param {?SearchSortMode} [sort=''] Search sort mode.\r\n\t * @returns {Promise<Search>} Search instance.\r\n\t * @async\r\n\t */\r\n\tasync search(query, page = 1, sort = '') {\r\n\t\tlet { host, apiPath, } = this.getAPIArgs('api', 'search'),\r\n\t\t\tsearch = Search.parse(\r\n\t\t\t\tawait this.request({\r\n\t\t\t\t\thost,\r\n\t\t\t\t\tpath: apiPath(query, page, sort),\r\n\t\t\t\t})\r\n\t\t\t);\r\n\r\n\t\tObject.assign(search, {\r\n\t\t\tapi: this,\r\n\t\t\tquery,\r\n\t\t\tpage,\r\n\t\t\tsort,\r\n\t\t});\r\n\r\n\t\treturn search;\r\n\t}\r\n\r\n\t/**\r\n\t * Search by query.\r\n\t * @param {string} query Query.\r\n\t * @param {?number} [page=1] Starting page ID.\r\n\t * @param {?SearchSortMode} [sort=''] Search sort mode.\r\n\t * @yields {Search} Search instance.\r\n\t * @async\r\n\t * @returns {AsyncGenerator<Search, Search, Search>}\r\n\t */\r\n\tasync * searchGenerator(query, page = 1, sort = '') {\r\n\t\tlet search = await this.search(query, page, sort);\r\n\r\n\t\twhile (search.page <= search.pages) {\r\n\t\t\tyield search;\r\n\t\t\tsearch = await this.search(query, search.page + 1, sort);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Search related books.\r\n\t * @param {number|Book} book Book instance or Book ID.\r\n\t * @returns {Promise<Search>} Search instance.\r\n\t * @async\r\n\t */\r\n\tasync searchAlike(book) {\r\n\t\tlet { host, apiPath, } = this.getAPIArgs('api', 'searchAlike');\r\n\r\n\t\treturn Search.parse(\r\n\t\t\tawait this.request({\r\n\t\t\t\thost,\r\n\t\t\t\tpath: apiPath(\r\n\t\t\t\t\tbook instanceof Book\r\n\t\t\t\t\t\t? book.id\r\n\t\t\t\t\t\t: +book\r\n\t\t\t\t),\r\n\t\t\t})\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * Search by tag id.\r\n\t * @param {number|Tag} tag Tag or Tag ID.\r\n\t * @param {?number} [page=1] Page ID.\r\n\t * @param {?SearchSortMode} [sort=''] Search sort mode.\r\n\t * @returns {Promise<Search>} Search instance.\r\n\t * @async\r\n\t */\r\n\tasync searchTagged(tag, page = 1, sort = '') {\r\n\t\tif (!(tag instanceof Tag))\r\n\t\t\ttag = Tag.get({ id: +tag, });\r\n\t\tlet { host, apiPath, } = this.getAPIArgs('api', 'searchTagged'),\r\n\t\t\tsearch = Search.parse(\r\n\t\t\t\tawait this.request({\r\n\t\t\t\t\thost,\r\n\t\t\t\t\tpath: apiPath(tag.id, page, sort),\r\n\t\t\t\t})\r\n\t\t\t);\r\n\r\n\t\tObject.assign(search, {\r\n\t\t\tapi : this,\r\n\t\t\tquery: tag,\r\n\t\t\tpage,\r\n\t\t\tsort,\r\n\t\t});\r\n\r\n\t\treturn search;\r\n\t}\r\n\r\n\t/**\r\n\t * Get book by id.\r\n\t * @param {number} bookID Book ID.\r\n\t * @returns {Promise<Book>} Book instance.\r\n\t * @async\r\n\t */\r\n\tasync getBook(bookID) {\r\n\t\tlet { host, apiPath, } = this.getAPIArgs('api', 'book');\r\n\r\n\t\treturn Book.parse(\r\n\t\t\tawait this.request({\r\n\t\t\t\thost,\r\n\t\t\t\tpath: apiPath(bookID),\r\n\t\t\t})\r\n\t\t);\r\n\t}\r\n\r\n\t/**\r\n\t * Get random book.\r\n\t * @returns {Promise<Book>} Book instance.\r\n\t * @async\r\n\t */\r\n\tasync getRandomBook() {\r\n\t\tlet { host, apiPath, } = this.getAPIArgs('api', 'randomBookRedirect');\r\n\r\n\t\ttry {\r\n\t\t\tawait this.request({\r\n\t\t\t\thost,\r\n\t\t\t\tpath: apiPath(),\r\n\t\t\t}); // Will always throw\r\n\t\t} catch (error) {\r\n\t\t\tif (!(error instanceof APIError))\r\n\t\t\t\tthrow error;\r\n\t\t\tconst response = error.httpResponse;\r\n\t\t\tif (!response || response.statusCode !== 302)\r\n\t\t\t\tthrow error;\r\n\t\t\tconst id = +((/\\d+/).exec(response.headers.location) || {})[0];\r\n\t\t\tif (isNaN(id))\r\n\t\t\t\tthrow APIError.absorb(new Error('Bad redirect'), response);\r\n\t\t\treturn await this.getBook(id);\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Get image URL.\r\n\t * @param {Image} image Image.\r\n\t * @returns {string} Image URL.\r\n\t */\r\n\tgetImageURL(image) {\r\n\t\tif (image instanceof Image) {\r\n\t\t\tlet { host, apiPath, } = image.isCover\r\n\t\t\t\t? this.getAPIArgs('thumbs', 'bookCover')\r\n\t\t\t\t: this.getAPIArgs('images', 'bookPage');\r\n\r\n\t\t\treturn `http${this.ssl ? 's' : ''}://${host}` + (image.isCover\r\n\t\t\t\t? apiPath(image.book.media, image.type.extension)\r\n\t\t\t\t: apiPath(image.book.media, image.id, image.type.extension));\r\n\t\t}\r\n\t\tthrow new Error('image must be Image instance.');\r\n\t}\r\n\r\n\t/**\r\n\t * Get image thumbnail URL.\r\n\t * @param {Image} image Image.\r\n\t * @returns {string} Image thumbnail URL.\r\n\t */\r\n\tgetThumbURL(image) {\r\n\t\tif (image instanceof Image && !image.isCover) {\r\n\t\t\tlet { host, apiPath, } = this.getAPIArgs('thumbs', 'bookThumb');\r\n\r\n\t\t\treturn `http${this.ssl ? 's' : ''}://${host}`\r\n\t\t\t\t+ apiPath(image.book.media, image.id, image.type.extension);\r\n\t\t}\r\n\t\tthrow new Error('image must be Image instance and not book cover.');\r\n\t}\r\n}\r\n\r\nexport default API;\r\n","import { Agent, } from 'http';\r\nimport { Agent as SSLAgent, } from 'https';\r\n\r\n\r\n/**\r\n * Agent-like object or Agent class or it's instance.\r\n * @global\r\n * @typedef {object|Agent|SSLAgent} httpAgent\r\n */\r\n\r\n/**\r\n * Common nHentai API hosts object.\r\n * @global\r\n * @typedef {object} nHentaiHosts\r\n * @property {?string} api Main API host.\r\n * @property {?string} images Media API host.\r\n * @property {?string} thumbs Media thumbnails API host.\r\n */\r\n\r\n/**\r\n * Common nHentai options object.\r\n * @global\r\n * @typedef {object} nHentaiOptions\r\n * @property {?nHentaiHosts} hosts Hosts.\r\n * @property {?boolean} ssl Prefer HTTPS over HTTP.\r\n * @property {?httpAgent} agent HTTP(S) agent.\r\n */\r\n\r\n/**\r\n * Applies provided options on top of defaults.\r\n * @param {?nHentaiOptions} [options={}] Options to apply.\r\n * @returns {nHentaiOptions} Unified options.\r\n */\r\nfunction processOptions({\r\n\thosts: {\r\n\t\tapi = 'nhentai.net',\r\n\t\timages = 'i.nhentai.net',\r\n\t\tthumbs = 't.nhentai.net',\r\n\t} = {},\r\n\tssl = true,\r\n\tagent = null,\r\n} = {}) {\r\n\tif (!agent)\r\n\t\tagent = ssl\r\n\t\t\t? SSLAgent\r\n\t\t\t: Agent;\r\n\r\n\tif (agent.constructor.name === 'Function')\r\n\t\tagent = new agent();\r\n\r\n\treturn {\r\n\t\thosts: {\r\n\t\t\tapi,\r\n\t\t\timages,\r\n\t\t\tthumbs,\r\n\t\t},\r\n\t\tssl,\r\n\t\tagent,\r\n\t};\r\n}\r\n\r\nexport default processOptions;\r\n","import API from './api';\r\nimport Book from './book';\r\nimport Image from './image';\r\nimport { Search, SearchSort, } from './search';\r\nimport { Tag, } from './tag';\r\n\r\n\r\nexport {\r\n\tAPI,\r\n\tSearch,\r\n\tSearchSort,\r\n\tBook,\r\n\tImage,\r\n\tTag,\r\n};\r\n\r\n/**\r\n * @typedef { import(\"./tag\").TagTypes } TagTypes\r\n */\r\n\r\n/**\r\n * @type {TagTypes}\r\n */\r\nexport const TagTypes = {\r\n\tUnknown : Tag.types.Unknown,\r\n\tTag : Tag.types.Tag,\r\n\tCategory : Tag.types.Category,\r\n\tArtist : Tag.types.Artist,\r\n\tParody : Tag.types.Parody,\r\n\tCharacter: Tag.types.Character,\r\n\tGroup : Tag.types.Group,\r\n\tLanguage : Tag.types.Language,\r\n};\r\n"],"names":["TagType","constructor","type","knownTypes","this","isKnown","UnknownTagType","toString","Tag","tag","id","name","count","url","types","Unknown","Object","assign","get","compare","strict","map","prop","reduce","accum","current","includeCount","Category","Artist","Parody","Character","Group","Language","known","toLowerCase","ImageType","extension","UnknownImageType","Image","image","t","w","width","h","height","JPEG","book","Book","isCover","filename","PNG","GIF","TagsArray","Array","args","toNames","from","title","media","media_id","favorites","num_favorites","scanlator","uploaded","Date","upload_date","tags","cover","parse","images","pages","english","japanese","pretty","setCover","isArray","forEach","pushPage","bind","pushTag","UnknownBook","page","push","hasTag","some","elem","hasTagWith","getTagsWith","filter","pureTags","TagTypes","categories","artists","parodies","characters","groups","languages","SearchSort","Search","search","num_pages","perPage","per_page","result","length","books","query","sort","pushBook","getNextPage","api","Error","API","searchTagged","APIError","message","error","httpResponse","originalError","options","params","processOptions","hosts","thumbs","ssl","agent","SSLAgent","Agent","net","https","http","request","Promise","resolve","reject","headers","process","versions","node","_response","response","statusCode","contentType","test","resume","absorb","setEncoding","rawData","on","chunk","JSON","getAPIArgs","hostType","host","APIPath","apiPath","path","bookID","exec","location","isNaN","getBook","getImageURL","getThumbURL","tagID","mediaID"],"mappings":"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