* add prettier

* lint-staged

* prettier

* upgrade husky
This commit is contained in:
nyagami
2024-03-23 22:00:29 +07:00
committed by GitHub
parent c22979b10f
commit 5f9a067ab9
113 changed files with 79244 additions and 50841 deletions
+257 -236
View File
@@ -1,9 +1,9 @@
import { Plugin } from "@typings/plugin";
import { FilterTypes, Filters } from "@libs/filterInputs";
import { fetchApi, fetchFile } from "@libs/fetch";
import { NovelStatus } from "@libs/novelStatus";
import { load as parseHTML } from "cheerio";
import dayjs from "dayjs";
import { Plugin } from '@typings/plugin';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { fetchApi, fetchFile } from '@libs/fetch';
import { NovelStatus } from '@libs/novelStatus';
import { load as parseHTML } from 'cheerio';
import dayjs from 'dayjs';
const statusKey: { [key: number]: string } = {
1: NovelStatus.Ongoing,
@@ -13,11 +13,11 @@ const statusKey: { [key: number]: string } = {
};
class RLIB implements Plugin.PluginBase {
id = "RLIB";
name = "RanobeLib";
site = "https://ranobelib.me";
version = "1.0.0";
icon = "src/ru/ranobelib/icon.png";
id = 'RLIB';
name = 'RanobeLib';
site = 'https://ranobelib.me';
version = '1.0.0';
icon = 'src/ru/ranobelib/icon.png';
ui: string | undefined = undefined;
async popularNovels(
@@ -27,63 +27,74 @@ class RLIB implements Plugin.PluginBase {
filters,
}: Plugin.PopularNovelsOptions<typeof this.filters>,
): Promise<Plugin.NovelItem[]> {
let url = this.site + "/manga-list?sort=";
url += showLatestNovels ? "last_chapter_at" : filters?.sort?.value || "rate";
url += "&dir=" + (filters?.order?.value || "desc");
url += "&chapters[min]=" + (filters?.require_chapters?.value ? "1" : "0");
let url = this.site + '/manga-list?sort=';
url += showLatestNovels
? 'last_chapter_at'
: filters?.sort?.value || 'rate';
url += '&dir=' + (filters?.order?.value || 'desc');
url += '&chapters[min]=' + (filters?.require_chapters?.value ? '1' : '0');
Object.entries(filters || {}).forEach(([type, { value }]: any) => {
if (value instanceof Array && value.length) {
url += "&" + type + "[]=" + value.join("&" + type + "[]=");
url += '&' + type + '[]=' + value.join('&' + type + '[]=');
}
if (value?.include instanceof Array && value.include.length) {
url += "&" + type + "[include][]=" +
value.include.join("&" + type + "[include][]=");
url +=
'&' +
type +
'[include][]=' +
value.include.join('&' + type + '[include][]=');
}
if (value?.exclude instanceof Array && value.exclude.length) {
url += "&" + type + "[exclude][]=" +
value.exclude.join("&" + type + "[exclude][]=");
url +=
'&' +
type +
'[exclude][]=' +
value.exclude.join('&' + type + '[exclude][]=');
}
});
url += "&page=" + pageNo;
url += '&page=' + pageNo;
const result = await fetchApi(url).then((res) => res.text());
const result = await fetchApi(url).then(res => res.text());
const loadedCheerio = parseHTML(result);
this.ui = loadedCheerio("a.header-right-menu__item")
.attr("href")
?.replace?.(/[^0-9]/g, "");
this.ui = loadedCheerio('a.header-right-menu__item')
.attr('href')
?.replace?.(/[^0-9]/g, '');
const novels: Plugin.NovelItem[] = [];
loadedCheerio(".media-card-wrap").each((index, element) => {
const name = loadedCheerio(element).find(".media-card__title").text();
const cover = loadedCheerio(element).find("a.media-card").attr("data-src");
const url = loadedCheerio(element).find("a.media-card").attr("href");
loadedCheerio('.media-card-wrap').each((index, element) => {
const name = loadedCheerio(element).find('.media-card__title').text();
const cover = loadedCheerio(element)
.find('a.media-card')
.attr('data-src');
const url = loadedCheerio(element).find('a.media-card').attr('href');
if (!url) return;
novels.push({ name, cover, path: url.replace(this.site, "") });
novels.push({ name, cover, path: url.replace(this.site, '') });
});
return novels;
}
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
const body = await fetchApi(this.resolveUrl(novelPath, true))
.then((res) => res.text());
const body = await fetchApi(this.resolveUrl(novelPath, true)).then(res =>
res.text(),
);
const loadedCheerio = parseHTML(body);
const novel: Plugin.SourceNovel = {
path: novelPath,
name: loadedCheerio(".media-name__main").text()?.trim?.() || "",
name: loadedCheerio('.media-name__main').text()?.trim?.() || '',
};
novel.cover = loadedCheerio(".container_responsive img").attr("src");
novel.cover = loadedCheerio('.container_responsive img').attr('src');
novel.summary = loadedCheerio(".media-description__text").text().trim();
novel.summary = loadedCheerio('.media-description__text').text().trim();
novel.genres = loadedCheerio('div[class="media-tags"]')
.text()
.trim()
.replace(/[\n\r]+/g, ", ")
.replace(/ /g, "");
.replace(/[\n\r]+/g, ', ')
.replace(/ /g, '');
loadedCheerio(
'div[class="media-info-list paper"] > [class="media-info-list__item"]',
@@ -92,12 +103,12 @@ class RLIB implements Plugin.PluginBase {
.find('div[class="media-info-list__title"]')
.text();
if (name === "Автор") {
if (name === 'Автор') {
novel.author = loadedCheerio(this)
.find('div[class="media-info-list__value"]')
.text()
.trim();
} else if (name === "Художник") {
} else if (name === 'Художник') {
novel.artist = loadedCheerio(this)
.find('div[class="media-info-list__value"]')
.text()
@@ -105,9 +116,9 @@ class RLIB implements Plugin.PluginBase {
}
});
this.ui = loadedCheerio("a.header-right-menu__item")
.attr("href")
?.replace?.(/[^0-9]/g, "");
this.ui = loadedCheerio('a.header-right-menu__item')
.attr('href')
?.replace?.(/[^0-9]/g, '');
const chaptersRaw = body.match(/window\.__DATA__ = ({.*?});/);
if (chaptersRaw instanceof Array && chaptersRaw.length >= 2) {
@@ -119,7 +130,8 @@ class RLIB implements Plugin.PluginBase {
chaptersJson.manga.engName ||
chaptersJson.manga.name;
}
novel.status = statusKey[chaptersJson.manga.status] || NovelStatus.Unknown;
novel.status =
statusKey[chaptersJson.manga.status] || NovelStatus.Unknown;
this.ui = chaptersJson?.user?.id;
if (!chaptersJson.chapters.list?.length) return novel;
@@ -127,16 +139,19 @@ class RLIB implements Plugin.PluginBase {
const customPage: { [key: number]: string } = {};
const customOrder: { [key: number]: number } = {};
if (chaptersJson.chapters.branches?.length && chaptersJson.chapters.branches.length > 1) {
if (
chaptersJson.chapters.branches?.length &&
chaptersJson.chapters.branches.length > 1
) {
//if the novel is being translated by more than one team
chaptersJson.chapters.branches.forEach(({ teams, id }) => {
if (teams?.length) {
customPage[id || 0] =
teams.find((team) => team.is_active)?.name || teams[0].name;
teams.find(team => team.is_active)?.name || teams[0].name;
}
});
//fixes the chapter's position.
chaptersJson.chapters.list.forEach((chapter) => {
chaptersJson.chapters.list.forEach(chapter => {
chapter.index = customOrder[chapter.branch_id || 0] || 1;
customOrder[chapter.branch_id || 0] =
(customOrder[chapter.branch_id || 0] || 1) + 1;
@@ -147,19 +162,24 @@ class RLIB implements Plugin.PluginBase {
chaptersJson.chapters.list.forEach((chapter, chapterIndex) =>
chapters.push({
name:
"Том " + chapter.chapter_volume +
" Глава " + chapter.chapter_number +
(chapter.chapter_name ? " " + chapter.chapter_name.trim() : ""),
'Том ' +
chapter.chapter_volume +
' Глава ' +
chapter.chapter_number +
(chapter.chapter_name ? ' ' + chapter.chapter_name.trim() : ''),
path:
novelPath +
"/v" + chapter.chapter_volume +
"/c" + chapter.chapter_number +
"?bid=" + (chapter.branch_id || ""),
releaseTime: dayjs(chapter.chapter_created_at).format("LLL"),
'/v' +
chapter.chapter_volume +
'/c' +
chapter.chapter_number +
'?bid=' +
(chapter.branch_id || ''),
releaseTime: dayjs(chapter.chapter_created_at).format('LLL'),
chapterNumber:
customOrder[chapter.branch_id || 0] - (chapter.index || 0) ||
totalChapters - chapterIndex,
page: customPage[chapter.branch_id || 0] || "Основной перевод",
page: customPage[chapter.branch_id || 0] || 'Основной перевод',
}),
);
novel.chapters =
@@ -176,38 +196,39 @@ class RLIB implements Plugin.PluginBase {
}
async parseChapter(chapterPath: string): Promise<string> {
const result = await fetchApi(this.resolveUrl(chapterPath))
.then((res) => res.text());
const result = await fetchApi(this.resolveUrl(chapterPath)).then(res =>
res.text(),
);
const loadedCheerio = parseHTML(result);
loadedCheerio(".reader-container img").each((index, element) => {
loadedCheerio('.reader-container img').each((index, element) => {
const src =
loadedCheerio(element).attr("data-src") ||
loadedCheerio(element).attr("src");
if (!src?.startsWith("http")) {
loadedCheerio(element).attr("src", this.site + src);
loadedCheerio(element).attr('data-src') ||
loadedCheerio(element).attr('src');
if (!src?.startsWith('http')) {
loadedCheerio(element).attr('src', this.site + src);
} else {
loadedCheerio(element).attr("src", src);
loadedCheerio(element).attr('src', src);
}
loadedCheerio(element).removeAttr("data-src");
loadedCheerio(element).removeAttr('data-src');
});
const chapterText = loadedCheerio(".reader-container").html();
return chapterText || "";
const chapterText = loadedCheerio('.reader-container').html();
return chapterText || '';
}
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
const result = await fetchApi(
this.site + "/search?q=" + searchTerm + "&type=manga",
this.site + '/search?q=' + searchTerm + '&type=manga',
);
const body = (await result.json()) as Manga[];
const novels: Plugin.NovelItem[] = [];
body.forEach((novel) =>
body.forEach(novel =>
novels.push({
name: novel.rus_name || novel.name,
cover: novel.coverImage,
path: "/" + novel.slug,
path: '/' + novel.slug,
}),
);
@@ -216,223 +237,223 @@ class RLIB implements Plugin.PluginBase {
fetchImage = fetchFile;
resolveUrl = (path: string, isNovel?: boolean) =>
this.site + path + (this.ui ? (isNovel ? "?" : "&") + "ui=" + this.ui : "");
this.site + path + (this.ui ? (isNovel ? '?' : '&') + 'ui=' + this.ui : '');
filters = {
sort: {
label: "Сортировка",
value: "rate",
label: 'Сортировка',
value: 'rate',
options: [
{ label: "Рейтинг", value: "rate" },
{ label: "Имя", value: "name" },
{ label: "Просмотры", value: "views" },
{ label: "Дате добавления", value: "created_at" },
{ label: "Дате обновления", value: "last_chapter_at" },
{ label: "Количество глав", value: "chap_count" },
{ label: 'Рейтинг', value: 'rate' },
{ label: 'Имя', value: 'name' },
{ label: 'Просмотры', value: 'views' },
{ label: 'Дате добавления', value: 'created_at' },
{ label: 'Дате обновления', value: 'last_chapter_at' },
{ label: 'Количество глав', value: 'chap_count' },
],
type: FilterTypes.Picker,
},
order: {
label: "Порядок",
value: "desc",
label: 'Порядок',
value: 'desc',
options: [
{ label: "По убыванию", value: "desc" },
{ label: "По возрастанию", value: "asc" },
{ label: 'По убыванию', value: 'desc' },
{ label: 'По возрастанию', value: 'asc' },
],
type: FilterTypes.Picker,
},
type: {
label: "Тип",
label: 'Тип',
value: [],
options: [
{ label: "Авторский", value: "14" },
{ label: "Английский", value: "13" },
{ label: "Китай", value: "12" },
{ label: "Корея", value: "11" },
{ label: "Фанфик", value: "15" },
{ label: "Япония", value: "10" },
{ label: 'Авторский', value: '14' },
{ label: 'Английский', value: '13' },
{ label: 'Китай', value: '12' },
{ label: 'Корея', value: '11' },
{ label: 'Фанфик', value: '15' },
{ label: 'Япония', value: '10' },
],
type: FilterTypes.CheckboxGroup,
},
format: {
label: "Формат выпуска",
label: 'Формат выпуска',
value: { include: [], exclude: [] },
options: [
{ label: "4-кома (Ёнкома)", value: "1" },
{ label: "В цвете", value: "4" },
{ label: "Веб", value: "6" },
{ label: "Вебтун", value: "7" },
{ label: "Додзинси", value: "3" },
{ label: "Сборник", value: "2" },
{ label: "Сингл", value: "5" },
{ label: '4-кома (Ёнкома)', value: '1' },
{ label: 'В цвете', value: '4' },
{ label: 'Веб', value: '6' },
{ label: 'Вебтун', value: '7' },
{ label: 'Додзинси', value: '3' },
{ label: 'Сборник', value: '2' },
{ label: 'Сингл', value: '5' },
],
type: FilterTypes.ExcludableCheckboxGroup,
},
status: {
label: "Статус перевода",
label: 'Статус перевода',
value: [],
options: [
{ label: "Продолжается", value: "1" },
{ label: "Завершен", value: "2" },
{ label: "Заморожен", value: "3" },
{ label: "Заброшен", value: "4" },
{ label: 'Продолжается', value: '1' },
{ label: 'Завершен', value: '2' },
{ label: 'Заморожен', value: '3' },
{ label: 'Заброшен', value: '4' },
],
type: FilterTypes.CheckboxGroup,
},
manga_status: {
label: "Статус тайтла",
label: 'Статус тайтла',
value: [],
options: [
{ label: "Онгоинг", value: "1" },
{ label: "Завершён", value: "2" },
{ label: "Анонс", value: "3" },
{ label: "Приостановлен", value: "4" },
{ label: "Выпуск прекращён", value: "5" },
{ label: 'Онгоинг', value: '1' },
{ label: 'Завершён', value: '2' },
{ label: 'Анонс', value: '3' },
{ label: 'Приостановлен', value: '4' },
{ label: 'Выпуск прекращён', value: '5' },
],
type: FilterTypes.CheckboxGroup,
},
genres: {
label: "Жанры",
label: 'Жанры',
value: { include: [], exclude: [] },
options: [
{ label: "Арт", value: "32" },
{ label: "Безумие", value: "91" },
{ label: "Боевик", value: "34" },
{ label: "Боевые искусства", value: "35" },
{ label: "Вампиры", value: "36" },
{ label: "Военное", value: "89" },
{ label: "Гарем", value: "37" },
{ label: "Гендерная интрига", value: "38" },
{ label: "Героическое фэнтези", value: "39" },
{ label: "Демоны", value: "81" },
{ label: "Детектив", value: "40" },
{ label: "Детское", value: "88" },
{ label: "Дзёсэй", value: "41" },
{ label: "Драма", value: "43" },
{ label: "Игра", value: "44" },
{ label: "Исекай", value: "79" },
{ label: "История", value: "45" },
{ label: "Киберпанк", value: "46" },
{ label: "Кодомо", value: "76" },
{ label: "Комедия", value: "47" },
{ label: "Космос", value: "83" },
{ label: "Магия", value: "85" },
{ label: "Махо-сёдзё", value: "48" },
{ label: "Машины", value: "90" },
{ label: "Меха", value: "49" },
{ label: "Мистика", value: "50" },
{ label: "Музыка", value: "80" },
{ label: "Научная фантастика", value: "51" },
{ label: "Омегаверс", value: "77" },
{ label: "Пародия", value: "86" },
{ label: "Повседневность", value: "52" },
{ label: "Полиция", value: "82" },
{ label: "Постапокалиптика", value: "53" },
{ label: "Приключения", value: "54" },
{ label: "Психология", value: "55" },
{ label: "Романтика", value: "56" },
{ label: "Самурайский боевик", value: "57" },
{ label: "Сверхъестественное", value: "58" },
{ label: "Сёдзё", value: "59" },
{ label: "Сёдзё-ай", value: "60" },
{ label: "Сёнэн", value: "61" },
{ label: "Сёнэн-ай", value: "62" },
{ label: "Спорт", value: "63" },
{ label: "Супер сила", value: "87" },
{ label: "Сэйнэн", value: "64" },
{ label: "Трагедия", value: "65" },
{ label: "Триллер", value: "66" },
{ label: "Ужасы", value: "67" },
{ label: "Фантастика", value: "68" },
{ label: "Фэнтези", value: "69" },
{ label: "Школа", value: "70" },
{ label: "Эротика", value: "71" },
{ label: "Этти", value: "72" },
{ label: "Юри", value: "73" },
{ label: "Яой", value: "74" },
{ label: 'Арт', value: '32' },
{ label: 'Безумие', value: '91' },
{ label: 'Боевик', value: '34' },
{ label: 'Боевые искусства', value: '35' },
{ label: 'Вампиры', value: '36' },
{ label: 'Военное', value: '89' },
{ label: 'Гарем', value: '37' },
{ label: 'Гендерная интрига', value: '38' },
{ label: 'Героическое фэнтези', value: '39' },
{ label: 'Демоны', value: '81' },
{ label: 'Детектив', value: '40' },
{ label: 'Детское', value: '88' },
{ label: 'Дзёсэй', value: '41' },
{ label: 'Драма', value: '43' },
{ label: 'Игра', value: '44' },
{ label: 'Исекай', value: '79' },
{ label: 'История', value: '45' },
{ label: 'Киберпанк', value: '46' },
{ label: 'Кодомо', value: '76' },
{ label: 'Комедия', value: '47' },
{ label: 'Космос', value: '83' },
{ label: 'Магия', value: '85' },
{ label: 'Махо-сёдзё', value: '48' },
{ label: 'Машины', value: '90' },
{ label: 'Меха', value: '49' },
{ label: 'Мистика', value: '50' },
{ label: 'Музыка', value: '80' },
{ label: 'Научная фантастика', value: '51' },
{ label: 'Омегаверс', value: '77' },
{ label: 'Пародия', value: '86' },
{ label: 'Повседневность', value: '52' },
{ label: 'Полиция', value: '82' },
{ label: 'Постапокалиптика', value: '53' },
{ label: 'Приключения', value: '54' },
{ label: 'Психология', value: '55' },
{ label: 'Романтика', value: '56' },
{ label: 'Самурайский боевик', value: '57' },
{ label: 'Сверхъестественное', value: '58' },
{ label: 'Сёдзё', value: '59' },
{ label: 'Сёдзё-ай', value: '60' },
{ label: 'Сёнэн', value: '61' },
{ label: 'Сёнэн-ай', value: '62' },
{ label: 'Спорт', value: '63' },
{ label: 'Супер сила', value: '87' },
{ label: 'Сэйнэн', value: '64' },
{ label: 'Трагедия', value: '65' },
{ label: 'Триллер', value: '66' },
{ label: 'Ужасы', value: '67' },
{ label: 'Фантастика', value: '68' },
{ label: 'Фэнтези', value: '69' },
{ label: 'Школа', value: '70' },
{ label: 'Эротика', value: '71' },
{ label: 'Этти', value: '72' },
{ label: 'Юри', value: '73' },
{ label: 'Яой', value: '74' },
],
type: FilterTypes.ExcludableCheckboxGroup,
},
tags: {
label: "Теги",
label: 'Теги',
value: { include: [], exclude: [] },
options: [
{ label: "Авантюристы", value: "328" },
{ label: "Антигерой", value: "176" },
{ label: "Бессмертные", value: "333" },
{ label: "Боги", value: "218" },
{ label: "Борьба за власть", value: "309" },
{ label: "Брат и сестра", value: "360" },
{ label: "Ведьма", value: "339" },
{ label: "Видеоигры", value: "204" },
{ label: "Виртуальная реальность", value: "214" },
{ label: "Владыка демонов", value: "349" },
{ label: "Военные", value: "198" },
{ label: "Воспоминания из другого мира", value: "310" },
{ label: "Выживание", value: "212" },
{ label: "ГГ женщина", value: "294" },
{ label: "ГГ имба", value: "292" },
{ label: "ГГ мужчина", value: "295" },
{ label: "ГГ не ояш", value: "325" },
{ label: "ГГ не человек", value: "331" },
{ label: "ГГ ояш", value: "326" },
{ label: "Главный герой бог", value: "324" },
{ label: "Глупый ГГ", value: "298" },
{ label: "Горничные", value: "171" },
{ label: "Гуро", value: "306" },
{ label: "Гяру", value: "197" },
{ label: "Демоны", value: "157" },
{ label: "Драконы", value: "313" },
{ label: "Древний мир", value: "317" },
{ label: "Зверолюди", value: "163" },
{ label: "Зомби", value: "155" },
{ label: "Исторические фигуры", value: "323" },
{ label: "Кулинария", value: "158" },
{ label: "Культивация", value: "161" },
{ label: "ЛГБТ", value: "344" },
{ label: "ЛитРПГ", value: "319" },
{ label: "Лоли", value: "206" },
{ label: "Магия", value: "170" },
{ label: "Машинный перевод", value: "345" },
{ label: "Медицина", value: "159" },
{ label: "Межгалактическая война", value: "330" },
{ label: "Монстр Девушки", value: "207" },
{ label: "Монстры", value: "208" },
{ label: "Мрачный мир", value: "316" },
{ label: "Музыка", value: "358" },
{ label: "Музыка", value: "209" },
{ label: "Ниндзя", value: "199" },
{ label: "Обратный Гарем", value: "210" },
{ label: "Офисные Работники", value: "200" },
{ label: "Пираты", value: "341" },
{ label: "Подземелья", value: "314" },
{ label: "Политика", value: "311" },
{ label: "Полиция", value: "201" },
{ label: "Преступники / Криминал", value: "205" },
{ label: "Призраки / Духи", value: "196" },
{ label: "Призыватели", value: "329" },
{ label: "Прыжки между мирами", value: "321" },
{ label: "Путешествие в другой мир", value: "318" },
{ label: "Путешествие во времени", value: "213" },
{ label: "Рабы", value: "355" },
{ label: "Ранги силы", value: "312" },
{ label: "Реинкарнация", value: "154" },
{ label: "Самураи", value: "202" },
{ label: "Скрытие личности", value: "315" },
{ label: "Средневековье", value: "174" },
{ label: "Традиционные игры", value: "203" },
{ label: "Умный ГГ", value: "303" },
{ label: "Характерный рост", value: "332" },
{ label: "Хикикомори", value: "167" },
{ label: "Эволюция", value: "322" },
{ label: "Элементы РПГ", value: "327" },
{ label: "Эльфы", value: "217" },
{ label: "Якудза", value: "165" },
{ label: 'Авантюристы', value: '328' },
{ label: 'Антигерой', value: '176' },
{ label: 'Бессмертные', value: '333' },
{ label: 'Боги', value: '218' },
{ label: 'Борьба за власть', value: '309' },
{ label: 'Брат и сестра', value: '360' },
{ label: 'Ведьма', value: '339' },
{ label: 'Видеоигры', value: '204' },
{ label: 'Виртуальная реальность', value: '214' },
{ label: 'Владыка демонов', value: '349' },
{ label: 'Военные', value: '198' },
{ label: 'Воспоминания из другого мира', value: '310' },
{ label: 'Выживание', value: '212' },
{ label: 'ГГ женщина', value: '294' },
{ label: 'ГГ имба', value: '292' },
{ label: 'ГГ мужчина', value: '295' },
{ label: 'ГГ не ояш', value: '325' },
{ label: 'ГГ не человек', value: '331' },
{ label: 'ГГ ояш', value: '326' },
{ label: 'Главный герой бог', value: '324' },
{ label: 'Глупый ГГ', value: '298' },
{ label: 'Горничные', value: '171' },
{ label: 'Гуро', value: '306' },
{ label: 'Гяру', value: '197' },
{ label: 'Демоны', value: '157' },
{ label: 'Драконы', value: '313' },
{ label: 'Древний мир', value: '317' },
{ label: 'Зверолюди', value: '163' },
{ label: 'Зомби', value: '155' },
{ label: 'Исторические фигуры', value: '323' },
{ label: 'Кулинария', value: '158' },
{ label: 'Культивация', value: '161' },
{ label: 'ЛГБТ', value: '344' },
{ label: 'ЛитРПГ', value: '319' },
{ label: 'Лоли', value: '206' },
{ label: 'Магия', value: '170' },
{ label: 'Машинный перевод', value: '345' },
{ label: 'Медицина', value: '159' },
{ label: 'Межгалактическая война', value: '330' },
{ label: 'Монстр Девушки', value: '207' },
{ label: 'Монстры', value: '208' },
{ label: 'Мрачный мир', value: '316' },
{ label: 'Музыка', value: '358' },
{ label: 'Музыка', value: '209' },
{ label: 'Ниндзя', value: '199' },
{ label: 'Обратный Гарем', value: '210' },
{ label: 'Офисные Работники', value: '200' },
{ label: 'Пираты', value: '341' },
{ label: 'Подземелья', value: '314' },
{ label: 'Политика', value: '311' },
{ label: 'Полиция', value: '201' },
{ label: 'Преступники / Криминал', value: '205' },
{ label: 'Призраки / Духи', value: '196' },
{ label: 'Призыватели', value: '329' },
{ label: 'Прыжки между мирами', value: '321' },
{ label: 'Путешествие в другой мир', value: '318' },
{ label: 'Путешествие во времени', value: '213' },
{ label: 'Рабы', value: '355' },
{ label: 'Ранги силы', value: '312' },
{ label: 'Реинкарнация', value: '154' },
{ label: 'Самураи', value: '202' },
{ label: 'Скрытие личности', value: '315' },
{ label: 'Средневековье', value: '174' },
{ label: 'Традиционные игры', value: '203' },
{ label: 'Умный ГГ', value: '303' },
{ label: 'Характерный рост', value: '332' },
{ label: 'Хикикомори', value: '167' },
{ label: 'Эволюция', value: '322' },
{ label: 'Элементы РПГ', value: '327' },
{ label: 'Эльфы', value: '217' },
{ label: 'Якудза', value: '165' },
],
type: FilterTypes.ExcludableCheckboxGroup,
},
require_chapters: {
label: "Только проекты с главами",
label: 'Только проекты с главами',
value: true,
type: FilterTypes.Switch,
},