Plugins fixed (#903)

* test

* fast fix

* fix

* fix

* Update ranobelib.ts
This commit is contained in:
Rider21
2024-04-25 17:53:16 +03:00
committed by GitHub
parent 2c4490e5bd
commit 9bc558936d
24 changed files with 63683 additions and 62335 deletions
+127 -218
View File
@@ -3,20 +3,12 @@ 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,
2: NovelStatus.Completed,
3: NovelStatus.OnHiatus,
4: NovelStatus.Cancelled,
};
class RLIB implements Plugin.PluginBase {
id = 'RLIB';
name = 'RanobeLib';
site = 'https://ranobelib.me';
version = '1.0.0';
name = 'RanobeLib (OLD)';
site = 'https://old.ranobelib.me/old/';
version = '1.1.0';
icon = 'src/ru/ranobelib/icon.png';
ui: string | undefined = undefined;
@@ -27,51 +19,71 @@ 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?page=' + pageNo;
url +=
'&sort_by=' +
(showLatestNovels
? 'last_chapter_at'
: filters?.sort_by?.value || 'rating_score');
url += '&sort_type=' + (filters?.sort_type?.value || 'desc');
Object.entries(filters || {}).forEach(([type, { value }]: any) => {
if (value instanceof Array && value.length) {
url += '&' + type + '[]=' + value.join('&' + type + '[]=');
}
if (value?.include instanceof Array && value.include.length) {
url +=
'&' +
type +
'[include][]=' +
value.include.join('&' + type + '[include][]=');
}
if (value?.exclude instanceof Array && value.exclude.length) {
url +=
'&' +
type +
'[exclude][]=' +
value.exclude.join('&' + type + '[exclude][]=');
}
});
if (filters?.require_chapters?.value) {
url += '&chapters[min]=1';
}
if (filters?.types?.value?.length) {
url += '&types[]=' + filters.types.value.join('&types[]=');
}
if (filters?.scanlateStatus?.value?.length) {
url +=
'&scanlateStatus[]=' +
filters.scanlateStatus.value.join('&scanlateStatus[]=');
}
if (filters?.manga_status?.value?.length) {
url +=
'&manga_status[]=' +
filters.manga_status.value.join('&manga_status[]=');
}
url += '&page=' + pageNo;
if (filters?.genres) {
if (filters.genres.value?.include?.length) {
url += '&genres[]=' + filters.genres.value.include.join('&genres[]=');
}
if (filters.genres.value?.exclude?.length) {
url +=
'&genres_exclude[]=' +
filters.genres.value.exclude.join('&genres_exclude[]=');
}
}
if (filters?.tags) {
if (filters.tags.value?.include?.length) {
url += '&tags[]=' + filters.tags.value.include.join('&tags[]=');
}
if (filters.tags.value?.exclude?.length) {
url +=
'&tags_exclude[]=' +
filters.tags.value.exclude.join('&tags_exclude[]=');
}
}
const result = await fetchApi(url).then(res => res.text());
const loadedCheerio = parseHTML(result);
const novels: Plugin.NovelItem[] = [];
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');
if (!url) return;
novels.push({ name, cover, path: url.replace(this.site, '') });
});
const novelsRaw = result.match(/window\.__CATALOG_ITEMS__ = (\[.*?\]);/);
if (novelsRaw instanceof Array && novelsRaw.length >= 2) {
const novelsJson: resNovels[] = JSON.parse(novelsRaw[1]);
novelsJson.forEach(novel => {
novels.push({
name: novel.rus_name,
cover: novel.cover.default,
path: novel.slug_url,
});
});
}
return novels;
}
@@ -97,7 +109,7 @@ class RLIB implements Plugin.PluginBase {
.replace(/ /g, '');
loadedCheerio(
'div[class="media-info-list paper"] > [class="media-info-list__item"]',
'div.media-info-list > div[class="media-info-list__item"]',
).each(function () {
let name = loadedCheerio(this)
.find('div[class="media-info-list__title"]')
@@ -120,77 +132,26 @@ class RLIB implements Plugin.PluginBase {
.attr('href')
?.replace?.(/[^0-9]/g, '');
const chaptersRaw = body.match(/window\.__DATA__ = ({.*?});/);
const chaptersRaw = body.match(/window\.__CHAPTERS__ = (\[.*?\]);/);
if (chaptersRaw instanceof Array && chaptersRaw.length >= 2) {
const chaptersJson: responseBook = JSON.parse(chaptersRaw[1]);
const chaptersJson: resChapters[] = JSON.parse(chaptersRaw[1]);
if (!novel.name) {
novel.name =
chaptersJson.manga.rusName ||
chaptersJson.manga.engName ||
chaptersJson.manga.name;
}
novel.status =
statusKey[chaptersJson.manga.status] || NovelStatus.Unknown;
this.ui = chaptersJson?.user?.id;
if (!chaptersJson.chapters.list?.length) return novel;
const totalChapters = chaptersJson.chapters.list.length;
const customPage: { [key: number]: string } = {};
const customOrder: { [key: number]: number } = {};
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;
}
});
//fixes the chapter's position.
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;
});
}
if (!chaptersJson?.length) return novel;
const chapters: Plugin.ChapterItem[] = [];
chaptersJson.chapters.list.forEach((chapter, chapterIndex) =>
chaptersJson.forEach(chapter =>
chapters.push({
name:
'Том ' +
chapter.chapter_volume +
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'),
chapterNumber:
customOrder[chapter.branch_id || 0] - (chapter.index || 0) ||
totalChapters - chapterIndex,
page: customPage[chapter.branch_id || 0] || 'Основной перевод',
chapter.number +
(chapter.name ? ' ' + chapter.name.trim() : ''),
path: novelPath + '/v' + chapter.volume + '/c' + chapter.number,
chapterNumber: chapter.index + 1,
}),
);
novel.chapters =
chaptersJson.chapters.branches?.length &&
chaptersJson.chapters.branches.length > 1
? chapters.sort((a, b) => {
if ((a.page || 0) > (b.page || 0)) return 1;
if ((a.page || 0) < (b.page || 0)) return -1;
return (a.chapterNumber || 0) - (b.chapterNumber || 0);
})
: chapters.reverse();
novel.chapters = chapters;
}
return novel;
}
@@ -218,17 +179,15 @@ class RLIB implements Plugin.PluginBase {
}
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
const result = await fetchApi(
this.site + '/search?q=' + searchTerm + '&type=manga',
);
const body = (await result.json()) as Manga[];
const result = await fetchApi(this.site + 'api/manga?q=' + searchTerm);
const { data }: { data: resNovels[] } = await result.json();
const novels: Plugin.NovelItem[] = [];
body.forEach(novel =>
data.forEach(novel =>
novels.push({
name: novel.rus_name || novel.name,
cover: novel.coverImage,
path: '/' + novel.slug,
cover: novel?.cover?.default || '',
path: novel.slug_url,
}),
);
@@ -237,23 +196,25 @@ 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 ? '?ui=' + this.ui : '');
filters = {
sort: {
sort_by: {
label: 'Сортировка',
value: 'rate',
value: 'rating_score',
options: [
{ label: 'Рейтинг', value: 'rate' },
{ label: 'Имя', value: 'name' },
{ label: 'Просмотры', value: 'views' },
{ label: 'Дате добавления', value: 'created_at' },
{ label: 'По рейтингу', value: 'rate_avg' },
{ label: 'По популярности', value: 'rating_score' },
{ label: 'По просмотрам', value: 'views' },
{ label: 'Количеству глав', value: 'chap_count' },
{ label: 'Дате обновления', value: 'last_chapter_at' },
{ label: 'Количество глав', value: 'chap_count' },
{ label: 'Дате добавления', value: 'created_at' },
{ label: 'По названию (A-Z)', value: 'name' },
{ label: 'По названию (А-Я)', value: 'rus_name' },
],
type: FilterTypes.Picker,
},
order: {
sort_type: {
label: 'Порядок',
value: 'desc',
options: [
@@ -262,34 +223,20 @@ class RLIB implements Plugin.PluginBase {
],
type: FilterTypes.Picker,
},
type: {
types: {
label: 'Тип',
value: [],
options: [
{ label: 'Авторский', value: '14' },
{ label: 'Английский', value: '13' },
{ label: 'Китай', value: '12' },
{ label: 'Корея', value: '11' },
{ label: 'Фанфик', value: '15' },
{ label: 'Япония', value: '10' },
{ label: 'Корея', value: '11' },
{ label: 'Китай', value: '12' },
{ label: 'Английский', value: '13' },
{ label: 'Авторский', value: '14' },
{ label: 'Фанфик', value: '15' },
],
type: FilterTypes.CheckboxGroup,
},
format: {
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' },
],
type: FilterTypes.ExcludableCheckboxGroup,
},
status: {
scanlateStatus: {
label: 'Статус перевода',
value: [],
options: [
@@ -366,6 +313,7 @@ class RLIB implements Plugin.PluginBase {
{ label: 'Ужасы', value: '67' },
{ label: 'Фантастика', value: '68' },
{ label: 'Фэнтези', value: '69' },
{ label: 'Хентай', value: '84' },
{ label: 'Школа', value: '70' },
{ label: 'Эротика', value: '71' },
{ label: 'Этти', value: '72' },
@@ -379,7 +327,7 @@ class RLIB implements Plugin.PluginBase {
value: { include: [], exclude: [] },
options: [
{ label: 'Авантюристы', value: '328' },
{ label: 'Антигерой', value: '176' },
{ label: 'Антигерой', value: '175' },
{ label: 'Бессмертные', value: '333' },
{ label: 'Боги', value: '218' },
{ label: 'Борьба за власть', value: '309' },
@@ -462,85 +410,46 @@ class RLIB implements Plugin.PluginBase {
export default new RLIB();
interface responseBook {
hasStickyPermission: boolean;
bookmark?: null;
auth: boolean;
comments_version: string;
manga: Manga;
chapters: Chapters;
user?: User;
}
interface Manga {
interface resNovels {
id: number;
name: string;
rusName?: string;
rus_name?: string;
engName?: string;
rus_name: string;
eng_name: string;
slug: string;
status: number;
chapters_count: number;
altNames?: string[] | null;
coverImage?: string;
href?: string;
slug_url: string;
cover: Cover;
ageRestriction: AgeRestrictionOrTypeOrStatus;
site: number;
type: AgeRestrictionOrTypeOrStatus;
rating: Rating;
is_licensed: boolean;
model: string;
status: AgeRestrictionOrTypeOrStatus;
releaseDateString: string;
}
interface Chapters {
list?: ListEntity[];
teams?: TeamsEntity[];
branches?: BranchesEntity[];
is_paid?: string[] | null;
interface Cover {
filename: string;
thumbnail: string;
default: string;
}
interface ListEntity {
index?: number; //crutch
chapter_id: number;
chapter_slug: string;
chapter_name: string;
chapter_number: string;
chapter_volume: number;
chapter_moderated: number;
chapter_user_id: number;
chapter_expired_at: string;
chapter_scanlator_id: number;
chapter_created_at: string;
status?: null;
price: number;
branch_id?: number;
username: string;
}
interface TeamsEntity {
name: string;
alt_name: string;
cover: string;
slug: string;
interface AgeRestrictionOrTypeOrStatus {
id: number;
branch_id: number;
sale: number;
href: string;
pivot: Pivot;
label: string;
}
interface Pivot {
manga_id: number;
team_id: number;
interface Rating {
average: string;
averageFormated: string;
votes: number;
votesFormated: string;
user: number;
}
interface BranchesEntity {
interface resChapters {
id: number;
manga_id: number;
index: number;
item_number: number;
volume: string;
number: string;
number_secondary: string;
name: string;
teams?: TeamsEntity1[] | null;
is_subscribed: boolean;
}
interface TeamsEntity1 {
id: number;
name: string;
slug: string;
cover: string;
branch_id: number;
is_active: number;
}
interface User {
id: string;
avatar: string;
access: boolean;
isAdmin: boolean;
paid: boolean;
}