node-hitomi
Version:
Hitomi.la API for Node.js
121 lines (117 loc) • 3.39 kB
JavaScript
'use strict';
const constants = require('../internal/constants.cjs');
const base = require('../internal/base.cjs');
const error = require('./error.cjs');
const Extension = {
Avif: 'avif',
Webp: 'webp',
Jxl: 'jxl'
};
const ThumbnailSize = {
Small: 'small',
Medium: 'smallbig',
Big: 'big'
};
class Media extends base.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 error.HitomiError.unavailableExtension;
}
break;
}
case Extension.Jxl: {
if(!this.hasJxl) {
throw error.HitomiError.unavailableExtension;
}
break;
}
case Extension.Webp: {
if(!this.hasWebp) {
throw error.HitomiError.unavailableExtension;
}
break;
}
default: {
throw error.HitomiError.invalidMember('Extension', Extension);
}
}
let subdomain;
let path;
if(thumbnailSize) {
let member = 'Big';
switch(thumbnailSize) {
case ThumbnailSize.Medium: {
if(extension !== Extension.Avif) {
throw new error.HitomiError(error.ErrorCode.InvalidCombination, 'ThumbnailSize.Medium', 'used only with avif');
}
member = 'Medium';
}
case ThumbnailSize.Big: {
if(!this.hasThumbnail) {
throw new error.HitomiError(error.ErrorCode.InvalidCombination, 'ThumbnailSize.' + member, 'used only with image that has thumbnail');
}
}
case ThumbnailSize.Small: {
break;
}
default: {
throw error.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 + '.' + constants.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.' + constants.BASE_DOMAIN + '/videos/' + fileName;
this.posterUrl = '//a.' + constants.BASE_DOMAIN + '/videos/posters/' + Media.createHashPath(hash) + '/' + hash + '.webp';
}
fetch() {
return this.request(this.url);
}
fetchPoster() {
return this.request(this.posterUrl);
}
}
exports.Extension = Extension;
exports.Image = Image;
exports.ThumbnailSize = ThumbnailSize;
exports.Video = Video;