node-hitomi
Version:
Hitomi.la API for Node.js
177 lines (173 loc) • 4.41 kB
JavaScript
;
const error = require('../structures/error.cjs');
const constants = require('../internal/constants.cjs');
const base = require('../internal/base.cjs');
const tag = require('../structures/tag.cjs');
const gallery = require('../structures/gallery.cjs');
const NameInitial = {
A: 'a',
B: 'b',
C: 'c',
D: 'd',
E: 'e',
F: 'f',
G: 'g',
H: 'h',
I: 'i',
J: 'j',
K: 'k',
L: 'l',
M: 'm',
N: 'n',
O: 'o',
P: 'p',
Q: 'q',
R: 'r',
S: 's',
T: 't',
U: 'u',
V: 'v',
W: 'w',
X: 'x',
Y: 'y',
Z: 'z',
_123: '123'
};
class TagManager extends base.Base {
constructor(hitomi) {
super(hitomi);
}
create(type, name, isNegative = false) {
if(!name.length) {
throw new error.HitomiError(error.ErrorCode.InvalidArgument, 'Name', 'empty', false);
}
return new tag.Tag(this.hitomi, type, name.replace(/_/g, ' '), isNegative);
}
parse(expression) {
expression = expression.trim() + ' ';
const tags = [];
const rawTags = new Set();
let currentIndex = 0;
let nextIndex;
while((nextIndex = expression.indexOf(' ', currentIndex)) !== -1) {
const colonIndex = expression.indexOf(':', currentIndex);
if(colonIndex !== -1 && colonIndex < nextIndex) {
const rawTag = expression.slice(currentIndex, nextIndex);
const isNegative = expression[currentIndex] === '-';
if(!rawTags.has(rawTag)) {
tags.push(new tag.Tag(this.hitomi, expression.slice(currentIndex + isNegative, colonIndex), expression.slice(colonIndex + 1, nextIndex).replace(/_/g, ' '), isNegative));
rawTags.add(rawTag);
}
}
currentIndex = nextIndex + 1;
}
return tags;
}
async search(term) {
const isNegative = term[0] === '-';
let i = term.indexOf(':') + 1;
let path;
if(i) {
const type = term.slice(isNegative, i - 1);
if(!tag.Tag.TYPES.has(type)) {
throw error.HitomiError.invalidMember('Type', tag.Tag.TYPES);
}
path = '/' + type;
} else {
if(isNegative) {
i++;
}
path = '/global';
}
for(; i < term.length && term[i] !== ':'; i++) {
path += '/';
switch(term[i]) {
case '.': {
path += 'dot';
break;
}
case '/': {
path += 'slash';
break;
}
default: {
path += term[i];
}
}
}
const response = await this.hitomi.request(constants.TAG_INDEX_DOMAIN, path + '.json', 3);
const tagAndCounts = [];
for(i = 0; i < response.length; i++) {
tagAndCounts.push([
new tag.Tag(this.hitomi, response[i][2], response[i][0]),
response[i][1]
]);
}
return tagAndCounts;
}
async list(type, startsWith) {
const tags = [];
let names;
switch(type) {
case 'type': {
names = gallery.Gallery.TYPES;
}
case 'language': {
if(!names) {
names = tag.Language.NAMES;
}
for(const name of names) {
tags.push(new tag.Tag(this.hitomi, type, name));
}
break;
}
default: {
if(!startsWith) {
throw new error.HitomiError(error.ErrorCode.InvalidCombination, 'StartsWith', 'provided except forlanguage and type');
}
if(!TagManager.NAME_INITIALS.has(startsWith)) {
throw error.HitomiError.invalidMember('StartsWith', TagManager.NAME_INITIALS);
}
let target = 'href="/' + type + '/';
let area;
switch(type) {
case 'male':
case 'female': {
target = 'href="/tag/' + type + '%3A';
}
case 'tag': {
area = 'tags';
break;
}
case 'series': {
area = type;
break;
}
case 'artist':
case 'character':
case 'group': {
area = type + 's';
break;
}
default: {
throw error.HitomiError.invalidMember('Type', tag.Tag.TYPES);
}
}
const response = await this.hitomi.request(constants.FRONT_DOMAIN, '/all' + area + '-' + startsWith + '.html', 2);
const endIndex = target.length - 1;
let currentIndex;
let nextIndex = 0;
while((currentIndex = response.indexOf(target, nextIndex) + target.length) !== endIndex) {
nextIndex = response.indexOf('.', currentIndex);
if(type !== 'tag' || !response.startsWith('male', currentIndex) && !response.startsWith('female', currentIndex)) {
tags.push(new tag.Tag(this.hitomi, type, decodeURIComponent(response.slice(currentIndex, nextIndex - 4))));
}
}
}
}
return tags;
}
}
TagManager.NAME_INITIALS = new Set(Object.values(NameInitial));
exports.NameInitial = NameInitial;
exports.TagManager = TagManager;