UNPKG

node-hitomi

Version:
303 lines (216 loc) 9.28 kB
<div align='center'> [![banner](.github/assets/banner.png)](https://github.com/H2Owater425/node-hitomi) ### Hitomi.la API for Node.js <sup>Would you call me a gentleman?</sup> [![npm version](https://img.shields.io/npm/v/node-hitomi?style=flat-square)](https://npmjs.org/package/node-hitomi) [![npm type definition](https://img.shields.io/npm/types/node-hitomi?style=flat-square)](https://npmjs.org/package/node-hitomi) [![ci status](https://img.shields.io/github/actions/workflow/status/H2Owater425/node-hitomi/continuous-integration.yml?branch=main&style=flat-square&logo=github&label=ci)](https://github.com/H2Owater425/node-hitomi/actions/workflows/continuous-integration.yml) [![license](https://img.shields.io/github/license/H2Owater425/node-hitomi?style=flat-square)](https://github.com/H2Owater425/node-hitomi/blob/main/LICENSE) </div> ## Installation > [!IMPORTANT] > > Due to frequent changes on Hitomi.la, it is highly recommended to use the latest version. **Node.js 10.20 or newer is required.** ```sh npm install node-hitomi yarn add node-hitomi pnpm add node-hitomi bun add node-hitomi deno add npm:node-hitomi ``` ## Features - **Gallery Search:** Filter by tags and title, choose sort order, and paginate results. - **Gallery Retrieval:** Load full gallery metadata, including title, type, language, artists, and relations. - **Tag Management:** Create, parse, search, and list tags. Also list available languages for a tag. - **Image Downloads:** Resolve URLs and fetch images in AVIF/WebP/JXL (when available), with optional thumbnail sizes. - **Video Downloads:** Fetch gallery videos and poster images. ## Usage The package exports a default client, but you can also create your own client for custom configuration. ```typescript import { Hitomi, RequestContext } from 'node-hitomi'; import { Agent, RequestOptions } from 'https'; const agent: Agent = new Agent({ keepAlive: true, // Bypass server name indication field blocking servername: '', rejectUnauthorized: false }); const hitomi = new Hitomi({ onRequest: function (context: RequestContext<RequestOptions>): void { // Inspect or modify the request context before each HTTP request console.log(context); context.options.agent = agent; context.options.headers['X-Forwarded-Host'] = context.host; context.host = 'proxy.example.com'; }, indexMaximumAge: 300000, imageContextMaximumAge: 1800000 }); ``` If you use CommonJS: ```typescript const { hitomi, SortType /* and more... */ } = require('node-hitomi'); ``` --- ### Galleries `GalleryManager` lets you retrieve individual galleries and list matching gallery references. #### `GalleryManager.retrieve(id)` Retrieves a full `Gallery` by its id. ```typescript import hitomi from 'node-hitomi'; // Retrieve a gallery by id const gallery = await hitomi.galleries.retrieve(123456); console.log(`Title: ${gallery.title.display}`); console.log(`Type: ${gallery.type}`); console.log(`Language: ${gallery.language?.name}`); ``` #### `GalleryManager.list(options?)` Lists galleries that match the given criteria and returns `GalleryReference[]`. You can pass tags, a title query, sort options, and paging options. ```typescript import hitomi, { SortType } from 'node-hitomi'; // Parse a search expression into tags const tags = hitomi.tags.parse('male:sole_male -female:netorare series:blue_archive'); // List matching gallery references const references = await hitomi.galleries.list({ tags: tags, title: 'serina', orderBy: SortType.PopularityMonth }); // Resolve the first reference to a full gallery if(references.length > 0) { const firstGallery = await references[0].retrieve(); console.log(firstGallery.title.display); } ``` > [!WARNING] > > Not every `options.page` usage is valid. It must meet the restrictions below. > > - Only one non-language tag is allowed (optionally combined with a language tag). > - Negative tags and title queries are not supported. > > ```typescript > const simpleTags = hitomi.tags.parse('type:manga language:english'); > > // List matching gallery references with pagination > const pagedReferences = await hitomi.galleries.list({ > tags: simpleTags, > orderBy: SortType.DatePublished, > page: { > index: 0, > size: 25 > } > }); > > console.log(pagedReferences, `(${pagedReferences.length} galleries)`); > ``` --- ### Tags `TagManager` helps you create, parse, search, and list tags. #### `TagManager.create(type, name, isNegative?)` Creates a `Tag` from a type and name. You can pass `true` to make it negative. ```typescript import hitomi from 'node-hitomi'; // Create a series tag and list available languages for that tag const tag = hitomi.tags.create('series', 'trickcal_revive', false); const languages = await tag.listLanguages(); console.log(languages); ``` #### `TagManager.parse(expression)` Parses a space-separated expression into unique `Tag[]`. The expected format is `[-]type:name`, where spaces are represented by underscores. ```typescript import hitomi from 'node-hitomi'; // Parse a string expression into tags const parsedTags = hitomi.tags.parse('female:yandere male:sole_male -tag:group'); console.log(parsedTags); ``` #### `TagManager.search(term)` Searches tags by partial term and returns tuples of `[Tag, count]`, where `count` is the number of galleries associated with each tag. ```typescript import hitomi from 'node-hitomi'; // Search tags and print their gallery counts const tagAndCounts = await hitomi.tags.search('character:agnes'); for(const [tag, count] of tagAndCounts) { console.log(`${String(tag)} (${count} galleries)`); } ``` #### `TagManager.list(type, startsWith?)` Lists tags of a specific type, optionally filtered by an initial character. ```typescript import hitomi, { NameInitial } from 'node-hitomi'; // List female tags that start with 'a' const femaleATags = await hitomi.tags.list('female', NameInitial.A); for(const tag of femaleATags) { console.log(String(tag)); } ``` --- ### Media #### `Image.resolveUrl(extension, thumbnailSize?)` Resolves an image URL in the requested format and optional thumbnail size. > [!WARNING] > > Not every `extension` and `thumbnailSize` combination is valid. It must meet the restrictions below. > > | Thumbnail Size | Extension | Requirement (must be true) | > | :------------- | :-------- | :------------------------------- | > | *(none)* | *(all)* | `has{Extension}` | > | `Small` | *(all)* | `has{Extension}` | > | `Medium` | `Avif` | `hasThumbnail && has{Extension}` | > | `Big` | *(all)* | `hasThumbnail && has{Extension}` | ```typescript import hitomi, { Extension, ThumbnailSize } from 'node-hitomi'; // Retrieve a gallery by id and get representative thumbnails const gallery = await hitomi.galleries.retrieve(123456); const thumbnails = gallery.getThumbnails(); // Resolve the full-size WebP URL const imageUrl = await thumbnails[0].resolveUrl(Extension.Webp); console.log(`Image URL: ${imageUrl}`); // Resolve the medium AVIF thumbnail URL const thumbnailUrl = await thumbnails[1].resolveUrl(Extension.Avif, ThumbnailSize.Medium); console.log(`Thumbnail URL: ${thumbnailUrl}`); ``` #### `Image.fetch(extension, thumbnailSize?)` Fetches an image as a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). The same `extension` and `thumbnailSize` restrictions as [`Image.resolveUrl`](#imageresolveurlextension-thumbnailsize) apply. ```typescript import hitomi, { Extension, ThumbnailSize } from 'node-hitomi'; import { writeFileSync } from 'fs'; // Retrieve a gallery by id and get representative thumbnails const gallery = await hitomi.galleries.retrieve(123456); const thumbnails = gallery.getThumbnails(); // Fetch and save the full-size WebP image const imageBuffer = await thumbnails[0].fetch(Extension.Webp); writeFileSync('image.webp', imageBuffer); // Fetch and save the medium AVIF thumbnail const thumbnailBuffer = await thumbnails[1].fetch(Extension.Avif, ThumbnailSize.Medium); writeFileSync('thumbnail.avif', thumbnailBuffer); ``` #### `Video.fetch()` Fetches a gallery video as an MP4 [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). ```typescript import hitomi from 'node-hitomi'; import { writeFileSync } from 'fs'; const gallery = await hitomi.galleries.retrieve(123456); if(gallery.video) { // Fetch and store the MP4 video const videoBuffer = await gallery.video.fetch(); writeFileSync('video.mp4', videoBuffer); } ``` #### `Video.fetchPoster()` Fetches the video poster as a WebP [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array). ```typescript import hitomi from 'node-hitomi'; import { writeFileSync } from 'fs'; const gallery = await hitomi.galleries.retrieve(123456); if(gallery.video) { // Fetch and store the WebP poster image const posterBuffer = await gallery.video.fetchPoster(); writeFileSync('poster.webp', posterBuffer); } ``` ## Contribution Contributions are welcome. Feel free to open an issue for bugs or submit a pull request with improvements. ## License This project is licensed under the [MIT License](LICENSE).