UNPKG

node-hitomi

Version:
116 lines (113 loc) 3.23 kB
import { BASE_DOMAIN } from '../internal/constants.mjs'; import { Base } from '../internal/base.mjs'; import { HitomiError, ErrorCode } from './error.mjs'; const Extension = { Avif: 'avif', Webp: 'webp', Jxl: 'jxl' }; const ThumbnailSize = { Small: 'small', Medium: 'smallbig', Big: 'big' }; class Media extends Base { static createHashPath(hash, separator = '/') { return hash.slice(-1) + separator + hash.slice(-3, -1); } constructor(hitomi, width, height) { super(hitomi); this.width = width; this.height = height; } request(url) { const index = url.indexOf('/', 2); return this.hitomi.request(url.slice(2, index), url.slice(index), 0); } } class Image extends Media { constructor(hitomi, width, height, hash, name, hasAvif, hasWebp, hasJxl, hasThumbnail) { super(hitomi, width, height); this.hash = hash; this.name = name; this.hasAvif= hasAvif; this.hasWebp = hasWebp; this.hasJxl = hasJxl; this.hasThumbnail = hasThumbnail; } async resolveUrl(extension, thumbnailSize) { switch(extension) { case Extension.Avif: { if(!this.hasAvif) { throw HitomiError.unavailableExtension; } break; } case Extension.Jxl: { if(!this.hasJxl) { throw HitomiError.unavailableExtension; } break; } case Extension.Webp: { if(!this.hasWebp) { throw HitomiError.unavailableExtension; } break; } default: { throw HitomiError.invalidMember('Extension', Extension); } } let subdomain; let path; if(thumbnailSize) { let member = 'Big'; switch(thumbnailSize) { case ThumbnailSize.Medium: { if(extension !== Extension.Avif) { throw new HitomiError(ErrorCode.InvalidCombination, 'ThumbnailSize.Medium', 'used only with avif'); } member = 'Medium'; } case ThumbnailSize.Big: { if(!this.hasThumbnail) { throw new HitomiError(ErrorCode.InvalidCombination, 'ThumbnailSize.' + member, 'used only with image that has thumbnail'); } } case ThumbnailSize.Small: { break; } default: { throw HitomiError.invalidMember('ThumbnailSize', ThumbnailSize); } } subdomain = 'tn'; path = extension + thumbnailSize + 'tn/' + Media.createHashPath(this.hash) + '/' + this.hash; } else { const hashCode = Number.parseInt(Media.createHashPath(this.hash, ''), 16); const context = await this.hitomi.imageContext.retrieve(); subdomain = extension[0] + (context[0].has(hashCode) === context[1] ? '2' : '1'); path = context[2] + hashCode + '/' + this.hash; } return '//' + subdomain + '.' + BASE_DOMAIN + '/' + path + '.' + extension; } async fetch(extension, thumbnailSize) { return this.request(await this.resolveUrl(extension, thumbnailSize)); } } class Video extends Media { constructor(hitomi, width, height, fileName, hash) { super(hitomi, width, height); this.fileName = fileName; this.url = '//streaming.' + BASE_DOMAIN + '/videos/' + fileName; this.posterUrl = '//a.' + BASE_DOMAIN + '/videos/posters/' + Media.createHashPath(hash) + '/' + hash + '.webp'; } fetch() { return this.request(this.url); } fetchPoster() { return this.request(this.posterUrl); } } export { Extension, Image, ThumbnailSize, Video };