Feat: Vite + React (#991)
* remove test_web * integrate reactjs-vite * update lint-staged * simple cors proxy server which handles cookies and redirections * overrider global fetch with proxy call * simple examples for set cookies and fetch request * app bar * add redux * loadPlugins. error: cant load urlencode module * add ts-node for transforming * plugin search & select * plugin slice * accordion components * path item & popular novel * optional for filters * search novel * parse novel * parseChapter * update cookies * default filters * fix: import path * bundler module resolution * new json_plugins * fix: generate multisrc (missing filters) * fix: host scripts * fix: plugin code path * remove typechecking action
This commit is contained in:
@@ -1,454 +0,0 @@
|
||||
import { Plugin } from '@typings/plugin';
|
||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||
import { defaultCover } from '@libs/defaultCover';
|
||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
||||
import { NovelStatus } from '@libs/novelStatus';
|
||||
|
||||
const statusKey: { [key: string]: string } = {
|
||||
'0': NovelStatus.Unknown,
|
||||
'1': NovelStatus.Ongoing,
|
||||
'2': NovelStatus.Completed,
|
||||
'3': NovelStatus.OnHiatus,
|
||||
'4': NovelStatus.Cancelled,
|
||||
};
|
||||
|
||||
class Neobook implements Plugin.PluginBase {
|
||||
id = 'neobook';
|
||||
name = 'Neobook';
|
||||
site = 'https://neobook.org';
|
||||
version = '1.0.1';
|
||||
icon = 'src/ru/neobook/icon.png';
|
||||
|
||||
async fetchNovels(
|
||||
page: number,
|
||||
{
|
||||
filters,
|
||||
showLatestNovels,
|
||||
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
||||
searchTerm?: string,
|
||||
): Promise<Plugin.NovelItem[]> {
|
||||
const formData = new FormData();
|
||||
formData.append('version', '4.0');
|
||||
formData.append('uid', '0');
|
||||
formData.append('utoken', '');
|
||||
formData.append('resource', 'general');
|
||||
formData.append('action', 'get_bundle');
|
||||
formData.append('bundle', 'bundle_books');
|
||||
formData.append('target', 'feed');
|
||||
formData.append('page', page.toString());
|
||||
formData.append('filter_category_id', filters?.category?.value || '0');
|
||||
formData.append('filter_completed', '-1');
|
||||
formData.append('filter_search', searchTerm || '');
|
||||
formData.append('filter_tags', filters?.tags?.value || '');
|
||||
formData.append(
|
||||
'filter_sort',
|
||||
showLatestNovels ? 'new' : filters?.sort?.value || 'popular',
|
||||
);
|
||||
formData.append('filter_timeread', filters?.timeread?.value || '0-999999');
|
||||
|
||||
const { bundle_books }: { bundle_books: BundleBooks } = await fetchApi(
|
||||
'https://api.neobook.org/',
|
||||
{
|
||||
method: 'post',
|
||||
body: formData,
|
||||
},
|
||||
).then(res => res.json());
|
||||
const novels: Plugin.NovelItem[] = [];
|
||||
|
||||
if (bundle_books.feed?.length) {
|
||||
bundle_books.feed?.forEach(novel =>
|
||||
novels.push({
|
||||
name: novel.title,
|
||||
cover: novel?.attachment?.image?.m || defaultCover,
|
||||
path: novel.token + '/',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return novels;
|
||||
}
|
||||
|
||||
popularNovels = this.fetchNovels;
|
||||
|
||||
async searchNovels(
|
||||
searchTerm: string,
|
||||
page: number,
|
||||
): Promise<Plugin.NovelItem[]> {
|
||||
const defaultOptions: any = {
|
||||
filters: undefined,
|
||||
showLatestNovels: false,
|
||||
};
|
||||
return this.fetchNovels(page, defaultOptions, searchTerm);
|
||||
}
|
||||
|
||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||
const body = await fetchApi(this.resolveUrl(novelPath, true)).then(res =>
|
||||
res.text(),
|
||||
);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
path: novelPath,
|
||||
name: '',
|
||||
cover: defaultCover,
|
||||
};
|
||||
|
||||
const bookRaw = body.match(/var postData = ({.*?});/);
|
||||
if (bookRaw instanceof Array && bookRaw[1]) {
|
||||
const book: Novels = JSON.parse(bookRaw[1]);
|
||||
|
||||
novel.name = book.title;
|
||||
novel.summary = book.text?.replace?.(/<br>/g, '\n') || book.text_fix;
|
||||
novel.author =
|
||||
book.user && book.user.firstname && book.user.lastname
|
||||
? book.user.firstname + ' ' + book.user.lastname
|
||||
: book.user?.initials || '';
|
||||
novel.status = statusKey[book.status || '0'] || NovelStatus.Unknown;
|
||||
|
||||
if (book.attachment?.image?.m) novel.cover = book.attachment.image.m;
|
||||
if (book.tags?.length) novel.genres = book.tags.join(',');
|
||||
|
||||
const chapters: Plugin.ChapterItem[] = [];
|
||||
|
||||
book.chapters?.forEach((chapter, chapterIndex) => {
|
||||
if (chapter.access == '1' && chapter.status == '1') {
|
||||
chapters.push({
|
||||
name: chapter.title || 'Глава ' + (chapterIndex + 1),
|
||||
path: `?book=${book.token}&chapter=${chapter.token}`,
|
||||
releaseTime: null,
|
||||
chapterNumber: Number(chapter.sort) || chapterIndex + 1,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
novel.chapters = chapters;
|
||||
}
|
||||
return novel;
|
||||
}
|
||||
|
||||
async parseChapter(chapterPath: string): Promise<string> {
|
||||
const body = await fetchApi(this.resolveUrl(chapterPath)).then(res =>
|
||||
res.text(),
|
||||
);
|
||||
|
||||
const bookRaw = body.match(/var data = ({.*?});/);
|
||||
let chapterText = '';
|
||||
|
||||
if (bookRaw instanceof Array && bookRaw[1]) {
|
||||
const token = chapterPath.split('=')[2];
|
||||
const book: Novels = JSON.parse(bookRaw[1]);
|
||||
const chapter = book.chapters?.find?.(chapter => chapter.token == token);
|
||||
chapterText = (chapter?.data?.html || '').replace(/<br>/g, '');
|
||||
}
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
|
||||
resolveUrl = (path: string, isNovel?: boolean) =>
|
||||
this.site + (isNovel ? '/book/' : '/reader/') + path;
|
||||
|
||||
filters = {
|
||||
sort: {
|
||||
label: 'Сортировка:',
|
||||
value: 'popular',
|
||||
options: [
|
||||
{ label: 'Сначала популярные', value: 'popular' },
|
||||
{ label: 'Сначала новые', value: 'new' },
|
||||
{ label: 'В случайном порядке', value: 'rand' },
|
||||
],
|
||||
type: FilterTypes.Picker,
|
||||
},
|
||||
timeread: {
|
||||
label: 'Время прочтения:',
|
||||
value: '0-999999',
|
||||
options: [
|
||||
{ label: 'Все', value: '0-999999' },
|
||||
{ label: 'Менее 15 минут', value: '0-900' },
|
||||
{ label: '15-30 минут', value: '900-1800' },
|
||||
{ label: '30-60 минут', value: '1800-3600' },
|
||||
{ label: '1-2 часа', value: '3600-7200' },
|
||||
{ label: 'Более 2 часов', value: '7200-999999' },
|
||||
],
|
||||
type: FilterTypes.Picker,
|
||||
},
|
||||
category: {
|
||||
label: 'Жанр:',
|
||||
value: '',
|
||||
options: [
|
||||
{ label: 'Все', value: '' },
|
||||
{ label: 'Антиутопия', value: '10' },
|
||||
{ label: 'Детектив', value: '13' },
|
||||
{ label: 'Детские книги', value: '14' },
|
||||
{ label: 'Драма', value: '15' },
|
||||
{ label: 'Другое', value: '16' },
|
||||
{ label: 'История', value: '18' },
|
||||
{ label: 'Мелодрама', value: '21' },
|
||||
{ label: 'Мистика', value: '22' },
|
||||
{ label: 'Научная фантастика', value: '23' },
|
||||
{ label: 'Нон-фикшн', value: '35' },
|
||||
{ label: 'Подростки и молодежь', value: '26' },
|
||||
{ label: 'Постапокалипсис', value: '27' },
|
||||
{ label: 'Поэзия', value: '28' },
|
||||
{ label: 'Приключения', value: '29' },
|
||||
{ label: 'Рассказ', value: '34' },
|
||||
{ label: 'Роман', value: '36' },
|
||||
{ label: 'Творчество', value: '40' },
|
||||
{ label: 'Триллер', value: '91' },
|
||||
{ label: 'Ужасы', value: '90' },
|
||||
{ label: 'Фантастика', value: '41' },
|
||||
{ label: 'Фанфик', value: '42' },
|
||||
{ label: 'Фэнтези', value: '44' },
|
||||
{ label: 'Эротика', value: '46' },
|
||||
{ label: 'Юмор', value: '47' },
|
||||
],
|
||||
type: FilterTypes.Picker,
|
||||
},
|
||||
tags: {
|
||||
label: 'Тэги:',
|
||||
value: '',
|
||||
type: FilterTypes.TextInput,
|
||||
},
|
||||
} satisfies Filters;
|
||||
}
|
||||
|
||||
export default new Neobook();
|
||||
|
||||
interface BundleBooks {
|
||||
categories: any[];
|
||||
topSection: any[];
|
||||
feed: Novels[];
|
||||
}
|
||||
|
||||
interface BottomSection {
|
||||
id: string;
|
||||
boost: string;
|
||||
preferenceid: string;
|
||||
type_id: string;
|
||||
contentTypeid: string;
|
||||
name: string;
|
||||
title: string;
|
||||
more: string;
|
||||
part: string;
|
||||
items: Novels[];
|
||||
}
|
||||
|
||||
interface Novels {
|
||||
status?: string;
|
||||
error?: string;
|
||||
login?: Login;
|
||||
requestid?: string;
|
||||
bundleBooks?: BundleBooks;
|
||||
available?: string;
|
||||
boosted?: string;
|
||||
id?: string;
|
||||
token: string;
|
||||
type?: string;
|
||||
typeid?: string;
|
||||
verified?: string;
|
||||
audio?: string;
|
||||
labelid?: string;
|
||||
labelTitle?: string;
|
||||
languageid?: string;
|
||||
languageTitle?: string;
|
||||
categoryid?: string;
|
||||
categoryTitle?: string;
|
||||
price?: Price;
|
||||
priceSpecial?: Price;
|
||||
priceDiscount?: string;
|
||||
title: string;
|
||||
text: string;
|
||||
text_fix: string;
|
||||
copyright?: string;
|
||||
align?: string;
|
||||
completed?: string;
|
||||
timereadS?: string;
|
||||
views?: string;
|
||||
likes?: string;
|
||||
comments?: string;
|
||||
bookmarks?: string;
|
||||
isLike?: string;
|
||||
isBookmark?: string;
|
||||
isPurchase?: string;
|
||||
isSelf?: string;
|
||||
dateEdit?: string;
|
||||
datePublish?: string;
|
||||
link_path?: string;
|
||||
user?: NovelsUser;
|
||||
attachment?: Attachment;
|
||||
adult?: string;
|
||||
adultPlaceholder?: string;
|
||||
nft?: string;
|
||||
feed?: Feed;
|
||||
counters?: NovelsCounters;
|
||||
rating?: Rating;
|
||||
readTime?: ReadTime;
|
||||
readProgress?: ReadProgress;
|
||||
tags?: string[];
|
||||
chapters?: Chapter[];
|
||||
lastComments?: any[];
|
||||
bottomSection?: BottomSection;
|
||||
carouselItems?: Novels[];
|
||||
book?: Novels;
|
||||
activeChapterIndex?: number;
|
||||
}
|
||||
|
||||
interface Attachment {
|
||||
has: string;
|
||||
id: string;
|
||||
typeid: string;
|
||||
userid: string;
|
||||
width: string;
|
||||
height: string;
|
||||
duration: string;
|
||||
image: Image;
|
||||
video: string;
|
||||
origin: string;
|
||||
ext: string;
|
||||
}
|
||||
|
||||
interface Image {
|
||||
s: string;
|
||||
m: string;
|
||||
l: string;
|
||||
has?: string;
|
||||
}
|
||||
|
||||
interface Chapter {
|
||||
id: string;
|
||||
token: string;
|
||||
status: string;
|
||||
sort: string;
|
||||
free: string;
|
||||
title: string;
|
||||
words: string;
|
||||
access: string;
|
||||
loaded: string;
|
||||
data?: Data;
|
||||
}
|
||||
|
||||
interface Data {
|
||||
available: string;
|
||||
access: string;
|
||||
id: string;
|
||||
token: string;
|
||||
postid: string;
|
||||
free: string;
|
||||
title: string;
|
||||
html: string;
|
||||
dateEdit: string;
|
||||
datePublish: string;
|
||||
attachment: Attachment;
|
||||
lastComments: any[];
|
||||
}
|
||||
|
||||
interface NovelsCounters {
|
||||
impressions: Bookmarks;
|
||||
views: Bookmarks;
|
||||
likes: Bookmarks;
|
||||
comments: Bookmarks;
|
||||
bookmarks: Bookmarks;
|
||||
}
|
||||
|
||||
interface Bookmarks {
|
||||
value: string;
|
||||
formatted: string;
|
||||
}
|
||||
|
||||
interface Feed {
|
||||
name: string;
|
||||
canRemoved: string;
|
||||
}
|
||||
|
||||
interface Login {
|
||||
uid: string;
|
||||
utoken: string;
|
||||
has: string;
|
||||
banned: string;
|
||||
verified: string;
|
||||
acc: string;
|
||||
arm: string;
|
||||
grm: string;
|
||||
counters: LoginCounters;
|
||||
user: LoginUser;
|
||||
pro: any[];
|
||||
earn: any[];
|
||||
boost: any[];
|
||||
deposit: any[];
|
||||
affiliate: any[];
|
||||
preferences: any[];
|
||||
}
|
||||
|
||||
interface LoginCounters {
|
||||
notifications: string;
|
||||
messages: string;
|
||||
}
|
||||
|
||||
interface LoginUser {
|
||||
id: string;
|
||||
username: string;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
photo: string;
|
||||
status: string;
|
||||
balance: Balance;
|
||||
connectedService: string;
|
||||
}
|
||||
|
||||
interface Balance {
|
||||
neo: Price;
|
||||
}
|
||||
|
||||
interface Price {
|
||||
has: string;
|
||||
value: string;
|
||||
currency: Currency;
|
||||
formatted: string;
|
||||
deadline: string;
|
||||
}
|
||||
|
||||
type Currency = 'NEO';
|
||||
|
||||
interface Rating {
|
||||
value: string;
|
||||
count: string;
|
||||
my: string;
|
||||
}
|
||||
|
||||
interface ReadProgress {
|
||||
chapter: string;
|
||||
progress: string;
|
||||
}
|
||||
|
||||
interface ReadTime {
|
||||
hours: string;
|
||||
minutes: string;
|
||||
}
|
||||
|
||||
interface NovelsUser {
|
||||
available: string;
|
||||
boosted: string;
|
||||
id: string;
|
||||
status: string;
|
||||
verified: string;
|
||||
metrica: string;
|
||||
cover: Image;
|
||||
photo: Image;
|
||||
username: string;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
initials: string;
|
||||
link: string;
|
||||
about: string;
|
||||
subscriptions: string;
|
||||
subscribers: string;
|
||||
publications: string;
|
||||
books: string;
|
||||
poems: string;
|
||||
posts: string;
|
||||
drafts: string;
|
||||
isPro: string;
|
||||
isAdult: string;
|
||||
isBanned: string;
|
||||
isSubscription: string;
|
||||
isBlock: string;
|
||||
gr: string;
|
||||
}
|
||||
Reference in New Issue
Block a user